active-orient 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/Gemfile +16 -0
- data/Guardfile +21 -0
- data/LICENSE +22 -0
- data/README.md +275 -0
- data/acitve-orient.gemspec +27 -0
- data/config/boot.rb +24 -0
- data/config/connect.yml +13 -0
- data/lib/base.rb +220 -0
- data/lib/base_properties.rb +147 -0
- data/lib/model.rb +441 -0
- data/lib/orient.rb +98 -0
- data/lib/query.rb +88 -0
- data/lib/rest.rb +942 -0
- data/lib/support.rb +315 -0
- data/test.rb +4 -0
- data/usecase.md +91 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e808c07b0d7ea4b6ab44772dcfbed8521f1dbe1b
|
4
|
+
data.tar.gz: d1dc78cd6f6b0b5b8da14512e54e39777479b31c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: da1131e98ac909ba6e77526f52a6c9ae608eabe9d9a0d5d3dc2bb2a702fb7454493a2de516746e00f2d6f1f60c1bb4191393f49a28da470a3f3e87c763b624cb
|
7
|
+
data.tar.gz: 883ea6e68bfe1fc3a88dc4b7d9173a1a454c6141d6dfb234286aab3de1677666a0a115a022acd36049ed9cbf490abfd153287a547c67dddb9115671daf4bf9a8
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec
|
3
|
+
gem 'activesupport' , "~>4.2"
|
4
|
+
gem 'activemodel'
|
5
|
+
gem 'rest-client', :git => 'git://github.com/rest-client/rest-client.git'
|
6
|
+
group :development, :test do
|
7
|
+
gem "rspec"
|
8
|
+
gem 'rspec-its'
|
9
|
+
gem 'rspec-collection_matchers'
|
10
|
+
gem 'rspec-context-private'
|
11
|
+
gem 'guard'
|
12
|
+
gem 'guard-rspec'
|
13
|
+
## gem 'database_cleaner'
|
14
|
+
gem 'rb-inotify'
|
15
|
+
gem 'pry'
|
16
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
5
|
+
require "ostruct"
|
6
|
+
|
7
|
+
# Generic Ruby apps
|
8
|
+
rspec = OpenStruct.new
|
9
|
+
rspec.spec = ->(m) { "spec/#{m}_spec.rb" }
|
10
|
+
rspec.spec_dir = "spec"
|
11
|
+
rspec.spec_helper = "spec/spec_helper.rb"
|
12
|
+
|
13
|
+
|
14
|
+
watch(%r{^spec/.+_spec\.rb$})
|
15
|
+
watch(%r{^spec/usecase/(.+).rb$})
|
16
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
17
|
+
watch('spec/spec_helper.rb') { "spec" }
|
18
|
+
|
19
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
20
|
+
end
|
21
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 topofocus
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
# ActiveOrient
|
2
|
+
Use OrientDB to persistently store dynamic Ruby-Objects and use database queries to manage even very large
|
3
|
+
datasets.
|
4
|
+
|
5
|
+
The Package ist tested with Ruby 2.2.1 and Orientdb 2.1.
|
6
|
+
|
7
|
+
|
8
|
+
To start you need a ruby 2.x Installation and a working OrientDB-Instance.
|
9
|
+
Clone the project and run bundle install/ bundle update,
|
10
|
+
then modify »config/connect.yml«.
|
11
|
+
Its adviserable to generate the rdoc-documentation with executing
|
12
|
+
```
|
13
|
+
rdoc
|
14
|
+
```
|
15
|
+
from the source directory of AcitiveOrient and then to load the doc-directory into any browser.
|
16
|
+
|
17
|
+
For a quick start, go to the home directory of the package and start an irb-session
|
18
|
+
|
19
|
+
then
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require './config/boot'
|
23
|
+
r = ActiveOrient::OrientDB.new database: 'First'
|
24
|
+
=> #<ActiveOrient::OrientDB:0x000000048d0488 @res=#<RestClient::Resource:0x00000004927288
|
25
|
+
@url="http://localhost:2480", @block=nil,
|
26
|
+
@options={:user=>"xx", :password=>"***"}>, @database="First", @classes=[]>
|
27
|
+
```
|
28
|
+
|
29
|
+
»r« is the Database-Instance itself. Obviously the database is empty.
|
30
|
+
|
31
|
+
|
32
|
+
Let's create some classes
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
M = r.open_class 'classname' #
|
36
|
+
M = r.create_class 'classname' # creates or opens a basic document-class
|
37
|
+
M = r.create_vertex_class 'classname' # creates or opens a vertex-class
|
38
|
+
M = r.create_edge_class 'classname' # creates or opens an edge-class, providing bidirectional links between documents
|
39
|
+
|
40
|
+
r.delete_class M # universal removal-of-the-class-method
|
41
|
+
```
|
42
|
+
|
43
|
+
|
44
|
+
»M« is the ActiveOrient::Model-Class itself, a constant pointing to the class-definition of the ruby-class.
|
45
|
+
Its a shortcut for »ActiveOrient::Model::{Classname} and is reused if defined elsewhere.
|
46
|
+
|
47
|
+
If a schema is used, properties can be created and retrieved as well
|
48
|
+
```ruby
|
49
|
+
r.create_properties( M ) do
|
50
|
+
{ symbol: { propertyType: 'STRING' },
|
51
|
+
con_id: { propertyType: 'INTEGER' },
|
52
|
+
details: { propertyType: 'LINK', linkedClass: 'Contracts' }
|
53
|
+
}
|
54
|
+
|
55
|
+
r.get_class_properties M
|
56
|
+
```
|
57
|
+
or
|
58
|
+
```ruby
|
59
|
+
M.create_property 'symbol'
|
60
|
+
M.create_property 'con_id', type: 'integer'
|
61
|
+
M.create_property 'details', type: 'link', other_class: 'Contracts'
|
62
|
+
```
|
63
|
+
|
64
|
+
#### Active Model interface
|
65
|
+
|
66
|
+
Every OrientDB-Database-Class is mirrord as Ruby-Class. The Class itself is defined by
|
67
|
+
```ruby
|
68
|
+
M = r.create_class 'classname'
|
69
|
+
M = r.create_class { superclass_name: 'classname' }
|
70
|
+
Vertex = r.create_vertex_class 'classname'
|
71
|
+
Edge = r.create_edge_class 'classname'
|
72
|
+
```
|
73
|
+
and is of TYPE ActiveOrient::Model::{classname}
|
74
|
+
|
75
|
+
As for ActiveRecord-Tables, the Class itself provides methods to inspect and to filter datasets form the database.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
M.all
|
79
|
+
M.first
|
80
|
+
M.last
|
81
|
+
```
|
82
|
+
returns an Array containing all Documents/Edges of the Class; the first and the last Record.
|
83
|
+
```ruby
|
84
|
+
M.where town: 'Berlin'
|
85
|
+
```
|
86
|
+
performs a query on the class and returns the result as Array
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
M.count where: { town: 'Berlin' }
|
90
|
+
```
|
91
|
+
gets the number of datasets fullfilling the search-criteria. Any parameter defining a valid
|
92
|
+
SQL-Query in Orientdb can be provided to the count, where, first and last-method.
|
93
|
+
|
94
|
+
A »normal« Query is submitted via
|
95
|
+
```ruby
|
96
|
+
M.get_documents projection: { projection-parameter }
|
97
|
+
distinct: { some parameters }
|
98
|
+
where: { where-parameter }
|
99
|
+
order: { sorting-parameters }
|
100
|
+
group_by: { one grouping-parameter}
|
101
|
+
unwind:
|
102
|
+
skip:
|
103
|
+
limit:
|
104
|
+
|
105
|
+
# or
|
106
|
+
query = OrientSupport::OrientQuery.new {paramter}
|
107
|
+
M.get_documents query: query
|
108
|
+
|
109
|
+
```
|
110
|
+
|
111
|
+
Basic graph-support:
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
vertex_1 = Vertex.create color: "blue"
|
117
|
+
vertex_2 = Vertex.create flower: "rose"
|
118
|
+
Edge.create_edge attributes: { :birthday => Date.today }, from: vertex_1, to: vertex_2
|
119
|
+
```
|
120
|
+
connects the vertices and assigns the attributes to the edge
|
121
|
+
|
122
|
+
|
123
|
+
#### Links
|
124
|
+
|
125
|
+
A record in a database-class is defined by a »rid«. Every Model-Object comes with a handy »link«-method.
|
126
|
+
|
127
|
+
In OrientDB links are used to realise unidirectional 1:1 and 1:n relationships.
|
128
|
+
|
129
|
+
ActiveOrient autoloads Model-objects when they are accessed. As a consequence,
|
130
|
+
if an Object is stored in Cluster 30 and id 2, then "#30:2" fully qualifies the ActiveOrient::Model object.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
TestLinks = r.create_class 'Test_link_class'
|
134
|
+
TestBase = r.create_class 'Test_base_class'
|
135
|
+
|
136
|
+
link_document = TestLinks.create att: 'one attribute'
|
137
|
+
base_document = TestBase.create base: 'my_base', single_link: link_document
|
138
|
+
```
|
139
|
+
base_document.single_link just contains the rid. When accessed, the ActiveOrient::Model::Testlinkclass-object is autoloaded and
|
140
|
+
``` ruby
|
141
|
+
base_document.single_link.att
|
142
|
+
```
|
143
|
+
reads the stored content of link_document.
|
144
|
+
|
145
|
+
To store a list of links to other Database-Objects a simple Array is allocated
|
146
|
+
``` ruby
|
147
|
+
# predefined linkmap-properties
|
148
|
+
base_document = TestBase.create links: []
|
149
|
+
( 0 .. 20 ).each{ |y| base_document.links << TestLinks.create nr: y }
|
150
|
+
end
|
151
|
+
#or in schemaless-mode
|
152
|
+
base_document = TestBase.create links: (0..20).map{|y| TestLinks.create nr: y }
|
153
|
+
|
154
|
+
|
155
|
+
```
|
156
|
+
base_document.links behaves like a ruby-array.
|
157
|
+
|
158
|
+
If you got an undirectional graph
|
159
|
+
|
160
|
+
a --> b ---> c --> d
|
161
|
+
|
162
|
+
the graphelements can be explored by joining the objects ( a.b.c.d ), or (a.b[5].c[9].d )
|
163
|
+
|
164
|
+
#### Edges
|
165
|
+
|
166
|
+
Edges are easily handled
|
167
|
+
```ruby
|
168
|
+
Vertex = r.create_vertex_class 'd1'
|
169
|
+
Eedge = r.create_edge_class 'e1'
|
170
|
+
|
171
|
+
start = Vertex.create something: 'nice'
|
172
|
+
the_end = Vertex.create something: 'not_nice'
|
173
|
+
the_edge = Edge.create_edge attributes: { transform_to: 'very bad' },
|
174
|
+
from: start,
|
175
|
+
to: the_end
|
176
|
+
|
177
|
+
(...)
|
178
|
+
the_edge.delete
|
179
|
+
```
|
180
|
+
|
181
|
+
There is a basic support for traversals throught a graph.
|
182
|
+
The Edges are accessed by their names (downcase).
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
start.e1[0]
|
186
|
+
--> #<ActiveOrient::Model::E1:0x000000041e4e30
|
187
|
+
@metadata={"type"=>"d", "class"=>"E1", "version"=>60, "fieldTypes"=>"out=x,in=x",
|
188
|
+
"cluster"=>16, "record"=>43},
|
189
|
+
@attributes={"out"=>"#31:23", "in"=>"#31:15", "transform_to"=>"very bad" }>
|
190
|
+
```
|
191
|
+
The Attributes "in" and "out" can be used to move across the graph
|
192
|
+
```ruby
|
193
|
+
start.e1[0].out.something
|
194
|
+
---> "not_nice
|
195
|
+
start.e1[0].in.something
|
196
|
+
---> "nice
|
197
|
+
```
|
198
|
+
|
199
|
+
#### Execute SQL-Commands
|
200
|
+
At least - sql-commands can be executed as batch
|
201
|
+
|
202
|
+
The ActiveOrient::Query-Class provides a Query-Stack and an Records-Array which keeps the results.
|
203
|
+
The ActiveOrient::Query-Class acts as Parent-Class for aggregated Records (without a @rid), which are ActiveOrient::Model::Myquery Objects. If a Query returns a database-record, the correct ActiveOrient::Model-Class is instantiated.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
ach = ActiveOrient::Query.new
|
207
|
+
|
208
|
+
ach.queries << 'create class Contracts ABSTRACT'
|
209
|
+
ach.queries << 'create property Contracts.subcategory link'
|
210
|
+
ach.queries << 'create property Contracts.details link'
|
211
|
+
ach.queries << 'create class Stocks extends Contracts'
|
212
|
+
ach.queries << 'create class Futures extends Contracts'
|
213
|
+
result = ach.execute_queries transaction: false
|
214
|
+
|
215
|
+
|
216
|
+
|
217
|
+
```
|
218
|
+
queries the database as demonstrated above. In addition, the generated query itself is added to the »queries«-Stack and the result can be found in sample_query.records.
|
219
|
+
|
220
|
+
This feature can be used as a substitute for simple functions
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
roq = ActiveOrient::Query.new
|
224
|
+
roq.queries =["select name, categories.subcategories.contracts from Industries where name containstext …'ial'"]
|
225
|
+
roq.execute_queries.each{|x| puts x.name, x.categories.inspect }
|
226
|
+
--> Basic Materials [["#21:1"]]
|
227
|
+
--> Financial [["#21:2"]]
|
228
|
+
--> Industrial [["#23:0", "#23:1"]]
|
229
|
+
```
|
230
|
+
|
231
|
+
OrientDB supports the execution of SQL-Batch-Commands.
|
232
|
+
( http://orientdb.com/docs/2.0/orientdb.wiki/SQL-batch.html )
|
233
|
+
This is supported simply by using a Array as Argument for ActiveOrient::Query.queries
|
234
|
+
|
235
|
+
Therefor complex queries can be simplified using database-variables
|
236
|
+
```ruby
|
237
|
+
ach = ActiveOrient::Query.new
|
238
|
+
ach.queries << [ "select expand( contracts ) from Openinterest"
|
239
|
+
"let con = select expand( contracts ) from Openinterest; ",
|
240
|
+
"...", ... ]
|
241
|
+
result = ach.execute_queries
|
242
|
+
```
|
243
|
+
|
244
|
+
The contract-documents are accessible with
|
245
|
+
```ruby
|
246
|
+
r.get_document '21:1'
|
247
|
+
--><Stocks: con_id: 77680640 currency: EUR details: #18:1 exchange: SMART local_symbol: BAS
|
248
|
+
primary_exchange: IBIS subcategory: #14:1 symbol: BAS>
|
249
|
+
```
|
250
|
+
or
|
251
|
+
```ruby
|
252
|
+
my_query = ActiveOrient::Query.new
|
253
|
+
['Contracts', 'Industries', 'Categories', 'Subcategories'].each do |table|
|
254
|
+
my_query.queries = [ "select count(*) from #{table}"]
|
255
|
+
|
256
|
+
count = my_query.execute_queries
|
257
|
+
# count=> [#<ActiveOrient::Model::Myquery:0x00000003b317c8
|
258
|
+
# @metadata={"type"=>"d", "class"=>nil, "version"=>0, "fieldTypes"=>"count=l"},
|
259
|
+
# @attributes={"count"=>4 } ] --> an Array with one Element, therefor count.pop
|
260
|
+
puts "Table #{table} \t #{count.pop.count} Datasets "
|
261
|
+
end
|
262
|
+
-->Table Contracts 56 Datasets
|
263
|
+
-->Table Industries 8 Datasets
|
264
|
+
-->Table Categories 22 Datasets
|
265
|
+
-->Table Subcategories 35 Datasets
|
266
|
+
|
267
|
+
```
|
268
|
+
|
269
|
+
Note that the fetched Object is of type »Stocks« (ActiveOrient::Model::Stocks).
|
270
|
+
|
271
|
+
The ActiveOrient-API documentation can be found here: https://github.com/orientechnologies/orientdb-docs/wiki/OrientDB-ActiveOrient
|
272
|
+
and the ActiveModel-documentation is here: http://www.rubydoc.info/gems/activemodel
|
273
|
+
|
274
|
+
|
275
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "active-orient"
|
6
|
+
s.version = File.open('VERSION').read.strip
|
7
|
+
s.authors = [" Hartmut Bischoff"]
|
8
|
+
s.email = ["topofocus@gmail.com"]
|
9
|
+
s.homepage = 'https://github.com/topofocus/active-orient'
|
10
|
+
s.licenses = ['MIT']
|
11
|
+
s.summary = 'Pure ruby client for OrientDB based on ActiveModel'
|
12
|
+
s.description = 'Persistent ORM for OrientDB, based on ActiveModel'
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.required_ruby_version = '>= 2.2.0'
|
15
|
+
s.date = Time.now.strftime "%Y-%m-%d"
|
16
|
+
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
s.bindir = "exe"
|
20
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
|
22
|
+
s.add_development_dependency "bundler", "~> 1.8"
|
23
|
+
s.add_development_dependency "rake", "~> 10.0"
|
24
|
+
s.add_dependency 'activesupport'
|
25
|
+
# s.add_dependency 'rest-client', :git => 'git://github.com/rest-client/rest-client.git'
|
26
|
+
|
27
|
+
end
|
data/config/boot.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'yaml'
|
3
|
+
project_root = File.expand_path('../..', __FILE__)
|
4
|
+
require "#{project_root}/lib/support.rb"
|
5
|
+
require "#{project_root}/lib/base.rb"
|
6
|
+
require "#{project_root}/lib/base_properties.rb"
|
7
|
+
|
8
|
+
require "#{project_root}/lib/model.rb"
|
9
|
+
require "#{project_root}/lib/orient.rb"
|
10
|
+
require "#{project_root}/lib/rest.rb"
|
11
|
+
#require "#{project_root}/lib/graph.rb"
|
12
|
+
|
13
|
+
# require all the models and libraries
|
14
|
+
libs= Dir.glob(File.join( project_root, "lib",'**', "*rb"))
|
15
|
+
result = libs.reverse.collect { |file| [file, require( file )] }
|
16
|
+
|
17
|
+
logger = Logger.new '/dev/stdout'
|
18
|
+
#REST::Graph.logger = logger
|
19
|
+
ActiveOrient::Model.logger = logger
|
20
|
+
ActiveOrient::OrientDB.logger = logger
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
data/config/connect.yml
ADDED
data/lib/base.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
module ActiveOrient
|
2
|
+
require 'active_model'
|
3
|
+
#
|
4
|
+
# Base class for tableless IB data Models, extends ActiveModel API
|
5
|
+
class Base
|
6
|
+
extend ActiveModel::Naming
|
7
|
+
extend ActiveModel::Callbacks
|
8
|
+
include ActiveModel::Validations
|
9
|
+
include ActiveModel::Serialization
|
10
|
+
include ActiveModel::Serializers::Xml
|
11
|
+
include ActiveModel::Serializers::JSON
|
12
|
+
|
13
|
+
|
14
|
+
##Every Rest::Base-Object is stored in the @@rid_store
|
15
|
+
## The Objects are just references to the @@rid_store.
|
16
|
+
## any Change of the Object is thus synchonized to any allocated variable
|
17
|
+
#
|
18
|
+
@@rid_store = Hash.new
|
19
|
+
|
20
|
+
def self.display_riid
|
21
|
+
@@rid_store
|
22
|
+
end
|
23
|
+
def self.remove_riid obj
|
24
|
+
@@rid_store[obj.riid]=nil
|
25
|
+
end
|
26
|
+
def self.get_riid link
|
27
|
+
|
28
|
+
end
|
29
|
+
def self.store_riid obj
|
30
|
+
if obj.rid.present? && obj.riid.all?{|x| x.present? && x>=0} # only positive values are stored
|
31
|
+
## return the presence of a stored object as true by the block
|
32
|
+
## the block is only executed if the presence is confirmed
|
33
|
+
## Nothing is returned from the class-method
|
34
|
+
if @@rid_store[obj.riid].present?
|
35
|
+
yield if block_given?
|
36
|
+
end
|
37
|
+
@@rid_store[obj.riid] = obj
|
38
|
+
@@rid_store[obj.riid] # return_value
|
39
|
+
else
|
40
|
+
obj # no rid-value: just return the obj
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
define_model_callbacks :initialize
|
46
|
+
|
47
|
+
mattr_accessor :logger
|
48
|
+
# If a opts hash is given, keys are taken as attribute names, values as data.
|
49
|
+
# The model instance fields are then set automatically from the opts Hash.
|
50
|
+
def initialize attributes={}, opts={}
|
51
|
+
logger.progname= "ActiveOrient::Base#initialize"
|
52
|
+
#possible_link_array_candidates = link_candidates = Hash.new
|
53
|
+
@metadata = HashWithIndifferentAccess.new
|
54
|
+
# @edges = HashWithIndifferentAccess.new
|
55
|
+
|
56
|
+
run_callbacks :initialize do
|
57
|
+
# puts "initialize::attributes: #{attributes.inspect}"
|
58
|
+
|
59
|
+
attributes.keys.each do | att |
|
60
|
+
unless att[0] == "@" # @ identifies Metadata-attributes
|
61
|
+
att = att.to_sym if att.is_a?(String)
|
62
|
+
unless self.class.instance_methods.detect{|x| x == att }
|
63
|
+
self.class.define_property att, nil
|
64
|
+
# logger.debug { "property #{att.to_s} assigned to #{self.class.to_s}" }
|
65
|
+
else
|
66
|
+
# logger.info{ "property #{att.to_s} NOT assigned " }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if attributes['@type'] == 'd' # document
|
72
|
+
@metadata[ :type ] = attributes.delete '@type'
|
73
|
+
@metadata[ :class ] = attributes.delete '@class'
|
74
|
+
@metadata[ :version ] = attributes.delete '@version'
|
75
|
+
@metadata[ :fieldTypes ] = attributes.delete '@fieldTypes'
|
76
|
+
if attributes.has_key?( '@rid' )
|
77
|
+
rid = attributes.delete '@rid'
|
78
|
+
cluster, record = rid[1,rid.size].split(':')
|
79
|
+
@metadata[ :cluster ] = cluster.to_i
|
80
|
+
@metadata[ :record ] = record.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
#### edges -- remove in_ and out_ and de-capitalize the remaining edge
|
84
|
+
if @metadata[ :fieldTypes ].present? && (@metadata[ :fieldTypes ] =~ /=g/)
|
85
|
+
edges = @metadata['fieldTypes'].split(',').find_all{|x| x=~/=g/}.map{|x| x.split('=').first}
|
86
|
+
edges.each do |edge|
|
87
|
+
operator, *base_edge = edge.split('_')
|
88
|
+
base_edge = base_edge.join('_')
|
89
|
+
unless self.class.instance_methods.detect{|x| x == base_edge }
|
90
|
+
## define two methods: out_{Edge}/{in_Edge} -> edge.
|
91
|
+
self.class.define_property base_edge, nil
|
92
|
+
self.class.send :alias_method, base_edge.underscore, edge #
|
93
|
+
# logger.debug { "#{link}:: edge #{edge} assigned to #{self.class.to_s} and remaped to #{base_edge.underscore}" }
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
self.attributes = attributes # set_attribute_defaults is now after_init callback
|
102
|
+
end
|
103
|
+
ActiveOrient::Base.store_riid self
|
104
|
+
end
|
105
|
+
|
106
|
+
# ActiveModel API (for serialization)
|
107
|
+
|
108
|
+
def attributes
|
109
|
+
@attributes ||= HashWithIndifferentAccess.new
|
110
|
+
end
|
111
|
+
|
112
|
+
def attributes= attrs
|
113
|
+
attrs.keys.each { |key| self.send("#{key}=", attrs[key]) }
|
114
|
+
end
|
115
|
+
|
116
|
+
# ActiveModel-style read/write_attribute accessors
|
117
|
+
# Here we define the autoload mechanism
|
118
|
+
def [] key
|
119
|
+
|
120
|
+
iv= attributes[key.to_sym]
|
121
|
+
# iv.from_orient unless iv.nil?
|
122
|
+
if iv.is_a?(String) && iv.rid? #&& @metadata[:fieldTypes].present? && @metadata[:fieldTypes].include?( key.to_s+"=x" )
|
123
|
+
# puts "autoload: #{iv}"
|
124
|
+
ActiveOrient::Model.autoload_object iv
|
125
|
+
elsif iv.is_a?(Array) # && @metadata[:fieldTypes].present? && @metadata[:fieldTypes].match( key.to_s+"=[znmgx]" )
|
126
|
+
# puts "autoload: #{iv.inspect}"
|
127
|
+
OrientSupport::Array.new self, *iv.map{|y| (y.is_a?(String) && y.rid?) ? ActiveOrient::Model.autoload_object( y ) : y }
|
128
|
+
else
|
129
|
+
|
130
|
+
iv
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def update_attribute key, value
|
135
|
+
@attributes[key] = value
|
136
|
+
end
|
137
|
+
=begin
|
138
|
+
Here we define how the attributes are initialized
|
139
|
+
Key and val are set by the RestCliend
|
140
|
+
=end
|
141
|
+
def []= key, val
|
142
|
+
val = val.rid if val.is_a? ActiveOrient::Model
|
143
|
+
# if val.is_a?(Array) # && @metadata[:fieldTypes].present? && @metadata[:fieldTypes].include?( key.to_s+"=n" )
|
144
|
+
# if @metadata[ :fieldTypes ] =~ /out=x,in=x/
|
145
|
+
# puts "VAL is a ARRAY"
|
146
|
+
# else
|
147
|
+
# puts "METADATA: #{ @metadata[ :fieldTypes ]} "
|
148
|
+
# end
|
149
|
+
# val# = val.map{|x| if val.is_a? ActiveOrient::Model then val.rid else val end }
|
150
|
+
# end
|
151
|
+
attributes[key.to_sym] = case val
|
152
|
+
when Array
|
153
|
+
if val.first.is_a?(Hash)
|
154
|
+
v=val.map do |x|
|
155
|
+
if x.is_a?( Hash )
|
156
|
+
HashWithIndifferentAccess.new(x)
|
157
|
+
else
|
158
|
+
x
|
159
|
+
end
|
160
|
+
end
|
161
|
+
OrientSupport::Array.new( self, *v )
|
162
|
+
else
|
163
|
+
OrientSupport::Array.new( self, *val )
|
164
|
+
end
|
165
|
+
when Hash
|
166
|
+
HashWithIndifferentAccess.new(val)
|
167
|
+
else
|
168
|
+
val
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_model
|
174
|
+
self
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
|
179
|
+
### Noop methods mocking ActiveRecord::Base macros
|
180
|
+
|
181
|
+
def self.attr_protected *args
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.attr_accessible *args
|
185
|
+
end
|
186
|
+
|
187
|
+
### ActiveRecord::Base association API mocks
|
188
|
+
|
189
|
+
def self.belongs_to model, *args
|
190
|
+
attr_accessor model
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.has_one model, *args
|
194
|
+
attr_accessor model
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.has_many models, *args
|
198
|
+
attr_accessor models
|
199
|
+
|
200
|
+
define_method(models) do
|
201
|
+
self.instance_variable_get("@#{models}") ||
|
202
|
+
self.instance_variable_set("@#{models}", [])
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.find *args
|
207
|
+
[]
|
208
|
+
end
|
209
|
+
|
210
|
+
### ActiveRecord::Base callback API mocks
|
211
|
+
|
212
|
+
define_model_callbacks :initialize, :only => :after
|
213
|
+
|
214
|
+
### ActiveRecord::Base misc
|
215
|
+
|
216
|
+
def self.serialize *properties
|
217
|
+
end
|
218
|
+
|
219
|
+
end # Model
|
220
|
+
end # module
|