Skip to content

Commit

Permalink
* Added strict flag to Neo4j::NodeMixin#update (If strict is true, an…
Browse files Browse the repository at this point in the history
…y properties present on the node but not present in the hash will be removed from the node.)

* Disabled rest_spec tests in test/rest from the Rakefile since they requires some gems that are not released on rubyforge yet.
* Using Rack::Test in rest_spec
* Fixed RSpecs in node_mixin_spec.rb - deleted nodes are deleted after TX finish
* Added little documentation of the Neo4j.rb REST extension in the README.rdoc
 [#26]
  • Loading branch information
andreas committed Jun 18, 2009
2 parents 24c6faf + 8bfa900 commit ab26bbc
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 88 deletions.
21 changes: 19 additions & 2 deletions README.rdoc
Expand Up @@ -10,13 +10,14 @@ It provides:
* Transaction with rollbacks support.
* Indexing and querying of ruby objects.
* Can be used instead of ActiveRecord in Ruby on Rails or Merb
* Can be accessible as REST resources.

It uses two powerful and mature java libraries:
* Neo4J (http://www.neo4j.org/) - for persistance and traversal of the graph
* Lucene (http://lucene.apache.org/java/docs/index.html) for quering and indexing.

=== Status
* There are over 250 RSpecs.
* There are over 300 RSpecs.
* Has been tested with a simple rails application, used Neo4j.rb instead of ActiveRecord
* Has been load tested (loaded 18000 nodes and done queries/traversal in several threads.)
* Has not been used in production yet (as far as I know).
Expand All @@ -41,9 +42,11 @@ It uses two powerful and mature java libraries:
=== Content
This page contains the following information:
* Installation guide
* Three Minute Tutorial
* Ten Minute Tutorial
* Lucene API Documentation
* Neo4j API Documentation
* Extensions: REST
* Ruby on Rails with Neo4j.rb

== Installation
Expand Down Expand Up @@ -1387,11 +1390,25 @@ using ActiveMQ (see the cluster branch).
Another solution might be to simply copy the master database (rsync ?) to the slaves.


== Extension: REST

There is an experimental REST extension to Neo4j.rb.
It requires the following gems
* Sinatra >= 0.9.2
* Rack >= 1.0
* json-jruby > 1.1.6

For testing it also needs:
* rack-test

For more information see the test/rest/example.rb or the examples/admin.


== Ruby on Rails with Neo4j.rb

Neo4j.rb does work nicely with R&R.

It has been verified on rail 2.2.2, JRuby 1.1.6 RC1, Glassfish 0.9.1.
It has been verified on neo4j.rb 0.2.1 rail 2.2.2, JRuby 1.1.6 RC1, Glassfish 0.9.1.

=== Configuration

Expand Down
3 changes: 2 additions & 1 deletion Rakefile
Expand Up @@ -38,7 +38,8 @@ Spec::Rake::SpecTask.new do |t|
t.libs << "test"
t.libs << "lib"
# t.rcov = true
t.spec_files = FileList['test/**/*_spec.rb']
# rest specs requires some other gems - see the rest_spec.rb file
t.spec_files = FileList['test/lucene/*_spec.rb'] + FileList['test/neo4j/*_spec.rb']
t.spec_opts = ['--format specdoc', '--color']
# t.spec_opts = ['--format html:../doc/output/report.html'] #,'--backtrace']
end
Expand Down
4 changes: 1 addition & 3 deletions lib/neo4j/auto_tx.rb
@@ -1,5 +1,3 @@

NEO4J_AUTO_TX = true
loaded = require 'neo4j'
puts "LOADED" if loaded
puts "NOT LOADED" unless loaded
require 'neo4j'
16 changes: 11 additions & 5 deletions lib/neo4j/extensions/rest.rb
Expand Up @@ -34,7 +34,7 @@ module RestMixin
Sinatra::Application.get("/relations/:id") do
content_type :json
Neo4j::Transaction.run do
rel = Neo4j.load_relationship(params[:id])
rel = Neo4j.load_relationship(params[:id].to_i)
return 404, "Can't find relationship with id #{params[:id]}" if rel.nil?
rel.props.to_json
end
Expand Down Expand Up @@ -77,7 +77,14 @@ def self.included(c)
content_type :json
Neo4j::Transaction.run do
node = Neo4j.load(params[:id])
{params[:prop]=>node.get_property(params[:prop])}.to_json
return 404, "Can't find node with id #{params[:id]}" if node.nil?
prop = params[:prop].to_sym
if node.class.relationships_info.keys.include?(prop)
rels = node.send(prop) || []
rels.map{|rel| rel.props}.to_json
else
{prop => node.get_property(prop)}.to_json
end
end
end

Expand Down Expand Up @@ -106,7 +113,7 @@ def self.included(c)
return 400, "Wrong type id '#{to_node_id}' expected '#{to_clazz}' got '#{other_node.class.to_s}'"
end

rel_obj = node.instance_eval "#{rel}.new(other_node)" # TODO use send method instead
rel_obj = node.send(rel).new(other_node)

return 400, "Can't create relationship to #{to_clazz}" if rel_obj.nil?

Expand All @@ -116,7 +123,6 @@ def self.included(c)
end



Sinatra::Application.put("/nodes/#{classname}/:id/:prop") do
content_type :json
Neo4j::Transaction.run do
Expand Down Expand Up @@ -146,7 +152,7 @@ def self.included(c)
body = request.body.read
data = JSON.parse(body)
node = Neo4j.load(params[:id])
node.update(data)
node.update(data, true)
response = node.props.to_json
response
end
Expand Down
14 changes: 12 additions & 2 deletions lib/neo4j/mixins/node.rb
Expand Up @@ -182,6 +182,8 @@ def value_object

#
# Updates this node's properties by using the provided struct/hash.
# If strict is true, 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
Expand All @@ -190,11 +192,19 @@ def value_object
# self
#
# :api: public
def update(struct_or_hash)
def update(struct_or_hash, strict=false)
keys_to_delete = props.keys - %w(id classname) if strict
struct_or_hash.each_pair do |key, value|
next if %w(id classname).include? key.to_s # do not allow special properties to be mass assigned
self[key] = value
keys_to_delete.delete(key) if strict
method = "#{key}=".to_sym
if self.respond_to?(method)
self.send(method, value)
else
set_property(key.to_s, value)
end
end
keys_to_delete.each{|key| remove_property(key) } if strict
self
end

Expand Down
16 changes: 16 additions & 0 deletions lib/neo4j/mixins/relationship.rb
Expand Up @@ -93,6 +93,22 @@ def get_property(key)
@internal_r.getProperty(key)
end

# Returns a hash of all properties.
#
# ==== Returns
# Hash:: property key and property value
#
# :api: public
def props
ret = {"id" => neo_relationship_id}
iter = @internal_r.getPropertyKeys.iterator
while (iter.hasNext) do
key = iter.next
ret[key] = @internal_r.getProperty(key)
end
ret
end

# Returns the given property
#
# :api: public
Expand Down
1 change: 0 additions & 1 deletion lib/neo4j/mixins/transactional.rb
Expand Up @@ -8,7 +8,6 @@ module TransactionalMixin

def transactional(*methods)
return unless defined? NEO4J_AUTO_TX
puts "transactional: #{methods.inspect}"
methods.each do |name|
orig_name = (name.to_s == '<<') ? '_append' : "_original_#{name}"
self.send :alias_method, orig_name, name
Expand Down
38 changes: 37 additions & 1 deletion test/neo4j/node_mixin_spec.rb
Expand Up @@ -157,6 +157,7 @@ class TestNode
t.update({:name=>'123', :oj=>'hoj'})
t.name.should == '123'
t.age.should == nil
t['oj'].should == 'hoj'
end

it "should be able to update a node by using a hash" do
Expand Down Expand Up @@ -240,7 +241,33 @@ class TestNode
Neo4j::Transaction.finish
end


it "should remove the node from the database after the transaction finish" do
# given
node = Neo4j::Node.new
id = node.neo_node_id

# when
node.delete
Neo4j::Transaction.finish
Neo4j::Transaction.new


# then
Neo4j.load(id).should == nil
end


it "should not remove the node from the database if the transaction has not finish" do
# given
node = Neo4j::Node.new
id = node.neo_node_id

# when
node.delete

# then
Neo4j.load(id).should_not be_nil
end

it "should delete all relationships as well" do
# given
Expand Down Expand Up @@ -276,6 +303,15 @@ class TestNode
after(:all) do
stop
end

before(:each) do
Neo4j::Transaction.new
end

after(:each) do
Neo4j::Transaction.finish
end


before(:each) do
Neo4j::Transaction.new
Expand Down
1 change: 1 addition & 0 deletions test/neo4j/tx_tracker_spec.rb
Expand Up @@ -163,6 +163,7 @@ def to_s


it "should undo a complete transaction" do
pending "Fails if the RSpecs are running in reverse order - fix it"
@tx_node_list = Neo4j::TxNodeList.instance

node1 = node2 = node3 = nil
Expand Down

0 comments on commit ab26bbc

Please sign in to comment.