From 0b5b5474c6e83c64218129b8c8631073495ac0f7 Mon Sep 17 00:00:00 2001 From: maxdemarzi Date: Mon, 15 Nov 2010 13:08:09 -0800 Subject: [PATCH] starting on relationships --- LICENSE | 19 ++++ README.rdoc | 44 ++++++++ lib/neography.rb | 3 +- lib/neography/config.rb | 152 ++++++++++++++++++++++++++ lib/neography/node.rb | 6 + lib/neography/relationship.rb | 69 ++++++++++++ spec/integration/node_spec.rb | 8 +- spec/integration/relationship_spec.rb | 27 +++++ spec/spec_helper.rb | 4 +- 9 files changed, 328 insertions(+), 4 deletions(-) create mode 100644 LICENSE create mode 100644 README.rdoc create mode 100644 lib/neography/config.rb create mode 100644 lib/neography/relationship.rb create mode 100644 spec/integration/relationship_spec.rb diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a7429f --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Max De Marzi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..4e23f2b --- /dev/null +++ b/README.rdoc @@ -0,0 +1,44 @@ +== Welcome to Neography + +Neography is a thin ruby wrapper to the Neo4j Rest API, for more information: +* {Getting Started with REST for Neo4j}[http://wiki.neo4j.org/content/Getting_Started_REST] +* {Neo4j Rest API Reference}[http://components.neo4j.org/neo4j-rest/] + + +=== Installation + +gem install 'neography' +require 'neography' + + +==== Configuration + +Neography::Config.use do |config| + config[:protocol] = 'http://' + config[:server] = 'localhost' + config[:port] = '9999' +end + +==== Rails + +Just add gem 'neography' to your Gemfile and run bundle install + +Use the defaults (shown above) or create neography.rb in your config/initializers directory. + +=== Documentation + +Neography::Node.new # Create an empty node +Neography::Node.new(:age => 31, :name => "Max") # Create a node with some properties +Neography::Node.load(id) # Get a node and its properties +Neography::Node.set_properties(3, {:age => 31, :name => "Max"} ) # Deletes any existing properties with the passed hash +Neography::Node.properties(3) # Returns a hash of a node's properties or nil +Neography::Node.remove_property(3, :age) # Deletes the existing property +Neography::Node.del(3) # Deletes the node + + +=== License +* Neography - MIT, see the LICENSE file http://github.com/maxdemarzi/neography/tree/master/LICENSE. +* Lucene - Apache, see http://lucene.apache.org/java/docs/features.html +* Neo4j - Dual free software/commercial license, see http://neo4j.org/ + + diff --git a/lib/neography.rb b/lib/neography.rb index 60b355d..c692bb4 100644 --- a/lib/neography.rb +++ b/lib/neography.rb @@ -44,8 +44,9 @@ def evaluate_response(response) #Net::HTTP.http_logger_options = {:verbose => true} #Net::HTTP.http_logger_options = {:body => true} +require 'neography/config' require 'neography/neo' require 'neography/node' - +require 'neography/relationship' find_and_require_user_defined_code diff --git a/lib/neography/config.rb b/lib/neography/config.rb new file mode 100644 index 0000000..f60d84a --- /dev/null +++ b/lib/neography/config.rb @@ -0,0 +1,152 @@ +module Neography + + # == Keeps configuration for neography + # + # The most important configuration options are Neograophy::Config[:server] and Neograophy::Config[:port] which are + # used to locate where the neo4j database and is stored on the network. + # If these options are not supplied then the default of localhost:9999 will be used. + # + # ==== Default Configurations + # :protocol:: default http:// protocol to use (can be https://) + # :server:: default localhost where the database is stored on the network + # :port:: default 9999 what port is listening + # + class Config + # This code is copied from merb-core/config.rb. + class << self + # Returns the hash of default config values for neography + # + # ==== Returns + # Hash:: The defaults for the config. + def defaults + @defaults ||= { + :protocol => 'http://', + :server => 'localhost', + :port => '9999' + } + end + + + # Yields the configuration. + # + # ==== Block parameters + # c :: The configuration parameters, a hash. + # + # ==== Examples + # Neography::Config.use do |config| + # config[:server] = '192.168.1.13' + # end + # + # ==== Returns + # nil + def use + @configuration ||= {} + yield @configuration + nil + end + + + # Set the value of a config entry. + # + # ==== Parameters + # key :: The key to set the parameter for. + # val :: The value of the parameter. + # + def []=(key, val) + (@configuration ||= setup)[key] = val + end + + + # Gets the the value of a config entry + # + # ==== Parameters + # key:: The key of the config entry value we want + # + def [](key) + (@configuration ||= setup)[key] + end + + + # Remove the value of a config entry. + # + # ==== Parameters + # key:: The key of the parameter to delete. + # + # ==== Returns + # The value of the removed entry. + # + def delete(key) + @configuration.delete(key) + end + + + # Remove all configuration. This can be useful for testing purpose. + # + # + # ==== Returns + # nil + # + def delete_all + @configuration = nil + end + + + # Retrieve the value of a config entry, returning the provided default if the key is not present + # + # ==== Parameters + # key:: The key to retrieve the parameter for. + # default::The default value to return if the parameter is not set. + # + # ==== Returns + # The value of the configuration parameter or the default. + # + def fetch(key, default) + @configuration.fetch(key, default) + end + + # Sets up the configuration + # + # ==== Returns + # The configuration as a hash. + # + def setup() + @configuration = {} + @configuration.merge!(defaults) + @configuration + end + + + # Returns the configuration as a hash. + # + # ==== Returns + # The config as a hash. + # + def to_hash + @configuration + end + + # Returns the config as YAML. + # + # ==== Returns + # The config as YAML. + # + def to_yaml + require "yaml" + @configuration.to_yaml + end + + # Returns the configuration as a string. + # + # ==== Returns + # The config as a string. + # + def to_s + setup + @configuration[:protocol] + @configuration[:server].to_s + ':' + @configuration[:port].to_s + end + + + end + end + +end \ No newline at end of file diff --git a/lib/neography/node.rb b/lib/neography/node.rb index 58232e5..30b044e 100644 --- a/lib/neography/node.rb +++ b/lib/neography/node.rb @@ -46,6 +46,12 @@ def remove_property(id, property) response.parsed_response end + def remove_properties(id) + response = delete("/node/#{id}/properties") + evaluate_response(response) + response.parsed_response + end + def del(id) response = delete("/node/#{id}") evaluate_response(response) diff --git a/lib/neography/relationship.rb b/lib/neography/relationship.rb new file mode 100644 index 0000000..5cacd53 --- /dev/null +++ b/lib/neography/relationship.rb @@ -0,0 +1,69 @@ +module Neography + class Relationship + include HTTParty + base_uri Neography::Config.to_s + format :json + + class << self + + def new(type, from, to, props = nil) + options = { :body => {:to => Neography::Config.to_s + "/node/#{to[:neo_id]}", :data => props, :type => type }.to_json, :headers => {'Content-Type' => 'application/json'} } + response = post("/node/#{from[:neo_id]}/relationships", options) + evaluate_response(response) + build_relationship(response) + end + + def load(id) + begin + response = get("/node/#{id}") + evaluate_response(response) + build_relationship(response) + rescue + nil + end + end + + def properties(id) + get("/relationship/#{id}/properties") + end + + def set_properties(id, properties) + options = { :body => properties.to_json, :headers => {'Content-Type' => 'application/json'} } + response = put("/relationship/#{id}/properties", options) + evaluate_response(response) + response.parsed_response + end + + def remove_property(id, property) + response = delete("/relationship/#{id}/properties/#{property}") + evaluate_response(response) + response.parsed_response + end + + def remove_properties(id) + response = delete("/relationship/#{id}/properties") + evaluate_response(response) + response.parsed_response + end + + def del(id) + response = delete("/relationship/#{id}") + evaluate_response(response) + response.parsed_response + end + + private + + def build_relationship(response) + begin + relationship = response.parsed_response["data"] + rescue + relationship = Array.new + end + relationship[:rel_id] = response.parsed_response["self"].split('/').last + relationship + end + + end + end +end diff --git a/spec/integration/node_spec.rb b/spec/integration/node_spec.rb index c232acb..579762d 100644 --- a/spec/integration/node_spec.rb +++ b/spec/integration/node_spec.rb @@ -54,6 +54,12 @@ Neography::Node.remove_property(9999, :height).should be_nil end + it "can delete all of a node's properties" do + Neography::Node.set_properties(2, {:age => 32, :name => "Tom", :weight => 200} ).should be_nil + Neography::Node.remove_properties(2).should be_nil + Neography::Node.properties(2).should be_nil + end + it "can delete an unrelated node" do newnode = Neography::Node.new Neography::Node.del(newnode[:neo_id]).should be_nil @@ -72,7 +78,7 @@ it "returns nil if it tries to delete a node that has existing relationships" do node1 = Neography::Node.new node2 = Neography::Node.new - pending "create relationship from node1 to node2" + Neography::Relationship.new(:friends, node1, node2) Neography::Node.del(node1[:neo_id]).should be_nil Neography::Node.del(node2[:neo_id]).should be_nil end diff --git a/spec/integration/relationship_spec.rb b/spec/integration/relationship_spec.rb new file mode 100644 index 0000000..c5b03fe --- /dev/null +++ b/spec/integration/relationship_spec.rb @@ -0,0 +1,27 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +describe Neography::Relationship do + it "can create an empty relationship" do + Neography::Relationship.new(:friends, Neography::Node.new, Neography::Node.new).should include(:rel_id) + end + + it "can create a relationship with one property" do + Neography::Relationship.new(:friends, Neography::Node.new, Neography::Node.new, {:since => '10-1-2010'}).should include("since") + end + + it "can create a relationship with multiple properties" do + Neography::Relationship.new(:friends, Neography::Node.new, Neography::Node.new, {:since => '10-1-2010', :closeness => 'bff'}).should include("closeness"=>"bff", "since"=>"10-1-2010") + end + + it "can get a relationship's properties" do + rel = Neography::Relationship.new(:friends, Neography::Node.new, Neography::Node.new) + Neography::Relationship.set_properties(rel[:rel_id], {:since => '10-1-2010'} ).should be_nil + Neography::Relationship.properties(rel[:rel_id]).should include("since"=>"10-1-2010") + end + + it "returns nil if a relationship has no properties" do + rel = Neography::Relationship.new(:friends, Neography::Node.new, Neography::Node.new) + Neography::Relationship.properties(rel[:rel_id]).should be_nil + end + +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2539c70..d63b985 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,6 +8,6 @@ #To test against a real database: #1. Make sure empty database is running (./bin/neo4j-rest start) #2. Uncomment the next two lines -#FakeWeb.clean_registry -#FakeWeb.allow_net_connect = true +FakeWeb.clean_registry +FakeWeb.allow_net_connect = true