sem4r 0.1.1 → 0.1.2

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.
Files changed (199) hide show
  1. data/Gemfile +11 -0
  2. data/Gemfile.lock +36 -0
  3. data/README.rdoc +30 -0
  4. data/Rakefile +15 -12
  5. data/VERSION.yml +1 -1
  6. data/examples_blog/2009-11-29-hello-world.rb +0 -1
  7. data/examples_sem4r/01_get_account.rb +1 -1
  8. data/examples_sem4r/02_get_info.rb +6 -2
  9. data/examples_sem4r/03_list_ad.rb +1 -1
  10. data/examples_sem4r/04_list_keywords.rb +1 -1
  11. data/examples_sem4r/05_request_report.rb +1 -2
  12. data/examples_sem4r/05_request_report_2010.rb +19 -4
  13. data/examples_sem4r/06_create_campaigns.rb +1 -1
  14. data/examples_sem4r/07_create_campaigns_block.rb +1 -1
  15. data/examples_sem4r/07_create_campaigns_simple.rb +1 -1
  16. data/examples_sem4r/08_ad_params.rb +1 -1
  17. data/examples_sem4r/09_targeting_idea.rb +1 -1
  18. data/examples_sem4r/10_get_location.rb +1 -1
  19. data/examples_sem4r/11_submit_bulk_job.rb +1 -1
  20. data/examples_sem4r/12_list_bulk_job.rb +1 -1
  21. data/examples_sem4r/30_prune_empty_adgroup.rb +1 -1
  22. data/examples_sem4r/31_empty_accounts.rb +1 -1
  23. data/examples_sem4r/example_helper.rb +5 -10
  24. data/lib/sem4r/account.rb +3 -306
  25. data/lib/sem4r/ad_group/ad_group.rb +16 -14
  26. data/lib/sem4r/ad_group/ad_group_bids.rb +11 -10
  27. data/lib/sem4r/ad_group/ad_group_service.rb +5 -5
  28. data/lib/sem4r/ad_group_ad/ad_group_ad.rb +1 -1
  29. data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +5 -5
  30. data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +9 -9
  31. data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +9 -9
  32. data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +6 -6
  33. data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +5 -6
  34. data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +0 -1
  35. data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +4 -4
  36. data/lib/sem4r/ad_group_criterion/criterion.rb +3 -3
  37. data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +3 -3
  38. data/lib/sem4r/ad_group_criterion/criterion_placement.rb +2 -2
  39. data/lib/sem4r/ad_param/ad_param.rb +3 -3
  40. data/lib/sem4r/ad_param/ad_param_service.rb +5 -5
  41. data/lib/sem4r/adwords.rb +1 -0
  42. data/lib/sem4r/api_counters.rb +1 -0
  43. data/lib/sem4r/base.rb +1 -0
  44. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +2 -2
  45. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_account_extension.rb +59 -0
  46. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +5 -5
  47. data/lib/sem4r/campaign/campaign.rb +10 -10
  48. data/lib/sem4r/campaign/campaign_account_extension.rb +87 -0
  49. data/lib/sem4r/campaign/campaign_service.rb +6 -6
  50. data/lib/sem4r/campaign_target/campaign_target_service.rb +22 -22
  51. data/lib/sem4r/cli/cli_command.rb +1 -0
  52. data/lib/sem4r/cli/cli_common_args.rb +10 -3
  53. data/lib/sem4r/cli/cli_helpers.rb +1 -0
  54. data/lib/sem4r/cli/cli_sem.rb +1 -0
  55. data/lib/sem4r/cli/{cli_download_report.rb → commands/cli_download_report.rb} +2 -1
  56. data/lib/sem4r/cli/{cli_ideas.rb → commands/cli_ideas.rb} +1 -0
  57. data/lib/sem4r/cli/{cli_commands.rb → commands/cli_info.rb} +3 -23
  58. data/lib/sem4r/cli/{cli_list_ads.rb → commands/cli_list_ads.rb} +1 -0
  59. data/lib/sem4r/cli/commands/cli_list_campaign.rb +35 -0
  60. data/lib/sem4r/cli/commands/cli_list_client.rb +36 -0
  61. data/lib/sem4r/cli/{cli_list_keywords.rb → commands/cli_list_keywords.rb} +1 -0
  62. data/lib/sem4r/cli/commands/cli_list_report.rb +34 -0
  63. data/lib/sem4r/cli/commands/cli_repdef.rb +109 -0
  64. data/lib/sem4r/cli/commands/cli_report.rb +78 -0
  65. data/lib/sem4r/cli/{cli_request_report.rb → commands/cli_request_report.rb} +1 -0
  66. data/lib/sem4r/credentials.rb +1 -0
  67. data/lib/sem4r/extensions.rb +22 -1
  68. data/lib/sem4r/geo_location/geo_location_account_extension.rb +43 -0
  69. data/lib/sem4r/geo_location/geo_location_service.rb +4 -4
  70. data/lib/sem4r/info/info_account_extension.rb +48 -0
  71. data/lib/sem4r/info/info_selector.rb +108 -0
  72. data/lib/sem4r/info/info_service.rb +11 -66
  73. data/lib/sem4r/{common/operation.rb → operation.rb} +3 -1
  74. data/lib/sem4r/report_definition/report_definition.rb +75 -15
  75. data/lib/sem4r/report_definition/report_definition_account_extension.rb +79 -0
  76. data/lib/sem4r/report_definition/report_definition_operation.rb +3 -1
  77. data/lib/sem4r/report_definition/report_definition_service.rb +7 -7
  78. data/lib/sem4r/report_definition/report_field.rb +57 -0
  79. data/lib/sem4r/sem4r_error.rb +1 -0
  80. data/lib/sem4r/services/http_connector.rb +93 -0
  81. data/lib/sem4r/services/service.rb +1 -1
  82. data/lib/sem4r/services/soap_call.rb +27 -5
  83. data/lib/sem4r/services/soap_connector.rb +2 -147
  84. data/lib/sem4r/services/soap_dumper.rb +105 -0
  85. data/lib/sem4r/services/soap_message_v13.rb +15 -9
  86. data/lib/sem4r/services/{soap_message_v2009.rb → soap_message_v2010.rb} +26 -12
  87. data/lib/sem4r/targeting_idea/targeting_idea.rb +16 -11
  88. data/lib/sem4r/targeting_idea/targeting_idea_account_extension.rb +52 -0
  89. data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +36 -10
  90. data/lib/sem4r/targeting_idea/targeting_idea_service.rb +5 -5
  91. data/lib/sem4r/v13_account/account_account_extension.rb +103 -0
  92. data/lib/sem4r/v13_account/billing_address.rb +10 -21
  93. data/lib/sem4r/v13_report/report.rb +8 -8
  94. data/lib/sem4r/v13_report/report_account_extension.rb +63 -0
  95. data/lib/sem4r/v13_report/report_job.rb +1 -1
  96. data/lib/sem4r.rb +27 -6
  97. data/lib/sem4r_cli.rb +4 -6
  98. data/lib/{sem4r → soap_helpers}/soap_attributes.rb +105 -13
  99. data/sem4r.gemspec +229 -190
  100. data/spec/{aggregates_spec_helper.rb → aggregates_rspec_helper.rb} +0 -0
  101. data/spec/build_fixtures.rb +84 -0
  102. data/spec/rspec_hash.rb +66 -0
  103. data/spec/{spec_helper.rb → rspec_helper.rb} +4 -2
  104. data/spec/rspec_matchers.rb +102 -0
  105. data/spec/rspec_sem4r_helper.rb +88 -0
  106. data/spec/sem4r/account_spec.rb +3 -4
  107. data/spec/sem4r/ad_group/ad_group_bids_spec.rb +4 -3
  108. data/spec/sem4r/ad_group/ad_group_service_spec.rb +8 -7
  109. data/spec/sem4r/ad_group/ad_group_spec.rb +5 -4
  110. data/spec/{fixtures/services/ad_group → sem4r/ad_group/fixtures}/get-first-req.xml +0 -0
  111. data/spec/{fixtures/services/ad_group → sem4r/ad_group/fixtures}/get-first-res.xml +0 -0
  112. data/spec/{fixtures/services/ad_group → sem4r/ad_group/fixtures}/get-manual-cpm-bids-req.xml +0 -0
  113. data/spec/{fixtures/services/ad_group → sem4r/ad_group/fixtures}/get-manual-cpm-bids-res.xml +0 -0
  114. data/spec/{fixtures/services/ad_group → sem4r/ad_group/fixtures}/mutate_add-req.xml +0 -0
  115. data/spec/{fixtures/services/ad_group → sem4r/ad_group/fixtures}/mutate_add-res.xml +0 -0
  116. data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +3 -3
  117. data/spec/sem4r/ad_group_ad/ad_group_ad_service_spec.rb +6 -5
  118. data/spec/sem4r/ad_group_ad/ad_group_ad_spec.rb +6 -5
  119. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/get_mobile_ad-req.xml +0 -0
  120. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/get_mobile_ad-res.xml +0 -0
  121. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/get_text_ad-req.xml +0 -0
  122. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/get_text_ad-res.xml +0 -0
  123. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/mutate_add_mobile_ad-req.xml +0 -0
  124. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/mutate_add_mobile_ad-res.xml +0 -0
  125. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/mutate_add_text_ad-req.xml +0 -0
  126. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/mutate_add_text_ad-res.xml +0 -0
  127. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/mutate_add_two_criterions-req.xml +0 -0
  128. data/spec/{fixtures/services/ad_group_ad → sem4r/ad_group_ad/fixtures}/mutate_add_two_criterions-res.xml +0 -0
  129. data/spec/sem4r/ad_group_criterion/ad_group_criterion_bids_spec.rb +5 -4
  130. data/spec/sem4r/ad_group_criterion/ad_group_criterion_service_spec.rb +6 -5
  131. data/spec/sem4r/ad_group_criterion/ad_group_criterion_spec.rb +6 -5
  132. data/spec/sem4r/ad_group_criterion/criterion_spec.rb +5 -4
  133. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/get-req.xml +0 -0
  134. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/get-res.xml +0 -0
  135. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/mutate_add_criterion_keyword-req.xml +0 -0
  136. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/mutate_add_criterion_keyword-res.xml +0 -0
  137. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/mutate_add_criterion_placement-req.xml +0 -0
  138. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/mutate_add_criterion_placement-res.xml +0 -0
  139. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/mutate_add_negative_keyword-req.xml +0 -0
  140. data/spec/{fixtures/services/ad_group_criterion → sem4r/ad_group_criterion/fixtures}/mutate_add_negative_keyword-res.xml +0 -0
  141. data/spec/sem4r/ad_param/ad_param_service_spec.rb +6 -5
  142. data/spec/sem4r/ad_param/ad_param_spec.rb +3 -2
  143. data/spec/{fixtures/services/ad_param → sem4r/ad_param/fixtures}/mutate_set-req.xml +0 -0
  144. data/spec/{fixtures/services/ad_param → sem4r/ad_param/fixtures}/mutate_set-res.xml +0 -0
  145. data/spec/sem4r/adwords_spec.rb +1 -1
  146. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +6 -5
  147. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +4 -3
  148. data/spec/{fixtures/services/bulk_mutate_job → sem4r/bulk_mutate_job/fixtures}/get-req.xml +0 -0
  149. data/spec/{fixtures/services/bulk_mutate_job → sem4r/bulk_mutate_job/fixtures}/get-res.xml +0 -0
  150. data/spec/{fixtures/services/bulk_mutate_job → sem4r/bulk_mutate_job/fixtures}/mutate-req.xml +0 -0
  151. data/spec/{fixtures/services/bulk_mutate_job → sem4r/bulk_mutate_job/fixtures}/mutate-res.xml +0 -0
  152. data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +3 -2
  153. data/spec/sem4r/campaign/campaign_service_spec.rb +8 -7
  154. data/spec/sem4r/campaign/campaign_spec.rb +3 -2
  155. data/spec/{fixtures/services/campaign → sem4r/campaign/fixtures}/get-req.xml +0 -0
  156. data/spec/{fixtures/services/campaign → sem4r/campaign/fixtures}/get-res.xml +0 -0
  157. data/spec/{fixtures/services/campaign → sem4r/campaign/fixtures}/mutate_add-req.xml +0 -0
  158. data/spec/{fixtures/services/campaign → sem4r/campaign/fixtures}/mutate_add-res.xml +0 -0
  159. data/spec/sem4r/cli/cli_spec.rb +2 -3
  160. data/spec/sem4r/credentials_spec.rb +1 -1
  161. data/spec/sem4r/info/account_info_extension_spec.rb +51 -0
  162. data/spec/sem4r/info/fixtures/get_unit_count-req.xml +34 -0
  163. data/spec/sem4r/info/fixtures/get_unit_count-res.xml +27 -0
  164. data/spec/sem4r/nokogiri_parsing_spec.rb +105 -0
  165. data/spec/{fixtures/services/report_definition → sem4r/report_definition/fixtures}/mutate_add-req.xml +0 -0
  166. data/spec/{fixtures/services/report_definition → sem4r/report_definition/fixtures}/mutate_add-req_orig.xml +0 -0
  167. data/spec/sem4r/report_definition/report_definition_service_spec.rb +5 -3
  168. data/spec/sem4r/report_definition/report_definition_spec.rb +6 -3
  169. data/spec/sem4r/rexml_parsing_spec.rb +22 -22
  170. data/spec/sem4r/services/service_spec.rb +2 -1
  171. data/spec/sem4r/services/soap_call_spec.rb +6 -5
  172. data/spec/sem4r/services/soap_message_v13_spec.rb +5 -11
  173. data/spec/{fixtures/services/targeting_idea → sem4r/targeting_idea/fixtures}/get-req-all-options.xml +0 -0
  174. data/spec/{fixtures/services/targeting_idea → sem4r/targeting_idea/fixtures}/get-req.xml +9 -11
  175. data/spec/sem4r/targeting_idea/fixtures/get-res.xml +2276 -0
  176. data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +30 -11
  177. data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +4 -3
  178. data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +5 -4
  179. data/spec/sem4r/v13_account/account_account_extension_spec.rb +47 -0
  180. data/spec/sem4r/v13_account/account_service_spec.rb +6 -5
  181. data/spec/sem4r/v13_account/billing_address_spec.rb +40 -0
  182. data/spec/{fixtures/services/v13_account → sem4r/v13_account/fixtures}/get_account_info-req.xml +0 -0
  183. data/spec/{fixtures/services/v13_account → sem4r/v13_account/fixtures}/get_account_info-res.xml +0 -0
  184. data/spec/{fixtures/services/v13_account → sem4r/v13_account/fixtures}/get_client_accounts-req.xml +0 -0
  185. data/spec/{fixtures/services/v13_account → sem4r/v13_account/fixtures}/get_client_accounts-res.xml +0 -0
  186. data/spec/{fixtures/services/v13_report → sem4r/v13_report/fixtures}/get_all_jobs-req.xml +0 -0
  187. data/spec/{fixtures/services/v13_report → sem4r/v13_report/fixtures}/get_all_jobs-res.xml +2 -2
  188. data/spec/{fixtures/services/v13_report → sem4r/v13_report/fixtures}/schedule_report_job-req.xml +0 -0
  189. data/spec/{fixtures/services/v13_report → sem4r/v13_report/fixtures}/schedule_report_job-res.xml +0 -0
  190. data/spec/sem4r/v13_report/report_service_spec.rb +13 -11
  191. data/spec/sem4r/v13_report/report_spec.rb +4 -3
  192. data/spec/sem4r_stubs.rb +182 -0
  193. data/spec/soap_helpers/soap_attributes_spec.rb +129 -0
  194. metadata +225 -125
  195. data/spec/fixtures/services/info/get_unit_count-req.xml +0 -30
  196. data/spec/fixtures/services/info/get_unit_count-res.xml +0 -29
  197. data/spec/fixtures/services/targeting_idea/get-res.xml +0 -3601
  198. data/spec/sem4r/soap_attributes_spec.rb +0 -116
  199. data/spec/sem4r_spec_helper.rb +0 -353
@@ -0,0 +1,105 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ # -------------------------------------------------------------------------
24
+
25
+ module Sem4r
26
+
27
+ module SoapDumper
28
+
29
+ def initialize
30
+ @soap_dump = false
31
+ @soap_dump_log = nil
32
+ end
33
+
34
+ def dump_soap_options( dump_options )
35
+ @soap_dump = true
36
+ @soap_dump_log = nil
37
+
38
+ if dump_options[:directory]
39
+ @soap_dump_dir = dump_options[:directory]
40
+ else
41
+ @soap_dump_log = File.open( dump_options[:file], "w" )
42
+ end
43
+ @soap_dump_format = false || dump_options[:format]
44
+
45
+ @soap_dump_interceptor = dump_options[:interceptor] if dump_options[:interceptor]
46
+ end
47
+
48
+ def dump_soap_request(service_url, request_xml)
49
+ return unless @soap_dump
50
+ %w{email password developerToken authToken clientEmail}.each do |tag|
51
+ request_xml = request_xml.gsub(/<#{tag}([^>]*)>.*<\/#{tag}>/, "<#{tag}\\1>***censured***</#{tag}>")
52
+ end
53
+ str = ""
54
+ str << "<!-- Post to '#{service_url}' -->\n"
55
+ str << xml_to_s(request_xml) << "\n"
56
+ dump(service_url, "req", str)
57
+ end
58
+
59
+ def dump_soap_response(service_url, response_xml)
60
+ return unless @soap_dump
61
+ response_xml.gsub(/<email[^>]*>.+<\/email>/, "<email>**censured**</email>")
62
+ str = ""
63
+ str << "<!-- response -->\n" unless @soap_dump_dir
64
+ str << xml_to_s(response_xml) << "\n"
65
+ str << "<!-- end -->" unless @soap_dump_dir
66
+ dump(service_url, "res", str)
67
+ end
68
+
69
+ private
70
+
71
+ def dump(service_url, type, str)
72
+
73
+ @soap_dump_interceptor.call(service_url, type, str) if @soap_dump_interceptor
74
+
75
+ if @soap_dump_dir
76
+ service = service_url.match(/\/([^\/]*)$/)[1]
77
+ filename = Time.now.strftime "%Y%m%d-%H%M%S-%L-#{service}-#{type}.xml"
78
+
79
+ FileUtils.mkdir_p(@soap_dump_dir) unless File.directory?(@soap_dump_dir)
80
+ pathname = File.join(@soap_dump_dir, filename)
81
+ File.open(pathname, "w") {|f|
82
+ f.puts str
83
+ }
84
+ else
85
+ @soap_dump_log.puts str
86
+ @soap_dump_log.flush
87
+ end
88
+ end
89
+
90
+ def xml_to_s(xml)
91
+ if !@soap_dump_format
92
+ xml
93
+ else
94
+ # TODO: using nokogiri also for pretty print xml ?
95
+ require 'rexml/document'
96
+ xml_document = REXML::Document.new(xml)
97
+ f = REXML::Formatters::Pretty.new
98
+ out = String.new
99
+ f.write(xml_document, out)
100
+ # remove xml directive
101
+ out.gsub("<?xml version='1.0' encoding='UTF-8'?>","")
102
+ end
103
+ end
104
+ end
105
+ end
@@ -43,8 +43,12 @@ module Sem4r
43
43
  soap_message = build_soap_message
44
44
  response_xml = @connector.send(service_url, soap_action, soap_message)
45
45
  # erase namespace 'nsX'so it more simple parsing the xml
46
- response_xml = response_xml.gsub(/ns\d:/, "")
47
- @response = REXML::Document.new(response_xml)
46
+
47
+ # response_xml = response_xml.gsub(/ns\d:/, "")
48
+ # @response = REXML::Document.new(response_xml)
49
+ response_xml.gsub!(/\b(ns\d:|xsi:|s:|soapenv:|env:|soap:)/, "")
50
+ response_xml.gsub!(/xmlns=["'].*?['"]/, '')
51
+ @response = Nokogiri::XML::Document.parse(response_xml)
48
52
 
49
53
  # extract information from header
50
54
  # <soapenv:Header>
@@ -53,12 +57,13 @@ module Sem4r
53
57
  # <units soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v13">5</units>
54
58
  # <requestId soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v13">abade53d3dbecd45600e7d14563f10f1</requestId>
55
59
  # </soapenv:Header>
56
- header = REXML::XPath.first(@response, "//soapenv:Header")
60
+
61
+ header = @response.xpath("//Header").first
57
62
  if header
58
63
  @counters = {
59
- :response_time => header.elements['responseTime'].text.to_i,
60
- :operations => header.elements['operations'].text.to_i,
61
- :units => header.elements['units'].text.to_i
64
+ :response_time => header.at_xpath('responseTime').text.to_i,
65
+ :operations => header.at_xpath('operations').text.to_i,
66
+ :units => header.at_xpath('units').text.to_i
62
67
  }
63
68
  end
64
69
 
@@ -77,10 +82,11 @@ module Sem4r
77
82
  # </soapenv:Fault>
78
83
  # </soapenv:Body>
79
84
  #</soapenv:Envelope>
80
- fault_el = REXML::XPath.first(@response, "//soapenv:Fault")
85
+ #=======
86
+ fault_el = @response.xpath("//Fault").first
81
87
  if fault_el
82
- fault_code = fault_el.elements['faultcode'].text
83
- fault_string = fault_el.elements['faultstring'].text
88
+ fault_code = fault_el.at_xpath('faultcode').text
89
+ fault_string = fault_el.at_xpath('faultstring').text
84
90
  raise SoapError, "#{fault_code}: '#{fault_string}'"
85
91
  end
86
92
  self
@@ -23,7 +23,7 @@
23
23
 
24
24
  module Sem4r
25
25
 
26
- class SoapMessageV2009
26
+ class SoapMessageV2010
27
27
 
28
28
  attr_reader :response
29
29
  attr_reader :counters
@@ -50,29 +50,45 @@ module Sem4r
50
50
  end
51
51
 
52
52
  def send(service_url)
53
- response_xml = @connector.send(service_url, "", build_soap_message)
53
+ send_raw(service_url, build_soap_message)
54
+ end
55
+
56
+ def send_raw(service_url, soap_message)
57
+ soap_message = soap_message.dup
58
+ soap_message.gsub!(/{authentication_token}/, @credentials.authentication_token)
59
+ soap_message.gsub!(/{useragent}/, @credentials.useragent)
60
+ soap_message.gsub!(/{developer_token}/, @credentials.developer_token)
61
+ # soap_message.gsub!(/{client_email}/, @credentials.client_email)
62
+ _send_raw(service_url, soap_message)
63
+ end
64
+
65
+ private
66
+
67
+ def _send_raw(service_url, soap_message)
68
+ response_xml = @connector.send(service_url, "", soap_message)
54
69
  # erase namespace so it more simple parsing the xml
55
- response_xml = response_xml.gsub(/ns\d:/, "")
56
- @response = REXML::Document.new(response_xml)
70
+ response_xml.gsub!(/\b(ns\d:|xsi:|s:|soapenv:|env:|soap:)/, "")
71
+ response_xml.gsub!(/xmlns=["'].*?['"]/, '')
72
+ @response = Nokogiri::XML::Document.parse(response_xml)
57
73
 
58
74
  #
59
75
  # extract information from header
60
76
  #
61
- header = REXML::XPath.first(@response, "//ResponseHeader")
77
+ header = @response.at_xpath("//ResponseHeader")
62
78
  if header
63
79
  @counters = {
64
- :operations => header.elements['operations'].text.to_i,
65
- :response_time => header.elements['responseTime'].text.to_i,
66
- :units => header.elements['units'].text.to_i
80
+ :operations => header.at_xpath("operations").text.to_i,
81
+ :response_time => header.at_xpath("responseTime").text.to_i,
82
+ :units => header.at_xpath("units").text.to_i
67
83
  }
68
84
  end
69
85
 
70
86
  #
71
87
  # check soap fault
72
88
  #
73
- fault_el = REXML::XPath.first(@response, "//soap:Fault")
89
+ fault_el = @response.at_xpath("//Fault")
74
90
  if fault_el
75
- fault_string = fault_el.elements['faultstring'].text
91
+ fault_string = fault_el.at_xpath('faultstring').text
76
92
  @logger.error("soap error: #{fault_string}") if @logger
77
93
  raise fault_string
78
94
  end
@@ -108,8 +124,6 @@ module Sem4r
108
124
  #</soap:Envelope>
109
125
  end
110
126
 
111
- private
112
-
113
127
  def build_soap_header
114
128
  auth_token = @credentials.authentication_token
115
129
 
@@ -56,10 +56,10 @@ module Sem4r
56
56
  end
57
57
 
58
58
  def self.from_element(el)
59
- els = REXML::XPath.match( el, "data")
59
+ els = el.xpath("data")
60
60
  @attributes = els.map do |el|
61
- el1 = el.elements["value"]
62
- xml_type = el1.elements["Attribute.Type"].text.strip
61
+ el1 = el.at_xpath("value")
62
+ xml_type = el1.at_xpath("Attribute.Type").text.strip
63
63
  case xml_type
64
64
  when IdeaTypeAttribute
65
65
  TIdeaTypeAttribute.from_element(el1)
@@ -67,6 +67,11 @@ module Sem4r
67
67
  TKeywordAttribute.from_element(el1)
68
68
  when MonthlySearchVolumeAttribute
69
69
  TMonthlySearchVolumeAttribute.from_element(el1)
70
+ else
71
+ # added from biilmann trunk
72
+ Struct.new(:key, :text).new(
73
+ el.xpath("key").text.strip,
74
+ el1.xpath("value").text.strip)
70
75
  end
71
76
  end
72
77
  end
@@ -90,10 +95,10 @@ module Sem4r
90
95
  end
91
96
 
92
97
  def self.from_element( el )
93
- el1 = el.elements["value"]
98
+ el1 = el.at_xpath("value")
94
99
  new do
95
- text el1.elements["text"].text
96
- match_type el1.elements["matchType"].text
100
+ text el1.at_xpath("text").text
101
+ match_type el1.at_xpath("matchType").text
97
102
  end
98
103
  end
99
104
 
@@ -116,11 +121,11 @@ module Sem4r
116
121
 
117
122
  def self.from_element( el )
118
123
  historical_values = []
119
- el.elements.each do |node|
124
+ el.children.each do |node|
120
125
  next if node.name == "Attribute.Type"
121
- historical_value = { :year => node.elements["year"].text,
122
- :month => node.elements["month"].text}
123
- historical_value.merge!(:count => node.elements["count"].text) if node.elements["count"]
126
+ historical_value = { :year => node.at_xpath("year").text,
127
+ :month => node.at_xpath("month").text}
128
+ historical_value.merge!(:count => node.at_xpath("count").text) if node.at_xpath("count")
124
129
  historical_values << historical_value
125
130
  end
126
131
  new do
@@ -146,7 +151,7 @@ module Sem4r
146
151
 
147
152
  def self.from_element( el )
148
153
  new do
149
- value el.elements["value"].text
154
+ value el.at_xpath("value").text
150
155
  end
151
156
  end
152
157
 
@@ -0,0 +1,52 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ # -------------------------------------------------------------------------
24
+
25
+ module Sem4r
26
+
27
+ module TargetingIdeaExtension
28
+ ############################################################################
29
+ # Targeting Idea
30
+
31
+ def targeting_idea(&block)
32
+ selector = TargetingIdeaSelector.new(&block)
33
+ soap_message = service.targeting_idea.get(@credentials, selector.to_xml)
34
+ add_counters( soap_message.counters )
35
+ rval = soap_message.response.at_xpath("//getResponse/rval")
36
+ els = rval.xpath("entries")
37
+ els.map do |el|
38
+ TargetingIdea.from_element( el )
39
+ end
40
+ end
41
+
42
+ def p_targeting_idea(&block)
43
+ targeting_ideas = targeting_idea(&block)
44
+ targeting_ideas.each{ |idea| puts idea }
45
+ end
46
+ end
47
+
48
+ class Account
49
+ include TargetingIdeaExtension
50
+ end
51
+
52
+ end
@@ -27,7 +27,7 @@ module Sem4r
27
27
  class RelatedToKeywordSearchParameter
28
28
  include SoapAttributes
29
29
 
30
- g_accessor :text
30
+ g_set_accessor :text
31
31
  g_accessor :match_type
32
32
 
33
33
  def initialize(&block)
@@ -37,15 +37,16 @@ module Sem4r
37
37
  end
38
38
 
39
39
  def to_xml
40
- <<-EOS
41
- <s:searchParameters xsi:type="s:RelatedToKeywordSearchParameter">
42
- <s:keywords xsi:type="Keyword">
43
- <Criterion.Type>Keyword</Criterion.Type>
44
- <text>#{text}</text>
45
- <matchType>#{match_type}</matchType>
46
- </s:keywords>
47
- </s:searchParameters>
48
- EOS
40
+ xml = ""
41
+ xml << '<s:searchParameters xsi:type="s:RelatedToKeywordSearchParameter">'
42
+ texts.each do |t|
43
+ xml << '<s:keywords xsi:type="Keyword">'
44
+ xml << "<Criterion.Type>Keyword</Criterion.Type>"
45
+ xml << "<text>#{t}</text>"
46
+ xml << "<matchType>#{match_type}</matchType>"
47
+ xml << "</s:keywords>"
48
+ end
49
+ xml << '</s:searchParameters>'
49
50
  end
50
51
  end
51
52
 
@@ -118,6 +119,27 @@ module Sem4r
118
119
  end
119
120
  end
120
121
 
122
+ class NgramGroupsSearchParameter
123
+ include SoapAttributes
124
+
125
+ g_set_accessor :ngram
126
+
127
+ def initialize(&block)
128
+ if block_given?
129
+ block.arity < 1 ? instance_eval(&block) : block.call(self)
130
+ end
131
+ end
132
+
133
+ def to_xml
134
+ xml = ""
135
+ xml << '<s:searchParameters xsi:type="s:NgramGroupsSearchParameter">'
136
+ ngrams.each do |t|
137
+ xml << "<s:ngramGroups>#{t}</s:ngramGroups>"
138
+ end
139
+ xml << '</s:searchParameters>'
140
+ end
141
+ end
142
+
121
143
  class TargetingIdeaSelector
122
144
  include SoapAttributes
123
145
 
@@ -153,6 +175,10 @@ module Sem4r
153
175
  def country_target_search_parameter(&block)
154
176
  @search_parameters << CountryTargetSearchParameter.new(&block)
155
177
  end
178
+
179
+ def ngram_group_search_parameter(&block)
180
+ @search_parameters << NgramGroupsSearchParameter.new(&block)
181
+ end
156
182
 
157
183
  def to_xml
158
184
  xml=<<-EOFS
@@ -28,14 +28,14 @@ module Sem4r
28
28
  def initialize(connector)
29
29
  @connector = connector
30
30
 
31
- @header_namespace = "https://adwords.google.com/api/adwords/cm/v201003"
32
- @service_namespace = "https://adwords.google.com/api/adwords/o/v201003"
31
+ @header_namespace = "https://adwords.google.com/api/adwords/cm/v201008"
32
+ @service_namespace = "https://adwords.google.com/api/adwords/o/v201008"
33
33
 
34
- @sandbox_service_url = "https://adwords-sandbox.google.com/api/adwords/o/v201003/TargetingIdeaService"
35
- @production_service_url = "https://adwords.google.com/api/adwords/o/v201003/TargetingIdeaService"
34
+ @sandbox_service_url = "https://adwords-sandbox.google.com/api/adwords/o/v201008/TargetingIdeaService"
35
+ @production_service_url = "https://adwords.google.com/api/adwords/o/v201008/TargetingIdeaService"
36
36
  end
37
37
 
38
- soap_call_v2009 :get, :mutate => false
38
+ soap_call_v2010 :get, :mutate => false
39
39
 
40
40
  ################
41
41
 
@@ -0,0 +1,103 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ # -------------------------------------------------------------------------
24
+
25
+ module Sem4r
26
+
27
+ module AccountAccountExtension
28
+ ############################################################################
29
+ # Info Account - Service Account
30
+
31
+ def p_info
32
+ _info unless @currency_code
33
+ puts "currency_code: #{@currency_code}"
34
+ puts "customer_id: #{@customer_id}"
35
+ puts @billing_address
36
+ end
37
+
38
+ def currency_code
39
+ _info unless @currency_code
40
+ @currency_code
41
+ end
42
+
43
+ def customer_id
44
+ _info unless @customer_id
45
+ @customer_id
46
+ end
47
+
48
+ private
49
+
50
+ def _info
51
+ soap_message = service.account.account_info(credentials)
52
+ add_counters( soap_message.counters )
53
+ el = soap_message.response.at_xpath("//getAccountInfoResponse/getAccountInfoReturn")
54
+ @currency_code = el.at_xpath('currencyCode').text.strip
55
+ @customer_id = el.at_xpath('customerId').text.strip
56
+ @billing_address = BillingAddress.from_element( el.at_xpath('billingAddress') )
57
+ end
58
+
59
+ public
60
+
61
+ ############################################################################
62
+ # Account - Service Account
63
+
64
+ def client_accounts(refresh = false)
65
+ _client_accounts unless @accounts and !refresh
66
+ @accounts
67
+ end
68
+
69
+ def p_client_accounts(refresh = false)
70
+ cs = client_accounts(refresh)
71
+ # puts "#{cs.length} client accounts"
72
+ cs.each do |account|
73
+ puts account.to_s
74
+ end
75
+ self
76
+ end
77
+
78
+ alias clients client_accounts
79
+ alias p_clients p_client_accounts
80
+
81
+ private
82
+
83
+ def _client_accounts
84
+ soap_message = service.account.client_accounts(credentials)
85
+ add_counters( soap_message.counters )
86
+ els = soap_message.response.xpath("//getClientAccountsReturn")
87
+ @accounts = els.map do |el|
88
+ client_email = el.text
89
+ Account.new( adwords, Credentials.new(@credentials, client_email) )
90
+ end
91
+ end
92
+
93
+ public
94
+
95
+ ###########################################################################
96
+
97
+ end
98
+
99
+ class Account
100
+ include AccountAccountExtension
101
+ end
102
+
103
+ end
@@ -30,6 +30,13 @@ module Sem4r
30
30
  g_accessor :address_line1
31
31
  g_accessor :address_line2
32
32
  g_accessor :city
33
+ g_accessor :country_code
34
+ g_accessor :email, :xpath => 'emailAddress'
35
+ g_accessor :fax_number
36
+ g_accessor :name
37
+ g_accessor :phone, :xpath => 'phoneNumber'
38
+ g_accessor :postal_code
39
+ g_accessor :state
33
40
 
34
41
  def initialize(&block)
35
42
  if block_given?
@@ -37,28 +44,10 @@ module Sem4r
37
44
  end
38
45
  end
39
46
 
40
- # <billingAddress>
41
- # <addressLine1>1600 Amphitheatre Parkway</addressLine1>
42
- # <addressLine2>Building #42</addressLine2>
43
- # <city>Mountain View</city>
44
- # <companyName>Some Company</companyName>
45
- # <countryCode>US</countryCode>
46
- # <emailAddress>Some@email</emailAddress>
47
- # <faxNumber>4085551213</faxNumber>
48
- # <name>Some contact</name>
49
- # <phoneNumber>4085551212</phoneNumber>
50
- # <postalCode>94043</postalCode>
51
- # <state>CA</state>
52
- # </billingAddress>
53
- def self.from_element(el)
54
- new do
55
- company_name el.elements["companyName"].text.strip
56
- address_line1 el.elements["addressLine1"].text.strip
57
- address_line2 el.elements["addressLine2"].text.strip
58
- city el.elements["city"].text.strip
59
- end
47
+ def self.from_element(*args)
48
+ _from_element(*args)
60
49
  end
61
-
50
+
62
51
  def to_s
63
52
  "#{@company_name} #{@address_line1} #{@address_line2} #{@city}"
64
53
  end
@@ -130,11 +130,11 @@ module Sem4r
130
130
 
131
131
  def self.from_element(account, el)
132
132
  new(account) do
133
- @id = el.elements["id"].text.strip.to_i # id is read only
134
- name el.elements["name"].text.strip
135
- start_day el.elements["startDay"].text.strip
136
- end_day el.elements["endDay"].text.strip
137
- @status = el.elements["status"].text.strip # status is read only
133
+ @id = el.at_xpath("id").text.strip.to_i # id is read only
134
+ name el.at_xpath("name").text.strip
135
+ start_day el.at_xpath("startDay").text.strip
136
+ end_day el.at_xpath("endDay").text.strip
137
+ @status = el.at_xpath("status").text.strip # status is read only
138
138
  end
139
139
  end
140
140
 
@@ -144,7 +144,7 @@ module Sem4r
144
144
  return @status unless refresh
145
145
  soap_message = service.report.status(credentials, @id)
146
146
  add_counters( soap_message.counters )
147
- el = REXML::XPath.first( soap_message.response, "//getReportJobStatusResponse/getReportJobStatusReturn")
147
+ el = soap_message.response.xpath("//getReportJobStatusResponse/getReportJobStatusReturn").first
148
148
  @status = el.text
149
149
  end
150
150
 
@@ -163,7 +163,7 @@ module Sem4r
163
163
  def schedule
164
164
  soap_message = service.report.schedule(credentials, to_xml)
165
165
  add_counters( soap_message.counters )
166
- el = REXML::XPath.first( soap_message.response, "//scheduleReportJobResponse/scheduleReportJobReturn")
166
+ el = soap_message.response.xpath("//scheduleReportJobResponse/scheduleReportJobReturn").first
167
167
  @id = el.text
168
168
  # puts "requested report assigned job nr. #{@job_id}"
169
169
  ReportJob.new(self, @id)
@@ -172,7 +172,7 @@ module Sem4r
172
172
  def url
173
173
  soap_message = service.report.url(credentials, @id)
174
174
  add_counters( soap_message.counters )
175
- el = REXML::XPath.first( soap_message.response, "//getReportDownloadUrlResponse/getReportDownloadUrlReturn")
175
+ el = soap_message.response.xpath("//getReportDownloadUrlResponse/getReportDownloadUrlReturn").first
176
176
  url = el.text
177
177
  url
178
178
  end