ad_gear_client 0.3.8
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.
- data/.gitignore +6 -0
- data/LICENSE +21 -0
- data/README.rdoc +55 -0
- data/Rakefile +132 -0
- data/VERSION +1 -0
- data/ad_gear_client.gemspec +114 -0
- data/examples/.gitignore +1 -0
- data/examples/ad_gear.yml.sample +8 -0
- data/examples/create_campaign.rb +23 -0
- data/examples/create_placement.rb +57 -0
- data/examples/prelude.rb +27 -0
- data/examples/read_write_site.rb +36 -0
- data/examples/upload_file_to_ad_unit.rb +49 -0
- data/lib/ad_gear/ad_spot.rb +7 -0
- data/lib/ad_gear/ad_spot_membership.rb +5 -0
- data/lib/ad_gear/ad_unit.rb +17 -0
- data/lib/ad_gear/ad_unit_click.rb +5 -0
- data/lib/ad_gear/ad_unit_file.rb +6 -0
- data/lib/ad_gear/ad_unit_interaction.rb +5 -0
- data/lib/ad_gear/ad_unit_variable.rb +5 -0
- data/lib/ad_gear/advertiser.rb +4 -0
- data/lib/ad_gear/base.rb +169 -0
- data/lib/ad_gear/click.rb +4 -0
- data/lib/ad_gear/config.rb +143 -0
- data/lib/ad_gear/core_ext.rb +18 -0
- data/lib/ad_gear/file.rb +4 -0
- data/lib/ad_gear/format.rb +4 -0
- data/lib/ad_gear/has_many_array.rb +84 -0
- data/lib/ad_gear/interaction.rb +4 -0
- data/lib/ad_gear/placement_membership.rb +5 -0
- data/lib/ad_gear/placement_rule.rb +5 -0
- data/lib/ad_gear/publisher.rb +7 -0
- data/lib/ad_gear/site.rb +6 -0
- data/lib/ad_gear/template.rb +4 -0
- data/lib/ad_gear/upload.rb +46 -0
- data/lib/ad_gear/variable.rb +4 -0
- data/lib/ad_gear/web_campaign.rb +6 -0
- data/lib/ad_gear/web_placement.rb +26 -0
- data/lib/ad_gear/xml_format.rb +35 -0
- data/lib/ad_gear.rb +108 -0
- data/test/ad_gear/ad_spot_test.rb +43 -0
- data/test/ad_gear/config_test.rb +114 -0
- data/test/ad_gear/placement_rule_test.rb +22 -0
- data/test/ad_gear/site_test.rb +69 -0
- data/test/ad_gear/upload_test.rb +58 -0
- data/test/ad_gear_test.rb +23 -0
- data/test/fixtures/access-denied-no-auth.txt +13 -0
- data/test/test_helper.rb +33 -0
- metadata +163 -0
data/lib/ad_gear/base.rb
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
module AdGear
|
2
|
+
class Base < ActiveResource::Base
|
3
|
+
def initialize(params={})
|
4
|
+
super({})
|
5
|
+
params.each do |key, value|
|
6
|
+
send("#{key}=", value)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Declares a relationship where we store the ID, but accept and/or return the full object.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# class AdGear::AdSpot < AdGear::Base
|
14
|
+
# belongs_to :format
|
15
|
+
# belongs_to :bookable, :polymorphic => true
|
16
|
+
# belongs_to :web_campaign, :as => :campaign
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# include AdGear
|
20
|
+
# AdSpot.new(:format => Format.find(1), :bookable => Publisher.find(2), :web_campaign => WebCampaign.find(3)).attributes
|
21
|
+
# #=> {"format_id" => 1, "bookable_id" => 2, "bookable_type" => "Publisher", "campaign_id" => 3}
|
22
|
+
def self.belongs_to(*attributes)
|
23
|
+
options = attributes.extract_options!
|
24
|
+
attributes.each do |attribute|
|
25
|
+
attribute_name = attribute.to_s
|
26
|
+
belongs_to_associations[attribute_name] = options.reverse_merge(:as => attribute_name)
|
27
|
+
|
28
|
+
if options[:polymorphic] then
|
29
|
+
polymorphic_belongs_to_attribute_writer_for(attribute_name)
|
30
|
+
polymorphic_belongs_to_attribute_reader_for(attribute_name)
|
31
|
+
else
|
32
|
+
belongs_to_attribute_writer_for(attribute_name)
|
33
|
+
belongs_to_attribute_reader_for(attribute_name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.belongs_to_associations #:nodoc:
|
39
|
+
@belongs_to_associations ||= {}
|
40
|
+
end
|
41
|
+
|
42
|
+
# Defines a managed sub-collection. Elements managed through a #has_many are
|
43
|
+
# ready for use by AttributeFu or ActiveRecord's 2.3 nested_attributes.
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# class AdGear::AdUnit < AdGear::Base
|
47
|
+
# has_many :ad_unit_files, :ad_unit_clicks
|
48
|
+
# has_many :ad_unit_interactions, :ad_unit_variables
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# AdGear::AdUnit.new(:ad_unit_files => [AdUnitFile.new(...)]).to_xml
|
52
|
+
# <ad-unit>
|
53
|
+
# <ad-unit-file-attributes>
|
54
|
+
# <new>
|
55
|
+
# <n0>
|
56
|
+
# ...
|
57
|
+
# </n0>
|
58
|
+
# </new>
|
59
|
+
# <!-- If there were "old" records, they'd be here
|
60
|
+
# </ad-unit-file-attributes>
|
61
|
+
# </ad-unit>
|
62
|
+
def self.has_many(*collections)
|
63
|
+
collections.flatten.compact.each do |collection|
|
64
|
+
collection_name = collection.to_s
|
65
|
+
|
66
|
+
# Remember what collections we are managing, for use in #to_xml
|
67
|
+
has_many_collections << collection_name
|
68
|
+
|
69
|
+
# Define a getter for the collection that transforms a plain Array into
|
70
|
+
# a HasManyArray which knows about new and old records
|
71
|
+
define_method(collection_name) do
|
72
|
+
arr = @attributes[collection_name]
|
73
|
+
return arr if arr.kind_of?(AdGear::HasManyArray)
|
74
|
+
@attributes[collection_name] = AdGear::HasManyArray.new(collection_name.to_sym, AdGear.const_get(collection_name.classify), arr)
|
75
|
+
end
|
76
|
+
|
77
|
+
define_method("#{collection_name}=") do |arr|
|
78
|
+
@attributes[collection_name] = if arr.kind_of?(AdGear::HasManyArray) then
|
79
|
+
arr
|
80
|
+
else
|
81
|
+
AdGear::HasManyArray.new(collection_name.to_sym, AdGear.const_get(collection_name.classify), arr)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.has_many_collections #:nodoc:
|
88
|
+
@has_many_collections ||= []
|
89
|
+
end
|
90
|
+
|
91
|
+
# Defines a list of attributes that should be ignored and not sent back to the server.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# class AdGear::Site < AdGear::Base
|
95
|
+
# ignorable_attributes :embed_code
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# AdGear::Site.find(13).to_xml
|
99
|
+
# <site>
|
100
|
+
# ...
|
101
|
+
# <!-- no embed-code element -->
|
102
|
+
# </site>
|
103
|
+
def self.ignorable_attributes(*attributes)
|
104
|
+
@ignorable_attributes ||= []
|
105
|
+
@ignorable_attributes += attributes.flatten.compact.map {|name| name.to_s}
|
106
|
+
@ignorable_attributes
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_xml(options={})
|
110
|
+
self.class.has_many_collections.each do |name|
|
111
|
+
send(name) # convert the @attributes value into a HasManyArray
|
112
|
+
end
|
113
|
+
|
114
|
+
hash = if self.class.ignorable_attributes.empty?
|
115
|
+
@attributes
|
116
|
+
else
|
117
|
+
returning(@attributes.dup) do |hash|
|
118
|
+
self.class.ignorable_attributes.each do |attr_name|
|
119
|
+
hash.delete(attr_name)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
hash.to_xml({:root => self.class.element_name}.merge(options))
|
124
|
+
end
|
125
|
+
|
126
|
+
# Delegates to #to_xml, as this is the simplest thing that could possibly work
|
127
|
+
def encode(options={}) #:nodoc:
|
128
|
+
self.to_xml
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def self.polymorphic_belongs_to_attribute_writer_for(attribute_name) #:nodoc:
|
134
|
+
define_method("#{attribute_name}=") do |value|
|
135
|
+
@attributes[self.class.belongs_to_attribute_name_for_id(attribute_name)] = value ? value.id : nil
|
136
|
+
@attributes[self.class.belongs_to_attribute_name_for_class(attribute_name)] = value ? value.class.name.demodulize : nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.polymorphic_belongs_to_attribute_reader_for(attribute_name) #:nodoc:
|
141
|
+
define_method(attribute_name) do
|
142
|
+
klass = @attributes[self.class.belongs_to_attribute_name_for_class(attribute_name)]
|
143
|
+
id = @attributes[self.class.belongs_to_attribute_name_for_id(attribute_name)]
|
144
|
+
id.blank? ? nil : AdGear.const_get(AdGear.const_get(klass)).find(id)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.belongs_to_attribute_writer_for(attribute_name) #:nodoc:
|
149
|
+
define_method("#{attribute_name}=") do |value|
|
150
|
+
@attributes[self.class.belongs_to_attribute_name_for_id(attribute_name)] = value ? value.id : nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.belongs_to_attribute_reader_for(attribute_name) #:nodoc:
|
155
|
+
define_method(attribute_name) do
|
156
|
+
id = @attributes[self.class.belongs_to_attribute_name_for_id(attribute_name)]
|
157
|
+
id.blank? ? nil : AdGear.const_get(attribute_name.classify).find(id)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.belongs_to_attribute_name_for_class(association_name)
|
162
|
+
"%s_type" % belongs_to_associations[association_name][:as]
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.belongs_to_attribute_name_for_id(association_name)
|
166
|
+
"%s_id" % belongs_to_associations[association_name][:as]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module AdGear
|
5
|
+
class Config
|
6
|
+
# Raised when the specified environment could not be found in the YAML configuration file.
|
7
|
+
class MissingEnvironmentSpecification < RuntimeError; end
|
8
|
+
|
9
|
+
# Reads in a YAML file containing configuration parameters for the AdGear::Client. The file
|
10
|
+
# can contain sections relating to the environment the client is running within.
|
11
|
+
#
|
12
|
+
# The YAML configuration file *must* use strings as keys.
|
13
|
+
#
|
14
|
+
# @example A sample YAML configuration file:
|
15
|
+
# development:
|
16
|
+
# site: http://localhost:3000/api
|
17
|
+
# user: dummy
|
18
|
+
# password: whatever
|
19
|
+
# logger: <%= Rails.root %>/path/to/log/file.log
|
20
|
+
# production:
|
21
|
+
# site: http://admin.adgear.com/api
|
22
|
+
# user: your_real_login
|
23
|
+
# password: your_real_digest_password
|
24
|
+
# logger: /var/log/ad_gear.log
|
25
|
+
#
|
26
|
+
# @example Reading this sample file:
|
27
|
+
# AdGear.config = AdGear::Config.new("path/to/config/file", "development")
|
28
|
+
#
|
29
|
+
# @example Reading from +config/initializers/ad_gear.rb+ in a Rails environment
|
30
|
+
# AdGear.config = AdGear::Config.new(Rails.root + "config/ad_gear.yml", Rails.env)
|
31
|
+
#
|
32
|
+
# @param obj [#read, Hash, String] When the object responds to #read, reads in the IO-like
|
33
|
+
# object and parses it as a YAML stream. If the object is a
|
34
|
+
# Hash, use it as-is. Else, treat the object as the path to a
|
35
|
+
# YAML file containing the configuration.
|
36
|
+
# @param environment [String, Symbol, nil] If this is +nil+, then do no environment unpacking, else
|
37
|
+
# find the environment that matches this string. If none
|
38
|
+
# matches, a MissingEnvironmentSpecification exception will
|
39
|
+
# be raised.
|
40
|
+
#
|
41
|
+
# @raise MissingEnvironmentSpecification When the +environment+ parameter specifies a missing environment declaration.
|
42
|
+
def initialize(obj={}, environment=nil)
|
43
|
+
@config = Hash.new
|
44
|
+
@logger = nil
|
45
|
+
|
46
|
+
config = if obj.kind_of?(Hash) then
|
47
|
+
obj
|
48
|
+
elsif obj.respond_to?(:read) then
|
49
|
+
YAML.load(ERB.new(obj.read).result)
|
50
|
+
else
|
51
|
+
YAML.load(ERB.new(IO.read(obj)).result)
|
52
|
+
end
|
53
|
+
if environment then
|
54
|
+
raise MissingEnvironmentSpecification, "Could not find #{environment.to_s.inspect} in #{config.inspect} read from #{obj.inspect}. Add the missing environment declaration, or change the parameter to this method." unless config.respond_to?(:has_key?) && config.has_key?(environment.to_s)
|
55
|
+
config = config[environment.to_s]
|
56
|
+
end
|
57
|
+
|
58
|
+
config.each_pair do |key, value|
|
59
|
+
send("#{key}=", value)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a Hash of the currect configuration.
|
64
|
+
def to_hash
|
65
|
+
@config.dup
|
66
|
+
end
|
67
|
+
|
68
|
+
def site=(value)
|
69
|
+
@config["site"] = value
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the configured site, which is the root of the API.
|
73
|
+
def site
|
74
|
+
@config["site"]
|
75
|
+
end
|
76
|
+
|
77
|
+
def user=(value)
|
78
|
+
@config["user"] = value
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the configured username / login.
|
82
|
+
def user
|
83
|
+
@config["user"]
|
84
|
+
end
|
85
|
+
|
86
|
+
def password=(value)
|
87
|
+
@config["password"] = value
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the configured password.
|
91
|
+
def password
|
92
|
+
@config["password"]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Denies or allows using Basic authentication method. This will have an effect only if you are using http://github.com/francois/rails/ar_basic/activeresource
|
96
|
+
def use_basic_authentication
|
97
|
+
@config["use_basic_authentication"]
|
98
|
+
end
|
99
|
+
|
100
|
+
def use_basic_authentication=(value)
|
101
|
+
@config["use_basic_authentication"] = value
|
102
|
+
end
|
103
|
+
|
104
|
+
# Denies or allows using Digest authentication method. This will have an effect only if you are using http://github.com/francois/rails/ar_digest/activeresource
|
105
|
+
def use_digest_authentication
|
106
|
+
@config["use_digest_authentication"]
|
107
|
+
end
|
108
|
+
|
109
|
+
def use_digest_authentication=(value)
|
110
|
+
@config["use_digest_authentication"] = value
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns a format object suitable for use by ActiveResource.
|
114
|
+
def format
|
115
|
+
AdGear::XmlFormat
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the Logger instance that was configured in #logger=.
|
119
|
+
def logger
|
120
|
+
@logger
|
121
|
+
end
|
122
|
+
|
123
|
+
# Register a Logger for use by AdGear's client.
|
124
|
+
# @param value [nil, String] When nil, no Logger is assigned.
|
125
|
+
# When the values STDERR or STDOUT are used, logs to the specified stream.
|
126
|
+
# Else, it is taken as the path to a log file.
|
127
|
+
def logger=(value)
|
128
|
+
@config["logger"] = value
|
129
|
+
@logger = case value
|
130
|
+
when nil, ""
|
131
|
+
# No logger
|
132
|
+
nil
|
133
|
+
when /^std(err|out)$/i
|
134
|
+
# Existing stream
|
135
|
+
Logger.new(Object.const_get(value.upcase))
|
136
|
+
else
|
137
|
+
warn("WARNING: path to logger in AdGear::Client configuration is relative: CWD is #{Dir.pwd.inspect}") unless Pathname.new(value).absolute?
|
138
|
+
# Path to a file
|
139
|
+
Logger.new(value)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# ActiveSupport's Array#to_xml will build an XML with root set to the full class name including
|
3
|
+
# module(s) it may be nested in - we don't want that, we want it to be *just* the class name,
|
4
|
+
# make it so with this monkey patch...
|
5
|
+
#
|
6
|
+
# Note this is loaded last from AdGear.config= after ActiveSupport has already been loaded. We
|
7
|
+
# cannot simply override because ActiveSupport does lazy loading.
|
8
|
+
#
|
9
|
+
class Array
|
10
|
+
def to_xml_with_module_prefixing(options = {})
|
11
|
+
raise "Not all elements respond to to_xml in #{self.inspect}" unless all? { |e| e.respond_to? :to_xml }
|
12
|
+
root = all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ?
|
13
|
+
ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.to_s.split("::").last)) : "records"
|
14
|
+
to_xml_without_module_prefixing({ :root => root }.merge(options))
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method_chain :to_xml, :module_prefixing
|
18
|
+
end
|
data/lib/ad_gear/file.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module AdGear
|
2
|
+
class HasManyArray
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(name, klass, saved=nil)
|
6
|
+
@name = name.to_s.singularize.dasherize + "-attributes"
|
7
|
+
@klass = klass
|
8
|
+
@new = []
|
9
|
+
@saved = []
|
10
|
+
saved.each do |obj|
|
11
|
+
self << obj
|
12
|
+
end if saved
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(object)
|
16
|
+
root = if object.kind_of?(Hash) then
|
17
|
+
if object.has_key?("id") || object.has_key?(:id) then
|
18
|
+
object = @klass.new(object)
|
19
|
+
@saved
|
20
|
+
else
|
21
|
+
# Instantiate an object from the Hash, which will obviously be a new object
|
22
|
+
object = @klass.new(object)
|
23
|
+
@new
|
24
|
+
end
|
25
|
+
elsif object.respond_to?(:new_record?) && object.new_record?
|
26
|
+
@new
|
27
|
+
else
|
28
|
+
@saved
|
29
|
+
end
|
30
|
+
root << object
|
31
|
+
end
|
32
|
+
|
33
|
+
def each(&block)
|
34
|
+
@saved.each(&block)
|
35
|
+
@new.each(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def length
|
39
|
+
@saved.length + @new.length
|
40
|
+
end
|
41
|
+
|
42
|
+
alias_method :size, :length
|
43
|
+
|
44
|
+
def empty?
|
45
|
+
combined.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](*args)
|
49
|
+
combined[*args]
|
50
|
+
end
|
51
|
+
|
52
|
+
def first
|
53
|
+
combined.first
|
54
|
+
end
|
55
|
+
|
56
|
+
def last
|
57
|
+
combined.last
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_xml(options={})
|
61
|
+
xml = options[:builder] || Builder::XmlMarkup.new
|
62
|
+
xml.__send__(@name) do
|
63
|
+
unless @new.empty?
|
64
|
+
xml.tag!(:new) do
|
65
|
+
@new.each_with_index do |obj, index|
|
66
|
+
obj.to_xml(options.merge(:root => "n" << index.to_s))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
unless @saved.empty?
|
72
|
+
@saved.each do |obj|
|
73
|
+
obj.to_xml(options.merge(:root => "n" << obj.id.to_s))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def combined
|
81
|
+
@new + @saved
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/ad_gear/site.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
begin
|
2
|
+
gem "francois-rest-client", ">= 1.1.5"
|
3
|
+
rescue
|
4
|
+
# Ignored. We hope we got what we wanted
|
5
|
+
warn "Could not ensure francois-rest-client 1.1.5 is loaded. If you have problems uploading files, make sure you have that gem installed and that the AdGear::Client uses that version."
|
6
|
+
end
|
7
|
+
require "restclient"
|
8
|
+
|
9
|
+
module AdGear
|
10
|
+
class Upload < AdGear::Base
|
11
|
+
def save
|
12
|
+
retried = false
|
13
|
+
headers = Hash.new
|
14
|
+
begin
|
15
|
+
uri = self.class.site.merge(URI.parse(self.class.collection_path))
|
16
|
+
params = {"upload[uploaded_data]" => ::File.new(attributes["filename"]), "upload[filename]" => ::File.basename(attributes["filename"])}
|
17
|
+
out = RestClient.post(uri.to_s, params, headers)
|
18
|
+
logger.debug out if logger
|
19
|
+
load(connection.format.decode(out))
|
20
|
+
rescue RestClient::Unauthorized => e
|
21
|
+
# Retry once only
|
22
|
+
raise if retried
|
23
|
+
|
24
|
+
www_authenticate = e.response["www-authenticate"]
|
25
|
+
header = ActiveResource::Digest.authenticate(uri, self.class.user, self.class.password, www_authenticate, :post)
|
26
|
+
headers["Authorization"] = header
|
27
|
+
|
28
|
+
retried = true
|
29
|
+
retry
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_attribute(*args)
|
34
|
+
raise_unsupported_operation
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_attributes(*args)
|
38
|
+
raise_unsupported_operation
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
def raise_unsupported_operation
|
43
|
+
raise UnsupportedOperation, "Unsupported operation: uploads are write-once only"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module AdGear
|
2
|
+
class WebPlacement < AdGear::Base
|
3
|
+
belongs_to :campaign, :format
|
4
|
+
has_many :placement_rules, :placement_memberships
|
5
|
+
|
6
|
+
def selection_mechanism
|
7
|
+
case value = attributes["selection_mechanism"]
|
8
|
+
when "R" ; "rotated"
|
9
|
+
when "W" ; "weighted"
|
10
|
+
else ; value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def selection_mechanism=(value)
|
15
|
+
case value
|
16
|
+
when /^r/i
|
17
|
+
attributes["selection_mechanism"] = "R"
|
18
|
+
when /^w/i
|
19
|
+
attributes["selection_mechanism"] = "W"
|
20
|
+
else
|
21
|
+
# Assign whatever we received, and hope for the best
|
22
|
+
attributes["selection_mechanism"] = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support/core_ext/hash/conversions'
|
2
|
+
|
3
|
+
module AdGear
|
4
|
+
# Copied from ActiveResource's +active_resource/formats/xml_format.rb+. Changed +mime_type+ to return AdGear's expected mime type.
|
5
|
+
module XmlFormat
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def extension
|
9
|
+
"agml"
|
10
|
+
end
|
11
|
+
|
12
|
+
def mime_type
|
13
|
+
"application/vnd.bloom.adgear.v1+xml"
|
14
|
+
end
|
15
|
+
|
16
|
+
def encode(hash, options={})
|
17
|
+
hash.to_xml(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def decode(xml)
|
21
|
+
from_xml_data(Hash.from_xml(xml))
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
# Manipulate from_xml Hash, because xml_simple is not exactly what we
|
26
|
+
# want for Active Resource.
|
27
|
+
def from_xml_data(data)
|
28
|
+
if data.is_a?(Hash) && data.keys.size == 1
|
29
|
+
data.values.first
|
30
|
+
else
|
31
|
+
data
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|