babney-hominid 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ pkg/*
3
+ pkg/*.*
4
+ .svn
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Brian Getting
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,161 @@
1
+ h1. Hominid
2
+
3
+ Hominid is a Ruby gem that provides a wrapper for interacting with the "Mailchimp":http://www.mailchimp.com email marketing service API ("version 1.2":http://www.mailchimp.com/api/1.2/).
4
+
5
+ h2. Installation
6
+
7
+ <pre><code>sudo gem install hominid, :version => '>= 2.0.1', :source => "http://gemcutter.org"</code></pre>
8
+
9
+ Hominid is hosted at "Gemcutter":http://gemcutter.org. Be sure that you have the Gemcutter gem installed if you are having trouble installing Hominid:
10
+
11
+ <pre><code>sudo gem install gemcutter
12
+ gem tumble</code></pre>
13
+
14
+ h2. Configuration
15
+
16
+ You will need to create a "Mailchimp":http://www.mailchimp.com/signup account and get your API key (available at http://admin.mailchimp.com/account/api/) in order to get started.
17
+
18
+ If you are using Hominid inside a Rails application, you can create a config file at @/config/hominid.yml@ with your Mailchimp account information and basic configuration options:
19
+
20
+ <pre><code>development:
21
+ username: USERNAME
22
+ password: PASSWORD
23
+ api_key: API KEY
24
+ send_goodbye: false
25
+ send_notify: false
26
+ double_opt: false
27
+
28
+ ...</code></pre>
29
+
30
+ Run @rake hominid:config@ from within a Rails app to create an empty config file.
31
+ Note: You will need to <pre><code>require 'hominid'</code></pre> in your @Rakefile@ to make this rake task available to your application.
32
+
33
+ h2. Usage
34
+
35
+ Not all API methods are supported (yet). Currently there are classes for working with lists (_Hominid::List_), campaigns (_Hominid::Campaign_) and accessing the helper methods (_Hominid::Helper_).
36
+
37
+ h3. Working with Lists
38
+
39
+ The _Hominid::List_ class is available for working finding lists and working with particular lists. See _Hominid::List_ for more information.
40
+
41
+ h4. List Finder Methods
42
+
43
+ There are finder methods for working with lists. Refer to _Hominid::List_ to see the other finders availables.
44
+
45
+ <pre><code>lists = Hominid::List.all</code></pre>
46
+
47
+ <pre><code>list = Hominid::List.find_by_name("List Name")</code></pre>
48
+
49
+ <pre><code>list = Hominid::List.find(id_or_web_id)</code></pre>
50
+
51
+ h4. Subscribing
52
+
53
+ To subscribe a person or persons to a Mailchimp list:
54
+
55
+ <pre><code>list.subscribe("sample@emailaddress.com")</code></pre>
56
+
57
+ <pre><code>list.subscribe_many([{:EMAIL => 'sample@emailaddress.com', :EMAIL_TYPE => 'html'}, {:EMAIL => 'another@emailaddress.com', :EMAIL_TYPE => 'html'}])</code></pre>
58
+
59
+ h4. Unsubscribing
60
+
61
+ To unsubscribe a person or persons from a Mailchimp list:
62
+
63
+ <pre><code>list.unsubscribe("sample@emailaddress.com")</code></pre>
64
+
65
+ <pre><code>list.unsubscribe_many(['sample@emailaddress.com', 'another@emailaddress.com'])</code></pre>
66
+
67
+ h4. Updating
68
+
69
+ In the following example, we will be changing a person's email address on the Mailchimp list from @sample@ to @another@:
70
+
71
+ <pre><code>list.update_member('sample@emailaddress.com', {:EMAIL => 'another@emailaddress.com'}, 'html')</code></pre>
72
+
73
+ You can also updated other attributes by including the MERGE_VARS that you want to change, such as @EMAIL@, @FNAME@, @LNAME@ and @INTERESTS@. Get a list of merge tags for a particular list by running @list.merge_tags@.
74
+
75
+ h3. Working with Campaigns
76
+
77
+ The _Hominid::Campaign_ class provides methods for working with a campaigns.
78
+
79
+ h4. Campaign Finder Methods
80
+
81
+ There are finder methods for campaigns as well. Refer to _Hominid::Campaign_ to see the other finders available.
82
+
83
+ <pre><code>campaigns = Hominid::Campaign.all</code></pre>
84
+
85
+ <pre><code>campaigns = Hominid::Campaign.find_by_list_name("List Name")</code></pre>
86
+
87
+ h4. Creating a Campaign
88
+
89
+ You can create new campaigns using Hominid as well. Please refer to the documentation in _Hominid::Base_ for more information about the options available when creating a new campaign.
90
+
91
+ <pre><code>new_campaign = Hominid::Campaign.create('regular', options, content, segment_opts, type_opts)</code></pre>
92
+
93
+ h4. Schedule a Campaign
94
+
95
+ As an example of how to work with a particular campaign, use the _Hominid::Campaign_ class. Extending from the previous example, since the _#create_campaign_ method returns the ID of the created campaign, we can use it to instantiate the _Hominid::Campaign_ class and schedule our new campaign to go be delivered 2 days from now:
96
+
97
+ <pre><code>campaign = Hominid::Campaign.new(:id => new_campaign)</code></pre>
98
+
99
+ <pre><code>campaign.schedule_campaign(2.days.from_now)</code></pre>
100
+
101
+ h3. Helper Methods
102
+
103
+ The _Hominid::Helper_ class provides a way to access the helper methods for the Mailchimp API. For example, to create a new folder for filing campaigns:
104
+
105
+ <pre><code>folder = Hominid::Helper.create_folder("Folder Name")</code></pre>
106
+
107
+ h2. Syncing Your Application
108
+
109
+ If you are integrating an application with Mailchimp, Hominid will provide a way for your app to connect with your Mailchimp account. However, it does not provide a way for Mailchimp to connect to your application, which is why Mailchimp has implemented "web hooks":http://www.mailchimp.com/api/webhooks/.
110
+
111
+ The _Hominid::Webhook_ class helps with receiving <tt>POST</tt> data from a Mailchimp webhook:
112
+
113
+ <pre><code>hook = Hominid::Webhook.new(params)
114
+ case hook.event
115
+ when "subscribe"
116
+ user = User.find_by_email(hook.email)
117
+ user.opted_in = true
118
+ user.save
119
+ when "unsubscribe"
120
+ user = User.find_by_email(hook.email)
121
+ user.opted_in = false
122
+ user.save
123
+ when "profile"
124
+ user = User.find_by_email(hook.email)
125
+ user.first_name = hook.first_name
126
+ user.last_name = hook.last_name
127
+ user.email_type = hook.email_type
128
+ user.save
129
+ when "upemail"
130
+ user = User.find_by_email(hook.old_email)
131
+ user.email = hook.new_email
132
+ user.save
133
+ end</code></pre>
134
+
135
+ h2. Contributors
136
+
137
+ Hominid is maintained by "Brian Getting":http://terra-firma-design.com. A very special thank-you to "Michael Strüder":http://github.com/mikezter for all of his hard work. Also, Hominid wouldn't be anywhere near as awesome as it is today without fantastic contributions and inspiration from:
138
+
139
+ * "Alan Harper":http://github.com/aussiegeek
140
+ * "Will":http://github.com/willinfront
141
+ * "Ben Woosley":http://github.com/Empact
142
+ * "banker":http://github.com/banker
143
+ * "Kristoffer Renholm":http://github.com/renholm
144
+ * "Wiktor Schmidt":http://github.com/netguru
145
+ * "ron":http://github.com/ron
146
+ * "Matthew Carlson":http://mandarinsoda.com/
147
+ * "Kelly Mahan":http://digimedia.com/
148
+ * "C.G. Brown":http://www.projectlocker.com/
149
+
150
+ h2. Note on Patches/Pull Requests
151
+
152
+ # Fork the project.
153
+ # Make your feature addition or bug fix.
154
+ # Add tests for it. This is important so I don't break it in a future version unintentionally.
155
+ # Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
156
+ # Send me a pull request. Bonus points for topic branches.
157
+
158
+ h2. Copyright
159
+
160
+ Copyright (c) 2009 Brian Getting. See LICENSE for details.
161
+
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "babney-hominid"
8
+ gem.summary = %Q{Hominid is a Ruby gem for interacting with the Mailchimp API.}
9
+ gem.description = %Q{Hominid is a Ruby gem that provides a wrapper for interacting with the Mailchimp email marketing service API.}
10
+ gem.email = "brian@terra-firma-design.com"
11
+ gem.homepage = "http://github.com/bgetting/hominid"
12
+ gem.authors = ["Brian Getting", "Michael Strüder"]
13
+ gem.add_development_dependency "shoulda"
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/*_test.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION')
47
+ version = File.read('VERSION')
48
+ else
49
+ version = ""
50
+ end
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "hominid #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
57
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.1
@@ -0,0 +1,60 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{hominid}
8
+ s.version = "2.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brian Getting", "Michael Str\303\274der"]
12
+ s.date = %q{2009-11-11}
13
+ s.description = %q{Hominid is a Ruby gem that provides a wrapper for interacting with the Mailchimp email marketing service API.}
14
+ s.email = %q{brian@terra-firma-design.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.textile",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "hominid.gemspec",
26
+ "hominid.yml.tpl",
27
+ "lib/hominid.rb",
28
+ "lib/hominid/base.rb",
29
+ "lib/hominid/campaign.rb",
30
+ "lib/hominid/helper.rb",
31
+ "lib/hominid/list.rb",
32
+ "lib/hominid/webhook.rb",
33
+ "tasks/rails/hominid.rake",
34
+ "test/hominid_test.rb",
35
+ "test/test_helper.rb"
36
+ ]
37
+ s.homepage = %q{http://github.com/bgetting/hominid}
38
+ s.rdoc_options = ["--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.5}
41
+ s.summary = %q{Hominid is a Ruby gem for interacting with the Mailchimp API.}
42
+ s.test_files = [
43
+ "test/hominid_test.rb",
44
+ "test/test_helper.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
53
+ else
54
+ s.add_dependency(%q<shoulda>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<shoulda>, [">= 0"])
58
+ end
59
+ end
60
+
@@ -0,0 +1,25 @@
1
+ # Get your API key at http://admin.mailchimp.com/account/api/
2
+
3
+ development:
4
+ username:
5
+ password:
6
+ api_key:
7
+ send_goodbye: false
8
+ send_notify: false
9
+ double_opt_in: false
10
+
11
+ test:
12
+ username:
13
+ password:
14
+ api_key:
15
+ send_goodbye: false
16
+ send_notify: false
17
+ double_opt_in: false
18
+
19
+ production:
20
+ username:
21
+ password:
22
+ api_key:
23
+ send_goodbye: false
24
+ send_notify: false
25
+ double_opt_in: false
@@ -0,0 +1,54 @@
1
+ require 'xmlrpc/client'
2
+ require 'ostruct'
3
+
4
+ module Hominid
5
+
6
+ class StandardError < ::StandardError
7
+ end
8
+
9
+ class APIError < StandardError
10
+ def initialize(error)
11
+ super("<#{error.faultCode}> #{error.message}")
12
+ end
13
+ end
14
+
15
+ class ListError < APIError
16
+ end
17
+
18
+ class ListEmailError < ListError
19
+ end
20
+
21
+ class ListMergeError < ListError
22
+ end
23
+
24
+ class AlreadySubscribed < ListEmailError
25
+ end
26
+
27
+ class AlreadyUnsubscribed < ListEmailError
28
+ end
29
+
30
+ class NotExists < ListEmailError
31
+ end
32
+
33
+ class NotSubscribed < ListEmailError
34
+ end
35
+
36
+ class CommunicationError < StandardError
37
+ def initialize(message)
38
+ super(message)
39
+ end
40
+ end
41
+ end
42
+
43
+ begin
44
+ # include the provided rake task
45
+ require 'rake'
46
+ unless Rake::Task.task_defined? "hominid:config"
47
+ load File.join(File.dirname(__FILE__), '..', 'tasks', 'rails', 'hominid.rake')
48
+ end
49
+ rescue LoadError
50
+ # silently skip rake task inclusion unless the rake gem is installed
51
+ end
52
+
53
+ require 'hominid/base'
54
+
@@ -0,0 +1,90 @@
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
+ :secure => false}
19
+ @config = defaults.merge(config).freeze
20
+ if config[:secure]
21
+ @chimpApi = XMLRPC::Client.new2("https://#{api_endpoint}.api.mailchimp.com/#{MAILCHIMP_API_VERSION}/")
22
+ else
23
+ @chimpApi = XMLRPC::Client.new2("http://#{api_endpoint}.api.mailchimp.com/#{MAILCHIMP_API_VERSION}/")
24
+ end
25
+ end
26
+
27
+ # Security related methods
28
+ # --------------------------------
29
+
30
+ def add_api_key
31
+ @chimpApi.call("apikeyAdd", *@config.values_at(:username, :password, :api_key))
32
+ end
33
+
34
+ def expire_api_key
35
+ @chimpApi.call("apikeyExpire", *@config.values_at(:username, :password, :api_key))
36
+ end
37
+
38
+ def api_keys(include_expired = false)
39
+ username, password = *@config.values_at(:username, :password)
40
+ @chimpApi.call("apikeys", username, password, include_expired)
41
+ end
42
+
43
+ # Used internally by Hominid
44
+ # --------------------------------
45
+
46
+ # handle common cases for which the Mailchimp API would raise Exceptions
47
+ def clean_merge_tags(merge_tags)
48
+ return {} unless merge_tags.is_a? Hash
49
+ merge_tags.each do |key, value|
50
+ if merge_tags[key].is_a? String
51
+ merge_tags[key] = value.gsub("\v", '')
52
+ elsif merge_tags[key].nil?
53
+ merge_tags[key] = ''
54
+ end
55
+ end
56
+ end
57
+
58
+ def apply_defaults_to(options)
59
+ @config.merge(options)
60
+ end
61
+
62
+ def call(method, *args)
63
+ @chimpApi.call(method, @config[:api_key], *args)
64
+ rescue XMLRPC::FaultException => error
65
+ case error.faultCode
66
+ when 230
67
+ raise AlreadySubscribed.new(error)
68
+ when 231
69
+ raise AlreadyUnsubscribed.new(error)
70
+ when 232
71
+ raise NotExists.new(error)
72
+ when 233, 215
73
+ raise NotSubscribed.new(error)
74
+ else
75
+ raise APIError.new(error)
76
+ end
77
+ rescue RuntimeError => error
78
+ if error.message =~ /Wrong type NilClass\. Not allowed!/
79
+ hashes = args.select{|a| a.is_a? Hash}
80
+ errors = hashes.select{|k, v| v.nil? }.collect{ |k, v| "#{k} is Nil." }.join(' ')
81
+ raise CommunicationError.new(errors)
82
+ else
83
+ raise error
84
+ end
85
+ rescue Exception => error
86
+ raise CommunicationError.new(error.message)
87
+ end
88
+ end
89
+ end
90
+
@@ -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.to_i }
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 = 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(options = {})
9
+ # Get details for this account.
10
+ new(options).call("getAccountDetails")
11
+ end
12
+
13
+ def self.convert_css_to_inline(html, strip_css = false, options = {})
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, options = {})
19
+ # Create a new folder to file campaigns in
20
+ new(options).call("createFolder", name)
21
+ end
22
+
23
+ def self.generate_text(type, content, options = {})
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(options).call("generateText", type, content)
32
+ end
33
+
34
+ def self.html_to_text(content, options = {})
35
+ # Convert HTML content to text
36
+ new(options).call("generateText", 'html', content)
37
+ end
38
+
39
+ def self.ping(options = {})
40
+ # Ping the Mailchimp API
41
+ new(options).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", replace_interests = true)
144
+ # Update a member of this list
145
+ call("listUpdateMember", @list_id, current_email, merge_tags, email_type, replace_interests)
146
+ end
147
+
148
+ end
149
+ end
150
+
@@ -0,0 +1,131 @@
1
+ module Hominid
2
+
3
+ class Webhook < Base
4
+ # Expects a hash of POST data generated from Mailchimp:
5
+ #
6
+ # "type": "unsubscribe",
7
+ # "fired_at": "2009-03-26 21:54:00",
8
+ # "data[email]": "sample@emailaddress.com"
9
+ #
10
+ # Simple Usage:
11
+ #
12
+ # h = Hominid::Webhook.new(params)
13
+ #
14
+ # Sample params from Mailchimp webhook:
15
+ # params => { "type" => "subscribe",
16
+ # "fired_at" => "2009-03-26 21:35:57",
17
+ # "data" => { "id" => "8a25ff1d98",
18
+ # "list_id" => "8a25ff1d98",
19
+ # "email" => "api@mailchimp.com",
20
+ # "email_type" => "html",
21
+ # "merges" => {"EMAIL" => "api@mailchimp.com",
22
+ # "FNAME" => "Brian",
23
+ # "LNAME" => "Getting",
24
+ # "INTERESTS" => "Group1,Group2"},
25
+ # "ip_opt" => "10.20.10.30",
26
+ # "ip_signup" => "10.20.10.30" }}
27
+ #
28
+ # Returns an object with the following methods (NOTE: Not all methods are available
29
+ # for all event types. Refer to http://www.mailchimp.com/api/webhooks/ for information
30
+ # on what data will be available for each event):
31
+ #
32
+ # h.event <= (String) The event that fired the request. Possible events are:
33
+ # "subscribe", "unsubscribe", "profile", "upemail", "cleaned"
34
+ # h.fired_at <= (Datetime) When the webhook request was fired.
35
+ # h.id <= (String) The ID of the webhook request.
36
+ # h.list_id <= (String) The ID of the list that generated the request.
37
+ # h.email <= (String) The email address of the subscriber that generated the request.
38
+ # h.email_type <= (String) The email type of the subscriber that generated the request.
39
+ # h.first_name <= (String) The first name of the subscriber (if available).
40
+ # h.last_name <= (String) The first name of the subscriber (if available).
41
+ # h.interests <= (Array) An array of the interest groups.
42
+ # h.ip_opt <= (String) The opt in IP address.
43
+ # h.ip_signup <= (String) The signup IP address.
44
+ #
45
+
46
+ attr_reader :request
47
+
48
+ def initialize(*args)
49
+ post_data = args.last
50
+ raise HominidError.new('Please provide the POST data from a Mailchimp webhook request.') unless post_data.is_a?(Hash)
51
+ post_data.merge!({"event" => "#{post_data.delete('type')}"})
52
+ @request = hash_to_object(post_data)
53
+ end
54
+
55
+
56
+ def email
57
+ self.request.data.email if self.request.data.email
58
+ end
59
+
60
+ def email_type
61
+ self.request.data.email_type if self.request.data.email_type
62
+ end
63
+
64
+ def event
65
+ self.request.event if self.request.event
66
+ end
67
+
68
+ def fired_at
69
+ self.request.fired_at.to_datetime if self.request.fired_at
70
+ end
71
+
72
+ def first_name
73
+ self.request.data.merges.fname if self.request.data.merges.fname
74
+ end
75
+
76
+ def last_name
77
+ self.request.data.merges.lname if self.request.data.merges.lname
78
+ end
79
+
80
+ def id
81
+ self.request.data.id if self.request.data.id
82
+ end
83
+
84
+ def interests
85
+ self.request.data.merges.interests.split(',') if self.request.data.merges.interests
86
+ end
87
+
88
+ def ip_opt
89
+ self.request.data.ip_opt if self.request.data.ip_opt
90
+ end
91
+
92
+ def ip_signup
93
+ self.request.data.ip_signup if self.request.data.ip_signup
94
+ end
95
+
96
+ def list_id
97
+ self.request.data.list_id if self.request.data.list_id
98
+ end
99
+
100
+ def new_email
101
+ self.request.data.new_email if self.request.data.new_email
102
+ end
103
+
104
+ def old_email
105
+ self.request.data.old_email if self.request.data.old_email
106
+ end
107
+
108
+ def reason
109
+ self.request.data.reason if self.request.data.reason
110
+ end
111
+
112
+ private
113
+
114
+ def hash_to_object(object)
115
+ return case object
116
+ when Hash
117
+ object = object.clone
118
+ object.each do |key, value|
119
+ object[key.downcase] = hash_to_object(value)
120
+ end
121
+ OpenStruct.new(object)
122
+ when Array
123
+ object = object.clone
124
+ object.map! { |i| hash_to_object(i) }
125
+ else
126
+ object
127
+ end
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ namespace :hominid do
3
+ # Task to create the config file
4
+ desc "Generate a Hominid config file"
5
+ task :config => :environment do |t|
6
+ require 'fileutils'
7
+ if defined?(Rails.root)
8
+ config_file = File.join(Rails.root, 'config', 'hominid.yml')
9
+ template_file = File.join(File.dirname(__FILE__), '..', '..', 'hominid.yml.tpl')
10
+ unless File.exists? config_file
11
+ FileUtils.cp(
12
+ File.join(File.dirname(__FILE__), '..', '..', 'hominid.yml.tpl'),
13
+ File.join(Rails.root, 'config', 'hominid.yml')
14
+ )
15
+ puts 'Please edit config/hominid.yml to your needs.'
16
+ else
17
+ puts 'We left your existing config/hominid.yml untouched.'
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class HominidTest < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'hominid'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: babney-hominid
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brian Getting
8
+ - "Michael Str\xC3\xBCder"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-11-18 00:00:00 -08:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: shoulda
18
+ type: :development
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ version:
26
+ description: Hominid is a Ruby gem that provides a wrapper for interacting with the Mailchimp email marketing service API.
27
+ email: brian@terra-firma-design.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - LICENSE
34
+ - README.textile
35
+ files:
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.textile
39
+ - Rakefile
40
+ - VERSION
41
+ - hominid.gemspec
42
+ - hominid.yml.tpl
43
+ - lib/hominid.rb
44
+ - lib/hominid/base.rb
45
+ - lib/hominid/campaign.rb
46
+ - lib/hominid/helper.rb
47
+ - lib/hominid/list.rb
48
+ - lib/hominid/webhook.rb
49
+ - tasks/rails/hominid.rake
50
+ - test/hominid_test.rb
51
+ - test/test_helper.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/bgetting/hominid
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.5
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Hominid is a Ruby gem for interacting with the Mailchimp API.
80
+ test_files:
81
+ - test/hominid_test.rb
82
+ - test/test_helper.rb