diametric 0.0.1 → 0.0.2
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.
- data/Gemfile +14 -3
- data/README.md +26 -1
- data/Rakefile +17 -1
- data/diametric.gemspec +8 -7
- data/lib/diametric.rb +8 -0
- data/lib/diametric/config.rb +54 -0
- data/lib/diametric/config/environment.rb +42 -0
- data/lib/diametric/entity.rb +126 -28
- data/lib/diametric/generators/active_model.rb +42 -0
- data/lib/diametric/persistence.rb +49 -0
- data/lib/diametric/persistence/common.rb +21 -0
- data/lib/diametric/persistence/peer.rb +17 -11
- data/lib/diametric/persistence/rest.rb +14 -6
- data/lib/diametric/query.rb +34 -3
- data/lib/diametric/railtie.rb +52 -0
- data/lib/diametric/version.rb +1 -1
- data/lib/tasks/create_schema.rb +27 -0
- data/lib/tasks/diametric_config.rb +45 -0
- data/spec/diametric/config_spec.rb +60 -0
- data/spec/diametric/entity_spec.rb +96 -27
- data/spec/diametric/persistence/peer_spec.rb +2 -2
- data/spec/diametric/persistence/rest_spec.rb +10 -3
- data/spec/diametric/persistence_spec.rb +59 -0
- data/spec/diametric/query_spec.rb +56 -0
- data/spec/integration_spec.rb +29 -1
- data/spec/peer_integration_spec.rb +37 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/gen_entity_class.rb +7 -0
- data/spec/support/persistence_examples.rb +33 -1
- metadata +36 -13
- data/.gitignore +0 -22
- data/Guardfile +0 -8
- data/TODO.org +0 -12
@@ -9,7 +9,7 @@ describe Diametric::Entity do
|
|
9
9
|
it { should respond_to(:from_query) }
|
10
10
|
|
11
11
|
it "should generate a schema" do
|
12
|
-
|
12
|
+
expected = [
|
13
13
|
{ :"db/id" => Person.send(:tempid, :"db.part/db"),
|
14
14
|
:"db/ident" => :"person/name",
|
15
15
|
:"db/valueType" => :"db.type/string",
|
@@ -49,8 +49,27 @@ describe Diametric::Entity do
|
|
49
49
|
:"db/valueType" => :"db.type/string",
|
50
50
|
:"db/cardinality" => :"db.cardinality/one",
|
51
51
|
:"db/fulltext" => true,
|
52
|
+
:"db.install/_attribute" => :"db.part/db" },
|
53
|
+
{ :"db/id" => Person.send(:tempid, :"db.part/db"),
|
54
|
+
:"db/ident" => :"person/middle_name",
|
55
|
+
:"db/valueType" => :"db.type/string",
|
56
|
+
:"db/cardinality" => :"db.cardinality/one",
|
57
|
+
:"db.install/_attribute" => :"db.part/db" },
|
58
|
+
{ :"db/id" => Person.send(:tempid, :"db.part/db"),
|
59
|
+
:"db/ident" => :"person/nicknames",
|
60
|
+
:"db/valueType" => :"db.type/string",
|
61
|
+
:"db/cardinality" => :"db.cardinality/many",
|
62
|
+
:"db.install/_attribute" => :"db.part/db" },
|
63
|
+
{ :"db/id" => Person.send(:tempid, :"db.part/db"),
|
64
|
+
:"db/ident" => :"person/parent",
|
65
|
+
:"db/valueType" => :"db.type/ref",
|
66
|
+
:"db/cardinality" => :"db.cardinality/many",
|
67
|
+
:"db/doc" => "A person's parent",
|
52
68
|
:"db.install/_attribute" => :"db.part/db" }
|
53
69
|
]
|
70
|
+
Person.schema.each do |s|
|
71
|
+
s.should == expected.shift
|
72
|
+
end
|
54
73
|
end
|
55
74
|
end
|
56
75
|
|
@@ -67,6 +86,7 @@ describe Diametric::Entity do
|
|
67
86
|
subject.name = "Clinton"
|
68
87
|
subject.name.should == "Clinton"
|
69
88
|
end
|
89
|
+
|
70
90
|
end
|
71
91
|
|
72
92
|
describe ".new" do
|
@@ -79,6 +99,14 @@ describe Diametric::Entity do
|
|
79
99
|
person.name.should == "Dashiell D"
|
80
100
|
person.secret_name.should == "Monito"
|
81
101
|
end
|
102
|
+
|
103
|
+
it "should defaults attributes" do
|
104
|
+
Person.new.middle_name.should == "Danger"
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should transform default arrays into sets" do
|
108
|
+
Person.new.nicknames.should == Set.new(["Buddy", "Pal"])
|
109
|
+
end
|
82
110
|
end
|
83
111
|
|
84
112
|
describe ".from_query" do
|
@@ -91,38 +119,79 @@ describe Diametric::Entity do
|
|
91
119
|
end
|
92
120
|
|
93
121
|
describe "#tx_data" do
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
122
|
+
context "for an entity with cardinality/many attributes" do
|
123
|
+
|
124
|
+
let(:entity_class) do
|
125
|
+
gen_entity_class(named = "test") do
|
126
|
+
attribute :many, String, :cardinality => :many
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "with a dbid" do
|
131
|
+
it "should generate a protraction tx for added entries" do
|
132
|
+
entity = entity_class.new(:many => %w|foo bar|)
|
133
|
+
entity.many.should == Set["foo", "bar"]
|
134
|
+
entity.dbid = 1
|
135
|
+
entity.tx_data.should == [[:"db/add", 1, :"test/many", ["foo", "bar"]]]
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should generate a retraction tx for removed entries" do
|
139
|
+
entity = entity_class.new
|
140
|
+
entity.dbid = 1
|
141
|
+
entity.instance_variable_set(:"@changed_attributes", { 'many' => Set["original", "unchanged"]})
|
142
|
+
entity.many = Set["unchanged", "new"]
|
143
|
+
entity.tx_data.should == [
|
144
|
+
[:"db/retract", 1, :"test/many", ["original"]],
|
145
|
+
[:"db/add", 1, :"test/many", ["new"]]
|
146
|
+
]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "without a db/id" do
|
151
|
+
it "should generate a protraction tx" do
|
152
|
+
entity = entity_class.new(:many => %w|foo bar|)
|
153
|
+
tx = entity.tx_data.first
|
154
|
+
tx.should =~ [:"db/add", entity.send(:tempid), :"test/many", ['foo', 'bar']]
|
155
|
+
end
|
104
156
|
end
|
105
157
|
end
|
106
158
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
159
|
+
context "for an entity with only cardinality/one attributes" do
|
160
|
+
let(:goat) { Goat.new(:name => "Beans", :birthday => Date.parse("2002-04-15"))}
|
161
|
+
|
162
|
+
describe "without a dbid" do
|
163
|
+
it "should generate a transaction with a new tempid" do
|
164
|
+
# Equivalence is currently wrong on EDN tagged values.
|
165
|
+
tx = goat.tx_data.first
|
166
|
+
tx.keys.should =~ [:"db/id", :"goat/name", :"goat/birthday"]
|
167
|
+
tx[:"db/id"].to_edn.should match(%r"#db/id \[:db.part/user \-\d+\]")
|
168
|
+
tx[:"goat/name"].should == "Beans"
|
169
|
+
tx[:"goat/birthday"].should == goat.birthday
|
170
|
+
end
|
116
171
|
end
|
117
172
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
:"
|
123
|
-
|
124
|
-
|
173
|
+
describe "with a dbid" do
|
174
|
+
it "should generate a transaction with the dbid" do
|
175
|
+
goat.dbid = 1
|
176
|
+
goat.tx_data.should == [
|
177
|
+
{ :"db/id" => 1,
|
178
|
+
:"goat/name" => "Beans",
|
179
|
+
:"goat/birthday" => goat.birthday
|
180
|
+
}
|
181
|
+
]
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should generate a transaction with only specified attributes" do
|
185
|
+
goat.dbid = 1
|
186
|
+
goat.tx_data(:name).should == [
|
187
|
+
{ :"db/id" => 1,
|
188
|
+
:"goat/name" => "Beans"
|
189
|
+
}
|
190
|
+
]
|
191
|
+
end
|
125
192
|
end
|
193
|
+
|
126
194
|
end
|
195
|
+
|
127
196
|
end
|
128
197
|
end
|
@@ -21,7 +21,7 @@ describe Diametric::Persistence::Peer, :jruby do
|
|
21
21
|
let(:db_uri) { 'datomic:mem://hello' }
|
22
22
|
|
23
23
|
it "can connect to a Datomic database" do
|
24
|
-
subject.connect(db_uri)
|
24
|
+
subject.connect(:uri => db_uri)
|
25
25
|
subject.connection.should be_a(Java::DatomicPeer::LocalConnection)
|
26
26
|
end
|
27
27
|
|
@@ -29,7 +29,7 @@ describe Diametric::Persistence::Peer, :jruby do
|
|
29
29
|
let(:model_class) { Rat }
|
30
30
|
|
31
31
|
before(:all) do
|
32
|
-
subject.connect(db_uri)
|
32
|
+
subject.connect(:uri => db_uri)
|
33
33
|
Diametric::Persistence::Peer.create_schemas
|
34
34
|
end
|
35
35
|
end
|
@@ -10,12 +10,19 @@ describe Diametric::Persistence::REST, :integration do
|
|
10
10
|
attribute :age, Integer
|
11
11
|
end
|
12
12
|
|
13
|
+
let(:connection_options) do
|
14
|
+
{
|
15
|
+
:uri => db_uri,
|
16
|
+
:storage => storage,
|
17
|
+
:database => dbname
|
18
|
+
}
|
19
|
+
end
|
13
20
|
let(:db_uri) { ENV['DATOMIC_URI'] || 'http://localhost:9000' }
|
14
|
-
let(:storage) { ENV['DATOMIC_STORAGE'] || '
|
21
|
+
let(:storage) { ENV['DATOMIC_STORAGE'] || 'free' }
|
15
22
|
let(:dbname) { ENV['DATOMIC_NAME'] || "test-#{Time.now.to_i}" }
|
16
23
|
|
17
24
|
it "can connect to a Datomic database" do
|
18
|
-
subject.connect(
|
25
|
+
subject.connect(connection_options)
|
19
26
|
subject.connection.should be_a(Datomic::Client)
|
20
27
|
end
|
21
28
|
|
@@ -23,7 +30,7 @@ describe Diametric::Persistence::REST, :integration do
|
|
23
30
|
let(:model_class) { Mouse }
|
24
31
|
|
25
32
|
before(:all) do
|
26
|
-
subject.connect(
|
33
|
+
subject.connect(connection_options)
|
27
34
|
Diametric::Persistence::REST.create_schemas
|
28
35
|
end
|
29
36
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Prevent CRuby from blowing up
|
4
|
+
module Diametric
|
5
|
+
module Persistence
|
6
|
+
module Peer
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Diametric::Persistence do
|
12
|
+
let(:settings) { { :uri => 'http://example.com:9000' } }
|
13
|
+
let(:rest_class) { Diametric::Persistence::REST }
|
14
|
+
|
15
|
+
describe ".establish_base_connection" do
|
16
|
+
before { Diametric::Persistence.stub(:persistence_class => rest_class) }
|
17
|
+
|
18
|
+
it "connects" do
|
19
|
+
settings = { :uri => 'http://example.com:9000' }
|
20
|
+
rest_class.should_receive(:connect).with(settings)
|
21
|
+
|
22
|
+
Diametric::Persistence.establish_base_connection(settings)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "records the base persistence class" do
|
26
|
+
rest_class.stub(:connect)
|
27
|
+
Diametric::Persistence.establish_base_connection(settings)
|
28
|
+
Diametric::Persistence.instance_variable_get(:"@_persistence_class").should == rest_class
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ".included" do
|
33
|
+
before { Diametric::Persistence.stub(:persistence_class => rest_class) }
|
34
|
+
|
35
|
+
it "includes the recorded base persistence class" do
|
36
|
+
class FooEntity; end
|
37
|
+
FooEntity.should_receive(:send).with(:include, rest_class)
|
38
|
+
Diametric::Persistence.instance_variable_set(:"@_persistence_class", rest_class)
|
39
|
+
|
40
|
+
class FooEntity
|
41
|
+
include Diametric::Persistence
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ".persistence_class" do
|
47
|
+
it "returns REST for REST-like options" do
|
48
|
+
klass = Diametric::Persistence.persistence_class("http://localhost:9000")
|
49
|
+
klass.should be Diametric::Persistence::REST
|
50
|
+
Diametric::Persistence.rest?.should == true
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns Peer for Peer-like options", :jruby do
|
54
|
+
klass = Diametric::Persistence.persistence_class("datomic:free://localhost:9000/sample")
|
55
|
+
klass.should be Diametric::Persistence::Peer
|
56
|
+
Diametric::Persistence.peer?.should == true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -8,6 +8,8 @@ describe Diametric::Query do
|
|
8
8
|
query.where(:age => 2)
|
9
9
|
query.conditions.should be_empty
|
10
10
|
end
|
11
|
+
|
12
|
+
it "raises when non-searchable conditions (id?) are passed"
|
11
13
|
end
|
12
14
|
|
13
15
|
describe "#filter" do
|
@@ -17,6 +19,60 @@ describe Diametric::Query do
|
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
22
|
+
describe "#each" do
|
23
|
+
it "collapses cardinality/many attribute results" do
|
24
|
+
model = gen_entity_class :person do
|
25
|
+
attribute :name, String
|
26
|
+
attribute :likes, String, :cardinality => :many
|
27
|
+
end
|
28
|
+
model.stub(:q => [[1, "Stu", "chocolate"], [1, "Stu", "vanilla"]])
|
29
|
+
|
30
|
+
model.should_receive(:from_query).with([1, "Stu", ["chocolate", "vanilla"]])
|
31
|
+
Diametric::Query.new(model).each {|x| x}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#collapse_results" do
|
36
|
+
let (:model) do
|
37
|
+
gen_entity_class do
|
38
|
+
attribute :name, String
|
39
|
+
attribute :likes, String, :cardinality => :many
|
40
|
+
end
|
41
|
+
end
|
42
|
+
let(:query) { Diametric::Query.new(model) }
|
43
|
+
|
44
|
+
context "with multiple results per entity" do
|
45
|
+
it "collapses cardinality/many attributes into lists" do
|
46
|
+
results = [[1, "Stu", "chocolate"], [1, "Stu", "vanilla"], [1, "Stu", "raspberry"]]
|
47
|
+
collapsed = query.send(:collapse_results, results).first
|
48
|
+
collapsed.should == [1, "Stu", ["chocolate", "vanilla", "raspberry"]]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with single result per entity" do
|
53
|
+
it "collapses cardinality/many attributes into lists" do
|
54
|
+
results = [[1, "Stu", "chocolate"]]
|
55
|
+
collapsed = query.send(:collapse_results, results).first
|
56
|
+
collapsed.should == [1, "Stu", ["chocolate"]]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#all" do
|
62
|
+
it "returns set for values of cardinality/many" do
|
63
|
+
model = gen_entity_class :person do
|
64
|
+
attribute :name, String
|
65
|
+
attribute :likes, String, :cardinality => :many
|
66
|
+
end
|
67
|
+
model.stub(:q => [[1, "Stu", "chocolate"], [1, "Stu", "vanilla"]])
|
68
|
+
Diametric::Query.new(model).all.each do |e|
|
69
|
+
e.name.should == "Stu"
|
70
|
+
e.likes.class.should == Set
|
71
|
+
e.likes.should include "chocolate"
|
72
|
+
e.likes.should include "vanilla"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
20
76
|
|
21
77
|
describe "#data" do
|
22
78
|
it "should generate a query given no conditions or filters" do
|
data/spec/integration_spec.rb
CHANGED
@@ -7,7 +7,7 @@ require 'datomic/client'
|
|
7
7
|
describe Diametric::Entity, :integration => true do
|
8
8
|
before(:all) do
|
9
9
|
@datomic_uri = ENV['DATOMIC_URI'] || 'http://localhost:9000'
|
10
|
-
@storage = ENV['DATOMIC_STORAGE'] || '
|
10
|
+
@storage = ENV['DATOMIC_STORAGE'] || 'free'
|
11
11
|
@dbname = ENV['DATOMIC_NAME'] || "test-#{Time.now.to_i}"
|
12
12
|
@client = Datomic::Client.new @datomic_uri, @storage
|
13
13
|
@client.create_database(@dbname)
|
@@ -64,4 +64,32 @@ describe Diametric::Entity, :integration => true do
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
describe "with persistence module" do
|
68
|
+
before(:all) do
|
69
|
+
Robin.create_schema
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:query) { Diametric::Query.new(Robin) }
|
73
|
+
it "can update entity" do
|
74
|
+
robin = Robin.new(:name => "Mary", :age => 2)
|
75
|
+
robin.save
|
76
|
+
robin.update(:age => 3)
|
77
|
+
robin.name.should == "Mary"
|
78
|
+
robin.age.should == 3
|
79
|
+
end
|
80
|
+
it "should search upadated attributes" do
|
81
|
+
robin = query.where(:name => "Mary").first
|
82
|
+
robin.name.should == "Mary"
|
83
|
+
robin.age.should == 3
|
84
|
+
end
|
85
|
+
it "can destroy entity" do
|
86
|
+
robin = Robin.new(:name => "Mary", :age => 2)
|
87
|
+
robin.save
|
88
|
+
number_of_robins = Robin.all.size
|
89
|
+
number_of_robins.should >= 1
|
90
|
+
robin.destroy
|
91
|
+
Robin.all.size.should == (number_of_robins -1)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
67
95
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'datomic/client'
|
3
|
+
|
4
|
+
# Datomic's `rest` needs to run for these tests to pass:
|
5
|
+
# bin/rest 9000 test datomic:mem://
|
6
|
+
|
7
|
+
describe Diametric::Entity, :integration => true, :jruby => true do
|
8
|
+
before(:all) do
|
9
|
+
@datomic_uri = ENV['DATOMIC_URI'] || 'datomic:mem://animals'
|
10
|
+
Diametric::Persistence.establish_base_connection({:uri => @datomic_uri})
|
11
|
+
Penguin.create_schema
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:query) { Diametric::Query.new(Penguin) }
|
15
|
+
it "should update entity" do
|
16
|
+
penguin = Penguin.new(:name => "Mary", :age => 2)
|
17
|
+
penguin.save
|
18
|
+
penguin.update(:age => 3)
|
19
|
+
penguin.name.should == "Mary"
|
20
|
+
penguin.age.should == 3
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should search upadated attributes" do
|
24
|
+
penguin = query.where(:name => "Mary").first
|
25
|
+
penguin.name.should == "Mary"
|
26
|
+
penguin.age.should == 3
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should destroy entity" do
|
30
|
+
penguin = Penguin.new(:name => "Mary", :age => 2)
|
31
|
+
penguin.save
|
32
|
+
number_of_penguins = Penguin.all.size
|
33
|
+
number_of_penguins.should >= 1
|
34
|
+
penguin.destroy
|
35
|
+
Penguin.all.size.should == (number_of_penguins -1)
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -43,6 +43,9 @@ class Person
|
|
43
43
|
attribute :ssn, String, :unique => :value
|
44
44
|
attribute :secret_name, String, :unique => :identity
|
45
45
|
attribute :bio, String, :fulltext => true
|
46
|
+
attribute :middle_name, String, :default => "Danger"
|
47
|
+
attribute :nicknames, String, :cardinality => :many, :default => ["Buddy", "Pal"]
|
48
|
+
attribute :parent, Ref, :cardinality => :many, :doc => "A person's parent"
|
46
49
|
end
|
47
50
|
|
48
51
|
class Goat
|
@@ -51,3 +54,23 @@ class Goat
|
|
51
54
|
attribute :name, String
|
52
55
|
attribute :birthday, DateTime
|
53
56
|
end
|
57
|
+
|
58
|
+
require 'diametric/persistence/rest'
|
59
|
+
class Robin
|
60
|
+
include Diametric::Entity
|
61
|
+
include Diametric::Persistence::REST
|
62
|
+
|
63
|
+
attribute :name, String
|
64
|
+
attribute :age, Integer
|
65
|
+
end
|
66
|
+
|
67
|
+
if RUBY_ENGINE == "jruby"
|
68
|
+
require 'diametric/persistence/peer'
|
69
|
+
class Penguin
|
70
|
+
include Diametric::Entity
|
71
|
+
include Diametric::Persistence::Peer
|
72
|
+
|
73
|
+
attribute :name, String
|
74
|
+
attribute :age, Integer
|
75
|
+
end
|
76
|
+
end
|