ferryman 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.
- checksums.yaml +7 -0
- data/lib/ferryman.rb +141 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7d253c3ecf37cdcb02dd390586852f37feb235b9
|
4
|
+
data.tar.gz: c83d132003bfc24337c6a8e431bb94a36e75ce6f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 814c75376361c13e49b314e1c35696d94a8eca071bb6285dea51ead10c65b72e8ea92f2f3f160bcc41ea3776c69e31f08eab5919dfb4c77e2f736b8921320bc5
|
7
|
+
data.tar.gz: cda89fb80b7b3e9912de04a9ae7633b1914ca574020a8ba629accf9c76de1fbe7d1a9347f3d2258ce75b74099ee2c6509a06945df99ec9459edf79bb07927d19
|
data/lib/ferryman.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'global_phone'
|
2
|
+
|
3
|
+
GlobalPhone.db_path = ENV.fetch('NUMBER_DB_FILE', 'phone_numbers.json')
|
4
|
+
|
5
|
+
module Ferryman
|
6
|
+
class ConfigError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class DeliveryError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class RoutingRule
|
13
|
+
attr_reader :strategy
|
14
|
+
attr_reader :providers
|
15
|
+
|
16
|
+
MATCH_ALL = lambda {|_| true}
|
17
|
+
STRATEGIES = {
|
18
|
+
'first' => lambda {|providers| providers},
|
19
|
+
'random' => lambda {|providers| providers.shuffle}
|
20
|
+
}
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@providers = []
|
24
|
+
@match_rules = []
|
25
|
+
@strategy = 'first'
|
26
|
+
end
|
27
|
+
|
28
|
+
def accept?(phone_number)
|
29
|
+
matched = true
|
30
|
+
|
31
|
+
@match_rules.each do |rule|
|
32
|
+
matched &&= rule.call(phone_number)
|
33
|
+
break unless matched
|
34
|
+
end
|
35
|
+
|
36
|
+
matched
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_match(match)
|
40
|
+
raise ConfigError.new('match cannot be nil!') if match.nil?
|
41
|
+
|
42
|
+
if match == '*'
|
43
|
+
@match_rules << MATCH_ALL
|
44
|
+
elsif match.respond_to? 'has_key?'
|
45
|
+
if match.has_key?(:regions)
|
46
|
+
regions = Array.new(match[:regions])
|
47
|
+
@match_rules << lambda do |number|
|
48
|
+
parsed_number = GlobalPhone.parse(number)
|
49
|
+
regions.include? parsed_number.territory.name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
else
|
53
|
+
raise ConfigError.new("Unexpected value for 'match'")
|
54
|
+
end
|
55
|
+
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
def providers
|
60
|
+
STRATEGIES[@strategy].call(@providers)
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_provider(provider)
|
64
|
+
raise ConfigError.new('provider cannot be nil!') if provider.nil?
|
65
|
+
@providers << provider
|
66
|
+
end
|
67
|
+
|
68
|
+
def strategy=(strategy)
|
69
|
+
raise ConfigError.new('unknown strategy') unless STRATEGIES.has_key?(strategy)
|
70
|
+
@strategy = strategy
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class Config
|
76
|
+
attr_reader :rules, :providers
|
77
|
+
|
78
|
+
def initialize
|
79
|
+
@providers = {}
|
80
|
+
@rules = []
|
81
|
+
@arity = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_provider(name, provider)
|
85
|
+
raise ConfigError.new("Provider #{name} already exists") if @providers.has_key?(name)
|
86
|
+
raise ConfigError.new("Provider must accept at least a phone number") if provider.arity < 1
|
87
|
+
raise ConfigError.new("Provider arguments does not match with existing") unless @arity.nil? || provider.arity == @arity
|
88
|
+
|
89
|
+
@arity = provider.arity
|
90
|
+
@providers[name] = provider
|
91
|
+
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_routing_table(table)
|
96
|
+
table.each do |row|
|
97
|
+
raise ConfigError.new('providers cannot be nil!') if row[:providers].nil?
|
98
|
+
|
99
|
+
rule = RoutingRule.new
|
100
|
+
rule.set_match row[:match]
|
101
|
+
rule.strategy = row.fetch(:strategy, 'first')
|
102
|
+
row[:providers].each {|provider| rule.add_provider(provider)}
|
103
|
+
@rules << rule
|
104
|
+
end
|
105
|
+
|
106
|
+
true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Dispatcher
|
111
|
+
attr_reader :config
|
112
|
+
|
113
|
+
def initialize
|
114
|
+
@config = Config.new
|
115
|
+
end
|
116
|
+
|
117
|
+
def deliver(phone_number, *args)
|
118
|
+
@config.rules.each do |rule|
|
119
|
+
next unless rule.accept?(phone_number)
|
120
|
+
|
121
|
+
rule.providers.each do |provider_name|
|
122
|
+
provider = @config.providers[provider_name]
|
123
|
+
params = [phone_number] + args
|
124
|
+
begin
|
125
|
+
return provider.call(*params)
|
126
|
+
rescue DeliveryError
|
127
|
+
next
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def Ferryman.create
|
137
|
+
dispatcher = Dispatcher.new
|
138
|
+
yield dispatcher.config
|
139
|
+
dispatcher
|
140
|
+
end
|
141
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ferryman
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jimmy Shen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: global_phone
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: global_phone_dbgen
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description: Ferryman allows you to de-couple routing logic and providers
|
70
|
+
email: thejimmyshen@gmail.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- lib/ferryman.rb
|
76
|
+
homepage: https://github.com/jimmyshen/ferryman
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.2.2
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Rules-based routing of SMS messages to providers.
|
100
|
+
test_files: []
|