dalton 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ java_import "clojure.lang.Keyword"
2
+ java_import "clojure.lang.PersistentArrayMap"
3
+ java_import "clojure.lang.RT"
4
+
5
+ module Dalton
6
+ module Utility
7
+
8
+ module_function
9
+
10
+ def run_clojure_function(namespaced_function, *arguments)
11
+ namespace, function_name = namespaced_function.to_s.split('/', 2)
12
+ namespace && function_name or
13
+ raise ArgumentError, "Namespaced function required. Got: #{namespaced_function.inspect}"
14
+ RT.var(namespace, function_name).fn.invoke(*arguments)
15
+ end
16
+
17
+ def run_database_function(db, function_ident, *arguments)
18
+ function_entity = db.entity(Translation.from_ruby(function_ident))
19
+ function_entity.fn.invoke(*arguments)
20
+ end
21
+
22
+ def require_clojure(namespace)
23
+ require_function = RT.var("clojure.core", "require").fn
24
+ require_function.invoke(Java::ClojureLang::Symbol.intern(namespace))
25
+ end
26
+
27
+ require_clojure('datomic.function')
28
+ require_clojure('datomic.db')
29
+
30
+ def read_edn(edn)
31
+ readers = PersistentArrayMap.create({Keyword.intern('readers') => PersistentArrayMap.create({
32
+ Java::ClojureLang::Symbol.intern('db/fn') => RT.var('datomic.function', 'construct'),
33
+ Java::ClojureLang::Symbol.intern('db/id') => RT.var('datomic.db', 'id-literal')
34
+ })})
35
+
36
+ run_clojure_function("clojure.edn/read-string", readers, edn)
37
+ end
38
+
39
+ def rubify_edn(edn)
40
+ Translation.from_clj(read_edn(edn))
41
+ end
42
+
43
+ def clojure_equal?(one, other)
44
+ run_clojure_function('clojure.core/=', one, other)
45
+ end
46
+
47
+ def to_edn(clojure_data)
48
+ run_clojure_function('clojure.core/pr-str', clojure_data)
49
+ end
50
+
51
+ def sym(s)
52
+ s = s.to_s if s.is_a? Symbol
53
+ Java::ClojureLang::Symbol.intern(s)
54
+ end
55
+
56
+ def gensym(s)
57
+ run_clojure_function('clojure.core/gensym', sym(s))
58
+ end
59
+
60
+ def tempid(partition)
61
+ Peer.tempid(kw(partition))
62
+ end
63
+
64
+ def gensym(s)
65
+ run_clojure_function('clojure.core/gensym', sym(s))
66
+ end
67
+
68
+ def kw(k)
69
+ k = k.to_s if k.is_a? Symbol
70
+ k = k[1..-1] if k.start_with? ':'
71
+ Java::ClojureLang::Keyword.intern(k)
72
+ end
73
+
74
+ def list(*items)
75
+ Dalton::Utility.run_clojure_function("clojure.core/list*", items)
76
+ end
77
+
78
+ def with_meta(value, meta)
79
+ Dalton::Utility.run_clojure_function("clojure.core/with-meta", value, meta)
80
+ end
81
+
82
+ def meta(value)
83
+ Dalton::Utility.run_clojure_function("clojure.core/meta", value)
84
+ end
85
+
86
+ def tag(value, tag)
87
+ with_meta(value, Dalton::Translation.from_ruby(tag: tag))
88
+ end
89
+ end
90
+ end
91
+
@@ -0,0 +1,3 @@
1
+ module Dalton
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dalton::Connection do
4
+ include DatomicContext
5
+
6
+ let(:attribute) { :'db/doc' }
7
+ let(:value) { 'This is a test entity.' }
8
+
9
+ let!(:transaction_result) { conn.transact([{:'db/id' => Dalton::Connection.tempid, attribute => value}]) }
10
+
11
+ let(:entity_id) { transaction_result.tempids.values.first }
12
+
13
+ describe 'data storage, query, and retrieval' do
14
+
15
+ let(:query) { [:find, :'?e', :where, [:'?e', attribute, value]] }
16
+ let(:edn_query) { '[:find ?e :where [?e :db/doc "This is a test entity."]]' }
17
+
18
+ describe '#transact(datoms)' do
19
+
20
+ it 'stores data' do
21
+ expect(db.q(query).size).to eq(1)
22
+ end
23
+
24
+ it 'returns a transaction result' do
25
+ expect(transaction_result.db_before).to be_a(Dalton::Database)
26
+ expect(transaction_result.db_after).to be_a(Dalton::Database)
27
+ expect(transaction_result.tx_data).to be_a(Array)
28
+ expect(transaction_result.tempids).to be_a(Hash)
29
+ end
30
+
31
+ it 'refreshes the database' do
32
+ expect(conn.db).to eq(transaction_result.db_after)
33
+ end
34
+
35
+ describe 'errors' do
36
+ before do
37
+ # create an attribute with :db.unique/value
38
+ conn.transact([{:'db/id' => Dalton::Connection.tempid(:'db.part/db'),
39
+ :'db/ident' => :'user.test/unique-attr',
40
+ :'db/cardinality' => :'db.cardinality/one',
41
+ :'db/unique' => :'db.unique/value',
42
+ :'db/valueType' => :'db.type/string',
43
+ :'db.install/_attribute' => :'db.part/db'}])
44
+
45
+ tempid = Dalton::Connection.tempid(:'db.part/user')
46
+ conn.transact([[:'db/add', tempid, :'user.test/unique-attr', 'duplicate-value']])
47
+ end
48
+
49
+ describe 'in uniqueness' do
50
+ let(:error) {
51
+ err = nil
52
+ tempid = Dalton::Connection.tempid(:'db.part/user')
53
+ begin
54
+ conn.transact([[:'db/add', tempid, :'user.test/unique-attr', 'duplicate-value']])
55
+ rescue Dalton::UniqueConflict => e
56
+ err = e
57
+ end
58
+ err
59
+ }
60
+
61
+ it 'contains useful information' do
62
+ expect(error).to be_a(Dalton::UniqueConflict)
63
+ expect(error.attribute).to be(:'user.test/unique-attr')
64
+ expect(error.value).to eql('duplicate-value')
65
+ expect(error.existing_id).to be > 0
66
+ end
67
+ end
68
+
69
+ describe 'in type' do
70
+ let(:error) do
71
+ err = nil
72
+ tempid = Dalton::Connection.tempid(:'db.part/user')
73
+ begin
74
+ conn.transact([[:'db/add', tempid, :'user.test/unique-attr', 5]])
75
+ rescue Dalton::TypeError => e
76
+ err = e
77
+ end
78
+ err
79
+ end
80
+
81
+ it 'contains useful information' do
82
+ expect(error).to be_a(Dalton::TypeError)
83
+ expect(error.attribute).to be(:'user.test/unique-attr')
84
+ expect(error.value).to eql('5') # TODO: this sucks, but they're indistinguishable!
85
+ expect(error.type).to be(:string)
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#retract(entity)' do
92
+ shared_examples_for "a retraction" do
93
+ it 'retracts the entity' do
94
+ expect(conn.db.q(query).size).to eq(0)
95
+ end
96
+ end
97
+
98
+ context "when supplied an id" do
99
+ before do
100
+ conn.retract(entity_id)
101
+ end
102
+
103
+ it_behaves_like 'a retraction'
104
+ end
105
+
106
+ context "when supplied an entity" do
107
+ before do
108
+ conn.retract(conn.db.entity(entity_id))
109
+ end
110
+
111
+ it_behaves_like 'a retraction'
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '#tempid?' do
117
+ subject {Dalton::Connection.tempid?(id)}
118
+
119
+ context 'with a temp id' do
120
+ let (:id) {Dalton::Connection.tempid}
121
+
122
+ it { should be true }
123
+ end
124
+
125
+ context 'with a real id' do
126
+ let (:id) {entity_id}
127
+
128
+ it { should be false }
129
+ end
130
+ end
131
+ end
132
+
133
+
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dalton::Database do
4
+ include DatomicContext
5
+
6
+ describe 'data storage, query, and retrieval' do
7
+
8
+ let(:attribute) { :'db/doc' }
9
+ let(:value) { 'This is a test entity.' }
10
+
11
+ let!(:transaction_result) { conn.transact([{:'db/id' => Dalton::Connection.tempid, attribute => value}]) }
12
+
13
+ let(:entity_id) { transaction_result.tempids.values.first }
14
+
15
+ let(:query) { [:find, :'?e', :where, [:'?e', attribute, value]] }
16
+ let(:edn_query) { '[:find ?e :where [?e :db/doc "This is a test entity."]]' }
17
+
18
+ describe '#q(query)' do
19
+
20
+ shared_examples_for 'a query' do
21
+ it 'runs the query' do
22
+ expect(query_result).to be_a(Set)
23
+ expect(query_result.size).to eq(1)
24
+
25
+ entity_id = query_result.first.first
26
+ entity = db.entity(entity_id)
27
+
28
+ expect(entity[attribute]).to eq(value)
29
+ end
30
+ end
31
+
32
+ context "when the query is a ruby data structure" do
33
+ let(:query_result) { db.q(query) }
34
+
35
+ it_behaves_like 'a query'
36
+ end
37
+
38
+ context "when the query is an EDN string" do
39
+ let(:query_result) { db.q(edn_query) }
40
+
41
+ it_behaves_like 'a query'
42
+ end
43
+ end
44
+
45
+ describe '#entity(entity_id)' do
46
+ let(:entity) { db.entity(entity_id) }
47
+
48
+ it 'fetches an entity from the database' do
49
+ expect(entity[attribute]).to eq(value)
50
+ end
51
+ end
52
+
53
+ describe '#retrieve(query)' do
54
+ let(:results) { db.retrieve(query) }
55
+ let(:entity) { results.first }
56
+
57
+ it 'runs a query and retrieves entities' do
58
+ expect(results.to_a.size).to eq(1)
59
+ expect(entity[attribute]).to eq(value)
60
+ end
61
+
62
+ it 'returns a lazy enumerator' do
63
+ expect(results).to be_a(Enumerator::Lazy)
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ describe "#attribute" do
70
+ let(:attribute) { db.attribute(:'db/doc') }
71
+ it "retrieves an attribute definition" do
72
+ expect(attribute.hasAVET).to eq(false)
73
+ expect(attribute.hasFulltext).to eq(true)
74
+ expect(attribute.hasNoHistory).to eq(false)
75
+ expect(attribute.id).to be_a(Fixnum)
76
+ expect(attribute.ident).to eq(:'db/doc')
77
+ expect(attribute.isComponent).to eq(false)
78
+ expect(attribute.isIndexed).to eq(false)
79
+ expect(attribute.unique).to eq(nil)
80
+ expect(attribute.valueType).to eq(:'db.type/string')
81
+ end
82
+ end
83
+ end
84
+
85
+
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dalton::Datomization do
4
+ include DatomicContext
5
+
6
+ before do
7
+ conn.set_up_datomizer
8
+
9
+ conn.transact([{:'db/id' => Dalton::Connection.tempid(':db.part/db'),
10
+ :'db/ident' => :'test/map',
11
+ :'db/valueType' => :'db.type/ref',
12
+ :'db/cardinality' => :'db.cardinality/many',
13
+ :'db/doc' => "A reference attribute for testing datomization",
14
+ :'db/isComponent' => true,
15
+ :'dmzr.ref/type' => :'dmzr.type/map',
16
+ :'db.install/_attribute' => :'db.part/db',
17
+ },
18
+ {:'db/id' => Dalton::Connection.tempid(':db.part/db'),
19
+ :'db/ident' => :'test/vector',
20
+ :'db/valueType' => :'db.type/ref',
21
+ :'db/cardinality' => :'db.cardinality/many',
22
+ :'db/doc' => "A reference attribute for testing datomization",
23
+ :'db/isComponent' => true,
24
+ :'dmzr.ref/type' => :'dmzr.type/vector',
25
+ :'db.install/_attribute' => :'db.part/db',
26
+ },
27
+ {:'db/id' => Dalton::Connection.tempid(':db.part/db'),
28
+ :'db/ident' => :'test/edn',
29
+ :'db/valueType' => :'db.type/string',
30
+ :'db/cardinality' => :'db.cardinality/one',
31
+ :'db/doc' => "An EDN string field for edenization testing.",
32
+ :'dmzr.ref/type' => :'dmzr.type/edn',
33
+ :'db.install/_attribute' => :'db.part/db'}])
34
+ end
35
+
36
+ shared_examples_for "it round trips via datomization" do |attribute|
37
+ it "should store and retrieve the value" do
38
+ id = Dalton::Connection.tempid
39
+ original_data = {:'db/id' => id, attribute => value}
40
+ real_id = conn.datomize(original_data)
41
+ round_tripped_data = conn.db.undatomize(real_id)
42
+ expect(round_tripped_data[attribute]).to eq(value)
43
+ end
44
+ end
45
+
46
+ context "with an empty map" do
47
+ let(:value) { {} }
48
+
49
+ it_should_behave_like "it round trips via datomization", :'test/map'
50
+ it_should_behave_like "it round trips via datomization", :'test/edn'
51
+ end
52
+
53
+ context "with a single map element" do
54
+ let(:value) { {:a => 'grue'} }
55
+
56
+ it_should_behave_like "it round trips via datomization", :'test/map'
57
+ it_should_behave_like "it round trips via datomization", :'test/edn'
58
+ end
59
+
60
+ context "with multiple map elements" do
61
+ let(:value) { {:a => 'grue', :b => 'wumpus'} }
62
+
63
+ it_should_behave_like "it round trips via datomization", :'test/map'
64
+ it_should_behave_like "it round trips via datomization", :'test/edn'
65
+ end
66
+
67
+ context "with a nested map" do
68
+ let(:value) { {:a => {:b => 'fnord'}} }
69
+
70
+ it_should_behave_like "it round trips via datomization", :'test/map'
71
+ it_should_behave_like "it round trips via datomization", :'test/edn'
72
+ end
73
+
74
+ context "with an empty array" do
75
+ let(:value) { [] }
76
+
77
+ it_should_behave_like "it round trips via datomization", :'test/vector'
78
+ it_should_behave_like "it round trips via datomization", :'test/edn'
79
+ end
80
+
81
+ context "with array values" do
82
+ let(:value) { ['a', 'b', 'c'] }
83
+
84
+ it_should_behave_like "it round trips via datomization", :'test/vector'
85
+ it_should_behave_like "it round trips via datomization", :'test/edn'
86
+ end
87
+
88
+ context "with nested data structure values" do
89
+ let(:value) { [0, 1, 'a', 'b', 'c', [{:a => {:b => 'fnord'}}, 'x', 'y', 'z']] }
90
+
91
+ it_should_behave_like "it round trips via datomization", :'test/vector'
92
+ it_should_behave_like "it round trips via datomization", :'test/edn'
93
+ end
94
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dalton::Entity do
4
+ include DatomicContext
5
+
6
+ before do
7
+ conn.transact([{:'db/id' => Dalton::Connection.tempid(':db.part/db'),
8
+ :'db/ident' => :'test/stuff',
9
+ :'db/valueType' => :'db.type/ref',
10
+ :'db/cardinality' => :'db.cardinality/one',
11
+ :'db/doc' => 'A reference attribute for testing datomization',
12
+ :'db/isComponent' => true,
13
+ :'db.install/_attribute' => :'db.part/db',
14
+ }])
15
+ end
16
+
17
+ describe '#to_h' do
18
+ let!(:transaction_result) {
19
+ conn.transact([{:'db/id' => Dalton::Connection.tempid,
20
+ :'db/doc' => 'foo',
21
+ :'test/stuff' => {:'db/id' => Dalton::Connection.tempid,
22
+ :'db/doc' => 'bar'}}
23
+ ])
24
+
25
+ }
26
+ let(:tempids) { transaction_result.tempids.values.sort }
27
+
28
+ let(:entity) { conn.db.retrieve([:find, :'?e', :where, [:'?e', :'db/doc', 'foo']]).first }
29
+ subject { entity.to_h }
30
+
31
+ it 'should translate the entity to a hash' do
32
+ expect(subject).to eq({:'db/doc' => 'foo',
33
+ :'test/stuff' =>
34
+ {:'db/doc' => 'bar'}})
35
+ end
36
+ end
37
+ end