├── test ├── dummy │ ├── log │ │ └── .keep │ ├── app │ │ ├── mailers │ │ │ ├── .keep │ │ │ └── user_mailer.rb │ │ ├── models │ │ │ ├── .keep │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── account.rb │ │ │ ├── account_user.rb │ │ │ └── user.rb │ │ ├── assets │ │ │ ├── images │ │ │ │ └── .keep │ │ │ ├── javascripts │ │ │ │ └── application.js │ │ │ └── stylesheets │ │ │ │ └── application.css │ │ ├── controllers │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── application_controller.rb │ │ ├── views │ │ │ ├── user_mailer │ │ │ │ └── welcome.html.erb │ │ │ └── layouts │ │ │ │ └── application.html.erb │ │ └── helpers │ │ │ └── application_helper.rb │ ├── lib │ │ └── assets │ │ │ └── .keep │ ├── public │ │ ├── favicon.ico │ │ ├── 500.html │ │ ├── 422.html │ │ └── 404.html │ ├── bin │ │ ├── rake │ │ ├── bundle │ │ ├── rails │ │ └── setup │ ├── config │ │ ├── routes.rb │ │ ├── initializers │ │ │ ├── cookies_serializer.rb │ │ │ ├── session_store.rb │ │ │ ├── mime_types.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── assets.rb │ │ │ ├── to_time_preserves_timezone.rb │ │ │ ├── wrap_parameters.rb │ │ │ └── inflections.rb │ │ ├── environment.rb │ │ ├── boot.rb │ │ ├── database.yml │ │ ├── locales │ │ │ └── en.yml │ │ ├── secrets.yml │ │ ├── application.rb │ │ └── environments │ │ │ ├── development.rb │ │ │ ├── test.rb │ │ │ └── production.rb │ ├── config.ru │ ├── Rakefile │ ├── README.rdoc │ └── db │ │ └── schema.rb ├── models │ └── mailtime │ │ ├── log_test.rb │ │ ├── layout_test.rb │ │ └── template_test.rb ├── mailtime_test.rb ├── mailtime │ ├── mailtimer_test.rb │ ├── log_test.rb │ └── action_mailer │ │ └── metadata_test.rb └── test_helper.rb ├── lib ├── mailtime │ ├── version.rb │ ├── action_mailer │ │ ├── skip_mailtime_for.rb │ │ └── metadata.rb │ ├── interceptor.rb │ ├── engine.rb │ ├── mailtimer.rb │ ├── renderer.rb │ ├── processor.rb │ └── metadata_builder.rb ├── generators │ └── mailtime │ │ ├── install │ │ ├── templates │ │ │ └── mailtime.rb │ │ └── install_generator.rb │ │ └── populate │ │ └── populate_generator.rb └── mailtime.rb ├── .gitignore ├── app └── models │ └── mailtime │ ├── layout.rb │ ├── template.rb │ └── log.rb ├── bin └── rails ├── Gemfile ├── Rakefile ├── mailtime.gemspec ├── db └── migrate │ └── 20171107210800_create_mailtime_tables.rb ├── MIT-LICENSE ├── Gemfile.lock └── README.md /test/dummy/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/views/user_mailer/welcome.html.erb: -------------------------------------------------------------------------------- 1 | welcome to mailtime -------------------------------------------------------------------------------- /lib/mailtime/version.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | VERSION = "0.0.1" 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/models/account.rb: -------------------------------------------------------------------------------- 1 | class Account < ActiveRecord::Base 2 | 3 | 4 | end -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | mount Mailtime::Engine => "/mailtime" 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/app/models/account_user.rb: -------------------------------------------------------------------------------- 1 | class AccountUser < ActiveRecord::Base 2 | 3 | belongs_to :account 4 | belongs_to :user 5 | 6 | end -------------------------------------------------------------------------------- /lib/generators/mailtime/install/templates/mailtime.rb: -------------------------------------------------------------------------------- 1 | Mailtime.configure do |config| 2 | # 3 | # config.renderer = ::Mailtime::Renderer 4 | # 5 | end -------------------------------------------------------------------------------- /test/models/mailtime/log_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Mailtime 4 | class LogTest < ActiveSupport::TestCase 5 | 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | test/dummy/db/*.sqlite3 5 | test/dummy/db/*.sqlite3-journal 6 | test/dummy/log/*.log 7 | test/dummy/tmp/ 8 | test/dummy/.sass-cache 9 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /test/mailtime_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MailtimeTest < ActiveSupport::TestCase 4 | test 'truth' do 5 | assert_kind_of Module, Mailtime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/mailtime/layout.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | class Layout < ActiveRecord::Base 3 | 4 | has_many :templates, :class_name => '::Mailtime::Template' 5 | 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/mailtime/template.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | class Template < ActiveRecord::Base 3 | 4 | belongs_to :layout, :class_name => '::Mailtime::Layout' 5 | 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_dummy_session' 4 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /lib/mailtime/action_mailer/skip_mailtime_for.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | module ActionMailer 3 | module SkipMailtimeFor 4 | cattr_accessor(:skip_mailtime_for_methods) { [] } 5 | end 6 | end 7 | end -------------------------------------------------------------------------------- /test/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /test/dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | 3 | mailtimer :email 4 | 5 | after_commit :send_mail 6 | 7 | def send_mail 8 | UserMailer.welcome(self).deliver_now! 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/models/mailtime/log.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | class Log < ActiveRecord::Base 3 | 4 | alias_attribute :mailer_action, :mailer_method 5 | 6 | belongs_to :thing, :polymorphic => true 7 | 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/models/mailtime/layout_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Mailtime 4 | class LayoutTest < ActiveSupport::TestCase 5 | # test "the truth" do 6 | # assert true 7 | # end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/models/mailtime/template_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Mailtime 4 | class TemplateTest < ActiveSupport::TestCase 5 | # test "the truth" do 6 | # assert true 7 | # end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | 3 | def welcome(user) 4 | @user = user 5 | mail(:to => @user.email, :subject => "Welcome", :from => "hello@mailtime.test") 6 | end 7 | 8 | end 9 | 10 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) 6 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /lib/mailtime/interceptor.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Intercepts from ActionMailer::Base upon delivery 3 | # 4 | 5 | module Mailtime 6 | class Interceptor 7 | def self.delivering_email(mail) 8 | processor = Mailtime::Processor.new(mail) 9 | processor.execute 10 | processor.mail 11 | end 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/mailtime/mailtimer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Mailtime 4 | class MailtimerTest < ActiveSupport::TestCase 5 | 6 | setup do 7 | @mailtimer_klass = User 8 | @not_mailtimer_klass = Account 9 | end 10 | 11 | test "responds to mailtimer" do 12 | assert @mailtimer_klass.mailtimer? 13 | assert !@not_mailtimer_klass.mailtimer? 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /lib/generators/mailtime/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/active_record' 2 | 3 | module Mailtime 4 | module Generators 5 | class InstallGenerator < Rails::Generators::Base 6 | 7 | desc "Installs Mailtime by copying over migrations and an initializer" 8 | source_root File.expand_path("../templates", __FILE__) 9 | 10 | def copy_initializer 11 | copy_file 'mailtime.rb', 'config/initializers/mailtime.rb' 12 | end 13 | 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application. 3 | 4 | ENGINE_ROOT = File.expand_path('../..', __FILE__) 5 | ENGINE_PATH = File.expand_path('../../lib/mailtime/engine', __FILE__) 6 | 7 | # Set up gems listed in the Gemfile. 8 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 9 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 10 | 11 | require 'rails/all' 12 | require 'rails/engine/commands' 13 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/to_time_preserves_timezone.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Preserve the timezone of the receiver when calling to `to_time`. 4 | # Ruby 2.4 will change the behavior of `to_time` to preserve the timezone 5 | # when converting to an instance of `Time` instead of the previous behavior 6 | # of converting to the local system timezone. 7 | # 8 | # Rails 5.0 introduced this config option so that apps made with earlier 9 | # versions of Rails are not affected when upgrading. 10 | ActiveSupport.to_time_preserves_timezone = true 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /test/dummy/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /test/mailtime/log_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Mailtime 4 | class LogTest < ActiveSupport::TestCase 5 | 6 | setup do 7 | @user = User.new(:email => "john@mailtime.dev", :name => "John") 8 | @user.save! 9 | @user.send_mail 10 | end 11 | 12 | test "user is present" do 13 | assert @user.present? 14 | end 15 | 16 | test "created a mailtime log" do 17 | assert_equal 1, Mailtime::Log.count 18 | end 19 | 20 | test "mailtime log has correct user_id" do 21 | assert_equal Mailtime::Log.last.user_id, @user.id 22 | end 23 | 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/mailtime/action_mailer/metadata.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | module ActionMailer 3 | module Metadata 4 | 5 | def self.included(base) 6 | base.include AbstractController::Callbacks 7 | base.after_action :inject_mailtime_metadata 8 | end 9 | 10 | private 11 | 12 | def mailtime_metadata 13 | @mailtime_metadata ||= Mailtime::MetadataBuilder.new(self) 14 | end 15 | 16 | def inject_mailtime_metadata 17 | self.message.instance_variable_set(:@mailtime_metadata, mailtime_metadata) 18 | self.message.class.send(:attr_reader, :mailtime_metadata) 19 | end 20 | 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /lib/mailtime.rb: -------------------------------------------------------------------------------- 1 | require 'mailtime/mailtimer' 2 | require 'mailtime/action_mailer/skip_mailtime_for' 3 | require 'mailtime/action_mailer/metadata' 4 | require 'mailtime/engine' 5 | 6 | require 'mailtime/renderer' 7 | require 'mailtime/processor' 8 | require 'mailtime/interceptor' 9 | require 'mailtime/metadata_builder' 10 | 11 | module Mailtime 12 | class << self 13 | attr_accessor :configuration 14 | end 15 | 16 | def self.configure 17 | self.configuration ||= Configuration.new 18 | yield(configuration) 19 | end 20 | 21 | class Configuration 22 | attr_accessor :renderer 23 | 24 | def initialize 25 | @renderer = ::Mailtime::Renderer 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/mailtime/action_mailer/metadata_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | 4 | module Mailtime 5 | module ActionMailer 6 | class MetadataTest < ActiveSupport::TestCase 7 | 8 | setup do 9 | @user = OpenStruct.new(:email => "hello@mailtime.test", :name => "Test", :id => 1) 10 | @mailer = UserMailer.welcome(@user) 11 | end 12 | 13 | test 'mailer responds to mailtime_metadata' do 14 | assert_equal 'UserMailer', @mailer.mailtime_metadata.mailer_class 15 | assert_equal 'welcome', @mailer.mailtime_metadata.mailer_action 16 | assert_equal({'user' => @user}, @mailer.mailtime_metadata.action_variables) 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Declare your gem's dependencies in mailtime.gemspec. 4 | # Bundler will treat runtime dependencies like base dependencies, and 5 | # development dependencies will be added by default to the :development group. 6 | gemspec 7 | 8 | # Declare any dependencies that are still in development here instead of in 9 | # your gemspec. These might include edge Rails or gems from your path or 10 | # Git. Remember to move these dependencies to your gemspec before releasing 11 | # your gem to rubygems.org. 12 | 13 | # To use a debugger 14 | # gem 'byebug', group: [:development, :test] 15 | 16 | gem 'pry', :group => [:development, :test] 17 | gem 'pry-rails', :group => [:development, :test] 18 | gem 'database_cleaner', :group => [:development, :test] -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /lib/mailtime/engine.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | 3 | require 'mailtime' 4 | 5 | module Mailtime 6 | class Engine < ::Rails::Engine 7 | 8 | isolate_namespace Mailtime 9 | 10 | ActiveSupport.on_load :action_mailer do 11 | ::ActionMailer::Base.send(:include, Mailtime::ActionMailer::Metadata) 12 | ::ActionMailer::Base.send(:extend, Mailtime::ActionMailer::SkipMailtimeFor) 13 | ::ActionMailer::Base.register_interceptor(::Mailtime::Interceptor) 14 | 15 | class ::ActionMailer::Base 16 | def self.skip_mailtime_for(*method_names) 17 | self.skip_mailtime_for_methods = method_names 18 | end 19 | end 20 | end 21 | 22 | ActiveSupport.on_load :active_record do 23 | ::ActiveRecord::Base.send(:include, Mailtime::Mailtimer) 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/mailtime/mailtimer.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Allows 3 | # 4 | module Mailtime 5 | module Mailtimer 6 | extend ActiveSupport::Concern 7 | 8 | module ClassMethods 9 | 10 | def mailtimer(email_attribute = :email, options = {}) 11 | has_many :mailtime_logs, :as => :thing, :class_name => "Mailtime::Log" 12 | cattr_accessor :mailtimer_email_attribute 13 | cattr_accessor :mailtimer_options 14 | self.mailtimer_email_attribute = email_attribute.to_sym 15 | self.mailtimer_options = options.symbolize_keys 16 | end 17 | 18 | def find_for_mailtimer(email) 19 | self.find_by(mailtimer_email_attribute => email) 20 | end 21 | 22 | def mailtimer? 23 | respond_to?(:mailtimer_email_attribute) 24 | end 25 | 26 | alias_method :mailtime_enabled?, :mailtimer? 27 | 28 | end 29 | end 30 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'Mailtime' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.rdoc') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) 18 | load 'rails/tasks/engine.rake' 19 | 20 | 21 | load 'rails/tasks/statistics.rake' 22 | 23 | 24 | 25 | Bundler::GemHelper.install_tasks 26 | 27 | require 'rake/testtask' 28 | 29 | Rake::TestTask.new(:test) do |t| 30 | t.libs << 'lib' 31 | t.libs << 'test' 32 | t.pattern = 'test/**/*_test.rb' 33 | t.verbose = false 34 | end 35 | 36 | 37 | task default: :test 38 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV["RAILS_ENV"] = "test" 3 | 4 | require File.expand_path("../../test/dummy/config/environment.rb", __FILE__) 5 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)] 6 | ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__) 7 | require "rails/test_help" 8 | 9 | # Filter out Minitest backtrace while allowing backtrace from other libraries 10 | # to be shown. 11 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new 12 | 13 | # Load support files 14 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } 15 | 16 | class ActiveSupport::TestCase 17 | ActiveRecord::Migration.check_pending! 18 | DatabaseCleaner.strategy = :truncation 19 | setup { DatabaseCleaner.start } 20 | teardown { DatabaseCleaner.clean } 21 | end -------------------------------------------------------------------------------- /mailtime.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | 3 | # Maintain your gem's version: 4 | require "mailtime/version" 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |s| 8 | s.name = "mailtime" 9 | s.version = Mailtime::VERSION 10 | s.authors = ["Josh Brody"] 11 | s.email = ["josh@josh.mn"] 12 | s.homepage = "" 13 | s.summary = "Summary of Mailtime." 14 | s.description = "Description of Mailtime." 15 | s.license = "MIT" 16 | 17 | s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"] 18 | s.test_files = Dir["test/**/*"] 19 | 20 | s.add_dependency "rails", "~> 4.2.9" 21 | 22 | s.add_development_dependency "sqlite3" 23 | s.add_development_dependency "pry" 24 | s.add_development_dependency "pry-rails" 25 | s.add_development_dependency "database_cleaner" 26 | end 27 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /lib/mailtime/renderer.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | class Renderer 3 | 4 | def initialize(mail) 5 | @mail = mail 6 | end 7 | 8 | # todo: clean 9 | # shoudl really return a mail#body object 10 | def render 11 | if template 12 | @mail.mailtime_metadata.action_variables.each do |k,v| 13 | instance_variable_set("@#{k}", v) 14 | end 15 | layout = ERB.new(Mailtime::Layout.find(template.layout_id).content) 16 | inner = ERB.new(template.content) 17 | 18 | result = layout.result(get_binding { 19 | inner.result(binding) 20 | }) 21 | return result 22 | end 23 | @mail.body.to_s 24 | end 25 | 26 | private 27 | 28 | def get_binding 29 | binding 30 | end 31 | 32 | def template 33 | @template ||= ::Mailtime::Template.find_by(:klass => @mail.mailtime_metadata.klass, :action => @mail.mailtime_metadata.action) 34 | end 35 | 36 | end 37 | end -------------------------------------------------------------------------------- /db/migrate/20171107210800_create_mailtime_tables.rb: -------------------------------------------------------------------------------- 1 | class CreateMailtimeTables < ActiveRecord::Migration 2 | def change 3 | create_table :mailtime_logs do |t| 4 | t.integer :thing_id 5 | t.string :thing_type 6 | t.string :mailer_class 7 | t.string :mailer_method 8 | t.json :context 9 | 10 | t.timestamps null: false 11 | end 12 | add_index :mailtime_logs, [:thing_id, :thing_type] 13 | add_index :mailtime_logs, :mailer_class 14 | add_index :mailtime_logs, :mailer_method 15 | 16 | create_table :mailtime_templates do |t| 17 | t.string :name 18 | t.string :klass 19 | t.string :action 20 | t.text :content 21 | t.integer :layout_id 22 | t.timestamps null: false 23 | end 24 | 25 | add_index :mailtime_templates, :layout_id 26 | add_index :mailtime_templates, [:klass, :action] 27 | 28 | 29 | create_table :mailtime_layouts do |t| 30 | t.string :name 31 | t.text :content 32 | 33 | t.timestamps null: false 34 | end 35 | 36 | end 37 | end -------------------------------------------------------------------------------- /test/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 3a3dc12598d29812b195ae22b1ac7b0aacd8e33494a38ac7b4aa6c479933b89ece53d4d836b2403b3a41683b0ed8c27285a2eaad62b46a608efb72d084a34197 15 | 16 | test: 17 | secret_key_base: 6788c4933c7d58a83385a336250af6e8e9c4b5d07a701c38a5c64dd3c7d6af7b9bfc9d415adc26cdca579e1a5e666a2bdbfe89bc5c02f23477f3688085095527 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Josh Brody 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | Bundler.require(*Rails.groups) 6 | require "mailtime" 7 | 8 | module Dummy 9 | class Application < Rails::Application 10 | # Settings in config/environments/* take precedence over those specified here. 11 | # Application configuration should go into files in config/initializers 12 | # -- all .rb files in that directory are automatically loaded. 13 | 14 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 15 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 16 | # config.time_zone = 'Central Time (US & Canada)' 17 | 18 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 19 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 20 | # config.i18n.default_locale = :de 21 | 22 | # Do not swallow errors in after_commit/after_rollback callbacks. 23 | config.active_record.raise_in_transactional_callbacks = true 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /lib/mailtime/processor.rb: -------------------------------------------------------------------------------- 1 | module Mailtime 2 | class Processor 3 | 4 | attr_reader :mail 5 | def initialize(mail) 6 | @mail = mail 7 | end 8 | 9 | def execute 10 | if should_be_processed? 11 | create_log 12 | render_body 13 | end 14 | self 15 | end 16 | 17 | private 18 | 19 | def should_not_be_processed? 20 | mail.mailtime_metadata.klass.constantize.skip_mailtime_for_methods.map(&:to_sym).include?(mail.mailtime_metadata.action.to_sym) 21 | end 22 | 23 | def should_be_processed? 24 | !should_not_be_processed? 25 | end 26 | 27 | def create_log 28 | Mailtime::Log.create(:thing_id => mail.mailtime_metadata.thing.id, 29 | :thing_type => mail.mailtime_metadata.thing.class.to_s, 30 | :mailer_class => mail.mailtime_metadata.klass, 31 | :mailer_action => mail.mailtime_metadata.action, 32 | :context => mail.mailtime_metadata.context) 33 | end 34 | 35 | def render_body 36 | renderer = render_klass.new(mail) 37 | @mail.body = renderer.render 38 | end 39 | 40 | def render_klass 41 | Mailtime.configuration.renderer.to_s.safe_constantize || ::Mailtime::Renderer 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /lib/generators/mailtime/populate/populate_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/active_record' 2 | 3 | module Mailtime 4 | module Generators 5 | class PopulateGenerator < Rails::Generators::Base 6 | 7 | desc "Does some hocus pocus" 8 | 9 | def create_templates 10 | Rails.application.eager_load! 11 | layout = ::Mailtime::Layout.create!(:name => "Default", :content => "<%= yield %>") 12 | ::ActionMailer::Base.descendants.each do |klass| 13 | view_paths = klass.view_paths.paths 14 | klass.instance_methods(false).each do |mthd| 15 | created = false 16 | view_paths.each do |view_path| 17 | break if created 18 | view_path.instance_variable_get(:@path) 19 | template = Mailtime::Template.find_or_initialize_by(:action => mthd, :klass => klass) 20 | if template.new_record? 21 | template.name = [klass, mthd].join("#") 22 | template.layout_id = layout.id 23 | file = File.read("#{view_path}/#{klass.to_s.underscore}/#{mthd}.html.erb") rescue nil 24 | next if file.nil? 25 | template.content = file 26 | created = template.save! 27 | end 28 | end 29 | end 30 | 31 | end 32 | end 33 | 34 | end 35 | end 36 | end -------------------------------------------------------------------------------- /lib/mailtime/metadata_builder.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Helps build the Mailtime::Log 3 | # 4 | module Mailtime 5 | class MetadataBuilder 6 | 7 | def self.for(instance) 8 | new(instance).metadata 9 | end 10 | 11 | def initialize(instance) 12 | @instance = instance 13 | end 14 | 15 | def klass 16 | @instance.class.to_s 17 | end 18 | 19 | def action 20 | @instance.action_name 21 | end 22 | 23 | def action_variables 24 | @action_variables ||= @instance.instance_variables.select { |variable| @instance.instance_variable_get(variable).class < ::ActiveRecord::Base }.each_with_object({}) do |v, h| 25 | h[v.to_s.gsub('@', '')] = @instance.instance_variable_get(v) 26 | end 27 | end 28 | 29 | def thing 30 | @thing ||= begin 31 | mailtime_enabled_models = action_variables.select { |_, v| v.class.mailtime_enabled? } 32 | key = mailtime_enabled_models.keys.detect { |k| "#{k.classify}Mailer" == klass || "#{k.classify.to_s.pluralize}Mailer" == klass } 33 | found = nil 34 | if key 35 | found = action_variables[key] 36 | end 37 | found 38 | end 39 | end 40 | 41 | def context 42 | action_variables.as_json 43 | end 44 | 45 | def mailtime_template 46 | @mailtime_template ||= Mailtime::Template.find_by(:klass => klass, :action => action) 47 | end 48 | 49 | end 50 | end -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations. 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 31 | # yet still be able to expire them through the digest params. 32 | config.assets.digest = true 33 | 34 | # Adds additional error checking when serving assets at runtime. 35 | # Checks for improperly declared sprockets dependencies. 36 | # Raises helpful error messages. 37 | config.assets.raise_runtime_errors = true 38 | 39 | # Raises error for missing translations 40 | # config.action_view.raise_on_missing_translations = true 41 | end 42 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20171107210923) do 15 | 16 | create_table "mailtime_layouts", force: :cascade do |t| 17 | t.string "name" 18 | t.text "content" 19 | t.datetime "created_at", null: false 20 | t.datetime "updated_at", null: false 21 | end 22 | 23 | create_table "mailtime_logs", force: :cascade do |t| 24 | t.integer "user_id" 25 | t.string "mailer_class" 26 | t.string "mailer_method" 27 | t.datetime "created_at", null: false 28 | t.datetime "updated_at", null: false 29 | end 30 | 31 | add_index "mailtime_logs", ["mailer_class"], name: "index_mailtime_logs_on_mailer_class" 32 | add_index "mailtime_logs", ["mailer_method"], name: "index_mailtime_logs_on_mailer_method" 33 | add_index "mailtime_logs", ["user_id"], name: "index_mailtime_logs_on_user_id" 34 | 35 | create_table "mailtime_templates", force: :cascade do |t| 36 | t.string "name" 37 | t.text "content" 38 | t.datetime "created_at", null: false 39 | t.datetime "updated_at", null: false 40 | t.integer "layout_id" 41 | end 42 | 43 | add_index "mailtime_templates", ["layout_id"], name: "index_mailtime_templates_on_layout_id" 44 | 45 | create_table "users", force: :cascade do |t| 46 | t.string "email" 47 | t.string "name" 48 | end 49 | 50 | create_table "accounts", force: :cascade do |t| 51 | t.string "name" 52 | t.string "address" 53 | end 54 | 55 | create_table "account_users", force: :cascade do |t| 56 | t.integer "account_id" 57 | t.integer "user_id" 58 | end 59 | 60 | add_index "account_users", ["account_id", "user_id"], name: "index_account_users_on_account_id_and_user_id" 61 | 62 | end 63 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | mailtime (0.0.1) 5 | rails (~> 4.2.9) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | actionmailer (4.2.10) 11 | actionpack (= 4.2.10) 12 | actionview (= 4.2.10) 13 | activejob (= 4.2.10) 14 | mail (~> 2.5, >= 2.5.4) 15 | rails-dom-testing (~> 1.0, >= 1.0.5) 16 | actionpack (4.2.10) 17 | actionview (= 4.2.10) 18 | activesupport (= 4.2.10) 19 | rack (~> 1.6) 20 | rack-test (~> 0.6.2) 21 | rails-dom-testing (~> 1.0, >= 1.0.5) 22 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 23 | actionview (4.2.10) 24 | activesupport (= 4.2.10) 25 | builder (~> 3.1) 26 | erubis (~> 2.7.0) 27 | rails-dom-testing (~> 1.0, >= 1.0.5) 28 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 29 | activejob (4.2.10) 30 | activesupport (= 4.2.10) 31 | globalid (>= 0.3.0) 32 | activemodel (4.2.10) 33 | activesupport (= 4.2.10) 34 | builder (~> 3.1) 35 | activerecord (4.2.10) 36 | activemodel (= 4.2.10) 37 | activesupport (= 4.2.10) 38 | arel (~> 6.0) 39 | activesupport (4.2.10) 40 | i18n (~> 0.7) 41 | minitest (~> 5.1) 42 | thread_safe (~> 0.3, >= 0.3.4) 43 | tzinfo (~> 1.1) 44 | arel (6.0.4) 45 | builder (3.2.3) 46 | coderay (1.1.1) 47 | concurrent-ruby (1.0.5) 48 | crass (1.0.2) 49 | database_cleaner (1.6.2) 50 | erubis (2.7.0) 51 | globalid (0.4.1) 52 | activesupport (>= 4.2.0) 53 | i18n (0.9.1) 54 | concurrent-ruby (~> 1.0) 55 | loofah (2.1.1) 56 | crass (~> 1.0.2) 57 | nokogiri (>= 1.5.9) 58 | mail (2.7.0) 59 | mini_mime (>= 0.1.1) 60 | method_source (0.8.2) 61 | mini_mime (0.1.4) 62 | mini_portile2 (2.3.0) 63 | minitest (5.10.3) 64 | nokogiri (1.8.1) 65 | mini_portile2 (~> 2.3.0) 66 | pry (0.10.4) 67 | coderay (~> 1.1.0) 68 | method_source (~> 0.8.1) 69 | slop (~> 3.4) 70 | pry-rails (0.3.5) 71 | pry (>= 0.9.10) 72 | rack (1.6.8) 73 | rack-test (0.6.3) 74 | rack (>= 1.0) 75 | rails (4.2.10) 76 | actionmailer (= 4.2.10) 77 | actionpack (= 4.2.10) 78 | actionview (= 4.2.10) 79 | activejob (= 4.2.10) 80 | activemodel (= 4.2.10) 81 | activerecord (= 4.2.10) 82 | activesupport (= 4.2.10) 83 | bundler (>= 1.3.0, < 2.0) 84 | railties (= 4.2.10) 85 | sprockets-rails 86 | rails-deprecated_sanitizer (1.0.3) 87 | activesupport (>= 4.2.0.alpha) 88 | rails-dom-testing (1.0.8) 89 | activesupport (>= 4.2.0.beta, < 5.0) 90 | nokogiri (~> 1.6) 91 | rails-deprecated_sanitizer (>= 1.0.1) 92 | rails-html-sanitizer (1.0.3) 93 | loofah (~> 2.0) 94 | railties (4.2.10) 95 | actionpack (= 4.2.10) 96 | activesupport (= 4.2.10) 97 | rake (>= 0.8.7) 98 | thor (>= 0.18.1, < 2.0) 99 | rake (12.2.1) 100 | slop (3.6.0) 101 | sprockets (3.7.1) 102 | concurrent-ruby (~> 1.0) 103 | rack (> 1, < 3) 104 | sprockets-rails (3.2.1) 105 | actionpack (>= 4.0) 106 | activesupport (>= 4.0) 107 | sprockets (>= 3.0.0) 108 | sqlite3 (1.3.13) 109 | thor (0.20.0) 110 | thread_safe (0.3.6) 111 | tzinfo (1.2.4) 112 | thread_safe (~> 0.1) 113 | 114 | PLATFORMS 115 | ruby 116 | 117 | DEPENDENCIES 118 | database_cleaner 119 | mailtime! 120 | pry 121 | pry-rails 122 | sqlite3 123 | 124 | BUNDLED WITH 125 | 1.11.2 126 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | # config.action_controller.asset_host = 'http://assets.example.com' 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 68 | # the I18n.default_locale when a translation cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Use default logging formatter so that PID and timestamp are not suppressed. 75 | config.log_formatter = ::Logger::Formatter.new 76 | 77 | # Do not dump schema after migrations. 78 | config.active_record.dump_schema_after_migration = false 79 | end 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mailtime 2 | 3 | Makes sending mail with Rails ~great again~ more bearable by allowing you to manage the mail from ActiveRecord as well as log it. 4 | 5 | ### Not using ActionMailer? 6 | 7 | No problem, but it's untested. Check out **Without ActionMailer** below. 8 | 9 | ## Mailtime... 10 | 11 | **Does** 12 | 13 | * Tells you (via ActiveRecord object) that a `thing` has been mailed, what they were mailed, and under what context 14 | * (optionally) allows you to completely manage your ActionMailer templates (and optional layouts) from ActiveRecord 15 | 16 | **Does not** 17 | 18 | * Operate as any sort of SMTP/mail protocol service/server 19 | 20 | ## Installation 21 | 22 | Add to your Gemfile: `gem 'mailtime'` 23 | 24 | Run `bundle install` 25 | 26 | Copy over migrations 27 | 28 | `$ rake mailtime:install:migrations` 29 | 30 | And migrate, 31 | 32 | `rake db:migrate` 33 | 34 | ## Usage 35 | 36 | ### ActiveRecord Objects 37 | 38 | Add `mailtimer NAME [options]` to each class that has an `email` attribute associated with it (or, rather, is emailable) 39 | 40 | * NAME is the attribute (within ActiveRecord) that holds the object's email address. Default `email` 41 | * [options] is your favorite optional hash but is mostly useless as of writing. 42 | 43 | ### In your mailer methods 44 | 45 | Mailtime serializes a collection of defined instance variables in your mailer method. Additionally, it detects the `Mailtime::Log#thing` by using Rails conventions — if there is an instance variable with a class of Person in PersonMailer/PersonsMailer/PeopleMailer, it will assign the `thing` (polymorphic) to that object. 46 | 47 | #### Skip a method 48 | 49 | `skip_mailtime_for :welcome` in your mailer class. 50 | 51 | ## How it works 52 | 53 | Mailtime hooks into `ActionMailer` with an `Interceptor`. It injects some methods into `ActionMailer::Base` which allows 54 | it to to extract some metadata to associate with the Mailtime objects: `Mailtime::Log`, `Mailtime::Template`, and 55 | `Mailtime::Layout`. 56 | 57 | Mailtime requires no additional configuration or special sending methods. It only cares that you're using ActionMailer. 58 | This could be easily changed to suit your application's needs. 59 | 60 | ### Without ActionMailer 61 | 62 | Before you attempt mail delivery, hook into `Mailtime::Interceptor` and pass the closest-thing-to-a-mail-object object. 63 | This object must respond to `mailtime_metadata` and be an object that contains `mailer_class`, `mailer_action`, and 64 | `action_variables` — a hash that represents any context (instance variables) that were used to build the mail. [Example](lib/mailtime/action_mailer/metadata.rb). 65 | 66 | Pretending closest-thing-to-a-mail-object object is called `mail_api_call` from class `MailApiCall`... 67 | 68 | ```Ruby 69 | class MailApiCall 70 | 71 | def mailtime_metadata 72 | OpenStruct.new( 73 | :mailer_class => self.class.to_s, 74 | :mailer_action => self.action_name, 75 | :action_variables => {:user => @user}, 76 | :thing => @user 77 | ) 78 | end 79 | 80 | end 81 | ``` 82 | 83 | ### Views 84 | 85 | Mailtime doesn't ship with views or authentication for said non-existent views. 86 | 87 | ## About 88 | 89 | It's very opinioniated right now. 90 | 91 | ### Inspiration 92 | 93 | * [Maily](https://github.com/markets/maily) 94 | * [Maily Herald](https://github.com/Sology/maily_herald) 95 | 96 | ### Todo 97 | 98 | * Some examples for ERB, ActiveAdmin, RailsAdmin 99 | * Allow you to mail things (e.g. User, Account) using ActiveMailer without touching code (with contexts to query things to mail) 100 | * Versioning is probably important since you can edit the mailer's contents, and Mailtime doesn't store the output 101 | * Maybe Mailtime should store the output? 102 | * ... maybe Mailtime should build and store the output when a version limit is reached for a particular mailer? 103 | * Support MySQL and things that don't support JSON columns 104 | * Mail header API or something 105 | * before/after hooks? 106 | 107 | ### Is it any good? 108 | 109 | [Yes.](https://news.ycombinator.com/item?id=3067434) 110 | --------------------------------------------------------------------------------