active-orient 0.4 → 0.80
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 +5 -5
- data/.gitignore +1 -0
- data/.graphs.txt.swp +0 -0
- data/Gemfile +9 -5
- data/Guardfile +12 -4
- data/README.md +70 -281
- data/VERSION +1 -1
- data/active-orient.gemspec +9 -7
- data/bin/active-orient-0.6.gem +0 -0
- data/bin/active-orient-console +97 -0
- data/changelog.md +60 -0
- data/config/boot.rb +70 -17
- data/config/config.yml +10 -0
- data/config/connect.yml +11 -6
- data/examples/books.rb +154 -65
- data/examples/streets.rb +89 -85
- data/graphs.txt +70 -0
- data/lib/active-orient.rb +78 -6
- data/lib/base.rb +266 -168
- data/lib/base_properties.rb +76 -65
- data/lib/class_utils.rb +187 -0
- data/lib/database_utils.rb +99 -0
- data/lib/init.rb +80 -0
- data/lib/java-api.rb +442 -0
- data/lib/jdbc.rb +211 -0
- data/lib/model/custom.rb +29 -0
- data/lib/model/e.rb +6 -0
- data/lib/model/edge.rb +114 -0
- data/lib/model/model.rb +134 -0
- data/lib/model/the_class.rb +657 -0
- data/lib/model/the_record.rb +313 -0
- data/lib/model/vertex.rb +371 -0
- data/lib/orientdb_private.rb +48 -0
- data/lib/other.rb +423 -0
- data/lib/railtie.rb +68 -0
- data/lib/rest/change.rb +150 -0
- data/lib/rest/create.rb +287 -0
- data/lib/rest/delete.rb +150 -0
- data/lib/rest/operations.rb +222 -0
- data/lib/rest/read.rb +189 -0
- data/lib/rest/rest.rb +120 -0
- data/lib/rest_disabled.rb +24 -0
- data/lib/support/conversions.rb +42 -0
- data/lib/support/default_formatter.rb +7 -0
- data/lib/support/errors.rb +41 -0
- data/lib/support/logging.rb +38 -0
- data/lib/support/orient.rb +305 -0
- data/lib/support/orientquery.rb +647 -0
- data/lib/support/query.rb +92 -0
- data/rails.md +154 -0
- data/rails/activeorient.rb +32 -0
- data/rails/config.yml +10 -0
- data/rails/connect.yml +17 -0
- metadata +89 -30
- data/lib/model.rb +0 -461
- data/lib/orient.rb +0 -98
- data/lib/query.rb +0 -88
- data/lib/rest.rb +0 -1036
- data/lib/support.rb +0 -347
- data/test.rb +0 -4
- data/usecase.md +0 -91
data/examples/streets.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
=begin
|
2
|
-
Streets Example
|
2
|
+
---> The Streets Example does not work as the structure of external data used changed!
|
3
3
|
|
4
|
+
Streets Example
|
4
5
|
|
5
6
|
We load german cities from wikipedia and parse the document for cities and countries(states).
|
6
7
|
Further we load a collection of popular streetnames from a web-side called »strassen-in-deutschland«
|
@@ -15,115 +16,118 @@ At last we print the connected cities.
|
|
15
16
|
module DataImport
|
16
17
|
def read_german_street_names
|
17
18
|
doc = Nokogiri::HTML(open('http://www.strassen-in-deutschland.de/die-haeufigsten-strassennamen-in-deutschland.html'))
|
18
|
-
strassen = doc.css('
|
19
|
+
strassen = doc.css("td[data-header='Straßenname: '] a") # identified via css-inspector in browser
|
19
20
|
# search for the css and include only links, then display the text-part
|
20
21
|
strassen.children.map( &:to_s )[3..-1] # omit the first three (strassen in deutschland, straßenverzeichnis, straßen)
|
21
22
|
end
|
22
23
|
|
23
|
-
def read_german_cities_from_wikipedia
|
24
|
-
# we extract <li> -elements and use the text until "("
|
24
|
+
def read_german_cities_from_wikipedia
|
25
|
+
# we extract <li> -elements and use the text until "("
|
25
26
|
#doc.xpath("//li").at(80)
|
26
|
-
# => #<Nokogiri::XML::Element:0x1ba551c name="li" children=[#<Nokogiri::XML::Element:0x1ba533c name="a" attributes=[#<Nokogiri::XML::Attr:0x1ba52d8 name="href" value="/wiki/Angerm%C3%BCnde">, #<Nokogiri::XML::Attr:0x1ba52c4 name="title" value="Angermünde">] children=[#<Nokogiri::XML::Text:0x1ba4c84 "Angermünde">]>, #<Nokogiri::XML::Text:0x1ba4ae0 " (Brandenburg)">]>
|
27
|
+
# => #<Nokogiri::XML::Element:0x1ba551c name="li" children=[#<Nokogiri::XML::Element:0x1ba533c name="a" attributes=[#<Nokogiri::XML::Attr:0x1ba52d8 name="href" value="/wiki/Angerm%C3%BCnde">, #<Nokogiri::XML::Attr:0x1ba52c4 name="title" value="Angermünde">] children=[#<Nokogiri::XML::Text:0x1ba4c84 "Angermünde">]>, #<Nokogiri::XML::Text:0x1ba4ae0 " (Brandenburg)">]>
|
27
28
|
|
28
29
|
doc = Nokogiri::HTML(open('https://en.wikipedia.org/wiki/List_of_cities_and_towns_in_Germany'))
|
30
|
+
print doc
|
29
31
|
doc.xpath("//li").map{|x| x.text[0 .. x.text.index('(')-2] if x.text.index('(').present? }.compact
|
30
32
|
end
|
31
33
|
|
32
|
-
def
|
34
|
+
def read_german_cities_and_states_from_wikipedia
|
33
35
|
doc =
|
34
|
-
|
35
|
-
doc.xpath("//li").map do |x|
|
36
|
-
if x.text.index('(').present?
|
37
|
-
|
36
|
+
Nokogiri::HTML(open('https://en.wikipedia.org/wiki/List_of_cities_and_towns_in_Germany'))
|
37
|
+
doc.xpath("//li").map do |x|
|
38
|
+
if x.text.index('(').present?
|
39
|
+
[ x.text[0 .. x.text.index('(')-2] , x.text[ x.text.index('(')+1 .. x.text.index(')')-1] ]
|
38
40
|
end
|
39
41
|
end.compact
|
40
|
-
end
|
42
|
+
end
|
41
43
|
|
42
44
|
end # module
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
ActiveOrient::Model::Connects.create_property :distance, type: :integer, index: :notunique
|
66
|
-
ActiveOrient::OrientDB.logger.info { "Vertex- and Edge-Classes rebuilded" }
|
67
|
-
end
|
46
|
+
class StreetExample
|
47
|
+
|
48
|
+
include DataImport
|
49
|
+
def initialize db, rebuild: true
|
50
|
+
if rebuild
|
51
|
+
db.delete_class :State
|
52
|
+
db.delete_class :City
|
53
|
+
db.delete_class :Street
|
54
|
+
db.delete_class :CONNECTS
|
55
|
+
db.create_vertex_class :state, :city, :street
|
56
|
+
db.create_edge_class :CONNECTS
|
57
|
+
State.create_property( :name, type: :string, index: :unique )
|
58
|
+
City.create_properties( { name: { type: :string },
|
59
|
+
state: { type: :link, :linked_class => 'State' } }
|
60
|
+
) do
|
61
|
+
{ citi_idx: :unique }
|
62
|
+
end
|
63
|
+
Street.create_property :name , type: :string, index: :notunique
|
64
|
+
CONNECTS.create_property :distance, type: :integer, index: :notunique
|
65
|
+
logger.progname = "StreetsExample#Initialize"
|
66
|
+
logger.info { "Vertex- and Edge-Classes rebuilded" }
|
68
67
|
end
|
68
|
+
end
|
69
69
|
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
C.create_edge :from => street_record, :to => cities
|
71
|
+
def read_from_web
|
72
|
+
read_german_cities_and_states_from_wikipedia.each do |city,state|
|
73
|
+
state = State.update_or_create( where: { name: state }).first
|
74
|
+
City.create name: city, state: "##{state.rid}"
|
75
|
+
end
|
76
|
+
logger.progname = "StreetsExample#ReadFromWeb"
|
77
|
+
logger.info { "#{City.count} Cities imported from Wikipedia " }
|
78
|
+
|
79
|
+
cities_rids = City.all.map &:rid
|
80
|
+
read_german_street_names.each_with_index do |street, i|
|
81
|
+
street_record = Street.create name: street
|
82
|
+
count = i
|
83
|
+
cities = Array.new
|
84
|
+
while count < cities_rids.size && cities.size < 5 do
|
85
|
+
cities << cities_rids[count]
|
86
|
+
count = count + i
|
89
87
|
end
|
90
|
-
|
88
|
+
CONNECTS.create_edge :from => street_record, :to => cities
|
91
89
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
90
|
+
logger.progname = "StreetsExample#ReadFromWeb"
|
91
|
+
logger.info { "#{CONNECTS.count} Edges between Streets and Cities created " }
|
92
|
+
end
|
93
|
+
|
94
|
+
def display_streets_per_state
|
95
|
+
State.all.each do |state|
|
96
|
+
streets= Street.all.map do |street |
|
97
|
+
if street.connects.in.detect{|x| x.state == state }
|
98
|
+
street.name + " connects " + street.connects.in.map( &:name ).join('; ')
|
99
|
+
end
|
100
|
+
end.compact
|
101
|
+
unless streets.empty?
|
102
|
+
puts "..................................."
|
103
|
+
puts state.name
|
104
|
+
puts "..................................."
|
105
|
+
puts streets.join("\n")
|
106
106
|
end
|
107
107
|
end
|
108
|
-
|
108
|
+
end
|
109
|
+
end
|
109
110
|
|
110
111
|
if $0 == __FILE__
|
111
112
|
|
112
|
-
require '../config/boot'
|
113
|
-
require 'open-uri'
|
114
|
-
require 'nokogiri'
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
113
|
+
require '../config/boot'
|
114
|
+
require 'open-uri'
|
115
|
+
require 'nokogiri'
|
116
|
+
|
117
|
+
ActiveOrient::OrientDB.default_server= { user: 'root', password: 'tretretre' }
|
118
|
+
r = ActiveOrient::OrientDB.new database: 'StreetTest'
|
119
|
+
ActiveOrient::OrientDB.logger.level = Logger::INFO
|
120
|
+
s= StreetExample.new r, rebuild: true
|
121
|
+
|
122
|
+
def to_orient
|
123
|
+
#puts "here hash"
|
124
|
+
substitute_hash = Hash.new
|
125
|
+
keys.each{|k| substitute_hash[k] = self[k].to_orient}
|
126
|
+
substitute_hash
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
s.read_from_web if City.count.zero?
|
131
|
+
s.display_streets_per_state
|
128
132
|
|
129
133
|
end
|
data/graphs.txt
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
Graphen
|
2
|
+
======
|
3
|
+
|
4
|
+
Die Felder `in` und `out` bündeln die Verknüpfungen zu anderen Vertices.
|
5
|
+
|
6
|
+
Die Verknüpfungen erfolgen über `Edges`, die wiederum Eigenschaften haben. Zuerst sind die `Edges` Klassen und
|
7
|
+
unterscheiden sich üder den Namen. Zusätzlich besteht Vererbung.
|
8
|
+
|
9
|
+
Beispielgraph
|
10
|
+
@db.create_vertex_class :v1
|
11
|
+
@db.create_class( :v2 ){ V1 }
|
12
|
+
@db.create_edge_class "e1"
|
13
|
+
@db.create_class( :e2 ){ E1 }
|
14
|
+
@db.create_class( :e3 ){ E1 }
|
15
|
+
|
16
|
+
vertices = (1..10).map{|y| V2.create node: y}
|
17
|
+
E2.create from: V1.create( item: 1 ), to: vertices
|
18
|
+
|
19
|
+
v1 = V.first.to_human => "<V1[#25:0]: out: {E2=>10}, item : 1>"
|
20
|
+
v2 = V.last.to_human => "<V2[#40:0]: in: {E2=>1}, node : 8>"
|
21
|
+
|
22
|
+
Boardmittel von ActiveOrient
|
23
|
+
----------------------------
|
24
|
+
|
25
|
+
## Edges anlisten
|
26
|
+
|
27
|
+
#### Nur Links
|
28
|
+
|
29
|
+
v1.edges => ["#49:0", "#50:0", "#51:0", "#52:0", "#53:0", "#54:0", "#55:0", "#56:0", "#49:1", "#50:1"]
|
30
|
+
v2.edges => ["#56:0"]
|
31
|
+
|
32
|
+
|
33
|
+
#### Auflösung der Objekte
|
34
|
+
|
35
|
+
v2.in.map &:to_human => ["<E2: in : #<V2:0x000000000397fcb8>, out : #<V1:0x0000000002ed0060>>"]
|
36
|
+
|
37
|
+
#### Verfügbare Methoden
|
38
|
+
v2.in v2.in_e v2.in_e2 v2.in_e2= v2.in_edges
|
39
|
+
|
40
|
+
|
41
|
+
### Selektive Auswahl
|
42
|
+
|
43
|
+
Beispiel: TimeGrid
|
44
|
+
|
45
|
+
Der Vertex hat folgende Struktur
|
46
|
+
|
47
|
+
t.to_human
|
48
|
+
=> "<Tag[82:8927]: in: {TG::DAY_OF=>1, TG::GRID_OF=>1}, out: {ML::L_OHLC=>1, TG::GRID_OF=>1}, value : 25>"
|
49
|
+
|
50
|
+
dann listet t.detect_edges :out, /ml/ die Edges der ML-Klassen auf:
|
51
|
+
|
52
|
+
t.detect_edges( :out , /ml/).to_human
|
53
|
+
=> ["<L_OHLC[#151:16] -i-> #161:22 { } -o-> #82:8927>"]
|
54
|
+
|
55
|
+
|
56
|
+
#### Node
|
57
|
+
|
58
|
+
Ein Node ist ein direkt über eine Edge verbundener Vertex
|
59
|
+
|
60
|
+
Q = OrientSupport::OrientQuery
|
61
|
+
s= Q.new projection: "expand( outE('e1').in[node <5])"
|
62
|
+
s.from = '#25:0'
|
63
|
+
s.to_s => select expand( outE('v1').in[node <5]) from #25:0
|
64
|
+
|
65
|
+
Aufruf in OrientQuery: Q.node Edge_class, condition
|
66
|
+
|
67
|
+
Fügt den Node in das Porjections-Array ein.
|
68
|
+
|
69
|
+
|
70
|
+
|
data/lib/active-orient.rb
CHANGED
@@ -1,7 +1,79 @@
|
|
1
|
-
require "support.rb"
|
2
|
-
require "base.rb"
|
3
|
-
require "base_properties.rb"
|
4
1
|
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
module OrientDB
|
3
|
+
UsingJava = RUBY_PLATFORM == 'java' ? true : false
|
4
|
+
unless UsingJava
|
5
|
+
DocumentDatabase = nil
|
6
|
+
DocumentDatabasePool = nil
|
7
|
+
DocumentDatabasePooled = nil
|
8
|
+
GraphDatabase = nil
|
9
|
+
OTraverse = nil
|
10
|
+
Document = nil
|
11
|
+
IndexType = nil
|
12
|
+
OClassImpl = nil
|
13
|
+
PropertyImpl = nil
|
14
|
+
Schema = nil
|
15
|
+
SchemaProxy = nil
|
16
|
+
SchemaType = nil
|
17
|
+
SQLCommand = nil
|
18
|
+
SQLSynchQuery = nil
|
19
|
+
User = nil
|
20
|
+
RemoteStorage = nil
|
21
|
+
ServerAdmin = nil
|
22
|
+
# defined in other.rb
|
23
|
+
#JavaDate
|
24
|
+
#RecordList = nil
|
25
|
+
# RidBag = nil
|
26
|
+
# RecordSet = nil
|
27
|
+
end
|
28
|
+
end # module OrientDB
|
29
|
+
require 'active_model'
|
30
|
+
#require 'active_model/serializers'
|
31
|
+
require_relative "support/orientquery.rb"
|
32
|
+
require_relative "support/conversions.rb"
|
33
|
+
#require_relative "support/logging.rb"
|
34
|
+
require_relative "support/errors.rb"
|
35
|
+
require_relative "base.rb"
|
36
|
+
require_relative "base_properties.rb"
|
37
|
+
require_relative "support/orient.rb"
|
38
|
+
#require_relative "query.rb"
|
39
|
+
if OrientDB::UsingJava
|
40
|
+
require_relative 'java-api.rb'
|
41
|
+
end
|
42
|
+
require_relative "orientdb_private.rb" # manage private functions
|
43
|
+
require_relative "database_utils.rb" #common methods without rest.specific content
|
44
|
+
require_relative "class_utils.rb" #common methods without rest.specific content
|
45
|
+
require_relative "other.rb"
|
46
|
+
require_relative "rest/rest.rb"
|
47
|
+
require_relative "model/model.rb"
|
48
|
+
require 'active_support/core_ext/string' # provides blank?, present?, presence etc
|
49
|
+
require_relative 'init.rb'
|
50
|
+
# create Base Classes
|
51
|
+
|
52
|
+
require_relative "model/vertex.rb"
|
53
|
+
require_relative "model/edge.rb"
|
54
|
+
|
55
|
+
require_relative "railtie" if defined?(Rails)
|
56
|
+
|
57
|
+
module ActiveOrient
|
58
|
+
mattr_accessor :database
|
59
|
+
mattr_accessor :db_pool
|
60
|
+
mattr_accessor :database_classes
|
61
|
+
mattr_accessor :default_server
|
62
|
+
|
63
|
+
## displays all allocated classes
|
64
|
+
##
|
65
|
+
## usage:
|
66
|
+
## puts ActiveOrient::show_classes
|
67
|
+
#
|
68
|
+
#
|
69
|
+
def self.show_classes
|
70
|
+
|
71
|
+
'-'* 45+"\n"+
|
72
|
+
"Database Class -> ActiveOrient ClassName\n"+
|
73
|
+
'-'* 45+"\n"+
|
74
|
+
ActiveOrient.database_classes.map{|x,y| "#{"%15s"% x} -> #{y.to_s}" }.join("\n") + "\n"+
|
75
|
+
'-'* 45 + "\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
data/lib/base.rb
CHANGED
@@ -1,220 +1,318 @@
|
|
1
1
|
module ActiveOrient
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
|
3
|
+
|
4
|
+
# Base class for tableless IB data Models, extends ActiveModel API
|
5
|
+
|
5
6
|
class Base
|
7
|
+
# include OrientSupport::Logging
|
6
8
|
extend ActiveModel::Naming
|
7
9
|
extend ActiveModel::Callbacks
|
8
10
|
include ActiveModel::Validations
|
9
11
|
include ActiveModel::Serialization
|
10
|
-
include ActiveModel::Serializers::Xml
|
12
|
+
# include ActiveModel::Serializers::Xml
|
11
13
|
include ActiveModel::Serializers::JSON
|
12
|
-
|
14
|
+
include OrientDB
|
15
|
+
include Conversions # mocking ActiveModel::Conversions
|
16
|
+
|
17
|
+
mattr_accessor :logger
|
18
|
+
|
19
|
+
define_model_callbacks :initialize
|
20
|
+
# ActiveRecord::Base callback API mocks
|
21
|
+
define_model_callbacks :initialize, :only => :after
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
## any Change of the Object is thus synchonized to any allocated variable
|
17
|
-
#
|
18
|
-
@@rid_store = Hash.new
|
23
|
+
# Used to read the metadata
|
24
|
+
attr_reader :metadata
|
19
25
|
|
20
|
-
|
26
|
+
=begin
|
27
|
+
Every Rest::Base-Object is stored in the @@rid_store
|
28
|
+
The Objects are just references to the @@rid_store.
|
29
|
+
Any Change of the Object is thus synchonized to any allocated variable.
|
30
|
+
=end
|
31
|
+
@@rid_store = Hash.new
|
32
|
+
@@mutex = Mutex.new
|
33
|
+
|
34
|
+
def self.display_rid
|
21
35
|
@@rid_store
|
22
36
|
end
|
23
|
-
def self.remove_riid obj
|
24
|
-
@@rid_store[obj.riid]=nil
|
25
|
-
end
|
26
|
-
def self.get_riid link
|
27
37
|
|
38
|
+
=begin
|
39
|
+
removes an Item from the cache
|
40
|
+
|
41
|
+
obj has to provide a method #rid
|
42
|
+
|
43
|
+
thus a string or a Model-Object is accepted
|
44
|
+
=end
|
45
|
+
|
46
|
+
def self.remove_rid obj
|
47
|
+
if obj &.rid.present?
|
48
|
+
@@mutex.synchronize do
|
49
|
+
@@rid_store.delete obj.rid
|
50
|
+
end
|
51
|
+
else
|
52
|
+
logger.error "Cache entry not removed: #{obj} "
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.get_rid rid
|
57
|
+
rid = rid[1..-1] if rid[0]=='#'
|
58
|
+
@@rid_store[rid]
|
28
59
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
60
|
+
|
61
|
+
def self.reset_rid_store
|
62
|
+
@@rid_store = Hash.new
|
42
63
|
end
|
43
64
|
|
65
|
+
=begin
|
66
|
+
Stores the obj in the cache.
|
44
67
|
|
45
|
-
|
68
|
+
If the cache-value exists, it is updated by the data provided in obj
|
69
|
+
and the cached obj is returned
|
70
|
+
|
71
|
+
=end
|
72
|
+
def self.store_rid obj
|
73
|
+
|
74
|
+
@@mutex.synchronize do
|
75
|
+
if obj.rid.present? && obj.rid.rid?
|
76
|
+
if @@rid_store[obj.rid].present?
|
77
|
+
@@rid_store[obj.rid].transfer_content from: obj
|
78
|
+
else
|
79
|
+
@@rid_store[obj.rid] = obj
|
80
|
+
end
|
81
|
+
@@rid_store[obj.rid]
|
82
|
+
else
|
83
|
+
obj
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# rails compatibility
|
90
|
+
# remap rid to id unless id is present
|
91
|
+
# def id
|
92
|
+
# attributes[:id].present? ? attributes[:id] : rrid
|
93
|
+
# end
|
46
94
|
|
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
95
|
|
106
|
-
|
96
|
+
=begin
|
97
|
+
If a opts hash is given, keys are taken as attribute names, values as data.
|
98
|
+
The model instance fields are then set automatically from the opts Hash.
|
99
|
+
=end
|
100
|
+
|
101
|
+
def initialize attributes = {}, opts = {}
|
102
|
+
logger.progname = "ActiveOrient::Base#initialize"
|
103
|
+
@metadata = Hash.new # HashWithIndifferentAccess.new
|
104
|
+
@d = nil if RUBY_PLATFORM == 'java' && attributes.is_a?( Document )
|
105
|
+
run_callbacks :initialize do
|
106
|
+
if RUBY_PLATFORM == 'java' && attributes.is_a?( Document )
|
107
|
+
@d = attributes
|
108
|
+
attributes = @d.values
|
109
|
+
@metadata[:class] = @d.class_name
|
110
|
+
@metadata[:version] = @d.version
|
111
|
+
@metadata[:cluster], @metadata[:record] = @d.rid[1,@d.rid.size].split(':')
|
112
|
+
puts "Metadata: #{@metadata}"
|
113
|
+
end
|
114
|
+
|
115
|
+
# transform $current to :current and $current.mgr to :mgr
|
116
|
+
transformers = attributes.keys.map{|x| [x, x[1..-1].split(".").last.to_sym] if x[0] == '$'}.compact
|
117
|
+
# transformers: [ [original key, modified key] , [] ]
|
118
|
+
transformers.each do |a|
|
119
|
+
attributes[a.last] = attributes[a.first]
|
120
|
+
attributes.delete a.first
|
121
|
+
end
|
122
|
+
|
123
|
+
attributes.keys.each do |att|
|
124
|
+
unless att[0] == "@" # @ identifies Metadata-attributes
|
125
|
+
unless self.class.instance_methods.detect{|x| x == att.to_sym}
|
126
|
+
self.class.define_property att.to_sym, nil
|
127
|
+
else
|
128
|
+
#logger.info{"Property #{att.to_s} NOT assigned"}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
if attributes['@type'] == 'd' # document via REST
|
133
|
+
@metadata[:type] = attributes.delete '@type'
|
134
|
+
@metadata[:class] = attributes.delete '@class'
|
135
|
+
@metadata[:version] = attributes.delete '@version'
|
136
|
+
@metadata[:fieldTypes] = attributes.delete '@fieldTypes'
|
137
|
+
|
138
|
+
if attributes.has_key?('@rid')
|
139
|
+
rid = attributes.delete '@rid'
|
140
|
+
cluster, record = rid[1 .. -1].split(':')
|
141
|
+
@metadata[:cluster] = cluster.to_i
|
142
|
+
@metadata[:record] = record.to_i
|
143
|
+
end
|
144
|
+
|
145
|
+
if @metadata[:fieldTypes ].present? && (@metadata[:fieldTypes] =~ /=g/)
|
146
|
+
@metadata[:edges] = { :in => [], :out => [] }
|
147
|
+
edges = @metadata[:fieldTypes].split(',').find_all{|x| x=~/=g/}.map{|x| x.split('=').first}
|
148
|
+
# puts "Detected EDGES: #{edges.inspect}"
|
149
|
+
edges.each do |edge|
|
150
|
+
operator, *base_edge = edge.split('_')
|
151
|
+
base_edge = base_edge.join('_')
|
152
|
+
@metadata[:edges][operator.to_sym] << base_edge
|
153
|
+
end
|
154
|
+
# unless self.class.instance_methods.detect{|x| x == base_edge}
|
155
|
+
# ## define two methods: out_{Edge}/in_{Edge} -> edge.
|
156
|
+
# self.class.define_property base_edge, nil
|
157
|
+
# allocate_edge_method = -> (edge) do
|
158
|
+
# unless (ee=db.get_db_superclass(edge)) == "E"
|
159
|
+
# allocate_edge_method[ee]
|
160
|
+
# self.class.send :alias_method, base_edge.underscore, edge
|
161
|
+
# ## define inherented classes, tooa
|
162
|
+
#
|
163
|
+
|
164
|
+
# end
|
165
|
+
# end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
self.attributes = attributes # set_attribute_defaults is now after_init callback
|
169
|
+
end
|
170
|
+
# puts "Storing #{self.rid} to rid-store"
|
171
|
+
# ActiveOrient::Base.store_rid( self ) do | cache_obj|
|
172
|
+
# cache_obj.reload! self
|
173
|
+
# end
|
174
|
+
end
|
175
|
+
|
176
|
+
# ActiveModel API (for serialization)
|
177
|
+
|
178
|
+
def included_links
|
179
|
+
meta= Hash[ @metadata[:fieldTypes].split(',').map{|x| x.split '='} ]
|
180
|
+
meta.map{|x,y| x if y=='x'}.compact
|
181
|
+
end
|
107
182
|
|
108
183
|
def attributes
|
109
|
-
@attributes ||=
|
184
|
+
@attributes ||= Hash.new # WithIndifferentAccess.new
|
110
185
|
end
|
111
186
|
|
112
187
|
def attributes= attrs
|
113
|
-
attrs.keys.each
|
188
|
+
attrs.keys.each{|key| self.send("#{key}=", attrs[key])}
|
114
189
|
end
|
115
190
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
else
|
129
|
-
|
130
|
-
iv
|
131
|
-
end
|
132
|
-
end
|
191
|
+
def my_metadata key: nil, symbol: nil
|
192
|
+
if @metadata[:fieldTypes].present?
|
193
|
+
meta= Hash[ @metadata[:fieldTypes].split(',').map{|x| x.split '='} ]
|
194
|
+
if key.present?
|
195
|
+
meta[key.to_s]
|
196
|
+
elsif symbol.present?
|
197
|
+
meta.map{|x,y| x if y == symbol.to_s }.compact
|
198
|
+
else
|
199
|
+
meta
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
133
203
|
|
134
|
-
def update_attribute key, value
|
135
|
-
@attributes[key] = value
|
136
|
-
end
|
137
204
|
=begin
|
138
|
-
|
139
|
-
|
205
|
+
ActiveModel-style read/write_attribute accessors
|
206
|
+
|
207
|
+
Autoload mechanism and data conversion are defined in the method "from_orient" of each class
|
140
208
|
=end
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
209
|
+
|
210
|
+
def [] key
|
211
|
+
|
212
|
+
iv = attributes[key]
|
213
|
+
if my_metadata( key: key) == "t"
|
214
|
+
# needed in case of
|
215
|
+
# obj.date = {some-date}
|
216
|
+
# --> perfrom an action on the date without saving prior
|
217
|
+
case iv
|
218
|
+
when String
|
219
|
+
iv =~ /00:00:00/ ? Date.parse(iv) : DateTime.parse(iv)
|
220
|
+
when Date, DateTime
|
221
|
+
iv
|
222
|
+
else
|
223
|
+
raise "incompatable type used: #{iv} (#{iv.class}) -- Date or DateTime required"
|
224
|
+
end
|
225
|
+
elsif my_metadata( key: key) == "x"
|
226
|
+
iv = ActiveOrient::Model.autoload_object iv
|
227
|
+
elsif iv.is_a? Array
|
228
|
+
OrientSupport::Array.new( work_on: self, work_with: iv.from_orient){ key.to_sym }
|
229
|
+
elsif iv.is_a? Hash
|
230
|
+
# if iv.keys.include?("@class" )
|
231
|
+
# ActiveOrient::Model.orientdb_class( name: iv["@class"] ).new iv
|
232
|
+
# else
|
233
|
+
# iv
|
234
|
+
OrientSupport::Hash.new( self, iv.from_orient){ key.to_sym }
|
235
|
+
# end
|
236
|
+
# elsif iv.is_a? RecordMap
|
237
|
+
# iv
|
238
|
+
# puts "RecordSet detected"
|
239
|
+
else
|
240
|
+
iv.from_orient
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def []= key, val
|
245
|
+
val = val.rid if val.is_a?( ActiveOrient::Model ) && val.rid.rid?
|
246
|
+
attributes[key.to_sym] = case val
|
247
|
+
when Array
|
248
|
+
if val.first.is_a?(Hash)
|
249
|
+
v = val.map{ |x| x }
|
250
|
+
OrientSupport::Array.new(work_on: self, work_with: v ){ key_to_sym }
|
251
|
+
else
|
252
|
+
OrientSupport::Array.new(work_on: self, work_with: val ){ key_to_sym }
|
253
|
+
end
|
254
|
+
when Hash
|
255
|
+
if val.keys.include?("@class" )
|
256
|
+
OrientSupport::Array.new( work_on: self, work_with: val.from_orient){ key.to_sym }
|
257
|
+
else
|
258
|
+
OrientSupport::Hash.new( self, val )
|
259
|
+
end
|
260
|
+
else
|
261
|
+
val
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def update_attribute key, value
|
266
|
+
@attributes[key] = value
|
171
267
|
end
|
172
268
|
|
173
|
-
def to_model
|
269
|
+
def to_model # :nodoc:
|
174
270
|
self
|
175
271
|
end
|
176
272
|
|
273
|
+
# Noop methods mocking ActiveRecord::Base macros
|
177
274
|
|
178
|
-
|
179
|
-
### Noop methods mocking ActiveRecord::Base macros
|
180
|
-
|
181
275
|
def self.attr_protected *args
|
182
276
|
end
|
183
277
|
|
184
278
|
def self.attr_accessible *args
|
185
279
|
end
|
186
280
|
|
187
|
-
|
188
|
-
|
189
|
-
def self.belongs_to model, *args
|
281
|
+
# ActiveRecord::Base association API mocks
|
282
|
+
#
|
283
|
+
def self.belongs_to model, *args # :nodoc:
|
190
284
|
attr_accessor model
|
191
285
|
end
|
192
|
-
|
193
|
-
def self.has_one model, *args
|
286
|
+
#
|
287
|
+
def self.has_one model, *args # :nodoc:
|
194
288
|
attr_accessor model
|
195
289
|
end
|
196
|
-
|
197
|
-
def self.has_many models, *args
|
290
|
+
#
|
291
|
+
def self.has_many models, *args # :nodoc:
|
198
292
|
attr_accessor models
|
199
|
-
|
200
293
|
define_method(models) do
|
201
|
-
self.instance_variable_get("@#{models}") ||
|
202
|
-
self.instance_variable_set("@#{models}", [])
|
294
|
+
self.instance_variable_get("@#{models}") || self.instance_variable_set("@#{models}", [])
|
203
295
|
end
|
204
296
|
end
|
205
|
-
|
206
|
-
def self.find *args
|
207
|
-
[]
|
297
|
+
#
|
298
|
+
# def self.find *args
|
299
|
+
# []
|
300
|
+
# end
|
301
|
+
#
|
302
|
+
=begin
|
303
|
+
(Experimental)
|
304
|
+
Exclude some properties from loading via get, reload!, get_document, get_record
|
305
|
+
=end
|
306
|
+
def self.exclude_the_following_properties *args
|
307
|
+
puts "excluding #{args}"
|
308
|
+
@excluded = (@excluded.is_a?( Array))? @excluded + args : args
|
309
|
+
puts "#{self.inspect} --> excluded #{@excluded}"
|
208
310
|
end
|
209
311
|
|
210
|
-
|
211
|
-
|
212
|
-
define_model_callbacks :initialize, :only => :after
|
213
|
-
|
214
|
-
### ActiveRecord::Base misc
|
215
|
-
|
216
|
-
def self.serialize *properties
|
312
|
+
# ActiveRecord::Base misc
|
313
|
+
def self.serialize *properties # :nodoc:
|
217
314
|
end
|
218
|
-
|
315
|
+
# Enable lazy loading
|
316
|
+
ActiveSupport.run_load_hooks(:active_orient, self)
|
219
317
|
end # Model
|
220
|
-
end # module
|
318
|
+
end # module
|