adapi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,90 @@
1
+ module Adapi
2
+
3
+ # http://code.google.com/apis/adwords/docs/reference/latest/CampaignTargetService.html
4
+ #
5
+ class CampaignTarget < Api
6
+
7
+ def initialize(params = {})
8
+ params[:service_name] = :CampaignTargetService
9
+ super(params)
10
+ end
11
+
12
+ # FIXME params should be the same as in other services, for example ad_group
13
+ #
14
+ def self.create(params = {})
15
+ campaign_target_service = CampaignTarget.new
16
+
17
+ raise "No Campaign ID" unless params[:campaign_id]
18
+ campaign_id = params[:campaign_id].to_i
19
+
20
+ # transform our own high-level target parameters to google low-level
21
+ # target parameters
22
+ operations = []
23
+
24
+ params[:targets].each_pair do |targetting_type, targetting_settings|
25
+ operations << { :operator => 'SET',
26
+ :operand => {
27
+ :xsi_type => "#{targetting_type.to_s.capitalize}TargetList",
28
+ :campaign_id => campaign_id,
29
+ :targets => self.create_targets(targetting_type, targetting_settings)
30
+ }
31
+ }
32
+ end
33
+
34
+ response = campaign_target_service.service.mutate(operations)
35
+
36
+ targets = response[:value] || []
37
+ targets.each do |target|
38
+ puts "Campaign target of type #{target[:"@xsi:type"]} for campaign id " +
39
+ "#{target[:campaign_id]} was set."
40
+ end
41
+
42
+ targets
43
+ end
44
+
45
+ def self.find(params = {})
46
+ campaign_target_service = CampaignTarget.new
47
+
48
+ selector = {} # select all campaign targets by default
49
+ selector[:campaign_ids] = params[:campaign_ids] if params[:campaign_ids]
50
+
51
+ response = campaign_target_service.service.get(selector)
52
+
53
+ targets = nil
54
+ if response and response[:entries]
55
+ targets = response[:entries]
56
+ targets.each do |target|
57
+ p target
58
+ end
59
+ else
60
+ puts "No campaign targets found."
61
+ end
62
+
63
+ targets
64
+ end
65
+
66
+ # transform our own high-level target parameters to google low-level
67
+ #
68
+ def self.create_targets(target_type, target_data)
69
+ case target_type
70
+ when :language
71
+ target_data.map { |language| { :language_code => language } }
72
+ # example: ['cz','sk'] => [{:language_code => 'cz'}, {:language_code => 'sk'}]
73
+ when :geo
74
+ geo_targets = []
75
+ target_data.each_pair do |geo_type, geo_values|
76
+ geo_values.each do |geo_value|
77
+ geo_targets << {
78
+ :xsi_type => "#{geo_type.to_s.capitalize}Target",
79
+ :excluded => false,
80
+ "#{geo_type}_code".to_sym => geo_value
81
+ }
82
+ end
83
+ end
84
+ geo_targets
85
+ else nil
86
+ end
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,54 @@
1
+
2
+ # PS: what about this config setting?
3
+ # Campaign.create(:data => campaign_data, :account => :my_account_alias)
4
+
5
+ module Adapi
6
+ class Config
7
+
8
+ # display hash of all account settings
9
+ #
10
+ def self.settings
11
+ @settings ||= self.load_settings
12
+ end
13
+
14
+ # display actual account settings
15
+ # if it's not available, set to :default account settings
16
+ #
17
+ def self.read # = @data
18
+ @data ||= self.settings[:default]
19
+ end
20
+
21
+ # TODO described in README, but should be documented here as well
22
+ #
23
+ def self.set(params = {})
24
+ # hash of params - default
25
+ if params.is_a?(Hash)
26
+ @data = params
27
+ # set alias from adapi.yml
28
+ elsif params.is_a?(Symbol)
29
+ @data = @settings[params]
30
+ end
31
+ end
32
+
33
+ # params:
34
+ # * path - default: user's home directory
35
+ # * filename - default: adapi.yml
36
+ # TODO: set to HOME/adwords_api as default
37
+ def self.load_settings(params = {})
38
+ params[:path] ||= ENV['HOME']
39
+ params[:filename] ||= 'adapi.yml'
40
+
41
+ adapi_path = File.join(params[:path], params[:filename])
42
+ adwords_api_path = File.join(ENV['HOME'], 'adwords_api.yml')
43
+
44
+ if File.exists?(adapi_path)
45
+ @settings = YAML::load(File.read(adapi_path)) rescue {}
46
+ elsif File.exists?(adwords_api_path)
47
+ @settings = { :default => YAML::load(File.read(adwords_api_path)) } rescue {}
48
+ end
49
+
50
+ @settings
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module Adapi
2
+ VERSION = "0.0.1"
3
+ end
data/lib/collection.rb ADDED
@@ -0,0 +1,429 @@
1
+ # = Collection
2
+ #
3
+ # Module which allows to use a proxy class for wrapping collections of all sorts.
4
+ #
5
+ # Let's take a collection of articles, for example (see also the test suite below).
6
+ #
7
+ # The collection item class could look like this:
8
+ #
9
+ # class Article
10
+ # attr_reader :title
11
+ # def initialize(title); @title = title; end
12
+ # end
13
+ #
14
+ # The collection class could look like this, using the provided DSL:
15
+ #
16
+ # class ArticleCollection
17
+ # include Collection
18
+ #
19
+ # item_class Article # 1)
20
+ # item_key :title # 2)
21
+ # load_collection do |*args| # 3)
22
+ # args.pop.map { |title| item_class.new(title) }
23
+ # end
24
+ # end
25
+ #
26
+ # As you can see, you include the module and specify, which class should be collection items wrapped in [1],
27
+ # what is the main attribute for a collection item [2], and how you would like to load the collection [3].
28
+ #
29
+ # Note, that you can also override the corresponding methods directly (see tests below).
30
+ #
31
+ # This allows to do following operations with the collection:
32
+ #
33
+ # articles = ArticleCollection.new ['one', 'two']
34
+ #
35
+ # puts "\n~~~ The collection..."
36
+ # p articles
37
+ #
38
+ # puts "\n~~~ Last item..."
39
+ # p articles.last
40
+ #
41
+ # puts "\n~~~ Add 'three' and 'four'..."
42
+ # articles << 'three'
43
+ # articles.add 'four'
44
+ #
45
+ # p articles
46
+ #
47
+ # puts "\n~~~ Deleting 'three' and 'four'..."
48
+ # articles >> 'three'
49
+ # articles.delete Article.new('four')
50
+ #
51
+ # p articles
52
+ #
53
+ # puts "\n~~~ Iteration..."
54
+ # articles.each_with_index do |a, i|
55
+ # puts "#{i+1}. #{a.title}"
56
+ # end
57
+ #
58
+ # puts "\n~~~ Mapping..."
59
+ # p articles.map { |a| a.title }
60
+ #
61
+ # puts "\n~~~ Accessors..."
62
+ # p articles['one']
63
+ # p articles.find 'two'
64
+ #
65
+ # puts "\n~~~ Size..."
66
+ # p articles.size
67
+ #
68
+ # You may want to customize adding/removing items to the collection, for example.
69
+ #
70
+ # That's easy: just re-implement the `<<` or `>>` methods with custom logic, and call `super()`:
71
+ #
72
+ # def << item
73
+ # return false unless Tag.new(:article => @article, :value => item).valid?
74
+ #
75
+ # @article.tags = super(:article => @article, :value => item)
76
+ # self
77
+ # end
78
+ # alias :add :<<
79
+ #
80
+ # -----------------------------------
81
+ # (c) 2001 Karel Minarik; MIT License
82
+ #
83
+ module Collection
84
+ include Enumerable
85
+
86
+ def self.included(base)
87
+ base.extend DSL
88
+ base.class_eval do
89
+ def self.method_added(name)
90
+ case name
91
+ when :<< then alias_method :add, name
92
+ when :>> then alias_method :delete, name
93
+ when :[] then alias_method :find, name
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ module DSL
100
+
101
+ def item_class klass=nil
102
+ klass ? @item_class = klass : @item_class
103
+ end
104
+
105
+ def item_key key=nil
106
+ key ? @item_key = key : @item_key
107
+ end
108
+
109
+ def load_collection &block
110
+ block_given? ? @load_collection = block : @load_collection
111
+ end
112
+
113
+ end
114
+
115
+ def initialize(*args)
116
+ if self.class.instance_variable_defined?(:@load_collection)
117
+ @collection = load_collection.call(*args)
118
+ else
119
+ @collection = load_collection(*args)
120
+ end
121
+ end
122
+
123
+ def << item
124
+ item = item_class.new(item) unless item.is_a? item_class
125
+ @collection << item
126
+ self
127
+ end
128
+ alias :add :<<
129
+
130
+ def >> item
131
+ item = item.send(item_key) if item.respond_to? item_key
132
+ @collection.reject! { |a| a.send(item_key) == item }
133
+ self
134
+ end
135
+ alias :delete :>>
136
+
137
+ def [] key
138
+ @collection.select { |a| a.send(item_key) == key }.first
139
+ end
140
+ alias :find :[]
141
+
142
+ def last
143
+ @collection.reverse.first
144
+ end
145
+
146
+ def <=> other
147
+ self <=> other
148
+ end
149
+
150
+ def each(&block)
151
+ @collection.each(&block)
152
+ end
153
+
154
+ def include? value
155
+ @collection.any? { |i| i.send(item_key) == value }
156
+ end
157
+
158
+ def empty?
159
+ @collection.empty?
160
+ end
161
+
162
+ def to_a
163
+ @collection.map { |i| i.send(item_key) }
164
+ end
165
+
166
+ def size
167
+ @collection.size
168
+ end
169
+
170
+ def inspect
171
+ %Q|<#{self.class.name} #{@collection.inspect}>|
172
+ end
173
+
174
+ def load_collection
175
+ self.class.load_collection ||
176
+ raise(NoMethodError, "Please implement 'load_collection' method in your collection class")
177
+ end
178
+
179
+ def item_class
180
+ self.class.item_class ||
181
+ raise(NoMethodError, "Please implement 'item_class' method in your collection class")
182
+ end
183
+
184
+ def item_key
185
+ self.class.item_key ||
186
+ raise(NoMethodError, "Please implement 'item_key' method in your collection class")
187
+ end
188
+
189
+ end
190
+
191
+
192
+ if $0 == __FILE__
193
+ require 'rubygems'
194
+ require 'test/unit'
195
+ require 'shoulda'
196
+ require 'mocha'
197
+
198
+ class CollectionTest < Test::Unit::TestCase
199
+
200
+ context "Collection module" do
201
+
202
+ setup { class MyCollection; include Collection; end }
203
+
204
+ should "have abstract methods" do
205
+ assert_raise(NoMethodError) do
206
+ MyCollection.new.load_collection
207
+ MyCollection.new.item_class
208
+ MyCollection.new.item_key
209
+ end
210
+ end
211
+
212
+ should "pass arguments from initialize to load_collection" do
213
+ list = ['one', 'two']
214
+ MyCollection.any_instance.expects(:load_collection).with( list ).returns( list )
215
+
216
+ MyCollection.new list
217
+ end
218
+
219
+ should "be iterable" do
220
+ MyCollection.any_instance.stubs(:load_collection).returns( [] )
221
+
222
+ assert_respond_to MyCollection.new, :each
223
+ assert_respond_to MyCollection.new, :size
224
+ assert_respond_to MyCollection.new, :empty?
225
+ end
226
+
227
+ end
228
+
229
+ context "Collection module included" do
230
+
231
+ setup do
232
+ class Article
233
+ attr_reader :title
234
+ def initialize(title); @title = title; end
235
+ end
236
+
237
+ class ArticleCollection
238
+ include Collection
239
+
240
+ item_class Article
241
+ item_key :title
242
+ load_collection do |*args|
243
+ args.pop.map { |title| item_class.new(title) }
244
+ end
245
+ end
246
+
247
+ @articles = ArticleCollection.new ['One', 'Two']
248
+ end
249
+
250
+ should "set item_class" do
251
+ assert_equal Article, @articles.item_class
252
+ end
253
+
254
+ should "set item_key" do
255
+ assert_equal :title, @articles.item_key
256
+ end
257
+
258
+ should "load the collection" do
259
+ assert_equal 2, @articles.size
260
+ assert_same_elements ['One', 'Two'], @articles.to_a
261
+ end
262
+
263
+ should "walk like an Enumerable" do
264
+ assert_same_elements ['One', 'Two'], @articles.map { |a| a.title }
265
+ end
266
+
267
+ should "answer to empty?" do
268
+ assert ! @articles.empty?
269
+ end
270
+
271
+ should "return size" do
272
+ assert_equal 2, @articles.size
273
+ end
274
+
275
+ should "return first" do
276
+ assert_equal 'One', @articles.first.title
277
+ end
278
+
279
+ should "return last" do
280
+ assert_equal 'Two', @articles.last.title
281
+ end
282
+
283
+ should "add item by key" do
284
+ assert @articles << 'Three'
285
+ assert_equal 3, @articles.size
286
+ end
287
+
288
+ should "add item instance" do
289
+ assert @articles << Article.new('Three')
290
+ assert_equal 3, @articles.size
291
+ assert_equal 'Three', @articles.last.title
292
+ end
293
+
294
+ should "remove item by key" do
295
+ assert @articles >> 'Two'
296
+ assert_equal 1, @articles.size
297
+ end
298
+
299
+ should "remove item instance" do
300
+ assert @articles >> Article.new('Two')
301
+ assert_equal 1, @articles.size
302
+ assert_equal 'One', @articles.last.title
303
+ end
304
+
305
+ should "get item by key" do
306
+ assert_not_nil @articles['One']
307
+ assert_equal 'One', @articles['One'].title
308
+ end
309
+
310
+ should "query for item by key" do
311
+ assert @articles.include?('One'), "#{@articles.inspect} should contain 'One'"
312
+ assert ! @articles.include?('FourtyTwo'), "#{@articles.inspect} should NOT contain 'FourtyTwo'"
313
+ end
314
+
315
+ should "serialize collection to an Array, by key" do
316
+ assert_same_elements ['One', 'Two'], @articles.to_a
317
+ end
318
+
319
+ should "have aliases" do
320
+ assert_respond_to @articles, :add
321
+ assert_respond_to @articles, :delete
322
+ assert_respond_to @articles, :find
323
+ end
324
+
325
+ end
326
+
327
+ context "Collection module used without DSL" do
328
+
329
+ setup do
330
+ class Article
331
+ attr_reader :title
332
+ def initialize(title); @title = title; end
333
+ end
334
+
335
+ class NoDSLArticleCollection
336
+ include Collection
337
+
338
+ def load_collection(*args); args.pop.map { |title| item_class.new(title) }; end
339
+ def item_class; Article; end
340
+ def item_key; :title; end
341
+ end
342
+
343
+ @articles = NoDSLArticleCollection.new ['One', 'Two']
344
+ end
345
+
346
+ should "set item_class" do
347
+ assert_equal Article, @articles.item_class
348
+ end
349
+
350
+ should "set item_key" do
351
+ assert_equal :title, @articles.item_key
352
+ end
353
+
354
+ should "load the collection" do
355
+ assert_equal 2, @articles.size
356
+ assert_same_elements ['One', 'Two'], @articles.to_a
357
+ end
358
+
359
+ end
360
+
361
+ context "Collection with customized manipulation methods" do
362
+
363
+ setup do
364
+ class Article
365
+ attr_reader :title
366
+ def initialize(title); @title = title; end
367
+ end
368
+
369
+ class ArticleCollection
370
+ include Collection
371
+
372
+ item_class Article
373
+ item_key :title
374
+ load_collection do |*args|
375
+ args.pop.map { |title| item_class.new(title) }
376
+ end
377
+
378
+ def << item
379
+ return false if item == 'foo'
380
+ super
381
+ end
382
+
383
+ def >> item
384
+ raise "Foorbidden!" if item == 'foo'
385
+ super
386
+ end
387
+
388
+ def [] item
389
+ return nil if item == 'One'
390
+ super
391
+ end
392
+
393
+ end
394
+
395
+ @articles = ArticleCollection.new ['One', 'Two']
396
+ end
397
+
398
+ should "return false when adding adding 'foo'" do
399
+ assert ! (@articles << 'foo')
400
+ assert_equal 2, @articles.size
401
+ end
402
+
403
+ should "raise exception when trying to remove 'foo'" do
404
+ assert_raise(RuntimeError) do
405
+ @articles >> 'foo'
406
+ assert_equal 2, @articles.size
407
+ end
408
+ end
409
+
410
+ should "have alias for add" do
411
+ assert ! @articles.add('foo')
412
+ assert_equal 2, @articles.size
413
+ end
414
+
415
+ should "have alias for delete" do
416
+ assert_raise(RuntimeError) do
417
+ @articles.delete('foo')
418
+ assert_equal 2, @articles.size
419
+ end
420
+ end
421
+
422
+ should "have alias for find" do
423
+ assert_nil @articles.find('One')
424
+ end
425
+
426
+ end
427
+
428
+ end
429
+ end