letter_opener_db 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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +99 -0
- data/README_JA.md +95 -0
- data/Rakefile +8 -0
- data/app/controllers/letter_opener_db/mail_records_controller.rb +38 -0
- data/app/models/letter_opener_db/mail_record.rb +37 -0
- data/app/views/letter_opener_db/mail_records/index.html.erb +217 -0
- data/app/views/letter_opener_db/mail_records/show.html.erb +102 -0
- data/config/routes.rb +3 -0
- data/lib/generators/letter_opener_db/install/install_generator.rb +20 -0
- data/lib/generators/letter_opener_db/install/templates/create_letter_opener_db_mail_records.rb +18 -0
- data/lib/letter_opener_db/delivery_method.rb +18 -0
- data/lib/letter_opener_db/engine.rb +5 -0
- data/lib/letter_opener_db/railtie.rb +10 -0
- data/lib/letter_opener_db/version.rb +5 -0
- data/lib/letter_opener_db.rb +13 -0
- data/lib/tasks/letter_opener_db.rake +8 -0
- data/sig/letter_opener_db.rbs +4 -0
- metadata +67 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 793c69840ecc16b1839cd9b35387adbf43b3d35a93d4d579021aebc72e8237cc
|
4
|
+
data.tar.gz: 77af71136e1929d5e4f538eb77107429e6c2c74d31f3911ff0965220e53e923a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 708be12697fe937481224d13c01085b2534bda89df9586551df87a6805327bb5d9f07129478adc808ff7e717ffc9fee2813f8d3d21fb06c3d8da29430f0b22c8
|
7
|
+
data.tar.gz: 3158a1c06a1417b38d665fb69bf5689d5227fc5b45ec95f129d5b92d9ffcf838ca79b68acdb11d6aead30f8550457d5a56b98aa57b722948ad742898bca7ae73
|
data/.rspec
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Seiya Takahashi.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# LetterOpenerDb
|
2
|
+
|
3
|
+
**LetterOpenerDb** is a Ruby gem that intercepts outgoing emails in your Rails application and stores them in a database for easy viewing, searching, and debugging. Inspired by [letter_opener](https://github.com/ryanb/letter_opener), this gem provides a convenient web interface instead of relying on local file system storage.
|
4
|
+
|
5
|
+
## Motivation
|
6
|
+
|
7
|
+
While the original **letter_opener** gem saves emails as files, it has several limitations:
|
8
|
+
|
9
|
+
1. **No Search Functionality**: Storing emails on the file system makes them difficult to search.
|
10
|
+
2. **Server Scalability Issues**: If you run multiple servers (e.g., on a staging environment), emails are scattered across different file systems, making it impossible to view all captured emails in one place.
|
11
|
+
|
12
|
+
To address these issues, **LetterOpenerDb** saves the emails in a database, enabling:
|
13
|
+
|
14
|
+
- **Search** by subject, body, or other fields
|
15
|
+
- **Centralized Access** to all emails, even in multi-server environments
|
16
|
+
|
17
|
+
## Features
|
18
|
+
|
19
|
+
- **Database Storage**
|
20
|
+
All emails sent by ActionMailer are recorded in the database.
|
21
|
+
- **Web Interface**
|
22
|
+
Easily browse and inspect sent emails through an embedded Rails engine.
|
23
|
+
- **Simple Search**
|
24
|
+
Search by text in the subject or body of emails.
|
25
|
+
- **Rails Engine**
|
26
|
+
Includes models, controllers, and views for quick installation and easy maintenance.
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
Add this line to your application's Gemfile, pointing to your Git repository (private or public) or a local path:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
gem 'letter_opener_db'
|
34
|
+
```
|
35
|
+
|
36
|
+
### Then execute:
|
37
|
+
|
38
|
+
`bundle install`
|
39
|
+
|
40
|
+
### Setup
|
41
|
+
|
42
|
+
1. Migrations
|
43
|
+
Generate and run the migrations to create the letter_opener_db_mail_records table:
|
44
|
+
|
45
|
+
```sh
|
46
|
+
rails generate letter_opener_db:install
|
47
|
+
rails db:migrate
|
48
|
+
```
|
49
|
+
|
50
|
+
2. Delivery Method
|
51
|
+
In your config/environments/development.rb (and/or config/environments/test.rb), set:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
config.action_mailer.delivery_method = :letter_opener_db
|
55
|
+
```
|
56
|
+
|
57
|
+
3. Mount the Engine
|
58
|
+
In your main config/routes.rb, add:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
mount LetterOpenerDb::Engine, at: '/letter_opener_db'
|
62
|
+
```
|
63
|
+
|
64
|
+
You can choose any path you prefer (e.g., '/mails').
|
65
|
+
|
66
|
+
## Usage
|
67
|
+
|
68
|
+
- Send Emails as you normally would in development or test environments.
|
69
|
+
- Access the emails at http://localhost:3000/letter_opener_db/mail_records (or your chosen mount path).
|
70
|
+
- Browse and Search
|
71
|
+
- See all captured emails, sorted by send time.
|
72
|
+
- Click on a subject to view details (recipients, subject, body, etc.).
|
73
|
+
- Use the simple search box to filter emails by subject or body text.
|
74
|
+
|
75
|
+
## Development
|
76
|
+
|
77
|
+
1. Clone or Fork the repository.
|
78
|
+
2. Edit the gem’s files in the lib and app directories as needed.
|
79
|
+
3. Test changes by running: `bundle exec rspec`
|
80
|
+
|
81
|
+
4. Optionally, add this gem as a path reference in another Rails app to test integration:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
gem 'letter_opener_db', path: '../letter_opener_db'
|
85
|
+
```
|
86
|
+
|
87
|
+
## Contributing
|
88
|
+
|
89
|
+
Contributions are welcome! To submit a feature or fix:
|
90
|
+
|
91
|
+
1. Fork this repository.
|
92
|
+
2. Create a new branch (git checkout -b feature/my-feature).
|
93
|
+
3. Commit your changes (git commit -m 'Add my feature').
|
94
|
+
4. Push the branch (git push origin feature/my-feature).
|
95
|
+
5. Open a Pull Request with a clear description of your changes.
|
96
|
+
|
97
|
+
## License
|
98
|
+
|
99
|
+
This project is released under the MIT License.
|
data/README_JA.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# LetterOpenerDb
|
2
|
+
|
3
|
+
**LetterOpenerDb** は、Rails アプリケーションで送信されるメールをフックし、ファイルシステムではなくデータベースに保存して、簡単に閲覧・検索・デバッグできるようにする Ruby gem です。
|
4
|
+
[letter_opener](https://github.com/ryanb/letter_opener) から着想を得ていますが、ファイルシステムに依存せず、Web インターフェースでメールの内容を確認できるようにしています。
|
5
|
+
|
6
|
+
## 作成の背景 (Motivation)
|
7
|
+
|
8
|
+
オリジナルの **letter_opener** gem はメールをファイルとして保存しますが、以下のような制限があります:
|
9
|
+
|
10
|
+
1. **検索ができない**: ファイルシステムに保存したメールを検索するのは容易ではありません。
|
11
|
+
2. **サーバーを増やした場合の問題**: ステージング環境などで複数台のサーバーを起動すると、メールが各サーバーのファイルシステムに分散してしまい、すべてのメールを一元的に閲覧できなくなります。
|
12
|
+
|
13
|
+
こうした問題を解決するため、**LetterOpenerDb** ではメールをデータベースに保存し、下記が可能になります:
|
14
|
+
|
15
|
+
- **件名や本文等の検索**
|
16
|
+
- **マルチサーバー環境でも一括管理** して全メールを閲覧
|
17
|
+
|
18
|
+
## 特徴 (Features)
|
19
|
+
|
20
|
+
- **データベース保存**
|
21
|
+
ActionMailer が送信したすべてのメールをデータベースに保存します。
|
22
|
+
- **Web インターフェース**
|
23
|
+
Rails エンジンとして組み込み、ブラウザで送信メールを簡単に閲覧できます。
|
24
|
+
- **シンプルな検索機能**
|
25
|
+
件名や本文をテキスト検索できるため、メールを素早く見つけられます。
|
26
|
+
- **Rails エンジン**
|
27
|
+
マイグレーション、コントローラ、ビューを含むため、インストールが簡単です。
|
28
|
+
|
29
|
+
## インストール (Installation)
|
30
|
+
|
31
|
+
アプリケーションの `Gemfile` に以下の行を追加します。
|
32
|
+
(プライベートまたはパブリックの Git リポジトリ、あるいはローカルパスを指定可能です)
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
gem 'letter_opener_db'
|
36
|
+
```
|
37
|
+
|
38
|
+
### その後実行:
|
39
|
+
|
40
|
+
`bundle install`
|
41
|
+
|
42
|
+
### セットアップ (Setup)
|
43
|
+
|
44
|
+
1. マイグレーション
|
45
|
+
letter_opener_db_mail_records テーブルを作成するため、以下を実行します:
|
46
|
+
|
47
|
+
```sh
|
48
|
+
rails generate letter_opener_db:install
|
49
|
+
rails db:migrate
|
50
|
+
```
|
51
|
+
|
52
|
+
2. メール送信方法の設定 (Delivery Method)
|
53
|
+
|
54
|
+
config/environments/development.rb (または config/environments/test.rb) に以下を追記します:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
config.action_mailer.delivery_method = :letter_opener_db
|
58
|
+
```
|
59
|
+
|
60
|
+
3. エンジンのマウント (Mount the Engine)
|
61
|
+
|
62
|
+
config/routes.rb に以下を追加します:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
mount LetterOpenerDb::Engine, at: '/letter_opener_db'
|
66
|
+
```
|
67
|
+
|
68
|
+
'/mails' など、お好みのパスを指定することも可能です。
|
69
|
+
|
70
|
+
## 使い方 (Usage)
|
71
|
+
|
72
|
+
- 開発やテスト環境で、通常どおりメールを送信します。
|
73
|
+
- http://localhost:3000/letter_opener_db/mail_records (または設定したパス) にアクセスすると、保存されたメールを閲覧できます。
|
74
|
+
- 一覧・検索
|
75
|
+
- 送信したメールが時系列で表示されます。
|
76
|
+
- 件名をクリックすると、宛先や本文を含む詳細情報が表示されます。
|
77
|
+
- 件名や本文で検索することが可能です。
|
78
|
+
|
79
|
+
## 開発 (Development)
|
80
|
+
|
81
|
+
1. リポジトリをクローンまたはフォークします。
|
82
|
+
2. lib ディレクトリや app ディレクトリ内のファイルを必要に応じて編集します。
|
83
|
+
3. 下記コマンドで変更内容をテストします:
|
84
|
+
`bundle exec rspec`
|
85
|
+
4. 別の Rails アプリにローカルパスで参照して動作確認も可能です:
|
86
|
+
|
87
|
+
`gem 'letter_opener_db', path: '../letter_opener_db'`
|
88
|
+
|
89
|
+
## コントリビュート (Contributing)
|
90
|
+
|
91
|
+
機能追加や修正の提案は歓迎します!手順は以下のとおりです: 1. このリポジトリをフォークしてください。 2. 新しいブランチを作成します (git checkout -b feature/my-feature)。 3. 変更をコミットします (git commit -m 'Add my feature')。 4. ブランチをプッシュします (git push origin feature/my-feature)。 5. 変更内容を明確に説明した Pull Request を作成してください。
|
92
|
+
|
93
|
+
## ライセンス (License)
|
94
|
+
|
95
|
+
このプロジェクトは MIT License のもとで公開されています。
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module LetterOpenerDb
|
2
|
+
class MailRecordsController < ApplicationController
|
3
|
+
DEFAULT_PER_PAGE = 25.freeze
|
4
|
+
DEFAULT_PAGE = 1.freeze
|
5
|
+
|
6
|
+
def index
|
7
|
+
@page = (params[:page] || DEFAULT_PAGE).to_i
|
8
|
+
@per = (params[:per] || DEFAULT_PER_PAGE).to_i
|
9
|
+
|
10
|
+
mail_records = MailRecord.search_by_text(params[:q])
|
11
|
+
|
12
|
+
@total_count = mail_records.count
|
13
|
+
|
14
|
+
offset = (@page - 1) * @per
|
15
|
+
|
16
|
+
@mail_records = mail_records
|
17
|
+
.order(sent_at: sort_order)
|
18
|
+
.limit(@per)
|
19
|
+
.offset(offset)
|
20
|
+
end
|
21
|
+
|
22
|
+
def show
|
23
|
+
@mail_record = MailRecord.find(params[:id])
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy
|
27
|
+
@mail_record = MailRecord.find(params[:id])
|
28
|
+
@mail_record.destroy
|
29
|
+
redirect_to mail_records_path
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def sort_order
|
35
|
+
params[:sort] == "asc" ? :asc : :desc
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module LetterOpenerDb
|
2
|
+
class MailRecord < ApplicationRecord
|
3
|
+
validates :to, presence: true
|
4
|
+
validates :subject, presence: true
|
5
|
+
|
6
|
+
scope :search_by_text, ->(keyword) {
|
7
|
+
if keyword.present?
|
8
|
+
where(
|
9
|
+
"subject LIKE :q OR html_part LIKE :q OR text_part LIKE :q
|
10
|
+
OR \"to\" = :keyword OR \"from\" = :keyword",
|
11
|
+
q: "%#{keyword}%",
|
12
|
+
keyword: keyword
|
13
|
+
)
|
14
|
+
else
|
15
|
+
all
|
16
|
+
end
|
17
|
+
}
|
18
|
+
|
19
|
+
def body_html
|
20
|
+
return "" if html_part.blank?
|
21
|
+
|
22
|
+
_, content = html_part.split("\r\n\r\n", 2)
|
23
|
+
content.to_s.strip
|
24
|
+
end
|
25
|
+
|
26
|
+
def body_text
|
27
|
+
return "" if text_part.blank?
|
28
|
+
|
29
|
+
_, content = text_part.split("\r\n\r\n", 2)
|
30
|
+
content.to_s.strip
|
31
|
+
end
|
32
|
+
|
33
|
+
def body_preview
|
34
|
+
body_text[0, 30]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
<style>
|
2
|
+
/* Overall layout */
|
3
|
+
body {
|
4
|
+
font-family: sans-serif;
|
5
|
+
margin: 20px;
|
6
|
+
background-color: #f8f8f8;
|
7
|
+
}
|
8
|
+
|
9
|
+
.lo-db-container {
|
10
|
+
max-width: 900px;
|
11
|
+
margin: 0 auto;
|
12
|
+
background-color: #fff;
|
13
|
+
padding: 16px;
|
14
|
+
border-radius: 6px;
|
15
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
16
|
+
}
|
17
|
+
|
18
|
+
h1 {
|
19
|
+
margin-bottom: 16px;
|
20
|
+
color: #333;
|
21
|
+
}
|
22
|
+
|
23
|
+
/* Search form area */
|
24
|
+
.lo-db-search-row {
|
25
|
+
display: flex;
|
26
|
+
justify-content: space-between;
|
27
|
+
align-items: center;
|
28
|
+
margin-bottom: 16px;
|
29
|
+
}
|
30
|
+
.lo-db-search-form {
|
31
|
+
display: inline-flex;
|
32
|
+
align-items: center;
|
33
|
+
}
|
34
|
+
.lo-db-search-form input[type="text"] {
|
35
|
+
padding: 6px 8px;
|
36
|
+
font-size: 14px;
|
37
|
+
border: 1px solid #ccc;
|
38
|
+
border-radius: 4px;
|
39
|
+
margin-right: 6px;
|
40
|
+
}
|
41
|
+
.lo-db-search-form select {
|
42
|
+
margin-left: 6px;
|
43
|
+
padding: 4px 6px;
|
44
|
+
font-size: 14px;
|
45
|
+
}
|
46
|
+
.lo-db-search-form input[type="submit"] {
|
47
|
+
margin-left: 6px;
|
48
|
+
padding: 6px 12px;
|
49
|
+
font-size: 14px;
|
50
|
+
border: none;
|
51
|
+
border-radius: 4px;
|
52
|
+
background-color: #333;
|
53
|
+
color: #fff;
|
54
|
+
cursor: pointer;
|
55
|
+
}
|
56
|
+
.lo-db-search-form input[type="submit"]:hover {
|
57
|
+
background-color: #555;
|
58
|
+
}
|
59
|
+
|
60
|
+
.lo-db-total-count {
|
61
|
+
font-size: 14px;
|
62
|
+
}
|
63
|
+
|
64
|
+
/* Table */
|
65
|
+
table.lo-db-table {
|
66
|
+
width: 100%;
|
67
|
+
border-collapse: collapse;
|
68
|
+
}
|
69
|
+
table.lo-db-table thead {
|
70
|
+
background-color: #f3f3f3;
|
71
|
+
}
|
72
|
+
table.lo-db-table th,
|
73
|
+
table.lo-db-table td {
|
74
|
+
border: 1px solid #ddd;
|
75
|
+
padding: 8px;
|
76
|
+
text-align: left;
|
77
|
+
vertical-align: middle;
|
78
|
+
}
|
79
|
+
table.lo-db-table th {
|
80
|
+
font-weight: bold;
|
81
|
+
color: #333;
|
82
|
+
}
|
83
|
+
table.lo-db-table tbody tr:nth-child(even) {
|
84
|
+
background-color: #fafafa;
|
85
|
+
}
|
86
|
+
table.lo-db-table a {
|
87
|
+
color: #0066cc;
|
88
|
+
text-decoration: none;
|
89
|
+
}
|
90
|
+
table.lo-db-table a:hover {
|
91
|
+
text-decoration: underline;
|
92
|
+
}
|
93
|
+
|
94
|
+
/* Pagination */
|
95
|
+
.lo-db-pagination {
|
96
|
+
margin-top: 36px;
|
97
|
+
margin-bottom: 16px;
|
98
|
+
display: flex;
|
99
|
+
align-items: center;
|
100
|
+
justify-content: center;
|
101
|
+
gap: 12px;
|
102
|
+
}
|
103
|
+
.lo-db-pagination .page-info {
|
104
|
+
font-weight: bold;
|
105
|
+
margin: 0 4px;
|
106
|
+
}
|
107
|
+
.lo-db-pagination a {
|
108
|
+
color: #0066cc;
|
109
|
+
text-decoration: none;
|
110
|
+
border: 1px solid #ccc;
|
111
|
+
padding: 4px 8px;
|
112
|
+
border-radius: 4px;
|
113
|
+
font-size: 14px;
|
114
|
+
background-color: #f9f9f9;
|
115
|
+
}
|
116
|
+
.lo-db-pagination a:hover {
|
117
|
+
background-color: #ddd;
|
118
|
+
}
|
119
|
+
.lo-db-pagination .disabled-link {
|
120
|
+
color: #999;
|
121
|
+
border-color: #eee;
|
122
|
+
background-color: #fafafa;
|
123
|
+
cursor: not-allowed;
|
124
|
+
text-decoration: none;
|
125
|
+
}
|
126
|
+
</style>
|
127
|
+
|
128
|
+
<div class="lo-db-container">
|
129
|
+
<h1>Letter Opener DB</h1>
|
130
|
+
|
131
|
+
<!-- Search row -->
|
132
|
+
<div class="lo-db-search-row">
|
133
|
+
<!-- Search form -->
|
134
|
+
<%= form_with url: mail_records_path, method: :get, local: true, class: "lo-db-search-form" do |f| %>
|
135
|
+
<!-- Keyword -->
|
136
|
+
<%= f.text_field :q, value: params[:q] %>
|
137
|
+
|
138
|
+
<!-- Sort by date: asc or desc -->
|
139
|
+
<label for="sort_select" style="margin-left: 8px;">Sort by date</label>
|
140
|
+
<%= f.select :sort, [["Newest first", "desc"], ["Oldest first", "asc"]],
|
141
|
+
{ selected: params[:sort] || "desc" },
|
142
|
+
{ id: "sort_select" } %>
|
143
|
+
|
144
|
+
<!-- Per page selection -->
|
145
|
+
<label for="per_input" style="margin-left: 8px;">Per Page</label>
|
146
|
+
<%= f.select :per, [10, 25, 50, 100].map {|n| [n, n]}, { selected: @per }, { id: "per_input" } %>
|
147
|
+
|
148
|
+
<%= f.submit "Search" %>
|
149
|
+
<% end %>
|
150
|
+
|
151
|
+
<!-- Total record count -->
|
152
|
+
<div class="lo-db-total-count">
|
153
|
+
Total: <%= @total_count %> mails
|
154
|
+
</div>
|
155
|
+
</div>
|
156
|
+
|
157
|
+
<!-- Table of mail records -->
|
158
|
+
<table class="lo-db-table">
|
159
|
+
<thead>
|
160
|
+
<tr>
|
161
|
+
<th>Subject</th>
|
162
|
+
<th>To</th>
|
163
|
+
<th>From</th>
|
164
|
+
<th>Sent At</th>
|
165
|
+
<th>Body Preview</th>
|
166
|
+
</tr>
|
167
|
+
</thead>
|
168
|
+
<tbody>
|
169
|
+
<% @mail_records.each do |record| %>
|
170
|
+
<tr>
|
171
|
+
<td>
|
172
|
+
<%= link_to record.subject,
|
173
|
+
mail_record_path(record,
|
174
|
+
sort: params[:sort],
|
175
|
+
per: @per,
|
176
|
+
q: params[:q],
|
177
|
+
page: @page
|
178
|
+
) %>
|
179
|
+
</td>
|
180
|
+
<td><%= record.to %></td>
|
181
|
+
<td><%= record.from %></td>
|
182
|
+
<td><%= record.sent_at.strftime("%Y-%m-%d %H:%M:%S") %></td>
|
183
|
+
<td><%= record.body_preview %>...</td>
|
184
|
+
</tr>
|
185
|
+
<% end %>
|
186
|
+
</tbody>
|
187
|
+
</table>
|
188
|
+
|
189
|
+
<!-- Pagination: Prev / Next -->
|
190
|
+
<div class="lo-db-pagination">
|
191
|
+
<% total_pages = (@total_count.to_f / @per).ceil %>
|
192
|
+
|
193
|
+
<% prev_page = @page - 1 %>
|
194
|
+
<% next_page = @page + 1 %>
|
195
|
+
|
196
|
+
<!-- Prev link -->
|
197
|
+
<% if @page > 1 %>
|
198
|
+
<%= link_to "Prev",
|
199
|
+
mail_records_path(page: prev_page, per: @per, sort: params[:sort], q: params[:q]) %>
|
200
|
+
<% else %>
|
201
|
+
<a class="disabled-link">Prev</a>
|
202
|
+
<% end %>
|
203
|
+
|
204
|
+
<!-- Page info: "Page X / Y" -->
|
205
|
+
<span class="page-info">
|
206
|
+
Page <%= @page %> / <%= total_pages %>
|
207
|
+
</span>
|
208
|
+
|
209
|
+
<!-- Next link -->
|
210
|
+
<% if @page < total_pages %>
|
211
|
+
<%= link_to "Next",
|
212
|
+
mail_records_path(page: next_page, per: @per, sort: params[:sort], q: params[:q]) %>
|
213
|
+
<% else %>
|
214
|
+
<a class="disabled-link">Next</a>
|
215
|
+
<% end %>
|
216
|
+
</div>
|
217
|
+
</div>
|
@@ -0,0 +1,102 @@
|
|
1
|
+
<style>
|
2
|
+
body {
|
3
|
+
font-family: sans-serif;
|
4
|
+
margin: 20px;
|
5
|
+
background-color: #f8f8f8;
|
6
|
+
}
|
7
|
+
|
8
|
+
.lo-db-show-container {
|
9
|
+
max-width: 800px;
|
10
|
+
margin: 0 auto;
|
11
|
+
padding: 16px;
|
12
|
+
background-color: #fff;
|
13
|
+
border-radius: 6px;
|
14
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
15
|
+
position: relative;
|
16
|
+
}
|
17
|
+
|
18
|
+
.lo-db-show-container h1 {
|
19
|
+
margin-top: 0;
|
20
|
+
color: #333;
|
21
|
+
}
|
22
|
+
|
23
|
+
.lo-db-meta p {
|
24
|
+
margin: 0 0 5px;
|
25
|
+
}
|
26
|
+
|
27
|
+
.lo-db-part {
|
28
|
+
margin-bottom: 16px;
|
29
|
+
padding: 10px;
|
30
|
+
border: 1px solid #ccc;
|
31
|
+
background-color: #fafafa;
|
32
|
+
border-radius: 4px;
|
33
|
+
}
|
34
|
+
|
35
|
+
.lo-db-part h2 {
|
36
|
+
margin-top: 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
.lo-db-delete-form {
|
40
|
+
position: absolute;
|
41
|
+
top: 16px;
|
42
|
+
right: 16px;
|
43
|
+
}
|
44
|
+
|
45
|
+
.lo-db-delete-button {
|
46
|
+
padding: 6px 12px;
|
47
|
+
background-color: #c00;
|
48
|
+
color: #fff;
|
49
|
+
border: none;
|
50
|
+
border-radius: 4px;
|
51
|
+
cursor: pointer;
|
52
|
+
}
|
53
|
+
.lo-db-delete-button:hover {
|
54
|
+
background-color: #a00;
|
55
|
+
}
|
56
|
+
|
57
|
+
.lo-db-back-link {
|
58
|
+
display: inline-block;
|
59
|
+
margin-top: 16px;
|
60
|
+
padding: 6px 12px;
|
61
|
+
background-color: #333;
|
62
|
+
color: #fff;
|
63
|
+
text-decoration: none;
|
64
|
+
border-radius: 4px;
|
65
|
+
}
|
66
|
+
.lo-db-back-link:hover {
|
67
|
+
background-color: #555;
|
68
|
+
}
|
69
|
+
</style>
|
70
|
+
|
71
|
+
<div class="lo-db-show-container">
|
72
|
+
<%= form_with url: mail_record_path(@mail_record), method: :delete, local: true, class: "lo-db-delete-form" do %>
|
73
|
+
<%= button_tag "Delete",
|
74
|
+
class: "lo-db-delete-button" %>
|
75
|
+
<% end %>
|
76
|
+
|
77
|
+
<h1><%= @mail_record.subject %></h1>
|
78
|
+
|
79
|
+
<div class="lo-db-meta">
|
80
|
+
<p><strong>To:</strong> <%= @mail_record.to %></p>
|
81
|
+
<p><strong>From:</strong> <%= @mail_record.from %></p>
|
82
|
+
<p><strong>Sent At:</strong> <%= @mail_record.sent_at %></p>
|
83
|
+
</div>
|
84
|
+
|
85
|
+
<hr />
|
86
|
+
|
87
|
+
<% if @mail_record.html_part.present? %>
|
88
|
+
<div class="lo-db-part">
|
89
|
+
<h2>HTML Part</h2>
|
90
|
+
<%= raw @mail_record.body_html %>
|
91
|
+
</div>
|
92
|
+
<% end %>
|
93
|
+
|
94
|
+
<% if @mail_record.text_part.present? %>
|
95
|
+
<div class="lo-db-part">
|
96
|
+
<h2>Text Part</h2>
|
97
|
+
<pre><%= @mail_record.body_text %></pre>
|
98
|
+
</div>
|
99
|
+
<% end %>
|
100
|
+
|
101
|
+
<%= link_to "Back", mail_records_path, class: "lo-db-back-link" %>
|
102
|
+
</div>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "rails/generators/migration"
|
3
|
+
|
4
|
+
module LetterOpenerDb
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
8
|
+
|
9
|
+
def self.next_migration_number(_dirname)
|
10
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
11
|
+
end
|
12
|
+
|
13
|
+
def copy_migrations
|
14
|
+
migration_template(
|
15
|
+
"create_letter_opener_db_mail_records.rb",
|
16
|
+
"db/migrate/create_letter_opener_db_mail_records.rb"
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/generators/letter_opener_db/install/templates/create_letter_opener_db_mail_records.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateLetterOpenerDbMailRecords < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :letter_opener_db_mail_records do |t|
|
4
|
+
t.text :to
|
5
|
+
t.text :from
|
6
|
+
t.text :subject
|
7
|
+
t.text :html_part
|
8
|
+
t.text :text_part
|
9
|
+
t.datetime :sent_at
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :letter_opener_db_mail_records,
|
15
|
+
[:to, :subject, :html_part, :text_part],
|
16
|
+
name: "index_letter_opener_db_mail_records_on_to_and_subject_and_body"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module LetterOpenerDb
|
2
|
+
class DeliveryMethod
|
3
|
+
def initialize(values)
|
4
|
+
@settings = values
|
5
|
+
end
|
6
|
+
|
7
|
+
def deliver!(mail)
|
8
|
+
MailRecord.create!(
|
9
|
+
to: mail.to.join(", "),
|
10
|
+
from: mail.from.join(", "),
|
11
|
+
subject: mail.subject,
|
12
|
+
html_part: mail.html_part,
|
13
|
+
text_part: mail.text_part,
|
14
|
+
sent_at: Time.current
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module LetterOpenerDb
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
initializer "letter_opener_db.add_delivery_method" do
|
4
|
+
ActiveSupport.on_load(:action_mailer) do
|
5
|
+
require "letter_opener_db/delivery_method"
|
6
|
+
ActionMailer::Base.add_delivery_method :letter_opener_db, LetterOpenerDb::DeliveryMethod
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "letter_opener_db/version"
|
4
|
+
if defined?(Rails)
|
5
|
+
require "letter_opener_db/railtie"
|
6
|
+
require "letter_opener_db/engine"
|
7
|
+
require "generators/letter_opener_db/install/install_generator"
|
8
|
+
require "letter_opener_db/delivery_method"
|
9
|
+
end
|
10
|
+
|
11
|
+
module LetterOpenerDb
|
12
|
+
class Error < StandardError; end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: letter_opener_db
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Takahashi
|
8
|
+
- Seiya Takahashi
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2025-02-22 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A gem that stores ActionMailer emails in the database and allows searching.
|
15
|
+
email:
|
16
|
+
- seiya4@icloud.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".rspec"
|
22
|
+
- LICENSE.txt
|
23
|
+
- README.md
|
24
|
+
- README_JA.md
|
25
|
+
- Rakefile
|
26
|
+
- app/controllers/letter_opener_db/mail_records_controller.rb
|
27
|
+
- app/models/letter_opener_db/mail_record.rb
|
28
|
+
- app/views/letter_opener_db/mail_records/index.html.erb
|
29
|
+
- app/views/letter_opener_db/mail_records/show.html.erb
|
30
|
+
- config/routes.rb
|
31
|
+
- lib/generators/letter_opener_db/install/install_generator.rb
|
32
|
+
- lib/generators/letter_opener_db/install/templates/create_letter_opener_db_mail_records.rb
|
33
|
+
- lib/letter_opener_db.rb
|
34
|
+
- lib/letter_opener_db/delivery_method.rb
|
35
|
+
- lib/letter_opener_db/engine.rb
|
36
|
+
- lib/letter_opener_db/railtie.rb
|
37
|
+
- lib/letter_opener_db/version.rb
|
38
|
+
- lib/tasks/letter_opener_db.rake
|
39
|
+
- sig/letter_opener_db.rbs
|
40
|
+
homepage: https://github.com/PeterTakahashi/letter_opener_db
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata:
|
44
|
+
homepage_uri: https://github.com/PeterTakahashi/letter_opener_db
|
45
|
+
source_code_uri: https://github.com/PeterTakahashi/letter_opener_db
|
46
|
+
changelog_uri: https://github.com/PeterTakahashi/letter_opener_db/blob/main/CHANGELOG.md
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
- app
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '2.6'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubygems_version: 3.5.11
|
64
|
+
signing_key:
|
65
|
+
specification_version: 4
|
66
|
+
summary: Save mail sending history to DB (like letter_opener)
|
67
|
+
test_files: []
|