mail_engine 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -5,13 +5,13 @@ gem "activesupport", '>= 3.0.0'
5
5
  gem "httparty"
6
6
  gem "nokogiri"
7
7
  gem "deep_cloneable"
8
- gem "sqlite3-ruby", :require => "sqlite3"
9
8
  gem 'rack', :git => 'git://github.com/rack/rack.git'
10
9
  gem "liquid"
11
10
  gem 'kaminari', :git => 'https://github.com/amatsuda/kaminari.git'
12
11
  gem "carrierwave", :git => "git://github.com/jnicklas/carrierwave.git"
13
12
 
14
13
  group :development do
14
+ gem "sqlite3-ruby", :require => "sqlite3"
15
15
  gem "annotate"
16
16
  gem "bluecloth"
17
17
  gem "yard"
data/README.mkd CHANGED
@@ -25,6 +25,15 @@ to fit our needs in real project:
25
25
  6. It doesn't support reusable partials, which like you hope to put a fixed content of footer or header.
26
26
  7. It doesn't support recurring mail sending.
27
27
 
28
+ Serveral places you need developer's help without Mail Engine:
29
+
30
+ 1. Change text in system email.
31
+ 2. Localize mail templates in system.
32
+ 3. Prepare user list for marketing mail if you want to use mailchimp.
33
+ 4. Analyse system mail send log.
34
+ 5. Add multipart for email, like text version email or html version email.
35
+ 6. Find untranslated mail templates.
36
+
28
37
  Installation
29
38
  ============
30
39
  ** Step 1: **
@@ -97,6 +106,7 @@ Below is the config for development env.
97
106
  development:
98
107
  log_mail: false # if you want to enable the mail log, set true.
99
108
  user_class_name: "User" # Specify the User model in your system, which will used for mail schedule feature to find user groups and payloads.
109
+ user_locale_column: "locale" # Specify the locale column of user table, this will help sending correct language of mail to user.
100
110
  mount_at: "/admin/mail_engine" # set the url path for mail engine.
101
111
  access_check_method: "logged_in?" # set a method name which will execute before each page action in mail engine.
102
112
  replacement_email: "dev@youdomain.com" # if you want to send all mail to one email, like on staging server or development environment, you can set this, or else don't set it.
@@ -213,6 +223,7 @@ You can set a mail sending schedule, here are some useful setting items:
213
223
  6. Payloads which load from user model and send to template when sending.
214
224
  7. First send date time, this date will indicate when this mail will be send first time, and according to the seding period it will resend at the same time after one period.
215
225
  8. You can control if run the schedule or not.
226
+ 9. Users with different locale setting will receive different language of email. (if not found that language, you can set default template or not send.)
216
227
 
217
228
  ![Mail Schedule](https://github.com/hlxwell/mail-engine/raw/master/screenshots/schedule.png "Mail Schedule")
218
229
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.11
1
+ 0.1.12
@@ -14,8 +14,11 @@
14
14
  # last_sent_at :datetime
15
15
  # created_at :datetime
16
16
  # updated_at :datetime
17
+ # default_locale :string
17
18
  #
18
19
 
20
+ class MailEngine::MissingMailTemplate < StandardError; end
21
+
19
22
  class MailEngine::MailSchedule < ActiveRecord::Base
20
23
  validates_presence_of :name, :user_group, :mail_template_id, :period, :count, :first_send_at
21
24
  validates_inclusion_of :period, :in => %w( once daily weekly monthly yearly), :message => "period should in 'daily', 'weekly' and 'monthly'"
@@ -63,7 +66,7 @@ class MailEngine::MailSchedule < ActiveRecord::Base
63
66
  end
64
67
 
65
68
  # other periods
66
- rest_count = (count == 0 ? schedule_count : count - sent_count)
69
+ rest_count = (count == 0 ? schedule_count : count - sent_count)
67
70
  (1..rest_count).map do |i|
68
71
  last_schedule_time + i.send(PERIOD_TO_UNIT_TABLE[period].first)
69
72
  end
@@ -138,20 +141,35 @@ class MailEngine::MailSchedule < ActiveRecord::Base
138
141
  # used in Rake task, the main mail sending method.
139
142
  def sendmail
140
143
  puts "---> Schedule: '#{self.name}'" if Rails.env != 'test'
141
-
142
- unless ready_to_send?
143
- puts "---> not ready"
144
- return false
145
- end
144
+ return false unless ready_to_send? # puts "---> not ready"
146
145
 
147
146
  MailEngine::USER_MODEL.send(self.user_group.to_sym).each do |user|
148
- puts "-> sending mail to #{user.email}"
147
+ puts "-> sending mail to #{user.email}" if Rails.env != 'test'
149
148
  ### FIXME user.email, what if user don't have email column.
150
- MailEngine::MailDispatcher.send(mail_template.template_name.to_sym, :to => user.email, :values => self.load_payload(user) ).deliver
149
+ user_locale = user.send(MailEngine::Base.current_config["user_locale_column"])
150
+
151
+ begin
152
+ # when there is default_locale been set, should fallback to default_locale template, when can't find the locale user in user settings.
153
+ if !mail_template.existed_variations.map(&:locale).include?(user_locale)
154
+ if self.default_locale.present?
155
+ raise MailEngine::MissingMailTemplate.new("not found template for user locale: #{user_locale}")
156
+ else
157
+ raise "skip sending mail since not found correspond locale template."
158
+ end
159
+ end
160
+
161
+ I18n.with_locale user_locale do
162
+ MailEngine::MailDispatcher.send(mail_template.template_name.to_sym, :to => user.email, :values => self.load_payload(user) ).deliver
163
+ end
164
+ rescue MailEngine::MissingMailTemplate
165
+ I18n.with_locale self.default_locale do
166
+ MailEngine::MailDispatcher.send(mail_template.template_name.to_sym, :to => user.email, :values => self.load_payload(user) ).deliver
167
+ end
168
+ rescue
169
+ # do nothing, just don't sent mail.
170
+ end
151
171
  end
152
172
 
153
- self.last_sent_at = Time.now
154
- self.sent_count += 1
155
- self.save
173
+ self.update_attributes :last_sent_at => Time.now, :sent_count => self.sent_count + 1
156
174
  end
157
175
  end
@@ -123,17 +123,20 @@ class MailEngine::MailTemplate < ActiveRecord::Base
123
123
 
124
124
  # list the templates with same path, but different locale and format
125
125
  def variations(for_partial = false)
126
- existed_variations = self.class.where(:path => self.path, :partial => for_partial).order("locale, format")
127
-
128
126
  all_variation_codes = I18n.available_locales.product([:html, :text])
129
- existed_variation_codes = existed_variations.map do |template|
127
+ current_existed_variations = existed_variations(for_partial)
128
+ existed_variation_codes = current_existed_variations.map do |template|
130
129
  [template.locale.to_sym, template.format.to_sym]
131
130
  end
132
131
  missing_variations = (all_variation_codes - existed_variation_codes).map do |locale, format|
133
132
  MailEngine::MailTemplate.new :locale => locale.to_s, :format => format.to_s
134
133
  end
135
134
 
136
- missing_variations + existed_variations
135
+ missing_variations + current_existed_variations
136
+ end
137
+
138
+ def existed_variations for_partial = false
139
+ self.class.where(:path => self.path, :partial => for_partial).order("locale, format")
137
140
  end
138
141
 
139
142
  def actual_path
@@ -39,6 +39,14 @@
39
39
  <%= f.check_box :available %>
40
40
  <div class="hint">If you don't want to execute this schedule, leave it unchecked.</div>
41
41
  </div>
42
+ <div class="field">
43
+ <%= f.label :default_locale, "Default Locale" %><br />
44
+ <%= f.select :default_locale, I18n.available_locales, :include_blank => true %>
45
+ <div class="hint">
46
+ When can find mail template for user's locale, system will send default locale template to user.
47
+ If you leave this blank, system won't send mail to this kind of users.
48
+ </div>
49
+ </div>
42
50
  <div class="field">
43
51
  <%= f.label :payload %><br />
44
52
  <% MailEngine::USER_MODEL.payload_columns.each do |column| %>
@@ -37,12 +37,16 @@
37
37
  <td>Last send at</td>
38
38
  <td><%= @mail_schedule.last_sent_at %></td>
39
39
  </tr>
40
+ <tr>
41
+ <td>Default Locale</td>
42
+ <td><%= @mail_schedule.default_locale %></td>
43
+ </tr>
40
44
  <tr>
41
45
  <td>Payload</td>
42
46
  <td><%= @mail_schedule.payload %></td>
43
47
  </tr>
44
48
  </table>
45
-
49
+
46
50
  <%= render "mail_engine/mail_logs/related_logs", :logs => @mail_schedule.logs %>
47
51
  </div>
48
52
  <br />
@@ -11,6 +11,7 @@ class CreateMailSchedules < ActiveRecord::Migration
11
11
  t.datetime :first_send_at
12
12
  t.datetime :last_sent_at
13
13
  t.boolean :available, :default => false
14
+ t.string :default_locale
14
15
 
15
16
  t.timestamps
16
17
  end
@@ -1,6 +1,7 @@
1
1
  development:
2
2
  log_mail: false
3
3
  user_class_name: "User"
4
+ user_locale_column: "locale"
4
5
  mount_at: "/admin/mail_engine"
5
6
  access_check_method: "logged_in?"
6
7
  default_from: "your@email.address"
@@ -14,6 +15,7 @@ development:
14
15
  test:
15
16
  log_mail: false
16
17
  user_class_name: "User"
18
+ user_locale_column: "locale"
17
19
  mount_at: "/admin/mail_engine"
18
20
  access_check_method: "logged_in?"
19
21
  replacement_email: "dev@youdomain.com"
@@ -27,6 +29,7 @@ test:
27
29
  production:
28
30
  log_mail: false
29
31
  user_class_name: "User"
32
+ user_locale_column: "locale"
30
33
  mount_at: "/admin/mail_engine"
31
34
  access_check_method: "logged_in?"
32
35
  default_from: "your@email.address"
@@ -0,0 +1,43 @@
1
+ module MailEngine
2
+ module RakeLocker
3
+ TRY_TIMES = 4
4
+
5
+ def lock_task
6
+ raise "Please pass block." unless block_given?
7
+
8
+ if locked?
9
+ puts 'locked!'
10
+ lock
11
+ else
12
+ lock
13
+ yield
14
+ unlock
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def locked?
21
+ # if no convert_files_lock1, not locked
22
+ return false unless FileTest.exist? "#{Rails.root}/tmp/convert_files_lock1"
23
+ # if no convert_files_lock4, return locked
24
+ return true unless FileTest.exist? "#{Rails.root}/tmp/convert_files_lock#{TRY_TIMES}"
25
+ ### tried TRY_TIMES times, found convert_files_lock4 file, unlock it
26
+ unlock_task
27
+ return false
28
+ end
29
+
30
+ def lock
31
+ (1..TRY_TIMES).each do |n|
32
+ unless FileTest.exist? "#{Rails.root}/tmp/convert_files_lock#{n}"
33
+ `touch #{Rails.root}/tmp/convert_files_lock#{n}`
34
+ break
35
+ end
36
+ end
37
+ end
38
+
39
+ def unlock
40
+ `rm #{Rails.root}/tmp/convert_files_lock*`
41
+ end
42
+ end
43
+ end
@@ -1,8 +1,12 @@
1
+ require 'mail_engine/rake_locker'
1
2
  namespace :mail_engine do
3
+ include MailEngine::RakeLocker
2
4
  desc "Check mail schedule table and send the scheduled mail."
3
5
  task :sendmail => :environment do
4
- puts "==== Start sending scheduled mail ===="
5
- MailEngine::MailSchedule.available.each { |schedule| schedule.sendmail }
6
- puts "==== End sending scheduled mail ===="
6
+ lock_task do
7
+ puts "==== Start sending scheduled mail ===="
8
+ MailEngine::MailSchedule.available.each { |schedule| schedule.sendmail }
9
+ puts "==== End sending scheduled mail ===="
10
+ end
7
11
  end
8
12
  end
data/mail_engine.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mail_engine}
8
- s.version = "0.1.11"
8
+ s.version = "0.1.12"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["michael he"]
12
- s.date = %q{2011-03-16}
12
+ s.date = %q{2011-03-17}
13
13
  s.description = %q{Rails system mail management solution.}
14
14
  s.email = %q{hlxwell@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -221,6 +221,7 @@ Gem::Specification.new do |s|
221
221
  "lib/mail_engine/liquid_view_patch/liquid_view.rb",
222
222
  "lib/mail_engine/mail_log_subscriber.rb",
223
223
  "lib/mail_engine/mail_template_resolver.rb",
224
+ "lib/mail_engine/rake_locker.rb",
224
225
  "lib/mail_engine/sendgrid.rb",
225
226
  "lib/mail_engine/sendgrid/base.rb",
226
227
  "lib/mail_engine/sendgrid/rest_api.rb",
@@ -382,8 +383,8 @@ Gem::Specification.new do |s|
382
383
  "test/dummy/app/mailers/test/test_mailer.rb",
383
384
  "test/dummy/app/mailers/user_mailer.rb",
384
385
  "test/dummy/app/models/user.rb",
385
- "test/dummy/app/views/user_mailer/notify.html.erb",
386
- "test/dummy/app/views/user_mailer/notify.text.liquid",
386
+ "test/dummy/app/views/user_mailer/notify.en.html.erb",
387
+ "test/dummy/app/views/user_mailer/notify.en.text.liquid",
387
388
  "test/dummy/config.ru",
388
389
  "test/dummy/config/application.rb",
389
390
  "test/dummy/config/boot.rb",
@@ -629,11 +630,11 @@ Gem::Specification.new do |s|
629
630
  s.add_runtime_dependency(%q<httparty>, [">= 0"])
630
631
  s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
631
632
  s.add_runtime_dependency(%q<deep_cloneable>, [">= 0"])
632
- s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 0"])
633
633
  s.add_runtime_dependency(%q<rack>, [">= 0"])
634
634
  s.add_runtime_dependency(%q<liquid>, [">= 0"])
635
635
  s.add_runtime_dependency(%q<kaminari>, [">= 0"])
636
636
  s.add_runtime_dependency(%q<carrierwave>, [">= 0"])
637
+ s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
637
638
  s.add_development_dependency(%q<annotate>, [">= 0"])
638
639
  s.add_development_dependency(%q<bluecloth>, [">= 0"])
639
640
  s.add_development_dependency(%q<yard>, [">= 0"])
@@ -646,11 +647,11 @@ Gem::Specification.new do |s|
646
647
  s.add_dependency(%q<httparty>, [">= 0"])
647
648
  s.add_dependency(%q<nokogiri>, [">= 0"])
648
649
  s.add_dependency(%q<deep_cloneable>, [">= 0"])
649
- s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
650
650
  s.add_dependency(%q<rack>, [">= 0"])
651
651
  s.add_dependency(%q<liquid>, [">= 0"])
652
652
  s.add_dependency(%q<kaminari>, [">= 0"])
653
653
  s.add_dependency(%q<carrierwave>, [">= 0"])
654
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
654
655
  s.add_dependency(%q<annotate>, [">= 0"])
655
656
  s.add_dependency(%q<bluecloth>, [">= 0"])
656
657
  s.add_dependency(%q<yard>, [">= 0"])
@@ -664,11 +665,11 @@ Gem::Specification.new do |s|
664
665
  s.add_dependency(%q<httparty>, [">= 0"])
665
666
  s.add_dependency(%q<nokogiri>, [">= 0"])
666
667
  s.add_dependency(%q<deep_cloneable>, [">= 0"])
667
- s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
668
668
  s.add_dependency(%q<rack>, [">= 0"])
669
669
  s.add_dependency(%q<liquid>, [">= 0"])
670
670
  s.add_dependency(%q<kaminari>, [">= 0"])
671
671
  s.add_dependency(%q<carrierwave>, [">= 0"])
672
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
672
673
  s.add_dependency(%q<annotate>, [">= 0"])
673
674
  s.add_dependency(%q<bluecloth>, [">= 0"])
674
675
  s.add_dependency(%q<yard>, [">= 0"])
@@ -17,7 +17,7 @@ Dummy::Application.configure do
17
17
  # Don't care if the mailer can't send
18
18
  config.action_mailer.perform_deliveries = true
19
19
  config.action_mailer.raise_delivery_errors = true
20
- config.action_mailer.delivery_method = :smtp
20
+ config.action_mailer.delivery_method = :test
21
21
 
22
22
  config.action_mailer.smtp_settings = {
23
23
  :address => "smtp.sendgrid.net",
@@ -11,6 +11,7 @@ class CreateMailSchedules < ActiveRecord::Migration
11
11
  t.datetime :first_send_at
12
12
  t.datetime :last_sent_at
13
13
  t.boolean :available
14
+ t.string :default_locale
14
15
 
15
16
  t.timestamps
16
17
  end
@@ -34,6 +34,7 @@ ActiveRecord::Schema.define(:version => 20110217062316) do
34
34
  t.datetime "first_send_at"
35
35
  t.datetime "last_sent_at"
36
36
  t.boolean "available"
37
+ t.string "default_locale"
37
38
  t.datetime "created_at"
38
39
  t.datetime "updated_at"
39
40
  end
data/test/factories.rb CHANGED
@@ -105,6 +105,7 @@ FactoryGirl.define do
105
105
  factory :mail_schedule, :class => MailEngine::MailSchedule do
106
106
  name "New Data"
107
107
  association :mail_template, :factory => :marketing_mail_template, :format => "html"
108
+ # default_locale 'en'
108
109
  user_group "all"
109
110
  count 0
110
111
  sent_count 0
@@ -2,6 +2,42 @@ require 'test_helper'
2
2
 
3
3
  class MailEngine::MailScheduleTest < ActiveSupport::TestCase
4
4
 
5
+ context "schedule with 2 locale users" do
6
+ setup do
7
+ User.destroy_all
8
+ ActionMailer::Base.deliveries.clear
9
+
10
+ @user_en = FactoryGirl.create(:user, :locale => 'en')
11
+ @user_zh = FactoryGirl.create(:user, :email => "mmm@gmail.com", :locale => 'zh')
12
+ @schedule = FactoryGirl.create(:mail_schedule, :period => "once")
13
+ @en_template = @schedule.mail_template
14
+ end
15
+
16
+ should "send correct language mail template to user" do
17
+ zh_template = FactoryGirl.create(:marketing_mail_template, :body => "chinese marketing template", :locale => "zh")
18
+
19
+ assert_equal 2, MailEngine::MailTemplate.count
20
+ assert @schedule.ready_to_send?
21
+ assert_equal 0, ActionMailer::Base.deliveries.size
22
+ @schedule.sendmail
23
+ assert_equal 2, ActionMailer::Base.deliveries.size
24
+ assert ActionMailer::Base.deliveries.first.body.raw_source.include?(@en_template.body)
25
+ assert ActionMailer::Base.deliveries.last.body.raw_source.include?(zh_template.body)
26
+ end
27
+
28
+ should "not send mail without correct template and default template" do
29
+ @schedule.sendmail
30
+ assert_equal 1, ActionMailer::Base.deliveries.size
31
+ end
32
+
33
+ should "send mail without correct template but has set default template" do
34
+ @schedule.default_locale = 'en'
35
+ @schedule.sendmail
36
+ assert_equal 2, ActionMailer::Base.deliveries.size
37
+ assert ActionMailer::Base.deliveries.last.body.raw_source.include?(@en_template.body)
38
+ end
39
+ end
40
+
5
41
  context "schedule" do
6
42
  setup do
7
43
  @user = FactoryGirl.create(:user)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_engine
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 11
10
- version: 0.1.11
9
+ - 12
10
+ version: 0.1.12
11
11
  platform: ruby
12
12
  authors:
13
13
  - michael he
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-16 00:00:00 +08:00
18
+ date: 2011-03-17 00:00:00 +08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -88,7 +88,7 @@ dependencies:
88
88
  version: "0"
89
89
  requirement: *id005
90
90
  prerelease: false
91
- name: sqlite3-ruby
91
+ name: rack
92
92
  type: :runtime
93
93
  - !ruby/object:Gem::Dependency
94
94
  version_requirements: &id006 !ruby/object:Gem::Requirement
@@ -102,7 +102,7 @@ dependencies:
102
102
  version: "0"
103
103
  requirement: *id006
104
104
  prerelease: false
105
- name: rack
105
+ name: liquid
106
106
  type: :runtime
107
107
  - !ruby/object:Gem::Dependency
108
108
  version_requirements: &id007 !ruby/object:Gem::Requirement
@@ -116,7 +116,7 @@ dependencies:
116
116
  version: "0"
117
117
  requirement: *id007
118
118
  prerelease: false
119
- name: liquid
119
+ name: kaminari
120
120
  type: :runtime
121
121
  - !ruby/object:Gem::Dependency
122
122
  version_requirements: &id008 !ruby/object:Gem::Requirement
@@ -130,7 +130,7 @@ dependencies:
130
130
  version: "0"
131
131
  requirement: *id008
132
132
  prerelease: false
133
- name: kaminari
133
+ name: carrierwave
134
134
  type: :runtime
135
135
  - !ruby/object:Gem::Dependency
136
136
  version_requirements: &id009 !ruby/object:Gem::Requirement
@@ -144,8 +144,8 @@ dependencies:
144
144
  version: "0"
145
145
  requirement: *id009
146
146
  prerelease: false
147
- name: carrierwave
148
- type: :runtime
147
+ name: sqlite3-ruby
148
+ type: :development
149
149
  - !ruby/object:Gem::Dependency
150
150
  version_requirements: &id010 !ruby/object:Gem::Requirement
151
151
  none: false
@@ -458,6 +458,7 @@ files:
458
458
  - lib/mail_engine/liquid_view_patch/liquid_view.rb
459
459
  - lib/mail_engine/mail_log_subscriber.rb
460
460
  - lib/mail_engine/mail_template_resolver.rb
461
+ - lib/mail_engine/rake_locker.rb
461
462
  - lib/mail_engine/sendgrid.rb
462
463
  - lib/mail_engine/sendgrid/base.rb
463
464
  - lib/mail_engine/sendgrid/rest_api.rb
@@ -619,8 +620,8 @@ files:
619
620
  - test/dummy/app/mailers/test/test_mailer.rb
620
621
  - test/dummy/app/mailers/user_mailer.rb
621
622
  - test/dummy/app/models/user.rb
622
- - test/dummy/app/views/user_mailer/notify.html.erb
623
- - test/dummy/app/views/user_mailer/notify.text.liquid
623
+ - test/dummy/app/views/user_mailer/notify.en.html.erb
624
+ - test/dummy/app/views/user_mailer/notify.en.text.liquid
624
625
  - test/dummy/config.ru
625
626
  - test/dummy/config/application.rb
626
627
  - test/dummy/config/boot.rb