monkey_wrench 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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