active-orient 0.2
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.
- 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
|