active-orient 0.6 → 0.42

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 +4 -4
  2. data/.gitignore +0 -1
  3. data/Gemfile +4 -10
  4. data/Guardfile +4 -12
  5. data/README.md +198 -261
  6. data/VERSION +1 -1
  7. data/active-orient-0.4.gem +0 -0
  8. data/active-orient-0.41.gem +0 -0
  9. data/active-orient.gemspec +5 -6
  10. data/config/boot.rb +0 -84
  11. data/config/connect.yml +4 -8
  12. data/examples/books.rb +39 -86
  13. data/examples/streets.rb +84 -85
  14. data/lib/active-orient.rb +9 -57
  15. data/lib/base.rb +145 -172
  16. data/lib/base_properties.rb +44 -40
  17. data/lib/model.rb +468 -0
  18. data/lib/orient.rb +60 -114
  19. data/lib/query.rb +73 -71
  20. data/lib/rest.rb +1059 -0
  21. data/lib/support.rb +319 -386
  22. data/test.rb +4 -0
  23. data/usecase.md +91 -0
  24. metadata +20 -65
  25. data/bin/active-orient-console +0 -38
  26. data/config/config.yml +0 -10
  27. data/create_project +0 -19
  28. data/examples/test_commands.rb +0 -92
  29. data/examples/test_commands_2.rb +0 -54
  30. data/examples/test_commands_3.rb +0 -48
  31. data/examples/test_commands_4.rb +0 -28
  32. data/examples/time_graph.md +0 -162
  33. data/gratefuldeadconcerts.md +0 -94
  34. data/lib/class_utils.rb +0 -300
  35. data/lib/database_utils.rb +0 -106
  36. data/lib/init.rb +0 -45
  37. data/lib/java-api.rb +0 -437
  38. data/lib/jdbc.rb +0 -211
  39. data/lib/model/edge.rb +0 -55
  40. data/lib/model/model.rb +0 -91
  41. data/lib/model/the_class.rb +0 -500
  42. data/lib/model/the_record.rb +0 -322
  43. data/lib/model/vertex.rb +0 -136
  44. data/lib/orientdb_private.rb +0 -48
  45. data/lib/other.rb +0 -330
  46. data/lib/rest/change.rb +0 -137
  47. data/lib/rest/create.rb +0 -488
  48. data/lib/rest/delete.rb +0 -134
  49. data/lib/rest/operations.rb +0 -160
  50. data/lib/rest/read.rb +0 -150
  51. data/lib/rest/rest.rb +0 -112
  52. data/lib/rest_disabled.rb +0 -24
  53. data/linkmap.md +0 -75
  54. data/namespace.md +0 -111
  55. data/old_lib_functions/two_general_class.rb +0 -139
  56. data/rails.md +0 -125
  57. data/rails/activeorient.rb +0 -53
  58. data/rails/config.yml +0 -10
  59. data/rails/connect.yml +0 -17
  60. data/usecase_oo.md +0 -61
@@ -1,152 +1,98 @@
1
1
  module OrientSupport
2
-
3
- # This Module fences specialized Ruby objects
2
+ =begin
3
+ This Module fences specialized ruby objects
4
+ =end
4
5
 
5
6
  class Array < Array
6
7
  include OrientSupport::Support
7
- mattr_accessor :logger
8
-
9
8
  =begin
10
- Initialisation method stores the model-instance to work on in @orient.
11
- The keyword_parameter "work_on" holds the record to work_ion.
12
- Ihe second argument is the array to work with
9
+ Initialisation method stores the modelinstance in @orient.
13
10
 
14
- If instead of a model-instance the model-class is provided, a new model-instance is created and returned
15
- Its up to the caller to save the new instance in the database
11
+ Further a list of array-elements is expected, which are forwarded (as Array) to Array
16
12
 
17
- Further a list of array-elements is expected, which are forwarded (as Array) to Array
18
- =end
19
-
20
- def initialize work_on:, work_with:
21
- @orient = work_on.class == Class ? work_on.new : work_on
22
- super work_with
23
- @name = @orient.attributes.key(self)
24
- # puts "ORIENT: #{@orient.inspect} "
25
- @name = yield if @name.nil? && block_given?
26
- # puts "NAME: #{@name.inspect}"
27
- # puts "SELF: #{self.inspect}"
28
- end
29
13
 
30
- def record
31
- @orient
14
+ =end
15
+ def initialize modelinstance, *args
16
+ @orient = modelinstance
17
+ super args
18
+ @name = modelinstance.attributes.key(self)
19
+
32
20
  end
33
- =begin
34
- Append the argument to the Array, changes the Array itself.
35
21
 
36
- The change is transmitted to the database immediately
37
- =end
38
- def << *arg
39
- @orient.add_item_to_property(@name, *arg) if @name.present?
22
+ def << arg
23
+ @orient.add_item_to_property( @name, arg ) if @name.present?
40
24
  super
41
25
  end
42
26
 
43
27
  =begin
44
- Updating of single items
28
+ Updating of single items
45
29
 
46
- This only works if the hole embedded Array is previously loaded into the Ruby-array.
30
+ this only works if the hole embedded Array is previosly loaded into the ruby-array.
47
31
  =end
48
-
32
+
49
33
  def []= key, value
50
34
  super
51
- @orient.update set: {@name => self} if @name.present?
35
+ @orient.update set: { @name => self } if @name.present?
52
36
  end
53
37
 
54
- # def [] *arg
55
- # #puts "ARG #{arg}"
56
- # super
57
- # end
58
- #
59
- =begin
60
- Remove_at performs Array#delete_at
61
- =end
62
- def remove_at *pos
63
- @orient.remove_position_from_property(@name,*pos) if @name.present?
64
- end
65
-
66
- #
67
- # alias :del_org :delete
68
- =begin
69
- Remove performs Array#delete
38
+ def [] *arg
39
+ # puts "[] ARG: #{arg.inspect}"
40
+ # arg.each{|u| puts "[] #{u.inspect} : #{u.class} " }
41
+ super
70
42
 
71
- If the Array-element is a link, this is removed, the linked table is untouched
72
- =end
73
- def remove *item
74
- @orient.remove_item_from_property(@name,*item) if @name.present?
75
- end
76
- ###
77
- ## just works with Hashes as parameters
78
- def where *item
79
- where_string = item.map{|m| where_string = compose_where( m ) }.join(' and ')
80
- subquery= OrientSupport::OrientQuery.new from: @orient, projection: "expand( #{@name})"
81
- q= OrientSupport::OrientQuery.new from: subquery, where: item
82
- # query = "SELECT FROM ( SELECT EXPAND( #{@name} ) FROM #{@orient.classname}) #{where_string} "
83
- # puts q.compose
84
- # sql_cmd = -> (command) {{ type: "cmd", language: "sql", command: command }}
85
- # @orient.orientdb.execute do
86
- # sql_cmd[query.to_s]
87
- # end
88
- @orient.query q
89
43
  end
90
44
 
91
- def method_missing *args
92
-
93
- map{|x| x.send *args }
94
- rescue NoMethodError => e
95
- logger.progname = "OrientSupport::Array#MethodMissing"
96
- logger.error{"Undefined method: #{e.message}"}
45
+ def delete_at *pos
46
+ if @name.present?
47
+ delete self[*pos]
48
+ else
49
+ super
50
+ end
51
+ # old version: works only if the hole array is loaded into memory
52
+ # self[*pos]=nil
53
+ # @orient.update set:{ @name => self.compact }
97
54
  end
98
55
 
99
- end #Class
100
-
101
- class LinkMap < OrientSupport::Array
102
- def []= arg
56
+ def delete_if
57
+ if @name.present?
58
+ delete *self.map{|y| y if yield(y) }.compact # if the block returns true then delete the item
59
+ else
60
+ super
61
+ end
103
62
  end
104
- end #Class
105
63
 
106
-
107
-
108
-
109
- class Hash < HashWithIndifferentAccess
110
- include OrientSupport::Support
111
- def initialize modelinstance, args
112
- @orient = modelinstance
113
- super args.from_orient
114
- # @name is the property of @orient to work on
115
- @name = modelinstance.attributes.key(self)
116
- # puts "ORIENT: #{@orient.inspect} "
117
- @name = yield if @name.nil? && block_given?
118
- # puts "NAME: #{@name.inspect}"
119
- # puts "SELF: #{self.inspect}"
64
+ def delete *item
65
+ @orient.remove_item_from_property( @name ) { item } if @name.present?
120
66
  end
121
67
 
68
+ def where *item
69
+ where_string = item.map{|m| where_string = compose_where m }.join( ' and ' )
70
+ query = "select from ( select expand( #{@name} ) from #{@orient.classname}) #{where_string} "
71
+ puts query
72
+ @orient.query query
122
73
 
123
- def []= key, value
124
- puts " i will handle this in the future"
125
- #@orient.attributes[key] = value
126
-
127
- # r = (@orient.query "update #{@orient.rid} put #{@name} = #{key.to_orient}, #{value.to_orient} RETURN AFTER @this").pop
128
- super key, value
129
- @orient.update set:{ @name => self}
130
- # @orient = @orient.class(@orient.rid){r} if r.is_a? ActiveOrient::Model
131
- # self[ key ]= value
132
- # puts self.inspect
133
- #@orient[@name]=self.merge key => value
134
- #
135
74
  end
136
75
 
137
- def delete key
138
- super key
139
- @orient.update set:{ @name => self}
76
+ def method_missing *args
77
+ map{|x| x.send args.first }
140
78
  end
79
+ end
80
+ class LinkMap < OrientSupport::Array
141
81
 
142
- def delete_if &b
143
- super &b
144
- @orient.update set:{ @name => self}
145
-
82
+ def []= arg
146
83
  end
84
+ end
147
85
 
148
- # self
86
+ #
87
+ # class Hash < Hash_with_indifferent_access
88
+ # # additional and overlayed methods for Hash-Objects in OrientDB
89
+ # def initialize modelinstance, *args
90
+ # @orient = modelinstance
91
+ # super args
92
+ # @name = modelinstance.attributes.key(self)
93
+ #
149
94
  # end
95
+ #
96
+ # end
150
97
 
151
- end
152
- end #Module
98
+ end
@@ -1,86 +1,88 @@
1
- module ActiveOrient # :nodoc:
2
1
 
3
- ## this is depreciated and not maintained anymore
4
- class Query < ActiveOrient::Model
2
+ module ActiveOrient
3
+ class Query < ActiveOrient::Model
5
4
 
6
- has_many :records
7
- has_many :queries
5
+ has_many :records
6
+ has_many :queries
8
7
 
9
- def reset_records
10
- self.records= []
11
- end
12
- alias reset_results reset_records
13
-
14
- def reset_queries
15
- self.queries = []
16
- end
8
+ def reset_records
9
+ self.records= []
10
+ end
11
+ alias reset_results reset_records
17
12
 
13
+ def reset_queries
14
+ self.queries = []
15
+ end
18
16
  =begin
19
- Calls ActiveOrient::ActiveOrient#GetRecords
20
- Stores the query in the query-stack and saves the result in the record-Array
17
+ calls ActiveOrient::ActiveOrient#GetDocuments
18
+ stores the query in the query-stack and saves the result in the record-Array
21
19
 
22
- Returns the count of assigned records
20
+ returns the count of assigned records
23
21
  =end
24
22
 
25
- def get_records o_class , **args
26
- query = OrientSupport::OrientQuery.new classname(o_class), args
27
- self.queries << query.compose
28
- count = 0
29
- orientdb.get_records(o_class, query: query.compose).each{|c| records << c; count += 1}
30
- count
31
- end
32
- alias get_documents get_records
23
+ def get_documents o_class , **args
24
+
25
+ query = OrientSupport::OrientQuery.new class_name(o_class), args
26
+ self.queries << query.compose
27
+ count= 0
28
+ orientdb.get_documents( o_class , query: query.compose ).each{|c| records << c; count+=1 }
29
+ count
30
+ end
33
31
 
34
32
  =begin
35
- All predefined queries are send to the database.
36
- The result is stored in the records.
37
- Unknown Records are of Type ActiveOrient::Model::Myquery, uses ActiveOrient::Orientdb.execute which tries to autosuggest the ActiveOrient::Model::{Class}
33
+ All predefined queries are send to the database.
34
+ The result is stored in the records.
35
+ Unknown Records are of Type ActiveOrient::Model::Myquery, uses ActiveOrient::Orientdb.execute which tries to autosuggest the ActiveOrient::Model::{Class}
38
36
 
39
- example: Multible Records
40
- ach = ActiveOrient::Query.new
41
- ach.queries << 'create class Contracts ABSTRACT'
42
- ach.queries << 'create property Contracts.details link'
43
- ach.queries << 'create class Stocks extends Contracts'
44
- result = ach.execute_queries transaction: false
37
+ example: Multible Records
38
+ ach = ActiveOrient::Query.new
39
+ ach.queries << 'create class Contracts ABSTRACT'
40
+ ach.queries << 'create property Contracts.details link'
41
+ ach.queries << 'create class Stocks extends Contracts'
42
+ result = ach.execute_queries transaction: false
45
43
 
46
- example: Batch
47
- q = ActiveOrient::Query.new
48
- q.queries << [
49
- "select expand( contracts ) from Openinterest"
50
- "let con = select expand( contracts ) from Openinterest;",
51
- "let sub = select from Subcategories where contracts in $con;",
52
- "let cat = select from Categories where subcategories in $sub;",
53
- "let ind = select from Industries where categories in $cat;",
54
- "SELECT expand(unionall) FROM (SELECT unionall( $con, $cat))"
55
- ]
56
- q.execute_queries.each{|x| puts "X #{x.inspect}" }
57
- =end
44
+ example: Batch
45
+ q = ActiveOrient::Query.new
46
+ q.queries << [
47
+ "select expand( contracts ) from Openinterest"
48
+ "let con = select expand( contracts ) from Openinterest; ",
49
+ "let sub = select from Subcategories where contracts in $con;",
50
+ "let cat = select from Categories where subcategories in $sub;",
51
+ "let ind = select from Industries where categories in $cat;",
52
+ "SELECT expand(unionall) FROM (SELECT unionall( $con, $cat))"
53
+ ]
54
+ q.execute_queries.each{|x| puts "X #{x.inspect}" }
58
55
 
59
- def execute_queries reset: true, transaction: true
60
- reset_records if reset
61
- begin
62
- orientdb.execute( transaction: transaction ) do
63
- result = queries.map do |q|
64
- # command: words are seperated by one space only, thus squeeze multible spaces
65
- sql_cmd = -> (command) {{type: "cmd", language: "sql", command: command.squeeze(' ') }}
66
- batch_cmd = -> (command_array){{type: "script", language: "sql", script: command_array}}
67
- case q
68
- when String
69
- sql_cmd[q]
70
- when Hash
71
- q
72
- when Array
73
- batch_cmd[q]
74
- else
75
- nil
76
- end # case
77
- end.compact
56
+ =end
57
+ def execute_queries reset: true, transaction: true
58
+ reset_records if reset
59
+ begin
60
+ orientdb.execute( transaction: transaction ) do
61
+ result = queries.map do |q|
62
+ # command: words are seperated by one space only, thus squeeze multible spaces
63
+ sql_cmd = -> (command) { { type: "cmd", language: "sql", command: command.squeeze(' ') } }
64
+ batch_cmd = ->( command_array ){ {type: "script", language: "sql", script: command_array } }
65
+ case q
66
+ when String
67
+ sql_cmd[ q ]
68
+ when Hash
69
+ q
70
+ when Array
71
+ batch_cmd[ q ]
72
+ else
73
+ nil
74
+ end # case
75
+ end.compact
78
76
  # save the result in records
79
- result.each{|y| records << y}
80
- end # block
81
- rescue RestClient::InternalServerError => e
82
- puts e.inspect
83
- end
84
- end # def execute_queries
85
- end # class
77
+ result.each{|y| records << y }
78
+
79
+ end # block
80
+ rescue RestClient::InternalServerError => e
81
+ puts e.inspect
82
+ end
83
+
84
+ end # def execute_queries
85
+
86
+ end # class
87
+
86
88
  end # module
@@ -0,0 +1,1059 @@
1
+ module ActiveOrient
2
+ require 'cgi'
3
+ require 'rest-client'
4
+ require 'active_support/core_ext/string' # provides blank?, present?, presence etc
5
+ require 'model'
6
+ require 'model'
7
+ =begin
8
+ OrientDB performs queries to a OrientDB-Database
9
+
10
+ The communication is based on the ActiveOrient-API.
11
+
12
+ The OrientDB-Server is specified in config/connect.yml
13
+
14
+ A Sample:
15
+ :orientdb:
16
+ :server: localhost
17
+ :port: 2480
18
+ :database: working-database
19
+ :admin:
20
+ :user: admin-user
21
+ :pass: admin-password
22
+
23
+
24
+ =end
25
+
26
+ class OrientDB
27
+ mattr_accessor :logger ## borrowed from active_support
28
+ mattr_accessor :default_server ##
29
+ ## expected
30
+ # ActiveOrient::OrientDB.default_server = { server: 'localhost', port: 2480, user: '**', password: '**', database: 'temp'
31
+ include OrientSupport::Support
32
+
33
+ =begin
34
+ Contructor: OrientDB is conventionally initialized.
35
+ Thus several instances pointing to the same or different databases can coexist
36
+
37
+ A simple
38
+ xyz = ActiveOrient::OrientDB.new
39
+ uses the database specified in the yaml-file »config/connect.yml« and connects
40
+ xyz = ActiveOrient::OrientDB.new database: my_fency_database
41
+ accesses the database »my_fency_database«. The database is created if its not existing.
42
+
43
+ *USECASE*
44
+ xyz = ActiveOrient::Model.orientdb = ActiveOrient::OrientDB.new
45
+ initialises the Database-Connection and publishes the Instance to any ActiveOrient::Model-Object
46
+ =end
47
+
48
+
49
+ def initialize database: nil, connect: true
50
+ self.default_server = { :server => 'localhost', :port => 2480, :protocol => 'http',
51
+ :user => 'root', :password => 'root', :database => 'temp' }.merge default_server
52
+ @res = get_ressource
53
+ @database = database.presence || default_server[:database]
54
+ self.logger = Logger.new('/dev/stdout') unless logger.present?
55
+ # @database=@database#.camelcase
56
+ connect() if connect
57
+ # save existing classes
58
+ @classes = []
59
+ ActiveOrient::Model.orientdb = self
60
+ end
61
+
62
+ ## included for development , should be removed in production release
63
+ def ressource
64
+ @res
65
+ end
66
+ ##
67
+
68
+ # called in the beginning or after a 404-Error
69
+ def get_ressource
70
+ login = [ default_server[:user].to_s , default_server[:password].to_s ]
71
+ server_adress = [ default_server[:protocol] ,"://" , default_server[ :server ], ":" , default_server[ :port ]].map(&:to_s).join('')
72
+ RestClient::Resource.new( server_adress, *login )
73
+ end
74
+
75
+ def connect
76
+ i = 0
77
+ begin
78
+ logger.progname = 'OrientDB#Connect'
79
+ r= @res["/connect/#{ @database }" ].get
80
+ if r.code == 204
81
+ logger.info{ "Connected to database #{@database} " }
82
+ true
83
+ else
84
+ logger.error{ "Connection to database #{@database} could NOT be established" }
85
+ nil
86
+ end
87
+ rescue RestClient::Unauthorized => e
88
+ if i.zero?
89
+ logger.info{ "Database #{@database} NOT present --> creating" }
90
+ i=i+1
91
+ create_database
92
+ retry
93
+ else
94
+ Kernel.exit
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ ## -----------------------------------------------------------------------------------------
101
+ ##
102
+ ## Database stuff
103
+ ##
104
+ ## get_databases
105
+ ## create_database
106
+ ## change_database
107
+ ## delete_database
108
+ ## -----------------------------------------------------------------------------------------
109
+
110
+ =begin
111
+ returns an Array with available Database-Names as Elements
112
+ =end
113
+ def get_databases
114
+ JSON.parse( @res["/listDatabases"].get.body)['databases']
115
+ end
116
+
117
+ =begin
118
+ Creates a database with the given name and switches to this database as working-database
119
+ Types are either 'plocal' or 'memory'
120
+
121
+ returns the name of the working-database
122
+ =end
123
+ def create_database type: 'plocal' , database: @database
124
+ logger.progname = 'OrientDB#CreateDatabase'
125
+ old_d = @database
126
+ @classes = []
127
+ @database = database
128
+ begin
129
+ response = @res[database_uri{ type }].post ""
130
+ if response.code == 200
131
+ logger.info{ "Database #{@database} successfully created and stored as working database"}
132
+ else
133
+ @database = old_d
134
+ logger.error{ "Database #{name} was NOT created. Working Database is still #{@database} "}
135
+ end
136
+ rescue RestClient::InternalServerError => e
137
+ @database = old_d
138
+ logger.error{ "Database #{name} was NOT created. Working Database is still #{@database} "}
139
+ end
140
+ @database
141
+
142
+ end
143
+ =begin
144
+ changes the working-database to {name}
145
+ =end
146
+ def change_database name
147
+ @classes = []
148
+ @database = name
149
+
150
+ end
151
+ =begin
152
+ deletes the database and returns true on success
153
+
154
+ after the removal of the database, the working-database might be empty
155
+ =end
156
+ def delete_database database:
157
+ @classes = []
158
+ logger.progname = 'OrientDB#DropDatabase'
159
+ old_ds = @database
160
+ change_database database
161
+ begin
162
+ response = @res[database_uri].delete
163
+ if database == old_ds
164
+ change_database ""
165
+ logger.info{ "Working database deleted" }
166
+ else
167
+ change_database old_ds
168
+ logger.info{ "Database #{database} deleted, working database is still #{@database} "}
169
+ end
170
+ rescue RestClient::InternalServerError => e
171
+ logger.info{ "Database #{database} NOT deleted" }
172
+ change_database old_ds
173
+ end
174
+ !response.nil? && response.code == 204 ? true : false
175
+
176
+ end
177
+
178
+ ## -----------------------------------------------------------------------------------------
179
+ ##
180
+ ## Inspect, Create and Delete Classes
181
+ ##
182
+ ## inspect_classes
183
+ ## create_class
184
+ ## delete_class
185
+ ##
186
+ ## -----------------------------------------------------------------------------------------
187
+
188
+ =begin
189
+ returns an Array with (unmodified) Class-attribute-hash-Elements
190
+
191
+ get_classes 'name', 'superClass'
192
+ returns
193
+ [ {"name"=>"E", "superClass"=>""},
194
+ {"name"=>"OFunction", "superClass"=>""},
195
+ {"name"=>"ORole", "superClass"=>"OIdentity"}
196
+ (...)
197
+ ]
198
+ =end
199
+
200
+ def get_classes *attributes
201
+ i = 0
202
+ begin
203
+ #puts "database_uri #{database_uri}"
204
+ response = @res[database_uri].get
205
+ if response.code == 200
206
+ classes= JSON.parse( response.body )['classes' ]
207
+ unless attributes.empty?
208
+ classes.map{|y| y.select{| v,_| attributes.include?(v) } }
209
+ else
210
+ classes
211
+ end
212
+
213
+ #.map{ |y| y.name }
214
+ else
215
+ []
216
+ end
217
+ rescue JSON::ParserError
218
+ if i.zero?
219
+ i = i + 1
220
+ retry
221
+ else
222
+ raise
223
+ end
224
+ end
225
+
226
+ end
227
+ =begin
228
+ returns the class_hierachie
229
+
230
+ to fetch all Vertices
231
+ class_hiearchie( base_class: 'V').flatten
232
+ to fetch all Edges
233
+ class_hierachie( base_class: 'E').flatten
234
+
235
+ Notice: base_class has to be noted as String! There is no implicit conversion from Symbol or Class
236
+ =end
237
+
238
+ def class_hierachie base_class: '', requery: false
239
+ @all_classes = get_classes( 'name', 'superClass') if requery || @all_classes.blank?
240
+ def fv s # :nodoc:
241
+ @all_classes.find_all{ |x| x[ 'superClass' ]== s }.map{| v| v[ 'name' ]} #.camelize }
242
+ end
243
+
244
+ def fx v # :nodoc:
245
+ fv(v).map{ |x| ar = fx( x ) ; ar.empty? ? x : [ x , ar ] }
246
+ end
247
+
248
+ fx base_class
249
+ end
250
+ =begin
251
+ returns an array with all names of the classes of the database
252
+ caches the result.
253
+
254
+ parameter: include_system_classes: false|true, requery: false|true
255
+ =end
256
+ def database_classes include_system_classes: false, requery: false
257
+ requery = true if @classes.empty?
258
+ if requery
259
+ class_hierachie requery: true
260
+ system_classes = ["OFunction", "OIdentity", "ORIDs", "ORestricted", "ORole", "OSchedule", "OTriggered", "OUser", "_studio"]
261
+ all_classes = get_classes( 'name' ).map( &:values).flatten
262
+ @classes = include_system_classes ? all_classes : all_classes - system_classes
263
+ end
264
+ @classes
265
+ end
266
+
267
+ alias inspect_classes database_classes
268
+
269
+ =begin
270
+ Creates classes and class-hierachies in OrientDB and in Ruby.
271
+
272
+
273
+ Takes an Array or a Hash as argument and returns an Array of
274
+ successfull allocated Ruby-Classes
275
+
276
+ If the argument is an array, Basic-Classes are build.
277
+
278
+ Otherwise key/value pairs are assumend to follow this terminology
279
+ { SuperClass => [ class, class, ...], SuperClass => [] , ... }
280
+ =end
281
+
282
+ def create_classes classes
283
+ # rebuild cashed classes-array
284
+ # first the classes-string is camelized (this is used to allocate the ruby-class)
285
+ # Then the database is queried for this string or the underscored string-variant
286
+ # the name-building process is independend from the method »class_name«
287
+ database_classes requery: true
288
+ consts = Array.new
289
+ execute transaction: false do
290
+ class_cmd = ->(s,n) do
291
+ n = n.to_s.camelize
292
+ consts << ActiveOrient::Model.orientdb_class( name: n)
293
+ unless database_classes.include?(n) || database_classes.include?(n.underscore)
294
+ { type: "cmd", language: 'sql', command: "create class #{n} extends #{s}" }
295
+
296
+ end
297
+ end ## class_cmd
298
+
299
+ if classes.is_a?(Array)
300
+ classes.map do | n |
301
+ n = n.to_s.camelize # capitalize
302
+ consts << ActiveOrient::Model.orientdb_class( name: n)
303
+ unless database_classes.include?( n ) || database_classes.include?(n.underscore)
304
+ { type: "cmd", language: 'sql', command: "create class #{n} " }
305
+ end
306
+ end
307
+ elsif classes.is_a?(Hash)
308
+ classes.keys.map do | superclass |
309
+ items = Array.new
310
+ superClass = superclass.to_s.camelize
311
+ items << { type: "cmd", language: 'sql', command: "create class #{superClass} abstract" } unless database_classes.flatten.include?( superClass ) || database_classes.flatten.include?( superClass.underscore )
312
+ items << if classes[superclass].is_a?( String ) || classes[superclass].is_a?( Symbol )
313
+ class_cmd[superClass, classes[superclass] ]
314
+ elsif classes[superclass].is_a?( Array )
315
+ classes[superclass].map{|n| class_cmd[superClass, n] }
316
+ end
317
+ #puts items.flatten.map{|x| x[:command]}
318
+ items # returnvalue
319
+ end.flatten
320
+ end.compact # erase nil-entries, in case the class is already allocated
321
+ end
322
+ # refresh cached class-informations
323
+ database_classes requery: true
324
+ # returns an array of allocated Constants/Classes
325
+ consts
326
+ end
327
+
328
+ =begin
329
+ creates a class and returns the a ActiveOrient::Model:{Newclass}-Class- (Constant)
330
+ which is designed to take any documents stored in this class
331
+
332
+ Predefined attributes: version, cluster, record
333
+
334
+ Other attributes are assigned dynamically upon reading documents
335
+
336
+ The classname is Camelized by default, eg: classnames are always Capitalized, underscores ('_') indicate
337
+ that the following letter is capitalized, too.
338
+
339
+ Other class-names can be used if a "$" is placed at the begining of the name-string. However, most of the
340
+ model-based methods will not work in this case.
341
+ =end
342
+ def create_class newclass
343
+ create_classes( [ newclass ] ).first
344
+ end
345
+
346
+ alias open_class create_class
347
+
348
+ def create_vertex_class name , superclass: 'V'
349
+ create_classes( { superclass => name } ).first
350
+ end
351
+
352
+ def create_edge_class name , superclass: 'E'
353
+ create_classes( { superclass => name } ).first
354
+ end
355
+
356
+ =begin
357
+ nexus_edge connects two documents/vertexes
358
+ The parameter o_class can be either a class or a string
359
+ =end
360
+
361
+ def nexus_edge o_class , attributes: {}, from:, to:, unique: false
362
+ logger.progname = "ActiveOrient::OrientDB#NexusEdge"
363
+ if from.is_a? Array
364
+ from.map{|f| nexus_edge o_class, attributes: attributes, from: f, to: to, unique: unique }
365
+ elsif to.is_a? Array
366
+ to.map{|t| nexus_edge o_class, attributes: attributes, from: from, to: t, unique: unique }
367
+ else
368
+
369
+ if unique
370
+ wwhere = { out: from.to_orient, in: to.to_orient }.merge(attributes.to_orient)
371
+ existing_edge = get_documents( from: o_class, where: wwhere )
372
+ if existing_edge.first.is_a?( ActiveOrient::Model )
373
+ logger.debug { "reusing edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} " }
374
+ return existing_edge.first
375
+ end
376
+ end
377
+ logger.debug { "creating edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} " }
378
+ response= execute( o_class, transaction: false) do
379
+ #[ { type: "cmd", language: 'sql', command: CGI.escapeHTML("create edge #{class_name(o_class)} from #{translate_to_rid[m]} to #{to.to_orient}; ")} ]
380
+ attr_string = attributes.blank? ? "" : "set #{ generate_sql_list attributes.to_orient }"
381
+ [ { type: "cmd", language: 'sql',
382
+ command: "create edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} #{attr_string}"} ]
383
+ end
384
+ if response.is_a?(Array) && response.size == 1
385
+ response.pop # RETURN_VALUE
386
+ else
387
+ response
388
+ end
389
+ end
390
+ end
391
+
392
+ =begin
393
+ Deletes a single edge when providing a single rid-link (#00:00)
394
+ Deletes multible edges when providing a list of rid-links
395
+ Todo: implement delete_edges after querying the database in one statement
396
+
397
+ =end
398
+ def delete_edge *rid
399
+ rid = rid.map do |mm|
400
+ if mm.is_a?(String)
401
+ if mm.rid?
402
+ mm
403
+ elsif mm.is_a?(Rest::Model)
404
+ mm.rid
405
+ else
406
+ nil
407
+ end
408
+ end
409
+ end.compact
410
+ response= execute transaction: false do
411
+ [ { type: "cmd", language: 'sql', command: CGI.escapeHTML("delete edge #{rid.join(',') }")} ]
412
+ end
413
+ if response.is_a?( Array ) && response.size == 1
414
+ response.pop # RETURN_VALUE
415
+ else
416
+ response
417
+ end
418
+ end
419
+ =begin
420
+ deletes the specified class and returns true on success
421
+
422
+
423
+ todo: remove all instances of the class
424
+ =end
425
+ def delete_class o_class
426
+ cl= class_name(o_class)
427
+ logger.progname = 'OrientDB#DeleteClass'
428
+
429
+ if database_classes.include? cl
430
+ begin
431
+ response = @res[class_uri{ cl } ].delete
432
+ if response.code == 204
433
+ # return_value: sussess of the removal
434
+ !database_classes( requery: true ).include?(cl)
435
+ # don't delete the ruby-class
436
+ # ActiveOrient::Model.send :remove_const, cl.to_sym if o_class.is_a?(Class)
437
+ end
438
+ rescue RestClient::InternalServerError => e
439
+ if database_classes( requery: true).include?( cl )
440
+ logger.error{ "Class #{cl} still present" }
441
+ logger.error{ e.inspect }
442
+ false
443
+ else
444
+ true
445
+ end
446
+ end
447
+ else
448
+ logger.info { "Class #{cl} not present. "}
449
+ end
450
+
451
+ end
452
+ =begin
453
+ create a single property on class-level.
454
+
455
+ supported types: https://orientdb.com/docs/last/SQL-Create-Property.html
456
+
457
+ if index is to be specified, it's defined in the optional block
458
+ create_property(class, field){ :unique | :notunique } --> creates an automatic-Index on the given field
459
+ create_property( class, field){ { »name« => :unique | :notunique | :full_text } } --> creates a manual index
460
+
461
+ =end
462
+ def create_property o_class, field, index: nil, **args
463
+ logger.progname= 'OrientDB#CreateProperty'
464
+ c= create_properties o_class, {field => args} # translate_property_hash( field, args )
465
+ if index.nil? && block_given?
466
+ index = yield
467
+ end
468
+ if c==1 && index.present?
469
+ if index.is_a?( String ) || index.is_a?( Symbol )
470
+ create_index o_class, name: field, type: index
471
+ elsif index.is_a? Hash
472
+ bez= index.keys.first
473
+ # puts "index: #{index.inspect}"
474
+ # puts "bez: #{bez} ---> #{index.keys.inspect}"
475
+ create_index o_class, name: bez, type: index[bez], on: [ field ]
476
+ end
477
+ end
478
+
479
+ end
480
+
481
+
482
+ def create_index o_class , name:, on: :automatic, type: :unique
483
+
484
+ execute transaction: false do
485
+ c = class_name o_class
486
+ command = if on == :automatic
487
+ "create index #{c}.#{name} #{type.to_s.upcase}"
488
+ elsif on.is_a? Array
489
+ "create index #{name} on #{class_name(o_class)}( #{on.join(', ')}) #{type.to_s.upcase}"
490
+ else
491
+ nil
492
+ end
493
+ [ { type: "cmd", language: 'sql', command: command } ]
494
+ end
495
+
496
+ end
497
+ ## -----------------------------------------------------------------------------------------
498
+ ##
499
+ ## Properties
500
+ ##
501
+ ## create_properties
502
+ ## get_class_properties
503
+ ## delete_properties
504
+
505
+ ##
506
+ ## -----------------------------------------------------------------------------------------
507
+ private
508
+ def translate_property_hash field, type: nil, linked_class: nil, **args
509
+ type = type.presence || args[:propertyType].presence || args[:property_type]
510
+ linked_class = linked_class.presence || args[:linkedClass]
511
+ if type.present?
512
+ if linked_class.nil?
513
+ { field => { propertyType: type.to_s.upcase } }
514
+ else
515
+ { field => { propertyType: type.to_s.upcase, linkedClass: class_name( linked_class ) } }
516
+ end
517
+ end
518
+
519
+ end
520
+ public
521
+ =begin
522
+
523
+ creates properties and optional an associated index as defined in the provided block
524
+
525
+ create_properties( classname or class, properties as hash ) { index }
526
+
527
+ The default-case
528
+
529
+ create_properties( :my_high_sophisticated_database_class,
530
+ con_id: { type: :integer },
531
+ details: { type: :link, linked_class: 'Contracts' } ) do
532
+ contract_idx: :notunique
533
+ end
534
+
535
+ A composite index
536
+
537
+ create_properties( :my_high_sophisticated_database_class,
538
+ con_id: { type: :integer },
539
+ symbol: { type: :string } ) do
540
+ { name: 'indexname',
541
+ on: [ :con_id , :details ] # default: all specified properties
542
+ type: :notunique # default: :unique
543
+ }
544
+ end
545
+
546
+ =end
547
+ def create_properties o_class, all_properties, &b
548
+
549
+ all_properties_in_a_hash = HashWithIndifferentAccess.new
550
+ all_properties.each{| field, args | all_properties_in_a_hash.merge! translate_property_hash( field, args ) }
551
+
552
+ begin
553
+ count = if all_properties_in_a_hash.is_a?( Hash )
554
+ response = @res[ property_uri(class_name(o_class)) ].post all_properties_in_a_hash.to_json
555
+ if response.code == 201
556
+ response.body.to_i
557
+ else
558
+ 0
559
+ end
560
+ end
561
+ rescue RestClient::InternalServerError => e
562
+ response = JSON.parse( e.response)['errors'].pop
563
+ error_message = response['content'].split(':').last
564
+ # if error_message ~= /Missing linked class/
565
+ logger.progname= 'OrientDB#CreatePropertes'
566
+ logger.error { "Properties in #{class_name(o_class)} were NOT created" }
567
+ logger.error { "Error-code #{response['code']} --> #{response['content'].split(':').last }" }
568
+ nil
569
+ end
570
+ ### index
571
+ if block_given? && count == all_properties_in_a_hash.size
572
+ index = yield
573
+ if index.is_a?( Hash )
574
+ if index.size == 1
575
+ create_index o_class, name: index.keys.first, on: all_properties_in_a_hash.keys, type: index.values.first
576
+ else
577
+ index_hash = HashWithIndifferentAccess.new( type: :unique, on: all_properties_in_a_hash.keys ).merge index
578
+ create_index o_class, index_hash # i [:name], on: index_hash[:on], type: index_hash[:type]
579
+ end
580
+ end
581
+ end
582
+ count # return_value
583
+ end
584
+
585
+ def delete_property o_class, field
586
+ logger.progname = 'OrientDB#DeleteProperty'
587
+ begin
588
+ response = @res[property_uri( class_name(o_class)){ field } ].delete
589
+ true if response.code == 204
590
+ rescue RestClient::InternalServerError => e
591
+ logger.error{ "Property #{ field } in class #{ class_name(o_class) } NOT deleted" }
592
+ false
593
+ end
594
+
595
+ end
596
+
597
+ def get_class_properties o_class # :nodoc:
598
+ JSON.parse( @res[ class_uri{ class_name(o_class) } ].get )
599
+ end
600
+
601
+ def print_class_properties o_class
602
+ puts "Detected Properties for class #{class_name(o_class)}"
603
+
604
+ rp = get_class_properties o_class
605
+ n= rp['name']
606
+ puts rp['properties'].map{|x| [ n+'.'+x['name'], x['type'],x['linkedClass'] ].compact.join(' -> ' )}.join("\n")
607
+
608
+ end
609
+ #
610
+ ## -----------------------------------------------------------------------------------------
611
+ ##
612
+ ## Documents
613
+ ##
614
+ ## create_document get_document delete_document
615
+ ## update_or_create_document patch_document
616
+ ##
617
+ ##
618
+ ## get_documents
619
+ ## update_documents
620
+ ## delete_documents
621
+ ## update_or_create_documents
622
+ ## -----------------------------------------------------------------------------------------
623
+
624
+ =begin #nodoc#
625
+ If properties are allocated on class-level, they can be preinitialized using
626
+ this method.
627
+ This is disabled for now, because it does not seem nessesary
628
+
629
+ =end
630
+ def preallocate_class_properties o_class
631
+ p= get_class_properties( o_class )['properties']
632
+ unless p.nil? || p.blank?
633
+ predefined_attributes = p.map do | property |
634
+ [ property['name'] ,
635
+ case property['type']
636
+ when 'LINKMAP'
637
+ Array.new
638
+ when 'STRING'
639
+ ''
640
+ else
641
+ nil
642
+ end ]
643
+ end.to_h
644
+ else
645
+ {}
646
+ end
647
+ end
648
+
649
+ =begin
650
+ Creates an Object in the Database and returns this as ActuveOrient::Model-Instance
651
+ =end
652
+
653
+ def create_document o_class, attributes: {}
654
+ attributes = yield if attributes.empty? && block_given?
655
+ post_argument = { '@class' => class_name(o_class) }.merge(attributes).to_orient
656
+
657
+ begin
658
+ logger.progname = 'OrientDB#CreateDocument'
659
+ response = @res[ document_uri ].post post_argument.to_json
660
+ data= JSON.parse( response.body )
661
+ ActiveOrient::Model.orientdb_class( name: data['@class']).new data
662
+ rescue RestClient::InternalServerError => e
663
+ response = JSON.parse( e.response)['errors'].pop
664
+ logger.error { response['content'].split(':')[1..-1].join(':') }
665
+ logger.error { "No Object allocated" }
666
+ nil # return_value
667
+ end
668
+ end
669
+
670
+
671
+ def delete_document record_id
672
+ logger.progname = 'OrientDB#DeleteDocument'
673
+ begin
674
+ response = @res[document_uri{ record_id } ].delete
675
+ true if response.code == 204
676
+ rescue RestClient::InternalServerError => e
677
+ logger.error{ "Document #{ record_id } NOT deleted" }
678
+ false
679
+ end
680
+
681
+ end
682
+ =begin
683
+ retrieves documents from a query
684
+
685
+ If raw is specified, the JSON-Array is returned, eg
686
+ { "@type"=>"d",
687
+ "@rid"=>"#15:1",
688
+ "@version"=>1,
689
+ "@class"=>"DocumebntKlasse10",
690
+ "con_id"=>343,
691
+ "symbol"=>"EWTZ"
692
+ }
693
+ otherwise a ActiveModel-Instance of o_class is created and returned
694
+ =end
695
+ def get_documents raw: false, query: nil, **args
696
+ query = OrientSupport::OrientQuery.new( args ) if query.nil?
697
+ i=0
698
+ begin
699
+ logger.progname = 'OrientDB#GetDocuments'
700
+ url = query_sql_uri + query.compose( destination: :rest) + "/#{query.get_limit}"
701
+ response = @res[URI.encode(url) ].get
702
+ r=JSON.parse( response.body )['result'].map do |document |
703
+ # parameter: raw is set --> don't initilize a model object
704
+ if raw
705
+ document
706
+ # query returns an anonymus class: Use the provided Block or the Dummy-Model MyQuery
707
+ elsif document['@class'].blank?
708
+ block_given? ? yield.new( document ) : ActiveOrient::Model::MyQuery.new( document )
709
+ else
710
+ ActiveOrient::Model.orientdb_class( name: document['@class']).new document
711
+ end
712
+ end
713
+ rescue RestClient::InternalServerError => e
714
+ response = JSON.parse( e.response)['errors'].pop
715
+ logger.error { response['content'].split(':').last }
716
+ i=i+1
717
+ if i > 1
718
+ raise
719
+ else
720
+ query.dataset_name = query.database_class.underscore
721
+ logger.info { "trying to query using #{o_class}" }
722
+ retry
723
+ end
724
+ rescue URI::InvalidURIError => e
725
+ logger.error "Invalid URI detected"
726
+ logger.error query.to_s
727
+ logger.info "trying batch processing "
728
+ sql_cmd = -> (command) { { type: "cmd", language: "sql", command: command } }
729
+ response= execute { [ sql_cmd[ query.to_s ] ] }
730
+ logger.info "success: to avoid this delay use ActiveOrient::Model#query_database insteed"
731
+ response
732
+ end
733
+
734
+ end
735
+
736
+ def count_documents **args
737
+ logger.progname = 'OrientDB#count_documents'
738
+
739
+ query = OrientSupport::OrientQuery.new args
740
+ query.projection << 'COUNT (*)'
741
+ result = get_documents raw: true, query: query
742
+
743
+ result.first['COUNT']
744
+
745
+ end
746
+
747
+ =begin
748
+ Inserts a Document with the attributes provided in the attributes-hash
749
+ eg
750
+ create_document class_name: @classname, attributes: { con_id: 343, symbol: 'EWTZ' }
751
+
752
+ untested: for hybrid and schema-less documents the following syntax is supported
753
+ create_document class_name: "Account",
754
+ attributes: { date: 1350426789, amount: 100.34,
755
+ "@fieldTypes" => "date=t,amount=c" }
756
+
757
+ The supported special types are:
758
+ n
759
+ 'f' for float
760
+ 'c' for decimal
761
+ 'l' for long
762
+ 'd' for double
763
+ 'b' for byte and binary
764
+ 'a' for date
765
+ 't' for datetime
766
+ 's' for short
767
+ 'e' for Set, because arrays and List are serialized as arrays like [3,4,5]
768
+ 'x' for links
769
+ 'n' for linksets
770
+ 'z' for linklist
771
+ 'm' for linkmap
772
+ 'g' for linkbag
773
+
774
+ =end
775
+ =begin
776
+ Creating a new Database-Entry ( where is omitted )
777
+
778
+ otherwise updating the Database-Entry (if present)
779
+
780
+ The optional Block should provide a hash with attributes(properties). These are used if
781
+ a new dataset is created.
782
+ =end
783
+ def create_or_update_document o_class , **args, &b
784
+ logger.progname = 'Rest#CreateOrUpdateDocument'
785
+ r= update_or_create_documents o_class, **args, &b
786
+ if r.size > 1
787
+ logger.error { "multible documents updated by #{ generate_sql_list( where )}" }
788
+ end
789
+ r.first # return_value
790
+ end
791
+ =begin
792
+ Based on the query specified in :where records are updated according to :set
793
+
794
+ Returns an Array of updated documents
795
+
796
+ The optional Block should provide a hash with attributes(properties). These are used if
797
+ a new dataset is created.
798
+
799
+ ### das ist noch nicht rund.
800
+ #
801
+ =end
802
+ def update_or_create_documents o_class , set: {}, where: {} , **args , &b
803
+ logger.progname = 'Rest#UpdateOrCreateDocuments'
804
+ if where.blank?
805
+ [ create_document( o_class, attributes: set ) ]
806
+ else
807
+ set.extract!( where.keys ) # removes any keys from where in set
808
+ possible_documents = get_documents from: class_name( o_class ), where: where, **args
809
+ if possible_documents.empty?
810
+ if block_given?
811
+ more_where = yield # do Preparations prior to the creation of the dataset
812
+ # if the block returns a Hash , it is merged into the insert_query.
813
+ where.merge! more_where if more_where.is_a?(Hash)
814
+ end
815
+ [ create_document( o_class, attributes: set.merge(where) ) ]
816
+ else
817
+ possible_documents.map{| doc | doc.update( set: set ) }
818
+ end
819
+ end
820
+ end
821
+ =begin
822
+ Deletes documents.
823
+ They are defined by a query. All records which match the attributes are deleted.
824
+ An Array with freed index-values is returned
825
+ =end
826
+ def delete_documents o_class, where: {}
827
+ get_documents( from: o_class, where: where).map do |doc|
828
+ if doc['@type']=='d' # document
829
+ index = doc['@rid'][1,doc['@rid'].size] # omit the first character ('#')
830
+ r=@res[ document_uri{ index }].delete
831
+ index if r.code==204 && r.body.empty? # return_value
832
+ end
833
+
834
+ end
835
+
836
+ end
837
+ =begin
838
+ Retrieves a Document from the Database as ActiveOrient::Model::{class}
839
+ The argument can either be a rid (#[x}:{y}) or a link({x}:{y})
840
+ If no Document is found, nil is returned
841
+
842
+ In the optional block, a subset of properties can be defined (as array of names)
843
+ =end
844
+ def get_document rid
845
+
846
+ rid = rid[1 .. rid.length] if rid[0]=='#'
847
+
848
+ response = @res[ document_uri { rid } ].get
849
+
850
+ raw_data = JSON.parse( response.body) #.merge( "#no_links" => "#no_links" )
851
+ ActiveOrient::Model.orientdb_class( name: raw_data['@class']).new raw_data
852
+
853
+ rescue RestClient::InternalServerError => e
854
+ if e.http_body.split(':').last =~ /was not found|does not exist in database/
855
+ nil
856
+ else
857
+ logger.progname 'OrientDB#GetDocument'
858
+ logger.error "something went wrong"
859
+ logger.error e.http_body.inspect
860
+ raise
861
+ end
862
+ end
863
+ =begin
864
+ Lazy Updating of the given Document.
865
+ =end
866
+ def patch_document rid
867
+ logger.progname = 'Rest#PatchDocument'
868
+ content = yield
869
+ if content.is_a? Hash
870
+ content.each do | key, value |
871
+ # puts "content: #{key}, #{value.class}"
872
+ # content[key]= value.to_orient #if value.is_a? ActiveOrient::Model
873
+ end
874
+ @res[ document_uri { rid } ].patch content.to_orient.to_json
875
+ else
876
+ logger.error { "FAILED: The Block must provide an Hash with properties to be updated"}
877
+ end
878
+ end
879
+ =begin
880
+ update_documents classname,
881
+ set: { :symbol => 'TWR' },
882
+ where: { con_id: 340 }
883
+
884
+ replaces the symbol to TWR in each record where the con_id is 340
885
+ Both set and where take multible attributes
886
+ returns the JSON-Response.
887
+
888
+ =end
889
+
890
+
891
+ def update_documents o_class, set: , where: {}
892
+ url = "update #{class_name(o_class)} set "<< generate_sql_list(set) << compose_where(where)
893
+ response = @res[ URI.encode( command_sql_uri << url) ].post '' #url.to_json
894
+ end
895
+
896
+ ## -----------------------------------------------------------------------------------------
897
+ ##
898
+ ## Functions and Batch
899
+ ##
900
+ ##
901
+ ## -----------------------------------------------------------------------------------------
902
+
903
+ =begin
904
+ Execute a predefined Function
905
+ =end
906
+ def call_function *args
907
+ #puts "uri:#{function_uri { args.join('/') } }"
908
+ @res[ function_uri { args.join('/') } ].post ''
909
+ rescue RestClient::InternalServerError => e
910
+ puts JSON.parse(e.http_body)
911
+ end
912
+
913
+
914
+ =begin
915
+ Executes a list of commands and returns the result-array (if present)
916
+
917
+ structure of the provided block:
918
+ [{ type: "cmd",
919
+ language: "sql",
920
+ command: "create class Person extends V"
921
+ },
922
+ (...)
923
+ ]
924
+
925
+ It was first used by ActiveOrient::Query.execute_queries
926
+ Later I discovered that some Queries are not interpretated correctly by #GetDocuments
927
+ but are submitted without Error via batch-processing.
928
+ For Instance, this valid query
929
+ > 'select expand( first_list[5].second_list[9] ) from base where label = 9 ' <
930
+ can only be submitted via batch
931
+
932
+ =end
933
+ def execute classname = 'Myquery', transaction: true
934
+ #puts "classname##execute:: #{classname}"
935
+ batch = { transaction: transaction, operations: yield }
936
+ unless batch[:operations].blank?
937
+ # puts "post: #{batch.to_json}"
938
+ response = @res[ batch_uri ].post batch.to_json
939
+ # puts "response: #{JSON.parse(response.body)['result'].inspect}"
940
+ if response.code == 200
941
+ if response.body['result'].present?
942
+ result= JSON.parse(response.body)['result']
943
+ result.map do |x|
944
+ if x.is_a? Hash
945
+ if x.has_key?('@class')
946
+ ActiveOrient::Model.orientdb_class( name: x['@class']).new x
947
+ elsif x.has_key?( 'value' )
948
+ x['value']
949
+ else
950
+ # puts "ActiveOrient::Execute"
951
+ # puts "o_class: #{o_class.inspect}"
952
+ ActiveOrient::Model.orientdb_class( name: classname).new x
953
+ end
954
+ end
955
+ end.compact # return_value
956
+
957
+ else
958
+ response.body
959
+ end
960
+ else
961
+ nil
962
+ end
963
+ end
964
+ rescue RestClient::InternalServerError => e
965
+ raise
966
+
967
+ end
968
+ =begin
969
+ Converts a given name to the camelized database-classname
970
+
971
+ Converts a given class-constant to the corresponding database-classname
972
+
973
+ returns a valid database-class name, nil if the class not exists
974
+ =end
975
+ def class_name name_or_class
976
+ name= if name_or_class.is_a? Class
977
+ name_or_class.to_s.split('::').last
978
+ elsif name_or_class.is_a? ActiveOrient::Model
979
+ name_or_class.classname
980
+ else
981
+ name_or_class.to_s.camelize
982
+ end
983
+ if database_classes.include?(name)
984
+ name
985
+ elsif database_classes.include?(name.underscore)
986
+ name.underscore
987
+ else
988
+ logger.progname = 'OrientDB#ClassName'
989
+ logger.info{ "Classname #{name} not present in active Database" }
990
+ nil
991
+ end
992
+ end
993
+ # def compose_where arg
994
+ # if arg.blank?
995
+ # ""
996
+ # elsif arg.is_a? String
997
+ # if arg =~ /[w|W]here/
998
+ # arg
999
+ # else
1000
+ # "where "+arg
1001
+ # end
1002
+ # elsif arg.is_a? Hash
1003
+ # " where " + generate_sql_list(arg)
1004
+ # end
1005
+ # end
1006
+ private
1007
+
1008
+ #def generate_sql_list attributes={}
1009
+ # attributes.map do | key, value |
1010
+ # case value
1011
+ # when Numeric
1012
+ # key.to_s << " = " << value.to_s
1013
+ # else # String, Symbol
1014
+ # key.to_s << ' = ' << "\'#{ value }\'"
1015
+ # # else
1016
+ # # puts "ERROR, value-> #{value}, class -> #{value.class}"
1017
+ # end
1018
+ # end.join( ' and ' )
1019
+ #
1020
+ #end
1021
+ #
1022
+ def property_uri(this_class_name)
1023
+ if block_given?
1024
+ "property/#{ @database }/#{this_class_name}/" << yield
1025
+ else
1026
+ "property/#{ @database }/#{this_class_name}"
1027
+ end
1028
+ end
1029
+
1030
+ def self.simple_uri *names
1031
+ names.each do | name |
1032
+ m_name = (name.to_s << '_uri').to_sym
1033
+ define_method( m_name ) do | &b |
1034
+ if b
1035
+ "#{name.to_s}/"<< @database << "/" << b.call
1036
+ else
1037
+ "#{name.to_s}/"<< @database
1038
+ end # branch
1039
+ end # def
1040
+ end
1041
+ end
1042
+
1043
+ def self.sql_uri *names
1044
+ names.each do | name |
1045
+ define_method ((name.to_s << '_sql_uri').to_sym) do
1046
+ "#{name.to_s}/" << @database << "/sql/"
1047
+ end
1048
+ end
1049
+ end
1050
+
1051
+ simple_uri :database, :document, :class, :batch, :function
1052
+ sql_uri :command , :query
1053
+
1054
+
1055
+
1056
+ end # class
1057
+ end # module
1058
+ __END__
1059
+