neo4j 2.0.0.alpha.5-java → 2.0.0.alpha.6-java
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.
- 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
|