enkimail 0.1.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e5ca7bbe0f36972c0f3291cf76789a7b09743c58f1730fae9f722c7d19c9bd7f
4
+ data.tar.gz: 8f6ddd5b31ca53ed0f198a74a982f7ffe72c0ee9b3b9f10b79aa681ebe71323b
5
+ SHA512:
6
+ metadata.gz: 9250bafefcc6d06f1e925532031981fd498e077587f42931af275b55de44495b466249dfab69d4b199181c1baf6e63fc8c0903f5ff5fd8493f0d9ff95ecbf979
7
+ data.tar.gz: cf20324020a395c486b5863216c98ace782703b3c490c2b12cb7904d38484b7728a9529e0fb42d7351ef95e1176b20220e455f34acc41d75d7176d752d5f391e
data/.rspec_status ADDED
@@ -0,0 +1,6 @@
1
+ example_id | status | run_time |
2
+ ---------------------------------------------- | ------ | --------------- |
3
+ ./spec/enkimail/client_spec.rb[1:1:1] | passed | 0.39165 seconds |
4
+ ./spec/enkimail/client_spec.rb[1:1:2] | passed | 0.02099 seconds |
5
+ ./spec/enkimail/delivery_method_spec.rb[1:1:1] | passed | 0.07246 seconds |
6
+ ./spec/enkimail/delivery_method_spec.rb[1:1:2] | passed | 0.00045 seconds |
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Albert Oliva
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,117 @@
1
+ # Enkimail Ruby Gem
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/enkimail.svg)](https://badge.fury.io/rb/enkimail)
4
+
5
+ `enkimail` is the official gem for integrating [Enkimail](https://enkimail.com) transactional email service with Ruby on Rails. It allows you to send emails using Enkimail's infrastructure simply by configuring the ActionMailer `delivery_method`.
6
+
7
+ ## Features
8
+
9
+ * **Native Rails Integration:** Automatically registers as an ActionMailer delivery method.
10
+ * **Multipart Support:** Automatically handles HTML and plain text email bodies.
11
+ * **Attachments:** Built-in support for sending attachments (automatically Base64 encoded for the API).
12
+ * **Flexible Configuration:** Allows defining a `base_url` for testing in development or staging environments.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's `Gemfile`:
17
+
18
+ ```ruby
19
+ gem 'enkimail'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle install
25
+
26
+ Or install it manually:
27
+
28
+ $ gem install enkimail
29
+
30
+ ## Prerequisites
31
+
32
+ Before using the gem, ensure that:
33
+ 1. **Verified Domain:** Your sending domain must be verified in the Enkimail dashboard.
34
+ 2. **Verified Sender:** The `from` email address you use must be a verified sender in your Enkimail account.
35
+
36
+ Emails sent from unverified domains or senders will be rejected by the API.
37
+
38
+ ## Configuration in Rails
39
+
40
+ To use Enkimail as your email provider, edit your environment configuration file (e.g., `config/environments/production.rb`):
41
+
42
+ ```ruby
43
+ Rails.application.configure do
44
+ # ...
45
+ config.action_mailer.delivery_method = :enkimail
46
+ config.action_mailer.enkimail_settings = {
47
+ api_key: ENV['ENKIMAIL_API_KEY']
48
+ }
49
+
50
+ # Optional: Configure a global 'from' address
51
+ # This address MUST be a verified sender in your Enkimail dashboard.
52
+ config.action_mailer.default_options = { from: 'no-reply@yourdomain.com' }
53
+ end
54
+ ```
55
+
56
+ ### Configuration Options
57
+
58
+ | Option | Description | Required |
59
+ | :--- | :--- | :--- |
60
+ | `api_key` | Your Enkimail API Key. | Yes |
61
+ | `base_url` | API base URL (defaults to `https://api.enkimail.com`). | No |
62
+
63
+ ## Usage
64
+
65
+ You can set the `from` address globally in your `ApplicationMailer` or specifically in each mailer method. Remember that Enkimail requires the sender to be verified.
66
+
67
+ ### Setting a Default Sender
68
+
69
+ ```ruby
70
+ class ApplicationMailer < ActionMailer::Base
71
+ default from: 'info@yourdomain.com'
72
+ layout 'mailer'
73
+ end
74
+ ```
75
+
76
+ ### Overriding the Sender in a Specific Mailer
77
+
78
+ ```ruby
79
+ class UserMailer < ApplicationMailer
80
+ def welcome_email(user)
81
+ @user = user
82
+ mail(
83
+ to: @user.email,
84
+ subject: 'Welcome to Enkimail',
85
+ from: 'onboarding@yourdomain.com' # Must be a verified sender
86
+ )
87
+ end
88
+ end
89
+ ```
90
+
91
+ ### Sending Attachments
92
+
93
+ The gem handles attachment processing automatically:
94
+
95
+ ```ruby
96
+ def invoice_email(user, invoice_pdf)
97
+ attachments['invoice.pdf'] = invoice_pdf
98
+ mail(to: user.email, subject: 'Your Invoice')
99
+ end
100
+ ```
101
+
102
+ ## Local Development
103
+
104
+ If you are developing the Enkimail API locally and want to test the gem against your local Rails instance, you can configure the `base_url`:
105
+
106
+ ```ruby
107
+ # config/environments/development.rb
108
+ config.action_mailer.delivery_method = :enkimail
109
+ config.action_mailer.enkimail_settings = {
110
+ api_key: 'test_key',
111
+ base_url: 'http://localhost:3000'
112
+ }
113
+ ```
114
+
115
+ ## License
116
+
117
+ 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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+
6
+ module Enkimail
7
+ class Client
8
+ DEFAULT_BASE_URL = "https://api.enkimail.com"
9
+
10
+ attr_reader :api_key, :base_url
11
+
12
+ def initialize(api_key, base_url: nil)
13
+ @api_key = api_key
14
+ @base_url = (base_url || DEFAULT_BASE_URL).chomp("/")
15
+ end
16
+
17
+ def deliver(mail)
18
+ response = connection.post("/api/v1/transactional_emails") do |req|
19
+ req.body = build_payload(mail)
20
+ end
21
+
22
+ handle_response(response)
23
+ rescue Faraday::Error => e
24
+ raise Error, "Enkimail Connection Error: #{e.message}"
25
+ end
26
+
27
+ private
28
+
29
+ def connection
30
+ @connection ||= Faraday.new(url: base_url) do |conn|
31
+ conn.request :json
32
+ conn.headers["Authorization"] = "Bearer #{api_key}"
33
+ conn.headers["Content-Type"] = "application/json"
34
+ conn.adapter Faraday.default_adapter
35
+ end
36
+ end
37
+
38
+ def build_payload(mail)
39
+ payload = {
40
+ from: (mail.from || []).join(", "),
41
+ to: (mail.to || []).join(", "),
42
+ cc: (mail.cc || []).join(", "),
43
+ bcc: (mail.bcc || []).join(", "),
44
+ subject: mail.subject,
45
+ attachments: build_attachments(mail)
46
+ }
47
+
48
+ if mail.multipart?
49
+ payload[:body] = mail.text_part&.body&.decoded
50
+ payload[:html_body] = mail.html_part&.body&.decoded
51
+ else
52
+ if mail.content_type =~ /html/
53
+ payload[:html_body] = mail.body&.decoded
54
+ else
55
+ payload[:body] = mail.body&.decoded
56
+ end
57
+ end
58
+
59
+ # Clean up nil values
60
+ payload.compact
61
+ end
62
+
63
+ def build_attachments(mail)
64
+ return nil unless mail.attachments.any?
65
+
66
+ mail.attachments.map do |attachment|
67
+ {
68
+ name: attachment.filename,
69
+ content: [attachment.body.decoded].pack("m0"), # Base64
70
+ content_type: attachment.content_type.split(";").first
71
+ }
72
+ end
73
+ end
74
+
75
+ def handle_response(response)
76
+ return response if response.success?
77
+
78
+ begin
79
+ error_info = JSON.parse(response.body)
80
+ error_message = error_info["error"] || error_info["errors"] || "Unknown API error"
81
+ rescue JSON::ParserError
82
+ error_message = "HTTP #{response.status}: #{response.body[0..100]}"
83
+ end
84
+
85
+ raise Error, "Enkimail API Error (#{response.status}): #{error_message}"
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "client"
4
+
5
+ module Enkimail
6
+ class DeliveryMethod
7
+ attr_accessor :settings
8
+
9
+ def initialize(settings)
10
+ @settings = settings
11
+ end
12
+
13
+ def deliver!(mail)
14
+ validate_settings!
15
+ client = Client.new(settings[:api_key], base_url: settings[:base_url])
16
+ client.deliver(mail)
17
+ end
18
+
19
+ private
20
+
21
+ def validate_settings!
22
+ raise Error, "Enkimail API Key is missing. Please set config.action_mailer.enkimail_settings = { api_key: '...' }" if settings[:api_key].nil? || settings[:api_key].empty?
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Enkimail
4
+ VERSION = "0.1.5"
5
+ end
data/lib/enkimail.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "enkimail/version"
4
+ require_relative "enkimail/client"
5
+ require_relative "enkimail/delivery_method"
6
+
7
+ module Enkimail
8
+ class Error < StandardError; end
9
+
10
+ # Rails integration
11
+ if defined?(ActionMailer)
12
+ ActionMailer::Base.add_delivery_method :enkimail, Enkimail::DeliveryMethod
13
+ end
14
+ end
data/sig/enkimail.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Enkimail
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Enkimail::Client do
6
+ let(:api_key) { "test_api_key" }
7
+ let(:client) { described_class.new(api_key) }
8
+ let(:mail) do
9
+ Mail.new do
10
+ from "Sender Name <sender@example.com>"
11
+ to "Recipient Name <recipient@example.com>"
12
+ subject "Test Subject"
13
+ body "Hello world"
14
+
15
+ add_file filename: "test.txt", content: "test content"
16
+ end
17
+ end
18
+
19
+ describe "#deliver" do
20
+ let(:endpoint) { "https://api.enkimail.com/api/v1/transactional_emails" }
21
+
22
+ before do
23
+ stub_request(:post, endpoint)
24
+ .with(
25
+ headers: {
26
+ "Authorization" => "Bearer #{api_key}",
27
+ "Content-Type" => "application/json"
28
+ }
29
+ )
30
+ .to_return(status: 200, body: { status: "success" }.to_json)
31
+ end
32
+
33
+ it "sends the correct payload to the API" do
34
+ client.deliver(mail)
35
+
36
+ expect(a_request(:post, endpoint).with { |req|
37
+ payload = JSON.parse(req.body)
38
+ expect(payload["from"]).to eq("Sender Name <sender@example.com>")
39
+ expect(payload["to"]).to eq("Recipient Name <recipient@example.com>")
40
+ expect(payload["subject"]).to eq("Test Subject")
41
+ expect(payload["body"]).to eq("Hello world")
42
+ expect(payload["attachments"]).to be_an(Array)
43
+ expect(payload["attachments"].first["name"]).to eq("test.txt")
44
+ expect(payload["attachments"].first["content"]).to eq([ "test content" ].pack("m0"))
45
+ expect(payload["attachments"].first["content_type"]).to eq("text/plain")
46
+ }).to have_been_made.once
47
+ end
48
+
49
+ it "handles API errors correctly" do
50
+ stub_request(:post, endpoint).to_return(
51
+ status: 401,
52
+ body: { error: "Invalid API Key" }.to_json
53
+ )
54
+
55
+ expect {
56
+ client.deliver(mail)
57
+ }.to raise_error(Enkimail::Error, /Invalid API Key/)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Enkimail::DeliveryMethod do
6
+ let(:settings) { { api_key: "abc_123" } }
7
+ let(:delivery_method) { described_class.new(settings) }
8
+ let(:mail) { Mail.new }
9
+
10
+ describe "#deliver!" do
11
+ it "initializes the client and calls deliver" do
12
+ client_double = instance_double(Enkimail::Client)
13
+ expect(Enkimail::Client).to receive(:new).with("abc_123", base_url: nil).and_return(client_double)
14
+ expect(client_double).to receive(:deliver).with(mail)
15
+
16
+ delivery_method.deliver!(mail)
17
+ end
18
+
19
+ it "raises error if api_key is missing" do
20
+ bad_delivery = described_class.new({})
21
+ expect {
22
+ bad_delivery.deliver!(mail)
23
+ }.to raise_error(Enkimail::Error, /API Key is missing/)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "enkimail"
4
+ require "webmock/rspec"
5
+ require "mail"
6
+
7
+ RSpec.configure do |config|
8
+ # Enable flags like --only-failures and --next-failure
9
+ config.example_status_persistence_file_path = ".rspec_status"
10
+
11
+ # Disable RSpec exposing methods globally on `Module` and `main`
12
+ config.disable_monkey_patching!
13
+
14
+ config.expect_with :rspec do |c|
15
+ c.syntax = :expect
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: enkimail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.5
5
+ platform: ruby
6
+ authors:
7
+ - Albert Oliva
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-03-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mail
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '2.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '2.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ description: Integrate Rails with Enkimail API to send emails seamlessly.
84
+ email:
85
+ - albert.oliva@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".rspec_status"
91
+ - LICENSE.txt
92
+ - README.md
93
+ - Rakefile
94
+ - lib/enkimail.rb
95
+ - lib/enkimail/client.rb
96
+ - lib/enkimail/delivery_method.rb
97
+ - lib/enkimail/version.rb
98
+ - sig/enkimail.rbs
99
+ - spec/enkimail/client_spec.rb
100
+ - spec/enkimail/delivery_method_spec.rb
101
+ - spec/spec_helper.rb
102
+ homepage: https://github.com/enkimail/enkimail-ruby
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ allowed_push_host: https://rubygems.org
107
+ homepage_uri: https://github.com/enkimail/enkimail-ruby
108
+ source_code_uri: https://github.com/enkimail/enkimail-ruby
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: 3.2.0
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: Enkimail delivery method for ActionMailer
128
+ test_files: []