animoto 0.1.1.beta1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.md +8 -4
  2. data/lib/animoto.rb +1 -1
  3. data/lib/animoto/assets/base.rb +1 -1
  4. data/lib/animoto/assets/footage.rb +1 -1
  5. data/lib/animoto/assets/image.rb +1 -1
  6. data/lib/animoto/assets/song.rb +1 -1
  7. data/lib/animoto/assets/title_card.rb +1 -1
  8. data/lib/animoto/client.rb +21 -27
  9. data/lib/animoto/http_engines/base.rb +15 -7
  10. data/lib/animoto/http_engines/curl_adapter.rb +3 -4
  11. data/lib/animoto/http_engines/net_http_adapter.rb +3 -5
  12. data/lib/animoto/http_engines/patron_adapter.rb +2 -4
  13. data/lib/animoto/http_engines/rest_client_adapter.rb +1 -3
  14. data/lib/animoto/http_engines/typhoeus_adapter.rb +1 -3
  15. data/lib/animoto/manifests/base.rb +1 -1
  16. data/lib/animoto/manifests/directing.rb +1 -1
  17. data/lib/animoto/manifests/directing_and_rendering.rb +55 -28
  18. data/lib/animoto/manifests/rendering.rb +7 -6
  19. data/lib/animoto/resources/base.rb +8 -8
  20. data/lib/animoto/resources/jobs/base.rb +2 -2
  21. data/lib/animoto/resources/jobs/directing.rb +7 -2
  22. data/lib/animoto/resources/jobs/directing_and_rendering.rb +34 -8
  23. data/lib/animoto/resources/jobs/rendering.rb +17 -3
  24. data/lib/animoto/resources/storyboard.rb +20 -4
  25. data/lib/animoto/resources/video.rb +33 -11
  26. data/lib/animoto/response_parsers/base.rb +13 -8
  27. data/lib/animoto/response_parsers/json_adapter.rb +2 -4
  28. data/lib/animoto/response_parsers/yajl_adapter.rb +2 -4
  29. data/lib/animoto/support/content_type.rb +1 -0
  30. data/lib/animoto/support/coverable.rb +1 -1
  31. data/lib/animoto/support/dynamic_class_loader.rb +82 -141
  32. data/lib/animoto/support/errors.rb +4 -0
  33. data/lib/animoto/support/hash.rb +25 -0
  34. data/lib/animoto/support/standard_envelope.rb +70 -19
  35. data/lib/animoto/support/string.rb +31 -0
  36. data/lib/animoto/support/visual.rb +1 -1
  37. data/spec/animoto/client_spec.rb +1 -25
  38. data/spec/animoto/http_engines/base_spec.rb +1 -1
  39. data/spec/animoto/resources/base_spec.rb +8 -7
  40. data/spec/spec_helper.rb +1 -0
  41. metadata +12 -21
  42. data/lib/animoto/callbacks/base.rb +0 -45
  43. data/lib/animoto/callbacks/directing.rb +0 -7
  44. data/lib/animoto/callbacks/directing_and_rendering.rb +0 -7
  45. data/lib/animoto/callbacks/rendering.rb +0 -7
  46. data/spec/animoto/callbacks/base_spec.rb +0 -76
  47. data/spec/animoto/callbacks/directing_and_rendering_spec.rb +0 -5
  48. data/spec/animoto/callbacks/directing_spec.rb +0 -5
  49. data/spec/animoto/callbacks/rendering_spec.rb +0 -5
data/README.md CHANGED
@@ -35,14 +35,14 @@ This document does not cover the details of the Animoto API itself. For such inf
35
35
 
36
36
  You can install the Animoto API Ruby Client either through [RubyGems](http://rubygems.org):
37
37
 
38
- gem install animoto -v 0.1.0.alpha0 --pre
38
+ gem install animoto
39
39
 
40
40
  Or by cloning [the repository](http://github.com/animoto/animoto_api_client_ruby) on [GitHub](http://github.com/) and building it from source:
41
41
 
42
42
  git clone git://github.com/animoto/animoto_api_client_ruby.git
43
43
  cd animoto_api_client_ruby
44
44
  gem build animoto.gemspec
45
- gem install animoto-0.1.0.alpha0.gem
45
+ gem install animoto-1.0.0.gem
46
46
 
47
47
  ### Prerequisites
48
48
 
@@ -102,7 +102,6 @@ client and using HTTP callbacks for status updates.
102
102
  # and format.
103
103
  manifest = Manifests::DirectingAndRendering.new(
104
104
  :title => "Amazing Title!",
105
- :producer => "Fishy Joe",
106
105
  :resolution => "720p",
107
106
  :framerate => 24,
108
107
  :format => 'h264'
@@ -167,7 +166,12 @@ status.
167
166
 
168
167
  # Now it's time to render the storyboard into a video. First we create
169
168
  # a rendering manifest.
170
- manifest = Manifests::Rendering.new(storyboard, :resolution => "720p", :framerate => 24, :format => 'h264')
169
+ manifest = Manifests::Rendering.new(
170
+ storyboard,
171
+ :resolution => "720p",
172
+ :framerate => 24,
173
+ :format => 'h264'
174
+ )
171
175
 
172
176
  # Send the manifest to the API.
173
177
  rendering_job = client.render!(manifest)
data/lib/animoto.rb CHANGED
@@ -6,6 +6,6 @@ module Animoto
6
6
  #
7
7
  # @return [String]
8
8
  def self.version
9
- "0.1.1.beta1"
9
+ "1.0.0"
10
10
  end
11
11
  end
@@ -19,7 +19,7 @@ module Animoto
19
19
  # Returns a representation of this asset as a Hash. Used mainly for generating
20
20
  # manifests.
21
21
  #
22
- # @return [Hash<String,Object>] this asset as a Hash
22
+ # @return [Hash{String=>Object}] this asset as a Hash
23
23
  def to_hash
24
24
  { 'source_url' => @source }
25
25
  end
@@ -19,7 +19,7 @@ module Animoto
19
19
 
20
20
  # Returns a representation of this Footage as a Hash.
21
21
  #
22
- # @return [Hash<String,Object>] this asset as a Hash
22
+ # @return [Hash{String=>Object}] this asset as a Hash
23
23
  # @see Animoto::Support::Visual#to_hash
24
24
  # @see Animoto::Assets::Base#to_hash
25
25
  def to_hash
@@ -10,7 +10,7 @@ module Animoto
10
10
 
11
11
  # Returns a representation of this Image as a Hash.
12
12
  #
13
- # @return [Hash<String,Object>] this asset as a Hash
13
+ # @return [Hash{String=>Object}] this asset as a Hash
14
14
  # @see Animoto::Support::Visual#to_hash
15
15
  # @see Animoto::Assets::Base#to_hash
16
16
  def to_hash
@@ -13,7 +13,7 @@ module Animoto
13
13
 
14
14
  # Returns a representation of this Song as a Hash.
15
15
  #
16
- # @return [Hash<String,Object>] this asset as a Hash
16
+ # @return [Hash{String=>Object}] this asset as a Hash
17
17
  # @see Animoto::Assets::Base#to_hash
18
18
  def to_hash
19
19
  hash = super
@@ -21,7 +21,7 @@ module Animoto
21
21
 
22
22
  # Returns a representation of this TitleCard as a Hash.
23
23
  #
24
- # @return [Hash<String,Object>] this TitleCard as a Hash
24
+ # @return [Hash{String=>Object}] this TitleCard as a Hash
25
25
  # @see Animoto::Support::Visual#to_hash
26
26
  def to_hash
27
27
  hash = super
@@ -6,7 +6,9 @@ require 'animoto/support/content_type'
6
6
  require 'animoto/support/coverable'
7
7
  require 'animoto/support/dynamic_class_loader'
8
8
  require 'animoto/support/errors'
9
+ require 'animoto/support/hash'
9
10
  require 'animoto/support/standard_envelope'
11
+ require 'animoto/support/string'
10
12
  require 'animoto/support/visual'
11
13
 
12
14
  require 'animoto/resources/base'
@@ -28,18 +30,19 @@ require 'animoto/manifests/directing'
28
30
  require 'animoto/manifests/directing_and_rendering'
29
31
  require 'animoto/manifests/rendering'
30
32
 
31
- require 'animoto/callbacks/base'
32
- require 'animoto/callbacks/directing'
33
- require 'animoto/callbacks/directing_and_rendering'
34
- require 'animoto/callbacks/rendering'
35
-
36
33
  require 'animoto/http_engines/base'
37
34
  require 'animoto/response_parsers/base'
38
35
 
39
36
  module Animoto
40
37
  class Client
38
+
39
+ # The default endpoint where requests go.
41
40
  API_ENDPOINT = "https://api2-sandbox.animoto.com/"
41
+
42
+ # The version of the Animoto API this client targets.
42
43
  API_VERSION = 1
44
+
45
+ # The common prefix all vendor-specific Animoto content types share.
43
46
  BASE_CONTENT_TYPE = "application/vnd.animoto"
44
47
 
45
48
  # Your API key.
@@ -149,25 +152,16 @@ module Animoto
149
152
  #
150
153
  # @param [Class] klass the resource class you're finding
151
154
  # @param [String] url the URL of the resource you want
152
- # @param [Hash<Symbol,Object>] options
155
+ # @param [Hash{Symbol=>Object}] options
153
156
  # @return [Resources::Base] the resource object found
154
157
  def find klass, url, options = {}
155
158
  klass.load(find_request(klass, url, options))
156
159
  end
157
-
158
- # Returns a callback object of the specified type given the callback body.
159
- #
160
- # @param [Class] klass the callback class
161
- # @param [String] body the HTTP body of the callback
162
- # @return [Callbacks::Base] the callback object
163
- def process_callback klass, body
164
- klass.new(response_parser.parse(body))
165
- end
166
-
160
+
167
161
  # Sends a request to start directing a storyboard.
168
162
  #
169
163
  # @param [Manifests::Directing] manifest the manifest to direct
170
- # @param [Hash<Symbol,Object>] options
164
+ # @param [Hash{Symbol=>Object}] options
171
165
  # @return [Jobs::Directing] a job to monitor the status of the directing
172
166
  def direct! manifest, options = {}
173
167
  Resources::Jobs::Directing.load(send_manifest(manifest, Resources::Jobs::Directing.endpoint, options))
@@ -176,7 +170,7 @@ module Animoto
176
170
  # Sends a request to start rendering a video.
177
171
  #
178
172
  # @param [Manifests::Rendering] manifest the manifest to render
179
- # @param [Hash<Symbol,Object>] options
173
+ # @param [Hash{Symbol=>Object}] options
180
174
  # @return [Jobs::Rendering] a job to monitor the status of the rendering
181
175
  def render! manifest, options = {}
182
176
  Resources::Jobs::Rendering.load(send_manifest(manifest, Resources::Jobs::Rendering.endpoint, options))
@@ -185,7 +179,7 @@ module Animoto
185
179
  # Sends a request to start directing and rendering a video.
186
180
  #
187
181
  # @param [Manifests::DirectingAndRendering] manifest the manifest to direct and render
188
- # @param [Hash<Symbol,Object>] options
182
+ # @param [Hash{Symbol=>Object}] options
189
183
  # @return [Jobs::DirectingAndRendering] a job to monitor the status of the directing and rendering
190
184
  def direct_and_render! manifest, options = {}
191
185
  Resources::Jobs::DirectingAndRendering.load(send_manifest(manifest, Resources::Jobs::DirectingAndRendering.endpoint, options))
@@ -195,7 +189,7 @@ module Animoto
195
189
  # see if it's ready if you are not using HTTP callbacks.
196
190
  #
197
191
  # @param [Resources::Base] resource the resource to update
198
- # @param [Hash<Symbol,Object>] options
192
+ # @param [Hash{Symbol=>Object}] options
199
193
  # @return [Resources::Base] the given resource with the latest attributes
200
194
  def reload! resource, options = {}
201
195
  resource.load(find_request(resource.class, resource.url, options))
@@ -231,8 +225,8 @@ module Animoto
231
225
  #
232
226
  # @param [Class] klass the Resource class you're looking for
233
227
  # @param [String] url the URL of the resource
234
- # @param [Hash<Symbol,Object>] options
235
- # @return [Hash<String,Object>] deserialized response body
228
+ # @param [Hash{Symbol=>Object}] options
229
+ # @return [Hash{String=>Object}] deserialized response body
236
230
  def find_request klass, url, options = {}
237
231
  request(:get, url, nil, { "Accept" => content_type_of(klass) }, options)
238
232
  end
@@ -241,8 +235,8 @@ module Animoto
241
235
  #
242
236
  # @param [Manifests::Base] manifest the manifest being acted on
243
237
  # @param [String] endpoint the endpoint to send the request to
244
- # @param [Hash<Symbol,Object>] options
245
- # @return [Hash<String,Object>] deserialized response body
238
+ # @param [Hash{Symbol=>Object}] options
239
+ # @return [Hash{String=>Object}] deserialized response body
246
240
  def send_manifest manifest, endpoint, options = {}
247
241
  u = URI.parse(self.endpoint)
248
242
  u.path = endpoint
@@ -260,10 +254,10 @@ module Animoto
260
254
  # @param [Symbol] method which HTTP method to use (should be lowercase, i.e. :get instead of :GET)
261
255
  # @param [String] url the URL of the request
262
256
  # @param [String,nil] body the request body
263
- # @param [Hash<String,String>] headers the request headers (will be sent as-is, which means you should
257
+ # @param [Hash{String=>String}] headers the request headers (will be sent as-is, which means you should
264
258
  # specify "Content-Type" => "..." instead of, say, :content_type => "...")
265
- # @param [Hash<Symbol,Object>] options
266
- # @return [Hash<String,Object>] deserialized response body
259
+ # @param [Hash{Symbol=>Object}] options
260
+ # @return [Hash{String=>Object}] deserialized response body
267
261
  # @raise [Error]
268
262
  def request method, url, body, headers = {}, options = {}
269
263
  code, body = catch(:fail) do
@@ -1,7 +1,15 @@
1
1
  module Animoto
2
- module HTTPEngines
3
- extend Support::DynamicClassLoader(File.expand_path(File.dirname(__FILE__)))
2
+ module HTTPEngines
3
+ extend Support::DynamicClassLoader
4
4
 
5
+ dynamic_class_path File.expand_path(File.dirname(__FILE__))
6
+
7
+ adapter 'Curl'
8
+ adapter 'NetHTTP'
9
+ adapter 'Patron'
10
+ adapter 'RestClient'
11
+ adapter 'Typhoeus'
12
+
5
13
  # @abstract Override {#request} to subclass.
6
14
  class Base
7
15
 
@@ -12,23 +20,23 @@ module Animoto
12
20
  # instead of :GET)
13
21
  # @param [String] url the URL to request
14
22
  # @param [String,nil] body the request body
15
- # @param [Hash<String,String>] headers request headers to send; names will be sent as-is
23
+ # @param [Hash{String=>String}] headers request headers to send; names will be sent as-is
16
24
  # (for example, use keys like "Content-Type" and not :content_type)
17
- # @param [Hash<Symbol,Object>] options
25
+ # @param [Hash{Symbol=>Object}] options
18
26
  # @option options [Integer] :timeout set a timeout
19
27
  # @option options [String] :username the authentication username
20
28
  # @option options [String] :password the authentication password
21
29
  # @return [String] the response body
22
- # @raise [NotImplementedError] if called on the abstract class
30
+ # @raise [AbstractMethodError] if called on the abstract class
23
31
  def request method, url, body = nil, headers = {}, options = {}
24
- raise NotImplementedError
32
+ raise AbstractMethodError
25
33
  end
26
34
 
27
35
  private
28
36
 
29
37
  # Checks the response and raises an error if the status isn't success.
30
38
  #
31
- # @param [Fixnum] code the HTTP status code
39
+ # @param [Integer] code the HTTP status code
32
40
  # @param [String] body the HTTP response body
33
41
  # @return [void]
34
42
  # @raise [Animoto::Error] if the status isn't between 200 and 299
@@ -19,7 +19,7 @@ module Animoto
19
19
  # @param [Symbol] method the HTTP method
20
20
  # @param [String] url the URL to request
21
21
  # @param [String,nil] body the request body
22
- # @param [Hash<String,String>] headers hash of HTTP request headers
22
+ # @param [Hash{String=>String}] headers hash of HTTP request headers
23
23
  # @return [Curl::Easy] the Easy instance
24
24
  def build_curl method, url, body, headers, options
25
25
  ::Curl::Easy.new(url) do |c|
@@ -36,6 +36,7 @@ module Animoto
36
36
  # @param [Curl::Easy] curl the Easy object with the request parameters
37
37
  # @param [Symbol] method the HTTP method to use
38
38
  # @param [String] body the HTTP request body
39
+ # @return [void]
39
40
  def perform curl, method, body
40
41
  case method
41
42
  when :get
@@ -44,8 +45,6 @@ module Animoto
44
45
  curl.http_post(body)
45
46
  end
46
47
  end
47
- end
48
-
49
- adapter_map.merge! :curl => CurlAdapter
48
+ end
50
49
  end
51
50
  end
@@ -39,9 +39,9 @@ module Animoto
39
39
  # @param [Symbol] method which HTTP method to use (should be lowercase, i.e. :get instead of :GET)
40
40
  # @param [String] uri the request path
41
41
  # @param [String,nil] body the request body
42
- # @param [Hash<String,String>] headers the request headers (will be sent as-is, which means you should
42
+ # @param [Hash{String=>String}] headers the request headers (will be sent as-is, which means you should
43
43
  # specify "Content-Type" => "..." instead of, say, :content_type => "...")
44
- # @param [Hash<Symbol,Object>] options
44
+ # @param [Hash{Symbol=>Object}] options
45
45
  # @return [Net::HTTPRequest] the request object
46
46
  def build_request method, uri, body, headers, options
47
47
  req = HTTP_METHOD_MAP[method].new uri.path
@@ -50,8 +50,6 @@ module Animoto
50
50
  req.basic_auth options[:username], options[:password]
51
51
  req
52
52
  end
53
- end
54
-
55
- adapter_map.merge! :net_http => NetHTTPAdapter
53
+ end
56
54
  end
57
55
  end
@@ -16,7 +16,7 @@ module Animoto
16
16
 
17
17
  # Builds the Session object.
18
18
  #
19
- # @param [Hash<Symbol,Object>] options options for the Session
19
+ # @param [Hash{Symbol=>Object}] options options for the Session
20
20
  # @return [Patron::Session] the Session object
21
21
  def build_session options
22
22
  session = ::Patron::Session.new
@@ -25,8 +25,6 @@ module Animoto
25
25
  session.password = options[:password]
26
26
  session
27
27
  end
28
- end
29
-
30
- adapter_map.merge! :patron => PatronAdapter
28
+ end
31
29
  end
32
30
  end
@@ -18,8 +18,6 @@ module Animoto
18
18
  check_response response.code, response.body
19
19
  response.body
20
20
  end
21
- end
22
-
23
- adapter_map.merge! :rest_client => RestClientAdapter
21
+ end
24
22
  end
25
23
  end
@@ -17,8 +17,6 @@ module Animoto
17
17
  check_response response.code, response.body
18
18
  response.body
19
19
  end
20
- end
21
-
22
- adapter_map.merge! :typhoeus => TyphoeusAdapter
20
+ end
23
21
  end
24
22
  end
@@ -13,7 +13,7 @@ module Animoto
13
13
  # Returns a representation of this manifest as a Hash, used to populate
14
14
  # request bodies when directing, rendering, etc.
15
15
  #
16
- # @return [Hash<String,Object>] the manifest as a Hash
16
+ # @return [Hash{String=>Object}] the manifest as a Hash
17
17
  def to_hash
18
18
  {}
19
19
  end
@@ -119,7 +119,7 @@ module Animoto
119
119
 
120
120
  # Returns a representation of this manifest as a Hash.
121
121
  #
122
- # @return [Hash<String,String>] the manifest as a Hash
122
+ # @return [Hash{String=>Object}] the manifest as a Hash
123
123
  # @raise [ArgumentError] if a callback URL is specified but not the format
124
124
  def to_hash options = {}
125
125
  hash = { 'directing_job' => { 'directing_manifest' => {} } }
@@ -1,48 +1,75 @@
1
1
  module Animoto
2
2
  module Manifests
3
- class DirectingAndRendering < Animoto::Manifests::Directing
4
-
5
- # The vertical resolution of the rendered video. Valid values are '180p', '270p',
6
- # '360p', '480p', '576p', '720p' or '1080p'.
7
- # @return [String]
8
- attr_accessor :resolution
3
+
4
+ # A directing-and-rendering manifest is little more than just a single envelope
5
+ # with a directing manifest and a rendering manifest embedded within.
6
+ class DirectingAndRendering < Animoto::Manifests::Base
9
7
 
10
- # The framerate of the rendered video. Valid values are 12, 15, 24 or 30.
11
- # @return [Fixnum]
12
- attr_accessor :framerate
8
+ # The embedded directing manifest
9
+ # @return [Manifests::Directing]
10
+ attr_reader :directing_manifest
13
11
 
14
- # The format of the rendered video. Valid values are 'h264', 'h264-iphone', 'flv' or 'iso'.
12
+ # The embedded rendering manifest
13
+ # @return [Manifests::Rendering]
14
+ attr_reader :rendering_manifest
15
+
16
+ # A URL to receive a callback after directing is finished.
15
17
  # @return [String]
16
- attr_accessor :format
18
+ attr_accessor :http_callback_url
17
19
 
20
+ # The format of the callback; either 'xml' or 'json'.
21
+ # @return [String]
22
+ attr_accessor :http_callback_format
23
+
18
24
  # Creates a new directing-and-rendering manifest.
19
25
  #
20
- # @param [Hash<Symbol,Object>] options
26
+ # @param [Hash{Symbol=>Object}] options
27
+ # @option options [String] :title the title of this project
28
+ # @option options [String] :pacing ('default') the pacing for this project
21
29
  # @option options [String] :resolution the vertical resolution of the rendered video
22
30
  # @option options [Integer] :framerate the framerate of the rendered video
23
31
  # @option options [String] :format the format of the rendered video
24
- # @return [Manifests::DirectingAndRendering] the manifest
25
- # @see Animoto::Manifests::Directing#initialize
26
- def initialize options = {}
27
- super
28
- @resolution = options[:resolution]
29
- @framerate = options[:framerate]
30
- @format = options[:format]
32
+ # @option options [String] :http_callback_url a URL to receive a callback when this job is done
33
+ # @option options [String] :http_callback_format the format of the callback
34
+ def initialize options = {}
35
+ @directing_manifest = Manifests::Directing.new(options.only(:title, :pacing))
36
+ @rendering_manifest = Manifests::Rendering.new(options.only(:resolution, :framerate, :format))
37
+
38
+ @http_callback_url = options[:http_callback_url]
39
+ @http_callback_format = options[:http_callback_format]
31
40
  end
32
-
41
+
42
+ # Delegates method calls to the underlying directing or rendering manifests if
43
+ # they respond to the call.
44
+ #
45
+ # @raise [NoMethodError] if the underlying manifests don't respond
46
+ def method_missing *args
47
+ name = args.first
48
+ if directing_manifest.respond_to?(name)
49
+ directing_manifest.__send__ *args
50
+ elsif rendering_manifest.respond_to?(name)
51
+ rendering_manifest.__send__ *args
52
+ else
53
+ super
54
+ end
55
+ end
56
+
33
57
  # Returns a representation of this manifest as a Hash.
34
58
  #
35
- # @return [Hash<String,String>] the manifest as a Hash
59
+ # @return [Hash{String=>Object}] the manifest as a Hash
36
60
  # @raise [ArgumentError] if a callback URL is specified but not the format
37
61
  # @see Animoto::Manifests::Directing#to_hash
62
+ # @see Animoto::Manifests::Rendering#to_hash
38
63
  def to_hash options = {}
39
- hash = super
40
- directing_job = hash.delete('directing_job')
41
- hash['directing_and_rendering_job'] = directing_job.merge('rendering_manifest' => { 'rendering_parameters' => {}})
42
- params = hash['directing_and_rendering_job']['rendering_manifest']['rendering_parameters']
43
- params['resolution'] = resolution
44
- params['framerate'] = framerate
45
- params['format'] = format
64
+ hash = { 'directing_and_rendering_job' => {} }
65
+ job = hash['directing_and_rendering_job']
66
+ if http_callback_url
67
+ raise ArgumentError, "You must specify a http_callback_format (either 'xml' or 'json')" if http_callback_format.nil?
68
+ job['http_callback'] = http_callback_url
69
+ job['http_callback_format'] = http_callback_format
70
+ end
71
+ job['directing_manifest'] = self.directing_manifest.to_hash['directing_job']['directing_manifest']
72
+ job['rendering_manifest'] = self.rendering_manifest.to_hash['rendering_job']['rendering_manifest']
46
73
  hash
47
74
  end
48
75