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