active-orient 0.42 → 0.79

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 (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