bank_routing 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 88bde94de995d591ff3d81b965520e43283061dd
4
- data.tar.gz: 2bb2075b2c196d0f0e4d44f71e2db3ae983dd156
3
+ metadata.gz: 6095689d8c9a679401259f9aa5fda46f02bdd3d1
4
+ data.tar.gz: 49eeb38e38a0b73334103bc1ae47fb4673451895
5
5
  SHA512:
6
- metadata.gz: 003ed0264f41dd7069f20e7c6ab8c79e76cec0539fbdac77c6a8400e18131ee4be352a5b26854637fa6f69d71a6f859ff67933eb3d970112e364d1b35f7d87b6
7
- data.tar.gz: eb60cc1b4dd8ccbb089420044e0bb926a4e20757da48718edab82d990ba581eda9242c104ebd6a0d68f70955b3a506d6577f2aa31d0cf5e29cd51483c16b6d5f
6
+ metadata.gz: cb6c5ab3054ab5fad8a5e9b1770a04660b09b4eaef9b8904bfeb707fcb71d40f181c337c9b7382e3ffd068cf0d52db39832805839c15d1160ebd0aa746151ca0
7
+ data.tar.gz: 69612ef6bfa9b1be5fe9fa58cef15c3c3632f312386cddddc4c6cfec1744f04a126c45d117e26a8b3db67e600f174f6175f436cf2a511f8fc210e856eb9e3886
data/README.md CHANGED
@@ -26,6 +26,7 @@ RoutingNumber.get(121000358)["name"] # => "Bank of America"
26
26
  By default, the routing number database is loaded from a local copy of the Federal Reserve dump file and stored in memory (it's not really that big). To change that behavior:
27
27
 
28
28
  ```ruby
29
+ require 'bank_routing/storage/redis'
29
30
  RoutingNumber.init!( store_in: :redis, store_opts: { db: 15 }, fetch_fed_data: true )
30
31
  ```
31
32
 
@@ -34,6 +35,7 @@ This will store the routing number database, after being loaded from the Federal
34
35
  You can also configure the service independently of loading the routing database. This works especially well in systems where there are child processes connecting to the same store (Redis, for example). In this case, the routing database will be loaded on first access (by the first child process of a Unicorn web server, for example) - it's essentially lazy-loaded, so a priming call is suggested after system startup. It is worth noting that if a shared data store is used (Redis), it will load the database only once, no matter how many processes attempt access.
35
36
 
36
37
  ```ruby
38
+ require 'bank_routing/storage/redis'
37
39
  RoutingNumber.options = {
38
40
  store_in: :redis,
39
41
  store_opts: {
@@ -1,163 +1,14 @@
1
1
  require 'logger'
2
2
  require 'yajl'
3
3
 
4
+ require "bank_routing/storage/memory"
5
+
4
6
  class RoutingNumber
5
-
7
+
6
8
  class InvalidStore < StandardError; end
7
-
8
- class StoreBase
9
-
10
- def initialize(opts = {})
11
- @options = opts
12
- @logger = opts.delete(:logger)
13
- end
14
-
15
- def log
16
- @logger ||= Logger.new(STDOUT)
17
- end
18
-
19
- def options
20
- @options
21
- end
22
-
23
- def loaded?
24
- @loaded
25
- end
26
-
27
- def loaded!
28
- @loaded = true
29
- end
30
-
31
- def loading!
32
- return false if loading?
33
- @loading = true
34
- end
35
-
36
- def loading?
37
- @loading
38
- end
39
-
40
- def done_loading!
41
- @loading = false
42
- loaded!
43
- end
44
-
45
- def save(num,obj)
46
- puts "Don't know how to save!"
47
- end
48
-
49
- def get(num)
50
- puts "Don't know how to get!"
51
- end
52
-
53
- def shutdown!
54
- end
55
-
56
- def reconnect!
57
- end
58
-
59
- end
60
-
61
- class MemStore < StoreBase
62
-
63
- def initialize(opts = {})
64
- @options = opts
65
- @vals = {}
66
- end
67
-
68
- def save(num,obj)
69
- @vals[num.to_s] = obj
70
- end
71
-
72
- def get(num)
73
- @vals[num.to_s]
74
- end
75
-
76
- def shutdown!
77
- @vals = nil
78
- end
79
-
80
- end
81
-
82
- class RedisStore < StoreBase
83
-
84
- LOADED_KEY = "LoadedRoutingNumbers".freeze
85
- LOADING_KEY = "#{LOADED_KEY}::loading".freeze
86
- STORAGE_KEY = "RoutingNumberLookup"
87
-
88
- def initialize(*args)
89
- super(*args)
90
- require 'redis'
91
- end
92
-
93
- def loaded?
94
- store.exists(LOADED_KEY)
95
- end
96
-
97
- def loaded!
98
- store.set(LOADED_KEY,"yes")
99
- store.expire(LOADED_KEY,60*60*24*7)
100
- end
101
-
102
- def loading!
103
- store.setnx(LOADING_KEY,"yup")
104
- end
105
-
106
- def loading?
107
- loading!
108
- end
109
-
110
- def done_loading!
111
- store.del(LOADING_KEY)
112
- loaded!
113
- end
114
-
115
- def save(num,obj)
116
- store.hset(STORAGE_KEY,num.to_s,Yajl::Encoder.encode(obj))
117
- end
118
-
119
- def get(num)
120
- if cnt = store.hget(STORAGE_KEY,num.to_s)
121
- Yajl::Parser.new(symbolize_keys: true).parse(cnt)
122
- else
123
- nil
124
- end
125
- end
126
-
127
- def store
128
- @store ||= (
129
- log.info "Connecting to Redis."
130
- log.debug "Connection settings: #{options.inspect}"
131
- Redis.new(options)
132
- )
133
- end
134
-
135
- def reconnect!
136
- @store = nil
137
- store
138
- end
139
-
140
- def shutdown!
141
- @store = nil
142
- end
143
-
144
- end
145
-
146
- # class MemcacheStore < StoreBase
147
- #
148
- #
149
- #
150
- # end
151
-
152
- Stores = {
153
- default: MemStore,
154
- memory: MemStore,
155
- redis: RedisStore,
156
- # memcached: MemcachedStore
157
- }
158
-
9
+
159
10
  class << self
160
-
11
+
161
12
  FORMAT = [
162
13
  [:route,9],
163
14
  [:office_code,1],
@@ -178,7 +29,7 @@ class RoutingNumber
178
29
  [:data_view_code,1],
179
30
  [:filler,5]
180
31
  ].freeze
181
-
32
+
182
33
  DefaultOptions = {
183
34
  store_in: :memory,
184
35
  routing_data_url: "https://www.fededirectory.frb.org/FedACHdir.txt",
@@ -188,7 +39,7 @@ class RoutingNumber
188
39
  mapping_file: File.expand_path(File.dirname(__FILE__) + "/../../data/mappings.json"),
189
40
  metadata_file: File.expand_path(File.dirname(__FILE__) + "/../../data/metadata.json")
190
41
  }.freeze
191
-
42
+
192
43
  def init!(opts = {})
193
44
  return if @initted
194
45
  @options = options.merge(opts)
@@ -202,11 +53,11 @@ class RoutingNumber
202
53
  end
203
54
  @initted = true
204
55
  end
205
-
56
+
206
57
  def options=(opts)
207
58
  @options = options.merge(opts)
208
59
  end
209
-
60
+
210
61
  def get(num)
211
62
  init!(options)
212
63
  if cnt = store.get(num.to_s)
@@ -217,7 +68,7 @@ class RoutingNumber
217
68
  end
218
69
  alias_method :[], :get
219
70
  alias_method :find, :get
220
-
71
+
221
72
  def store_in(name, opts={})
222
73
  raise InvalidStore unless s_cls = Stores[name.to_sym]
223
74
  return if @store.is_a?(s_cls)
@@ -228,26 +79,31 @@ class RoutingNumber
228
79
  options[:store_opts] = opts
229
80
  init!
230
81
  end
231
-
82
+
232
83
  def reconnect!
233
84
  log.info "Reconnecting!"
234
85
  store.reconnect!
235
86
  end
236
-
87
+
88
+ def fetch_fresh_data!( fetch=true )
89
+ options[ :fetch_fed_data ] = fetch
90
+ load_routing_numbers( get_raw_data )
91
+ end
92
+
237
93
  private
238
-
94
+
239
95
  def store
240
96
  @store ||= default_store
241
97
  end
242
-
98
+
243
99
  def default_store
244
100
  Stores[:default].new(logger: log)
245
101
  end
246
-
102
+
247
103
  def options
248
104
  @options ||= DefaultOptions.dup
249
105
  end
250
-
106
+
251
107
  def get_raw_data
252
108
  unless options[:fetch_fed_data]
253
109
  log.info "Using routing data from local file at: #{options[:routing_data_file]}"
@@ -258,7 +114,7 @@ class RoutingNumber
258
114
  Typhoeus::Request.get(options[:routing_data_url], :ssl_verifypeer => false).body
259
115
  end
260
116
  end
261
-
117
+
262
118
  def load_routing_numbers(data=get_raw_data)
263
119
  if store.loading!
264
120
  loading!
@@ -269,7 +125,7 @@ class RoutingNumber
269
125
  done_loading!
270
126
  end
271
127
  end
272
-
128
+
273
129
  def process_line(line)
274
130
  obj = unpack_line(line)
275
131
  rt = obj.delete(:route)
@@ -280,24 +136,24 @@ class RoutingNumber
280
136
  print "\r #{@cur} loaded (#{tm.round(2)} seconds ~ #{tm > 0 ? (@cur / tm).round(0) : "?"}/sec) "
281
137
  end
282
138
  end
283
-
139
+
284
140
  def pretty_maps
285
141
  @pretty_maps
286
142
  end
287
-
143
+
288
144
  def loading!
289
145
  @cur = 0
290
146
  @st_time = Time.now
291
147
  @pretty_maps = Yajl::Parser.parse(IO.read(options[:mapping_file]))
292
148
  @metadata = Yajl::Parser.parse(IO.read(options[:metadata_file]))
293
149
  end
294
-
150
+
295
151
  def done_loading!
296
152
  tm = Time.now - @st_time
297
153
  print "\r #{@cur} loaded (#{tm.round(2)} seconds ~ #{tm > 0 ? (@cur / tm).round(0) : "?"}/sec) \n"
298
154
  @cur = @st_time = @pretty_maps = @metadata = nil
299
155
  end
300
-
156
+
301
157
  def unpack_line(line)
302
158
  vals = line.unpack(unpack_template)
303
159
  obj = {}
@@ -310,7 +166,7 @@ class RoutingNumber
310
166
  obj.delete(:garbage)
311
167
  obj
312
168
  end
313
-
169
+
314
170
  def converted(name,val)
315
171
  if pretty_maps[name.to_s] and (v = pretty_maps[name.to_s][val])
316
172
  v
@@ -318,17 +174,17 @@ class RoutingNumber
318
174
  val
319
175
  end
320
176
  end
321
-
177
+
322
178
  def unpack_template
323
179
  @unpack_format ||= FORMAT.map do |(name,length)|
324
180
  "A#{length}"
325
181
  end.join
326
182
  end
327
-
183
+
328
184
  def log
329
185
  @logger ||= Logger.new(STDOUT)
330
186
  end
331
-
187
+
332
188
  end
333
-
334
- end
189
+
190
+ end
@@ -0,0 +1,58 @@
1
+ class RoutingNumber
2
+
3
+ Stores = {}
4
+
5
+ class StoreBase
6
+
7
+ def initialize(opts = {})
8
+ @options = opts
9
+ @logger = opts.delete(:logger)
10
+ end
11
+
12
+ def log
13
+ @logger ||= Logger.new(STDOUT)
14
+ end
15
+
16
+ def options
17
+ @options
18
+ end
19
+
20
+ def loaded?
21
+ @loaded
22
+ end
23
+
24
+ def loaded!
25
+ @loaded = true
26
+ end
27
+
28
+ def loading!
29
+ return false if loading?
30
+ @loading = true
31
+ end
32
+
33
+ def loading?
34
+ @loading
35
+ end
36
+
37
+ def done_loading!
38
+ @loading = false
39
+ loaded!
40
+ end
41
+
42
+ def save(num,obj)
43
+ puts "Don't know how to save!"
44
+ end
45
+
46
+ def get(num)
47
+ puts "Don't know how to get!"
48
+ end
49
+
50
+ def shutdown!
51
+ end
52
+
53
+ def reconnect!
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,29 @@
1
+ require "bank_routing/storage/base"
2
+
3
+ class RoutingNumber
4
+
5
+ class MemStore < StoreBase
6
+
7
+ def initialize(opts = {})
8
+ @options = opts
9
+ @vals = {}
10
+ end
11
+
12
+ def save(num,obj)
13
+ @vals[num.to_s] = obj
14
+ end
15
+
16
+ def get(num)
17
+ @vals[num.to_s]
18
+ end
19
+
20
+ def shutdown!
21
+ @vals = nil
22
+ end
23
+
24
+ end
25
+
26
+ Stores[:default] = MemStore
27
+ Stores[:memory] = MemStore
28
+
29
+ end
@@ -0,0 +1,73 @@
1
+ require "bank_routing/storage/base"
2
+
3
+ class RoutingNumber
4
+
5
+ class RedisStore < StoreBase
6
+
7
+ LOADED_KEY = "LoadedRoutingNumbers".freeze
8
+ LOADING_KEY = "#{LOADED_KEY}::loading".freeze
9
+ STORAGE_KEY = "RoutingNumberLookup"
10
+
11
+ def initialize(*args)
12
+ super(*args)
13
+ require 'redis'
14
+ end
15
+
16
+ def loaded?
17
+ store.exists(LOADED_KEY)
18
+ end
19
+
20
+ def loaded!
21
+ store.set(LOADED_KEY,"yes")
22
+ store.expire(LOADED_KEY,60*60*24*7)
23
+ end
24
+
25
+ def loading!
26
+ store.setnx(LOADING_KEY,"yup")
27
+ end
28
+
29
+ def loading?
30
+ loading!
31
+ end
32
+
33
+ def done_loading!
34
+ store.del(LOADING_KEY)
35
+ loaded!
36
+ end
37
+
38
+ def save(num,obj)
39
+ store.hset(STORAGE_KEY,num.to_s,Yajl::Encoder.encode(obj))
40
+ end
41
+
42
+ def get(num)
43
+ if cnt = store.hget(STORAGE_KEY,num.to_s)
44
+ Yajl::Parser.new(symbolize_keys: true).parse(cnt)
45
+ else
46
+ nil
47
+ end
48
+ end
49
+
50
+ def store
51
+ @store ||= connect
52
+ end
53
+
54
+ def connect
55
+ log.info "Connecting to Redis."
56
+ log.debug "Connection settings: #{options.inspect}"
57
+ Redis.new(options)
58
+ end
59
+
60
+ def reconnect!
61
+ @store = nil
62
+ store
63
+ end
64
+
65
+ def shutdown!
66
+ @store = nil
67
+ end
68
+
69
+ end
70
+
71
+ Stores[:redis] = RedisStore
72
+
73
+ end
@@ -1,4 +1,4 @@
1
1
  module BankRouting
2
2
  # bank_routing version
3
- VERSION = "1.0.1"
4
- end
3
+ VERSION = "1.0.2"
4
+ end
@@ -8,14 +8,27 @@ describe BankRouting do
8
8
  end
9
9
 
10
10
  describe RoutingNumber do
11
-
11
+
12
12
  it "should get a number with no config" do
13
13
  RoutingNumber.get(121000358)[:name].should eq("Bank of America")
14
14
  end
15
-
15
+
16
16
  it "should take a config and reload" do
17
+ require 'bank_routing/storage/redis'
18
+ RoutingNumber.store_in :redis
19
+ RoutingNumber.get(121000358)[:name].should eq("Bank of America")
20
+ end
21
+
22
+ it "should load fresh data" do
23
+ require 'bank_routing/storage/redis'
17
24
  RoutingNumber.store_in :redis
18
25
  RoutingNumber.get(121000358)[:name].should eq("Bank of America")
26
+ RoutingNumber.fetch_fresh_data!(false)
27
+ RoutingNumber.get(121000358)[:name].should eq("Bank of America")
19
28
  end
20
-
29
+
30
+ it "should include metadata" do
31
+ RoutingNumber.get(114994196)[:prepaid_card].should eq(true)
32
+ end
33
+
21
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bank_routing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cozy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-10 00:00:00.000000000 Z
11
+ date: 2014-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -131,6 +131,9 @@ files:
131
131
  - data/metadata.json
132
132
  - lib/bank_routing.rb
133
133
  - lib/bank_routing/routing_numbers.rb
134
+ - lib/bank_routing/storage/base.rb
135
+ - lib/bank_routing/storage/memory.rb
136
+ - lib/bank_routing/storage/redis.rb
134
137
  - lib/bank_routing/version.rb
135
138
  - spec/bank_routing_spec.rb
136
139
  - spec/spec_helper.rb