appmail 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf42054c6e347a5c7cad150857aa334eab7e5ea6
4
+ data.tar.gz: 6a1e0e40ed2c04a67232875132d9d2dddb4ad7ee
5
+ SHA512:
6
+ metadata.gz: b7e91f5b55dff971e14a56a146684d23666737209459d40c01da057f985e2b1bc5903667357ae4eaea8b8cd489ebe8d87ff526f745748c673af9492f8ba40386
7
+ data.tar.gz: 9d90c3d37b84bfe5e8585d9a72607ab214b137765d2afb952d16dc144102d1562f5b9da7e2cd18fcacc171aaccd476d1162a2d84e99ead6e27c5afb8dad37970
@@ -0,0 +1,18 @@
1
+ require 'app_mail/client'
2
+ require 'app_mail/config'
3
+
4
+ module AppMail
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
+ AppMail::Client.instance.send_message(&block)
16
+ end
17
+
18
+ end
@@ -0,0 +1,31 @@
1
+ require 'base64'
2
+
3
+ module AppMail
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,51 @@
1
+ require 'moonrope_client'
2
+ require 'app_mail/message_scope'
3
+ require 'app_mail/send_message'
4
+
5
+ module AppMail
6
+ class Client
7
+
8
+ #
9
+ # Create and cache a global instance of client based on the environment variables
10
+ # which can be provided. In 90% of cases, AppMail will be accessed through this.
11
+ #
12
+ def self.instance
13
+ @instance ||= Client.new(AppMail.config.host, AppMail.config.server_key)
14
+ end
15
+
16
+ #
17
+ # Initialize a new client with the host and API key
18
+ #
19
+ def initialize(host, server_key)
20
+ @host = host
21
+ @server_key = server_key
22
+ end
23
+
24
+ #
25
+ # Provide a scope to access messages
26
+ #
27
+ def messages
28
+ MessageScope.new(self)
29
+ end
30
+
31
+ #
32
+ # Send a message
33
+ #
34
+ def send_message(&block)
35
+ message = SendMessage.new(self)
36
+ block.call(message)
37
+ message.send!
38
+ end
39
+
40
+ #
41
+ # Return the backend moonrope instance for this client
42
+ #
43
+ def moonrope
44
+ @moonrope ||= begin
45
+ headers= {'X-Server-API-Key' => @server_key}
46
+ MoonropeClient::Connection.new(@host, :headers => headers, :ssl => true)
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ module AppMail
2
+ class Config
3
+
4
+ def host
5
+ @host || ENV['APPMAIL_HOST'] || 'api.appmail.io'
6
+ end
7
+ attr_writer :host
8
+
9
+ def server_key
10
+ @server_key || ENV['APPMAIL_KEY'] || raise(Error, "Server key has not been configured. Set it using the `AppMail.configure` block or use `APPMAIL_KEY` environment variable.")
11
+ end
12
+ attr_writer :server_key
13
+
14
+ end
15
+ end
@@ -0,0 +1,47 @@
1
+ module AppMail
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 AppMail
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 'app_mail/error'
2
+ require 'app_mail/header_set'
3
+ require 'app_mail/attachment'
4
+
5
+ module AppMail
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
+ AppMail::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 AppMail::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 'app_mail/message'
2
+
3
+ module AppMail
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,77 @@
1
+ require 'base64'
2
+ require 'app_mail/send_result'
3
+
4
+ module AppMail
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 to(*addresses)
28
+ @attributes[:to] ||= []
29
+ @attributes[:to] += addresses
30
+ end
31
+
32
+ def cc(*addresses)
33
+ @attributes[:cc] ||= []
34
+ @attributes[:cc] += addresses
35
+ end
36
+
37
+ def bcc(*addresses)
38
+ @attributes[:bcc] ||= []
39
+ @attributes[:bcc] += addresses
40
+ end
41
+
42
+ def subject(subject)
43
+ @attributes[:subject] = subject
44
+ end
45
+
46
+ def tag(tag)
47
+ @attributes[:tag] = subject
48
+ end
49
+
50
+ def reply_to(reply_to)
51
+ @attributes[:reply_to] = subject
52
+ end
53
+
54
+ def plain_body(content)
55
+ @attributes[:plain_body] = content
56
+ end
57
+
58
+ def html_body(content)
59
+ @attributes[:html_body] = content
60
+ end
61
+
62
+ def header(key, value)
63
+ @attributes[:headers] ||= {}
64
+ @attributes[:headers][key.to_s] = value
65
+ end
66
+
67
+ def attach(filename, content_type, data)
68
+ @attributes[:attachments] ||= []
69
+ @attributes[:attachments] << {
70
+ :name => filename,
71
+ :content_type => content_type,
72
+ :data => Base64.encode64(data)
73
+ }
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,30 @@
1
+ module AppMail
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
@@ -0,0 +1,3 @@
1
+ module AppMail
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1 @@
1
+ require 'app_mail'
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appmail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Adam Cooke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-29 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 AppMail E-Mail Platform
34
+ email:
35
+ - me@adamcooke.io
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - lib/app_mail.rb
41
+ - lib/app_mail/attachment.rb
42
+ - lib/app_mail/client.rb
43
+ - lib/app_mail/config.rb
44
+ - lib/app_mail/error.rb
45
+ - lib/app_mail/header_set.rb
46
+ - lib/app_mail/message.rb
47
+ - lib/app_mail/message_scope.rb
48
+ - lib/app_mail/send_message.rb
49
+ - lib/app_mail/send_result.rb
50
+ - lib/app_mail/version.rb
51
+ - lib/appmail.rb
52
+ homepage: https://appmail.io
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 AppMail E-Mail Platform
76
+ test_files: []