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.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.graphs.txt.swp +0 -0
  4. data/Gemfile +9 -5
  5. data/Guardfile +12 -4
  6. data/README.md +70 -281
  7. data/VERSION +1 -1
  8. data/active-orient.gemspec +9 -7
  9. data/bin/active-orient-0.6.gem +0 -0
  10. data/bin/active-orient-console +97 -0
  11. data/changelog.md +60 -0
  12. data/config/boot.rb +70 -17
  13. data/config/config.yml +10 -0
  14. data/config/connect.yml +11 -6
  15. data/examples/books.rb +154 -65
  16. data/examples/streets.rb +89 -85
  17. data/graphs.txt +70 -0
  18. data/lib/active-orient.rb +78 -6
  19. data/lib/base.rb +266 -168
  20. data/lib/base_properties.rb +76 -65
  21. data/lib/class_utils.rb +187 -0
  22. data/lib/database_utils.rb +99 -0
  23. data/lib/init.rb +80 -0
  24. data/lib/java-api.rb +442 -0
  25. data/lib/jdbc.rb +211 -0
  26. data/lib/model/custom.rb +29 -0
  27. data/lib/model/e.rb +6 -0
  28. data/lib/model/edge.rb +114 -0
  29. data/lib/model/model.rb +134 -0
  30. data/lib/model/the_class.rb +657 -0
  31. data/lib/model/the_record.rb +313 -0
  32. data/lib/model/vertex.rb +371 -0
  33. data/lib/orientdb_private.rb +48 -0
  34. data/lib/other.rb +423 -0
  35. data/lib/railtie.rb +68 -0
  36. data/lib/rest/change.rb +150 -0
  37. data/lib/rest/create.rb +287 -0
  38. data/lib/rest/delete.rb +150 -0
  39. data/lib/rest/operations.rb +222 -0
  40. data/lib/rest/read.rb +189 -0
  41. data/lib/rest/rest.rb +120 -0
  42. data/lib/rest_disabled.rb +24 -0
  43. data/lib/support/conversions.rb +42 -0
  44. data/lib/support/default_formatter.rb +7 -0
  45. data/lib/support/errors.rb +41 -0
  46. data/lib/support/logging.rb +38 -0
  47. data/lib/support/orient.rb +305 -0
  48. data/lib/support/orientquery.rb +647 -0
  49. data/lib/support/query.rb +92 -0
  50. data/rails.md +154 -0
  51. data/rails/activeorient.rb +32 -0
  52. data/rails/config.yml +10 -0
  53. data/rails/connect.yml +17 -0
  54. metadata +89 -30
  55. data/lib/model.rb +0 -461
  56. data/lib/orient.rb +0 -98
  57. data/lib/query.rb +0 -88
  58. data/lib/rest.rb +0 -1036
  59. data/lib/support.rb +0 -347
  60. data/test.rb +0 -4
  61. data/usecase.md +0 -91
@@ -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('#strassen-in-deutschland_main a' ) # identified via css-inspector in browser
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 read_german_cities_and_states_fom_wikipedia
34
+ def read_german_cities_and_states_from_wikipedia
33
35
  doc =
34
- Nokogiri::HTML(open('https://en.wikipedia.org/wiki/List_of_cities_and_towns_in_Germany'))
35
- doc.xpath("//li").map do |x|
36
- if x.text.index('(').present?
37
- [ x.text[0 .. x.text.index('(')-2] , x.text[ x.text.index('(')+1 .. x.text.index(')')-1] ]
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
- class StreetExample
45
-
46
- include DataImport
47
- def initialize db, rebuild: true
48
- if rebuild
49
- ActiveOrient::OrientDB.logger.progname = "StreetsExample#Initialize"
50
- db.delete_class :state
51
- db.delete_class :city
52
- db.delete_class :street
53
- db.delete_class :connects
54
- db.create_vertex_class :state
55
- db.create_vertex_class :city
56
- db.create_vertex_class :street
57
- db.create_edge_class :connects
58
- ActiveOrient::Model::State.create_property( :name, type: :string, index: :unique )
59
- ActiveOrient::Model::City.create_properties( { name: { type: :string },
60
- state: { type: :link, :linked_class => 'State' } }
61
- ) do
62
- { citi_idx: :unique }
63
- end
64
- ActiveOrient::Model::Street.create_property :name , type: :string, index: :notunique
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
- def read_from_web
72
- ActiveOrient::OrientDB.logger.progname = "StreetsExample#ReadFromWeb"
73
- read_german_cities_and_states_fom_wikipedia.each do |city,state|
74
- state = State.update_or_create( where: { name: state }).first
75
- City.create name: city, state: state.link
76
- end
77
- ActiveOrient::OrientDB.logger.info { "#{City.count} Cities imported from Wikipedia " }
78
-
79
- cities_rids = City.all.map &:link
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
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
- ActiveOrient::OrientDB.logger.info { "#{C.count} Edges between Streets and Cities created " }
88
+ CONNECTS.create_edge :from => street_record, :to => cities
91
89
  end
92
-
93
- def display_streets_per_state
94
- State.all.each do |state|
95
- streets= Street.all.map do |street |
96
- if street.connects.in.detect{|x| x.state == state }
97
- street.name + " verbindet " + street.connects.in.map( &:name ).join('; ')
98
- end
99
- end.compact
100
- unless streets.empty?
101
- puts "..................................."
102
- puts state.name
103
- puts "..................................."
104
- puts streets.join("\n")
105
- end
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
- end
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
- ActiveOrient::OrientDB.default_server= { user: 'hctw', password: 'hc' }
117
- r = ActiveOrient::OrientDB.new database: 'StreetTest'
118
- ActiveOrient::OrientDB.logger.level = Logger::INFO
119
- s= StreetExample.new r, rebuild: true
120
-
121
- City = r.open_class :city
122
- State = r.open_class :state
123
- Street = r.open_class :street
124
- C = r.open_class :connects
125
-
126
- s.read_from_web if City.count.zero?
127
- s.display_streets_per_state
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
@@ -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
+
@@ -1,7 +1,79 @@
1
- require "support.rb"
2
- require "base.rb"
3
- require "base_properties.rb"
4
1
 
5
- require "model.rb"
6
- require "orient.rb"
7
- require "rest.rb"
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
+
@@ -1,220 +1,318 @@
1
1
  module ActiveOrient
2
- require 'active_model'
3
- #
4
- # Base class for tableless IB data Models, extends ActiveModel API
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
- ##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
23
+ # Used to read the metadata
24
+ attr_reader :metadata
19
25
 
20
- def self.display_riid
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
- 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
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
- define_model_callbacks :initialize
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
- # ActiveModel API (for serialization)
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 ||= HashWithIndifferentAccess.new
184
+ @attributes ||= Hash.new # WithIndifferentAccess.new
110
185
  end
111
186
 
112
187
  def attributes= attrs
113
- attrs.keys.each { |key| self.send("#{key}=", attrs[key]) }
188
+ attrs.keys.each{|key| self.send("#{key}=", attrs[key])}
114
189
  end
115
190
 
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
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
- Here we define how the attributes are initialized
139
- Key and val are set by the RestCliend
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
- 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
-
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
- ### ActiveRecord::Base association API mocks
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
- ### ActiveRecord::Base callback API mocks
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