transloadit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ .bundle
2
+ .rvmrc
3
+ .yardoc
4
+
5
+ Gemfile.lock
6
+
7
+ coverage
8
+ doc
9
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # transloadit
2
+
3
+ Fantastic file uploading for your web application.
4
+
5
+ ## Description
6
+
7
+ This is the official Ruby gem for [Transloadit](transloadit.com). It allows
8
+ you to automate uploading files through the Transloadit REST API.
9
+
10
+ ## Install
11
+
12
+ gem install transloadit
13
+
14
+ ## Getting started
15
+
16
+ To get started, you need to require the 'transloadit' gem:
17
+
18
+ $ irb -rubygems
19
+ >> require 'transloadit'
20
+ => true
21
+
22
+ Then create a Transloadit instance, which will maintain your authentication
23
+ credentials and allow us to make requests to the API.
24
+
25
+ transloadit = Transloadit.new(
26
+ key: 'transloadit-auth-key',
27
+ secret: 'transloadit-auth-secret'
28
+ )
29
+
30
+ ### 1. Resize and store an image
31
+
32
+ This example demonstrates how you can create an assembly to resize an image
33
+ and store the result on [Amazon S3](http://aws.amazon.com/s3/).
34
+
35
+ First, we create two steps: one to resize the image to 320x240, and another to
36
+ store the image in our S3 bucket.
37
+
38
+ resize = transloadit.step '/image/resize',
39
+ width: 320,
40
+ height: 240
41
+
42
+ store = transloadit.step '/s3/store',
43
+ key: 'aws-access-key-id',
44
+ secret: 'aws-secret-access-key',
45
+ bucket: 's3-bucket-name'
46
+
47
+ Now that we have the steps, we create an assembly (which is just a request to
48
+ process a file or set of files) and let Transloadit do the rest.
49
+
50
+ assembly = transloadit.assembly(
51
+ steps: [ resize, store ]
52
+ )
53
+
54
+ response = assembly.submit! open('lolcat.jpg')
55
+
56
+ When the `submit!` method returns, the file has been uploaded but may not yet
57
+ be done processing. We can use the returned object to check if processing has
58
+ completed, or examine other attributes of the request.
59
+
60
+ # returns the unique API ID of the assembly
61
+ response[:assembly_id] # => '9bd733a...'
62
+
63
+ # returns the API URL endpoint for the assembly
64
+ response[:assembly_url] # => 'http://api2.vivian.transloadit.com/assemblies/9bd733a...'
65
+
66
+ # checks how many bytes were expected / received by transloadit
67
+ response[:bytes_expected] # => 92933
68
+ response[:bytes_received] # => 92933
69
+
70
+ # checks if all processing has been completed
71
+ response.completed? # => false
72
+
73
+ # cancels further processing on the assembly
74
+ response.cancel! # => true
75
+
76
+ It's important to note that none of these queries are "live" (with the
77
+ exception of the `cancel!` method). They all check the response given by the
78
+ API at the time the assembly was created. You have to explicitly ask the
79
+ assembly to reload its results from the API.
80
+
81
+ # reloads the response's contents from the REST API
82
+ response.reload!
83
+
84
+ In general, you use hash accessor syntax to query any direct attribute from
85
+ the [response](http://transloadit.com/docs/assemblies#response-format).
86
+ Methods suffixed by a question mark provide a more readable way of quering
87
+ state (e.g., `assembly.completed?` vs. checking the result of
88
+ `assembly[:ok]`). Methods suffixed by a bang make a live query against the
89
+ Transloadit HTTP API.
90
+
91
+ ### 2. Uploading multiple files
92
+
93
+ Multiple files can be given to the `submit!` method in order to upload more
94
+ than one file in the same request. You can also pass a single step for the
95
+ `steps` parameter, without having to wrap it in an Array.
96
+
97
+ assembly = transloadit.assembly(steps: store)
98
+
99
+ response = assembly.submit!(
100
+ open('puppies.jpg'),
101
+ open('kittens.jpg'),
102
+ open('ferrets.jpg')
103
+ )
104
+
105
+ ### 3. Parallel Assembly
106
+
107
+ Transloadit allows you to perform several processing steps in parallel. You
108
+ simply need to `use` other steps. Following
109
+ [their example](http://transloadit.com/docs/assemblies#special-parameters):
110
+
111
+ encode = transloadit.step '/video/encode', { ... }
112
+ thumbs = transloadit.step '/video/thumbs', { ... }
113
+ export = transloadit.step '/s3/store', { ... }
114
+
115
+ export.use [ encode, thumbs ]
116
+
117
+ transloadit.assembly(
118
+ steps: [ encode, thumbs, export ]
119
+ ).submit! open('ninja-cat.mpg')
120
+
121
+ You can also tell a step to use the original uploaded file by passing the
122
+ Symbol `:original` instead of another step.
123
+
124
+ Check the YARD documentation for more information on using
125
+ [use](http://rubydoc.info/gems/transloadit/frames/Transloadit/Step#use-instance_method).
126
+
127
+ ## Documentation
128
+
129
+ Up-to-date YARD documentation is automatically generated. You can view the
130
+ docs for the [released gem](http://rubydoc.info/gems/transloadit/frames) or
131
+ for the latest [git master](http://rubydoc.info/github/transloadit/ruby-sdk/master/frames).
132
+
133
+ ## Compatibility
134
+
135
+ At a minimum, this gem should work on MRI 1.9.2, 1.8.7, 1.8.6, and Rubinius
136
+ 1.2.0. If it doesn't, please file a [bug report](https://github.com/transloadit/ruby-sdk/issues).
137
+ Compatibility patches for other Rubies are welcomed.
138
+
139
+ You can run `rake test:multiruby` to test transloadit against all supported
140
+ Rubies. Run `rake test:multiruby:setup` once beforehand, though, to set up the
141
+ RVM environments. [RVM](rvm.beginrescueend.com/) must be installed in order to
142
+ test against multiple Rubies.
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require 'rake/gempackagetask'
2
+ require 'rake/testtask'
3
+
4
+ RUBIES = %w{ 1.9.2 1.8.7 1.8.6 rbx-1.2.0 }
5
+
6
+ GEMSPEC = 'transloadit.gemspec'
7
+
8
+ spec = eval open(GEMSPEC).read
9
+ Rake::GemPackageTask.new(spec) do |gem|
10
+ gem.need_tar = true
11
+ end
12
+
13
+ Rake::TestTask.new do |test|
14
+ test.libs << 'test'
15
+ test.pattern = 'test/**/test_*.rb'
16
+ end
17
+
18
+ namespace :test do
19
+ begin
20
+ `rvm -v` # raise an exception if RVM isn't installed
21
+
22
+ desc 'Run tests against all supported Rubies'
23
+ task :multiruby do
24
+ system "rvm #{RUBIES.join(',')} ruby bundle exec rake -s test"
25
+
26
+ # clean up after Rubinius
27
+ require 'pathname'
28
+ Pathname.glob('**/*.rbc').each {|path| path.unlink }
29
+ end
30
+
31
+ namespace :multiruby do
32
+ desc 'Prepare supported rubiesfor testing'
33
+ task :setup do
34
+ warn 'Preparing multiruby. This may take awhile...'
35
+
36
+ # create gemsets, install bundler, bundle
37
+ RUBIES.each {|ruby| system "rvm #{ruby} gemset create transloadit" }
38
+ system "rvm #{RUBIES.join(',')} gem install bundler --no-ri --no-rdoc"
39
+ system "rvm #{RUBIES.join(',')} ruby bundle install"
40
+ end
41
+ end
42
+ rescue Errno::ENOENT
43
+ desc 'You need `rvm` installed to test against multiple Rubies'
44
+ task :multiruby
45
+ end
46
+ end
47
+
48
+ begin
49
+ require 'yard'
50
+ require 'yard/rake/yardoc_task'
51
+
52
+ YARD::Rake::YardocTask.new :doc do |yard|
53
+ yard.options = %w{
54
+ --title Transloadit
55
+ --readme README.md
56
+ --markup rdoc
57
+ }
58
+ end
59
+ rescue
60
+ desc 'You need the `yard` gem to generate documentation'
61
+ task :doc
62
+ end
@@ -0,0 +1,114 @@
1
+ require 'json'
2
+
3
+ #
4
+ # Implements the Transloadit REST API in Ruby. Check the {file:README.md README}
5
+ # for usage instructions.
6
+ #
7
+ class Transloadit
8
+ autoload :Assembly, 'transloadit/assembly'
9
+ autoload :Request, 'transloadit/request'
10
+ autoload :Response, 'transloadit/response'
11
+ autoload :Step, 'transloadit/step'
12
+ autoload :VERSION, 'transloadit/version'
13
+
14
+ # @return [String] your Transloadit auth key
15
+ attr_accessor :key
16
+
17
+ # @return [String] your Transloadit auth secret, for signing requests
18
+ attr_accessor :secret
19
+
20
+ #
21
+ # Creates a new instance of the Transloadit API.
22
+ #
23
+ # @param [Hash] options a hash of options, which can be any of:
24
+ # @option options [String] :key your auth key from the
25
+ # {credentials}[https://transloadit.com/accounts/credentials] page
26
+ # (required)
27
+ # @option options [String] :secret your auth secret from the
28
+ # {credentials}[https://transloadit.com/accounts/credentials] page, for
29
+ # signing requests (optional)
30
+ #
31
+ def initialize(options = {})
32
+ self.key = options[:key]
33
+ self.secret = options[:secret]
34
+
35
+ _ensure_key_provided
36
+ end
37
+
38
+ #
39
+ # Creates a Transloadit::Step describing a step in an upload assembly.
40
+ #
41
+ # @param [String] robot the robot to use in this step (e.g., '/image/resize')
42
+ # @param [Hash] options a hash of options to customize the robot's
43
+ # operation; see the {online documentation}[http://transloadit.com/docs/building-assembly-instructions]
44
+ # for robot-specific options
45
+ # @return [Step] the created Step
46
+ #
47
+ def step(robot, options = {})
48
+ Transloadit::Step.new(robot, options)
49
+ end
50
+
51
+ #
52
+ # Creates a Transloadit::Assembly ready to be sent to the REST API.
53
+ #
54
+ # @param [Hash] options additional parameters to send with the assembly
55
+ # submission; for a full list of parameters, see the official
56
+ # documentation on {templates}[http://transloadit.com/docs/templates].
57
+ # @option options [Step, Array<Step>] :steps the steps to perform in this
58
+ # assembly
59
+ # @option options [String] :notify_url A URL to be POSTed when the assembly
60
+ # has finished processing
61
+ # @option options [String] :template_id the ID of a
62
+ # {template}[https://transloadit.com/templates] to use instead of
63
+ # specifying options here directly
64
+ #
65
+ def assembly(options = {})
66
+ Transloadit::Assembly.new(self, options)
67
+ end
68
+
69
+ #
70
+ # @return [String] a human-readable version of the Transloadit.
71
+ #
72
+ def inspect
73
+ self.to_hash.inspect
74
+ end
75
+
76
+ #
77
+ # @return [Hash] a Transloadit-compatible Hash of the instance's contents
78
+ #
79
+ def to_hash
80
+ result = { :key => self.key }
81
+ result.update(:expires => _generate_expiry) unless self.secret.nil?
82
+ result
83
+ end
84
+
85
+ #
86
+ # @return [String] JSON-encoded String containing the object's hash contents
87
+ #
88
+ def to_json
89
+ self.to_hash.to_json
90
+ end
91
+
92
+ private
93
+
94
+ #
95
+ # Raises an ArgumentError if no {#key} has been assigned.
96
+ #
97
+ def _ensure_key_provided
98
+ unless self.key
99
+ raise ArgumentError, 'an authentication key must be provided'
100
+ end
101
+ end
102
+
103
+ #
104
+ # Automatically generates API-compatible request expiration times 5 minutes
105
+ # from now.
106
+ #
107
+ # @param [Integer] duration the number of seconds from now to set the
108
+ # expiry time
109
+ # @return [String] an API-compatible timestamp
110
+ #
111
+ def _generate_expiry(duration = 5 * 60)
112
+ (Time.now + duration).utc.strftime('%Y/%m/%d %H:%M:%S+00:00')
113
+ end
114
+ end
@@ -0,0 +1,122 @@
1
+ require 'transloadit'
2
+
3
+ #
4
+ # Represents a Assembly ready to be sent to the REST API for processing. An
5
+ # Assembly can contain one or more Steps for processing or point to a
6
+ # server-side template. It's submitted along with a list of files to process,
7
+ # at which point Transloadit will process and store the files according to the
8
+ # rules in the Assembly.
9
+ #
10
+ # See the Transloadit {documentation}[http://transloadit.com/docs/building-assembly-instructions]
11
+ # for futher information on Assemblies and their parameters.
12
+ #
13
+ class Transloadit::Assembly
14
+ # @return [Transloadit] the associated Transloadit instance
15
+ attr_reader :transloadit
16
+
17
+ # @return [Hash] the options describing the Assembly
18
+ attr_accessor :options
19
+
20
+ #
21
+ # Creates a new Assembly authenticated using the given +transloadit+
22
+ # instance.
23
+ #
24
+ # @param [Transloadit] transloadit the associated Transloadit instance
25
+ # @param [Hash] options the configuration for the Assembly;
26
+ # see {Transloadit#assembly}
27
+ #
28
+ def initialize(transloadit, options = {})
29
+ self.transloadit = transloadit
30
+ self.options = options
31
+ end
32
+
33
+ #
34
+ # @return [Hash] the processing steps, formatted for sending to Transloadit
35
+ #
36
+ def steps
37
+ _wrap_steps_in_hash options[:steps]
38
+ end
39
+
40
+ #
41
+ # Submits the assembly for processing. Accepts as many IO objects as you
42
+ # wish to process in the assembly. The last argument is an optional Hash
43
+ # of parameters to send along with the request.
44
+ #
45
+ # @overload submit!(*ios)
46
+ # @param [Array<IO>] *ios the files for the assembly to process
47
+ #
48
+ # @overload submit!(*ios, params = {})
49
+ # @param [Array<IO>] *ios the files for the assembly to process
50
+ # @param [Hash] params additional POST data to submit with the request
51
+ #
52
+ def submit!(*ios)
53
+ params = self.to_hash.update _extract_options!(*ios)
54
+
55
+ ios.each_with_index do |f, i|
56
+ params.update "file_#{i}" => f
57
+ end
58
+
59
+ request = Transloadit::Request.new '/assemblies',
60
+ self.transloadit.secret
61
+
62
+ request.post(params).extend!(Transloadit::Response::Assembly)
63
+ end
64
+
65
+ #
66
+ # @return [String] a human-readable version of the Assembly
67
+ #
68
+ def inspect
69
+ self.to_hash.inspect
70
+ end
71
+
72
+ #
73
+ # @return [Hash] a Transloadit-compatible Hash of the Assembly's contents
74
+ #
75
+ def to_hash
76
+ self.options.merge(
77
+ :auth => self.transloadit.to_hash,
78
+ :steps => self.steps
79
+ ).delete_if {|k,v| v.nil? }
80
+ end
81
+
82
+ #
83
+ # @return [String] JSON-encoded String containing the Assembly's contents
84
+ #
85
+ def to_json
86
+ self.to_hash.to_json
87
+ end
88
+
89
+ protected
90
+
91
+ attr_writer :transloadit
92
+
93
+ private
94
+
95
+ #
96
+ # Returns a Transloadit-compatible Hash wrapping the +steps+ passed to it.
97
+ # Accepts any supported format the +steps+ could come in.
98
+ #
99
+ # @param [nil, Hash, Step, Array] steps the steps to encode
100
+ # @return [Hash] the Transloadit-compatible hash of steps
101
+ #
102
+ def _wrap_steps_in_hash(steps)
103
+ case steps
104
+ when nil then steps
105
+ when Hash then steps
106
+ when Transloadit::Step then steps.to_hash
107
+ else
108
+ steps.inject({}) {|h, s| h.update s }
109
+ end
110
+ end
111
+
112
+ #
113
+ # Extracts the last argument from a set of arguments if it's a hash.
114
+ # Otherwise, returns an empty hash.
115
+ #
116
+ # @param *args the arguments to search for an options hash
117
+ # @return [Hash] the options passed, otherwise an empty hash
118
+ #
119
+ def _extract_options!(*args)
120
+ args.last.is_a?(Hash) ? args.pop : {}
121
+ end
122
+ end
@@ -0,0 +1,123 @@
1
+ require 'transloadit'
2
+
3
+ require 'rest-client'
4
+ require 'openssl'
5
+
6
+ #
7
+ # Wraps requests to the Transloadit API. Ensures all API requests return a
8
+ # parsed Transloadit::Response, and abstracts away finding a lightly-used
9
+ # instance on startup.
10
+ #
11
+ class Transloadit::Request
12
+ # The default Transloadit API endpoint.
13
+ API_ENDPOINT = URI.parse('http://api2.transloadit.com/')
14
+
15
+ # The default headers to send to the API.
16
+ API_HEADERS = { 'User-Agent' => %{Transloadit Ruby SDK #{Transloadit::VERSION}} }
17
+
18
+ # The HMAC algorithm used for calculation request signatures.
19
+ HMAC_ALGORITHM = OpenSSL::Digest::Digest.new('sha1')
20
+
21
+ # @return [String] the API endpoint for the request
22
+ attr_reader :url
23
+
24
+ # @return [String] the authentication secret to sign the request with
25
+ attr_accessor :secret
26
+
27
+ def self.bored!
28
+ self.api(self.bored)
29
+ end
30
+
31
+ def initialize(url, secret = nil)
32
+ self.url = URI.parse(url.to_s)
33
+ self.secret = secret
34
+ end
35
+
36
+ def get(params = {})
37
+ self.request! do
38
+ self.api[url.path + self.to_query(params)].get(API_HEADERS)
39
+ end
40
+ end
41
+
42
+ def delete(params = {})
43
+ self.request! do
44
+ self.api[url.path + self.to_query(params)].delete(API_HEADERS)
45
+ end
46
+ end
47
+
48
+ def post(payload = {})
49
+ self.request! do
50
+ self.api[url.path].post(self.to_payload(payload), API_HEADERS)
51
+ end
52
+ end
53
+
54
+ def inspect
55
+ self.url.to_s.inspect
56
+ end
57
+
58
+ def to_hash
59
+ { :signature => self.signature }.delete_if {|k,v| v.nil? }
60
+ end
61
+
62
+ def to_json
63
+ self.to_hash.to_json
64
+ end
65
+
66
+ protected
67
+
68
+ attr_writer :url
69
+
70
+ def self.bored
71
+ self.new(API_ENDPOINT + '/instances/bored').get['api2_host']
72
+ end
73
+
74
+ def self.api(uri = nil)
75
+ @api = RestClient::Resource.new(uri) if uri
76
+ @api ||= RestClient::Resource.new(self.bored)
77
+ end
78
+
79
+ def api
80
+ @api ||= begin
81
+ case self.url.host
82
+ when String then RestClient::Resource.new(self.url.host)
83
+ else self.class.api
84
+ end
85
+ end
86
+ end
87
+
88
+ def to_payload(params = nil)
89
+ return self.to_hash if params.nil?
90
+ return self.to_hash if params.respond_to?(:empty?) and params.empty?
91
+
92
+ self.to_hash.update(:params => params.to_json)
93
+ end
94
+
95
+ def to_query(params = nil)
96
+ return '' if params.nil?
97
+ return '' if params.respond_to?(:empty?) and params.empty?
98
+
99
+ escape = Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
100
+ params = URI.escape(params.to_json, escape)
101
+
102
+ '?' + self.to_hash.
103
+ update(:params => params).
104
+ map {|k,v| "#{k}=#{v}" }.
105
+ join('&')
106
+ end
107
+
108
+ def request!(&request)
109
+ Transloadit::Response.new request.call
110
+ rescue RestClient::Exception => e
111
+ Transloadit::Response.new e.response
112
+ end
113
+
114
+ def signature
115
+ self.class._hmac(self.secret, self.params.to_json) if self.secret
116
+ end
117
+
118
+ private
119
+
120
+ def self._hmac(key, message)
121
+ OpenSSL::HMAC.hexdigest HMAC_ALGORITHM, key, message
122
+ end
123
+ end
@@ -0,0 +1,43 @@
1
+ require 'transloadit'
2
+ require 'delegate'
3
+
4
+ class Transloadit::Response < Delegator
5
+ autoload :Assembly, 'transloadit/response/assembly'
6
+
7
+ def initialize(response, &extension)
8
+ self.__setobj__(response)
9
+
10
+ instance_eval(&extension) if block_given?
11
+ end
12
+
13
+ def [](attribute)
14
+ self.body[attribute]
15
+ end
16
+
17
+ def body
18
+ JSON.parse self.__getobj__.body
19
+ end
20
+
21
+ def inspect
22
+ self.body.inspect
23
+ end
24
+
25
+ def extend!(mod)
26
+ self.extend(mod)
27
+ self
28
+ end
29
+
30
+ protected
31
+
32
+ def __getobj__
33
+ @response
34
+ end
35
+
36
+ def __setobj__(response)
37
+ @response = response
38
+ end
39
+
40
+ def replace(other)
41
+ self.__setobj__ other.__getobj__
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ require 'transloadit'
2
+
3
+ module Transloadit::Response::Assembly
4
+ def reload!
5
+ self.replace Transloadit::Request.new(self['assembly_url']).get
6
+ end
7
+
8
+ def cancel!
9
+ self.replace Transloadit::Request.new(self['assembly_url']).delete
10
+ end
11
+
12
+ def completed?
13
+ r['ok'] == 'ASSEMBLY_COMPLETED'
14
+ end
15
+ end
@@ -0,0 +1,92 @@
1
+ require 'transloadit'
2
+
3
+ #
4
+ # Implements the concept of a step in the Transloadit API. Each Step has a
5
+ # +robot+ (e.g., '/image/resize' or '/video/thumbnail') and a hash of
6
+ # +options+ specific to the chosen robot.
7
+ #
8
+ # See the Transloadit {documentation}[http://transloadit.com/docs/building-assembly-instructions]
9
+ # for futher information on robot types and their parameters.
10
+ #
11
+ class Transloadit::Step
12
+ # @return [String] the robot to use
13
+ attr_reader :robot
14
+
15
+ # @return [Hash] the robot's options
16
+ attr_accessor :options
17
+
18
+ #
19
+ # Creates a new Step with the given +robot+.
20
+ #
21
+ # @param [String] robot the robot to use
22
+ # @param [Hash] options the configuration options for the robot; see
23
+ # {Transloadit#step} for possible values
24
+ #
25
+ def initialize(robot, options = {})
26
+ self.robot = robot
27
+ self.options = options
28
+ end
29
+
30
+ #
31
+ # Automatically generates a unique, 32-character hex name for the step that
32
+ # uses this robot.
33
+ #
34
+ # @return [String] a randomly generated name
35
+ #
36
+ def name
37
+ # rand() is "good enough" for this; we generate 128 random bits (same
38
+ # length as a UUID for future compatibility) and convert it to hex
39
+ @name ||= rand(2 ** 128).to_s(16).rjust(32, '0')
40
+ end
41
+
42
+ #
43
+ # Specifies that this Step should process the provided +input+ instead of
44
+ # the output of the Step before it.
45
+ #
46
+ # @param [Step, Array<Step>, Symbol, nil] input The input
47
+ # step to use. Follows the conventions outlined in the
48
+ # online {documentation}[http://transloadit.com/docs/building-assembly-instructions#special-parameters].
49
+ # The symbol +:original+ specifies that the original file should be sent
50
+ # to the robot. A Step indicates that this Step's output should be used
51
+ # as the input to this one. Likewise, an array of Steps tells Transloadit
52
+ # to use pass each of their outputs to this Step. And lastly, an explicit
53
+ # nil clears the setting and restores it to its default input.
54
+ #
55
+ # @return [String, Array<String>, nil> The value for the +:use+ parameter
56
+ # that will actually be sent to the REST API.
57
+ #
58
+ def use(input)
59
+ self.options.delete(:use) and return if input.nil?
60
+
61
+ self.options[:use] = case input
62
+ when Symbol then input.inspect
63
+ when Array then input.map {|i| i.name }
64
+ else [ input.name ]
65
+ end
66
+ end
67
+
68
+ #
69
+ # @return [String] a human-readable version of the Step
70
+ #
71
+ def inspect
72
+ self.to_hash[self.name].inspect
73
+ end
74
+
75
+ #
76
+ # @return [Hash] a Transloadit-compatible Hash of the Step's contents
77
+ #
78
+ def to_hash
79
+ { self.name => options.merge(:robot => self.robot) }
80
+ end
81
+
82
+ #
83
+ # @return [String] JSON-encoded String containing the Step's hash contents
84
+ #
85
+ def to_json
86
+ self.to_hash.to_json
87
+ end
88
+
89
+ protected
90
+
91
+ attr_writer :robot
92
+ end
@@ -0,0 +1,4 @@
1
+ class Transloadit
2
+ # The current version of Transloadit.
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,67 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://api2.transloadit.com:80/instances/bored
6
+ body:
7
+ headers:
8
+ accept:
9
+ - "*/*; q=0.5, application/xml"
10
+ accept-encoding:
11
+ - gzip, deflate
12
+ user-agent:
13
+ - Transloadit Ruby SDK 0.0.1
14
+ response: !ruby/struct:VCR::Response
15
+ status: !ruby/struct:VCR::ResponseStatus
16
+ code: 200
17
+ message: OK
18
+ headers:
19
+ access-control-allow-headers:
20
+ - X-Requested-With, Content-Type, Accept, Content-Length
21
+ access-control-allow-methods:
22
+ - POST, GET, PUT, DELETE, OPTIONS
23
+ access-control-allow-origin:
24
+ - "*"
25
+ content-type:
26
+ - text/plain
27
+ content-length:
28
+ - "98"
29
+ connection:
30
+ - keep-alive
31
+ body: "{\"ok\":\"BORED_INSTANCE_FOUND\",\"host\":\"vivian.transloadit.com\",\"api2_host\":\"vivian.transloadit.com\"}"
32
+ http_version: "1.1"
33
+ - !ruby/struct:VCR::HTTPInteraction
34
+ request: !ruby/struct:VCR::Request
35
+ method: :post
36
+ uri: http://vivian.transloadit.com:80/assemblies
37
+ body: params=%7B%22steps%22%3A%7B%22a479db2c601661d8f914caf9cf258c0b%22%3A%7B%22robot%22%3A%22%2Fvideo%2Fthumbs%22%7D%7D%2C%22redirect_url%22%3A%22http%3A%2F%2Ffoo.bar%2F%22%2C%22auth%22%3A%7B%22key%22%3A%22%22%7D%2C%22file_0%22%3A%22%23%3CFile%3A0x00000100c63738%3E%22%7D
38
+ headers:
39
+ accept:
40
+ - "*/*; q=0.5, application/xml"
41
+ accept-encoding:
42
+ - gzip, deflate
43
+ user-agent:
44
+ - Transloadit Ruby SDK 0.0.1
45
+ content-length:
46
+ - "298"
47
+ content-type:
48
+ - application/x-www-form-urlencoded
49
+ response: !ruby/struct:VCR::Response
50
+ status: !ruby/struct:VCR::ResponseStatus
51
+ code: 302
52
+ message: Moved Temporarily
53
+ headers:
54
+ content-type:
55
+ - text/plain
56
+ access-control-allow-origin:
57
+ - "*"
58
+ access-control-allow-methods:
59
+ - POST, GET, PUT, DELETE, OPTIONS
60
+ access-control-allow-headers:
61
+ - X-Requested-With, Content-Type, Accept, Content-Length
62
+ location:
63
+ - http://foo.bar/?assembly_id=177c56e5435176f4877fbc1b397fa4f0&assembly_url=http://api2.vivian.transloadit.com/assemblies/177c56e5435176f4877fbc1b397fa4f0
64
+ transfer-encoding:
65
+ - chunked
66
+ body: "{\"ok\":\"ASSEMBLY_COMPLETED\",\"message\":\"The assembly was successfully completed.\",\"assembly_id\":\"177c56e5435176f4877fbc1b397fa4f0\",\"assembly_url\":\"http://api2.vivian.transloadit.com/assemblies/177c56e5435176f4877fbc1b397fa4f0\",\"bytes_received\":298,\"bytes_expected\":298,\"client_agent\":\"Transloadit Ruby SDK 0.0.1\",\"client_ip\":\"69.180.12.41\",\"client_referer\":null,\"start_date\":\"2011/02/07 04:29:15 GMT\",\"upload_duration\":0.038,\"execution_duration\":0.002,\"fields\":{},\"uploads\":[],\"results\":{}}"
67
+ http_version: "1.1"
@@ -0,0 +1,16 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ require 'simplecov'
5
+
6
+ SimpleCov.start { add_filter '/test/' }
7
+
8
+ require 'minitest/autorun'
9
+ require 'transloadit'
10
+ require 'vcr'
11
+
12
+ VCR.config do |c|
13
+ c.cassette_library_dir = 'test/fixtures/cassettes'
14
+ c.default_cassette_options = { :record => :new_episodes }
15
+ c.stub_with :webmock
16
+ end
@@ -0,0 +1,97 @@
1
+ require 'test_helper'
2
+
3
+ describe Transloadit do
4
+ before do
5
+ @key = 'a'
6
+ @secret = 'b'
7
+ end
8
+
9
+ it 'must allow initialization' do
10
+ t = Transloadit.new(:key => @key, :secret => @secret)
11
+ t.must_be_kind_of Transloadit
12
+ end
13
+
14
+ it 'must not be initialized with no arguments' do
15
+ lambda { Transloadit.new }.must_raise ArgumentError
16
+ end
17
+
18
+ it 'must require a key' do
19
+ lambda { Transloadit.new(:secret => @secret) }.must_raise ArgumentError
20
+ end
21
+
22
+ it 'must not require a secret' do
23
+ t = Transloadit.new(:key => @key)
24
+ t.must_be_kind_of Transloadit
25
+ end
26
+
27
+ describe 'when initialized' do
28
+ before do
29
+ @transloadit = Transloadit.new(
30
+ :key => @key,
31
+ :secret => @secret
32
+ )
33
+ end
34
+
35
+ it 'must allow access to the key' do
36
+ @transloadit.key.must_equal @key
37
+ end
38
+
39
+ it 'must allow access to the secret' do
40
+ @transloadit.secret.must_equal @secret
41
+ end
42
+
43
+ it 'must create steps' do
44
+ step = @transloadit.step('/image/resize', :width => 320)
45
+
46
+ step.must_be_kind_of Transloadit::Step
47
+ step.robot. must_equal '/image/resize'
48
+ step.options.must_equal :width => 320
49
+ end
50
+
51
+ it 'must create assemblies' do
52
+ step = @transloadit.step('')
53
+ assembly = @transloadit.assembly :steps => step
54
+
55
+ assembly.must_be_kind_of Transloadit::Assembly
56
+ assembly.steps.must_equal step.to_hash
57
+ end
58
+
59
+ it 'must create assemblies with multiple steps' do
60
+ steps = [
61
+ @transloadit.step(''),
62
+ @transloadit.step(''),
63
+ ]
64
+
65
+ assembly = @transloadit.assembly :steps => steps
66
+ assembly.steps.must_equal steps.inject({}) {|h,s| h.merge s }
67
+ end
68
+
69
+ it 'must inspect like a hash' do
70
+ @transloadit.inspect.must_equal @transloadit.to_hash.inspect
71
+ end
72
+
73
+ it 'must produce Transloadit-compatible hash output' do
74
+ @transloadit.to_hash[:key] .must_equal @key
75
+ @transloadit.to_hash[:expires].
76
+ must_match %r{\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}\+00:00}
77
+ end
78
+
79
+ it 'must produce Transloadit-compatible JSON output' do
80
+ @transloadit.to_json.must_equal @transloadit.to_hash.to_json
81
+ end
82
+ end
83
+
84
+ describe 'with no secret' do
85
+ before do
86
+ @transloadit = Transloadit.new(:key => @key)
87
+ end
88
+
89
+ it 'must not include a secret in its hash output' do
90
+ @transloadit.to_hash.keys.wont_include :secret
91
+ end
92
+
93
+ it 'must not include a secret in its JSON output' do
94
+ @transloadit.to_json.must_equal @transloadit.to_hash.to_json
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,87 @@
1
+ require 'test_helper'
2
+
3
+ describe Transloadit::Assembly do
4
+ before do
5
+ @transloadit = Transloadit.new(:key => '')
6
+ end
7
+
8
+ it 'must allow initialization' do
9
+ Transloadit::Assembly.new(@transloadit).
10
+ must_be_kind_of Transloadit::Assembly
11
+ end
12
+
13
+ describe 'when initialized' do
14
+ before do
15
+ @step = @transloadit.step '/video/thumbs'
16
+ @redirect = 'http://foo.bar/'
17
+
18
+ @assembly = Transloadit::Assembly.new @transloadit,
19
+ :steps => @step,
20
+ :redirect_url => @redirect
21
+ end
22
+
23
+ it 'must store a pointer to the transloadit instance' do
24
+ @assembly.transloadit.must_equal @transloadit
25
+ end
26
+
27
+ it 'must remember the options passed' do
28
+ @assembly.options.must_equal(
29
+ :steps => @step,
30
+ :redirect_url => @redirect
31
+ )
32
+ end
33
+
34
+ it 'must wrap its step in a hash' do
35
+ @assembly.steps.must_equal @step.to_hash
36
+ end
37
+
38
+ it 'must not wrap a nil step' do
39
+ @assembly.options[:steps] = nil
40
+ @assembly.steps.must_equal nil
41
+ end
42
+
43
+ it 'must not wrap a hash step' do
44
+ @assembly.options[:steps] = { :foo => 1 }
45
+ @assembly.steps.must_equal :foo => 1
46
+ end
47
+
48
+ it 'must inspect like a hash' do
49
+ @assembly.inspect.must_equal @assembly.to_hash.inspect
50
+ end
51
+
52
+ it 'must produce Transloadit-compatible hash output' do
53
+ @assembly.to_hash.must_equal(
54
+ :auth => @transloadit.to_hash,
55
+ :steps => @assembly.steps,
56
+ :redirect_url => @redirect
57
+ )
58
+ end
59
+
60
+ it 'must produce Transloadit-compatible JSON output' do
61
+ @assembly.to_json.must_equal @assembly.to_hash.to_json
62
+ end
63
+
64
+ it 'must submit files for upload' do
65
+ VCR.use_cassette 'submit_assembly' do
66
+ response = @assembly.submit! open('lib/transloadit/version.rb')
67
+ response.code.must_equal 302
68
+ response.headers[:location].must_match %r{^http://foo.bar/}
69
+ end
70
+ end
71
+ end
72
+
73
+ describe 'with multiple steps' do
74
+ before do
75
+ @encode = @transloadit.step '/video/encode'
76
+ @thumbs = @transloadit.step '/video/thumbs'
77
+
78
+ @assembly = Transloadit::Assembly.new @transloadit,
79
+ :steps => [ @encode, @thumbs ]
80
+ end
81
+
82
+ it 'must wrap its steps into one hash' do
83
+ @assembly.to_hash[:steps].keys.must_include @encode.name
84
+ @assembly.to_hash[:steps].keys.must_include @thumbs.name
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ require 'test_helper'
2
+
3
+ describe Transloadit::Request do
4
+ it 'should be tested'
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'test_helper'
2
+
3
+ describe Transloadit::Response do
4
+ it 'should be tested'
5
+ end
@@ -0,0 +1,103 @@
1
+ require 'test_helper'
2
+
3
+ describe Transloadit::Step do
4
+ it 'must allow initialization' do
5
+ Transloadit::Step.new('/s3/store').must_be_kind_of Transloadit::Step
6
+ end
7
+
8
+ describe 'when initialized' do
9
+ before do
10
+ @robot = '/s3/store'
11
+ @key = 'aws-access-key-id'
12
+ @secret = 'aws-secret-access-key'
13
+ @bucket = 's3-bucket-name'
14
+
15
+ @step = Transloadit::Step.new '/s3/store',
16
+ :key => @key,
17
+ :secret => @secret,
18
+ :bucket => @bucket
19
+ end
20
+
21
+ it 'must generate a name' do
22
+ @step.name.wont_equal nil
23
+ end
24
+
25
+ it 'must generate a unique name' do
26
+ @step.name.wont_equal Transloadit::Step.new('').name
27
+ end
28
+
29
+ it 'must generate a name with 32 hex characters' do
30
+ @step.name.length.must_equal 32
31
+ end
32
+
33
+ it 'must remember the type' do
34
+ @step.robot.must_equal @robot
35
+ end
36
+
37
+ it 'must remember the parameters' do
38
+ @step.options.must_equal(
39
+ :key => @key,
40
+ :secret => @secret,
41
+ :bucket => @bucket
42
+ )
43
+ end
44
+
45
+ it 'must inspect like a hash' do
46
+ @step.inspect.must_equal @step.to_hash[@step.name].inspect
47
+ end
48
+
49
+ it 'must produce Transloadit-compatible hash output' do
50
+ @step.to_hash.must_equal(
51
+ @step.name => {
52
+ :robot => @robot,
53
+ :key => @key,
54
+ :secret => @secret,
55
+ :bucket => @bucket
56
+ }
57
+ )
58
+ end
59
+
60
+ it 'must produce Transloadit-compatible JSON output' do
61
+ @step.to_json.must_equal @step.to_hash.to_json
62
+ end
63
+ end
64
+
65
+ describe 'when using alternative inputs' do
66
+ before do
67
+ @step = Transloadit::Step.new '/image/resize'
68
+ end
69
+
70
+ it 'must allow using the original file as input' do
71
+ @step.use(:original).must_equal ':original'
72
+ @step.options[:use] .must_equal ':original'
73
+ end
74
+
75
+ it 'must allow using another step' do
76
+ input = Transloadit::Step.new '/video/thumbnail'
77
+
78
+ @step.use(input). must_equal [ input.name ]
79
+ @step.options[:use].must_equal [ input.name ]
80
+ end
81
+
82
+ it 'must allow using multiple steps' do
83
+ inputs = [
84
+ Transloadit::Step.new('/video/thumbnail'),
85
+ Transloadit::Step.new('/image/resize')
86
+ ]
87
+
88
+ @step.use(inputs). must_equal inputs.map {|i| i.name }
89
+ @step.options[:use].must_equal inputs.map {|i| i.name }
90
+ end
91
+
92
+ it 'must allow using nothing' do
93
+ @step.use :original
94
+ @step.use(nil).must_equal nil
95
+ @step.options.keys.wont_include(:use)
96
+ end
97
+
98
+ it 'must include the used steps in the hash output' do
99
+ @step.use(:original). must_equal ':original'
100
+ @step.to_hash[@step.name][:use].must_equal ':original'
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,36 @@
1
+ $:.unshift File.expand_path('../lib', __FILE__)
2
+
3
+ require 'transloadit/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'transloadit'
7
+ gem.version = Transloadit::VERSION
8
+ gem.platform = Gem::Platform::RUBY
9
+
10
+ gem.authors = %w{ Stephen Touset }
11
+ gem.email = %w{ stephen@touset.org }
12
+ gem.homepage = 'http://github.com/stouset/transloadit/'
13
+
14
+ gem.summary = 'Official Ruby gem for Transloadit'
15
+ gem.description = 'The transloadit gem allows you to automate uploading files through the Transloadit REST API'
16
+
17
+ gem.required_rubygems_version = '>= 1.3.6'
18
+ gem.rubyforge_project = 'transloadit'
19
+
20
+ gem.files = `git ls-files`.split("\n")
21
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ gem.require_paths = %w{ lib }
23
+
24
+ gem.add_dependency 'rest-client'
25
+ gem.add_dependency 'json'
26
+
27
+ gem.add_development_dependency 'rake'
28
+ gem.add_development_dependency 'minitest' # needed for < 1.9.2
29
+ gem.add_development_dependency 'simplecov'
30
+
31
+ gem.add_development_dependency 'vcr'
32
+ gem.add_development_dependency 'webmock'
33
+
34
+ gem.add_development_dependency 'yard'
35
+ gem.add_development_dependency 'rdiscount' # for YARD rdoc formatting
36
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: transloadit
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Stephen
9
+ - Touset
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-02-09 00:00:00 -05:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: rest-client
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: "0"
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: json
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ type: :runtime
38
+ version_requirements: *id002
39
+ - !ruby/object:Gem::Dependency
40
+ name: rake
41
+ prerelease: false
42
+ requirement: &id003 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id003
50
+ - !ruby/object:Gem::Dependency
51
+ name: minitest
52
+ prerelease: false
53
+ requirement: &id004 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ type: :development
60
+ version_requirements: *id004
61
+ - !ruby/object:Gem::Dependency
62
+ name: simplecov
63
+ prerelease: false
64
+ requirement: &id005 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ type: :development
71
+ version_requirements: *id005
72
+ - !ruby/object:Gem::Dependency
73
+ name: vcr
74
+ prerelease: false
75
+ requirement: &id006 !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ type: :development
82
+ version_requirements: *id006
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ prerelease: false
86
+ requirement: &id007 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ type: :development
93
+ version_requirements: *id007
94
+ - !ruby/object:Gem::Dependency
95
+ name: yard
96
+ prerelease: false
97
+ requirement: &id008 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "0"
103
+ type: :development
104
+ version_requirements: *id008
105
+ - !ruby/object:Gem::Dependency
106
+ name: rdiscount
107
+ prerelease: false
108
+ requirement: &id009 !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: "0"
114
+ type: :development
115
+ version_requirements: *id009
116
+ description: The transloadit gem allows you to automate uploading files through the Transloadit REST API
117
+ email:
118
+ - stephen@touset.org
119
+ executables: []
120
+
121
+ extensions: []
122
+
123
+ extra_rdoc_files: []
124
+
125
+ files:
126
+ - .gitignore
127
+ - Gemfile
128
+ - README.md
129
+ - Rakefile
130
+ - lib/transloadit.rb
131
+ - lib/transloadit/assembly.rb
132
+ - lib/transloadit/request.rb
133
+ - lib/transloadit/response.rb
134
+ - lib/transloadit/response/assembly.rb
135
+ - lib/transloadit/step.rb
136
+ - lib/transloadit/version.rb
137
+ - test/fixtures/cassettes/submit_assembly.yml
138
+ - test/test_helper.rb
139
+ - test/unit/test_transloadit.rb
140
+ - test/unit/transloadit/test_assembly.rb
141
+ - test/unit/transloadit/test_request.rb
142
+ - test/unit/transloadit/test_response.rb
143
+ - test/unit/transloadit/test_step.rb
144
+ - transloadit.gemspec
145
+ has_rdoc: true
146
+ homepage: http://github.com/stouset/transloadit/
147
+ licenses: []
148
+
149
+ post_install_message:
150
+ rdoc_options: []
151
+
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ none: false
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: "0"
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: 1.3.6
166
+ requirements: []
167
+
168
+ rubyforge_project: transloadit
169
+ rubygems_version: 1.5.0
170
+ signing_key:
171
+ specification_version: 3
172
+ summary: Official Ruby gem for Transloadit
173
+ test_files:
174
+ - test/fixtures/cassettes/submit_assembly.yml
175
+ - test/test_helper.rb
176
+ - test/unit/test_transloadit.rb
177
+ - test/unit/transloadit/test_assembly.rb
178
+ - test/unit/transloadit/test_request.rb
179
+ - test/unit/transloadit/test_response.rb
180
+ - test/unit/transloadit/test_step.rb