customerio-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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 67c845f6f2c71391044755e3412a1674f24a129dab76f841455eeebf135fc897
4
+ data.tar.gz: 21785d8a8cd965fa463ce6ebbc2ca8c3336bff9cd1b195a923ebebf129025bd8
5
+ SHA512:
6
+ metadata.gz: f07811ee4d2cc6e212327f39537ce20a7ac681af323dd0b249d8fcd5cf185ca40d4b4e03f94599b413ff706e10e807b59d303755c5aac5296952ce1527233a66
7
+ data.tar.gz: 30664d979cba3887204f723ecccd2506360b871061f8d6f31a23dfb0ccc4cc97981ff0789092100610aa92c6c5a7ecaf03fe2cbab94723b92f1ffdf1cbfd01f2
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,4 @@
1
+ = Changelog
2
+
3
+ == 0.1.0
4
+ * Initial release, inspired by https://github.com/ActiveCampaign/postmark-rails
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
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,64 @@
1
+ # Customer.io Rails Gem
2
+
3
+ [![License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://www.opensource.org/licenses/MIT)
4
+ [![Gem Version](https://badge.fury.io/rb/customerio-rails.svg)](https://badge.fury.io/rb/customerio-rails)
5
+
6
+ The `customerio-rails` gem is a drop-in ActionMailer adapter to send emails via [Customer.io](https://customer.io).
7
+ The gem has been created for fast implementation and targets Customer.io’s transactional email capabilities.
8
+
9
+ ## Usage
10
+
11
+ ## Requirements
12
+
13
+ You will need a [Customer.io](https://customer.io/) account set up to use it.
14
+
15
+ ### Supported Rails Versions
16
+
17
+ - Rails 8.0
18
+ - Rails 7.0
19
+
20
+ ### App API Key
21
+
22
+ Generate an **App API Key** (this is different than the pair _Site ID_ / _API Key_) from your _Workspace Settings_ > Your Workspace > _Manage API Credentials_ > _App API Keys_.
23
+
24
+ ## Installation
25
+
26
+ Add `customerio-rails` to your Gemfile and run `bundle install`.
27
+
28
+ ```ruby
29
+ gem 'customerio-rails'
30
+ ```
31
+
32
+ Set Customer.io as your preferred mail delivery method via `config/application.rb`:
33
+
34
+ ```ruby
35
+ config.action_mailer.delivery_method = :customerio
36
+ config.action_mailer.customerio_settings = { app_api_key: '<Your APP API Key>', region: Customerio::Regions::US }
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ Use it as you would usually:
42
+
43
+ ```ruby
44
+ class MyMailer < ApplicationMailer
45
+ def whatever # whatever.{html,text}.erb will be used as body
46
+ mail to: 'someone@somewhere.com', subject: 'Hello from Rails!'
47
+ end
48
+ end
49
+ ```
50
+
51
+ Or rely on Customer.io's templated messages with:
52
+
53
+ ```ruby
54
+ class MyMailer < ApplicationMailer
55
+ def whatever_with_template
56
+ mail to: 'someone@somewhere.com', subject: 'Hello from Rails!', transactional_message_id: 3, message_data: { myvar: 'a value', another_one: 42x }
57
+ end
58
+ end
59
+ ```
60
+
61
+ ## License
62
+
63
+ The Customer.io Rails gem is licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php) license.
64
+ Refer to the [LICENSE](./LICENSE) file for more information.
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CustomerioRails
4
+ module Mail
5
+ class Customerio
6
+ attr_accessor :settings
7
+
8
+ def initialize(values)
9
+ self.settings = {
10
+ region: ENV['CUSTOMERIO_REGION'] || ::Customerio::Regions::US,
11
+ app_api_key: ENV['CUSTOMERIO_APP_API_KEY']
12
+ }.merge(values)
13
+ end
14
+
15
+ def deliver!(mail)
16
+ (Array.wrap(mail.to) + Array.wrap(mail.cc)).compact.each do |to|
17
+ params = {
18
+ to: to,
19
+ from: Array.wrap(mail.from).first,
20
+ subject: mail.subject,
21
+ reply_to: mail.reply_to,
22
+ bcc: mail.bcc,
23
+ headers: mail.headers,
24
+ identifiers: { email: to }
25
+ }
26
+ if mail[:transactional_message_id]
27
+ params[:transactional_message_id] = mail[:transactional_message_id].unparsed_value
28
+ params[:message_data] = mail[:message_data]&.unparsed_value || {}
29
+ else
30
+ params[:body] = mail.html_part&.body&.to_s || mail.body.to_s
31
+ params[:body_plain] = mail.text_part&.body&.to_s
32
+ end
33
+ request = ::Customerio::SendEmailRequest.new(params.compact)
34
+ mail.attachments.each do |attachment|
35
+ request.attach(attachment.filename, attachment.read)
36
+ end
37
+ api_client.send_email(request)
38
+ end
39
+ end
40
+
41
+ def api_client
42
+ ::Customerio::APIClient.new(settings[:app_api_key], region: settings[:region])
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CustomerioRails
4
+ class Railtie < Rails::Railtie
5
+ initializer 'customerio-rails', :before => 'action_mailer.set_configs' do
6
+ ActiveSupport.on_load :action_mailer do
7
+ CustomerioRails.install
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CustomerioRails
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/rescuable'
4
+ require 'action_mailer'
5
+ require 'customerio'
6
+
7
+ module CustomerioRails
8
+ module WithTemplatedMessageMailer
9
+ def mail(*args, **kwargs)
10
+ if kwargs[:transactional_message_id].present?
11
+ # force a blank body to avoid looking for a template locally
12
+ kwargs[:body] = ''
13
+ end
14
+ super
15
+ end
16
+ end
17
+
18
+ def self.install
19
+ require 'customerio-rails/mail'
20
+ ActionMailer::Base.add_delivery_method :customerio, CustomerioRails::Mail::Customerio
21
+ ActionMailer::Base.prepend(WithTemplatedMessageMailer)
22
+ end
23
+ end
24
+
25
+ if defined?(Rails)
26
+ require 'customerio-rails/railtie'
27
+ else
28
+ CustomerioRails.install
29
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "CustomerioRails" do
6
+ let!(:api_client) { Customerio::APIClient.new('app-api-token') }
7
+
8
+ def deliver(message)
9
+ if message.respond_to?(:deliver_now)
10
+ message.deliver_now
11
+ else
12
+ message.deliver
13
+ end
14
+ end
15
+
16
+ before do
17
+ allow(Customerio::APIClient).to receive(:new) { api_client }
18
+ end
19
+
20
+ it 'should allow setting an app api key' do
21
+ ActionMailer::Base.customerio_settings = {:app_api_key => 'app-api-token'}
22
+ expect(ActionMailer::Base.customerio_settings[:app_api_key]).to eq('app-api-token')
23
+ end
24
+
25
+ it "should use customerio for delivery" do
26
+ allow(api_client).to receive(:send_email) do |message|
27
+ expect(message.message[:subject]).to eq("hello")
28
+ end
29
+ deliver(TestMailer.simple_message)
30
+ end
31
+
32
+ it "should work with multipart messages" do
33
+ allow(api_client).to receive(:send_email) do |message|
34
+ expect(message.message[:body].strip).to eq("<b>hello</b>")
35
+ expect(message.message[:body_plain].strip).to eq("hello")
36
+ end
37
+ deliver(TestMailer.multipart_message)
38
+ end
39
+
40
+ it 'should work with messages containing attachments' do
41
+ allow(api_client).to receive(:send_email) do |message|
42
+ expect(message.message[:attachments]).not_to be_empty
43
+ end
44
+ deliver(TestMailer.message_with_attachment)
45
+ end
46
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Delivering messages with customerio-rails' do
6
+ let(:app_api_key) { 'CUSTOMERIO_APP_API_KEY' }
7
+
8
+ before do
9
+ ActionMailer::Base.customerio_settings = { app_api_key: app_api_key }
10
+ response = Net::HTTPSuccess.new(1.0, 200, '{}')
11
+ allow(response).to receive(:body).and_return('{}')
12
+ mocked_http = double('mocked_http')
13
+ allow(mocked_http).to receive(:request).with(satisfy { |request|
14
+ expect(Array.wrap(expected_body)).to include(JSON.parse(request.body))
15
+ }).and_return(response)
16
+ allow_any_instance_of(Net::HTTP).to receive(:start).and_yield(mocked_http)
17
+ end
18
+
19
+ context 'when delivering a simple message' do
20
+ let(:expected_body) do
21
+ { 'to' => 'sheldon@bigbangtheory.com', 'from' => 'leonard@bigbangtheory.com', 'subject' => 'hello',
22
+ 'body' => "hello\n", 'headers' => {}, 'identifiers' => { 'email' => 'sheldon@bigbangtheory.com' }, 'attachments' => {} }
23
+ end
24
+
25
+ let(:message) { TestMailer.simple_message }
26
+
27
+ it do
28
+ message.deliver!
29
+ end
30
+
31
+ context 'with a reply-to address' do
32
+ let(:expected_body) do
33
+ super().merge('reply_to' => ['raj@bigbangtheory.com'])
34
+ end
35
+
36
+ it do
37
+ message.reply_to = 'raj@bigbangtheory.com'
38
+ message.deliver!
39
+ end
40
+ end
41
+
42
+ context 'with a bcc address' do
43
+ let(:expected_body) do
44
+ super().merge('bcc' => ['raj@bigbangtheory.com'])
45
+ end
46
+
47
+ it do
48
+ message.bcc = 'raj@bigbangtheory.com'
49
+ message.deliver!
50
+ end
51
+ end
52
+
53
+ context 'with a cc address' do
54
+ let(:expected_body) do
55
+ [super(), super().merge('to' => 'raj@bigbangtheory.com', 'identifiers' => { 'email' => 'raj@bigbangtheory.com' })]
56
+ end
57
+
58
+ it do
59
+ message.cc = 'raj@bigbangtheory.com'
60
+ message.deliver!
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'when delivering a multipart message' do
66
+ let(:expected_body) do
67
+ { 'to' => 'sheldon@bigbangtheory.com', 'from' => 'leonard@bigbangtheory.com',
68
+ 'subject' => 'Your invitation to join Mixlr.', 'body' => "<b>hello</b>\n", "body_plain" => "hello\n", 'headers' => {}, 'identifiers' => { 'email' => 'sheldon@bigbangtheory.com' }, 'attachments' => {} }
69
+ end
70
+
71
+ it do
72
+ message = TestMailer.multipart_message
73
+ message.deliver!
74
+ end
75
+ end
76
+
77
+ context 'when delivering a message with attachments' do
78
+ let(:expected_body) do
79
+ { 'to' => 'sheldon@bigbangtheory.com', 'from' => 'leonard@bigbangtheory.com',
80
+ 'subject' => 'Message with attachment.', 'body' => 'attachments?', 'headers' => {}, 'identifiers' => { 'email' => 'sheldon@bigbangtheory.com' }, 'attachments' => { 'empty.gif' => 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==' } }
81
+ end
82
+
83
+ it do
84
+ message = TestMailer.message_with_attachment
85
+ message.deliver!
86
+ end
87
+ end
88
+
89
+ context 'when delivering a message with inline image' do
90
+ let(:message) { TestMailer.message_with_inline_image }
91
+
92
+ let(:expected_body) do
93
+ { 'to' => 'sheldon@bigbangtheory.com', 'from' => 'leonard@bigbangtheory.com',
94
+ 'subject' => 'Message with inline image.', 'body' => message.html_part.body.to_s, 'headers' => {}, 'identifiers' => { 'email' => 'sheldon@bigbangtheory.com' }, 'attachments' => { 'empty.gif' => 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==' } }
95
+ end
96
+
97
+ it do
98
+ message.deliver!
99
+ end
100
+ end
101
+
102
+ context 'when delivering a message with templated message' do
103
+ let(:message) { TestMailer.message_with_template }
104
+
105
+ let(:expected_body) do
106
+ { 'to' => 'sheldon@bigbangtheory.com', 'from' => 'leonard@bigbangtheory.com', 'message_data' => { 'foo' => 'bar' },
107
+ 'subject' => 'Message with template.', 'transactional_message_id' => '123', 'headers' => {}, 'identifiers' => { 'email' => 'sheldon@bigbangtheory.com' }, 'attachments' => {} }
108
+ end
109
+
110
+ it do
111
+ message.deliver!
112
+ end
113
+ end
114
+ end
Binary file
@@ -0,0 +1,42 @@
1
+ class TestMailer < ActionMailer::Base
2
+ default subject: 'hello',
3
+ to: 'sheldon@bigbangtheory.com',
4
+ from: 'leonard@bigbangtheory.com'
5
+
6
+ def simple_message
7
+ mail
8
+ end
9
+
10
+ def multipart_message
11
+ mail(subject: "Your invitation to join Mixlr.") do |format|
12
+ format.text
13
+ format.html
14
+ end
15
+ end
16
+
17
+ def message_with_attachment
18
+ attachments['empty.gif'] = File.read(image_file)
19
+ mail(subject: "Message with attachment.")
20
+ end
21
+
22
+ def message_with_inline_image
23
+ attachments.inline['empty.gif'] = File.read(image_file)
24
+ mail(subject: "Message with inline image.")
25
+ end
26
+
27
+ def message_with_metadata
28
+ metadata['foo'] = 'bar'
29
+ mail(subject: 'Message with metadata.')
30
+ end
31
+
32
+ def message_with_template
33
+ mail(subject: 'Message with template.', transactional_message_id: '123', message_data: { foo: 'bar' })
34
+ end
35
+
36
+ protected
37
+
38
+ def image_file
39
+ File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'empty.gif')
40
+ end
41
+
42
+ end
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
4
+ </head>
5
+ <body text="#000000" bgcolor="#FFFFFF">
6
+ <img alt="empty" src="<%= attachments['empty.gif'].url %>">
7
+ </body>
8
+ </html>
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require 'rubygems'
7
+ require 'customerio'
8
+ require 'customerio-rails'
9
+ require 'json'
10
+
11
+ ActionMailer::Base.delivery_method = :customerio
12
+ ActionMailer::Base.prepend_view_path(File.join(File.dirname(__FILE__), "fixtures", "views"))
13
+
14
+ Dir["#{File.dirname(__FILE__)}/fixtures/models/*.rb"].each { |f| require f }
15
+
16
+ RSpec.configure do |config|
17
+ def make_mailer(base, &block)
18
+ mailer = Class.new(base, &block)
19
+ @mailer_number = 0
20
+ stub_const("Mailer#{@mailer_number += 1}", mailer)
21
+ mailer
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: customerio-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sylvain Utard
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-04-18 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: actionmailer
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 7.0.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 7.0.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: customerio
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.3.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 5.3.0
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ description: Drop-in ActionMailer adapter to send emails via Customer.io
55
+ executables: []
56
+ extensions: []
57
+ extra_rdoc_files:
58
+ - LICENSE
59
+ - README.md
60
+ files:
61
+ - CHANGELOG.rdoc
62
+ - LICENSE
63
+ - README.md
64
+ - lib/customerio-rails.rb
65
+ - lib/customerio-rails/mail.rb
66
+ - lib/customerio-rails/railtie.rb
67
+ - lib/customerio-rails/version.rb
68
+ - spec/customerio-rails_spec.rb
69
+ - spec/delivery_spec.rb
70
+ - spec/fixtures/empty.gif
71
+ - spec/fixtures/models/test_mailer.rb
72
+ - spec/fixtures/views/test_mailer/message_with_attachment.erb
73
+ - spec/fixtures/views/test_mailer/message_with_inline_image.html.erb
74
+ - spec/fixtures/views/test_mailer/multipart_message.html.erb
75
+ - spec/fixtures/views/test_mailer/multipart_message.text.erb
76
+ - spec/fixtures/views/test_mailer/simple_message.erb
77
+ - spec/spec.opts
78
+ - spec/spec_helper.rb
79
+ homepage: https://github.com/altertable-ai/customerio-rails
80
+ licenses: []
81
+ metadata: {}
82
+ rdoc_options:
83
+ - "--charset=UTF-8"
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.6.2
98
+ specification_version: 4
99
+ summary: Customer.io adapter for ActionMailer
100
+ test_files:
101
+ - spec/customerio-rails_spec.rb
102
+ - spec/delivery_spec.rb
103
+ - spec/fixtures/empty.gif
104
+ - spec/fixtures/models/test_mailer.rb
105
+ - spec/fixtures/views/test_mailer/message_with_attachment.erb
106
+ - spec/fixtures/views/test_mailer/message_with_inline_image.html.erb
107
+ - spec/fixtures/views/test_mailer/multipart_message.html.erb
108
+ - spec/fixtures/views/test_mailer/multipart_message.text.erb
109
+ - spec/fixtures/views/test_mailer/simple_message.erb
110
+ - spec/spec.opts
111
+ - spec/spec_helper.rb