postal-ruby 1.0.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/lib/postal.rb +22 -0
- data/lib/postal/attachment.rb +31 -0
- data/lib/postal/client.rb +61 -0
- data/lib/postal/config.rb +15 -0
- data/lib/postal/error.rb +47 -0
- data/lib/postal/header_set.rb +21 -0
- data/lib/postal/message.rb +156 -0
- data/lib/postal/message_scope.rb +40 -0
- data/lib/postal/send_message.rb +81 -0
- data/lib/postal/send_raw_message.rb +37 -0
- data/lib/postal/send_result.rb +30 -0
- data/lib/postal/version.rb +3 -0
- metadata +77 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: decfbd216e5711fdb213bd6896c656f608e252b7
|
4
|
+
data.tar.gz: f7696e1e19fbca0f46a6f9ba0475b5b7f124decb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5d2a13891fdd768c6fa07d819fb2fe702e599888aebfccac0a47e37171b0bafae9cf4d46afdab61694dbefe76340062724d8f3c16d7ed3c1df9555787ea6f7d3
|
7
|
+
data.tar.gz: 9583d073dbcaee6ae0a1ff1f8d9203b3e325645c66e9b986de38d54b8ee0f8ae4c0e25282a44f19b9babc4031e508271daaba44838cd0e25b27cea76a75439e7
|
data/lib/postal.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'postal/client'
|
2
|
+
require 'postal/config'
|
3
|
+
|
4
|
+
module Postal
|
5
|
+
|
6
|
+
def self.config
|
7
|
+
@config ||= Config.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure(&block)
|
11
|
+
block.call(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.send_message(&block)
|
15
|
+
Postal::Client.instance.send_message(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.send_raw_message(&block)
|
19
|
+
Postal::Client.instance.send_raw_message(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Postal
|
4
|
+
class Attachment
|
5
|
+
|
6
|
+
def initialize(attributes)
|
7
|
+
@attributes = attributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def filename
|
11
|
+
@attributes['filename']
|
12
|
+
end
|
13
|
+
|
14
|
+
def content_type
|
15
|
+
@attributes['content_type']
|
16
|
+
end
|
17
|
+
|
18
|
+
def size
|
19
|
+
@attributes['size']
|
20
|
+
end
|
21
|
+
|
22
|
+
def hash
|
23
|
+
@attributes['hash']
|
24
|
+
end
|
25
|
+
|
26
|
+
def data
|
27
|
+
@data ||= Base64.decode64(@attributes['data'])
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'moonrope_client'
|
2
|
+
require 'postal/message_scope'
|
3
|
+
require 'postal/send_message'
|
4
|
+
require 'postal/send_raw_message'
|
5
|
+
|
6
|
+
module Postal
|
7
|
+
class Client
|
8
|
+
|
9
|
+
#
|
10
|
+
# Create and cache a global instance of client based on the environment variables
|
11
|
+
# which can be provided. In 90% of cases, Postal will be accessed through this.
|
12
|
+
#
|
13
|
+
def self.instance
|
14
|
+
@instance ||= Client.new(Postal.config.host, Postal.config.server_key)
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Initialize a new client with the host and API key
|
19
|
+
#
|
20
|
+
def initialize(host, server_key)
|
21
|
+
@host = host
|
22
|
+
@server_key = server_key
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Provide a scope to access messages
|
27
|
+
#
|
28
|
+
def messages
|
29
|
+
MessageScope.new(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Send a message
|
34
|
+
#
|
35
|
+
def send_message(&block)
|
36
|
+
message = SendMessage.new(self)
|
37
|
+
block.call(message)
|
38
|
+
message.send!
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Send a raw message
|
43
|
+
#
|
44
|
+
def send_raw_message(&block)
|
45
|
+
message = SendRawMessage.new(self)
|
46
|
+
block.call(message)
|
47
|
+
message.send!
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Return the backend moonrope instance for this client
|
52
|
+
#
|
53
|
+
def moonrope
|
54
|
+
@moonrope ||= begin
|
55
|
+
headers= {'X-Server-API-Key' => @server_key}
|
56
|
+
MoonropeClient::Connection.new(@host, :headers => headers, :ssl => true)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Postal
|
2
|
+
class Config
|
3
|
+
|
4
|
+
def host
|
5
|
+
@host || ENV['POSTAL_HOST'] || raise(Error, "Host has not been configured. Set it using the `Postal.configure` block or use `POSTAL_HOST` environment variable.")
|
6
|
+
end
|
7
|
+
attr_writer :host
|
8
|
+
|
9
|
+
def server_key
|
10
|
+
@server_key || ENV['POSTAL_KEY'] || raise(Error, "Server key has not been configured. Set it using the `Postal.configure` block or use `POSTAL_KEY` environment variable.")
|
11
|
+
end
|
12
|
+
attr_writer :server_key
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
data/lib/postal/error.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Postal
|
2
|
+
|
3
|
+
#
|
4
|
+
# A generic error that all errors will inherit from
|
5
|
+
#
|
6
|
+
class Error < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
#
|
10
|
+
# Raised when a message cannot be found by its ID
|
11
|
+
#
|
12
|
+
class MessageNotFound < Error
|
13
|
+
def initialize(id)
|
14
|
+
@id = id
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
"No message found matching ID '#{@id}'"
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Raised when a message cannot be found by its ID
|
28
|
+
#
|
29
|
+
class SendError < Error
|
30
|
+
def initialize(code, error_message)
|
31
|
+
@code = code
|
32
|
+
@error_message = error_message
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :code
|
36
|
+
attr_reader :error_message
|
37
|
+
|
38
|
+
def message
|
39
|
+
"[#{@code}] #{@error_message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Postal
|
2
|
+
class HeaderSet
|
3
|
+
|
4
|
+
def initialize(headers)
|
5
|
+
@headers = headers
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](name)
|
9
|
+
@headers[name.to_s.downcase]
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_key?(key)
|
13
|
+
@headers.has_key?(name.to_s.downcase)
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(*args, &block)
|
17
|
+
@headers.send(*args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'postal/error'
|
2
|
+
require 'postal/header_set'
|
3
|
+
require 'postal/attachment'
|
4
|
+
|
5
|
+
module Postal
|
6
|
+
class Message
|
7
|
+
|
8
|
+
#
|
9
|
+
# Find a specific messsage with the given scope
|
10
|
+
#
|
11
|
+
def self.find_with_scope(scope, id)
|
12
|
+
api = scope.client.moonrope.messages.message(:id => id.to_i, :_expansions => scope.expansions)
|
13
|
+
if api.success?
|
14
|
+
Message.new(scope.client, api.data)
|
15
|
+
elsif api.status == 'error' && api.data['code'] == 'MessageNotFound'
|
16
|
+
raise MessageNotFound.new(id)
|
17
|
+
else
|
18
|
+
raise Error, "Couldn't load message from API (#{api.data})"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# If methods are called directly on the Message class, we likely want to see if we can
|
24
|
+
# run them through the global client message scope.
|
25
|
+
#
|
26
|
+
def self.method_missing(name, *args, &block)
|
27
|
+
if MessageScope.instance_methods(false).include?(name)
|
28
|
+
Postal::Client.instance.messages.send(name, *args, &block)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Initialize a new message object with the client and a set of initial attributes.
|
36
|
+
#
|
37
|
+
def initialize(client, attributes)
|
38
|
+
@client = client
|
39
|
+
@attributes = attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Return the message ID
|
44
|
+
#
|
45
|
+
def id
|
46
|
+
@attributes['id']
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Return the message token
|
51
|
+
#
|
52
|
+
def token
|
53
|
+
@attributes['token']
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Set a has of all the attributes from the API that should be exposed through
|
58
|
+
# the Message class.
|
59
|
+
#
|
60
|
+
ATTRIBUTES = {
|
61
|
+
:status => [:status, :status],
|
62
|
+
:last_delivery_attempt => [:status, :last_delivery_attempt, :timestamp],
|
63
|
+
:held? => [:status, :held, :boolean],
|
64
|
+
:hold_expiry => [:status, :hold_expiry, :timestamp],
|
65
|
+
:rcpt_to => [:details, :rcpt_to],
|
66
|
+
:mail_from => [:details, :mail_from],
|
67
|
+
:subject => [:details, :subject],
|
68
|
+
:message_id => [:details, :message_id],
|
69
|
+
:timestamp => [:details, :timestamp, :timestamp],
|
70
|
+
:direction => [:details, :direction],
|
71
|
+
:size => [:details, :size],
|
72
|
+
:bounce? => [:details, :bounce, :boolean],
|
73
|
+
:bounce_for_id => [:details, :bounce],
|
74
|
+
:tag => [:details, :tag],
|
75
|
+
:received_with_ssl? => [:details, :received_with_ssl, :boolean],
|
76
|
+
:inspected? => [:inspection, :inspected, :boolean],
|
77
|
+
:spam? => [:inspection, :spam, :boolean],
|
78
|
+
:spam_score => [:inspection, :spam_score],
|
79
|
+
:threat? => [:inspection, :thret, :boolean],
|
80
|
+
:threat_details => [:inspection, :threat_details],
|
81
|
+
:plain_body => [:plain_body],
|
82
|
+
:html_body => [:html_body],
|
83
|
+
}
|
84
|
+
|
85
|
+
#
|
86
|
+
# Catch calls to any of the default attributes for a message and return the
|
87
|
+
# data however we'd like it
|
88
|
+
#
|
89
|
+
def method_missing(name, *args, &block)
|
90
|
+
if mapping = ATTRIBUTES[name.to_sym]
|
91
|
+
expansion, attribute, type = mapping
|
92
|
+
value = from_expansion(expansion, attribute)
|
93
|
+
case type
|
94
|
+
when :timestamp
|
95
|
+
value ? Time.at(value) : nil
|
96
|
+
when :boolean
|
97
|
+
value == 1
|
98
|
+
else
|
99
|
+
value
|
100
|
+
end
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Return a set of headers which can be queried like a hash however looking up
|
108
|
+
# values using [] will be case-insensitive.
|
109
|
+
#
|
110
|
+
def headers
|
111
|
+
@headers ||= HeaderSet.new(from_expansion(:headers))
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Return an array of attachment objects
|
116
|
+
#
|
117
|
+
def attachments
|
118
|
+
@attachments ||= from_expansion(:attachments).map do |a|
|
119
|
+
Attachment.new(a)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Return the full raw message
|
125
|
+
#
|
126
|
+
def raw_message
|
127
|
+
@raw_message ||= Base64.decode64(from_expansion(:raw_message))
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def from_expansion(expansion, attribute = nil, loaded = false)
|
133
|
+
if @attributes.has_key?(expansion.to_s) || loaded
|
134
|
+
attribute ? @attributes[expansion.to_s][attribute.to_s] : @attributes[expansion.to_s]
|
135
|
+
else
|
136
|
+
load_expansions(expansion)
|
137
|
+
from_expansion(expansion, attribute, true)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def load_expansions(*names)
|
142
|
+
puts "\e[31mLoading expansion #{names}\e[0m"
|
143
|
+
api = @client.moonrope.messages.message(:id => self.id, :_expansions => names)
|
144
|
+
if api.success?
|
145
|
+
names.each do |expansion_name|
|
146
|
+
if api.data.has_key?(expansion_name.to_s)
|
147
|
+
@attributes[expansion_name.to_s] = api.data[expansion_name.to_s]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
else
|
151
|
+
raise Postal::Error, "Couldn't load expansion data (#{names.join(', ')}) for message ID '#{self.id}'"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'postal/message'
|
2
|
+
|
3
|
+
module Postal
|
4
|
+
class MessageScope
|
5
|
+
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
@includes = []
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Add includes to the scope
|
15
|
+
#
|
16
|
+
def includes(*includes)
|
17
|
+
@includes.push(*includes)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Return the current includes
|
23
|
+
#
|
24
|
+
def expansions
|
25
|
+
if @includes.include?(:all)
|
26
|
+
true
|
27
|
+
else
|
28
|
+
@includes.map(&:to_s)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Find a given message by its ID
|
34
|
+
#
|
35
|
+
def find_by_id(id)
|
36
|
+
Message.find_with_scope(self, id)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'postal/send_result'
|
3
|
+
|
4
|
+
module Postal
|
5
|
+
class SendMessage
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
@attributes = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def send!
|
13
|
+
api = @client.moonrope.request(:send, :message, @attributes)
|
14
|
+
if api.success?
|
15
|
+
SendResult.new(@client, api.data)
|
16
|
+
elsif api.status == 'error'
|
17
|
+
raise SendError.new(api.data['code'], api.data['message'])
|
18
|
+
else
|
19
|
+
raise Error, "Couldn't send message"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def from(address)
|
24
|
+
@attributes[:from] = address
|
25
|
+
end
|
26
|
+
|
27
|
+
def sender(address)
|
28
|
+
@attributes[:sender] = address
|
29
|
+
end
|
30
|
+
|
31
|
+
def to(*addresses)
|
32
|
+
@attributes[:to] ||= []
|
33
|
+
@attributes[:to] += addresses
|
34
|
+
end
|
35
|
+
|
36
|
+
def cc(*addresses)
|
37
|
+
@attributes[:cc] ||= []
|
38
|
+
@attributes[:cc] += addresses
|
39
|
+
end
|
40
|
+
|
41
|
+
def bcc(*addresses)
|
42
|
+
@attributes[:bcc] ||= []
|
43
|
+
@attributes[:bcc] += addresses
|
44
|
+
end
|
45
|
+
|
46
|
+
def subject(subject)
|
47
|
+
@attributes[:subject] = subject
|
48
|
+
end
|
49
|
+
|
50
|
+
def tag(tag)
|
51
|
+
@attributes[:tag] = subject
|
52
|
+
end
|
53
|
+
|
54
|
+
def reply_to(reply_to)
|
55
|
+
@attributes[:reply_to] = subject
|
56
|
+
end
|
57
|
+
|
58
|
+
def plain_body(content)
|
59
|
+
@attributes[:plain_body] = content
|
60
|
+
end
|
61
|
+
|
62
|
+
def html_body(content)
|
63
|
+
@attributes[:html_body] = content
|
64
|
+
end
|
65
|
+
|
66
|
+
def header(key, value)
|
67
|
+
@attributes[:headers] ||= {}
|
68
|
+
@attributes[:headers][key.to_s] = value
|
69
|
+
end
|
70
|
+
|
71
|
+
def attach(filename, content_type, data)
|
72
|
+
@attributes[:attachments] ||= []
|
73
|
+
@attributes[:attachments] << {
|
74
|
+
:name => filename,
|
75
|
+
:content_type => content_type,
|
76
|
+
:data => Base64.encode64(data)
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'postal/send_result'
|
3
|
+
|
4
|
+
module Postal
|
5
|
+
class SendRawMessage
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
@attributes = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def send!
|
13
|
+
api = @client.moonrope.request(:send, :raw, @attributes)
|
14
|
+
if api.success?
|
15
|
+
SendResult.new(@client, api.data)
|
16
|
+
elsif api.status == 'error'
|
17
|
+
raise SendError.new(api.data['code'], api.data['message'])
|
18
|
+
else
|
19
|
+
raise Error, "Couldn't send message"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def mail_from(address)
|
24
|
+
@attributes[:mail_from] = address
|
25
|
+
end
|
26
|
+
|
27
|
+
def rcpt_to(*addresses)
|
28
|
+
@attributes[:rcpt_to] ||= []
|
29
|
+
@attributes[:rcpt_to] += addresses
|
30
|
+
end
|
31
|
+
|
32
|
+
def data(data)
|
33
|
+
@attributes[:data] = Base64.encode64(data)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Postal
|
2
|
+
class SendResult
|
3
|
+
|
4
|
+
def initialize(client, result)
|
5
|
+
@client = client
|
6
|
+
@result = result
|
7
|
+
end
|
8
|
+
|
9
|
+
def message_id
|
10
|
+
@result['message_id']
|
11
|
+
end
|
12
|
+
|
13
|
+
def recipients
|
14
|
+
@recipients ||= begin
|
15
|
+
@result['messages'].each_with_object({}) do |(recipient, message_details), hash|
|
16
|
+
hash[recipient.to_s.downcase] = Message.new(@client, message_details)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](recipient)
|
22
|
+
recipients[recipient.to_s.downcase]
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
recipients.size
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: postal-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Cooke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: moonrope-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.2
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '1.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.2
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.1'
|
33
|
+
description: Ruby library for the Postal e-mail platform
|
34
|
+
email:
|
35
|
+
- me@adamcooke.io
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- lib/postal.rb
|
41
|
+
- lib/postal/attachment.rb
|
42
|
+
- lib/postal/client.rb
|
43
|
+
- lib/postal/config.rb
|
44
|
+
- lib/postal/error.rb
|
45
|
+
- lib/postal/header_set.rb
|
46
|
+
- lib/postal/message.rb
|
47
|
+
- lib/postal/message_scope.rb
|
48
|
+
- lib/postal/send_message.rb
|
49
|
+
- lib/postal/send_raw_message.rb
|
50
|
+
- lib/postal/send_result.rb
|
51
|
+
- lib/postal/version.rb
|
52
|
+
homepage: https://github.com/atech/postal
|
53
|
+
licenses:
|
54
|
+
- MIT
|
55
|
+
metadata: {}
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 2.5.1
|
73
|
+
signing_key:
|
74
|
+
specification_version: 4
|
75
|
+
summary: Ruby library for the Postal e-mail platform
|
76
|
+
test_files: []
|
77
|
+
has_rdoc:
|