neoid 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,3 @@
1
+ ## v0.0.1
2
+
3
+ * initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in neoid.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Elad Ossadon
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,271 @@
1
+ # DRAFT ONLY - GEM IS NOT HOSTED YET.
2
+
3
+ # Neoid
4
+
5
+ Make your ActiveRecords stored and searchable on Neo4j graph database, in order to make fast graph queries that MySQL would crawl while doing them.
6
+
7
+ Neoid to Neo4j is like Sunspot to Solr. You get the benefits of Neo4j speed while keeping your schema on your plain old RDBMS.
8
+
9
+ Neoid doesn't require JRuby. It's based on the great [Neography](https://github.com/maxdemarzi/neography) gem which uses Neo4j's REST API.
10
+
11
+ Neoid offers querying Neo4j for IDs of objects and then fetch them from your RDBMS, or storing all desired data on Neo4j.
12
+
13
+
14
+
15
+ ## Installation
16
+
17
+ Add to your Gemfile and run the `bundle` command to install it.
18
+
19
+ gem 'neoid'
20
+
21
+
22
+ **Requires Ruby 1.9.2 or later.**
23
+
24
+ ## Usage
25
+
26
+ ### First app configuration:
27
+
28
+ In an initializer, such as `config/initializers/01_neo4j.rb`:
29
+
30
+ ENV["NEO4J_URL"] ||= "http://localhost:7474"
31
+
32
+ uri = URI.parse(ENV["NEO4J_URL"])
33
+
34
+ $neo = Neography::Rest.new(neo4j_uri.to_s)
35
+
36
+ Neography::Config.tap do |c|
37
+ c.server = uri.host
38
+ c.port = uri.port
39
+
40
+ if uri.user && uri.password
41
+ c.authentication = 'basic'
42
+ c.username = uri.user
43
+ c.password = uri.password
44
+ end
45
+ end
46
+
47
+ Neoid.db = $neo
48
+
49
+
50
+ `01_` in the file name is in order to get this file loaded first, before the models (files are loaded alphabetically).
51
+
52
+ If you have a better idea (I bet you do!) please let me know.
53
+
54
+
55
+ ### ActiveRecord configuration
56
+
57
+ #### Nodes
58
+
59
+ For nodes, first include the `Neoid::Node` module in your model:
60
+
61
+
62
+ class User < ActiveRecord::Base
63
+ include Neoid::Node
64
+ end
65
+
66
+
67
+ This will help to create a corresponding node on Neo4j when a user is created, delete it when a user is destroyed, and update it if needed.
68
+
69
+ Then, you can customize what fields will be saved on the node in Neo4j, by implementing `to_neo` method:
70
+
71
+
72
+ class User < ActiveRecord::Base
73
+ include Neoid::Node
74
+
75
+ def to_neo
76
+ {
77
+ slug: slug,
78
+ display_name: display_name
79
+ }
80
+ end
81
+ end
82
+
83
+ You can use `neo_properties_to_hash`, a helper method to make things shorter:
84
+
85
+
86
+ def to_neo
87
+ neo_properties_to_hash(%w(slug display_name))
88
+ end
89
+
90
+
91
+ #### Relationships
92
+
93
+ Let's assume that a `User` can `Like` `Movie`s:
94
+
95
+
96
+ # user.rb
97
+
98
+ class User < ActiveRecord::Base
99
+ include Neoid::Node
100
+
101
+ has_many :likes
102
+ has_many :movies, through: :likes
103
+
104
+ def to_neo
105
+ neo_properties_to_hash(%w(slug display_name))
106
+ end
107
+ end
108
+
109
+
110
+ # movie.rb
111
+
112
+ class Movie < ActiveRecord::Base
113
+ include Neoid::Node
114
+
115
+ has_many :likes
116
+ has_many :users, through: :likes
117
+
118
+ def to_neo
119
+ neo_properties_to_hash(%w(slug name))
120
+ end
121
+ end
122
+
123
+
124
+ # like.rb
125
+
126
+ class Like < ActiveRecord::Base
127
+ belongs_to :user
128
+ belongs_to :movie
129
+ end
130
+
131
+
132
+
133
+ Now let's make the `Like` model a Neoid, by including the `Neoid::Relationship` module, and define the relationship (start & end nodes and relationship type) options with `neoidable` method:
134
+
135
+
136
+ class Like < ActiveRecord::Base
137
+ belongs_to :user
138
+ belongs_to :movie
139
+
140
+ include Neoid::Relationship
141
+ neoidable start_node: :user, end_node: :movie, type: :likes
142
+ end
143
+
144
+
145
+ Neoid adds `neo_node` and `neo_relationships` to nodes and relationships, respectively.
146
+
147
+ So you could do:
148
+
149
+ user = User.create!(display_name: "elado")
150
+ user.movies << Movie.create("Memento")
151
+ user.movies << Movie.create("Inception")
152
+
153
+ user.neo_node # => #<Neography::Node…>
154
+ user.neo_node.display_name # => "elado"
155
+
156
+ rel = user.likes.first.neo_relationship
157
+ rel.start_node # user.neo_node
158
+ rel.end_node # user.movies.first.neo_node
159
+ rel.rel_type # 'likes'
160
+
161
+
162
+ ## Querying
163
+
164
+ You can query with all [Neography](https://github.com/maxdemarzi/neography)'s API: `traverse`, `execute_query` for Cypher, and `execute_script` for Gremlin.
165
+
166
+ ### Gremlin Example:
167
+
168
+ These examples query Neo4j using Gremlin for IDs of objects, and then fetches them from ActiveRecord with an `in` query.
169
+
170
+ Of course, you can store using the `to_neo` all the data you need in Neo4j and avoid querying ActiveRecord.
171
+
172
+
173
+ **Most popular categories**
174
+
175
+ gremlin_query = <<-GREMLIN
176
+ m = [:]
177
+
178
+ g.v(0)
179
+ .out('movies_subref').out
180
+ .inE('likes')
181
+ .inV
182
+ .groupCount(m).iterate()
183
+
184
+ m.sort{-it.value}.collect{it.key.ar_id}
185
+ GREMLIN
186
+
187
+ movie_ids = Neoid.db.execute_script(gremlin_query)
188
+
189
+ Movie.where(id: movie_ids)
190
+
191
+
192
+ Assuming we have another `Friendship` model which is a relationship with start/end nodes of `user` and type of `friends`,
193
+
194
+ **Movies of user friends that the user doesn't have**
195
+
196
+ user = User.find(1)
197
+
198
+ gremlin_query = <<-GREMLIN
199
+ u = g.idx('users_index')[[ar_id:'#{user.id}']][0].toList()[0]
200
+ movies = []
201
+
202
+ u
203
+ .out('likes').aggregate(movies).back(2)
204
+ .out('friends').out('likes')
205
+ .dedup
206
+ .except(movies).collect{it.ar_id}
207
+ GREMLIN
208
+
209
+ movie_ids = Neoid.db.execute_script(gremlin_query)
210
+
211
+ Movie.where(id: movie_ids)
212
+
213
+
214
+ `[0].toList()[0]` is in order to get a pipeline object which we can actually query on.
215
+
216
+
217
+ ## Behind The Scenes
218
+
219
+ Whenever the `neo_node` on nodes or `neo_relationship` on relationships is called, Neoid checks if there's a corresponding node/relationship in Neo4j. If not, it does the following:
220
+
221
+ ### For Nodes:
222
+
223
+ 1. Ensures there's a sub reference node (read [here](http://docs.neo4j.org/chunked/stable/tutorials-java-embedded-index.html) about sub reference nodes)
224
+ 2. Creates a node based on the ActiveRecord, with the `id` attribute and all other attributes from `to_neo`
225
+ 3. Creates a relationship between the sub reference node and the newly created node
226
+ 4. Adds the ActiveRecord `id` to a node index, pointing to the Neo4j node id, for fast lookup in the future
227
+
228
+ Then, when it needs to find it again, it just seeks the node index with that ActiveRecord id for its neo node id.
229
+
230
+ ### For Relationships:
231
+
232
+ Like Nodes, it uses an index (relationship index) to look up a relationship by ActiveRecord id
233
+
234
+ 1. With the options passed in the `neoidable`, it fetches the `start_node` and `end_node`
235
+ 2. Then, it calls `neo_node` on both, in order to create the Neo4j nodes if they're not created yet, and creates the relationship with the type from the options.
236
+ 3. Add the relationship to the relationship index.
237
+
238
+ ## Testing
239
+
240
+ Neoid tests run on a regular Neo4j database, on port 7574. You probably want to have it running on a different instance than your development one.
241
+
242
+ In order to do that:
243
+
244
+ Copy the Neo4j folder to a different location,
245
+
246
+ **or**
247
+
248
+ symlink `bin`, `lib`, `plugins`, `system`, copy `conf` and create an empty `data` folder.
249
+
250
+ 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`
251
+
252
+
253
+ 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.
254
+
255
+
256
+ ## Contributing
257
+
258
+ 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.
259
+
260
+
261
+
262
+ ## To Do
263
+
264
+ * Auto create node when creating an AR, instead of lazily-creating it
265
+ * `after_update` to update a node/relationship.
266
+ * Allow to disable sub reference nodes through options
267
+ * 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)
268
+
269
+ ---
270
+
271
+ developed by [@elado](http://twitter.com/elado) | named by [@ekampf](http://twitter.com/ekampf)
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,22 @@
1
+ require "neoid/version"
2
+ require "neoid/model_config"
3
+ require "neoid/model_additions"
4
+ require "neoid/node"
5
+ require "neoid/relationship"
6
+ require "neoid/railtie" if defined? Rails
7
+
8
+ module Neoid
9
+ class << self
10
+ attr_accessor :db
11
+ attr_accessor :ref_node
12
+
13
+ def db
14
+ raise "Neoid.db wasn't supplied" unless @db
15
+ @db
16
+ end
17
+
18
+ def ref_node
19
+ @ref_node ||= Neography::Node.load(Neoid.db.get_root['self'])
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,46 @@
1
+ module Neoid
2
+ module ModelAdditions
3
+ module ClassMethods
4
+ def neoidable(options)
5
+ @config = Neoid::ModelConfig.new
6
+ yield(@config) if block_given?
7
+ @neoidable_options = options
8
+ end
9
+
10
+ def neoidable_options
11
+ @neoidable_options
12
+ end
13
+ end
14
+
15
+ module InstanceMethods
16
+ def to_neo
17
+ {}
18
+ end
19
+
20
+ def neo_index_name
21
+ @index_name ||= "#{self.class.name.tableize}_index"
22
+ end
23
+
24
+ protected
25
+ def neo_properties_to_hash(*property_list)
26
+ property_list.flatten.inject({}) { |all, property|
27
+ all[property] = self.send(property)
28
+ all
29
+ }
30
+ end
31
+
32
+ private
33
+ def _neo_representation
34
+ @_neo_representation ||= begin
35
+ results = neo_find_by_id
36
+ if results
37
+ neo_load(results.first['self'])
38
+ else
39
+ node = neo_create
40
+ node
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ module Neoid
2
+ class ModelConfig
3
+ @properties = []
4
+
5
+ attr_accessor :properties
6
+
7
+ def property(name)
8
+ @properties << name
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,85 @@
1
+ module Neoid
2
+ module Node
3
+ module ClassMethods
4
+ def neo_subref_rel_type
5
+ @_neo_subref_rel_type ||= "#{self.name.tableize}_subref"
6
+ end
7
+ def neo_subref_node_rel_type
8
+ @_neo_subref_node_rel_type ||= self.name.tableize
9
+ end
10
+
11
+ def neo_subref_node
12
+ @_neo_subref_node ||= begin
13
+ subref_node_query = Neoid.ref_node.outgoing(neo_subref_rel_type)
14
+
15
+ if subref_node_query.to_a.blank?
16
+ node = Neography::Node.create(type: self.name, name: neo_subref_rel_type)
17
+ Neography::Relationship.create(
18
+ neo_subref_rel_type,
19
+ Neoid.ref_node,
20
+ node
21
+ )
22
+ else
23
+ node = subref_node_query.first
24
+ end
25
+
26
+ node
27
+ end
28
+ end
29
+ end
30
+
31
+ module InstanceMethods
32
+ def neo_find_by_id
33
+ Neoid.db.get_node_index(neo_index_name, :ar_id, self.id)
34
+ end
35
+
36
+ def neo_create
37
+ data = self.to_neo.merge(ar_type: self.class.name, ar_id: self.id)
38
+ data.reject! { |k, v| v.nil? }
39
+
40
+ node = Neography::Node.create(data)
41
+
42
+ begin
43
+ Neography::Relationship.create(
44
+ self.class.neo_subref_node_rel_type,
45
+ self.class.neo_subref_node,
46
+ node
47
+ )
48
+ rescue Exception => e
49
+ puts [$!.message] + $!.backtrace
50
+ raise e
51
+ end
52
+
53
+ Neoid.db.add_node_to_index(neo_index_name, :ar_id, self.id, node)
54
+ node
55
+ end
56
+
57
+ def neo_load(node)
58
+ Neography::Node.load(node)
59
+ end
60
+
61
+ def neo_node
62
+ _neo_representation
63
+ end
64
+
65
+ def neo_destroy
66
+ return unless neo_node
67
+ Neoid.db.remove_node_from_index(neo_index_name, neo_node)
68
+ neo_node.del
69
+ end
70
+ end
71
+
72
+ def self.included(receiver)
73
+ Neoid.db.create_node_index(receiver.name.tableize)
74
+
75
+ receiver.extend Neoid::ModelAdditions::ClassMethods
76
+ receiver.send :include, Neoid::ModelAdditions::InstanceMethods
77
+ receiver.extend ClassMethods
78
+ receiver.send :include, InstanceMethods
79
+
80
+ receiver.neo_subref_node # ensure
81
+
82
+ receiver.after_destroy :neo_destroy
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,53 @@
1
+ module Neoid
2
+ module Relationship
3
+ module InstanceMethods
4
+ def neo_find_by_id
5
+ Neoid.db.get_relationship_index(neo_index_name, :ar_id, self.id)
6
+ end
7
+
8
+ def neo_create
9
+ options = self.class.neoidable_options
10
+
11
+ start_node = self.send(options[:start_node])
12
+ end_node = self.send(options[:end_node])
13
+
14
+ return unless start_node && end_node
15
+
16
+ relationship = Neography::Relationship.create(
17
+ options[:type],
18
+ start_node.neo_node,
19
+ end_node.neo_node
20
+ )
21
+
22
+ Neoid.db.add_relationship_to_index(neo_index_name, :ar_id, self.id, relationship)
23
+
24
+ relationship
25
+ end
26
+
27
+ def neo_load(relationship)
28
+ Neography::Relationship.load(relationship)
29
+ end
30
+
31
+ def neo_destroy
32
+ return unless neo_relationship
33
+ Neoid.db.remove_relationship_from_index(neo_index_name, neo_relationship)
34
+ puts neo_relationship.del
35
+ end
36
+
37
+ def neo_relationship
38
+ _neo_representation
39
+ end
40
+ end
41
+
42
+ def self.included(receiver)
43
+ Neoid.db.create_relationship_index(receiver.name.tableize)
44
+
45
+ receiver.extend Neoid::ModelAdditions::ClassMethods
46
+ receiver.send :include, Neoid::ModelAdditions::InstanceMethods
47
+ receiver.send :include, InstanceMethods
48
+
49
+ receiver.after_create :neo_create
50
+ receiver.after_destroy :neo_destroy
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Neoid
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "neoid/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "neoid"
7
+ s.version = Neoid::VERSION
8
+ s.authors = ["Elad Ossadon"]
9
+ s.email = ["elad@ossadon.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Neo4j for ActiveRecord}
12
+ s.description = %q{Extend Ruby on Rails ActiveRecord with Neo4j nodes. Keep RDBMS and utilize the power of Neo4j queries}
13
+
14
+ s.rubyforge_project = "neoid"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "rest-client"
23
+ s.add_runtime_dependency "neography"
24
+ s.add_runtime_dependency "supermodel"
25
+ end
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ class User < SuperModel::Base
4
+ include ActiveModel::Validations::Callbacks
5
+
6
+ has_many :likes
7
+ has_many :movies, through: :likes
8
+
9
+
10
+ # _test_movies is here because SuperModel doesn't handle has_many queries
11
+ # it simulates the database. see comments in each method to see a regular AR implementation
12
+ def _test_movies
13
+ @_test_movies ||= []
14
+ end
15
+
16
+ def likes?(movie)
17
+ # likes.where(movie_id: movie.id).exists?
18
+ _test_movies.any? { |it| it.movie_id == movie.id }
19
+ end
20
+
21
+ def like!(movie)
22
+ # movies << movie unless likes?(movie)
23
+ _test_movies << Like.create(user_id: self.id, movie_id: movie.id) unless likes?(movie)
24
+ end
25
+
26
+ def unlike!(movie)
27
+ # likes.where(movie_id: movie.id, user_id: self.id).destroy_all
28
+ _test_movies.delete_if { |it| it.destroy if it.movie_id == movie.id }
29
+ end
30
+
31
+ include Neoid::Node
32
+
33
+ def to_neo
34
+ neo_properties_to_hash(%w( name slug ))
35
+ end
36
+ end
37
+
38
+ class Movie < SuperModel::Base
39
+ include ActiveModel::Validations::Callbacks
40
+
41
+ has_many :likes
42
+ has_many :users, through: :likes
43
+
44
+ include Neoid::Node
45
+
46
+ def to_neo
47
+ neo_properties_to_hash(%w( name slug year ))
48
+ end
49
+ end
50
+
51
+ class Like < SuperModel::Base
52
+ include ActiveModel::Validations::Callbacks
53
+
54
+ belongs_to :user
55
+ belongs_to :movie
56
+
57
+ include Neoid::Relationship
58
+
59
+ neoidable start_node: :user, end_node: :movie, type: :likes
60
+
61
+ def to_neo
62
+ neo_properties_to_hash(%w( rate ))
63
+ end
64
+ end
65
+
66
+ require 'spec_helper'
67
+ require 'fileutils'
68
+
69
+ describe Neoid::ModelAdditions do
70
+ before(:each) do
71
+ [ User, Movie ].each { |klass|
72
+ klass.instance_variable_set(:@_neo_subref_node, nil)
73
+ }
74
+ Neoid.ref_node = nil
75
+ end
76
+
77
+ context "nodes" do
78
+ context "create graph nodes" do
79
+ it "should call neo_create on a neo_node for user" do
80
+ user = User.create(name: "Elad Ossadon")
81
+
82
+ user.neo_find_by_id.should be_nil
83
+
84
+ user.should_receive(:neo_create)
85
+ user.neo_node
86
+ end
87
+
88
+ it "should create a neo_node for user" do
89
+ user = User.create(name: "Elad Ossadon", slug: "elado")
90
+
91
+ user.neo_node.should_not be_nil
92
+
93
+ user.neo_node.ar_id.should == user.id
94
+ user.neo_node.name.should == user.name
95
+ user.neo_node.slug.should == user.slug
96
+ end
97
+
98
+ it "should create a neo_node for movie" do
99
+ movie = Movie.create(name: "Memento", slug: "memento-1999", year: 1999)
100
+
101
+ movie.neo_node.should_not be_nil
102
+
103
+ movie.neo_node.ar_id.should == movie.id
104
+ movie.neo_node.name.should == movie.name
105
+ movie.neo_node.year.should == movie.year
106
+ end
107
+ end
108
+
109
+ context "find by id" do
110
+ it "should find a neo_node for user" do
111
+ user = User.create(name: "Elad Ossadon", slug: "elado")
112
+
113
+ user.neo_find_by_id.should be_nil
114
+ user.neo_node.should_not be_nil
115
+ user.neo_find_by_id.should_not be_nil
116
+ end
117
+ end
118
+ end
119
+
120
+ context "relationships" do
121
+ let(:user) { User.create(name: "Elad Ossadon", slug: "elado") }
122
+ let(:movie) { Movie.create(name: "Memento", slug: "memento-1999", year: 1999) }
123
+
124
+ it "should create a relationship on neo4j" do
125
+ user.like! movie
126
+ like = user.likes.first
127
+
128
+ like.neo_find_by_id.should_not be_nil
129
+
130
+ like.neo_relationship.should_not be_nil
131
+
132
+ like.neo_relationship.start_node.should == user.neo_node
133
+ like.neo_relationship.end_node.should == movie.neo_node
134
+ like.neo_relationship.rel_type.should == 'likes'
135
+ end
136
+
137
+ it "should delete a relationship on deleting a record" do
138
+ user.like! movie
139
+ like = user.likes.first
140
+
141
+ relationship_neo_id = like.neo_relationship.neo_id
142
+
143
+ Neography::Relationship.load(relationship_neo_id).should_not be_nil
144
+
145
+ user.unlike! movie
146
+
147
+ Neography::Relationship.load(relationship_neo_id).should be_nil
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Neoid do
4
+
5
+ end
@@ -0,0 +1,28 @@
1
+ require 'neoid'
2
+ require 'supermodel'
3
+ require 'neography'
4
+ require 'rest-client'
5
+
6
+ uri = URI.parse(ENV["NEO4J_URL"] || "http://localhost:7574")
7
+ $neo = Neography::Rest.new(uri.to_s)
8
+
9
+ Neography::Config.tap do |c|
10
+ c.server = uri.host
11
+ c.port = uri.port
12
+
13
+ if uri.user && uri.password
14
+ c.authentication = 'basic'
15
+ c.username = uri.user
16
+ c.password = uri.password
17
+ end
18
+ end
19
+
20
+ Neoid.db = $neo
21
+
22
+ RSpec.configure do |config|
23
+ config.mock_with :rspec
24
+
25
+ config.before(:all) do
26
+ RestClient.delete "#{uri}/cleandb/secret-key"
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: neoid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Elad Ossadon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70350248004300 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70350248004300
25
+ - !ruby/object:Gem::Dependency
26
+ name: rest-client
27
+ requirement: &70350248003700 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70350248003700
36
+ - !ruby/object:Gem::Dependency
37
+ name: neography
38
+ requirement: &70350248003040 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70350248003040
47
+ - !ruby/object:Gem::Dependency
48
+ name: supermodel
49
+ requirement: &70350248002080 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70350248002080
58
+ description: Extend Ruby on Rails ActiveRecord with Neo4j nodes. Keep RDBMS and utilize
59
+ the power of Neo4j queries
60
+ email:
61
+ - elad@ossadon.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - .rspec
68
+ - CHANGELOG.md
69
+ - Gemfile
70
+ - LICENSE
71
+ - README.md
72
+ - Rakefile
73
+ - lib/neoid.rb
74
+ - lib/neoid/model_additions.rb
75
+ - lib/neoid/model_config.rb
76
+ - lib/neoid/node.rb
77
+ - lib/neoid/relationship.rb
78
+ - lib/neoid/version.rb
79
+ - neoid.gemspec
80
+ - spec/neoid/model_additions_spec.rb
81
+ - spec/neoid_spec.rb
82
+ - spec/spec_helper.rb
83
+ homepage: ''
84
+ licenses: []
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ segments:
96
+ - 0
97
+ hash: -1013384318664591928
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ segments:
105
+ - 0
106
+ hash: -1013384318664591928
107
+ requirements: []
108
+ rubyforge_project: neoid
109
+ rubygems_version: 1.8.10
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: Neo4j for ActiveRecord
113
+ test_files:
114
+ - spec/neoid/model_additions_spec.rb
115
+ - spec/neoid_spec.rb
116
+ - spec/spec_helper.rb