sem4r 0.1.2 → 0.1.3

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 (175) hide show
  1. data/Gemfile +13 -2
  2. data/Gemfile.lock +13 -7
  3. data/README.rdoc +44 -7
  4. data/Rakefile +2 -122
  5. data/VERSION.yml +1 -1
  6. data/bin/sem +1 -0
  7. data/examples_sem4r/01_get_account.rb +1 -0
  8. data/examples_sem4r/02_get_info.rb +1 -0
  9. data/examples_sem4r/03_list_ad.rb +1 -0
  10. data/examples_sem4r/04_list_keywords.rb +1 -0
  11. data/examples_sem4r/05_request_report.rb +25 -14
  12. data/examples_sem4r/{05_request_report_2010.rb → 06_request_report_definition.rb} +2 -4
  13. data/examples_sem4r/{06_create_campaigns.rb → 07_create_campaigns.rb} +1 -0
  14. data/examples_sem4r/{07_create_campaigns_block.rb → 08_create_campaigns_block.rb} +1 -0
  15. data/examples_sem4r/{07_create_campaigns_simple.rb → 09_create_campaigns_simple.rb} +1 -0
  16. data/examples_sem4r/{08_ad_params.rb → 10_ad_params.rb} +1 -0
  17. data/examples_sem4r/{09_targeting_idea.rb → 11_targeting_idea.rb} +1 -0
  18. data/examples_sem4r/{10_get_location.rb → 12_get_location.rb} +1 -0
  19. data/examples_sem4r/{11_submit_bulk_job.rb → 13_submit_bulk_job.rb} +1 -1
  20. data/examples_sem4r/{12_list_bulk_job.rb → 14_list_bulk_job.rb} +3 -0
  21. data/examples_sem4r/30_prune_empty_adgroup.rb +1 -0
  22. data/examples_sem4r/31_empty_accounts.rb +1 -0
  23. data/examples_sem4r/example_helper.rb +2 -1
  24. data/lib/sem4r/ad_group/ad_group.rb +22 -7
  25. data/lib/sem4r/ad_group/ad_group_bids.rb +1 -1
  26. data/lib/sem4r/ad_group/ad_group_service.rb +12 -5
  27. data/lib/sem4r/ad_group_ad/ad_group_ad.rb +1 -0
  28. data/lib/sem4r/ad_group_ad/ad_group_ad_operations.rb +1 -0
  29. data/lib/sem4r/ad_group_ad/ad_group_ad_service.rb +14 -9
  30. data/lib/sem4r/ad_group_ad/ad_group_mobile_ad.rb +16 -25
  31. data/lib/sem4r/ad_group_ad/ad_group_text_ad.rb +18 -14
  32. data/lib/sem4r/ad_group_criterion/ad_group_criterion.rb +27 -19
  33. data/lib/sem4r/ad_group_criterion/ad_group_criterion_bids.rb +2 -1
  34. data/lib/sem4r/ad_group_criterion/ad_group_criterion_operations.rb +1 -0
  35. data/lib/sem4r/ad_group_criterion/ad_group_criterion_service.rb +9 -4
  36. data/lib/sem4r/ad_group_criterion/criterion.rb +1 -0
  37. data/lib/sem4r/ad_group_criterion/criterion_keyword.rb +1 -0
  38. data/lib/sem4r/ad_group_criterion/criterion_placement.rb +1 -0
  39. data/lib/sem4r/ad_param/ad_param.rb +24 -8
  40. data/lib/sem4r/ad_param/ad_param_service.rb +8 -5
  41. data/lib/sem4r/adwords.rb +232 -76
  42. data/lib/sem4r/base.rb +1 -1
  43. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job.rb +41 -35
  44. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_account_extension.rb +49 -15
  45. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_selector.rb +29 -20
  46. data/lib/sem4r/bulk_mutate_job/bulk_mutate_job_service.rb +13 -14
  47. data/lib/sem4r/bulk_mutate_job/job_operations.rb +1 -1
  48. data/lib/sem4r/campaign/campaign.rb +1 -0
  49. data/lib/sem4r/campaign/campaign_account_extension.rb +2 -1
  50. data/lib/sem4r/campaign/campaign_service.rb +9 -5
  51. data/lib/sem4r/credentials.rb +11 -5
  52. data/lib/sem4r/geo_location/address.rb +56 -0
  53. data/lib/sem4r/geo_location/geo_location_account_extension.rb +27 -6
  54. data/lib/sem4r/geo_location/geo_location_selector.rb +57 -0
  55. data/lib/sem4r/geo_location/geo_location_service.rb +9 -16
  56. data/lib/sem4r/info/info_account_extension.rb +3 -2
  57. data/lib/sem4r/info/info_selector.rb +2 -1
  58. data/lib/sem4r/info/info_service.rb +10 -3
  59. data/lib/sem4r/operation.rb +39 -20
  60. data/lib/sem4r/report_definition/report_definition.rb +37 -19
  61. data/lib/sem4r/report_definition/report_definition_account_extension.rb +66 -16
  62. data/lib/sem4r/report_definition/report_definition_operation.rb +2 -2
  63. data/lib/sem4r/report_definition/report_definition_selector.rb +1 -1
  64. data/lib/sem4r/report_definition/report_definition_service.rb +11 -7
  65. data/lib/sem4r/report_definition/report_field.rb +3 -2
  66. data/lib/sem4r/sem4r_templates.rb +77 -0
  67. data/lib/sem4r/{services/service.rb → service.rb} +23 -23
  68. data/lib/sem4r/targeting_idea/targeting_idea.rb +4 -4
  69. data/lib/sem4r/targeting_idea/targeting_idea_account_extension.rb +1 -1
  70. data/lib/sem4r/targeting_idea/targeting_idea_selector.rb +6 -6
  71. data/lib/sem4r/targeting_idea/targeting_idea_service.rb +7 -5
  72. data/lib/sem4r/v13_account/account_account_extension.rb +7 -10
  73. data/lib/sem4r/v13_account/account_service.rb +10 -4
  74. data/lib/sem4r/v13_account/billing_address.rb +2 -2
  75. data/lib/sem4r/v13_report/report_service.rb +11 -9
  76. data/lib/sem4r.rb +46 -31
  77. data/lib/{sem4r/cli → sem4r_cli}/cli_helpers.rb +3 -2
  78. data/lib/{sem4r/cli/cli_command.rb → sem4r_cli/cli_mini_framework.rb} +77 -32
  79. data/lib/{sem4r/cli/cli_common_args.rb → sem4r_cli/cli_sem.rb} +177 -157
  80. data/lib/{sem4r/cli/commands/cli_list_ads.rb → sem4r_cli/commands/cli_ads.rb} +7 -9
  81. data/lib/sem4r_cli/commands/cli_campaign.rb +69 -0
  82. data/lib/{sem4r/cli/commands/cli_list_client.rb → sem4r_cli/commands/cli_clients.rb} +2 -2
  83. data/lib/{sem4r/cli → sem4r_cli}/commands/cli_ideas.rb +35 -27
  84. data/lib/{sem4r/cli → sem4r_cli}/commands/cli_info.rb +3 -4
  85. data/lib/{sem4r/cli/commands/cli_repdef.rb → sem4r_cli/commands/cli_job.rb} +57 -55
  86. data/lib/{sem4r/cli/commands/cli_list_keywords.rb → sem4r_cli/commands/cli_keywords.rb} +14 -13
  87. data/lib/sem4r_cli/commands/cli_profile.rb +138 -0
  88. data/lib/{sem4r/cli/commands/cli_report.rb → sem4r_cli/commands/cli_repdef.rb} +67 -20
  89. data/lib/{sem4r/cli/commands/cli_request_report.rb → sem4r_cli/commands/cli_report.rb} +82 -19
  90. data/lib/sem4r_cli.rb +5 -7
  91. data/lib/sem4r_soap/http_connector.rb +206 -0
  92. data/lib/{soap_helpers → sem4r_soap}/soap_attributes.rb +8 -3
  93. data/lib/{sem4r/services → sem4r_soap}/soap_dumper.rb +36 -20
  94. data/lib/{sem4r/services → sem4r_soap}/soap_error.rb +5 -2
  95. data/lib/sem4r_soap/soap_response.rb +75 -0
  96. data/lib/sem4r_soap/soap_service.rb +137 -0
  97. data/lib/{sem4r/cli/cli_sem.rb → sem4r_soap/soap_service_v13.rb} +27 -28
  98. data/lib/sem4r_soap/soap_service_v2010.rb +80 -0
  99. data/lib/sem4r_soap.rb +17 -0
  100. data/sem4r.gemspec +93 -58
  101. data/spec/build_fixtures.rb +49 -42
  102. data/spec/fixtures/password.example.yml +3 -0
  103. data/spec/fixtures/sem4r.example.yml +6 -0
  104. data/spec/fixtures/{services/error.xml → soap_error.xml} +0 -0
  105. data/spec/fixtures/soap_error2.xml +29 -0
  106. data/spec/helpers/dump_interceptor.rb +90 -0
  107. data/spec/helpers/fixtures_bulk_mutate_job.rb +48 -0
  108. data/spec/helpers/fixtures_geo_location.rb +45 -0
  109. data/{lib/sem4r/campaign_criterion/campaign_criterion_service.rb → spec/helpers/fixtures_info.rb} +10 -6
  110. data/spec/helpers/fixtures_report_definition.rb +65 -0
  111. data/spec/{rspec_hash.rb → helpers/rspec_hash.rb} +1 -0
  112. data/spec/{rspec_matchers.rb → helpers/rspec_matchers.rb} +46 -19
  113. data/spec/{rspec_sem4r_helper.rb → helpers/rspec_sem4r_helper.rb} +19 -16
  114. data/spec/{sem4r_stubs.rb → helpers/sem4r_stubs.rb} +5 -4
  115. data/spec/rspec_helper.rb +4 -4
  116. data/spec/sem4r/account_spec.rb +2 -3
  117. data/spec/sem4r/ad_group/ad_group_service_spec.rb +1 -1
  118. data/spec/sem4r/ad_group/ad_group_spec.rb +1 -1
  119. data/spec/sem4r/ad_group_ad/ad_group_ad_operation_spec.rb +3 -3
  120. data/spec/sem4r/adwords_spec.rb +62 -39
  121. data/{lib/sem4r/cli/commands/cli_list_campaign.rb → spec/sem4r/bulk_mutate_job/bulk_mutate_job_selector_spec.rb} +14 -11
  122. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_service_spec.rb +2 -3
  123. data/spec/sem4r/bulk_mutate_job/bulk_mutate_job_spec.rb +23 -17
  124. data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-req.xml +35 -0
  125. data/spec/sem4r/bulk_mutate_job/fixtures/get-list_job-res.xml +417 -0
  126. data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-req.xml +106 -0
  127. data/spec/sem4r/bulk_mutate_job/fixtures/mutate-add_job-res.xml +48 -0
  128. data/spec/sem4r/bulk_mutate_job/job_operation_spec.rb +5 -7
  129. data/spec/sem4r/campaign/campaign_service_spec.rb +2 -2
  130. data/spec/sem4r/credentials_spec.rb +10 -8
  131. data/spec/sem4r/geo_location/address_spec.rb +62 -0
  132. data/spec/sem4r/geo_location/fixtures/get-req.xml +42 -0
  133. data/spec/sem4r/geo_location/fixtures/get-res.xml +60 -0
  134. data/spec/sem4r/info/fixtures/get-req.xml +34 -0
  135. data/spec/sem4r/info/fixtures/get-res.xml +27 -0
  136. data/spec/sem4r/nokogiri_parsing_spec.rb +1 -0
  137. data/spec/sem4r/operation_spec.rb +72 -0
  138. data/spec/sem4r/report_definition/fixtures/get-list-repdef-req.xml +22 -0
  139. data/spec/sem4r/report_definition/fixtures/get-list-repdef-res.xml +73 -0
  140. data/spec/sem4r/report_definition/fixtures/getReportFields-req.xml +24 -0
  141. data/spec/sem4r/report_definition/fixtures/getReportFields-res.xml +1199 -0
  142. data/spec/sem4r/report_definition/fixtures/mutate-add-report-req.xml +63 -0
  143. data/spec/sem4r/report_definition/fixtures/mutate-add-report-res.xml +68 -0
  144. data/spec/sem4r/report_definition/report_definition_service_spec.rb +5 -5
  145. data/spec/sem4r/report_definition/report_definition_spec.rb +28 -43
  146. data/{lib/sem4r/cli/commands/cli_list_report.rb → spec/sem4r/report_definition/report_field_spec.rb} +12 -10
  147. data/spec/sem4r/rexml_parsing_spec.rb +1 -0
  148. data/spec/sem4r/{services/service_spec.rb → service_spec.rb} +1 -1
  149. data/spec/sem4r/targeting_idea/targeting_idea_selector_spec.rb +1 -1
  150. data/spec/sem4r/targeting_idea/targeting_idea_service_spec.rb +1 -1
  151. data/spec/sem4r/targeting_idea/targeting_idea_spec.rb +1 -1
  152. data/spec/sem4r/v13_account/account_service_spec.rb +2 -2
  153. data/spec/sem4r/v13_report/report_service_spec.rb +2 -2
  154. data/spec/{sem4r/cli → sem4r_cli}/cli_spec.rb +22 -27
  155. data/spec/{soap_helpers → sem4r_soap}/soap_attributes_spec.rb +3 -3
  156. data/spec/{sem4r/services/soap_message_v13_spec.rb → sem4r_soap/soap_response_spec.rb} +10 -15
  157. data/spec/{sem4r/services/soap_call_spec.rb → sem4r_soap/soap_service_spec.rb} +35 -54
  158. data/tasks/jeweler.rake +66 -0
  159. data/tasks/rspec.rake +21 -0
  160. data/tasks/sem4r.rake +25 -0
  161. data/tasks/yard.rake +31 -0
  162. metadata +173 -76
  163. data/lib/sem4r/ad_extension_override/ad_extension_override_service.rb +0 -30
  164. data/lib/sem4r/api_counters.rb +0 -8
  165. data/lib/sem4r/campaign_target/campaign_target_service.rb +0 -30
  166. data/lib/sem4r/cli/commands/cli_download_report.rb +0 -82
  167. data/lib/sem4r/services/http_connector.rb +0 -93
  168. data/lib/sem4r/services/soap_call.rb +0 -122
  169. data/lib/sem4r/services/soap_connector.rb +0 -139
  170. data/lib/sem4r/services/soap_message_v13.rb +0 -135
  171. data/lib/sem4r/services/soap_message_v2010.rb +0 -184
  172. data/lib/sem4r/v13_traffic_estimator/traffic_estimator_service.rb +0 -30
  173. data/spec/aggregates_rspec_helper.rb +0 -59
  174. data/spec/sem4r/report_definition/fixtures/mutate_add-req.xml +0 -24
  175. data/spec/sem4r/report_definition/fixtures/mutate_add-req_orig.xml +0 -45
data/lib/sem4r/adwords.rb CHANGED
@@ -27,80 +27,149 @@ module Sem4r
27
27
  class Adwords
28
28
 
29
29
  attr_reader :profile
30
-
30
+
31
31
  class << self
32
- def sandbox( config = nil )
33
- new( "sandbox", config )
32
+
33
+ #
34
+ # Initialize Adwords with sandbox profile
35
+ # @see Adwords#initialize
36
+ #
37
+ def sandbox(options = nil)
38
+ new("sandbox", options)
34
39
  end
35
40
 
36
- def production( config = nil )
37
- new( "production", config )
41
+ #
42
+ # Initialize Adwords with production profile
43
+ # @see Adwords#initialize
44
+ #
45
+ def production(options = nil)
46
+ new("production", options)
38
47
  end
39
48
 
40
- def search_config_file
41
- config_filename = "sem4r.yml"
49
+ #
50
+ # @private
51
+ # Search configuration file in standard locations
52
+ # @return [OpenStruct] with
53
+ # :config_dir
54
+ # :config_file
55
+ # :password_file
56
+ #
57
+ def search_config
58
+ config_filename = "sem4r.yml"
59
+ password_filename = "sem4r_passwords.yml"
42
60
 
43
61
  #
44
62
  # try current directory
45
63
  #
46
- return File.expand_path(config_filename) if File.exists?( config_filename)
64
+ # return File.expand_path(config_filename) if File.exists?( config_filename)
47
65
 
48
66
  #
49
67
  # try ~/.sem4r/sem4r.yml
50
68
  #
51
69
  # require 'etc'
52
70
  # homedir = Etc.getpwuid.dir
53
- homedir = ENV['HOME']
54
- config_filepath = File.join( homedir, ".sem4r", config_filename)
55
- return config_filepath if File.exists?( config_filepath )
71
+ homedir = ENV['HOME']
72
+ config_filepath = File.join(homedir, ".sem4r", config_filename)
73
+ if File.exists?(config_filepath)
74
+ return OpenStruct.new(
75
+ :config_dir => File.join(homedir, ".sem4r"),
76
+ :config_file => config_filepath,
77
+ :password_file => File.join(homedir, ".sem4r", password_filename))
78
+ end
56
79
 
57
80
  #
58
81
  # try <home_sem4r>/config/sem4r
59
82
  #
60
- config_filepath = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "..", "config", config_filename ) )
61
- return config_filepath if File.exists?( config_filepath )
83
+ config_filepath = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", config_filename))
84
+ if File.exists?(config_filepath)
85
+ return OpenStruct.new(
86
+ :config_dir => nil,
87
+ :config_file => config_filepath,
88
+ :password_file => nil)
89
+ end
62
90
 
63
- config_filepath = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "..", "config", "sem4r.example.yml" ) )
64
- return config_filepath if File.exists?( config_filepath )
91
+ config_filepath = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", "sem4r.example.yml"))
92
+ if File.exists?(config_filepath)
93
+ return OpenStruct.new(
94
+ :config_dir => nil,
95
+ :config_file => config_filepath,
96
+ :password_file => nil)
97
+ end
65
98
 
66
- nil
99
+ OpenStruct.new(
100
+ :config_dir => nil,
101
+ :config_file => nil,
102
+ :password_file => nil)
67
103
  end
68
104
 
69
- def profiles( profile_file = nil )
70
- profile_file = search_config_file unless profile_file
105
+ #
106
+ # Return list of profile contained into config file
107
+ #
108
+ def profiles(profile_file = nil)
71
109
  unless profile_file
72
- raise Sem4rError, "config file 'sem4r' not found"
110
+ config = search_config
111
+ unless config
112
+ raise Sem4rError, "config file 'sem4r' not found"
113
+ end
114
+ profile_file = config.config_file
73
115
  end
74
116
  # puts "Loaded profiles from #{profile_file}"
75
- yaml = YAML::load( File.open( profile_file ) )
117
+ yaml = YAML::load(File.open(profile_file))
76
118
  profiles = yaml['google_adwords']
77
119
  profiles
78
120
  end
79
121
  end
80
122
 
81
- # new( "sandbox", {:email=>"..."} )
82
- # new( {:environment=>"...", email => "..." } )
83
- # new( "sandbox" )
84
- # new() sandbox default
85
- def initialize( profile = "sandbox", options = nil )
86
- @logger = nil
123
+ #
124
+ # initialize Adwords lib
125
+ # profiles "sandbox" and "production" have blocked environment
126
+ #
127
+ # @param [String] profile name
128
+ # @param [Hash] options the options for profile.
129
+ #
130
+ # @option opts [String] :config_file default to "~/.sem4r/sem4r.yml"
131
+ # @option opts [String] :password_file default to "~/.sem4r/sem4r_password.yml"
132
+ #
133
+ # @option opts [String] :mutable default to true,
134
+ # if true doesn't call mutable api call
135
+ #
136
+ # @option opts [String] :environment "sandbox", "production"
137
+ # @option opts [String] :email
138
+ # @option opts [String] :password
139
+ # @option opts [String] :developer_token
140
+ #
141
+ # @example
142
+ # Adwords.new( "sandbox", {:email=>"..."} )
143
+ # Adwords.new( {:environment=>"...", email => "..." } )
144
+ # Adwords.new( "sandbox" )
145
+ # Adwords.new() # default to sandbox
146
+ #
147
+ def initialize(profile = "sandbox", options = nil)
148
+ all_keys = %w{environment email password developer_token mutable config_file password_file}
149
+ @logger = nil
87
150
  if not options.nil?
88
- # new( "sandbox", {:email=>"..."} )
89
- @profile = profile.to_s
90
- options = options.stringify_keys
91
- options.assert_valid_keys("environment", "email", "password", "developer_token", "mutable", "config_file")
92
- f = options.delete("config_file")
93
- self.config_file= f if f
151
+ # new( "profile", {:email=>"..."} )
152
+ options = options.stringify_keys
153
+ options.assert_valid_keys(*all_keys)
154
+
155
+ config_file = options.delete("config_file")
156
+ password_file = options.delete("password_file")
157
+ configure(config_file, password_file)
158
+
159
+ @profile = profile.to_s
94
160
  @options = load_config(@profile)
95
161
  @options = @options.merge(options)
96
162
  elsif profile.respond_to?(:keys)
97
163
  # new( {:environment=>"...", email => "..." } )
98
- @options = profile.stringify_keys
99
- @options.assert_valid_keys("environment", "email", "password", "developer_token", "mutable")
164
+ options = profile.stringify_keys
165
+ options.assert_valid_keys( *(all_keys-%w{config_file password_file}) )
166
+ configure
167
+ @options = options
100
168
  @profile = "anonymous_" + @options["environment"]
101
169
  else
102
170
  # new( "sandbox" )
103
171
  @profile = profile.to_s
172
+ configure
104
173
  @options = load_config(@profile)
105
174
  end
106
175
  if ["sandbox", "production"].include?(profile)
@@ -108,48 +177,124 @@ module Sem4r
108
177
  @options["environment"] = profile
109
178
  end
110
179
  if @options["environment"] != profile
111
- raise "you cannot use profile '#{profile}' with environment #{@options["environment"]}"
180
+ raise "you cannot use profile '#{profile}' with environment '#{@options["environment"]}'"
112
181
  end
113
182
  end
183
+
184
+ try_to_find_password_in_password_file unless @options["password"]
114
185
  end
115
186
 
116
187
  def to_s
117
188
  "adwords profile: '#{profile}' config file: '#{config_file}'"
118
189
  end
119
190
 
120
- def config_file=(file_path)
121
- config_filepath = File.expand_path(file_path)
122
- if File.exists?( config_filepath)
123
- @config_filepath = config_filepath
124
- else
125
- puts "#{config_filepath} not exists"
126
- end
127
- @config_filepath
191
+ #
192
+ # returns profiles contained into current config file
193
+ #
194
+ def profiles
195
+ self.class.profiles @config.config_file
128
196
  end
129
197
 
130
- # return the config file name
198
+ #
199
+ # @return [String] the config file name
200
+ #
131
201
  def config_file
132
- return @config_filepath if @config_filepath
133
- @config_file_path = Adwords.search_config_file
134
- @config_file_path
202
+ return @config.config_file if @config
203
+ @config = Adwords.search_config
204
+ unless @config
205
+ puts "cannot find configuration files"
206
+ end
207
+ @config.config_file
208
+ end
209
+
210
+ ######################################################################################################
211
+ # Password
212
+
213
+ #
214
+ # password is defined?
215
+ #
216
+ def has_password?
217
+ !@options["password"].nil?
218
+ end
219
+
220
+ #
221
+ # set password
222
+ #
223
+ def password=(pwd)
224
+ if @account
225
+ raise "already connected cannot change password"
226
+ end
227
+ @options["password"]=pwd
228
+ end
229
+
230
+ def try_to_find_password_in_password_file
231
+ return unless @config.password_file
232
+ return unless File.exists?(@config.password_file)
233
+ passwords = YAML.load(File.open(@config.password_file))
234
+ pass = passwords[@options["email"]]
235
+ @options["password"] = pass if pass
236
+ end
237
+
238
+ def save_passwords
239
+ if @config.password_file.nil?
240
+ raise "cannot save password"
241
+ end
242
+ puts "save password in #{@config.password_file} (security warning!)"
243
+ passwords = {}
244
+ if File.exists?(@config.password_file)
245
+ passwords = YAML.load(File.open(@config.password_file))
246
+ end
247
+ passwords[@options["email"]] = @options["password"]
248
+ File.open(@config.password_file, "w") do |f|
249
+ f.write(passwords.to_yaml)
250
+ end
135
251
  end
136
252
 
253
+ private
254
+
255
+ #
256
+ # @private
257
+ #
258
+ # force config paths
259
+ # and the config file
260
+ #
261
+ def configure(config_file = nil, password_file = nil)
262
+ @config = Adwords.search_config
263
+ unless config_file.nil?
264
+ config_file = File.expand_path(config_file)
265
+ unless File.exists?(config_file)
266
+ raise "#{config_file} not exists"
267
+ end
268
+ @config.config_file = config_file
269
+ end
270
+ unless password_file.nil?
271
+ password_file = File.expand_path(password_file)
272
+ @config.password_file = password_file
273
+ end
274
+ @config
275
+ end
276
+
277
+
278
+ # @private
279
+ # load the configuration
137
280
  def load_config(profile)
138
- unless config_file
281
+ unless @config.config_file
139
282
  raise Sem4rError, "config file 'sem4r' not found"
140
283
  end
141
284
  # puts "Loaded profiles from #{config_file}"
142
- yaml = YAML::load( File.open( config_file ) )
143
- config = yaml['google_adwords'][profile]
285
+ yaml = YAML::load(File.open(@config.config_file))
286
+ config = yaml['google_adwords'][profile]
144
287
  config || {}
145
288
  end
146
289
 
290
+ public
291
+
147
292
  ##########################################################################
148
293
  # logging
149
294
 
150
- def dump_soap_options( dump_options )
295
+ def dump_soap_options(dump_options)
151
296
  @dump_soap_options = dump_options
152
- @connector.dump_soap_options( dump_options ) if @connector
297
+ @connector.dump_soap_options(dump_options) if @connector
153
298
  end
154
299
 
155
300
  def dump_soap?
@@ -166,13 +311,13 @@ module Sem4r
166
311
 
167
312
  def logger= logger
168
313
  unless logger.instance_of?(Logger)
169
- file = File.open( logger, "a" )
170
- file.sync = true
171
- logger = Logger.new(file)
314
+ file = File.open(logger, "a")
315
+ file.sync = true
316
+ logger = Logger.new(file)
172
317
  logger.formatter = proc { |severity, datetime, progname, msg|
173
318
  "#{datetime.strftime("%H:%M:%S")}: #{msg}\n"
174
319
  }
175
- end
320
+ end
176
321
  @logger= logger
177
322
 
178
323
  @connector.logger= logger if @connector
@@ -182,11 +327,20 @@ module Sem4r
182
327
  @logger
183
328
  end
184
329
 
330
+ ##########################################################################
331
+ # others
332
+
333
+ #
334
+ # @return [Account]
335
+ #
185
336
  def account
186
337
  deferred_initialize unless @initialized
187
338
  @account
188
339
  end
189
340
 
341
+ #
342
+ # print api counters on stdout
343
+ #
190
344
  def p_counters
191
345
  operations = @counters[:operations]
192
346
  units = @counters[:units]
@@ -199,12 +353,12 @@ module Sem4r
199
353
 
200
354
  attr_reader :service
201
355
 
202
- #
356
+ # @private
203
357
  # TODO: credentials are necessary because you might use more then account/credentials
204
358
  # at the same time
205
359
  #
206
- def add_counters( credentials, counters )
207
- counters.each_pair { |k,v|
360
+ def add_counters(credentials, counters)
361
+ counters.each_pair { |k, v|
208
362
  @counters[k] ||= 0
209
363
  @counters[k] += v
210
364
  }
@@ -212,32 +366,34 @@ module Sem4r
212
366
 
213
367
  private
214
368
 
369
+ # @private
370
+ # really initialize connections to the google server
371
+ #
215
372
  def deferred_initialize
216
373
  @initialized = true
217
374
 
218
- @connector = SoapConnector.new
375
+ @connector = Sem4rSoap::HttpConnector.get(@logger, :http_client)
219
376
  @connector.dump_soap_options(@dump_soap_options) if @dump_soap_options
220
- @connector.logger=(@logger) if @logger
221
-
222
- @credentials = Credentials.new(
223
- :environment => @options["environment"],
224
- :email => @options["email"],
225
- :password => @options["password"],
226
- :useragent => "Sem4r Adwords Ruby Client Library (http://github.com/sem4r/sem4r)",
227
- :developer_token => @options["developer_token"],
228
- :mutable => @options["mutable"]
377
+
378
+ @credentials = Credentials.new(
379
+ :environment => @options["environment"],
380
+ :email => @options["email"],
381
+ :password => @options["password"],
382
+ :useragent => "Sem4r Adwords Ruby Client Library (http://github.com/sem4r/sem4r)",
383
+ :developer_token => @options["developer_token"],
384
+ :mutable => @options["mutable"]
229
385
  )
230
386
  @credentials.connector = @connector
231
387
 
232
- @service = Service.new(@connector)
233
- @account = Account.new( self, @credentials )
234
- @counters = {
235
- :operations => 0,
236
- :response_time => 0,
237
- :units => 0
388
+ @service = Service.new(@connector)
389
+ @account = Account.new(self, @credentials)
390
+ @counters = {
391
+ :operations => 0,
392
+ :response_time => 0,
393
+ :units => 0
238
394
  }
239
395
  end
240
396
 
241
397
  end
242
398
 
243
- end
399
+ end # module Sem4r
data/lib/sem4r/base.rb CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  module Sem4r
26
26
  class Base
27
- include SoapAttributes
27
+ include Sem4rSoap::SoapAttributes
28
28
 
29
29
  attr_reader :adwords
30
30
  attr_reader :credentials
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  # -------------------------------------------------------------------------
2
3
  # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
3
4
  #
@@ -19,21 +20,22 @@
19
20
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #
23
23
  # -------------------------------------------------------------------------
24
24
 
25
25
  module Sem4r
26
26
  class BulkMutateJob
27
- include SoapAttributes
27
+ include Sem4rSoap::SoapAttributes
28
28
 
29
29
  attr_reader :id
30
30
  attr_accessor :campaign_id
31
31
  attr_reader :operations
32
+ attr_accessor :num_parts
32
33
 
33
34
  g_accessor :status
34
35
 
35
36
  def initialize(&block)
36
37
  @operations = []
38
+ @num_parts = 1
37
39
  if block_given?
38
40
  block.arity < 1 ? instance_eval(&block) : block.call(self)
39
41
  end
@@ -48,54 +50,58 @@ module Sem4r
48
50
  @operations.empty?
49
51
  end
50
52
 
51
- def add( something )
53
+ def add(something)
52
54
  case something
53
- when AdGroupTextAd
54
- ad_operation = AdGroupAdOperation.new
55
- ad_operation.add something
56
- else
57
- raise "how you suppose I must do when incounter a #{something.class}?"
55
+ when AdGroupTextAd
56
+ ad_operation = AdGroupAdOperation.add something
57
+ else
58
+ raise "how you suppose I must do when incounter a #{something.class}?"
58
59
  end
59
60
  self
60
61
  end
61
62
 
62
- def to_xml(tag)
63
- xml =""
64
-
65
- if tag
66
- xml += "<operand xsi:type='BulkMutateJob'>"
67
- end
68
- xml += <<-EOS
69
- <request>
70
- <partIndex>0</partIndex>
71
- <operationStreams>
72
- <scopingEntityId>
73
- <type>CAMPAIGN_ID</type>
74
- <value>#{campaign_id}</value>
75
- </scopingEntityId>
76
- EOS
63
+ #
64
+ # @private
65
+ #
66
+ def _xml(t)
67
+ if @id
68
+ t.id id
69
+ else
70
+ t.request do |t|
71
+ t.partIndex 0
77
72
 
78
- if @operations
79
- @operations.each do |operation|
80
- xml += operation.to_xml('operations')
73
+ t.operationStreams do |t|
74
+ t.scopingEntityId do |t|
75
+ t.type "CAMPAIGN_ID"
76
+ t.value campaign_id
77
+ end
78
+ @operations.each { |operation| operation.xml(t, "operations") } if @operations
79
+ end
81
80
  end
81
+ t.numRequestParts num_parts
82
82
  end
83
-
84
- xml +=<<-EOS
85
- </operationStreams>
86
- </request>
87
- <numRequestParts>1</numRequestParts>
88
- EOS
83
+ end
89
84
 
85
+ #
86
+ # Marshal to xml using Builder
87
+ # @parm [Builder::XmlBuilder]
88
+ #
89
+ def xml(t, tag = nil)
90
90
  if tag
91
- xml += "</operand>"
91
+ t.__send__(tag, {"xsi:type"=>'BulkMutateJob'}) { |t| _xml(t) }
92
+ else
93
+ _xml(t)
92
94
  end
93
95
  end
94
96
 
97
+ def to_xml(tag = "operand")
98
+ xml(Builder::XmlMarkup.new, tag)
99
+ end
100
+
95
101
  def self.from_element(el)
96
102
  new do
97
- @id = el.at_xpath("id").text.strip.to_i
98
- status el.at_xpath("status").text
103
+ @id = el.at_xpath("id").text.strip.to_i
104
+ status el.at_xpath("status").text
99
105
  end
100
106
  end
101
107
 
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  # -------------------------------------------------------------------------
2
3
  # Copyright (c) 2009-2010 Sem4r sem4ruby@gmail.com
3
4
  #
@@ -19,24 +20,41 @@
19
20
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #
23
23
  # -------------------------------------------------------------------------
24
24
 
25
25
  module Sem4r
26
26
 
27
27
  module BulkMutateJobAccountExtension
28
- ############################################################################
29
- # Bulk Jobs
30
28
 
31
- def p_jobs
32
- selector = BulkMutateJobSelector.new
33
- soap_message = service.bulk_mutate_job.all(credentials, selector)
34
- add_counters( soap_message.counters )
35
- els = soap_message.response.xpath("//getResponse/rval")
36
- jobs = els.map do |el|
37
- BulkMutateJob.from_element(el)
29
+ def job_result(job_id)
30
+ selector = BulkMutateJobSelector.new do
31
+ jobId job_id
32
+ stats true
33
+ history true
38
34
  end
35
+ soap_message = service.bulk_mutate_job.get(credentials, selector.to_xml)
36
+ add_counters(soap_message.counters)
37
+ self
38
+ end
39
+
40
+ def job_delete(job_id)
41
+ job = BulkMutateJob.new
42
+ job.instance_eval { @id = job_id }
43
+ operation = JobOperation.remove(job)
44
+
45
+ soap_message = service.bulk_mutate_job.mutate(credentials, operation.to_xml("operation"))
46
+ add_counters(soap_message.counters)
47
+ self
48
+ end
49
+
50
+ def job_mutate(bulk_mutate_job)
51
+ soap_message = service.bulk_mutate_job.mutate(credentials, bulk_mutate_job.to_xml("operation"))
52
+ add_counters(soap_message.counters)
53
+ el = soap_message.response.at_xpath("//rval")
54
+ BulkMutateJob.from_element(el)
55
+ end
39
56
 
57
+ def p_jobs
40
58
  puts "#{jobs.length} bulk mutate jobs"
41
59
  jobs.each do |job|
42
60
  puts job.to_s
@@ -44,11 +62,27 @@ module Sem4r
44
62
  self
45
63
  end
46
64
 
47
- def job_mutate(bulk_mutate_job)
48
- soap_message = service.bulk_mutate_job.mutate(credentials, bulk_mutate_job)
49
- add_counters( soap_message.counters )
50
- el = soap_message.response.at_xpath("//rval")
51
- BulkMutateJob.from_element(el)
65
+ def jobs(refresh = false)
66
+ _jobs unless @jobs and !refresh
67
+ @jobs
68
+ end
69
+
70
+ private
71
+
72
+ def _jobs
73
+ selector = BulkMutateJobSelector.new do
74
+ status BulkMutateJobSelector::COMPLETED
75
+ status BulkMutateJobSelector::PROCESSING
76
+ status BulkMutateJobSelector::FAILED
77
+ status BulkMutateJobSelector::PENDING
78
+ end
79
+ soap_message = service.bulk_mutate_job.get(credentials, selector.to_xml)
80
+ add_counters(soap_message.counters)
81
+ els = soap_message.response.xpath("//getResponse/rval")
82
+ @jobs = els.map do |el|
83
+ BulkMutateJob.from_element(el)
84
+ end
85
+ @jobs
52
86
  end
53
87
  end
54
88