active-orient 0.6 → 0.42

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