animoto 0.0.0.alpha3 → 0.0.0.alpha4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/animoto/asset.rb +0 -6
- data/lib/animoto/client.rb +106 -112
- data/lib/animoto/dynamic_class_loader.rb +64 -0
- data/lib/animoto/http_engine.rb +34 -0
- data/lib/animoto/http_engines/curl_adapter.rb +39 -0
- data/lib/animoto/http_engines/net_http_adapter.rb +55 -0
- data/lib/animoto/http_engines/patron_adapter.rb +27 -0
- data/lib/animoto/http_engines/rest_client_adapter.rb +25 -0
- data/lib/animoto/http_engines/typhoeus_adapter.rb +24 -0
- data/lib/animoto/manifest.rb +0 -7
- data/lib/animoto/resource.rb +3 -3
- data/lib/animoto/response_parser.rb +38 -0
- data/lib/animoto/response_parsers/json_adapter.rb +21 -0
- data/lib/animoto/response_parsers/yajl_adapter.rb +21 -0
- data/lib/animoto/standard_envelope.rb +1 -1
- data/lib/animoto.rb +1 -1
- data/spec/animoto/client_spec.rb +43 -20
- data/spec/animoto/http_engine_spec.rb +27 -0
- data/spec/animoto/response_parsers/json_adapter_spec.rb +93 -0
- data/spec/animoto/response_parsers/yajl_adapter_spec.rb +93 -0
- data/spec/spec_helper.rb +1 -1
- metadata +17 -4
data/lib/animoto/asset.rb
CHANGED
data/lib/animoto/client.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'net/http'
|
3
|
-
require 'net/https'
|
4
|
-
require 'json'
|
5
1
|
require 'yaml'
|
2
|
+
require 'uri'
|
6
3
|
|
7
4
|
require 'animoto/errors'
|
8
5
|
require 'animoto/content_type'
|
@@ -25,19 +22,18 @@ require 'animoto/job'
|
|
25
22
|
require 'animoto/directing_and_rendering_job'
|
26
23
|
require 'animoto/directing_job'
|
27
24
|
require 'animoto/rendering_job'
|
25
|
+
require 'animoto/dynamic_class_loader'
|
26
|
+
require 'animoto/http_engine'
|
27
|
+
require 'animoto/response_parser'
|
28
28
|
|
29
29
|
module Animoto
|
30
30
|
class Client
|
31
31
|
API_ENDPOINT = "https://api2-staging.animoto.com/"
|
32
32
|
API_VERSION = 1
|
33
33
|
BASE_CONTENT_TYPE = "application/vnd.animoto"
|
34
|
-
HTTP_METHOD_MAP = {
|
35
|
-
:get => Net::HTTP::Get,
|
36
|
-
:post => Net::HTTP::Post
|
37
|
-
}
|
38
34
|
|
39
35
|
attr_accessor :key, :secret, :endpoint
|
40
|
-
attr_reader
|
36
|
+
attr_reader :http_engine, :response_parser
|
41
37
|
|
42
38
|
# Creates a new Client object which handles credentials, versioning, making requests, and
|
43
39
|
# parsing responses.
|
@@ -52,27 +48,61 @@ module Animoto
|
|
52
48
|
# @return [Client]
|
53
49
|
# @raise [ArgumentError] if no credentials are supplied
|
54
50
|
def initialize *args
|
55
|
-
@debug = ENV['DEBUG']
|
56
51
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
57
52
|
@key = args[0]
|
58
53
|
@secret = args[1]
|
59
54
|
@endpoint = options[:endpoint]
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
55
|
+
configure_from_rc_file
|
56
|
+
@endpoint ||= API_ENDPOINT
|
57
|
+
__send__ :http_engine=, options[:http_engine] || :net_http
|
58
|
+
__send__ :response_parser=, options[:response_parser] || :json
|
59
|
+
end
|
60
|
+
|
61
|
+
# Set the HTTP engine this client will use.
|
62
|
+
#
|
63
|
+
# @param [HTTPEngine, Symbol, Class] engine you may pass a
|
64
|
+
# HTTPEngine instance to use, or the symbolic name of a adapter to use,
|
65
|
+
# or a Class whose instances respond to #request and return a String of
|
66
|
+
# the response body
|
67
|
+
# @see Animoto::HTTPEngine
|
68
|
+
# @return [HTTPEngine] the engine instance
|
69
|
+
# @raise [ArgumentError] if given a class without the correct interface
|
70
|
+
def http_engine= engine
|
71
|
+
@http_engine = case engine
|
72
|
+
when Animoto::HTTPEngine
|
73
|
+
engine
|
74
|
+
when Class
|
75
|
+
if engine.instance_methods.include?('request')
|
76
|
+
engine.new
|
77
|
+
else
|
78
|
+
raise ArgumentError
|
79
|
+
end
|
80
|
+
else
|
81
|
+
Animoto::HTTPEngine[engine].new
|
66
82
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
83
|
+
end
|
84
|
+
|
85
|
+
# Set the response parser this client will use.
|
86
|
+
#
|
87
|
+
# @param [ResponseParser, Symbol, Class] parser you may pass a
|
88
|
+
# ResponseParser instance to use, or the symbolic name of a adapter to use,
|
89
|
+
# or a Class whose instances respond to #parse, #unparse, and #format.
|
90
|
+
# @see Animoto::ResponseParser
|
91
|
+
# @return [ResponseParser] the parser instance
|
92
|
+
# @raise [ArgumentError] if given a class without the correct interface
|
93
|
+
def response_parser= parser
|
94
|
+
@response_parser = case parser
|
95
|
+
when Animoto::ResponseParser
|
96
|
+
parser
|
97
|
+
when Class
|
98
|
+
if %{format parse unparse}.all? { |m| parser.instance_methods.include? m }
|
99
|
+
parser.new
|
100
|
+
else
|
101
|
+
raise ArgumentError
|
102
|
+
end
|
103
|
+
else
|
104
|
+
Animoto::ResponseParser[parser].new
|
72
105
|
end
|
73
|
-
|
74
|
-
@endpoint ||= API_ENDPOINT
|
75
|
-
@format = 'json'
|
76
106
|
end
|
77
107
|
|
78
108
|
# Finds a resource by its URL.
|
@@ -124,15 +154,40 @@ module Animoto
|
|
124
154
|
|
125
155
|
private
|
126
156
|
|
157
|
+
# Sets the API credentials from an .animotorc file. First looks for one in the current
|
158
|
+
# directory, then checks ~/.animotorc, then finally /etc/.animotorc.
|
159
|
+
#
|
160
|
+
# @raise [ArgumentError] if none of the files are found
|
161
|
+
def configure_from_rc_file
|
162
|
+
catch(:done) do
|
163
|
+
current_path = Dir.pwd + '/.animotorc'
|
164
|
+
home_path = File.expand_path('~/.animotorc')
|
165
|
+
config = if File.exist?(current_path)
|
166
|
+
YAML.load(File.read(current_path))
|
167
|
+
elsif File.exist?(home_path)
|
168
|
+
home_path = File.expand_path '~/.animotorc'
|
169
|
+
YAML.load(File.read(home_path))
|
170
|
+
elsif File.exist?('/etc/.animotorc')
|
171
|
+
YAML.load(File.read('/etc/.animotorc'))
|
172
|
+
end
|
173
|
+
if config
|
174
|
+
@key ||= config['key']
|
175
|
+
@secret ||= config['secret']
|
176
|
+
@endpoint ||= config['endpoint']
|
177
|
+
throw :done if @key && @secret
|
178
|
+
end
|
179
|
+
raise ArgumentError, "You must supply your key and secret"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
127
183
|
# Builds a request to find a resource.
|
128
184
|
#
|
129
185
|
# @param [Class] klass the Resource class you're looking for
|
130
186
|
# @param [String] url the URL of the resource
|
131
187
|
# @param [Hash] options
|
132
|
-
# @return [Hash] deserialized
|
188
|
+
# @return [Hash] deserialized response body
|
133
189
|
def find_request klass, url, options = {}
|
134
|
-
|
135
|
-
request(:get, URI.parse(url), nil, { "Accept" => content_type_of(klass) }, options)
|
190
|
+
request(:get, url, nil, { "Accept" => content_type_of(klass) }, options)
|
136
191
|
end
|
137
192
|
|
138
193
|
# Builds a request requiring a manifest.
|
@@ -140,104 +195,43 @@ module Animoto
|
|
140
195
|
# @param [Manifest] manifest the manifest being acted on
|
141
196
|
# @param [String] endpoint the endpoint to send the request to
|
142
197
|
# @param [Hash] options
|
143
|
-
# @return [Hash] deserialized
|
198
|
+
# @return [Hash] deserialized response body
|
144
199
|
def send_manifest manifest, endpoint, options = {}
|
145
|
-
# request(:post, endpoint, manifest.to_json, { "Accept" => "application/#{format}", "Content-Type" => content_type_of(manifest) }, options)
|
146
200
|
u = URI.parse(endpoint)
|
147
201
|
u.path = endpoint
|
148
|
-
request(
|
202
|
+
request(
|
203
|
+
:post,
|
204
|
+
u.to_s,
|
205
|
+
response_parser.unparse(manifest.to_hash),
|
206
|
+
{ "Accept" => "application/#{response_parser.format}", "Content-Type" => content_type_of(manifest) },
|
207
|
+
options
|
208
|
+
)
|
149
209
|
end
|
150
210
|
|
151
211
|
# Makes a request and parses the response.
|
152
212
|
#
|
153
213
|
# @param [Symbol] method which HTTP method to use (should be lowercase, i.e. :get instead of :GET)
|
154
|
-
# @param [
|
214
|
+
# @param [String] url the URL of the request
|
155
215
|
# @param [String, nil] body the request body
|
156
216
|
# @param [Hash<String,String>] headers the request headers (will be sent as-is, which means you should
|
157
217
|
# specify "Content-Type" => "..." instead of, say, :content_type => "...")
|
158
218
|
# @param [Hash] options
|
159
|
-
# @return [Hash] deserialized
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
puts "********************* REQUEST *******************"
|
167
|
-
puts "#{req.method} #{uri.to_s} HTTP/#{http.instance_variable_get(:@curr_http_version)}\r\n"
|
168
|
-
req.each_capitalized { |header, value| puts "#{header}: #{value}\r\n" }
|
169
|
-
puts "\r\n"
|
170
|
-
puts req.body unless req.method == 'GET'
|
219
|
+
# @return [Hash] deserialized response body
|
220
|
+
# @raise [Error]
|
221
|
+
def request method, url, body, headers = {}, options = {}
|
222
|
+
error = catch(:fail) do
|
223
|
+
options = { :username => @key, :password => @secret }.merge(options)
|
224
|
+
response = http_engine.request(method, url, body, headers, options)
|
225
|
+
return response_parser.parse(response)
|
171
226
|
end
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
puts "\r\n"
|
178
|
-
body = response.body
|
179
|
-
if body.nil? || body.empty?
|
180
|
-
puts "(No content)"
|
181
|
-
else
|
182
|
-
puts body
|
183
|
-
end
|
227
|
+
if error
|
228
|
+
errors = response_parser.parse(error)['response']['status']['errors']
|
229
|
+
raise Animoto::Error.new(errors.collect { |e| e['message'] }.join(', '))
|
230
|
+
else
|
231
|
+
raise Animoto::Error
|
184
232
|
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
# Builds the request object.
|
189
|
-
#
|
190
|
-
# @param [Symbol] method which HTTP method to use (should be lowercase, i.e. :get instead of :GET)
|
191
|
-
# @param [String] uri the request path
|
192
|
-
# @param [String, nil] body the request body
|
193
|
-
# @param [Hash<String,String>] headers the request headers (will be sent as-is, which means you should
|
194
|
-
# specify "Content-Type" => "..." instead of, say, :content_type => "...")
|
195
|
-
# @param [Hash] options
|
196
|
-
# @return [Net::HTTPRequest] the request object
|
197
|
-
def build_request method, uri, body, headers, options
|
198
|
-
req = HTTP_METHOD_MAP[method].new uri.path
|
199
|
-
req.body = body
|
200
|
-
req.initialize_http_header headers
|
201
|
-
req.basic_auth key, secret
|
202
|
-
req
|
203
|
-
end
|
204
|
-
|
205
|
-
# Verifies and parses the response.
|
206
|
-
#
|
207
|
-
# @param [Net::HTTPResponse] response the response object
|
208
|
-
# @return [Hash] deserialized JSON response body
|
209
|
-
def read_response response
|
210
|
-
check_status response
|
211
|
-
parse_response response
|
212
|
-
end
|
213
|
-
|
214
|
-
# Checks the status of the response to make sure it's successful.
|
215
|
-
#
|
216
|
-
# @param [Net::HTTPResponse] response the response object
|
217
|
-
# @return [nil]
|
218
|
-
# @raise [Error,RuntimeError] if the response code isn't in the 200 range
|
219
|
-
def check_status response
|
220
|
-
unless (200..299).include?(response.code.to_i)
|
221
|
-
if response.body
|
222
|
-
begin
|
223
|
-
json = JSON.parse(response.body)
|
224
|
-
errors = json['response']['status']['errors']
|
225
|
-
rescue => e
|
226
|
-
raise response.message
|
227
|
-
else
|
228
|
-
raise Animoto::Error.new(errors.collect { |e| e['message'] }.join(', '))
|
229
|
-
end
|
230
|
-
else
|
231
|
-
raise response.message
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
# Parses a JSON response body into a Hash.
|
237
|
-
# @param [Net::HTTPResponse] response the response object
|
238
|
-
# @return [Hash] deserialized JSON response body
|
239
|
-
def parse_response response
|
240
|
-
JSON.parse(response.body)
|
233
|
+
rescue NoMethodError => e
|
234
|
+
raise Animoto::Error.new("Invalid response (#{error.inspect})")
|
241
235
|
end
|
242
236
|
|
243
237
|
# Creates the full content type string given a Resource class or instance
|
@@ -247,7 +241,7 @@ module Animoto
|
|
247
241
|
# "application/vnd.animoto.storyboard-v1+json")
|
248
242
|
def content_type_of klass_or_instance
|
249
243
|
klass = klass_or_instance.is_a?(Class) ? klass_or_instance : klass_or_instance.class
|
250
|
-
"#{BASE_CONTENT_TYPE}.#{klass.content_type}-v#{API_VERSION}+#{format}"
|
244
|
+
"#{BASE_CONTENT_TYPE}.#{klass.content_type}-v#{API_VERSION}+#{response_parser.format}"
|
251
245
|
end
|
252
246
|
|
253
247
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Animoto
|
2
|
+
module DynamicClassLoader
|
3
|
+
|
4
|
+
# If a reference is made to a class under this one that hasn't been initialized yet,
|
5
|
+
# this will attempt to require a file with that class' name (underscored). If one is
|
6
|
+
# found, and the file defines the class requested, will return that class object.
|
7
|
+
#
|
8
|
+
# @param [Symbol] const the uninitialized class' name
|
9
|
+
# @return [Class] the class found
|
10
|
+
# @raise [NameError] if the file defining the class isn't found, or if the file required
|
11
|
+
# doesn't define the class.
|
12
|
+
def const_missing const
|
13
|
+
engine_name = underscore_class_name(const.to_s)
|
14
|
+
file_name = File.dirname(__FILE__) + "/#{search_path}/#{engine_name}.rb"
|
15
|
+
if File.exist?(file_name)
|
16
|
+
require file_name
|
17
|
+
const_defined?(const) ? const_get(const) : super
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the adapter class for the given symbol. If the file defining the class
|
24
|
+
# hasn't been loaded, will try to load it.
|
25
|
+
#
|
26
|
+
# @param [Symbol] engine the symbolic name of the adapter
|
27
|
+
# @return [Class] the class
|
28
|
+
# @raise [NameError] if the class isn't found
|
29
|
+
def [] engine
|
30
|
+
require(File.dirname(__FILE__) + "/#{search_path}/#{engine.to_s.gsub(/[^\w]/,'.?')}_adapter.rb") unless $".grep(/#{engine.to_s}_adapter/).first
|
31
|
+
rescue LoadError
|
32
|
+
raise NameError, "Couldn't locate adapter named \"#{engine}\""
|
33
|
+
else
|
34
|
+
adapter_map[engine]
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Returns the path relative to this file where dynamically loaded files can be found.
|
40
|
+
# Defaults to the underscored class name plus 's'.
|
41
|
+
#
|
42
|
+
# @return [String] the path
|
43
|
+
def search_path
|
44
|
+
"#{underscore_class_name(self)}s"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a Hash mapping the symbolic names for adapters to their classes.
|
48
|
+
#
|
49
|
+
# @return [Hash] the map of adapters
|
50
|
+
def adapter_map
|
51
|
+
@adapter_map ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Turns a camel-cased class name into an underscored version. Will only affect the base name
|
55
|
+
# of the class, so, for example, Animoto::DirectingAndRenderingJob becomes 'directing_and_rendering_job'
|
56
|
+
#
|
57
|
+
# @param [Class, String] klass a class or name of a class
|
58
|
+
# @return [String] the underscored version of the name
|
59
|
+
def underscore_class_name klass
|
60
|
+
klass_name = klass.is_a?(Class) ? klass.name.split('::').last : klass
|
61
|
+
klass_name.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Animoto
|
2
|
+
class HTTPEngine
|
3
|
+
extend DynamicClassLoader
|
4
|
+
|
5
|
+
# Make a request.
|
6
|
+
#
|
7
|
+
# @param [Symbol] method the HTTP method to use, should be lower-case (that is, :get
|
8
|
+
# instead of :GET)
|
9
|
+
# @param [String] url the URL to request
|
10
|
+
# @param [String,nil] body the request body
|
11
|
+
# @param [Hash<String,String>] headers request headers to send; names will be sent as-is
|
12
|
+
# (for example, use keys like "Content-Type" and not :content_type)
|
13
|
+
# @param [Hash<Symbol,Object>] options
|
14
|
+
# @option options :timeout set a timeout
|
15
|
+
# @option options :username the authentication username
|
16
|
+
# @option options :password the authentication password
|
17
|
+
# @return [String] the response body
|
18
|
+
# @raise [NotImplementedError] if called on the abstract class
|
19
|
+
def request method, url, body = nil, headers = {}, options = {}
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Checks the response and raises an error if the status isn't success.
|
26
|
+
#
|
27
|
+
# @param [Fixnum] code the HTTP status code
|
28
|
+
# @param [String] body the HTTP response body
|
29
|
+
# @raise [Animoto::Error] if the status isn't between 200 and 299
|
30
|
+
def check_response code, body
|
31
|
+
throw(:fail, body) unless (200..299).include?(code)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'curl'
|
2
|
+
|
3
|
+
module Animoto
|
4
|
+
class HTTPEngine
|
5
|
+
class CurlAdapter < Animoto::HTTPEngine
|
6
|
+
|
7
|
+
def request method, url, body = nil, headers = {}, options = {}
|
8
|
+
curl = build_curl method, url, body, headers, options
|
9
|
+
perform curl, method
|
10
|
+
check_response curl.response_code, curl.body_str
|
11
|
+
curl.body_str
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_curl method, url, body, headers, options
|
17
|
+
::Curl::Easy.new(url) do |c|
|
18
|
+
c.username = options[:username]
|
19
|
+
c.password = options[:password]
|
20
|
+
c.timeout = options[:timeout]
|
21
|
+
c.post_body = body
|
22
|
+
headers.each { |header, value| c.headers[header] = value }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def perform curl, method
|
27
|
+
case method
|
28
|
+
when :get
|
29
|
+
curl.http_get
|
30
|
+
when :post
|
31
|
+
curl.http_post(body)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
adapter_map.merge! :curl => CurlAdapter
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module Animoto
|
5
|
+
class HTTPEngine
|
6
|
+
class NetHTTPAdapter < Animoto::HTTPEngine
|
7
|
+
|
8
|
+
HTTP_METHOD_MAP = {
|
9
|
+
:get => Net::HTTP::Get,
|
10
|
+
:post => Net::HTTP::Post
|
11
|
+
}
|
12
|
+
|
13
|
+
def request method, url, body = nil, headers = {}, options = {}
|
14
|
+
uri = URI.parse(url)
|
15
|
+
http = build_http uri
|
16
|
+
req = build_request method, uri, body, headers, options
|
17
|
+
response = http.request req
|
18
|
+
check_response response.code.to_i, response.body
|
19
|
+
response.body
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Makes a new HTTP object.
|
25
|
+
#
|
26
|
+
# @param [URI] uri a URI object of the request URL
|
27
|
+
# @return [Net::HTTP] the HTTP object
|
28
|
+
def build_http uri
|
29
|
+
http = Net::HTTP.new uri.host, uri.port
|
30
|
+
http.use_ssl = true
|
31
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
32
|
+
http
|
33
|
+
end
|
34
|
+
|
35
|
+
# Builds the request object.
|
36
|
+
#
|
37
|
+
# @param [Symbol] method which HTTP method to use (should be lowercase, i.e. :get instead of :GET)
|
38
|
+
# @param [String] uri the request path
|
39
|
+
# @param [String, nil] body the request body
|
40
|
+
# @param [Hash<String,String>] headers the request headers (will be sent as-is, which means you should
|
41
|
+
# specify "Content-Type" => "..." instead of, say, :content_type => "...")
|
42
|
+
# @param [Hash] options
|
43
|
+
# @return [Net::HTTPRequest] the request object
|
44
|
+
def build_request method, uri, body, headers, options
|
45
|
+
req = HTTP_METHOD_MAP[method].new uri.path
|
46
|
+
req.body = body
|
47
|
+
req.initialize_http_header headers
|
48
|
+
req.basic_auth options[:username], options[:password]
|
49
|
+
req
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
adapter_map.merge! :net_http => NetHTTPAdapter
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'patron'
|
2
|
+
|
3
|
+
module Animoto
|
4
|
+
class HTTPEngine
|
5
|
+
class PatronAdapter < Animoto::HTTPEngine
|
6
|
+
|
7
|
+
def request method, url, body = nil, headers = {}, options = {}
|
8
|
+
session = build_session options
|
9
|
+
response = session.request method, url, headers, :data => body
|
10
|
+
check_response response.status, response.body
|
11
|
+
response.body
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_session options
|
17
|
+
session = ::Patron::Session.new
|
18
|
+
session.timeout = options[:timeout]
|
19
|
+
session.username = options[:username]
|
20
|
+
session.password = options[:password]
|
21
|
+
session
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
adapter_map.merge! :patron => PatronAdapter
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
|
3
|
+
module Animoto
|
4
|
+
class HTTPEngine
|
5
|
+
class RestClientAdapter < Animoto::HTTPEngine
|
6
|
+
|
7
|
+
def request method, url, body = nil, headers = {}, options = {}
|
8
|
+
response = ::RestClient::Request.execute({
|
9
|
+
:method => method,
|
10
|
+
:url => url,
|
11
|
+
:headers => headers,
|
12
|
+
:payload => body,
|
13
|
+
:user => options[:username],
|
14
|
+
:password => options[:password],
|
15
|
+
:timeout => options[:timeout]
|
16
|
+
})
|
17
|
+
check_response response.code, response.body
|
18
|
+
response.body
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
adapter_map.merge! :rest_client => RestClientAdapter
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
|
3
|
+
module Animoto
|
4
|
+
class HTTPEngine
|
5
|
+
class TyphoeusAdapter < Animoto::HTTPEngine
|
6
|
+
|
7
|
+
def request method, url, body = nil, headers = {}, options = {}
|
8
|
+
response = ::Typhoeus::Request.run(url, {
|
9
|
+
:method => method,
|
10
|
+
:body => body,
|
11
|
+
:headers => headers,
|
12
|
+
:timeout => options[:timeout],
|
13
|
+
:username => options[:username],
|
14
|
+
:password => options[:password]
|
15
|
+
})
|
16
|
+
check_response response.code, response.body
|
17
|
+
response.body
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
adapter_map.merge! :typhoeus => TyphoeusAdapter
|
23
|
+
end
|
24
|
+
end
|
data/lib/animoto/manifest.rb
CHANGED
data/lib/animoto/resource.rb
CHANGED
@@ -50,13 +50,13 @@ module Animoto
|
|
50
50
|
self.class.payload_key
|
51
51
|
end
|
52
52
|
|
53
|
-
# Makes a new instance of this class from a deserialized
|
53
|
+
# Makes a new instance of this class from a deserialized response body. Note that
|
54
54
|
# it assumes the hash you're passing is structured correctly and does no format checking
|
55
55
|
# at all, so if the hash is not in the "standard envelope", this method will most likely
|
56
56
|
# raise an error.
|
57
57
|
#
|
58
58
|
# @private
|
59
|
-
# @param [Hash] body the deserialized
|
59
|
+
# @param [Hash] body the deserialized response body
|
60
60
|
# @return [Resource] an instance of this class
|
61
61
|
def self.load body
|
62
62
|
new unpack_standard_envelope(body)
|
@@ -116,7 +116,7 @@ module Animoto
|
|
116
116
|
# Update this instance with new attributes from the response body.
|
117
117
|
#
|
118
118
|
# @private
|
119
|
-
# @param [Hash] body deserialized
|
119
|
+
# @param [Hash] body deserialized from a response body
|
120
120
|
# @return [self] this instance, updated
|
121
121
|
def load body = {}
|
122
122
|
instantiate unpack_standard_envelope(body)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Animoto
|
2
|
+
class ResponseParser
|
3
|
+
extend DynamicClassLoader
|
4
|
+
|
5
|
+
# Returns the format of this parser class.
|
6
|
+
#
|
7
|
+
# @return [String] the format
|
8
|
+
def self.format
|
9
|
+
@format
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the format of this parser.
|
13
|
+
#
|
14
|
+
# @return [String] the format
|
15
|
+
def format
|
16
|
+
self.class.format
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parses a response body into a usable Hash.
|
20
|
+
#
|
21
|
+
# @param [String] body the HTTP response body
|
22
|
+
# @return [Hash] the parsed response
|
23
|
+
# @raise [NotImplementedError] if called on the abstract class
|
24
|
+
def parse body
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
# Serializes a Hash into the format for this parser.
|
29
|
+
#
|
30
|
+
# @param [Hash] hash the hash to serialize
|
31
|
+
# @return [String] the serialized data
|
32
|
+
# @raise [NotImplementedError] if called on the abstract class
|
33
|
+
def unparse hash
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Animoto
|
4
|
+
class ResponseParser
|
5
|
+
class JSONAdapter < Animoto::ResponseParser
|
6
|
+
|
7
|
+
@format = 'json'
|
8
|
+
|
9
|
+
def parse body
|
10
|
+
::JSON.parse body
|
11
|
+
end
|
12
|
+
|
13
|
+
def unparse hash
|
14
|
+
::JSON.unparse hash
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
adapter_map.merge! :json => JSONAdapter
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'yajl'
|
2
|
+
|
3
|
+
module Animoto
|
4
|
+
class ResponseParser
|
5
|
+
class YajlAdapter < Animoto::ResponseParser
|
6
|
+
|
7
|
+
@format = 'json'
|
8
|
+
|
9
|
+
def parse body
|
10
|
+
::Yajl::Parser.parse body
|
11
|
+
end
|
12
|
+
|
13
|
+
def unparse hash
|
14
|
+
::Yajl::Encoder.encode hash
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
adapter_map.merge! :yajl => YajlAdapter
|
20
|
+
end
|
21
|
+
end
|
data/lib/animoto.rb
CHANGED
data/spec/animoto/client_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'base64'
|
1
2
|
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
3
|
|
3
4
|
describe Animoto::Client do
|
@@ -42,15 +43,16 @@ describe Animoto::Client do
|
|
42
43
|
|
43
44
|
describe "automatically" do
|
44
45
|
before do
|
46
|
+
@here_path = File.expand_path("./.animotorc")
|
45
47
|
@home_path = File.expand_path("~/.animotorc")
|
46
48
|
@etc_path = "/etc/.animotorc"
|
47
49
|
@config = "key: joe\nsecret: secret\nendpoint: https://api.animoto.com/"
|
48
50
|
end
|
49
51
|
|
50
|
-
describe "when
|
52
|
+
describe "when ./.animotorc exists" do
|
51
53
|
before do
|
52
|
-
File.stubs(:exist?).with(@
|
53
|
-
File.stubs(:read).with(@
|
54
|
+
File.stubs(:exist?).with(@here_path).returns(true)
|
55
|
+
File.stubs(:read).with(@here_path).returns(@config)
|
54
56
|
end
|
55
57
|
|
56
58
|
it "should configure itself based on the options in ~/.animotorc" do
|
@@ -61,28 +63,47 @@ describe Animoto::Client do
|
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
64
|
-
describe "when
|
66
|
+
describe "when ./.animotorc doesn't exist" do
|
65
67
|
before do
|
66
|
-
File.stubs(:exist?).with(@
|
68
|
+
File.stubs(:exist?).with(@here_path).returns(false)
|
67
69
|
end
|
68
70
|
|
69
|
-
describe "when
|
71
|
+
describe "when ~/.animotorc exists" do
|
70
72
|
before do
|
71
|
-
File.stubs(:exist?).with(@
|
72
|
-
File.stubs(:read).with(@
|
73
|
+
File.stubs(:exist?).with(@home_path).returns(true)
|
74
|
+
File.stubs(:read).with(@home_path).returns(@config)
|
73
75
|
end
|
74
|
-
|
75
|
-
it "should configure itself based on the options in
|
76
|
+
|
77
|
+
it "should configure itself based on the options in ~/.animotorc" do
|
76
78
|
c = Animoto::Client.new
|
77
79
|
c.key.should == "joe"
|
78
80
|
c.secret.should == "secret"
|
79
81
|
c.endpoint.should == "https://api.animoto.com/"
|
80
82
|
end
|
81
83
|
end
|
84
|
+
|
85
|
+
describe "when ~/.animotorc doesn't exist" do
|
86
|
+
before do
|
87
|
+
File.stubs(:exist?).with(@home_path).returns(false)
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "when /etc/.animotorc exists" do
|
91
|
+
before do
|
92
|
+
File.stubs(:exist?).with(@etc_path).returns(true)
|
93
|
+
File.stubs(:read).with(@etc_path).returns(@config)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should configure itself based on the options in /etc/.animotorc" do
|
97
|
+
c = Animoto::Client.new
|
98
|
+
c.key.should == "joe"
|
99
|
+
c.secret.should == "secret"
|
100
|
+
end
|
101
|
+
end
|
82
102
|
|
83
|
-
|
84
|
-
|
85
|
-
|
103
|
+
describe "when /etc/.animotorc doesn't exist" do
|
104
|
+
it "should raise an error" do
|
105
|
+
lambda { Animoto::Client.new }.should raise_error
|
106
|
+
end
|
86
107
|
end
|
87
108
|
end
|
88
109
|
end
|
@@ -91,9 +112,10 @@ describe Animoto::Client do
|
|
91
112
|
|
92
113
|
describe "finding an instance by identifier" do
|
93
114
|
before do
|
94
|
-
@url = "https://api.animoto.com/storyboards/1"
|
95
|
-
|
96
|
-
|
115
|
+
@url = "https://joe:secret@api.animoto.com/storyboards/1"
|
116
|
+
hash = {'response'=>{'status'=>{'code'=>200},'payload'=>{'storyboard'=>{'links'=>{'self'=>@url,'preview'=>'http://animoto.com/preview/1.mp4'},'metadata'=>{'duration'=>100,'visuals_count'=>1}}}}}
|
117
|
+
body = client.response_parser.unparse(hash)
|
118
|
+
stub_request(:get, @url).to_return(:body => body, :status => [200,"OK"])
|
97
119
|
end
|
98
120
|
|
99
121
|
it "should make a GET request to the given url" do
|
@@ -103,7 +125,7 @@ describe Animoto::Client do
|
|
103
125
|
|
104
126
|
it "should ask for a response in the proper format" do
|
105
127
|
client.find(Animoto::Storyboard, @url)
|
106
|
-
WebMock.should have_requested(:get, @url).with(:headers => { 'Accept' => "application/vnd.animoto.storyboard-v1+json"})
|
128
|
+
WebMock.should have_requested(:get, @url).with(:headers => { 'Accept' => "application/vnd.animoto.storyboard-v1+json" })
|
107
129
|
end
|
108
130
|
|
109
131
|
it "should not sent a request body" do
|
@@ -118,10 +140,11 @@ describe Animoto::Client do
|
|
118
140
|
|
119
141
|
describe "reloading an instance" do
|
120
142
|
before do
|
121
|
-
@url = 'https://api.animoto.com/jobs/directing/1'
|
143
|
+
@url = 'https://joe:secret@api.animoto.com/jobs/directing/1'
|
122
144
|
@job = Animoto::DirectingJob.new :state => 'initial', :url => @url
|
123
|
-
|
124
|
-
|
145
|
+
hash = {'response'=>{'status'=>{'code'=>200},'payload'=>{'directing_job'=>{'state'=>'retrieving_assets','links'=>{'self'=>@url,'storyboard'=>'http://api.animoto.com/storyboards/1'}}}}}
|
146
|
+
body = client.response_parser.unparse(hash)
|
147
|
+
stub_request(:get, @url).to_return(:body => body, :status => [200,"OK"])
|
125
148
|
@job.state.should == 'initial' # sanity check
|
126
149
|
end
|
127
150
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Animoto::HTTPEngine do
|
4
|
+
|
5
|
+
describe "autoloading subclasses" do
|
6
|
+
before do
|
7
|
+
Animoto::HTTPEngine.const_defined?(:BeefHearts).should be_false
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
after do
|
13
|
+
Animoto::HTTPEngine.remove_const(:BeefHearts)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "making a request" do
|
18
|
+
before do
|
19
|
+
@engine = Animoto::HTTPEngine.new
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should raise an implementation error" do
|
23
|
+
lambda { @engine.request(:get, "http://www.example.com/thing") }.should raise_error(NotImplementedError)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Animoto::ResponseParser::JSONAdapter do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@parser = Animoto::ResponseParser::JSONAdapter.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should be JSON format" do
|
10
|
+
@parser.format.should == 'json'
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "parsing" do
|
14
|
+
before do
|
15
|
+
@json = %Q!{"result":{"rank":1,"score":1.9,"tags":["hooray","for","dolphins"],"message":"woohoo"}}!
|
16
|
+
@hash = @parser.parse(@json)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return a hash" do
|
20
|
+
@hash.should be_an_instance_of(Hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should turn the object root into a hash" do
|
24
|
+
@hash.should have_key('result')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should turn array elements into an array" do
|
28
|
+
@hash['result'].should have_key('tags')
|
29
|
+
@hash['result']['tags'].should be_an_instance_of(Array)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should preserve the order of elements in an array" do
|
33
|
+
@hash['result']['tags'].should == ["hooray", "for", "dolphins"]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should turn each object attribute into a key/value pair in the hash" do
|
37
|
+
@hash['result'].should have_key('rank')
|
38
|
+
@hash['result']['rank'].should be_an_instance_of(Fixnum)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should turn attributes with strictly numeric values into integers" do
|
42
|
+
@hash['result']['rank'].should eql(1)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should turn attributes with content representing floats into floats" do
|
46
|
+
@hash['result']['score'].should eql(1.9)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should turn strings into strings" do
|
50
|
+
@hash['result']['message'].should be_an_instance_of(String)
|
51
|
+
@hash['result']['message'].should == "woohoo"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "unparsing" do
|
56
|
+
before do
|
57
|
+
@obj = {
|
58
|
+
'result' => {
|
59
|
+
'tags' => [ 'hooray', 'for', 'dolphins' ],
|
60
|
+
'message' => 'woohoo',
|
61
|
+
'rank' => 1,
|
62
|
+
'score' => 1.9
|
63
|
+
}
|
64
|
+
}
|
65
|
+
@json_str = @parser.unparse(@obj)
|
66
|
+
@json = ::JSON.parse(@json_str)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should turn hashes into objects with attributes" do
|
70
|
+
@json.should have_key('result')
|
71
|
+
@json['result'].should_not be_empty
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should turn arrays into arrays" do
|
75
|
+
@json['result'].should have_key('tags')
|
76
|
+
@json['result']['tags'].should be_an_instance_of(Array)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should turn strings into text content" do
|
80
|
+
@json['result'].should have_key('message')
|
81
|
+
@json['result']['message'].should == "woohoo"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should turn integers into integers" do
|
85
|
+
@json_str.should =~ /"rank"\s*:\s*1/
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should turn floats into floats" do
|
89
|
+
@json_str.should =~ /"score"\s*:\s*1\.9/
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Animoto::ResponseParser::YajlAdapter do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@parser = Animoto::ResponseParser::YajlAdapter.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should be JSON format" do
|
10
|
+
@parser.format.should == 'json'
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "parsing" do
|
14
|
+
before do
|
15
|
+
@json = %Q!{"result":{"rank":1,"score":1.9,"tags":["hooray","for","dolphins"],"message":"woohoo"}}!
|
16
|
+
@hash = @parser.parse(@json)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return a hash" do
|
20
|
+
@hash.should be_an_instance_of(Hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should turn the object root into a hash" do
|
24
|
+
@hash.should have_key('result')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should turn array elements into an array" do
|
28
|
+
@hash['result'].should have_key('tags')
|
29
|
+
@hash['result']['tags'].should be_an_instance_of(Array)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should preserve the order of elements in an array" do
|
33
|
+
@hash['result']['tags'].should == ["hooray", "for", "dolphins"]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should turn each object attribute into a key/value pair in the hash" do
|
37
|
+
@hash['result'].should have_key('rank')
|
38
|
+
@hash['result']['rank'].should be_an_instance_of(Fixnum)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should turn attributes with strictly numeric values into integers" do
|
42
|
+
@hash['result']['rank'].should eql(1)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should turn attributes with content representing floats into floats" do
|
46
|
+
@hash['result']['score'].should eql(1.9)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should turn strings into strings" do
|
50
|
+
@hash['result']['message'].should be_an_instance_of(String)
|
51
|
+
@hash['result']['message'].should == "woohoo"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "unparsing" do
|
56
|
+
before do
|
57
|
+
@obj = {
|
58
|
+
'result' => {
|
59
|
+
'tags' => [ 'hooray', 'for', 'dolphins' ],
|
60
|
+
'message' => 'woohoo',
|
61
|
+
'rank' => 1,
|
62
|
+
'score' => 1.9
|
63
|
+
}
|
64
|
+
}
|
65
|
+
@json_str = @parser.unparse(@obj)
|
66
|
+
@json = ::JSON.parse(@json_str)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should turn hashes into objects with attributes" do
|
70
|
+
@json.should have_key('result')
|
71
|
+
@json['result'].should_not be_empty
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should turn arrays into arrays" do
|
75
|
+
@json['result'].should have_key('tags')
|
76
|
+
@json['result']['tags'].should be_an_instance_of(Array)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should turn strings into text content" do
|
80
|
+
@json['result'].should have_key('message')
|
81
|
+
@json['result']['message'].should == "woohoo"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should turn integers into integers" do
|
85
|
+
@json_str.should =~ /"rank"\s*:\s*1/
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should turn floats into floats" do
|
89
|
+
@json_str.should =~ /"score"\s*:\s*1\.9/
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: animoto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: -
|
4
|
+
hash: -1710980402
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
-
|
11
|
-
version: 0.0.0.
|
10
|
+
- alpha4
|
11
|
+
version: 0.0.0.alpha4
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Animoto
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-09-
|
19
|
+
date: 2010-09-08 00:00:00 -04:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -53,14 +53,24 @@ files:
|
|
53
53
|
- ./lib/animoto/directing_and_rendering_manifest.rb
|
54
54
|
- ./lib/animoto/directing_job.rb
|
55
55
|
- ./lib/animoto/directing_manifest.rb
|
56
|
+
- ./lib/animoto/dynamic_class_loader.rb
|
56
57
|
- ./lib/animoto/errors.rb
|
57
58
|
- ./lib/animoto/footage.rb
|
59
|
+
- ./lib/animoto/http_engine.rb
|
60
|
+
- ./lib/animoto/http_engines/curl_adapter.rb
|
61
|
+
- ./lib/animoto/http_engines/net_http_adapter.rb
|
62
|
+
- ./lib/animoto/http_engines/patron_adapter.rb
|
63
|
+
- ./lib/animoto/http_engines/rest_client_adapter.rb
|
64
|
+
- ./lib/animoto/http_engines/typhoeus_adapter.rb
|
58
65
|
- ./lib/animoto/image.rb
|
59
66
|
- ./lib/animoto/job.rb
|
60
67
|
- ./lib/animoto/manifest.rb
|
61
68
|
- ./lib/animoto/rendering_job.rb
|
62
69
|
- ./lib/animoto/rendering_manifest.rb
|
63
70
|
- ./lib/animoto/resource.rb
|
71
|
+
- ./lib/animoto/response_parser.rb
|
72
|
+
- ./lib/animoto/response_parsers/json_adapter.rb
|
73
|
+
- ./lib/animoto/response_parsers/yajl_adapter.rb
|
64
74
|
- ./lib/animoto/song.rb
|
65
75
|
- ./lib/animoto/standard_envelope.rb
|
66
76
|
- ./lib/animoto/storyboard.rb
|
@@ -76,11 +86,14 @@ files:
|
|
76
86
|
- ./spec/animoto/directing_job_spec.rb
|
77
87
|
- ./spec/animoto/directing_manifest_spec.rb
|
78
88
|
- ./spec/animoto/footage_spec.rb
|
89
|
+
- ./spec/animoto/http_engine_spec.rb
|
79
90
|
- ./spec/animoto/image_spec.rb
|
80
91
|
- ./spec/animoto/job_spec.rb
|
81
92
|
- ./spec/animoto/rendering_job_spec.rb
|
82
93
|
- ./spec/animoto/rendering_manifest_spec.rb
|
83
94
|
- ./spec/animoto/resource_spec.rb
|
95
|
+
- ./spec/animoto/response_parsers/json_adapter_spec.rb
|
96
|
+
- ./spec/animoto/response_parsers/yajl_adapter_spec.rb
|
84
97
|
- ./spec/animoto/song_spec.rb
|
85
98
|
- ./spec/animoto/standard_envelope_spec.rb
|
86
99
|
- ./spec/animoto/storyboard_spec.rb
|