videojuicer-vj-sdk 0.1.5 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/Rakefile +3 -3
  2. data/VERSION.yml +1 -1
  3. data/lib/core_ext/hash.rb +13 -1
  4. data/lib/core_ext/object.rb +15 -0
  5. data/lib/core_ext/string.rb +11 -0
  6. data/lib/sdk_connection_harness.rb +25 -11
  7. data/lib/videojuicer.rb +13 -9
  8. data/lib/videojuicer/asset/audio.rb +5 -4
  9. data/lib/videojuicer/asset/base.rb +30 -15
  10. data/lib/videojuicer/asset/flash.rb +8 -0
  11. data/lib/videojuicer/asset/image.rb +2 -2
  12. data/lib/videojuicer/asset/video.rb +12 -9
  13. data/lib/videojuicer/campaign.rb +10 -1
  14. data/lib/videojuicer/campaign_policy.rb +116 -0
  15. data/lib/videojuicer/criterion/base.rb +56 -0
  16. data/lib/videojuicer/criterion/date_range.rb +7 -17
  17. data/lib/videojuicer/criterion/geolocation.rb +11 -20
  18. data/lib/videojuicer/criterion/request.rb +8 -17
  19. data/lib/videojuicer/criterion/time.rb +8 -18
  20. data/lib/videojuicer/criterion/week_day.rb +13 -17
  21. data/lib/videojuicer/oauth/request_proxy.rb +19 -13
  22. data/lib/videojuicer/presentation.rb +6 -0
  23. data/lib/videojuicer/promo/base.rb +72 -0
  24. data/lib/videojuicer/resource/base.rb +17 -15
  25. data/lib/videojuicer/resource/embeddable.rb +30 -0
  26. data/lib/videojuicer/resource/inferrable.rb +67 -7
  27. data/lib/videojuicer/resource/property_registry.rb +19 -0
  28. data/lib/videojuicer/resource/relationships/belongs_to.rb +2 -1
  29. data/lib/videojuicer/resource/types.rb +28 -0
  30. data/spec/assets/audio_spec.rb +24 -0
  31. data/spec/assets/flash_spec.rb +24 -0
  32. data/spec/assets/image_spec.rb +24 -0
  33. data/spec/assets/text_spec.rb +24 -0
  34. data/spec/assets/video_spec.rb +24 -0
  35. data/spec/campaign_policy_spec.rb +41 -0
  36. data/spec/campaign_spec.rb +0 -12
  37. data/spec/criterion/date_range_spec.rb +24 -0
  38. data/spec/criterion/geolocation_spec.rb +23 -0
  39. data/spec/criterion/request_spec.rb +23 -0
  40. data/spec/criterion/time_spec.rb +23 -0
  41. data/spec/criterion/week_day_spec.rb +23 -0
  42. data/spec/files/flash.swf +0 -0
  43. data/spec/helpers/be_equal_to.rb +26 -0
  44. data/spec/helpers/spec_fixtures.rb +214 -0
  45. data/spec/helpers/spec_helper.rb +27 -25
  46. data/spec/presentation_spec.rb +2 -15
  47. data/spec/promos/audio_spec.rb +23 -0
  48. data/spec/promos/image_spec.rb +24 -0
  49. data/spec/promos/text_spec.rb +23 -0
  50. data/spec/promos/video_spec.rb +23 -0
  51. data/spec/request_proxy_spec.rb +8 -12
  52. data/spec/session_spec.rb +3 -1
  53. data/spec/shared/dependent_spec.rb +40 -0
  54. data/spec/shared/embeddable_spec.rb +34 -0
  55. data/spec/shared/model_spec.rb +74 -0
  56. data/spec/shared/resource_spec.rb +12 -41
  57. data/spec/spec.opts +1 -1
  58. data/spec/user_spec.rb +5 -27
  59. data/tasks/vj-core.rb +3 -2
  60. data/vj-sdk.gemspec +56 -24
  61. metadata +53 -23
  62. data/lib/videojuicer/criterion/affiliate.rb +0 -21
  63. data/lib/videojuicer/criterion/embed.rb +0 -12
  64. data/spec/audio_spec.rb +0 -45
  65. data/spec/criteria/date_range_spec.rb +0 -37
  66. data/spec/criteria/geolocation_spec.rb +0 -38
  67. data/spec/criteria/request_spec.rb +0 -36
  68. data/spec/criteria/time_spec.rb +0 -37
  69. data/spec/criteria/week_day_spec.rb +0 -39
  70. data/spec/image_spec.rb +0 -44
  71. data/spec/text_spec.rb +0 -42
  72. data/spec/video_spec.rb +0 -50
@@ -0,0 +1,56 @@
1
+ module Videojuicer
2
+ module Criterion
3
+ def self.model_map
4
+ { :date_criteria => Videojuicer::Criterion::DateRange,
5
+ :geolocation_criteria => Videojuicer::Criterion::Geolocation,
6
+ :request_criteria => Videojuicer::Criterion::Request,
7
+ :time_criteria => Videojuicer::Criterion::Time,
8
+ :week_day_criteria => Videojuicer::Criterion::WeekDay
9
+ }
10
+ end
11
+
12
+ class Base
13
+ def self.inherited(base)
14
+ base.send(:include, Videojuicer::Resource)
15
+ base.send(:extend, Videojuicer::Criterion::Base::ClassMethods)
16
+ base.send(:include, Videojuicer::Criterion::Base::InstanceMethods)
17
+ end
18
+
19
+ module ClassMethods
20
+ def singular_name
21
+ "criterion"
22
+ end
23
+
24
+ def base_path(options={})
25
+ "/criteria/#{self.to_s.split("::").last.snake_case}"
26
+ end
27
+
28
+ def get(*args); raise NoMethodError; end
29
+ def all(*args); raise NoMethodError; end
30
+ def first(*args); raise NoMethodError; end
31
+ end
32
+
33
+ module InstanceMethods
34
+ def save(*args); raise NoMethodError; end
35
+ def destroy(*args); raise NoMethodError; end
36
+ def ==(other); self.attributes == other.attributes; end
37
+ def eql?(other); self.attributes.eql?(other.attributes) end
38
+ def matcher_attributes; end
39
+
40
+ def matcher_attributes
41
+ result = {}
42
+ self.matcher_keys.each do |key|
43
+ result[key] = self.send(key)
44
+ end
45
+ return result
46
+ end
47
+
48
+ def matcher_keys
49
+ raise NoMethodError, "Matcher Attributes needs to be implemented for #{self.class}";
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -1,24 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
1
3
  module Videojuicer
2
4
  module Criterion
3
- class DateRange
4
- include Videojuicer::Resource
5
- include Videojuicer::Exceptions
6
-
5
+ class DateRange < Base
6
+
7
7
  property :until, DateTime
8
- property :after, DateTime
9
- property :created_at, DateTime
10
- property :updated_at, DateTime
11
-
12
- def self.plural_name
13
- "criteria"
14
- end
15
-
16
- def self.singular_name
17
- "criterion"
18
- end
8
+ property :from, DateTime
19
9
 
20
- def self.base_path
21
- "/criteria/date_range"
10
+ def matcher_keys
11
+ [:until, :from]
22
12
  end
23
13
  end
24
14
  end
@@ -1,27 +1,18 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
1
3
  module Videojuicer
2
4
  module Criterion
3
- class Geolocation
4
- include Videojuicer::Resource
5
- include Videojuicer::Exceptions
6
-
7
- property :city, String
8
- property :region, String
9
- property :country, String
10
- #property :exclude, Boolean
11
- property :created_at, DateTime
12
- property :updated_at, DateTime
13
-
14
- def self.plural_name
15
- "criteria"
16
- end
17
-
18
- def self.singular_name
19
- "criterion"
20
- end
5
+ class Geolocation < Base
6
+
7
+ property :country, String
8
+ property :region, String
9
+ property :city, String
10
+ property :exclude, Boolean
21
11
 
22
- def self.base_path
23
- "/criteria/geolocation"
12
+ def matcher_keys
13
+ [:country, :region, :city, :exclude]
24
14
  end
15
+
25
16
  end
26
17
  end
27
18
  end
@@ -1,23 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
1
3
  module Videojuicer
2
4
  module Criterion
3
- class Request
4
- include Videojuicer::Resource
5
- include Videojuicer::Exceptions
6
-
7
- property :referrer, String
8
- property :created_at, DateTime
9
- property :updated_at, DateTime
10
-
11
- def self.plural_name
12
- "criteria"
13
- end
14
-
15
- def self.singular_name
16
- "criterion"
17
- end
5
+ class Request < Base
6
+
7
+ property :referrer, String, :nullable => false
8
+ property :exclude, Boolean
18
9
 
19
- def self.base_path
20
- "/criteria/request"
10
+ def matcher_keys
11
+ [:referrer, :exclude]
21
12
  end
22
13
  end
23
14
  end
@@ -1,24 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
1
3
  module Videojuicer
2
4
  module Criterion
3
- class Time
4
- include Videojuicer::Resource
5
- include Videojuicer::Exceptions
6
-
7
- property :until, String
8
- property :after, String
9
- property :created_at, DateTime
10
- property :updated_at, DateTime
11
-
12
- def self.plural_name
13
- "criteria"
14
- end
15
-
16
- def self.singular_name
17
- "criterion"
18
- end
5
+ class Time < Base
6
+
7
+ property :until, DateTime
8
+ property :from, DateTime
19
9
 
20
- def self.base_path
21
- "/criteria/time"
10
+ def matcher_keys
11
+ [:until, :from]
22
12
  end
23
13
  end
24
14
  end
@@ -1,23 +1,19 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
1
3
  module Videojuicer
2
4
  module Criterion
3
- class WeekDay
4
- include Videojuicer::Resource
5
- include Videojuicer::Exceptions
6
-
7
- property :day, Integer
8
- property :created_at, DateTime
9
- property :updated_at, DateTime
10
-
11
- def self.plural_name
12
- "criteria"
13
- end
14
-
15
- def self.singular_name
16
- "criterion"
17
- end
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
18
14
 
19
- def self.base_path
20
- "/criteria/week_day"
15
+ def matcher_keys
16
+ [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :exclude]
21
17
  end
22
18
  end
23
19
  end
@@ -62,7 +62,7 @@ module Videojuicer
62
62
  end
63
63
 
64
64
  # Generate the HTTP Request and handle the response
65
- url = "#{protocol}://#{host}:#{port}#{path}"
65
+ url = "#{host_stub}#{path}"
66
66
  request = request_class_for_method(method).new("#{path}?#{query_string}")
67
67
  # Generate the multipart body and headers
68
68
  if multipart_params.any?
@@ -85,6 +85,8 @@ module Videojuicer
85
85
  begin
86
86
  #response = HTTPClient.send(method, url, multipart_params)
87
87
  response = Net::HTTP.start(host, port) {|http| http.request(request) }
88
+ rescue EOFError => e
89
+ raise "EOF error when accessing #{url.inspect}"
88
90
  rescue Errno::ECONNREFUSED => e
89
91
  raise "Could not connect to #{url.inspect}"
90
92
  end
@@ -92,6 +94,10 @@ module Videojuicer
92
94
  return handle_response(response, request)
93
95
  end
94
96
 
97
+ def host_stub
98
+ "#{protocol}://#{host}:#{port}"
99
+ end
100
+
95
101
  # Handles an HTTPResponse object appropriately. Redirects are followed,
96
102
  # error states raise errors and success responses are returned directly.
97
103
  def handle_response(response, request)
@@ -105,35 +111,35 @@ module Videojuicer
105
111
  response
106
112
  when 401
107
113
  # Authentication problem
108
- response_error Unauthenticated, response
114
+ response_error Unauthenticated, request, response
109
115
  when 403
110
116
  # Attempted to perform a forbidden action
111
- response_error Forbidden, response
117
+ response_error Forbidden, request, response
112
118
  when 404
113
119
  # Resource URL not valid
114
- response_error NoResource, response
120
+ response_error NoResource, request, response
115
121
  when 406
116
122
  # Excuse me WTF r u doin
117
- response_error NotAcceptable, response
123
+ response_error NotAcceptable, request, response
118
124
  when 411
119
125
  # App-side server error where request is not properly constructed.
120
- response_error ContentLengthRequired, response
126
+ response_error ContentLengthRequired, request, response
121
127
  when 500..600
122
128
  # Remote application failure
123
- response_error RemoteApplicationError, response
129
+ response_error RemoteApplicationError, request, response
124
130
  else
125
- response_error UnhandledHTTPStatus, response
131
+ response_error UnhandledHTTPStatus, request, response
126
132
  end
127
133
  end
128
134
 
129
135
  # Handles the response as an error of the desired type.
130
- def response_error(exception_klass, response)
136
+ def response_error(exception_klass, request, response)
131
137
  begin
132
138
  e = JSON.parse(response.body)
133
139
  e = e["error"]
134
140
  raise exception_klass, "#{e["message"]} \n #{(e["backtrace"] || []).join("\n")}"
135
141
  rescue JSON::ParserError
136
- raise exception_klass, "#{exception_klass.to_s} : Response code was #{response.code}"
142
+ raise exception_klass, "#{exception_klass.to_s} : Response code was #{response.code} for request: #{request.path}"
137
143
  end
138
144
 
139
145
  end
@@ -206,13 +212,13 @@ module Videojuicer
206
212
 
207
213
  # Calculates and returns the signature secret to be used for this proxy object.
208
214
  def signature_secret
209
- [consumer_secret, token_secret].collect {|e| CGI.escape(e.to_s)}.join("&")
215
+ [consumer_secret, token_secret].collect {|e| CGI.rfc3986_escape(e.to_s)}.join("&")
210
216
  end
211
217
 
212
218
  # Returns the unencrypted signature base string for this proxy object and the
213
219
  # given request properties.
214
220
  def signature_base_string(method, path, params)
215
- s = [method.to_s.upcase, "#{protocol}://#{host}#{path}", normalize_params(params)].collect {|e| CGI.escape(e)}.join("&")
221
+ s = [method.to_s.upcase, "#{protocol}://#{host}#{path}", normalize_params(params)].collect {|e| CGI.rfc3986_escape(e)}.join("&")
216
222
  end
217
223
 
218
224
  # Returns a string representing a normalised parameter hash. Supports nesting for
@@ -220,7 +226,7 @@ module Videojuicer
220
226
  # the key 'bar inside {:foo=>{:bar=>"baz"}} will be named foo[bar] in the signature
221
227
  # and in the eventual request object.
222
228
  def normalize_params(params, *hash_path)
223
- flatten_params(params).sort {|a,b| a.to_s <=> b.to_s}.collect {|k, v| "#{CGI.escape(k)}=#{CGI.escape(v.to_s)}" }.join("&")
229
+ 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("&")
224
230
  end
225
231
 
226
232
  def flatten_params(params, *hash_path)
@@ -1,6 +1,7 @@
1
1
  module Videojuicer
2
2
  class Presentation
3
3
  include Videojuicer::Resource
4
+ include Videojuicer::Resource::Embeddable
4
5
  include Videojuicer::Exceptions
5
6
 
6
7
  property :slug, String
@@ -28,5 +29,10 @@ module Videojuicer
28
29
 
29
30
  property :tag_list, String
30
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
+
31
37
  end
32
38
  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
@@ -17,6 +17,7 @@ module Videojuicer
17
17
  include Videojuicer::OAuth::ProxyFactory
18
18
  include Videojuicer::Resource::Inferrable
19
19
  include Videojuicer::Resource::PropertyRegistry
20
+ include Videojuicer::Resource::Types
20
21
  include Videojuicer::Resource::Relationships::BelongsTo
21
22
 
22
23
  def self.included(base)
@@ -57,9 +58,9 @@ module Videojuicer
57
58
  proxy = proxy_for(config)
58
59
  param_key = self.class.parameter_name
59
60
  response = if new_record?
60
- proxy.post(resource_path(:validate), param_key=>returnable_attributes)
61
+ proxy.post(resource_path(:validate, :nested=>false), param_key=>returnable_attributes)
61
62
  else
62
- proxy.put(resource_path(:validate), param_key=>returnable_attributes)
63
+ proxy.put(resource_path(:validate, :nested=>false), param_key=>returnable_attributes)
63
64
  end
64
65
 
65
66
  # Parse and handle response
@@ -98,20 +99,11 @@ module Videojuicer
98
99
  return save
99
100
  end
100
101
 
101
- # Returns the appropriate resource path for this object.
102
- # If the object is a new record, then the root object type path
103
- # will be given. If the object is not new (has an ID) then the
104
- # specific ID will be used.
105
- def resource_path(action=nil)
106
- action_stem = (action)? "/#{action}" : ""
107
- (new_record?)? self.class.resource_path(action) : "#{self.class.resource_path}/#{id}#{action_stem}.json"
108
- end
109
-
110
102
  # Makes a call to the API for the current attributes on this object.
111
103
  # Overwrites the current instance attribute values.
112
104
  def reload
113
105
  raise NoResource, "Cannot load remote attributes for new records" if new_record?
114
- response = proxy_for(config).get(resource_path)
106
+ response = proxy_for(config).get(resource_path(nil, :nested=>false))
115
107
  return validate_response(response)
116
108
  end
117
109
 
@@ -135,7 +127,7 @@ module Videojuicer
135
127
  def all(options={})
136
128
  # Get a proxy
137
129
  options = (options.empty?)? {} : {parameter_name=>options} # FIXME this is a hacky workaround for singleton scope.
138
- response = instance_proxy.get(resource_path, options)
130
+ response = instance_proxy.get(base_path(:nested=>false), options)
139
131
  op = JSON.parse(response.body)
140
132
 
141
133
  items = (op["items"] rescue op) # If "items" is on the returned object then this is a collection hash.
@@ -151,11 +143,21 @@ module Videojuicer
151
143
  all(options.merge(:limit=>1)).first
152
144
  end
153
145
 
146
+ def create(attrs={})
147
+ o = new(attrs)
148
+ o.save
149
+ return o
150
+ end
151
+
154
152
  # Fetches an object given an ID. Straight forward.
155
153
  def get(id)
156
154
  o = new(:id=>id)
157
- o.reload
158
- o
155
+ begin
156
+ o.reload
157
+ o
158
+ rescue Videojuicer::Exceptions::NoResource
159
+ nil
160
+ end
159
161
  end
160
162
 
161
163
  def destroy(id)