missive 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08e4bc0cf32260c7519cd0ec0ab1b6f378b6e15f658ca559d593f5e2f69a51eb'
4
- data.tar.gz: bb9b7dd6e243f03b4efb6cda902d542b0b7eec9e2e24c1e725f3237a1c39cfdb
3
+ metadata.gz: 63e8195762299f95bdc217efcf1a81dbd8be46ec029ea90b8b4db430af37eabd
4
+ data.tar.gz: 94532fa3c3cd42c9086b35385773c0d2f8483dad96690b0d78523512736aa86e
5
5
  SHA512:
6
- metadata.gz: 61713bf0d79f835d91a739073efeae708dde7033d2f64aab5f11b696b69483f532c4e795eae4db8d13c256f25b05afdf413d8262fa9a9e2c5062902d41efc82a
7
- data.tar.gz: 9441fbb01803f621f33252a5e1be5fa9ff94fd5c20b5355bfe3a1e0ba44824512404fef1c28378f8ac549ba3f162910a52dbc3e1af478caf9d858b8be3f34257
6
+ metadata.gz: d365c0b22c19489e7d61d46746bec34d58834c167066f02eb71cec287e855a7e820e1eb535e066c2f871cc948338bbbd5151efac7838ca037e2455b36c4dbe07
7
+ data.tar.gz: d7c9d72e65bbac0b683b6f5b69b888547f7ebc5245c35b6b9f582042ad8ef524b775acaed2aa47d17dd4a2be689315c02e41570913edebf7c213b5d0dc728f6f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ## [Unreleased]
2
2
 
3
+ - Add install generator
4
+ - Add Postmark Bulk API implementation
5
+
3
6
  ## [0.0.1] - 2025-10-14
4
7
 
5
8
  - First release: base models, database structure
data/README.md CHANGED
@@ -55,6 +55,12 @@ A lightweight Rails toolkit for building newsletter features. Missive provides t
55
55
  bundle add missive
56
56
  ```
57
57
 
58
+ Install the migrations:
59
+
60
+ ```bash
61
+ rails generate missive:install
62
+ ```
63
+
58
64
  ### Configuration
59
65
 
60
66
  Missive uses the same configuration as `postmark-rails`. Please follow the [`postmark-rails` configuration instructions](https://github.com/ActiveCampaign/postmark-rails?tab=readme-ov-file#installation) to set up your Postmark API credentials.
@@ -180,6 +186,68 @@ list.create_message!(subject: "Hello world!")
180
186
  message.send!
181
187
  ```
182
188
 
189
+ ### Sending with Postmark Bulk API
190
+
191
+ Missive leverages the [Bulk Email API](https://postmarkapp.com/developer/api/bulk-email) endpoints.
192
+
193
+ > [!WARNING]
194
+ > This endpoint is available to early access customers only. You need to request access. [Learn more](https://postmarkapp.com/support/article/1311-the-early-access-program-for-the-new-bulk-api)
195
+
196
+ The official [postmark gem](https://github.com/ActiveCampaign/postmark-gem) does not support these endpoints yet, so Missive ships with `Stamp`, a thin layer over Postmark's official library.
197
+
198
+ #### Sending a message in bulk
199
+
200
+ You can pass a Hash that matches the body expected by the API.
201
+
202
+ ```rb
203
+ Missive::Stamp::ApiClient.deliver_in_bulk(
204
+ from: "sender@example.com",
205
+ subject: "Hello {{name}}",
206
+ html_body: "<p>Hello {{name}}</p>",
207
+ text_body: "Hello {{name}}",
208
+ messages: [
209
+ {
210
+ to: "jane.doe@example.com",
211
+ template_model: {name: "Jane"}
212
+ },
213
+ {
214
+ to: "john.doe@example.com",
215
+ template_model: {name: "John"}
216
+ }
217
+ ]
218
+ )
219
+ ```
220
+
221
+ You can also pass a `Mail` instance and an Array of recipients.
222
+
223
+ ```rb
224
+ mail = Mail.new do
225
+ from "sender@example.com"
226
+ subject "Hello {{name}}"
227
+ body: "Hello {{name}}"
228
+ end
229
+
230
+ Missive::Stamp::ApiClient.deliver_message_in_bulk(
231
+ mail,
232
+ [
233
+ {
234
+ to: "jane.doe@example.com",
235
+ template_model: {name: "Jane"}
236
+ },
237
+ {
238
+ to: "john.doe@example.com",
239
+ template_model: {name: "John"}
240
+ }
241
+ ]
242
+ )
243
+ ```
244
+
245
+ #### Getting the status of a bulk API request
246
+
247
+ ```rb
248
+ Missive::Stamp::ApiClient.get_bulk_status("f24af63c-533d-4b7a-ad65-4a7b3202d3a7")
249
+ ```
250
+
183
251
  ## License
184
252
 
185
253
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/migration"
5
+ require "rails/generators/active_record"
6
+
7
+ module Missive
8
+ module Generators
9
+ class InstallGenerator < Rails::Generators::Base
10
+ include Rails::Generators::Migration
11
+
12
+ source_root File.expand_path("templates", __dir__)
13
+
14
+ def self.next_migration_number(dirname)
15
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
16
+ end
17
+
18
+ def copy_missive_migrations
19
+ migration_template "migrations/install_missive.rb.erb", "db/migrate/install_missive.rb"
20
+ end
21
+
22
+ private
23
+
24
+ def migration_class_name
25
+ if Rails::VERSION::MAJOR >= 5
26
+ "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
27
+ else
28
+ "ActiveRecord::Migration"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,74 @@
1
+ class InstallMissive < <%= migration_class_name %>
2
+ def change
3
+ create_table :missive_subscribers do |t|
4
+ t.string :email, null: false
5
+ t.timestamp :suppressed_at
6
+ t.integer :suppression_reason
7
+ t.references :user, foreign_key: true
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ create_table :missive_lists do |t|
13
+ t.string :name, null: false
14
+ t.integer :subscriptions_count, default: 0
15
+ t.integer :messages_count, default: 0
16
+ t.timestamp :last_message_sent_at
17
+ t.string :postmark_message_stream_id
18
+
19
+ t.timestamps
20
+ end
21
+
22
+ create_table :missive_messages do |t|
23
+ t.string :subject, null: false
24
+ t.integer :dispatches_count, default: 0
25
+ t.references :list, null: false, foreign_key: {to_table: "missive_lists"}
26
+ t.string :postmark_message_stream_id
27
+ t.timestamp :sent_at
28
+
29
+ t.timestamps
30
+ end
31
+
32
+ create_table :missive_dispatches do |t|
33
+ t.references :subscriber, null: false, foreign_key: {to_table: "missive_subscribers"}
34
+ t.references :message, null: false, foreign_key: {to_table: "missive_messages"}
35
+ t.string :postmark_message_stream_id
36
+ t.string :postmark_message_id
37
+ t.timestamp :sent_at
38
+ t.timestamp :delivered_at
39
+ t.timestamp :opened_at
40
+ t.timestamp :clicked_at
41
+ t.timestamp :suppressed_at
42
+ t.integer :suppression_reason
43
+
44
+ t.index [:subscriber_id, :message_id], unique: true
45
+
46
+ t.timestamps
47
+ end
48
+
49
+ create_table :missive_subscriptions do |t|
50
+ t.references :subscriber, null: false, foreign_key: {to_table: "missive_subscribers"}
51
+ t.references :list, null: false, foreign_key: {to_table: "missive_lists"}
52
+ t.timestamp :suppressed_at
53
+ t.integer :suppression_reason
54
+
55
+ t.index [:subscriber_id, :list_id], unique: true
56
+
57
+ t.timestamps
58
+ end
59
+
60
+ create_table :missive_senders do |t|
61
+ t.string :email, null: false
62
+ t.string :name
63
+ t.string :reply_to_email
64
+ t.integer :postmark_sender_signature_id
65
+ t.references :user, foreign_key: false
66
+
67
+ t.timestamps
68
+ end
69
+
70
+ add_reference :missive_dispatches, :sender, null: false, foreign_key: {to_table: "missive_senders"}
71
+ add_reference :missive_lists, :sender, null: false, foreign_key: {to_table: "missive_senders"}
72
+ add_reference :missive_messages, :sender, null: false, foreign_key: {to_table: "missive_senders"}
73
+ end
74
+ end
@@ -0,0 +1,40 @@
1
+ module Missive
2
+ # Stamp is a thin layer over Postmark's official library,
3
+ # that improves it for Missive's needs.
4
+ #
5
+ # Main features:
6
+ # - implementation of the [Bulk API](https://postmarkapp.com/developer/api/bulk-email)
7
+ module Stamp
8
+ class ApiClient < ::Postmark::ApiClient
9
+ # Send bulk emails, passing a hash
10
+ # https://postmarkapp.com/developer/api/bulk-email#send-bulk-emails
11
+ def deliver_in_bulk(message_hash)
12
+ data = serialize(::Postmark::MessageHelper.to_postmark(message_hash))
13
+
14
+ with_retries do
15
+ format_response http_client.post("email/bulk", data)
16
+ end
17
+ end
18
+
19
+ # Send bulk emails, passing a message and its recipients
20
+ # https://postmarkapp.com/developer/api/bulk-email#send-bulk-emails
21
+ def deliver_message_in_bulk(message, recipients = [])
22
+ data = serialize(message.to_postmark_hash.merge(messages: recipients))
23
+
24
+ with_retries do
25
+ response, error = take_response_of { http_client.post("email/bulk", data) }
26
+ update_message(message, response)
27
+ raise error if error
28
+
29
+ format_response(response, compatible: true)
30
+ end
31
+ end
32
+
33
+ # Get the status/details of a bulk API request
34
+ # https://postmarkapp.com/developer/api/bulk-email#get-a-bulk-send-status
35
+ def get_bulk_status(id)
36
+ format_response http_client.get("email/bulk/#{id}")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module Missive
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.2".freeze
3
3
  end
data/lib/missive.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "missive/version"
2
2
  require "missive/engine"
3
+ require "postmark"
4
+ require "missive/stamp/api_client"
3
5
 
4
6
  module Missive
5
7
  # Your code goes here...
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: missive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hans Lemuet
@@ -100,16 +100,13 @@ files:
100
100
  - app/models/missive/subscription.rb
101
101
  - app/views/layouts/missive/application.html.erb
102
102
  - config/routes.rb
103
- - db/migrate/20251002000005_create_missive_subscribers.rb
104
- - db/migrate/20251004191513_create_missive_lists.rb
105
- - db/migrate/20251004193630_create_missive_messages.rb
106
- - db/migrate/20251004201105_create_missive_dispatches.rb
107
- - db/migrate/20251006214059_create_missive_subscriptions.rb
108
- - db/migrate/20251013205354_create_missive_senders.rb
109
103
  - lefthook.yml
104
+ - lib/generators/missive/install_generator.rb
105
+ - lib/generators/missive/templates/migrations/install_missive.rb.erb
110
106
  - lib/missive.rb
111
107
  - lib/missive/engine.rb
112
108
  - lib/missive/railtie.rb
109
+ - lib/missive/stamp/api_client.rb
113
110
  - lib/missive/version.rb
114
111
  - lib/tasks/missive_tasks.rake
115
112
  homepage: https://github.com/Spone/missive
@@ -133,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
130
  - !ruby/object:Gem::Version
134
131
  version: '0'
135
132
  requirements: []
136
- rubygems_version: 3.6.7
133
+ rubygems_version: 3.7.2
137
134
  specification_version: 4
138
135
  summary: Toolbox for managing newsletters in Rails, sending them with Postmark.
139
136
  test_files: []
@@ -1,12 +0,0 @@
1
- class CreateMissiveSubscribers < ActiveRecord::Migration[8.0]
2
- def change
3
- create_table :missive_subscribers do |t|
4
- t.string :email, null: false
5
- t.timestamp :suppressed_at
6
- t.integer :suppression_reason
7
- t.references :user, foreign_key: true
8
-
9
- t.timestamps
10
- end
11
- end
12
- end
@@ -1,13 +0,0 @@
1
- class CreateMissiveLists < ActiveRecord::Migration[8.0]
2
- def change
3
- create_table :missive_lists do |t|
4
- t.string :name, null: false
5
- t.integer :subscriptions_count, default: 0
6
- t.integer :messages_count, default: 0
7
- t.timestamp :last_message_sent_at
8
- t.string :postmark_message_stream_id
9
-
10
- t.timestamps
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- class CreateMissiveMessages < ActiveRecord::Migration[8.0]
2
- def change
3
- create_table :missive_messages do |t|
4
- t.string :subject, null: false
5
- t.integer :dispatches_count, default: 0
6
- t.references :list, null: false, foreign_key: {to_table: "missive_lists"}
7
- t.string :postmark_message_stream_id
8
- t.timestamp :sent_at
9
-
10
- t.timestamps
11
- end
12
- end
13
- end
@@ -1,20 +0,0 @@
1
- class CreateMissiveDispatches < ActiveRecord::Migration[8.0]
2
- def change
3
- create_table :missive_dispatches do |t|
4
- t.references :subscriber, null: false, foreign_key: {to_table: "missive_subscribers"}
5
- t.references :message, null: false, foreign_key: {to_table: "missive_messages"}
6
- t.string :postmark_message_stream_id
7
- t.string :postmark_message_id
8
- t.timestamp :sent_at
9
- t.timestamp :delivered_at
10
- t.timestamp :opened_at
11
- t.timestamp :clicked_at
12
- t.timestamp :suppressed_at
13
- t.integer :suppression_reason
14
-
15
- t.index [:subscriber_id, :message_id], unique: true
16
-
17
- t.timestamps
18
- end
19
- end
20
- end
@@ -1,14 +0,0 @@
1
- class CreateMissiveSubscriptions < ActiveRecord::Migration[8.0]
2
- def change
3
- create_table :missive_subscriptions do |t|
4
- t.references :subscriber, null: false, foreign_key: {to_table: "missive_subscribers"}
5
- t.references :list, null: false, foreign_key: {to_table: "missive_lists"}
6
- t.timestamp :suppressed_at
7
- t.integer :suppression_reason
8
-
9
- t.index [:subscriber_id, :list_id], unique: true
10
-
11
- t.timestamps
12
- end
13
- end
14
- end
@@ -1,17 +0,0 @@
1
- class CreateMissiveSenders < ActiveRecord::Migration[8.0]
2
- def change
3
- create_table :missive_senders do |t|
4
- t.string :email, null: false
5
- t.string :name
6
- t.string :reply_to_email
7
- t.integer :postmark_sender_signature_id
8
- t.references :user, foreign_key: false
9
-
10
- t.timestamps
11
- end
12
-
13
- add_reference :missive_dispatches, :sender, null: false, foreign_key: {to_table: "missive_senders"}
14
- add_reference :missive_lists, :sender, null: false, foreign_key: {to_table: "missive_senders"}
15
- add_reference :missive_messages, :sender, null: false, foreign_key: {to_table: "missive_senders"}
16
- end
17
- end