mailtime 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6796e6fba8e145db97279c3bfc86388553a9733
4
- data.tar.gz: 0f3bf01438f14798f016290245047cf4ace18e17
3
+ metadata.gz: bb256a8b9fab916c09f46c910c3b7a11580ee127
4
+ data.tar.gz: 1aa93d7716724bee86d10518770dabe15913b29b
5
5
  SHA512:
6
- metadata.gz: 728b353305727c26f8f16c24583051f98771efe706722381c1e62e5b09630afdac8d304a63d849ff9851e7775371b047bf072cc60cf653973a51b011181d7066
7
- data.tar.gz: d0ec9ee59c341e33f48bad171db20753c501441e87456bc9157015a113955ddd2dd3521e7ce7d096b71797f8d061ee55ca89f88471f5d3a27e1f8f6cab3abdd5
6
+ metadata.gz: 7c394d658b4561da7d544d8fed55c6dfbd268ad290f710ca585fcbb591184861dcefea20b0e8f6663dc05bc522eb2b4deacf217926b86b76f8ca40f89f1463bc
7
+ data.tar.gz: 7ae9bce261872232413da2c8b10abafce14d377a7758dd60d168d819ed90f9dc7d4dd16153095fbf8f0f5d40e5e22ffb550e3c82f201850ab565a493924b5f84
data/README.md CHANGED
@@ -38,7 +38,24 @@ Mailtime aims to be highly flexible and tries not to dictate your setup. For exa
38
38
 
39
39
  ## Usage
40
40
 
41
- Should "just work", but...
41
+ Using Mailtime is simple. Just add `acts_as_mailtime_loggable` to each class you want to associate a `Mailtime::MailLog` to.
42
+
43
+ #### acts_as_mailtime_loggable options
44
+
45
+ ```Ruby
46
+ class User < ActiveRecord::Base
47
+ acts_as_mailtime_loggable :email_address, :fields => [:to, :cc]
48
+ ```
49
+
50
+ will tell `Mailtime::MailLog` to find a `User` that matches an `ActionMailer#mail`'s `#to` or `#cc` attributes.
51
+
52
+ #### Configuration
53
+
54
+ The default initializer that's installed when you run `rails g mailtime:install` is self-documented.
55
+
56
+ ## Additional Setup
57
+
58
+ #### MailTemplate
42
59
 
43
60
  You'll want somewhere to view your logs and manage your templates. Basic scaffolding will do just fine.
44
61
 
@@ -64,25 +81,25 @@ Here's an example using `SimpleForm`:
64
81
 
65
82
  `MailTemplate.mail_actions` is an array of mailer classes and their public instance methods, e.g. `['UserMailer' => ['password_reset_email']]`
66
83
 
84
+ #### MailLog
85
+
86
+ A `MailLog` stores the `MailTemplate`, `action` (from the mailer), `klass` (from the mailer), `field` (such as `to`, `cc`, `bcc), `loggable` (from `acts_as_mailtime_loggable`), `headers` (from `ActionMailer#mail`), and `scope` (the instance variables defined in the action)
87
+
88
+ You'll probably want somewhere to look at these.
89
+
67
90
  ## Todo
68
91
 
69
- * Better loading of mailers
70
- * Allow config to specify the user class
71
- * Fallbacks
72
- * Import existing mailers
92
+ * Import existing mailers with a rake task
73
93
  * Layouts
74
94
  * Tests
75
- * BCC, CC
95
+ * BCC, CC for templates
76
96
  * Configure overrides for subject
77
- * Custom user finder
78
97
 
79
98
  ## Maybe todo
80
99
 
81
- * WYSIWIG?
82
- * Sync?
100
+ * Dump for production?
83
101
  * Versioning for templates?
84
102
  * Specify multiple formats?
85
- * A/B testing?
86
103
 
87
104
  ## Contributing
88
105
 
@@ -16,8 +16,11 @@ class MailtimeMigration < ActiveRecord::Migration
16
16
  t.string :template
17
17
  t.string :action
18
18
  t.string :klass
19
- t.integer :user_id
20
- t.json :headers
19
+ t.string :field
20
+ t.integer :loggable_id
21
+ t.string :loggable_type
22
+ t.jsonb :headers
23
+ t.jsonb :scope
21
24
 
22
25
  t.timestamps null: false
23
26
  end
@@ -25,6 +28,9 @@ class MailtimeMigration < ActiveRecord::Migration
25
28
  add_index :mailtime_mail_logs, :template
26
29
  add_index :mailtime_mail_logs, :action
27
30
  add_index :mailtime_mail_logs, :klass
28
- add_index :mailtime_mail_logs, :user_id
31
+ add_index :mailtime_mail_logs, [:loggable_id, :loggable_type]
32
+ add_index :mailtime_mail_logs, :field
33
+ add_index :mailtime_mail_logs, :headers, using: :gin
34
+ add_index :mailtime_mail_logs, :scope, using: :gin
29
35
  end
30
36
  end
@@ -2,14 +2,12 @@ Mailtime.configure do |config|
2
2
 
3
3
  # == Mailers
4
4
  #
5
- # Unfortunately, Rails doesn't autoload mailers in development
6
- # If you don't mind restarting your Rails server every time you add a new mailer,
7
- # you can set this to false.
8
- config.lazy_load_mailers = false
5
+ # Unfortunately, Rails doesn't autoload all mailers in development.
6
+ # Want to preload all your mailers? Uncomment below.
7
+ # config.preload_mailers!
9
8
 
10
- # If you're wondering why Devise's mailers aren't getting picked up (or something similar)
11
- # you can specify additional mailers below.
12
- #config.mailers << 'Devise::Mailer'
9
+ # Have a mailer that doesn't exist in app/mailers? Add it below.
10
+ # config.mailer << 'Devise::Mail'
13
11
 
14
12
  # == Rendering
15
13
  #
@@ -25,21 +23,25 @@ Mailtime.configure do |config|
25
23
  # If for some reason, calling #render on your renderer is not okay, you can specify
26
24
  # the method below. (ps. the method should output the contents of the template + mail context)
27
25
  # so that it can be assigned to the `body` of the mail
28
- #config.renderer_method = :render
26
+ # config.renderer_method = :render
29
27
 
30
28
  # == Logging
31
29
  #
32
- # Log the mail to the database
30
+ # Log the mail to the database. Set to `false` if you don't want to log anything.
33
31
  config.log = true
34
32
 
35
- # If your user class is not `User`, you can tell Mailtime what it is and for each mail that gets logged
36
- # to associate it to the instance variable that has this class.
37
- # todo
38
- # config.user_class = 'User'
33
+ # Perform lookups on the following fields and associate the logs to them
34
+ # Valid options are :to, :cc, and :bcc
35
+ # config.log_fields = [:to]
36
+
37
+ # Sometimes, a mail action gets polluted with instance variables we don't care about.
38
+ # Because we log all instance variables, you can remove any named instance variables here.
39
+ # Defaults to Devise::Mail's ['devise_mapping', 'scope_name']
40
+ # config.reject_scope_keys = ['devise_mapping', 'scope_name']
39
41
 
40
42
  # == Fallbacks
41
- # falls back to ERB if the template doesn't exist in the database
42
- # raises an error if the template doesn't exist in ERB
43
- config.fallback_to_erb = false
43
+ # Falls back to the mail's body if the template doesn't exist in the database
44
+ # If set to false, raises a `TemplateNotFound` error if the template doesn't exist
45
+ config.fallback = true
44
46
 
45
47
  end
@@ -25,10 +25,14 @@ module Mailtime
25
25
  self.message.instance_variable_set(:@mailer_klass, mailer_klass)
26
26
  self.message.instance_variable_set(:@mailer_action, mailer_action)
27
27
  self.message.instance_variable_set(:@action_variables, action_variables)
28
+ self.message.instance_variable_set(:@template_path, self.headers[:template_path] || mailer_klass)
29
+ self.message.instance_variable_set(:@template_name, self.headers[:template_name] || mailer_action)
28
30
 
29
31
  self.message.class.send(:attr_reader, :mailer_klass)
30
32
  self.message.class.send(:attr_reader, :mailer_action)
31
33
  self.message.class.send(:attr_reader, :action_variables)
34
+ self.message.class.send(:attr_reader, :template_path)
35
+ self.message.class.send(:attr_reader, :template_name)
32
36
  end
33
37
 
34
38
  end
@@ -4,11 +4,9 @@ module Mailtime
4
4
  module MailLogConcern
5
5
 
6
6
  extend ::ActiveSupport::Concern
7
-
8
7
  included do
9
8
  self.table_name = 'mailtime_mail_logs'
10
- belongs_to :user, :class_name => 'User',
11
- :foreign_key => :user_id
9
+ belongs_to :loggable, :polymorphic => true
12
10
  end
13
11
 
14
12
  end
@@ -1,4 +1,4 @@
1
- require 'mailtime/mail_log_concern'
1
+ require 'mailtime/active_record/models/concerns/mail_log_concern'
2
2
 
3
3
  module Mailtime
4
4
  class MailLog < ActiveRecord::Base
@@ -1,4 +1,4 @@
1
- require 'mailtime/mail_template_concern'
1
+ require 'mailtime/active_record/models/concerns/mail_template_concern'
2
2
 
3
3
  module Mailtime
4
4
  class MailTemplate < ActiveRecord::Base
@@ -0,0 +1,11 @@
1
+ module Mailtime
2
+ module ActsAsMailtimeLoggable
3
+
4
+ def acts_as_mailtime_loggable(attr = 'email', opts = {})
5
+ require 'mailtime/loggable'
6
+ include Mailtime::Loggable
7
+ Mailtime.configuration.loggable_lookups << { self.to_s => {:attribute => attr, :fields => opts[:fields]} }
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ module Mailtime
2
+
3
+ class Configuration
4
+ attr_accessor :mailers, :render, :log, :log_fields, :fallback, :renderer, :renderer_method, :lazy_load_mailers,
5
+ :user_class, :reject_scope_keys, :loggable_lookups
6
+
7
+ def initialize
8
+ @mailers = load_mailers
9
+ @render = true
10
+ @log = true
11
+ @log_fields = [:to]
12
+ @fallback = true
13
+ @renderer = 'Mailtime::Renderers::ErbRenderer'
14
+ @renderer_method = :render
15
+ @user_class = 'User'
16
+ @user_finder = nil
17
+ @reject_scope_keys = ['devise_mapping', 'scope_name']
18
+ @loggable_lookups = []
19
+ end
20
+
21
+ def mailer(klass)
22
+ @mailers << klass
23
+ end
24
+
25
+ def loggables
26
+ @loggable_lookups.each_with_object({}) do |k,h|
27
+ key = k.keys.first
28
+ h[key] = k[key]
29
+ end
30
+ end
31
+
32
+ def self.loggable_lookups
33
+ @loggable_lookups ||= []
34
+ end
35
+
36
+ def self.loggable_lookups=(val)
37
+ @loggable_lookups
38
+ end
39
+
40
+ def preload_mailers!
41
+ preload_mailers
42
+ end
43
+
44
+ def preload_mailers
45
+ Dir.glob(Rails.root.join("app/mailers/**/*_mailer.rb"))
46
+ .each { |klass| require klass }
47
+ end
48
+
49
+ protected
50
+
51
+ def load_mailers
52
+ ObjectSpace.each_object(Class).select { |klass| klass < ::ActionMailer::Base }
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,11 @@
1
+ module Mailtime
2
+ module Loggable
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ has_many :mail_logs, :as => :loggable, :class_name => 'Mailtime::MailLog'
7
+ end
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ module Mailtime
2
+ class MailLogService
3
+
4
+ attr_accessor :field, :loggable
5
+ def initialize(mail, loggable_class, options)
6
+ @mail = mail
7
+ @loggable_class = loggable_class
8
+ @loggable = nil
9
+ @options = options
10
+ @fields = []
11
+ end
12
+
13
+ def execute
14
+ log
15
+ end
16
+
17
+ def log
18
+ return unless loggable?
19
+ create_mail_log
20
+ end
21
+
22
+ def loggable?
23
+ @mail.action_variables.detect { |_, var| class_is_loggable?(var) && attribute_is_loggable?(var) } && @fields.any?
24
+ end
25
+
26
+ def class_is_loggable?(var)
27
+ if var.class.to_s == @loggable_class
28
+ @loggable = var
29
+ end
30
+ end
31
+
32
+ def attribute_is_loggable?(var)
33
+ fields = @options[:fields] || Mailtime.configuration.log_fields || [:to, :cc, :bcc]
34
+ fields.each do |field|
35
+ if @mail.send(field).include?(var.send(@options[:attribute]))
36
+ @fields << field
37
+ end
38
+ end
39
+ end
40
+
41
+ def create_mail_log
42
+ @fields.each do |field|
43
+ ::Mailtime::MailLog.create(
44
+ :loggable => @loggable,
45
+ :mailtime_mail_template_id => @template.try(:id),
46
+ :action => @mail.mailer_action,
47
+ :klass => @mail.mailer_klass,
48
+ :headers => extract_headers,
49
+ :scope => extract_scope,
50
+ :field => field
51
+ )
52
+ end
53
+ end
54
+
55
+ def extract_headers
56
+ headers = {}
57
+ @mail.header_fields.map { |k| headers[k.name] = k.value }
58
+ headers
59
+ end
60
+
61
+ def extract_scope
62
+ @mail.action_variables.as_json.except(*Mailtime.configuration.reject_scope_keys)
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,49 @@
1
+ require 'mailtime/models'
2
+ require 'mailtime/renderers/base_renderer'
3
+ require 'mailtime/renderers/erb_renderer'
4
+ require 'mailtime/processor/mail_log_service'
5
+ require 'mailtime/processor/processor'
6
+ module Mailtime
7
+
8
+ class Processor
9
+
10
+ def initialize(mail)
11
+ @mail = mail
12
+ end
13
+
14
+ def execute
15
+ process_mail
16
+ end
17
+
18
+ protected
19
+
20
+ def process_mail
21
+ log_mail
22
+ render_mail
23
+ @mail
24
+ end
25
+
26
+ private
27
+
28
+ def log_mail
29
+ return unless Mailtime.configuration.log
30
+ log_for_recipients
31
+ end
32
+
33
+ def render_mail
34
+ return @mail unless Mailtime.configuration.render
35
+ renderer = Mailtime.configuration.renderer.to_s.constantize.new(@mail)
36
+ @mail.body = renderer.send(Mailtime.configuration.renderer_method)
37
+ @mail
38
+ end
39
+
40
+ def log_for_recipients
41
+ Mailtime.configuration.loggables.each do |loggable, options|
42
+ svc = MailLogService.new(@mail, loggable, options)
43
+ svc.log
44
+ end
45
+ end
46
+
47
+
48
+ end
49
+ end
@@ -1,7 +1,18 @@
1
+ require 'rails'
2
+ require 'mailtime'
1
3
  class Railtie < Rails::Railtie
2
4
  initializer "Include your code in the controller" do
3
5
  ActiveSupport.on_load(:action_controller) do
4
6
  include Mailtime
5
7
  end
6
8
  end
9
+
10
+ rake_tasks do
11
+ namespace :mailtime do
12
+ desc 'Create a bunch of MailTemplate(s) based on existing mailers'
13
+ task :sync_mailers do
14
+ puts "Todo."
15
+ end
16
+ end
17
+ end
7
18
  end
@@ -2,6 +2,8 @@ module Mailtime
2
2
  module Renderers
3
3
  class BaseRenderer
4
4
 
5
+ class TemplateNotFound < StandardError; end
6
+
5
7
  # we inject mail with the instance variables from your ActionMailer action.
6
8
  # access them via mail.action_variables
7
9
  def initialize(mail)
@@ -15,8 +17,17 @@ module Mailtime
15
17
 
16
18
  protected
17
19
 
20
+ # find the template. If one doesn't exist, and we want to fallback,
21
+ # pretend like we have a template with an OpenStruct
18
22
  def template
19
- ::Mailtime::MailTemplate.find_by(:klass => @mail.mailer_klass, :action => @mail.mailer_action)
23
+ _template = ::Mailtime::MailTemplate.find_by(:klass => @mail.mailer_klass, :action => @mail.mailer_action)
24
+ if _template.nil?
25
+ unless Mailtime.configuration.fallback
26
+ raise TemplateNotFound, "Template doesn't exist for klass #{@mail.mailer_klass} and action #{@mail.mailer_action}"
27
+ end
28
+ _template = OpenStruct.new(:content => @mail.body.raw_source)
29
+ end
30
+ _template
20
31
  end
21
32
 
22
33
  end
@@ -0,0 +1,23 @@
1
+ require 'erb'
2
+ require 'mailtime/renderers/erb_renderer'
3
+
4
+ module Mailtime
5
+ module Renderers
6
+ class ErbRenderer < BaseRenderer
7
+
8
+ def render
9
+ set_instance_variables_from_mailer
10
+ ERB.new(@template.content).result(binding)
11
+ end
12
+
13
+ protected
14
+
15
+ def set_instance_variables_from_mailer
16
+ @mail.action_variables.each do |k, v|
17
+ instance_variable_set("@#{k}", v) rescue NameError
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Mailtime
2
- VERSION = "0.1.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/mailtime.rb CHANGED
@@ -2,11 +2,13 @@ begin
2
2
  require 'rails/engine'
3
3
  require 'mailtime/engine'
4
4
  rescue LoadError
5
-
6
5
  end
7
6
 
7
+
8
8
  module Mailtime
9
9
 
10
+ require 'mailtime/configuration'
11
+
10
12
  class << self
11
13
  attr_accessor :configuration
12
14
  end
@@ -16,54 +18,16 @@ module Mailtime
16
18
  yield(configuration)
17
19
  end
18
20
 
19
- class Configuration
20
- attr_accessor :mailers, :render, :log, :fallback_to_erb, :renderer, :renderer_method, :lazy_load_mailers,
21
- :user_class
22
-
23
- def initialize
24
- @lazy_load_mailers = false
25
- @mailers = load_mailers
26
- @render = true
27
- @log = true
28
- @fallback_to_erb = true
29
- @renderer = 'Mailtime::Renderers::BaseRenderer'
30
- @renderer_method = :render
31
- @user_class = 'User'
32
- @user_finder = nil
33
- end
34
-
35
- def load_mailers
36
- mailers = []
37
- unless @lazy_load_mailers
38
- mailers = require_and_load_mailers
39
- end
40
- mailers
41
- end
42
-
43
- protected
44
-
45
- def require_and_load_mailers
46
- mailers = []
47
- Dir.glob("#{::Rails.root}/app/mailers/**/*_mailer.rb").each do |m|
48
- require m rescue nil
49
- end
50
- mailers << ::ActionMailer::Base.descendants.map(&:to_s)
51
- mailers
52
- end
53
-
54
- end
55
-
56
- protected
57
-
58
- def self.find_user_by(attr = :email, value = nil)
59
- ::User.find_by(attr => value)
21
+ if defined?(ActiveRecord::Base)
22
+ require 'mailtime/acts_as_mailtime_loggable'
23
+ ActiveRecord::Base.extend Mailtime::ActsAsMailtimeLoggable
60
24
  end
61
25
 
62
26
  end
63
27
 
64
28
  require 'mailtime/action_mailer/base'
65
29
  require 'mailtime/action_mailer/interceptor'
66
- require 'mailtime/processor'
30
+ require 'mailtime/processor/processor'
67
31
  require 'mailtime/railtie'
68
- require 'mailtime/mail_log_concern'
69
- require 'mailtime/mail_template_concern'
32
+ require 'mailtime/active_record/models/concerns/mail_log_concern'
33
+ require 'mailtime/active_record/models/concerns/mail_template_concern'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Brody
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-04-01 00:00:00.000000000 Z
11
+ date: 2017-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -45,15 +45,20 @@ files:
45
45
  - lib/mailtime.rb
46
46
  - lib/mailtime/action_mailer/base.rb
47
47
  - lib/mailtime/action_mailer/interceptor.rb
48
+ - lib/mailtime/active_record/models/concerns/mail_log_concern.rb
49
+ - lib/mailtime/active_record/models/concerns/mail_template_concern.rb
48
50
  - lib/mailtime/active_record/models/mail_log.rb
49
51
  - lib/mailtime/active_record/models/mail_template.rb
52
+ - lib/mailtime/acts_as_mailtime_loggable.rb
53
+ - lib/mailtime/configuration.rb
50
54
  - lib/mailtime/engine.rb
51
- - lib/mailtime/mail_log_concern.rb
52
- - lib/mailtime/mail_template_concern.rb
55
+ - lib/mailtime/loggable.rb
53
56
  - lib/mailtime/models.rb
54
- - lib/mailtime/processor.rb
57
+ - lib/mailtime/processor/mail_log_service.rb
58
+ - lib/mailtime/processor/processor.rb
55
59
  - lib/mailtime/railtie.rb
56
60
  - lib/mailtime/renderers/base_renderer.rb
61
+ - lib/mailtime/renderers/erb_renderer.rb
57
62
  - lib/mailtime/version.rb
58
63
  - mailtime.gemspec
59
64
  homepage: https://github.com/joshmn/mailtime
@@ -1,39 +0,0 @@
1
- require 'mailtime/models'
2
- require 'mailtime/renderers/base_renderer'
3
-
4
- module Mailtime
5
- class Processor
6
-
7
- def initialize(mail)
8
- @mail = mail
9
- end
10
-
11
- def execute
12
- create_mail_log if Mailtime.configuration.log
13
- if Mailtime.configuration.render
14
- renderer = Mailtime.configuration.renderer.to_s.constantize.new(@mail)
15
- @mail.body = renderer.send(Mailtime.configuration.renderer_method)
16
- end
17
- @mail
18
- end
19
-
20
- protected
21
-
22
- def create_mail_log
23
- ::Mailtime::MailLog.create(
24
- :user => User.find_by(:email =>@mail.to.first),
25
- :mailtime_mail_template_id => @template.try(:id),
26
- :action => @mail.mailer_action,
27
- :klass => @mail.mailer_klass,
28
- :headers => extract_headers
29
- )
30
- end
31
-
32
- def extract_headers
33
- headers = {}
34
- @mail.header_fields.map { |k| headers[k.name] = k.value }
35
- headers
36
- end
37
-
38
- end
39
- end