po_box 0.1.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +103 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/po_box_manifest.js +1 -0
  6. data/app/assets/stylesheets/po_box/application.css +15 -0
  7. data/app/controllers/po_box/application_controller.rb +4 -0
  8. data/app/controllers/po_box/emails_controller.rb +68 -0
  9. data/app/controllers/po_box/inboxes_controller.rb +54 -0
  10. data/app/helpers/po_box/application_helper.rb +4 -0
  11. data/app/helpers/po_box/emails_helper.rb +4 -0
  12. data/app/jobs/po_box/application_job.rb +4 -0
  13. data/app/mailboxes/application_mailbox.rb +2 -0
  14. data/app/mailboxes/po_box/inbox_mailbox.rb +54 -0
  15. data/app/mailers/po_box/application_mailer.rb +6 -0
  16. data/app/mailers/po_box/emailable_mailer.rb +12 -0
  17. data/app/models/concerns/po_box/emailable.rb +33 -0
  18. data/app/models/po_box/application_record.rb +5 -0
  19. data/app/models/po_box/email.rb +11 -0
  20. data/app/models/po_box/inbox.rb +11 -0
  21. data/app/views/layouts/po_box/application.html.erb +12 -0
  22. data/app/views/layouts/po_box/mailer.html.erb +12 -0
  23. data/app/views/layouts/po_box/mailer.text.erb +1 -0
  24. data/app/views/po_box/emails/_email.html.erb +49 -0
  25. data/app/views/po_box/emails/_form.html.erb +51 -0
  26. data/app/views/po_box/emails/edit.html.erb +7 -0
  27. data/app/views/po_box/emails/index.html.erb +11 -0
  28. data/app/views/po_box/emails/new.html.erb +6 -0
  29. data/app/views/po_box/emails/show.html.erb +7 -0
  30. data/app/views/po_box/home/index.html.erb +2 -0
  31. data/app/views/po_box/inboxes/_form.html.erb +19 -0
  32. data/app/views/po_box/inboxes/_inbox.html.erb +6 -0
  33. data/app/views/po_box/inboxes/edit.html.erb +7 -0
  34. data/app/views/po_box/inboxes/index.html.erb +15 -0
  35. data/app/views/po_box/inboxes/new.html.erb +6 -0
  36. data/app/views/po_box/inboxes/show.html.erb +8 -0
  37. data/config/initializers/po_box.rb +1 -0
  38. data/config/routes.rb +7 -0
  39. data/db/migrate/20230425172819_create_po_box_inboxes.rb +10 -0
  40. data/db/migrate/20230426005820_create_po_box_emails.rb +17 -0
  41. data/lib/generators/po_box/USAGE +12 -0
  42. data/lib/generators/po_box/po_box_generator.rb +33 -0
  43. data/lib/generators/po_box/templates/po_box.rb +1 -0
  44. data/lib/po_box/engine.rb +13 -0
  45. data/lib/po_box/version.rb +3 -0
  46. data/lib/po_box.rb +10 -0
  47. data/lib/tasks/po_box.rake +8 -0
  48. metadata +232 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4a96717f6fc9f8aee68cbf574b60c2da573d7693a1173279a84b16e1a1666d38
4
+ data.tar.gz: b5b7dcb9dcea500041f806517edc8ece9cdc18af18241900ce28adc43dd525de
5
+ SHA512:
6
+ metadata.gz: 5522631b60e572e57049274c7c730d257c44eb94d0ef900b528c628329dfd0ac68e0ea166be00c8773ae3c9d3c46693c6336971881205553228760259a637b9e
7
+ data.tar.gz: d6209dc03e50daa7ff937fee1ae5b39075a4a54b7215a7e80ec9290b9502864f5e1fc341e0a3585eacdfa7d9baa7de34ee8c644032a4627c337ff649ed96fede
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Leo Policastro
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.
data/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # PoBox
2
+
3
+ PoBox is a Ruby on Rails engine that provides inboxes and email addresses for your users.
4
+
5
+ ## Requirements
6
+
7
+ 1. [ActiveStorage](https://edgeguides.rubyonrails.org/active_storage_overview.html) needs to be installed and configured.
8
+ 2. [ActionMailbox](https://edgeguides.rubyonrails.org/action_mailbox_basics.html) needs to be installed and configured.
9
+ 3. A model that the inbox and emails will be associated with needs to exist in the host application. e.g. `User` or `Account`.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem "po_box"
17
+ ```
18
+
19
+ And then run:
20
+
21
+ ```bash
22
+ $ bundle
23
+ ```
24
+
25
+ Or install it yourself as:
26
+
27
+ ```bash
28
+ $ gem install po_box
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ **IMPORTANT:** Make sure you have ActiveStorage and ActionMailbox installed and configured.
34
+
35
+ 1. `bin/rails po_box:install:migrations`
36
+ 2. `bin/rails g po_box emailable_model` (where `emailable_model` is the name of the model you want to make emailable, e.g. `user`)
37
+ 3. `bin/rails db:migrate`
38
+ 4. `bin/rails po_box:backfill_emailables`
39
+
40
+ ## Limitations
41
+
42
+ 1. Can only be applied to one model per application, for now.
43
+
44
+ ## Usage
45
+
46
+ Visit http://localhost:3000/rails/conductor/action_mailbox/inbound_emails to send an email to your app.
47
+
48
+ Send the email to the address that is associated with the emailable model. e.g.
49
+
50
+ ```ruby
51
+ User.inboxes.first.address
52
+
53
+ # => "some#64b0@yourapp"
54
+ ```
55
+
56
+ <!-- If your emailable_class is a User -->
57
+
58
+ ```ruby
59
+
60
+ irb(main):003:0> u = User.last
61
+ User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
62
+ =>
63
+ #<User:0x000000011131a328
64
+ ...
65
+ irb(main):004:0> u.inboxes.first
66
+ PoBox::Inbox Load (0.4ms) SELECT "po_box_inboxes".* FROM "po_box_inboxes" WHERE "po_box_inboxes"."emailable_id" = $1 AND "po_box_inboxes"."emailable_type" = $2 ORDER BY "po_box_inboxes"."id" ASC LIMIT $3 [["emailable_id", 1], ["emailable_type", "User"], ["LIMIT", 1]]
67
+ =>
68
+ #<PoBox::Inbox:0x00000001113119a8
69
+ id: 1,
70
+ emailable_type: "User",
71
+ emailable_id: 1,
72
+ address: "some#64b0",
73
+ created_at: Thu, 04 May 2023 00:28:13.107181000 UTC +00:00,
74
+ updated_at: Thu, 04 May 2023 00:28:13.107181000 UTC +00:00>
75
+ irb(main):005:0>
76
+
77
+ irb(main):005:0> u.inboxes.first.emails.last
78
+ PoBox::Inbox Load (0.5ms) SELECT "po_box_inboxes".* FROM "po_box_inboxes" WHERE "po_box_inboxes"."emailable_id" = $1 AND "po_box_inboxes"."emailable_type" = $2 ORDER BY "po_box_inboxes"."id" ASC LIMIT $3 [["emailable_id", 1], ["emailable_type", "User"], ["LIMIT", 1]]
79
+ PoBox::Email Load (0.5ms) SELECT "po_box_emails".* FROM "po_box_emails" WHERE "po_box_emails"."inbox_id" = $1 ORDER BY "po_box_emails"."id" DESC LIMIT $2 [["inbox_id", 1], ["LIMIT", 1]]
80
+ =>
81
+ #<PoBox::Email:0x00000001079c27e8
82
+ id: 1,
83
+ inbox_id: 1,
84
+ cc: nil,
85
+ from: "anyone@aol.com",
86
+ multipart: true,
87
+ read: false,
88
+ subject: "Something subjecty",
89
+ to: "some#64b0@yourapp.com",
90
+ message_id: "6452fc3aa9815_568e39a83101b@My-MacBook-Pro-2.local.mail",
91
+ body: "Hello World",
92
+ created_at: Thu, 04 May 2023 00:28:44.171561000 UTC +00:00,
93
+ updated_at: Thu, 04 May 2023 00:28:44.483199000 UTC +00:00>
94
+ irb(main):006:0>
95
+ ```
96
+
97
+ ## Contributing
98
+
99
+ Bug reports and pull requests are always welcome!
100
+
101
+ ## License
102
+
103
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/po_box .css
@@ -0,0 +1,15 @@
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 other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ module PoBox
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,68 @@
1
+ module PoBox
2
+ class EmailsController < ApplicationController
3
+ before_action :set_inbox
4
+ before_action :set_email, only: %i[show edit update destroy]
5
+
6
+ # GET /emails
7
+ def index
8
+ @emails = Email.all
9
+ end
10
+
11
+ # GET /emails/1
12
+ def show
13
+ end
14
+
15
+ # GET /emails/new
16
+ def new
17
+ @email = Email.new
18
+ end
19
+
20
+ # GET /emails/1/edit
21
+ def edit
22
+ end
23
+
24
+ # POST /emails
25
+ def create
26
+ @email = Email.new(email_params)
27
+
28
+ if @email.save
29
+ redirect_to inbox_email_path(@inbox, @email), notice: "Email was successfully created."
30
+ else
31
+ render :new, status: :unprocessable_entity
32
+ end
33
+ end
34
+
35
+ # PATCH/PUT /emails/1
36
+ def update
37
+ if @email.update(email_params)
38
+ redirect_to inbox_email_path(@inbox, @email), notice: "Email was successfully updated."
39
+ else
40
+ render :edit, status: :unprocessable_entity
41
+ end
42
+ end
43
+
44
+ # DELETE /emails/1
45
+ def destroy
46
+ @email.destroy
47
+ redirect_to inbox_emails_url(@inbox), notice: "Email was successfully destroyed."
48
+ end
49
+
50
+ private
51
+
52
+ def set_inbox
53
+ @inbox = Inbox.find(params[:inbox_id])
54
+ rescue ActiveRecord::RecordNotFound
55
+ redirect_to po_box_inboxes_path, notice: "Inbox not found"
56
+ end
57
+
58
+ # Use callbacks to share common setup or constraints between actions.
59
+ def set_email
60
+ @email = Email.find(params[:id])
61
+ end
62
+
63
+ # Only allow a list of trusted parameters through.
64
+ def email_params
65
+ params.require(:email).permit(:inbox_id, :cc, :from, :multipart, :read, :subject, :to, :message_id)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,54 @@
1
+ module PoBox
2
+ class InboxesController < ApplicationController
3
+ before_action :set_inbox, only: %i[show edit update destroy]
4
+
5
+ def index
6
+ @inboxes = Inbox.all
7
+ end
8
+
9
+ def show
10
+ end
11
+
12
+ def new
13
+ @inbox = Inbox.new
14
+ end
15
+
16
+ def edit
17
+ end
18
+
19
+ def create
20
+ @inbox = Inbox.new(inbox_params)
21
+
22
+ if @inbox.save
23
+ redirect_to @inbox, notice: "Inbox was successfully created."
24
+ else
25
+ render :new, status: :unprocessable_entity
26
+ end
27
+ end
28
+
29
+ def update
30
+ if @inbox.update(inbox_params)
31
+ redirect_to @inbox, notice: "Inbox was successfully updated."
32
+ else
33
+ render :edit, status: :unprocessable_entity
34
+ end
35
+ end
36
+
37
+ def destroy
38
+ @inbox.destroy
39
+ redirect_to inboxes_url, notice: "Inbox was successfully destroyed."
40
+ end
41
+
42
+ private
43
+
44
+ def inbox_params
45
+ params.require(:inbox).permit(:name, :address)
46
+ end
47
+
48
+ def set_inbox
49
+ @inbox = Inbox.find(params[:id])
50
+ rescue ActiveRecord::RecordNotFound
51
+ redirect_to po_box_inboxes_path, notice: "Inbox not found"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,4 @@
1
+ module PoBox
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module PoBox
2
+ module EmailsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module PoBox
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationMailbox < ActionMailbox::Base
2
+ end
@@ -0,0 +1,54 @@
1
+ module PoBox
2
+ class InboxMailbox < ApplicationMailbox
3
+ before_processing :ensure_inbox
4
+
5
+ def process
6
+ email = create_email
7
+ handle_attachments(email) if mail.attachments.any?
8
+ end
9
+
10
+ private
11
+
12
+ def ensure_inbox
13
+ return if inbox.present?
14
+
15
+ bounce_with EmailableMailer.missing_emailable(mail)
16
+ end
17
+
18
+ def inbox
19
+ @inbox ||= PoBox::Inbox.find_by(address: address)
20
+ end
21
+
22
+ def address
23
+ mail.to.first.split("@").first
24
+ end
25
+
26
+ def create_email
27
+ inbox.emails.create!(
28
+ cc: mail.cc,
29
+ from: mail.from.first,
30
+ multipart: mail.multipart?,
31
+ read: false,
32
+ subject: mail.subject,
33
+ to: mail.to.first,
34
+ message_id: mail.message_id,
35
+ body: if mail.html_part
36
+ mail.html_part.decoded
37
+ elsif mail.text_part
38
+ mail.text_part.decoded
39
+ else
40
+ mail.decoded
41
+ end
42
+ )
43
+ end
44
+
45
+ def handle_attachments(email)
46
+ mail.attachments.each do |attachment|
47
+ email.files.attach(
48
+ io: StringIO.new(attachment.body.decoded),
49
+ filename: attachment.filename
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ module PoBox
2
+ class ApplicationMailer < ActionMailer::Base
3
+ # default from: "from@example.com"
4
+ # layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module PoBox
2
+ class EmailableMailer < ApplicationMailer
3
+ def missing_emailable(mail)
4
+ mail(
5
+ to: mail.sender,
6
+ subject: "Inbox not found",
7
+ body: "We were unable to find an inbox for this email. Please contact support.",
8
+ content_type: "text/html"
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ module PoBox
2
+ module Emailable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :inboxes, as: :emailable, class_name: "PoBox::Inbox", dependent: :destroy
7
+
8
+ after_create_commit :set_inbox
9
+
10
+ accepts_nested_attributes_for :inboxes, allow_destroy: true
11
+
12
+ private
13
+
14
+ def set_inbox
15
+ return if inboxes.any?
16
+
17
+ inbox_address = if email.present?
18
+ "#{email.split("@").first}##{SecureRandom.hex(2)}"
19
+ else
20
+ SecureRandom.hex(8)
21
+ end
22
+
23
+ inbox = inboxes.new
24
+ loop do
25
+ inbox.address = inbox_address
26
+ break unless PoBox.emailable_class.joins(:inboxes).where(po_box_inboxes: {address: inbox.address}).exists?
27
+ end
28
+
29
+ save
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ module PoBox
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module PoBox
2
+ class Email < ApplicationRecord
3
+ belongs_to :inbox, class_name: "PoBox::Inbox"
4
+
5
+ has_many_attached :files
6
+
7
+ validates :from, presence: true
8
+
9
+ delegate :raw_source, to: :body
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module PoBox
2
+ class Inbox < ApplicationRecord
3
+ belongs_to :emailable, polymorphic: true, class_name: PoBox.emailable_class.to_s
4
+
5
+ has_many :emails, class_name: "PoBox::Email", dependent: :destroy
6
+
7
+ define_method PoBox.emailable_class.to_s.underscore do
8
+ emailable
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Active email</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+ <%= stylesheet_link_tag "po_box/application", media: "all" %>
8
+ </head>
9
+ <body>
10
+ <%= yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
+ <style>
6
+ /* Email styles need to be inline */
7
+ </style>
8
+ </head>
9
+ <body>
10
+ <%= yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1,49 @@
1
+ <div id="<%= dom_id email %>">
2
+ <% if email.cc %>
3
+ <p>
4
+ <strong>Cc:</strong>
5
+ <%= email.cc %>
6
+ </p>
7
+ <% end %>
8
+ <p>
9
+ <strong>Subject:</strong>
10
+ <%= email.subject %>
11
+ </p>
12
+ <p>
13
+ <strong>From:</strong>
14
+ <%= email.from %>
15
+ </p>
16
+ <p>
17
+ <strong>To:</strong>
18
+ <%= email.to %>
19
+ </p>
20
+ <p>
21
+ <strong>Body:</strong>
22
+ <%= email.body %>
23
+ </p>
24
+ <% email.files.each do |file| %>
25
+ <div>
26
+ <% if file.representable? %>
27
+ <%= image_tag main_app.url_for(file.representation(resize_to_limit: [100, 100])) %>
28
+ <% else %>
29
+ <%= link_to file.filename, main_app.url_for(file) %>
30
+ <% end %>
31
+ </div>
32
+ <% end %>
33
+ <p>
34
+ <strong>Multipart:</strong>
35
+ <%= email.multipart %>
36
+ </p>
37
+ <p>
38
+ <strong>Emailable ID:</strong>
39
+ <%= email.inbox_id %>
40
+ </p>
41
+ <p>
42
+ <strong>Read:</strong>
43
+ <%= email.read %>
44
+ </p>
45
+ <p>
46
+ <strong>Message ID:</strong>
47
+ <%= email.message_id %>
48
+ </p>
49
+ </div>
@@ -0,0 +1,51 @@
1
+ <%= form_with(model: email, url: url) 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
+ <ul>
6
+ <% email.errors.each do |error| %>
7
+ <li><%= error.full_message %></li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
11
+ <% end %>
12
+ <div>
13
+ <%= form.label :inbox_id, style: "display: block" %>
14
+ <%= form.text_field :inbox_id %>
15
+ </div>
16
+ <div>
17
+ <%= form.label :cc, style: "display: block" %>
18
+ <%= form.text_field :cc %>
19
+ </div>
20
+ <div>
21
+ <%= form.label :from, style: "display: block" %>
22
+ <%= form.text_field :from %>
23
+ </div>
24
+ <div>
25
+ <%= form.label :multipart, style: "display: block" %>
26
+ <%= form.check_box :multipart %>
27
+ </div>
28
+ <div>
29
+ <%= form.label :read, style: "display: block" %>
30
+ <%= form.check_box :read %>
31
+ </div>
32
+ <div>
33
+ <%= form.label :subject, style: "display: block" %>
34
+ <%= form.text_field :subject %>
35
+ </div>
36
+ <div>
37
+ <%= form.label :to, style: "display: block" %>
38
+ <%= form.text_field :to %>
39
+ </div>
40
+ <div>
41
+ <%= form.label :message_id, style: "display: block" %>
42
+ <%= form.text_field :message_id %>
43
+ </div>
44
+ <div>
45
+ <%= form.label :body, style: "display: block" %>
46
+ <%= form.text_area :body %>
47
+ </div>
48
+ <div>
49
+ <%= form.submit %>
50
+ </div>
51
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <h1>Editing email</h1>
2
+ <%= render "form", email: @email, url: inbox_email_path(@inbox, @email) %>
3
+ <br>
4
+ <div>
5
+ <%= link_to "Show this email", inbox_email_path(@inbox, @email) %> |
6
+ <%= link_to "Back to emails", inbox_emails_path(@inbox) %>
7
+ </div>
@@ -0,0 +1,11 @@
1
+ <p style="color: green"><%= notice %></p>
2
+ <h1>Emails</h1>
3
+ <div id="emails">
4
+ <% @emails.each do |email| %>
5
+ <%= render email %>
6
+ <p>
7
+ <%= link_to "Show this email", inbox_email_path(@inbox, email) %>
8
+ </p>
9
+ <% end %>
10
+ </div>
11
+ <%= link_to "New email", new_inbox_email_path(@inbox) %>
@@ -0,0 +1,6 @@
1
+ <h1>New email</h1>
2
+ <%= render "form", email: @email, url: inbox_emails_path(@inbox) %>
3
+ <br>
4
+ <div>
5
+ <%= link_to "Back to emails", inbox_emails_path(@inbox) %>
6
+ </div>
@@ -0,0 +1,7 @@
1
+ <p style="color: green"><%= notice %></p>
2
+ <%= render @email %>
3
+ <div>
4
+ <%= link_to "Edit this email", edit_inbox_email_path(@inbox, @email) %> |
5
+ <%= link_to "Back to emails", inbox_emails_path(@inbox) %>
6
+ <%= button_to "Destroy this email", inbox_email_path(@inbox, @email, method: :delete) %>
7
+ </div>
@@ -0,0 +1,2 @@
1
+ <h1>Home#index</h1>
2
+ <p>Find me in app/views/po_box/home/index.html.erb</p>
@@ -0,0 +1,19 @@
1
+ <%= form_with(model: inbox) do |form| %>
2
+ <% if inbox.errors.any? %>
3
+ <div style="color: red">
4
+ <h2><%= pluralize(inbox.errors.count, "error") %> prohibited this inbox from being saved:</h2>
5
+ <ul>
6
+ <% inbox.errors.each do |error| %>
7
+ <li><%= error.full_message %></li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
11
+ <% end %>
12
+ <div>
13
+ <%= form.label :address, style: "display: block" %>
14
+ <%= form.text_field :address %>
15
+ </div>
16
+ <div>
17
+ <%= form.submit %>
18
+ </div>
19
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <div id="<%= dom_id inbox %>">
2
+ <p>
3
+ <strong>Email Address:</strong>
4
+ <%= inbox.address %>
5
+ </p>
6
+ </div>
@@ -0,0 +1,7 @@
1
+ <h1>Editing inbox</h1>
2
+ <%= render "form", inbox: @inbox %>
3
+ <br>
4
+ <div>
5
+ <%= link_to "Show this inbox", @inbox %> |
6
+ <%= link_to "Back to inboxes", inboxes_path %>
7
+ </div>
@@ -0,0 +1,15 @@
1
+ <p style="color: green"><%= notice %></p>
2
+ <h1>Email addresses</h1>
3
+ <div class="">
4
+ <%= link_to "New inbox", new_inbox_path %>
5
+ </div>
6
+ <div id="inboxes">
7
+ <% @inboxes.each do |inbox| %>
8
+ <%= render inbox %>
9
+ <div>
10
+ <%= link_to "Show this inbox", inbox %>
11
+ <%= link_to "Edit inbox", edit_inbox_path(inbox) %>
12
+ <%= button_to "Destroy", edit_inbox_path(inbox) %>
13
+ </div>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1,6 @@
1
+ <h1>New inbox</h1>
2
+ <%= render "form", inbox: @inbox %>
3
+ <br>
4
+ <div>
5
+ <%= link_to "Back to inboxes", inboxes_path %>
6
+ </div>
@@ -0,0 +1,8 @@
1
+ <p style="color: green"><%= notice %></p>
2
+ <%= render @inbox %>
3
+ <%= link_to "Emails", inbox_emails_path(@inbox) %> |
4
+ <div>
5
+ <%= link_to "Edit this inbox", edit_inbox_path() %> |
6
+ <%= link_to "Back to inboxes", inboxes_path %>
7
+ <%= button_to "Destroy this inbox", @inbox, method: :delete %>
8
+ </div>
@@ -0,0 +1 @@
1
+ PoBox.emailable_class = "Owner"
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ PoBox::Engine.routes.draw do
2
+ resources :inboxes do
3
+ resources :emails
4
+ end
5
+
6
+ root "inboxes#index"
7
+ end
@@ -0,0 +1,10 @@
1
+ class CreatePoBoxInboxes < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :po_box_inboxes do |t|
4
+ t.references :emailable, polymorphic: true, null: false
5
+ t.string :address, null: false, index: {unique: true}
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ class CreatePoBoxEmails < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :po_box_emails do |t|
4
+ t.references :inbox, foreign_key: {to_table: :po_box_inboxes}
5
+ t.string :cc
6
+ t.string :from
7
+ t.boolean :multipart
8
+ t.boolean :read, default: false, null: false
9
+ t.string :subject
10
+ t.string :to
11
+ t.string :message_id
12
+ t.text :body
13
+
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ bin/rails generate po_box User
6
+
7
+ This will create:
8
+ An initializer in config/initializers/po_box.rb
9
+ A migration to add the inbox address to the emailable table
10
+ Mount the engine in config/routes.rb
11
+ Add the emailable concern to the User model
12
+ Add the route all emails to the po_box/inbox mailbox
@@ -0,0 +1,33 @@
1
+ class PoBoxGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("templates", __dir__)
3
+
4
+ def setup_initializer
5
+ create_file "config/initializers/po_box.rb", "PoBox.emailable_class = \"#{name.camelize}\""
6
+ end
7
+
8
+ def mount_engine
9
+ route "mount PoBox::Engine, at: \"/po_box\""
10
+ end
11
+
12
+ def include_concerns
13
+ inject_into_file "app/models/#{name.tableize.singularize}.rb", after: "class #{name.camelize} < ApplicationRecord\n" do
14
+ <<-RUBY
15
+ include PoBox::Emailable
16
+ RUBY
17
+ end
18
+ end
19
+
20
+ def setup_inbox
21
+ inject_into_file "app/mailboxes/application_mailbox.rb", after: "class ApplicationMailbox < ActionMailbox::Base\n" do
22
+ <<-RUBY
23
+ routing all: "po_box/inbox"
24
+ RUBY
25
+ end
26
+ end
27
+
28
+ def append_to_manifest
29
+ append_to_file "app/assets/config/manifest.js" do
30
+ "//= link po_box/application.css\n"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1 @@
1
+ PoBox.emailable_class = ARGV[0]
@@ -0,0 +1,13 @@
1
+ module PoBox
2
+ mattr_accessor :application_controller
3
+
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace PoBox
6
+
7
+ config.generators do |g|
8
+ g.test_framework :rspec
9
+ g.fixture_replacement :factory_bot
10
+ g.factory_bot dir: "spec/factories"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module PoBox
2
+ VERSION = "0.1.0"
3
+ end
data/lib/po_box.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "po_box/version"
2
+ require "po_box/engine"
3
+
4
+ module PoBox
5
+ mattr_accessor :emailable_class
6
+
7
+ def self.emailable_class
8
+ @@emailable_class.constantize
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ namespace :po_box do
2
+ desc "Backfill inboxes for emailables"
3
+ task backfill_emailables: :environment do
4
+ PoBox.emailable_class.all.each do |emailable|
5
+ emailable.send(:set_inbox)
6
+ end
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,232 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: po_box
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Leo Policastro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-05-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.4.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.4.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: factory_bot_rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: standard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: overcommit
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faker
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: shoulda-matchers
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: coderay
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: Create inboxes for your users to send and receive emails.
154
+ email:
155
+ - lbpdev@proton.me
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - MIT-LICENSE
161
+ - README.md
162
+ - Rakefile
163
+ - app/assets/config/po_box_manifest.js
164
+ - app/assets/stylesheets/po_box/application.css
165
+ - app/controllers/po_box/application_controller.rb
166
+ - app/controllers/po_box/emails_controller.rb
167
+ - app/controllers/po_box/inboxes_controller.rb
168
+ - app/helpers/po_box/application_helper.rb
169
+ - app/helpers/po_box/emails_helper.rb
170
+ - app/jobs/po_box/application_job.rb
171
+ - app/mailboxes/application_mailbox.rb
172
+ - app/mailboxes/po_box/inbox_mailbox.rb
173
+ - app/mailers/po_box/application_mailer.rb
174
+ - app/mailers/po_box/emailable_mailer.rb
175
+ - app/models/concerns/po_box/emailable.rb
176
+ - app/models/po_box/application_record.rb
177
+ - app/models/po_box/email.rb
178
+ - app/models/po_box/inbox.rb
179
+ - app/views/layouts/po_box/application.html.erb
180
+ - app/views/layouts/po_box/mailer.html.erb
181
+ - app/views/layouts/po_box/mailer.text.erb
182
+ - app/views/po_box/emails/_email.html.erb
183
+ - app/views/po_box/emails/_form.html.erb
184
+ - app/views/po_box/emails/edit.html.erb
185
+ - app/views/po_box/emails/index.html.erb
186
+ - app/views/po_box/emails/new.html.erb
187
+ - app/views/po_box/emails/show.html.erb
188
+ - app/views/po_box/home/index.html.erb
189
+ - app/views/po_box/inboxes/_form.html.erb
190
+ - app/views/po_box/inboxes/_inbox.html.erb
191
+ - app/views/po_box/inboxes/edit.html.erb
192
+ - app/views/po_box/inboxes/index.html.erb
193
+ - app/views/po_box/inboxes/new.html.erb
194
+ - app/views/po_box/inboxes/show.html.erb
195
+ - config/initializers/po_box.rb
196
+ - config/routes.rb
197
+ - db/migrate/20230425172819_create_po_box_inboxes.rb
198
+ - db/migrate/20230426005820_create_po_box_emails.rb
199
+ - lib/generators/po_box/USAGE
200
+ - lib/generators/po_box/po_box_generator.rb
201
+ - lib/generators/po_box/templates/po_box.rb
202
+ - lib/po_box.rb
203
+ - lib/po_box/engine.rb
204
+ - lib/po_box/version.rb
205
+ - lib/tasks/po_box.rake
206
+ homepage: https://github.com/leopolicastro/po_box
207
+ licenses:
208
+ - MIT
209
+ metadata:
210
+ homepage_uri: https://github.com/leopolicastro/po_box
211
+ source_code_uri: https://github.com/leopolicastro/po_box
212
+ changelog_uri: https://github.com/leopolicastro/po_box/blob/main/CHANGELOG.md
213
+ post_install_message:
214
+ rdoc_options: []
215
+ require_paths:
216
+ - lib
217
+ required_ruby_version: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ required_rubygems_version: !ruby/object:Gem::Requirement
223
+ requirements:
224
+ - - ">="
225
+ - !ruby/object:Gem::Version
226
+ version: '0'
227
+ requirements: []
228
+ rubygems_version: 3.4.10
229
+ signing_key:
230
+ specification_version: 4
231
+ summary: Create inboxes for your users to send and receive emails.
232
+ test_files: []