neoon 0.0.3 → 0.0.4
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -2
- data/README.md +79 -20
- data/Rakefile +2 -0
- data/lib/faraday/neoon/raise_error.rb +26 -0
- data/lib/neoon/client/connection.rb +1 -1
- data/lib/neoon/client/request.rb +0 -2
- data/lib/neoon/cypher/handler.rb +36 -0
- data/lib/neoon/cypher/instance_query.rb +50 -0
- data/lib/neoon/cypher/query.rb +22 -0
- data/lib/neoon/cypher/schema/constraints.rb +19 -0
- data/lib/neoon/cypher/schema/indexes.rb +25 -0
- data/lib/neoon/error.rb +71 -0
- data/lib/neoon/model/node.rb +44 -0
- data/lib/neoon/model/schema.rb +39 -17
- data/lib/neoon/node.rb +19 -4
- data/lib/neoon/railtie.rb +5 -4
- data/lib/neoon/tasks/database.rake +15 -0
- data/lib/neoon/tasks/server.rake +35 -0
- data/lib/neoon/version.rb +1 -1
- data/lib/neoon.rb +14 -2
- data/lib/rails/neoon.rb +2 -2
- data/neoon.gemspec +2 -1
- data/spec/app/models/topic.rb +3 -2
- data/spec/neoon/config_spec.rb +42 -0
- data/spec/neoon/model/config_spec.rb +32 -0
- data/spec/neoon/model/node_spec.rb +82 -0
- data/spec/neoon/model/schema_spec.rb +105 -0
- metadata +34 -7
- data/lib/neoon/client/indexing.rb +0 -25
- data/lib/neoon/model/service.rb +0 -69
- data/lib/neoon/railties/database.rake +0 -2
- data/spec/neoon_spec.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1ff7ac30436f19194698ede55cfe68529918122
|
4
|
+
data.tar.gz: 3b62de2caf2e48745e2f67d9705490b5cfe43e48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5baa0e81c289446fee6744879834a45a878b976d265c10c871b0b666bdcc9c69f581108a665c1959533e0f73ae9241a2247da2d822043ac5ff8d084e708b0ed
|
7
|
+
data.tar.gz: dcb1ea83f6b94370962a93f9a829833f2a1e1f6f882e1ea4458b530f56f8776b078644433bce4090b99b56b683743fae9e444ad645db6234a0be490497ee1026
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
A simple Ruby wrapper for Neo4j with focus on Cypher and the features of Neo4j 2.0
|
6
6
|
|
7
|
-
|
7
|
+
---
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -22,6 +22,22 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
+
You can easily install Neo4j in your application path:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
bundle exec rake neo4j:server:install
|
29
|
+
```
|
30
|
+
|
31
|
+
Then start(stop/restart) the Neo4j server by:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
bundle exec rake neo4j:server:start
|
35
|
+
|
36
|
+
bundle exec rake neo4j:server:stop
|
37
|
+
|
38
|
+
bundle exec rake neo4j:server:restart
|
39
|
+
```
|
40
|
+
|
25
41
|
First you have to initialize a client:
|
26
42
|
|
27
43
|
```ruby
|
@@ -34,17 +50,17 @@ Set configuration:
|
|
34
50
|
|
35
51
|
```ruby
|
36
52
|
Neoon.configure do |config|
|
37
|
-
config.preload_models = true #
|
53
|
+
config.preload_models = true # This will load your models — helps updating the indexed nodes at the (Rails) boot (default: false)
|
38
54
|
end
|
39
55
|
```
|
40
56
|
|
41
57
|
To query using Cypher:
|
42
58
|
|
43
59
|
```ruby
|
44
|
-
$neo.q(
|
60
|
+
$neo.q('START node=node(*) RETURN node')
|
45
61
|
```
|
46
62
|
|
47
|
-
|
63
|
+
With ActiveRecord models, initialize Neoon like so (with example of using properties/index):
|
48
64
|
|
49
65
|
```ruby
|
50
66
|
class Topic < ActiveRecord::Base
|
@@ -52,45 +68,88 @@ class Topic < ActiveRecord::Base
|
|
52
68
|
include Neoon::Node
|
53
69
|
|
54
70
|
neoon do |c|
|
55
|
-
c.property :name
|
56
|
-
c.property :slug, :index =>
|
71
|
+
c.property :name, :index => true
|
72
|
+
c.property :slug, :index => :unique do
|
57
73
|
"#{self.id}-#{self.name.underscore}"
|
58
74
|
end
|
75
|
+
c.property :created_at
|
59
76
|
end
|
60
77
|
|
61
78
|
end
|
62
79
|
```
|
63
80
|
|
64
|
-
|
81
|
+
#### Indexing
|
65
82
|
|
66
|
-
This will be used internally to auto
|
83
|
+
This will be used internally to auto index models nodes.
|
67
84
|
|
68
85
|
```ruby
|
69
|
-
|
86
|
+
Topic.neo_index_list #=> [:name, :slug]
|
87
|
+
|
88
|
+
#
|
89
|
+
# Sync the indexed nodes as described in each model config. It returns the indexed fields.
|
90
|
+
# Remember, this will be called on each model on the boot if preload_models set to true.
|
91
|
+
Topic.neo_schema_update #=> [:name, :slug]
|
92
|
+
```
|
70
93
|
|
71
|
-
|
94
|
+
---
|
72
95
|
|
73
|
-
Neoon
|
96
|
+
### Neoon::Cypher::Query
|
74
97
|
|
75
|
-
|
98
|
+
`Neoon::Cypher::InstanceQuery` should be initialized with an Class name or `label`. You can use `Neoon::Cypher::Query` to manually create indexes, constraints, etc.
|
76
99
|
|
77
|
-
|
100
|
+
```ruby
|
101
|
+
l = Neoon::Cypher::Query.new('Person') #=> #<Neoon::Cypher::Query:0x007fe8926d2068 @label="Person">
|
78
102
|
|
79
|
-
|
103
|
+
l.create_index(:name).run
|
104
|
+
# l.drop_index(:name).run
|
80
105
|
|
81
|
-
|
106
|
+
l.create_constraint(:username).run
|
107
|
+
# l.drop_constraint(:username).run
|
82
108
|
|
83
|
-
|
84
|
-
|
85
|
-
|
109
|
+
l.list_indexes #=> { :name => true, :username => "UNIQUENESS" }
|
110
|
+
```
|
111
|
+
|
112
|
+
---
|
113
|
+
|
114
|
+
### Neoon::Cypher::InstanceQuery
|
115
|
+
|
116
|
+
`Neoon::Cypher::InstanceQuery` should be initialized with an object that respond to `id`, `class.name` as it will represent the `label` and `neo_node_properties` as it will represent the `args`.
|
117
|
+
|
118
|
+
You can use `Neoon::Cypher::InstanceQuery` to manually create operations on nodes related to an object, etc.
|
119
|
+
|
120
|
+
Use it with Struct:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
Customer = Struct.new(:id, :neo_node_properties)
|
124
|
+
cus = Customer.new(50, {:name => 'Julie', :address => 'PS'}) #=> #<Neoon::Cypher::InstanceQuery:0x007feb35953d00 @id=50, @label="Customer", @args={:name=>"Julie", :address=>"PS"}>
|
125
|
+
|
126
|
+
c = Neoon::Cypher::InstanceQuery.new(cus)
|
127
|
+
|
128
|
+
c.find_node.run #=> Return node in Neo4j if already saved
|
129
|
+
c.create_node.run #=> Create object node / or update it
|
130
|
+
c.delete_node.run #=> Remove object node
|
131
|
+
```
|
132
|
+
|
133
|
+
Note that the key of finding nodes in Neo4j is `id` as saved in Neo4j with key `db_id`.
|
134
|
+
|
135
|
+
Another example on the model we defined above:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
t = Neoon::Cypher::InstanceQuery.new(Topic.first) #=> #<Neoon::Cypher::InstanceQuery:0x007fe894410b98 @id=1, @label="Topic", @args={...}>
|
139
|
+
|
140
|
+
t.find_node.run #=> Returns node in Neo4j if already saved
|
141
|
+
t.create_node.run #=> Create object node / or update it
|
142
|
+
t.delete_node.run #=> Remove object node
|
86
143
|
```
|
87
144
|
|
145
|
+
---
|
146
|
+
|
88
147
|
**The gem is still at heavy development. More to come!**
|
89
148
|
|
90
149
|
## TODO
|
91
150
|
|
92
|
-
1.
|
93
|
-
2.
|
151
|
+
1. Add inline docs
|
152
|
+
2. ADD TESTS!!!
|
94
153
|
|
95
154
|
## Contributing
|
96
155
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Faraday
|
2
|
+
module Neoon
|
3
|
+
class RaiseError < Faraday::Response::Middleware
|
4
|
+
|
5
|
+
def call(env)
|
6
|
+
@app.call(env).on_complete do |env|
|
7
|
+
case env[:status]
|
8
|
+
when (400..499)
|
9
|
+
body = JSON.parse(env[:body])
|
10
|
+
raise "Neoon::Error::#{body["cause"]["exception"]}".constantize, "#{{
|
11
|
+
:message => body["message"],
|
12
|
+
:exception => body["exception"],
|
13
|
+
:cause => {
|
14
|
+
:message => body["cause"]["message"],
|
15
|
+
:exception => body["cause"]["exception"]
|
16
|
+
}
|
17
|
+
} if env[:body]}"
|
18
|
+
when (500..599)
|
19
|
+
raise 'Something went error with Neo4j server.'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -12,7 +12,6 @@ module Neoon
|
|
12
12
|
end
|
13
13
|
|
14
14
|
include Request
|
15
|
-
include Indexing
|
16
15
|
|
17
16
|
private
|
18
17
|
|
@@ -36,6 +35,7 @@ module Neoon
|
|
36
35
|
builder.use FaradayMiddleware::EncodeJson
|
37
36
|
builder.use FaradayMiddleware::Mashify
|
38
37
|
builder.use FaradayMiddleware::ParseJson, :content_type => /\bjson$/
|
38
|
+
builder.use Faraday::Neoon::RaiseError
|
39
39
|
|
40
40
|
builder.adapter Faraday.default_adapter
|
41
41
|
end
|
data/lib/neoon/client/request.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Neoon
|
2
|
+
module Cypher
|
3
|
+
class Handler
|
4
|
+
|
5
|
+
attr_reader :query, :args
|
6
|
+
|
7
|
+
def initialize(hash)
|
8
|
+
@query = hash[:query]
|
9
|
+
@args = hash[:args]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_cypher
|
13
|
+
if query.is_a?(Array)
|
14
|
+
query.map { |q| q.gsub(/\s+/, ' ').strip }
|
15
|
+
else
|
16
|
+
query.gsub(/\s+/, ' ').strip
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
if query.is_a?(Array)
|
22
|
+
query.each { |q| make_cypher_request(q, args) }
|
23
|
+
else
|
24
|
+
make_cypher_request(query, args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def make_cypher_request(query, args = nil)
|
31
|
+
Neoon.db.cypher(query, args)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Neoon
|
2
|
+
module Cypher
|
3
|
+
class InstanceQuery
|
4
|
+
|
5
|
+
attr_reader :id, :label, :args
|
6
|
+
|
7
|
+
def initialize(object)
|
8
|
+
@id = object.id
|
9
|
+
@label = object.class.name
|
10
|
+
@args = object.neo_node_properties
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_node
|
14
|
+
cypher_query = <<-CYPHER
|
15
|
+
MATCH node:#{label} WHERE node._id = #{id}
|
16
|
+
RETURN node
|
17
|
+
CYPHER
|
18
|
+
cypherable(:query => cypher_query)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_node
|
22
|
+
cypher_query = <<-CYPHER
|
23
|
+
MERGE (node:#{label} { _id: #{id} })
|
24
|
+
ON CREATE node SET node = {props}
|
25
|
+
ON MATCH node SET node = {props}
|
26
|
+
RETURN node
|
27
|
+
CYPHER
|
28
|
+
cypherable(:query => cypher_query, :args => {:props => args})
|
29
|
+
end
|
30
|
+
alias_method :update_node, :create_node
|
31
|
+
alias_method :save_node, :create_node
|
32
|
+
|
33
|
+
def delete_node
|
34
|
+
cypher_query = <<-CYPHER
|
35
|
+
MATCH node:#{label}
|
36
|
+
WHERE node._id = #{id}
|
37
|
+
DELETE node
|
38
|
+
CYPHER
|
39
|
+
cypherable(:query => cypher_query)
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def cypherable(query)
|
45
|
+
Cypher::Handler.new(query)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Neoon
|
2
|
+
module Cypher
|
3
|
+
class Query
|
4
|
+
|
5
|
+
attr_reader :label
|
6
|
+
|
7
|
+
def initialize(klass)
|
8
|
+
@label = klass.is_a?(String) ? klass : klass.name
|
9
|
+
end
|
10
|
+
|
11
|
+
include Neoon::Cypher::Schema::Indexes
|
12
|
+
include Neoon::Cypher::Schema::Constraints
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def cypherable(query)
|
17
|
+
Cypher::Handler.new(query)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Neoon
|
2
|
+
module Cypher
|
3
|
+
module Schema
|
4
|
+
module Constraints
|
5
|
+
|
6
|
+
def create_constraint(key)
|
7
|
+
cypher_query = "CREATE CONSTRAINT ON (node:#{label}) ASSERT node.#{key.to_s.downcase} IS UNIQUE"
|
8
|
+
cypherable(:query => cypher_query)
|
9
|
+
end
|
10
|
+
|
11
|
+
def drop_constraint(key)
|
12
|
+
cypher_query = "DROP CONSTRAINT ON (node:#{label}) ASSERT node.#{key.to_s.downcase} IS UNIQUE"
|
13
|
+
cypherable(:query => cypher_query)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Neoon
|
2
|
+
module Cypher
|
3
|
+
module Schema
|
4
|
+
module Indexes
|
5
|
+
|
6
|
+
def list_indexes
|
7
|
+
# The only none Cypher query
|
8
|
+
idx_keys = Neoon.db.get("/schema/index/#{label}") + Neoon.db.get("/schema/constraint/#{label}")
|
9
|
+
idx_keys.reduce({}) { |k, v| k[v.send('property-keys').first.to_sym] = v.type || true; k }
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_index(key)
|
13
|
+
cypher_query = "CREATE INDEX ON :#{label}(#{key.to_s.downcase})"
|
14
|
+
cypherable(:query => cypher_query)
|
15
|
+
end
|
16
|
+
|
17
|
+
def drop_index(key)
|
18
|
+
cypher_query = "DROP INDEX ON :#{label}(#{key.to_s.downcase})"
|
19
|
+
cypherable(:query => cypher_query)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/neoon/error.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Neoon
|
2
|
+
module Error
|
3
|
+
|
4
|
+
class Exception < StandardError; end
|
5
|
+
class IndexException < Exception; end
|
6
|
+
class SchemaException < Exception; end
|
7
|
+
class CypherException < Exception; end
|
8
|
+
|
9
|
+
# org.neo4j.kernel.api.exceptions
|
10
|
+
class BeginTransactionFailureException < Exception; end
|
11
|
+
class ConstraintCreationException < Exception; end
|
12
|
+
class EntityNotFoundException < Exception; end
|
13
|
+
class KernelException < Exception; end
|
14
|
+
class LabelNotFoundKernelException < Exception; end
|
15
|
+
class PropertyKeyIdNotFoundKernelException < Exception; end
|
16
|
+
class PropertyKeyNotFoundException < Exception; end
|
17
|
+
class PropertyNotFoundException < Exception; end
|
18
|
+
class RelationshipTypeIdNotFoundKernelException < Exception; end
|
19
|
+
class TransactionalException < Exception; end
|
20
|
+
class TransactionFailureException < Exception; end
|
21
|
+
|
22
|
+
# org.neo4j.kernel.api.exceptions.index
|
23
|
+
class ExceptionDuringFlipKernelException < IndexException; end
|
24
|
+
class FlipFailedKernelException < IndexException; end
|
25
|
+
class IndexNotFoundKernelException < IndexException; end
|
26
|
+
class IndexPopulationFailedKernelException < IndexException; end
|
27
|
+
class IndexProxyAlreadyClosedKernelException < IndexException; end
|
28
|
+
|
29
|
+
# org.neo4j.kernel.api.exceptions.schema
|
30
|
+
class AddIndexFailureException < SchemaException; end
|
31
|
+
class AlreadyConstrainedException < SchemaException; end
|
32
|
+
class AlreadyIndexedException < SchemaException; end
|
33
|
+
class CreateConstraintFailureException < SchemaException; end
|
34
|
+
class DropConstraintFailureException < SchemaException; end
|
35
|
+
class DropIndexFailureException < SchemaException; end
|
36
|
+
class IllegalTokenNameException < SchemaException; end
|
37
|
+
class IndexBelongsToConstraintException < SchemaException; end
|
38
|
+
class IndexBrokenKernelException < SchemaException; end
|
39
|
+
class MalformedSchemaRuleException < SchemaException; end
|
40
|
+
class NoSuchConstraintException < SchemaException; end
|
41
|
+
class NoSuchIndexException < SchemaException; end
|
42
|
+
class SchemaAndDataModificationInSameTransactionException < SchemaException; end
|
43
|
+
class SchemaKernelException < SchemaException; end
|
44
|
+
class SchemaRuleNotFoundException < SchemaException; end
|
45
|
+
class TooManyLabelsException < SchemaException; end
|
46
|
+
|
47
|
+
# org.neo4j.cypher.CypherException
|
48
|
+
class CypherExecutionException < CypherException; end
|
49
|
+
class UniquePathNotUniqueException < CypherException; end
|
50
|
+
# class EntityNotFoundException < CypherException; end
|
51
|
+
class CypherTypeException < CypherException; end
|
52
|
+
class IterableRequiredException < CypherException; end
|
53
|
+
class ParameterNotFoundException < CypherException; end
|
54
|
+
class ParameterWrongTypeException < CypherException; end
|
55
|
+
class PatternException < CypherException; end
|
56
|
+
class InternalException < CypherException; end
|
57
|
+
class MissingIndexException < CypherException; end
|
58
|
+
class MissingConstraintException < CypherException; end
|
59
|
+
class InvalidAggregateException < CypherException; end
|
60
|
+
class NodeStillHasRelationshipsException < CypherException; end
|
61
|
+
class ProfilerStatisticsNotReadyException < CypherException; end
|
62
|
+
class UnknownLabelException < CypherException; end
|
63
|
+
class IndexHintException < CypherException; end
|
64
|
+
class LabelScanHintException < CypherException; end
|
65
|
+
class UnableToPickStartPointException < CypherException; end
|
66
|
+
class InvalidSemanticsException < CypherException; end
|
67
|
+
class OutOfBoundsException < CypherException; end
|
68
|
+
class MergeConstraintConflictException < CypherException; end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Neoon
|
2
|
+
module Model
|
3
|
+
module Node
|
4
|
+
|
5
|
+
def neo_node
|
6
|
+
_cypher_query.find_node.run.data.first.first.data
|
7
|
+
end
|
8
|
+
|
9
|
+
def neo_create
|
10
|
+
_cypher_query.create_node.run.data
|
11
|
+
end
|
12
|
+
alias_method :neo_save, :neo_create
|
13
|
+
alias_method :neo_update, :neo_create
|
14
|
+
|
15
|
+
def neo_destroy
|
16
|
+
_cypher_query.delete_node.run.data
|
17
|
+
end
|
18
|
+
|
19
|
+
def neo_node_properties
|
20
|
+
_neo_node.merge({ :_id => self.id })
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def _cypher_query
|
26
|
+
Neoon::Cypher::InstanceQuery.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def _neo_node
|
30
|
+
return {} unless self.class.neo_model_config.properties
|
31
|
+
hash = self.class.neo_model_config.properties.inject({}) do |all, (field, block)|
|
32
|
+
all[field] = if block[:block]
|
33
|
+
instance_eval(&block[:block])
|
34
|
+
else
|
35
|
+
self.send(field) rescue (raise "No field #{field} for #{self.class.name}")
|
36
|
+
end
|
37
|
+
all
|
38
|
+
end
|
39
|
+
hash.reject { |k, v| v.nil? }
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/neoon/model/schema.rb
CHANGED
@@ -3,37 +3,59 @@ module Neoon
|
|
3
3
|
module Schema
|
4
4
|
|
5
5
|
def neo_index_list
|
6
|
-
|
6
|
+
_cypher_query.list_indexes
|
7
7
|
end
|
8
8
|
|
9
|
-
def neo_index_create
|
10
|
-
|
9
|
+
def neo_index_create key
|
10
|
+
if neo_schema_index_keys.select { |k,v| v == 'UNIQUENESS' }.include? key
|
11
|
+
_cypher_query.create_constraint(key).run
|
12
|
+
else
|
13
|
+
_cypher_query.create_index(key).run
|
14
|
+
end
|
15
|
+
true
|
11
16
|
end
|
12
17
|
|
13
|
-
def neo_index_drop
|
14
|
-
|
18
|
+
def neo_index_drop key
|
19
|
+
if neo_index_list[key] == 'UNIQUENESS'
|
20
|
+
_cypher_query.drop_constraint(key).run
|
21
|
+
else
|
22
|
+
_cypher_query.drop_index(key).run
|
23
|
+
end
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def neo_index_drop_all
|
28
|
+
neo_index_list.each { |k, _| neo_index_drop(k) }
|
15
29
|
end
|
16
30
|
|
17
31
|
def neo_index_update
|
18
|
-
cl = neo_index_list
|
19
|
-
|
20
|
-
return
|
32
|
+
cl, ck = neo_index_list.to_a, neo_schema_index_keys.to_a
|
33
|
+
return cl if cl == ck
|
34
|
+
return neo_index_drop_all if ck.empty?
|
21
35
|
|
22
|
-
|
23
|
-
|
24
|
-
neo_index_list
|
36
|
+
(cl - ck).each{ |k| neo_index_drop(k.first) } unless (cl - ck).empty?
|
37
|
+
(ck - cl).each{ |k| neo_index_create(k.first) } unless (ck - cl).empty?
|
25
38
|
end
|
26
39
|
|
27
|
-
|
40
|
+
def neo_schema_update
|
41
|
+
neo_index_update
|
42
|
+
neo_index_list
|
43
|
+
end
|
28
44
|
|
29
|
-
def
|
30
|
-
|
45
|
+
def neo_schema_index_keys
|
46
|
+
neo_model_config.properties.inject({}) do |all, (k, v)|
|
47
|
+
all[k] = true if v[:index]
|
48
|
+
all[k] = 'UNIQUENESS' if v[:index] == :unique
|
49
|
+
all
|
50
|
+
end
|
31
51
|
end
|
32
52
|
|
33
|
-
|
34
|
-
|
53
|
+
protected
|
54
|
+
|
55
|
+
def _cypher_query
|
56
|
+
Neoon::Cypher::Query.new(self)
|
35
57
|
end
|
36
58
|
|
37
59
|
end
|
38
60
|
end
|
39
|
-
end
|
61
|
+
end
|
data/lib/neoon/node.rb
CHANGED
@@ -2,15 +2,30 @@ module Neoon
|
|
2
2
|
module Node
|
3
3
|
|
4
4
|
module ClassMethods
|
5
|
-
|
5
|
+
attr_reader :neo_model_config
|
6
|
+
|
7
|
+
def neo_model_config
|
8
|
+
@neo_model_config ||= Model::Config.new(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def neoon(opts = {})
|
12
|
+
yield(neo_model_config) if block_given?
|
6
13
|
|
7
|
-
|
14
|
+
opts.each do |key, value|
|
15
|
+
raise "No such option #{key} for #{self.name} model" unless neo_model_config.respond_to?("#{key}=")
|
16
|
+
neo_model_config.send("#{key}=", value)
|
17
|
+
end
|
18
|
+
end
|
8
19
|
end
|
9
20
|
|
10
21
|
def self.included(receiver)
|
11
|
-
receiver.send :include, Model::
|
22
|
+
receiver.send :include, Model::Node
|
23
|
+
receiver.extend Model::Schema
|
12
24
|
receiver.extend ClassMethods
|
13
|
-
|
25
|
+
|
26
|
+
receiver.after_save :neo_save
|
27
|
+
receiver.after_destroy :neo_destroy
|
28
|
+
|
14
29
|
Neoon.config.models << receiver
|
15
30
|
end
|
16
31
|
|
data/lib/neoon/railtie.rb
CHANGED
@@ -6,16 +6,17 @@ module Rails
|
|
6
6
|
class Railtie < ::Rails::Railtie
|
7
7
|
|
8
8
|
rake_tasks do
|
9
|
-
load
|
9
|
+
load 'neoon/tasks/server.rake'
|
10
|
+
load 'neoon/tasks/database.rake'
|
10
11
|
end
|
11
12
|
|
12
|
-
initializer
|
13
|
+
initializer 'neoon.neo_schema_update' do
|
13
14
|
config.after_initialize do
|
14
|
-
::Neoon.config.models.each(&:
|
15
|
+
::Neoon.config.models.each(&:neo_schema_update)
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
|
-
initializer
|
19
|
+
initializer 'neoon.preload_models' do |app|
|
19
20
|
config.to_prepare do
|
20
21
|
Rails::Neoon.preload_models(app)
|
21
22
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
namespace :neo4j do
|
2
|
+
namespace :server do
|
3
|
+
|
4
|
+
# Taken from https://github.com/maxdemarzi/neography/blob/master/lib/neography/tasks.rb (Unix)
|
5
|
+
desc 'Install Neo4j server'
|
6
|
+
task :install, :edition, :version do |t, args|
|
7
|
+
args.with_defaults(:edition => 'community', :version => '2.0.0-M05')
|
8
|
+
puts "Installing Neo4j-#{args[:edition]}-#{args[:version]}..."
|
9
|
+
%x[curl -O http://dist.neo4j.org/neo4j-#{args[:edition]}-#{args[:version]}-unix.tar.gz]
|
10
|
+
%x[tar -xvzf neo4j-#{args[:edition]}-#{args[:version]}-unix.tar.gz]
|
11
|
+
%x[mv neo4j-#{args[:edition]}-#{args[:version]} neo4j]
|
12
|
+
%x[rm neo4j-#{args[:edition]}-#{args[:version]}-unix.tar.gz]
|
13
|
+
puts 'Neo4j Installed in to neo4j directory.'
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Start Neo4j server'
|
17
|
+
task :start do
|
18
|
+
puts 'Starting Neo4j...'
|
19
|
+
%x[neo4j/bin/neo4j start]
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Stop Neo4j server'
|
23
|
+
task :stop do
|
24
|
+
puts 'Stopping Neo4j...'
|
25
|
+
%x[neo4j/bin/neo4j stop]
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Restart Neo4j server'
|
29
|
+
task :restart do
|
30
|
+
puts 'Restarting Neo4j...'
|
31
|
+
%x[neo4j/bin/neo4j restart]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/neoon/version.rb
CHANGED
data/lib/neoon.rb
CHANGED
@@ -1,16 +1,28 @@
|
|
1
1
|
require 'multi_json'
|
2
|
+
require 'faraday'
|
3
|
+
require 'faraday_middleware'
|
4
|
+
require 'faraday/neoon/raise_error'
|
2
5
|
|
3
6
|
require 'neoon/version'
|
4
7
|
require 'neoon/config'
|
8
|
+
require 'neoon/error'
|
9
|
+
|
5
10
|
require 'neoon/client/request'
|
6
|
-
require 'neoon/client/indexing'
|
7
11
|
require 'neoon/client/connection'
|
8
12
|
|
13
|
+
require 'neoon/cypher/handler'
|
14
|
+
require 'neoon/cypher/schema/indexes'
|
15
|
+
require 'neoon/cypher/schema/constraints'
|
16
|
+
require 'neoon/cypher/query'
|
17
|
+
require 'neoon/cypher/instance_query'
|
18
|
+
|
9
19
|
require 'neoon/model/config'
|
10
20
|
require 'neoon/model/schema'
|
11
|
-
require 'neoon/model/
|
21
|
+
require 'neoon/model/node'
|
22
|
+
|
12
23
|
require 'neoon/node'
|
13
24
|
|
25
|
+
|
14
26
|
if defined?(Rails)
|
15
27
|
require 'neoon/railtie'
|
16
28
|
end
|
data/lib/rails/neoon.rb
CHANGED
@@ -6,13 +6,13 @@ module Rails
|
|
6
6
|
models.each do |path|
|
7
7
|
files = Dir.glob("#{path}/**/*.rb")
|
8
8
|
files.sort.each do |file|
|
9
|
-
load_model(file.gsub("#{path}/" ,
|
9
|
+
load_model(file.gsub("#{path}/" , '').gsub('.rb', ''))
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
def preload_models(app)
|
15
|
-
models = app.config.paths[
|
15
|
+
models = app.config.paths['app/models']
|
16
16
|
load_models(models) if ::Neoon.config.preload_models
|
17
17
|
end
|
18
18
|
|
data/neoon.gemspec
CHANGED
@@ -19,10 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.required_ruby_version =
|
22
|
+
spec.required_ruby_version = '>= 1.9'
|
23
23
|
|
24
24
|
spec.add_runtime_dependency 'faraday'
|
25
25
|
spec.add_runtime_dependency 'faraday_middleware'
|
26
|
+
spec.add_runtime_dependency 'activesupport'
|
26
27
|
spec.add_runtime_dependency 'multi_json'
|
27
28
|
spec.add_runtime_dependency 'hashie'
|
28
29
|
|
data/spec/app/models/topic.rb
CHANGED
@@ -2,10 +2,11 @@ class Topic < ActiveRecord::Base
|
|
2
2
|
include Neoon::Node
|
3
3
|
|
4
4
|
neoon do |c|
|
5
|
-
c.property :name
|
6
|
-
c.property :slug, :index =>
|
5
|
+
c.property :name, :index => true
|
6
|
+
c.property :slug, :index => :unique do
|
7
7
|
"#{self.id}-#{self.name.underscore}"
|
8
8
|
end
|
9
|
+
c.property :created_at
|
9
10
|
end
|
10
11
|
|
11
12
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Neoon::Config do
|
4
|
+
|
5
|
+
context 'no block given' do
|
6
|
+
it 'returns the config singleton' do
|
7
|
+
expect(Neoon.config.class).to eq Neoon::Config
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns config.preload_models false' do
|
11
|
+
expect(Neoon.config.preload_models).to be_false
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should have no model' do
|
15
|
+
expect(Neoon.config.models).to be_empty
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with block given' do
|
20
|
+
before do
|
21
|
+
Neoon.configure do |c|
|
22
|
+
c.preload_models = true
|
23
|
+
end
|
24
|
+
require 'app/models/topic' # fake loading a model
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
Neoon.configure do |c|
|
29
|
+
c.preload_models = false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns config.preload_models true' do
|
34
|
+
expect(Neoon.config.preload_models).to be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should have models' do
|
38
|
+
expect(Neoon.config.models).not_to be_empty
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Neoon::Model::Config do
|
4
|
+
|
5
|
+
before do
|
6
|
+
require 'app/models/topic'
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns the model config singleton' do
|
10
|
+
expect(Topic.neo_model_config.class).to eq Neoon::Model::Config
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'properties' do
|
14
|
+
it 'stores them' do
|
15
|
+
expect(Topic.neo_model_config.properties).not_to be_nil
|
16
|
+
expect(Topic.neo_model_config.properties.keys).to eql [ :name, :slug, :created_at ]
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has block' do
|
20
|
+
expect(Topic.neo_model_config.properties[:slug][:block]).to be_a(Proc)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has index' do
|
24
|
+
expect(Topic.neo_model_config.properties[:name][:index]).to be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'has unique index' do
|
28
|
+
expect(Topic.neo_model_config.properties[:slug][:index]).to eql :unique
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Neoon::Model::Node do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
require 'app/models/topic'
|
7
|
+
Topic.destroy_all
|
8
|
+
end
|
9
|
+
|
10
|
+
context '' do
|
11
|
+
it 'responds to neoon' do
|
12
|
+
expect(Topic).to respond_to(:neoon)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'responds to neo_model_config' do
|
16
|
+
expect(Topic).to respond_to(:neo_model_config)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'save' do
|
21
|
+
before do
|
22
|
+
@t = Topic.where(:name => 'Apple').first_or_create
|
23
|
+
end
|
24
|
+
|
25
|
+
after :all do
|
26
|
+
Topic.destroy_all
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'responds to neo_save' do
|
30
|
+
@t.respond_to?(:neo_save).should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'sets neo_node_properties' do
|
34
|
+
@t.neo_node_properties[:_id].should == @t.id
|
35
|
+
@t.neo_node_properties[:name].should == @t.name
|
36
|
+
@t.neo_node_properties[:slug].should == "#{@t.id}-#{@t.name.underscore}"
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'saves to neo' do
|
40
|
+
@t.neo_node._id.should == @t.id
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'updates to neo after assigning attr and save OR update_attribute()' do
|
44
|
+
@t.name = 'MacBook'
|
45
|
+
@t.save
|
46
|
+
@t.neo_node.name.should == 'MacBook'
|
47
|
+
@t.neo_node._id.should == @t.id
|
48
|
+
|
49
|
+
@t.update_attribute(:name, 'Technology')
|
50
|
+
@t.neo_node.name.should == 'Technology'
|
51
|
+
@t.neo_node._id.should == @t.id
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'has 1 record' do
|
55
|
+
Topic.count.should == Neoon.db.q('MATCH n:Topic return n').data.count
|
56
|
+
Topic.count.should == 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'destroy' do
|
61
|
+
before do
|
62
|
+
@t1 = Topic.where(:name => 'Amr').first_or_create
|
63
|
+
end
|
64
|
+
|
65
|
+
after :all do
|
66
|
+
Topic.destroy_all
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'responds to neo_destroy' do
|
70
|
+
@t1.respond_to?(:neo_destroy).should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'has 2 records' do
|
74
|
+
topic = Topic.new name: 'Jo'
|
75
|
+
topic.save
|
76
|
+
|
77
|
+
Topic.count.should == Neoon.db.q('MATCH n:Topic return n').data.count
|
78
|
+
Topic.count.should == 2
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Neoon::Model::Schema do
|
4
|
+
|
5
|
+
before do
|
6
|
+
require 'app/models/topic'
|
7
|
+
Topic.neo_schema_update
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'list index' do
|
11
|
+
context 'from model config' do
|
12
|
+
it 'responds to neo_schema_index_keys' do
|
13
|
+
expect(Topic).to respond_to(:neo_schema_index_keys)
|
14
|
+
end
|
15
|
+
it 'returns all indexed properties' do
|
16
|
+
expect(Topic.neo_schema_index_keys.keys).to eql [:name, :slug]
|
17
|
+
end
|
18
|
+
it 'returns indexed properties' do
|
19
|
+
expect(Topic.neo_schema_index_keys.select{|_, v| v == true}.keys).to eql [:name]
|
20
|
+
end
|
21
|
+
it 'returns unique indexed properties' do
|
22
|
+
expect(Topic.neo_schema_index_keys.select{|_, v| v == 'UNIQUENESS'}.keys).to eql [:slug]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'from Neo4j' do
|
28
|
+
it 'returns all indexed properties' do
|
29
|
+
expect(Topic.neo_index_list.keys).to eql [:name, :slug]
|
30
|
+
end
|
31
|
+
it 'returns indexed properties' do
|
32
|
+
expect(Topic.neo_index_list.select{|_, v| v == true}.keys).to eql [:name]
|
33
|
+
end
|
34
|
+
it 'returns unique indexed properties' do
|
35
|
+
expect(Topic.neo_index_list.select{|_, v| v == 'UNIQUENESS'}.keys).to eql [:slug]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'index operation' do
|
41
|
+
before :each do
|
42
|
+
begin
|
43
|
+
Topic.neo_index_drop(:name)
|
44
|
+
Topic.neo_index_drop(:slug)
|
45
|
+
rescue
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'no indexes in Neo4j' do
|
51
|
+
expect(Topic.neo_index_list.keys).to eql []
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'create index' do
|
55
|
+
it 'responds to neo_index_create' do
|
56
|
+
expect(Topic).to respond_to(:neo_index_create)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'creates indexes for properties' do
|
60
|
+
expect(Topic.neo_index_create(:name)).to be_true
|
61
|
+
expect(Topic.neo_index_list.keys).to eql [:name]
|
62
|
+
|
63
|
+
expect(Topic.neo_index_create(:slug)).to be_true
|
64
|
+
expect(Topic.neo_index_list.keys).to eql [:name, :slug]
|
65
|
+
|
66
|
+
expect { Topic.neo_index_create(:name) }.to raise_error Neoon::Error::AlreadyIndexedException
|
67
|
+
expect { Topic.neo_index_create(:slug) }.to raise_error Neoon::Error::AlreadyConstrainedException
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'drop index' do
|
73
|
+
before do
|
74
|
+
Topic.neo_index_create(:name)
|
75
|
+
Topic.neo_index_create(:slug)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'responds to neo_index_drop' do
|
79
|
+
expect(Topic).to respond_to(:neo_index_drop)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'drops indexes for properties' do
|
83
|
+
expect(Topic.neo_index_drop(:name)).to be_true
|
84
|
+
expect(Topic.neo_index_list.keys).to eql [:slug]
|
85
|
+
|
86
|
+
expect(Topic.neo_index_drop(:slug)).to be_true
|
87
|
+
expect(Topic.neo_index_list.keys).to eql []
|
88
|
+
|
89
|
+
expect { Topic.neo_index_drop(:name) }.to raise_error Neoon::Error::DropIndexFailureException
|
90
|
+
expect { Topic.neo_index_drop(:slug) }.to raise_error Neoon::Error::DropIndexFailureException
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'update index' do
|
95
|
+
before do
|
96
|
+
Topic.neo_schema_update
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'updates index as in model config' do
|
100
|
+
Topic.neo_index_list.keys.should == [:name, :slug]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neoon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amr Tamimi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: multi_json
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,22 +150,32 @@ files:
|
|
136
150
|
- LICENSE.txt
|
137
151
|
- README.md
|
138
152
|
- Rakefile
|
153
|
+
- lib/faraday/neoon/raise_error.rb
|
139
154
|
- lib/neoon.rb
|
140
155
|
- lib/neoon/client/connection.rb
|
141
|
-
- lib/neoon/client/indexing.rb
|
142
156
|
- lib/neoon/client/request.rb
|
143
157
|
- lib/neoon/config.rb
|
158
|
+
- lib/neoon/cypher/handler.rb
|
159
|
+
- lib/neoon/cypher/instance_query.rb
|
160
|
+
- lib/neoon/cypher/query.rb
|
161
|
+
- lib/neoon/cypher/schema/constraints.rb
|
162
|
+
- lib/neoon/cypher/schema/indexes.rb
|
163
|
+
- lib/neoon/error.rb
|
144
164
|
- lib/neoon/model/config.rb
|
165
|
+
- lib/neoon/model/node.rb
|
145
166
|
- lib/neoon/model/schema.rb
|
146
|
-
- lib/neoon/model/service.rb
|
147
167
|
- lib/neoon/node.rb
|
148
168
|
- lib/neoon/railtie.rb
|
149
|
-
- lib/neoon/
|
169
|
+
- lib/neoon/tasks/database.rake
|
170
|
+
- lib/neoon/tasks/server.rake
|
150
171
|
- lib/neoon/version.rb
|
151
172
|
- lib/rails/neoon.rb
|
152
173
|
- neoon.gemspec
|
153
174
|
- spec/app/models/topic.rb
|
154
|
-
- spec/
|
175
|
+
- spec/neoon/config_spec.rb
|
176
|
+
- spec/neoon/model/config_spec.rb
|
177
|
+
- spec/neoon/model/node_spec.rb
|
178
|
+
- spec/neoon/model/schema_spec.rb
|
155
179
|
- spec/spec_helper.rb
|
156
180
|
- spec/support/database.yml
|
157
181
|
- spec/support/schema.rb
|
@@ -181,7 +205,10 @@ specification_version: 4
|
|
181
205
|
summary: A simple Ruby wrapper for Neo4j with focus on Cypher
|
182
206
|
test_files:
|
183
207
|
- spec/app/models/topic.rb
|
184
|
-
- spec/
|
208
|
+
- spec/neoon/config_spec.rb
|
209
|
+
- spec/neoon/model/config_spec.rb
|
210
|
+
- spec/neoon/model/node_spec.rb
|
211
|
+
- spec/neoon/model/schema_spec.rb
|
185
212
|
- spec/spec_helper.rb
|
186
213
|
- spec/support/database.yml
|
187
214
|
- spec/support/schema.rb
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module Neoon
|
2
|
-
module Client
|
3
|
-
module Indexing
|
4
|
-
|
5
|
-
def list(label)
|
6
|
-
Neoon.db.get("/schema/index/#{label}")
|
7
|
-
.map{|f| f.send("property-keys")}.flatten.map(&:to_s).sort
|
8
|
-
end
|
9
|
-
|
10
|
-
def create(label, keys = [])
|
11
|
-
keys.each do |key|
|
12
|
-
Neoon.db.cypher("CREATE INDEX ON :#{label}(#{key.to_s.downcase})")
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def drop(label, keys = [])
|
17
|
-
keys.each do |key|
|
18
|
-
Neoon.db.cypher("DROP INDEX ON :#{label}(#{key.to_s.downcase})")
|
19
|
-
end
|
20
|
-
neo_index_list
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/neoon/model/service.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
module Neoon
|
2
|
-
module Model
|
3
|
-
module Service
|
4
|
-
|
5
|
-
module ClassMethods
|
6
|
-
attr_reader :neo_model_config
|
7
|
-
|
8
|
-
def neo_model_config
|
9
|
-
@neo_model_config ||= Neoon::Model::Config.new(self)
|
10
|
-
end
|
11
|
-
|
12
|
-
def neoon(opts = {})
|
13
|
-
yield(neo_model_config) if block_given?
|
14
|
-
|
15
|
-
opts.each do |key, value|
|
16
|
-
raise "No such option #{key} for #{self.name} model" unless neo_model_config.respond_to?("#{key}=")
|
17
|
-
neo_model_config.send("#{key}=", value)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module InstanceMethods
|
23
|
-
def neo_node_props
|
24
|
-
neo_node.merge({ :db_id => self.id })
|
25
|
-
end
|
26
|
-
|
27
|
-
def neo_save
|
28
|
-
cypher_query = <<-CYPHER
|
29
|
-
MERGE (node:#{self.class.name} { db_id: #{self.id} })
|
30
|
-
ON CREATE node SET node = {props}
|
31
|
-
ON MATCH node SET node = {props}
|
32
|
-
RETURN node
|
33
|
-
CYPHER
|
34
|
-
Neoon.db.q(cypher_query, { :props => neo_node_props })
|
35
|
-
end
|
36
|
-
|
37
|
-
def neo_destroy
|
38
|
-
cypher_query = <<-CYPHER
|
39
|
-
CYPHER
|
40
|
-
Neoon.db.q(cypher_query)
|
41
|
-
end
|
42
|
-
|
43
|
-
protected
|
44
|
-
|
45
|
-
def neo_node
|
46
|
-
return {} unless self.class.neo_model_props
|
47
|
-
hash = self.class.neo_model_props.inject({}) do |all, (field, block)|
|
48
|
-
all[field] = if block[:block]
|
49
|
-
instance_eval(&block[:block])
|
50
|
-
else
|
51
|
-
self.send(field) rescue (raise "No field #{field} for #{self.class.name}")
|
52
|
-
end
|
53
|
-
all
|
54
|
-
end
|
55
|
-
hash.reject { |k, v| v.nil? }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.included(receiver)
|
60
|
-
receiver.extend ClassMethods
|
61
|
-
receiver.extend Schema
|
62
|
-
receiver.send :include, InstanceMethods
|
63
|
-
|
64
|
-
receiver.after_save :neo_save
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/spec/neoon_spec.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Neoon do
|
4
|
-
|
5
|
-
describe ".config" do
|
6
|
-
|
7
|
-
context "no block given" do
|
8
|
-
it "returns the config singleton" do
|
9
|
-
Neoon.config.should eq Neoon::config
|
10
|
-
end
|
11
|
-
|
12
|
-
it "returns config.preload_models false" do
|
13
|
-
Neoon.config.preload_models.should be_false
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should have no model" do
|
17
|
-
Neoon.config.models.should be_empty
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context "with block given" do
|
22
|
-
before do
|
23
|
-
Neoon.configure do |c|
|
24
|
-
c.preload_models = true
|
25
|
-
end
|
26
|
-
require 'app/models/topic' # fake loading a model
|
27
|
-
end
|
28
|
-
|
29
|
-
after do
|
30
|
-
Neoon.configure do |c|
|
31
|
-
c.preload_models = false
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
it "returns config.preload_models true" do
|
36
|
-
Neoon.config.preload_models.should be_true
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should have models" do
|
40
|
-
Neoon.config.models.should_not be_empty
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|