mailpace-rails 0.4.3 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7357c468655883089e9d6d028387cd78a5b5499c3277ea5581837f3255ac192
4
- data.tar.gz: c70e63597707b4811523a427efe95a48417a75f93b8c25443a1d0731f09dd260
3
+ metadata.gz: c31f68a3d8b609aae9c7b86a5012a3c681f8309e4e8a4e71c1c79149efd4cb7e
4
+ data.tar.gz: 4df6d4972fc3b231dd7db9093b249759a55279e254499cfa8a2e7224905c2497
5
5
  SHA512:
6
- metadata.gz: 8c88ded5300cd4a9a7538f31a7b98a72d714b112469d96a628cbb160643c176a4ec543fbc52e7dd41a450ab3ea6edccd84be333a8480edd0f9e9f3588dcd1ba4
7
- data.tar.gz: d691dbb4676db53f50d835e1fd993a88e2b1e838f20b06156b1dfbbefb95a2be2e0e88517581c611abde47e9f7ddc1142158381ddf1c61d728b53f337aa876dd
6
+ metadata.gz: 9c1711d716265352e54655562c3ecc0c53972c30e5123e648cc1cba7a63be36c0358fbfe922aecb179944e33cbac5ac2a29e0c47e3870909dfad96ca927eb979
7
+ data.tar.gz: 3f897e1309b778bcf0356a86df2baeb1bb52395c53749698d63004fdd94168c7238ed1519806577e35283e877bf1f3a63963060e61582239a26eda18c0f2ba6c
data/README.md CHANGED
@@ -2,12 +2,14 @@
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
4
4
  [![Gem Version](https://badge.fury.io/rb/mailpace-rails.svg)](https://badge.fury.io/rb/mailpace-rails)
5
- [![MailPace Rails](https://circleci.com/gh/mailpace/mailpace-rails.svg?style=svg)](https://app.circleci.com/pipelines/github/mailpace/mailpace-rails)
5
+ [![CI](https://github.com/mailpace/mailpace-rails/actions/workflows/ci.yml/badge.svg)](https://github.com/mailpace/mailpace-rails/actions/workflows/ci.yml)
6
6
 
7
7
  [MailPace](https://mailpace.com) lets you send transactional emails from your app over an easy to use API.
8
8
 
9
9
  The MailPace Rails Gem is a plug in for ActionMailer to send emails via [MailPace](https://mailpace.com) to make sending emails from Rails apps super simple.
10
10
 
11
+ > **New in 0.5.0: The `httparty` dependency has been removed — requests now use Ruby's built-in `Net::HTTP`, and the delivery response stays backward compatible**
12
+
11
13
  > **New in 0.4.0: [Idempotent Requests](https://docs.mailpace.com/guide/idempotency/), Improved Error Handling, InReplyTo and References support**
12
14
 
13
15
  > **New in 0.3.0: The ability to consume [inbound emails](https://docs.mailpace.com/guide/inbound/) from MailPace via ActionMailbox**
@@ -4,7 +4,9 @@ module Mailpace
4
4
  # Provides the delivery method & sets up action mailbox
5
5
  class Engine < ::Rails::Engine
6
6
  initializer 'mailpace.add_delivery_method', before: 'action_mailer.set_configs' do
7
- ActionMailer::Base.add_delivery_method(:mailpace, Mailpace::DeliveryMethod)
7
+ ActiveSupport.on_load(:action_mailer) do
8
+ add_delivery_method(:mailpace, Mailpace::DeliveryMethod)
9
+ end
8
10
  end
9
11
 
10
12
  config.action_mailbox.mailpace = ActiveSupport::OrderedOptions.new
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+ require 'json'
5
+ require 'net/http'
6
+
7
+ module Mailpace
8
+ # The value returned by Mailpace::DeliveryMethod#deliver!.
9
+ #
10
+ # Because the delivery method sets `return_response: true`, Mail hands this
11
+ # object back from `mail.deliver!` / `deliver_now!`. It mirrors the public
12
+ # interface of the HTTParty::Response this gem used to return, so that
13
+ # removing the httparty dependency does not change what callers can do with
14
+ # the result: `res['id']`, `res.parsed_response`, `res.code`, `res.body`,
15
+ # `res.headers` and the `*?` status helpers all keep working.
16
+ #
17
+ # Not carried over from HTTParty::Response: `#request` (an HTTParty::Request)
18
+ # and Marshal support (`_dump` / `_load`).
19
+ class Response
20
+ # Presents the parsed headers the way HTTParty did: case-insensitive lookup
21
+ # with joined values (`headers['Content-Type']` => "application/json").
22
+ class Headers < ::SimpleDelegator
23
+ include ::Net::HTTPHeader
24
+
25
+ def initialize(values = {})
26
+ @header = {}
27
+ values.each_pair do |key, value|
28
+ Array(value).each { |sub_value| add_field(key, sub_value) }
29
+ end
30
+ super(@header)
31
+ end
32
+ end
33
+
34
+ attr_reader :response, :body, :headers
35
+
36
+ def initialize(response)
37
+ @response = response
38
+ @body = response.body
39
+ @headers = Headers.new(response.to_hash)
40
+ @parsed_response = parse(@body)
41
+ end
42
+
43
+ def parsed_response
44
+ @parsed_response
45
+ end
46
+
47
+ def code
48
+ response.code.to_i
49
+ end
50
+
51
+ def http_version
52
+ response.http_version
53
+ end
54
+
55
+ def nil?
56
+ response.nil? || body.nil? || body.empty?
57
+ end
58
+
59
+ def to_s
60
+ body.to_s
61
+ end
62
+
63
+ def to_hash
64
+ parsed_response
65
+ end
66
+
67
+ def [](key)
68
+ parsed_response[key] if parsed_response.respond_to?(:[])
69
+ end
70
+
71
+ def inspect
72
+ "#<#{self.class} code=#{code} parsed_response=#{parsed_response.inspect}>"
73
+ end
74
+
75
+ def method_missing(name, *args, &block)
76
+ return parsed_response.send(name, *args, &block) if parsed_response.respond_to?(name)
77
+
78
+ super
79
+ end
80
+
81
+ def respond_to_missing?(name, include_private = false)
82
+ parsed_response.respond_to?(name, include_private) || super
83
+ end
84
+
85
+ # HTTParty defines a predicate per HTTP status class (ok?, created?,
86
+ # not_found?, server_error?, ...). Rebuild the same set from Net::HTTP's
87
+ # tables so status checks keep working.
88
+ ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge(::Net::HTTPResponse::CODE_TO_OBJ).each do |_code, klass|
89
+ name = klass.name.sub('Net::HTTP', '')
90
+ name = "#{name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase}?"
91
+
92
+ define_method(name) { klass === response }
93
+ end
94
+
95
+ private
96
+
97
+ def parse(body)
98
+ JSON.parse(body.to_s)
99
+ rescue JSON::ParserError
100
+ nil
101
+ end
102
+ end
103
+ end
@@ -1,5 +1,5 @@
1
1
  module Mailpace
2
2
  module Rails
3
- VERSION = '0.4.3'
3
+ VERSION = '0.5.0'
4
4
  end
5
5
  end
@@ -1,9 +1,10 @@
1
1
  require 'action_mailer'
2
2
  require 'action_mailbox/engine'
3
- require 'httparty'
3
+ require 'net/http'
4
4
  require 'uri'
5
5
  require 'json'
6
6
  require 'mailpace-rails/version'
7
+ require 'mailpace-rails/response'
7
8
  require 'mailpace-rails/engine' if defined? Rails
8
9
 
9
10
  module Mailpace
@@ -27,32 +28,33 @@ module Mailpace
27
28
  end
28
29
 
29
30
  check_delivery_params(mail)
30
- result = HTTParty.post(
31
- 'https://app.mailpace.com/api/v1/send',
32
- body: {
33
- from: address_list(mail.header[:from])&.addresses&.first.to_s,
34
- to: address_list(mail.header[:to])&.addresses&.join(','),
35
- subject: mail.subject,
36
- htmlbody: htmlbody,
37
- textbody: textbody,
38
- cc: address_list(mail.header[:cc])&.addresses&.join(','),
39
- bcc: address_list(mail.header[:bcc])&.addresses&.join(','),
40
- replyto: address_list(mail.header[:reply_to])&.addresses&.join(','),
41
- inreplyto: mail.header['In-Reply-To'].to_s,
42
- references: mail.header['References'].to_s,
43
- list_unsubscribe: mail.header['list_unsubscribe'].to_s,
44
- attachments: format_attachments(mail.attachments),
45
- tags: mail.header['tags'].to_s
46
- }.delete_if { |_key, value| value.blank? }.to_json,
47
- headers: {
48
- 'User-Agent' => "MailPace Rails Gem v#{Mailpace::Rails::VERSION}",
49
- 'Accept' => 'application/json',
50
- 'Content-Type' => 'application/json',
51
- 'Mailpace-Server-Token' => settings[:api_token]
52
- }.tap do |h|
53
- h['Idempotency-Key'] = mail.header['idempotency_key'].to_s if mail.header['idempotency_key']
54
- end
55
- )
31
+
32
+ uri = URI('https://app.mailpace.com/api/v1/send')
33
+ request = Net::HTTP::Post.new(uri)
34
+ request['User-Agent'] = "MailPace Rails Gem v#{Mailpace::Rails::VERSION}"
35
+ request['Accept'] = 'application/json'
36
+ request['Content-Type'] = 'application/json'
37
+ request['Mailpace-Server-Token'] = settings[:api_token]
38
+ request['Idempotency-Key'] = mail.header['idempotency_key'].to_s if mail.header['idempotency_key']
39
+ request.body = {
40
+ from: address_list(mail.header[:from])&.addresses&.first.to_s,
41
+ to: address_list(mail.header[:to])&.addresses&.join(','),
42
+ subject: mail.subject,
43
+ htmlbody: htmlbody,
44
+ textbody: textbody,
45
+ cc: address_list(mail.header[:cc])&.addresses&.join(','),
46
+ bcc: address_list(mail.header[:bcc])&.addresses&.join(','),
47
+ replyto: address_list(mail.header[:reply_to])&.addresses&.join(','),
48
+ inreplyto: mail.header['In-Reply-To'].to_s,
49
+ references: mail.header['References'].to_s,
50
+ list_unsubscribe: mail.header['list_unsubscribe'].to_s,
51
+ attachments: format_attachments(mail.attachments),
52
+ tags: mail.header['tags'].to_s
53
+ }.delete_if { |_key, value| value.blank? }.to_json
54
+
55
+ result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
56
+ http.request(request)
57
+ end
56
58
 
57
59
  handle_response(result)
58
60
  end
@@ -72,12 +74,13 @@ module Mailpace
72
74
  end
73
75
 
74
76
  def handle_response(result)
75
- return result unless result.code != 200
77
+ response = Mailpace::Response.new(result)
78
+ return response if result.is_a?(Net::HTTPOK)
76
79
 
77
- parsed_response = result.parsed_response
78
- error_message = join_error_messages(parsed_response)
80
+ error_message = join_error_messages(response.parsed_response)
81
+ error_message = "HTTP #{response.code}" if error_message.empty?
79
82
 
80
- raise DeliveryError, "MAILPACE Error: #{error_message}" unless error_message.empty?
83
+ raise DeliveryError, "MAILPACE Error: #{error_message}"
81
84
  end
82
85
 
83
86
  def format_attachments(attachments)
@@ -101,9 +104,11 @@ module Mailpace
101
104
  end
102
105
  end
103
106
 
104
- def join_error_messages(response)
107
+ def join_error_messages(parsed_response)
105
108
  # Join 'error' and 'errors' keys from response into a single string
106
- [response['error'], response['errors']].compact.join(', ')
109
+ return '' unless parsed_response.is_a?(Hash)
110
+
111
+ [parsed_response['error'], parsed_response['errors']].compact.join(', ')
107
112
  end
108
113
  end
109
114
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailpace-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - MailPace
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-06-09 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: actionmailer
@@ -52,20 +51,6 @@ dependencies:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
53
  version: 6.0.0
55
- - !ruby/object:Gem::Dependency
56
- name: httparty
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 0.18.1
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: 0.18.1
69
54
  - !ruby/object:Gem::Dependency
70
55
  name: rails
71
56
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +94,7 @@ files:
109
94
  - config/routes.rb
110
95
  - lib/mailpace-rails.rb
111
96
  - lib/mailpace-rails/engine.rb
97
+ - lib/mailpace-rails/response.rb
112
98
  - lib/mailpace-rails/version.rb
113
99
  homepage: https://mailpace.com
114
100
  licenses:
@@ -116,7 +102,6 @@ licenses:
116
102
  metadata:
117
103
  source_code_uri: https://github.com/mailpace/mailpace-rails
118
104
  changelog_uri: https://github.com/mailpace/mailpace-rails/blob/master/CHANGELOG.md
119
- post_install_message:
120
105
  rdoc_options: []
121
106
  require_paths:
122
107
  - lib
@@ -131,8 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
116
  - !ruby/object:Gem::Version
132
117
  version: '0'
133
118
  requirements: []
134
- rubygems_version: 3.5.22
135
- signing_key:
119
+ rubygems_version: 4.0.20
136
120
  specification_version: 4
137
121
  summary: Lets you send transactional emails from your app over an easy to use API
138
122
  test_files: []