sem4r 0.1.1

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 (187) hide show
  1. data/LICENSE +22 -0
  2. data/README.rdoc +74 -0
  3. data/Rakefile +126 -0
  4. data/VERSION.yml +5 -0
  5. data/bin/sem +34 -0
  6. data/config/sem4r.example.yml +51 -0
  7. data/examples_blog/2009-11-29-hello-world.rb +12 -0
  8. data/examples_blog/2009-12-12-create-campaign.rb +29 -0
  9. data/examples_blog/2010-02-06-constants-scope.rb +8 -0
  10. data/examples_blog/2010-02-07-ad-parameters.rb +47 -0
  11. data/examples_sem4r/01_get_account.rb +37 -0
  12. data/examples_sem4r/02_get_info.rb +32 -0
  13. data/examples_sem4r/03_list_ad.rb +47 -0
  14. data/examples_sem4r/04_list_keywords.rb +54 -0
  15. data/examples_sem4r/05_request_report.rb +51 -0
  16. data/examples_sem4r/05_request_report_2010.rb +40 -0
  17. data/examples_sem4r/06_create_campaigns.rb +86 -0
  18. data/examples_sem4r/07_create_campaigns_block.rb +99 -0
  19. data/examples_sem4r/07_create_campaigns_simple.rb +65 -0
  20. data/examples_sem4r/08_ad_params.rb +70 -0
  21. data/examples_sem4r/09_targeting_idea.rb +59 -0
  22. data/examples_sem4r/10_get_location.rb +28 -0
  23. data/examples_sem4r/11_submit_bulk_job.rb +82 -0
  24. data/examples_sem4r/12_list_bulk_job.rb +29 -0
  25. data/examples_sem4r/30_prune_empty_adgroup.rb +50 -0
  26. data/examples_sem4r/31_empty_accounts.rb +40 -0
  27. data/examples_sem4r/example_helper.rb +115 -0
  28. data/lib/sem4r/account.rb +349 -0
  29. data/lib/sem4r/ad_extension_override/ad_extension_override_service.rb +30 -0
  30. data/lib/sem4r/ad_group/ad_group.rb +340 -0
  31. data/lib/sem4r/ad_group/ad_group_bids.rb +170 -0
  32. data/lib/sem4r/ad_group/ad_group_service.rb +75 -0
  33. data/lib/sem4r/ad_group/mobile_ad_image.rb +33 -0
  34. data/lib/sem4r/ad_group_ad/ad_group_ad.rb +98 -0
  35. data/lib/sem4r/ad_group_ad/ad_group_ad_operations.rb +38 -0
  36. data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +56 -0
  37. data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +137 -0
  38. data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +98 -0
  39. data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +144 -0
  40. data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +115 -0
  41. data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +35 -0
  42. data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +58 -0
  43. data/lib/sem4r/ad_group_criterion/criterion.rb +67 -0
  44. data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +80 -0
  45. data/lib/sem4r/ad_group_criterion/criterion_placement.rb +66 -0
  46. data/lib/sem4r/ad_param/ad_param.rb +96 -0
  47. data/lib/sem4r/ad_param/ad_param_operation.rb +34 -0
  48. data/lib/sem4r/ad_param/ad_param_service.rb +59 -0
  49. data/lib/sem4r/adwords.rb +242 -0
  50. data/lib/sem4r/api_counters.rb +7 -0
  51. data/lib/sem4r/base.rb +43 -0
  52. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +108 -0
  53. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_selector.rb +56 -0
  54. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +57 -0
  55. data/lib/sem4r/bulk_mutate_job/job_operations.rb +35 -0
  56. data/lib/sem4r/campaign/campaign.rb +193 -0
  57. data/lib/sem4r/campaign/campaign_service.rb +91 -0
  58. data/lib/sem4r/campaign_criterion/campaign_criterion_service.rb +30 -0
  59. data/lib/sem4r/campaign_target/campaign_target_service.rb +30 -0
  60. data/lib/sem4r/cli/cli_command.rb +101 -0
  61. data/lib/sem4r/cli/cli_commands.rb +60 -0
  62. data/lib/sem4r/cli/cli_common_args.rb +293 -0
  63. data/lib/sem4r/cli/cli_download_report.rb +81 -0
  64. data/lib/sem4r/cli/cli_helpers.rb +57 -0
  65. data/lib/sem4r/cli/cli_ideas.rb +100 -0
  66. data/lib/sem4r/cli/cli_list_ads.rb +74 -0
  67. data/lib/sem4r/cli/cli_list_keywords.rb +76 -0
  68. data/lib/sem4r/cli/cli_request_report.rb +104 -0
  69. data/lib/sem4r/cli/cli_sem.rb +64 -0
  70. data/lib/sem4r/common/operation.rb +78 -0
  71. data/lib/sem4r/credentials.rb +86 -0
  72. data/lib/sem4r/extensions.rb +62 -0
  73. data/lib/sem4r/geo_location/geo_location_service.rb +59 -0
  74. data/lib/sem4r/info/info_service.rb +115 -0
  75. data/lib/sem4r/report_definition/report_definition.rb +104 -0
  76. data/lib/sem4r/report_definition/report_definition_operation.rb +34 -0
  77. data/lib/sem4r/report_definition/report_definition_selector.rb +31 -0
  78. data/lib/sem4r/report_definition/report_definition_service.rb +55 -0
  79. data/lib/sem4r/sem4r_error.rb +28 -0
  80. data/lib/sem4r/services/service.rb +74 -0
  81. data/lib/sem4r/services/soap_call.rb +100 -0
  82. data/lib/sem4r/services/soap_connector.rb +284 -0
  83. data/lib/sem4r/services/soap_error.rb +38 -0
  84. data/lib/sem4r/services/soap_message_v13.rb +129 -0
  85. data/lib/sem4r/services/soap_message_v2009.rb +170 -0
  86. data/lib/sem4r/soap_attributes.rb +141 -0
  87. data/lib/sem4r/targeting_idea/targeting_idea.rb +158 -0
  88. data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +200 -0
  89. data/lib/sem4r/targeting_idea/targeting_idea_service.rb +51 -0
  90. data/lib/sem4r/v13_account/account_service.rb +54 -0
  91. data/lib/sem4r/v13_account/billing_address.rb +67 -0
  92. data/lib/sem4r/v13_report/report.rb +185 -0
  93. data/lib/sem4r/v13_report/report_job.rb +51 -0
  94. data/lib/sem4r/v13_report/report_service.rb +89 -0
  95. data/lib/sem4r/v13_traffic_estimator/traffic_estimator_service.rb +30 -0
  96. data/lib/sem4r.rb +142 -0
  97. data/lib/sem4r_cli.rb +40 -0
  98. data/sem4r.gemspec +247 -0
  99. data/spec/aggregates_spec_helper.rb +59 -0
  100. data/spec/fixtures/sem4r.example.yml +26 -0
  101. data/spec/fixtures/services/ad_group/get-first-req.xml +28 -0
  102. data/spec/fixtures/services/ad_group/get-first-res.xml +91 -0
  103. data/spec/fixtures/services/ad_group/get-manual-cpm-bids-req.xml +106 -0
  104. data/spec/fixtures/services/ad_group/get-manual-cpm-bids-res.xml +75 -0
  105. data/spec/fixtures/services/ad_group/mutate_add-req.xml +52 -0
  106. data/spec/fixtures/services/ad_group/mutate_add-res.xml +33 -0
  107. data/spec/fixtures/services/ad_group_ad/get_mobile_ad-req.xml +28 -0
  108. data/spec/fixtures/services/ad_group_ad/get_mobile_ad-res.xml +194 -0
  109. data/spec/fixtures/services/ad_group_ad/get_text_ad-req.xml +29 -0
  110. data/spec/fixtures/services/ad_group_ad/get_text_ad-res.xml +55 -0
  111. data/spec/fixtures/services/ad_group_ad/mutate_add_mobile_ad-req.xml +50 -0
  112. data/spec/fixtures/services/ad_group_ad/mutate_add_mobile_ad-res.xml +42 -0
  113. data/spec/fixtures/services/ad_group_ad/mutate_add_text_ad-req.xml +37 -0
  114. data/spec/fixtures/services/ad_group_ad/mutate_add_text_ad-res.xml +32 -0
  115. data/spec/fixtures/services/ad_group_ad/mutate_add_two_criterions-req.xml +83 -0
  116. data/spec/fixtures/services/ad_group_ad/mutate_add_two_criterions-res.xml +95 -0
  117. data/spec/fixtures/services/ad_group_criterion/get-req.xml +28 -0
  118. data/spec/fixtures/services/ad_group_criterion/get-res.xml +242 -0
  119. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_keyword-req.xml +48 -0
  120. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_keyword-res.xml +47 -0
  121. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_placement-req.xml +35 -0
  122. data/spec/fixtures/services/ad_group_criterion/mutate_add_criterion_placement-res.xml +32 -0
  123. data/spec/fixtures/services/ad_group_criterion/mutate_add_negative_keyword-req.xml +37 -0
  124. data/spec/fixtures/services/ad_group_criterion/mutate_add_negative_keyword-res.xml +51 -0
  125. data/spec/fixtures/services/ad_param/mutate_set-req.xml +43 -0
  126. data/spec/fixtures/services/ad_param/mutate_set-res.xml +29 -0
  127. data/spec/fixtures/services/bulk_mutate_job/get-req.xml +36 -0
  128. data/spec/fixtures/services/bulk_mutate_job/get-res.xml +54 -0
  129. data/spec/fixtures/services/bulk_mutate_job/mutate-req.xml +69 -0
  130. data/spec/fixtures/services/bulk_mutate_job/mutate-res.xml +48 -0
  131. data/spec/fixtures/services/campaign/get-req.xml +37 -0
  132. data/spec/fixtures/services/campaign/get-res.xml +1986 -0
  133. data/spec/fixtures/services/campaign/mutate_add-req.xml +37 -0
  134. data/spec/fixtures/services/campaign/mutate_add-res.xml +42 -0
  135. data/spec/fixtures/services/error.xml +28 -0
  136. data/spec/fixtures/services/info/get_unit_count-req.xml +30 -0
  137. data/spec/fixtures/services/info/get_unit_count-res.xml +29 -0
  138. data/spec/fixtures/services/report_definition/mutate_add-req.xml +24 -0
  139. data/spec/fixtures/services/report_definition/mutate_add-req_orig.xml +45 -0
  140. data/spec/fixtures/services/targeting_idea/get-req-all-options.xml +57 -0
  141. data/spec/fixtures/services/targeting_idea/get-req.xml +60 -0
  142. data/spec/fixtures/services/targeting_idea/get-res.xml +3601 -0
  143. data/spec/fixtures/services/v13_account/get_account_info-req.xml +23 -0
  144. data/spec/fixtures/services/v13_account/get_account_info-res.xml +54 -0
  145. data/spec/fixtures/services/v13_account/get_client_accounts-req.xml +22 -0
  146. data/spec/fixtures/services/v13_account/get_client_accounts-res.xml +37 -0
  147. data/spec/fixtures/services/v13_report/get_all_jobs-req.xml +21 -0
  148. data/spec/fixtures/services/v13_report/get_all_jobs-res.xml +109 -0
  149. data/spec/fixtures/services/v13_report/schedule_report_job-req.xml +56 -0
  150. data/spec/fixtures/services/v13_report/schedule_report_job-res.xml +24 -0
  151. data/spec/sem4r/account_spec.rb +86 -0
  152. data/spec/sem4r/ad_group/ad_group_bids_spec.rb +67 -0
  153. data/spec/sem4r/ad_group/ad_group_service_spec.rb +66 -0
  154. data/spec/sem4r/ad_group/ad_group_spec.rb +212 -0
  155. data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +88 -0
  156. data/spec/sem4r/ad_group_ad/ad_group_ad_service_spec.rb +55 -0
  157. data/spec/sem4r/ad_group_ad/ad_group_ad_spec.rb +173 -0
  158. data/spec/sem4r/ad_group_criterion/ad_group_criterion_bids_spec.rb +60 -0
  159. data/spec/sem4r/ad_group_criterion/ad_group_criterion_service_spec.rb +55 -0
  160. data/spec/sem4r/ad_group_criterion/ad_group_criterion_spec.rb +103 -0
  161. data/spec/sem4r/ad_group_criterion/criterion_spec.rb +85 -0
  162. data/spec/sem4r/ad_param/ad_param_service_spec.rb +55 -0
  163. data/spec/sem4r/ad_param/ad_param_spec.rb +59 -0
  164. data/spec/sem4r/adwords_spec.rb +110 -0
  165. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +63 -0
  166. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +69 -0
  167. data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +48 -0
  168. data/spec/sem4r/campaign/campaign_service_spec.rb +66 -0
  169. data/spec/sem4r/campaign/campaign_spec.rb +105 -0
  170. data/spec/sem4r/cli/cli_spec.rb +71 -0
  171. data/spec/sem4r/credentials_spec.rb +65 -0
  172. data/spec/sem4r/report_definition/report_definition_service_spec.rb +44 -0
  173. data/spec/sem4r/report_definition/report_definition_spec.rb +105 -0
  174. data/spec/sem4r/rexml_parsing_spec.rb +103 -0
  175. data/spec/sem4r/services/service_spec.rb +36 -0
  176. data/spec/sem4r/services/soap_call_spec.rb +115 -0
  177. data/spec/sem4r/services/soap_message_v13_spec.rb +54 -0
  178. data/spec/sem4r/soap_attributes_spec.rb +116 -0
  179. data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +120 -0
  180. data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +44 -0
  181. data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +53 -0
  182. data/spec/sem4r/v13_account/account_service_spec.rb +60 -0
  183. data/spec/sem4r/v13_report/report_service_spec.rb +104 -0
  184. data/spec/sem4r/v13_report/report_spec.rb +79 -0
  185. data/spec/sem4r_spec_helper.rb +353 -0
  186. data/spec/spec_helper.rb +12 -0
  187. metadata +375 -0
@@ -0,0 +1,129 @@
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
+ module Sem4r
25
+
26
+ class SoapMessageV13
27
+
28
+ attr_reader :response
29
+ attr_reader :counters
30
+
31
+ def initialize(connector, credentials)
32
+ @credentials = credentials
33
+ @connector = connector
34
+ @response = nil
35
+ @counters = {}
36
+ end
37
+
38
+ def body=(soap_body_content)
39
+ @soap_body_content = soap_body_content
40
+ end
41
+
42
+ def send(service_url, soap_action)
43
+ soap_message = build_soap_message
44
+ response_xml = @connector.send(service_url, soap_action, soap_message)
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)
48
+
49
+ # extract information from header
50
+ # <soapenv:Header>
51
+ # <responseTime soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v13">16</responseTime>
52
+ # <operations soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v13">5</operations>
53
+ # <units soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v13">5</units>
54
+ # <requestId soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v13">abade53d3dbecd45600e7d14563f10f1</requestId>
55
+ # </soapenv:Header>
56
+ header = REXML::XPath.first(@response, "//soapenv:Header")
57
+ if header
58
+ @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
62
+ }
63
+ end
64
+
65
+ # check soap fault
66
+ #<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
67
+ # <soapenv:Body>
68
+ # <soapenv:Fault>
69
+ # <faultcode>soapenv:Server.generalException</faultcode>
70
+ # <faultstring>An internal error has occurred. Please retry your request.</faultstring>
71
+ # <detail>
72
+ # <fault xmlns:ns1="https://adwords.google.com/api/adwords/v13">
73
+ # <code>0</code>
74
+ # <message>An internal error has occurred. Please retry your request.</message>
75
+ # </fault>
76
+ # </detail>
77
+ # </soapenv:Fault>
78
+ # </soapenv:Body>
79
+ #</soapenv:Envelope>
80
+ fault_el = REXML::XPath.first(@response, "//soapenv:Fault")
81
+ if fault_el
82
+ fault_code = fault_el.elements['faultcode'].text
83
+ fault_string = fault_el.elements['faultstring'].text
84
+ raise SoapError, "#{fault_code}: '#{fault_string}'"
85
+ end
86
+ self
87
+ end
88
+
89
+ private
90
+
91
+ def build_soap_header(credentials)
92
+ str= <<-EOFS
93
+ <env:Header>
94
+ <email env:mustUnderstand="0">#{credentials.email}</email>
95
+ <password env:mustUnderstand="0">#{credentials.password}</password>
96
+ EOFS
97
+
98
+ if credentials.client_email
99
+ str += "<clientEmail env:mustUnderstand=\"0\">#{credentials.client_email}</clientEmail>"
100
+ end
101
+
102
+ str += <<-EOFS
103
+ <useragent env:mustUnderstand="0">#{credentials.useragent}</useragent>
104
+ <developerToken env:mustUnderstand="0">#{credentials.developer_token}</developerToken>
105
+ </env:Header>
106
+ EOFS
107
+ str
108
+ end
109
+
110
+ def build_soap_message
111
+ soap_message = '<?xml version="1.0" encoding="utf-8" ?>'
112
+ soap_message +=<<-EOFS
113
+ <env:Envelope
114
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
115
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
116
+ xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
117
+ EOFS
118
+ soap_message += build_soap_header(@credentials)
119
+ soap_message += "<env:Body>"
120
+ soap_message += @soap_body_content
121
+ soap_message += <<-EOFS
122
+ </env:Body>
123
+ </env:Envelope>
124
+ EOFS
125
+ soap_message
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,170 @@
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
+ module Sem4r
25
+
26
+ class SoapMessageV2009
27
+
28
+ attr_reader :response
29
+ attr_reader :counters
30
+
31
+ def initialize(connector, credentials)
32
+ @connector = connector
33
+ @credentials = credentials
34
+ @response = nil
35
+ @counters = {}
36
+ @logger = nil
37
+ end
38
+
39
+ def logger=(logger)
40
+ @logger= logger
41
+ end
42
+
43
+ def init(header_namespace, service_namespace)
44
+ @header_namespace = header_namespace
45
+ @service_namespace = service_namespace
46
+ end
47
+
48
+ def body=(soap_body_content)
49
+ @soap_body_content = soap_body_content
50
+ end
51
+
52
+ def send(service_url)
53
+ response_xml = @connector.send(service_url, "", build_soap_message)
54
+ # erase namespace so it more simple parsing the xml
55
+ response_xml = response_xml.gsub(/ns\d:/, "")
56
+ @response = REXML::Document.new(response_xml)
57
+
58
+ #
59
+ # extract information from header
60
+ #
61
+ header = REXML::XPath.first(@response, "//ResponseHeader")
62
+ if header
63
+ @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
67
+ }
68
+ end
69
+
70
+ #
71
+ # check soap fault
72
+ #
73
+ fault_el = REXML::XPath.first(@response, "//soap:Fault")
74
+ if fault_el
75
+ fault_string = fault_el.elements['faultstring'].text
76
+ @logger.error("soap error: #{fault_string}") if @logger
77
+ raise fault_string
78
+ end
79
+ self
80
+
81
+ # <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
82
+ # <soap:Header>
83
+ # <ResponseHeader xmlns="https://adwords.google.com/api/adwords/cm/v200909">
84
+ # <requestId>e7c3b00f339bcaf56b7df47db97efdb7</requestId>
85
+ # <operations>1</operations>
86
+ # <responseTime>231</responseTime>
87
+ # <units>1</units>
88
+ # </ResponseHeader>
89
+ # </soap:Header>
90
+ # <soap:Body>
91
+ # <soap:Fault>
92
+ # <faultcode>soap:Server</faultcode>
93
+ # <faultstring>[CampaignError.DUPLICATE_CAMPAIGN_NAME @ operations[0].operand.name]</faultstring>
94
+ # <detail>
95
+ # <ApiExceptionFault xmlns="https://adwords.google.com/api/adwords/cm/v200909">
96
+ # <message>[CampaignError.DUPLICATE_CAMPAIGN_NAME @ operations[0].operand.name]</message>
97
+ # <ApplicationException.Type>ApiException</ApplicationException.Type>
98
+ # <errors xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CampaignError">
99
+ # <fieldPath>operations[0].operand.name</fieldPath>
100
+ # <trigger></trigger>
101
+ # <ApiError.Type>CampaignError</ApiError.Type>
102
+ # <reason>DUPLICATE_CAMPAIGN_NAME</reason>
103
+ # </errors>
104
+ # </ApiExceptionFault>
105
+ # </detail>
106
+ # </soap:Fault>
107
+ # </soap:Body>
108
+ #</soap:Envelope>
109
+ end
110
+
111
+ private
112
+
113
+ def build_soap_header
114
+ auth_token = @credentials.authentication_token
115
+
116
+ str = "<env:Header>"
117
+
118
+ if @service_namespace
119
+ str += "<s:RequestHeader env:mustUnderstand=\"0\">"
120
+ else
121
+ str += "<RequestHeader env:mustUnderstand=\"0\">"
122
+ end
123
+
124
+ str +=<<-EOFS
125
+ <authToken>#{auth_token}</authToken>
126
+ <userAgent>#{@credentials.useragent}</userAgent>
127
+ <developerToken>#{@credentials.developer_token}</developerToken>
128
+ EOFS
129
+
130
+ if @credentials.client_email
131
+ str += "<clientEmail>#{@credentials.client_email}</clientEmail>"
132
+ end
133
+
134
+ if @service_namespace
135
+ str += "</s:RequestHeader>"
136
+ else
137
+ str += "</RequestHeader>"
138
+ end
139
+ str += "</env:Header>"
140
+ str
141
+ end
142
+
143
+ def build_soap_message
144
+ soap_message = '<?xml version="1.0" encoding="utf-8" ?>'
145
+ soap_message +=<<-EOFS
146
+ <env:Envelope
147
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
148
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
149
+ xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
150
+ xmlns="#{@header_namespace}"
151
+ EOFS
152
+
153
+ if @service_namespace
154
+ soap_message += " xmlns:s=\"#{@service_namespace}\""
155
+ end
156
+ soap_message += ">"
157
+
158
+ soap_message += build_soap_header
159
+ soap_message += "<env:Body>"
160
+ soap_message += @soap_body_content
161
+ soap_message += <<-EOFS
162
+ </env:Body>
163
+ </env:Envelope>
164
+ EOFS
165
+ soap_message
166
+ end
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,141 @@
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
+ module Sem4r
25
+ module SoapAttributes
26
+
27
+ def self.included(base)
28
+ base.extend ClassMethods
29
+ end
30
+
31
+ module ClassMethods
32
+
33
+ def enum(set, values)
34
+ tmp = values.map do |v|
35
+ if const_defined?(v.to_sym)
36
+ const_get(v.to_sym)
37
+ else
38
+ const_set(v.to_sym, v.to_s)
39
+ end
40
+ end
41
+ const_set( set.to_sym, tmp )
42
+ end
43
+
44
+ def g_reader(name)
45
+ attr_reader name
46
+ end
47
+
48
+ #
49
+ # constraints
50
+ # values_in
51
+ #
52
+ # TODO: g_accessor prende in input anche elements ed estrae automaticamente l'elemento che interessa
53
+ # cioe' invece di scrivere headline el.elements["headline"].text dovrebbe bastare scrivere
54
+ # headline el
55
+ def g_accessor(name, constraints = {})
56
+ name = name.to_s
57
+
58
+ # vales_in
59
+ enum = nil
60
+ if constraints.key?(:values_in)
61
+ enum = const_get(constraints[:values_in])
62
+ end
63
+
64
+ # if_type
65
+ if_type = nil
66
+ if constraints.key?(:if_type)
67
+ if_type = constraints[:if_type]
68
+ other_instance_var = "type"
69
+ end
70
+
71
+ # default_value
72
+ default_value = nil
73
+ if constraints.key?(:default)
74
+ default_value = constraints[:default]
75
+ end
76
+
77
+ define_method "#{name}=" do |value|
78
+ if enum and !enum.include?(value)
79
+ raise "Value '#{value}' not permitted "
80
+ end
81
+ if if_type
82
+ type = instance_variable_get "@#{other_instance_var}"
83
+ raise "type must be '#{if_type}' instead of '#{type}'" unless if_type == type
84
+ end
85
+ instance_variable_set "@#{name}", value
86
+ end
87
+
88
+ define_method "#{name}" do |*values| # |value = nil| is incorrect in ruby 1.8
89
+ raise ArgumentError, "wrong number of arguments (#{values.size} for 0)" if values.length > 1
90
+ value = values.first
91
+ if value
92
+ self.__send__("#{name}=", value)
93
+ elsif instance_variable_defined? "@#{name}"
94
+ instance_variable_get "@#{name}"
95
+ else
96
+ default_value
97
+ end
98
+ end
99
+ end
100
+
101
+ def g_set_accessor(column, constraints = {})
102
+ column = column.to_s
103
+ columns = "#{column}s"
104
+
105
+ # values_in
106
+ enum = nil
107
+ if constraints.key?(:values_in)
108
+ enum = const_get(constraints[:values_in])
109
+ end
110
+
111
+ define_method "#{column}=" do |value|
112
+ if enum and !enum.include?(value)
113
+ raise "Value not permitted #{value}"
114
+ end
115
+ instance_eval <<-EOFS
116
+ @#{columns} ||= []
117
+ @#{columns} << value unless @#{columns}.include?(value)
118
+ EOFS
119
+ end
120
+
121
+ alias_method column, "#{column}="
122
+
123
+ define_method "#{columns}" do |*values|
124
+ if values and !values.empty?
125
+ instance_eval "@#{columns} ||= []"
126
+ values.each do |value|
127
+ instance_eval "@#{column}(value)"
128
+ end
129
+ else
130
+ instance_eval "@#{columns} ||= []"
131
+ instance_variable_get "@#{columns}"
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+ end
141
+
@@ -0,0 +1,158 @@
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
+
28
+ class TargetingIdea
29
+ include SoapAttributes
30
+
31
+ enum :AttributeTypes, [
32
+ :AdFormatSpecListAttribute,
33
+ :BooleanAttribute,
34
+ :DoubleAttribute,
35
+ :IdeaTypeAttribute,
36
+ :InStreamAdInfoAttribute,
37
+ :IntegerAttribute,
38
+ :IntegerSetAttribute,
39
+ :LongAttribute,
40
+ :MonthlySearchVolumeAttribute,
41
+ :PlacementTypeAttribute,
42
+ :StringAttribute,
43
+ :WebpageDescriptorAttribute,
44
+ :KeywordAttribute,
45
+ :MoneyAttribute,
46
+ :PlacementAttribute,
47
+ :LongRangeAttribute,
48
+ :MonthlySearchVolumeAttribute]
49
+
50
+ attr_reader :attributes
51
+
52
+ def initialize(&block)
53
+ if block_given?
54
+ block.arity < 1 ? instance_eval(&block) : block.call(self)
55
+ end
56
+ end
57
+
58
+ def self.from_element(el)
59
+ els = REXML::XPath.match( el, "data")
60
+ @attributes = els.map do |el|
61
+ el1 = el.elements["value"]
62
+ xml_type = el1.elements["Attribute.Type"].text.strip
63
+ case xml_type
64
+ when IdeaTypeAttribute
65
+ TIdeaTypeAttribute.from_element(el1)
66
+ when KeywordAttribute
67
+ TKeywordAttribute.from_element(el1)
68
+ when MonthlySearchVolumeAttribute
69
+ TMonthlySearchVolumeAttribute.from_element(el1)
70
+ end
71
+ end
72
+ end
73
+
74
+ def to_s
75
+ @attributes.collect { |attr| attr.to_s }.join("\n")
76
+ end
77
+
78
+ end
79
+
80
+ class TKeywordAttribute
81
+ include SoapAttributes
82
+
83
+ g_accessor :text
84
+ g_accessor :match_type
85
+
86
+ def initialize(&block)
87
+ if block_given?
88
+ block.arity < 1 ? instance_eval(&block) : block.call(self)
89
+ end
90
+ end
91
+
92
+ def self.from_element( el )
93
+ el1 = el.elements["value"]
94
+ new do
95
+ text el1.elements["text"].text
96
+ match_type el1.elements["matchType"].text
97
+ end
98
+ end
99
+
100
+ def to_s
101
+ "Keyword '#{text}' '#{match_type}'"
102
+ end
103
+ end
104
+
105
+ class TMonthlySearchVolumeAttribute
106
+ include SoapAttributes
107
+
108
+ g_accessor :text
109
+ g_accessor :values
110
+
111
+ def initialize(&block)
112
+ if block_given?
113
+ block.arity < 1 ? instance_eval(&block) : block.call(self)
114
+ end
115
+ end
116
+
117
+ def self.from_element( el )
118
+ historical_values = []
119
+ el.elements.each do |node|
120
+ 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"]
124
+ historical_values << historical_value
125
+ end
126
+ new do
127
+ values historical_values
128
+ end
129
+ end
130
+
131
+ def to_s
132
+ "Values: #{values.inspect}"
133
+ end
134
+ end
135
+
136
+ class TIdeaTypeAttribute
137
+ include SoapAttributes
138
+
139
+ g_accessor :value
140
+
141
+ def initialize(&block)
142
+ if block_given?
143
+ block.arity < 1 ? instance_eval(&block) : block.call(self)
144
+ end
145
+ end
146
+
147
+ def self.from_element( el )
148
+ new do
149
+ value el.elements["value"].text
150
+ end
151
+ end
152
+
153
+ def to_s
154
+ "Idea '#{value}'"
155
+ end
156
+ end
157
+
158
+ end