adapi 0.0.7 → 0.0.8

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