module Neo4j::JavaPropertyMixin
# This is the property to use to map ruby classes to Neo4j Nodes
CLASSNAME_PROPERTY = "_classname"
# Returns the unique id of this node.
# Ids are garbage collected over time so are only guaranteed to be unique at a specific set of time: if the node is deleted,
# it's likely that a new node at some point will get the old id. Note: this make node ids brittle as public APIs.
def neo_id
getId
end
def _wrapper=(wrapper) # :nodoc:
@_wrapper = wrapper
end
def _java_node
self
end
# Returns true if this property container has a property accessible through the given key, false otherwise.
def property?(key)
has_property?(key.to_s)
end
# Returns the given property if it exist or nil if it does not exist.
def [](key)
return unless property?(key)
if @_wrapper and @_wrapper.class.marshal?(key)
Marshal.load(String.from_java_bytes(get_property(key.to_s)))
else
get_property(key.to_s)
end
end
# Sets the given property to given value.
# Will generate an event if the property does not start with '_' (which could be an internal property, like _classname)
#
def []=(key, value)
k = key.to_s
old_value = self[key]
if value.nil?
delete_property(k)
elsif @_wrapper and @_wrapper.class.marshal?(key)
setProperty(k, Marshal.dump(value).to_java_bytes)
else
value = java.lang.Double.new(value) if value.is_a? Float
setProperty(k, value)
end
if (@_wrapper and k[0, 1] != '_') # do not want events on internal properties
@_wrapper.class.indexer.on_property_changed(@_wrapper, k) if @_wrapper.class.respond_to? :indexer
Neo4j.event_handler.property_changed(@_wrapper, k, old_value, value)
end
end
# Removes the property from this node.
# This is same as setting a property value to nil.
#
# For more information see JavaDoc PropertyContainer#removeProperty
#
# ==== Example
# a = Node.new
# a[:foo] = 2
# a.delete_property('foo')
# a[:foo] # => nil
#
# ==== Returns
# true if the property was removed, false otherwise
#
def delete_property (name)
removed = !removeProperty(name).nil?
if (removed and @_wrapper and name[0] != '_') # do not want events on internal properties
@_wrapper.class.indexer.on_property_changed(self, name)
end
removed
end
# Returns a hash of all properties.
#
# === Returns
# Hash:: property key and property value with the '_neo_id' as the neo_id
#
def props
ret = {"_neo_id" => getId()}
iter = getPropertyKeys.iterator
while (iter.hasNext) do
key = iter.next
ret[key] = getProperty(key)
end
ret
end
# Updates this node/relationship's properties by using the provided struct/hash.
# If the option {:strict => true}
is given, any properties present on
# the node but not present in the hash will be removed from the node.
#
# === Parameters
# struct_or_hash<#each_pair>:: the key and value to be set, should respond to 'each_pair'
# options:: further options defining the context of the update, should be a Hash
#
# === Returns
# self
#
def update(struct_or_hash, options={})
strict = options[:strict]
keys_to_delete = props.keys - %w(_neo_id _classname) if strict
struct_or_hash.each_pair do |key, value|
next if %w(_neo_id _classname).include? key.to_s # do not allow special properties to be mass assigned
keys_to_delete.delete(key) if strict
setter_meth = "#{key}=".to_sym
if @_wrapper && @_wrapper.respond_to?(setter_meth)
@_wrapper.send(setter_meth, value)
else
self[key] = value
end
end
keys_to_delete.each{|key| delete_property(key) } if strict
self
end
def equal?(o)
eql?(o)
end
def eql?(o)
return false unless o.respond_to?(:neo_id)
o.neo_id == neo_id
end
def ==(o)
eql?(o)
end
# Same as neo_id but returns a String instead of a Fixnum.
# Used by Ruby on Rails.
#
def to_param
neo_id.to_s
end
# Loads a Neo node wrapper if possible
# If the neo property '_classname' does not exist then it will map the neo node to the ruby class Neo4j::Node
def wrapper
return self unless wrapper?
@_wrapper ||= wrapper_class.new(self)
@_wrapper
end
def wrapper?
property?(CLASSNAME_PROPERTY)
end
def wrapper_class # :nodoc:
return nil unless wrapper?
classname = get_property(CLASSNAME_PROPERTY)
classname.split("::").inject(Kernel) do |container, name|
container.const_get(name.to_s)
end
end
end