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
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ log/*
2
+ core-fixtures.yml
3
+ pkg/*
4
+ .DS_Store
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 danski
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
File without changes
data/README.rdoc ADDED
@@ -0,0 +1,7 @@
1
+ = vj-sdk
2
+
3
+ Description goes here.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 danski. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,70 @@
1
+ require 'rubygems'
2
+ require 'merb-core'
3
+ require 'rake'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "vj-sdk"
9
+ gem.summary = "Videojuicer core-sdk"
10
+ gem.email = "dan@videojuicer.com"
11
+ gem.homepage = "http://github.com/danski/vj-sdk"
12
+ gem.authors = ["danski", "thejohnny", "knowtheory", "sixones", "btab"]
13
+
14
+ # Declare dependencies
15
+ gem.add_dependency "json", ">= 1.0"
16
+ gem.add_dependency "ruby-hmac", ">= 0.3.2"
17
+ gem.add_dependency "mash", ">= 0.0.3"
18
+ end
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new do |t|
25
+ t.spec_opts = ['--options', 'spec/spec.opts']
26
+ t.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ namespace :spec do
30
+ task :sdk do
31
+ require 'tasks/vj-core'
32
+ Rake::Task["videojuicer:core:setup"].invoke
33
+ Rake::Task["spec"].execute
34
+ Rake::Task["videojuicer:core:cleanup"].invoke
35
+ end
36
+ end
37
+ task :default => :"spec:sdk"
38
+
39
+ begin
40
+ require 'rcov/rcovtask'
41
+ Rcov::RcovTask.new do |test|
42
+ test.libs << 'test'
43
+ test.pattern = 'test/**/*_test.rb'
44
+ test.verbose = true
45
+ end
46
+ rescue LoadError
47
+ task :rcov do
48
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
49
+ end
50
+ end
51
+
52
+ require 'rake/rdoctask'
53
+ Rake::RDocTask.new do |rdoc|
54
+ if File.exist?('VERSION.yml')
55
+ config = YAML.load(File.read('VERSION.yml'))
56
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
57
+ else
58
+ version = ""
59
+ end
60
+
61
+ rdoc.rdoc_dir = 'rdoc'
62
+ rdoc.title = "vj-sdk #{version}"
63
+ rdoc.rdoc_files.include('README*')
64
+ rdoc.rdoc_files.include('lib/**/*.rb')
65
+ end
66
+
67
+ desc "provide a console like merb -i or script/console"
68
+ task :console do
69
+ exec "irb -r irb/completion -r lib/videojuicer.rb -r lib/sdk_connection_harness.rb"
70
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 2
4
+ :patch: 1
@@ -0,0 +1,12 @@
1
+ class CGI
2
+ class << self
3
+
4
+ def rfc3986_escape(str)
5
+ escape(str).gsub("+", "%20")
6
+ end
7
+ def rfc3986_unescape(str)
8
+ unescape(str)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ class Hash
2
+
3
+ # Returns a new hash just like this one, but with all the string keys expressed as symbols.
4
+ # Also applies to hashes within self.
5
+ # Based on an implementation within Rails 2.x, thanks Rails!
6
+ def deep_symbolize
7
+ target = dup
8
+ target.inject({}) do |memo, (key, value)|
9
+ value = value.deep_symbolize if value.is_a?(Hash)
10
+ memo[(key.to_sym rescue key) || key] = value
11
+ memo
12
+ end
13
+ end
14
+
15
+ # Returns a new hash just like this one, but with all the symbol keys expressed as strings.
16
+ # Also applies to hashes within self.
17
+ # Based on an implementation within Rails 2.x, thanks Rails!
18
+ def deep_stringify
19
+ target = dup
20
+ target.inject({}) do |memo, (key, value)|
21
+ value = value.deep_stringify if value.is_a?(Hash)
22
+ memo[(key.to_s rescue key) || key] = value
23
+ memo
24
+ end
25
+ end
26
+
27
+ # Merges self with another hash, recursively.
28
+ #
29
+ # This code was lovingly stolen from some random gem:
30
+ # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
31
+ #
32
+ # Thanks to whoever made it.
33
+ def deep_merge(hash)
34
+ target = dup
35
+
36
+ hash.keys.each do |key|
37
+ if hash[key].is_a? Hash and self[key].is_a? Hash
38
+ target[key] = target[key].deep_merge(hash[key])
39
+ next
40
+ end
41
+ target[key] = hash[key]
42
+ end
43
+ target
44
+ end
45
+
46
+ def to_xml
47
+ inject("") do |memo, (key, value)|
48
+ memo << "<#{key}>#{(value.respond_to?(:to_xml))? value.to_xml : value}</#{key}>"
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,15 @@
1
+ class Object
2
+
3
+ # Cribbed from DataMapper's Extlib library.
4
+ def full_const_get(name)
5
+ list = name.split("::")
6
+ list.shift if list.first.blank?
7
+ obj = self
8
+ list.each do |x|
9
+ # This is required because const_get tries to look for constants in the
10
+ # ancestor chain, but we only want constants that are HERE
11
+ obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
12
+ end
13
+ obj
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ class String
2
+ def blank?
3
+ strip.empty?
4
+ end
5
+
6
+ def snake_case
7
+ return self.downcase if self =~ /^[A-Z]+$/
8
+ self.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/
9
+ return $+.downcase
10
+ end
11
+ end
@@ -0,0 +1,101 @@
1
+ begin
2
+ require 'extlib'
3
+ rescue LoadError
4
+ require 'mash'
5
+ end
6
+ require 'yaml'
7
+ class SDKConnectionHarness
8
+ class << self
9
+
10
+ attr_accessor :server_pid
11
+ attr_accessor :fixtures
12
+
13
+ def core_directory
14
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "vj-core"))
15
+ end
16
+
17
+ def start!
18
+ stop! if running?
19
+ puts "Starting vj-core from #{core_directory}\n"
20
+ Thread.new do
21
+ cur_dir = Dir.pwd
22
+ Dir.chdir(core_directory) do
23
+ `./bin/merb -d #{app_server} -p #{port} -e test --log ./log/sdk-development.log`
24
+ end
25
+ Dir.chdir(cur_dir)
26
+ end
27
+ end
28
+
29
+ def stop!
30
+ Thread.new do
31
+ `killall -9 "merb : worker (port #{port})"`
32
+ end
33
+ end
34
+
35
+ def running?
36
+ uri = URI.parse("http://localhost:#{port}/")
37
+ req = Net::HTTP::Get.new(uri.path)
38
+
39
+ begin
40
+ resp = Net::HTTP.start(uri.host, uri.port) do |http|
41
+ http.request(req)
42
+ end
43
+ return true
44
+ rescue Exception => e
45
+ # Connection refused means the daemon isn't running
46
+ return false
47
+ end
48
+ end
49
+
50
+ def load_fixtures
51
+ Dir.chdir(core_directory) do
52
+ out = `./bin/rake videojuicer:sdk:setup MERB_ENV=test`
53
+ out = out.match(/!!!([^!]+)!!!/m)
54
+ self.fixtures = out[1]
55
+ end
56
+ end
57
+
58
+ def write_fixtures
59
+ f = File.open(File.join(File.dirname(__FILE__), "..", "core-fixtures.yml"), "w+")
60
+ f.rewind
61
+ f.write(self.fixtures)
62
+ f.close
63
+ end
64
+
65
+ def app_server
66
+ begin
67
+ require 'thin'
68
+ adapter = 'thin'
69
+ rescue LoadError
70
+ adapter = 'mongrel'
71
+ end
72
+ return "--adapter #{adapter}"
73
+ end
74
+
75
+ def port
76
+ 6666
77
+ end
78
+
79
+ def connect(overrides={})
80
+ fixtures = Mash.new(YAML.load(load_fixtures)).merge(overrides)
81
+ configure_test_settings(fixtures)
82
+ Videojuicer.enter_scope :seed_name => fixtures[:seed][:name],
83
+ :consumer_key=>fixtures["write-master"][:consumer][:consumer_key],
84
+ :consumer_secret=>fixtures["write-master"][:consumer][:consumer_secret],
85
+ :token=>fixtures["write-master"][:authorized_token][:oauth_token],
86
+ :token_secret=>fixtures["write-master"][:authorized_token][:oauth_token_secret]
87
+ end
88
+
89
+ def configure_test_settings(overrides={})
90
+ Videojuicer.configure!({
91
+ :consumer_key => nil,
92
+ :consumer_secret => nil,
93
+ :api_version => 1,
94
+ :protocol => "http",
95
+ :host => "localhost",
96
+ :port => port
97
+ }.merge(overrides))
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Asset
5
+ class Audio < Base
6
+ property :bit_rate, Integer, :writer => :private # bits per second
7
+ property :channels, Integer, :writer => :private
8
+ property :duration, Integer, :writer => :private # milliseconds
9
+ property :format, String, :writer => :private
10
+ property :sample_rate, Integer, :writer => :private # hertz
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,80 @@
1
+ module Videojuicer
2
+ module Asset
3
+ class Base
4
+
5
+ def self.inherited(base)
6
+ base.send(:include, Videojuicer::Resource)
7
+ base.send(:extend, Videojuicer::Asset::Base::ClassMethods)
8
+ base.send(:include, Videojuicer::Asset::Base::InstanceMethods)
9
+
10
+ # - heritage
11
+ base.property :derived_internally, Videojuicer::Resource::Types::Boolean, :writer => :private
12
+ base.property :original_asset_id, Integer, :writer => :private
13
+ base.property :original_asset_type, String, :writer => :private
14
+ base.property :preset_id, Integer, :writer => :private
15
+ base.property :user_id, Integer, :writer => :private
16
+
17
+ # - transformation
18
+ base.property :source_space_window, String, :writer => :private
19
+ base.property :source_time_window, String, :writer => :private
20
+
21
+ # - generic file handling
22
+ base.property :file, File
23
+ base.property :file_name, String
24
+ base.property :file_size, Integer, :writer => :private # bytes
25
+
26
+ # - common metadata
27
+ base.property :licensed_at, Date
28
+ base.property :licensed_by, String
29
+ base.property :licensed_under, String
30
+ base.property :published_at, Date
31
+
32
+ # - access control / workflow
33
+ base.property :url, String, :writer => :private
34
+ base.property :state, String, :writer => :private
35
+ base.property :state_changed_at, DateTime, :writer => :private
36
+ base.property :state_changed_url, String
37
+ base.property :created_at, DateTime
38
+ base.property :updated_at, DateTime
39
+ end
40
+
41
+ module ClassMethods
42
+ def singular_name
43
+ "asset"
44
+ end
45
+
46
+ def base_path(options={})
47
+ "/assets/#{self.to_s.downcase.split("::").last}"
48
+ end
49
+ end
50
+
51
+ module InstanceMethods
52
+ def derive(preset)
53
+ response = proxy_for(config).post(resource_path, :preset_id => preset.id)
54
+ self.class.new(JSON.parse(response.body))
55
+ end
56
+
57
+ def file
58
+ raise "use the value of #{self.class}#url to download a copy of the asset"
59
+ end
60
+
61
+ def returnable_attributes
62
+ attrs = super
63
+ attrs.delete(:file) unless new_record?
64
+ attrs
65
+ end
66
+
67
+ def set_derived(from_asset, preset)
68
+ params = {
69
+ :original_asset_type => from_asset.class.to_s.split("::").last,
70
+ :original_asset_id => from_asset.id,
71
+ :preset_id => preset.id
72
+ }
73
+ response = proxy_for(config).post(resource_path(:set_derived), params)
74
+ self.attributes = JSON.parse(response.body)
75
+ end
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Asset
5
+ class Flash < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Asset
5
+ class Image < Base
6
+ property :width, Integer, :writer => :private # pixels
7
+ property :height, Integer, :writer => :private # pixels
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Asset
5
+ class Text < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Asset
5
+ class Video < Base
6
+ property :bit_rate, Integer, :writer => :private # bits per second
7
+ property :duration, Integer, :writer => :private # milliseconds
8
+
9
+ property :audio_bit_rate, Integer, :writer => :private # bits per second
10
+ property :audio_channels, Integer, :writer => :private
11
+ property :audio_format, String, :writer => :private
12
+ property :audio_sample_rate, Integer, :writer => :private # hertz
13
+
14
+ property :video_bit_rate, Integer, :writer => :private # bits per second
15
+ property :video_format, String, :writer => :private
16
+ property :video_frame_rate, Float, :writer => :private # frames per second
17
+
18
+ property :width, Integer, :writer => :private # pixels
19
+ property :height, Integer, :writer => :private # pixels
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module Videojuicer
2
+ class Campaign
3
+ include Videojuicer::Resource
4
+ include Videojuicer::Exceptions
5
+
6
+ property :name, String
7
+ property :user_id, Integer
8
+ property :created_at, DateTime
9
+ property :updated_at, DateTime
10
+ belongs_to :user, :class=>Videojuicer::User
11
+
12
+ attr_accessor :campaign_policies
13
+ def campaign_policies=(arg)
14
+ # Loop over and make objects or something
15
+ @campaign_policies=(arg)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,116 @@
1
+ module Videojuicer
2
+ class Campaign
3
+ class CampaignPolicy
4
+ include Videojuicer::Resource
5
+
6
+ # Resource should note that this is a class within a class
7
+ # Should treat parent class as parent resource
8
+
9
+ property :presentation_id, Integer
10
+ property :campaign_id, Integer
11
+ property :created_at, DateTime
12
+ property :updated_at, DateTime
13
+
14
+ belongs_to :campaign
15
+
16
+ attr_accessor :criteria
17
+ def criteria=(object_hash)
18
+ # TODO instantiate criteria in a useful object of some sort
19
+
20
+ # Fail unless arg is an array
21
+ # Loop over array do |mem|
22
+ # if mem is a hash
23
+ # instantiate as a criterion
24
+
25
+
26
+ # if criterion object already on this policy
27
+ # leave be
28
+ # else
29
+ # add_criteria(mem)
30
+ # end
31
+ # end
32
+
33
+ # what you want to be able to do is:
34
+ # policy.criteria.include?(some_criterion)
35
+ @criteria = {}
36
+ object_hash.map do |type_sym, criteria|
37
+ @criteria[type_sym.to_sym] =
38
+ criteria.map do |criterion|
39
+ Videojuicer::Criterion.model_map[type_sym.to_sym].new(criterion) #rescue raise("No criteria class found for #{type_sym} in #{Videojuicer::Criterion.model_map.inspect}")
40
+ end
41
+ end
42
+ end
43
+
44
+ def criteria
45
+ @criteria.values.flatten
46
+ end
47
+
48
+ %w(date_criteria geolocation_criteria request_criteria time_criteria week_day_criteria).each do |key|
49
+ class_eval <<-DEF
50
+ def #{key}
51
+ @criteria[:#{key}]
52
+ end
53
+ DEF
54
+ end
55
+
56
+
57
+ def add_criteria(*criteria)
58
+ responses = criteria.map do |criterion|
59
+ proxy_for(config).post("#{path_for_dependents}#{criterion.class.base_path}", {:criterion => criterion.attributes})
60
+ end
61
+ self.reload
62
+ responses
63
+ end
64
+
65
+ def remove_criteria(*criteria)
66
+ responses = criteria.map do |criterion|
67
+ proxy_for(config).delete("#{path_for_dependents}#{criterion.class.base_path}", {:criterion => criterion.attributes})
68
+ end
69
+ self.reload
70
+ responses
71
+ end
72
+
73
+ def promos=(object_hash={})
74
+ @promos = {}
75
+ object_hash.map do |type_sym, promos|
76
+ @promos[type_sym.to_sym] =
77
+ promos.map do |promo|
78
+ Videojuicer::Promo.model_map[type_sym.to_sym].new(promo)
79
+ end
80
+ end
81
+ end
82
+
83
+ def promos
84
+ @promos.values.flatten
85
+ end
86
+
87
+ %w(audio images texts videos).each do |key|
88
+ class_eval <<-DEF
89
+ def #{key}
90
+ @promos[:#{key}]
91
+ end
92
+ DEF
93
+ end
94
+
95
+ def add_promo(*promos)
96
+ responses = promos.map do |promo|
97
+ proxy_for(config).post("#{path_for_dependents}#{promo.class.base_path}", {:promo => promo.attributes})
98
+ end
99
+ self.reload
100
+ responses
101
+ end
102
+
103
+ def remove_promo(*promos)
104
+ responses = promos.map do |promo|
105
+ proxy_for(config).delete("#{path_for_dependents}#{promo.class.base_path}", {:promo => promo.attributes})
106
+ end
107
+ self.reload
108
+ responses
109
+ end
110
+
111
+ def path_for_dependents
112
+ self.class.compile_route(self.class.nesting_route,{:campaign_policy_id => self.id, :campaign_id => self.campaign_id})
113
+ end
114
+ end
115
+ end
116
+ end
@@ -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
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Criterion
5
+ class DateRange < 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,18 @@
1
+ require File.join(File.dirname(__FILE__), "base")
2
+
3
+ module Videojuicer
4
+ module Criterion
5
+ class Geolocation < Base
6
+
7
+ property :country, String
8
+ property :region, String
9
+ property :city, String
10
+ property :exclude, Boolean
11
+
12
+ def matcher_keys
13
+ [:country, :region, :city, :exclude]
14
+ end
15
+
16
+ end
17
+ end
18
+ end