madmimi 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -1
- data/README.rdoc +51 -1
- data/Rakefile +4 -3
- data/VERSION +1 -1
- data/lib/madmimi.rb +136 -131
- data/madmimi.gemspec +10 -7
- metadata +23 -8
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -6,7 +6,7 @@ The power of Mad Mimi in your Ruby application. Deliver emails, track statistics
|
|
6
6
|
|
7
7
|
gem install madmimi - or if you prefer to live on the edge, just clone this repository and build it from scratch.
|
8
8
|
|
9
|
-
==
|
9
|
+
== Basic Usage
|
10
10
|
|
11
11
|
Dependencies:
|
12
12
|
active_support (I intend to remove this in the not too distant future, and build my own implementation.)
|
@@ -33,6 +33,53 @@ mimi.promotions -> returns a hash of your promotions
|
|
33
33
|
|
34
34
|
mimi.mailing_stats('promotion_id', 'mailing_id') -> get stats on a specific mailing
|
35
35
|
|
36
|
+
== Sending E-Mail (using the Mailer API)
|
37
|
+
|
38
|
+
=== Replacing keys in your email body text:
|
39
|
+
|
40
|
+
options = { 'promotion_name' => 'Test Promotion', 'recipients' => 'Nicholas Young <nicholas@madmimi.com>', 'from' => 'MadMimi Ruby <rubygem@madmimi.com>', 'subject' => 'Test Subject' }
|
41
|
+
|
42
|
+
yaml_body = { 'greeting' => 'Hello', 'name' => 'Nicholas' }
|
43
|
+
|
44
|
+
mimi.send_mail(options, yaml_body)
|
45
|
+
|
46
|
+
=== Sending Raw HTML (presumably generated by your app)
|
47
|
+
|
48
|
+
options = { 'promotion_name' => 'Test Promotion', 'recipients' => 'Nicholas Young <nicholas@madmimi.com>', 'from' => 'MadMimi Ruby <rubygem@madmimi.com>', 'subject' => 'Test Subject' }
|
49
|
+
|
50
|
+
raw_html = "<html><head><title>My great promotion!</title></head><body>Body stuff[[tracking_beacon]]</body></html>"
|
51
|
+
|
52
|
+
mimi.send_html(options, raw_html)
|
53
|
+
|
54
|
+
=== Sending Plain Text
|
55
|
+
|
56
|
+
options = { 'promotion_name' => 'Test Promotion', 'recipients' => 'Nicholas Young <nicholas@madmimi.com>', 'from' => 'MadMimi Ruby <rubygem@madmimi.com>', 'subject' => 'Test Subject' }
|
57
|
+
|
58
|
+
plain_text = "Plain text email contents [[unsubscribe]]"
|
59
|
+
|
60
|
+
mimi.send_plaintext(options, plain_text)
|
61
|
+
|
62
|
+
=== Return values
|
63
|
+
|
64
|
+
In most cases, a return value of a single space indicates success.
|
65
|
+
|
66
|
+
On success, #send_mail, #send_html, and #send_plaintext return String with a numeric mailing_id. This mailing_id can be used to look up stats with #mailing_stats.
|
67
|
+
|
68
|
+
Errors or issues preventing operation completing return a human-readable String.
|
69
|
+
|
70
|
+
Therefore, if the return value is not a space, or is not a numeric String value, then
|
71
|
+
there is probably an error or uncompleted operation.
|
72
|
+
|
73
|
+
=== Specific options keys
|
74
|
+
|
75
|
+
'raw_html': Must include at least one of the [[tracking_beacon]] or [[peek_image]] tags.
|
76
|
+
|
77
|
+
'promotion_name': If a promotion doesn't exist under the given name, it will be created.
|
78
|
+
If it exists and you specify raw_html, the promotion body will be replaced.
|
79
|
+
|
80
|
+
'list_name': For all of the #send methods, if 'list_name' is provided, the recipients
|
81
|
+
will be those for an already-existing "audience."
|
82
|
+
|
36
83
|
== Note on Patches/Pull Requests
|
37
84
|
|
38
85
|
* Fork the project.
|
@@ -43,6 +90,9 @@ mimi.mailing_stats('promotion_id', 'mailing_id') -> get stats on a specific mail
|
|
43
90
|
(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)
|
44
91
|
* Send me a pull request. Bonus points for topic branches.
|
45
92
|
|
93
|
+
== Contributors
|
94
|
+
tuker
|
95
|
+
|
46
96
|
== Copyright
|
47
97
|
|
48
98
|
Copyright (c) 2010 Nicholas Young. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -8,9 +8,10 @@ begin
|
|
8
8
|
gem.summary = %Q{Mad Mimi API wrapper for Ruby}
|
9
9
|
gem.description = %Q{Send emails, track statistics, and manage your subscriber base with ease.}
|
10
10
|
gem.email = "nicholas@madmimi.com"
|
11
|
-
gem.homepage = "http://github.com/
|
12
|
-
gem.authors = ["Nicholas Young"]
|
13
|
-
gem.add_development_dependency "
|
11
|
+
gem.homepage = "http://github.com/madmimi/madmimi-gem"
|
12
|
+
gem.authors = ["Nicholas Young", "Marc Heiligers"]
|
13
|
+
gem.add_development_dependency "crack", ">= 0.1.7"
|
14
|
+
gem.add_development_dependency "shoulda", "2.10.3"
|
14
15
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
16
|
end
|
16
17
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.8
|
data/lib/madmimi.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Mad Mimi
|
1
|
+
# Mad Mimi for Ruby
|
2
2
|
|
3
3
|
# License
|
4
4
|
|
@@ -29,186 +29,191 @@
|
|
29
29
|
require 'uri'
|
30
30
|
require 'net/http'
|
31
31
|
require 'net/https'
|
32
|
-
require '
|
32
|
+
require 'crack'
|
33
|
+
require 'csv'
|
33
34
|
|
34
35
|
class MadMimi
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
36
|
+
|
37
|
+
class MadMimiError < StandardError; end
|
38
|
+
|
39
|
+
BASE_URL = 'api.madmimi.com'
|
40
|
+
NEW_LISTS_PATH = '/audience_lists'
|
41
|
+
AUDIENCE_MEMBERS_PATH = '/audience_members'
|
42
|
+
AUDIENCE_LISTS_PATH = '/audience_lists/lists.xml'
|
43
|
+
MEMBERSHIPS_PATH = '/audience_members/%email%/lists.xml'
|
44
|
+
SUPPRESSED_SINCE_PATH = '/audience_members/suppressed_since/%timestamp%.txt'
|
45
|
+
PROMOTIONS_PATH = '/promotions.xml'
|
46
|
+
MAILING_STATS_PATH = '/promotions/%promotion_id%/mailings/%mailing_id%.xml'
|
47
|
+
SEARCH_PATH = '/audience_members/search.xml'
|
48
|
+
MAILER_PATH = '/mailer'
|
49
|
+
MAILER_TO_LIST_PATH = '/mailer/to_list'
|
47
50
|
|
48
51
|
def initialize(username, api_key)
|
49
|
-
|
50
|
-
@@api_settings[:api_key] = api_key
|
52
|
+
@api_settings = { :username => username, :api_key => api_key }
|
51
53
|
end
|
52
|
-
|
54
|
+
|
53
55
|
def username
|
54
|
-
|
56
|
+
@api_settings[:username]
|
55
57
|
end
|
56
|
-
|
58
|
+
|
57
59
|
def api_key
|
58
|
-
|
60
|
+
@api_settings[:api_key]
|
59
61
|
end
|
60
|
-
|
62
|
+
|
61
63
|
def default_opt
|
62
|
-
{
|
64
|
+
{ :username => username, :api_key => api_key }
|
63
65
|
end
|
64
|
-
|
65
|
-
# Refactor this method asap
|
66
|
-
def do_request(path, req_type = :get, options = {}, transactional = false)
|
67
|
-
resp = href = "";
|
68
|
-
case req_type
|
69
|
-
when :get then
|
70
|
-
begin
|
71
|
-
http = Net::HTTP.new(BASE_URL, 80)
|
72
|
-
http.start do |http|
|
73
|
-
req = Net::HTTP::Get.new(path)
|
74
|
-
req.set_form_data(options)
|
75
|
-
response = http.request(req)
|
76
|
-
resp = response.body
|
77
|
-
end
|
78
|
-
resp
|
79
|
-
rescue SocketError
|
80
|
-
raise "Host unreachable."
|
81
|
-
end
|
82
|
-
when :post then
|
83
|
-
begin
|
84
|
-
if transactional == true
|
85
|
-
http = Net::HTTP.new(BASE_URL, 443)
|
86
|
-
http.use_ssl = true
|
87
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
88
|
-
else
|
89
|
-
http = Net::HTTP.new(BASE_URL, 80)
|
90
|
-
end
|
91
|
-
http.start do |http|
|
92
|
-
req = Net::HTTP::Post.new(path)
|
93
|
-
req.set_form_data(options)
|
94
|
-
response = http.request(req)
|
95
|
-
resp = response.body
|
96
|
-
end
|
97
|
-
rescue SocketError
|
98
|
-
raise "Host unreachable."
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
66
|
+
|
103
67
|
def lists
|
104
|
-
request = do_request(AUDIENCE_LISTS_PATH, :get
|
105
|
-
|
68
|
+
request = do_request(AUDIENCE_LISTS_PATH, :get)
|
69
|
+
Crack::XML.parse(request)
|
106
70
|
end
|
107
|
-
|
71
|
+
|
108
72
|
def memberships(email)
|
109
|
-
request = do_request(
|
110
|
-
|
73
|
+
request = do_request(MEMBERSHIPS_PATH.gsub('%email%', email), :get)
|
74
|
+
Crack::XML.parse(request)
|
111
75
|
end
|
112
|
-
|
76
|
+
|
113
77
|
def new_list(list_name)
|
114
|
-
|
115
|
-
do_request(NEW_LISTS_PATH, :post, options.merge(default_opt))
|
78
|
+
do_request(NEW_LISTS_PATH, :post, :name => list_name)
|
116
79
|
end
|
117
|
-
|
80
|
+
|
118
81
|
def delete_list(list_name)
|
119
|
-
|
120
|
-
do_request(NEW_LISTS_PATH + "/" + URI.escape(list_name), :post, options.merge(default_opt))
|
82
|
+
do_request("#{NEW_LISTS_PATH}/#{URI.escape(list_name)}", :post, :'_method' => 'delete')
|
121
83
|
end
|
122
|
-
|
84
|
+
|
123
85
|
def csv_import(csv_string)
|
124
|
-
|
125
|
-
do_request(AUDIENCE_MEMBERS_PATH, :post, options.merge(default_opt))
|
126
|
-
end
|
127
|
-
|
128
|
-
def build_csv(hash)
|
129
|
-
csv = ""
|
130
|
-
hash.keys.each do |key|
|
131
|
-
csv << "#{key},"
|
132
|
-
end
|
133
|
-
# strip out one char at the end
|
134
|
-
csv << "\n"
|
135
|
-
csv = csv[0..-1]
|
136
|
-
hash.values.each do |value|
|
137
|
-
csv << "#{value},"
|
138
|
-
end
|
139
|
-
csv = csv[0..-1]
|
140
|
-
csv << "\n"
|
86
|
+
do_request(AUDIENCE_MEMBERS_PATH, :post, :csv_file => csv_string)
|
141
87
|
end
|
142
|
-
|
88
|
+
|
143
89
|
def add_user(options)
|
144
90
|
csv_data = build_csv(options)
|
145
|
-
|
146
|
-
do_request(AUDIENCE_MEMBERS_PATH, :post, opt.merge(default_opt))
|
91
|
+
do_request(AUDIENCE_MEMBERS_PATH, :post, :csv_file => csv_data)
|
147
92
|
end
|
148
|
-
|
93
|
+
|
149
94
|
def add_to_list(email, list_name)
|
150
|
-
|
151
|
-
do_request(NEW_LISTS_PATH + "/" + URI.escape(list_name) + "/add", :post, options.merge(default_opt))
|
95
|
+
do_request("#{NEW_LISTS_PATH}/#{URI.escape(list_name)}/add", :post, :email => email)
|
152
96
|
end
|
153
|
-
|
97
|
+
|
154
98
|
def remove_from_list(email, list_name)
|
155
|
-
|
156
|
-
do_request(NEW_LISTS_PATH + "/" + URI.escape(list_name) + "/remove", :post, options.merge(default_opt))
|
99
|
+
do_request("#{NEW_LISTS_PATH}/#{URI.escape(list_name)}/remove", :post, :email => email)
|
157
100
|
end
|
158
|
-
|
101
|
+
|
159
102
|
def suppressed_since(timestamp)
|
160
|
-
do_request(
|
103
|
+
do_request(SUPPRESSED_SINCE_PATH.gsub('%timestamp%', timestamp), :get)
|
161
104
|
end
|
162
|
-
|
105
|
+
|
163
106
|
def promotions
|
164
|
-
request = do_request(PROMOTIONS_PATH, :get
|
165
|
-
|
107
|
+
request = do_request(PROMOTIONS_PATH, :get)
|
108
|
+
Crack::XML.parse(request)
|
166
109
|
end
|
167
|
-
|
110
|
+
|
168
111
|
def mailing_stats(promotion_id, mailing_id)
|
169
|
-
path =
|
170
|
-
request = do_request(path, :get
|
171
|
-
|
112
|
+
path = MAILING_STATS_PATH.gsub('%promotion_id%', promotion_id).gsub('%mailing_id%', mailing_id)
|
113
|
+
request = do_request(path, :get)
|
114
|
+
Crack::XML.parse(request)
|
172
115
|
end
|
173
|
-
|
116
|
+
|
174
117
|
def audience_search(query_string, raw = false)
|
175
|
-
|
176
|
-
|
177
|
-
Hash.from_xml(request)
|
118
|
+
request = do_request(SEARCH_PATH, :get, :raw => raw, :query => query_string)
|
119
|
+
Crack::XML.parse(request)
|
178
120
|
end
|
179
|
-
|
121
|
+
|
180
122
|
def send_mail(opt, yaml_body)
|
181
|
-
|
182
|
-
|
183
|
-
|
123
|
+
options = opt.dup
|
124
|
+
options[:body] = yaml_body.to_yaml
|
125
|
+
if !options[:list_name].nil?
|
126
|
+
do_request(MAILER_TO_LIST_PATH, :post, options, true)
|
184
127
|
else
|
185
|
-
do_request(
|
128
|
+
do_request(MAILER_PATH, :post, options, true)
|
186
129
|
end
|
187
130
|
end
|
188
|
-
|
131
|
+
|
189
132
|
def send_html(opt, html)
|
133
|
+
options = opt.dup
|
190
134
|
if html.include?('[[tracking_beacon]]') || html.include?('[[peek_image]]')
|
191
|
-
|
192
|
-
if
|
193
|
-
|
135
|
+
options[:raw_html] = html
|
136
|
+
if !options[:list_name].nil?
|
137
|
+
unless html.include?('[[unsubscribe]]') || html.include?('[[opt_out]]')
|
138
|
+
raise MadMimiError, "When specifying list_name, include the [[unsubscribe]] or [[opt_out]] macro in your HTML before sending."
|
139
|
+
end
|
140
|
+
do_request(MAILER_TO_LIST_PATH, :post, options, true)
|
194
141
|
else
|
195
|
-
do_request(
|
142
|
+
do_request(MAILER_PATH, :post, options, true)
|
196
143
|
end
|
197
144
|
else
|
198
|
-
raise
|
145
|
+
raise MadMimiError, "You'll need to include either the [[tracking_beacon]] or [[peek_image]] macro in your HTML before sending."
|
199
146
|
end
|
200
147
|
end
|
201
|
-
|
148
|
+
|
202
149
|
def send_plaintext(opt, plaintext)
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
150
|
+
options = opt.dup
|
151
|
+
options[:raw_plain_text] = plaintext
|
152
|
+
if !options[:list_name].nil?
|
153
|
+
if plaintext.include?('[[unsubscribe]]') || plaintext.include?('[[opt_out]]')
|
154
|
+
do_request(MAILER_TO_LIST_PATH, :post, options, true)
|
207
155
|
else
|
208
|
-
|
156
|
+
raise MadMimiError, "You'll need to include either the [[unsubscribe]] or [[opt_out]] macro in your text before sending."
|
209
157
|
end
|
210
158
|
else
|
211
|
-
|
159
|
+
do_request(MAILER_PATH, :post, options, true)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# Refactor this method asap
|
166
|
+
def do_request(path, req_type = :get, options = {}, transactional = false)
|
167
|
+
options = options.merge(default_opt)
|
168
|
+
form_data = options.inject({}) { |m, (k, v)| m[k.to_s] = v; m }
|
169
|
+
resp = href = ""
|
170
|
+
case req_type
|
171
|
+
when :get then
|
172
|
+
begin
|
173
|
+
http = Net::HTTP.new(BASE_URL, 80)
|
174
|
+
http.start do |http|
|
175
|
+
req = Net::HTTP::Get.new(path)
|
176
|
+
req.set_form_data(form_data)
|
177
|
+
response = http.request(req)
|
178
|
+
resp = response.body.strip
|
179
|
+
end
|
180
|
+
resp
|
181
|
+
rescue SocketError
|
182
|
+
raise "Host unreachable."
|
183
|
+
end
|
184
|
+
when :post then
|
185
|
+
begin
|
186
|
+
if transactional == true
|
187
|
+
http = Net::HTTP.new(BASE_URL, 443)
|
188
|
+
http.use_ssl = true
|
189
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
190
|
+
else
|
191
|
+
http = Net::HTTP.new(BASE_URL, 80)
|
192
|
+
end
|
193
|
+
http.start do |http|
|
194
|
+
req = Net::HTTP::Post.new(path)
|
195
|
+
req.set_form_data(form_data)
|
196
|
+
response = http.request(req)
|
197
|
+
resp = response.body.strip
|
198
|
+
end
|
199
|
+
rescue SocketError
|
200
|
+
raise "Host unreachable."
|
201
|
+
end
|
212
202
|
end
|
213
203
|
end
|
214
|
-
|
204
|
+
|
205
|
+
def build_csv(hash)
|
206
|
+
if CSV.respond_to?(:generate_row) # before Ruby 1.9
|
207
|
+
buffer = ''
|
208
|
+
CSV.generate_row(hash.keys, hash.keys.size, buffer)
|
209
|
+
CSV.generate_row(hash.values, hash.values.size, buffer)
|
210
|
+
buffer
|
211
|
+
else # Ruby 1.9 and after
|
212
|
+
CSV.generate do |csv|
|
213
|
+
csv << hash.keys
|
214
|
+
csv << hash.values
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
data/madmimi.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{madmimi}
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.0.8"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Nicholas Young"]
|
12
|
-
s.date = %q{2010-05-
|
11
|
+
s.authors = ["Nicholas Young", "Marc Heiligers"]
|
12
|
+
s.date = %q{2010-05-26}
|
13
13
|
s.description = %q{Send emails, track statistics, and manage your subscriber base with ease.}
|
14
14
|
s.email = %q{nicholas@madmimi.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
"test/helper.rb",
|
28
28
|
"test/test_madmimi.rb"
|
29
29
|
]
|
30
|
-
s.homepage = %q{http://github.com/
|
30
|
+
s.homepage = %q{http://github.com/madmimi/madmimi-gem}
|
31
31
|
s.rdoc_options = ["--charset=UTF-8"]
|
32
32
|
s.require_paths = ["lib"]
|
33
33
|
s.rubygems_version = %q{1.3.6}
|
@@ -42,12 +42,15 @@ Gem::Specification.new do |s|
|
|
42
42
|
s.specification_version = 3
|
43
43
|
|
44
44
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
45
|
-
s.add_development_dependency(%q<
|
45
|
+
s.add_development_dependency(%q<crack>, [">= 0.1.7"])
|
46
|
+
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
46
47
|
else
|
47
|
-
s.add_dependency(%q<
|
48
|
+
s.add_dependency(%q<crack>, [">= 0.1.7"])
|
49
|
+
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
48
50
|
end
|
49
51
|
else
|
50
|
-
s.add_dependency(%q<
|
52
|
+
s.add_dependency(%q<crack>, [">= 0.1.7"])
|
53
|
+
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
metadata
CHANGED
@@ -5,32 +5,47 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
8
|
+
- 8
|
9
|
+
version: 1.0.8
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Nicholas Young
|
13
|
+
- Marc Heiligers
|
13
14
|
autorequire:
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-05-
|
18
|
+
date: 2010-05-26 00:00:00 -05:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
22
|
+
name: crack
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
25
|
requirements:
|
25
26
|
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 1
|
31
|
+
- 7
|
32
|
+
version: 0.1.7
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: shoulda
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "="
|
26
41
|
- !ruby/object:Gem::Version
|
27
42
|
segments:
|
28
43
|
- 2
|
44
|
+
- 10
|
29
45
|
- 3
|
30
|
-
|
31
|
-
version: 2.3.5
|
46
|
+
version: 2.10.3
|
32
47
|
type: :development
|
33
|
-
version_requirements: *
|
48
|
+
version_requirements: *id002
|
34
49
|
description: Send emails, track statistics, and manage your subscriber base with ease.
|
35
50
|
email: nicholas@madmimi.com
|
36
51
|
executables: []
|
@@ -51,7 +66,7 @@ files:
|
|
51
66
|
- test/helper.rb
|
52
67
|
- test/test_madmimi.rb
|
53
68
|
has_rdoc: true
|
54
|
-
homepage: http://github.com/
|
69
|
+
homepage: http://github.com/madmimi/madmimi-gem
|
55
70
|
licenses: []
|
56
71
|
|
57
72
|
post_install_message:
|