active-orient 0.42 → 0.79

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/Gemfile +13 -5
  4. data/Guardfile +12 -4
  5. data/README.md +67 -280
  6. data/VERSION +1 -1
  7. data/active-orient.gemspec +6 -5
  8. data/bin/active-orient-0.6.gem +0 -0
  9. data/bin/active-orient-console +85 -0
  10. data/config/boot.rb +72 -1
  11. data/config/config.yml +10 -0
  12. data/config/connect.yml +9 -4
  13. data/examples/books.rb +92 -40
  14. data/examples/streets.rb +89 -85
  15. data/examples/test_commands.rb +97 -0
  16. data/examples/test_commands_2.rb +59 -0
  17. data/examples/test_commands_3.rb +55 -0
  18. data/examples/test_commands_4.rb +33 -0
  19. data/examples/time_graph.md +162 -0
  20. data/lib/active-orient.rb +75 -9
  21. data/lib/base.rb +238 -169
  22. data/lib/base_properties.rb +68 -60
  23. data/lib/class_utils.rb +226 -0
  24. data/lib/database_utils.rb +98 -0
  25. data/lib/init.rb +79 -0
  26. data/lib/java-api.rb +442 -0
  27. data/lib/jdbc.rb +211 -0
  28. data/lib/model/custom.rb +26 -0
  29. data/lib/model/edge.rb +70 -0
  30. data/lib/model/model.rb +134 -0
  31. data/lib/model/the_class.rb +607 -0
  32. data/lib/model/the_record.rb +266 -0
  33. data/lib/model/vertex.rb +236 -0
  34. data/lib/orientdb_private.rb +48 -0
  35. data/lib/other.rb +371 -0
  36. data/lib/railtie.rb +68 -0
  37. data/lib/rest/change.rb +147 -0
  38. data/lib/rest/create.rb +279 -0
  39. data/lib/rest/delete.rb +134 -0
  40. data/lib/rest/operations.rb +211 -0
  41. data/lib/rest/read.rb +171 -0
  42. data/lib/rest/rest.rb +112 -0
  43. data/lib/rest_disabled.rb +24 -0
  44. data/lib/support/logging.rb +38 -0
  45. data/lib/support/orient.rb +196 -0
  46. data/lib/support/orientquery.rb +469 -0
  47. data/rails.md +154 -0
  48. data/rails/activeorient.rb +32 -0
  49. data/rails/config.yml +10 -0
  50. data/rails/connect.yml +17 -0
  51. metadata +65 -24
  52. data/active-orient-0.4.gem +0 -0
  53. data/active-orient-0.41.gem +0 -0
  54. data/lib/model.rb +0 -468
  55. data/lib/orient.rb +0 -98
  56. data/lib/query.rb +0 -88
  57. data/lib/rest.rb +0 -1059
  58. data/lib/support.rb +0 -372
  59. data/test.rb +0 -4
  60. data/usecase.md +0 -91
@@ -0,0 +1,266 @@
1
+ module ModelRecord
2
+ ############### RECORD FUNCTIONS ###############
3
+
4
+ ############# GET #############
5
+
6
+ def from_orient # :nodoc:
7
+ self
8
+ end
9
+
10
+ # Returns just the name of the Class
11
+
12
+ def self.classname # :nodoc:
13
+ self.class.to_s.split(':')[-1]
14
+ end
15
+ =begin
16
+ flag whether a property exists on the Record-level
17
+ =end
18
+ def has_property? property
19
+ attributes.keys.include? property.to_s
20
+ end
21
+
22
+ def properties
23
+ { "@type" => "d", "@class" => self.metadata[:class] }.merge attributes
24
+ end
25
+
26
+ #
27
+ # Obtain the RID of the Record (format: *00:00*)
28
+ #
29
+
30
+ def rid
31
+ begin
32
+ "#{@metadata[:cluster]}:#{@metadata[:record]}"
33
+ rescue
34
+ "0:0"
35
+ end
36
+ end
37
+ =begin
38
+ The extended representation of RID (format: *#00:00* )
39
+ =end
40
+ def rrid
41
+ "#" + rid
42
+ end
43
+ alias to_orient rrid
44
+
45
+ def to_or
46
+ rid.rid? ? rrid : "{ #{embedded} }"
47
+ end
48
+ =begin
49
+ Query uses the current model-record as origin of the query.
50
+
51
+ It sends the OrientSupport::OrientQuery directly to the database and returns an
52
+ ActiveOrient::Model-Object or an Array of Model-Objects as result.
53
+
54
+ *Usage:* Query the Database by traversing through edges and vertices starting at a known location
55
+
56
+ =end
57
+
58
+ def query query
59
+
60
+ query.from = rrid if query.is_a? OrientSupport::OrientQuery
61
+ result = orientdb.execute do
62
+ query.to_s
63
+ end
64
+ if result.is_a? Array
65
+ OrientSupport::Array.new work_on: self, work_with: result
66
+ else
67
+ result
68
+ end # return value
69
+ end
70
+
71
+ =begin
72
+ Fires a »where-Query» to the database starting with the current model-record.
73
+
74
+ Attributes:
75
+ * a string ( obj.find "in().out().some_attribute >3" )
76
+ * a hash ( obj.find 'some_embedded_obj.name' => 'test' )
77
+ * an array
78
+
79
+ Returns the result-set, ie. a Query-Object which contains links to the addressed records.
80
+
81
+ =end
82
+ def find attributes = {}
83
+ q = OrientSupport::OrientQuery.new from: self, where: attributes
84
+ query q
85
+ end
86
+
87
+ # Get the version of the object
88
+ def version # :nodoc:
89
+ if document.present?
90
+ document.version
91
+ else
92
+ @metadata[:version]
93
+ end
94
+ end
95
+
96
+ def version= version # :nodoc:
97
+ @metadata[:version] = version
98
+ end
99
+
100
+ def increment_version # :nodoc:
101
+ @metadata[:version] += 1
102
+ end
103
+
104
+
105
+ ############# DELETE ###########
106
+
107
+ # Removes the Model-Instance from the database.
108
+ #
109
+ # It is overloaded in Vertex and Edge.
110
+
111
+ def remove
112
+ orientdb.delete_record self
113
+ ActiveOrient::Base.remove_rid self ##if is_edge? # removes the obj from the rid_store
114
+ end
115
+
116
+ alias delete remove
117
+
118
+ ########### UPDATE ############
119
+
120
+ =begin
121
+ Convient update of the dataset by calling sql-patch
122
+
123
+ Previously changed attributes are saved to the database.
124
+
125
+ Using the optional :set argument ad-hoc attributes can be defined
126
+
127
+ obj = ActiveOrient::Model::Contracts.first
128
+ obj.name = 'new_name'
129
+ obj.update set: { yesterdays_event: 35 }
130
+ updates both, the »name« and the »yesterdays_event«-properties
131
+
132
+ _note:_ The keyword »set« is optional, thus
133
+ obj.update yesterdays_event: 35
134
+ is identical
135
+ =end
136
+
137
+ def update set:{}, add: nil, to: nil, **args
138
+ logger.progname = 'ActiveOrient::Model#Update'
139
+
140
+ if block_given? # calling vs. a block is used internally
141
+ # to remove an Item from lists and sets call update(remove: true){ query }
142
+ set_or_remove = args[:remove].present? ? "remove" : "set"
143
+ transfer_content from: query( "update #{rrid} #{set_or_remove} #{ yield } return after @this" )&.first
144
+ else
145
+ set.merge! args
146
+ # set.merge updated_at: DateTime.now
147
+
148
+ if rid.rid?
149
+ transfer_content from: db.update( self, set, version )
150
+ # if the updated dataset changed, drop the changes made siently
151
+ # self # return value
152
+ else # new record
153
+ @attributes.merge! set
154
+ save
155
+ end
156
+ end
157
+
158
+ end
159
+
160
+ # mocking active record
161
+ def update_attribute the_attribute, the_value # :nodoc:
162
+ update { " #{the_attribute} = #{the_value.to_or} " }
163
+ end
164
+
165
+ def update_attributes **args # :nodoc:
166
+ update args
167
+ end
168
+
169
+ ########## SAVE ############
170
+
171
+ =begin
172
+ Saves the record by calling update or creating the record
173
+
174
+ ORD.create_class :a
175
+ a = A.new
176
+ a.test = 'test'
177
+ a.save
178
+
179
+ a = A.first
180
+ a.test = 'test'
181
+ a.save
182
+
183
+ =end
184
+ def save
185
+ transfer_content from: if rid.rid?
186
+ db.update self, attributes, version
187
+ else
188
+ db.create_record self, attributes: attributes, cache: false
189
+ end
190
+ ActiveOrient::Base.store_rid self
191
+ end
192
+
193
+ def reload!
194
+ transfer_content from: db.get_record(rid)
195
+ self
196
+ end
197
+
198
+
199
+ def transfer_content from:
200
+ # »from« can be either
201
+ # a model record (in case of create-record, get_record) or
202
+ # a hash containing {"@type"=>"d", "@rid"=>"#xx:yy", "@version"=>n, "@class"=>'a_classname'}
203
+ # and a list of updated properties (in case of db.update). Then update the version field and the
204
+ # attributes.
205
+ if from.is_a? ActiveOrient::Model
206
+ @metadata = from.metadata
207
+ @attributes = from.attributes
208
+ else
209
+ self.version = from['@version']
210
+ # throw from["@..."] away and convert keys to symbols, merge that into attributes
211
+ @attributes.merge! Hash[ from.delete_if{|k,_| k =~ /^@/}.map{|k,v| [k.to_sym, v.from_orient]}]
212
+ end
213
+ end
214
+ ########## CHECK PROPERTY ########
215
+
216
+ =begin
217
+ An Edge is defined
218
+ * when inherented from the superclass »E» (formal definition)
219
+ * if it has an in- and an out property
220
+
221
+ Actually we just check the second term as we trust the constuctor to work properly
222
+ =end
223
+
224
+ def is_edge? # :nodoc:
225
+ attributes.keys.include?('in') && attributes.keys.include?('out')
226
+ end
227
+
228
+ =begin
229
+ How to handle other calls
230
+
231
+ * if attribute is specified, display it
232
+ * if attribute= is provided, assign to the known property or create a new one
233
+
234
+ Example:
235
+ ORD.create_class :a
236
+ a = A.new
237
+ a.test= 'test' # <--- attribute: 'test=', argument: 'test'
238
+ a.test # <--- attribute: 'test' --> fetch attributes[:test]
239
+
240
+ Assignments are performed only in ruby-space.
241
+ Automatic database-updates are deactivated for now
242
+ =end
243
+ def method_missing *args
244
+ # if the first entry of the parameter-array is a known attribute
245
+ # proceed with the assignment
246
+ if args.size == 1
247
+ attributes[args.first.to_sym] # return the attribute-value
248
+ elsif args[0][-1] == "="
249
+ if args.size == 2
250
+ # if rid.rid?
251
+ # update set:{ args[0][0..-2] => args.last }
252
+ # else
253
+ self.attributes[ args[0][0..-2] ] = args.last
254
+ # end
255
+ else
256
+ self.attributes[ args[0][0..-2] ] = args[1 .. -1]
257
+ # update set: {args[0][0..-2] => args[1 .. -1] } if rid.rid?
258
+ end
259
+ else
260
+ raise NameError, "Unknown method call #{args.first.to_s}", caller
261
+ end
262
+ end
263
+ #end
264
+
265
+
266
+ end
@@ -0,0 +1,236 @@
1
+ class V < ActiveOrient::Model
2
+ ## link to the library-class
3
+
4
+ =begin
5
+ specialized creation of vertices, overloads model#create
6
+ =end
7
+ def self.create( **keyword_arguments )
8
+ new_vert = db.create_vertex self, attributes: keyword_arguments
9
+ new_vert = new_vert.pop if new_vert.is_a?( Array) && new_vert.size == 1
10
+ if new_vert.nil?
11
+ logger.error('Vertex'){ "Table #{ref_name} ->> create failed: #{keyword_arguments.inspect}" }
12
+ elsif block_given?
13
+ yield new_vert
14
+ else
15
+ new_vert # returns the created vertex (or an array of created vertices)
16
+ end
17
+ end
18
+ =begin
19
+ Vertex#delete fires a "delete vertex" command to the database.
20
+ The where statement can be empty ( "" or {}"), then all vertices are removed
21
+
22
+ The rid-cache is reseted, too
23
+ =end
24
+ def self.delete where: ""
25
+ db.execute { "delete vertex #{ref_name} #{db.compose_where(where)}" }
26
+ reset_rid_store
27
+ end
28
+
29
+ #Present Classes (Hierarchy)
30
+ #---
31
+ #- - E
32
+ # - - - e1
33
+ # - - e2
34
+ # - e3
35
+ #- - V
36
+ # - - - v1
37
+ # - - v2
38
+
39
+ #v.to_human
40
+ # => "<V2[#36:0]: in: {E2=>1}, node : 4>"
41
+ #
42
+ # v.detect_edges( :in, 2).to_human
43
+ # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
44
+ # v.detect_edges( :in, E1).to_human
45
+ # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
46
+ # v.detect_edges( :in, /e/).to_human
47
+ # => ["<E2: in : #<V2:0x0000000002e66228>, out : #<V1:0x0000000002ed0060>>"]
48
+ #
49
+ def detect_edges kind = :in, edge_name = nil # :nodoc:
50
+ ## returns a list of inherented classes
51
+ get_superclass = ->(e) do
52
+ n = orientdb.get_db_superclass(e)
53
+ n =='E' ? e : e + ',' + get_superclass[n]
54
+ end
55
+ if edge_name.nil?
56
+ edges(kind).map &:expand
57
+ else
58
+ e_name = if edge_name.is_a?(Regexp)
59
+ edge_name
60
+ else
61
+ Regexp.new case edge_name
62
+ when Class
63
+ edge_name.ref_name
64
+ when String
65
+ edge_name
66
+ when Symbol
67
+ edge_name.to_s
68
+ when Numeric
69
+ edge_name.to_i.to_s
70
+ end
71
+ end
72
+ the_edges = @metadata[:edges][kind].find_all{|y| get_superclass[y].split(',').detect{|x| x =~ e_name } }
73
+
74
+ the_edges.map do | the_edge|
75
+ candidate= attributes["#{kind.to_s}_#{the_edge}".to_sym]
76
+ candidate.present? ? candidate.map( &:expand ).first : nil
77
+ end
78
+ end
79
+ end
80
+
81
+ # Lists all connected Vertices
82
+ #
83
+ # The Edge-classes can be specified via Classname or a regular expression.
84
+ #
85
+ # If a regular expression is used, the database-names are searched.
86
+ def nodes in_or_out = :out, via: nil, where: nil, expand: false
87
+ if via.present?
88
+ edges = detect_edges( in_or_out, via )
89
+ detected_nodes = edges.map do |e|
90
+ q = OrientSupport::OrientQuery.new
91
+ q.nodes in_or_out, via: e.class, where: where, expand: expand
92
+ query( q )
93
+ end.first
94
+ end
95
+ end
96
+
97
+
98
+
99
+ =begin
100
+ »in« and »out« provide the main access to edges.
101
+ »in» is a reserved keyword. Therfore its only an alias to `in_e`.
102
+
103
+ If called without a parameter, all connected edges are retrieved.
104
+
105
+ If called with a string, symbol or class, the edge-class is resolved and even inherented
106
+ edges are retrieved.
107
+
108
+ =end
109
+
110
+ def in_e edge_name= nil
111
+ detect_edges :in, edge_name
112
+ end
113
+
114
+ alias_method :in, :in_e
115
+
116
+ def out edge_name = nil
117
+ detect_edges :out, edge_name
118
+ end
119
+ =begin
120
+ Retrieves connected edges
121
+
122
+ The basic usage is to fetch all/ incomming/ outgoing edges
123
+
124
+ Model-Instance.edges :in # :out | :all
125
+
126
+ One can filter specific edges by providing parts of the edge-name
127
+
128
+ Model-Instance.edges 'in_sector'
129
+ Model-Instance.edges /sector/
130
+
131
+ The method returns an array of rid's.
132
+
133
+ Example:
134
+
135
+ Industry.first.attributes.keys
136
+ => ["in_sector_classification", "k", "name", "created_at", "updated_at"] # edge--> in ...
137
+
138
+ Industry.first.edges :out
139
+ => []
140
+
141
+ Industry.first.edges :in
142
+ => ["#61:0", "#61:9", "#61:21", "#61:33", "#61:39", "#61:93", "#61:120", "#61:150", "#61:240", "#61:252", "#61:264", "#61:279", "#61:303", "#61:339" ...]
143
+
144
+
145
+
146
+ To fetch the associated records use the ActiveOrient::Model.autoload_object method
147
+
148
+ ActiveOrient::Model.autoload_object Industry.first.edges( :in).first
149
+ # or
150
+ Industry.autoload_object Industry.first.edges( /sector/ ).first
151
+ => #<SectorClassification:0x00000002daad20 @metadata={"type"=>"d", "class"=>"sector_classification", "version"=>1, "fieldTypes"=>"out=x,in=x", "cluster"=>61, "record"=>0},(...)
152
+
153
+ =end
154
+
155
+ def edges kind=:all # :all, :in, :out
156
+ expression = case kind
157
+ when :all
158
+ /^in|^out/
159
+ when :in
160
+ /^in/
161
+ when :out
162
+ /^out/
163
+ when String
164
+ /#{kind}/
165
+ when Regexp
166
+ kind
167
+ else
168
+ return []
169
+ end
170
+
171
+ edges = attributes.keys.find_all{ |x| x =~ expression }
172
+ edges.map{|x| attributes[x]}.flatten
173
+ end
174
+
175
+ =begin
176
+ »in_edges« and »out_edges« are shortcuts to »edges :in« and »edges :out«
177
+
178
+ Its easy to expand the result:
179
+ tg.out( :ohlc).out.out_edges
180
+ => [["#102:11032", "#121:0"]]
181
+ tg.out( :ohlc).out.out_edges.from_orient
182
+ => [[#<TG::GRID_OF:0x00000002620e38
183
+
184
+ this displays the out-edges correctly
185
+
186
+ whereas
187
+ tg.out( :ohlc).out.edges( :out)
188
+ => [["#101:11032", "#102:11032", "#94:10653", "#121:0"]]
189
+
190
+ returns all edges. The parameter (:out) is not recognized, because out is already a nested array.
191
+
192
+ this
193
+ tg.out( :ohlc).first.out.edges( :out)
194
+ is a walkaround, but using in_- and out_edges is more elegant.
195
+ =end
196
+ def in_edges
197
+ edges :in
198
+ end
199
+ def out_edges
200
+ edges :out
201
+ end
202
+
203
+ # def remove
204
+ # db.delete_vertex self
205
+ # end
206
+ =begin
207
+ Human readable represantation of Vertices
208
+
209
+ Format: < Classname : Edges, Attributes >
210
+ =end
211
+ def to_human
212
+ count_and_display_classes = ->(array){array.map(&:class)&.group_by(&:itself)&.transform_values(&:count)}
213
+
214
+ the_ins = count_and_display_classes[ in_e]
215
+ the_outs = count_and_display_classes[ out]
216
+
217
+ in_and_out = in_edges.empty? ? "" : "in: #{the_ins}, "
218
+ in_and_out += out_edges.empty? ? "" : "out: #{the_outs}, "
219
+
220
+
221
+ #Default presentation of ActiveOrient::Model-Objects
222
+
223
+ "<#{self.class.to_s.demodulize}[#{rid}]: " + in_and_out + content_attributes.map do |attr, value|
224
+ v= case value
225
+ when ActiveOrient::Model
226
+ "< #{self.class.to_s.demodulize} : #{value.rid} >"
227
+ when OrientSupport::Array
228
+ value.to_s
229
+ # value.rrid #.to_human #.map(&:to_human).join("::")
230
+ else
231
+ value.from_orient
232
+ end
233
+ "%s : %s" % [ attr, v] unless v.nil?
234
+ end.compact.sort.join(', ') + ">".gsub('"' , ' ')
235
+ end
236
+ end