rails_mail 0.9.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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +170 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/images/rails_mail/rails-mail.png +0 -0
  6. data/app/channels/rails_mail/application_cable/channel.rb +6 -0
  7. data/app/channels/rails_mail/application_cable/connection.rb +17 -0
  8. data/app/channels/rails_mail/emails_channel.rb +7 -0
  9. data/app/controllers/rails_mail/base_controller.rb +13 -0
  10. data/app/controllers/rails_mail/emails_controller.rb +40 -0
  11. data/app/controllers/rails_mail/frontends_controller.rb +51 -0
  12. data/app/frontend/rails_mail/application.js +15 -0
  13. data/app/frontend/rails_mail/modules/auto_submit.js +30 -0
  14. data/app/frontend/rails_mail/modules/consumer.js +3 -0
  15. data/app/frontend/rails_mail/modules/email_highlight_controller.js +48 -0
  16. data/app/frontend/rails_mail/modules/emails_channel.js +12 -0
  17. data/app/frontend/rails_mail/modules/timeago.js +41 -0
  18. data/app/frontend/rails_mail/style.css +0 -0
  19. data/app/frontend/rails_mail/vendor/action_cable.js +512 -0
  20. data/app/frontend/rails_mail/vendor/date-fns.js +8 -0
  21. data/app/frontend/rails_mail/vendor/stimulus.js +2408 -0
  22. data/app/frontend/rails_mail/vendor/tailwind/tailwind.min.js +83 -0
  23. data/app/frontend/rails_mail/vendor/turbo.js +6697 -0
  24. data/app/helpers/rails_mail/emails_helper.rb +4 -0
  25. data/app/helpers/rails_mail/turbo_helper.rb +41 -0
  26. data/app/jobs/rails_mail/trim_emails_job.rb +40 -0
  27. data/app/models/rails_mail/email.rb +45 -0
  28. data/app/views/layouts/rails_mail/application.html.erb +72 -0
  29. data/app/views/rails_mail/emails/_email.html.erb +7 -0
  30. data/app/views/rails_mail/emails/_empty_state.html.erb +5 -0
  31. data/app/views/rails_mail/emails/_form.html.erb +22 -0
  32. data/app/views/rails_mail/emails/_show.html.erb +50 -0
  33. data/app/views/rails_mail/emails/edit.html.erb +12 -0
  34. data/app/views/rails_mail/emails/index.html.erb +16 -0
  35. data/app/views/rails_mail/emails/index.turbo_stream.erb +7 -0
  36. data/app/views/rails_mail/emails/new.html.erb +11 -0
  37. data/app/views/rails_mail/emails/show.html.erb +43 -0
  38. data/app/views/rails_mail/shared/_email.html.erb +25 -0
  39. data/app/views/rails_mail/shared/_email_sidebar.html.erb +9 -0
  40. data/config/routes.rb +13 -0
  41. data/db/migrate/20250118201227_create_rails_mail_emails.rb +9 -0
  42. data/lib/generators/rails_mail/install/install_generator.rb +15 -0
  43. data/lib/generators/rails_mail/install/templates/initializer.rb +23 -0
  44. data/lib/rails_mail/configuration.rb +35 -0
  45. data/lib/rails_mail/delivery_method.rb +22 -0
  46. data/lib/rails_mail/engine.rb +11 -0
  47. data/lib/rails_mail/version.rb +3 -0
  48. data/lib/rails_mail.rb +40 -0
  49. data/lib/tasks/rails_mail_tasks.rake +10 -0
  50. metadata +132 -0
@@ -0,0 +1,4 @@
1
+ module RailsMail
2
+ module EmailsHelper
3
+ end
4
+ end
@@ -0,0 +1,41 @@
1
+ module RailsMail
2
+ module TurboHelper
3
+ def turbo_frame_tag(name, src: nil, &block)
4
+ tag.turbo_frame id: name, src: src do
5
+ if block_given?
6
+ yield
7
+ end
8
+ end
9
+ end
10
+
11
+ def turbo_stream_from(*streamables, **attributes)
12
+ return unless defined?(::ActionCable)
13
+
14
+ raise ArgumentError, "streamables can't be blank" unless streamables.any?(&:present?)
15
+ attributes[:channel] = attributes[:channel]&.to_s || "Turbo::StreamsChannel"
16
+ # attributes[:"signed-stream-name"] = Turbo::StreamsChannel.signed_stream_name(streamables)
17
+
18
+ tag.turbo_cable_stream_source(**attributes)
19
+ end
20
+
21
+ def turbo_stream
22
+ TurboStreamBuilder.new
23
+ end
24
+
25
+ class TurboStreamBuilder
26
+ def append(target:, content:)
27
+ build_stream("append", target, content)
28
+ end
29
+
30
+ def prepend(target:, content:)
31
+ build_stream("prepend", target, content)
32
+ end
33
+
34
+ private
35
+
36
+ def build_stream(action, target, content)
37
+ %(<turbo-stream action="#{action}" target="#{target}"><template>#{content}</template></turbo-stream>)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ module RailsMail
2
+ class TrimEmailsJob < ApplicationJob
3
+ queue_as :default
4
+
5
+ def perform
6
+ return unless should_trim?
7
+
8
+ if RailsMail.configuration.trim_emails_older_than
9
+ trim_by_age
10
+ end
11
+
12
+ if RailsMail.configuration.trim_emails_max_count
13
+ trim_by_count
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def should_trim?
20
+ RailsMail.configuration.trim_emails_older_than.present? ||
21
+ RailsMail.configuration.trim_emails_max_count.present?
22
+ end
23
+
24
+ def trim_by_age
25
+ cutoff_date = RailsMail.configuration.trim_emails_older_than.ago
26
+ RailsMail::Email.where("created_at < ?", cutoff_date).destroy_all
27
+ end
28
+
29
+ def trim_by_count
30
+ max_count = RailsMail.configuration.trim_emails_max_count
31
+ return unless max_count.positive?
32
+
33
+ emails_to_delete = RailsMail::Email
34
+ .order(created_at: :desc)
35
+ .offset(max_count)
36
+
37
+ emails_to_delete.destroy_all
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,45 @@
1
+ module RailsMail
2
+ class Email < ApplicationRecord
3
+ store_accessor :data, :from, :to, :cc, :bcc, :subject, :body, :content_type, :attachments
4
+
5
+ validates :from, presence: true
6
+ validates :to, presence: true
7
+
8
+ after_create_commit :broadcast_email
9
+ after_create :schedule_trim_job
10
+
11
+ scope :search, ->(query) {
12
+ where("CAST(data AS CHAR) LIKE :q", q: "%#{query}%")
13
+ }
14
+
15
+ def text?
16
+ content_type&.include?("text/plain")
17
+ end
18
+
19
+ def html?
20
+ content_type&.include?("text/html")
21
+ end
22
+ private
23
+
24
+ def broadcast_email
25
+ return unless defined?(::Turbo) && defined?(::ActionCable)
26
+
27
+ ::Turbo::StreamsChannel.broadcast_prepend_to(
28
+ "rails_mail:emails",
29
+ target: "email-sidebar",
30
+ partial: "rails_mail/shared/email",
31
+ locals: { email: self }
32
+ )
33
+ rescue NameError => e
34
+ Rails.logger.debug "Skipping broadcast: #{e.message}"
35
+ end
36
+
37
+ def schedule_trim_job
38
+ if RailsMail.configuration.sync_via == :now
39
+ RailsMail::TrimEmailsJob.perform_now
40
+ else
41
+ RailsMail::TrimEmailsJob.perform_later
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,72 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>RailsMail - <%= yield(:title) if content_for?(:title) %></title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <%= action_cable_meta_tag if defined?(::ActionCable) %>
7
+ <%= csrf_meta_tags %>
8
+ <%= csp_meta_tag %>
9
+ <%= tag.link rel: "stylesheet", href: frontend_static_path(:style, format: :css, v: RailsMail::VERSION, locale: nil), nonce: content_security_policy_nonce %>
10
+
11
+ <%= tag.script "", src: frontend_static_path(:tailwind, format: :js, v: RailsMail::VERSION, locale: nil), nonce: content_security_policy_nonce %>
12
+
13
+ <% importmaps = RailsMail::FrontendsController.js_modules.keys.index_with { |module_name| frontend_module_path(module_name, format: :js, locale: nil) } %>
14
+ <%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
15
+ <%= tag.script "", type: "module", nonce: content_security_policy_nonce do %> import "application"; <% end %>
16
+
17
+ </head>
18
+
19
+ <body class="bg-gray-100">
20
+ <div class="flex h-screen">
21
+ <!-- Sidebar -->
22
+ <div class="w-80 bg-white border-r border-gray-200 flex flex-col">
23
+ <div class="flex-none p-4">
24
+ <div class="flex items-center justify-between">
25
+ <div class="flex items-center">
26
+ <%= link_to root_path, class: "text-gray-900" do %>
27
+ <h1 class="text-xl font-bold">Rails Mail</h1>
28
+ <% end %>
29
+ </div>
30
+
31
+ <% if instance_eval(&RailsMail.show_clear_button?) %>
32
+ <%= button_to destroy_all_emails_path,
33
+ class: "text-sm text-gray-600 hover:text-gray-900",
34
+ form: { data: { turbo_confirm: "Are you sure you want to clear all emails?" } },
35
+ method: :delete do %>
36
+ <div class="flex items-center">
37
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
38
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
39
+ </svg>
40
+ Clear All
41
+ </div>
42
+ <% end %>
43
+ <% end %>
44
+ </div>
45
+ </div>
46
+
47
+ <div id="search-form" class="flex-none px-2 pb-2">
48
+ <%= form_with url: rails_mail.emails_path, method: :get, data: { turbo_stream: true, turbo_frame: "emails_list",controller: 'auto-submit' } do |f| %>
49
+ <%= f.search_field :q,
50
+ value: params[:q],
51
+ class: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500",
52
+ placeholder: "Search emails...",
53
+ data: { action: 'keyup->auto-submit#submit search->auto-submit#submit' } %>
54
+ <% end %>
55
+ </div>
56
+
57
+ <nav class="flex-1 overflow-y-auto px-2">
58
+ <%= render "rails_mail/shared/email_sidebar" %>
59
+ </nav>
60
+ </div>
61
+
62
+ <!-- Main content -->
63
+ <div class="flex-1 overflow-y-auto">
64
+ <div class="p-8">
65
+ <turbo-frame id="email_content">
66
+ <%= yield %>
67
+ </turbo-frame>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </body>
72
+ </html>
@@ -0,0 +1,7 @@
1
+ <div id="<%= dom_id email %>">
2
+ <p>
3
+ <strong>Data:</strong>
4
+ <%= email.data %>
5
+ </p>
6
+
7
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="flex flex-col items-center justify-center h-screen">
2
+ <h1 class="text-2xl font-bold mb-4">No emails at the moment</h1>
3
+ <h2 class="text-xl mb-16">Once emails are sent from your app, they will appear here</h2>
4
+ <%= image_tag "rails_mail/rails-mail.png", class: "mb-16" %>
5
+ </div>
@@ -0,0 +1,22 @@
1
+ <%= form_with(model: email) do |form| %>
2
+ <% if email.errors.any? %>
3
+ <div style="color: red">
4
+ <h2><%= pluralize(email.errors.count, "error") %> prohibited this email from being saved:</h2>
5
+
6
+ <ul>
7
+ <% email.errors.each do |error| %>
8
+ <li><%= error.full_message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div>
15
+ <%= form.label :data, style: "display: block" %>
16
+ <%= form.text_field :data %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= form.submit %>
21
+ </div>
22
+ <% end %>
@@ -0,0 +1,50 @@
1
+ <%= turbo_frame_tag "email_content" do %>
2
+ <div class="bg-white shadow rounded-lg" data-email-id="<%= email.id %>"
3
+ data-email-highlight-current-id-value="<%= email.id %>">
4
+ <div class="px-6 py-4 border-b border-gray-200">
5
+ <h1 class="text-2xl font-semibold text-gray-900"><%= email.subject %></h1>
6
+ <div class="mt-2 text-sm text-gray-600">
7
+ <div>From: <%= email.from %></div>
8
+ <div>To: <%= email.to.join(", ") %></div>
9
+ <% if email.cc.present? %>
10
+ <div>CC: <%= email.cc.join(", ") %></div>
11
+ <% end %>
12
+ <% if email.bcc.present? %>
13
+ <div>BCC: <%= email.bcc.join(", ") %></div>
14
+ <% end %>
15
+ <div class="text-gray-400 mt-1">
16
+ <%= email.created_at.strftime("%B %d, %Y at %I:%M %p") %>
17
+ </div>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="px-6 py-4">
22
+ <% if email.attachments.present? %>
23
+ <div class="mb-4 p-4 bg-gray-50 rounded-md">
24
+ <h3 class="text-sm font-medium text-gray-900 mb-2">Attachments</h3>
25
+ <div class="space-y-2">
26
+ <% email.attachments.each do |attachment| %>
27
+ <div class="flex items-center text-sm">
28
+ <svg class="h-5 w-5 text-gray-400 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
29
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" />
30
+ </svg>
31
+ <span><%= attachment["filename"] %></span>
32
+ <span class="ml-2 text-gray-500">(<%= attachment["content_type"] %>)</span>
33
+ </div>
34
+ <% end %>
35
+ </div>
36
+ </div>
37
+ <% end %>
38
+
39
+ <div class="prose max-w-none">
40
+ <% if email.html? %>
41
+ <%= sanitize(email.body,
42
+ tags: ActionView::Base.sanitized_allowed_tags + ['table', 'tbody', 'tr', 'td'],
43
+ attributes: ActionView::Base.sanitized_allowed_attributes + ['style']) %>
44
+ <% else %>
45
+ <%= simple_format email.body %>
46
+ <% end %>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <% content_for :title, "Editing email" %>
2
+
3
+ <h1>Editing email</h1>
4
+
5
+ <%= render "form", email: @email %>
6
+
7
+ <br>
8
+
9
+ <div>
10
+ <%= link_to "Show this email", @email %> |
11
+ <%= link_to "Back to emails", emails_path %>
12
+ </div>
@@ -0,0 +1,16 @@
1
+ <% if @email.present? %>
2
+ <%= turbo_frame_tag "email_content", src: email_path(@email) do %>
3
+ <div class="bg-white shadow rounded-lg">
4
+ <div class="px-6 py-4">
5
+ <div class="animate-pulse">
6
+ <div class="h-4 bg-gray-200 rounded w-3/4 mb-4"></div>
7
+ <div class="h-3 bg-gray-200 rounded w-1/2 mb-2"></div>
8
+ <div class="h-3 bg-gray-200 rounded w-1/3"></div>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ <% end %>
13
+ <% else %>
14
+ <%= render "rails_mail/emails/empty_state" %>
15
+ <% end %>
16
+
@@ -0,0 +1,7 @@
1
+ <turbo-stream action="update" target="email-sidebar">
2
+ <template>
3
+ <% @emails&.each do |email| %>
4
+ <%= render partial: "rails_mail/shared/email", locals: {email: email}, formats: [:html] %>
5
+ <% end %>
6
+ </template>
7
+ </turbo-stream>
@@ -0,0 +1,11 @@
1
+ <% content_for :title, "New email" %>
2
+
3
+ <h1>New email</h1>
4
+
5
+ <%= render "form", email: @email %>
6
+
7
+ <br>
8
+
9
+ <div>
10
+ <%= link_to "Back to emails", emails_path %>
11
+ </div>
@@ -0,0 +1,43 @@
1
+ <%= turbo_frame_tag "email_content" do %>
2
+ <div class="bg-white shadow rounded-lg">
3
+ <div class="px-6 py-4 border-b border-gray-200">
4
+ <h1 class="text-2xl font-semibold text-gray-900"><%= @email.subject %></h1>
5
+ <div class="mt-2 text-sm text-gray-600">
6
+ <div>From: <%= @email.from %></div>
7
+ <div>To: <%= @email.to.join(", ") %></div>
8
+ <% if @email.cc.present? %>
9
+ <div>CC: <%= @email.cc.join(", ") %></div>
10
+ <% end %>
11
+ <% if @email.bcc.present? %>
12
+ <div>BCC: <%= @email.bcc.join(", ") %></div>
13
+ <% end %>
14
+ <div class="text-gray-400 mt-1">
15
+ <%= @email.created_at.strftime("%B %d, %Y at %I:%M %p") %>
16
+ </div>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="px-6 py-4">
21
+ <% if @email.attachments.present? %>
22
+ <div class="mb-4 p-4 bg-gray-50 rounded-md">
23
+ <h3 class="text-sm font-medium text-gray-900 mb-2">Attachments</h3>
24
+ <div class="space-y-2">
25
+ <% @email.attachments.each do |attachment| %>
26
+ <div class="flex items-center text-sm">
27
+ <svg class="h-5 w-5 text-gray-400 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
28
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" />
29
+ </svg>
30
+ <span><%= attachment["filename"] %></span>
31
+ <span class="ml-2 text-gray-500">(<%= attachment["content_type"] %>)</span>
32
+ </div>
33
+ <% end %>
34
+ </div>
35
+ </div>
36
+ <% end %>
37
+
38
+ <div class="prose max-w-none">
39
+ <%= simple_format @email.body %>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <%
2
+ on_current_page = current_page?(RailsMail::Engine.routes.url_helpers.email_path(email))
3
+ highlight_class = on_current_page ? 'bg-gray-100' : ''
4
+ %>
5
+ <%= link_to RailsMail::Engine.routes.url_helpers.email_path(email),
6
+ class: "block px-3 py-2 rounded-md hover:bg-gray-50 #{highlight_class} active:bg-gray-100 focus:bg-gray-100",
7
+ data: {
8
+ "turbo-frame": "email_content",
9
+ "turbo-action": "advance",
10
+ "turbo-prefetch": "false",
11
+ "email-highlight-target": "link",
12
+ "email-id": email.id,
13
+ action: "click->email-highlight#highlight"
14
+ } do %>
15
+ <div class="text-sm font-medium text-gray-900 truncate"><%= email.subject %></div>
16
+ <div class="text-xs text-gray-500">
17
+ <span class="truncate"><%= email.from %></span>
18
+ <time
19
+ data-controller="timeago"
20
+ data-timeago-datetime-value="<%= email.created_at.iso8601 %>"
21
+ data-timeago-refresh-interval-value="1000"
22
+ data-timeago-include-seconds-value="true"
23
+ class="ml-2 text-gray-400"><%= time_ago_in_words(email.created_at) %> ago</time>
24
+ </div>
25
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <% if defined?(::ActionCable) %>
2
+ <%= turbo_stream_from "rails_mail:emails", channel: "RailsMail::EmailsChannel" %>
3
+ <% end %>
4
+
5
+ <div id="email-sidebar" data-controller="email-highlight">
6
+ <% @emails&.each do |email| %>
7
+ <%= render "rails_mail/shared/email", email: email %>
8
+ <% end %>
9
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ RailsMail::Engine.routes.draw do
2
+ scope :frontend, controller: :frontends do
3
+ get "modules/*name", action: :module, as: :frontend_module, constraints: { format: "js" }
4
+ get "static/:name", action: :static, as: :frontend_static, constraints: { format: %w[css js] }
5
+ end
6
+
7
+ resources :emails, only: [ :index, :show ] do
8
+ collection do
9
+ delete "emails/destroy_all", to: "emails#destroy_all", as: :destroy_all
10
+ end
11
+ end
12
+ root to: "emails#index"
13
+ end
@@ -0,0 +1,9 @@
1
+ class CreateRailsMailEmails < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :rails_mail_emails do |t|
4
+ t.json :data
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module RailsMail
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def copy_migration
7
+ rake "rails_mail:install:migrations"
8
+ end
9
+
10
+ def copy_initializer
11
+ template "initializer.rb", "config/initializers/rails_mail.rb"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ # rubocop:disable Layout/CommentIndentation
2
+
3
+ RailsMail.configure do |config|
4
+ # Authentication setup
5
+ # if left blank, authentication is skipped
6
+ # config.authentication_callback do
7
+ # Example implementation for Authlogic:
8
+ # user_session = UserSession.find
9
+ # msg = Rails.env.development? ? 'Forbidden - make sure you have the correct permission in config/initializers/rails_mail.rb' : 'Not Found'
10
+ # raise ActionController::RoutingError.new(msg) unless user_session&.user&.admin?
11
+ # end
12
+
13
+ config.show_clear_button do
14
+ # show clear button in development
15
+ # and prefer to trim in non-development environments
16
+ # to prevent accidental deletion of emails
17
+ Rails.env.development?
18
+ end
19
+
20
+ config.trim_emails_older_than = 30.days
21
+ config.trim_emails_max_count = 1000
22
+ config.sync_via = :later
23
+ end
@@ -0,0 +1,35 @@
1
+ module RailsMail
2
+ class Configuration
3
+ attr_accessor :authentication_callback, :show_clear_button,
4
+ :trim_emails_older_than, :trim_emails_max_count, :sync_via
5
+
6
+ def initialize
7
+ @authentication_callback = nil
8
+ @show_clear_button = nil
9
+ @trim_emails_older_than = nil
10
+ @trim_emails_max_count = nil
11
+ @sync_via = :later
12
+ end
13
+
14
+ def sync_via=(value)
15
+ unless [ :now, :later ].include?(value)
16
+ raise ArgumentError, "sync_via must be :now or :later"
17
+ end
18
+ @sync_via = value
19
+ end
20
+
21
+ def authentication_callback=(callback)
22
+ unless callback.nil? || callback.respond_to?(:call)
23
+ raise ArgumentError, "authentication_callback must be nil or respond to #call"
24
+ end
25
+ @authentication_callback = callback
26
+ end
27
+
28
+ def show_clear_button=(callback)
29
+ unless callback.nil? || callback.respond_to?(:call)
30
+ raise ArgumentError, "show_clear_button must be nil or respond to #call"
31
+ end
32
+ @show_clear_button = callback
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ module RailsMail
2
+ class DeliveryMethod
3
+ def initialize(options = {})
4
+ end
5
+
6
+ def deliver!(mail)
7
+ RailsMail::Email.create!(
8
+ from: mail.from&.first,
9
+ to: mail.to,
10
+ cc: mail.cc,
11
+ bcc: mail.bcc,
12
+ subject: mail.subject,
13
+ body: mail.body.to_s,
14
+ content_type: mail.content_type,
15
+ attachments: mail.attachments.map { |a| { filename: a.filename, content_type: a.content_type.split(";").first } }
16
+ )
17
+ rescue => e
18
+ Rails.logger.error("Failed to deliver email: #{e.message}")
19
+ raise e
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module RailsMail
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsMail
4
+
5
+ initializer "rails_mail.add_delivery_method" do
6
+ ActiveSupport.on_load :action_mailer do
7
+ ActionMailer::Base.add_delivery_method :rails_mail, RailsMail::DeliveryMethod
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module RailsMail
2
+ VERSION = "0.9.0"
3
+ end
data/lib/rails_mail.rb ADDED
@@ -0,0 +1,40 @@
1
+ require "rails_mail/version"
2
+ require "rails_mail/engine"
3
+ require "rails_mail/delivery_method"
4
+ require "rails_mail/configuration"
5
+
6
+ module RailsMail
7
+ class << self
8
+ def configure
9
+ yield(configuration)
10
+ end
11
+
12
+ def configuration
13
+ @configuration ||= Configuration.new
14
+ end
15
+
16
+ def reset_configuration!
17
+ @configuration = Configuration.new
18
+ end
19
+
20
+ def authentication_callback(&block)
21
+ @authentication_callback = block if block
22
+ @authentication_callback || ->(request) { true }
23
+ end
24
+
25
+ def show_clear_button?(&block)
26
+ @show_clear_button = block if block
27
+ @show_clear_button || ->(request) { true }
28
+ end
29
+ end
30
+
31
+ # class Configuration
32
+ # def authentication_callback(&block)
33
+ # RailsMail.authentication_callback(&block)
34
+ # end
35
+
36
+ # def show_clear_button(&block)
37
+ # RailsMail.show_clear_button?(&block)
38
+ # end
39
+ # end
40
+ end
@@ -0,0 +1,10 @@
1
+ desc "Copy over the migration and initializer RailsMail"
2
+ namespace :rails_mail do
3
+ task :install do
4
+ Rails::Command.invoke :generate, [ "rails_mail:install" ]
5
+ end
6
+
7
+ # task :update do
8
+ # Rails::Command.invoke :generate, [ "rails_mail:update" ]
9
+ # end
10
+ end