vj-sdk 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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