Skip to content

Commit

Permalink
first code commit
Browse files Browse the repository at this point in the history
  • Loading branch information
elado committed Jan 18, 2012
1 parent 53ef9dc commit 89f5552
Show file tree
Hide file tree
Showing 16 changed files with 504 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
*.gem
.bundle
Gemfile.lock
pkg/*
.DS_Store
1 change: 1 addition & 0 deletions .rspec
@@ -0,0 +1 @@
--color
4 changes: 4 additions & 0 deletions Gemfile
@@ -0,0 +1,4 @@
source "http://rubygems.org"

# Specify your gem's dependencies in neoid.gemspec
gemspec
19 changes: 19 additions & 0 deletions LICENSE
@@ -0,0 +1,19 @@
Copyright (c) 2012 Elad Ossadon

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.
54 changes: 41 additions & 13 deletions README.md
Expand Up @@ -25,25 +25,33 @@ Add to your Gemfile and run the `bundle` command to install it.

### First app configuration:

In an initializer, such as `config/initializers/neo4j.rb`:
In an initializer, such as `config/initializers/01_neo4j.rb`:

ENV["NEO4J_URL"] ||= "http://localhost:7474"

uri = URI.parse(ENV["NEO4J_URL"])

neo4j_uri = URI.parse(ENV["NEO4J_URL"] || "http://localhost:7474/")
$neo = Neography::Rest.new(neo4j_uri.to_s)

Neography::Config.tap do |c|
c.server = neo4j_uri.host
c.port = neo4j_uri.port
c.server = uri.host
c.port = uri.port

if neo4j_uri.user && neo4j_uri.password
if uri.user && uri.password
c.authentication = 'basic'
c.username = neo4j_uri.user
c.password = neo4j_uri.password
c.username = uri.user
c.password = uri.password
end
end

Neoid.db = $neo


`01_` in the file name is in order to get this file loaded first, before the models (files are loaded alphabetically).

If you have a better idea (I bet you do!) please let me know.


### ActiveRecord configuration

#### Nodes
Expand Down Expand Up @@ -145,6 +153,11 @@ So you could do:
user.neo_node # => #<Neography::Node…>
user.neo_node.display_name # => "elado"

rel = user.likes.first.neo_relationship
rel.start_node # user.neo_node
rel.end_node # user.movies.first.neo_node
rel.rel_type # 'likes'


## Querying

Expand All @@ -171,7 +184,7 @@ Of course, you can store using the `to_neo` all the data you need in Neo4j and a
m.sort{-it.value}.collect{it.key.ar_id}
GREMLIN

movie_ids = $neo.execute_script(gremlin_query)
movie_ids = Neoid.db.execute_script(gremlin_query)

Movie.where(id: movie_ids)

Expand All @@ -193,7 +206,7 @@ Assuming we have another `Friendship` model which is a relationship with start/e
.except(movies).collect{it.ar_id}
GREMLIN

movie_ids = $neo.execute_script(gremlin_query)
movie_ids = Neoid.db.execute_script(gremlin_query)

Movie.where(id: movie_ids)

Expand Down Expand Up @@ -228,16 +241,31 @@ Neoid tests run on a regular Neo4j database, on port 7574. You probably want to

In order to do that:

Copy the Neo4j folder to a different location, **or** symlink `bin`, `lib`, `plugins`, `system`, copy `conf` and create an empty `data` folder.
Copy the Neo4j folder to a different location,

**or**

symlink `bin`, `lib`, `plugins`, `system`, copy `conf` and create an empty `data` folder.

Then, edit `conf/neo4j-server.properties` and set the port (`org.neo4j.server.webserver.port`) from 7474 to 7574 and run the server with `bin/neo4j start`


Download and configure [neo4j-clean-remote-db-addon](https://github.com/jexp/neo4j-clean-remote-db-addon). For the test database, leave the default `secret-key` key.
Download, install and configure [neo4j-clean-remote-db-addon](https://github.com/jexp/neo4j-clean-remote-db-addon). For the test database, leave the default `secret-key` key.


## Contributing

Please create a [new issue](https://github.com/elado/neoid/issues) if you run into any bugs. Contribute patches via pull requests. Write tests and make sure all tests pass.


## TODO

## To Do

* Auto create node when creating an AR, instead of lazily-creating it
* `after_update` to update a node/relationship.
* Allow to disable sub reference nodes through options
* Execute queries/scripts from model and not Neography (e.g. `Movie.neo_gremlin(gremlin_query)` with query that outputs IDs, returns a list)
* Execute queries/scripts from model and not Neography (e.g. `Movie.neo_gremlin(gremlin_query)` with query that outputs IDs, returns a list of `Movie`s)

---

developed by [@elado](http://twitter.com/elado) | named by [@ekampf](http://twitter.com/ekampf)
6 changes: 6 additions & 0 deletions Rakefile
@@ -0,0 +1,6 @@
require "bundler/gem_tasks"
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)

task default: :spec
22 changes: 22 additions & 0 deletions lib/neoid.rb
@@ -0,0 +1,22 @@
require "neoid/version"
require "neoid/model_config"
require "neoid/model_additions"
require "neoid/node"
require "neoid/relationship"
require "neoid/railtie" if defined? Rails

module Neoid
class << self
attr_accessor :db
attr_accessor :ref_node

def db
raise "Neoid.db wasn't supplied" unless @db
@db
end

def ref_node
@ref_node ||= Neography::Node.load(Neoid.db.get_root['self'])
end
end
end
46 changes: 46 additions & 0 deletions lib/neoid/model_additions.rb
@@ -0,0 +1,46 @@
module Neoid
module ModelAdditions
module ClassMethods
def neoidable(options)
@config = Neoid::ModelConfig.new
yield(@config) if block_given?
@neoidable_options = options
end

def neoidable_options
@neoidable_options
end
end

module InstanceMethods
def to_neo
{}
end

def neo_index_name
@index_name ||= "#{self.class.name.tableize}_index"
end

protected
def neo_properties_to_hash(*property_list)
property_list.flatten.inject({}) { |all, property|
all[property] = self.send(property)
all
}
end

private
def _neo_representation
@_neo_representation ||= begin
results = neo_find_by_id
if results
neo_load(results.first['self'])
else
node = neo_create
node
end
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/neoid/model_config.rb
@@ -0,0 +1,11 @@
module Neoid
class ModelConfig
@properties = []

attr_accessor :properties

def property(name)
@properties << name
end
end
end
85 changes: 85 additions & 0 deletions lib/neoid/node.rb
@@ -0,0 +1,85 @@
module Neoid
module Node
module ClassMethods
def neo_subref_rel_type
@_neo_subref_rel_type ||= "#{self.name.tableize}_subref"
end
def neo_subref_node_rel_type
@_neo_subref_node_rel_type ||= self.name.tableize
end

def neo_subref_node
@_neo_subref_node ||= begin
subref_node_query = Neoid.ref_node.outgoing(neo_subref_rel_type)

if subref_node_query.to_a.blank?
node = Neography::Node.create(type: self.name, name: neo_subref_rel_type)
Neography::Relationship.create(
neo_subref_rel_type,
Neoid.ref_node,
node
)
else
node = subref_node_query.first
end

node
end
end
end

module InstanceMethods
def neo_find_by_id
Neoid.db.get_node_index(neo_index_name, :ar_id, self.id)
end

def neo_create
data = self.to_neo.merge(ar_type: self.class.name, ar_id: self.id)
data.reject! { |k, v| v.nil? }

node = Neography::Node.create(data)

begin
Neography::Relationship.create(
self.class.neo_subref_node_rel_type,
self.class.neo_subref_node,
node
)
rescue Exception => e
puts [$!.message] + $!.backtrace
raise e
end

Neoid.db.add_node_to_index(neo_index_name, :ar_id, self.id, node)
node
end

def neo_load(node)
Neography::Node.load(node)
end

def neo_node
_neo_representation
end

def neo_destroy
return unless neo_node
Neoid.db.remove_node_from_index(neo_index_name, neo_node)
neo_node.del
end
end

def self.included(receiver)
Neoid.db.create_node_index(receiver.name.tableize)

receiver.extend Neoid::ModelAdditions::ClassMethods
receiver.send :include, Neoid::ModelAdditions::InstanceMethods
receiver.extend ClassMethods
receiver.send :include, InstanceMethods

receiver.neo_subref_node # ensure

receiver.after_destroy :neo_destroy
end
end
end
53 changes: 53 additions & 0 deletions lib/neoid/relationship.rb
@@ -0,0 +1,53 @@
module Neoid
module Relationship
module InstanceMethods
def neo_find_by_id
Neoid.db.get_relationship_index(neo_index_name, :ar_id, self.id)
end

def neo_create
options = self.class.neoidable_options

start_node = self.send(options[:start_node])
end_node = self.send(options[:end_node])

return unless start_node && end_node

relationship = Neography::Relationship.create(
options[:type],
start_node.neo_node,
end_node.neo_node
)

Neoid.db.add_relationship_to_index(neo_index_name, :ar_id, self.id, relationship)

relationship
end

def neo_load(relationship)
Neography::Relationship.load(relationship)
end

def neo_destroy
return unless neo_relationship
Neoid.db.remove_relationship_from_index(neo_index_name, neo_relationship)
puts neo_relationship.del
end

def neo_relationship
_neo_representation
end
end

def self.included(receiver)
Neoid.db.create_relationship_index(receiver.name.tableize)

receiver.extend Neoid::ModelAdditions::ClassMethods
receiver.send :include, Neoid::ModelAdditions::InstanceMethods
receiver.send :include, InstanceMethods

receiver.after_create :neo_create
receiver.after_destroy :neo_destroy
end
end
end
3 changes: 3 additions & 0 deletions lib/neoid/version.rb
@@ -0,0 +1,3 @@
module Neoid
VERSION = "0.0.1.alpha"
end

0 comments on commit 89f5552

Please sign in to comment.