hypostasis 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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