rails_mail 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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