hominid 1.2.1 → 2.0.0

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,85 @@
1
+ module Hominid
2
+ class Base
3
+
4
+ # MailChimp API Documentation: http://www.mailchimp.com/api/1.2/
5
+ MAILCHIMP_API_VERSION = "1.2"
6
+
7
+ def initialize(config = {})
8
+ if defined?(Rails.root) && (!config || config.empty?)
9
+ config = YAML.load(File.open("#{Rails.root}/config/hominid.yml"))[Rails.env].symbolize_keys
10
+ end
11
+ api_endpoint = config[:api_key].split('-').last
12
+ config.merge(:username => config[:username].to_s, :password => config[:password].to_s)
13
+ defaults = {:send_welcome => false,
14
+ :double_opt_in => false,
15
+ :update_existing => true,
16
+ :replace_interests => true,
17
+ :merge_tags => {}}
18
+ @config = defaults.merge(config).freeze
19
+ @chimpApi = XMLRPC::Client.new2("http://#{api_endpoint}.api.mailchimp.com/#{MAILCHIMP_API_VERSION}/")
20
+ end
21
+
22
+ # Security related methods
23
+ # --------------------------------
24
+
25
+ def add_api_key
26
+ @chimpApi.call("apikeyAdd", *@config.values_at(:username, :password, :api_key))
27
+ end
28
+
29
+ def expire_api_key
30
+ @chimpApi.call("apikeyExpire", *@config.values_at(:username, :password, :api_key))
31
+ end
32
+
33
+ def api_keys(include_expired = false)
34
+ username, password = *@config.values_at(:username, :password)
35
+ @chimpApi.call("apikeys", username, password, include_expired)
36
+ end
37
+
38
+ # Used internally by Hominid
39
+ # --------------------------------
40
+
41
+ # handle common cases for which the Mailchimp API would raise Exceptions
42
+ def clean_merge_tags(merge_tags)
43
+ return {} unless merge_tags.is_a? Hash
44
+ merge_tags.each do |key, value|
45
+ if merge_tags[key].is_a? String
46
+ merge_tags[key] = value.gsub("\v", '')
47
+ elsif merge_tags[key].nil?
48
+ merge_tags[key] = ''
49
+ end
50
+ end
51
+ end
52
+
53
+ def apply_defaults_to(options)
54
+ @config.merge(options)
55
+ end
56
+
57
+ def call(method, *args)
58
+ @chimpApi.call(method, @config[:api_key], *args)
59
+ rescue XMLRPC::FaultException => error
60
+ case error.faultCode
61
+ when 230
62
+ raise AlreadySubscribed.new(error)
63
+ when 231
64
+ raise AlreadyUnsubscribed.new(error)
65
+ when 232
66
+ raise NotExists.new(error)
67
+ when 233, 215
68
+ raise NotSubscribed.new(error)
69
+ else
70
+ raise HominidError.new(error)
71
+ end
72
+ rescue RuntimeError => error
73
+ if error.message =~ /Wrong type NilClass\. Not allowed!/
74
+ hashes = args.select{|a| a.is_a? Hash}
75
+ errors = hashes.select{|k, v| v.nil? }.collect{ |k, v| "#{k} is Nil." }.join(' ')
76
+ raise CommunicationError.new(errors)
77
+ else
78
+ raise error
79
+ end
80
+ rescue Exception => error
81
+ raise CommunicationError.new(error.message)
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,172 @@
1
+ module Hominid
2
+
3
+ class Campaign < Base
4
+
5
+ # Campaign related methods
6
+ # --------------------------------
7
+
8
+ attr_reader :campaign_id
9
+ attr_reader :attributes
10
+
11
+ def initialize(*args)
12
+ options = args.last.is_a?(Hash) ? args.last : {}
13
+ raise StandardError.new('Please provide a Campaign ID.') unless options[:id]
14
+ @campaign_id = options.delete(:id)
15
+ @attributes = options.delete(:attributes)
16
+ super(options)
17
+ end
18
+
19
+ def self.all
20
+ # Get all campaigns for this mailchimp account
21
+ new(:id => 0).call("campaigns").to_a.collect { |c| Campaign.new(:id => c.delete('id'), :attributes => c) }
22
+ end
23
+
24
+ def self.find_by_list_name(list_name)
25
+ new(:id => 0).call("campaigns", {:list_id => List.find_by_name(list_name).list_id}).to_a.collect { |c| Campaign.new(:id=> c.delete('id'), :attributes => c) }
26
+ end
27
+
28
+ def self.find_by_list_id(list_id)
29
+ # Find all campaigns for the given list
30
+ new(:id => 0).call("campaigns", {:list_id => list_id}).to_a.collect { |c| Campaign.new(:id=> c.delete('id'), :attributes => c) }
31
+ end
32
+
33
+ def self.find_by_title(title)
34
+ # Find campaign by title
35
+ all.find { |c| c.attributes['title'] =~ /#{title}/ }
36
+ end
37
+
38
+ def self.find_by_type(type)
39
+ # Find campaign by type. Possible choices are:
40
+ # 'regular'
41
+ # 'plaintext'
42
+ # 'absplit'
43
+ # 'rss'
44
+ # 'inspection'
45
+ # 'trans'
46
+ # 'auto'
47
+ all.find { |campaign| campaign.attributes['type'] =~ /#{type}/ }
48
+ end
49
+
50
+ def self.find_by_web_id(web_id)
51
+ # Find campaigns by web_id
52
+ all.find { |campaign| campaign.attributes['web_id'] =~ /#{web_id}/ }
53
+ end
54
+
55
+ def self.find_by_id(id)
56
+ # Find campaign by id
57
+ all.find { |campaign| (campaign.campaign_id == id.to_s) }
58
+ end
59
+
60
+ def self.find(id_or_web_id)
61
+ # Campaign finder method
62
+ all = self.all
63
+ campaign = self.find_by_id(id_or_web_id.to_s).to_a + self.find_by_web_id(id_or_web_id.to_i).to_a
64
+ return campaign.blank? ? nil : campaign.first
65
+ end
66
+
67
+ def self.create(type = 'regular', options = {}, content = {}, segment_options = {}, type_opts = {})
68
+ # Create a new campaign
69
+ # The options hash should be structured as follows:
70
+ #
71
+ # :list_id = (string) The ID of the list to send this campaign to.
72
+ # :subject = (string) The subject of the campaign.
73
+ # :from_email = (string) The email address this campaign will come from.
74
+ # :from_name = (string) The name that this campaign will come from.
75
+ # :to_email = (string) The To: name recipients will see.
76
+ # :template_id = (integer) The ID of the template to use for this campaign (optional).
77
+ # :folder_id = (integer) The ID of the folder to file this campaign in (optional).
78
+ # :tracking = (array) What to track for this campaign (optional).
79
+ # :title = (string) Internal title for this campaign (optional).
80
+ # :authenticate = (boolean) Set to true to authenticate campaign (optional).
81
+ # :analytics = (array) Google analytics tags (optional).
82
+ # :auto_footer = (boolean) Auto-generate the footer (optional)?
83
+ # :inline_css = (boolean) Inline the CSS styles (optional)?
84
+ # :generate_text = (boolean) Auto-generate text from HTML email (optional)?
85
+ #
86
+ # Visit http://www.mailchimp.com/api/1.2/campaigncreate.func.php for more information about creating
87
+ # campaigns via the API.
88
+ #
89
+ new(:id => 0).call("campaignCreate", type, options, content, segment_options, type_opts)
90
+ ## TODO: Return the new campaign with the ID returned from the API
91
+ end
92
+
93
+ def self.templates
94
+ # Get the templates for this account
95
+ new(:id => 0).call("campaignTemplates")
96
+ end
97
+
98
+ def add_order(order)
99
+ # Attach Ecommerce Order Information to a campaign.
100
+ # The order hash should be structured as follows:
101
+ #
102
+ # :id = (string) the order id
103
+ # :email_id = (string) email id of the subscriber (mc_eid query string)
104
+ # :total = (double) Show only campaigns with this from_name.
105
+ # :shipping = (string) *optional - the total paid for shipping fees.
106
+ # :tax = (string) *optional - the total tax paid.
107
+ # :store_id = (string) a unique id for the store sending the order in
108
+ # :store_name = (string) *optional - A readable name for the store, typicaly the hostname.
109
+ # :plugin_id = (string) the MailChimp-assigned Plugin Id. Using 1214 for the moment.
110
+ # :items = (array) the individual line items for an order, using the following keys:
111
+ #
112
+ # :line_num = (integer) *optional - line number of the item on the order
113
+ # :product_id = (integer) internal product id
114
+ # :product_name = (string) the name for the product_id associated with the item
115
+ # :category_id = (integer) internal id for the (main) category associated with product
116
+ # :category_name = (string) the category name for the category id
117
+ # :qty = (double) the quantity of items ordered
118
+ # :cost = (double) the cost of a single item (i.e., not the extended cost of the line)
119
+ order.merge(:campaign_id => @campaign_id)
120
+ call("campaignEcommAddOrder", order)
121
+ end
122
+
123
+ def campaign_stats()
124
+ # Get the stats of a campaign
125
+ call("campaignStats", @campaign_id)
126
+ end
127
+
128
+ # Get the HTML & text content for a campaign
129
+ # :for_archive = (boolean) default true, true returns the content as it would appear in the archive, false returns the raw HTML/text
130
+ def campaign_content(for_archive = true)
131
+ # Get the content of a campaign
132
+ call("campaignContent", @campaign_id, for_archive)
133
+ end
134
+
135
+ def delete_campaign()
136
+ # Delete a campaign
137
+ call("campaignDelete", @campaign_id)
138
+ end
139
+
140
+ def replicate_campaign()
141
+ # Replicate a campaign (returns ID of new campaign)
142
+ call("campaignReplicate", @campaign_id)
143
+ end
144
+
145
+ def schedule_campaign(time = "#{1.day.from_now}")
146
+ # Schedule a campaign
147
+ ## TODO: Add support for A/B Split scheduling
148
+ call("campaignSchedule", @campaign_id, time)
149
+ end
150
+
151
+ def send_now()
152
+ # Send a campaign
153
+ call("campaignSendNow", @campaign_id)
154
+ end
155
+
156
+ # Send a test of a campaign
157
+ def send_test(emails = {})
158
+ call("campaignSendTest", @campaign_id, emails)
159
+ end
160
+
161
+ def update(name, value)
162
+ # Update a campaign
163
+ call("campaignUpdate", @campaign_id, name, value)
164
+ end
165
+
166
+ def unschedule()
167
+ # Unschedule a campaign
168
+ call("campaignUnschedule", @campaign_id)
169
+ end
170
+ end
171
+ end
172
+
@@ -0,0 +1,45 @@
1
+ module Hominid
2
+
3
+ class Helper < Base
4
+
5
+ # Helper methods
6
+ # --------------------------------
7
+
8
+ def self.account_details
9
+ # Get details for this account.
10
+ new.call("getAccountDetails")
11
+ end
12
+
13
+ def self.convert_css_to_inline(html, strip_css = false)
14
+ # Convert CSS styles to inline styles and (optionally) remove original styles
15
+ new.call("inlineCss", html, strip_css)
16
+ end
17
+
18
+ def self.create_folder(name)
19
+ # Create a new folder to file campaigns in
20
+ new.call("createFolder", name)
21
+ end
22
+
23
+ def self.generate_text(type, content)
24
+ # Have HTML content auto-converted to a text-only format.
25
+ # The options for text type are:
26
+ # 'html' => Expects a string of HTML(default).
27
+ # 'template' => Expects an array.
28
+ # 'url' => Expects a valid and public URL.
29
+ # 'cid' => Expects a campaign ID.
30
+ # 'tid' => Expects a template ID.
31
+ new.call("generateText", type, content)
32
+ end
33
+
34
+ def self.html_to_text(content)
35
+ # Convert HTML content to text
36
+ new.call("generateText", 'html', content)
37
+ end
38
+
39
+ def self.ping
40
+ # Ping the Mailchimp API
41
+ new.call("ping")
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,150 @@
1
+ module Hominid
2
+
3
+ class List < Base
4
+
5
+ # List related methods
6
+ # --------------------------------
7
+
8
+ attr_reader :list_id
9
+ attr_reader :attributes
10
+
11
+ def initialize(*args)
12
+ options = args.last.is_a?(Hash) ? args.last : {}
13
+ raise StandardError.new('Please provide a List ID.') unless options[:id]
14
+ @list_id = options.delete(:id)
15
+ @attributes = options.delete(:attributes)
16
+ super(options)
17
+ end
18
+
19
+ def self.all
20
+ # Get all lists for this mailchimp account
21
+ new(:id => 0).call("lists").to_a.collect { |list| List.new(:id => list.delete('id'), :attributes => list) }
22
+ end
23
+
24
+ def self.find_by_name(name)
25
+ # Find list by name
26
+ all.find { |list| list.attributes['name'] =~ /#{name}/ }
27
+ end
28
+
29
+ def self.find_by_web_id(web_id)
30
+ # Find list by name
31
+ all.find { |list| (list.attributes['web_id'] == web_id.to_i) }
32
+ end
33
+
34
+ def self.find_by_id(id)
35
+ # Find list by id
36
+ all.find { |list| (list.list_id == id.to_s) }
37
+ end
38
+
39
+ def self.find(id_or_web_id)
40
+ # List finder method
41
+ all = self.all
42
+ list = self.find_by_id(id_or_web_id.to_s).to_a + self.find_by_web_id(id_or_web_id.to_i).to_a
43
+ return list.blank? ? nil : list.first
44
+ end
45
+
46
+ def create_group(group)
47
+ # Add an interest group to a list
48
+ call("listInterestGroupAdd", @list_id, group)
49
+ end
50
+ alias :interest_group_add :create_group
51
+
52
+ def create_tag(tag, name, required = false)
53
+ # Add a merge tag to a list
54
+ call("listMergeVarAdd", @list_id, tag, name, required)
55
+ end
56
+ alias :merge_var_add :create_tag
57
+
58
+ def delete_group(group)
59
+ # Delete an interest group for a list
60
+ call("listInterestGroupDel", @list_id, group)
61
+ end
62
+ alias :interest_group_del :delete_group
63
+
64
+ def delete_tag(tag)
65
+ # Delete a merge tag and all its members
66
+ call("listMergeVarDel", @list_id, tag)
67
+ end
68
+ alias :merge_var_del :delete_tag
69
+
70
+ def groups()
71
+ # Get the interest groups for a list
72
+ call("listInterestGroups", @list_id)
73
+ end
74
+ alias :interest_groups :groups
75
+
76
+
77
+ def member_info(email)
78
+ # Get a member of a list
79
+ call("listMemberInfo", @list_id, email)
80
+ end
81
+
82
+ def members(status = "subscribed", since = "2000-01-01 00:00:00", start = 0, limit = 100)
83
+ # Get members of a list based on status
84
+ # Select members based on one of the following statuses:
85
+ # 'subscribed'
86
+ # 'unsubscribed'
87
+ # 'cleaned'
88
+ # 'updated'
89
+ #
90
+ # Select members that have updated their status or profile by providing
91
+ # a "since" date in the format of YYYY-MM-DD HH:MM:SS
92
+ call("listMembers", @list_id, status, since, start, limit)
93
+ end
94
+
95
+ def merge_tags()
96
+ # Get the merge tags for a list
97
+ call("listMergeVars", @list_id)
98
+ end
99
+ alias :merge_vars :merge_tags
100
+
101
+ def subscribe(email, options = {})
102
+ # Subscribe a member to this list
103
+ merge_tags = clean_merge_tags options[:merge_tags]
104
+ options = apply_defaults_to({:email_type => "html"}.merge(options))
105
+ call(
106
+ "listSubscribe",
107
+ @list_id,
108
+ email,
109
+ merge_tags,
110
+ *options.values_at(
111
+ :email_type,
112
+ :double_opt_in,
113
+ :update_existing,
114
+ :replace_interests,
115
+ :send_welcome
116
+ )
117
+ )
118
+ end
119
+
120
+ def subscribe_many(subscribers, options = {})
121
+ # Subscribe an array of email addresses
122
+ # subscribers(array) = [{:EMAIL => 'example@email.com', :EMAIL_TYPE => 'html'}]
123
+ subscribers = subscribers.collect { |subscriber| clean_merge_tags(subscriber) }
124
+ options = apply_defaults_to({:update_existing => true}.merge(options))
125
+ call("listBatchSubscribe", @list_id, subscribers, *options.values_at(:double_opt_in, :update_existing, :replace_interests))
126
+ end
127
+ alias :batch_subscribe :subscribe_many
128
+
129
+ def unsubscribe(current_email, options = {})
130
+ # Unsubscribe a list member
131
+ options = apply_defaults_to({:delete_member => true}.merge(options))
132
+ call("listUnsubscribe", @list_id, current_email, *options.values_at(:delete_member, :send_goodbye, :send_notify))
133
+ end
134
+
135
+ def unsubscribe_many(emails, options = {})
136
+ # Unsubscribe an array of email addresses
137
+ # emails(array) = ['first@email.com', 'second@email.com']
138
+ options = apply_defaults_to({:delete_member => true}.merge(options))
139
+ call("listBatchUnsubscribe", @list_id, emails, *options.values_at(:delete_member, :send_goodbye, :send_notify))
140
+ end
141
+ alias :batch_unsubscribe :unsubscribe_many
142
+
143
+ def update_member(current_email, merge_tags = {}, email_type = "html")
144
+ # Update a member of this list
145
+ call("listUpdateMember", @list_id, current_email, merge_tags, email_type, true)
146
+ end
147
+
148
+ end
149
+ end
150
+