dalton 0.0.1

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.
@@ -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