neo4j 2.0.0.alpha.5-java → 2.0.0.alpha.6-java
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -0
- data/Gemfile +0 -4
- data/README.rdoc +106 -62
- data/lib/neo4j.rb +7 -33
- data/lib/neo4j/performance.rb +43 -0
- data/lib/neo4j/rails/accept_id.rb +19 -18
- data/lib/neo4j/rails/attributes.rb +366 -120
- data/lib/neo4j/rails/finders.rb +41 -15
- data/lib/neo4j/rails/has_n.rb +203 -0
- data/lib/neo4j/rails/identity.rb +25 -0
- data/lib/neo4j/rails/model.rb +65 -242
- data/lib/neo4j/rails/nested_attributes.rb +108 -0
- data/lib/neo4j/rails/node_persistance.rb +56 -0
- data/lib/neo4j/rails/observer.rb +0 -2
- data/lib/neo4j/rails/persistence.rb +32 -154
- data/lib/neo4j/rails/rack_middleware.rb +26 -2
- data/lib/neo4j/rails/rails.rb +9 -6
- data/lib/neo4j/rails/railtie.rb +1 -2
- data/lib/neo4j/rails/relationship.rb +18 -125
- data/lib/neo4j/rails/relationship_persistence.rb +107 -0
- data/lib/neo4j/rails/relationships/node_dsl.rb +72 -44
- data/lib/neo4j/rails/relationships/relationships.rb +187 -59
- data/lib/neo4j/rails/relationships/rels_dsl.rb +18 -17
- data/lib/neo4j/rails/relationships/storage.rb +19 -17
- data/lib/neo4j/rails/timestamps.rb +53 -51
- data/lib/neo4j/rails/transaction.rb +9 -1
- data/lib/neo4j/rails/validations/uniqueness.rb +1 -1
- data/lib/neo4j/rails/versioning/versioning.rb +2 -2
- data/lib/neo4j/version.rb +1 -1
- data/lib/orm_adapter/adapters/neo4j.rb +47 -46
- data/neo4j.gemspec +1 -1
- metadata +10 -69
- data/lib/neo4j/algo/algo.rb +0 -294
- data/lib/neo4j/batch/batch.rb +0 -4
- data/lib/neo4j/batch/indexer.rb +0 -109
- data/lib/neo4j/batch/inserter.rb +0 -179
- data/lib/neo4j/batch/rule_inserter.rb +0 -24
- data/lib/neo4j/batch/rule_node.rb +0 -72
- data/lib/neo4j/config.rb +0 -177
- data/lib/neo4j/core_ext/class/inheritable_attributes.rb +0 -12
- data/lib/neo4j/core_ext/class/rewrite_inheritable_attributes.rb +0 -170
- data/lib/neo4j/database.rb +0 -158
- data/lib/neo4j/equal.rb +0 -21
- data/lib/neo4j/event_handler.rb +0 -263
- data/lib/neo4j/has_list/class_methods.rb +0 -11
- data/lib/neo4j/has_list/has_list.rb +0 -3
- data/lib/neo4j/has_list/mapping.rb +0 -133
- data/lib/neo4j/has_n/class_methods.rb +0 -119
- data/lib/neo4j/has_n/decl_relationship_dsl.rb +0 -246
- data/lib/neo4j/has_n/has_n.rb +0 -3
- data/lib/neo4j/has_n/mapping.rb +0 -98
- data/lib/neo4j/identity_map.rb +0 -140
- data/lib/neo4j/index/class_methods.rb +0 -108
- data/lib/neo4j/index/index.rb +0 -39
- data/lib/neo4j/index/indexer.rb +0 -341
- data/lib/neo4j/index/indexer_registry.rb +0 -68
- data/lib/neo4j/index/lucene_query.rb +0 -256
- data/lib/neo4j/load.rb +0 -25
- data/lib/neo4j/migrations/class_methods.rb +0 -110
- data/lib/neo4j/migrations/extensions.rb +0 -58
- data/lib/neo4j/migrations/lazy_node_mixin.rb +0 -41
- data/lib/neo4j/migrations/migration.rb +0 -112
- data/lib/neo4j/migrations/migrations.rb +0 -6
- data/lib/neo4j/migrations/node_mixin.rb +0 -80
- data/lib/neo4j/migrations/ref_node_wrapper.rb +0 -32
- data/lib/neo4j/model.rb +0 -4
- data/lib/neo4j/neo4j.rb +0 -216
- data/lib/neo4j/node.rb +0 -270
- data/lib/neo4j/node_mixin/class_methods.rb +0 -51
- data/lib/neo4j/node_mixin/node_mixin.rb +0 -141
- data/lib/neo4j/paginated.rb +0 -23
- data/lib/neo4j/property/class_methods.rb +0 -79
- data/lib/neo4j/property/property.rb +0 -111
- data/lib/neo4j/rails/mapping/property.rb +0 -183
- data/lib/neo4j/rails/rel_persistence.rb +0 -237
- data/lib/neo4j/relationship.rb +0 -239
- data/lib/neo4j/relationship_mixin/class_methods.rb +0 -36
- data/lib/neo4j/relationship_mixin/relationship_mixin.rb +0 -142
- data/lib/neo4j/relationship_set.rb +0 -58
- data/lib/neo4j/rels/rels.rb +0 -110
- data/lib/neo4j/rels/traverser.rb +0 -102
- data/lib/neo4j/rule/class_methods.rb +0 -201
- data/lib/neo4j/rule/event_listener.rb +0 -66
- data/lib/neo4j/rule/functions/count.rb +0 -43
- data/lib/neo4j/rule/functions/function.rb +0 -74
- data/lib/neo4j/rule/functions/functions.rb +0 -3
- data/lib/neo4j/rule/functions/sum.rb +0 -29
- data/lib/neo4j/rule/rule.rb +0 -150
- data/lib/neo4j/rule/rule_node.rb +0 -217
- data/lib/neo4j/to_java.rb +0 -31
- data/lib/neo4j/transaction.rb +0 -73
- data/lib/neo4j/traversal/filter_predicate.rb +0 -25
- data/lib/neo4j/traversal/prune_evaluator.rb +0 -14
- data/lib/neo4j/traversal/rel_expander.rb +0 -31
- data/lib/neo4j/traversal/traversal.rb +0 -141
- data/lib/neo4j/traversal/traverser.rb +0 -284
- data/lib/neo4j/type_converters/type_converters.rb +0 -288
data/CHANGELOG
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
== 2.0.0.alpha.6 / 2012-04-15
|
2
|
+
* Complete rewrite and smaller change of API + lots of refactoring and better RSpecs
|
3
|
+
* Moved code to the neo4j-core and neo4j-wrapper gems
|
4
|
+
* Changed API - index properties using the Neo4j::Rails::Model (property :name, :index => :exact)
|
5
|
+
* Changed API - rel_type always returns a Symbol
|
6
|
+
* Changed API - #rels and #rel first parameter is always :outgoing, :incoming or :both
|
7
|
+
* Cypher DSL support, see neo4j-core
|
8
|
+
* Made the Lucene indexing more flexible
|
9
|
+
* Renamed size methods to count since it does simply count all the relationships (e.g. Person.all.count)
|
10
|
+
* Modularization - e.g. make it possible to create your own wrapper
|
11
|
+
* Added builder method for has_one relationships (just like ActiveRecord build_best_friend)
|
12
|
+
|
1
13
|
== 2.0.0.alpha.5 / 2012-03-27
|
2
14
|
* Fix for HA/cluster bug [#173]
|
3
15
|
* Upgrade to neo4j-community jars 1.7.0.alpha.1
|
data/Gemfile
CHANGED
@@ -2,9 +2,6 @@ source :gemcutter
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
gem 'neo4j-advanced', "1.7.0.alpha.1", :require => false
|
6
|
-
gem 'neo4j-enterprise', "1.7.0.alpha.1", :require => false
|
7
|
-
|
8
5
|
group 'development' do
|
9
6
|
gem 'guard'
|
10
7
|
gem 'ruby_gntp', :require => false # GrowlNotify for Mac
|
@@ -12,7 +9,6 @@ group 'development' do
|
|
12
9
|
gem 'rb-fsevent', :require => false
|
13
10
|
gem 'rb-fchange', :require => false
|
14
11
|
gem "guard-rspec"
|
15
|
-
gem "horo", ">= 1.0.2" # TODO: Why horo, YARD seems to be much better option?
|
16
12
|
#gem 'ruby-debug-base19' if RUBY_VERSION.include? "1.9"
|
17
13
|
#gem 'ruby-debug-base' if RUBY_VERSION.include? "1.8"
|
18
14
|
#gem "ruby-debug-ide"
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
= Welcome to Neo4j.rb {<img src="https://secure.travis-ci.org/andreasronge/neo4j.png" />}[http://travis-ci.org/andreasronge/neo4j] {<img src="https://gemnasium.com/andreasronge/neo4j.png" />}[https://gemnasium.com/andreasronge/neo4j]
|
2
2
|
|
3
3
|
Neo4j.rb is a graph database for JRuby.
|
4
4
|
|
@@ -9,25 +9,28 @@ It uses two powerful and mature Java libraries:
|
|
9
9
|
* {Neo4J}[http://www.neo4j.org] - for persistence and traversal of the graph
|
10
10
|
* {Lucene}[http://lucene.apache.org/java/docs/index.html] for querying and indexing.
|
11
11
|
|
12
|
-
|
12
|
+
== Documentation
|
13
|
+
|
14
|
+
==== Version < 2.0.alpha.5
|
13
15
|
|
14
16
|
* {Guides and API}[http://neo4j.rubyforge.org/guides/index.html]
|
15
17
|
* {RDoc}[http://neo4j.rubyforge.org]
|
16
18
|
* {Blog: Neo4j and Rails 3}[http://blog.jayway.com/2011/03/02/neo4j-rb-1-0-0-and-rails-3/]
|
17
19
|
* {Kvitter Example Application}[https://github.com/andreasronge/kvitter/]
|
18
20
|
|
19
|
-
|
21
|
+
==== Version >= 2.0
|
20
22
|
|
21
|
-
|
23
|
+
* {YARD}[http://rdoc.info/github/andreasronge/neo4j/master/frames]
|
24
|
+
* RSpecs - There are 2023 RSpecs (478/neo4j-core, 425/neo4j-wrapper and 1120/this gem - 2012 April)
|
22
25
|
|
23
|
-
|
24
|
-
* More modular (create your own wrapper or use Neo4j::NodeMixin/Neo4j::Rails::Model)
|
25
|
-
* Better performance
|
26
|
-
* More consistent API
|
27
|
-
* Better quality of RSpecs, code and docs (YARD)
|
26
|
+
=== Presentation Materials and other URLs
|
28
27
|
|
28
|
+
* {JRubyConf 2012 May}[http://jrubyconf.com/#speakers]
|
29
|
+
* {Presentation: RailsConf 2011}[http://andreasronge.github.com/neo4j-railsconf.pdf]
|
30
|
+
* {Nordic Ruby 2010 May 21-23}[http://nordicruby.org/speakers#user_29]
|
31
|
+
* {Docs from NeoTechnology}[http://docs.neo4j.org/]
|
29
32
|
|
30
|
-
|
33
|
+
== Why Neo4j.rb or a Graph Database ?
|
31
34
|
|
32
35
|
Here are some of the major benefits of Neo4j.rb
|
33
36
|
|
@@ -43,64 +46,50 @@ Here are some of the major benefits of Neo4j.rb
|
|
43
46
|
* ACID Transaction with rollbacks support.
|
44
47
|
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
==== Neo4j::Node
|
49
|
+
== Public API
|
49
50
|
|
50
|
-
|
51
|
+
The neo4j gem depends on the neo4j-wrapper and neo4j-core gems.
|
51
52
|
|
52
|
-
|
53
|
-
require 'neo4j'
|
53
|
+
=== neo4j gem
|
54
54
|
|
55
|
-
|
56
|
-
node = Neo4j::Node.new (:name => 'andreas')
|
57
|
-
node.outgoing(:friends) << Neo4j::Node.new (:name => 'peter')
|
58
|
-
node.outgoing(:friends).each {|node| puts "name #{node[:name]}"}
|
59
|
-
end
|
55
|
+
{Neo4j::Rails::Model}
|
60
56
|
|
61
|
-
|
57
|
+
{Neo4j::Rails::Relationship}
|
62
58
|
|
63
|
-
|
59
|
+
{Neo4j::Rails::Observer}
|
64
60
|
|
65
|
-
|
66
|
-
include Neo4j::NodeMixin
|
67
|
-
property :name
|
68
|
-
property :city
|
61
|
+
{Neo4j::Railtie}
|
69
62
|
|
70
|
-
|
71
|
-
has_one :address
|
72
|
-
index :name
|
73
|
-
end
|
63
|
+
{Neo4j::Rails::Versioning}
|
74
64
|
|
75
|
-
# assume we have an transaction already running
|
76
|
-
andreas = Person.new (:name => 'andreas')
|
77
|
-
andreas.friends << Person.new (:name => 'peter')
|
78
|
-
andreas.friends.each {|person| puts "name #{person.name}" }
|
79
|
-
Person.find("name: andreas").first.name # => 'andreas'
|
80
65
|
|
81
|
-
====
|
66
|
+
==== Example
|
82
67
|
|
83
68
|
Example of using Neo4j with Rails 3 (ActiveModel)
|
84
69
|
|
85
|
-
|
86
|
-
|
87
|
-
|
70
|
+
class User < Neo4j::Rails::Model
|
71
|
+
attr_accessor :password
|
72
|
+
attr_accessible :email, :password, :password_confirmation, :pending_account
|
73
|
+
|
74
|
+
after_save :encrypt_password
|
88
75
|
|
89
|
-
|
76
|
+
email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
|
90
77
|
|
91
|
-
|
78
|
+
# add an exact lucene index on the email property
|
79
|
+
property :email, :index => :exact
|
92
80
|
|
93
|
-
|
94
|
-
index :email
|
81
|
+
has_one(:avatar).to(Avator)
|
95
82
|
|
96
|
-
|
83
|
+
validates :email, :presence => true,:format => { :with => email_regex }
|
84
|
+
validates :email, :uniqueness => true, :unless => :pending_account?
|
85
|
+
accepts_nested_attributes_for :avatar, :allow_destroy => true
|
97
86
|
|
98
|
-
|
99
|
-
validates :email, :uniqueness => true, :unless => :pending_account?
|
100
|
-
accepts_nested_attributes_for :avatar, :allow_destroy => true
|
87
|
+
end
|
101
88
|
|
102
|
-
|
103
|
-
|
89
|
+
u = User.new(:name => 'kalle', :age => 42, :email => "bla@foo.com")
|
90
|
+
u.save
|
91
|
+
|
92
|
+
Make sure you are using JRuby !
|
104
93
|
|
105
94
|
==== Generate a Rails Application
|
106
95
|
|
@@ -116,33 +105,85 @@ Example of creating an Neo4j Application from scratch:
|
|
116
105
|
open a webbrowser: http://localhost:3000/users
|
117
106
|
|
118
107
|
To run it with Tomcat instead of WEBrick
|
119
|
-
|
108
|
+
|
120
109
|
gem install trinidad
|
121
110
|
trinidad
|
122
111
|
|
123
|
-
|
112
|
+
|
113
|
+
=== {neo4j-wrapper}[http://github.com/andreasronge/neo4j-wrapper]
|
114
|
+
|
115
|
+
{Neo4j::NodeMixin}[http://rdoc.info/github/andreasronge/neo4j-wrapper/Neo4j/NodeMixin]
|
116
|
+
|
117
|
+
{Neo4j::RelationshipMixin}[http://rdoc.info/github/andreasronge/neo4j-wrapper/Neo4j/RelationshipMixin]
|
118
|
+
|
119
|
+
==== Example
|
120
|
+
|
121
|
+
Example of mapping a ruby class to a node and delaring properties and relationships and lucene index.
|
122
|
+
|
123
|
+
class Person
|
124
|
+
include Neo4j::NodeMixin
|
125
|
+
property :name
|
126
|
+
property :city
|
127
|
+
|
128
|
+
has_n :friends
|
129
|
+
has_one :address
|
130
|
+
index :name
|
131
|
+
end
|
132
|
+
|
133
|
+
# assume we have an transaction already running !
|
134
|
+
andreas = Person.new (:name => 'andreas')
|
135
|
+
andreas.friends << Person.new (:name => 'peter')
|
136
|
+
andreas.friends.each {|person| puts "name #{person.name}" }
|
137
|
+
Person.find("name: andreas").first.name # => 'andreas'
|
138
|
+
|
139
|
+
The Neo4j::NodeMixin and Neo4j::RelationshipMixin is implemented in the {neo4j-wrapper}[http://github.com/andreasronge/neo4j-wrapper] gem
|
140
|
+
|
141
|
+
|
142
|
+
=== neo4j-core gem
|
143
|
+
|
144
|
+
{Neo4j::Node}[http://rdoc.info/github/andreasronge/neo4j-core/Neo4j/Node] The Java Neo4j Node
|
145
|
+
|
146
|
+
{Neo4j::Relationship}[http://rdoc.info/github/andreasronge/neo4j-core/Neo4j/Relationship] The Java Relationship
|
147
|
+
|
148
|
+
{Neo4j}[http://rdoc.info/github/andreasronge/neo4j-core/Neo4j] The Database
|
149
|
+
|
150
|
+
{Neo4j::Cypher}[http://rdoc.info/github/andreasronge/neo4j-core/Neo4j/Cypher] Cypher DSL, see RSpec spec/neo4j/cypher_spec
|
151
|
+
|
152
|
+
{Neo4j::Algo}[http://rdoc.info/github/andreasronge/neo4j-core/Neo4j/Algo] Included algorithms, like shortest path
|
153
|
+
|
154
|
+
==== Example
|
155
|
+
|
156
|
+
Example of creating a Neo4j::Node
|
157
|
+
|
158
|
+
require "rubygems"
|
159
|
+
require 'neo4j'
|
160
|
+
|
161
|
+
Neo4j::Transaction.run do
|
162
|
+
node = Neo4j::Node.new(:name => 'andreas')
|
163
|
+
node.outgoing(:friends) << Neo4j::Node.new(:name => 'peter')
|
164
|
+
node.outgoing(:friends).each {|node| puts "name #{node[:name]}"}
|
165
|
+
end
|
166
|
+
|
167
|
+
The Neo4j::Node and Neo4j::Relationship is implemented in the {neo4j-core}[http://github.com/andreasronge/neo4j-core] gem.
|
168
|
+
|
169
|
+
== Architecture
|
124
170
|
|
125
171
|
As you seen above, neo4j.rb consists of a three layers API:
|
126
172
|
|
127
|
-
* Layer 1. For interacting with the basic building blocks of the graph database (node, properties and relationship), see Neo4j::Node and Neo4j::Relationship classes.
|
128
|
-
* Layer 2. A binding API to Ruby objects, see Neo4j::NodeMixin and Neo4j::RelationshipMixin modules.
|
129
|
-
* Layer 3. An implementation of the Rails Active Model and a subset of the Active Record API, see Neo4j::
|
173
|
+
* Layer 1, neo4j-core. For interacting with the basic building blocks of the graph database (node, properties and relationship), see Neo4j::Node and Neo4j::Relationship classes.
|
174
|
+
* Layer 2, neo4j-wrapper. A binding API to Ruby objects, see Neo4j::NodeMixin and Neo4j::RelationshipMixin modules.
|
175
|
+
* Layer 3, neo4j. An implementation of the Rails Active Model and a subset of the Active Record API, see Neo4j::RailsNode and Neo4j::RailsRelationship class.
|
130
176
|
|
131
177
|
Notice that you can always access the lower layers if you want to do some more advanced. You can even access the Java API directly.
|
132
178
|
|
133
|
-
|
134
|
-
* {Presentation: RailsConf 2011}[http://andreasronge.github.com/neo4j-railsconf.pdf]
|
135
|
-
* {Nordic Ruby 2010 May 21-23}[http://nordicruby.org/speakers#user_29]
|
136
|
-
* {Neo4j wiki, check the guidelines and domain modeling gallery pages}[http://wiki.neo4j.org/content/Main_Page]
|
137
|
-
|
138
|
-
=== Project information
|
179
|
+
== Project information
|
139
180
|
* {GitHub}[http://github.com/andreasronge/neo4j/tree/master]
|
140
181
|
* {Issue Tracking}[https://github.com/andreasronge/neo4j/issues]
|
141
182
|
* {Twitter}[http://twitter.com/ronge]
|
142
183
|
* {Mailing list, neo4jrb@googlegroups.com}[http://groups.google.com/group/neo4jrb]
|
143
184
|
* {Read only, old lighthouse issues}[http://neo4j.lighthouseapp.com]
|
144
185
|
|
145
|
-
|
186
|
+
== Configuration
|
146
187
|
|
147
188
|
{Development configuration}[http://neo4j.rubyforge.org/guides/index.html#development-and-testing-configuration] - You can configure Neo4j through the {Neo4j::Config}[http://neo4j.rubyforge.org/Neo4j/Config.html] object.
|
148
189
|
|
@@ -163,3 +204,6 @@ Do you need help - send me an email (andreas.ronge at gmail dot com).
|
|
163
204
|
* Neo4j.rb - MIT, see the LICENSE file http://github.com/andreasronge/neo4j/tree/master/LICENSE.
|
164
205
|
* Lucene - Apache, see http://lucene.apache.org/java/docs/features.html
|
165
206
|
* \Neo4j - Dual free software/commercial license, see http://neo4j.org/
|
207
|
+
|
208
|
+
Notice there are different license for the neo4j-community, neo4j-advanced and neo4j-enterprise jar gems.
|
209
|
+
Only the neo4j-community gem is by default required.
|
data/lib/neo4j.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'enumerator'
|
2
1
|
require 'forwardable'
|
3
2
|
require 'time'
|
4
3
|
require 'date'
|
@@ -6,43 +5,18 @@ require 'tmpdir'
|
|
6
5
|
|
7
6
|
# Rails
|
8
7
|
require 'rails/railtie'
|
8
|
+
require 'active_support/concern'
|
9
9
|
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
-
require '
|
11
|
-
|
12
|
-
# core extensions
|
13
|
-
require 'neo4j/core_ext/class/inheritable_attributes'
|
14
|
-
|
10
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
11
|
+
require 'active_support/core_ext/class/attribute'
|
15
12
|
|
16
|
-
|
17
|
-
include Java
|
13
|
+
require 'active_model'
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
15
|
+
require 'neo4j-core'
|
16
|
+
require 'neo4j-wrapper'
|
22
17
|
|
23
|
-
require 'neo4j-community'
|
24
|
-
require 'neo4j/version'
|
25
|
-
require 'neo4j/neo4j'
|
26
|
-
require 'neo4j/paginated'
|
27
|
-
require 'neo4j/node'
|
28
|
-
require 'neo4j/relationship'
|
29
|
-
require 'neo4j/relationship_set'
|
30
|
-
require 'neo4j/type_converters/type_converters'
|
31
|
-
require 'neo4j/index/index'
|
32
|
-
require 'neo4j/traversal/traversal'
|
33
|
-
require 'neo4j/property/property'
|
34
|
-
require 'neo4j/has_n/has_n'
|
35
|
-
require 'neo4j/node_mixin/node_mixin'
|
36
|
-
require 'neo4j/relationship_mixin/relationship_mixin'
|
37
|
-
require 'neo4j/rule/rule'
|
38
|
-
require 'neo4j/rels/rels'
|
39
18
|
require 'neo4j/rails/rails'
|
40
|
-
require 'neo4j/model'
|
41
|
-
require 'neo4j/migrations/migrations'
|
42
|
-
require 'neo4j/algo/algo'
|
43
|
-
require 'neo4j/batch/batch'
|
44
|
-
require 'orm_adapter/adapters/neo4j'
|
45
|
-
require 'neo4j/identity_map'
|
46
19
|
|
47
20
|
|
21
|
+
require 'orm_adapter/adapters/neo4j'
|
48
22
|
Dir["#{File.dirname(__FILE__)}/tasks/**/*.rake"].each { |ext| load ext } if defined?(Rake) && respond_to?(:namespace)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'neo4j'
|
3
|
+
|
4
|
+
class MyIndex
|
5
|
+
include Neo4j::NodeMixin
|
6
|
+
index(:name) # default :exact
|
7
|
+
index(:things)
|
8
|
+
index(:age, :field_type => Fixnum) # default :exact
|
9
|
+
index(:wheels, :field_type => Fixnum)
|
10
|
+
index(:description, :type => :fulltext)
|
11
|
+
end
|
12
|
+
|
13
|
+
def rm_db_storage
|
14
|
+
FileUtils.rm_rf Neo4j::Config[:storage_path]
|
15
|
+
raise "Can't delete db" if File.exist?(Neo4j::Config[:storage_path])
|
16
|
+
end
|
17
|
+
|
18
|
+
def finish_tx
|
19
|
+
return unless @tx
|
20
|
+
@tx.success
|
21
|
+
@tx.finish
|
22
|
+
@tx = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def new_tx
|
26
|
+
finish_tx if @tx
|
27
|
+
@tx = Neo4j::Transaction.new
|
28
|
+
end
|
29
|
+
|
30
|
+
rm_db_storage
|
31
|
+
|
32
|
+
10.times do
|
33
|
+
t = Time.now
|
34
|
+
new_tx
|
35
|
+
(0..1000).each do
|
36
|
+
MyIndex.new :things => 'bla', :name => 'foo', :age => 42, :wheels => 53, :description => "Bla bla"
|
37
|
+
end
|
38
|
+
d1 = Time.now - t
|
39
|
+
t = Time.now
|
40
|
+
finish_tx
|
41
|
+
d2 = Time.now - t
|
42
|
+
puts "Index #{d2}, create #{d1} tot #{d1 + d2} - #{d2/(d1 + d2).round(2)}"
|
43
|
+
end
|
@@ -1,15 +1,16 @@
|
|
1
1
|
module Neo4j::Rails
|
2
|
-
# Allows accepting id for association objects.
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
2
|
+
# Allows accepting id for association objects.
|
3
|
+
# @example
|
4
|
+
# class Book < Neo4j::Rails::Model
|
5
|
+
# has_one(:author).to(Author)
|
6
|
+
# accepts_id_for :author
|
7
|
+
# end
|
7
8
|
#
|
8
|
-
# This would add a author_id getter and setter on Book. You could use
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# TODO: Support for has_n associations
|
9
|
+
# # This would add a author_id getter and setter on Book. You could use
|
10
|
+
# book = Book.new(:name => 'Graph DBs', :author_id => 11)
|
11
|
+
# book.author_id # 11
|
12
|
+
# book.author_id = 13
|
13
|
+
# @note TODO: Support for has_n associations
|
13
14
|
module AcceptId
|
14
15
|
extend ActiveSupport::Concern
|
15
16
|
|
@@ -17,11 +18,11 @@ module Neo4j::Rails
|
|
17
18
|
# Adds association_id getter and setter for one or more has_one associations
|
18
19
|
#
|
19
20
|
# @example
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
21
|
+
# class Book < Neo4j::Rails::Model
|
22
|
+
# has_one(:author).to(Author)
|
23
|
+
# has_one(:publisher).to(Publisher)
|
24
|
+
# accepts_id_for :author, :publisher
|
25
|
+
# end
|
25
26
|
def accepts_id_for(*association_names)
|
26
27
|
association_names.each do |association_name|
|
27
28
|
define_association_id_getter(association_name)
|
@@ -32,8 +33,8 @@ module Neo4j::Rails
|
|
32
33
|
|
33
34
|
# Check if model accepsts id for its association
|
34
35
|
# @example
|
35
|
-
#
|
36
|
-
#
|
36
|
+
# Book.accepts_id_for?(:author) => true
|
37
|
+
# Book.accepts_id_for?(:genre) => false
|
37
38
|
def accepts_id_for?(association_name)
|
38
39
|
accepts_id_associations.include?(association_name)
|
39
40
|
end
|
@@ -56,7 +57,7 @@ module Neo4j::Rails
|
|
56
57
|
class_eval %Q{
|
57
58
|
def #{association_name}_id=(id)
|
58
59
|
relation_target_class = self.class._decl_rels[:#{association_name}].target_class
|
59
|
-
association_class = relation_target_class <= self.class ? Neo4j::Model : relation_target_class
|
60
|
+
association_class = relation_target_class <= self.class ? Neo4j::Rails::Model : relation_target_class
|
60
61
|
self.#{association_name} = id.present? ? association_class.find(id) : nil
|
61
62
|
end
|
62
63
|
}, __FILE__, __LINE__
|
@@ -10,12 +10,16 @@ module Neo4j
|
|
10
10
|
# about attributes, we mean all the properties apart from those hidden ones.
|
11
11
|
module Attributes
|
12
12
|
extend ActiveSupport::Concern
|
13
|
+
extend TxMethods
|
13
14
|
|
14
15
|
included do
|
15
16
|
include ActiveModel::Dirty # track changes to attributes
|
16
17
|
include ActiveModel::MassAssignmentSecurity # handle attribute hash assignment
|
17
18
|
|
18
|
-
|
19
|
+
class << self
|
20
|
+
attr_accessor :attribute_defaults
|
21
|
+
end
|
22
|
+
|
19
23
|
self.attribute_defaults ||= {}
|
20
24
|
|
21
25
|
# save the original [] and []= to use as read/write to Neo4j
|
@@ -29,16 +33,184 @@ module Neo4j
|
|
29
33
|
# whenever we refer to [] or []=. use our local properties store
|
30
34
|
alias_method :[], :read_local_property
|
31
35
|
alias_method :[]=, :write_local_property
|
36
|
+
|
37
|
+
def self.inherited(sub_klass)
|
38
|
+
super
|
39
|
+
return if sub_klass.to_s[0..0] == '#' # this is really for anonymous test classes
|
40
|
+
setup_neo4j_subclass(sub_klass)
|
41
|
+
sub_klass.send(:define_method, :attribute_defaults) do
|
42
|
+
self.class.attribute_defaults
|
43
|
+
end
|
44
|
+
sub_klass.attribute_defaults = self.attribute_defaults.clone
|
45
|
+
# Hmm, could not do this from the Finders Mixin Module - should be moved
|
46
|
+
sub_klass.rule(:_all, :functions => Neo4j::Wrapper::Rule::Functions::Size.new) if sub_klass.respond_to?(:rule)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def init_on_create(*)
|
51
|
+
self._classname = self.class.to_s
|
52
|
+
write_default_attributes
|
53
|
+
write_changed_attributes
|
54
|
+
clear_changes
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize_attributes(attributes)
|
58
|
+
@_properties = {}
|
59
|
+
@_properties_before_type_cast={}
|
60
|
+
self.attributes = attributes if attributes
|
61
|
+
end
|
62
|
+
|
63
|
+
# Mass-assign attributes. Stops any protected attributes from being assigned.
|
64
|
+
def attributes=(attributes, guard_protected_attributes = true)
|
65
|
+
attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes
|
66
|
+
|
67
|
+
multi_parameter_attributes = []
|
68
|
+
attributes.each do |k, v|
|
69
|
+
if k.to_s.include?("(")
|
70
|
+
multi_parameter_attributes << [k, v]
|
71
|
+
else
|
72
|
+
respond_to?("#{k}=") ? send("#{k}=", v) : self[k] = v
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
assign_multiparameter_attributes(multi_parameter_attributes)
|
77
|
+
end
|
78
|
+
|
79
|
+
def attribute_defaults
|
80
|
+
self.class.attribute_defaults
|
81
|
+
end
|
82
|
+
|
83
|
+
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
|
84
|
+
# If saving fails because the resource is invalid then false will be returned.
|
85
|
+
def update_attributes(attributes)
|
86
|
+
self.attributes = attributes
|
87
|
+
save
|
88
|
+
end
|
89
|
+
tx_methods :update_attributes
|
90
|
+
|
91
|
+
# Same as #update_attributes, but raises an exception if saving fails.
|
92
|
+
def update_attributes!(attributes)
|
93
|
+
self.attributes = attributes
|
94
|
+
save!
|
95
|
+
end
|
96
|
+
tx_methods :update_attributes!
|
97
|
+
|
98
|
+
def reset_attributes
|
99
|
+
@_properties = {}
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
# Updates a single attribute and saves the record.
|
105
|
+
# This is especially useful for boolean flags on existing records. Also note that
|
106
|
+
#
|
107
|
+
# * Validation is skipped.
|
108
|
+
# * Callbacks are invoked.
|
109
|
+
# * Updates all the attributes that are dirty in this object.
|
110
|
+
#
|
111
|
+
def update_attribute(name, value)
|
112
|
+
respond_to?("#{name}=") ? send("#{name}=", value) : self[name] = value
|
113
|
+
save(:validate => false)
|
114
|
+
end
|
115
|
+
|
116
|
+
def hash
|
117
|
+
persisted? ? _java_entity.neo_id.hash : super
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_param
|
121
|
+
persisted? ? neo_id.to_s : nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_model
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns an Enumerable of all (primary) key attributes
|
129
|
+
# or nil if model.persisted? is false
|
130
|
+
def to_key
|
131
|
+
persisted? ? [id] : nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return the properties from the Neo4j Node, merged with those that haven't
|
135
|
+
# yet been saved
|
136
|
+
def props
|
137
|
+
ret = {}
|
138
|
+
property_names.each do |property_name|
|
139
|
+
ret[property_name] = respond_to?(property_name) ? send(property_name) : send(:[], property_name)
|
140
|
+
end
|
141
|
+
ret
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return all the attributes for this model as a hash attr => value. Doesn't
|
145
|
+
# include properties that start with <tt>_</tt>.
|
146
|
+
def attributes
|
147
|
+
ret = {}
|
148
|
+
attribute_names.each do |attribute_name|
|
149
|
+
ret[attribute_name] = self.class._decl_props[attribute_name.to_sym] ? send(attribute_name) : send(:[], attribute_name)
|
150
|
+
end
|
151
|
+
ret
|
152
|
+
end
|
153
|
+
|
154
|
+
# Known properties are either in the @_properties, the declared
|
155
|
+
# attributes or the property keys for the persisted node.
|
156
|
+
def property_names
|
157
|
+
# initialize @_properties if needed since
|
158
|
+
# we can ask property names before the object is initialized (active_support initialize callbacks, respond_to?)
|
159
|
+
@_properties ||= {}
|
160
|
+
keys = @_properties.keys + self.class._decl_props.keys.map(&:to_s)
|
161
|
+
keys += _java_entity.property_keys.to_a if persisted?
|
162
|
+
keys.flatten.uniq
|
163
|
+
end
|
164
|
+
|
165
|
+
# Known attributes are either in the @_properties, the declared
|
166
|
+
# attributes or the property keys for the persisted node. Any attributes
|
167
|
+
# that start with <tt>_</tt> are rejected
|
168
|
+
def attribute_names
|
169
|
+
property_names.reject { |property_name| _invalid_attribute_name?(property_name) }
|
170
|
+
end
|
171
|
+
|
172
|
+
# Known properties are either in the @_properties, the declared
|
173
|
+
# properties or the property keys for the persisted node
|
174
|
+
def property?(name)
|
175
|
+
return false unless @_properties
|
176
|
+
@_properties.has_key?(name) ||
|
177
|
+
self.class._decl_props.has_key?(name) ||
|
178
|
+
persisted? && super
|
179
|
+
end
|
180
|
+
|
181
|
+
def property_changed?
|
182
|
+
return !@_properties.empty? unless persisted?
|
183
|
+
!!@_properties.keys.find { |k| self._java_node[k] != @_properties[k] }
|
184
|
+
end
|
185
|
+
|
186
|
+
# Return true if method_name is the name of an appropriate attribute
|
187
|
+
# method
|
188
|
+
def attribute?(name)
|
189
|
+
name[0] != ?_ && property?(name)
|
32
190
|
end
|
33
191
|
|
192
|
+
|
193
|
+
# Wrap the getter in a conversion from Java to Ruby
|
194
|
+
def read_local_property_with_type_conversion(property)
|
195
|
+
self.class._converter(property).to_ruby(read_local_property_without_type_conversion(property))
|
196
|
+
end
|
197
|
+
|
198
|
+
# Wrap the setter in a conversion from Ruby to Java
|
199
|
+
def write_local_property_with_type_conversion(property, value)
|
200
|
+
@_properties_before_type_cast[property.to_sym]=value if self.class._decl_props.has_key? property.to_sym
|
201
|
+
conv_value = self.class._converter(property.to_sym).to_java(value)
|
202
|
+
write_local_property_without_type_conversion(property, conv_value)
|
203
|
+
end
|
204
|
+
|
205
|
+
|
34
206
|
# The behaviour of []= changes with a Rails Model, where nothing gets written
|
35
207
|
# to Neo4j until the object is saved, during which time all the validations
|
36
208
|
# and callbacks are run to ensure correctness
|
37
209
|
def write_local_property(key, value)
|
38
210
|
key_s = key.to_s
|
39
|
-
if !@
|
211
|
+
if !@_properties.has_key?(key_s) || @_properties[key_s] != value
|
40
212
|
attribute_will_change!(key_s)
|
41
|
-
@
|
213
|
+
@_properties[key_s] = value.nil? ? attribute_defaults[key_s] : value
|
42
214
|
end
|
43
215
|
value
|
44
216
|
end
|
@@ -47,29 +219,186 @@ module Neo4j
|
|
47
219
|
# the DB if we don't have one
|
48
220
|
def read_local_property(key)
|
49
221
|
key = key.to_s
|
50
|
-
if @
|
51
|
-
@
|
222
|
+
if @_properties.has_key?(key)
|
223
|
+
@_properties[key]
|
52
224
|
else
|
53
|
-
@
|
225
|
+
@_properties[key] = (persisted? && _java_entity.has_property?(key)) ? read_attribute(key) : attribute_defaults[key]
|
54
226
|
end
|
55
227
|
end
|
56
228
|
|
57
|
-
# Mass-assign attributes. Stops any protected attributes from being assigned.
|
58
|
-
def attributes=(attributes, guard_protected_attributes = true)
|
59
|
-
attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes
|
60
229
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
230
|
+
module ClassMethods
|
231
|
+
# Returns all defined properties
|
232
|
+
def columns
|
233
|
+
self._decl_props.keys
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
# Declares a property.
|
238
|
+
# It support the following hash options:
|
239
|
+
# <tt>:default</tt>,<tt>:null</tt>,<tt>:limit</tt>,<tt>:type</tt>,<tt>:index</tt>,<tt>:converter</tt>
|
240
|
+
#
|
241
|
+
# @example Set the property type,
|
242
|
+
# class Person < Neo4j::RailsModel
|
243
|
+
# property :age, :type => Time
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# @example Set the property type,
|
247
|
+
# class Person < Neo4j::RailsModel
|
248
|
+
# property :age, :default => 0
|
249
|
+
# end
|
250
|
+
# @example
|
251
|
+
# class Person < Neo4j::RailsModel
|
252
|
+
# property :age, :null => false
|
253
|
+
# end
|
254
|
+
# Property must be there
|
255
|
+
#
|
256
|
+
# @example Property has a length limit
|
257
|
+
# class Person < Neo4j::RailsModel
|
258
|
+
# property :name, :limit => 128
|
259
|
+
# end
|
260
|
+
#
|
261
|
+
# @example Index with lucene.
|
262
|
+
# class Person < Neo4j::RailsModel
|
263
|
+
# property :name, :index => :exact
|
264
|
+
# property :year, :index => :exact, :type => Fixnum # index as fixnum too
|
265
|
+
# property :description, :index => :fulltext
|
266
|
+
# end
|
267
|
+
#
|
268
|
+
# @example Using a custom converter
|
269
|
+
# module MyConverter
|
270
|
+
# def to_java(v)
|
271
|
+
# "Java:#{v}"
|
272
|
+
# end
|
273
|
+
#
|
274
|
+
# def to_ruby(v)
|
275
|
+
# "Ruby:#{v}"
|
276
|
+
# end
|
277
|
+
#
|
278
|
+
# def index_as
|
279
|
+
# String
|
280
|
+
# end
|
281
|
+
#
|
282
|
+
# extend self
|
283
|
+
# end
|
284
|
+
#
|
285
|
+
# class Person < Neo4j::RailsModel
|
286
|
+
# property :name, :converter => MyConverter
|
287
|
+
# end
|
288
|
+
#
|
289
|
+
def property(*args)
|
290
|
+
options = args.extract_options!
|
291
|
+
args.each do |property_sym|
|
292
|
+
property_setup(property_sym, options)
|
67
293
|
end
|
68
294
|
end
|
69
295
|
|
70
|
-
|
296
|
+
|
297
|
+
protected
|
298
|
+
def property_setup(property, options)
|
299
|
+
_decl_props[property] = options
|
300
|
+
handle_property_options_for(property, options)
|
301
|
+
define_property_methods_for(property, options)
|
302
|
+
define_property_before_type_cast_methods_for(property, options)
|
303
|
+
end
|
304
|
+
|
305
|
+
def handle_property_options_for(property, options)
|
306
|
+
attribute_defaults[property.to_s] = options[:default] if options.has_key?(:default)
|
307
|
+
|
308
|
+
converter = options[:converter] || Neo4j::TypeConverters.converter(_decl_props[property][:type])
|
309
|
+
_decl_props[property][:converter] = converter
|
310
|
+
|
311
|
+
if options.include?(:index)
|
312
|
+
index(property, :type => options[:index], :field_type => converter.index_as)
|
313
|
+
end
|
314
|
+
|
315
|
+
if options.has_key?(:null) && options[:null] === false
|
316
|
+
validates(property, :non_nil => true, :on => :create)
|
317
|
+
validates(property, :non_nil => true, :on => :update)
|
318
|
+
end
|
319
|
+
validates(property, :length => {:maximum => options[:limit]}) if options[:limit]
|
320
|
+
end
|
321
|
+
|
322
|
+
def define_property_methods_for(property, options)
|
323
|
+
unless method_defined?(property)
|
324
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
325
|
+
def #{property}
|
326
|
+
send(:[], "#{property}")
|
327
|
+
end
|
328
|
+
RUBY
|
329
|
+
end
|
330
|
+
|
331
|
+
unless method_defined?("#{property}=".to_sym)
|
332
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
333
|
+
def #{property}=(value)
|
334
|
+
send(:[]=, "#{property}", value)
|
335
|
+
end
|
336
|
+
RUBY
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def define_property_before_type_cast_methods_for(property, options)
|
341
|
+
property_before_type_cast = "#{property}_before_type_cast"
|
342
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
343
|
+
def #{property_before_type_cast}=(value)
|
344
|
+
@_properties_before_type_cast[:#{property}]=value
|
345
|
+
end
|
346
|
+
|
347
|
+
def #{property_before_type_cast}
|
348
|
+
@_properties_before_type_cast.has_key?(:#{property}) ? @_properties_before_type_cast[:#{property}] : self.#{property}
|
349
|
+
end
|
350
|
+
RUBY
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
protected
|
356
|
+
|
357
|
+
|
358
|
+
# Ensure any defaults are stored in the DB
|
359
|
+
def write_default_attributes
|
360
|
+
self.class.attribute_defaults.each do |attribute, value|
|
361
|
+
write_attribute(attribute, Neo4j::TypeConverters.convert(value, attribute, self.class, false)) unless changed_attributes.has_key?(attribute) || _java_node.has_property?(attribute)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
# Write attributes to the Neo4j DB only if they're altered
|
366
|
+
def write_changed_attributes
|
367
|
+
@_properties.each do |attribute, value|
|
368
|
+
write_attribute(attribute, value) if changed_attributes.has_key?(attribute)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
|
374
|
+
def attribute_missing(method_id, *args, &block)
|
375
|
+
method_name = method_id.method_name
|
376
|
+
if property?(method_name)
|
377
|
+
self[method_name]
|
378
|
+
else
|
379
|
+
super
|
380
|
+
end
|
71
381
|
end
|
72
382
|
|
383
|
+
# TODO THIS IS ONLY NEEDED IN ACTIVEMODEL < 3.2, ?
|
384
|
+
# To get ActiveModel::Dirty to work, we need to be able to call undeclared
|
385
|
+
# properties as though they have get methods
|
386
|
+
def method_missing(method_id, *args, &block)
|
387
|
+
method_name = method_id.to_s
|
388
|
+
if property?(method_name)
|
389
|
+
self[method_name]
|
390
|
+
else
|
391
|
+
super
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def _invalid_attribute_name?(attr_name)
|
396
|
+
attr_name.to_s[0] == ?_ && !self.class._decl_props.include?(attr_name.to_sym)
|
397
|
+
end
|
398
|
+
|
399
|
+
|
400
|
+
|
401
|
+
|
73
402
|
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
74
403
|
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
75
404
|
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
@@ -79,7 +408,7 @@ module Neo4j
|
|
79
408
|
# attribute will be set to nil.
|
80
409
|
def assign_multiparameter_attributes(pairs)
|
81
410
|
execute_callstack_for_multiparameter_attributes(
|
82
|
-
|
411
|
+
extract_callstack_for_multiparameter_attributes(pairs)
|
83
412
|
)
|
84
413
|
end
|
85
414
|
|
@@ -100,19 +429,21 @@ module Neo4j
|
|
100
429
|
|
101
430
|
#TODO: Consider extracting hardcoded assignments into "Binders"
|
102
431
|
value = if Neo4j::TypeConverters::TimeConverter.convert?(decl_type)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
432
|
+
instantiate_time_object(name, values)
|
433
|
+
elsif Neo4j::TypeConverters::DateConverter.convert?(decl_type)
|
434
|
+
begin
|
435
|
+
values = values_with_empty_parameters.collect do |v|
|
436
|
+
v.nil? ? 1 : v
|
437
|
+
end
|
438
|
+
Date.new(*values)
|
439
|
+
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
|
440
|
+
instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
|
441
|
+
end
|
442
|
+
elsif Neo4j::TypeConverters::DateTimeConverter.convert?(decl_type)
|
443
|
+
DateTime.new(*values)
|
444
|
+
else
|
445
|
+
raise "Unknown type #{decl_type}"
|
446
|
+
end
|
116
447
|
|
117
448
|
send(name + "=", value)
|
118
449
|
end
|
@@ -129,12 +460,12 @@ module Neo4j
|
|
129
460
|
# if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
|
130
461
|
# Time.zone.local(*values)
|
131
462
|
# else
|
132
|
-
|
463
|
+
Time.time_with_datetime_fallback(self.class.default_timezone, *values)
|
133
464
|
# end
|
134
465
|
end
|
135
466
|
|
136
467
|
def extract_callstack_for_multiparameter_attributes(pairs)
|
137
|
-
attributes = {
|
468
|
+
attributes = {}
|
138
469
|
|
139
470
|
for pair in pairs
|
140
471
|
multiparameter_name, value = pair
|
@@ -142,10 +473,10 @@ module Neo4j
|
|
142
473
|
attributes[attribute_name] = [] unless attributes.include?(attribute_name)
|
143
474
|
|
144
475
|
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
|
145
|
-
attributes[attribute_name] << [
|
476
|
+
attributes[attribute_name] << [find_parameter_position(multiparameter_name), parameter_value]
|
146
477
|
end
|
147
478
|
|
148
|
-
attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
|
479
|
+
attributes.each { |name, values| attributes[name] = values.sort_by { |v| v.first }.collect { |v| v.last } }
|
149
480
|
end
|
150
481
|
|
151
482
|
|
@@ -164,99 +495,14 @@ module Neo4j
|
|
164
495
|
@changed_attributes.clear
|
165
496
|
end
|
166
497
|
|
167
|
-
# Return the properties from the Neo4j Node, merged with those that haven't
|
168
|
-
# yet been saved
|
169
|
-
def props
|
170
|
-
ret = {}
|
171
|
-
property_names.each do |property_name|
|
172
|
-
ret[property_name] = respond_to?(property_name) ? send(property_name) : send(:[], property_name)
|
173
|
-
end
|
174
|
-
ret
|
175
|
-
end
|
176
|
-
|
177
|
-
# Return all the attributes for this model as a hash attr => value. Doesn't
|
178
|
-
# include properties that start with <tt>_</tt>.
|
179
|
-
def attributes
|
180
|
-
ret = {}
|
181
|
-
attribute_names.each do |attribute_name|
|
182
|
-
ret[attribute_name] = self._decl_props[attribute_name.to_sym] ? send(attribute_name) : send(:[], attribute_name)
|
183
|
-
end
|
184
|
-
ret
|
185
|
-
end
|
186
|
-
|
187
|
-
# Known properties are either in the @properties, the declared
|
188
|
-
# attributes or the property keys for the persisted node.
|
189
|
-
def property_names
|
190
|
-
# initialize @properties if needed since
|
191
|
-
# we can ask property names before the object is initialized (active_support initialize callbacks, respond_to?)
|
192
|
-
@properties ||= {}
|
193
|
-
keys = @properties.keys + self.class._decl_props.keys.map { |k| k.to_s }
|
194
|
-
keys += _java_entity.property_keys.to_a if persisted?
|
195
|
-
keys.flatten.uniq
|
196
|
-
end
|
197
|
-
|
198
|
-
# Known attributes are either in the @properties, the declared
|
199
|
-
# attributes or the property keys for the persisted node. Any attributes
|
200
|
-
# that start with <tt>_</tt> are rejected
|
201
|
-
def attribute_names
|
202
|
-
property_names.reject { |property_name| _invalid_attribute_name?(property_name) }
|
203
|
-
end
|
204
|
-
|
205
|
-
def _invalid_attribute_name?(attr_name)
|
206
|
-
attr_name.to_s[0] == ?_ && !self.class._decl_props.include?(attr_name.to_sym)
|
207
|
-
end
|
208
|
-
|
209
|
-
# Known properties are either in the @properties, the declared
|
210
|
-
# properties or the property keys for the persisted node
|
211
|
-
def property?(name)
|
212
|
-
return false unless @properties
|
213
|
-
@properties.has_key?(name) ||
|
214
|
-
self.class._decl_props.has_key?(name) ||
|
215
|
-
persisted? && super
|
216
|
-
end
|
217
|
-
|
218
|
-
def property_changed?
|
219
|
-
return !@properties.empty? unless persisted?
|
220
|
-
!!@properties.keys.find{|k| self._java_node.getProperty(k.to_s) != @properties[k] }
|
221
|
-
end
|
222
|
-
|
223
|
-
# Return true if method_name is the name of an appropriate attribute
|
224
|
-
# method
|
225
|
-
def attribute?(name)
|
226
|
-
name[0] != ?_ && property?(name)
|
227
|
-
end
|
228
|
-
|
229
498
|
def _classname
|
230
499
|
self.class.to_s
|
231
500
|
end
|
232
501
|
|
233
502
|
def _classname=(value)
|
234
|
-
write_local_property_without_type_conversion("_classname",value)
|
235
|
-
end
|
236
|
-
|
237
|
-
|
238
|
-
# TODO THIS IS ONLY NEEDED IN ACTIVEMODEL < 3.2, ?
|
239
|
-
# To get ActiveModel::Dirty to work, we need to be able to call undeclared
|
240
|
-
# properties as though they have get methods
|
241
|
-
def method_missing(method_id, *args, &block)
|
242
|
-
method_name = method_id.to_s
|
243
|
-
if property?(method_name)
|
244
|
-
self[method_name]
|
245
|
-
else
|
246
|
-
super
|
247
|
-
end
|
503
|
+
write_local_property_without_type_conversion("_classname", value)
|
248
504
|
end
|
249
505
|
|
250
|
-
# Wrap the getter in a conversion from Java to Ruby
|
251
|
-
def read_local_property_with_type_conversion(property)
|
252
|
-
Neo4j::TypeConverters.to_ruby(self.class, property, read_local_property_without_type_conversion(property))
|
253
|
-
end
|
254
|
-
|
255
|
-
# Wrap the setter in a conversion from Ruby to Java
|
256
|
-
def write_local_property_with_type_conversion(property, value)
|
257
|
-
@properties_before_type_cast[property.to_sym]=value if self.class._decl_props.has_key? property.to_sym
|
258
|
-
write_local_property_without_type_conversion(property, Neo4j::TypeConverters.to_java(self.class, property, value))
|
259
|
-
end
|
260
506
|
end
|
261
507
|
end
|
262
508
|
end
|