diametric 0.1.2-java → 0.1.3-java
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.
- 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
|
#
|