bsm-openx 1.9.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.
- data/.gitignore +3 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +22 -0
- data/README.txt +85 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/bsm-openx.gemspec +92 -0
- data/lib/openx.rb +41 -0
- data/lib/openx/image.rb +22 -0
- data/lib/openx/invocation.rb +62 -0
- data/lib/openx/services.rb +32 -0
- data/lib/openx/services/advertiser.rb +31 -0
- data/lib/openx/services/agency.rb +34 -0
- data/lib/openx/services/banner.rb +94 -0
- data/lib/openx/services/base.rb +97 -0
- data/lib/openx/services/campaign.rb +52 -0
- data/lib/openx/services/channel.rb +42 -0
- data/lib/openx/services/persistance.rb +64 -0
- data/lib/openx/services/publisher.rb +36 -0
- data/lib/openx/services/session.rb +44 -0
- data/lib/openx/services/targeting_rule.rb +125 -0
- data/lib/openx/services/targeting_rules.rb +26 -0
- data/lib/openx/services/zone.rb +82 -0
- data/lib/openx/xmlrpc_client.rb +72 -0
- data/test/assets/300x250.jpg +0 -0
- data/test/assets/cat.swf +0 -0
- data/test/helper.rb +131 -0
- data/test/test_advertiser.rb +82 -0
- data/test/test_agency.rb +94 -0
- data/test/test_banner.rb +85 -0
- data/test/test_base.rb +40 -0
- data/test/test_campaign.rb +64 -0
- data/test/test_channel.rb +65 -0
- data/test/test_openx.rb +17 -0
- data/test/test_publisher.rb +69 -0
- data/test/test_services.rb +16 -0
- data/test/test_session.rb +42 -0
- data/test/test_targeting_rule.rb +86 -0
- data/test/test_targeting_rules.rb +13 -0
- data/test/test_zone.rb +101 -0
- metadata +122 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
module OpenX
|
2
|
+
module Services
|
3
|
+
|
4
|
+
class TargetingRule < Hash
|
5
|
+
TYPES = {
|
6
|
+
'Client' => %w(Browser Domain Ip Language Os Useragent),
|
7
|
+
'Geo' => %w(Areacode City Continent Country Dma Latlong Netspeed Organisation Postalcode Region),
|
8
|
+
'Site' => %w(Channel Pageurl Referingpage Source Variable),
|
9
|
+
'Time' => %w(Date Day Hour)
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
DATA_KEYS = ['comparison', 'data', 'logical'].freeze
|
13
|
+
ALL_KEYS = (DATA_KEYS + ['type']).freeze
|
14
|
+
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
def self.instantiate(hash)
|
18
|
+
rule = new(hash['type'])
|
19
|
+
DATA_KEYS.each {|k| rule.update k => hash[k] }
|
20
|
+
rule
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(type)
|
24
|
+
super().update('type' => verify_type(type), 'logical' => 'and')
|
25
|
+
end
|
26
|
+
|
27
|
+
def equal?(value)
|
28
|
+
update 'comparison' => '==', 'data' => convert(value)
|
29
|
+
end
|
30
|
+
alias_method :eq?, :equal?
|
31
|
+
alias_method :is?, :equal?
|
32
|
+
alias_method :==, :equal?
|
33
|
+
|
34
|
+
def not_equal?(value)
|
35
|
+
update 'comparison' => '!=', 'data' => convert(value)
|
36
|
+
end
|
37
|
+
alias_method :ne?, :not_equal?
|
38
|
+
alias_method :not?, :not_equal?
|
39
|
+
|
40
|
+
def lt?(value)
|
41
|
+
update 'comparison' => '<', 'data' => convert(value)
|
42
|
+
end
|
43
|
+
alias_method :<, :lt?
|
44
|
+
|
45
|
+
def gt?(value)
|
46
|
+
update 'comparison' => '>', 'data' => convert(value)
|
47
|
+
end
|
48
|
+
alias_method :>, :gt?
|
49
|
+
|
50
|
+
def lte?(value)
|
51
|
+
update 'comparison' => '<=', 'data' => convert(value)
|
52
|
+
end
|
53
|
+
alias_method :<=, :lte?
|
54
|
+
|
55
|
+
def gte?(value)
|
56
|
+
update 'comparison' => '>=', 'data' => convert(value)
|
57
|
+
end
|
58
|
+
alias_method :>=, :gte?
|
59
|
+
|
60
|
+
def match?(value)
|
61
|
+
update 'comparison' => '=x', 'data' => convert(value)
|
62
|
+
end
|
63
|
+
alias_method :=~, :match?
|
64
|
+
|
65
|
+
def &(other)
|
66
|
+
other.logical('and')
|
67
|
+
end
|
68
|
+
|
69
|
+
def |(other)
|
70
|
+
other.logical('or')
|
71
|
+
end
|
72
|
+
|
73
|
+
def no_match?(value)
|
74
|
+
update 'comparison' => '!x', 'data' => convert(value)
|
75
|
+
end
|
76
|
+
|
77
|
+
def include?(*value)
|
78
|
+
update 'comparison' => '=~', 'data' => convert(value)
|
79
|
+
end
|
80
|
+
alias_method :contains?, :include?
|
81
|
+
|
82
|
+
def exclude?(*value)
|
83
|
+
update 'comparison' => '!~', 'data' => convert(value)
|
84
|
+
end
|
85
|
+
alias_method :does_not_contain?, :exclude?
|
86
|
+
|
87
|
+
def with(value)
|
88
|
+
update 'data' => convert(value)
|
89
|
+
end
|
90
|
+
|
91
|
+
def compare(value)
|
92
|
+
update 'comparison' => value
|
93
|
+
end
|
94
|
+
|
95
|
+
def logical(value)
|
96
|
+
value = value.to_s
|
97
|
+
update 'logical' => (value == 'or' ? value : 'and')
|
98
|
+
end
|
99
|
+
|
100
|
+
def complete?
|
101
|
+
values_at(*ALL_KEYS).all? {|v| !v.nil? }
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def verify_type(type)
|
107
|
+
group, component = type.to_s.split(':').last(2)
|
108
|
+
raise "Invalid type '#{self['type']}'" unless TYPES.key?(group) && TYPES[group].include?(component)
|
109
|
+
"#{group}:#{component}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def convert(value)
|
113
|
+
case value
|
114
|
+
when Array
|
115
|
+
value.map {|v| convert(v) }.join(',')
|
116
|
+
when Regexp
|
117
|
+
value.source
|
118
|
+
else
|
119
|
+
value.to_s
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module OpenX
|
2
|
+
module Services
|
3
|
+
|
4
|
+
# Create targeting rule sets. Example:
|
5
|
+
#
|
6
|
+
# rules = OpenX::Services::TargetingRules.new do |t|
|
7
|
+
# t['Site:Pageurl'].include?('test') &
|
8
|
+
# t['Client:Ip'].match?(/^127\./) |
|
9
|
+
# t['Geo:Country'].include?('GB', 'US')
|
10
|
+
# end
|
11
|
+
class TargetingRules < Array
|
12
|
+
|
13
|
+
def initialize(&block)
|
14
|
+
super([])
|
15
|
+
block.call(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](key)
|
19
|
+
rule = TargetingRule.new(key)
|
20
|
+
push(rule)
|
21
|
+
rule
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module OpenX
|
2
|
+
module Services
|
3
|
+
class Zone < Base
|
4
|
+
# Delivery types
|
5
|
+
BANNER = 'delivery-b'
|
6
|
+
INTERSTITIAL = 'delivery-i'
|
7
|
+
TEXT = 'delivery-t'
|
8
|
+
EMAIL = 'delivery-e'
|
9
|
+
|
10
|
+
# Tag Types
|
11
|
+
JAVASCRIPT = 'adjs'
|
12
|
+
LOCAL = 'local'
|
13
|
+
IFRAME = 'adframe'
|
14
|
+
XMLRPC_TAG = 'xmlrpc'
|
15
|
+
|
16
|
+
openx_accessor :name => :zoneName,
|
17
|
+
:id => :zoneId,
|
18
|
+
:width => :width,
|
19
|
+
:height => :height,
|
20
|
+
:type => :type,
|
21
|
+
:publisher_id => :publisherId
|
22
|
+
|
23
|
+
has_one :publisher
|
24
|
+
|
25
|
+
self.create = 'ox.addZone'
|
26
|
+
self.update = 'ox.modifyZone'
|
27
|
+
self.delete = 'ox.deleteZone'
|
28
|
+
self.find_one = 'ox.getZone'
|
29
|
+
self.find_all = 'ox.getZoneListByPublisherId'
|
30
|
+
|
31
|
+
class << self
|
32
|
+
|
33
|
+
# Deliver +zone_id+ to +ip_address+ with +cookies+,
|
34
|
+
def deliver(zone_id, options = {})
|
35
|
+
options = { 'ip_address' => '192.168.1.1', 'cookies' => [] }.update(options)
|
36
|
+
server = XmlrpcClient.new("#{connection.host}/delivery/axmlrpc.php")
|
37
|
+
server.call('openads.view', options, "zone:#{zone_id}", 0, '', '', true, [])
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(params = {})
|
43
|
+
raise "need publisher" unless params[:publisher_id] || params[:publisher]
|
44
|
+
params[:publisher_id] ||= params[:publisher].id
|
45
|
+
super(params)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Link this zone to +campaign+
|
49
|
+
def link_campaign(campaign)
|
50
|
+
raise "Zone must be saved" if new_record?
|
51
|
+
raise ArgumentError.new("Campaign must be saved")if campaign.new_record?
|
52
|
+
remote.call("ox.linkCampaign", self.id, campaign.id)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Unlink this zone from +campaign+
|
56
|
+
def unlink_campaign(campaign)
|
57
|
+
raise "Zone must be saved" if new_record?
|
58
|
+
raise ArgumentError.new("Campaign must be saved")if campaign.new_record?
|
59
|
+
remote.call("ox.unlinkCampaign", self.id, campaign.id)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Link this zone to +banner+
|
63
|
+
def link_banner(banner)
|
64
|
+
raise "Zone must be saved" if new_record?
|
65
|
+
raise ArgumentError.new("Banner must be saved")if banner.new_record?
|
66
|
+
remote.call("ox.linkBanner", self.id, banner.id)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Unlink this zone from +banner+
|
70
|
+
def unlink_banner(banner)
|
71
|
+
raise "Zone must be saved" if new_record?
|
72
|
+
raise ArgumentError.new("Banner must be saved")if banner.new_record?
|
73
|
+
remote.call("ox.unlinkBanner", self.id, banner.id)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Generate tags for displaying this zone using +tag_type+
|
77
|
+
def generate_tags(tag_type = IFRAME)
|
78
|
+
remote.call("ox.generateTags", self.id, tag_type, [])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'xmlrpc/client'
|
2
|
+
|
3
|
+
module OpenX
|
4
|
+
|
5
|
+
unless defined? HTTPBroken
|
6
|
+
# A module that captures all the possible Net::HTTP exceptions
|
7
|
+
# from http://pastie.org/pastes/145154
|
8
|
+
module HTTPBroken; end
|
9
|
+
[
|
10
|
+
Timeout::Error, Errno::EINVAL, Errno::EPIPE,
|
11
|
+
Errno::ECONNRESET, EOFError, Net::HTTPBadResponse,
|
12
|
+
Net::HTTPHeaderSyntaxError, Net::ProtocolError
|
13
|
+
].each {|m| m.send(:include, HTTPBroken) }
|
14
|
+
end
|
15
|
+
|
16
|
+
class XmlrpcClient
|
17
|
+
attr_reader :client, :url
|
18
|
+
|
19
|
+
def initialize(url)
|
20
|
+
@url = url
|
21
|
+
init_client!
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(method, *args)
|
25
|
+
@client.call(method, *convert(args))
|
26
|
+
rescue HTTPBroken
|
27
|
+
raise unless OpenX.configuration['retry']
|
28
|
+
init_client!
|
29
|
+
retry
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def convert(args)
|
35
|
+
args
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def init_client!
|
41
|
+
@client = XMLRPC::Client.new2(url)
|
42
|
+
@client.timeout = OpenX.configuration['timeout']
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
class XmlrpcSessionClient < XmlrpcClient
|
49
|
+
|
50
|
+
attr_reader :session
|
51
|
+
|
52
|
+
def initialize(session)
|
53
|
+
@session = session
|
54
|
+
super(session.url)
|
55
|
+
end
|
56
|
+
|
57
|
+
def call(method, *args)
|
58
|
+
super
|
59
|
+
rescue XMLRPC::FaultException => error
|
60
|
+
raise unless error.message =~ /Session ID.*invalid/i
|
61
|
+
session.recreate!
|
62
|
+
retry
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def convert(args)
|
68
|
+
[session.id] + args
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
Binary file
|
data/test/assets/cat.swf
ADDED
Binary file
|
data/test/helper.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../lib')))
|
2
|
+
|
3
|
+
ENV['OPENX_ENV'] = 'test'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'bundler'
|
7
|
+
|
8
|
+
Bundler.setup
|
9
|
+
Bundler.require :test
|
10
|
+
|
11
|
+
require 'test/unit'
|
12
|
+
require 'openx'
|
13
|
+
|
14
|
+
module OpenX
|
15
|
+
class TestCase < ActiveSupport::TestCase
|
16
|
+
TEST_SWF = File.expand_path(File.join(File.dirname(__FILE__), 'assets', 'cat.swf'))
|
17
|
+
TEST_JPG = File.expand_path(File.join(File.dirname(__FILE__), 'assets', '300x250.jpg'))
|
18
|
+
|
19
|
+
Base = OpenX::Services::Base
|
20
|
+
Banner = OpenX::Services::Banner
|
21
|
+
Zone = OpenX::Services::Zone
|
22
|
+
Publisher = OpenX::Services::Publisher
|
23
|
+
Campaign = OpenX::Services::Campaign
|
24
|
+
Agency = OpenX::Services::Agency
|
25
|
+
Advertiser = OpenX::Services::Advertiser
|
26
|
+
Session = OpenX::Services::Session
|
27
|
+
Channel = OpenX::Services::Channel
|
28
|
+
TargetingRule = OpenX::Services::TargetingRule
|
29
|
+
TargetingRules = OpenX::Services::TargetingRules
|
30
|
+
|
31
|
+
undef :default_test
|
32
|
+
|
33
|
+
def setup
|
34
|
+
raise 'Please create the credentials file' unless File.exists?(OpenX.config_file)
|
35
|
+
end
|
36
|
+
|
37
|
+
def agency
|
38
|
+
@agency ||= Agency.create!({
|
39
|
+
:name => "Testing! - #{Time.now}",
|
40
|
+
:contact_name => 'Contact Name!',
|
41
|
+
:email => 'foo@bar.com'
|
42
|
+
})
|
43
|
+
end
|
44
|
+
|
45
|
+
def advertiser
|
46
|
+
@advertiser ||= Advertiser.create!(
|
47
|
+
{
|
48
|
+
:name => "adv-#{Time.now}",
|
49
|
+
:contact_name => 'Contact Name!',
|
50
|
+
:email => 'foo@bar.com'
|
51
|
+
}.merge(:agency => agency)
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def campaign
|
56
|
+
@campaign ||= Campaign.create!(
|
57
|
+
{
|
58
|
+
:advertiser => advertiser,
|
59
|
+
:name => "Campaign-#{Time.now}",
|
60
|
+
:impressions => 2000
|
61
|
+
}
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def publisher
|
66
|
+
@publisher ||= Publisher.create!(
|
67
|
+
{
|
68
|
+
:agency => agency,
|
69
|
+
:name => "Publisher! - #{Time.now}",
|
70
|
+
:contact_name => 'Aaron Patterson',
|
71
|
+
:email => 'aaron@tenderlovemaking.com',
|
72
|
+
:username => 'one',
|
73
|
+
:password => 'two',
|
74
|
+
}
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def zone
|
79
|
+
@zone ||= Zone.create!(
|
80
|
+
{
|
81
|
+
:publisher => publisher,
|
82
|
+
:name => "Zone - #{Time.now}",
|
83
|
+
:type => Zone::BANNER,
|
84
|
+
:width => 300,
|
85
|
+
:height => 250,
|
86
|
+
}
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
def channel
|
91
|
+
@channel ||= Channel.create!(
|
92
|
+
{
|
93
|
+
:publisher => publisher,
|
94
|
+
:name => "Channel - #{Time.now}",
|
95
|
+
:comments => 'Random Comments',
|
96
|
+
:description => 'Random Description'
|
97
|
+
}
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def banner
|
102
|
+
@banner ||= Banner.create!({
|
103
|
+
:name => "Banner-#{Time.now}",
|
104
|
+
:storage_type => Banner::LOCAL_SQL,
|
105
|
+
:campaign => campaign,
|
106
|
+
:url => 'http://tenderlovemaking.com/',
|
107
|
+
:file_name => 'oogabooga',
|
108
|
+
:image => OpenX::Image.new(File.open(TEST_SWF, 'rb'))
|
109
|
+
})
|
110
|
+
end
|
111
|
+
|
112
|
+
def targeting_rules
|
113
|
+
@targeting_rules ||= OpenX::Services::TargetingRules.new do |t|
|
114
|
+
t['Site:Pageurl'].include?('test') &
|
115
|
+
t['Client:Ip'].match?(/^127\./) |
|
116
|
+
t['Geo:Country'].include?('GB', 'US')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def destroy
|
121
|
+
@banner.destroy if defined? @banner
|
122
|
+
@zone.destroy if defined? @zone
|
123
|
+
@publisher.destroy if defined? @publisher
|
124
|
+
@campaign.destroy if defined? @campaign
|
125
|
+
@advertiser.destroy if defined? @advertiser
|
126
|
+
@agency.destroy if defined? @agency
|
127
|
+
@channel.destroy if defined? @channel
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class AdvertiserTest < OpenX::TestCase
|
4
|
+
def test_create_advertiser
|
5
|
+
advertiser = nil
|
6
|
+
assert_nothing_raised {
|
7
|
+
advertiser = Advertiser.create!(init_params.merge({
|
8
|
+
:agency => agency,
|
9
|
+
}))
|
10
|
+
}
|
11
|
+
assert_not_nil advertiser
|
12
|
+
init_params.each do |k,v|
|
13
|
+
assert_equal(v, advertiser.send(:"#{k}"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_destroy
|
18
|
+
a = nil
|
19
|
+
assert_nothing_raised {
|
20
|
+
a = Advertiser.create!(init_params.merge({:agency => agency}))
|
21
|
+
}
|
22
|
+
id = a.id
|
23
|
+
assert_nothing_raised {
|
24
|
+
a.destroy
|
25
|
+
}
|
26
|
+
assert_raises(XMLRPC::FaultException) {
|
27
|
+
Advertiser.find(id)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_create_advertiser_with_agency
|
32
|
+
advertiser = nil
|
33
|
+
assert_nothing_raised {
|
34
|
+
advertiser = agency.create_advertiser!(init_params)
|
35
|
+
}
|
36
|
+
assert_not_nil advertiser
|
37
|
+
init_params.each do |k,v|
|
38
|
+
assert_equal(v, advertiser.send(:"#{k}"))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_find_advertiser
|
43
|
+
advertiser = nil
|
44
|
+
assert_nothing_raised {
|
45
|
+
advertiser = agency.create_advertiser!(init_params)
|
46
|
+
}
|
47
|
+
assert_not_nil advertiser
|
48
|
+
|
49
|
+
original = advertiser
|
50
|
+
advertiser = Advertiser.find(advertiser.id)
|
51
|
+
assert_equal(original, advertiser)
|
52
|
+
assert_not_nil advertiser
|
53
|
+
assert_not_nil advertiser.agency
|
54
|
+
init_params.each do |k,v|
|
55
|
+
assert_equal(v, advertiser.send(:"#{k}"))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_find_all_advertisers
|
60
|
+
advertiser = nil
|
61
|
+
assert_nothing_raised {
|
62
|
+
advertiser = agency.create_advertiser!(init_params)
|
63
|
+
}
|
64
|
+
assert_not_nil advertiser
|
65
|
+
|
66
|
+
advertisers = Advertiser.find(:all, agency.id)
|
67
|
+
|
68
|
+
advertiser = advertisers.find { |a| a.id == advertiser.id }
|
69
|
+
assert_not_nil advertiser
|
70
|
+
init_params.each do |k,v|
|
71
|
+
assert_equal(v, advertiser.send(:"#{k}"))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def init_params
|
76
|
+
{
|
77
|
+
:name => 'Test advertiser',
|
78
|
+
:contact_name => 'Contact name',
|
79
|
+
:email => 'email@example.com',
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|