jjp-openx 1.1.4

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.
@@ -0,0 +1,134 @@
1
+ require 'yaml'
2
+
3
+ module OpenX
4
+ module Services
5
+ class Base
6
+ include Comparable
7
+
8
+ # HOME || HOMEPATH is for Win32 users who do not have HOME set by default
9
+ #
10
+ # Configuration can be overridden for Rails at load time by doing this:
11
+ # OpenX::Services::Base.configuration =
12
+ # YAML.load_file(File.join(Rails.root, 'config', 'credentials.yml'))[Rails.env]
13
+ #
14
+ # Rescue nil is there for Rails sites that are monitored by Monit,
15
+ # which does not set the environment variables as expected.
16
+ # Note that the configuration can be set explicitly (as above),
17
+ # in which case this constant is not used and can safely be nil.
18
+ CONFIGURATION_YAML = File.join((ENV['HOME'] || ENV['HOMEPATH']), '.openx', 'credentials.yml') rescue nil
19
+
20
+ @@connection = nil
21
+ @@configuration = nil
22
+
23
+ class << self
24
+ attr_accessor :translations
25
+ attr_accessor :create, :update, :delete, :find_one, :find_all
26
+
27
+ def configuration
28
+ @@configuration ||=
29
+ YAML.load_file(CONFIGURATION_YAML)[ENV['OPENX_ENV'] || 'production']
30
+ end
31
+
32
+ def configuration=(c); @@configuration = c; end
33
+
34
+ def connection= c
35
+ @@connection = c
36
+ end
37
+
38
+ def connection
39
+ unless @@connection
40
+ @@connection = Session.new(configuration['url'])
41
+ @@connection.create( configuration['username'],
42
+ configuration['password']
43
+ )
44
+ end
45
+ @@connection
46
+ end
47
+
48
+ def create!(params = {})
49
+ new(params).save!
50
+ end
51
+
52
+ def openx_accessor(accessor_map)
53
+ @translations ||= {}
54
+ @translations = accessor_map.merge(@translations)
55
+ accessor_map.each do |ruby,openx|
56
+ attr_accessor :"#{ruby}"
57
+ end
58
+ end
59
+
60
+ def has_one(*things)
61
+ things.each do |thing|
62
+ attr_writer :"#{thing}"
63
+ define_method(:"#{thing}") do
64
+ klass = thing.to_s.capitalize.gsub(/_[a-z]/) { |m| m[1].chr.upcase }
65
+ klass = OpenX::Services.const_get(:"#{klass}")
66
+ klass.find(send("#{thing}_id"))
67
+ end
68
+ end
69
+ end
70
+
71
+ def find(id, *args)
72
+ session = self.connection
73
+ server = XmlrpcClient.new2("#{session.url}")
74
+ if id == :all
75
+ responses = server.call(find_all(), session, *args)
76
+ responses.map { |response|
77
+ new(translate(response))
78
+ }
79
+ else
80
+ response = server.call(find_one(), session, id)
81
+ new(translate(response))
82
+ end
83
+ end
84
+
85
+ def destroy(id)
86
+ new(:id => id).destroy
87
+ end
88
+
89
+ private
90
+ def translate(response)
91
+ params = {}
92
+ self.translations.each { |k,v|
93
+ params[k] = response[v.to_s] if response[v.to_s]
94
+ }
95
+ params
96
+ end
97
+ end
98
+
99
+ def initialize(params = {})
100
+ @id = nil
101
+ params.each { |k,v| send(:"#{k}=", v) }
102
+ @server = XmlrpcClient.new2("#{self.class.connection.url}")
103
+ end
104
+
105
+ def new_record?; @id.nil?; end
106
+
107
+ def save!
108
+ params = {}
109
+ session = self.class.connection
110
+ self.class.translations.keys.each { |k|
111
+ value = send(:"#{k}")
112
+ params[self.class.translations[k].to_s] = value if value
113
+ }
114
+
115
+ if new_record?
116
+ @id = @server.call(self.class.create, session, params)
117
+ else
118
+ @server.call(self.class.update, session, params)
119
+ end
120
+ self
121
+ end
122
+
123
+ def destroy
124
+ session = self.class.connection
125
+ @server.call(self.class.delete, session, id)
126
+ @id = nil
127
+ end
128
+
129
+ def <=>(other)
130
+ self.id <=> other.id
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,51 @@
1
+ module OpenX
2
+ module Services
3
+ class Campaign < Base
4
+ # Translate our property names to OpenX property names
5
+ openx_accessor :name => :campaignName,
6
+ :advertiser_id => :advertiserId,
7
+ :id => :campaignId,
8
+ :start_date => :startDate,
9
+ :end_date => :endDate,
10
+ :impressions => :impressions,
11
+ :target_impressions => :targetImpressions,
12
+ :target_clicks => :targetClicks,
13
+ :revenue => :revenue,
14
+ :revenue_type => :revenueType,
15
+ :impressions => :impressions,
16
+ :clicks => :clicks,
17
+ :priority => :priority,
18
+ :weight => :weight,
19
+ :campaign_type => :campaignType
20
+
21
+ has_one :advertiser
22
+
23
+ self.create = 'ox.addCampaign'
24
+ self.update = 'ox.modifyCampaign'
25
+ self.delete = 'ox.deleteCampaign'
26
+ self.find_one = 'ox.getCampaign'
27
+ self.find_all = 'ox.getCampaignListByAdvertiserId'
28
+
29
+ # Revenue types
30
+ CPM = 1
31
+ CPC = 2
32
+ CPA = 3
33
+ MONTHLY_TENANCY = 4
34
+
35
+ # Campaign types
36
+ REMNANT = 1
37
+ HIGH = 2
38
+ EXCLUSIVE = 3
39
+
40
+ def initialize(params = {})
41
+ raise ArgumentError.new("Missing advertiser_id") unless params[:advertiser_id] || params[:advertiser]
42
+ params[:advertiser_id] ||= params[:advertiser].id
43
+ super(params)
44
+ end
45
+
46
+ def banners
47
+ Banner.find(:all, self.id)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,31 @@
1
+ module OpenX
2
+ module Services
3
+ class Publisher < Base
4
+ openx_accessor :name => :publisherName,
5
+ :contact_name => :contactName,
6
+ :email => :emailAddress,
7
+ :username => :username,
8
+ :password => :password,
9
+ :id => :publisherId,
10
+ :agency_id => :agencyId
11
+
12
+ has_one :agency
13
+
14
+ self.create = 'ox.addPublisher'
15
+ self.update = 'ox.modifyPublisher'
16
+ self.delete = 'ox.deletePublisher'
17
+ self.find_one = 'ox.getPublisher'
18
+ self.find_all = 'ox.getPublisherListByAgencyId'
19
+
20
+ def initialize(params = {})
21
+ raise "need agency" unless params[:agency_id] || params[:agency]
22
+ params[:agency_id] ||= params[:agency].id
23
+ super(params)
24
+ end
25
+
26
+ def zones
27
+ Zone.find(:all, self.id)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ module OpenX
2
+ module Services
3
+ class Session
4
+ attr_accessor :url, :id
5
+ attr_accessor :user, :password
6
+
7
+ def initialize(url)
8
+ @url = url
9
+ @server = XmlrpcClient.new2("#{@url}")
10
+ @id = nil
11
+ end
12
+
13
+ def create(user, password)
14
+ @user = user
15
+ @password = password
16
+ @id = @server.call('ox.logon', @user, @password)
17
+ self
18
+ end
19
+
20
+ def recreate!
21
+ raise "Unable to refresh Session" unless @user && @password
22
+ @id = @server.call('ox.logon', @user, @password)
23
+ self
24
+ end
25
+
26
+ def destroy
27
+ @server.call('ox.logoff', @id)
28
+ @id = nil
29
+ self
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,98 @@
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, ip_address = '192.168.1.1', cookies = []
35
+ url = "#{self.configuration['root']}/delivery/axmlrpc.php"
36
+ server = XmlrpcClient.new2(url)
37
+ server.call('openads.view', {
38
+ 'cookies' => cookies,
39
+ 'remote_addr' => ip_address,
40
+ }, "zone:#{zone_id}", 0, '', '', true, [])
41
+ end
42
+ end
43
+
44
+ def initialize(params = {})
45
+ raise "need publisher" unless params[:publisher_id] || params[:publisher]
46
+ params[:publisher_id] ||= params[:publisher].id
47
+ super(params)
48
+ end
49
+
50
+ # Link this zone to +campaign+
51
+ def link_campaign(campaign)
52
+ raise "Zone must be saved" if new_record?
53
+ raise ArgumentError.new("Campaign must be saved")if campaign.new_record?
54
+
55
+ session = self.class.connection
56
+ server = XmlrpcClient.new2("#{session.url}")
57
+ server.call("ox.linkCampaign", session, self.id, campaign.id)
58
+ end
59
+
60
+ # Unlink this zone from +campaign+
61
+ def unlink_campaign(campaign)
62
+ raise "Zone must be saved" if new_record?
63
+ raise ArgumentError.new("Campaign must be saved")if campaign.new_record?
64
+
65
+ session = self.class.connection
66
+ server = XmlrpcClient.new2("#{session.url}")
67
+ server.call("ox.unlinkCampaign", session, self.id, campaign.id)
68
+ end
69
+
70
+ # Link this zone to +banner+
71
+ def link_banner(banner)
72
+ raise "Zone must be saved" if new_record?
73
+ raise ArgumentError.new("Banner must be saved")if banner.new_record?
74
+
75
+ session = self.class.connection
76
+ server = XmlrpcClient.new2("#{session.url}")
77
+ server.call("ox.linkBanner", session, self.id, banner.id)
78
+ end
79
+
80
+ # Unlink this zone from +banner+
81
+ def unlink_banner(banner)
82
+ raise "Zone must be saved" if new_record?
83
+ raise ArgumentError.new("Banner must be saved")if banner.new_record?
84
+
85
+ session = self.class.connection
86
+ server = XmlrpcClient.new2("#{session.url}")
87
+ server.call("ox.unlinkBanner", session, self.id, banner.id)
88
+ end
89
+
90
+ # Generate tags for displaying this zone using +tag_type+
91
+ def generate_tags(tag_type = IFRAME)
92
+ session = self.class.connection
93
+ server = XmlrpcClient.new2("#{session.url}")
94
+ server.call("ox.generateTags", session, self.id, tag_type, [])
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,8 @@
1
+ require 'openx/services/base'
2
+ require 'openx/services/session'
3
+ require 'openx/services/advertiser'
4
+ require 'openx/services/agency'
5
+ require 'openx/services/campaign'
6
+ require 'openx/services/banner'
7
+ require 'openx/services/publisher'
8
+ require 'openx/services/zone'
@@ -0,0 +1,78 @@
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, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse,
11
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError].each {|m| m.send(:include, HTTPBroken)}
12
+ end
13
+
14
+ class XmlrpcClient
15
+ @uri = nil
16
+ @server = nil
17
+
18
+ @retry_on_http_error = true
19
+ @timeout = 10 # seconds
20
+
21
+ class << self
22
+ attr_accessor :retry_on_http_error, :timeout
23
+
24
+ def init_server(uri)
25
+ server = XMLRPC::Client.new2(uri)
26
+ server.timeout = self.timeout
27
+ #server.instance_variable_get(:@http).set_debug_output($stderr)
28
+ server
29
+ end
30
+
31
+ def new2(uri)
32
+ server = init_server(uri)
33
+ new(server, uri)
34
+ end
35
+ end
36
+
37
+ def initialize(server, uri)
38
+ @server = server
39
+ @uri = uri
40
+ end
41
+
42
+ def call(method, *args)
43
+ if args.first.is_a?(OpenX::Services::Session)
44
+ session = args.shift()
45
+ args.unshift(session.id)
46
+ begin
47
+ do_call(method, *args)
48
+ rescue XMLRPC::FaultException => sess_id_err
49
+ if sess_id_err.message.strip == 'Session ID is invalid'
50
+ session.recreate!
51
+ args.shift()
52
+ args.unshift(session.id)
53
+ do_call(method, *args)
54
+ else
55
+ raise sess_id_err
56
+ end
57
+ end
58
+ else
59
+ do_call(method, *args)
60
+ end
61
+ end
62
+
63
+ def do_call(method, *args)
64
+ begin
65
+ @server.call(method, *args)
66
+ rescue HTTPBroken => httpbe
67
+ if self.class.retry_on_http_error
68
+ @server = self.class.init_server(@uri)
69
+ @server.call(method, *args)
70
+ else
71
+ raise httpbe
72
+ end
73
+ end
74
+ end
75
+ private :do_call
76
+
77
+ end
78
+ end
data/lib/openx.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'xmlrpc/client'
2
+ require 'openx/xmlrpc_client'
3
+ require 'openx/services'
4
+ require 'openx/invocation'
5
+ require 'openx/image'
6
+
7
+ module OpenX
8
+ VERSION = '1.1.0'
9
+ end
@@ -0,0 +1,110 @@
1
+ Index: www/api/v2/xmlrpc/BannerXmlRpcService.php
2
+ ===================================================================
3
+ --- www/api/v2/xmlrpc/BannerXmlRpcService.php (revision 44144)
4
+ +++ www/api/v2/xmlrpc/BannerXmlRpcService.php (working copy)
5
+ @@ -86,7 +86,7 @@
6
+ 1, array('campaignId', 'bannerName', 'storageType', 'fileName',
7
+ 'imageURL', 'htmlTemplate', 'width', 'height', 'weight',
8
+ 'target', 'url', 'bannerText', 'status', 'adserver', 'transparent',
9
+ - 'capping', 'sessionCapping', 'block', 'comments'),
10
+ + 'capping', 'sessionCapping', 'block', 'comments', 'keyword'),
11
+ array('aImage', 'aBackupImage'), $oResponseWithError)) {
12
+
13
+ return $oResponseWithError;
14
+ @@ -121,7 +121,7 @@
15
+ 1, array('bannerId', 'campaignId', 'bannerName', 'storageType', 'fileName',
16
+ 'imageURL', 'htmlTemplate', 'width', 'height', 'weight',
17
+ 'target', 'url', 'bannerText', 'status', 'adserver', 'transparent',
18
+ - 'capping', 'sessionCapping', 'block', 'comments'),
19
+ + 'capping', 'sessionCapping', 'block', 'comments', 'keyword'),
20
+ array('aImage', 'aBackupImage'), $oResponseWithError)) {
21
+
22
+ return $oResponseWithError;
23
+ Index: lib/OA/Dll/Banner.php
24
+ ===================================================================
25
+ --- lib/OA/Dll/Banner.php (revision 44144)
26
+ +++ lib/OA/Dll/Banner.php (working copy)
27
+ @@ -203,7 +203,8 @@
28
+ !$this->checkStructureNotRequiredIntegerField($oBanner, 'capping') ||
29
+ !$this->checkStructureNotRequiredIntegerField($oBanner, 'sessionCapping') ||
30
+ !$this->checkStructureNotRequiredIntegerField($oBanner, 'block') ||
31
+ - !$this->checkStructureNotRequiredStringField($oBanner, 'comments')
32
+ + !$this->checkStructureNotRequiredStringField($oBanner, 'comments') ||
33
+ + !$this->checkStructureNotRequiredStringField($oBanner, 'keyword')
34
+ ) {
35
+ return false;
36
+ }
37
+ Index: lib/OA/Dll/BannerInfo.php
38
+ ===================================================================
39
+ --- lib/OA/Dll/BannerInfo.php (revision 44144)
40
+ +++ lib/OA/Dll/BannerInfo.php (working copy)
41
+ @@ -210,6 +210,13 @@
42
+ var $comments;
43
+
44
+ /**
45
+ + * This field provides keywords to be stored.
46
+ + *
47
+ + * @var string $keyword
48
+ + */
49
+ + var $keyword;
50
+ +
51
+ + /**
52
+ * This method sets all default values when adding a new banner.
53
+ *
54
+ * @access public
55
+ @@ -251,6 +258,10 @@
56
+ if (is_null($this->block)) {
57
+ // Leave null
58
+ }
59
+ +
60
+ + if (is_null($this->keyword)) {
61
+ + // Leave null
62
+ + }
63
+ }
64
+
65
+ function encodeImage($aImage)
66
+ @@ -305,6 +316,7 @@
67
+ 'aImage' => 'custom',
68
+ 'aBackupImage' => 'custom',
69
+ 'comments' => 'string',
70
+ + 'keyword' => 'string',
71
+ );
72
+ }
73
+ }
74
+ Index: lib/xmlrpc/php/BannerInfo.php
75
+ ===================================================================
76
+ --- lib/xmlrpc/php/BannerInfo.php (revision 44144)
77
+ +++ lib/xmlrpc/php/BannerInfo.php (working copy)
78
+ @@ -210,6 +210,13 @@
79
+ var $comments;
80
+
81
+ /**
82
+ + * This field provides keywords to be stored.
83
+ + *
84
+ + * @var string $keyword
85
+ + */
86
+ + var $keyword;
87
+ +
88
+ + /**
89
+ * This method sets all default values when adding a new banner.
90
+ *
91
+ * @access public
92
+ @@ -251,6 +258,10 @@
93
+ if (is_null($this->block)) {
94
+ // Leave null
95
+ }
96
+ +
97
+ + if (is_null($this->keyword)) {
98
+ + // Leave null
99
+ + }
100
+ }
101
+
102
+ function encodeImage($aImage)
103
+ @@ -305,6 +316,7 @@
104
+ 'aImage' => 'custom',
105
+ 'aBackupImage' => 'custom',
106
+ 'comments' => 'string',
107
+ + 'keyword' => 'string',
108
+ );
109
+ }
110
+ }
Binary file
Binary file
data/test/helper.rb ADDED
@@ -0,0 +1,99 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../lib')))
2
+
3
+ require 'test/unit'
4
+ require 'openx'
5
+
6
+ ENV['OPENX_ENV'] = 'test'
7
+
8
+ module OpenX
9
+ class TestCase < Test::Unit::TestCase
10
+ TEST_SWF = File.expand_path(File.join(File.dirname(__FILE__), 'assets', 'cat.swf'))
11
+ TEST_JPG = File.expand_path(File.join(File.dirname(__FILE__), 'assets', '300x250.jpg'))
12
+
13
+ include OpenX::Services
14
+
15
+ undef :default_test
16
+
17
+ def setup
18
+ assert(
19
+ File.exists?(Base::CONFIGURATION_YAML),
20
+ 'Please create the credentials file'
21
+ )
22
+ end
23
+
24
+ def agency
25
+ @agency ||= Agency.create!(
26
+ {
27
+ :name => "Testing! - #{Time.now}",
28
+ :contact_name => 'Contact Name!',
29
+ :email => 'foo@bar.com'
30
+ }
31
+ )
32
+ end
33
+
34
+ def advertiser
35
+ @advertiser ||= Advertiser.create!(
36
+ {
37
+ :name => "adv-#{Time.now}",
38
+ :contact_name => 'Contact Name!',
39
+ :email => 'foo@bar.com'
40
+ }.merge(:agency => agency)
41
+ )
42
+ end
43
+
44
+ def campaign
45
+ @campaign ||= Campaign.create!(
46
+ {
47
+ :advertiser => advertiser,
48
+ :name => "Campaign-#{Time.now}",
49
+ :impressions => 2000
50
+ }
51
+ )
52
+ end
53
+
54
+ def publisher
55
+ @publisher ||= Publisher.create!(
56
+ {
57
+ :agency => agency,
58
+ :name => "Publisher! - #{Time.now}",
59
+ :contact_name => 'Aaron Patterson',
60
+ :email => 'aaron@tenderlovemaking.com',
61
+ :username => 'one',
62
+ :password => 'two',
63
+ }
64
+ )
65
+ end
66
+
67
+ def zone
68
+ @zone ||= Zone.create!(
69
+ {
70
+ :publisher => publisher,
71
+ :name => "Zone - #{Time.now}",
72
+ :type => Zone::BANNER,
73
+ :width => 300,
74
+ :height => 250,
75
+ }
76
+ )
77
+ end
78
+
79
+ def banner
80
+ @banner ||= Banner.create!({
81
+ :name => "Banner-#{Time.now}",
82
+ :storage_type => Banner::LOCAL_SQL,
83
+ :campaign => campaign,
84
+ :url => 'http://tenderlovemaking.com/',
85
+ :file_name => 'oogabooga',
86
+ :image => OpenX::Image.new(File.open(TEST_SWF, 'rb'))
87
+ })
88
+ end
89
+
90
+ def destroy
91
+ @banner.destroy if defined? @banner
92
+ @zone.destroy if defined? @zone
93
+ @publisher.destroy if defined? @publisher
94
+ @campaign.destroy if defined? @campaign
95
+ @advertiser.destroy if defined? @advertiser
96
+ @agency.destroy if defined? @agency
97
+ end
98
+ end
99
+ end