ar_mailer_aws 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArMailerAWS::Clients::SMTP do
4
+
5
+ describe '#send_emails' do
6
+ before do
7
+ allow(ArMailerAWS).to receive(:client_config).and_return({smtp: {
8
+ address: 'smtp.example.com',
9
+ port: 587,
10
+ domain: 'example.com',
11
+ authentication: 'plain',
12
+ enable_starttls_auto: true,
13
+ user_name: 'test@example.com',
14
+ password: '123456'
15
+ }})
16
+ @client = ArMailerAWS::Clients::SMTP.new
17
+ end
18
+
19
+ it 'start smtp session' do
20
+ session = Net::SMTP.new('smtp.example.com', 587)
21
+ expect(Net::SMTP).to receive(:new).with('smtp.example.com', 587).and_return(session)
22
+ expect(session).to receive(:start).with('example.com', 'test@example.com', '123456', 'plain')
23
+ @client.send_emails([])
24
+ end
25
+
26
+ it 'send emails' do
27
+ session = double('session').as_null_object
28
+ email = double('email', id: 1, mail: 'mail', from: 'from', to: 'to')
29
+
30
+ allow(Net::SMTP).to receive(:new).and_return(session)
31
+ allow(session).to receive(:start).and_yield(session)
32
+ expect(session).to receive(:send_message).with('mail', 'from', 'to')
33
+ expect(email).to receive(:destroy)
34
+ @client.send_emails([email])
35
+ end
36
+ end
37
+
38
+ end
@@ -4,23 +4,23 @@ describe ArMailerAWS::Mailer do
4
4
 
5
5
  it 'initializer email_class option' do
6
6
  mailer = ArMailerAWS::Mailer.new(email_class: CustomEmailClass)
7
- mailer.email_class.name.should == 'CustomEmailClass'
7
+ expect(mailer.email_class.name).to eq 'CustomEmailClass'
8
8
  end
9
9
 
10
10
  context 'delivering' do
11
11
  before do
12
12
  @mail = double('Mail')
13
- @mail.stub(:return_path).and_return('from@example.com')
14
- @mail.stub(:destinations).and_return(['to@example.com'])
15
- @mail.stub(:encoded).and_return('email content')
13
+ allow(@mail).to receive(:return_path).and_return('from@example.com')
14
+ allow(@mail).to receive(:destinations).and_return(['to@example.com'])
15
+ allow(@mail).to receive(:encoded).and_return('email content')
16
16
  @mailer = ArMailerAWS::Mailer.new
17
17
  end
18
18
 
19
19
  it '#check_params' do
20
20
  params = @mailer.send(:check_params, @mail)
21
- params[0].should == 'from@example.com'
22
- params[1].should == ['to@example.com']
23
- params[2].should == 'email content'
21
+ expect(params[0]).to eq 'from@example.com'
22
+ expect(params[1]).to eq ['to@example.com']
23
+ expect(params[2]).to eq 'email content'
24
24
  end
25
25
 
26
26
  it 'store emails into db on deliver!' do
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArMailerAWS::OptionsParser do
4
+
5
+ it 'return defaults if no options specified' do
6
+ options = ArMailerAWS::OptionsParser.parse_options([])
7
+ expect(options.batch_size).to eq 100
8
+ expect(options.delay).to eq 180
9
+ expect(options.quota).to eq 10_000
10
+ expect(options.rate).to eq 5
11
+ expect(options.max_age).to eq 3600 * 24 * 7
12
+ expect(options.max_attempts).to eq 5
13
+ end
14
+
15
+ it 'batch_size' do
16
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-b 10)).batch_size).to eq 10
17
+ end
18
+
19
+ it 'delay' do
20
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-d 90)).delay).to eq 90
21
+ end
22
+
23
+ it 'quota' do
24
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-q 100)).quota).to eq 100
25
+ end
26
+
27
+ it 'rate' do
28
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-r 7)).rate).to eq 7
29
+ end
30
+
31
+ it 'max_age' do
32
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-m 300)).max_age).to eq 300
33
+ end
34
+
35
+ it 'max_attempts' do
36
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-a 10)).max_attempts).to eq 10
37
+ end
38
+
39
+ it 'verbose' do
40
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-v)).verbose).to be_truthy
41
+ end
42
+
43
+ it 'pid_dir' do
44
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(-p tmp/pids)).pid_dir).to eq 'tmp/pids'
45
+ end
46
+
47
+ it 'app_name' do
48
+ expect(ArMailerAWS::OptionsParser.parse_options(%w(--app-name my_daemon)).app_name).to eq 'my_daemon'
49
+ end
50
+
51
+ end
@@ -12,6 +12,7 @@ ActiveRecord::Schema.define(:version => 1) do
12
12
  t.string :from
13
13
  t.string :to
14
14
  t.text :mail, limit: 16777215
15
+ t.integer :send_attempts_count, default: 0
15
16
  t.datetime :last_send_attempt_at
16
17
  t.datetime :created_at
17
18
  end
metadata CHANGED
@@ -1,139 +1,167 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar_mailer_aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Leschenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-22 00:00:00.000000000 Z
11
+ date: 2014-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: daemons
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.1.9
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.1.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: aws-sdk
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - ~>
45
+ - - "~>"
32
46
  - !ruby/object:Gem::Version
33
47
  version: '1.0'
34
- type: :runtime
48
+ type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - ~>
52
+ - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '1.0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: activesupport
56
+ name: mandrill-api
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - '>='
59
+ - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '3.0'
48
- type: :runtime
61
+ version: 1.0.49
62
+ type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - '>='
66
+ - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '3.0'
68
+ version: 1.0.49
69
+ - !ruby/object:Gem::Dependency
70
+ name: mail
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.5.4
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.5.4
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: bundler
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
- - - ~>
87
+ - - "~>"
60
88
  - !ruby/object:Gem::Version
61
89
  version: '1.3'
62
90
  type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
- - - ~>
94
+ - - "~>"
67
95
  - !ruby/object:Gem::Version
68
96
  version: '1.3'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rake
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
- - - '>='
101
+ - - ">="
74
102
  - !ruby/object:Gem::Version
75
103
  version: '0'
76
104
  type: :development
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
- - - '>='
108
+ - - ">="
81
109
  - !ruby/object:Gem::Version
82
110
  version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: rspec
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
- - - '>='
115
+ - - ">="
88
116
  - !ruby/object:Gem::Version
89
117
  version: '0'
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
- - - '>='
122
+ - - ">="
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: forgery
99
127
  requirement: !ruby/object:Gem::Requirement
100
128
  requirements:
101
- - - '>='
129
+ - - ">="
102
130
  - !ruby/object:Gem::Version
103
131
  version: '0'
104
132
  type: :development
105
133
  prerelease: false
106
134
  version_requirements: !ruby/object:Gem::Requirement
107
135
  requirements:
108
- - - '>='
136
+ - - ">="
109
137
  - !ruby/object:Gem::Version
110
138
  version: '0'
111
139
  - !ruby/object:Gem::Dependency
112
140
  name: sqlite3
113
141
  requirement: !ruby/object:Gem::Requirement
114
142
  requirements:
115
- - - '>='
143
+ - - ">="
116
144
  - !ruby/object:Gem::Version
117
145
  version: '0'
118
146
  type: :development
119
147
  prerelease: false
120
148
  version_requirements: !ruby/object:Gem::Requirement
121
149
  requirements:
122
- - - '>='
150
+ - - ">="
123
151
  - !ruby/object:Gem::Version
124
152
  version: '0'
125
153
  - !ruby/object:Gem::Dependency
126
154
  name: activerecord
127
155
  requirement: !ruby/object:Gem::Requirement
128
156
  requirements:
129
- - - '>='
157
+ - - ">="
130
158
  - !ruby/object:Gem::Version
131
159
  version: '3.0'
132
160
  type: :development
133
161
  prerelease: false
134
162
  version_requirements: !ruby/object:Gem::Requirement
135
163
  requirements:
136
- - - '>='
164
+ - - ">="
137
165
  - !ruby/object:Gem::Version
138
166
  version: '3.0'
139
167
  description: Daemon for sending butches of emails via Amazon SES using ActiveRecord
@@ -145,8 +173,9 @@ executables:
145
173
  extensions: []
146
174
  extra_rdoc_files: []
147
175
  files:
148
- - .gitignore
149
- - .travis.yml
176
+ - ".gitignore"
177
+ - ".travis.yml"
178
+ - CHANGELOG.md
150
179
  - Gemfile
151
180
  - LICENSE.txt
152
181
  - README.md
@@ -154,19 +183,26 @@ files:
154
183
  - ar_mailer_aws.gemspec
155
184
  - bin/ar_mailer_aws
156
185
  - lib/ar_mailer_aws.rb
186
+ - lib/ar_mailer_aws/clients/amazon_ses.rb
187
+ - lib/ar_mailer_aws/clients/base.rb
188
+ - lib/ar_mailer_aws/clients/mandrill.rb
189
+ - lib/ar_mailer_aws/clients/smtp.rb
157
190
  - lib/ar_mailer_aws/mailer.rb
191
+ - lib/ar_mailer_aws/options_parser.rb
158
192
  - lib/ar_mailer_aws/railtie.rb
159
- - lib/ar_mailer_aws/sender.rb
160
193
  - lib/ar_mailer_aws/version.rb
161
194
  - lib/generators/ar_mailer_aws/ar_mailer_aws_generator.rb
162
195
  - lib/generators/ar_mailer_aws/templates/ar_mailer_aws.rb
163
196
  - lib/generators/ar_mailer_aws/templates/migration.rb
164
197
  - lib/generators/ar_mailer_aws/templates/model.rb
165
198
  - spec/ar_mailer_aws/ar_mailer_aws_spec.rb
199
+ - spec/ar_mailer_aws/clients/amazon_ses_spec.rb
200
+ - spec/ar_mailer_aws/clients/base_spec.rb
201
+ - spec/ar_mailer_aws/clients/smtp_spec.rb
166
202
  - spec/ar_mailer_aws/mailer_spec.rb
167
- - spec/ar_mailer_aws/parse_options_spec.rb
168
- - spec/ar_mailer_aws/sender_spec.rb
203
+ - spec/ar_mailer_aws/options_parser_spec.rb
169
204
  - spec/spec_helper.rb
205
+ - unused/sender.rb
170
206
  homepage: https://github.com/leschenko/ar_mailer_aws
171
207
  licenses:
172
208
  - MIT
@@ -177,23 +213,25 @@ require_paths:
177
213
  - lib
178
214
  required_ruby_version: !ruby/object:Gem::Requirement
179
215
  requirements:
180
- - - '>='
216
+ - - ">="
181
217
  - !ruby/object:Gem::Version
182
218
  version: '0'
183
219
  required_rubygems_version: !ruby/object:Gem::Requirement
184
220
  requirements:
185
- - - '>='
221
+ - - ">="
186
222
  - !ruby/object:Gem::Version
187
223
  version: '0'
188
224
  requirements: []
189
225
  rubyforge_project:
190
- rubygems_version: 2.0.2
226
+ rubygems_version: 2.4.3
191
227
  signing_key:
192
228
  specification_version: 4
193
- summary: Send butches of emails via Amazon SES
229
+ summary: Send batches of emails via Amazon SES
194
230
  test_files:
195
231
  - spec/ar_mailer_aws/ar_mailer_aws_spec.rb
232
+ - spec/ar_mailer_aws/clients/amazon_ses_spec.rb
233
+ - spec/ar_mailer_aws/clients/base_spec.rb
234
+ - spec/ar_mailer_aws/clients/smtp_spec.rb
196
235
  - spec/ar_mailer_aws/mailer_spec.rb
197
- - spec/ar_mailer_aws/parse_options_spec.rb
198
- - spec/ar_mailer_aws/sender_spec.rb
236
+ - spec/ar_mailer_aws/options_parser_spec.rb
199
237
  - spec/spec_helper.rb
@@ -1,90 +0,0 @@
1
- module ArMailerAWS
2
- class Sender
3
- attr_reader :options, :model, :ses
4
-
5
- def initialize(options={})
6
- @options = options.is_a?(Hash) ? OpenStruct.new(options) : options
7
- @model = ArMailerAWS.email_class.constantize
8
- @ses = AWS::SimpleEmailService.new ArMailerAWS.ses_options
9
- @day = Date.today
10
- @sent_count = 0
11
- end
12
-
13
- def send_batch
14
- cleanup
15
- emails = find_emails
16
- log "found #{emails.length} emails to deliver"
17
- send_emails(emails) unless emails.empty?
18
- end
19
-
20
- def send_emails(emails)
21
- sent_per_second = 0
22
- emails.each do |email|
23
- if exceed_quota?
24
- log "exceed daily quota in #{@quota}, sent by mailer #{@sent_count}, other #{@sent_last_24_hours}"
25
- return
26
- end
27
- begin
28
- if sent_per_second == options.rate
29
- sleep 1
30
- sent_per_second = 0
31
- else
32
- sent_per_second += 1
33
- end
34
- log "send email to #{email.to}"
35
- @ses.send_raw_email email.mail, from: email.from, to: email.to
36
- email.destroy
37
- @sent_count += 1
38
- rescue => e
39
- log "ERROR sending email #{email.id} - #{email.inspect}: #{e.message}\n #{e.backtrace.join("\n ")}", :error
40
- ArMailerAWS.error_proc.call(email, e) if ArMailerAWS.error_proc
41
- email.update_column(:last_send_attempt_at, Time.now)
42
- end
43
- end
44
- end
45
-
46
- def exceed_quota?
47
- if @day == Date.today
48
- options.quota <= @sent_count + sent_last_24_hours
49
- else
50
- @sent_count = 0
51
- @sent_last_24_hours = nil
52
- false
53
- end
54
- end
55
-
56
- def sent_last_24_hours
57
- @sent_last_24_hours ||= begin
58
- count = @ses.quotas[:sent_last_24_hours]
59
- log "#{count} emails sent last 24 hours"
60
- count
61
- end
62
- end
63
-
64
- def find_emails
65
- @model.where('last_send_attempt_at IS NULL OR last_send_attempt_at < ?', Time.now - 300).limit(options.batch_size)
66
- end
67
-
68
- def cleanup
69
- return if options.max_age.to_i.zero?
70
- timeout = Time.now - options.max_age
71
- emails = @model.destroy_all(['last_send_attempt_at IS NOT NULL AND created_at < ?', timeout])
72
-
73
- log "expired #{emails.length} emails"
74
- end
75
-
76
- def log(msg, level=:info)
77
- formatted_msg = "[#{Time.now}] ar_mailer_aws: #{msg}"
78
- puts formatted_msg if options.verbose
79
- if logger
80
- logger.send(level, msg)
81
- elsif options.verbose && defined? Rails
82
- Rails.logger.send(level, formatted_msg)
83
- end
84
- end
85
-
86
- def logger
87
- ArMailerAWS.logger
88
- end
89
- end
90
- end