adapi 0.0.7 → 0.0.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.
@@ -23,6 +23,7 @@ module Adapi
23
23
  # Location.find(:country => 'CZ', :region => 'Prague' :city => 'Prague')
24
24
  #
25
25
  # TODO add legacy aliases: :city_name, :province_code, :country_code
26
+ # TODO move search by id into separate method
26
27
  #
27
28
  def self.find(amount = :all, params = {})
28
29
  # set amount = :first by default
@@ -37,17 +38,23 @@ module Adapi
37
38
  # in which language to retrieve locations
38
39
  params[:locale] ||= 'en'
39
40
 
40
- # support for legacy parameter
41
+ # support for legacy :province parameter
41
42
  if params[:province] and not params[:region]
42
43
  params[:region] = params[:province]
43
44
  end
44
45
 
46
+ # if :region parameter is in old format, replace it with province name
47
+ if params[:region] && (params[:region] =~ /^[A-Z]{2}\-\w{1,3}$/)
48
+ province_name = ConstantData::Location::Province.find_name_by_province_code(params[:region])
49
+ params[:region] = province_name if province_name
50
+ end
51
+
45
52
  # if :country parameter is valid country code, replace it with country name
46
53
  if params[:country] && (params[:country].size == 2)
47
54
  country_name = ConstantData::Location::Country.find_name_by_country_code(params[:country])
48
55
  params[:country] = country_name if country_name
49
56
  end
50
-
57
+
51
58
  # determine by what criteria to search
52
59
  location_type, location_name = nil, nil
53
60
  LOCATIONS_HIERARCHY.each do |param_name|
@@ -58,7 +65,7 @@ module Adapi
58
65
  end
59
66
  end
60
67
 
61
- raise "Invalid params" unless location_name
68
+ raise "Invalid params" if location_name.nil? and not params[:id]
62
69
 
63
70
  selector = {
64
71
  :fields => ['Id', 'LocationName', 'CanonicalName', 'DisplayType', 'ParentLocations', 'Reach'],
@@ -70,23 +77,31 @@ module Adapi
70
77
  ]
71
78
  }
72
79
 
80
+ if params[:id]
81
+ selector[:predicates] = [
82
+ { :field => 'Id', :operator => 'EQUALS', :values => [ params[:id].to_i ] }
83
+ ]
84
+ end
85
+
73
86
  # returns array of locations. and now the fun begins
74
87
  locations = Location.new.service.get(selector)
88
+
89
+ if params[:id]
90
+ return locations.first[:location] rescue nil
91
+ end
75
92
 
76
93
  # now we have to find location with correct display_type and TODO hierarchy
77
94
  # problematic example: Prague is both city and province (region)
78
95
 
79
- location = nil
80
96
  locations.each do |entry|
97
+ next unless entry.is_a?(Hash)
98
+
81
99
  if entry[:location][:display_type] == location_type
82
- location = entry[:location]
83
- break
100
+ return entry[:location]
84
101
  end
85
102
  end
86
103
 
87
- # FIXME for now, omit hierarchy as check just display type. results will be in 99,5% the same
88
-
89
- location
104
+ nil
90
105
  end
91
106
 
92
107
  # Displays location tree - location with its parents
@@ -1,10 +1,19 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Adapi
4
- VERSION = "0.0.7"
4
+ VERSION = "0.0.8"
5
5
 
6
6
  # CHANGELOG:
7
7
  #
8
+ # 0.0.8
9
+ # updated to AdWords API version v201109
10
+ # updated gem dependencies
11
+ # removed obsolete monkeypatches
12
+ # improved SOAP logging - enable pretty logging and configurable log path
13
+ # added conversion of legacy province_code to province_name in location search
14
+ # added tests for Campaign, CampaignCriterion and Location service
15
+ # added Getting Started section to README
16
+ #
8
17
  # 0.0.7
9
18
  # fix Location search by country code
10
19
  # hotfix OAuth
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ # manually hardcode timeouts for HTTPI to 5 minutes (300 seconds)
4
+ # TODO check if there's still no way to do it properly through HTTPI
5
+ # TODO enable user to set timeout in adapi configuration
6
+
7
+ module HTTPI
8
+ class Request
9
+ def open_timeout
10
+ 300
11
+ end
12
+
13
+ def read_timeout
14
+ 300
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+
2
+ # This monkeypatch adds option to prettify Savon SOAP log
3
+ #
4
+ # Not necessary, but very convenient feature - unless you're fond of deciphering
5
+ # endless SOAP request/response one-liners.
6
+ #
7
+ # Can be safely removed. Should be only temporary, as the idea is taken straight
8
+ # out of Savon pull requests and should be included in its future version.
9
+
10
+ module Savon
11
+ module Config
12
+
13
+ # Logs a given +message+. Optionally filtered if +xml+ is truthy.
14
+ def log(message, xml = false)
15
+ return unless log?
16
+
17
+ # ORIG
18
+ # message = filter_xml(message) if xml && !log_filter.empty?
19
+
20
+ # NEW: always run filter_xml method
21
+ message = filter_xml(message) # if xml && !log_filter.empty?
22
+
23
+ logger.send log_level, message
24
+ end
25
+
26
+ # Filters the given +xml+ based on log filter.
27
+ def filter_xml(xml)
28
+ doc = Nokogiri::XML(xml)
29
+ return xml unless doc.errors.empty?
30
+
31
+ log_filter.each do |filter|
32
+ doc.xpath("//*[local-name()='#{filter}']").map { |node| node.content = "***FILTERED***" }
33
+ end
34
+
35
+ # ORIG
36
+ # doc.root.to_s
37
+
38
+ # NEW: return formatted SOAP
39
+ doc.to_xml(:indent => 2)
40
+ end
41
+
42
+ end
43
+ end
@@ -2,10 +2,14 @@
2
2
 
3
3
  the_bids = { :xsi_type => 'ManualCPCAdGroupBids', :keyword_max_cpc => 10 }
4
4
 
5
- Factory.define :ad_group, :class => Adapi::AdGroup do |f|
6
- f.sequence(:campaign_id) { |n| n }
7
- f.name "AdGroup %d" % (Time.new.to_f * 1000).to_i
8
- f.status 'ENABLED'
9
- f.bids the_bids
10
- f.keywords [ 'dem codez', '"top coder"', '[-code]' ]
5
+ FactoryGirl.define do
6
+
7
+ factory :ad_group, :class => Adapi::AdGroup do
8
+ sequence(:campaign_id) { |n| n }
9
+ name "AdGroup %d" % (Time.new.to_f * 1000).to_i
10
+ status 'ENABLED'
11
+ bids the_bids
12
+ keywords [ 'dem codez', '"top coder"', '[-code]' ]
13
+ end
14
+
11
15
  end
@@ -1,10 +1,14 @@
1
1
  # encoding: utf-8
2
2
 
3
- Factory.define :text_ad, :class => Adapi::Ad::TextAd do |f|
4
- f.sequence(:ad_group_id) { |n| n }
5
- f.headline 'Code like Neo'
6
- f.description1 'Need mad coding skills?'
7
- f.description2 'Check out my new blog!'
8
- f.url 'http://www.demcodez.com'
9
- f.display_url 'http://www.demcodez.com'
3
+ FactoryGirl.define do
4
+
5
+ factory :text_ad, :class => Adapi::Ad::TextAd do
6
+ sequence(:ad_group_id) { |n| n }
7
+ headline 'Code like Neo'
8
+ description1 'Need mad coding skills?'
9
+ description2 'Check out my new blog!'
10
+ url 'http://www.demcodez.com'
11
+ display_url 'http://www.demcodez.com'
12
+ end
13
+
10
14
  end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ FactoryGirl.define do
4
+
5
+ a_bidding_strategy = { :xsi_type => 'BudgetOptimizer', :bid_ceiling => 55 }
6
+
7
+ factory :valid_campaign, :class => Adapi::Campaign do
8
+ sequence(:id) { |n| n }
9
+ sequence(:name) { |n| 'Campaign #%s' % n }
10
+ status 'PAUSED'
11
+ bidding_strategy a_bidding_strategy
12
+ budget 50
13
+ end
14
+
15
+ end
@@ -3,15 +3,15 @@
3
3
  require 'test_helper'
4
4
 
5
5
  module Adapi
6
- class CreateCampaignTest < Test::Unit::TestCase
7
- context "non-existent Campaign" do
6
+ class CampaignCreateTest < Test::Unit::TestCase
7
+ context "non-existent campaign" do
8
8
  should "not be found" do
9
9
  # FIXME randomly generated id, but it might actually exist
10
10
  assert_nil Adapi::Campaign.find(Time.new.to_i)
11
11
  end
12
12
  end
13
13
 
14
- context "existing Campaign" do
14
+ context "existing campaign" do
15
15
  setup do
16
16
  @campaign_data = {
17
17
  :name => "Campaign #%d" % (Time.new.to_f * 1000).to_i,
@@ -23,6 +23,13 @@ module Adapi
23
23
  :target_search_network => true,
24
24
  :target_content_network => false,
25
25
  :target_content_contextual => false
26
+ },
27
+
28
+ :criteria => {
29
+ :language => %w{ en cs },
30
+ :location => {
31
+ :name => { :city => 'Brno', :region => 'CZ-JM', :country => 'CZ' }
32
+ }
26
33
  }
27
34
  }
28
35
 
@@ -37,6 +44,14 @@ module Adapi
37
44
 
38
45
  assert_equal @campaign_data[:status], @campaign.status
39
46
  assert_equal @campaign_data[:name], @campaign.name
47
+
48
+ # TODO move campaign criteria to separate test
49
+ criteria = Adapi::CampaignCriterion.find(:campaign_id => @campaign.id)
50
+ assert_equal %w{ cs en }, criteria.map { |c| c[:code] if c[:type] == "LANGUAGE" }.compact.sort
51
+
52
+ location = criteria.find { |c| c[:type] == "LOCATION" }
53
+ assert_equal "Brno", location[:location_name]
54
+ assert_equal "City", location[:display_type]
40
55
  end
41
56
 
42
57
  should "still be found after deletion" do
@@ -46,7 +61,7 @@ module Adapi
46
61
 
47
62
  assert_equal "DELETED", deleted_campaign.status
48
63
  assert_equal @campaign_data[:name], deleted_campaign.name
49
- end
64
+ end
50
65
 
51
66
  end
52
67
 
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ require 'test_helper'
4
+
5
+ module Adapi
6
+ class FindLocationTest < Test::Unit::TestCase
7
+ context "non-existent Location" do
8
+ setup do
9
+ @location = Adapi::Location.find( :city => "Shamballa" )
10
+ end
11
+
12
+ should "not be found" do
13
+ assert_nil @location
14
+ end
15
+ end
16
+
17
+ context "existing Location" do
18
+
19
+ should "be found by name" do
20
+ @location = Adapi::Location.find( :country=>"CZ", :province=>"Prague", :city=>"Prague" )
21
+ assert_not_nil @location
22
+ assert_equal 1003803, @location[:id]
23
+ assert_equal "Prague", @location[:location_name]
24
+ assert_equal "City", @location[:display_type]
25
+ end
26
+
27
+ should "be found by id" do
28
+ @location = Adapi::Location.find( :id => 1003803 )
29
+ assert_not_nil @location
30
+ assert_equal 1003803, @location[:id]
31
+ assert_equal "Prague", @location[:location_name]
32
+ assert_equal "City", @location[:display_type]
33
+ end
34
+
35
+ should "be found by country_code" do
36
+ @location = Adapi::Location.find( :country => "CZ" )
37
+ assert_not_nil @location
38
+ assert_equal 2203, @location[:id]
39
+ assert_equal "Czech Republic", @location[:location_name]
40
+ assert_equal "Country", @location[:display_type]
41
+ end
42
+
43
+ should "be found by province_code" do
44
+ @location = Adapi::Location.find( :country => "CZ", :province => "CZ-JM" )
45
+ assert_not_nil @location
46
+ assert_equal 20219, @location[:id]
47
+ assert_equal "South Moravia", @location[:location_name]
48
+ assert_equal "Region", @location[:display_type]
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
@@ -12,7 +12,7 @@ module Adapi
12
12
 
13
13
  context "valid new TextAd instance" do
14
14
  setup do
15
- @text_ad = Factory.build(:text_ad)
15
+ @text_ad = FactoryGirl.build(:text_ad)
16
16
  end
17
17
 
18
18
  should "be valid" do
@@ -12,7 +12,7 @@ module Adapi
12
12
 
13
13
  context "valid new instance" do
14
14
  setup do
15
- @ad_group = Factory.build(:ad_group)
15
+ @ad_group = FactoryGirl.build(:ad_group)
16
16
  end
17
17
 
18
18
  should "be valid" do
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ require 'test_helper'
4
+
5
+ module Adapi
6
+ class CampaignTest < Test::Unit::TestCase
7
+
8
+ context "invalid Campaign" do
9
+ setup do
10
+ @campaign = Campaign.new
11
+ end
12
+
13
+ should "not be valid?" do
14
+ assert ! @campaign.valid?
15
+ end
16
+
17
+ should "return error messages" do
18
+ @campaign.valid? # creates errors messages
19
+ assert ! @campaign.errors.full_messages.empty?
20
+ end
21
+ end
22
+
23
+ context "Campaign status" do
24
+
25
+ should "be required" do
26
+ campaign = FactoryGirl.build(:valid_campaign, :status => nil)
27
+ assert ! campaign.valid?
28
+ assert campaign.errors.has_key? :status
29
+ end
30
+
31
+ should "should be only ACTIVE, DELETED or PAUSED" do
32
+ campaign = FactoryGirl.build(:valid_campaign, :status => "TEST")
33
+ assert ! campaign.valid?
34
+ assert campaign.errors.has_key? :status
35
+ end
36
+
37
+ end
38
+
39
+ context "Campaign name" do
40
+
41
+ should "be required" do
42
+ campaign = FactoryGirl.build(:valid_campaign, :name => nil)
43
+ assert ! campaign.valid?
44
+ assert campaign.errors.has_key? :name
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-25 00:00:00.000000000 Z
12
+ date: 2012-05-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: google-ads-common
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.6.3
21
+ version: 0.7.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: 0.6.3
29
+ version: 0.7.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: google-adwords-api
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - '='
36
36
  - !ruby/object:Gem::Version
37
- version: 0.5.2
37
+ version: 0.6.0
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - '='
44
44
  - !ruby/object:Gem::Version
45
- version: 0.5.2
45
+ version: 0.6.0
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: activemodel
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -98,7 +98,7 @@ dependencies:
98
98
  requirements:
99
99
  - - ~>
100
100
  - !ruby/object:Gem::Version
101
- version: '0.7'
101
+ version: 0.8.0
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
@@ -106,7 +106,7 @@ dependencies:
106
106
  requirements:
107
107
  - - ~>
108
108
  - !ruby/object:Gem::Version
109
- version: '0.7'
109
+ version: 0.8.0
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: yard
112
112
  requirement: !ruby/object:Gem::Requirement
@@ -192,17 +192,17 @@ dependencies:
192
192
  requirement: !ruby/object:Gem::Requirement
193
193
  none: false
194
194
  requirements:
195
- - - ! '>='
195
+ - - ~>
196
196
  - !ruby/object:Gem::Version
197
- version: '0'
197
+ version: 3.3.0
198
198
  type: :development
199
199
  prerelease: false
200
200
  version_requirements: !ruby/object:Gem::Requirement
201
201
  none: false
202
202
  requirements:
203
- - - ! '>='
203
+ - - ~>
204
204
  - !ruby/object:Gem::Version
205
- version: '0'
205
+ version: 3.3.0
206
206
  - !ruby/object:Gem::Dependency
207
207
  name: minitest
208
208
  requirement: !ruby/object:Gem::Requirement
@@ -270,23 +270,27 @@ files:
270
270
  - lib/adapi/constant_data.rb
271
271
  - lib/adapi/constant_data/country.rb
272
272
  - lib/adapi/constant_data/language.rb
273
+ - lib/adapi/constant_data/province.rb
273
274
  - lib/adapi/keyword.rb
274
275
  - lib/adapi/location.rb
275
276
  - lib/adapi/version.rb
276
- - lib/ads_common_monkeypatch.rb
277
- - lib/httpi_request_monkeypatch.rb
277
+ - lib/httpi_monkeypatch.rb
278
+ - lib/savon_monkeypatch.rb
278
279
  - test/config/adapi.yml.template
279
280
  - test/config/adwords_api.yml.template
280
281
  - test/factories/.gitignore
281
282
  - test/factories/ad_group_factory.rb
282
283
  - test/factories/ad_text_factory.rb
284
+ - test/factories/campaign_factory.rb
283
285
  - test/integration/create_campaign_test.rb
286
+ - test/integration/find_location_test.rb
284
287
  - test/test_helper.rb
285
288
  - test/unit/.gitignore
286
289
  - test/unit/ad/ad_text_test.rb
287
290
  - test/unit/ad_group_test.rb
288
291
  - test/unit/ad_test.rb
289
292
  - test/unit/campaign_criterion_test.rb
293
+ - test/unit/campaign_test.rb
290
294
  - test/unit/config_test.rb
291
295
  homepage: https://github.com/lstejskal/adapi
292
296
  licenses: []
@@ -300,12 +304,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
300
304
  - - ! '>='
301
305
  - !ruby/object:Gem::Version
302
306
  version: '0'
307
+ segments:
308
+ - 0
309
+ hash: -478548313315719997
303
310
  required_rubygems_version: !ruby/object:Gem::Requirement
304
311
  none: false
305
312
  requirements:
306
313
  - - ! '>='
307
314
  - !ruby/object:Gem::Version
308
315
  version: '0'
316
+ segments:
317
+ - 0
318
+ hash: -478548313315719997
309
319
  requirements: []
310
320
  rubyforge_project: adapi
311
321
  rubygems_version: 1.8.21