vin_exploder 0.1.0
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.
- data/Gemfile +3 -0
- data/Gemfile.lock +47 -0
- data/README.textile +74 -0
- data/lib/vin_exploder/adapter.rb +47 -0
- data/lib/vin_exploder/cache/activerecord_cache_store.rb +42 -0
- data/lib/vin_exploder/cache/sequel_cache_store.rb +40 -0
- data/lib/vin_exploder/cache.rb +76 -0
- data/lib/vin_exploder/configuration.rb +45 -0
- data/lib/vin_exploder/exploder.rb +30 -0
- data/lib/vin_exploder/explosion.rb +40 -0
- data/lib/vin_exploder/version.rb +3 -0
- data/lib/vin_exploder.rb +19 -0
- data/spec/abstract_adapter_spec.rb +16 -0
- data/spec/cache/activerecord_cache_store_spec.rb +74 -0
- data/spec/cache/cache_store_spec.rb +49 -0
- data/spec/cache/sequel_cache_store_spec.rb +62 -0
- data/spec/configuration_spec.rb +50 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/vin_exploder_spec.rb +51 -0
- metadata +139 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
vin_exploder (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activemodel (3.0.8)
|
10
|
+
activesupport (= 3.0.8)
|
11
|
+
builder (~> 2.1.2)
|
12
|
+
i18n (~> 0.5.0)
|
13
|
+
activerecord (3.0.8)
|
14
|
+
activemodel (= 3.0.8)
|
15
|
+
activesupport (= 3.0.8)
|
16
|
+
arel (~> 2.0.10)
|
17
|
+
tzinfo (~> 0.3.23)
|
18
|
+
activesupport (3.0.8)
|
19
|
+
arel (2.0.10)
|
20
|
+
builder (2.1.2)
|
21
|
+
diff-lcs (1.1.2)
|
22
|
+
i18n (0.5.0)
|
23
|
+
rspec (2.6.0)
|
24
|
+
rspec-core (~> 2.6.0)
|
25
|
+
rspec-expectations (~> 2.6.0)
|
26
|
+
rspec-mocks (~> 2.6.0)
|
27
|
+
rspec-core (2.6.3)
|
28
|
+
rspec-expectations (2.6.0)
|
29
|
+
diff-lcs (~> 1.1.2)
|
30
|
+
rspec-mocks (2.6.0)
|
31
|
+
sequel (3.24.1)
|
32
|
+
simplecov (0.4.2)
|
33
|
+
simplecov-html (~> 0.4.4)
|
34
|
+
simplecov-html (0.4.5)
|
35
|
+
sqlite3 (1.3.3)
|
36
|
+
tzinfo (0.3.27)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
activerecord (>= 3.0.0)
|
43
|
+
rspec (>= 2.6.0)
|
44
|
+
sequel
|
45
|
+
simplecov
|
46
|
+
sqlite3
|
47
|
+
vin_exploder!
|
data/README.textile
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
h1. VinExploder
|
2
|
+
|
3
|
+
A ruby library for retrieving and caching Vehicle Identification Number (VIN) lookups.
|
4
|
+
|
5
|
+
h2. How to install
|
6
|
+
|
7
|
+
*Bundler*
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
gem vin_exploder
|
11
|
+
</pre>
|
12
|
+
|
13
|
+
*Rubygems*
|
14
|
+
|
15
|
+
<pre>
|
16
|
+
gem install vin_exploder
|
17
|
+
</pre>
|
18
|
+
|
19
|
+
h2. How to use
|
20
|
+
|
21
|
+
Using the vin exploder will require a decoding service adapter. Thankfully one is available in the Vinquery "gem":http://github.com/tinomen/vinquery
|
22
|
+
|
23
|
+
<pre>
|
24
|
+
require 'vin_exploder'
|
25
|
+
# require the Vinquery.com adapter
|
26
|
+
require 'vinquery/vin_exploder/adapter'
|
27
|
+
|
28
|
+
# tell vin_exploder to use the Vinquery.com adapter and pass it's required config
|
29
|
+
VinExploder.config.adapter :vinquery, {:url => 'VINQUERY_URL', :access_code => 'ACCESS_CODE', :report_type => 'REPORT_TYPE'}
|
30
|
+
# request the decoded VIN data
|
31
|
+
vin_explosion = VinExploder.get('1FTWX31R19EB18840')
|
32
|
+
|
33
|
+
vin_explosion.valid? # true
|
34
|
+
vin_explosion.make # Ford
|
35
|
+
</pre>
|
36
|
+
|
37
|
+
h3. Rails use
|
38
|
+
|
39
|
+
Create an initializer for the config
|
40
|
+
|
41
|
+
<pre>
|
42
|
+
require 'vin_exploder'
|
43
|
+
require 'vinquery/vin_exploder/adapter'
|
44
|
+
|
45
|
+
VinExploder.config.adapter :vinquery, {:url => 'VINQUERY_URL', :access_code => 'ACCESS_CODE', :report_type => 'REPORT_TYPE'}
|
46
|
+
</pre>
|
47
|
+
|
48
|
+
h3. Optional Cache
|
49
|
+
|
50
|
+
To save on the cost of looking up a VIN multiple times setup a cache
|
51
|
+
|
52
|
+
<pre>
|
53
|
+
require 'vin_exploder'
|
54
|
+
# require the Sequel based cache store
|
55
|
+
require 'vin_exploder/cache/sequel_cache_store'
|
56
|
+
# require the Vinquery.com adapter
|
57
|
+
require 'vinquery/vin_exploder/adapter'
|
58
|
+
|
59
|
+
# tell vin_exploder to use the Sequel based cache store adapter and pass in the connection config
|
60
|
+
VinExploder.config.cache_store :sequel_cache_store, "mysql://localhost:3896/vin_cache"
|
61
|
+
|
62
|
+
# tell vin_exploder to use the Vinquery.com adapter and pass it's required config
|
63
|
+
VinExploder.config.adapter :vinquery, {:url => 'VINQUERY_URL', :access_code => 'ACCESS_CODE', :report_type => 'REPORT_TYPE'}
|
64
|
+
|
65
|
+
# request the decoded VIN data
|
66
|
+
vin_explosion = VinExploder.get('1FTWX31R19EB18840')
|
67
|
+
|
68
|
+
vin_explosion.valid? #=> true
|
69
|
+
vin_explosion.make #=> Ford
|
70
|
+
|
71
|
+
# decoded VIN data is retrieved from cache
|
72
|
+
vin_explosion2 = VinExploder.get('1FTWX31R19EB18840')
|
73
|
+
vin_explosion2.make #=> Ford
|
74
|
+
</pre>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module VinExploder
|
3
|
+
|
4
|
+
module Decode
|
5
|
+
|
6
|
+
class VinExploderAdapterError < StandardError; end
|
7
|
+
|
8
|
+
class AbstractAdapter
|
9
|
+
|
10
|
+
def initialize(options={})
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
# Fetch, normalize and return the decoded data as a hash.
|
15
|
+
#
|
16
|
+
# *vin*::
|
17
|
+
# The Vehicle Identification Number
|
18
|
+
#
|
19
|
+
# == Returns:
|
20
|
+
# An array containing the decoded data hash.
|
21
|
+
#
|
22
|
+
# Should an error occur while decoding the vin the hash should
|
23
|
+
# include a key of :errors with an array of errors
|
24
|
+
# in the form of: {'error name' => 'error message'}
|
25
|
+
#
|
26
|
+
# Normalize the hash from the vendor to use keys known by VinExploder.
|
27
|
+
# All keys should be made into symbols following the ruby snake case convention.
|
28
|
+
#
|
29
|
+
# The keys that must be normalized are as follows:
|
30
|
+
# make
|
31
|
+
# model
|
32
|
+
# year => YYYY
|
33
|
+
# driveline => FWD|4WD|AWD
|
34
|
+
# body_style
|
35
|
+
# fuel_type => GAS|DIESEL|HYBRID
|
36
|
+
# trim_level
|
37
|
+
#
|
38
|
+
# All remaining keys should be symbols but can remain vendor specific
|
39
|
+
def explode(vin)
|
40
|
+
raise NotImplementedError.new("Unimplemented explode method.")
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'Base64'
|
3
|
+
|
4
|
+
module VinExploder
|
5
|
+
module Cache
|
6
|
+
|
7
|
+
# A VinExploder cache adapter using ActiveRecord for saving the decoded vin attributes.
|
8
|
+
#
|
9
|
+
# this store assumes a model with 2 attributes:
|
10
|
+
# - *key*: the first 8, 10th and 11th characters of the vin
|
11
|
+
# - *data*: A hash of the decoded vin attributes
|
12
|
+
class ActiveRecordCacheStore < Store
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
super
|
16
|
+
@model = options[:model_class]
|
17
|
+
end
|
18
|
+
|
19
|
+
def read(vin)
|
20
|
+
key = make_vin_cache_key(vin)
|
21
|
+
obj = @model.find_by_key(key)
|
22
|
+
obj.data unless obj.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def write(vin, hash)
|
26
|
+
key = make_vin_cache_key(vin)
|
27
|
+
obj = @model.new :key => key, :data => hash
|
28
|
+
obj.save
|
29
|
+
hash
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete(vin)
|
33
|
+
key = make_vin_cache_key(vin)
|
34
|
+
obj = @model.find_by_key(key)
|
35
|
+
deleted = obj.delete()
|
36
|
+
!deleted.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'Base64'
|
3
|
+
|
4
|
+
module VinExploder
|
5
|
+
module Cache
|
6
|
+
|
7
|
+
# A VinExploder cache adapter using Sequel for saving the decoded vin attributes.
|
8
|
+
#
|
9
|
+
# this store assumes there are 2 columns on the table:
|
10
|
+
# - *key*: the first 8, 10th and 11th characters of the vin
|
11
|
+
# - *data*: A hash of the decoded vin attributes
|
12
|
+
class SequelCacheStore < Store
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
super
|
16
|
+
@connection = Sequel.connect(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def read(vin)
|
20
|
+
key = make_vin_cache_key(vin)
|
21
|
+
data = @connection[:vins].where(:key => key).first
|
22
|
+
Marshal.load(data[:data]) unless data.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def write(vin, hash)
|
26
|
+
key = make_vin_cache_key(vin)
|
27
|
+
@connection[:vins].insert(:key => key, :data => Marshal.dump(hash))
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(vin)
|
32
|
+
key = make_vin_cache_key(vin)
|
33
|
+
result = @connection[:vins].where(:key => key).delete
|
34
|
+
result > 0
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
|
2
|
+
module VinExploder
|
3
|
+
|
4
|
+
module Cache
|
5
|
+
|
6
|
+
|
7
|
+
# An abstract cache store class that acts as a null cache as well
|
8
|
+
class Store
|
9
|
+
|
10
|
+
attr_reader :connection
|
11
|
+
|
12
|
+
# Create a new cache.
|
13
|
+
def initialize(options = nil)
|
14
|
+
@options = options ? options.dup : {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Fetches VIN data from the cache using the given key. If the VIN has been
|
18
|
+
# cached, then the VIN attributes are returned.
|
19
|
+
#
|
20
|
+
# If the VIN is not in the cache (a cache miss occurred),
|
21
|
+
# then nil will be returned. However, if a block has been passed, then
|
22
|
+
# that block will be run in the event of a cache miss. The return value
|
23
|
+
# of the block will be written to the cache under the given VIN,
|
24
|
+
# and that return value will be returned.
|
25
|
+
#
|
26
|
+
# cache.write("VIN_NUMBER", {:make => 'Ford', :model => 'F150'})
|
27
|
+
# cache.fetch("VIN_NUMBER") # => {:make => 'Ford', :model => 'F150'}
|
28
|
+
#
|
29
|
+
# cache.fetch("VIN_NUMBER_2") # => nil
|
30
|
+
# cache.fetch("VIN_NUMBER_2") do
|
31
|
+
# {:make => 'Dodge', :model => '1500'}
|
32
|
+
# end
|
33
|
+
# cache.fetch("VIN_NUMBER_2") # => {:make => 'Dodge', :model => '1500'}
|
34
|
+
#
|
35
|
+
def fetch(vin)
|
36
|
+
if block_given?
|
37
|
+
hash = read(vin)
|
38
|
+
if hash.nil?
|
39
|
+
hash = yield
|
40
|
+
write(vin, hash)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
hash = read(vin)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Fetches VIN data from the cache, using the given key. If VIN has
|
48
|
+
# been cached with the given key, then the VIN attributes are returned. Otherwise,
|
49
|
+
# nil is returned.
|
50
|
+
def read(vin)
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Writes the value to the cache, with the key.
|
55
|
+
def write(vin, hash)
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
|
59
|
+
# Deletes an entry in the cache. Returns +true+ if an entry is deleted.
|
60
|
+
def delete(vin)
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
# the cache key for vins should be based on characters 0-8, 10-11.
|
67
|
+
# Position 9 is a checksum value and should not be used in the key.
|
68
|
+
def make_vin_cache_key(vin)
|
69
|
+
key = vin.slice(0,8)
|
70
|
+
key << vin.slice(10,2)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
module VinExploder
|
3
|
+
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
attr_accessor :cache_options, :adapter_options
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@cache_store = nil
|
10
|
+
@cache_options = {}
|
11
|
+
@adapter = nil
|
12
|
+
@adapter_options = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def cache_store(*args)
|
16
|
+
if args.empty?
|
17
|
+
case @cache_store
|
18
|
+
when Symbol
|
19
|
+
VinExploder::Cache.const_get(@cache_store.to_s.split('_').map{|s| s.capitalize }.join)
|
20
|
+
else
|
21
|
+
@cache_store
|
22
|
+
end
|
23
|
+
else
|
24
|
+
@cache_store = args.shift
|
25
|
+
@cache_options = args.shift || {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def adapter(*args)
|
30
|
+
if args.empty?
|
31
|
+
case @adapter
|
32
|
+
when Symbol
|
33
|
+
VinExploder::Decode.const_get(@adapter.to_s.split('_').map{|s| s.capitalize }.join)
|
34
|
+
else
|
35
|
+
@adapter
|
36
|
+
end
|
37
|
+
else
|
38
|
+
@adapter = args.shift
|
39
|
+
@adapter_options = args.shift || {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module VinExploder
|
2
|
+
|
3
|
+
class MissingAdapter < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class Exploder
|
7
|
+
|
8
|
+
def initialize(adapter, cache=nil)
|
9
|
+
raise MissingAdapter.new("No vin decoding vendor adapter has been provided.") if adapter.nil?
|
10
|
+
@cache = cache ? cache : Cache::Store.new
|
11
|
+
@adapter = adapter
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get vin Explosion.
|
15
|
+
#
|
16
|
+
# == Parameters
|
17
|
+
# vin:: vehicle identification number
|
18
|
+
#
|
19
|
+
# == Return
|
20
|
+
# An Explosion object containing the decoded vin attributes
|
21
|
+
def get(vin)
|
22
|
+
hash = @cache.fetch(vin) do
|
23
|
+
# get from vender adapter
|
24
|
+
@adapter.explode(vin)
|
25
|
+
end
|
26
|
+
Explosion.new vin, hash, hash.delete(:errors)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module VinExploder
|
4
|
+
|
5
|
+
class Explosion
|
6
|
+
|
7
|
+
attr_reader :vin, :success, :errors, :make, :model, :year, :driveline, :body_style, :fuel_type, :all
|
8
|
+
|
9
|
+
def initialize(vin, vin_hash, errors={})
|
10
|
+
@vin = vin
|
11
|
+
@all = vin_hash
|
12
|
+
@make = @all[:make]
|
13
|
+
@model = @all[:model]
|
14
|
+
@year = @all[:year]
|
15
|
+
@driveline = @all[:driveline]
|
16
|
+
@body_style = @all[:body_style]
|
17
|
+
@fuel_type = @all[:fuel_type]
|
18
|
+
@trim_level = @all[:trim_level]
|
19
|
+
@errors = errors.nil? ? {} : errors
|
20
|
+
@success = @errors.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
@success
|
25
|
+
end
|
26
|
+
alias :success? :valid?
|
27
|
+
|
28
|
+
# def _dump(level=-1)
|
29
|
+
# [@vin, @all.to_yaml].join('^')
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# def self._load(args)
|
33
|
+
# a = *args.split('^')
|
34
|
+
# hash = YAML.load(a[1])
|
35
|
+
# new(a[0], hash)
|
36
|
+
# end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/lib/vin_exploder.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module VinExploder
|
3
|
+
|
4
|
+
def self.explode(vin)
|
5
|
+
cache = VinExploder.config.cache_store ? VinExploder.config.cache_store.new(VinExploder.config.cache_options) : nil
|
6
|
+
adapter = VinExploder.config.adapter.new(VinExploder.config.adapter_options)
|
7
|
+
exploder = Exploder.new(adapter, cache)
|
8
|
+
explosion = exploder.get(vin)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.config
|
12
|
+
@@config ||= VinExploder::Configuration.new()
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'vin_exploder/cache'
|
18
|
+
require 'vin_exploder/explosion'
|
19
|
+
require 'vin_exploder/exploder'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vin_exploder/adapter'
|
3
|
+
|
4
|
+
module VinExploder
|
5
|
+
module Decode
|
6
|
+
|
7
|
+
describe AbstractAdapter do
|
8
|
+
|
9
|
+
it "should raise an NotImplementedError on explode" do
|
10
|
+
expect { AbstractAdapter.new.explode("VIN") }.to raise_error(NotImplementedError)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sqlite3'
|
3
|
+
require 'vin_exploder/cache'
|
4
|
+
require 'vin_exploder/cache/activerecord_cache_store'
|
5
|
+
|
6
|
+
module VinExploder
|
7
|
+
module Cache
|
8
|
+
|
9
|
+
class TestArCacheStore < ActiveRecord::Base
|
10
|
+
attr_accessor :vin, :make
|
11
|
+
serialize :data
|
12
|
+
def initialize(attributes={})
|
13
|
+
super attributes
|
14
|
+
key = attributes.delete(:key)
|
15
|
+
# @data = attributes[:data]
|
16
|
+
@attributes = {'key' => key, 'vin' => attributes[:vin], 'make' => attributes[:make], 'data' => attributes[:data]}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ActiveRecordCacheStore do
|
21
|
+
|
22
|
+
before(:all) do
|
23
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ':memory:')
|
24
|
+
ActiveRecord::Schema.define do
|
25
|
+
create_table :test_ar_cache_stores, :force => true do |t|
|
26
|
+
t.column :key, :string
|
27
|
+
t.column :vin, :string
|
28
|
+
t.column :make, :string
|
29
|
+
t.column :data, :text
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@store = ActiveRecordCacheStore.new :model_class => TestArCacheStore
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
after(:each) do
|
38
|
+
TestArCacheStore.delete_all
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should read and write a hash" do
|
42
|
+
hash = @store.read("fake_vin_number")
|
43
|
+
hash.should be_nil
|
44
|
+
@store.write("fake_vin_number", {:vin => 'fake_vin_number', :make => 'Ford'})
|
45
|
+
hash = @store.read("fake_vin_number")
|
46
|
+
hash.should_not be_nil
|
47
|
+
hash[:make].should == 'Ford'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should fetch and cache new vins" do
|
51
|
+
hash = @store.fetch("fake_vin_number") do
|
52
|
+
{:vin => 'fake_vin_number', :make => 'Ford'}
|
53
|
+
end
|
54
|
+
hash2 = @store.read("fake_vin_number")
|
55
|
+
hash[:vin].should == hash2[:vin]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should delete a vin from the cache" do
|
59
|
+
hash = @store.read("fake_vin_number")
|
60
|
+
hash.should be_nil
|
61
|
+
@store.write(:fake_vin_number, {:vin => 'fake_vin_number', :make => 'Ford'})
|
62
|
+
hash = @store.read("fake_vin_number")
|
63
|
+
hash.should_not be_nil
|
64
|
+
hash[:make].should == 'Ford'
|
65
|
+
deleted = @store.delete("fake_vin_number")
|
66
|
+
deleted.should == true
|
67
|
+
hash = @store.read("fake_vin_number")
|
68
|
+
hash.should be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module VinExploder
|
4
|
+
module Cache
|
5
|
+
|
6
|
+
describe Store do
|
7
|
+
|
8
|
+
describe '#read' do
|
9
|
+
it 'should return the cached vin explosion' do
|
10
|
+
Store.new.read('DOESNTMATTER').should == nil
|
11
|
+
end
|
12
|
+
it 'should return nil if no explosion found' do
|
13
|
+
Store.new.read('DOESNTMATTER').should == nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#write' do
|
18
|
+
it 'should return the value that was written' do
|
19
|
+
Store.new.write('VIN', 'EXPLODED_VIN').should == 'EXPLODED_VIN'
|
20
|
+
Store.new.read('VIN').should == nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#fetch' do
|
25
|
+
it 'should execute the block and pass through #write' do
|
26
|
+
store = Store.new
|
27
|
+
store.should_receive(:write).with('VIN', 'EXPLODED_VIN').exactly(1).times { 'EXPLODED_VIN' }
|
28
|
+
store.fetch('VIN') do
|
29
|
+
'EXPLODED_VIN'
|
30
|
+
end.should == 'EXPLODED_VIN'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should return nil with no block' do
|
34
|
+
store = Store.new
|
35
|
+
store.should_receive(:read).with('VIN').exactly(1).times { 'EXPLODED_VIN' }
|
36
|
+
store.fetch('VIN').should == 'EXPLODED_VIN'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#delete' do
|
41
|
+
it 'should return true if cache item was deleted' do
|
42
|
+
Store.new.delete('VIN').should == true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sqlite3'
|
3
|
+
require 'sequel'
|
4
|
+
require 'vin_exploder/cache'
|
5
|
+
require 'vin_exploder/cache/sequel_cache_store'
|
6
|
+
|
7
|
+
module VinExploder
|
8
|
+
module Cache
|
9
|
+
|
10
|
+
describe SequelCacheStore do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
@db_config = {:adapter => "sqlite"}
|
14
|
+
@store = SequelCacheStore.new @db_config
|
15
|
+
@store.connection.create_table(:vins) do
|
16
|
+
String :key, :unique=>true, :size=>10
|
17
|
+
String :data, :null=>false, :text=>true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
after(:each) do
|
22
|
+
@store.connection[:vins].truncate
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should initialize" do
|
26
|
+
@store.class.should == SequelCacheStore
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should read and write a hash" do
|
30
|
+
hash = @store.read(:fake_vin_number)
|
31
|
+
hash.should be_nil
|
32
|
+
@store.write(:fake_vin_number, {:vin => 'fake_vin_number', :make => 'Ford'})
|
33
|
+
hash = @store.read(:fake_vin_number)
|
34
|
+
hash.should_not be_nil
|
35
|
+
hash[:make].should == 'Ford'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should fetch and cache new vins" do
|
39
|
+
hash = @store.fetch(:fake_vin_number) do
|
40
|
+
{:vin => 'fake_vin_number', :make => 'Ford'}
|
41
|
+
end
|
42
|
+
hash2 = @store.read(:fake_vin_number)
|
43
|
+
hash[:vin].should == hash2[:vin]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should delete a vin from the cache" do
|
47
|
+
hash = @store.read(:fake_vin_number)
|
48
|
+
hash.should be_nil
|
49
|
+
@store.write(:fake_vin_number, {:vin => 'fake_vin_number', :make => 'Ford'})
|
50
|
+
hash = @store.read(:fake_vin_number)
|
51
|
+
hash.should_not be_nil
|
52
|
+
hash[:make].should == 'Ford'
|
53
|
+
deleted = @store.delete(:fake_vin_number)
|
54
|
+
deleted.should == true
|
55
|
+
hash = @store.read(:fake_vin_number)
|
56
|
+
hash.should be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vin_exploder/configuration'
|
3
|
+
|
4
|
+
module VinExploder
|
5
|
+
|
6
|
+
describe Configuration do
|
7
|
+
|
8
|
+
describe '#cache_store' do
|
9
|
+
it 'should return nil if no cache is set' do
|
10
|
+
c = Configuration.new
|
11
|
+
c.cache_store.should == nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should convert a symbol to a camel case constant within the VinExploder::Cache scope' do
|
15
|
+
class VinExploder::Cache::TestSymbolConst; end
|
16
|
+
c = Configuration.new
|
17
|
+
c.cache_store :test_symbol_const
|
18
|
+
c.cache_store.should == VinExploder::Cache::TestSymbolConst
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should return the constant previously set' do
|
22
|
+
c = Configuration.new
|
23
|
+
c.cache_store String
|
24
|
+
c.cache_store.should == String
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#adapter' do
|
29
|
+
it 'should return nil where no adapter defined' do
|
30
|
+
c = Configuration.new
|
31
|
+
c.adapter.should == nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should convert a symbol to a camel case constant within the VinExploder::Decode scope' do
|
35
|
+
class VinExploder::Decode::TestSymbolConst; end
|
36
|
+
c = Configuration.new
|
37
|
+
c.adapter :test_symbol_const
|
38
|
+
c.adapter.should == VinExploder::Decode::TestSymbolConst
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should return the constant previously set' do
|
42
|
+
c = Configuration.new
|
43
|
+
c.adapter String
|
44
|
+
c.adapter.should == String
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
if ENV["COVERAGE"]
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start 'rails'
|
5
|
+
end
|
6
|
+
|
7
|
+
require "bundler"
|
8
|
+
Bundler.setup
|
9
|
+
|
10
|
+
require 'rspec'
|
11
|
+
|
12
|
+
require 'vin_exploder'
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vin_exploder'
|
3
|
+
|
4
|
+
class VinExploder::Decode::TestAdapter; end
|
5
|
+
|
6
|
+
module VinExploder
|
7
|
+
|
8
|
+
describe Exploder do
|
9
|
+
|
10
|
+
describe '#new' do
|
11
|
+
it "should raise a MissingAdapter error if nil is provided to initialize" do
|
12
|
+
expect { Exploder.new(nil) }.to raise_error(MissingAdapter)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should raise a ArgumentError error if no arguments are provided to initialize" do
|
16
|
+
expect { Exploder.new }.to raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should initialize with a default cache store when no cache argument is passed" do
|
20
|
+
s = VinExploder::Cache::Store.new
|
21
|
+
VinExploder::Cache::Store.should_receive(:new).exactly(1).times { s }
|
22
|
+
e = Exploder.new(double("Adapter"))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#get' do
|
27
|
+
it "should get a vin Explosion" do
|
28
|
+
a = double("Adapter")
|
29
|
+
a.should_receive(:explode) { {} }
|
30
|
+
e = Exploder.new(a)
|
31
|
+
ex = e.get('VIN')
|
32
|
+
ex.class.should == VinExploder::Explosion
|
33
|
+
ex.valid?.should == true
|
34
|
+
ex.success?.should == true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "VinExploder#explode" do
|
39
|
+
it "should return an Explosion object" do
|
40
|
+
ta = VinExploder::Decode::TestAdapter.new
|
41
|
+
ta.should_receive(:explode) { {} }
|
42
|
+
VinExploder::Decode::TestAdapter.should_receive(:new){ ta }
|
43
|
+
|
44
|
+
VinExploder.config.adapter :test_adapter
|
45
|
+
VinExploder.explode("VIN").class.should == Explosion
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vin_exploder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jake Mallory
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-06-20 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.6.0
|
24
|
+
type: :development
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: simplecov
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: sequel
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: sqlite3
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id004
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: activerecord
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 3.0.0
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id005
|
70
|
+
description: A caching client for vin decoding web services. The caching allows the consumer to avoid the cost of secondary lookups.
|
71
|
+
email: tinomen@gmail.com
|
72
|
+
executables: []
|
73
|
+
|
74
|
+
extensions: []
|
75
|
+
|
76
|
+
extra_rdoc_files:
|
77
|
+
- Gemfile
|
78
|
+
- Gemfile.lock
|
79
|
+
- README.textile
|
80
|
+
files:
|
81
|
+
- lib/vin_exploder/adapter.rb
|
82
|
+
- lib/vin_exploder/cache/activerecord_cache_store.rb
|
83
|
+
- lib/vin_exploder/cache/sequel_cache_store.rb
|
84
|
+
- lib/vin_exploder/cache.rb
|
85
|
+
- lib/vin_exploder/configuration.rb
|
86
|
+
- lib/vin_exploder/exploder.rb
|
87
|
+
- lib/vin_exploder/explosion.rb
|
88
|
+
- lib/vin_exploder/version.rb
|
89
|
+
- lib/vin_exploder.rb
|
90
|
+
- Gemfile
|
91
|
+
- Gemfile.lock
|
92
|
+
- README.textile
|
93
|
+
- spec/abstract_adapter_spec.rb
|
94
|
+
- spec/cache/activerecord_cache_store_spec.rb
|
95
|
+
- spec/cache/cache_store_spec.rb
|
96
|
+
- spec/cache/sequel_cache_store_spec.rb
|
97
|
+
- spec/configuration_spec.rb
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
- spec/vin_exploder_spec.rb
|
100
|
+
homepage: http://github.com/tinomen/vin_exploder
|
101
|
+
licenses: []
|
102
|
+
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options:
|
105
|
+
- --title
|
106
|
+
- A caching client for vin decoding services
|
107
|
+
- --main
|
108
|
+
- README.textile
|
109
|
+
- --line-numbers
|
110
|
+
- --charset=UTF-8
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: "0"
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.3.6
|
125
|
+
requirements: []
|
126
|
+
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.7.2
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: A caching client for vin decoding services
|
132
|
+
test_files:
|
133
|
+
- spec/abstract_adapter_spec.rb
|
134
|
+
- spec/cache/activerecord_cache_store_spec.rb
|
135
|
+
- spec/cache/cache_store_spec.rb
|
136
|
+
- spec/cache/sequel_cache_store_spec.rb
|
137
|
+
- spec/configuration_spec.rb
|
138
|
+
- spec/spec_helper.rb
|
139
|
+
- spec/vin_exploder_spec.rb
|