skeevy 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/.gitignore +34 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/Guardfile +46 -0
- data/LICENSE +21 -0
- data/README.md +21 -0
- data/lib/skeevy.rb +37 -0
- data/lib/skeevy/cutter.rb +22 -0
- data/lib/skeevy/cutters/standard_key.rb +20 -0
- data/lib/skeevy/engine.rb +22 -0
- data/lib/skeevy/engines/directory_file.rb +74 -0
- data/lib/skeevy/engines/symbolic_hash.rb +37 -0
- data/lib/skeevy/instance.rb +46 -0
- data/lib/skeevy/version.rb +3 -0
- data/skeevy.gemspec +19 -0
- data/spec/lib/skeevy/cutters/standard_key_spec.rb +18 -0
- data/spec/lib/skeevy/engines/directory_file_spec.rb +50 -0
- data/spec/lib/skeevy/engines/symbolic_hash_spec.rb +15 -0
- data/spec/lib/skeevy/instance_spec.rb +49 -0
- data/spec/lib/skeevy/version_spec.rb +2 -0
- data/spec/lib/skeevy_spec.rb +55 -0
- data/spec/shared/cutter.rb +58 -0
- data/spec/shared/engine.rb +57 -0
- data/spec/spec_helper.rb +18 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 60dbb058fbd64464e26b03c882c45c4b2db40de1
|
4
|
+
data.tar.gz: 5590acba53788fcb6bff72a29541748c839138d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f664d159edf898beb72fb627f2f7d4b4d094137fb2092ebbe6546111b73418ec524fd818b4732b6cfff412a113af8b2b7ccc5be30058d59c98c8e99da49ce6c0
|
7
|
+
data.tar.gz: fdfc302598f411e23c598f63b96deaa9563ba2683d030ff8fd92cb8a752cd801efac25351a49664df52ccc5988f9f1e4550085df696ffa2af1b450ac5a011ccd
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
.*.sw[a-z]
|
15
|
+
|
16
|
+
## RubyMine and related
|
17
|
+
.idea
|
18
|
+
|
19
|
+
## PROJECT::GENERAL
|
20
|
+
coverage
|
21
|
+
rdoc
|
22
|
+
pkg
|
23
|
+
|
24
|
+
## PROJECT::SPECIFIC
|
25
|
+
*.gem
|
26
|
+
.bundle
|
27
|
+
Gemfile.lock
|
28
|
+
pkg/*
|
29
|
+
tmp/*
|
30
|
+
*.rbc
|
31
|
+
*.rbx
|
32
|
+
.ruby-gemset
|
33
|
+
.ruby-version
|
34
|
+
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %(app lib config test spec feature)
|
6
|
+
|
7
|
+
## Uncomment to clear the screen before every task
|
8
|
+
# clearing :on
|
9
|
+
|
10
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
11
|
+
# rspec may be run, below are examples of the most common uses.
|
12
|
+
# * bundler: 'bundle exec rspec'
|
13
|
+
# * bundler binstubs: 'bin/rspec'
|
14
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
15
|
+
# installed the spring binstubs per the docs)
|
16
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
17
|
+
# * 'just' rspec: 'rspec'
|
18
|
+
|
19
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
20
|
+
require "ostruct"
|
21
|
+
|
22
|
+
# Generic Ruby apps
|
23
|
+
rspec = OpenStruct.new
|
24
|
+
rspec.spec = ->(m) { "spec/#{m}_spec.rb" }
|
25
|
+
rspec.spec_dir = "spec"
|
26
|
+
rspec.spec_helper = "spec/spec_helper.rb"
|
27
|
+
|
28
|
+
watch(%r{^spec/.+_spec\.rb$})
|
29
|
+
watch(%r{^lib/(.+)\.rb$}) { |m|
|
30
|
+
rspec.spec.("lib/#{m[1]}")
|
31
|
+
}
|
32
|
+
watch(%r{^lib/skeevy/(.+)\.rb$}) { |m|
|
33
|
+
rspec.spec.("lib/skeevy/#{m[1]}")
|
34
|
+
rspec.spec.("lib/skeevy")
|
35
|
+
}
|
36
|
+
watch(%r{^lib/skeevy/cutters/(.+)\.rb$}) { |m|
|
37
|
+
rspec.spec.("lib/skeevy/cutters/#{m[1]}")
|
38
|
+
rspec.spec.("lib/skeevy")
|
39
|
+
}
|
40
|
+
watch(%r{^lib/skeevy/engines/(.+)\.rb$}) { |m|
|
41
|
+
rspec.spec.("lib/engines/#{m[1]}")
|
42
|
+
rspec.spec.("lib/skeevy")
|
43
|
+
}
|
44
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
45
|
+
|
46
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Craig Waterman
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Skeevy
|
2
|
+
==============
|
3
|
+
|
4
|
+
Simple Key Value abstractions, supporting pluggable storage engines.
|
5
|
+
|
6
|
+
Requirements
|
7
|
+
-----------------
|
8
|
+
|
9
|
+
Skeevy requires Ruby 2.1 for required named parameters. Rubinius 2.4+, JRuby
|
10
|
+
9000+ and MRI 2.1+ seem functional.
|
11
|
+
|
12
|
+
Installation
|
13
|
+
-----------------
|
14
|
+
|
15
|
+
gem install skeevy
|
16
|
+
|
17
|
+
|
18
|
+
Getting Started
|
19
|
+
-----------------
|
20
|
+
|
21
|
+
Start coding.
|
data/lib/skeevy.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'skeevy/version'
|
2
|
+
require 'skeevy/engine'
|
3
|
+
require 'skeevy/cutter'
|
4
|
+
require 'skeevy/cutters/standard_key'
|
5
|
+
require 'skeevy/engines/symbolic_hash'
|
6
|
+
require 'skeevy/engines/directory_file'
|
7
|
+
require 'skeevy/instance'
|
8
|
+
|
9
|
+
fail "Skeevy #{Skeevy::VERSION} requires Ruby 2.1 or later." if RUBY_VERSION < '2.1.0'
|
10
|
+
|
11
|
+
module Skeevy
|
12
|
+
|
13
|
+
# Poor-man's singleton
|
14
|
+
class << self
|
15
|
+
def register!(identifier:, engine: nil, cutter: nil)
|
16
|
+
raise(ArgumentError, "Identifier must be a symbol") unless identifier.is_a?(Symbol)
|
17
|
+
@instances ||= {}
|
18
|
+
@instances[identifier] = Instance.new identifier: identifier,
|
19
|
+
engine: engine,
|
20
|
+
cutter: cutter
|
21
|
+
end
|
22
|
+
|
23
|
+
def instance(identifier)
|
24
|
+
raise(ArgumentError, "Identifier must be a symbol") unless identifier.is_a?(Symbol)
|
25
|
+
@instances[identifier]
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](identifier)
|
29
|
+
instance(identifier)
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"Skeevy #{Skeevy::VERSION}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Skeevy
|
2
|
+
module Cutter
|
3
|
+
|
4
|
+
attr_accessor :instance
|
5
|
+
attr_reader :delimiter
|
6
|
+
attr_reader :prefix
|
7
|
+
|
8
|
+
def initialize(instance: nil, delimiter: '-', prefix: nil)
|
9
|
+
raise(ArgumentError, "Unable to init Skeevy Cutters without a delimiter") if delimiter.nil?
|
10
|
+
raise ArgumentError,
|
11
|
+
"Instance passed was not a Skeevy::Instance" unless
|
12
|
+
instance.is_a?(Skeevy::Instance) || instance.nil?
|
13
|
+
@instance = instance
|
14
|
+
@delimiter = delimiter
|
15
|
+
@prefix = prefix
|
16
|
+
end
|
17
|
+
|
18
|
+
def cut(hash:, ns:, object: nil)
|
19
|
+
raise NoMethodError, "Your Cutter is invalid."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Skeevy
|
2
|
+
module Cutters
|
3
|
+
class StandardKey
|
4
|
+
include Skeevy::Cutter
|
5
|
+
|
6
|
+
def cut(hash:, ns:, object: nil)
|
7
|
+
object ||= ''
|
8
|
+
hash ||= ''
|
9
|
+
ns ||= ''
|
10
|
+
raise ArgumentError, "Hash and namespace may not be blank" if hash.empty? || ns.empty?
|
11
|
+
raise ArgumentError, "#{object} contains an instance of delimiter: #{@delimiter}" if object.include?(@delimiter)
|
12
|
+
prefix ="#{@prefix}#{@delimiter}" unless @prefix.nil?
|
13
|
+
key = "#{ns}#{@delimiter}#{hash}"
|
14
|
+
suffix = "#{@delimiter}#{object}" unless object == ''
|
15
|
+
"#{prefix}#{key}#{suffix}"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Skeevy
|
2
|
+
module Engine
|
3
|
+
|
4
|
+
attr_accessor :instance
|
5
|
+
|
6
|
+
def read(key:)
|
7
|
+
raise NoMethodError, "Your Engine is missing .read"
|
8
|
+
end
|
9
|
+
|
10
|
+
def write!(key:, content:)
|
11
|
+
raise NoMethodError, "Your Engine is missing .write!"
|
12
|
+
end
|
13
|
+
|
14
|
+
def exist?(key:)
|
15
|
+
raise NoMethodError, "Your Engine is missing .exist?"
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete!(key:)
|
19
|
+
raise NoMethodError, "Your Engine is missing .delete!"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Skeevy
|
2
|
+
module Engines
|
3
|
+
class DirectoryFile
|
4
|
+
include Skeevy::Engine
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
def initialize(instance:, base_dir:, delimiter:, encoding: 'UTF-8')
|
8
|
+
raise ArgumentError,
|
9
|
+
"Instance passed was not a Skeevy::Instance" unless
|
10
|
+
instance.is_a?(Skeevy::Instance) || instance.nil?
|
11
|
+
@base_dir = base_dir
|
12
|
+
@encoding = encoding
|
13
|
+
@instance = instance
|
14
|
+
@delimiter = delimiter
|
15
|
+
@exists_cache = {}
|
16
|
+
ensure_base_dir_exists
|
17
|
+
end
|
18
|
+
|
19
|
+
def path_for(key:)
|
20
|
+
directory = key.split(@delimiter)
|
21
|
+
filename = directory.pop # naive
|
22
|
+
path = File.join(@base_dir, directory)
|
23
|
+
ensure_exists(path: path)
|
24
|
+
File.join(path, filename)
|
25
|
+
end
|
26
|
+
|
27
|
+
def exist?(key:, path: nil)
|
28
|
+
if path.nil?
|
29
|
+
File.exist? path_for(key: key)
|
30
|
+
else
|
31
|
+
File.exist? path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def read(key:)
|
36
|
+
path = path_for(key: key)
|
37
|
+
if exist?(path: path, key: nil)
|
38
|
+
File.open(path, "r:#{@encoding}") { |f|
|
39
|
+
f.read
|
40
|
+
}
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def write!(key:, content:)
|
47
|
+
path = path_for(key: key)
|
48
|
+
File.open(path, "w:#{@encoding}") { |f|
|
49
|
+
f.write content
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete!(key:)
|
54
|
+
path = path_for(key: key)
|
55
|
+
if exist?(path: path, key: nil)
|
56
|
+
File.unlink(path)
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def ensure_exists(path:)
|
65
|
+
FileUtils.mkdir_p(path) if @exists_cache[path].nil?
|
66
|
+
@exists_cache[path] = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def ensure_base_dir_exists
|
70
|
+
ensure_exists(path: @base_dir)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Skeevy
|
2
|
+
module Engines
|
3
|
+
class SymbolicHash
|
4
|
+
include Skeevy::Engine
|
5
|
+
|
6
|
+
def initialize(instance:)
|
7
|
+
raise ArgumentError,
|
8
|
+
"Instance passed was not a Skeevy::Instance" unless
|
9
|
+
instance.is_a?(Skeevy::Instance) || instance.nil?
|
10
|
+
@instance = instance
|
11
|
+
@items = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def exist?(key:)
|
15
|
+
@items.has_key? key.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def read(key:)
|
19
|
+
@items[key.to_sym]
|
20
|
+
end
|
21
|
+
|
22
|
+
def write!(key:, content:)
|
23
|
+
@items[key.to_sym] = content
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete!(key:)
|
27
|
+
k = key.to_sym
|
28
|
+
if @items.has_key?(k)
|
29
|
+
@items.delete k
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Skeevy
|
2
|
+
class Instance
|
3
|
+
|
4
|
+
attr_reader :engine, :cutter
|
5
|
+
|
6
|
+
def initialize(identifier:,
|
7
|
+
engine: nil,
|
8
|
+
cutter: nil)
|
9
|
+
raise(ArgumentError, "identifier must be a Symbol") unless identifier.is_a?(Symbol)
|
10
|
+
raise(ArgumentError, "engine is not a Skeevy Engine!") unless @engine.is_a?(Skeevy::Engine) || engine.nil?
|
11
|
+
raise(ArgumentError, "cutter is not a Skeevy Cutter!") unless @cutter.is_a?(Skeevy::Cutter) || cutter.nil?
|
12
|
+
@cutter = cutter || Skeevy::Cutters::StandardKey.new(instance: self)
|
13
|
+
@engine = engine || Skeevy::Engines::SymbolicHash.new(instance: self)
|
14
|
+
@identifier = identifier
|
15
|
+
end
|
16
|
+
|
17
|
+
def object_key(hash:, ns:, object:)
|
18
|
+
@cutter.cut(hash: hash, ns: ns, object: object)
|
19
|
+
end
|
20
|
+
|
21
|
+
def container_key(hash:, ns:)
|
22
|
+
@cutter.cut(hash: hash, ns: ns, object: nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
def read(key:)
|
26
|
+
@engine.read(key: key)
|
27
|
+
end
|
28
|
+
|
29
|
+
def write!(key:, content:)
|
30
|
+
@engine.write!(key: key, content: content)
|
31
|
+
end
|
32
|
+
|
33
|
+
def exist?(key:)
|
34
|
+
@engine.exist?(key: key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete!(key:)
|
38
|
+
@engine.delete!(key: key)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
{ identifier: @identifier, engine: @engine, cutter: @cutter}.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
data/skeevy.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'skeevy'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.date = '2014-12-19'
|
5
|
+
s.summary = "Skeevy"
|
6
|
+
s.description = "A simple key-value store for objects"
|
7
|
+
s.authors = ["Craig Waterman"]
|
8
|
+
s.email = 'craigwaterman@gmail.com'
|
9
|
+
s.homepage =
|
10
|
+
'http://rubygems.org/gems/skeevy'
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.require_paths = ['lib']
|
16
|
+
|
17
|
+
s.add_development_dependency 'rspec', '~> 3.1'
|
18
|
+
s.add_development_dependency 'guard-rspec', '~> 4.4.2'
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
RSpec.describe :standard_key do
|
2
|
+
|
3
|
+
let!(:the_cutter_class) { Skeevy::Cutters::StandardKey }
|
4
|
+
let!(:the_cutter) { Skeevy::Cutters::StandardKey.new }
|
5
|
+
include_examples "a skeevy cutter"
|
6
|
+
|
7
|
+
describe '#cut verification' do
|
8
|
+
it 'creates appropriate keys without object' do
|
9
|
+
expect(the_cutter.cut(hash: 'abc', ns: 'test')).to eq('test-abc')
|
10
|
+
end
|
11
|
+
it 'creates appropriate keys with object' do
|
12
|
+
expect(the_cutter.cut(hash: 'abc',
|
13
|
+
ns: 'test',
|
14
|
+
object: 'file.json')).to eq('test-abc-file.json')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
RSpec.describe :directory_file do
|
4
|
+
|
5
|
+
let!(:the_engine_class) { Skeevy::Engines::DirectoryFile }
|
6
|
+
let!(:the_engine) { the_engine_class.new(instance: nil, base_dir: 'tmp', delimiter: '-') }
|
7
|
+
let!(:write_key) { 'a-b-c-file.json' }
|
8
|
+
include_examples "a skeevy engine"
|
9
|
+
|
10
|
+
describe ".initialize verification" do
|
11
|
+
it 'rejects badly typed instances' do
|
12
|
+
expect {
|
13
|
+
the_engine_class.new(instance: :nope, base_dir: 'tmp', delimiter: '-')
|
14
|
+
}.to raise_error(ArgumentError, /was not a/)
|
15
|
+
end
|
16
|
+
it 'rejects missing keywords' do
|
17
|
+
expect {
|
18
|
+
the_engine_class.new(delimiter: '-')
|
19
|
+
}.to raise_error(ArgumentError, /missing keywords/)
|
20
|
+
expect {
|
21
|
+
the_engine_class.new(base_dir: 'tmp')
|
22
|
+
}.to raise_error(ArgumentError, /missing keywords/)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#write! verification' do
|
27
|
+
it 'should create the appropriate directory' do
|
28
|
+
write_file
|
29
|
+
expect(File.directory?('tmp/a/b/c')).to be_truthy
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should create the appropriate file' do
|
33
|
+
write_file
|
34
|
+
expect(File.exist?('tmp/a/b/c/file.json')).to be_truthy
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#delete! verification' do
|
39
|
+
it 'should remove the file' do
|
40
|
+
write_file
|
41
|
+
the_engine.delete!(key: write_key)
|
42
|
+
expect(File.exist?('tmp/a/b/c/file.json')).to be_falsey
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def write_file
|
47
|
+
the_engine.write!(key: write_key, content: '{}')
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe :symbolic_hash do
|
2
|
+
|
3
|
+
let!(:the_engine_class) { Skeevy::Engines::SymbolicHash }
|
4
|
+
let!(:the_engine) { the_engine_class.new(instance: nil) }
|
5
|
+
include_examples "a skeevy engine"
|
6
|
+
|
7
|
+
describe ".initialize verification" do
|
8
|
+
it 'rejects badly typed instances' do
|
9
|
+
expect {
|
10
|
+
the_engine_class.new(instance: :nope)
|
11
|
+
}.to raise_error(ArgumentError, /was not a/)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
RSpec.describe :instance do
|
2
|
+
|
3
|
+
let(:instance) { Skeevy::Instance.new(identifier: :test) }
|
4
|
+
let(:cutter) { instance.cutter }
|
5
|
+
let(:engine) { instance.engine }
|
6
|
+
|
7
|
+
describe '#object_key' do
|
8
|
+
it 'passes the function to @cutter' do
|
9
|
+
expect(cutter).to receive(:cut).with(hash: 'abc', ns: 'test', object: 'test')
|
10
|
+
instance.object_key(hash: 'abc', ns: 'test', object: 'test')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#container_key' do
|
15
|
+
it 'passes the function to @cutter' do
|
16
|
+
expect(cutter).to receive(:cut).with(hash: 'abc', ns: 'test', object: nil)
|
17
|
+
instance.container_key(hash: 'abc', ns: 'test')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#read' do
|
22
|
+
it 'passes the function to @engine' do
|
23
|
+
expect(engine).to receive(:read).with(key: 'abc')
|
24
|
+
instance.read(key: 'abc')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#write!' do
|
29
|
+
it 'passes the function to @engine' do
|
30
|
+
expect(engine).to receive(:write!).with(key: 'abc', content: 'testing')
|
31
|
+
instance.write!(key: 'abc', content: 'testing')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#exist?' do
|
36
|
+
it 'passes the function to @engine' do
|
37
|
+
expect(engine).to receive(:exist?).with(key: 'abc')
|
38
|
+
instance.exist?(key: 'abc')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#delete!' do
|
43
|
+
it 'passes the function to @engine' do
|
44
|
+
expect(engine).to receive(:delete!).with(key: 'abc')
|
45
|
+
instance.delete!(key: 'abc')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
RSpec.describe :skeevy do
|
2
|
+
|
3
|
+
let!(:default_instance) { Skeevy.register!(identifier: :default) }
|
4
|
+
|
5
|
+
describe '.register!' do
|
6
|
+
it 'requires an identifier' do
|
7
|
+
expect { Skeevy.register! }.to raise_error(ArgumentError, /identifier/)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'validates engine type' do
|
11
|
+
expect {
|
12
|
+
Skeevy.register!(identifier: :test, engine: "My Engine")
|
13
|
+
}.to raise_error(ArgumentError, /Engine!/)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'validates cutter type' do
|
17
|
+
expect {
|
18
|
+
Skeevy.register!(identifier: :test, cutter: "My Cutter")
|
19
|
+
}.to raise_error(ArgumentError, /Cutter!/)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'default instantiation' do
|
25
|
+
|
26
|
+
it 'creates a default instance with only an identifier' do
|
27
|
+
expect(default_instance).to be_instance_of(Skeevy::Instance)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has a StandardKey cutter' do
|
31
|
+
expect(default_instance.cutter).to be_instance_of(Skeevy::Cutters::StandardKey)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has a SymbolicHash engine' do
|
35
|
+
expect(default_instance.engine).to be_instance_of(Skeevy::Engines::SymbolicHash)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'makes the default Skeeve available via .instance' do
|
39
|
+
my_instance = Skeevy.instance(:default)
|
40
|
+
expect(my_instance).to equal(default_instance)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'ensures [] is equivalent to .instance' do
|
44
|
+
expect(Skeevy.instance(:default)).to equal(Skeevy[:default])
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '.inspect' do
|
50
|
+
it 'outputs the proper string' do
|
51
|
+
expect(Skeevy.inspect).to eq "Skeevy #{Skeevy::VERSION}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
RSpec.shared_examples 'a skeevy cutter' do
|
4
|
+
|
5
|
+
describe '.initialize accepts only proper instances' do
|
6
|
+
it 'allows nil instances' do
|
7
|
+
expect(the_cutter).to be_a_kind_of(Skeevy::Cutter)
|
8
|
+
end
|
9
|
+
it 'rejects badly typed instances' do
|
10
|
+
expect {
|
11
|
+
the_cutter_class.new(instance: :nope)
|
12
|
+
}.to raise_error(ArgumentError, /was not a/)
|
13
|
+
end
|
14
|
+
it 'rejects missing delimiter' do
|
15
|
+
expect {
|
16
|
+
the_cutter_class.new(delimiter: nil)
|
17
|
+
}.to raise_error(ArgumentError, /without a/)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#cut' do
|
22
|
+
it 'rejects missing keywords' do
|
23
|
+
expect {
|
24
|
+
the_cutter.cut(hash: '')
|
25
|
+
}.to raise_error(ArgumentError, /missing keyword/)
|
26
|
+
expect {
|
27
|
+
the_cutter.cut(ns: '')
|
28
|
+
}.to raise_error(ArgumentError, /missing keyword/)
|
29
|
+
end
|
30
|
+
it 'returns a string' do
|
31
|
+
expect(the_cutter.cut(hash: 'abc', ns: 'test')).to be_instance_of(String)
|
32
|
+
expect(the_cutter.cut(hash: 'abc', ns: 'test', object: 'file.json')).to be_instance_of(String)
|
33
|
+
end
|
34
|
+
it 'rejects empty hashes' do
|
35
|
+
expect {
|
36
|
+
the_cutter.cut(hash: '', ns: 'test')
|
37
|
+
}.to raise_error(ArgumentError, /not be blank/)
|
38
|
+
expect {
|
39
|
+
the_cutter.cut(hash: nil, ns: 'test')
|
40
|
+
}.to raise_error(ArgumentError, /not be blank/)
|
41
|
+
end
|
42
|
+
it 'rejects empty namespaces' do
|
43
|
+
expect {
|
44
|
+
the_cutter.cut(hash: 'abc', ns: '')
|
45
|
+
}.to raise_error(ArgumentError, /not be blank/)
|
46
|
+
expect {
|
47
|
+
the_cutter.cut(hash: 'abc', ns: nil)
|
48
|
+
}.to raise_error(ArgumentError, /not be blank/)
|
49
|
+
end
|
50
|
+
it 'rejects object names containing the class delimiter' do
|
51
|
+
expect {
|
52
|
+
the_cutter.cut(hash: 'abc', ns: 'test', object: 'bad-object-name.json')
|
53
|
+
}.to raise_error(ArgumentError, /of delimiter/)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
RSpec.shared_examples 'a skeevy engine' do
|
4
|
+
|
5
|
+
describe '.initialize accepts only proper instances' do
|
6
|
+
it 'should allow nil instances' do
|
7
|
+
expect(the_engine).to be_a_kind_of(Skeevy::Engine)
|
8
|
+
end
|
9
|
+
it 'should reject badly typed instances' do
|
10
|
+
expect {
|
11
|
+
Skeevy::Engines::SymbolicHash.new(instance: :nope)
|
12
|
+
}.to raise_error(ArgumentError, /was not a/)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#exist?' do
|
17
|
+
k = 'exist_test-file.json'
|
18
|
+
it 'properly identifies missing items' do
|
19
|
+
expect(the_engine.exist?(key: k)).to be_falsey
|
20
|
+
end
|
21
|
+
it 'properly identifies existing items' do
|
22
|
+
the_engine.write!(key: k, content: 'xyz')
|
23
|
+
expect(the_engine.exist?(key: k)).to be_truthy
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#write!' do
|
28
|
+
it 'writes to a key' do
|
29
|
+
k = 'write_test-file.json'
|
30
|
+
v = '{}'
|
31
|
+
the_engine.write!(key: k, content: v)
|
32
|
+
expect(the_engine.exist?(key: k)).to be_truthy
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#read' do
|
37
|
+
it 'returns proper results from a hash' do
|
38
|
+
k = 'read_test-file.json'
|
39
|
+
v = '{}'
|
40
|
+
the_engine.write!(key: k, content: v)
|
41
|
+
expect(the_engine.read(key: k)).to eq(v)
|
42
|
+
end
|
43
|
+
it 'returns nil when a key does not exist' do
|
44
|
+
expect(the_engine.read(key: 'missing_key_test')).to eq(nil)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#delete!' do
|
49
|
+
it 'removes keys that exist' do
|
50
|
+
k = 'delete_test-file.json'
|
51
|
+
the_engine.write!(key: k, content: '{}')
|
52
|
+
expect(the_engine.delete!(key: k)).to equal(true)
|
53
|
+
expect(the_engine.exist?(key: k)).to be_falsey
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'skeevy'
|
2
|
+
require 'shared/engine'
|
3
|
+
require 'shared/cutter'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.expect_with :rspec do |expectations|
|
7
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
8
|
+
end
|
9
|
+
|
10
|
+
config.mock_with :rspec do |mocks|
|
11
|
+
mocks.verify_partial_doubles = true
|
12
|
+
end
|
13
|
+
|
14
|
+
config.after(:all) do
|
15
|
+
FileUtils.rm_rf(Dir["tmp/[^.]*"])
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: skeevy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Craig Waterman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: guard-rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.4.2
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 4.4.2
|
41
|
+
description: A simple key-value store for objects
|
42
|
+
email: craigwaterman@gmail.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- ".gitignore"
|
48
|
+
- ".rspec"
|
49
|
+
- Gemfile
|
50
|
+
- Guardfile
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- lib/skeevy.rb
|
54
|
+
- lib/skeevy/cutter.rb
|
55
|
+
- lib/skeevy/cutters/standard_key.rb
|
56
|
+
- lib/skeevy/engine.rb
|
57
|
+
- lib/skeevy/engines/directory_file.rb
|
58
|
+
- lib/skeevy/engines/symbolic_hash.rb
|
59
|
+
- lib/skeevy/instance.rb
|
60
|
+
- lib/skeevy/version.rb
|
61
|
+
- skeevy.gemspec
|
62
|
+
- spec/lib/skeevy/cutters/standard_key_spec.rb
|
63
|
+
- spec/lib/skeevy/engines/directory_file_spec.rb
|
64
|
+
- spec/lib/skeevy/engines/symbolic_hash_spec.rb
|
65
|
+
- spec/lib/skeevy/instance_spec.rb
|
66
|
+
- spec/lib/skeevy/version_spec.rb
|
67
|
+
- spec/lib/skeevy_spec.rb
|
68
|
+
- spec/shared/cutter.rb
|
69
|
+
- spec/shared/engine.rb
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
homepage: http://rubygems.org/gems/skeevy
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.4.3
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Skeevy
|
95
|
+
test_files:
|
96
|
+
- spec/lib/skeevy/cutters/standard_key_spec.rb
|
97
|
+
- spec/lib/skeevy/engines/directory_file_spec.rb
|
98
|
+
- spec/lib/skeevy/engines/symbolic_hash_spec.rb
|
99
|
+
- spec/lib/skeevy/instance_spec.rb
|
100
|
+
- spec/lib/skeevy/version_spec.rb
|
101
|
+
- spec/lib/skeevy_spec.rb
|
102
|
+
- spec/shared/cutter.rb
|
103
|
+
- spec/shared/engine.rb
|
104
|
+
- spec/spec_helper.rb
|