hypostasis 0.2.3 → 0.3.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/README.md +17 -13
- data/lib/hypostasis.rb +2 -1
- data/lib/hypostasis/column_group.rb +48 -0
- data/lib/hypostasis/{document → column_group}/belongs_to.rb +1 -1
- data/lib/hypostasis/{document → column_group}/fields.rb +3 -1
- data/lib/hypostasis/{document → column_group}/findable.rb +21 -17
- data/lib/hypostasis/{document → column_group}/has_many.rb +1 -1
- data/lib/hypostasis/{document → column_group}/has_one.rb +1 -1
- data/lib/hypostasis/{document → column_group}/indexes.rb +3 -1
- data/lib/hypostasis/{document → column_group}/namespaced.rb +2 -2
- data/lib/hypostasis/{document → column_group}/persistence.rb +3 -3
- data/lib/hypostasis/data_models.rb +1 -1
- data/lib/hypostasis/data_models/{document.rb → column_group.rb} +5 -5
- data/lib/hypostasis/data_models/utilities.rb +2 -0
- data/lib/hypostasis/errors.rb +1 -1
- data/lib/hypostasis/key.rb +36 -0
- data/lib/hypostasis/namespace.rb +2 -2
- data/lib/hypostasis/version.rb +1 -1
- data/test/{document → column}/has_many_spec.rb +4 -4
- data/test/{document → column}/has_one_spec.rb +4 -4
- data/test/column_spec.rb +106 -0
- data/test/key_spec.rb +25 -0
- data/test/minitest_helper.rb +3 -3
- data/test/namespace_spec.rb +3 -3
- data/test/support/indexed_column.rb +12 -0
- data/test/support/sample_column.rb +9 -0
- metadata +25 -22
- data/lib/hypostasis/document.rb +0 -48
- data/test/document_spec.rb +0 -106
- data/test/support/indexed_document.rb +0 -12
- data/test/support/sample_document.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5d1ed53d8953d86bdbf7c430f94f7b0cb175d8a
|
4
|
+
data.tar.gz: 15cc8dcd3abb23678df603443e6f5f12e8797cd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb5c00de6a634eb076e224f8d863db4f0008252fa0b828c4806090598e4a4258ef5a443adcd83e74fa69d8acd0fe52b98bca843d8dcfea1038f017b940889ad7
|
7
|
+
data.tar.gz: a983faf3f5085fa01702658daaeb57486877a34f585fff043c5a4172d247ce880c88ed1a70abd3394a6ac6e1ce480d8900b8294e7e051ce904bb00082e40e3a3
|
data/README.md
CHANGED
@@ -19,8 +19,10 @@ variety of data models while using the same underlying storage system, provided
|
|
19
19
|
by FoundationDB. The data models Hypostasis currently aims to support are the
|
20
20
|
following:
|
21
21
|
|
22
|
-
* Document
|
23
22
|
* Key-Value
|
23
|
+
* Column Group
|
24
|
+
* Document
|
25
|
+
|
24
26
|
|
25
27
|
## Installation
|
26
28
|
|
@@ -68,14 +70,14 @@ basic language types currently suported include the following:
|
|
68
70
|
* Time
|
69
71
|
* Boolean
|
70
72
|
|
71
|
-
###
|
73
|
+
### ColumnGroup Data Model
|
72
74
|
|
73
75
|
require 'hypostasis'
|
74
76
|
|
75
|
-
ns = Hypostasis::Connection.create_namespace('keystore', {data_model: :
|
77
|
+
ns = Hypostasis::Connection.create_namespace('keystore', {data_model: :column_group})
|
76
78
|
|
77
|
-
class
|
78
|
-
include Hypostasis::
|
79
|
+
class SampleColumnGroup
|
80
|
+
include Hypostasis::ColumnGroup
|
79
81
|
|
80
82
|
field :name
|
81
83
|
field :age
|
@@ -85,17 +87,19 @@ basic language types currently suported include the following:
|
|
85
87
|
index :age
|
86
88
|
end
|
87
89
|
|
88
|
-
|
90
|
+
SampleColumnGroup.create(name: 'John', age: 21, dob: Date.today.prev_year(21))
|
89
91
|
|
90
|
-
|
92
|
+
SampleColumnGroup.find(<<id>>)
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
+
SampleColumnGroup.find_where(name: 'John')
|
95
|
+
SampleColumnGroup.find_where(age: 21)
|
94
96
|
|
95
|
-
The
|
96
|
-
|
97
|
-
|
98
|
-
|
97
|
+
The Column Group data model provides a data model very similar to what would be
|
98
|
+
provided by a traditional RDBMS or Column-Family data store with data organized
|
99
|
+
along the idea of tables and rows, but mapped directly to the underlying
|
100
|
+
key-value store of FoundationDB. Like the other data models, the Column Group
|
101
|
+
model is able to automatically encode and reconstitute certain basic Ruby data
|
102
|
+
types, including the following:
|
99
103
|
|
100
104
|
* String
|
101
105
|
* Fixnum
|
data/lib/hypostasis.rb
CHANGED
@@ -4,8 +4,9 @@ require 'hypostasis/errors'
|
|
4
4
|
|
5
5
|
require 'hypostasis/tuple'
|
6
6
|
require 'hypostasis/key_path'
|
7
|
+
require 'hypostasis/key'
|
7
8
|
require 'hypostasis/connection'
|
8
9
|
require 'hypostasis/data_models'
|
9
10
|
require 'hypostasis/namespace'
|
10
11
|
|
11
|
-
require 'hypostasis/
|
12
|
+
require 'hypostasis/column_group'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
|
4
|
+
require 'hypostasis/column_group/namespaced'
|
5
|
+
require 'hypostasis/column_group/fields'
|
6
|
+
require 'hypostasis/column_group/indexes'
|
7
|
+
require 'hypostasis/column_group/persistence'
|
8
|
+
require 'hypostasis/column_group/findable'
|
9
|
+
require 'hypostasis/column_group/belongs_to'
|
10
|
+
require 'hypostasis/column_group/has_one'
|
11
|
+
require 'hypostasis/column_group/has_many'
|
12
|
+
|
13
|
+
module Hypostasis::ColumnGroup
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
include Hypostasis::ColumnGroup::Namespaced
|
17
|
+
include Hypostasis::ColumnGroup::Fields
|
18
|
+
include Hypostasis::ColumnGroup::Indexes
|
19
|
+
include Hypostasis::ColumnGroup::Persistence
|
20
|
+
include Hypostasis::ColumnGroup::Findable
|
21
|
+
|
22
|
+
include Hypostasis::ColumnGroup::BelongsTo
|
23
|
+
include Hypostasis::ColumnGroup::HasOne
|
24
|
+
include Hypostasis::ColumnGroup::HasMany
|
25
|
+
|
26
|
+
attr_reader :id
|
27
|
+
|
28
|
+
def initialize(*attributes)
|
29
|
+
self.class.namespace.open
|
30
|
+
|
31
|
+
@fields = {}
|
32
|
+
self.class.fields.each {|name| @fields[name] = nil}
|
33
|
+
attributes.each {|hsh| hsh.each {|name, value| @fields[name.to_sym] = value}}
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_id
|
38
|
+
@id ||= SecureRandom.uuid
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_id(id)
|
42
|
+
@id ||= id.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
module ClassMethods
|
46
|
+
include Hypostasis::DataModels::Utilities
|
47
|
+
end
|
48
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Hypostasis::
|
1
|
+
module Hypostasis::ColumnGroup
|
2
2
|
module Fields
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
@@ -12,6 +12,8 @@ module Hypostasis::Document
|
|
12
12
|
self.class_eval { class_variable_get(:@@fields) }
|
13
13
|
end
|
14
14
|
|
15
|
+
private
|
16
|
+
|
15
17
|
def register_field(name)
|
16
18
|
self.class_eval do
|
17
19
|
class_variable_set(:@@fields, []) unless class_variable_defined?(:@@fields)
|
@@ -1,26 +1,14 @@
|
|
1
|
-
module Hypostasis::
|
1
|
+
module Hypostasis::ColumnGroup
|
2
2
|
module Findable
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
6
|
def find(id)
|
7
|
-
document_keys =
|
8
|
-
|
9
|
-
document_keys = tr.get_range_start_with(namespace.for_document(self, id)).to_a
|
7
|
+
document_keys = namespace.transact do |tr|
|
8
|
+
tr.get_range_start_with(namespace.for_column_group(self, id)).to_a
|
10
9
|
end
|
11
|
-
raise Hypostasis::Errors::
|
12
|
-
|
13
|
-
id = Hypostasis::Tuple.unpack(document_keys.first.key.split('\\')[1]).to_a[1]
|
14
|
-
document_keys.each do |key|
|
15
|
-
attribute_tuple = key.key.split('\\')[2]
|
16
|
-
next if attribute_tuple.nil?
|
17
|
-
unpacked_key = Hypostasis::Tuple.unpack(attribute_tuple)
|
18
|
-
raw_value = key.value
|
19
|
-
attributes[unpacked_key.to_a[0].to_sym] = reconstitute_value(unpacked_key, raw_value)
|
20
|
-
end
|
21
|
-
document = self.new(attributes)
|
22
|
-
document.set_id(id)
|
23
|
-
document
|
10
|
+
raise Hypostasis::Errors::ColumnGroupNotFound if document_keys.empty?
|
11
|
+
reconstitute_column_group(document_keys)
|
24
12
|
end
|
25
13
|
|
26
14
|
def find_where(field_value_pairs)
|
@@ -36,6 +24,22 @@ module Hypostasis::Document
|
|
36
24
|
results.uniq!
|
37
25
|
results.collect! {|result| find(result) }
|
38
26
|
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def reconstitute_column_group(keys)
|
31
|
+
attributes = {}
|
32
|
+
keys.each do |key|
|
33
|
+
attribute_tuple = key.key.split('\\')[2]
|
34
|
+
next if attribute_tuple.nil?
|
35
|
+
unpacked_key = Hypostasis::Tuple.unpack(attribute_tuple)
|
36
|
+
raw_value = key.value
|
37
|
+
attributes[unpacked_key.to_a[0].to_sym] = reconstitute_value(unpacked_key, raw_value)
|
38
|
+
end
|
39
|
+
document = self.new(attributes)
|
40
|
+
document.set_id(Hypostasis::Tuple.unpack(keys.first.key.split('\\')[1]).to_a[1])
|
41
|
+
document
|
42
|
+
end
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
module Hypostasis::
|
1
|
+
module Hypostasis::ColumnGroup
|
2
2
|
module Indexes
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
private
|
6
|
+
|
5
7
|
def indexed_fields_to_commit
|
6
8
|
self.class.class_eval { class_variable_set(:@@indexed_fields, []) unless class_variable_defined?(:@@indexed_fields) }
|
7
9
|
self.class.indexed_fields.collect do |field_name|
|
@@ -1,11 +1,11 @@
|
|
1
|
-
module Hypostasis::
|
1
|
+
module Hypostasis::ColumnGroup
|
2
2
|
module Namespaced
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
6
|
def use_namespace(namespace)
|
7
7
|
data_model = :key_value
|
8
|
-
data_model = :
|
8
|
+
data_model = :column_group if self.included_modules.include?(Hypostasis::ColumnGroup)
|
9
9
|
self.class_eval do
|
10
10
|
class_variable_set(:@@namespace, Hypostasis::Namespace.new(namespace.to_s, data_model))
|
11
11
|
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
module Hypostasis::
|
1
|
+
module Hypostasis::ColumnGroup
|
2
2
|
module Persistence
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
def save
|
6
6
|
generate_id
|
7
7
|
self.class.namespace.transact do |tr|
|
8
|
-
tr.set(self.class.namespace.
|
8
|
+
tr.set(self.class.namespace.for_column_group(self), true.to_s)
|
9
9
|
|
10
10
|
@fields.each do |field_name, value|
|
11
11
|
tr.set(self.class.namespace.for_field(self, field_name, value.class.to_s), value.to_s)
|
@@ -20,7 +20,7 @@ module Hypostasis::Document
|
|
20
20
|
|
21
21
|
def destroy
|
22
22
|
self.class.namespace.transact do |tr|
|
23
|
-
tr.clear_range_start_with(self.class.namespace.
|
23
|
+
tr.clear_range_start_with(self.class.namespace.for_column_group(self))
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -1,18 +1,18 @@
|
|
1
|
-
module Hypostasis::DataModels::
|
1
|
+
module Hypostasis::DataModels::ColumnGroup
|
2
2
|
def transact
|
3
3
|
database.transact do |tr|
|
4
4
|
yield tr
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
class_name =
|
10
|
-
document_id = id.nil? ?
|
8
|
+
def for_column_group(column_group, id = nil)
|
9
|
+
class_name = column_group.is_a?(Class) ? column_group.to_s : column_group.class.to_s
|
10
|
+
document_id = id.nil? ? column_group.id.to_s : id.to_s
|
11
11
|
name.to_s + '\\' + Hypostasis::Tuple.new(class_name, document_id).to_s
|
12
12
|
end
|
13
13
|
|
14
14
|
def for_field(document, field, type)
|
15
|
-
|
15
|
+
for_column_group(document) + '\\' + Hypostasis::Tuple.new(field.to_s, type.to_s).to_s
|
16
16
|
end
|
17
17
|
|
18
18
|
def for_index(document, field_name, value)
|
data/lib/hypostasis/errors.rb
CHANGED
@@ -10,5 +10,5 @@ module Hypostasis::Errors
|
|
10
10
|
class TupleExhausted < StandardError; end
|
11
11
|
class UnknownValueType < StandardError; end
|
12
12
|
class MustDefineFieldType < StandardError; end
|
13
|
-
class
|
13
|
+
class ColumnGroupNotFound < StandardError; end
|
14
14
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Hypostasis::Key
|
2
|
+
def initialize(key_path, value = nil)
|
3
|
+
@key_path = key_path
|
4
|
+
end
|
5
|
+
|
6
|
+
def first
|
7
|
+
unpack_tuple(decomposed_key.first)
|
8
|
+
end
|
9
|
+
|
10
|
+
def last
|
11
|
+
unpack_tuple(decomposed_key.last)
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](index)
|
15
|
+
unpack_tuple(decomposed_key[index.to_i])
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def decomposed_key
|
21
|
+
@decomposed_key ||= @key_path.split('\\')
|
22
|
+
end
|
23
|
+
|
24
|
+
def unpack_tuple(key)
|
25
|
+
begin
|
26
|
+
Hypostasis::Tuple.unpack(key)
|
27
|
+
rescue RuntimeError => e
|
28
|
+
if e.message.match(/^Unknown data type in DB:/)
|
29
|
+
key
|
30
|
+
else
|
31
|
+
raise e
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/hypostasis/namespace.rb
CHANGED
@@ -56,8 +56,8 @@ private
|
|
56
56
|
case @data_model
|
57
57
|
when :key_value
|
58
58
|
self.extend Hypostasis::DataModels::KeyValue
|
59
|
-
when :
|
60
|
-
self.extend Hypostasis::DataModels::
|
59
|
+
when :column_group
|
60
|
+
self.extend Hypostasis::DataModels::ColumnGroup
|
61
61
|
else
|
62
62
|
raise Hypostasis::Errors::UnknownNamespaceDataModel, "#{@data_model} unknown"
|
63
63
|
end
|
data/lib/hypostasis/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'minitest_helper'
|
2
2
|
|
3
3
|
class HasManyOwnerDocument
|
4
|
-
include Hypostasis::
|
4
|
+
include Hypostasis::ColumnGroup
|
5
5
|
|
6
6
|
use_namespace 'hasmany_docs'
|
7
7
|
|
@@ -12,7 +12,7 @@ class HasManyOwnerDocument
|
|
12
12
|
end
|
13
13
|
|
14
14
|
class HasManyChildDocument
|
15
|
-
include Hypostasis::
|
15
|
+
include Hypostasis::ColumnGroup
|
16
16
|
|
17
17
|
use_namespace 'hasmany_docs'
|
18
18
|
|
@@ -22,9 +22,9 @@ class HasManyChildDocument
|
|
22
22
|
belongs_to :has_many_owner_document
|
23
23
|
end
|
24
24
|
|
25
|
-
describe '
|
25
|
+
describe 'ColumnGroup has_many Relationship' do
|
26
26
|
before do
|
27
|
-
Hypostasis::Connection.create_namespace 'hasmany_docs', data_model: :
|
27
|
+
Hypostasis::Connection.create_namespace 'hasmany_docs', data_model: :column_group
|
28
28
|
@owner = HasManyOwnerDocument.create(name: 'John', age: '25')
|
29
29
|
@children = []
|
30
30
|
@children << HasManyChildDocument.create(name: 'James', age: '6', has_many_owner_document_id: @owner.id)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'minitest_helper'
|
2
2
|
|
3
3
|
class HasOneOwnerDocument
|
4
|
-
include Hypostasis::
|
4
|
+
include Hypostasis::ColumnGroup
|
5
5
|
|
6
6
|
use_namespace 'hasone_docs'
|
7
7
|
|
@@ -12,7 +12,7 @@ class HasOneOwnerDocument
|
|
12
12
|
end
|
13
13
|
|
14
14
|
class HasOneChildDocument
|
15
|
-
include Hypostasis::
|
15
|
+
include Hypostasis::ColumnGroup
|
16
16
|
|
17
17
|
use_namespace 'hasone_docs'
|
18
18
|
|
@@ -22,9 +22,9 @@ class HasOneChildDocument
|
|
22
22
|
belongs_to :has_one_owner_document
|
23
23
|
end
|
24
24
|
|
25
|
-
describe '
|
25
|
+
describe 'ColumnGroup has_one Relationship' do
|
26
26
|
before do
|
27
|
-
Hypostasis::Connection.create_namespace 'hasone_docs', data_model: :
|
27
|
+
Hypostasis::Connection.create_namespace 'hasone_docs', data_model: :column_group
|
28
28
|
@owner = HasOneOwnerDocument.create(name: 'John', age: '25')
|
29
29
|
@child = HasOneChildDocument.create(name: 'James', age: '6', has_one_owner_document_id: @owner.id)
|
30
30
|
end
|
data/test/column_spec.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Hypostasis::ColumnGroup do
|
4
|
+
let(:subject) { SampleColumn.new(name: 'John', age: 21, dob: Date.today.prev_year(21)) }
|
5
|
+
|
6
|
+
before do
|
7
|
+
Hypostasis::Connection.create_namespace 'sample_columns', data_model: :column_group
|
8
|
+
Hypostasis::Connection.create_namespace 'indexed_columns', data_model: :column_group
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
Hypostasis::Connection.destroy_namespace 'sample_columns'
|
13
|
+
Hypostasis::Connection.destroy_namespace 'indexed_columns'
|
14
|
+
end
|
15
|
+
|
16
|
+
it { subject.must_respond_to :name }
|
17
|
+
it { subject.must_respond_to :age }
|
18
|
+
it { subject.must_respond_to :dob }
|
19
|
+
|
20
|
+
it { subject.must_respond_to :name= }
|
21
|
+
it { subject.must_respond_to :age= }
|
22
|
+
it { subject.must_respond_to :dob= }
|
23
|
+
|
24
|
+
it { subject.name.must_equal 'John' }
|
25
|
+
it { subject.age.must_equal 21 }
|
26
|
+
it { subject.dob.must_equal Date.today.prev_year(21) }
|
27
|
+
|
28
|
+
it { subject.must_respond_to :save }
|
29
|
+
|
30
|
+
describe '#create' do
|
31
|
+
let(:subject) { SampleColumn.create(name: 'John', age: 21, dob: Date.today.prev_year(21)) }
|
32
|
+
|
33
|
+
after do
|
34
|
+
subject.destroy
|
35
|
+
end
|
36
|
+
|
37
|
+
it { subject.id.wont_be_nil }
|
38
|
+
it { database.get(column_path(subject)).must_equal 'true' }
|
39
|
+
it { database.get(field_path(subject, :name, String)).must_equal 'John' }
|
40
|
+
it { database.get(field_path(subject, :age, Fixnum)).must_equal '21' }
|
41
|
+
it { database.get(field_path(subject, :dob, Date)).must_equal Date.today.prev_year(21).to_s }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#save' do
|
45
|
+
let(:subject) { SampleColumn.new(name: 'John', age: 21, dob: Date.today.prev_year(21)) }
|
46
|
+
|
47
|
+
before do
|
48
|
+
subject.save
|
49
|
+
end
|
50
|
+
|
51
|
+
after do
|
52
|
+
subject.destroy
|
53
|
+
end
|
54
|
+
|
55
|
+
it { subject.id.wont_be_nil }
|
56
|
+
it { database.get(column_path(subject)).must_equal 'true' }
|
57
|
+
it { database.get(field_path(subject, :name, String)).must_equal 'John' }
|
58
|
+
it { database.get(field_path(subject, :age, Fixnum)).must_equal '21' }
|
59
|
+
it { database.get(field_path(subject, :dob, Date)).must_equal Date.today.prev_year(21).to_s }
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.find' do
|
63
|
+
let(:column_id) { subject.save.id }
|
64
|
+
|
65
|
+
after do
|
66
|
+
subject.destroy
|
67
|
+
end
|
68
|
+
|
69
|
+
it { SampleColumn.find(column_id).is_a?(SampleColumn).must_equal true }
|
70
|
+
it { SampleColumn.find(column_id).id.must_equal column_id }
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'indexing' do
|
74
|
+
before do
|
75
|
+
IndexedColumn.create(name: 'John', age: 21, dob: Date.today.prev_year(21))
|
76
|
+
IndexedColumn.create(name: 'Jane', age: 21, dob: Date.today.prev_year(21))
|
77
|
+
IndexedColumn.create(name: 'John', age: 23, dob: Date.today.prev_year(23))
|
78
|
+
IndexedColumn.create(name: 'Tom', age: 20, dob: Date.today.prev_year(20))
|
79
|
+
end
|
80
|
+
|
81
|
+
it { database.get_range_start_with(index_path(IndexedColumn, :name)).size.must_equal 4 }
|
82
|
+
it { database.get_range_start_with(index_path(IndexedColumn, :age)).size.must_equal 4 }
|
83
|
+
|
84
|
+
it { database.get_range_start_with(index_path(IndexedColumn, :name, 'John')).size.must_equal 2 }
|
85
|
+
it { database.get_range_start_with(index_path(IndexedColumn, :name, 'Jane')).size.must_equal 1 }
|
86
|
+
|
87
|
+
it { database.get_range_start_with(index_path(IndexedColumn, :age, 21)).size.must_equal 2 }
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '.find_where' do
|
91
|
+
before do
|
92
|
+
IndexedColumn.create(name: 'John', age: 21, dob: Date.today.prev_year(21))
|
93
|
+
IndexedColumn.create(name: 'Jane', age: 21, dob: Date.today.prev_year(21))
|
94
|
+
IndexedColumn.create(name: 'John', age: 23, dob: Date.today.prev_year(23))
|
95
|
+
IndexedColumn.create(name: 'Tom', age: 20, dob: Date.today.prev_year(20))
|
96
|
+
end
|
97
|
+
|
98
|
+
it { IndexedColumn.find_where(name: 'John').size.must_equal 2 }
|
99
|
+
it { IndexedColumn.find_where(age: 21).size.must_equal 2 }
|
100
|
+
it { IndexedColumn.find_where(name: 'Tom').size.must_equal 1 }
|
101
|
+
it { IndexedColumn.find_where(name: 'Tom').first.is_a?(IndexedColumn).must_equal true }
|
102
|
+
|
103
|
+
it { IndexedColumn.find_where(name: 'John', age: 23).size.must_equal 1 }
|
104
|
+
it { IndexedColumn.find_where(name: 'John', age: 23).first.is_a?(IndexedColumn).must_equal true }
|
105
|
+
end
|
106
|
+
end
|
data/test/key_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Hypostasis::Key do
|
4
|
+
let(:subject) { Hypostasis::Key.new('simple\\path\\example') }
|
5
|
+
|
6
|
+
it { subject.must_respond_to :[] }
|
7
|
+
it { subject.must_respond_to :first }
|
8
|
+
it { subject.must_respond_to :last }
|
9
|
+
|
10
|
+
it { subject[0].must_equal 'simple' }
|
11
|
+
it { subject[1].must_equal 'path' }
|
12
|
+
it { subject[2].must_equal 'example' }
|
13
|
+
|
14
|
+
it { subject.first.must_equal 'simple' }
|
15
|
+
it { subject.last.must_equal 'example'}
|
16
|
+
|
17
|
+
describe 'with Tuples' do
|
18
|
+
let(:tuple) { Hypostasis::Tuple.new('sample','tuple',15) }
|
19
|
+
let(:subject) { Hypostasis::Key.new("simple\\tuple\\#{tuple.to_s}") }
|
20
|
+
|
21
|
+
it { subject[0].must_equal 'simple' }
|
22
|
+
it { subject[1].must_equal 'tuple' }
|
23
|
+
it { subject[2].must_be_instance_of Hypostasis::Tuple }
|
24
|
+
end
|
25
|
+
end
|
data/test/minitest_helper.rb
CHANGED
@@ -13,8 +13,8 @@ require 'hypostasis'
|
|
13
13
|
|
14
14
|
require 'minitest/autorun'
|
15
15
|
|
16
|
-
require 'support/
|
17
|
-
require 'support/
|
16
|
+
require 'support/sample_column'
|
17
|
+
require 'support/indexed_column'
|
18
18
|
|
19
19
|
class Minitest::Spec
|
20
20
|
|
@@ -22,7 +22,7 @@ class Minitest::Spec
|
|
22
22
|
@database ||= FDB.open
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
25
|
+
def column_path(document)
|
26
26
|
document_namespace = document.class.namespace.to_s
|
27
27
|
document_tuple = Hypostasis::Tuple.new(document.class.to_s, document.id.to_s).to_s
|
28
28
|
document_namespace + '\\' + document_tuple
|
data/test/namespace_spec.rb
CHANGED
@@ -137,7 +137,7 @@ describe Hypostasis::Namespace do
|
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
|
-
describe 'for a
|
140
|
+
describe 'for a ColumnGroup namespace' do
|
141
141
|
before do
|
142
142
|
subject
|
143
143
|
end
|
@@ -146,9 +146,9 @@ describe Hypostasis::Namespace do
|
|
146
146
|
subject.destroy
|
147
147
|
end
|
148
148
|
|
149
|
-
let(:subject) { Hypostasis::Namespace.create('
|
149
|
+
let(:subject) { Hypostasis::Namespace.create('column_space', { data_model: :column_group }) }
|
150
150
|
|
151
|
-
it { database.get('
|
151
|
+
it { database.get('column_space\\' + Hypostasis::Tuple.new(['config','data_model']).to_s).must_equal 'column_group' }
|
152
152
|
end
|
153
153
|
|
154
154
|
describe 'for an unknown namespace type' do
|
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.
|
4
|
+
version: 0.3.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-
|
11
|
+
date: 2014-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fdb
|
@@ -97,35 +97,37 @@ files:
|
|
97
97
|
- Vagrantfile
|
98
98
|
- hypostasis.gemspec
|
99
99
|
- lib/hypostasis.rb
|
100
|
+
- lib/hypostasis/column_group.rb
|
101
|
+
- lib/hypostasis/column_group/belongs_to.rb
|
102
|
+
- lib/hypostasis/column_group/fields.rb
|
103
|
+
- lib/hypostasis/column_group/findable.rb
|
104
|
+
- lib/hypostasis/column_group/has_many.rb
|
105
|
+
- lib/hypostasis/column_group/has_one.rb
|
106
|
+
- lib/hypostasis/column_group/indexes.rb
|
107
|
+
- lib/hypostasis/column_group/namespaced.rb
|
108
|
+
- lib/hypostasis/column_group/persistence.rb
|
100
109
|
- lib/hypostasis/connection.rb
|
101
110
|
- lib/hypostasis/data_models.rb
|
102
|
-
- lib/hypostasis/data_models/
|
111
|
+
- lib/hypostasis/data_models/column_group.rb
|
103
112
|
- lib/hypostasis/data_models/key_value.rb
|
104
113
|
- lib/hypostasis/data_models/utilities.rb
|
105
|
-
- lib/hypostasis/document.rb
|
106
|
-
- lib/hypostasis/document/belongs_to.rb
|
107
|
-
- lib/hypostasis/document/fields.rb
|
108
|
-
- lib/hypostasis/document/findable.rb
|
109
|
-
- lib/hypostasis/document/has_many.rb
|
110
|
-
- lib/hypostasis/document/has_one.rb
|
111
|
-
- lib/hypostasis/document/indexes.rb
|
112
|
-
- lib/hypostasis/document/namespaced.rb
|
113
|
-
- lib/hypostasis/document/persistence.rb
|
114
114
|
- lib/hypostasis/errors.rb
|
115
|
+
- lib/hypostasis/key.rb
|
115
116
|
- lib/hypostasis/key_path.rb
|
116
117
|
- lib/hypostasis/namespace.rb
|
117
118
|
- lib/hypostasis/tuple.rb
|
118
119
|
- lib/hypostasis/version.rb
|
119
120
|
- provision.sh
|
121
|
+
- test/column/has_many_spec.rb
|
122
|
+
- test/column/has_one_spec.rb
|
123
|
+
- test/column_spec.rb
|
120
124
|
- test/connection_spec.rb
|
121
|
-
- test/document/has_many_spec.rb
|
122
|
-
- test/document/has_one_spec.rb
|
123
|
-
- test/document_spec.rb
|
124
125
|
- test/key_path_spec.rb
|
126
|
+
- test/key_spec.rb
|
125
127
|
- test/minitest_helper.rb
|
126
128
|
- test/namespace_spec.rb
|
127
|
-
- test/support/
|
128
|
-
- test/support/
|
129
|
+
- test/support/indexed_column.rb
|
130
|
+
- test/support/sample_column.rb
|
129
131
|
- test/tuple_spec.rb
|
130
132
|
homepage: ''
|
131
133
|
licenses:
|
@@ -152,13 +154,14 @@ signing_key:
|
|
152
154
|
specification_version: 4
|
153
155
|
summary: A layer for FoundationDB providing multiple data models for Ruby.
|
154
156
|
test_files:
|
157
|
+
- test/column/has_many_spec.rb
|
158
|
+
- test/column/has_one_spec.rb
|
159
|
+
- test/column_spec.rb
|
155
160
|
- test/connection_spec.rb
|
156
|
-
- test/document/has_many_spec.rb
|
157
|
-
- test/document/has_one_spec.rb
|
158
|
-
- test/document_spec.rb
|
159
161
|
- test/key_path_spec.rb
|
162
|
+
- test/key_spec.rb
|
160
163
|
- test/minitest_helper.rb
|
161
164
|
- test/namespace_spec.rb
|
162
|
-
- test/support/
|
163
|
-
- test/support/
|
165
|
+
- test/support/indexed_column.rb
|
166
|
+
- test/support/sample_column.rb
|
164
167
|
- test/tuple_spec.rb
|
data/lib/hypostasis/document.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
require 'active_support/inflector'
|
3
|
-
|
4
|
-
require 'hypostasis/document/namespaced'
|
5
|
-
require 'hypostasis/document/fields'
|
6
|
-
require 'hypostasis/document/indexes'
|
7
|
-
require 'hypostasis/document/persistence'
|
8
|
-
require 'hypostasis/document/findable'
|
9
|
-
require 'hypostasis/document/belongs_to'
|
10
|
-
require 'hypostasis/document/has_one'
|
11
|
-
require 'hypostasis/document/has_many'
|
12
|
-
|
13
|
-
module Hypostasis::Document
|
14
|
-
extend ActiveSupport::Concern
|
15
|
-
|
16
|
-
include Hypostasis::Document::Namespaced
|
17
|
-
include Hypostasis::Document::Fields
|
18
|
-
include Hypostasis::Document::Indexes
|
19
|
-
include Hypostasis::Document::Persistence
|
20
|
-
include Hypostasis::Document::Findable
|
21
|
-
|
22
|
-
include Hypostasis::Document::BelongsTo
|
23
|
-
include Hypostasis::Document::HasOne
|
24
|
-
include Hypostasis::Document::HasMany
|
25
|
-
|
26
|
-
attr_reader :id
|
27
|
-
|
28
|
-
def initialize(*attributes)
|
29
|
-
self.class.namespace.open
|
30
|
-
|
31
|
-
@fields = {}
|
32
|
-
self.class.fields.each {|name| @fields[name] = nil}
|
33
|
-
attributes.each {|hsh| hsh.each {|name, value| @fields[name.to_sym] = value}}
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
def generate_id
|
38
|
-
@id ||= SecureRandom.uuid
|
39
|
-
end
|
40
|
-
|
41
|
-
def set_id(id)
|
42
|
-
@id ||= id.to_s
|
43
|
-
end
|
44
|
-
|
45
|
-
module ClassMethods
|
46
|
-
include Hypostasis::DataModels::Utilities
|
47
|
-
end
|
48
|
-
end
|
data/test/document_spec.rb
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
require 'minitest_helper'
|
2
|
-
|
3
|
-
describe Hypostasis::Document do
|
4
|
-
let(:subject) { SampleDocument.new(name: 'John', age: 21, dob: Date.today.prev_year(21)) }
|
5
|
-
|
6
|
-
before do
|
7
|
-
Hypostasis::Connection.create_namespace 'sample_docs', data_model: :document
|
8
|
-
Hypostasis::Connection.create_namespace 'indexed_docs', data_model: :document
|
9
|
-
end
|
10
|
-
|
11
|
-
after do
|
12
|
-
Hypostasis::Connection.destroy_namespace 'sample_docs'
|
13
|
-
Hypostasis::Connection.destroy_namespace 'indexed_docs'
|
14
|
-
end
|
15
|
-
|
16
|
-
it { subject.must_respond_to :name }
|
17
|
-
it { subject.must_respond_to :age }
|
18
|
-
it { subject.must_respond_to :dob }
|
19
|
-
|
20
|
-
it { subject.must_respond_to :name= }
|
21
|
-
it { subject.must_respond_to :age= }
|
22
|
-
it { subject.must_respond_to :dob= }
|
23
|
-
|
24
|
-
it { subject.name.must_equal 'John' }
|
25
|
-
it { subject.age.must_equal 21 }
|
26
|
-
it { subject.dob.must_equal Date.today.prev_year(21) }
|
27
|
-
|
28
|
-
it { subject.must_respond_to :save }
|
29
|
-
|
30
|
-
describe '#create' do
|
31
|
-
let(:subject) { SampleDocument.create(name: 'John', age: 21, dob: Date.today.prev_year(21)) }
|
32
|
-
|
33
|
-
after do
|
34
|
-
subject.destroy
|
35
|
-
end
|
36
|
-
|
37
|
-
it { subject.id.wont_be_nil }
|
38
|
-
it { database.get(document_path(subject)).must_equal 'true' }
|
39
|
-
it { database.get(field_path(subject, :name, String)).must_equal 'John' }
|
40
|
-
it { database.get(field_path(subject, :age, Fixnum)).must_equal '21' }
|
41
|
-
it { database.get(field_path(subject, :dob, Date)).must_equal Date.today.prev_year(21).to_s }
|
42
|
-
end
|
43
|
-
|
44
|
-
describe '#save' do
|
45
|
-
let(:subject) { SampleDocument.new(name: 'John', age: 21, dob: Date.today.prev_year(21)) }
|
46
|
-
|
47
|
-
before do
|
48
|
-
subject.save
|
49
|
-
end
|
50
|
-
|
51
|
-
after do
|
52
|
-
subject.destroy
|
53
|
-
end
|
54
|
-
|
55
|
-
it { subject.id.wont_be_nil }
|
56
|
-
it { database.get(document_path(subject)).must_equal 'true' }
|
57
|
-
it { database.get(field_path(subject, :name, String)).must_equal 'John' }
|
58
|
-
it { database.get(field_path(subject, :age, Fixnum)).must_equal '21' }
|
59
|
-
it { database.get(field_path(subject, :dob, Date)).must_equal Date.today.prev_year(21).to_s }
|
60
|
-
end
|
61
|
-
|
62
|
-
describe '.find' do
|
63
|
-
let(:document_id) { subject.save.id }
|
64
|
-
|
65
|
-
after do
|
66
|
-
subject.destroy
|
67
|
-
end
|
68
|
-
|
69
|
-
it { SampleDocument.find(document_id).is_a?(SampleDocument).must_equal true }
|
70
|
-
it { SampleDocument.find(document_id).id.must_equal document_id }
|
71
|
-
end
|
72
|
-
|
73
|
-
describe 'indexing' do
|
74
|
-
before do
|
75
|
-
IndexedDocument.create(name: 'John', age: 21, dob: Date.today.prev_year(21))
|
76
|
-
IndexedDocument.create(name: 'Jane', age: 21, dob: Date.today.prev_year(21))
|
77
|
-
IndexedDocument.create(name: 'John', age: 23, dob: Date.today.prev_year(23))
|
78
|
-
IndexedDocument.create(name: 'Tom', age: 20, dob: Date.today.prev_year(20))
|
79
|
-
end
|
80
|
-
|
81
|
-
it { database.get_range_start_with(index_path(IndexedDocument, :name)).size.must_equal 4 }
|
82
|
-
it { database.get_range_start_with(index_path(IndexedDocument, :age)).size.must_equal 4 }
|
83
|
-
|
84
|
-
it { database.get_range_start_with(index_path(IndexedDocument, :name, 'John')).size.must_equal 2 }
|
85
|
-
it { database.get_range_start_with(index_path(IndexedDocument, :name, 'Jane')).size.must_equal 1 }
|
86
|
-
|
87
|
-
it { database.get_range_start_with(index_path(IndexedDocument, :age, 21)).size.must_equal 2 }
|
88
|
-
end
|
89
|
-
|
90
|
-
describe '.find_where' do
|
91
|
-
before do
|
92
|
-
IndexedDocument.create(name: 'John', age: 21, dob: Date.today.prev_year(21))
|
93
|
-
IndexedDocument.create(name: 'Jane', age: 21, dob: Date.today.prev_year(21))
|
94
|
-
IndexedDocument.create(name: 'John', age: 23, dob: Date.today.prev_year(23))
|
95
|
-
IndexedDocument.create(name: 'Tom', age: 20, dob: Date.today.prev_year(20))
|
96
|
-
end
|
97
|
-
|
98
|
-
it { IndexedDocument.find_where(name: 'John').size.must_equal 2 }
|
99
|
-
it { IndexedDocument.find_where(age: 21).size.must_equal 2 }
|
100
|
-
it { IndexedDocument.find_where(name: 'Tom').size.must_equal 1 }
|
101
|
-
it { IndexedDocument.find_where(name: 'Tom').first.is_a?(IndexedDocument).must_equal true }
|
102
|
-
|
103
|
-
it { IndexedDocument.find_where(name: 'John', age: 23).size.must_equal 1 }
|
104
|
-
it { IndexedDocument.find_where(name: 'John', age: 23).first.is_a?(IndexedDocument).must_equal true }
|
105
|
-
end
|
106
|
-
end
|