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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 462d37b0c81d0e774272fa08c03f0525d7556a6f
4
- data.tar.gz: b9f70df4a2d8b3f02ffdaa184bf9700a598e8d10
3
+ metadata.gz: f1fb7e50d7080a31af00eaab76aad6dbb8774f89
4
+ data.tar.gz: e9945689ec31a7df9d31b53d0ab435ac8f062b9d
5
5
  SHA512:
6
- metadata.gz: db6596b207fa4d09bdcd3df0037095e8513de1dab9ee7930d50dac15cabf93f0c06884583ad2e53b5f11431bf73447e165a12eca251667274759faafc5d38581
7
- data.tar.gz: 3f8af30002e9425e0804cbace7eb8ac1e15e1c8577ad62241c5b57198802c66da08b34587a7a4e53d9eb082e2905851cc8527ffae758c80556f9c3e9644d6d1b
6
+ metadata.gz: d0d209a20596ed072c6c1e22b900b21aed3079c77dd389448fbdf3d21eb4fe3365e16740c9efc139467c2096d40fed6bcaa87087830b4e455b41d710825515aa
7
+ data.tar.gz: c8e07eddbdccbbc7f83e4e10a48fdb27aa42f40397c7a3e6bbea215f97ffa1f140cef981c3032ae56bea3419aaa48af240d22f879feebf8e9f70c8844776accf
data/.gitignore CHANGED
@@ -12,3 +12,5 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+ test.yml
16
+ test_other.yml
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in store2.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem "rspec"
8
+ end
data/README.md CHANGED
@@ -28,28 +28,28 @@ require "store2"
28
28
 
29
29
  ```ruby
30
30
  store2 = Store2.open("test.yml")
31
- # => <#Store2::File:0x00f28746a47 @filename = "test.yml">
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
- # => <#Store2::Scoped:0x00f2d746a41 @keys = []>
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
- # => <#Store2::Scoped:0x00a2d746a91 @keys = ["users", "72AC97F03A"]
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
- # => <#Store2::Scoped:0x00922faa907 @keys = ["users", "72AC97F03A", "items"]
52
+ # => #<Store2::Scoped:0x00922faa907 @keys = ["users", "72AC97F03A", "items"]
53
53
  ```
54
54
 
55
55
  ### Reading the value
@@ -1,5 +1,13 @@
1
- require "store2/version"
1
+ require "yaml"
2
+
3
+ %w[
4
+ version
5
+
6
+ facade
7
+ file
8
+ scoped
9
+ ].each { |name| require "store2/#{name}" }
2
10
 
3
11
  module Store2
4
- # Your code goes here...
12
+ extend Facade
5
13
  end
@@ -0,0 +1,17 @@
1
+ module Store2
2
+ module Facade
3
+ def open(filename)
4
+ File.new(filename)
5
+ end
6
+
7
+ def _with_reset
8
+ _reset
9
+ yield
10
+ _reset
11
+ end
12
+
13
+ def _reset
14
+ File._reset
15
+ end
16
+ end
17
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Store2
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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
@@ -0,0 +1,7 @@
1
+ RSpec.describe Store2 do
2
+ describe ".open" do
3
+ it "opens a file" do
4
+ expect(Store2.open("test.yml")).to eq(Store2::File.new("test.yml"))
5
+ end
6
+ end
7
+ end
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.1
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