Skip to content
This repository has been archived by the owner on Dec 11, 2020. It is now read-only.

Simple interface #59

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
inherit_gem:
wetransfer_style: ruby/default.yml
wetransfer_style: ruby/default.yml
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.6.1
8 changes: 6 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ rvm:
- 2.4
- 2.5
- 2.6
- jruby-9.0
- jruby-9.0.5
- jruby-9.1.6
- jruby-9.2.0
before_install: echo Y | rvm @global do gem uninstall bundler ; gem install bundler -v 1.16.1
cache: bundler
matrix:
allow_failures:
- rvm: jruby-9.0
- rvm: jruby-9.0.5
- rvm: jruby-9.1.6
- rvm: jruby-9.2.0
script:
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bundle exec rspec --exclude-pattern "spec/integration_spec.rb"; fi'
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bundle exec rspec; fi'
Expand Down
99 changes: 86 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ For your API key and additional info please visit our [developer portal](https:/
Add this line to your application's Gemfile:

```ruby
gem 'wetransfer', version: '0.9.0.beta2'
gem 'wetransfer', version: '0.10.0.beta1'

# If you need Board support, as found in WeTransfer's Collect app, use version 0.9.x)
gem 'wetransfer', version: '0.9.0.beta3'
```

And then execute:
Expand Down Expand Up @@ -57,7 +60,7 @@ Open the file in your text editor and add this line:

WT_API_KEY=<your api key>

Make sure to replace `<your api key>` by your actual api key. Don't include the pointy brackets!
Make sure to replace `<your api key>` with your actual api key. Don't include the pointy brackets!

Great! Now you can go to your project file and use the client.

Expand All @@ -67,37 +70,107 @@ A transfer is a collection of files that can be created once, and downloaded unt

```ruby
# In your project file:
require 'we_transfer_client'
require 'we_transfer'

client = WeTransfer::Client.new(api_key: ENV.fetch('WT_API_KEY'))
```

Now that you've got the client set up you can use `create_transfer_and_upload_files` to, well, create a transfer, and upload all files!

```ruby
transfer = client.create_transfer_and_upload_files(message: 'All the Things') do |upload|
upload.add_file_at(path: '/path/to/local/file.jpg')
upload.add_file_at(path: '/path/to/another/local/file.jpg')
upload.add_file(name: 'README.txt', io: StringIO.new("You should read All the Things!"))
transfer = client.create_transfer_and_upload_files(message: 'All the Things') do |transfer|
# Add a file using File.open. If you do it like this, :name and :io params are optional
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We recently introduced empty directories, so maybe this method can better be named add_item? We could also leave it where it is at for now and add add_empty_directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will propose a change, keep you posted

transfer.add_file(io: File.open('Gemfile'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we provide auto-DWIM-ing of the filename from the File object we pass in io: it would be good to document how it works exactly. For example, will it only use the basename? Will it call #path on the file? I think I would even go as far as make the method "overloaded" - hit a different code path when you pass in a file: keyword argument and nothing else, and raise if you, say, pass both file: and name:. The rationale being mostly: if we provide "'magic'"™® it should be easy for the end-user to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, The SDK relies on File.basename deeper buried inside as a fallback only. You can rename the file by passing in a name.
Will add documentation to express this behaviour.


# Add a file with File.open, but give it a different name inside the transfer
transfer.add_file(
name: 'hello_world.rb',
io: File.open('path/to/file/with/different_name.rb')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README in this case encourages leaking file descriptors. For example, if I encountered a README like this I would probably assume that it is the add_file call that is going to perform #read on the passed IO objects, and I can (and should) actually call it like this:

File.open('/path/to/myfile', 'rb') do |fi|
   transfer.add_file(name: 'myfile', io: fi)
end

but with this example this will probably break as the actual reading of the file data will happen at block exit. Can there be a way to demonstrate it in the readme?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have an IRL conversation, since I'm unsure if I understand the ins and outs of your point.

)

# Using :name, :size and :io params.
# Specifying the size is not very useful in this case, but feel free to explicitly
# communicate the size of the coming io.
#
# The :name param is compulsory if it cannot be derived from the IO.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we say "cannot be derived" we probably need to explain how it will be derived if that is possible (by calling #path?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: See here for a closely related issue.

transfer.add_file(
name: 'README.txt',
size: 31,
io: StringIO.new("You should read All the Things!")
)
end

# To get a link to your transfer, call `url` on your transfer object:
transfer.url => "https://we.tl/t-123234="
transfer.url # => "https://we.tl/t-1232346"

# Or inspect the whole transfer:
transfer.to_h # =>
# {
# :id => "0d5ce492c0cd935b5376c7858b0ff5ae20190307162739",
# :state => "processing",
# :url => "https://we.tl/t-CVINGH30C4",
# :message => "test transfer",
# :files => [
# {
# :name => "README.txt",
# :size => 31,
# :id => "0e04833491a31776770ac4dcf83d1f4a20190307162739",
# :multipart => {
# :chunks => 1,
# :chunk_size => 31
# }
# }, {
# :name => "hello_world.rb",
# :size => 166,
# :id => "22423dd4b44300641a4659203ba5d1bb20190307162739",
# :multipart => {
# :chunks => 1,
# :chunk_size => 166
# }
# }
# ]
# }
```

The upload will be performed at the end of the block. Depending on your file sizes and network connection speed, this might take some time.

What are you waiting for? Open that link in your browser! Chop chop.

If you want to have more control over which files uploads when, it is also possible.

```ruby
# Create a transfer that consists of 1 file.
transfer = client.create_transfer(message: "test transfer") do |transfer|
# When creating a transfer, at least the name and the size of the file(s)
# must be known.
transfer.add_file(name: "small_file", size: 80)
end

# Upload the file. The Ruby SDK will upload the file in chunks.
transfer.upload_file(name: "small_file", io: StringIO.new("#" * 80))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enforces addressing files "by name" whereas in practice the server gives you file IDs at transfer create. It is in our best interest to stimulate the consumer of the SDK to either use an abstraction layer that handles ID binding for them:

transfer.files.first.upload(io: ...)

or encourage them to explore the related file metadata:

transfer.files #=> [<RemoteFile id: ...>]

Our approach to "auto-intuiting" the file IDs from the filenames has led to problems as we destructively sanitize the filenames when they enter our systems

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name that the user provided may not be equal to what the WT stack makes of it. This is documented in the Public API. The sdk allows you to call your file 'the-weather-is:.jpg', and reusing that name, while it will be
But from the high-level view of the SDK, I want users to not worry about this. Just as the sdk hides the uploading in chunks, this method hides the file naming details.

Can you agree to this if this is documented better, or do you want the SDK to get rid of this, and use the WT file naming logic only?


# Mark the file as completely uploaded. All the chunks of the file will be joined
# together to recreate the file
transfer.complete_file(name: "small_file")

# Mark the transfer as completely done, so your customers can start downloading it
transfer.finalize

# Inspect your transfer. Use transfer.to_h or transfer.to_json, depending on your scenario
transfer.to_h
```

## Boards

**NOTE**: **Boards are disabled from version 0.10.x** of the Ruby SDK. The latest releases that include **board support is found in version 0.9.x**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we remove Boards support from this version probably the documentation has to be removed as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the docs, but keep the comment about version 0.9, so peeps will know where to go to.


A board is a collection of files and links, but it is open for modifications. Like your portfolio: While working, you can make adjustments to it. A board is a fantastic place for showcasing your work in progress.

Boards need a WeTransfer Client to be present, just like transfers.

```ruby
# In your project file:
require 'we_transfer_client'
require 'we_transfer'

client = WeTransfer::Client.new(api_key: ENV.fetch('WT_API_KEY'))
```
Expand All @@ -106,10 +179,10 @@ After you create your client, you can

### Create a board and upload items


```ruby
board = client.create_board(name: 'Meow', description: 'On Cats') do |items|
items.add_file(name: 'big file.jpg', io: File.open('/path/to/huge_file.jpg', 'rb')items.add_file_at(path: '/path/to/another/file.txt')
items.add_file(name: 'big file.jpg', io: File.open('/path/to/huge_file.jpg')
items.add_file_at(path: '/path/to/another/file.txt')
items.add_web_url(url: 'http://wepresent.wetransfer.com', title: 'Time well spent')
end

Expand Down Expand Up @@ -150,12 +223,12 @@ Hooray!

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/wetransfer/wetransfer_ruby_sdk. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. More extensive contribution guidelines can be found [here](https://github.com/WeTransfer/wetransfer_ruby_sdk/blob/master/.github/CONTRIBUTING.md).
Bug reports and pull requests are welcome on GitHub at <https://github.com/wetransfer/wetransfer_ruby_sdk.> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. More extensive contribution guidelines can be found [here](https://github.com/WeTransfer/wetransfer_ruby_sdk/blob/master/.github/CONTRIBUTING.md).

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT) - the in-repo version of the license is [here](https://github.com/WeTransfer/wetransfer_ruby_sdk/blob/master/LICENSE.txt).

## Code of Conduct

Everyone interacting in the WeTransfer Ruby SDK project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/WeTransfer/wetransfer_ruby_sdk/blob/master/.github/CODE_OF_CONDUCT.md).
Everyone interacting in the WeTransfer Ruby SDK project’s code bases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/WeTransfer/wetransfer_ruby_sdk/blob/master/.github/CODE_OF_CONDUCT.md).
2 changes: 1 addition & 1 deletion bin/console
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby

require 'bundler/setup'
require 'we_transfer_client'
require 'we_transfer'

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
Expand Down
2 changes: 1 addition & 1 deletion examples/create_collection.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require_relative 'we_transfer_client'
require_relative 'we_transfer'
require 'dotenv'
Dotenv.load

Expand Down
2 changes: 1 addition & 1 deletion examples/create_transfer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require_relative 'we_transfer_client'
require_relative 'we_transfer'
require 'dotenv'
Dotenv.load

Expand Down
36 changes: 36 additions & 0 deletions lib/we_transfer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

require 'faraday'
require 'logger'
require 'json'
require 'ks'

%w[
logging
communicator
client
transfer
mini_io
we_transfer_file
remote_file version
].each do |file|
require_relative "we_transfer/#{file}"
end

module WeTransfer
NULL_LOGGER = Logger.new(nil)

def self.logger
@logger || NULL_LOGGER
end

# Set the logger to your preferred logger
#
# @params new_logger [Logger] the logger that WeTransfer SDK should use
#
# example:
# WeTransfer.logger = Rails.logger
def self.logger=(new_logger)
@logger = new_logger
end
end
32 changes: 32 additions & 0 deletions lib/we_transfer/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module WeTransfer
class Client
class Error < StandardError; end
extend Forwardable

# Initialize a WeTransfer::Client
#
# @param api_key [String] The API key you want to authenticate with
#
# @return [WeTransfer::Client]
def initialize(api_key:)
@communicator = Communicator.new(api_key)
end

def create_transfer(**args, &block)
transfer = WeTransfer::Transfer.new(args.merge(communicator: @communicator))
transfer.persist(&block)
end

def create_transfer_and_upload_files(**args, &block)
transfer = create_transfer(args, &block)
transfer
.upload_files
.complete_files
.finalize
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could even get rid of the transfer helper var, but it expresses intent so beautifully :)

end

def_delegator :@communicator, :find_transfer
end
end
Loading