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