lockally-rails 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/LICENSE +21 -0
- data/README.md +55 -0
- data/lib/lockally/rails/delivery_method.rb +112 -0
- data/lib/lockally/rails/railtie.rb +17 -0
- data/lib/lockally/rails/version.rb +7 -0
- data/lib/lockally/rails.rb +14 -0
- metadata +128 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8ea1171ccc4dd2969fd75c5c18484b89cc350994b44293e2ac3d425ff1c0cdf7
|
|
4
|
+
data.tar.gz: 5195841fe6c9cac0988904b7bb9965ad3a239ffe5fd071a3a75a026223c6466d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f8629d1a8a23b2f74a9ea37a4acc605670e13446c59c9c988fd9fadb6819ab7cd88ec0a15a939838dee1f327d72afe5dc0100378dc38c573395c2bfe2f922e8b
|
|
7
|
+
data.tar.gz: fc81ead5b95f2693d2fd047e17306973e5507e01c52ec32bc46bb3957c4ffc84d60bef14541861b86eb66a27085647a03599bb464852a9b3afec67df2d6d470c
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lockally
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# lockally-rails
|
|
2
|
+
|
|
3
|
+
Official [Lockally](https://lockally.com) ActionMailer adapter for Rails. Point Rails
|
|
4
|
+
mail at Lockally and every `deliver`/`deliver_later` — from mailers, Devise, Action
|
|
5
|
+
Mailbox bounces, anything — is delivered by Lockally with no other changes.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# Gemfile
|
|
11
|
+
gem "lockally-rails"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bundle install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Configure
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# config/environments/production.rb
|
|
22
|
+
config.action_mailer.delivery_method = :lockally
|
|
23
|
+
config.action_mailer.lockally_settings = {
|
|
24
|
+
api_key: ENV["LOCKALLY_API_KEY"], # lk_test_… runs in sandbox
|
|
25
|
+
# base_url: "https://api.lockally.com", # optional override
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The railtie registers the `:lockally` delivery method automatically when Rails boots.
|
|
30
|
+
|
|
31
|
+
## Send
|
|
32
|
+
|
|
33
|
+
Nothing special — use ActionMailer as usual:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
class UserMailer < ApplicationMailer
|
|
37
|
+
def welcome(user)
|
|
38
|
+
mail(from: "alerts@yourdomain.com", to: user.email, subject: "Welcome",
|
|
39
|
+
reply_to: "support@yourdomain.com")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
UserMailer.welcome(user).deliver_later
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Mapping notes
|
|
47
|
+
|
|
48
|
+
- **Reply-To** is sent as a header (Lockally has no `reply_to` field).
|
|
49
|
+
- **Attachments** are base64-encoded as `{ filename, content_type, content_base64 }`.
|
|
50
|
+
- Multipart mail maps its text and HTML parts to `text` / `html`.
|
|
51
|
+
- One `Idempotency-Key` is generated per send (24 h dedupe window).
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT © Lockally
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "base64"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
require "lockally"
|
|
6
|
+
|
|
7
|
+
module Lockally
|
|
8
|
+
module Rails
|
|
9
|
+
# ActionMailer delivery method that sends through the Lockally API (POST /v1/send).
|
|
10
|
+
#
|
|
11
|
+
# Registered as `:lockally` by the railtie, so:
|
|
12
|
+
#
|
|
13
|
+
# config.action_mailer.delivery_method = :lockally
|
|
14
|
+
# config.action_mailer.lockally_settings = { api_key: ENV["LOCKALLY_API_KEY"] }
|
|
15
|
+
#
|
|
16
|
+
# routes every ActionMailer `deliver`/`deliver_later` through Lockally.
|
|
17
|
+
class DeliveryMethod
|
|
18
|
+
attr_reader :settings
|
|
19
|
+
|
|
20
|
+
def initialize(settings = {})
|
|
21
|
+
@settings = settings || {}
|
|
22
|
+
@send_api = settings[:send_api] # injectable for tests
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# ActionMailer calls this with a Mail::Message.
|
|
26
|
+
def deliver!(mail)
|
|
27
|
+
send_api.v1_send_post(idempotency_key, build_request(mail))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Map a Mail::Message onto the Lockally send request. Public for unit testing.
|
|
31
|
+
def build_request(mail)
|
|
32
|
+
::Lockally::V1SendPostRequest.new(build_payload(mail))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_payload(mail)
|
|
36
|
+
payload = {
|
|
37
|
+
from: Array(mail.from).first,
|
|
38
|
+
to: Array(mail.to),
|
|
39
|
+
}
|
|
40
|
+
payload[:cc] = Array(mail.cc) if mail.cc
|
|
41
|
+
payload[:bcc] = Array(mail.bcc) if mail.bcc
|
|
42
|
+
payload[:subject] = mail.subject if mail.subject
|
|
43
|
+
|
|
44
|
+
text, html = bodies(mail)
|
|
45
|
+
payload[:text] = text if text
|
|
46
|
+
payload[:html] = html if html
|
|
47
|
+
|
|
48
|
+
headers = custom_headers(mail)
|
|
49
|
+
payload[:headers] = headers unless headers.empty?
|
|
50
|
+
|
|
51
|
+
attachments = mail.attachments.map do |part|
|
|
52
|
+
::Lockally::V1SendPostRequestAttachmentsInner.new(
|
|
53
|
+
filename: part.filename || "attachment",
|
|
54
|
+
content_type: part.mime_type || "application/octet-stream",
|
|
55
|
+
content_base64: Base64.strict_encode64(part.body.decoded),
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
payload[:attachments] = attachments unless attachments.empty?
|
|
59
|
+
|
|
60
|
+
payload
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# Returns [text, html]. Handles multipart and single-part messages.
|
|
66
|
+
def bodies(mail)
|
|
67
|
+
if mail.multipart?
|
|
68
|
+
text = mail.text_part&.body&.decoded
|
|
69
|
+
html = mail.html_part&.body&.decoded
|
|
70
|
+
[text, html]
|
|
71
|
+
elsif mail.mime_type == "text/html"
|
|
72
|
+
[nil, mail.body.decoded]
|
|
73
|
+
else
|
|
74
|
+
[mail.body.decoded, nil]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Lockally has no reply_to field, so Reply-To is carried in headers; standard
|
|
79
|
+
# MIME headers the Mail gem manages are skipped.
|
|
80
|
+
def custom_headers(mail)
|
|
81
|
+
managed = %w[from to cc bcc subject reply-to sender date message-id
|
|
82
|
+
content-type content-transfer-encoding mime-version]
|
|
83
|
+
out = {}
|
|
84
|
+
out["Reply-To"] = Array(mail.reply_to).join(", ") if mail.reply_to
|
|
85
|
+
mail.header.fields.each do |field|
|
|
86
|
+
next if managed.include?(field.name.downcase)
|
|
87
|
+
|
|
88
|
+
out[field.name] = field.value.to_s
|
|
89
|
+
end
|
|
90
|
+
out
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def send_api
|
|
94
|
+
@send_api ||= begin
|
|
95
|
+
config = ::Lockally::Configuration.new
|
|
96
|
+
config.access_token = settings[:api_key]
|
|
97
|
+
if settings[:base_url]
|
|
98
|
+
require "uri"
|
|
99
|
+
uri = URI.parse(settings[:base_url])
|
|
100
|
+
config.scheme = uri.scheme if uri.scheme
|
|
101
|
+
config.host = [uri.host, uri.port].compact.join(":")
|
|
102
|
+
end
|
|
103
|
+
::Lockally::SendApi.new(::Lockally::ApiClient.new(config))
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def idempotency_key
|
|
108
|
+
"lk-#{SecureRandom.hex(16)}"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/railtie"
|
|
4
|
+
require "lockally/rails/delivery_method"
|
|
5
|
+
|
|
6
|
+
module Lockally
|
|
7
|
+
module Rails
|
|
8
|
+
# Registers the `:lockally` ActionMailer delivery method when Rails boots.
|
|
9
|
+
class Railtie < ::Rails::Railtie
|
|
10
|
+
initializer "lockally.add_delivery_method" do
|
|
11
|
+
ActiveSupport.on_load(:action_mailer) do
|
|
12
|
+
add_delivery_method(:lockally, Lockally::Rails::DeliveryMethod)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lockally"
|
|
4
|
+
require "lockally/rails/version"
|
|
5
|
+
require "lockally/rails/delivery_method"
|
|
6
|
+
# Load the railtie only when running inside Rails (so the delivery method can also
|
|
7
|
+
# be used standalone, e.g. in tests or non-Rails apps).
|
|
8
|
+
require "lockally/rails/railtie" if defined?(::Rails::Railtie)
|
|
9
|
+
|
|
10
|
+
module Lockally
|
|
11
|
+
# ActionMailer adapter for Lockally. See Lockally::Rails::DeliveryMethod.
|
|
12
|
+
module Rails
|
|
13
|
+
end
|
|
14
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lockally-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Lockally
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: lockally
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.1'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '1'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0.1'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: mail
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.7'
|
|
40
|
+
type: :runtime
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.7'
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: railties
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '6.1'
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '6.1'
|
|
61
|
+
- !ruby/object:Gem::Dependency
|
|
62
|
+
name: minitest
|
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '5.0'
|
|
68
|
+
type: :development
|
|
69
|
+
prerelease: false
|
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '5.0'
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: rake
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '13.0'
|
|
82
|
+
type: :development
|
|
83
|
+
prerelease: false
|
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '13.0'
|
|
89
|
+
description: Send transactional email from Rails through Lockally by setting config.action_mailer.delivery_method
|
|
90
|
+
= :lockally.
|
|
91
|
+
email:
|
|
92
|
+
- support@lockally.com
|
|
93
|
+
executables: []
|
|
94
|
+
extensions: []
|
|
95
|
+
extra_rdoc_files: []
|
|
96
|
+
files:
|
|
97
|
+
- LICENSE
|
|
98
|
+
- README.md
|
|
99
|
+
- lib/lockally/rails.rb
|
|
100
|
+
- lib/lockally/rails/delivery_method.rb
|
|
101
|
+
- lib/lockally/rails/railtie.rb
|
|
102
|
+
- lib/lockally/rails/version.rb
|
|
103
|
+
homepage: https://github.com/lockallyinc/lockally-rails
|
|
104
|
+
licenses:
|
|
105
|
+
- MIT
|
|
106
|
+
metadata:
|
|
107
|
+
homepage_uri: https://lockally.com
|
|
108
|
+
source_code_uri: https://github.com/lockallyinc/lockally-rails
|
|
109
|
+
post_install_message:
|
|
110
|
+
rdoc_options: []
|
|
111
|
+
require_paths:
|
|
112
|
+
- lib
|
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '2.7'
|
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - ">="
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '0'
|
|
123
|
+
requirements: []
|
|
124
|
+
rubygems_version: 3.5.22
|
|
125
|
+
signing_key:
|
|
126
|
+
specification_version: 4
|
|
127
|
+
summary: Official Lockally ActionMailer adapter for Rails
|
|
128
|
+
test_files: []
|