neo4j-core 0.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/Gemfile +27 -0
  2. data/README.rdoc +27 -0
  3. data/config/neo4j/config.yml +102 -0
  4. data/lib/db/active_tx_log +1 -0
  5. data/lib/db/index/lucene-store.db +0 -0
  6. data/lib/db/index/lucene.log.1 +0 -0
  7. data/lib/db/index/lucene.log.active +0 -0
  8. data/lib/db/lock +0 -0
  9. data/lib/db/messages.log +530 -0
  10. data/lib/db/neostore +0 -0
  11. data/lib/db/neostore.id +0 -0
  12. data/lib/db/neostore.nodestore.db +0 -0
  13. data/lib/db/neostore.nodestore.db.id +0 -0
  14. data/lib/db/neostore.propertystore.db +0 -0
  15. data/lib/db/neostore.propertystore.db.arrays +0 -0
  16. data/lib/db/neostore.propertystore.db.arrays.id +0 -0
  17. data/lib/db/neostore.propertystore.db.id +0 -0
  18. data/lib/db/neostore.propertystore.db.index +0 -0
  19. data/lib/db/neostore.propertystore.db.index.id +0 -0
  20. data/lib/db/neostore.propertystore.db.index.keys +0 -0
  21. data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
  22. data/lib/db/neostore.propertystore.db.strings +0 -0
  23. data/lib/db/neostore.propertystore.db.strings.id +0 -0
  24. data/lib/db/neostore.relationshipstore.db +0 -0
  25. data/lib/db/neostore.relationshipstore.db.id +0 -0
  26. data/lib/db/neostore.relationshiptypestore.db +0 -0
  27. data/lib/db/neostore.relationshiptypestore.db.id +0 -0
  28. data/lib/db/neostore.relationshiptypestore.db.names +0 -0
  29. data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
  30. data/lib/db/nioneo_logical.log.2 +0 -0
  31. data/lib/db/nioneo_logical.log.active +0 -0
  32. data/lib/db/tm_tx_log.1 +0 -0
  33. data/lib/neo4j/config.rb +139 -0
  34. data/lib/neo4j/cypher.rb +156 -0
  35. data/lib/neo4j/neo4j.rb +244 -0
  36. data/lib/neo4j/neo4j.rb~ +214 -0
  37. data/lib/neo4j/node.rb +39 -0
  38. data/lib/neo4j/relationship.rb +61 -0
  39. data/lib/neo4j/transaction.rb +86 -0
  40. data/lib/neo4j/type_converters/type_converters.rb +287 -0
  41. data/lib/neo4j-core/cypher/cypher.rb +867 -0
  42. data/lib/neo4j-core/cypher/result_wrapper.rb +39 -0
  43. data/lib/neo4j-core/database.rb +191 -0
  44. data/lib/neo4j-core/equal/equal.rb +23 -0
  45. data/lib/neo4j-core/event_handler.rb +265 -0
  46. data/lib/neo4j-core/index/class_methods.rb +117 -0
  47. data/lib/neo4j-core/index/index.rb +36 -0
  48. data/lib/neo4j-core/index/index_config.rb +112 -0
  49. data/lib/neo4j-core/index/indexer.rb +243 -0
  50. data/lib/neo4j-core/index/indexer_registry.rb +55 -0
  51. data/lib/neo4j-core/index/lucene_query.rb +264 -0
  52. data/lib/neo4j-core/lazy_map.rb +21 -0
  53. data/lib/neo4j-core/node/class_methods.rb +77 -0
  54. data/lib/neo4j-core/node/node.rb +47 -0
  55. data/lib/neo4j-core/property/property.rb +94 -0
  56. data/lib/neo4j-core/relationship/class_methods.rb +80 -0
  57. data/lib/neo4j-core/relationship/relationship.rb +97 -0
  58. data/lib/neo4j-core/relationship_set.rb +61 -0
  59. data/lib/neo4j-core/rels/rels.rb +147 -0
  60. data/lib/neo4j-core/rels/traverser.rb +99 -0
  61. data/lib/neo4j-core/to_java.rb +51 -0
  62. data/lib/neo4j-core/traversal/evaluator.rb +36 -0
  63. data/lib/neo4j-core/traversal/filter_predicate.rb +30 -0
  64. data/lib/neo4j-core/traversal/prune_evaluator.rb +20 -0
  65. data/lib/neo4j-core/traversal/rel_expander.rb +35 -0
  66. data/lib/neo4j-core/traversal/traversal.rb +130 -0
  67. data/lib/neo4j-core/traversal/traverser.rb +295 -0
  68. data/lib/neo4j-core/version.rb +5 -0
  69. data/lib/neo4j-core.rb +64 -0
  70. data/neo4j-core.gemspec +31 -0
  71. metadata +145 -0
@@ -0,0 +1,214 @@
1
+ require 'neo4j-core/database'
2
+
3
+ # = Neo4j
4
+ #
5
+ # The Neo4j modules is used to interact with an Neo4j Database instance.
6
+ # You can for example start and stop an instance and list all the nodes that exist in the database.
7
+ #
8
+ # === Starting and Stopping Neo4j
9
+ # You don't normally need to start the Neo4j database since it will be automatically started when needed.
10
+ # Before the database is started you should configure where the database is stored, see Neo4j::Config.
11
+ #
12
+ module Neo4j
13
+
14
+ # The version of the Neo4j jar files
15
+ NEO_VERSION = Neo4j::Community::VERSION
16
+
17
+ class << self
18
+ # Start Neo4j using the default database.
19
+ # This is usally not required since the database will be started automatically when it is used.
20
+ # If the global variable $NEO4J_SERVER is defined then it will use that as the Java Graph DB. This can
21
+ # be used if you want to embed neo4j.rb and already got an instance of the Java Neo4j Database service.
22
+ #
23
+ # ==== Parameters
24
+ # config_file :: (optionally) if this is nil or not given use the Neo4j::Config, otherwise setup the Neo4j::Config file using the provided YAML configuration file.
25
+ # external_db :: (optionally) use this Java Neo4j instead of creating a new neo4j database service
26
+ def start(config_file=nil, external_db = $NEO4J_SERVER)
27
+ return if @db && @db.running?
28
+
29
+ Neo4j.config.default_file = config_file if config_file
30
+ if external_db
31
+ @db ||= Database.new
32
+ self.db.start_external_db(external_db)
33
+ else
34
+ db.start
35
+ end
36
+ end
37
+
38
+
39
+ # Sets the Neo4j::Database instance to use
40
+ # An Neo4j::Database instance wraps both the Neo4j Database and Lucene Database.
41
+ def db=(my_db)
42
+ @db = my_db
43
+ end
44
+
45
+ # Returns the database holding references to both the Neo4j Graph Database and the Lucene Database.
46
+ # Creates a new one if it does not exist, but does not start it.
47
+ def db
48
+ @db ||= Neo4j::Core::Database.new
49
+ end
50
+
51
+ def read_only?
52
+ @db && @db.graph && @db.graph.read_only?
53
+ end
54
+
55
+ # Returns a started db instance. Starts it's not running.
56
+ # if $NEO4J_SERVER is defined then use that Java Neo4j Database service instead of creating a new one.
57
+ def started_db
58
+ start unless db.running?
59
+ db
60
+ end
61
+
62
+ # Returns the current storage path of a running neo4j database.
63
+ # If the database is not running it returns nil.
64
+ def storage_path
65
+ return nil unless db.running?
66
+ db.storage_path
67
+ end
68
+
69
+ # Returns the Neo4j::Config class
70
+ # Same as typing; Neo4j::Config
71
+ def config
72
+ Neo4j::Config
73
+ end
74
+
75
+ # Executes a Cypher Query
76
+ # Check the neo4j http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html
77
+ # Returns an enumerable of hash values.
78
+ #
79
+ # === Usage
80
+ #
81
+ # q = Neo4j.query("START n=node({node}) RETURN n", 'node' => @node.neo_id)
82
+ # q.first['n'] #=> the @node
83
+ # q.columns.first => 'n'
84
+ #
85
+ def query(query, params = {})
86
+ engine = org.neo4j.cypher.javacompat.ExecutionEngine.new(db)
87
+ engine.execute(query, params)
88
+ end
89
+
90
+
91
+ # Returns the logger used by neo4j.
92
+ # If not specified (with Neo4j.logger=) it will use the standard Ruby logger.
93
+ # You can change standard logger threshold by configuration :logger_level.
94
+ #
95
+ # You can also specify which logger class should take care of logging with the
96
+ # :logger configuration.
97
+ #
98
+ # ==== Example
99
+ #
100
+ # Neo4j::Config[:logger] = Logger.new(STDOUT)
101
+ # Neo4j::Config[:logger_level] = Logger::ERROR
102
+ #
103
+ def logger
104
+ @logger ||= Neo4j::Config[:logger] || default_logger
105
+ end
106
+
107
+ # Sets which logger should be used.
108
+ # If this this is not called then the standard Ruby logger will be used.
109
+ def logger=(logger)
110
+ @logger = logger
111
+ end
112
+
113
+ def default_logger #:nodoc:
114
+ require 'logger'
115
+ logger = Logger.new(STDOUT)
116
+ logger.sev_threshold = Neo4j::Config[:logger_level] || Logger::INFO
117
+ logger
118
+ end
119
+
120
+
121
+ # Returns an unstarted db instance
122
+ #
123
+ # This is typically used for configuring the database, which must sometimes
124
+ # be done before the database is started
125
+ # if the database was already started an exception will be raised
126
+ def unstarted_db
127
+ @db ||= Database.new
128
+ raise "database was already started" if @db.running?
129
+ @db
130
+ end
131
+
132
+ # returns true if the database is running
133
+ def running?
134
+ @db && @db.running?
135
+ end
136
+
137
+
138
+ # Stops this database
139
+ # There are Ruby hooks that will do this automatically for you.
140
+ #
141
+ def shutdown(this_db = @db)
142
+ this_db.shutdown if this_db
143
+ end
144
+
145
+
146
+ # Returns the default reference node, which is a "starting point" in the node space.
147
+ #
148
+ def default_ref_node(this_db = self.started_db)
149
+ this_db.graph.reference_node
150
+ end
151
+
152
+ # Returns the reference node, which is a "starting point" in the node space.
153
+ # In case the ref_node has been assigned via the threadlocal_ref_node method, then that node will be returned instead.
154
+ #
155
+ # Usually, a client attaches relationships to this node that leads into various parts of the node space.
156
+ # For more information about common node space organizational patterns, see the design guide at http://wiki.neo4j.org/content/Design_Guide
157
+ #
158
+ def ref_node(this_db = self.started_db)
159
+ return Thread.current[:local_ref_node] if Thread.current[:local_ref_node]
160
+ default_ref_node(this_db)
161
+ end
162
+
163
+ # Changes the reference node on a threadlocal basis.
164
+ # This can be used to achieve multitenancy. All new entities will be attached to the new ref_node,
165
+ # which effectively partitions the graph, and hence scopes traversals.
166
+ def threadlocal_ref_node=(reference_node)
167
+ Thread.current[:local_ref_node] = reference_node.nil? ? nil : reference_node._java_node
168
+ end
169
+
170
+ # Returns a Management JMX Bean.
171
+ #
172
+ # Notice that this information is also provided by the jconsole Java tool, check http://wiki.neo4j.org/content/Monitoring_and_Deployment
173
+ # and http://docs.neo4j.org/chunked/milestone/operations-monitoring.html
174
+ #
175
+ # By default it returns the Primitivies JMX Bean that can be used to find number of nodes in use.
176
+ #
177
+ # ==== Example Neo4j Primititives
178
+ #
179
+ # Neo4j.management.get_number_of_node_ids_in_use
180
+ # Neo4j.management.getNumberOfPropertyIdsInUse
181
+ # Neo4j.management.getNumberOfRelationshipIdsInUse
182
+ # Neo4j.management.get_number_of_relationship_type_ids_in_use
183
+ #
184
+ # ==== Example Neo4j HA Cluster Info
185
+ #
186
+ # Neo4j.management(org.neo4j.management.HighAvailability).isMaster
187
+ #
188
+ # ==== Arguments
189
+ #
190
+ # jmx_clazz :: http://api.neo4j.org/current/org/neo4j/management/package-summary.html
191
+ # this_db :: default currently runnig instance or a newly started neo4j db instance
192
+ #
193
+ def management(jmx_clazz = org.neo4j.jmx.Primitives, this_db = self.started_db)
194
+ this_db.management(jmx_clazz)
195
+ end
196
+
197
+ # Returns an Enumerable object for all nodes in the database
198
+ def all_nodes(this_db = self.started_db)
199
+ Enumerator.new(this_db, :each_node)
200
+ end
201
+
202
+ # Same as #all_nodes but does not return wrapped nodes but instead raw java node objects.
203
+ def _all_nodes(this_db = self.started_db)
204
+ Enumerator.new(this_db, :_each_node)
205
+ end
206
+
207
+ # Returns the Neo4j::EventHandler
208
+ #
209
+ def event_handler(this_db = db)
210
+ this_db.event_handler
211
+ end
212
+
213
+ end
214
+ end
data/lib/neo4j/node.rb ADDED
@@ -0,0 +1,39 @@
1
+ module Neo4j
2
+ # A node in the graph with properties and relationships to other entities.
3
+ # Along with relationships, nodes are the core building blocks of the Neo4j data representation model.
4
+ # Node has three major groups of operations: operations that deal with relationships, operations that deal with properties and operations that traverse the node space.
5
+ # The property operations give access to the key-value property pairs.
6
+ # Property keys are always strings. Valid property value types are the primitives (<tt>String</tt>, <tt>Fixnum</tt>, <tt>Float</tt>, <tt>Boolean</tt>), and arrays of those primitives.
7
+ #
8
+ # The Neo4j::Node#new method does not return a new Ruby instance (!). Instead it will call the Neo4j Java API which will return a
9
+ # *org.neo4j.kernel.impl.core.NodeProxy* object. This java object includes the same mixin as this class. The #class method on the java object
10
+ # returns Neo4j::Node in order to make it feel like an ordinary Ruby object.
11
+ #
12
+ class Node
13
+ extend Neo4j::Core::Node::ClassMethods
14
+
15
+ include Neo4j::Core::Property
16
+ include Neo4j::Core::Rels
17
+ include Neo4j::Core::Traversal
18
+ include Neo4j::Core::Equal
19
+ include Neo4j::Core::Node
20
+
21
+ class << self
22
+
23
+
24
+ # This method is used to extend a Java Neo4j class so that it includes the same mixins as this class.
25
+ def extend_java_class(java_clazz)
26
+ java_clazz.class_eval do
27
+ include Neo4j::Core::Property
28
+ include Neo4j::Core::Rels
29
+ include Neo4j::Core::Traversal
30
+ include Neo4j::Core::Equal
31
+ include Neo4j::Core::Node
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ Neo4j::Node.extend_java_class(Java::OrgNeo4jKernelImplCore::NodeProxy)
38
+
39
+ end
@@ -0,0 +1,61 @@
1
+ module Neo4j
2
+
3
+
4
+ # A relationship between two nodes in the graph. A relationship has a start node, an end node and a type.
5
+ # You can attach properties to relationships like Neo4j::Node.
6
+ #
7
+ # The fact that the relationship API gives meaning to start and end nodes implicitly means that all relationships have a direction.
8
+ # In the example above, rel would be directed from node to otherNode.
9
+ # A relationship's start node and end node and their relation to outgoing and incoming are defined so that the assertions in the following code are true:
10
+ #
11
+ # Furthermore, Neo4j guarantees that a relationship is never "hanging freely,"
12
+ # i.e. start_node, end_node and other_node are guaranteed to always return valid, non-nil nodes.
13
+ #
14
+ # === Wrapping
15
+ #
16
+ # Notice that the Neo4j::Relationship.new does not create a Ruby object. Instead, it returns a Java
17
+ # Java::OrgNeo4jGraphdb::Relationship object which has been modified to feel more rubyish (like Neo4j::Node).
18
+ #
19
+ # @example
20
+ # a = Neo4j::Node.new
21
+ # b = Neo4j::Node.new
22
+ # rel = Neo4j::Relationship.new(:friends, a, b)
23
+ # # Now we have: (a) --- friends ---> (b)
24
+ #
25
+ # rel.start_node # => a
26
+ # rel.end_node # => b
27
+ #
28
+ # @example using the << operator on the Neo4j::Node relationship methods
29
+ #
30
+ # node.outgoing(:friends) << other_node << yet_another_node
31
+ #
32
+ # @see http://api.neo4j.org/current/org/neo4j/graphdb/Relationship.html
33
+ #
34
+ class Relationship
35
+ extend Neo4j::Core::Relationship::ClassMethods
36
+ include Neo4j::Core::Property
37
+ include Neo4j::Core::Equal
38
+ include Neo4j::Core::Relationship
39
+
40
+ # (see Neo4j::Core::Relationship::ClassMethods#new)
41
+ def initialize(rel_type, start_node, end_node, props={})
42
+ end
43
+
44
+
45
+ class << self
46
+ def extend_java_class(java_clazz) #:nodoc:
47
+ java_clazz.class_eval do
48
+ include Neo4j::Core::Property
49
+ include Neo4j::Core::Equal
50
+ include Neo4j::Core::Relationship
51
+ end
52
+ end
53
+
54
+ Neo4j::Relationship.extend_java_class(Java::OrgNeo4jKernelImplCore::RelationshipProxy)
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+
@@ -0,0 +1,86 @@
1
+ module Neo4j
2
+ #
3
+ # All modifying operations that work with the node space must be wrapped in a transaction. Transactions are thread confined.
4
+ # Neo4j does not implement true nested transaction, instead it uses flat nested transactions
5
+ #
6
+ # @see http://docs.neo4j.org/chunked/milestone/transactions.html
7
+ class Transaction
8
+
9
+ # Acquires a write lock for entity for this transaction. The lock (returned from this method) can be released manually, but if not it's released automatically when the transaction finishes.
10
+ # There is no implementation for this here because it's an java method
11
+ #
12
+ # @param [Neo4j::Relationship, Neo4j::Node] java_entity the entity to acquire a lock for. If another transaction currently holds a write lock to that entity this call will wait until it's released.
13
+ # @return [Java::OrgNeo4jGraphdb::Lock] a Lock which optionally can be used to release this lock earlier than when the transaction finishes. If not released (with Lock.release() it's going to be released with the transaction finishes.
14
+ # @see http://api.neo4j.org/current/org/neo4j/graphdb/Transaction.html#acquireWriteLock(Java::OrgNeo4jGraphdb::PropertyContainer)
15
+ # @see http://api.neo4j.org/current/org/neo4j/graphdb/Lock.html
16
+ def acquire_write_lock(java_entity)
17
+ end
18
+
19
+ # Acquires a read lock for entity for this transaction. The lock (returned from this method) can be released manually, but if not it's released automatically when the transaction finishes.
20
+ # There is no implementation for this here because it's an java method
21
+ # @param [Neo4j::Relationship, Neo4j::Node] java_entity the entity to acquire a lock for. If another transaction currently hold a write lock to that entity this call will wait until it's released.
22
+ # @return [Java::OrgNeo4jGraphdb::Lock] a Lock which optionally can be used to release this lock earlier than when the transaction finishes. If not released (with Lock.release() it's going to be released with the transaction finishes.
23
+ # @see http://api.neo4j.org/current/org/neo4j/graphdb/Transaction.html#acquireReadLock(Java::OrgNeo4jGraphdb::PropertyContainer)
24
+ # @see http://api.neo4j.org/current/org/neo4j/graphdb/Lock.html
25
+ def acquire_read_lock(java_entity)
26
+ end
27
+
28
+ # Starts a new Neo4j Transaction
29
+ # @return [Java::OrgNeo4jGraphdb::Transaction] a Java Neo4j Transaction object
30
+ # @see http://api.neo4j.org/current/org/neo4j/graphdb/Transaction.html
31
+ #
32
+ # @example
33
+ # tx = Neo4j::Transaction.new
34
+ # # modify something
35
+ # tx.success
36
+ # tx.finish
37
+ def self.new(instance = Neo4j.started_db)
38
+ instance.begin_tx
39
+ end
40
+
41
+ # Runs a block in a Neo4j transaction
42
+ #
43
+ # Many operations on neo requires an transaction. You will get much better performance if
44
+ # one transaction is wrapped around several neo operation instead of running one transaction per
45
+ # neo operation.
46
+ # If one transaction is already running then a 'placebo' transaction will be created.
47
+ # Performing a finish on a placebo transaction will not finish the 'real' transaction.
48
+ #
49
+ # If an exception occurs inside the block the transaction will rollback automatically.
50
+ #
51
+ # @example
52
+ #
53
+ # Neo4j::Transaction.run { node = PersonNode.new }
54
+ #
55
+ # @example access to the transaction and rollback
56
+ #
57
+ # Neo4j::Transaction.run do |t|
58
+ # # something failed
59
+ # t.failure # will cause a rollback
60
+ # end
61
+ #
62
+ # @yield the block which should be run under one transaction
63
+ # @yieldparam [Neo4j::Transaction]
64
+ # @return The value of the evaluated provided block
65
+ #
66
+ def self.run
67
+ raise ArgumentError.new("Expected a block to run in Transaction.run") unless block_given?
68
+
69
+ begin
70
+ tx = Neo4j::Transaction.new
71
+ ret = yield tx
72
+ tx.success
73
+ rescue Exception => e
74
+ if Neo4j::Config[:debug_java] && e.respond_to?(:cause)
75
+ puts "Java Exception in a transaction, cause: #{e.cause}"
76
+ e.cause.print_stack_trace
77
+ end
78
+ tx.failure unless tx.nil?
79
+ raise
80
+ ensure
81
+ tx.finish unless tx.nil?
82
+ end
83
+ ret
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,287 @@
1
+ module Neo4j
2
+
3
+ # Responsible for converting values from and to Java Neo4j and Lucene.
4
+ # You can implement your own converter by implementing the method <tt>convert?</tt>
5
+ # <tt>to_java</tt> and <tt>to_ruby</tt> in this module.
6
+ #
7
+ # There are currently three default converters that are triggered when a Time, Date or a DateTime is read or written
8
+ # if there is a type declared for the property.
9
+ #
10
+ # ==== Example
11
+ #
12
+ # Example of writing your own marshalling converter:
13
+ #
14
+ # class Foo
15
+ # include Neo4j::NodeMixin
16
+ # property :thing, :type => MyType
17
+ # end
18
+ #
19
+ # module Neo4j::TypeConverters
20
+ # class MyTypeConverter
21
+ # class << self
22
+ # def convert?(type)
23
+ # type == MyType
24
+ # end
25
+ #
26
+ # def to_java(val)
27
+ # "silly:#{val}"
28
+ # end
29
+ #
30
+ # def to_ruby(val)
31
+ # val.sub(/silly:/, '')
32
+ # end
33
+ # end
34
+ # end
35
+ # end
36
+ #
37
+ module TypeConverters
38
+
39
+ # The default converter to use if there isn't a specific converter for the type
40
+ class DefaultConverter
41
+ class << self
42
+
43
+ def to_java(value)
44
+ value
45
+ end
46
+
47
+ def to_ruby(value)
48
+ value
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ class BooleanConverter
55
+ class << self
56
+
57
+ def convert?(class_or_symbol)
58
+ :boolean == class_or_symbol
59
+ end
60
+
61
+ def to_java(value)
62
+ return nil if value.nil?
63
+ !!value && value != '0'
64
+ end
65
+
66
+ def to_ruby(value)
67
+ return nil if value.nil?
68
+ !!value && value != '0'
69
+ end
70
+ end
71
+ end
72
+
73
+ class SymbolConverter
74
+ class << self
75
+
76
+ def convert?(class_or_symbol)
77
+ :symbol == class_or_symbol || Symbol == class_or_symbol
78
+ end
79
+
80
+ def to_java(value)
81
+ return nil if value.nil?
82
+ value.to_s
83
+ end
84
+
85
+ def to_ruby(value)
86
+ return nil if value.nil?
87
+ value.to_sym
88
+ end
89
+ end
90
+ end
91
+
92
+
93
+ class StringConverter
94
+ class << self
95
+
96
+ def convert?(class_or_symbol)
97
+ [String, :string, :text].include? class_or_symbol
98
+ end
99
+
100
+ def to_java(value)
101
+ return nil if value.nil?
102
+ value.to_s
103
+ end
104
+
105
+ def to_ruby(value)
106
+ return nil if value.nil?
107
+ value.to_s
108
+ end
109
+ end
110
+ end
111
+
112
+
113
+
114
+ class FixnumConverter
115
+ class << self
116
+
117
+ def convert?(class_or_symbol)
118
+ Fixnum == class_or_symbol || :fixnum == class_or_symbol || :numeric == class_or_symbol
119
+ end
120
+
121
+ def to_java(value)
122
+ return nil if value.nil?
123
+ value.to_i
124
+ end
125
+
126
+ def to_ruby(value)
127
+ return nil if value.nil?
128
+ value.to_i
129
+ end
130
+ end
131
+ end
132
+
133
+ class FloatConverter
134
+ class << self
135
+
136
+ def convert?(clazz_or_symbol)
137
+ Float == clazz_or_symbol || :float == clazz_or_symbol
138
+ end
139
+
140
+ def to_java(value)
141
+ return nil if value.nil?
142
+ value.to_f
143
+ end
144
+
145
+ def to_ruby(value)
146
+ return nil if value.nil?
147
+ value.to_f
148
+ end
149
+ end
150
+ end
151
+
152
+ # Converts Date objects to Java long types. Must be timezone UTC.
153
+ class DateConverter
154
+ class << self
155
+
156
+ def convert?(clazz_or_symbol)
157
+ Date == clazz_or_symbol || :date == clazz_or_symbol
158
+ end
159
+
160
+ def to_java(value)
161
+ return nil if value.nil?
162
+ Time.utc(value.year, value.month, value.day).to_i
163
+ end
164
+
165
+ def to_ruby(value)
166
+ return nil if value.nil?
167
+ Time.at(value).utc.to_date
168
+ end
169
+ end
170
+ end
171
+
172
+ # Converts DateTime objects to and from Java long types. Must be timezone UTC.
173
+ class DateTimeConverter
174
+ class << self
175
+
176
+ def convert?(clazz_or_symbol)
177
+ DateTime == clazz_or_symbol || :datetime == clazz_or_symbol
178
+ end
179
+
180
+ # Converts the given DateTime (UTC) value to an Fixnum.
181
+ # Only utc times are supported !
182
+ def to_java(value)
183
+ return nil if value.nil?
184
+ if value.class == Date
185
+ Time.utc(value.year, value.month, value.day, 0, 0, 0).to_i
186
+ else
187
+ Time.utc(value.year, value.month, value.day, value.hour, value.min, value.sec).to_i
188
+ end
189
+ end
190
+
191
+ def to_ruby(value)
192
+ return nil if value.nil?
193
+ t = Time.at(value).utc
194
+ DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
195
+ end
196
+ end
197
+ end
198
+
199
+ class TimeConverter
200
+ class << self
201
+
202
+ def convert?(clazz_or_symbol)
203
+ Time == clazz_or_symbol || :time == clazz_or_symbol
204
+ end
205
+
206
+ # Converts the given DateTime (UTC) value to an Fixnum.
207
+ # Only utc times are supported !
208
+ def to_java(value)
209
+ return nil if value.nil?
210
+ if value.class == Date
211
+ Time.utc(value.year, value.month, value.day, 0, 0, 0).to_i
212
+ else
213
+ value.utc.to_i
214
+ end
215
+ end
216
+
217
+ def to_ruby(value)
218
+ return nil if value.nil?
219
+ Time.at(value).utc
220
+ end
221
+ end
222
+ end
223
+
224
+ class << self
225
+
226
+ # Mostly for testing purpose, You can use this method in order to
227
+ # add a converter while the neo4j has already started.
228
+ def converters=(converters)
229
+ @converters = converters
230
+ end
231
+
232
+ # Always returns a converter that handles to_ruby or to_java
233
+ # if +enforce_type+ is set to false then it will raise in case of unknown type
234
+ # otherwise it will return the DefaultConverter.
235
+ def converter(type = nil, enforce_type = true)
236
+ return DefaultConverter unless type
237
+ @converters ||= begin
238
+ Neo4j::TypeConverters.constants.find_all do |c|
239
+ Neo4j::TypeConverters.const_get(c).respond_to?(:convert?)
240
+ end.map do |c|
241
+ Neo4j::TypeConverters.const_get(c)
242
+ end
243
+ end
244
+ found = @converters.find {|c| c.convert?(type) }
245
+ raise "The type #{type.inspect} is unknown. Use one of #{@converters.map{|c| c.name }.join(", ")} or create a custom type converter." if !found && enforce_type
246
+ found or DefaultConverter
247
+ end
248
+
249
+ # Converts the given value to a Java type by using the registered converters.
250
+ # It just looks at the class of the given value unless an attribute name is given.
251
+ def convert(value, attribute = nil, klass = nil, enforce_type = true)
252
+ converter(attribute_type(value, attribute, klass), enforce_type).to_java(value)
253
+ end
254
+
255
+ # Converts the given property (key, value) to Java if there is a type converter for given type.
256
+ # The type must also be declared using Neo4j::NodeMixin#property property_name, :type => clazz
257
+ # If no Converter is defined for this value then it simply returns the given value.
258
+ def to_java(clazz, key, value)
259
+ type = clazz._decl_props[key.to_sym] && clazz._decl_props[key.to_sym][:type]
260
+ converter(type).to_java(value)
261
+ end
262
+
263
+ # Converts the given property (key, value) to Ruby if there is a type converter for given type.
264
+ # If no Converter is defined for this value then it simply returns the given value.
265
+ def to_ruby(clazz, key, value)
266
+ type = clazz._decl_props[key.to_sym] && clazz._decl_props[key.to_sym][:type]
267
+ converter(type).to_ruby(value)
268
+ end
269
+
270
+ private
271
+ def attribute_type(value, attribute = nil, klass = nil)
272
+ type = (attribute && klass) ? attribute_type_from_attribute_and_klass(value, attribute, klass) : nil
273
+ type || attribute_type_from_value(value)
274
+ end
275
+
276
+ def attribute_type_from_attribute_and_klass(value, attribute, klass)
277
+ if klass.respond_to?(:_decl_props)
278
+ (klass._decl_props.has_key?(attribute) && klass._decl_props[attribute][:type]) ? klass._decl_props[attribute][:type] : nil
279
+ end
280
+ end
281
+
282
+ def attribute_type_from_value(value)
283
+ value.class
284
+ end
285
+ end
286
+ end
287
+ end