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