diametric 0.1.2-java → 0.1.3-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -3
- data/Jarfile +13 -6
- data/LICENSE.txt +2 -2
- data/README.md +57 -26
- data/Rakefile +1 -0
- data/bin/datomic-rest +1 -1
- data/bin/download-datomic +1 -1
- data/datomic_version.yml +2 -2
- data/diametric-java.gemspec +11 -10
- data/ext/diametric/DiametricCollection.java +45 -38
- data/ext/diametric/DiametricConnection.java +16 -15
- data/ext/diametric/DiametricDatabase.java +9 -8
- data/ext/diametric/DiametricEntity.java +104 -21
- data/ext/diametric/DiametricObject.java +12 -1
- data/ext/diametric/DiametricPeer.java +52 -31
- data/ext/diametric/DiametricService.java +2 -0
- data/ext/diametric/DiametricSet.java +11 -4
- data/ext/diametric/DiametricUUID.java +8 -1
- data/ext/diametric/DiametricUtils.java +90 -62
- data/lib/diametric.rb +1 -0
- data/lib/diametric/associations/collection.rb +103 -0
- data/lib/diametric/entity.rb +166 -103
- data/lib/diametric/persistence/common.rb +0 -44
- data/lib/diametric/persistence/peer.rb +53 -2
- data/lib/diametric/persistence/rest.rb +27 -1
- data/lib/diametric/query.rb +49 -31
- data/lib/diametric/rest_service.rb +8 -9
- data/lib/diametric/service_base.rb +7 -7
- data/lib/diametric/transactor.rb +6 -5
- data/lib/diametric/version.rb +1 -1
- data/lib/diametric_service.jar +0 -0
- data/spec/config/free-transactor-template.properties +6 -6
- data/spec/developer_query_spec.rb +17 -6
- data/spec/diametric/entity_spec.rb +62 -139
- data/spec/diametric/peer_api_spec.rb +23 -23
- data/spec/diametric/persistence/peer_spec.rb +73 -11
- data/spec/diametric/persistence/rest_spec.rb +108 -16
- data/spec/diametric/query_spec.rb +3 -3
- data/spec/diametric/rest_service_spec.rb +4 -4
- data/spec/diametric/schema_spec.rb +526 -0
- data/spec/diametric/transactor_spec.rb +5 -6
- data/spec/integration_spec.rb +7 -7
- data/spec/peer_integration_spec.rb +25 -1
- data/spec/peer_seattle_spec.rb +1 -2
- data/spec/spec_helper.rb +31 -4
- data/spec/support/cardinarity_many_example.rb +37 -0
- data/spec/support/entities.rb +127 -0
- data/spec/support/has_a_example.rb +31 -0
- data/spec/support/has_many_example.rb +79 -0
- data/spec/support/persistence_examples.rb +13 -5
- data/spec/support/various_types_examples.rb +163 -0
- data/spec/test_version_file.yml +2 -2
- metadata +147 -75
@@ -31,6 +31,13 @@ public class DiametricSet extends RubyObject {
|
|
31
31
|
private Integer count = null; // unable to count the vector size that exceeds Integer
|
32
32
|
private DiametricCommon common = null;
|
33
33
|
|
34
|
+
static IRubyObject getDiametricSet(ThreadContext context, Set value) {
|
35
|
+
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::Set");
|
36
|
+
DiametricSet diametric_set = (DiametricSet)clazz.allocate();
|
37
|
+
diametric_set.init(value);
|
38
|
+
return diametric_set;
|
39
|
+
}
|
40
|
+
|
34
41
|
public DiametricSet(Ruby runtime, RubyClass klazz) {
|
35
42
|
super(runtime, klazz);
|
36
43
|
}
|
@@ -242,9 +249,9 @@ public class DiametricSet extends RubyObject {
|
|
242
249
|
Var var = DiametricService.getFn("clojure.set", "union");
|
243
250
|
if (set instanceof HashSet) {
|
244
251
|
PersistentHashSet value = convertHashSetToPersistentHashSet(set);
|
245
|
-
return
|
252
|
+
return DiametricSet.getDiametricSet(context, (Set)var.invoke(value, other));
|
246
253
|
} else {
|
247
|
-
return
|
254
|
+
return DiametricSet.getDiametricSet(context, (Set)var.invoke(set, other));
|
248
255
|
}
|
249
256
|
} catch (Throwable t) {
|
250
257
|
throw context.getRuntime().newRuntimeError(t.getMessage());
|
@@ -259,9 +266,9 @@ public class DiametricSet extends RubyObject {
|
|
259
266
|
Var var = DiametricService.getFn("clojure.set", "difference");
|
260
267
|
if (set instanceof HashSet) {
|
261
268
|
PersistentHashSet value = convertHashSetToPersistentHashSet(set);
|
262
|
-
return
|
269
|
+
return DiametricSet.getDiametricSet(context, (Set)var.invoke(value, other));
|
263
270
|
} else {
|
264
|
-
return
|
271
|
+
return DiametricSet.getDiametricSet(context, (Set)var.invoke(set, other));
|
265
272
|
}
|
266
273
|
} catch (Throwable t) {
|
267
274
|
throw context.getRuntime().newRuntimeError(t.getMessage());
|
@@ -17,6 +17,13 @@ public class DiametricUUID extends RubyObject {
|
|
17
17
|
private static final long serialVersionUID = 2083281771243513904L;
|
18
18
|
private java.util.UUID java_uuid = null;
|
19
19
|
|
20
|
+
static IRubyObject getDiametricUUID(ThreadContext context, java.util.UUID value) {
|
21
|
+
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::UUID");
|
22
|
+
DiametricUUID diametric_uuid = (DiametricUUID)clazz.allocate();
|
23
|
+
diametric_uuid.init((java.util.UUID)value);
|
24
|
+
return diametric_uuid;
|
25
|
+
}
|
26
|
+
|
20
27
|
public DiametricUUID(Ruby runtime, RubyClass klazz) {
|
21
28
|
super(runtime, klazz);
|
22
29
|
}
|
@@ -34,7 +41,7 @@ public class DiametricUUID extends RubyObject {
|
|
34
41
|
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::UUID");
|
35
42
|
DiametricUUID diametric_uuid = (DiametricUUID)clazz.allocate();
|
36
43
|
try {
|
37
|
-
java.util.UUID java_uuid = (UUID)
|
44
|
+
java.util.UUID java_uuid = (UUID) DiametricService.getFn("datomic.api", "squuid").invoke();
|
38
45
|
diametric_uuid.init(java_uuid);
|
39
46
|
return diametric_uuid;
|
40
47
|
} catch (Throwable t) {
|
@@ -6,10 +6,7 @@ import java.io.Reader;
|
|
6
6
|
import java.util.ArrayList;
|
7
7
|
import java.util.Collections;
|
8
8
|
import java.util.Date;
|
9
|
-
import java.util.HashMap;
|
10
|
-
import java.util.HashSet;
|
11
9
|
import java.util.List;
|
12
|
-
import java.util.Map;
|
13
10
|
import java.util.Set;
|
14
11
|
import java.util.UUID;
|
15
12
|
|
@@ -23,6 +20,7 @@ import org.jruby.RubyFloat;
|
|
23
20
|
import org.jruby.RubyHash;
|
24
21
|
import org.jruby.RubyNil;
|
25
22
|
import org.jruby.RubyString;
|
23
|
+
import org.jruby.RubySymbol;
|
26
24
|
import org.jruby.RubyTime;
|
27
25
|
import org.jruby.anno.JRubyMethod;
|
28
26
|
import org.jruby.anno.JRubyModule;
|
@@ -31,11 +29,14 @@ import org.jruby.javasupport.util.RuntimeHelpers;
|
|
31
29
|
import org.jruby.runtime.ThreadContext;
|
32
30
|
import org.jruby.runtime.builtin.IRubyObject;
|
33
31
|
|
32
|
+
import clojure.lang.APersistentMap;
|
34
33
|
import clojure.lang.APersistentVector;
|
35
34
|
import clojure.lang.IPersistentSet;
|
35
|
+
import clojure.lang.Keyword;
|
36
36
|
import clojure.lang.LazySeq;
|
37
37
|
import clojure.lang.PersistentHashSet;
|
38
38
|
import clojure.lang.PersistentVector;
|
39
|
+
import clojure.lang.Var;
|
39
40
|
import datomic.Util;
|
40
41
|
|
41
42
|
@JRubyModule(name="Diametric::Persistence::Utils")
|
@@ -67,6 +68,24 @@ public class DiametricUtils {
|
|
67
68
|
}
|
68
69
|
}
|
69
70
|
|
71
|
+
@JRubyMethod(meta=true)
|
72
|
+
public static IRubyObject read_string(ThreadContext context, IRubyObject klazz, IRubyObject arg) {
|
73
|
+
if (!(arg instanceof RubyString)) {
|
74
|
+
throw context.getRuntime().newArgumentError("Argument should be string");
|
75
|
+
}
|
76
|
+
RubyString ruby_string = (RubyString)arg;
|
77
|
+
try {
|
78
|
+
Var reader = DiametricService.getFn("clojure.core", "read-string");
|
79
|
+
Object value = reader.invoke((String)ruby_string.asJavaString());
|
80
|
+
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::Object");
|
81
|
+
DiametricObject diametric_object = (DiametricObject)clazz.allocate();
|
82
|
+
diametric_object.update(value);
|
83
|
+
return diametric_object;
|
84
|
+
} catch (Exception e) {
|
85
|
+
throw context.getRuntime().newRuntimeError(e.getMessage());
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
70
89
|
static String rubyStringToJava(IRubyObject arg) {
|
71
90
|
if (arg instanceof RubyString) {
|
72
91
|
// TODO probably, we need to specify encoding.
|
@@ -80,11 +99,7 @@ public class DiametricUtils {
|
|
80
99
|
if (value instanceof RubyNil) return null;
|
81
100
|
if (value instanceof RubyString) {
|
82
101
|
String str = (String)((RubyString)value).toJava(String.class);
|
83
|
-
|
84
|
-
return (Object)UUID.fromString(str);
|
85
|
-
} catch (IllegalArgumentException e) {
|
86
|
-
return (Object)str;
|
87
|
-
}
|
102
|
+
return getStringOrUUID(str);
|
88
103
|
}
|
89
104
|
if (value instanceof RubyBoolean) return (Object)((RubyBoolean)value).toJava(Boolean.class);
|
90
105
|
if (value instanceof RubyFixnum) return (Object)((RubyFixnum)value).toJava(Long.class);
|
@@ -99,6 +114,16 @@ public class DiametricUtils {
|
|
99
114
|
RubyTime tmvalue = (RubyTime)value;
|
100
115
|
return (Object)tmvalue.getJavaDate();
|
101
116
|
}
|
117
|
+
if (value instanceof RubySymbol) {
|
118
|
+
// schema or data keyword
|
119
|
+
RubyString edn_string = (RubyString)RuntimeHelpers.invoke(context, value, "to_s");
|
120
|
+
return (Object)Keyword.intern((String)edn_string.asJavaString());
|
121
|
+
}
|
122
|
+
if (value.respondsTo("to_edn") && value.respondsTo("symbol")) {
|
123
|
+
// EDN::Type::Symbol (query)
|
124
|
+
RubyString edn_string = (RubyString)RuntimeHelpers.invoke(context, value, "to_edn");
|
125
|
+
return (Object)clojure.lang.Symbol.intern((String)edn_string.asJavaString());
|
126
|
+
}
|
102
127
|
if (value.respondsTo("to_time")) {
|
103
128
|
// DateTime or Date
|
104
129
|
RubyTime tmvalue = (RubyTime)RuntimeHelpers.invoke(context, value, "to_time");
|
@@ -110,15 +135,23 @@ public class DiametricUtils {
|
|
110
135
|
}
|
111
136
|
//System.out.println("NOT YET CONVERTED");
|
112
137
|
//System.out.println("RESPONDSTO? TO_A:" + value.respondsTo("to_a"));
|
113
|
-
if (value.respondsTo("to_a")) { // might be Set for cardinality many type
|
114
|
-
|
115
|
-
}
|
138
|
+
//if (value.respondsTo("to_a")) { // might be Set for cardinality many type
|
139
|
+
// return getList(context, value);
|
140
|
+
//}
|
116
141
|
if (value instanceof DiametricObject) {
|
117
142
|
return ((DiametricObject)value).toJava();
|
118
143
|
}
|
119
144
|
return (Object)value.toJava(Object.class);
|
120
145
|
}
|
121
146
|
|
147
|
+
static Object getStringOrUUID(String str) {
|
148
|
+
try {
|
149
|
+
return (Object)UUID.fromString(str);
|
150
|
+
} catch (IllegalArgumentException e) {
|
151
|
+
return (Object)str;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
122
155
|
static IPersistentSet getPersistentSet(ThreadContext context, IRubyObject value) {
|
123
156
|
return PersistentHashSet.create((List)value.callMethod(context, "to_a"));
|
124
157
|
}
|
@@ -147,79 +180,74 @@ public class DiametricUtils {
|
|
147
180
|
if (value instanceof Double) return RubyFloat.newFloat(runtime, (Double)value);
|
148
181
|
if (value instanceof Date) return RubyTime.newTime(runtime, ((Date)value).getTime());
|
149
182
|
if (value instanceof Set) {
|
150
|
-
return getDiametricSet(context, (Set)value);
|
183
|
+
return DiametricSet.getDiametricSet(context, (Set)value);
|
151
184
|
}
|
152
185
|
if ((value instanceof APersistentVector) ||
|
153
186
|
(value instanceof LazySeq) ||
|
154
187
|
(value instanceof PersistentVector.ChunkedSeq)){
|
155
|
-
return getDiametricCollection(context, (List)value);
|
188
|
+
return DiametricCollection.getDiametricCollection(context, (List)value);
|
189
|
+
}
|
190
|
+
if (value instanceof datomic.Entity) {
|
191
|
+
return DiametricEntity.getDiametricEntity(context, value);
|
156
192
|
}
|
157
193
|
if (value instanceof java.util.UUID) {
|
158
|
-
|
159
|
-
DiametricUUID diametric_uuid = (DiametricUUID)clazz.allocate();
|
160
|
-
diametric_uuid.init((java.util.UUID)value);
|
161
|
-
return diametric_uuid;
|
194
|
+
return DiametricUUID.getDiametricUUID(context, (java.util.UUID)value);
|
162
195
|
}
|
163
196
|
return JavaUtil.convertJavaToUsableRubyObject(runtime, value);
|
164
197
|
}
|
165
198
|
|
166
|
-
static
|
167
|
-
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::Set");
|
168
|
-
DiametricSet diametric_set = (DiametricSet)clazz.allocate();
|
169
|
-
diametric_set.init(value);
|
170
|
-
return diametric_set;
|
171
|
-
}
|
172
|
-
|
173
|
-
static IRubyObject getDiametricCollection(ThreadContext context, List value) {
|
174
|
-
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::Collection");
|
175
|
-
DiametricCollection diametric_collection = (DiametricCollection)clazz.allocate();
|
176
|
-
diametric_collection.init((List)value);
|
177
|
-
return diametric_collection;
|
178
|
-
}
|
179
|
-
|
180
|
-
static List<Object> convertRubyTxDataToJava(ThreadContext context, IRubyObject arg) {
|
181
|
-
List<Object> tx_data = null;
|
199
|
+
static PersistentVector convertRubyTxDataToJava(ThreadContext context, IRubyObject arg) {
|
182
200
|
if (arg instanceof RubyArray) {
|
183
|
-
|
201
|
+
return fromRubyArray(context, (RubyArray)arg);
|
184
202
|
} else {
|
185
203
|
Object obj = arg.toJava(Object.class);
|
186
204
|
if (obj instanceof clojure.lang.PersistentVector) {
|
187
|
-
|
205
|
+
return (clojure.lang.PersistentVector)obj;
|
188
206
|
}
|
189
207
|
}
|
190
|
-
return
|
208
|
+
return null;
|
191
209
|
}
|
192
210
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
211
|
+
static PersistentVector fromRubyArray(ThreadContext context, RubyArray ruby_array) {
|
212
|
+
Var var = DiametricService.getFn("clojure.core", "vector");
|
213
|
+
PersistentVector clj_tx_data = (PersistentVector)var.invoke();
|
214
|
+
Var adder = DiametricService.getFn("clojure.core", "conj");
|
215
|
+
for (int i=0; i<ruby_array.getLength(); i++) {
|
216
|
+
Object element = ruby_array.get(i);
|
198
217
|
if (element instanceof RubyHash) {
|
199
|
-
|
200
|
-
|
201
|
-
while (true) {
|
202
|
-
IRubyObject pair = ruby_hash.shift(context);
|
203
|
-
if (pair instanceof RubyNil) break;
|
204
|
-
Object key = DiametricUtils.convertRubyToJava(context, ((RubyArray) pair).shift(context));
|
205
|
-
Object value = DiametricUtils.convertRubyToJava(context, ((RubyArray) pair).shift(context));
|
206
|
-
keyvals.put(key, value);
|
207
|
-
}
|
208
|
-
java_tx_data.add(Collections.unmodifiableMap(keyvals));
|
218
|
+
APersistentMap map = fromRubyHash(context, (RubyHash)element);
|
219
|
+
clj_tx_data = (PersistentVector)adder.invoke(clj_tx_data, map);
|
209
220
|
} else if (element instanceof RubyArray) {
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
}
|
218
|
-
java_tx_data.add(Collections.unmodifiableList(keyvals));
|
221
|
+
PersistentVector vector = fromRubyArray(context, (RubyArray)element);
|
222
|
+
clj_tx_data = (PersistentVector)adder.invoke(clj_tx_data, vector);
|
223
|
+
} else if (element instanceof IRubyObject) {
|
224
|
+
clj_tx_data =
|
225
|
+
(PersistentVector)adder.invoke(clj_tx_data, DiametricUtils.convertRubyToJava(context, (IRubyObject)element));
|
226
|
+
} else if (element instanceof String) {
|
227
|
+
clj_tx_data = (PersistentVector)adder.invoke(clj_tx_data, getStringOrUUID((String)element));
|
219
228
|
} else {
|
220
|
-
|
229
|
+
clj_tx_data = (PersistentVector)adder.invoke(clj_tx_data, element);
|
230
|
+
}
|
231
|
+
}
|
232
|
+
return clj_tx_data;
|
233
|
+
}
|
234
|
+
|
235
|
+
private static APersistentMap fromRubyHash(ThreadContext context, RubyHash ruby_hash) {
|
236
|
+
Var var = DiametricService.getFn("clojure.core", "hash-map");
|
237
|
+
APersistentMap map = (APersistentMap)var.invoke();
|
238
|
+
Var associator = DiametricService.getFn("clojure.core", "assoc");
|
239
|
+
while (true) {
|
240
|
+
IRubyObject pair = ruby_hash.shift(context);
|
241
|
+
if (pair instanceof RubyNil) break;
|
242
|
+
Object key = DiametricUtils.convertRubyToJava(context, ((RubyArray) pair).shift(context));
|
243
|
+
Object value = DiametricUtils.convertRubyToJava(context, ((RubyArray) pair).shift(context));
|
244
|
+
if (value instanceof RubyHash) {
|
245
|
+
value = fromRubyHash(context, (RubyHash)value);
|
246
|
+
} else if (value instanceof RubyArray) {
|
247
|
+
value = fromRubyArray(context, (RubyArray)value);
|
221
248
|
}
|
249
|
+
map = (APersistentMap)associator.invoke(map, key, value);
|
222
250
|
}
|
223
|
-
return
|
251
|
+
return map;
|
224
252
|
}
|
225
253
|
}
|
data/lib/diametric.rb
CHANGED
@@ -0,0 +1,103 @@
|
|
1
|
+
module Diametric
|
2
|
+
module Associations
|
3
|
+
class Collection
|
4
|
+
def initialize(base, name, enum=nil, &block)
|
5
|
+
@base = base
|
6
|
+
@attribute_name = name
|
7
|
+
@data = Set.new(enum, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(*entities)
|
11
|
+
old_data = @data.dup
|
12
|
+
entities.each do |entity|
|
13
|
+
if entity.dbid.nil? || entity.dbid.to_i < 0
|
14
|
+
entity.save
|
15
|
+
end
|
16
|
+
@data << entity.dbid
|
17
|
+
end
|
18
|
+
if old_data != @data
|
19
|
+
@base.send("#{@attribute_name}=", self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
alias :<< :add
|
23
|
+
|
24
|
+
def add_reified_entities(*entities)
|
25
|
+
entities.each do |entity|
|
26
|
+
@data << entity
|
27
|
+
end
|
28
|
+
@base.send("clean_#{@attribute_name}=", self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(*entities)
|
32
|
+
entities.each do |entity|
|
33
|
+
return unless self.include?(entity)
|
34
|
+
if entity.is_a?(Fixnum) || entity.respond_to?(:to_i)
|
35
|
+
# dbid or dbid object
|
36
|
+
@data.delete_if {|e| e.to_i == entity.to_i}
|
37
|
+
elsif entity.respond_to?(:dbid)
|
38
|
+
# reified or created entity
|
39
|
+
@data.delete_if {|e| e.dbid == entity.dbid}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy(*entities)
|
45
|
+
entities.each do |entity|
|
46
|
+
return unless self.include?(entity)
|
47
|
+
if entity.respond_to? :destroy
|
48
|
+
entity.destroy
|
49
|
+
end
|
50
|
+
self.delete(entity)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def include?(o)
|
55
|
+
if o.is_a?(Fixnum) || o.respond_to?(:to_i)
|
56
|
+
@data.find_all {|e| e.respond_to?(:to_i)}.collect(&:to_i).include?(o.to_i)
|
57
|
+
elsif o.respond_to?(:dbid)
|
58
|
+
@data.find_all {|e| e.respond_to?(:dbid)}.collect(&:dbid).include?(o.dbid)
|
59
|
+
else
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
alias :member? :include?
|
64
|
+
|
65
|
+
def &(enum)
|
66
|
+
# this method is used to test the given class is Set or not.
|
67
|
+
# don't delete this method.
|
68
|
+
n = self.class.new
|
69
|
+
@data.each { |o| n.add(o) if include?(o) }
|
70
|
+
n
|
71
|
+
end
|
72
|
+
alias intersection &
|
73
|
+
|
74
|
+
def inspect
|
75
|
+
return sprintf('#<%s: {%s}>', self.class, @data.to_a.inspect[1..-2])
|
76
|
+
end
|
77
|
+
|
78
|
+
def replace(entities)
|
79
|
+
@data = Set.new
|
80
|
+
entities.each do |entity|
|
81
|
+
if entity.dbid.nil? || entity.dbid.to_i < 0
|
82
|
+
@data << entity.dbid if entity.save
|
83
|
+
else
|
84
|
+
@data << entity
|
85
|
+
end
|
86
|
+
end
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def method_missing(method, *args, &block)
|
91
|
+
if !args.empty? && block
|
92
|
+
@data.send(method, args, &block)
|
93
|
+
elsif !args.empty?
|
94
|
+
@data.send(method, args)
|
95
|
+
elsif block
|
96
|
+
@data.send(method, &block)
|
97
|
+
else
|
98
|
+
@data.send(method)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/diametric/entity.rb
CHANGED
@@ -51,7 +51,7 @@ module Diametric
|
|
51
51
|
:cardinality => :one
|
52
52
|
}
|
53
53
|
|
54
|
-
@temp_ref = -
|
54
|
+
@temp_ref = -1000000
|
55
55
|
|
56
56
|
def self.included(base)
|
57
57
|
base.send(:extend, ClassMethods)
|
@@ -210,13 +210,7 @@ module Diametric
|
|
210
210
|
# @return [Array] A Datomic schema, as Ruby data that can be
|
211
211
|
# converted to EDN.
|
212
212
|
def schema
|
213
|
-
return peer_schema if self.instance_variable_get("@peer")
|
214
|
-
rest_schema
|
215
|
-
end
|
216
|
-
|
217
|
-
def rest_schema
|
218
213
|
defaults = {
|
219
|
-
:"db/id" => tempid(:"db.part/db"),
|
220
214
|
:"db/cardinality" => :"db.cardinality/one",
|
221
215
|
:"db.install/_attribute" => :"db.part/db"
|
222
216
|
}
|
@@ -238,13 +232,13 @@ module Diametric
|
|
238
232
|
schema << defaults.merge({
|
239
233
|
:"db/ident" => namespace(prefix, attribute),
|
240
234
|
:"db/valueType" => value_type(value_type),
|
235
|
+
:"db/id" => tempid(:"db.part/db")
|
241
236
|
}).merge(opts)
|
242
237
|
end
|
243
|
-
|
244
238
|
enum_schema = [
|
245
239
|
:"db/add", tempid(:"db.part/user"), :"db/ident"
|
246
240
|
]
|
247
|
-
prefix = self.
|
241
|
+
prefix = self.to_s.underscore
|
248
242
|
@enums.each do |key, values|
|
249
243
|
values.each do |value|
|
250
244
|
ident_value = :"#{prefix}.#{key.downcase}/#{value.to_s.sub(/_/, "-").downcase}"
|
@@ -255,48 +249,6 @@ module Diametric
|
|
255
249
|
schema_array
|
256
250
|
end
|
257
251
|
|
258
|
-
# Generates a Datomic schema for a model's attributes.
|
259
|
-
#
|
260
|
-
# @return [Array] A Datomic schema, as Ruby data that can be
|
261
|
-
# converted to EDN.
|
262
|
-
def peer_schema
|
263
|
-
defaults = {
|
264
|
-
":db/cardinality" => ":db.cardinality/one",
|
265
|
-
":db.install/_attribute" => ":db.part/db"
|
266
|
-
}
|
267
|
-
|
268
|
-
schema_array = @attributes.reduce([]) do |schema, (attribute, opts)|
|
269
|
-
opts = opts.dup
|
270
|
-
value_type = opts.delete(:value_type)
|
271
|
-
|
272
|
-
unless opts.empty?
|
273
|
-
opts[:cardinality] = namespace("db.cardinality", opts[:cardinality])
|
274
|
-
opts[:unique] = namespace("db.unique", opts[:unique]) if opts[:unique]
|
275
|
-
opts = opts.map { |k, v|
|
276
|
-
k = namespace("db", k)
|
277
|
-
[k, v]
|
278
|
-
}
|
279
|
-
opts = Hash[*opts.flatten]
|
280
|
-
end
|
281
|
-
|
282
|
-
schema << defaults.merge({
|
283
|
-
":db/id" => Diametric::Persistence::Peer.tempid(":db.part/db"),
|
284
|
-
":db/ident" => namespace(prefix, attribute),
|
285
|
-
":db/valueType" => value_type(value_type),
|
286
|
-
}).merge(opts)
|
287
|
-
end
|
288
|
-
|
289
|
-
prefix = self.name.downcase
|
290
|
-
@enums.each do |key, values|
|
291
|
-
values.each do |value|
|
292
|
-
ident_value = ":#{prefix}.#{key.downcase}/#{value.to_s.sub(/_/, "-").downcase}"
|
293
|
-
es = [":db/add", Diametric::Persistence::Peer.tempid(":db.part/user"), ":db/ident", ident_value]
|
294
|
-
schema_array << es
|
295
|
-
end
|
296
|
-
end
|
297
|
-
schema_array
|
298
|
-
end
|
299
|
-
|
300
252
|
# Given a set of Ruby data returned from a Datomic query, this
|
301
253
|
# can re-hydrate that data into a model instance.
|
302
254
|
#
|
@@ -316,13 +268,20 @@ module Diametric
|
|
316
268
|
parent.class.attribute_names.each do |e|
|
317
269
|
if parent.class.attributes[e][:value_type] == "ref"
|
318
270
|
ref = parent.instance_variable_get("@#{e.to_s}")
|
319
|
-
if ref.is_a?(Fixnum) ||
|
271
|
+
if ref.is_a?(Fixnum) ||
|
272
|
+
(self.instance_variable_get("@peer") && ref.is_a?(Diametric::Persistence::Entity))
|
320
273
|
child = reify(ref, connection)
|
321
274
|
child = resolve_ref_dbid(child, connection)
|
322
275
|
parent.instance_variable_set("@#{e.to_s}", child)
|
276
|
+
elsif ref.is_a?(Diametric::Associations::Collection)
|
277
|
+
children = Diametric::Associations::Collection.new(parent, e.to_s)
|
278
|
+
ref.each do |entity|
|
279
|
+
children.add_reified_entities(reify(entity, connection))
|
280
|
+
end
|
281
|
+
parent.instance_variable_set("@#{e.to_s}", children)
|
323
282
|
elsif ref.is_a?(Set)
|
324
283
|
children = ref.inject(Set.new) do |memo, entity|
|
325
|
-
|
284
|
+
child = reify(entity, connection)
|
326
285
|
memo.add(child)
|
327
286
|
memo
|
328
287
|
end
|
@@ -334,6 +293,38 @@ module Diametric
|
|
334
293
|
end
|
335
294
|
|
336
295
|
def reify(thing, conn_or_db=nil, resolve=false)
|
296
|
+
return peer_reify(thing, conn_or_db, resolve) if self.instance_variable_get("@peer")
|
297
|
+
rest_reify(thing, resolve)
|
298
|
+
end
|
299
|
+
|
300
|
+
def rest_reify(dbid, resolve)
|
301
|
+
query = [
|
302
|
+
:find, ~"?ident", ~"?v",
|
303
|
+
:in, ~"\$", [~"?e"],
|
304
|
+
:where, [~"?e", ~"?a", ~"?v"], [~"?a", :"db/ident", ~"?ident"]
|
305
|
+
]
|
306
|
+
entities = self.q(query, [[dbid]])
|
307
|
+
class_name = to_classname(entities.first.first)
|
308
|
+
instance = eval("#{class_name}.new")
|
309
|
+
entities.each do |k, v|
|
310
|
+
matched_data = /([a-zA-Z0-9_\.]+)\/([a-zA-Z0-9_]+)/.match(k.to_s)
|
311
|
+
attribute = instance.send(matched_data[2])
|
312
|
+
if attribute && attribute.is_a?(Diametric::Associations::Collection)
|
313
|
+
attribute.add_reified_entities(v)
|
314
|
+
else
|
315
|
+
instance.send("clean_#{matched_data[2]}=", v)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
instance.send("dbid=", dbid)
|
319
|
+
|
320
|
+
if resolve
|
321
|
+
instance = resolve_ref_dbid(instance, nil)
|
322
|
+
end
|
323
|
+
|
324
|
+
instance
|
325
|
+
end
|
326
|
+
|
327
|
+
def peer_reify(thing, conn_or_db=nil, resolve=false)
|
337
328
|
conn_or_db ||= Diametric::Persistence::Peer.connect.db
|
338
329
|
|
339
330
|
if conn_or_db.respond_to?(:db)
|
@@ -359,13 +350,11 @@ module Diametric
|
|
359
350
|
return thing
|
360
351
|
end
|
361
352
|
first_key = entity.keys.first
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
match_data = /:([a-zA-Z0-9_]+)\/([a-zA-Z0-9_]+)/.match(key)
|
368
|
-
instance.send("#{match_data[2]}=", entity[key])
|
353
|
+
class_name = to_classname(first_key)
|
354
|
+
instance = eval("#{class_name}.new")
|
355
|
+
entity.keys.each do |key|
|
356
|
+
matched_data = /:([a-zA-Z0-9_\.]+)\/([a-zA-Z0-9_]+)/.match(key)
|
357
|
+
instance.send("clean_#{matched_data[2]}=", entity[key])
|
369
358
|
end
|
370
359
|
instance.send("dbid=", Diametric::Persistence::Object.new(entity.get("db/id")))
|
371
360
|
|
@@ -376,6 +365,34 @@ module Diametric
|
|
376
365
|
instance
|
377
366
|
end
|
378
367
|
|
368
|
+
def to_classname(key)
|
369
|
+
names = []
|
370
|
+
# drops the first character ":"
|
371
|
+
key = key.to_s
|
372
|
+
if key[0] == ":"
|
373
|
+
key = key[1..-1]
|
374
|
+
end
|
375
|
+
key.chars.inject("") do |memo, c|
|
376
|
+
if c == "/"
|
377
|
+
# means the end of class name
|
378
|
+
names << memo
|
379
|
+
break
|
380
|
+
elsif c == "."
|
381
|
+
# namespace delimiter
|
382
|
+
names << memo
|
383
|
+
memo = ""
|
384
|
+
elsif c == "-"
|
385
|
+
# Clojure uses - for name, but Ruby can't
|
386
|
+
# converts dash to underscore
|
387
|
+
memo << "_"
|
388
|
+
else
|
389
|
+
memo << c
|
390
|
+
end
|
391
|
+
memo
|
392
|
+
end
|
393
|
+
names.collect(&:camelize).join("::")
|
394
|
+
end
|
395
|
+
|
379
396
|
def find(id)
|
380
397
|
if self.instance_variable_get("@peer")
|
381
398
|
connection ||= Diametric::Persistence::Peer.connect
|
@@ -383,19 +400,6 @@ module Diametric
|
|
383
400
|
reify(id, connection)
|
384
401
|
end
|
385
402
|
|
386
|
-
# Returns the prefix for this model used in Datomic. Can be
|
387
|
-
# overriden by declaring {#namespace_prefix}
|
388
|
-
#
|
389
|
-
# @example
|
390
|
-
# Mouse.prefix #=> "mouse"
|
391
|
-
# FireHouse.prefix #=> "fire_house"
|
392
|
-
# Person::User.prefix #=> "person.user"
|
393
|
-
#
|
394
|
-
# @return [String]
|
395
|
-
def prefix
|
396
|
-
@namespace_prefix || self.to_s.underscore.sub('/', '.')
|
397
|
-
end
|
398
|
-
|
399
403
|
# Create a temporary id placeholder.
|
400
404
|
#
|
401
405
|
# @param e [*#to_edn] Elements to put in the placeholder. Should
|
@@ -405,16 +409,25 @@ module Diametric
|
|
405
409
|
# @return [EDN::Type::Unknown] Temporary id placeholder.
|
406
410
|
def tempid(*e)
|
407
411
|
if self.instance_variable_get("@peer")
|
408
|
-
|
409
|
-
return Diametric::Persistence::Peer.tempid(":db.part/user")
|
410
|
-
else
|
411
|
-
return Diametric::Persistence::Peer.tempid(":db.part/db")
|
412
|
-
end
|
412
|
+
Diametric::Persistence::Peer.tempid(*e)
|
413
413
|
else
|
414
|
-
|
414
|
+
Diametric::Persistence::REST.tempid(*e)
|
415
415
|
end
|
416
416
|
end
|
417
417
|
|
418
|
+
# Returns the prefix for this model used in Datomic. Can be
|
419
|
+
# overriden by declaring {#namespace_prefix}
|
420
|
+
#
|
421
|
+
# @example
|
422
|
+
# Mouse.prefix #=> "mouse"
|
423
|
+
# FireHouse.prefix #=> "fire_house"
|
424
|
+
# Person::User.prefix #=> "person.user"
|
425
|
+
#
|
426
|
+
# @return [String]
|
427
|
+
def prefix
|
428
|
+
@namespace_prefix || self.to_s.underscore.gsub('/', '.')
|
429
|
+
end
|
430
|
+
|
418
431
|
# Namespace a attribute for Datomic.
|
419
432
|
#
|
420
433
|
# @param ns [#to_s] Namespace.
|
@@ -422,11 +435,7 @@ module Diametric
|
|
422
435
|
#
|
423
436
|
# @return [Symbol] Namespaced attribute.
|
424
437
|
def namespace(ns, attribute)
|
425
|
-
|
426
|
-
":" + [ns.to_s, attribute.to_s].join("/")
|
427
|
-
else
|
428
|
-
[ns.to_s, attribute.to_s].join("/").to_sym
|
429
|
-
end
|
438
|
+
[ns.to_s, attribute.to_s].join("/").to_sym
|
430
439
|
end
|
431
440
|
|
432
441
|
# Raise an error if validation failed.
|
@@ -457,13 +466,72 @@ module Diametric
|
|
457
466
|
define_attribute_method name
|
458
467
|
|
459
468
|
define_method(name) do
|
460
|
-
instance_variable_get("@#{name}")
|
469
|
+
ivar = instance_variable_get("@#{name}")
|
470
|
+
if ivar.nil? &&
|
471
|
+
self.class.attributes[name][:value_type] == Ref &&
|
472
|
+
self.class.attributes[name][:cardinality] == :many
|
473
|
+
ivar = Diametric::Associations::Collection.new(self, name)
|
474
|
+
end
|
475
|
+
ivar
|
461
476
|
end
|
462
477
|
|
463
478
|
define_method("#{name}=") do |value|
|
464
479
|
send("#{name}_will_change!") unless value == instance_variable_get("@#{name}")
|
465
|
-
|
466
|
-
|
480
|
+
case self.class.attributes[name][:value_type]
|
481
|
+
when Ref
|
482
|
+
case cardinality
|
483
|
+
when :many
|
484
|
+
if value.is_a?(Enumerable) && value.first.respond_to?(:save)
|
485
|
+
# entity type
|
486
|
+
ivar = send("#{name}")
|
487
|
+
instance_variable_set("@#{name}", ivar.replace(value))
|
488
|
+
elsif value.is_a?(Diametric::Associations::Collection)
|
489
|
+
instance_variable_set("@#{name}", value)
|
490
|
+
elsif value.is_a?(Enumerable)
|
491
|
+
# enum type
|
492
|
+
# however, falls here when empty array is given for entity type
|
493
|
+
instance_variable_set("@#{name}", Set.new(value))
|
494
|
+
end
|
495
|
+
when :one
|
496
|
+
if value.respond_to?(:save)
|
497
|
+
# entity type
|
498
|
+
if value.save
|
499
|
+
instance_variable_set("@#{name}", value.dbid)
|
500
|
+
end
|
501
|
+
else
|
502
|
+
# enum type
|
503
|
+
instance_variable_set("@#{name}", value)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
else # not Ref
|
507
|
+
case cardinality
|
508
|
+
when :many
|
509
|
+
instance_variable_set("@#{name}", Set.new(value))
|
510
|
+
when :one
|
511
|
+
instance_variable_set("@#{name}", value)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
define_method("clean_#{name}=") do |value|
|
517
|
+
if self.class.attributes[name][:value_type] != Ref && cardinality == :many
|
518
|
+
if value.is_a? Enumerable
|
519
|
+
value = Set.new(value)
|
520
|
+
else
|
521
|
+
# used from rest reify
|
522
|
+
ivar = instance_variable_get("@#{name}")
|
523
|
+
ivar ||= Set.new
|
524
|
+
value = ivar.add(value)
|
525
|
+
end
|
526
|
+
end
|
527
|
+
if self.class.attributes[name][:value_type] == Ref &&
|
528
|
+
cardinality == :many &&
|
529
|
+
!(value.is_a? Diametric::Associations::Collection)
|
530
|
+
if value.is_a? Enumerable
|
531
|
+
value = Diametric::Associations::Collection.new(self, name, value)
|
532
|
+
else
|
533
|
+
value = Diametric::Associations::Collection.new(self, name, [value])
|
534
|
+
end
|
467
535
|
end
|
468
536
|
instance_variable_set("@#{name}", value)
|
469
537
|
end
|
@@ -512,7 +580,6 @@ module Diametric
|
|
512
580
|
attribute_names.each do |attribute_name|
|
513
581
|
cardinality = self.class.attributes[attribute_name.to_sym][:cardinality]
|
514
582
|
|
515
|
-
#if cardinality == :many && self.class.instance_variable_get("@peer").nil?
|
516
583
|
if cardinality == :many
|
517
584
|
txes += cardinality_many_tx_data(attribute_name)
|
518
585
|
else
|
@@ -521,42 +588,38 @@ module Diametric
|
|
521
588
|
end
|
522
589
|
|
523
590
|
if entity_tx.present?
|
524
|
-
|
525
|
-
|
526
|
-
txes << entity_tx.merge({":db/id" => dbid})
|
527
|
-
else
|
528
|
-
txes << entity_tx.merge({:"db/id" => dbid || tempid})
|
529
|
-
end
|
591
|
+
@dbid ||= tempid
|
592
|
+
txes << entity_tx.merge({:"db/id" => dbid})
|
530
593
|
end
|
531
594
|
txes
|
532
595
|
end
|
533
596
|
|
534
597
|
def cardinality_many_tx_data(attribute_name)
|
535
|
-
|
598
|
+
changed = self.changed_attributes[attribute_name]
|
599
|
+
prev =
|
600
|
+
changed.is_a?(Diametric::Associations::Collection) ? changed.to_set : Array(changed).to_set
|
536
601
|
curr = self.send(attribute_name)
|
602
|
+
curr = curr.is_a?(Diametric::Associations::Collection) ? curr.to_set : curr
|
537
603
|
|
538
604
|
protractions = curr - prev
|
539
605
|
retractions = prev - curr
|
540
606
|
|
541
607
|
namespaced_attribute = self.class.namespace(self.class.prefix, attribute_name)
|
542
608
|
txes = []
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
txes_data(txes, ":db/add", namespaced_attribute, protractions) unless protractions.empty?
|
547
|
-
else
|
548
|
-
txes << [:"db/retract", (dbid || tempid), namespaced_attribute, retractions.to_a] unless retractions.empty?
|
549
|
-
txes << [:"db/add", (dbid || tempid) , namespaced_attribute, protractions.to_a] unless protractions.empty?
|
550
|
-
end
|
609
|
+
@dbid ||= tempid
|
610
|
+
txes << [:"db/retract", dbid, namespaced_attribute, retractions.to_a] unless retractions.empty?
|
611
|
+
txes << [:"db/add", dbid , namespaced_attribute, protractions.to_a] unless protractions.empty?
|
551
612
|
txes
|
552
613
|
end
|
553
614
|
|
615
|
+
=begin
|
554
616
|
def txes_data(txes, op, namespaced_attribute, set)
|
555
617
|
set.to_a.each do |s|
|
556
618
|
value = s.respond_to?(:dbid) ? s.dbid : s
|
557
619
|
txes << [op, @dbid, namespaced_attribute, value]
|
558
620
|
end
|
559
621
|
end
|
622
|
+
=end
|
560
623
|
|
561
624
|
# Returns hash of all attributes for this object
|
562
625
|
#
|