Skip to content
Permalink
Browse files

Implemented support for list and timeline index [#136 state:resolved]

  • Loading branch information...
andreasronge committed Nov 18, 2010
1 parent 50b5b60 commit 5ae58085da7c06f3ea855c2d5143fa8016d70c93
@@ -38,8 +38,10 @@
require 'neo4j/mapping/class_methods/root'
require 'neo4j/mapping/class_methods/property'
require 'neo4j/mapping/class_methods/relationship'
require 'neo4j/mapping/class_methods/list'
require 'neo4j/mapping/decl_relationship_dsl'
require 'neo4j/mapping/has_n'
require 'neo4j/mapping/has_list'
require 'neo4j/mapping/node_mixin'
require 'neo4j/mapping/relationship_mixin'
require 'neo4j/node_mixin'
@@ -0,0 +1,13 @@
module Neo4j::Mapping
module ClassMethods
module List
def has_list(name, params = {})
module_eval(%Q{
def #{name}
Neo4j::Mapping::HasList.new(self, '#{name}')
end}, __FILE__, __LINE__)
end
end
end
end

@@ -0,0 +1,134 @@
module Neo4j
module Mapping

# Enables creating and traversal of nodes in a list.
#
# It uses the TimeLine http://api.neo4j.org/current/org/neo4j/index/timeline/Timeline.html,
#
# Includes the Enumerable Mixin.
# The Neo4j::Mapping::ClassMethods::List#has_list methods returns an object of this type.
#
# === Example, index same as size of list
#
# class Person
# include Neo4j::NodeMixin
# has_list :feeds
# end
#
# person = Person.new
# person.feeds << Neo4j::Node:new << Neo4j::Node.new
#
# === Example, using a custom index
#
# class Person
# include Neo4j::NodeMixin
# has_list :feeds
# end
#
# person = Person.new
# person.feeds[42] = (a = Neo4j::Node:new)
# person.feeds[1251] = Neo4j::Node.new
#
# person.feeds[42] # => a
#
class HasList
include Enumerable
include ToJava

def initialize(node, name)
@time_line = org.neo4j.index.timeline.Timeline.new(name, node._java_node, true, Neo4j.started_db.graph)
@node = node
@name = name
self.size = 0 unless size
end

# returns the size of this list
# notice in order to get correct result you must call the #remove method when an item is removed from the list
def size
@node["_list_size_#{@name}"]
end

# same as #size == 0
def empty?
size == 0
end

# returns the first node with index n
def [](n)
@time_line.getAllNodesBetween(n-1, n+1).first
end

# returns all nodes with the given index n
def all(n)
@time_line.getAllNodesBetween(n-1, n+1)
end

# returns the first node in the list or nil
def first
@time_line.first_node
end

# returns the last node in the list or nil
def last
@time_line.last_node
end

# adds a node to the list with the given index n
def []=(n, other_node)
@time_line.add_node(other_node, n)
self.size = self.size + 1
end

# returns all the nodes between the given Range
def between(range)
@time_line.getAllNodesBetween(range.first-1, range.end+1)
end

# removes one node from the list and decrases the size of the list,
def remove(node)
@time_line.remove_node(node)
self.size = self.size - 1
end

# Required by the Enumerable mixin so that we can
#
# ==== Example
#
# class Person
# include Neo4j::NodeMixin
# has_list :feeds
# end
#
# person.feeds.each {|node| node}
#
def each
@time_line.all_nodes.iterator.each { |node|
if @raw then
yield node
else
yield node.wrapper
end }
end

# If called then it will only return the raw java nodes and not the Ruby wrappers using the Neo4j::NodeMixin
def raw
@raw = true
end

def <<(other)
@time_line.add_node(other, size)
self.size = self.size + 1
self
end


private
def size=(size)
@node["_list_size_#{@name}"] = size
end


end

end
end
@@ -1,22 +1,27 @@
module Neo4j::Mapping

# This Mixin is used to wrap Neo4j Java Nodes in Ruby objects.
# It includes the Neo4j::Index module and forwards a number of method to the raw Java node which
# intern includes a number of mixins, see below.
#
# === Instance Mixins
# It includes a number of mixins and forwards some methods to the raw Java node which in term includes a number of mixins, see below.
#
# === Instance Methods
#
# Mixins:
# * Neo4j::Index
# * Neo4j::Property
# * Neo4j::NodeRelationship
# * Neo4j::Equal
# * Neo4j::Index
#
# === Class Mixins
# === Class Methods
#
# Mixins:
# * Neo4j::Mapping::ClassMethods::Root
# * Neo4j::Mapping::ClassMethods::Property
# * Neo4j::Mapping::ClassMethods::InitNode
# * Neo4j::Mapping::ClassMethods::Relationship
# * Neo4j::Mapping::ClassMethods::Rule
# * Neo4j::Mapping::ClassMethods::List
# * Neo4j::Index::ClassMethods
#
module NodeMixin
@@ -64,6 +69,7 @@ def wrapper
self
end


def self.included(c) # :nodoc:
c.instance_eval do
class << self
@@ -76,6 +82,7 @@ class << self
c.extend ClassMethods::InitNode
c.extend ClassMethods::Relationship
c.extend ClassMethods::Rule
c.extend ClassMethods::List
c.extend Neo4j::Index::ClassMethods

def c.inherited(subclass)
@@ -0,0 +1,144 @@
require File.join(File.dirname(__FILE__), '..', 'spec_helper')

describe "Neo4j::NodeMixin#list :events", :type => :transactional do
before(:all) do
@clazz = create_node_mixin do
has_list :events
end
end

describe "not empty list" do
before(:each) do
@root = @clazz.new
@root.events[14243] = (@a = Neo4j::Node.new :name => 'a')
@root.events[42] = (@b = Neo4j::Node.new :name => 'b')
@root.events[42] = (@c = Neo4j::Node.new :name => 'c')
@root.events[100] = (@d = Neo4j::Node.new :name => 'd')
end

describe "generated method 'events" do
it "returns all nodes in this list" do
@root.events.should include(@a, @b, @c, @d)
end

describe "[n]" do
it "[n] returns the node with index n" do
@root.events[14243].should == @a
@root.events[100].should == @d
end
end

describe "[n]=" do
it "should increase the size when a node is added" do
lambda { @root.events[51235] = Neo4j::Node.new }.should change(@root.events, :size).by(1)
end

it "can be accessed with the [n] operator" do
@root.events[12345] = (node = Neo4j::Node.new)
@root.events[12345].should == node
end
end

describe "all(n)" do
it "all(n) returns all the items with index n" do
all = [*@root.events.all(42)]
all.should == [@b, @c]
end
end

describe "between(range)" do
it "events.between(x,y) returns all nodes between x and y (Fixnum)" do
@root.events.between(2..99998).should include(@b, @c, @d, @a)
@root.events.between(100..100).should include(@d)
[*@root.events.between(100..100)].size.should == 1
end
end

it "should delete the node from the list when the node is deleted" do
@c.del
new_tx
@root.events.should include(@a, @b, @d)
@root.events.should_not include(@c)
end

it "does not change size of the list when the node is deleted (!)" do
lambda { @a.del }.should_not change(@root.events, :size)
end


describe "remove" do
it "removes the node from the lists " do
@root.events.should include(@a)
@root.events.remove(@a)
@root.events.should_not include(@a)
end

it "decrease the size of the list by 1" do
lambda { @root.events.remove(@a) }.should change(@root.events, :size).by(-1)
end
end
end
end

describe "empty list" do
before(:each) do
@root = @clazz.new
end

describe "generated method 'events'" do
it "should be empty" do
@root.events.should be_empty
@root.events.size.should == 0
end

describe "<<" do
before(:each) do
@root.events << (@a = Neo4j::Node.new)
@root.events << (@b = Neo4j::Node.new)
@root.events << (@c = Neo4j::Node.new)
@root.events << (@d = Neo4j::Node.new)
end

it "returns all nodes in the order it was inserted" do
[*@root.events].should == [@a, @b, @c, @d]
end

it "is inserted with the same index as the size of the list" do
@root.events[0].should == @a
@root.events[1].should == @b
@root.events[2].should == @c
@root.events[3].should == @d
end

it "increase the size" do
lambda { @root.events << Neo4j::Node.new }.should change(@root.events, :size).by(1)
end

it "#last returns the last item inserted" do
@root.events.last.should == @d
end

it "#first returns the last item inserted" do
@root.events.first.should == @a
end

end

describe "first" do
it "returns nil" do
@root.events.first.should be_nil
end
end

describe "last" do
it "returns nil" do
@root.events.last.should be_nil
end
end

end
end


end

0 comments on commit 5ae5808

Please sign in to comment.
You can’t perform that action at this time.