diametric 0.0.4 → 0.1.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.
- checksums.yaml +15 -0
- data/Gemfile +21 -18
- data/Jarfile +15 -1
- data/README.md +22 -14
- data/Rakefile +17 -1
- data/bin/datomic-rest +33 -0
- data/bin/download-datomic +13 -0
- data/datomic_version.yml +4 -0
- data/diametric.gemspec +9 -6
- data/ext/diametric/DiametricCollection.java +147 -0
- data/ext/diametric/DiametricConnection.java +113 -0
- data/ext/diametric/DiametricDatabase.java +107 -0
- data/ext/diametric/DiametricEntity.java +90 -0
- data/ext/diametric/DiametricListenableFuture.java +47 -0
- data/ext/diametric/DiametricObject.java +66 -0
- data/ext/diametric/DiametricPeer.java +414 -0
- data/ext/diametric/DiametricService.java +102 -0
- data/ext/diametric/DiametricUUID.java +61 -0
- data/ext/diametric/DiametricUtils.java +183 -0
- data/lib/boolean_type.rb +3 -0
- data/lib/diametric.rb +24 -0
- data/lib/diametric/entity.rb +219 -14
- data/lib/diametric/generators/active_model.rb +2 -2
- data/lib/diametric/persistence.rb +0 -1
- data/lib/diametric/persistence/common.rb +28 -9
- data/lib/diametric/persistence/peer.rb +122 -87
- data/lib/diametric/persistence/rest.rb +4 -3
- data/lib/diametric/query.rb +94 -23
- data/lib/diametric/rest_service.rb +74 -0
- data/lib/diametric/service_base.rb +77 -0
- data/lib/diametric/transactor.rb +86 -0
- data/lib/diametric/version.rb +1 -1
- data/lib/diametric_service.jar +0 -0
- data/lib/value_enums.rb +8 -0
- data/spec/conf_helper.rb +55 -0
- data/spec/config/free-transactor-template.properties +73 -0
- data/spec/config/logback.xml +59 -0
- data/spec/data/seattle-data0.dtm +452 -0
- data/spec/data/seattle-data1.dtm +326 -0
- data/spec/developer_create_sample.rb +39 -0
- data/spec/developer_query_spec.rb +120 -0
- data/spec/diametric/config_spec.rb +1 -1
- data/spec/diametric/entity_spec.rb +263 -0
- data/spec/diametric/peer_api_spec.rb +147 -0
- data/spec/diametric/persistence/peer_many2many_spec.rb +76 -0
- data/spec/diametric/persistence/peer_spec.rb +13 -22
- data/spec/diametric/persistence/rest_spec.rb +12 -19
- data/spec/diametric/query_spec.rb +4 -5
- data/spec/diametric/rest_service_spec.rb +56 -0
- data/spec/diametric/transactor_spec.rb +68 -0
- data/spec/integration_spec.rb +5 -3
- data/spec/parent_child_sample.rb +42 -0
- data/spec/peer_integration_spec.rb +106 -22
- data/spec/peer_seattle_spec.rb +200 -0
- data/spec/rc2013_seattle_big.rb +82 -0
- data/spec/rc2013_seattle_small.rb +60 -0
- data/spec/rc2013_simple_sample.rb +72 -0
- data/spec/seattle_integration_spec.rb +106 -0
- data/spec/simple_validation_sample.rb +31 -0
- data/spec/spec_helper.rb +31 -45
- data/spec/support/entities.rb +157 -0
- data/spec/support/gen_entity_class.rb +2 -0
- data/spec/support/persistence_examples.rb +9 -5
- data/spec/test_version_file.yml +4 -0
- metadata +131 -75
- data/Jarfile.lock +0 -134
- data/lib/jrclj.rb +0 -63
@@ -0,0 +1,102 @@
|
|
1
|
+
package diametric;
|
2
|
+
|
3
|
+
import java.io.IOException;
|
4
|
+
|
5
|
+
import org.jruby.Ruby;
|
6
|
+
import org.jruby.RubyClass;
|
7
|
+
import org.jruby.RubyModule;
|
8
|
+
import org.jruby.runtime.ObjectAllocator;
|
9
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
10
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
11
|
+
|
12
|
+
public class DiametricService implements BasicLibraryService {
|
13
|
+
|
14
|
+
@Override
|
15
|
+
public boolean basicLoad(Ruby runtime) throws IOException {
|
16
|
+
RubyModule diametric = runtime.defineModule("Diametric");
|
17
|
+
RubyModule persistence = diametric.defineModuleUnder("Persistence");
|
18
|
+
|
19
|
+
RubyModule diametric_peer = persistence.defineModuleUnder("Peer");
|
20
|
+
diametric_peer.defineAnnotatedMethods(DiametricPeer.class);
|
21
|
+
|
22
|
+
RubyClass connection = persistence.defineClassUnder("Connection", runtime.getObject(), CONNECTION_ALLOCATOR);
|
23
|
+
connection.defineAnnotatedMethods(DiametricConnection.class);
|
24
|
+
|
25
|
+
RubyClass uuid = persistence.defineClassUnder("UUID", runtime.getObject(), UUID_ALLOCATOR);
|
26
|
+
uuid.defineAnnotatedMethods(DiametricUUID.class);
|
27
|
+
|
28
|
+
RubyClass diametric_object = persistence.defineClassUnder("Object", runtime.getObject(), DIAMETRIC_OBJECT_ALLOCATOR);
|
29
|
+
diametric_object.defineAnnotatedMethods(DiametricObject.class);
|
30
|
+
|
31
|
+
RubyClass diametric_query_result = persistence.defineClassUnder("Collection", runtime.getObject(), COLLECTION_ALLOCATOR);
|
32
|
+
diametric_query_result.defineAnnotatedMethods(DiametricCollection.class);
|
33
|
+
|
34
|
+
RubyClass diametric_listenable = persistence.defineClassUnder("ListenableFuture", runtime.getObject(), LISTENABLE_ALLOCATOR);
|
35
|
+
diametric_listenable.defineAnnotatedMethods(DiametricListenableFuture.class);
|
36
|
+
|
37
|
+
RubyClass diametric_database = persistence.defineClassUnder("Database", runtime.getObject(), DATABASE_ALLOCATOR);
|
38
|
+
diametric_database.defineAnnotatedMethods(DiametricDatabase.class);
|
39
|
+
|
40
|
+
RubyClass diametric_entity = persistence.defineClassUnder("Entity", runtime.getObject(), ENTITY_ALLOCATOR);
|
41
|
+
diametric_entity.defineAnnotatedMethods(DiametricEntity.class);
|
42
|
+
|
43
|
+
RubyModule diametric_utils = persistence.defineModuleUnder("Utils");
|
44
|
+
diametric_utils.defineAnnotatedMethods(DiametricUtils.class);
|
45
|
+
|
46
|
+
setupClojureRT();
|
47
|
+
|
48
|
+
return false;
|
49
|
+
}
|
50
|
+
|
51
|
+
public static final ObjectAllocator CONNECTION_ALLOCATOR = new ObjectAllocator() {
|
52
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
53
|
+
return new DiametricConnection(runtime, klazz);
|
54
|
+
}
|
55
|
+
};
|
56
|
+
|
57
|
+
public static final ObjectAllocator UUID_ALLOCATOR = new ObjectAllocator() {
|
58
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
59
|
+
return new DiametricUUID(runtime, klazz);
|
60
|
+
}
|
61
|
+
};
|
62
|
+
|
63
|
+
public static final ObjectAllocator DIAMETRIC_OBJECT_ALLOCATOR = new ObjectAllocator() {
|
64
|
+
DiametricObject diametric_object = null;
|
65
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
66
|
+
if (diametric_object == null) diametric_object = new DiametricObject(runtime, klazz);
|
67
|
+
try {
|
68
|
+
return (DiametricObject) diametric_object.clone();
|
69
|
+
} catch (CloneNotSupportedException e) {
|
70
|
+
return new DiametricObject(runtime, klazz);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
};
|
74
|
+
|
75
|
+
public static final ObjectAllocator COLLECTION_ALLOCATOR = new ObjectAllocator() {
|
76
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
77
|
+
return new DiametricCollection(runtime, klazz);
|
78
|
+
}
|
79
|
+
};
|
80
|
+
|
81
|
+
public static final ObjectAllocator LISTENABLE_ALLOCATOR = new ObjectAllocator() {
|
82
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
83
|
+
return new DiametricListenableFuture(runtime, klazz);
|
84
|
+
}
|
85
|
+
};
|
86
|
+
|
87
|
+
public static final ObjectAllocator DATABASE_ALLOCATOR = new ObjectAllocator() {
|
88
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
89
|
+
return new DiametricDatabase(runtime, klazz);
|
90
|
+
}
|
91
|
+
};
|
92
|
+
|
93
|
+
public static final ObjectAllocator ENTITY_ALLOCATOR = new ObjectAllocator() {
|
94
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
95
|
+
return new DiametricEntity(runtime, klazz);
|
96
|
+
}
|
97
|
+
};
|
98
|
+
|
99
|
+
private void setupClojureRT() {
|
100
|
+
clojure.lang.RT.var("clojure.core", "require").invoke(clojure.lang.Symbol.intern("datomic.api"));
|
101
|
+
}
|
102
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
package diametric;
|
2
|
+
|
3
|
+
import java.util.UUID;
|
4
|
+
|
5
|
+
import org.jruby.Ruby;
|
6
|
+
import org.jruby.RubyClass;
|
7
|
+
import org.jruby.RubyObject;
|
8
|
+
import org.jruby.RubyString;
|
9
|
+
import org.jruby.anno.JRubyClass;
|
10
|
+
import org.jruby.anno.JRubyMethod;
|
11
|
+
import org.jruby.javasupport.JavaUtil;
|
12
|
+
import org.jruby.runtime.ThreadContext;
|
13
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
14
|
+
|
15
|
+
@JRubyClass(name = "Diametric::Persistence::UUID")
|
16
|
+
public class DiametricUUID extends RubyObject {
|
17
|
+
private static final long serialVersionUID = 2083281771243513904L;
|
18
|
+
private java.util.UUID java_uuid = null;
|
19
|
+
|
20
|
+
public DiametricUUID(Ruby runtime, RubyClass klazz) {
|
21
|
+
super(runtime, klazz);
|
22
|
+
}
|
23
|
+
|
24
|
+
void init(java.util.UUID java_uuid) {
|
25
|
+
this.java_uuid = java_uuid;
|
26
|
+
}
|
27
|
+
|
28
|
+
java.util.UUID getUUID() {
|
29
|
+
return java_uuid;
|
30
|
+
}
|
31
|
+
|
32
|
+
@JRubyMethod(name = "new", meta = true)
|
33
|
+
public static IRubyObject rbNew(ThreadContext context, IRubyObject klazz) {
|
34
|
+
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::UUID");
|
35
|
+
DiametricUUID diametric_uuid = (DiametricUUID)clazz.allocate();
|
36
|
+
try {
|
37
|
+
java.util.UUID java_uuid = (UUID) clojure.lang.RT.var("datomic.api", "squuid").invoke();
|
38
|
+
diametric_uuid.init(java_uuid);
|
39
|
+
return diametric_uuid;
|
40
|
+
} catch (Throwable t) {
|
41
|
+
throw context.getRuntime().newRuntimeError(t.getMessage());
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
@JRubyMethod
|
46
|
+
public IRubyObject to_java(ThreadContext context) {
|
47
|
+
return JavaUtil.convertJavaToUsableRubyObject(context.getRuntime(), java_uuid);
|
48
|
+
}
|
49
|
+
|
50
|
+
@JRubyMethod
|
51
|
+
public IRubyObject to_s(ThreadContext context) {
|
52
|
+
if (java_uuid == null) return context.getRuntime().getNil();
|
53
|
+
return RubyString.newString(context.getRuntime(), java_uuid.toString());
|
54
|
+
}
|
55
|
+
|
56
|
+
@JRubyMethod
|
57
|
+
public IRubyObject generate(ThreadContext context, IRubyObject arg) {
|
58
|
+
if (java_uuid == null) return context.getRuntime().getNil();
|
59
|
+
return RubyString.newString(context.getRuntime(), java.util.UUID.randomUUID().toString());
|
60
|
+
}
|
61
|
+
}
|
@@ -0,0 +1,183 @@
|
|
1
|
+
package diametric;
|
2
|
+
|
3
|
+
import java.io.FileNotFoundException;
|
4
|
+
import java.io.FileReader;
|
5
|
+
import java.io.IOException;
|
6
|
+
import java.io.Reader;
|
7
|
+
import java.util.ArrayList;
|
8
|
+
import java.util.Collections;
|
9
|
+
import java.util.Date;
|
10
|
+
import java.util.HashMap;
|
11
|
+
import java.util.List;
|
12
|
+
import java.util.Map;
|
13
|
+
import java.util.UUID;
|
14
|
+
|
15
|
+
import org.jruby.Ruby;
|
16
|
+
import org.jruby.RubyArray;
|
17
|
+
import org.jruby.RubyBignum;
|
18
|
+
import org.jruby.RubyBoolean;
|
19
|
+
import org.jruby.RubyClass;
|
20
|
+
import org.jruby.RubyFixnum;
|
21
|
+
import org.jruby.RubyFloat;
|
22
|
+
import org.jruby.RubyHash;
|
23
|
+
import org.jruby.RubyNil;
|
24
|
+
import org.jruby.RubyString;
|
25
|
+
import org.jruby.RubyTime;
|
26
|
+
import org.jruby.anno.JRubyMethod;
|
27
|
+
import org.jruby.anno.JRubyModule;
|
28
|
+
import org.jruby.javasupport.JavaUtil;
|
29
|
+
import org.jruby.javasupport.util.RuntimeHelpers;
|
30
|
+
import org.jruby.runtime.ThreadContext;
|
31
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
32
|
+
|
33
|
+
import datomic.Util;
|
34
|
+
|
35
|
+
@JRubyModule(name="Diametric::Persistence::Utils")
|
36
|
+
public class DiametricUtils {
|
37
|
+
|
38
|
+
@JRubyMethod(meta=true)
|
39
|
+
public static IRubyObject read_all(ThreadContext context, IRubyObject klazz, IRubyObject arg) {
|
40
|
+
String filename = null;
|
41
|
+
if (arg instanceof RubyString) {
|
42
|
+
filename = DiametricUtils.rubyStringToJava(arg);
|
43
|
+
} else {
|
44
|
+
throw context.getRuntime().newArgumentError("Argument should be filename");
|
45
|
+
}
|
46
|
+
Reader reader = null;
|
47
|
+
try {
|
48
|
+
reader = new FileReader(filename);
|
49
|
+
List list = (List) Util.readAll(reader);
|
50
|
+
RubyArray array = RubyArray.newArray(context.getRuntime(), list.size());
|
51
|
+
array.addAll(list);
|
52
|
+
return array;
|
53
|
+
} catch (Exception e) {
|
54
|
+
throw context.getRuntime().newRuntimeError(e.getMessage());
|
55
|
+
} finally {
|
56
|
+
try {
|
57
|
+
if (reader != null) reader.close();
|
58
|
+
} catch (IOException e) {
|
59
|
+
// no-op
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
static String rubyStringToJava(IRubyObject arg) {
|
65
|
+
if (arg instanceof RubyString) {
|
66
|
+
// TODO probably, we need to specify encoding.
|
67
|
+
return (String) ((RubyString)arg).toJava(String.class);
|
68
|
+
} else {
|
69
|
+
return null;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
static Object convertRubyToJava(ThreadContext context, IRubyObject value) {
|
74
|
+
if (value instanceof RubyString) {
|
75
|
+
String str = (String)((RubyString)value).toJava(String.class);
|
76
|
+
try {
|
77
|
+
return (Object)UUID.fromString(str);
|
78
|
+
} catch (IllegalArgumentException e) {
|
79
|
+
return (Object)str;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
if (value instanceof RubyBoolean) return (Object)((RubyBoolean)value).toJava(Boolean.class);
|
83
|
+
if (value instanceof RubyFixnum) return (Object)((RubyFixnum)value).toJava(Long.class);
|
84
|
+
if (value instanceof DiametricUUID) return ((DiametricUUID)value).getUUID();
|
85
|
+
if (value instanceof RubyBignum) {
|
86
|
+
RubyString svalue = (RubyString)((RubyBignum)value).to_s();
|
87
|
+
java.math.BigInteger bivalue = new java.math.BigInteger((String)svalue.toJava(String.class));
|
88
|
+
return (Object)bivalue;
|
89
|
+
}
|
90
|
+
if (value instanceof RubyFloat) return (Object)((RubyFloat)value).toJava(Double.class);
|
91
|
+
if (value instanceof RubyTime) {
|
92
|
+
RubyTime tmvalue = (RubyTime)value;
|
93
|
+
return (Object)tmvalue.getJavaDate();
|
94
|
+
}
|
95
|
+
if (value.respondsTo("to_time")) {
|
96
|
+
// DateTime or Date
|
97
|
+
RubyTime tmvalue = (RubyTime)RuntimeHelpers.invoke(context, value, "to_time");
|
98
|
+
return (Object)tmvalue.getJavaDate();
|
99
|
+
}
|
100
|
+
//System.out.println("NOT YET CONVERTED");
|
101
|
+
//System.out.println("RESPONDSTO? TO_A:" + value.respondsTo("to_a"));
|
102
|
+
if (value.respondsTo("to_a")) { // might be Set for cardinality many type
|
103
|
+
RubyArray ruby_array = (RubyArray)RuntimeHelpers.invoke(context, value, "to_a");
|
104
|
+
List<Object> list = new ArrayList<Object>();
|
105
|
+
while (true) {
|
106
|
+
IRubyObject element = ruby_array.shift(context);
|
107
|
+
if (element.isNil()) break;
|
108
|
+
list.add(DiametricUtils.convertRubyToJava(context, element));
|
109
|
+
}
|
110
|
+
return Collections.unmodifiableList(list);
|
111
|
+
}
|
112
|
+
if (value instanceof DiametricObject) {
|
113
|
+
return ((DiametricObject)value).toJava();
|
114
|
+
}
|
115
|
+
return (Object)value.toJava(Object.class);
|
116
|
+
}
|
117
|
+
|
118
|
+
static IRubyObject convertJavaToRuby(ThreadContext context, Object value) {
|
119
|
+
Ruby runtime = context.getRuntime();
|
120
|
+
if (value instanceof String) return RubyString.newString(runtime, (String)value);
|
121
|
+
if (value instanceof Boolean) return RubyBoolean.newBoolean(runtime, (Boolean)value);
|
122
|
+
if (value instanceof Long) return RubyFixnum.newFixnum(runtime, (Long)value);
|
123
|
+
if (value instanceof clojure.lang.Keyword) {
|
124
|
+
return RubyString.newString(runtime, ((clojure.lang.Keyword)value).toString());
|
125
|
+
}
|
126
|
+
if (value instanceof java.math.BigInteger) return RubyBignum.newBignum(runtime, ((java.math.BigInteger)value).longValue());
|
127
|
+
if (value instanceof Double) return RubyFloat.newFloat(runtime, (Double)value);
|
128
|
+
if (value instanceof Date) return RubyTime.newTime(runtime, ((Date)value).getTime());
|
129
|
+
if (value instanceof java.util.UUID) {
|
130
|
+
RubyClass clazz = (RubyClass)context.getRuntime().getClassFromPath("Diametric::Persistence::UUID");
|
131
|
+
DiametricUUID diametric_uuid = (DiametricUUID)clazz.allocate();
|
132
|
+
diametric_uuid.init((java.util.UUID)value);
|
133
|
+
return diametric_uuid;
|
134
|
+
}
|
135
|
+
return JavaUtil.convertJavaToUsableRubyObject(runtime, value);
|
136
|
+
}
|
137
|
+
|
138
|
+
static List<Object> convertRubyTxDataToJava(ThreadContext context, IRubyObject arg) {
|
139
|
+
List<Object> tx_data = null;
|
140
|
+
if (arg instanceof RubyArray) {
|
141
|
+
tx_data = fromRubyArray(context, arg);
|
142
|
+
} else {
|
143
|
+
Object obj = arg.toJava(Object.class);
|
144
|
+
if (obj instanceof clojure.lang.PersistentVector) {
|
145
|
+
tx_data = (clojure.lang.PersistentVector)obj;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
return tx_data;
|
149
|
+
}
|
150
|
+
|
151
|
+
private static List<Object> fromRubyArray(ThreadContext context, IRubyObject arg) {
|
152
|
+
RubyArray ruby_tx_data = (RubyArray)arg;
|
153
|
+
List<Object> java_tx_data = new ArrayList<Object>();
|
154
|
+
for (int i=0; i<ruby_tx_data.getLength(); i++) {
|
155
|
+
IRubyObject element = (IRubyObject) ruby_tx_data.get(i);
|
156
|
+
if (element instanceof RubyHash) {
|
157
|
+
RubyHash ruby_hash = (RubyHash) element;
|
158
|
+
Map<Object, Object> keyvals = new HashMap<Object, Object>();
|
159
|
+
while (true) {
|
160
|
+
IRubyObject pair = ruby_hash.shift(context);
|
161
|
+
if (pair instanceof RubyNil) break;
|
162
|
+
Object key = DiametricUtils.convertRubyToJava(context, ((RubyArray) pair).shift(context));
|
163
|
+
Object value = DiametricUtils.convertRubyToJava(context, ((RubyArray) pair).shift(context));
|
164
|
+
keyvals.put(key, value);
|
165
|
+
}
|
166
|
+
java_tx_data.add(Collections.unmodifiableMap(keyvals));
|
167
|
+
} else if (element instanceof RubyArray) {
|
168
|
+
RubyArray ruby_array = (RubyArray) element;
|
169
|
+
List<Object> keyvals = new ArrayList<Object>();
|
170
|
+
while (true) {
|
171
|
+
IRubyObject ruby_element = ruby_array.shift(context);
|
172
|
+
if (ruby_element instanceof RubyNil) break;
|
173
|
+
Object key_or_value = DiametricUtils.convertRubyToJava(context, ruby_element);
|
174
|
+
keyvals.add(key_or_value);
|
175
|
+
}
|
176
|
+
java_tx_data.add(Collections.unmodifiableList(keyvals));
|
177
|
+
} else {
|
178
|
+
continue;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
return java_tx_data;
|
182
|
+
}
|
183
|
+
}
|
data/lib/boolean_type.rb
ADDED
data/lib/diametric.rb
CHANGED
@@ -6,6 +6,30 @@ require "diametric/errors"
|
|
6
6
|
|
7
7
|
require 'diametric/config'
|
8
8
|
|
9
|
+
def is_jruby?
|
10
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
11
|
+
true
|
12
|
+
else
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if is_jruby?
|
18
|
+
require 'lock_jar'
|
19
|
+
jar_file = File.join(File.dirname(__FILE__), "..", "Jarfile")
|
20
|
+
lock_file = File.join(File.dirname(__FILE__), "..", "Jarfile.lock")
|
21
|
+
current_dir = Dir.pwd
|
22
|
+
Dir.chdir(File.dirname(lock_file))
|
23
|
+
LockJar.lock(jar_file)
|
24
|
+
LockJar.install(lock_file)
|
25
|
+
LockJar.load(lock_file)
|
26
|
+
Dir.chdir(current_dir)
|
27
|
+
|
28
|
+
require 'diametric_service.jar'
|
29
|
+
require 'diametric/diametric'
|
30
|
+
require 'diametric/persistence/peer'
|
31
|
+
end
|
32
|
+
|
9
33
|
if defined?(Rails)
|
10
34
|
require 'diametric/railtie'
|
11
35
|
end
|
data/lib/diametric/entity.rb
CHANGED
@@ -3,6 +3,9 @@ require "edn"
|
|
3
3
|
require 'active_support/core_ext'
|
4
4
|
require 'active_support/inflector'
|
5
5
|
require 'active_model'
|
6
|
+
require 'set'
|
7
|
+
require 'value_enums'
|
8
|
+
require 'boolean_type'
|
6
9
|
|
7
10
|
module Diametric
|
8
11
|
|
@@ -29,15 +32,19 @@ module Diametric
|
|
29
32
|
# @return [Integer]
|
30
33
|
module Entity
|
31
34
|
Ref = "ref"
|
35
|
+
UUID = "uuid"
|
36
|
+
Double = "double"
|
32
37
|
# Conversions from Ruby types to Datomic types.
|
33
38
|
VALUE_TYPES = {
|
34
39
|
Symbol => "keyword",
|
35
40
|
String => "string",
|
36
41
|
Integer => "long",
|
37
|
-
Float => "
|
42
|
+
Float => "double",
|
38
43
|
BigDecimal => "bigdec",
|
39
44
|
DateTime => "instant",
|
40
|
-
|
45
|
+
Boolean => "boolean",
|
46
|
+
URI => "uri",
|
47
|
+
UUID => "uuid"
|
41
48
|
}
|
42
49
|
|
43
50
|
DEFAULT_OPTIONS = {
|
@@ -56,6 +63,7 @@ module Diametric
|
|
56
63
|
base.class_eval do
|
57
64
|
@attributes = {}
|
58
65
|
@defaults = {}
|
66
|
+
@enums = {}
|
59
67
|
@namespace_prefix = nil
|
60
68
|
@partition = :"db.part/user"
|
61
69
|
end
|
@@ -163,18 +171,57 @@ module Diametric
|
|
163
171
|
@attributes.keys
|
164
172
|
end
|
165
173
|
|
174
|
+
# @return [Array<Symbol>] Names of the entity's enums.
|
175
|
+
def enum_names
|
176
|
+
@enums.keys
|
177
|
+
end
|
178
|
+
|
179
|
+
# Add an enum to a {Diametric::Entity}.
|
180
|
+
#
|
181
|
+
# enum is used when attribute type is Ref and refers
|
182
|
+
# a set of values.
|
183
|
+
# name should be the same as corresponding attribute name.
|
184
|
+
#
|
185
|
+
# @example Add an enum of colors
|
186
|
+
# class Palette
|
187
|
+
# attribute :color, Ref
|
188
|
+
# enum :color, [:blue, :green, :yellow, :orange]
|
189
|
+
# end
|
190
|
+
# p = Pallet.new
|
191
|
+
# p.color = Pallet::Color::Green
|
192
|
+
#
|
193
|
+
# @param name [Symbol] The enum's name.
|
194
|
+
# @param values [Array] The enum values.
|
195
|
+
#
|
196
|
+
# @return void
|
197
|
+
def enum(name, values)
|
198
|
+
enum_values = nil
|
199
|
+
enum_values = values.to_set if values.is_a?(Array)
|
200
|
+
enum_values = values if values.is_a?(Set)
|
201
|
+
raise RuntimeError "values should be Array or Set" if enum_values.nil?
|
202
|
+
enum_name = name.to_s.capitalize
|
203
|
+
syms = values.collect(&:to_s).collect(&:upcase).collect(&:to_sym)
|
204
|
+
class_eval("module #{enum_name};enum #{syms};end")
|
205
|
+
@enums[name] = syms
|
206
|
+
end
|
207
|
+
|
166
208
|
# Generates a Datomic schema for a model's attributes.
|
167
209
|
#
|
168
210
|
# @return [Array] A Datomic schema, as Ruby data that can be
|
169
211
|
# converted to EDN.
|
170
212
|
def schema
|
213
|
+
return peer_schema if self.instance_variable_get("@peer")
|
214
|
+
rest_schema
|
215
|
+
end
|
216
|
+
|
217
|
+
def rest_schema
|
171
218
|
defaults = {
|
172
219
|
:"db/id" => tempid(:"db.part/db"),
|
173
220
|
:"db/cardinality" => :"db.cardinality/one",
|
174
221
|
:"db.install/_attribute" => :"db.part/db"
|
175
222
|
}
|
176
223
|
|
177
|
-
@attributes.reduce([]) do |schema, (attribute, opts)|
|
224
|
+
schema_array = @attributes.reduce([]) do |schema, (attribute, opts)|
|
178
225
|
opts = opts.dup
|
179
226
|
value_type = opts.delete(:value_type)
|
180
227
|
|
@@ -193,19 +240,147 @@ module Diametric
|
|
193
240
|
:"db/valueType" => value_type(value_type),
|
194
241
|
}).merge(opts)
|
195
242
|
end
|
243
|
+
|
244
|
+
enum_schema = [
|
245
|
+
:"db/add", tempid(:"db.part/user"), :"db/ident"
|
246
|
+
]
|
247
|
+
prefix = self.name.downcase
|
248
|
+
@enums.each do |key, values|
|
249
|
+
values.each do |value|
|
250
|
+
ident_value = :"#{prefix}.#{key.downcase}/#{value.to_s.sub(/_/, "-").downcase}"
|
251
|
+
es = [:"db/add", tempid(:"db.part/user"), :"db/ident", ident_value]
|
252
|
+
schema_array << es
|
253
|
+
end
|
254
|
+
end
|
255
|
+
schema_array
|
256
|
+
end
|
257
|
+
|
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
|
196
298
|
end
|
197
299
|
|
198
300
|
# Given a set of Ruby data returned from a Datomic query, this
|
199
301
|
# can re-hydrate that data into a model instance.
|
200
302
|
#
|
201
303
|
# @return [Entity]
|
202
|
-
def from_query(query_results)
|
304
|
+
def from_query(query_results, connection=nil, resolve=false)
|
203
305
|
dbid = query_results.shift
|
204
306
|
widget = self.new(Hash[attribute_names.zip query_results])
|
205
307
|
widget.dbid = dbid
|
308
|
+
|
309
|
+
if resolve
|
310
|
+
widget = resolve_ref_dbid(widget, connection)
|
311
|
+
end
|
206
312
|
widget
|
207
313
|
end
|
208
314
|
|
315
|
+
def resolve_ref_dbid(parent, connection)
|
316
|
+
parent.class.attribute_names.each do |e|
|
317
|
+
if parent.class.attributes[e][:value_type] == "ref"
|
318
|
+
ref = parent.instance_variable_get("@#{e.to_s}")
|
319
|
+
if ref.is_a?(Fixnum) || ref.is_a?(Java::DatomicQuery::EntityMap)
|
320
|
+
child = from_dbid_or_entity(ref, connection)
|
321
|
+
child = resolve_ref_dbid(child, connection)
|
322
|
+
parent.instance_variable_set("@#{e.to_s}", child)
|
323
|
+
elsif ref.is_a?(Set)
|
324
|
+
children = ref.inject(Set.new) do |memo, entity|
|
325
|
+
child = from_dbid_or_entity(entity, connection)
|
326
|
+
memo.add(child)
|
327
|
+
memo
|
328
|
+
end
|
329
|
+
parent.instance_variable_set("@#{e.to_s}", children)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
parent
|
334
|
+
end
|
335
|
+
|
336
|
+
def from_dbid_or_entity(thing, conn_or_db=nil, resolve=false)
|
337
|
+
conn_or_db ||= Diametric::Persistence::Peer.connect.db
|
338
|
+
|
339
|
+
if conn_or_db.respond_to?(:db)
|
340
|
+
conn_or_db = conn_or_db.db
|
341
|
+
end
|
342
|
+
|
343
|
+
if thing.is_a? Fixnum
|
344
|
+
dbid = thing
|
345
|
+
entity = conn_or_db.entity(dbid)
|
346
|
+
elsif thing.respond_to?(:eid)
|
347
|
+
dbid = thing.eid
|
348
|
+
if entity.respond_to?(:keys)
|
349
|
+
entity = thing
|
350
|
+
else
|
351
|
+
entity = conn_or_db.entity(dbid)
|
352
|
+
end
|
353
|
+
elsif thing.respond_to?(:to_java)
|
354
|
+
dbid = thing.to_java
|
355
|
+
entity = conn_or_db.entity(dbid)
|
356
|
+
else
|
357
|
+
return thing
|
358
|
+
end
|
359
|
+
first_key = entity.keys.first
|
360
|
+
match_data = /:([a-zA-Z0-9_]+)\/([a-zA-Z0-9_]+)/.match(first_key)
|
361
|
+
entity_name = match_data[1].capitalize.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}"}
|
362
|
+
instance = eval("#{entity_name}.new")
|
363
|
+
instance.send("#{match_data[2]}=", entity[first_key])
|
364
|
+
entity.keys[1..-1].each do |key|
|
365
|
+
match_data = /:([a-zA-Z0-9_]+)\/([a-zA-Z0-9_]+)/.match(key)
|
366
|
+
instance.send("#{match_data[2]}=", entity[key])
|
367
|
+
end
|
368
|
+
instance.send("dbid=", Diametric::Persistence::Object.new(dbid))
|
369
|
+
|
370
|
+
if resolve
|
371
|
+
instance = resolve_ref_dbid(instance, conn_or_db)
|
372
|
+
end
|
373
|
+
|
374
|
+
instance
|
375
|
+
end
|
376
|
+
|
377
|
+
def find(id)
|
378
|
+
if self.instance_variable_get("@peer")
|
379
|
+
connection ||= Diametric::Persistence::Peer.connect
|
380
|
+
end
|
381
|
+
from_dbid_or_entity(id, connection)
|
382
|
+
end
|
383
|
+
|
209
384
|
# Returns the prefix for this model used in Datomic. Can be
|
210
385
|
# overriden by declaring {#namespace_prefix}
|
211
386
|
#
|
@@ -227,7 +402,15 @@ module Diametric
|
|
227
402
|
#
|
228
403
|
# @return [EDN::Type::Unknown] Temporary id placeholder.
|
229
404
|
def tempid(*e)
|
230
|
-
|
405
|
+
if self.instance_variable_get("@peer")
|
406
|
+
if e[0].to_s.include?("user")
|
407
|
+
return Diametric::Persistence::Peer.tempid(":db.part/user")
|
408
|
+
else
|
409
|
+
return Diametric::Persistence::Peer.tempid(":db.part/db")
|
410
|
+
end
|
411
|
+
else
|
412
|
+
EDN.tagged_element('db/id', e)
|
413
|
+
end
|
231
414
|
end
|
232
415
|
|
233
416
|
# Namespace a attribute for Datomic.
|
@@ -237,9 +420,13 @@ module Diametric
|
|
237
420
|
#
|
238
421
|
# @return [Symbol] Namespaced attribute.
|
239
422
|
def namespace(ns, attribute)
|
240
|
-
|
423
|
+
if self.instance_variable_get("@peer")
|
424
|
+
":" + [ns.to_s, attribute.to_s].join("/")
|
425
|
+
else
|
426
|
+
[ns.to_s, attribute.to_s].join("/").to_sym
|
427
|
+
end
|
241
428
|
end
|
242
|
-
|
429
|
+
|
243
430
|
# Raise an error if validation failed.
|
244
431
|
#
|
245
432
|
# @example Raise the validation error.
|
@@ -253,7 +440,7 @@ module Diametric
|
|
253
440
|
private
|
254
441
|
|
255
442
|
def value_type(vt)
|
256
|
-
if vt.is_a?(Class)
|
443
|
+
if vt.is_a?(Class) || vt.is_a?(Module)
|
257
444
|
vt = VALUE_TYPES[vt]
|
258
445
|
end
|
259
446
|
namespace("db.type", vt)
|
@@ -323,6 +510,7 @@ module Diametric
|
|
323
510
|
attribute_names.each do |attribute_name|
|
324
511
|
cardinality = self.class.attributes[attribute_name.to_sym][:cardinality]
|
325
512
|
|
513
|
+
#if cardinality == :many && self.class.instance_variable_get("@peer").nil?
|
326
514
|
if cardinality == :many
|
327
515
|
txes += cardinality_many_tx_data(attribute_name)
|
328
516
|
else
|
@@ -331,9 +519,13 @@ module Diametric
|
|
331
519
|
end
|
332
520
|
|
333
521
|
if entity_tx.present?
|
334
|
-
|
522
|
+
if self.class.instance_variable_get("@peer")
|
523
|
+
@dbid ||= tempid
|
524
|
+
txes << entity_tx.merge({":db/id" => dbid})
|
525
|
+
else
|
526
|
+
txes << entity_tx.merge({:"db/id" => dbid || tempid})
|
527
|
+
end
|
335
528
|
end
|
336
|
-
|
337
529
|
txes
|
338
530
|
end
|
339
531
|
|
@@ -344,13 +536,26 @@ module Diametric
|
|
344
536
|
protractions = curr - prev
|
345
537
|
retractions = prev - curr
|
346
538
|
|
347
|
-
txes = []
|
348
539
|
namespaced_attribute = self.class.namespace(self.class.prefix, attribute_name)
|
349
|
-
txes
|
350
|
-
|
540
|
+
txes = []
|
541
|
+
if self.class.instance_variable_get("@peer")
|
542
|
+
@dbid ||= tempid
|
543
|
+
txes_data(txes, ":db/retract", namespaced_attribute, retractions) unless retractions.empty?
|
544
|
+
txes_data(txes, ":db/add", namespaced_attribute, protractions) unless protractions.empty?
|
545
|
+
else
|
546
|
+
txes << [:"db/retract", (dbid || tempid), namespaced_attribute, retractions.to_a] unless retractions.empty?
|
547
|
+
txes << [:"db/add", (dbid || tempid) , namespaced_attribute, protractions.to_a] unless protractions.empty?
|
548
|
+
end
|
351
549
|
txes
|
352
550
|
end
|
353
|
-
|
551
|
+
|
552
|
+
def txes_data(txes, op, namespaced_attribute, set)
|
553
|
+
set.to_a.each do |s|
|
554
|
+
value = s.respond_to?(:dbid) ? s.dbid : s
|
555
|
+
txes << [op, @dbid, namespaced_attribute, value]
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
354
559
|
# Returns hash of all attributes for this object
|
355
560
|
#
|
356
561
|
# @return [Hash<Symbol, object>] Hash of atrributes
|