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.
Files changed (67) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +21 -18
  3. data/Jarfile +15 -1
  4. data/README.md +22 -14
  5. data/Rakefile +17 -1
  6. data/bin/datomic-rest +33 -0
  7. data/bin/download-datomic +13 -0
  8. data/datomic_version.yml +4 -0
  9. data/diametric.gemspec +9 -6
  10. data/ext/diametric/DiametricCollection.java +147 -0
  11. data/ext/diametric/DiametricConnection.java +113 -0
  12. data/ext/diametric/DiametricDatabase.java +107 -0
  13. data/ext/diametric/DiametricEntity.java +90 -0
  14. data/ext/diametric/DiametricListenableFuture.java +47 -0
  15. data/ext/diametric/DiametricObject.java +66 -0
  16. data/ext/diametric/DiametricPeer.java +414 -0
  17. data/ext/diametric/DiametricService.java +102 -0
  18. data/ext/diametric/DiametricUUID.java +61 -0
  19. data/ext/diametric/DiametricUtils.java +183 -0
  20. data/lib/boolean_type.rb +3 -0
  21. data/lib/diametric.rb +24 -0
  22. data/lib/diametric/entity.rb +219 -14
  23. data/lib/diametric/generators/active_model.rb +2 -2
  24. data/lib/diametric/persistence.rb +0 -1
  25. data/lib/diametric/persistence/common.rb +28 -9
  26. data/lib/diametric/persistence/peer.rb +122 -87
  27. data/lib/diametric/persistence/rest.rb +4 -3
  28. data/lib/diametric/query.rb +94 -23
  29. data/lib/diametric/rest_service.rb +74 -0
  30. data/lib/diametric/service_base.rb +77 -0
  31. data/lib/diametric/transactor.rb +86 -0
  32. data/lib/diametric/version.rb +1 -1
  33. data/lib/diametric_service.jar +0 -0
  34. data/lib/value_enums.rb +8 -0
  35. data/spec/conf_helper.rb +55 -0
  36. data/spec/config/free-transactor-template.properties +73 -0
  37. data/spec/config/logback.xml +59 -0
  38. data/spec/data/seattle-data0.dtm +452 -0
  39. data/spec/data/seattle-data1.dtm +326 -0
  40. data/spec/developer_create_sample.rb +39 -0
  41. data/spec/developer_query_spec.rb +120 -0
  42. data/spec/diametric/config_spec.rb +1 -1
  43. data/spec/diametric/entity_spec.rb +263 -0
  44. data/spec/diametric/peer_api_spec.rb +147 -0
  45. data/spec/diametric/persistence/peer_many2many_spec.rb +76 -0
  46. data/spec/diametric/persistence/peer_spec.rb +13 -22
  47. data/spec/diametric/persistence/rest_spec.rb +12 -19
  48. data/spec/diametric/query_spec.rb +4 -5
  49. data/spec/diametric/rest_service_spec.rb +56 -0
  50. data/spec/diametric/transactor_spec.rb +68 -0
  51. data/spec/integration_spec.rb +5 -3
  52. data/spec/parent_child_sample.rb +42 -0
  53. data/spec/peer_integration_spec.rb +106 -22
  54. data/spec/peer_seattle_spec.rb +200 -0
  55. data/spec/rc2013_seattle_big.rb +82 -0
  56. data/spec/rc2013_seattle_small.rb +60 -0
  57. data/spec/rc2013_simple_sample.rb +72 -0
  58. data/spec/seattle_integration_spec.rb +106 -0
  59. data/spec/simple_validation_sample.rb +31 -0
  60. data/spec/spec_helper.rb +31 -45
  61. data/spec/support/entities.rb +157 -0
  62. data/spec/support/gen_entity_class.rb +2 -0
  63. data/spec/support/persistence_examples.rb +9 -5
  64. data/spec/test_version_file.yml +4 -0
  65. metadata +131 -75
  66. data/Jarfile.lock +0 -134
  67. 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
+ }
@@ -0,0 +1,3 @@
1
+ module Boolean; end
2
+ class TrueClass; include Boolean; end
3
+ class FalseClass; include Boolean; end
@@ -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
@@ -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 => "float",
42
+ Float => "double",
38
43
  BigDecimal => "bigdec",
39
44
  DateTime => "instant",
40
- URI => "uri"
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
- EDN.tagged_element('db/id', e)
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
- [ns.to_s, attribute.to_s].join("/").to_sym
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
- txes << entity_tx.merge({:"db/id" => dbid || tempid})
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 << [:"db/retract", (dbid || tempid), namespaced_attribute, retractions.to_a] unless retractions.empty?
350
- txes << [:"db/add", (dbid || tempid) , namespaced_attribute, protractions.to_a] unless protractions.empty?
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