hypostasis 0.0.3 → 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: 9dbac3fb199bc4d6edf4065159d9ec92e41b071b
4
- data.tar.gz: 06a5045d627c2b6a0e3e9d95f14ca106c0b95ce7
3
+ metadata.gz: 3ef5cc8995a6ef7a748862673f907a6b5fb95e43
4
+ data.tar.gz: bfa8febeae9d58a04764948cde524fea4172b706
5
5
  SHA512:
6
- metadata.gz: 4e378bc61f2ffd706f4a3824292cdc55b14712beab90a7e4bfd5210ca1623047599e34643039edf9700dd4b96bf2e56dd61fcb0c3f59ec16318be10fa477000f
7
- data.tar.gz: e6d76b2b7160982607a608a75ab966d59575d3bd53422d142a78145988752f1fbe06f645e2b5c97b01c6762557f22c3503f70bc7a73ba7ac9e344cbb215a854d
6
+ metadata.gz: afd6b2484b51d26be937f3152f0bfae13e2f5739ce8e0608f3aaadffbc4c70d53e2c6b08acfde1558aab8b7f81d0fd513f08f5cbe9cb3e1cb074bbc53431696b
7
+ data.tar.gz: af7539814a855d5ff6845fb136051a162757da126fe7547d3948d2508c1c0a6d97bf0bad8ce3f05ba6dfcea1445a83c40c38c978177751ce81ae446fdaeea308
data/README.md CHANGED
@@ -50,6 +50,29 @@ the project matures and the APIs stabilize.
50
50
 
51
51
  Until then, we suggest reading the code and the tests.
52
52
 
53
+ ### Key-Value Data Model
54
+
55
+ require 'hypostasis'
56
+
57
+ ns = Hypostasis::Connection.create_namespace('keystore', {data_model: :key_value})
58
+ ns.set('keyname', 10)
59
+
60
+ ns.get('keyname') #=> 10
61
+
62
+ The Key-Value data model provides some simple enhancements on top of
63
+ FoundationDB's core key-value capabilities, including automatic reconstitution
64
+ of basic language types and support for namespacing where keys are stored. The
65
+ basic language types currently suported include the following:
66
+
67
+ * String
68
+ * Fixnum
69
+ * Bignum
70
+ * Float
71
+ * Date
72
+ * DateTime
73
+ * Time
74
+ * Boolean
75
+
53
76
  ## Contributing
54
77
 
55
78
  1. Fork it
@@ -1,50 +1,28 @@
1
+ require 'singleton'
1
2
  require 'fdb'
2
3
 
3
4
  FDB.api_version 100
4
5
 
5
6
  class Hypostasis::Connection
6
- attr_reader :database
7
+ include ::Singleton
7
8
 
8
- @@config_key = Hypostasis::KeyPath.new('hypostasis', 'config', 'namespaces')
9
-
10
- def initialize
11
- @database = FDB.open
12
- end
13
-
14
- def self.config_path
15
- @@config_key
16
- end
17
-
18
- def get(key)
19
- database[key.to_s]
20
- end
21
-
22
- def set(key, value)
23
- (database[key.to_s] = value.to_s) == value.to_s
9
+ def self.open
10
+ self
24
11
  end
25
12
 
26
- def unset(key)
27
- database.clear(key).nil?
13
+ def self.database
14
+ @@database ||= FDB.open
28
15
  end
29
16
 
30
- def create_namespace(name, options = {data_model: :key_value})
31
- path = @@config_key.extend_path(name.to_s).to_s
32
- raise Hypostasis::Errors::NamespaceAlreadyCreated unless database[path].nil?
33
- database[path] = Marshal.dump(options)
34
- Hypostasis::Namespace.new(name, options[:data_model])
17
+ def self.create_namespace(name, options = {})
18
+ Hypostasis::Namespace.create(name, options)
35
19
  end
36
20
 
37
- def open_namespace(name)
38
- path = @@config_key.extend_path(name.to_s).to_s
39
- raise Hypostasis::Errors::NonExistentNamespace if database[path].nil?
40
- options = Marshal.load(database[path])
41
- Hypostasis::Namespace.new(name, options[:data_model])
21
+ def self.open_namespace(name)
22
+ Hypostasis::Namespace.open(name)
42
23
  end
43
24
 
44
- def destroy_namespace(name)
45
- path = @@config_key.extend_path(name.to_s).to_s
46
- raise Hypostasis::Errors::NonExistentNamespace if database[path].nil?
47
- database.clear_range_start_with(path)
25
+ def self.destroy_namespace(name)
48
26
  database.clear_range_start_with(name.to_s)
49
27
  true
50
28
  end
@@ -0,0 +1,3 @@
1
+ module Hypostasis::DataModels::Document
2
+
3
+ end
@@ -0,0 +1,37 @@
1
+ require 'date'
2
+
3
+ module Hypostasis::DataModels::KeyValue
4
+ def set(key, value)
5
+ key_path = "#{name}\\#{Hypostasis::Tuple.new(key.to_s, value.class.to_s).to_s}"
6
+ database.set(key_path, value.to_s)
7
+ end
8
+
9
+ def get(key)
10
+ key_path = "#{name}\\#{Hypostasis::Tuple.new(key.to_s).to_s}"
11
+ key = database.get_range_start_with(key_path).first.key
12
+ value = database.get_range_start_with(key_path).first.value
13
+ data_type = Hypostasis::Tuple.unpack(key.to_s.split('\\').last).to_a.last
14
+ case data_type
15
+ when 'Fixnum'
16
+ Integer(value)
17
+ when 'Bignum'
18
+ Integer(value)
19
+ when 'Float'
20
+ Float(value)
21
+ when 'String'
22
+ value
23
+ when 'Date'
24
+ Date.parse(value)
25
+ when 'DateTime'
26
+ DateTime.parse(value)
27
+ when 'Time'
28
+ Time.parse(value)
29
+ when 'TrueClass'
30
+ true
31
+ when 'FalseClass'
32
+ false
33
+ else
34
+ raise Hypostasis::Errors::UnknownValueType
35
+ end
36
+ end
37
+ end
@@ -1,4 +1,4 @@
1
- module Hypostasis::DataModel; end
1
+ module Hypostasis::DataModels; end
2
2
 
3
- require 'hypostasis/data_model/document'
4
- require 'hypostasis/data_model/key_value'
3
+ require 'hypostasis/data_models/key_value'
4
+ require 'hypostasis/data_models/document'
@@ -1,8 +1,11 @@
1
1
  module Hypostasis::Errors
2
2
  class NamespaceAlreadyCreated < StandardError; end
3
3
  class NonExistentNamespace < StandardError; end
4
+ class CanNotReadNamespaceConfig < StandardError; end
5
+ class UnknownNamespaceDataModel < StandardError; end
4
6
  class InvalidKeyPath < StandardError; end
5
7
  class KeyPathExhausted < StandardError; end
6
8
  class InvalidTuple < StandardError; end
7
9
  class TupleExhausted < StandardError; end
10
+ class UnknownValueType < StandardError; end
8
11
  end
@@ -1,16 +1,55 @@
1
1
  class Hypostasis::Namespace
2
2
  attr_reader :name, :data_model
3
3
 
4
- def initialize(name, data_model)
4
+ def initialize(name, data_model = :key_value)
5
5
  @name = name.to_s
6
6
  @data_model = data_model.to_sym
7
+
8
+ load_data_model
9
+ end
10
+
11
+ def destroy
12
+ database.clear_range_start_with(@name)
13
+ true
14
+ end
15
+
16
+ def self.create(name, options = {})
17
+ raise Hypostasis::Errors::NamespaceAlreadyCreated unless database[name].nil?
18
+ merged_options = { data_model: :key_value }.merge(options)
19
+
20
+ database.transact do |tr|
21
+ tr.set(name.to_s, 'true')
22
+ tr.set(name.to_s + '\\' + Hypostasis::Tuple.new('config','data_model').to_s, merged_options[:data_model].to_s)
23
+ end
24
+
25
+ Hypostasis::Namespace.new(name, merged_options[:data_model])
26
+ end
27
+
28
+ def self.open(name)
29
+ raise Hypostasis::Errors::NonExistentNamespace if database[name].nil?
30
+ data_model = database.get(name + '\\' + Hypostasis::Tuple.new('config','data_model').to_s)
31
+ raise Hypostasis::Errors::CanNotReadNamespaceConfig if data_model.nil?
32
+ Hypostasis::Namespace.new(name, data_model)
33
+ end
34
+
35
+ private
36
+
37
+ def self.database
38
+ Hypostasis::Connection.database
7
39
  end
8
40
 
9
- def key_path
10
- @key_path ||= Hypostasis::KeyPath.new(name)
41
+ def database
42
+ Hypostasis::Namespace.database
11
43
  end
12
44
 
13
- def config_path
14
- @config_path ||= Hypostasis::Connection.config_path.extend_path(name)
45
+ def load_data_model
46
+ case @data_model
47
+ when :key_value
48
+ self.extend Hypostasis::DataModels::KeyValue
49
+ when :document
50
+ self.extend Hypostasis::DataModels::Document
51
+ else
52
+ raise Hypostasis::Errors::UnknownNamespaceDataModel, "#{@data_model} unknown"
53
+ end
15
54
  end
16
55
  end
@@ -1,3 +1,3 @@
1
1
  module Hypostasis
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/hypostasis.rb CHANGED
@@ -5,6 +5,5 @@ require 'hypostasis/errors'
5
5
  require 'hypostasis/tuple'
6
6
  require 'hypostasis/key_path'
7
7
  require 'hypostasis/connection'
8
- require 'hypostasis/namespace'
9
-
10
8
  require 'hypostasis/data_models'
9
+ require 'hypostasis/namespace'
@@ -1,62 +1,45 @@
1
1
  require 'minitest_helper'
2
2
 
3
3
  describe Hypostasis::Connection do
4
- let(:subject) { Hypostasis::Connection.new }
5
-
6
- it { subject.must_respond_to :get }
7
- it { subject.must_respond_to :set }
8
- it { subject.must_respond_to :unset }
9
-
10
- describe '#get and #set' do
11
- before :each do
12
- subject.set('preset_key', 'preset_value')
13
- end
14
-
15
- after :each do
16
- subject.unset('preset_key')
17
- end
18
-
19
- it { subject.get('invalid_key').nil?.must_equal true }
20
- it { subject.get('preset_key').must_equal 'preset_value' }
21
- end
22
-
23
- describe '#unset' do
24
- it 'unsets a previously set key' do
25
- subject.set('preset_key', 'preset_value')
26
- subject.get('preset_key').must_equal 'preset_value'
27
- subject.unset('preset_key').must_equal true
28
- subject.get('preset_key').nil?.must_equal true
29
- end
30
-
31
- it 'returns true for an unset key' do
32
- subject.get('preset_key').nil?.must_equal true
33
- subject.unset('preset_key').must_equal true
34
- end
35
- end
4
+ let(:subject) { Hypostasis::Connection.open }
36
5
 
37
6
  describe '#create_namespace' do
38
- before :each do
7
+ before do
8
+ subject.destroy_namespace('already_created')
39
9
  subject.create_namespace('already_created')
40
10
  end
41
11
 
12
+ after do
13
+ subject.destroy_namespace('already_created')
14
+ subject.destroy_namespace('newly_created')
15
+ end
16
+
42
17
  it { subject.create_namespace('newly_created').is_a?(Hypostasis::Namespace).must_equal true }
43
18
  it { lambda { subject.create_namespace('already_created') }.must_raise Hypostasis::Errors::NamespaceAlreadyCreated }
44
19
  end
45
20
 
46
21
  describe '#open_namespace' do
47
- before :each do
22
+ before do
48
23
  subject.create_namespace('already_created')
49
24
  end
50
25
 
26
+ after do
27
+ subject.destroy_namespace('already_created')
28
+ end
29
+
51
30
  it { subject.open_namespace('already_created').is_a?(Hypostasis::Namespace).must_equal true }
52
31
  end
53
32
 
54
33
  describe '#destroy_namespace' do
55
- before :each do
34
+ before do
56
35
  subject.create_namespace('already_created')
57
36
  end
58
37
 
38
+ after do
39
+ subject.destroy_namespace('already_created')
40
+ end
41
+
59
42
  it { subject.destroy_namespace('already_created').must_equal true }
60
- it { lambda { subject.destroy_namespace('not_created') }.must_raise Hypostasis::Errors::NonExistentNamespace }
43
+ it { subject.destroy_namespace('not_created').must_equal true }
61
44
  end
62
45
  end
@@ -15,17 +15,6 @@ require 'minitest/autorun'
15
15
 
16
16
  class Minitest::Spec
17
17
 
18
- after do
19
- database.get_range_start_with("hypostasis\\config\\namespaces") do |kv|
20
- namespace = kv.key.gsub("hypostasis\\config\\namespaces\\", '')
21
- database.clear_range_start_with(namespace)
22
- end
23
-
24
- database.clear_range_start_with('hypostasis')
25
- end
26
-
27
- private
28
-
29
18
  def database
30
19
  @database ||= FDB.open
31
20
  end
@@ -1,19 +1,169 @@
1
1
  require 'minitest_helper'
2
2
 
3
3
  describe Hypostasis::Namespace do
4
- let(:subject) { Hypostasis::Namespace.new('demonstration', :document) }
4
+ let(:subject) { Hypostasis::Namespace.new('demonstration') }
5
5
 
6
6
  it { subject.must_respond_to :name }
7
7
  it { subject.name.must_equal 'demonstration' }
8
8
 
9
9
  it { subject.must_respond_to :data_model }
10
- it { subject.data_model.must_equal :document }
10
+ it { subject.data_model.must_equal :key_value }
11
11
 
12
- it { subject.must_respond_to :key_path }
13
- it { subject.key_path.is_a?(Hypostasis::KeyPath).must_equal true }
14
- it { subject.key_path.to_a.must_equal %w{demonstration} }
12
+ it { subject.must_respond_to :destroy }
15
13
 
16
- it { subject.must_respond_to :config_path }
17
- it { subject.config_path.is_a?(Hypostasis::KeyPath).must_equal true }
18
- it { subject.config_path.to_a.must_equal %w{hypostasis config namespaces demonstration} }
14
+ describe '#destroy' do
15
+ before do
16
+ Hypostasis::Connection.destroy_namespace('destroy_demo')
17
+ end
18
+
19
+ it {
20
+ ns = Hypostasis::Namespace.create('destroy_demo')
21
+ database.get('destroy_demo').must_equal 'true'
22
+ ns.destroy
23
+ database.get('destroy_demo').nil?.must_equal true
24
+ }
25
+ end
26
+
27
+ describe 'for a Key-Value namespace' do
28
+ before do
29
+ subject
30
+ end
31
+
32
+ after do
33
+ subject.destroy
34
+ end
35
+
36
+ let(:subject) { Hypostasis::Namespace.create('keyvalue_space', { data_model: :key_value }) }
37
+
38
+ it { database.get('keyvalue_space\\' + Hypostasis::Tuple.new(['config','data_model']).to_s).must_equal 'key_value' }
39
+ it { subject.must_respond_to :get }
40
+ it { subject.must_respond_to :set }
41
+
42
+ describe '#set' do
43
+ before do
44
+ subject.set('fixnum', 5)
45
+ end
46
+
47
+ it { database.get('keyvalue_space\\' + Hypostasis::Tuple.new('fixnum'.to_s, Fixnum.to_s).to_s).must_equal '5' }
48
+ end
49
+
50
+ describe '#get' do
51
+ describe 'for Fixnums' do
52
+ before do
53
+ subject.set('fixnum', 5)
54
+ end
55
+
56
+ it { subject.get('fixnum').must_equal 5 }
57
+ end
58
+
59
+ describe 'for Bignum' do
60
+ before do
61
+ subject.set('bignum', 12333333333333333333)
62
+ end
63
+
64
+ it { subject.get('bignum').must_equal 12333333333333333333 }
65
+ end
66
+
67
+ describe 'for Float' do
68
+ before do
69
+ subject.set('float', 1.2345)
70
+ end
71
+
72
+ it { subject.get('float').must_equal 1.2345 }
73
+ end
74
+
75
+ describe 'for String' do
76
+ before do
77
+ subject.set('string', 'a string')
78
+ end
79
+
80
+ it { subject.get('string').must_equal 'a string' }
81
+ end
82
+
83
+ describe 'for Date' do
84
+ before do
85
+ @date = Date.today
86
+ subject.set('date', @date)
87
+ end
88
+
89
+ it { subject.get('date').must_equal @date }
90
+ end
91
+
92
+ describe 'for DateTime' do
93
+ before do
94
+ @date = DateTime.now
95
+ subject.set('datetime', @date)
96
+ end
97
+
98
+ it { subject.get('datetime').year.must_equal @date.year }
99
+ it { subject.get('datetime').month.must_equal @date.month }
100
+ it { subject.get('datetime').day.must_equal @date.day }
101
+ it { subject.get('datetime').hour.must_equal @date.hour }
102
+ it { subject.get('datetime').minute.must_equal @date.minute }
103
+ it { subject.get('datetime').second.must_equal @date.second }
104
+ it { subject.get('datetime').zone.must_equal @date.zone }
105
+ end
106
+
107
+ describe 'for Time' do
108
+ before do
109
+ @time = Time.now
110
+ subject.set('time', @time)
111
+ end
112
+
113
+ it { subject.get('time').hour.must_equal @time.hour }
114
+ it { subject.get('time').min.must_equal @time.min }
115
+ it { subject.get('time').sec.must_equal @time.sec }
116
+ it { subject.get('time').zone.must_equal @time.zone }
117
+ end
118
+
119
+ describe 'for Bolean' do
120
+ before do
121
+ subject.set('true', true)
122
+ subject.set('false', false)
123
+ end
124
+
125
+ it { subject.get('true').must_equal true }
126
+ it { subject.get('false').must_equal false }
127
+ end
128
+
129
+ describe 'for unknown type' do
130
+ before do
131
+ class Unknown; end
132
+ database.set('keyvalue_space\\' + Hypostasis::Tuple.new('unknown'.to_s, Unknown.to_s).to_s, '1')
133
+ end
134
+
135
+ it { lambda { subject.get('unknown') }.must_raise Hypostasis::Errors::UnknownValueType }
136
+ end
137
+ end
138
+ end
139
+
140
+ describe 'for a Document namespace' do
141
+ before do
142
+ subject
143
+ end
144
+
145
+ after do
146
+ subject.destroy
147
+ end
148
+
149
+ let(:subject) { Hypostasis::Namespace.create('document_space', { data_model: :document }) }
150
+
151
+ it { database.get('document_space\\' + Hypostasis::Tuple.new(['config','data_model']).to_s).must_equal 'document' }
152
+ end
153
+
154
+ describe 'for an unknown namespace type' do
155
+ after do
156
+ Hypostasis::Connection.destroy_namespace('unknown_space')
157
+ end
158
+
159
+ it { lambda { Hypostasis::Namespace.create('unknown_space', { data_model: :unknown }) }.must_raise Hypostasis::Errors::UnknownNamespaceDataModel }
160
+ it {
161
+ database.set('unknown_space', 'true')
162
+ database.set('unknown_space\\' + Hypostasis::Tuple.new(['config','data_model']).to_s, 'unknown')
163
+
164
+ lambda {
165
+ Hypostasis::Namespace.open('unknown_space')
166
+ }.must_raise Hypostasis::Errors::UnknownNamespaceDataModel
167
+ }
168
+ end
19
169
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hypostasis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Thompson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-16 00:00:00.000000000 Z
11
+ date: 2014-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fdb
@@ -83,17 +83,15 @@ files:
83
83
  - hypostasis.gemspec
84
84
  - lib/hypostasis.rb
85
85
  - lib/hypostasis/connection.rb
86
- - lib/hypostasis/data_model/document.rb
87
- - lib/hypostasis/data_model/key_value.rb
88
86
  - lib/hypostasis/data_models.rb
87
+ - lib/hypostasis/data_models/document.rb
88
+ - lib/hypostasis/data_models/key_value.rb
89
89
  - lib/hypostasis/errors.rb
90
90
  - lib/hypostasis/key_path.rb
91
91
  - lib/hypostasis/namespace.rb
92
92
  - lib/hypostasis/tuple.rb
93
93
  - lib/hypostasis/version.rb
94
94
  - test/connection_spec.rb
95
- - test/data_models/document/document_spec.rb
96
- - test/data_models/key_value/key_value_spec.rb
97
95
  - test/key_path_spec.rb
98
96
  - test/minitest_helper.rb
99
97
  - test/namespace_spec.rb
@@ -124,8 +122,6 @@ specification_version: 4
124
122
  summary: A layer for FoundationDB providing multiple data models for Ruby.
125
123
  test_files:
126
124
  - test/connection_spec.rb
127
- - test/data_models/document/document_spec.rb
128
- - test/data_models/key_value/key_value_spec.rb
129
125
  - test/key_path_spec.rb
130
126
  - test/minitest_helper.rb
131
127
  - test/namespace_spec.rb
@@ -1,3 +0,0 @@
1
- module Hypostasis::DataModel::Document
2
-
3
- end
@@ -1,3 +0,0 @@
1
- module Hypostasis::DataModel::KeyValue
2
-
3
- end
@@ -1,5 +0,0 @@
1
- require 'minitest_helper'
2
-
3
- describe Hypostasis::DataModel::Document do
4
-
5
- end
@@ -1,5 +0,0 @@
1
- require 'minitest_helper'
2
-
3
- describe Hypostasis::DataModel::KeyValue do
4
-
5
- end