store2 0.0.1 → 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.
- 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
|