adapi 0.0.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.
@@ -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