vj-sdk 0.2.1

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.
Files changed (88) hide show
  1. data/.gitignore +4 -0
  2. data/LICENSE +20 -0
  3. data/README.markdown +0 -0
  4. data/README.rdoc +7 -0
  5. data/Rakefile +70 -0
  6. data/VERSION.yml +4 -0
  7. data/lib/core_ext/cgi.rb +12 -0
  8. data/lib/core_ext/hash.rb +52 -0
  9. data/lib/core_ext/object.rb +15 -0
  10. data/lib/core_ext/string.rb +11 -0
  11. data/lib/sdk_connection_harness.rb +101 -0
  12. data/lib/videojuicer/asset/audio.rb +13 -0
  13. data/lib/videojuicer/asset/base.rb +80 -0
  14. data/lib/videojuicer/asset/flash.rb +8 -0
  15. data/lib/videojuicer/asset/image.rb +10 -0
  16. data/lib/videojuicer/asset/text.rb +8 -0
  17. data/lib/videojuicer/asset/video.rb +22 -0
  18. data/lib/videojuicer/campaign.rb +19 -0
  19. data/lib/videojuicer/campaign_policy.rb +116 -0
  20. data/lib/videojuicer/criterion/base.rb +56 -0
  21. data/lib/videojuicer/criterion/date_range.rb +15 -0
  22. data/lib/videojuicer/criterion/geolocation.rb +18 -0
  23. data/lib/videojuicer/criterion/request.rb +15 -0
  24. data/lib/videojuicer/criterion/time.rb +15 -0
  25. data/lib/videojuicer/criterion/week_day.rb +20 -0
  26. data/lib/videojuicer/oauth/multipart_helper.rb +96 -0
  27. data/lib/videojuicer/oauth/proxy_factory.rb +18 -0
  28. data/lib/videojuicer/oauth/request_proxy.rb +280 -0
  29. data/lib/videojuicer/presentation.rb +38 -0
  30. data/lib/videojuicer/preset.rb +29 -0
  31. data/lib/videojuicer/promo/base.rb +72 -0
  32. data/lib/videojuicer/resource/base.rb +175 -0
  33. data/lib/videojuicer/resource/collection.rb +36 -0
  34. data/lib/videojuicer/resource/embeddable.rb +30 -0
  35. data/lib/videojuicer/resource/errors.rb +17 -0
  36. data/lib/videojuicer/resource/inferrable.rb +141 -0
  37. data/lib/videojuicer/resource/property_registry.rb +145 -0
  38. data/lib/videojuicer/resource/relationships/belongs_to.rb +39 -0
  39. data/lib/videojuicer/resource/types.rb +28 -0
  40. data/lib/videojuicer/session.rb +74 -0
  41. data/lib/videojuicer/shared/configurable.rb +103 -0
  42. data/lib/videojuicer/shared/exceptions.rb +32 -0
  43. data/lib/videojuicer/user.rb +43 -0
  44. data/lib/videojuicer.rb +110 -0
  45. data/spec/assets/audio_spec.rb +25 -0
  46. data/spec/assets/flash_spec.rb +24 -0
  47. data/spec/assets/image_spec.rb +25 -0
  48. data/spec/assets/text_spec.rb +24 -0
  49. data/spec/assets/video_spec.rb +25 -0
  50. data/spec/belongs_to_spec.rb +45 -0
  51. data/spec/campaign_policy_spec.rb +41 -0
  52. data/spec/campaign_spec.rb +25 -0
  53. data/spec/collection_spec.rb +31 -0
  54. data/spec/criterion/date_range_spec.rb +24 -0
  55. data/spec/criterion/geolocation_spec.rb +23 -0
  56. data/spec/criterion/request_spec.rb +23 -0
  57. data/spec/criterion/time_spec.rb +23 -0
  58. data/spec/criterion/week_day_spec.rb +23 -0
  59. data/spec/files/audio.mp3 +0 -0
  60. data/spec/files/empty_file +0 -0
  61. data/spec/files/flash.swf +0 -0
  62. data/spec/files/image.jpg +0 -0
  63. data/spec/files/text.txt +1 -0
  64. data/spec/files/video.mov +0 -0
  65. data/spec/helpers/be_equal_to.rb +26 -0
  66. data/spec/helpers/spec_fixtures.rb +227 -0
  67. data/spec/helpers/spec_helper.rb +53 -0
  68. data/spec/presentation_spec.rb +25 -0
  69. data/spec/preset_spec.rb +30 -0
  70. data/spec/promos/audio_spec.rb +23 -0
  71. data/spec/promos/image_spec.rb +24 -0
  72. data/spec/promos/text_spec.rb +23 -0
  73. data/spec/promos/video_spec.rb +23 -0
  74. data/spec/property_registry_spec.rb +130 -0
  75. data/spec/request_proxy_spec.rb +90 -0
  76. data/spec/session_spec.rb +98 -0
  77. data/spec/shared/asset_spec.rb +39 -0
  78. data/spec/shared/configurable_spec.rb +75 -0
  79. data/spec/shared/dependent_spec.rb +40 -0
  80. data/spec/shared/embeddable_spec.rb +34 -0
  81. data/spec/shared/model_spec.rb +74 -0
  82. data/spec/shared/resource_spec.rb +140 -0
  83. data/spec/spec.opts +3 -0
  84. data/spec/user_spec.rb +42 -0
  85. data/spec/videojuicer_spec.rb +122 -0
  86. data/tasks/vj-core.rb +72 -0
  87. data/vj-sdk.gemspec +168 -0
  88. metadata +209 -0
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Criterion
5
+ class Request < Base
6
+
7
+ property :referrer, String, :nullable => false
8
+ property :exclude, Boolean
9
+
10
+ def matcher_keys
11
+ [:referrer, :exclude]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Criterion
5
+ class Time < Base
6
+
7
+ property :until, DateTime
8
+ property :from, DateTime
9
+
10
+ def matcher_keys
11
+ [:until, :from]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Criterion
5
+ class WeekDay < Base
6
+ property :monday, Boolean
7
+ property :tuesday, Boolean
8
+ property :wednesday, Boolean
9
+ property :thursday, Boolean
10
+ property :friday, Boolean
11
+ property :saturday, Boolean
12
+ property :sunday, Boolean
13
+ property :exclude, Boolean
14
+
15
+ def matcher_keys
16
+ [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :exclude]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,96 @@
1
+ # Cribbed almost entirely from Merb's multipart request helper.
2
+ # Thanks, Yehuda!
3
+
4
+ module Videojuicer
5
+ module OAuth
6
+ module Multipart
7
+
8
+ require 'rubygems'
9
+ gem "mime-types"
10
+ require 'mime/types'
11
+
12
+ class Param
13
+ attr_accessor :key, :value
14
+
15
+ # ==== Parameters
16
+ # key<~to_s>:: The parameter key.
17
+ # value<~to_s>:: The parameter value.
18
+ def initialize(key, value)
19
+ @key = key
20
+ @value = value
21
+ end
22
+
23
+ # ==== Returns
24
+ # String:: The parameter in a form suitable for a multipart request.
25
+ def to_multipart
26
+ return %(Content-Disposition: form-data; name="#{key}"\r\n\r\n#{value}\r\n)
27
+ end
28
+ end
29
+
30
+ class FileParam
31
+ attr_accessor :key, :filename, :content
32
+
33
+ # ==== Parameters
34
+ # key<~to_s>:: The parameter key.
35
+ # filename<~to_s>:: Name of the file for this parameter.
36
+ # content<~to_s>:: Content of the file for this parameter.
37
+ def initialize(key, filename, content)
38
+ @key = key
39
+ @filename = filename
40
+ @content = content
41
+ end
42
+
43
+ # ==== Returns
44
+ # String::
45
+ # The file parameter in a form suitable for a multipart request.
46
+ def to_multipart
47
+ return %(Content-Disposition: form-data; name="#{key}"; filename="#{filename}"\r\n) + "Content-Type: #{MIME::Types.type_for(@filename).first}\r\n\r\n" + content + "\r\n"
48
+ end
49
+ end
50
+
51
+ class Post
52
+ BOUNDARY = '----------0xKhTmLbOuNdArY'
53
+ CONTENT_TYPE = "multipart/form-data, boundary=" + BOUNDARY
54
+
55
+ # ==== Parameters
56
+ # params<Hash>:: Optional params for the controller.
57
+ def initialize(params = {})
58
+ @multipart_params = []
59
+ push_params(params)
60
+ end
61
+
62
+ # Saves the params in an array of multipart params as Param and
63
+ # FileParam objects.
64
+ #
65
+ # ==== Parameters
66
+ # params<Hash>:: The params to add to the multipart params.
67
+ # prefix<~to_s>:: An optional prefix for the request string keys.
68
+ def push_params(params, prefix = nil)
69
+ params.sort_by {|k| k.to_s}.each do |key, value|
70
+ param_key = prefix.nil? ? key : "#{prefix}[#{key}]"
71
+ if value.respond_to?(:read)
72
+ @multipart_params << FileParam.new(param_key, value.path, value.read)
73
+ value.rewind
74
+ else
75
+ if value.is_a?(Hash) || value.is_a?(Mash)
76
+ value.keys.each do |k|
77
+ push_params(value, param_key)
78
+ end
79
+ else
80
+ @multipart_params << Param.new(param_key, value)
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ # ==== Returns
87
+ # Array[String, String]:: The query and the content type.
88
+ def to_multipart
89
+ query = @multipart_params.collect { |param| "--" + BOUNDARY + "\r\n" + param.to_multipart }.join("") + "--" + BOUNDARY + "--"
90
+ return query, CONTENT_TYPE
91
+ end
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,18 @@
1
+ =begin rdoc
2
+
3
+ ProxyFactory is a mixin that provides a convenience DSL for creating
4
+ new RequestProxy objects. It is intended for internal use within the SDK.
5
+
6
+ =end
7
+
8
+ module Videojuicer
9
+ module OAuth
10
+ module ProxyFactory
11
+
12
+ def proxy_for(options={})
13
+ Videojuicer::OAuth::RequestProxy.new(options)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,280 @@
1
+ =begin rdoc
2
+
3
+ The RequestProxy is, as the name suggests, a proxy through which HTTP requests are made
4
+ in order to have them correctly signed for verification under the OAuth protocol.
5
+
6
+ More information on OAuth: http://oauth.net
7
+
8
+
9
+
10
+ =end
11
+
12
+ require 'cgi'
13
+ require 'hmac'
14
+ require File.join(File.dirname(__FILE__), 'multipart_helper')
15
+
16
+ module Videojuicer
17
+ module OAuth
18
+
19
+ class RequestProxy
20
+
21
+ # Requests to the following ports will not include the port in the signature base string.
22
+ # See OAuth spec 1.0a section 9.1.2 for details.
23
+ EXCLUDED_BASE_STRING_PORTS = [80, 443].freeze
24
+
25
+ include Videojuicer::Exceptions
26
+ include Videojuicer::Configurable
27
+
28
+ # Initializes a new RequestProxy object which can be used to make requests.
29
+ # Accepts all the same options as Videojuicer::configure! as well as:
30
+ # +token+ - The OAuth token to use in requests made through this proxy.
31
+ # +token_secret+ - The OAuth token secret to use when encrypting the request signature.
32
+ def initialize(options={})
33
+ configure!(options)
34
+ end
35
+
36
+ # Makes a GET request given path and params.
37
+ # The host will be ascertained from the configuration options.
38
+ def get(path, params={}); make_request(:get, host, port, path, params); end
39
+
40
+ # Makes a POST request given path and params.
41
+ # The host will be ascertained from the configuration options.
42
+ def post(path, params={}); make_request(:post, host, port, path, params); end
43
+
44
+ # Makes a PUT request given path and params.
45
+ # The host will be ascertained from the configuration options.
46
+ def put(path, params={}); make_request(:put, host, port, path, params); end
47
+
48
+ # Makes a DELETE request given path and params.
49
+ # The host will be ascertained from the configuration options.
50
+ def delete(path, params={}); make_request(:delete, host, port, path, params); end
51
+
52
+ # Does the actual work of making a request. Returns a Net::HTTPResponse object.
53
+ def make_request(method, host, port, path, params={})
54
+ # Strip the files from the parameters to determine what, from the whole bundle, needs signing
55
+ signature_params, multipart_params = split_by_signature_eligibility(params)
56
+
57
+ if multipart_params.any?
58
+ # Sign the params and include the as multipart
59
+ multipart_params = flatten_params(
60
+ authify_params(method, path, signature_params).deep_merge(multipart_params)
61
+ )
62
+ query_string = ""
63
+ else
64
+ # Use the query string
65
+ query_string = authified_query_string(method, path, signature_params)
66
+ end
67
+
68
+ # Generate the HTTP Request and handle the response
69
+ url = "#{host_stub(protocol, host, port)}#{path}"
70
+ request = request_class_for_method(method).new("#{path}?#{query_string}")
71
+ # Generate the multipart body and headers
72
+ if multipart_params.any?
73
+ post_body, content_type = Multipart::Post.new(multipart_params).to_multipart
74
+ request.content_length = post_body.length
75
+ request.content_type = content_type
76
+ request.body = post_body
77
+ else
78
+ # Send a content-length on POST and PUT to avoid an HTTP 411 response
79
+ case method
80
+ when :post, :put
81
+ request = request_class_for_method(method).new("#{path}")
82
+ request.content_type = "application/x-www-form-urlencoded"
83
+ request.body = query_string
84
+ request.content_length = query_string.length
85
+ end
86
+ end
87
+
88
+
89
+ begin
90
+ #response = HTTPClient.send(method, url, multipart_params)
91
+ response = Net::HTTP.start(host, port) {|http| http.request(request) }
92
+ rescue EOFError => e
93
+ raise "EOF error when accessing #{url.inspect}"
94
+ rescue Errno::ECONNREFUSED => e
95
+ raise "Could not connect to #{url.inspect}"
96
+ end
97
+
98
+ return handle_response(response, request)
99
+ end
100
+
101
+ def host_stub(_protocol=protocol, _host=host, _port=port)
102
+ "#{_protocol}://#{_host}:#{_port}"
103
+ end
104
+
105
+ # Handles an HTTPResponse object appropriately. Redirects are followed,
106
+ # error states raise errors and success responses are returned directly.
107
+ def handle_response(response, request)
108
+ c = response.code.to_i
109
+ case c
110
+ when 200..399
111
+ # Successful or redirected response
112
+ response
113
+ when 415
114
+ # Validation error
115
+ response
116
+ when 401
117
+ # Authentication problem
118
+ response_error Unauthenticated, request, response
119
+ when 403
120
+ # Attempted to perform a forbidden action
121
+ response_error Forbidden, request, response
122
+ when 404
123
+ # Resource URL not valid
124
+ response_error NoResource, request, response
125
+ when 406
126
+ # Excuse me WTF r u doin
127
+ response_error NotAcceptable, request, response
128
+ when 411
129
+ # App-side server error where request is not properly constructed.
130
+ response_error ContentLengthRequired, request, response
131
+ when 500..600
132
+ # Remote application failure
133
+ response_error RemoteApplicationError, request, response
134
+ else
135
+ response_error UnhandledHTTPStatus, request, response
136
+ end
137
+ end
138
+
139
+ # Handles the response as an error of the desired type.
140
+ def response_error(exception_klass, request, response)
141
+ begin
142
+ e = JSON.parse(response.body)
143
+ e = e["error"]
144
+ raise exception_klass, "#{e["message"]} \n #{(e["backtrace"] || []).join("\n")}"
145
+ rescue JSON::ParserError
146
+ raise exception_klass, "#{exception_klass.to_s} : Response code was #{response.code} for request: #{request.path}"
147
+ end
148
+
149
+ end
150
+
151
+ # Splits a given parameter hash into two hashes - one containing all
152
+ # string and non-binary parameters, and one containing all file/binary parameters.
153
+ # This action is performed recursively so that:
154
+ # params = {:user=>{:attributes=>{:file=>some_file, :name=>"user name"}}, :foo=>"bar"}
155
+ # normal, multipart = split_multipart_params(params)
156
+ # normal.inspect # => {:user=>{:attributes=>{:name=>"user name"}}, :foo=>"bar"}
157
+ # multipart.inspect # => {:user=>{:attributes=>{:file=>some_file}}}
158
+ def split_by_signature_eligibility(params, *hash_path)
159
+ strings = {}
160
+ files = {}
161
+ params.each do |key, value|
162
+ if value.is_a?(Hash)
163
+ # Call recursively
164
+ s, f = split_by_signature_eligibility(value, *(hash_path+[key]))
165
+ strings = strings.deep_merge(s)
166
+ files = files.deep_merge(f)
167
+ else
168
+ # Insert it into files at the current key path if it is a binary,
169
+ # and into strings if it is not.
170
+ pwd = (value.respond_to?(:read))? files : strings
171
+ hash_path.each do |component|
172
+ pwd[component] ||= {}
173
+ pwd = pwd[component]
174
+ end
175
+ pwd[key] = value
176
+ end
177
+ end
178
+ return strings, files
179
+ end
180
+
181
+ def signed_url(method, path, params={})
182
+ "#{protocol}://#{host}:#{port}#{path}?#{authified_query_string(method, path, params)}"
183
+ end
184
+
185
+ # Authifies the given parameters and converts them into a query string.
186
+ def authified_query_string(method, path, params={})
187
+ normalize_params(authify_params(method, path, params))
188
+ end
189
+
190
+ # Takes a set of business parameters you want sent to the provider, and merges them
191
+ # with the proxy configuration to produce a set of parameters that will be accepted
192
+ # by the OAuth provider.
193
+ def authify_params(method, path, params)
194
+ params = {
195
+ :oauth_consumer_key=>consumer_key,
196
+ :oauth_token=>token,
197
+ :api_version=>api_version,
198
+ :oauth_timestamp=>Time.now.to_i,
199
+ :oauth_nonce=>rand(9999),
200
+ :oauth_signature_method=>"HMAC-SHA1",
201
+ :seed_name=>seed_name,
202
+ :user_id=>user_id
203
+ }.merge(params)
204
+ params.delete_if {|k,v| (!v) or (v.to_s.empty?) }
205
+ params[:oauth_signature] = signature(method, path, params)
206
+ return params
207
+ end
208
+
209
+ # Calculates and returns the encrypted signature for this proxy object and the
210
+ # given request properties.
211
+ def signature(method, path, params)
212
+ base = signature_base_string(method, path, params)
213
+ signature_octet = HMAC::SHA1.digest(signature_secret, base)
214
+ signature_base64 = [signature_octet].pack('m').chomp.gsub(/\n/, '')
215
+ end
216
+
217
+ # Calculates and returns the signature secret to be used for this proxy object.
218
+ def signature_secret
219
+ [consumer_secret, token_secret].collect {|e| CGI.rfc3986_escape(e.to_s)}.join("&")
220
+ end
221
+
222
+ # Returns the unencrypted signature base string for this proxy object and the
223
+ # given request properties.
224
+ def signature_base_string(method, path, params)
225
+ s = [method.to_s.upcase, "#{protocol}://#{signature_base_string_host}#{path}", normalize_params(params)].collect {|e| CGI.rfc3986_escape(e)}.join("&")
226
+ end
227
+
228
+ def signature_base_string_host
229
+ if EXCLUDED_BASE_STRING_PORTS.include?(port.to_i)
230
+ # Natural port. Ignore the port
231
+ host
232
+ else
233
+ # Weird port. Expect a signature.
234
+ "#{host}:#{port}"
235
+ end
236
+ end
237
+
238
+ # Returns a string representing a normalised parameter hash. Supports nesting for
239
+ # rails or merb-style object[attr] properties supplied as nested hashes. For instance,
240
+ # the key 'bar inside {:foo=>{:bar=>"baz"}} will be named foo[bar] in the signature
241
+ # and in the eventual request object.
242
+ def normalize_params(params, *hash_path)
243
+ flatten_params(params).sort {|a,b| a.to_s <=> b.to_s}.collect {|k, v| "#{CGI.rfc3986_escape(k)}=#{CGI.rfc3986_escape(v.to_s)}" }.join("&")
244
+ end
245
+
246
+ def flatten_params(params, *hash_path)
247
+ op = {}
248
+ params.sort {|a,b| a.to_s<=>b.to_s}.each do |key, value|
249
+ path = hash_path.dup
250
+ path << key.to_s
251
+
252
+ if value.is_a?(Hash)
253
+ op.merge! flatten_params(value, *path)
254
+ elsif value
255
+ key_path = path.first + path[1..(path.length-1)].collect {|h| "[#{h}]"}.join("")
256
+ op[key_path] = value
257
+ end
258
+ end
259
+ return op
260
+ end
261
+
262
+ # Returns the Net::HTTPRequest subclass needed to make a request for the given method.
263
+ def request_class_for_method(m, in_module=Net::HTTP)
264
+ case (m.is_a?(Symbol) ? m : m.downcase.to_sym rescue :get)
265
+ when :post
266
+ in_module::Post
267
+ when :put
268
+ in_module::Put
269
+ when :head
270
+ in_module::Head
271
+ when :delete
272
+ in_module::Delete
273
+ else
274
+ in_module::Get
275
+ end
276
+ end
277
+
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,38 @@
1
+ module Videojuicer
2
+ class Presentation
3
+ include Videojuicer::Resource
4
+ include Videojuicer::Resource::Embeddable
5
+ include Videojuicer::Exceptions
6
+
7
+ property :slug, String
8
+ property :title, String
9
+ property :author, String
10
+ property :author_url, String
11
+ property :abstract, String
12
+ property :user_id, Integer, :writer=>:private
13
+ belongs_to :user, :class=>Videojuicer::User
14
+ property :callback_url, String
15
+
16
+ property :state, String, :default=>"ready" # see the STATES constant for values
17
+ property :disclosure, String, :default=>"public" # see DISCLOSURES constant for values
18
+ property :publish_from, DateTime
19
+ property :publish_until, DateTime
20
+
21
+ property :document_layout, String
22
+ property :document_content, String # the presentation document
23
+ property :document_type, String, :default=>"SMIL 3.0"
24
+
25
+ property :created_at, DateTime
26
+ property :updated_at, DateTime
27
+
28
+ property :image_asset_id, Integer # FIXME make me a relationship helper
29
+
30
+ property :tag_list, String
31
+
32
+ def permalink
33
+ proxy = proxy_for(config)
34
+ "#{proxy.host_stub}/presentations/#{id}.html?seed_name=#{seed_name}".gsub(":80/","/")
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ module Videojuicer
2
+ class Preset
3
+ include Videojuicer::Resource
4
+
5
+ property :name, String
6
+ property :derived_type, String
7
+ property :created_at, DateTime
8
+ property :updated_at, DateTime
9
+
10
+ property :file_format, String
11
+
12
+ property :audio_bit_rate, Integer # bits per second
13
+ property :audio_channels, Integer
14
+ property :audio_format, String
15
+ property :audio_sample_rate, Integer # hertz
16
+
17
+ property :video_bit_rate, Integer # bits per second
18
+ property :video_format, String
19
+ property :video_frame_rate, Float # frames per second
20
+
21
+ property :width, Integer # pixels
22
+ property :height, Integer # pixels
23
+
24
+ def self.formats
25
+ response = instance_proxy.get(resource_route(:formats))
26
+ JSON.parse(response.body)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,72 @@
1
+ module Videojuicer
2
+ module Promo
3
+ def self.model_map
4
+ { :audio => Videojuicer::Promo::Audio,
5
+ :images => Videojuicer::Promo::Image,
6
+ :texts => Videojuicer::Promo::Text,
7
+ :videos => Videojuicer::Promo::Video
8
+ }
9
+ end
10
+
11
+ class Base
12
+
13
+ def self.inherited(base)
14
+ base.send(:include, Videojuicer::Resource)
15
+ base.send(:extend, Videojuicer::Promo::Base::ClassMethods)
16
+ base.send(:include, Videojuicer::Promo::Base::InstanceMethods)
17
+
18
+ base.property :campaign_policy_id, Integer
19
+ base.property :asset_id, Integer
20
+ base.property :role, String
21
+ base.property :href, String
22
+ end
23
+
24
+ module ClassMethods
25
+ def singular_name
26
+ "promo"
27
+ end
28
+
29
+ def base_path(options={})
30
+ "/promos/#{self.to_s.split("::").last.snake_case}"
31
+ end
32
+
33
+ def get(*args); raise NoMethodError; end
34
+ def all(*args); raise NoMethodError; end
35
+ def first(*args); raise NoMethodError; end
36
+ end
37
+
38
+ module InstanceMethods
39
+ def save(*args); raise NoMethodError; end
40
+ def destroy(*args); raise NoMethodError; end
41
+ def asset
42
+ Videojuicer::Asset.const_get(self.class.to_s.split("::").last).get(asset_id)
43
+ end
44
+ def matcher_keys
45
+ [:campaign_policy_id, :asset_id, :role, :href]
46
+ end
47
+ def matcher_attributes
48
+ matcher_keys.inject({}) do |memo, attr|
49
+ memo.update(attr => self.send(attr))
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ class Audio < Base
57
+ end
58
+
59
+ class Image < Base
60
+ end
61
+
62
+ class Text < Base
63
+ end
64
+
65
+ class Video < Base
66
+ end
67
+
68
+ class Heart < Base
69
+ end
70
+
71
+ end
72
+ end