monkey_wrench 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.mdown CHANGED
@@ -15,4 +15,9 @@ From there you've got a rich API for managing Lists and Members. To subscribe a
15
15
 
16
16
  ## Further Reading
17
17
 
18
- For more information, [check the documentation](http://rdoc.info/projects/rubypond/monkeywrench)
18
+ For more information, [check the documentation](http://rdoc.info/projects/rubypond/monkeywrench)
19
+
20
+ ## Credits & Contributions
21
+
22
+ * [David Heath](http://davidheath.org/)
23
+ * [Maxime Guilbot](http://github.com/maxime)
data/Rakefile CHANGED
@@ -1,11 +1,22 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+
6
+ spec_data = File.open('monkey_wrench.gemspec').read
7
+ spec = nil
8
+ Thread.new do
9
+ spec = eval("#{spec_data}")
10
+ end.join
11
+
12
+ Rake::GemPackageTask.new(spec) do |pkg|
13
+ pkg.need_zip = false
14
+ pkg.need_tar = false
15
+ end
3
16
 
4
17
  require 'rake/testtask'
5
18
  Rake::TestTask.new(:test) do |test|
6
19
  test.libs << 'lib' << 'test'
7
20
  test.pattern = 'test/**/*_test.rb'
8
21
  test.verbose = false
9
- end
10
-
11
- task :default => :test
22
+ end
data/lib/monkey_wrench.rb CHANGED
File without changes
@@ -10,9 +10,10 @@ module MonkeyWrench
10
10
 
11
11
  @@apikey = nil
12
12
  @@datacenter = nil
13
+ @@dryrun = false
13
14
 
14
15
  class << self
15
- def default_options
16
+ def default_query_params
16
17
  { :output => "json", :apikey=> @@apikey}
17
18
  end
18
19
 
@@ -20,14 +21,34 @@ module MonkeyWrench
20
21
  "http://#{datacenter}.api.mailchimp.com/1.2/"
21
22
  end
22
23
 
23
- def get(params)
24
- response = super(base_uri, :query => params.merge(default_options))
25
- handle_errors(response.parsed_response)
24
+ def default_retry_limit
25
+ 3
26
+ end
27
+
28
+ def get(params, http_options = {})
29
+ if @@dryrun
30
+ puts "GET #{base_uri} #{params.merge(default_query_params).inspect}"
31
+ return {}
32
+ else
33
+ robustly(http_options) do
34
+ response = super(base_uri, http_options.merge(:query => params.merge(default_query_params)))
35
+ handle_errors(response.parsed_response)
36
+ end
37
+ end
26
38
  end
27
39
 
28
- def post(params)
29
- response = super(base_uri, :query => params.merge(default_options))
30
- handle_errors(response.parsed_response)
40
+ def post(params, http_options = {})
41
+ if @@dryrun
42
+ puts "POST #{base_uri} #{params.merge(default_query_params).inspect}"
43
+ return {}
44
+ else
45
+ robustly(http_options) do
46
+ post_params = params.dup
47
+ get_params = default_query_params.merge(:method => post_params.delete(:method))
48
+ response = super(base_uri, http_options.merge(:body => post_params, :query => get_params))
49
+ handle_errors(response.parsed_response)
50
+ end
51
+ end
31
52
  end
32
53
 
33
54
  def handle_errors(objects)
@@ -55,12 +76,27 @@ module MonkeyWrench
55
76
  end
56
77
 
57
78
  private
58
- def get(params)
59
- self.class.get(params)
79
+ def self.robustly(http_options, &block)
80
+ retry_limit = http_options[:retry_limit] || default_retry_limit
81
+ attempts = 0
82
+ while attempts < retry_limit
83
+ begin
84
+ attempts += 1
85
+ return yield
86
+ rescue Timeout::Error => e
87
+ if attempts == retry_limit
88
+ raise e
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def get(*args)
95
+ self.class.get(*args)
60
96
  end
61
97
 
62
- def post(params)
63
- self.class.post(params)
98
+ def post(*args)
99
+ self.class.post(*args)
64
100
  end
65
101
 
66
102
  end
File without changes
File without changes
File without changes
@@ -28,7 +28,8 @@ module MonkeyWrench
28
28
  end
29
29
  @@apikey = config[:apikey]
30
30
  @@datacenter = config[:datacenter]
31
+ @@dryrun = config[:dryrun] || false
31
32
  super({})
32
33
  end
33
34
  end
34
- end
35
+ end
@@ -21,7 +21,7 @@ module MonkeyWrench
21
21
  def escape_keys!
22
22
  collect_kv!{|k,v| [CGI.escape(k.to_s), v]}
23
23
  end
24
-
24
+
25
25
  def to_mailchimp(index = nil, parent_name = nil)
26
26
  result = self.collect_kv do |k,v|
27
27
  if v.is_a?(Array) && v.first.is_a?(Hash)
@@ -35,7 +35,7 @@ module MonkeyWrench
35
35
  if parent_name
36
36
  v = v.collect_kv do |key,val|
37
37
  keyname = CGI.escape("#{parent_name.to_s}[#{key.to_s.upcase}]")
38
- [keyname, val]
38
+ [keyname, val.to_s]
39
39
  end
40
40
  else
41
41
  v = { k => v }.to_mailchimp(nil, k)
@@ -45,7 +45,7 @@ module MonkeyWrench
45
45
  i = 0
46
46
  v.each do |val|
47
47
  keyname = CGI.escape("#{k}[#{i}]")
48
- results[keyname] = val
48
+ results[keyname] = val.to_s
49
49
  i += 1
50
50
  end
51
51
  v = results
@@ -66,4 +66,4 @@ end
66
66
 
67
67
  class Hash
68
68
  include MonkeyWrench::Hash
69
- end
69
+ end
File without changes
@@ -2,7 +2,6 @@ require 'cgi'
2
2
 
3
3
  module MonkeyWrench
4
4
  class List < MonkeyWrench::Base
5
-
6
5
  # Finds a given list by name
7
6
  #
8
7
  # @example
@@ -13,6 +12,14 @@ module MonkeyWrench
13
12
  def self.find_by_name(list_name)
14
13
  lists = find_all.detect{|list| list.name == list_name}
15
14
  end
15
+
16
+ def ==(other_list)
17
+ other_list.is_a?(self.class) && self.id == other_list.id
18
+ end
19
+
20
+ def self.find(id)
21
+ new(:id => id)
22
+ end
16
23
 
17
24
  # Will compare another list against the current one and return true if
18
25
  # they are the same (based on list ID)
@@ -47,10 +54,11 @@ module MonkeyWrench
47
54
  #
48
55
  # @return [Array<MonkeyWrench::List>]
49
56
  def self.find_all
50
- lists = post({ :method => "lists" }).map do |list|
57
+ @@lists ||= post({ :method => "lists" }).map do |list|
51
58
  List.new(list)
52
59
  end
53
60
  end
61
+
54
62
  class << self
55
63
  alias :all :find_all
56
64
  end
@@ -188,7 +196,7 @@ module MonkeyWrench
188
196
  opts = opts.merge(contact_details)
189
197
  else
190
198
  email_address = contact_details
191
- end
199
+ end
192
200
  subscribe_one(email_address, opts)
193
201
  return { :success => 1, :errors => []}
194
202
  end
@@ -255,14 +263,25 @@ module MonkeyWrench
255
263
  end
256
264
  end
257
265
 
258
- def subscribe_in_batches(subscribers, opts)
259
- cumulative_response = { :success => 0, :errors => [] }
266
+ def batch_size
267
+ 1000
268
+ end
269
+
270
+ def each_batch(subscribers, batch_size, &block)
260
271
  i = 0
261
272
  while i < subscribers.size
262
- response = subscribe_one_batch(subscribers[i..i+9], opts)
263
- cumulative_response[:success] += response['success_count']
264
- cumulative_response[:errors] += response['errors']
265
- i += 10
273
+ start = Time.now
274
+ yield subscribers[i..(i+batch_size-1)], i
275
+ i += batch_size
276
+ end
277
+ end
278
+
279
+ def subscribe_in_batches(subscribers, opts)
280
+ cumulative_response = { :success => 0, :errors => [] }
281
+ each_batch(subscribers, batch_size) do |batch, i|
282
+ response = subscribe_one_batch(batch, opts)
283
+ cumulative_response[:success] += (response["success_count"] || 0)
284
+ cumulative_response[:errors] += (response["errors"] || [])
266
285
  end
267
286
  cumulative_response
268
287
  end
@@ -273,7 +292,12 @@ module MonkeyWrench
273
292
  params[:update_existing] = opts[:update_existing] if opts.has_key?(:update_existing)
274
293
  params[:replace_interests] = opts[:replace_interests] if opts.has_key?(:replace_interests)
275
294
  params.merge!({ :batch => subscribers }.to_mailchimp)
276
- post(params)
295
+ post(params, :timeout => timeout_for_batch(subscribers))
296
+ end
297
+
298
+ def timeout_for_batch(batch)
299
+ # 5 mins for a batch of 1000
300
+ ((batch.size.to_f / 1000) * (5 * 60)).to_i
277
301
  end
278
302
 
279
303
  def subscribe_one_at_a_time(subscribers, opts)
@@ -297,7 +321,7 @@ module MonkeyWrench
297
321
  :update_existing => opts.delete(:update_existing),
298
322
  :replace_interests => opts.delete(:replace_interests),
299
323
  :send_welcome => opts.delete(:send_welcome),
300
- :email => email_address
324
+ :email_address => email_address
301
325
  }
302
326
  params.reject!{ |k,v| v.nil? }
303
327
  merge_vars = { :merge_vars => opts }.to_mailchimp
File without changes
File without changes
File without changes
@@ -0,0 +1 @@
1
+ {"success_count":1000,"error_count":0,"errors":[]}
@@ -0,0 +1 @@
1
+ {"success_count":4,"error_count":0,"errors":[]}
@@ -1 +1 @@
1
- {"id":"a58cbece83","email":"david@email.com","email_type":"html","ip_opt":"66.132.220.59","ip_signup":null,"member_rating":4,"merges":{"EMAIL":"david@email.com","MERGE0":"david@email.com","FNAME":"David","MERGE1":"David","LNAME":"Heath","MERGE2":"Heath","EXPIRYDATE":"2008-02-04","MERGE5":"2008-02-04","USERNAME":"Test1","MERGE6":"Test1","DURATION":"1","MERGE3":"1","PRODUCT":"One week subscription","MERGE4":"One week subscription","CURRENCY":"GBP","MERGE7":"GBP","ROLLING":"false","MERGE8":"false","EXPIRED":"true","MERGE9":"true","TRIAL":"","MERGE10":"","MMERGE11":"","MERGE11":"","MMERGE12":"","MERGE12":"","MMERGE13":"","MERGE13":"","MMERGE14":"","MERGE14":"","TRACKER":"","MERGE15":"","SUBSTYPE":"","MERGE16":"","FIRSTEBOOK":"","MERGE17":"","MMERGE18":"","MERGE18":"","INTERESTS":"freetrial, tutorials"},"status":"subscribed","timestamp":"2009-11-16 14:11:57","lists":{"699791bff9":"unsubscribed"}}
1
+ {"id":"a58cbece83","email":"david@email.com","email_type":"html","ip_opt":"66.132.220.59","ip_signup":null,"member_rating":4,"merges":{"EMAIL":"david@email.com","MERGE0":"david@email.com","FNAME":"David","MERGE1":"David","LNAME":"Heath","MERGE2":"Heath","EXPIRYDATE":"2008-02-04","MERGE5":"2008-02-04","USERNAME":"Test1","MERGE6":"Test1","DURATION":"1","MERGE3":"1","PRODUCT":"One week subscription","MERGE4":"One week subscription","CURRENCY":"GBP","MERGE7":"GBP","ROLLING":"false","MERGE8":"false","EXPIRED":"true","MERGE9":"true","TRIAL":"","MERGE10":"","MMERGE11":"","MERGE11":"","MMERGE12":"","MERGE12":"","MMERGE13":"","MERGE13":"","MMERGE14":"","MERGE14":"","TRACKER":"","MERGE15":"","SUBSTYPE":"","MERGE16":"","FIRSTEBOOK":"","MERGE17":"","MMERGE18":"","MERGE18":"","INTERESTS":"freetrial, tutorials"},"status":"subscribed","timestamp":"2009-11-16 14:11:57","lists":{"699791bff9":"unsubscribed"}}
File without changes
@@ -0,0 +1,55 @@
1
+ $:.unshift File.expand_path("..", File.dirname(__FILE__))
2
+ require "test_helper"
3
+
4
+ class MonkeyWrench::BaseTest < Test::Unit::TestCase
5
+
6
+ context "making an HTTP GET" do
7
+ should "retry if HTTP GET times out" do
8
+ retries = sequence('retries')
9
+ response = mock('')
10
+ response.stubs(:parsed_response).returns("the response")
11
+ MonkeyWrench::Base.stubs(:base_uri).returns('http://example.com');
12
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
13
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
14
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).returns(response)
15
+ assert_equal MonkeyWrench::Base.get({}), 'the response'
16
+ end
17
+
18
+ should "rethrow Timeout::Error if retry limit exceeded" do
19
+ retries = sequence('retries')
20
+ response = mock('')
21
+ response.stubs(:parsed_response).returns("the response")
22
+ MonkeyWrench::Base.stubs(:base_uri).returns('http://example.com');
23
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
24
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
25
+ assert_raise Timeout::Error do
26
+ MonkeyWrench::Base.get({}, :retry_limit => 2)
27
+ end
28
+ end
29
+ end
30
+
31
+ context "making an HTTP POST" do
32
+ should "retry if HTTP POST times out" do
33
+ retries = sequence('retries')
34
+ response = mock('')
35
+ response.stubs(:parsed_response).returns("the response")
36
+ MonkeyWrench::Base.stubs(:base_uri).returns('http://example.com');
37
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
38
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
39
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).returns(response)
40
+ assert_equal MonkeyWrench::Base.post({}), 'the response'
41
+ end
42
+
43
+ should "rethrow Timeout::Error if retry limit exceeded" do
44
+ retries = sequence('retries')
45
+ response = mock('')
46
+ response.stubs(:parsed_response).returns("the response")
47
+ MonkeyWrench::Base.stubs(:base_uri).returns('http://example.com');
48
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
49
+ HTTParty::Request.any_instance.expects(:perform).in_sequence(retries).raises(Timeout::Error)
50
+ assert_raise Timeout::Error do
51
+ MonkeyWrench::Base.post({}, :retry_limit => 2)
52
+ end
53
+ end
54
+ end
55
+ end
File without changes
File without changes
File without changes
@@ -43,5 +43,15 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
43
43
  assert @example_hash.to_mailchimp.keys.include?("vars%5BEMAIL_ADDRESS%5D")
44
44
  assert @example_hash.to_mailchimp.keys.include?("vars%5BTYPE%5D")
45
45
  end
46
+
47
+ should "convert symbols to strings" do
48
+ assert_equal @example_hash.to_mailchimp['vars%5BTYPE%5D'], 'html'
49
+ end
50
+
51
+ should_eventually "recursively convert hashes" do
52
+ example = { :a => { :b => { :c => 1 }}}
53
+ expected = {"a%5BV%5D%5BC%5D" => 1}
54
+ assert_equal expected, example.to_mailchimp
55
+ end
46
56
  end
47
- end
57
+ end
File without changes
@@ -7,14 +7,15 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
7
7
  setup_config
8
8
  mock_chimp_post(:lists)
9
9
  @list = MonkeyWrench::List.find_by_name("A test list")
10
- clear_fakeweb
11
10
  end
12
11
 
13
12
  context "multiple subscribers at once" do
14
13
  should "subscribe users" do
15
- form_params = {"batch[0][EMAIL]" => "mail@chimp.com",
16
- "batch[0][TYPE]" => "html",
17
- :id => "my-list-id"}
14
+ form_params = {
15
+ :batch => [{'EMAIL' => "mail@chimp.com",
16
+ 'TYPE' => "html"
17
+ }],
18
+ :id => "my-list-id"}
18
19
  mock_chimp_post(:listBatchSubscribe, form_params)
19
20
 
20
21
  subscribers = [{:email => "mail@chimp.com", :type => :html}]
@@ -22,34 +23,23 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
22
23
  assert_equal expected, @list.subscribe(subscribers)
23
24
  end
24
25
 
25
- should "split more than ten subscribers into batches" do
26
- first_batch = {:id => "my-list-id"}
27
- subscribers = []
28
- 10.times do |i|
29
- first_batch["batch[#{i}][EMAIL]"] = "mail#{i}@chimp.com"
30
- first_batch["batch[#{i}][TYPE]"] = "html"
31
- subscribers << {:email => "mail#{i}@chimp.com", :type => :html}
32
- end
33
- mock_chimp_post(:listBatchSubscribe, first_batch,
34
- true, 'listBatchSubscribe10')
35
-
36
- second_batch = {:id => "my-list-id"}
37
- 5.times do |i|
38
- second_batch["batch[#{i}][EMAIL]"] = "mail1#{i}@chimp.com"
39
- second_batch["batch[#{i}][TYPE]"] = "html"
40
- subscribers << {:email => "mail1#{i}@chimp.com", :type => :html}
26
+ should "split more than one thousand subscribers into batches" do
27
+ subscribers = (1..1004).map do |i|
28
+ {:email => "mail#{i}@chimp.com", :type => :html}
41
29
  end
42
- mock_chimp_post(:listBatchSubscribe, second_batch,
43
- true, 'listBatchSubscribe5')
44
-
45
- expected = {:success => 15, :errors => []}
30
+ response_sequence = [
31
+ {:body => canned_response('listBatchSubscribe1000_success.json'), :headers => {'Content-Type' => 'application/json'}},
32
+ {:body => canned_response('listBatchSubscribe4_success.json'), :headers => {'Content-Type' => 'application/json'}}
33
+ ]
34
+ stub_request(:post, uri_for_remote_method('listBatchSubscribe')).to_return(response_sequence)
35
+ expected = {:success => 1004, :errors => []}
46
36
  assert_equal expected, @list.subscribe(subscribers)
47
37
  end
48
38
 
49
39
  should "send welcome email" do
50
- form_params = {"merge_vars[FOO]" => "bar", :id => "my-list-id",
51
- :email => "mail@chimp.com", :type => :html,
52
- :send_welcome => true}
40
+ form_params = {:merge_vars => {"FOO" => "bar"}, :id => "my-list-id",
41
+ :email_address => "mail@chimp.com", :type => "html",
42
+ :send_welcome => "true"}
53
43
  mock_chimp_post(:listSubscribe, form_params)
54
44
 
55
45
  subscribers = [{:email => "mail@chimp.com", :type => :html, :foo => "bar"}]
@@ -62,10 +52,10 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
62
52
 
63
53
  should "collate errors" do
64
54
  form_params = {
65
- "batch[0][EMAIL]" => "mail@chimp.com",
66
- "batch[0][TYPE]" => "html",
67
- "batch[1][EMAIL]" => "bademail@badmail",
68
- "batch[1][TYPE]" => "html",
55
+ :batch => [
56
+ {'EMAIL' => "mail@chimp.com", 'TYPE' => 'html'},
57
+ {'EMAIL' => "bademail@badmail", 'TYPE' => 'html'}
58
+ ],
69
59
  :id => "my-list-id"}
70
60
  mock_chimp_post(:listBatchSubscribe, form_params, true, 'listBatchSubscribe_with_error')
71
61
 
@@ -84,15 +74,14 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
84
74
 
85
75
  context "a single subscriber" do
86
76
  should "subsbscibe a user" do
87
- form_params = { :type=>:html,
88
- :update_existing=>true,
89
- "merge_vars[MY_DATE]"=>"20090101",
90
- :replace_interests=>false,
91
- :double_optin=>false,
92
- "merge_vars[FNAME]"=>"Joe",
77
+ form_params = { :type=> "html",
78
+ :update_existing => "true",
79
+ :merge_vars => {'MY_DATE'=>"20090101", 'FNAME' => 'Joe'},
80
+ :replace_interests => "false",
81
+ :double_optin => "false",
93
82
  :id => "my-list-id",
94
- :send_welcome=>true,
95
- :email => "mail@chimp.com" }
83
+ :send_welcome => "true",
84
+ :email_address => "mail@chimp.com" }
96
85
  mock_chimp_post(:listSubscribe, form_params)
97
86
 
98
87
  params = { :type => :html,
@@ -119,7 +108,7 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
119
108
 
120
109
  context "multiple subscribers at once" do
121
110
  should "unsubscribe" do
122
- form_params = {"emails[0]" => "mail@chimp.com", :id => "my-list-id"}
111
+ form_params = {"emails" => ["mail@chimp.com"], :id => "my-list-id"}
123
112
  mock_chimp_post(:listBatchUnsubscribe, form_params)
124
113
  subscribers = ["mail@chimp.com"]
125
114
 
@@ -128,8 +117,8 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
128
117
  end
129
118
 
130
119
  should "delete subscriber" do
131
- form_params = {"emails[0]" => "mail@chimp.com", :id => "my-list-id",
132
- :delete_member => true}
120
+ form_params = {"emails" => ["mail@chimp.com"], :id => "my-list-id",
121
+ :delete_member => "true"}
133
122
  mock_chimp_post(:listBatchUnsubscribe, form_params)
134
123
  subscribers = ["mail@chimp.com"]
135
124
 
@@ -138,8 +127,8 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
138
127
  end
139
128
 
140
129
  should "send goodbye" do
141
- form_params = {"emails[0]" => "mail@chimp.com", :id => "my-list-id",
142
- :send_goodbye => true}
130
+ form_params = {"emails" => ["mail@chimp.com"], :id => "my-list-id",
131
+ :send_goodbye => "true"}
143
132
  mock_chimp_post(:listBatchUnsubscribe, form_params)
144
133
  subscribers = ["mail@chimp.com"]
145
134
 
@@ -148,8 +137,8 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
148
137
  end
149
138
 
150
139
  should "send unsubscribe notification" do
151
- form_params = {"emails[0]" => "mail@chimp.com", :id => "my-list-id",
152
- :send_notify => true}
140
+ form_params = {"emails" => ["mail@chimp.com"], :id => "my-list-id",
141
+ :send_notify => "true"}
153
142
  mock_chimp_post(:listBatchUnsubscribe, form_params)
154
143
  subscribers = ["mail@chimp.com"]
155
144
 
@@ -160,7 +149,7 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
160
149
 
161
150
  context "a single subscriber" do
162
151
  should "unsubscribe" do
163
- form_params = { "emails[0]" => "mail@chimp.com", :id => "my-list-id" }
152
+ form_params = { "emails" => ["mail@chimp.com"], :id => "my-list-id" }
164
153
  mock_chimp_post(:listBatchUnsubscribe, form_params)
165
154
 
166
155
  expected = {:success => 1, :errors => []}
@@ -178,12 +167,14 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
178
167
 
179
168
  context "multiple subscribers at once" do
180
169
  should "opt out" do
181
- form_params = { "batch[0][EMAIL]" => "mail@chimp.com", :id => "my-list-id",
182
- "batch[1][EMAIL]" => "foo@bar.com"}
170
+ form_params = { :batch => [
171
+ {'EMAIL' => "mail@chimp.com"},
172
+ {'EMAIL' => 'foo@bar.com'}
173
+ ], :id => "my-list-id"
174
+ }
183
175
  mock_chimp_post(:listBatchSubscribe, form_params)
184
- form_params = { "emails[0]" => "mail@chimp.com", :id => "my-list-id",
185
- "emails[1]" => "foo@bar.com",
186
- :send_goodbye => false, :send_notify => false }
176
+ form_params = { :emails => ["mail@chimp.com", "foo@bar.com"], :id => "my-list-id",
177
+ :send_goodbye => "false", :send_notify => "false" }
187
178
  mock_chimp_post(:listBatchUnsubscribe, form_params)
188
179
  subscribers = ["mail@chimp.com", "foo@bar.com"]
189
180
 
@@ -194,10 +185,10 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
194
185
 
195
186
  context "a single subscriber" do
196
187
  should "opt out" do
197
- form_params = { "batch[0][EMAIL]" => "mail@chimp.com", :id => "my-list-id" }
188
+ form_params = { :batch => [{"EMAIL" => "mail@chimp.com"}], :id => "my-list-id" }
198
189
  mock_chimp_post(:listBatchSubscribe, form_params)
199
- form_params = { "emails[0]" => "mail@chimp.com", :id => "my-list-id",
200
- :send_goodbye => false, :send_notify => false}
190
+ form_params = { :emails => ["mail@chimp.com"], :id => "my-list-id",
191
+ :send_goodbye => "false", :send_notify => "false"}
201
192
  mock_chimp_post(:listBatchUnsubscribe, form_params)
202
193
 
203
194
  expected = {:success => 1, :errors => []}
@@ -223,9 +214,13 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
223
214
  end
224
215
 
225
216
  should "iterate over all members" do
226
- mock_chimp_post(:listMembers, {:id => "my-list-id", :start => 0, :limit => 15000 }, true, 'listMembers')
227
- mock_chimp_post(:listMembers, {:id => "my-list-id", :start => 1, :limit => 15000 }, true, 'listMembers')
228
- mock_chimp_post(:listMembers, {:id => "my-list-id", :start => 2, :limit => 15000 }, true, 'listMembers_none')
217
+ response_sequence = %w{listMembers listMembers listMembers_none}.map do |fixture|
218
+ {
219
+ :body => canned_response("#{fixture}_success.json"),
220
+ :headers => {'Content-Type' => 'application/json'}
221
+ }
222
+ end
223
+ stub_request(:post, uri_for_remote_method('listMembers')).to_return(response_sequence)
229
224
 
230
225
  expected = [
231
226
  MonkeyWrench::Member.new({"timestamp"=>"2009-11-12 15:46:20", "email"=>"david@email.com"}),
@@ -249,32 +244,39 @@ class MonkeyWrench::ListTest < Test::Unit::TestCase
249
244
  should "accept single member's email address change as hash" do
250
245
  member = {:email => "foo@bar.com", :new_email => "bar@foo.com"}
251
246
  form_params = {:email_address => "foo@bar.com",
252
- "merge_vars[EMAIL]" => 'bar@foo.com',
253
- :replace_interests => true, :id => "my-list-id"}
247
+ :merge_vars => {"EMAIL" => 'bar@foo.com'},
248
+ :replace_interests => "true", :id => "my-list-id"}
254
249
  mock_chimp_post(:listUpdateMember, form_params)
255
250
  @list.update_members(member, :replace_interests => true)
256
251
  end
257
252
 
258
253
  should "accept single member's email address change as list" do
259
254
  members = [{:email => "foo@bar.com", :new_email => "bar@foo.com"}]
260
- form_params = {:email_address => "foo@bar.com",
261
- "merge_vars[EMAIL]" => 'bar@foo.com',
262
- :replace_interests => true, :id => "my-list-id"}
255
+ form_params = {
256
+ :email_address => "foo@bar.com",
257
+ :merge_vars => {'EMAIL' => 'bar@foo.com'},
258
+ :replace_interests => "true", :id => "my-list-id"
259
+ }
263
260
  mock_chimp_post(:listUpdateMember, form_params)
264
261
  @list.update_members(members, :replace_interests => true)
265
262
  end
266
263
 
267
264
  should "update multiple members emails addresses" do
268
- members = [{:email => "foo@bar.com", :new_email => "bar@foo.com"},
265
+ members = [
266
+ {:email => "foo@bar.com", :new_email => "bar@foo.com"},
269
267
  {:email => "spock@vulcan.com", :new_email => "sylar@heroes.com"}
270
268
  ]
271
- form_params = {:email_address => "foo@bar.com",
272
- "merge_vars[EMAIL]" => 'bar@foo.com',
273
- :replace_interests => true, :id => "my-list-id"}
269
+ form_params = {
270
+ :email_address => "foo@bar.com",
271
+ :merge_vars => {"EMAIL" => 'bar@foo.com'},
272
+ :replace_interests => "true", :id => "my-list-id"
273
+ }
274
274
  mock_chimp_post(:listUpdateMember, form_params)
275
- form_params = {:email_address => "spock@vulcan.com",
276
- "merge_vars[EMAIL]" => 'sylar@heroes.com',
277
- :replace_interests => true, :id => "my-list-id"}
275
+ form_params = {
276
+ :email_address => "spock@vulcan.com",
277
+ :merge_vars => {'EMAIL' => 'sylar@heroes.com'},
278
+ :replace_interests => "true", :id => "my-list-id"
279
+ }
278
280
  mock_chimp_post(:listUpdateMember, form_params)
279
281
  @list.update_members(members, :replace_interests => true)
280
282
  end
File without changes
data/test/test_helper.rb CHANGED
@@ -2,66 +2,66 @@ require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
4
  require 'net/http'
5
+ require 'mocha'
6
+ require 'webmock/test_unit'
7
+ require 'yajl/json_gem'
5
8
 
6
9
  begin
7
10
  require "redgreen"
8
11
  rescue LoadError;
9
12
  end
10
13
 
11
- require File.expand_path(File.dirname(__FILE__)) + '/lib/fakeweb/lib/fake_web'
12
-
13
14
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
15
  $LOAD_PATH.unshift(File.dirname(__FILE__))
15
16
  require "#{File.dirname(__FILE__)}/../lib/monkey_wrench"
16
17
 
17
- FakeWeb.allow_net_connect = false
18
-
19
18
  class Test::Unit::TestCase
19
+ include WebMock
20
+
20
21
  protected
21
22
 
22
23
  def setup_config
23
24
  MonkeyWrench::Config.new(:apikey => "my-key", :datacenter => "my-dc")
24
25
  end
25
-
26
- def mock_response(method, api, dc, remote_method, params, fixture, is_success)
27
- params.merge!({ :method => remote_method, :output => :json, :apikey => api})
28
- form_params = map_form_params(params).gsub(/%5([b-d])/) {|s| s.upcase}
29
- uri = "http://#{dc}.api.mailchimp.com/1.2/?#{form_params}"
30
- response = File.read(json_fixture_path(fixture, is_success))
31
- store_response(uri, params, response)
32
- FakeWeb.register_uri(method, uri, { :body => response, :content_type => 'application/json' })
26
+
27
+ def uri_for_remote_method(remote_method)
28
+ get_params = { :method => remote_method, :output => :json, :apikey => "my-key"}
29
+ query_string = map_form_params(get_params).gsub(/%5([b-d])/) {|s| s.upcase}
30
+ "http://my-dc.api.mailchimp.com/1.2/?#{query_string}"
33
31
  end
34
-
35
- def mock_chimp_post(method, params = {}, is_success = true, fixture = nil)
36
- mock_response(:post, "my-key", "my-dc", method, params, fixture || method, is_success)
32
+
33
+ def mock_chimp_posts(remote_method, sequence)
34
+ uri = uri_for_remote_method(remote_method)
35
+ sequence.each do |response|
36
+ response_body = canned_response(fixture_filename(response[:fixture] || remote_method, response[:is_success]))
37
+ stub_request(:post, uri).with(:body => response[:params]).
38
+ to_return(:body => response_body, :headers => {'Content-Type' => 'application/json'})
39
+ end
37
40
  end
38
-
39
- def store_response(uri, params, response)
40
- @stored_responses ||= {}
41
- @stored_responses[uri] ||= {}
42
- @stored_responses[uri][params.collect_kv{|k,v| [k.to_s, v.to_s]}.inspect] = response
41
+
42
+ def mock_chimp_post(remote_method, post_params = {}, is_success = true, fixture = nil)
43
+ mock_chimp_posts remote_method, [{:params => post_params, :is_success => is_success, :fixture => fixture}]
43
44
  end
44
45
 
45
- def get_response(uri, actual_params)
46
- response = @stored_responses[uri][actual_params.inspect]
47
- raise "Unable to handle request to #{uri} with params: #{actual_params.inspect}" unless response
48
- response
46
+ def escape(string)
47
+ URI.escape(string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
49
48
  end
50
-
49
+
51
50
  def map_form_params(params)
52
- request = Net::HTTP::Post.new("http://localhost/")
53
- request.set_form_data(params)
54
- request.body
51
+ params.map { |k,v| escape(k.to_s) + '=' + escape(v.to_s) }.join('&')
55
52
  end
56
53
 
57
- def json_fixture_path(fixture, is_success)
58
- response = is_success ? "success" : "fail"
59
- File.join(File.dirname(__FILE__), "fixtures", "#{fixture}_#{response}.json")
54
+ def canned_response(filename)
55
+ File.read(fixture_path(filename))
60
56
  end
61
-
62
- def clear_fakeweb
63
- FakeWeb.clean_registry
64
- FakeWeb.allow_net_connect = false
57
+
58
+ def fixture_filename(fixture, is_success)
59
+ outcome = is_success ? "success" : "fail"
60
+ "#{fixture}_#{outcome}.json"
65
61
  end
66
62
 
63
+ def fixture_path(filename)
64
+ File.join(File.dirname(__FILE__), "fixtures", filename)
65
+ end
66
+
67
67
  end
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monkey_wrench
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 2
10
+ version: 0.1.2
5
11
  platform: ruby
6
12
  authors:
7
13
  - Glenn Gillen
@@ -9,29 +15,41 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-07-13 00:00:00 +10:00
18
+ date: 2010-10-01 00:00:00 +01:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
- name: json
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ name: yajl-ruby
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
20
26
  requirements:
21
27
  - - "="
22
28
  - !ruby/object:Gem::Version
23
- version: 1.1.9
24
- version:
29
+ hash: 13
30
+ segments:
31
+ - 0
32
+ - 7
33
+ - 7
34
+ version: 0.7.7
35
+ type: :runtime
36
+ version_requirements: *id001
25
37
  - !ruby/object:Gem::Dependency
26
38
  name: httparty
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
30
42
  requirements:
31
43
  - - "="
32
44
  - !ruby/object:Gem::Version
33
- version: 0.4.5
34
- version:
45
+ hash: 5
46
+ segments:
47
+ - 0
48
+ - 6
49
+ - 1
50
+ version: 0.6.1
51
+ type: :runtime
52
+ version_requirements: *id002
35
53
  description: A ruby API for managing lists, campaigns, subscribers, etc. within Mailchimp (http://www.mailchimp.com/)
36
54
  email: glenn@rubypond.com
37
55
  executables: []
@@ -58,7 +76,9 @@ files:
58
76
  - lib/monkey_wrench.rb
59
77
  - test/fixtures/api_fail.json
60
78
  - test/fixtures/campaigns_success.json
79
+ - test/fixtures/listBatchSubscribe1000_success.json
61
80
  - test/fixtures/listBatchSubscribe10_success.json
81
+ - test/fixtures/listBatchSubscribe4_success.json
62
82
  - test/fixtures/listBatchSubscribe5_success.json
63
83
  - test/fixtures/listBatchSubscribe_success.json
64
84
  - test/fixtures/listBatchSubscribe_with_error_success.json
@@ -71,6 +91,7 @@ files:
71
91
  - test/fixtures/listSubscribe_success.json
72
92
  - test/fixtures/listUnsubscribe_success.json
73
93
  - test/fixtures/listUpdateMember_success.json
94
+ - test/monkey_wrench/base_test.rb
74
95
  - test/monkey_wrench/campaign_aim_test.rb
75
96
  - test/monkey_wrench/campaign_stats_test.rb
76
97
  - test/monkey_wrench/campaign_test.rb
@@ -90,28 +111,36 @@ require_paths:
90
111
  - .
91
112
  - lib
92
113
  required_ruby_version: !ruby/object:Gem::Requirement
114
+ none: false
93
115
  requirements:
94
116
  - - ">="
95
117
  - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
96
121
  version: "0"
97
- version:
98
122
  required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
99
124
  requirements:
100
125
  - - ">="
101
126
  - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
102
130
  version: "0"
103
- version:
104
131
  requirements: []
105
132
 
106
133
  rubyforge_project:
107
- rubygems_version: 1.3.5
134
+ rubygems_version: 1.3.7
108
135
  signing_key:
109
136
  specification_version: 2
110
137
  summary: A ruby wrapper for the Mailchimp API
111
138
  test_files:
112
139
  - test/fixtures/api_fail.json
113
140
  - test/fixtures/campaigns_success.json
141
+ - test/fixtures/listBatchSubscribe1000_success.json
114
142
  - test/fixtures/listBatchSubscribe10_success.json
143
+ - test/fixtures/listBatchSubscribe4_success.json
115
144
  - test/fixtures/listBatchSubscribe5_success.json
116
145
  - test/fixtures/listBatchSubscribe_success.json
117
146
  - test/fixtures/listBatchSubscribe_with_error_success.json
@@ -124,6 +153,7 @@ test_files:
124
153
  - test/fixtures/listSubscribe_success.json
125
154
  - test/fixtures/listUnsubscribe_success.json
126
155
  - test/fixtures/listUpdateMember_success.json
156
+ - test/monkey_wrench/base_test.rb
127
157
  - test/monkey_wrench/campaign_aim_test.rb
128
158
  - test/monkey_wrench/campaign_stats_test.rb
129
159
  - test/monkey_wrench/campaign_test.rb