store2 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/README.md +4 -4
- data/lib/store2.rb +10 -2
- data/lib/store2/facade.rb +17 -0
- data/lib/store2/file.rb +88 -0
- data/lib/store2/scoped.rb +35 -0
- data/lib/store2/version.rb +1 -1
- data/spec/spec_helper.rb +34 -0
- data/spec/store2/file_spec.rb +110 -0
- data/spec/store2/scoped_spec.rb +133 -0
- data/spec/store2_spec.rb +7 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1fb7e50d7080a31af00eaab76aad6dbb8774f89
|
4
|
+
data.tar.gz: e9945689ec31a7df9d31b53d0ab435ac8f062b9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0d209a20596ed072c6c1e22b900b21aed3079c77dd389448fbdf3d21eb4fe3365e16740c9efc139467c2096d40fed6bcaa87087830b4e455b41d710825515aa
|
7
|
+
data.tar.gz: c8e07eddbdccbbc7f83e4e10a48fdb27aa42f40397c7a3e6bbea215f97ffa1f140cef981c3032ae56bea3419aaa48af240d22f879feebf8e9f70c8844776accf
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -28,28 +28,28 @@ require "store2"
|
|
28
28
|
|
29
29
|
```ruby
|
30
30
|
store2 = Store2.open("test.yml")
|
31
|
-
# =>
|
31
|
+
# => #<Store2::File:0x00f28746a47 @filename = "test.yml">
|
32
32
|
```
|
33
33
|
|
34
34
|
### Get root-scoped store object
|
35
35
|
|
36
36
|
```ruby
|
37
37
|
root_scoped = store2.build
|
38
|
-
# =>
|
38
|
+
# => #<Store2::Scoped:0x00f2d746a41 @keys = []>
|
39
39
|
```
|
40
40
|
|
41
41
|
### Get scoped store object
|
42
42
|
|
43
43
|
```ruby
|
44
44
|
user_scoped = store2.scoped("users", "72AC97F03A")
|
45
|
-
# =>
|
45
|
+
# => #<Store2::Scoped:0x00a2d746a91 @keys = ["users", "72AC97F03A"]
|
46
46
|
```
|
47
47
|
|
48
48
|
### Get scoped store object out of another scoped store object
|
49
49
|
|
50
50
|
```ruby
|
51
51
|
items_scoped = user_scoped.scoped("items")
|
52
|
-
# =>
|
52
|
+
# => #<Store2::Scoped:0x00922faa907 @keys = ["users", "72AC97F03A", "items"]
|
53
53
|
```
|
54
54
|
|
55
55
|
### Reading the value
|
data/lib/store2.rb
CHANGED
data/lib/store2/file.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Store2
|
2
|
+
class File < Struct.new(:filename)
|
3
|
+
def initialize(*args)
|
4
|
+
super
|
5
|
+
@data = load
|
6
|
+
self.class.register(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
scoped
|
11
|
+
end
|
12
|
+
|
13
|
+
def scoped(*keys)
|
14
|
+
Scoped.new(self, keys)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(keys)
|
18
|
+
keys.inject(data) { |data, key| data.fetch(key) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(keys, value)
|
22
|
+
get(keys[0...-1])[keys[-1]] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def has?(keys)
|
26
|
+
get(keys[0...-1]).has_key?(keys[-1])
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_or_set(keys, value)
|
30
|
+
set(keys, value) unless has?(keys)
|
31
|
+
get(keys)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fetch(keys, &block)
|
35
|
+
return block.call unless has?(keys)
|
36
|
+
get(keys)
|
37
|
+
end
|
38
|
+
|
39
|
+
def save
|
40
|
+
::File.write(filename, to_yaml)
|
41
|
+
end
|
42
|
+
|
43
|
+
def _reset
|
44
|
+
create
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
attr_reader :data
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def load
|
54
|
+
return create unless ::File.exist?(filename)
|
55
|
+
YAML.load_file(filename)
|
56
|
+
end
|
57
|
+
|
58
|
+
def create
|
59
|
+
clear
|
60
|
+
save
|
61
|
+
data
|
62
|
+
end
|
63
|
+
|
64
|
+
def clear
|
65
|
+
@data = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_yaml
|
69
|
+
data.to_yaml
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class << File
|
74
|
+
def register(instance)
|
75
|
+
instances << instance
|
76
|
+
end
|
77
|
+
|
78
|
+
def _reset
|
79
|
+
instances.each(&:_reset)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def instances
|
85
|
+
@_instances ||= []
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Store2
|
2
|
+
class Scoped < Struct.new(:store, :scope)
|
3
|
+
def scoped(*keys)
|
4
|
+
Scoped.new(store, scope + keys)
|
5
|
+
end
|
6
|
+
|
7
|
+
def get(*keys)
|
8
|
+
store.get(scope + keys)
|
9
|
+
end
|
10
|
+
|
11
|
+
def set(*keys, value)
|
12
|
+
store.set(scope + keys, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def has?(*keys)
|
16
|
+
store.has?(scope + keys)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_or_set(*keys, value)
|
20
|
+
store.get_or_set(scope + keys, value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch(*keys, &block)
|
24
|
+
store.fetch(scope + keys, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def save
|
28
|
+
store.save
|
29
|
+
end
|
30
|
+
|
31
|
+
def _reset
|
32
|
+
store._reset
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/store2/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "store2"
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.expect_with :rspec do |expectations|
|
5
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
6
|
+
end
|
7
|
+
|
8
|
+
config.mock_with :rspec do |mocks|
|
9
|
+
mocks.verify_partial_doubles = true
|
10
|
+
end
|
11
|
+
|
12
|
+
config.filter_run :focus
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
|
15
|
+
config.disable_monkey_patching!
|
16
|
+
|
17
|
+
config.warnings = true
|
18
|
+
|
19
|
+
if config.files_to_run.one?
|
20
|
+
config.default_formatter = 'doc'
|
21
|
+
end
|
22
|
+
|
23
|
+
config.profile_examples = 10
|
24
|
+
|
25
|
+
config.order = :random
|
26
|
+
|
27
|
+
Kernel.srand config.seed
|
28
|
+
|
29
|
+
config.around do |example|
|
30
|
+
Store2._with_reset do
|
31
|
+
example.run
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Store2
|
2
|
+
RSpec.describe File do
|
3
|
+
subject(:store) { File.new("test.yml") }
|
4
|
+
|
5
|
+
it "behaves like a value object" do
|
6
|
+
expect(store).to eq(File.new("test.yml"))
|
7
|
+
expect(store).not_to eq(File.new("test_other.yml"))
|
8
|
+
expect(store).not_to eq(nil)
|
9
|
+
expect(store).not_to eq(Object)
|
10
|
+
expect(store).not_to eq(Object.new)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#build" do
|
14
|
+
it "builds root scoped store" do
|
15
|
+
expect(store.build).to eq(Scoped.new(store, []))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#scoped" do
|
20
|
+
it "builds scoped store with passed keys" do
|
21
|
+
expect(store.scoped("users", "72AC97F03A")).to eq(
|
22
|
+
Scoped.new(store, ["users", "72AC97F03A"])
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#save" do
|
28
|
+
it "persists data" do
|
29
|
+
store.set(["my_secret"], {})
|
30
|
+
store.set(["my_secret", "password"], "is very secret")
|
31
|
+
|
32
|
+
store.save
|
33
|
+
|
34
|
+
expect(
|
35
|
+
File.new("test.yml").get(["my_secret", "password"])
|
36
|
+
).to eq("is very secret")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#get and #set" do
|
41
|
+
it "gets the value given list of keys" do
|
42
|
+
store.set(["hello"], {})
|
43
|
+
store.set(["hello", "world"], {})
|
44
|
+
store.set(["hello", "world", "test"], "12345")
|
45
|
+
|
46
|
+
expect(store.get(["hello", "world", "test"])).to eq("12345")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#has?" do
|
51
|
+
it "is true if key is present" do
|
52
|
+
store.set(["hello"], {})
|
53
|
+
store.set(["hello", "world"], {})
|
54
|
+
store.set(["hello", "world", "test"], "12345")
|
55
|
+
|
56
|
+
expect(store.has?(["hello", "world", "test"])).to eq(true)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "is false if key is not present" do
|
60
|
+
store.set(["hello"], {})
|
61
|
+
store.set(["hello", "world"], {})
|
62
|
+
|
63
|
+
expect(store.has?(["hello", "world", "test"])).to eq(false)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#get_or_set" do
|
68
|
+
it "is current value if it is present" do
|
69
|
+
store.set(["hello"], {})
|
70
|
+
store.set(["hello", "world"], {})
|
71
|
+
store.set(["hello", "world", "test"], "09876")
|
72
|
+
|
73
|
+
expect(store.get_or_set(["hello", "world", "test"], "12345")).to eq("09876")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "is default value if it is not present" do
|
77
|
+
store.set(["hello"], {})
|
78
|
+
store.set(["hello", "world"], {})
|
79
|
+
|
80
|
+
expect(store.get_or_set(["hello", "world", "test"], "12345")).to eq("12345")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "makes the value present if it wasn't" do
|
84
|
+
store.set(["hello"], {})
|
85
|
+
store.set(["hello", "world"], {})
|
86
|
+
store.get_or_set(["hello", "world", "test"], "12345")
|
87
|
+
|
88
|
+
expect(store.get(["hello", "world", "test"])).to eq("12345")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#fetch" do
|
93
|
+
it "behaves like get if value is present" do
|
94
|
+
store.set(["hello"], {})
|
95
|
+
store.set(["hello", "world"], {})
|
96
|
+
store.set(["hello", "world", "test"], "09876")
|
97
|
+
|
98
|
+
expect(store.fetch(["hello", "world", "test"]) { fail }).to eq("09876")
|
99
|
+
end
|
100
|
+
|
101
|
+
it "calls block if value is not present" do
|
102
|
+
store.set(["hello"], {})
|
103
|
+
store.set(["hello", "world"], {})
|
104
|
+
|
105
|
+
expect { store.fetch(["hello", "world", "test"]) { fail } }
|
106
|
+
.to raise_error(RuntimeError)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Store2
|
2
|
+
RSpec.describe Scoped do
|
3
|
+
subject(:root) { Scoped.new(store, []) }
|
4
|
+
subject(:scoped) { Scoped.new(store, ["users", "72AC97F03A"]) }
|
5
|
+
|
6
|
+
let(:store) { File.new("test.yml") }
|
7
|
+
let(:other_store) { File.new("test_other.yml") }
|
8
|
+
|
9
|
+
before do
|
10
|
+
expect(root.get).to eq({})
|
11
|
+
|
12
|
+
root.set("users", { "72AC97F03A" => {} })
|
13
|
+
end
|
14
|
+
|
15
|
+
it "behaves like a value object" do
|
16
|
+
expect(root).to eq(Scoped.new(store, []))
|
17
|
+
expect(scoped).to eq(Scoped.new(store, ["users", "72AC97F03A"]))
|
18
|
+
|
19
|
+
expect(root).not_to eq(Scoped.new(store, ["hello"]))
|
20
|
+
expect(root).not_to eq(Scoped.new(other_store, []))
|
21
|
+
|
22
|
+
expect(root).not_to eq(nil)
|
23
|
+
expect(root).not_to eq(Object)
|
24
|
+
expect(root).not_to eq(Object.new)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#scoped" do
|
28
|
+
it "builds further scoped " do
|
29
|
+
expect(root.scoped("items", "99A")).to eq(
|
30
|
+
Scoped.new(store, ["items", "99A"])
|
31
|
+
)
|
32
|
+
|
33
|
+
expect(scoped.scoped("items", "99A")).to eq(
|
34
|
+
Scoped.new(store, ["users", "72AC97F03A", "items", "99A"])
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#save" do
|
40
|
+
it "persists the data" do
|
41
|
+
scoped.set("items", { "123AA" => "sold" })
|
42
|
+
|
43
|
+
scoped.save
|
44
|
+
|
45
|
+
expect(
|
46
|
+
Scoped.new(File.new("test.yml"), ["users", "72AC97F03A"]).get("items", "123AA")
|
47
|
+
).to eq("sold")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#get and #set" do
|
52
|
+
it "gets the value given list of keys" do
|
53
|
+
scoped.set("hello", {})
|
54
|
+
scoped.set("hello", "world", {})
|
55
|
+
scoped.set("hello", "world", "test", "12345")
|
56
|
+
|
57
|
+
expect(scoped.get("hello", "world", "test")).to eq("12345")
|
58
|
+
expect(root.get("users", "72AC97F03A", "hello", "world", "test")).to eq("12345")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#has?" do
|
63
|
+
it "is true if key is present" do
|
64
|
+
scoped.set("hello", {})
|
65
|
+
scoped.set("hello", "world", {})
|
66
|
+
scoped.set("hello", "world", "test", "12345")
|
67
|
+
|
68
|
+
expect(scoped.has?("hello", "world", "test")).to eq(true)
|
69
|
+
expect(root.has?("users", "72AC97F03A", "hello", "world", "test")).to eq(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "is false if key is not present" do
|
73
|
+
scoped.set("hello", {})
|
74
|
+
scoped.set("hello", "world", {})
|
75
|
+
|
76
|
+
expect(scoped.has?("hello", "world", "test")).to eq(false)
|
77
|
+
expect(root.has?("users", "72AC97F03A", "hello", "world", "test")).to eq(false)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#get_or_set" do
|
82
|
+
it "is current value if it is present" do
|
83
|
+
scoped.set("hello", {})
|
84
|
+
scoped.set("hello", "world", {})
|
85
|
+
scoped.set("hello", "world", "test", "09876")
|
86
|
+
|
87
|
+
expect(scoped.get_or_set("hello", "world", "test", "12345")).to eq("09876")
|
88
|
+
expect(root.get_or_set("users", "72AC97F03A", "hello", "world", "test", "12345")).to eq("09876")
|
89
|
+
end
|
90
|
+
|
91
|
+
it "is default value if it is not present" do
|
92
|
+
scoped.set("hello", {})
|
93
|
+
scoped.set("hello", "world", {})
|
94
|
+
|
95
|
+
expect(scoped.get_or_set("hello", "world", "test", "12345")).to eq("12345")
|
96
|
+
expect(root.get_or_set("users", "72AC97F03A", "hello", "world", "test", "12345")).to eq("12345")
|
97
|
+
end
|
98
|
+
|
99
|
+
it "makes the value present if it wasn't" do
|
100
|
+
scoped.set("hello", {})
|
101
|
+
scoped.set("hello", "world", {})
|
102
|
+
scoped.get_or_set("hello", "world", "test", "12345")
|
103
|
+
|
104
|
+
expect(scoped.get("hello", "world", "test")).to eq("12345")
|
105
|
+
expect(root.get("users", "72AC97F03A", "hello", "world", "test")).to eq("12345")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#fetch" do
|
110
|
+
it "behaves like get if value is present" do
|
111
|
+
scoped.set("hello", {})
|
112
|
+
scoped.set("hello", "world", {})
|
113
|
+
scoped.set("hello", "world", "test", "09876")
|
114
|
+
|
115
|
+
expect(scoped.fetch("hello", "world", "test") { fail }).to eq("09876")
|
116
|
+
expect(root.fetch("users", "72AC97F03A", "hello", "world", "test") { fail }).to eq("09876")
|
117
|
+
end
|
118
|
+
|
119
|
+
it "calls block if value is not present" do
|
120
|
+
scoped.set("hello", {})
|
121
|
+
scoped.set("hello", "world", {})
|
122
|
+
|
123
|
+
expect {
|
124
|
+
scoped.fetch("hello", "world", "test") { fail }
|
125
|
+
}.to raise_error(RuntimeError)
|
126
|
+
|
127
|
+
expect {
|
128
|
+
root.fetch("users", "72AC97F03A", "hello", "world", "test") { fail }
|
129
|
+
}.to raise_error(RuntimeError)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/spec/store2_spec.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: store2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleksii Fedorov
|
@@ -46,12 +46,20 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- ".gitignore"
|
49
|
+
- ".rspec"
|
49
50
|
- Gemfile
|
50
51
|
- LICENSE.txt
|
51
52
|
- README.md
|
52
53
|
- Rakefile
|
53
54
|
- lib/store2.rb
|
55
|
+
- lib/store2/facade.rb
|
56
|
+
- lib/store2/file.rb
|
57
|
+
- lib/store2/scoped.rb
|
54
58
|
- lib/store2/version.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
- spec/store2/file_spec.rb
|
61
|
+
- spec/store2/scoped_spec.rb
|
62
|
+
- spec/store2_spec.rb
|
55
63
|
- store2.gemspec
|
56
64
|
homepage: ''
|
57
65
|
licenses:
|
@@ -77,4 +85,8 @@ rubygems_version: 2.2.2
|
|
77
85
|
signing_key:
|
78
86
|
specification_version: 4
|
79
87
|
summary: Persistence in YAML files, yay!
|
80
|
-
test_files:
|
88
|
+
test_files:
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
- spec/store2/file_spec.rb
|
91
|
+
- spec/store2/scoped_spec.rb
|
92
|
+
- spec/store2_spec.rb
|