mcmailer 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # -*- ruby -*-
2
+
3
+ # DO NOT EDIT THIS FILE. Instead, edit Rakefile, and run `rake bundler:gemfile`.
4
+
5
+ source "https://rubygems.org/"
6
+
7
+ gem "uuid", "2.3.5"
8
+ gem "gibbon", "0.3.5"
9
+
10
+ gem "rdoc", "~>4.0", :group => [:development, :test]
11
+ gem "hoe", "~>3.6", :group => [:development, :test]
12
+
13
+ # vim: syntax=ruby
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ gibbon (0.3.5)
5
+ httparty (> 0.6.0)
6
+ httparty (> 0.6.0)
7
+ json (> 1.4.0)
8
+ json (> 1.4.0)
9
+ rdoc
10
+ hoe (3.6.0)
11
+ rake (>= 0.8, < 11.0)
12
+ httparty (0.11.0)
13
+ multi_json (~> 1.0)
14
+ multi_xml (>= 0.5.2)
15
+ json (1.7.7)
16
+ macaddr (1.6.1)
17
+ systemu (~> 2.5.0)
18
+ multi_json (1.7.2)
19
+ multi_xml (0.5.3)
20
+ rake (10.0.4)
21
+ rdoc (4.0.1)
22
+ json (~> 1.4)
23
+ systemu (2.5.2)
24
+ uuid (2.3.5)
25
+ macaddr (~> 1.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ gibbon (= 0.3.5)
32
+ hoe (~> 3.6)
33
+ rdoc (~> 4.0)
34
+ uuid (= 2.3.5)
data/Manifest.txt CHANGED
@@ -1,7 +1,13 @@
1
1
  .autotest
2
+ Gemfile
3
+ Gemfile.lock
2
4
  History.txt
3
5
  Manifest.txt
4
6
  README.txt
5
7
  Rakefile
8
+ config/mcmailer.yml
6
9
  lib/mcmailer.rb
10
+ lib/mcmailer/mailchimp_client.rb
11
+ lib/mcmailer/mailchimp_notification_handler.rb
12
+ mailchimp.rake
7
13
  test/test_mcmailer.rb
@@ -0,0 +1,10 @@
1
+ test:
2
+ api_key: cc7a805fbe90a734204b4100ad3cbbcc-us6
3
+ timeout: 300
4
+ list_name: Mcmailer Test List
5
+ required_fields:
6
+ fname: text
7
+ affiliate: bool
8
+ optional_fields:
9
+ lname: text
10
+ freq_flyer: bool
data/lib/mcmailer.rb CHANGED
@@ -2,7 +2,7 @@ require File.expand_path('../mcmailer/mailchimp_client', __FILE__)
2
2
  require File.expand_path('../mcmailer/mailchimp_notification_handler', __FILE__)
3
3
 
4
4
  module Mcmailer
5
- VERSION = "0.0.3"
5
+ VERSION = "0.0.4"
6
6
 
7
7
  @config = {}
8
8
  @valid_config_keys = ['test', 'development', 'production']
@@ -0,0 +1,117 @@
1
+ require 'gibbon'
2
+
3
+ class MailchimpClient
4
+ class RequestTimeout < StandardError
5
+ def initialize(msg="Request exceeded time limit; consider increasing the timeout value in lib/mailchimp_client")
6
+ super
7
+ end
8
+ end
9
+
10
+ class ListNotFound < StandardError
11
+ def initialize(msg="Unable to find MailChimp list by the name specified")
12
+ super
13
+ end
14
+ end
15
+
16
+ class UnexpectedResponse < StandardError
17
+ end
18
+
19
+
20
+
21
+ attr_reader :previous_response
22
+
23
+ def initialize(api_key, timeout)
24
+ @api_key = api_key
25
+ @gibbon = Gibbon.new(@api_key)
26
+ @gibbon.timeout = timeout
27
+ end
28
+
29
+ def use_list(name)
30
+ begin
31
+ @previous_response = @gibbon.lists
32
+ rescue Timeout::Error
33
+ raise RequestTimeout
34
+ end
35
+
36
+ if @previous_response.has_key?('data')
37
+ lists = @previous_response['data']
38
+ matches = lists.select {|list| list.has_key?('name') && list['name'] == name}
39
+
40
+ if matches.empty?
41
+ raise ListNotFound, "provided name => '#{name}'"
42
+ elsif matches.length > 1
43
+ raise UnexpectedResponse, "list names should be unique but found #{matches.length} lists by the name of '#{name}'"
44
+ else
45
+ @list = matches.first
46
+ end
47
+ else
48
+ raise UnexpectedResponse, "response hash did not have a 'data' key"
49
+ end
50
+ end
51
+
52
+ def subscribers(start=0, limit=100)
53
+ @previous_response = @gibbon.list_members(:id => @list['id'], :start => start, :limit => limit)
54
+ if !@previous_response.has_key?('data')
55
+ raise UnexpectedResponse, "response hash did not have a 'data' key"
56
+ end
57
+
58
+ @previous_response['data']
59
+ rescue Timeout::Error
60
+ raise RequestTimeout
61
+ end
62
+
63
+ def subscribe(email, merge_vars={})
64
+ @previous_response = @gibbon.list_subscribe(:id => @list['id'], :email_address => email, :double_optin => false, :update_existing => true, :merge_vars => merge_vars)
65
+ rescue Timeout::Error
66
+ raise RequestTimeout
67
+ end
68
+
69
+ def subscriber_info(email)
70
+ @previous_response = @gibbon.list_member_info(:id => @list['id'], :email_address => [email])
71
+ rescue Timeout::Error
72
+ raise RequestTimeout
73
+ end
74
+
75
+ def batch_subscribe(batch)
76
+ @previous_response = @gibbon.list_batch_subscribe(:id => @list['id'], :batch => batch, :double_optin => false, :update_existing => true)
77
+ rescue Timeout::Error
78
+ raise RequestTimeout
79
+ end
80
+
81
+ def destroy!(email)
82
+ @previous_response = @gibbon.list_unsubscribe(:id => @list['id'], :email_address => email, :delete_member => true, :send_goodbye => false, :send_notify => false)
83
+ rescue Timeout::Error
84
+ raise RequestTimeout
85
+ end
86
+
87
+ def batch_destroy!(emails)
88
+ @previous_response = @gibbon.list_batch_unsubscribe(:id => @list['id'], :emails => emails, :delete_member => true, :send_goodbye => false, :send_notify => false)
89
+ rescue Timeout::Error
90
+ raise RequestTimeout
91
+ end
92
+
93
+ # WARNING: For large lists this is much slower than deleting all
94
+ # subscribers through Mailchimp's website, but it should work. It's
95
+ # useful for scenarios that require automation like Mailchimp unit
96
+ # tests.
97
+ def destroy_all_subscribers!
98
+ responses = []
99
+ batch_size = 1500
100
+
101
+ until subscribers.count.zero? do
102
+ begin
103
+ emails = self.subscribers(0, batch_size).collect {|s| s['email']}
104
+ batch_destroy!(emails)
105
+ rescue UnexpectedResponse
106
+ raise
107
+ end
108
+
109
+ responses << @previous_response
110
+ break if @previous_response['success_count'].zero?
111
+ end
112
+
113
+ @previous_response = responses
114
+ rescue Timeout::Error
115
+ raise RequestTimeout
116
+ end
117
+ end
@@ -0,0 +1,107 @@
1
+ class MailchimpNotificationHandler
2
+ NO_OP_LAMBDA = lambda {|data| nil}
3
+
4
+ def initialize(params, logger, logic, bool_cols=[])
5
+ @params = params
6
+ @logger = logger
7
+ @bool_columns = bool_cols
8
+ @logic = logic
9
+ @logic.default = NO_OP_LAMBDA
10
+
11
+ # These attrs get populated when the @params hash is parsed.
12
+ @notification_type = ''
13
+ @data = {}
14
+ @message = ''
15
+ end
16
+
17
+ def process
18
+ if valid_notification?
19
+ parse_params
20
+ @logger.info("[Mailchimp (#{@params['fired_at']})] #{@message}")
21
+ @logic[@notification_type].call(@data)
22
+ else
23
+ @logger.error("[Mailchimp] Invalid notification (possible reasons: forged notification from somebody besides Mailchimp, incorrect secret code, invalid notification from Mailchimp, or invalid handling of notification.)\nparams => #{@params}")
24
+ end
25
+ end
26
+
27
+
28
+ private
29
+
30
+ def valid_notification?
31
+ # NOTE: False cases below aren't exhaustive. Implement more
32
+ # false cases as necessary.
33
+ return false unless @params.has_key?('type')
34
+ return false unless @params.has_key?('fired_at')
35
+ return false unless @params.has_key?('data')
36
+
37
+ return true
38
+ end
39
+
40
+ def parse_params
41
+ @notification_type = @params['type']
42
+ @data = @params['data']
43
+ @data['fired_at'] = @params['fired_at']
44
+
45
+ case @notification_type
46
+ when 'subscribe' then
47
+ @message << "#{@data['email']} subscribed on Mailchimp! "
48
+ @message << human_readable_merge_vars
49
+ when 'unsubscribe' then
50
+ @message << "#{@data['email']} was unsubscribed from Mailchimp #{unsubscription_reason} "
51
+ @message << human_readable_merge_vars
52
+ when 'profile' then
53
+ @message << "#{@data['email']}'s profile was updated on Mailchimp. "
54
+ @message << human_readable_merge_vars
55
+ when 'upemail' then
56
+ @message << "the owner of #{@data['old_email']} changed their email address to #{@data['new_email']} on Mailchimp."
57
+ when 'cleaned' then
58
+ @message << "#{@params['data']['email']} was cleaned from Mailchimp #{cleaned_reason}"
59
+ when 'campaign' then
60
+ @message << "the campaign with subject '#{@data['subject']}' was #{@data['status']}."
61
+ else
62
+ @message << "WARNING: Unrecognized Mailchimp notification type '#{@notification_type}'!"
63
+ end
64
+ end
65
+
66
+ def unsubscription_reason
67
+ case @params['data']['reason']
68
+ when 'manual' then 'by request.'
69
+ when 'abuse' then 'due to abuse.'
70
+ else 'for some UNKNOWN reason.'
71
+ end
72
+ end
73
+
74
+ def cleaned_reason
75
+ case @params['data']['reason']
76
+ when 'hard' then "because of a hard bounce on the recipient's server."
77
+ when 'abuse' then "due to abuse."
78
+ else 'for some UNKNOWN reason.'
79
+ end
80
+ end
81
+
82
+ def human_readable_merge_vars
83
+ result = 'Merge variables are:'
84
+ merge_vars.each_pair do |key, value|
85
+ result << "\n\t#{key} => #{value}"
86
+ end
87
+
88
+ return result
89
+ end
90
+
91
+ def merge_vars
92
+ if @params['data'].has_key?('merges')
93
+ result = {}
94
+ @params['data']['merges'].each_pair do |key, value|
95
+ value = num_to_bool(value) if @bool_columns.include?(key.to_s)
96
+ result[key] = value
97
+ end
98
+ return result
99
+ end
100
+ return nil
101
+ end
102
+
103
+ def num_to_bool(string)
104
+ (string == '0' || sttring.nil? || string.empty?) ? false : true
105
+ end
106
+
107
+ end
data/mailchimp.rake ADDED
@@ -0,0 +1,47 @@
1
+ require 'lib/mailchimp_client'
2
+
3
+ namespace :mailchimp do
4
+ def mailchimp_client
5
+ return @mailchimp_client if @mailchimp_client
6
+ @mailchimp_client = MailchimpClient.new(MAILCHIMP[:api_key])
7
+ @mailchimp_client.use_list(MAILCHIMP[:list_name])
8
+ @mailchimp_client
9
+ end
10
+
11
+ desc "Add existing mailing list subscribers to Mailchimp"
12
+ task :batch_subscribe => [:environment] do
13
+ batch = User.find(:all, :conditions => ["opt_in_products = 1 OR opt_in_promotions = 1"]).collect {|user| user.to_mailchimp}
14
+ puts "Adding #{batch.count} emails to the '#{MAILCHIMP[:list_name]}' list..."
15
+
16
+ mailchimp_client.batch_subscribe(batch)
17
+
18
+ puts "Received this response from Mailchimp: #{mailchimp_client.previous_response}"
19
+ puts "Done!"
20
+ end
21
+
22
+ desc "Remove subscriber data on the Mailchimp server"
23
+ task :destroy_subscribers => [:environment] do
24
+ puts "WARNING: This will destroy subscriber data on the Mailchimp server"
25
+
26
+ after_confirmation do
27
+ mailchimp_client.destroy_all_subscribers!
28
+ puts "List now has #{mailchimp_client.subscribers.count} subscribers."
29
+ puts "Done!"
30
+ end
31
+ end
32
+
33
+ def after_confirmation(&block)
34
+ while true
35
+ print " Are you sure you want to proceed? [Y/N] "
36
+ response = STDIN.gets
37
+ if response =~ /^[yY]/
38
+ puts
39
+ block.call()
40
+ return
41
+ elsif response =~ /^[nN]/
42
+ puts " Terminating early as requested."
43
+ return
44
+ end
45
+ end
46
+ end
47
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mcmailer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -86,11 +86,17 @@ extra_rdoc_files:
86
86
  - README.txt
87
87
  files:
88
88
  - .autotest
89
+ - Gemfile
90
+ - Gemfile.lock
89
91
  - History.txt
90
92
  - Manifest.txt
91
93
  - README.txt
92
94
  - Rakefile
95
+ - config/mcmailer.yml
93
96
  - lib/mcmailer.rb
97
+ - lib/mcmailer/mailchimp_client.rb
98
+ - lib/mcmailer/mailchimp_notification_handler.rb
99
+ - mailchimp.rake
94
100
  - test/test_mcmailer.rb
95
101
  - .gemtest
96
102
  homepage: https://github.com/jmitchell/mcmailer
@@ -110,7 +116,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
116
  version: '0'
111
117
  segments:
112
118
  - 0
113
- hash: -111663555823437317
119
+ hash: 2007240709961413144
114
120
  required_rubygems_version: !ruby/object:Gem::Requirement
115
121
  none: false
116
122
  requirements: