ferryman 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/ferryman.rb +141 -0
  3. 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: []