hermes-rails 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9071b7e4a8721889e58bd6c4c197b19435190a6
4
+ data.tar.gz: e812e7eb39709a33c11cf6b78f20aced40b5fc58
5
+ SHA512:
6
+ metadata.gz: 34d64a8a92cdad335b5b4bdb9660f178c4d98163065209102befe8a86299116b14dd59a83254b202b4d7d4ebbf2cb9c7191316cbfd03a04dffdf34775ecdc9e0
7
+ data.tar.gz: e3fa78be6f7ec3b0c703d06e7e871ca2048c4c584aee63403c5c20f201310e5bae1ecf2a8e07b9365c28d813c3353a3f95c1e2448297bbb8ece0ef97ddc7e0d8
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Dogwood Labs, Inc.
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.
@@ -0,0 +1,3 @@
1
+ = MailgunRails
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'hermes'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,16 @@
1
+ require 'action_mailer'
2
+ require 'json'
3
+ require 'httparty'
4
+
5
+ # all of the Hermes support files
6
+ Dir[File.dirname(__FILE__) + '/support/*.rb'].each {|file| require file }
7
+ Dir[File.dirname(__FILE__) + '/hermes/*.rb'].each {|file| require file }
8
+
9
+ # all of the generic (abstract) provider support files
10
+ Dir[File.dirname(__FILE__) + '/providers/*.rb'].each {|file| require file }
11
+
12
+ # all of the actual provider support files
13
+ Dir[File.dirname(__FILE__) + '/providers/**/*.rb'].each {|file| require file }
14
+
15
+ module Hermes
16
+ end
@@ -0,0 +1,121 @@
1
+ require 'digest/md5'
2
+
3
+ module Hermes
4
+ class Deliverer
5
+ include Extractors
6
+
7
+ attr_reader :providers, :config
8
+
9
+ def initialize(settings)
10
+ @providers = {}
11
+
12
+ @config = settings[:config]
13
+
14
+ # this will most likely come back as [:email, :sms, :tweet, :webhook]
15
+ provider_types = @config.keys.reject{|key| key == :config}
16
+
17
+ # loop through each and construct a workable array
18
+ provider_types.each do |provider_type|
19
+ @providers[provider_type] ||= []
20
+ providers = settings[provider_type]
21
+ next unless providers.try(:any?)
22
+
23
+ # go through all of the providers and initialize each
24
+ providers.each do |provider_name, options|
25
+ # check to see that the provider class exists
26
+ provider_proper_name = "#{provider_name}_provider".camelize.to_sym
27
+ raise(ProviderNotFoundError, "Could not find provider class Hermes::#{provider_proper_name}") unless Hermes.constants.include?(provider_proper_name)
28
+
29
+ # initialize the provider with the given weight, defaults, and credentials
30
+ provider = Hermes.const_get(provider_proper_name).new(self, options)
31
+ @providers[provider_type] << provider
32
+ end
33
+
34
+ # make sure the provider type has an aggregate weight of more than 1
35
+ aweight = aggregate_weight_for_type(provider_type)
36
+ unless aweight > 0
37
+ raise(InvalidWeightError, "Provider type:#{provider_type} has aggregate weight:#{aweight}")
38
+ end
39
+ end
40
+ end
41
+
42
+ def test_mode?
43
+ !!@config[:test]
44
+ end
45
+
46
+ def handle_success(provider_name)
47
+ @config[:stats]
48
+ end
49
+
50
+ def handle_failure(provider_name, exception)
51
+
52
+ end
53
+
54
+ def should_deliver?
55
+ !self.test_mode? && ActionMailer::Base.perform_deliveries
56
+ end
57
+
58
+ def aggregate_weight_for_type(type)
59
+ providers = @providers[type]
60
+ return 0 if providers.empty?
61
+
62
+ providers.map(&:weight).inject(0, :+)
63
+ end
64
+
65
+ def weighted_provider_for_type(type)
66
+ providers = @providers[type]
67
+ return nil if providers.empty?
68
+
69
+ # get the aggregate weight, and do a rand based on it
70
+ random_index = rand(aggregate_weight_for_type(type))
71
+ # puts "random_index:#{random_index}"
72
+
73
+ # loop through each, exclusive range, and find the one that it falls on
74
+ running_total = 0
75
+ providers.each do |provider|
76
+ # puts "running_total:#{running_total}"
77
+ left_index = running_total
78
+ right_index = running_total + provider.weight
79
+ # puts "left_index:#{left_index} right_index:#{right_index}"
80
+
81
+ if (left_index...right_index).include?(random_index)
82
+ return provider
83
+ else
84
+ running_total += provider.weight
85
+ end
86
+ end
87
+ end
88
+
89
+ def delivery_type_for(rails_message)
90
+ to = extract_to(rails_message, format: :address)
91
+
92
+ if to.is_a?(Hash) && to[:twitter_username]
93
+ :tweet
94
+ elsif rails_message.to.first.include?('@')
95
+ :email
96
+ elsif to.is_a?(Phone)
97
+ :sms
98
+ elsif to.is_a?(URI)
99
+ :webhook
100
+ else
101
+ raise UnknownDeliveryTypeError, "Cannot determine provider type from provided to:#{to}"
102
+ end
103
+ end
104
+
105
+ def deliver!(rails_message)
106
+ # figure out what we're delivering
107
+ delivery_type = delivery_type_for(rails_message)
108
+
109
+ # set this on the message so it's available throughout
110
+ rails_message.hermes_type = delivery_type
111
+
112
+ # find a provider, weight matters here
113
+ provider = weighted_provider_for_type(delivery_type)
114
+
115
+ # and then send the message
116
+ provider.send_message(rails_message)
117
+ end
118
+ end
119
+ end
120
+
121
+ ActionMailer::Base.add_delivery_method :hermes, Hermes::Deliverer
@@ -0,0 +1,13 @@
1
+ module Hermes
2
+ # if provider is listed in settings, but no provider class is available
3
+ class ProviderNotFoundError < StandardError; end
4
+
5
+ # thrown if provider weight goes below 1
6
+ class InvalidWeightError < StandardError; end
7
+
8
+ # thrown if configuration does not provide all required credentials
9
+ class InsufficientCredentialsError < StandardError; end
10
+
11
+ # thrown if deliverer cannot figure out what type of provider to use for provided rails message
12
+ class UnknownDeliveryTypeError < StandardError; end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :hermes_type
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Hermes
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :mailgun_variables
4
+ attr_accessor :mailgun_options
5
+ attr_accessor :mailgun_recipient_variables
6
+ attr_accessor :mailgun_headers
7
+ attr_accessor :mailgun_domain
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ module Hermes
2
+ class MailgunAttachment < StringIO
3
+ attr_reader :original_filename, :content_type, :path
4
+
5
+ def initialize (attachment, *rest)
6
+ @path = ''
7
+ if rest.detect {|opt| opt[:inline] }
8
+ basename = @original_filename = attachment.cid
9
+ else
10
+ basename = @original_filename = attachment.filename
11
+ end
12
+ @content_type = attachment.content_type.split(';')[0]
13
+ super attachment.body.decoded
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ module Hermes
2
+ class MailgunProvider < Provider
3
+ required_credentials :api_key
4
+
5
+ def send_message(rails_message)
6
+ domain = rails_message.mailgun_domain || self.defaults[:domain]
7
+ message = self.mailgun_message(rails_message)
8
+
9
+ if self.deliverer.should_deliver?
10
+ self.client.send_message(domain, message)
11
+ end
12
+
13
+ self.message_success(rails_message)
14
+ end
15
+
16
+ def mailgun_message(rails_message)
17
+ message = Mailgun::MessageBuilder.new
18
+
19
+ # basics
20
+ message.set_from_address(extract_from(rails_message))
21
+ message.add_recipient(:to, extract_to(rails_message))
22
+ message.set_subject(rails_message[:subject])
23
+ message.set_html_body(extract_html(rails_message))
24
+ message.set_text_body(extract_text(rails_message))
25
+ message.set_message_id(rails_message.message_id)
26
+
27
+ # optionals
28
+ message.set_from_address('h:reply-to', rails_message[:reply_to].formatted.first) if rails_message[:reply_to]
29
+
30
+ # and any attachments
31
+ rails_message.attachments.try(:each) do |attachment|
32
+ message.add_attachment(Hermes::EmailAttachment.new(attachment))
33
+ end
34
+
35
+ return message
36
+ end
37
+
38
+ def client
39
+ Mailgun::Client.new(self.credentials[:api_key])
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ module Hermes
2
+ class OutboundWebhookProvider < Provider
3
+
4
+ def send_message(rails_message)
5
+ payload = payload(rails_message)
6
+ outbound_webhook = OutboundWebhook.create!(payload)
7
+ rails_message[:message_id] = outbound_webhook.id
8
+
9
+ byebug
10
+
11
+ if self.deliverer.should_deliver?
12
+ outbound_webhook.deliver_async
13
+ end
14
+
15
+ self.message_success(rails_message)
16
+ rescue Exception => e
17
+ self.message_failure(rails_message, e)
18
+ end
19
+
20
+ def payload(rails_message)
21
+ {
22
+ :endpoint => extract_to(rails_message),
23
+ :headers => {
24
+ 'Content-Type' => 'application/json'
25
+ },
26
+ :body => extract_text(rails_message)
27
+ }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :plivo_url
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ module Hermes
2
+ class PlivoProvider < Provider
3
+ required_credentials :auth_id, :auth_token
4
+
5
+ def send_message(rails_message)
6
+ payload = payload(rails_message)
7
+
8
+ if self.deliverer.should_deliver?
9
+ result = self.client.send_message(payload)
10
+ rails_message[:message_id] = result["api_id"]
11
+ else
12
+ # rails message still needs a fake sid as if it succeeded
13
+ rails_message[:message_id] = SecureRandom.uuid
14
+ end
15
+
16
+ self.message_success(rails_message)
17
+ end
18
+
19
+ def payload(rails_message)
20
+ {
21
+ src: extract_from(rails_message).full_number,
22
+ dst: extract_to(rails_message),
23
+ text: extract_text(rails_message),
24
+ type: :sms,
25
+ url: rails_message.plivo_url || self.defaults[:url]
26
+ }
27
+ end
28
+
29
+ def client
30
+ Plivo::RestAPI.new(self.credentials[:auth_id], self.credentials[:auth_token])
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,58 @@
1
+ module Hermes
2
+ class Provider
3
+ include Extractors
4
+
5
+ class << self
6
+ attr_accessor :_required_credentials
7
+
8
+ def required_credentials(*args)
9
+ self._required_credentials = args.to_a
10
+ end
11
+ end
12
+
13
+ attr_reader :deliverer, :defaults, :credentials, :weight
14
+
15
+ def initialize(deliverer, options = {})
16
+ options.symbolize_keys!
17
+
18
+ @deliverer = deliverer
19
+ @defaults = options[:defaults]
20
+ @credentials = (options[:credentials] || {}).symbolize_keys
21
+ @weight = options[:weight].to_i
22
+
23
+ if self.class._required_credentials.try(:any?)
24
+ # provider defines required credentials, let's make sure to check we have everything we need
25
+ if !((@credentials.keys & self.class._required_credentials) == self.class._required_credentials)
26
+ # we're missing something, raise here for hard failure
27
+ raise(InsufficientCredentialsError, "Credentials passed:#{@credentials.keys} do not satisfy all required:#{self.class._required_credentials}")
28
+ end
29
+ end
30
+
31
+ unless @weight >= 0
32
+ # provider weights need to be 0 (disabled), or greater than 0 to show as active
33
+ raise(InvalidWeightError, "Provider name:#{provider_name} has invalid weight:#{@weight}")
34
+ end
35
+ end
36
+
37
+ def provider_name
38
+ self.class.name.demodulize.underscore.gsub('_provider', '')
39
+ end
40
+
41
+ def send_message(rails_message)
42
+ raise "this is an abstract method and must be defined in the subclass"
43
+ end
44
+
45
+ def message_success(rails_message)
46
+ ActionMailer::Base.deliveries << rails_message if self.deliverer.test_mode?
47
+ self.deliverer.handle_success(self.class.name)
48
+ end
49
+
50
+ def message_failure(rails_message, exception)
51
+ Utils.log_and_puts "--- MESSAGE SEND FAILURE ---"
52
+ Utils.log_and_puts exception.message
53
+ Utils.log_and_puts exception.backtrace.join("\n")
54
+ Utils.log_and_puts "--- MESSAGE SEND FAILURE ---"
55
+ self.deliverer.handle_failure(self.class)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ module Hermes
2
+ class SendgridProvider < Provider
3
+ required_credentials :api_user, :api_key
4
+
5
+ def send_message(rails_message)
6
+ payload = payload(rails_message)
7
+
8
+ if self.deliverer.should_deliver?
9
+ client.send(payload)
10
+ end
11
+
12
+ self.message_success(message_success)
13
+ end
14
+
15
+ def payload(rails_message)
16
+ # requireds
17
+ message = SendGrid::Mail.new({
18
+ from: extract_from(rails_message, :address),
19
+ from_name: extract_from(rails_message, :name),
20
+ to: extract_to(rails_message, :address),
21
+ to_name: extract_to(rails_message, :name),
22
+ subject: rails_message.subject,
23
+ html: extract_html(rails_message),
24
+ text: extract_text(rails_message),
25
+ })
26
+
27
+ # optionals
28
+ message.reply_to = rails_message[:reply_to].formatted.first if rails_message[:reply_to]
29
+ message.message_id = rails_message[:message_id].value if rails_message.message_id
30
+
31
+ # and any attachments
32
+ rails_message.attachments.try(:each) do |attachment|
33
+ message.add_attachment_file(Hermes::EmailAttachment.new(attachment))
34
+ end
35
+
36
+ return message
37
+ end
38
+
39
+ def client
40
+ SendGrid::Client.new({
41
+ api_user: self.credentials[:api_user],
42
+ api_key: self.credentials[:api_key]
43
+ })
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :twilio_status_callback
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ module Hermes
2
+ class TwilioProvider < Provider
3
+ required_credentials :account_sid, :auth_token
4
+
5
+ def send_message(rails_message)
6
+ payload = payload(rails_message)
7
+
8
+ if self.deliverer.should_deliver?
9
+ result = self.client.account.messages.create(payload)
10
+
11
+ # set the sid onto the rails message as the message id, used for tracking
12
+ rails_message[:message_id] = result.sid
13
+ else
14
+ # rails message still needs a fake sid as if it succeeded
15
+ rails_message[:message_id] = SecureRandom.uuid
16
+ end
17
+
18
+ self.message_success(rails_message)
19
+ rescue Exception => e
20
+ self.message_failure(rails_message, e)
21
+ end
22
+
23
+ def payload(rails_message)
24
+ payload = {
25
+ to: extract_to(rails_message).full_number,
26
+ from: extract_from(rails_message),
27
+ body: extract_text(rails_message),
28
+ }
29
+
30
+ payload[:status_callback] = rails_message.twilio_status_callback if rails_message.twilio_status_callback
31
+
32
+ return payload
33
+ end
34
+
35
+ def client
36
+ Twilio::REST::Client.new(self.credentials[:account_sid], self.credentials[:auth_token])
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,6 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :twitter_oauth_token
4
+ attr_accessor :twitter_oauth_token_secret
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module Hermes
2
+ class TwitterProvider < Provider
3
+ required_credentials :consumer_key, :consumer_secret
4
+
5
+ def send_message(rails_message)
6
+ self.client(rails_message).update(extract_text(rails_message))
7
+ end
8
+
9
+ def client(rails_message)
10
+ # this will already be an instance of Twitter::client
11
+ client = extract_to(rails_message)
12
+
13
+ # just need to set the consumer key and secret and
14
+ # then we'll be ready for liftoff
15
+ client.consumer_key = self.credentials[:consumer_key]
16
+ client.consumer_secret = self.credentials[:consumer_secret]
17
+
18
+ return client
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Hermes
2
+ class B64Y
3
+ def self.encode(payload)
4
+ Base64.strict_encode64(YAML.dump(payload))
5
+ end
6
+
7
+ def self.decode(payload)
8
+ YAML.load(Base64.strict_decode64(payload))
9
+ rescue Exception => e
10
+ Utils.log_and_puts "--- DECODE FAILURE ---"
11
+ Utils.log_and_puts payload
12
+ Utils.log_and_puts "--- DECODE FAILURE ---"
13
+ raise e
14
+ end
15
+
16
+ def self.decodable?(payload)
17
+ # check to make sure when we decode that it's going to look like a YAML object
18
+ Base64.strict_decode64(payload)[0..2] == '---'
19
+ rescue
20
+ false
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Hermes
2
+ class EmailAttachment < StringIO
3
+ attr_reader :original_filename, :content_type, :path
4
+
5
+ def initialize (attachment, *rest)
6
+ @path = ''
7
+ if rest.detect {|opt| opt[:inline] }
8
+ basename = @original_filename = attachment.cid
9
+ else
10
+ basename = @original_filename = attachment.filename
11
+ end
12
+ @content_type = attachment.content_type.split(';')[0]
13
+ super attachment.body.decoded
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,68 @@
1
+ module Hermes
2
+ module Extractors
3
+ # @see http://stackoverflow.com/questions/4868205/rails-mail-getting-the-body-as-plain-text
4
+ def extract_html(rails_message)
5
+ if rails_message.html_part
6
+ rails_message.html_part.body.decoded
7
+ else
8
+ rails_message.content_type =~ /text\/html/ ? rails_message.body.decoded : nil
9
+ end
10
+ end
11
+
12
+ def extract_text(rails_message)
13
+ if rails_message.multipart?
14
+ rails_message.text_part ? rails_message.text_part.body.decoded.strip : nil
15
+ else
16
+ rails_message.content_type =~ /text\/plain/ ? rails_message.body.decoded.strip : nil
17
+ end
18
+ end
19
+
20
+ # format can be full|name|address
21
+ def extract_from(rails_message, format: :full)
22
+ from = complex_extract(rails_message.from.first)
23
+ return from[:value] if from[:decoded]
24
+
25
+ case format
26
+ when :full
27
+ rails_message[:from].formatted.first
28
+ when :name
29
+ rails_message[:from].address_list.addresses.first.name
30
+ when :address
31
+ rails_message[:from].address_list.addresses.first.address
32
+ end
33
+ end
34
+
35
+ # format can be full|name|address
36
+ def extract_to(rails_message, format: :full)
37
+ to = complex_extract(rails_message.to.first)
38
+ return to[:value] if to[:decoded]
39
+
40
+ case format
41
+ when :full
42
+ rails_message[:to].formatted.first
43
+ when :name
44
+ rails_message[:to].address_list.addresses.first.name
45
+ when :address
46
+ rails_message[:to].address_list.addresses.first.address
47
+ end
48
+ end
49
+
50
+ # when passing in to/from addresses that are complex objects
51
+ # like a Hash or Twitter::Client instance, they will be YAMLed
52
+ # and then Base64ed since Mail::Message really only wants
53
+ # to play with strings for these fields
54
+ def complex_extract(address_container)
55
+ if B64Y.decodable?(address_container)
56
+ {
57
+ decoded: true,
58
+ value: B64Y.decode(address_container)
59
+ }
60
+ else
61
+ {
62
+ decoded: false,
63
+ value: address_container
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,235 @@
1
+ module Hermes
2
+ class Phone
3
+
4
+ attr_accessor :country, :number
5
+
6
+ @@countries = {
7
+ :af => ['+93', 'Afghanistan'],
8
+ :al => ['+355', 'Albania'],
9
+ :dz => ['+213', 'Algeria'],
10
+ :as => ['+1684', 'American Samoa', :ws],
11
+ :ad => ['+376', 'Andorra'],
12
+ :ao => ['+244', 'Angola'],
13
+ :ai => ['+1264', 'Anguilla'],
14
+ :ag => ['+1268', 'Antigua and Barbuda'],
15
+ :ar => ['+54', 'Argentina'],
16
+ :am => ['+374', 'Armenia'],
17
+ :aw => ['+297', 'Aruba'],
18
+ :au => ['+61', 'Australia/Cocos/Christmas Island'],
19
+ :at => ['+43', 'Austria'],
20
+ :az => ['+994', 'Azerbaijan'],
21
+ :bs => ['+1', 'Bahamas'],
22
+ :bh => ['+973', 'Bahrain'],
23
+ :bd => ['+880', 'Bangladesh'],
24
+ :bb => ['+1246', 'Barbados'],
25
+ :by => ['+375', 'Belarus'],
26
+ :be => ['+32', 'Belgium'],
27
+ :bz => ['+501', 'Belize'],
28
+ :bj => ['+229', 'Benin'],
29
+ :bm => ['+1441', 'Bermuda'],
30
+ :bo => ['+591', 'Bolivia'],
31
+ :ba => ['+387', 'Bosnia and Herzegovina'],
32
+ :bw => ['+267', 'Botswana'],
33
+ :br => ['+55', 'Brazil'],
34
+ :bn => ['+673', 'Brunei'],
35
+ :bg => ['+359', 'Bulgaria'],
36
+ :bf => ['+226', 'Burkina Faso'],
37
+ :bi => ['+257', 'Burundi'],
38
+ :kh => ['+855', 'Cambodia'],
39
+ :cm => ['+237', 'Cameroon'],
40
+ :ca => ['+1', 'Canada'],
41
+ :cv => ['+238', 'Cape Verde'],
42
+ :ky => ['+1345', 'Cayman Islands'],
43
+ :cf => ['+236', 'Central Africa'],
44
+ :td => ['+235', 'Chad'],
45
+ :cl => ['+56', 'Chile'],
46
+ :cn => ['+86', 'China'],
47
+ :co => ['+57', 'Colombia'],
48
+ :km => ['+269', 'Comoros'],
49
+ :cg => ['+242', 'Congo'],
50
+ :cd => ['+243', 'Congo, Dem Rep'],
51
+ :cr => ['+506', 'Costa Rica'],
52
+ :hr => ['+385', 'Croatia'],
53
+ :cy => ['+357', 'Cyprus'],
54
+ :cz => ['+420', 'Czech Republic'],
55
+ :dk => ['+45', 'Denmark'],
56
+ :dj => ['+253', 'Djibouti'],
57
+ :dm => ['+1767', 'Dominica'],
58
+ :do => ['+1809', 'Dominican Republic'],
59
+ :eg => ['+20', 'Egypt'],
60
+ :sv => ['+503', 'El Salvador'],
61
+ :gq => ['+240', 'Equatorial Guinea'],
62
+ :ee => ['+372', 'Estonia'],
63
+ :et => ['+251', 'Ethiopia'],
64
+ :fo => ['+298', 'Faroe Islands'],
65
+ :fj => ['+679', 'Fiji'],
66
+ :fi => ['+358', 'Finland/Aland Islands'],
67
+ :fr => ['+33', 'France'],
68
+ :gf => ['+594', 'French Guiana'],
69
+ :pf => ['+689', 'French Polynesia'],
70
+ :ga => ['+241', 'Gabon'],
71
+ :gm => ['+220', 'Gambia'],
72
+ :ge => ['+995', 'Georgia'],
73
+ :de => ['+49', 'Germany'],
74
+ :gh => ['+233', 'Ghana'],
75
+ :gi => ['+350', 'Gibraltar'],
76
+ :gr => ['+30', 'Greece'],
77
+ :gl => ['+299', 'Greenland'],
78
+ :gd => ['+1473', 'Grenada'],
79
+ :gp => ['+590', 'Guadeloupe'],
80
+ :gu => ['+1671', 'Guam'],
81
+ :gt => ['+502', 'Guatemala'],
82
+ :gn => ['+224', 'Guinea'],
83
+ :gy => ['+592', 'Guyana'],
84
+ :ht => ['+509', 'Haiti'],
85
+ :hn => ['+504', 'Honduras'],
86
+ :hk => ['+852', 'Hong Kong'],
87
+ :hu => ['+36', 'Hungary'],
88
+ :is => ['+354', 'Iceland'],
89
+ :in => ['+91', 'India'],
90
+ :id => ['+62', 'Indonesia'],
91
+ :ir => ['+98', 'Iran'],
92
+ :iq => ['+964', 'Iraq'],
93
+ :ie => ['+353', 'Ireland'],
94
+ :il => ['+972', 'Israel'],
95
+ :it => ['+39', 'Italy'],
96
+ :jm => ['+1876', 'Jamaica'],
97
+ :jp => ['+81', 'Japan'],
98
+ :jo => ['+962', 'Jordan'],
99
+ :ke => ['+254', 'Kenya'],
100
+ :kr => ['+82', 'Korea, Republic of'],
101
+ :kw => ['+965', 'Kuwait'],
102
+ :kg => ['+996', 'Kyrgyzstan'],
103
+ :la => ['+856', 'Laos'],
104
+ :lv => ['+371', 'Latvia'],
105
+ :lb => ['+961', 'Lebanon'],
106
+ :ls => ['+266', 'Lesotho'],
107
+ :lr => ['+231', 'Liberia'],
108
+ :ly => ['+218', 'Libya'],
109
+ :li => ['+423', 'Liechtenstein'],
110
+ :lt => ['+370', 'Lithuania'],
111
+ :lu => ['+352', 'Luxembourg'],
112
+ :mo => ['+853', 'Macao'],
113
+ :mk => ['+389', 'Macedonia'],
114
+ :mg => ['+261', 'Madagascar'],
115
+ :mw => ['+265', 'Malawi'],
116
+ :my => ['+60', 'Malaysia'],
117
+ :mv => ['+960', 'Maldives'],
118
+ :ml => ['+223', 'Mali'],
119
+ :mt => ['+356', 'Malta'],
120
+ :mq => ['+596', 'Martinique'],
121
+ :mr => ['+222', 'Mauritania'],
122
+ :mu => ['+230', 'Mauritius'],
123
+ :mx => ['+52', 'Mexico'],
124
+ :mc => ['+377', 'Monaco'],
125
+ :mn => ['+976', 'Mongolia'],
126
+ :me => ['+382', 'Montenegro'],
127
+ :ms => ['+1664', 'Montserrat'],
128
+ :ma => ['+212', 'Morocco/Western Sahara'],
129
+ :mz => ['+258', 'Mozambique'],
130
+ :na => ['+264', 'Namibia'],
131
+ :np => ['+977', 'Nepal'],
132
+ :nl => ['+31', 'Netherlands'],
133
+ :nz => ['+64', 'New Zealand'],
134
+ :ni => ['+505', 'Nicaragua'],
135
+ :ne => ['+227', 'Niger'],
136
+ :ng => ['+234', 'Nigeria'],
137
+ :no => ['+47', 'Norway'],
138
+ :om => ['+968', 'Oman'],
139
+ :pk => ['+92', 'Pakistan'],
140
+ :ps => ['+970', 'Palestinian Territory'],
141
+ :pa => ['+507', 'Panama'],
142
+ :py => ['+595', 'Paraguay'],
143
+ :pe => ['+51', 'Peru'],
144
+ :ph => ['+63', 'Philippines'],
145
+ :pl => ['+48', 'Poland'],
146
+ :pt => ['+351', 'Portugal'],
147
+ :pr => ['+1787', 'Puerto Rico'],
148
+ :qa => ['+974', 'Qatar'],
149
+ :re => ['+262', 'Reunion/Mayotte'],
150
+ :ro => ['+40', 'Romania'],
151
+ :ru => ['+7', 'Russia/Kazakhstan'],
152
+ :rw => ['+250', 'Rwanda'],
153
+ :ws => ['+685', 'Samoa'],
154
+ :sm => ['+378', 'San Marino'],
155
+ :sa => ['+966', 'Saudi Arabia'],
156
+ :sn => ['+221', 'Senegal'],
157
+ :rs => ['+381', 'Serbia'],
158
+ :sc => ['+248', 'Seychelles'],
159
+ :sl => ['+232', 'Sierra Leone'],
160
+ :sg => ['+65', 'Singapore'],
161
+ :sk => ['+421', 'Slovakia'],
162
+ :si => ['+386', 'Slovenia'],
163
+ :za => ['+27', 'South Africa'],
164
+ :es => ['+34', 'Spain'],
165
+ :lk => ['+94', 'Sri Lanka'],
166
+ :kn => ['+1869', 'St Kitts and Nevis'],
167
+ :lc => ['+1758', 'St Lucia'],
168
+ :vc => ['+1784', 'St Vincent Grenadines'],
169
+ :sd => ['+249', 'Sudan'],
170
+ :sr => ['+597', 'Suriname'],
171
+ :sz => ['+268', 'Swaziland'],
172
+ :se => ['+46', 'Sweden'],
173
+ :ch => ['+41', 'Switzerland'],
174
+ :sy => ['+963', 'Syria'],
175
+ :tw => ['+886', 'Taiwan'],
176
+ :tj => ['+992', 'Tajikistan'],
177
+ :tz => ['+255', 'Tanzania'],
178
+ :th => ['+66', 'Thailand'],
179
+ :tg => ['+228', 'Togo'],
180
+ :to => ['+676', 'Tonga'],
181
+ :tt => ['+1868', 'Trinidad and Tobago'],
182
+ :tn => ['+216', 'Tunisia'],
183
+ :tr => ['+90', 'Turkey'],
184
+ :tc => ['+1649', 'Turks and Caicos Islands'],
185
+ :ug => ['+256', 'Uganda'],
186
+ :ua => ['+380', 'Ukraine'],
187
+ :ae => ['+971', 'United Arab Emirates'],
188
+ :gb => ['+44', 'United Kingdom'],
189
+ :us => ['+1', 'United States'],
190
+ :uy => ['+598', 'Uruguay'],
191
+ :uz => ['+998', 'Uzbekistan'],
192
+ :ve => ['+58', 'Venezuela'],
193
+ :vn => ['+84', 'Vietnam'],
194
+ :vg => ['+1284', 'Virgin Islands, British'],
195
+ :vi => ['+1340', 'Virgin Islands, U.S.'],
196
+ :ye => ['+967', 'Yemen'],
197
+ :zm => ['+260', 'Zambia'],
198
+ :zw => ['+263', 'Zimbabwe']
199
+ }.with_indifferent_access
200
+
201
+ def self.countries
202
+ @@countries
203
+ end
204
+
205
+ def ==(other_phone)
206
+ self.country == other_phone.country && self.number == other_phone.number
207
+ end
208
+
209
+ def to_s
210
+ self.full_number
211
+ end
212
+
213
+ def initialize(country, number)
214
+ @country = country
215
+ @number = number
216
+ end
217
+
218
+ def country_prefix
219
+ self.class.prefix_for_country(@country)
220
+ end
221
+
222
+ def country_name
223
+ self.class.name_for_country(@country)
224
+ end
225
+
226
+ def full_number
227
+ self.class.prefix_for_country(self.country) + self.number
228
+ end
229
+
230
+ def self.prefix_for_country(country)
231
+ return @@countries[country.to_s.downcase][0] unless @@countries[country.to_s.downcase].nil? || @@countries[country.to_s.downcase][0].nil?
232
+ return nil
233
+ end
234
+ end
235
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hermes-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Scott Klein
8
+ - Tyler Davis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-04-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 4.0.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 4.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: httparty
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0.12'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0.12'
42
+ - !ruby/object:Gem::Dependency
43
+ name: sqlite3
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: Rails Action Mailer adapter for balancing multiple providers across email,
57
+ SMS, and webhook
58
+ email:
59
+ - scott@statuspage.io
60
+ - tyler@statuspage.io
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - MIT-LICENSE
66
+ - README.rdoc
67
+ - Rakefile
68
+ - lib/hermes.rb
69
+ - lib/hermes/deliverer.rb
70
+ - lib/hermes/exceptions.rb
71
+ - lib/hermes/mail_ext.rb
72
+ - lib/hermes/version.rb
73
+ - lib/providers/mailgun/mail_ext.rb
74
+ - lib/providers/mailgun/mailgun_attachment.rb
75
+ - lib/providers/mailgun/mailgun_provider.rb
76
+ - lib/providers/outbound_webhook/outbound_webhook_provider.rb
77
+ - lib/providers/plivo/mail_ext.rb
78
+ - lib/providers/plivo/plivo_provider.rb
79
+ - lib/providers/provider.rb
80
+ - lib/providers/sendgrid/sendgrid_provider.rb
81
+ - lib/providers/twilio/mail_ext.rb
82
+ - lib/providers/twilio/twilio_provider.rb
83
+ - lib/providers/twitter/mail_ext.rb
84
+ - lib/providers/twitter/twitter_provider.rb
85
+ - lib/support/b64y.rb
86
+ - lib/support/email_attachment.rb
87
+ - lib/support/extractors.rb
88
+ - lib/support/phone.rb
89
+ homepage: https://github.com/StatusPage/hermes
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.2
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Rails Action Mailer adapter for balancing multiple providers across email,
113
+ SMS, and webhook
114
+ test_files: []