mailtime 0.1.0 → 0.4.0

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.
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