active_node 0.0.2.alpha
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.
- data/.gitignore +7 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +19 -0
- data/Rakefile +12 -0
- data/active_node.gemspec +30 -0
- data/lib/active_node/associations/association.rb +44 -0
- data/lib/active_node/associations/builder/association.rb +70 -0
- data/lib/active_node/associations/builder/collection_association.rb +25 -0
- data/lib/active_node/associations/builder/has_many.rb +11 -0
- data/lib/active_node/associations/collection_association.rb +73 -0
- data/lib/active_node/associations/has_many_association.rb +11 -0
- data/lib/active_node/associations.rb +60 -0
- data/lib/active_node/base.rb +14 -0
- data/lib/active_node/callbacks.rb +45 -0
- data/lib/active_node/core.rb +10 -0
- data/lib/active_node/errors.rb +13 -0
- data/lib/active_node/persistence.rb +106 -0
- data/lib/active_node/reflection.rb +235 -0
- data/lib/active_node/validations.rb +81 -0
- data/lib/active_node/version.rb +3 -0
- data/lib/active_node.rb +17 -0
- data/spec/functional/associations_spec.rb +77 -0
- data/spec/functional/persistence_spec.rb +29 -0
- data/spec/functional/validations_spec.rb +14 -0
- data/spec/models/client.rb +8 -0
- data/spec/models/neo_user.rb +9 -0
- data/spec/models/person.rb +5 -0
- data/spec/spec_helper.rb +67 -0
- metadata +193 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2013 Heinrich Klobuczek
|
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.
|
data/Rakefile
ADDED
data/active_node.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "active_node/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "active_node"
|
7
|
+
s.version = ActiveNode::VERSION
|
8
|
+
s.authors = ["Heinrich Klobuczek"]
|
9
|
+
s.email = ["heinrich@mail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = "ActiveRecord style Object Graph Mapping for neo4j"
|
12
|
+
s.description = "ActiveRecord style Object Graph Mapping for neo4j"
|
13
|
+
|
14
|
+
s.rubyforge_project = "active_node"
|
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_dependency "active_attr"
|
22
|
+
s.add_dependency "neography"
|
23
|
+
s.add_dependency "activesupport"
|
24
|
+
s.add_dependency "activemodel"
|
25
|
+
s.add_development_dependency "rspec", ">= 2.11"
|
26
|
+
s.add_development_dependency "net-http-spy", "0.2.1"
|
27
|
+
#s.add_development_dependency "rake", ">= 0.8.7"
|
28
|
+
s.add_development_dependency "coveralls"
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
|
3
|
+
module ActiveNode
|
4
|
+
module Associations
|
5
|
+
# = Active Record Associations
|
6
|
+
#
|
7
|
+
# This is the root class of all associations ('+ Foo' signifies an included module Foo):
|
8
|
+
#
|
9
|
+
# Association
|
10
|
+
# SingularAssociation
|
11
|
+
# HasOneAssociation
|
12
|
+
# HasOneThroughAssociation + ThroughAssociation
|
13
|
+
# BelongsToAssociation
|
14
|
+
# BelongsToPolymorphicAssociation
|
15
|
+
# CollectionAssociation
|
16
|
+
# HasAndBelongsToManyAssociation
|
17
|
+
# HasManyAssociation
|
18
|
+
# HasManyThroughAssociation + ThroughAssociation
|
19
|
+
class Association #:nodoc:
|
20
|
+
attr_reader :owner, :target, :reflection
|
21
|
+
|
22
|
+
delegate :options, :to => :reflection
|
23
|
+
|
24
|
+
def initialize(owner, reflection)
|
25
|
+
@owner, @reflection = owner, reflection
|
26
|
+
|
27
|
+
reset
|
28
|
+
end
|
29
|
+
|
30
|
+
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
31
|
+
def reset
|
32
|
+
@loaded = false
|
33
|
+
@target = nil
|
34
|
+
@stale_state = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
|
38
|
+
# polymorphic_type field on the owner.
|
39
|
+
def klass
|
40
|
+
reflection.klass
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ActiveNode::Associations::Builder
|
2
|
+
class Association #:nodoc:
|
3
|
+
class << self
|
4
|
+
attr_accessor :valid_options
|
5
|
+
end
|
6
|
+
|
7
|
+
self.valid_options = [:class_name]
|
8
|
+
|
9
|
+
attr_reader :model, :name, :options, :reflection
|
10
|
+
|
11
|
+
def self.build(*args)
|
12
|
+
new(*args).build
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(model, name, options)
|
16
|
+
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
|
17
|
+
|
18
|
+
@model = model
|
19
|
+
@name = name
|
20
|
+
@options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def mixin
|
24
|
+
@model
|
25
|
+
end
|
26
|
+
|
27
|
+
include Module.new { def build; end }
|
28
|
+
|
29
|
+
def build
|
30
|
+
validate_options
|
31
|
+
define_accessors
|
32
|
+
@reflection = model.create_reflection(macro, name, options, model)
|
33
|
+
super # provides an extension point
|
34
|
+
@reflection
|
35
|
+
end
|
36
|
+
|
37
|
+
def macro
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid_options
|
42
|
+
Association.valid_options
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_options
|
46
|
+
options.assert_valid_keys(valid_options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_accessors
|
50
|
+
define_readers
|
51
|
+
define_writers
|
52
|
+
end
|
53
|
+
|
54
|
+
def define_readers
|
55
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
56
|
+
def #{name}(*args)
|
57
|
+
association(:#{name}).reader(*args)
|
58
|
+
end
|
59
|
+
CODE
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_writers
|
63
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
64
|
+
def #{name}=(value)
|
65
|
+
association(:#{name}).writer(value)
|
66
|
+
end
|
67
|
+
CODE
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_node/associations'
|
2
|
+
|
3
|
+
module ActiveNode::Associations::Builder
|
4
|
+
class CollectionAssociation < Association #:nodoc:
|
5
|
+
def define_readers
|
6
|
+
super
|
7
|
+
|
8
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
9
|
+
def #{name.to_s.singularize}_ids
|
10
|
+
association(:#{name}).ids_reader
|
11
|
+
end
|
12
|
+
CODE
|
13
|
+
end
|
14
|
+
|
15
|
+
def define_writers
|
16
|
+
super
|
17
|
+
|
18
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
19
|
+
def #{name.to_s.singularize}_ids=(ids)
|
20
|
+
association(:#{name}).ids_writer(ids)
|
21
|
+
end
|
22
|
+
CODE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
module Associations
|
3
|
+
# = Active Record Association Collection
|
4
|
+
#
|
5
|
+
# CollectionAssociation is an abstract class that provides common stuff to
|
6
|
+
# ease the implementation of association proxies that represent
|
7
|
+
# collections. See the class hierarchy in AssociationProxy.
|
8
|
+
#
|
9
|
+
# CollectionAssociation:
|
10
|
+
# HasAndBelongsToManyAssociation => has_and_belongs_to_many
|
11
|
+
# HasManyAssociation => has_many
|
12
|
+
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
13
|
+
#
|
14
|
+
# CollectionAssociation class provides common methods to the collections
|
15
|
+
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
16
|
+
# +:through association+ option.
|
17
|
+
#
|
18
|
+
# You need to be careful with assumptions regarding the target: The proxy
|
19
|
+
# does not fetch records from the database until it needs them, but new
|
20
|
+
# ones created with +build+ are added to the target. So, the target may be
|
21
|
+
# non-empty and still lack children waiting to be read from the database.
|
22
|
+
# If you look directly to the database you cannot assume that's the entire
|
23
|
+
# collection because new records may have been added to the target, etc.
|
24
|
+
#
|
25
|
+
# If you need to work on all current children, new and existing records,
|
26
|
+
# +load_target+ and the +loaded+ flag are your friends.
|
27
|
+
class CollectionAssociation < Association #:nodoc:
|
28
|
+
|
29
|
+
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
30
|
+
def reader(force_reload = false)
|
31
|
+
@target ||= load_target
|
32
|
+
end
|
33
|
+
|
34
|
+
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
35
|
+
def writer(records)
|
36
|
+
@dirty = true
|
37
|
+
@target = records
|
38
|
+
end
|
39
|
+
|
40
|
+
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
41
|
+
def ids_reader
|
42
|
+
reader.map(&:id)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
46
|
+
def ids_writer(ids)
|
47
|
+
writer klass.find(ids.reject(&:blank?).map!(&:to_i))
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_target
|
51
|
+
owner.send(reflection.direction, reflection.type, reflection.klass)
|
52
|
+
end
|
53
|
+
|
54
|
+
def save
|
55
|
+
return unless @dirty
|
56
|
+
#delete all relations missing in new target
|
57
|
+
owner.node.rels(reflection.type).send(reflection.direction).each do |rel|
|
58
|
+
rel.del unless ids_reader.include? rel.other_node(owner.node).neo_id.to_i
|
59
|
+
end
|
60
|
+
original_target = owner.node.send(reflection.direction, reflection.type)
|
61
|
+
original_target_ids = original_target.map(&:neo_id).map(&:to_i)
|
62
|
+
#add relations missing in old target
|
63
|
+
@target.each { |n| original_target << n.node unless original_target_ids.include? n.id }
|
64
|
+
end
|
65
|
+
|
66
|
+
def reset
|
67
|
+
super
|
68
|
+
@target = owner.new_record? ? [] : nil
|
69
|
+
@dirty = false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
# = Active Record Has Many Association
|
3
|
+
module Associations
|
4
|
+
# This is the proxy that handles a has many association.
|
5
|
+
#
|
6
|
+
# If the association has a <tt>:through</tt> option further specialization
|
7
|
+
# is provided by its child HasManyThroughAssociation.
|
8
|
+
class HasManyAssociation < CollectionAssociation #:nodoc:
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'active_support/core_ext/enumerable'
|
2
|
+
require 'active_support/core_ext/string/conversions'
|
3
|
+
require 'active_support/core_ext/module/remove_method'
|
4
|
+
|
5
|
+
module ActiveNode
|
6
|
+
module Associations # :nodoc:
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
autoload :Association, 'active_node/associations/association'
|
11
|
+
autoload :CollectionAssociation, 'active_node/associations/collection_association'
|
12
|
+
autoload :HasManyAssociation, 'active_node/associations/has_many_association'
|
13
|
+
|
14
|
+
module Builder #:nodoc:
|
15
|
+
autoload :Association, 'active_node/associations/builder/association'
|
16
|
+
autoload :CollectionAssociation, 'active_node/associations/builder/collection_association'
|
17
|
+
|
18
|
+
autoload :HasMany, 'active_node/associations/builder/has_many'
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Clears out the association cache.
|
23
|
+
def clear_association_cache #:nodoc:
|
24
|
+
@association_cache.clear if persisted?
|
25
|
+
end
|
26
|
+
|
27
|
+
# :nodoc:
|
28
|
+
attr_reader :association_cache
|
29
|
+
|
30
|
+
# Returns the association instance for the given name, instantiating it if it doesn't already exist
|
31
|
+
def association(name) #:nodoc:
|
32
|
+
association = association_instance_get(name)
|
33
|
+
|
34
|
+
if association.nil?
|
35
|
+
reflection = self.class.reflect_on_association(name)
|
36
|
+
association = reflection.association_class.new(self, reflection)
|
37
|
+
association_instance_set(name, association)
|
38
|
+
end
|
39
|
+
|
40
|
+
association
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
|
45
|
+
def association_instance_get(name)
|
46
|
+
@association_cache[name.to_sym]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set the specified association instance.
|
50
|
+
def association_instance_set(name, association)
|
51
|
+
@association_cache[name] = association
|
52
|
+
end
|
53
|
+
|
54
|
+
module ClassMethods
|
55
|
+
def has_many(name, options = {})
|
56
|
+
Builder::HasMany.build(self, name, options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'active_attr'
|
2
|
+
require 'active_node/errors'
|
3
|
+
|
4
|
+
module ActiveNode
|
5
|
+
class Base
|
6
|
+
include ActiveAttr::Model
|
7
|
+
include Persistence
|
8
|
+
include Validations
|
9
|
+
include Callbacks
|
10
|
+
include Associations
|
11
|
+
include Reflection
|
12
|
+
include Core
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
module Callbacks
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
CALLBACKS = [
|
6
|
+
:after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
|
7
|
+
:before_save, :around_save, :after_save, :before_create, :around_create,
|
8
|
+
:after_create, :before_update, :around_update, :after_update,
|
9
|
+
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
|
10
|
+
]
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
include ActiveModel::Callbacks
|
14
|
+
end
|
15
|
+
|
16
|
+
included do
|
17
|
+
include ActiveModel::Validations::Callbacks
|
18
|
+
|
19
|
+
define_model_callbacks :initialize, :find, :touch, :only => :after
|
20
|
+
define_model_callbacks :save, :create, :update, :destroy
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy include_relationships=false
|
24
|
+
run_callbacks(:destroy) { super include_relationships }
|
25
|
+
end
|
26
|
+
|
27
|
+
def touch(*) #:nodoc:
|
28
|
+
run_callbacks(:touch) { super }
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def create_or_update #:nodoc:
|
34
|
+
run_callbacks(:save) { super }
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_record #:nodoc:
|
38
|
+
run_callbacks(:create) { super }
|
39
|
+
end
|
40
|
+
|
41
|
+
def update_record(*) #:nodoc:
|
42
|
+
run_callbacks(:update) { super }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
|
3
|
+
# = Active Node Errors
|
4
|
+
#
|
5
|
+
# Generic Active Node exception class.
|
6
|
+
class ActiveNodeError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
# Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
|
10
|
+
# saved because record is invalid.
|
11
|
+
class RecordNotSaved < ActiveNodeError
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
module Persistence
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def find ids
|
7
|
+
ids.is_a?(Enumerable) ? ids.map { |id| find(id) } : new_instance(Neography::Node.load(ids))
|
8
|
+
end
|
9
|
+
|
10
|
+
def all
|
11
|
+
result = Neography::Node.find(:node_auto_index, :type, type)
|
12
|
+
(result.is_a?(Enumerable) ? result : [result]).map { |node| new_instance(node) }.compact
|
13
|
+
end
|
14
|
+
|
15
|
+
def type
|
16
|
+
name.underscore
|
17
|
+
end
|
18
|
+
|
19
|
+
def wrap node, klass=nil
|
20
|
+
node.is_a?(Enumerable) ?
|
21
|
+
node.map { |n| wrap(n, klass) } :
|
22
|
+
node.is_a?(Neography::Node) && (active_node_class(node.type.camelize, klass)).try(:new, node) || node
|
23
|
+
end
|
24
|
+
|
25
|
+
def active_node_class(class_name, default_klass=nil)
|
26
|
+
klass = Module.const_get(class_name) rescue nil
|
27
|
+
klass && klass < ActiveNode::Base && klass || default_klass
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def new_instance node
|
32
|
+
node && new(node)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :node
|
37
|
+
#protected :node
|
38
|
+
|
39
|
+
delegate :neo_id, to: :node, allow_nil: true
|
40
|
+
def id
|
41
|
+
neo_id && neo_id.to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
alias :to_param :id
|
45
|
+
alias :persisted? :id
|
46
|
+
alias :[] :send
|
47
|
+
|
48
|
+
def initialize hash={}
|
49
|
+
@node, hash = hash, hash.send(:table) if hash.is_a? Neography::Node
|
50
|
+
super hash
|
51
|
+
end
|
52
|
+
|
53
|
+
def new_record?
|
54
|
+
!id
|
55
|
+
end
|
56
|
+
|
57
|
+
def save(*)
|
58
|
+
create_or_update
|
59
|
+
end
|
60
|
+
|
61
|
+
alias save! save
|
62
|
+
|
63
|
+
def destroy include_relationships=false
|
64
|
+
destroyable = destroy_associations include_relationships
|
65
|
+
node.del if destroyable
|
66
|
+
@destroyed = destroyable
|
67
|
+
end
|
68
|
+
|
69
|
+
def destroy!
|
70
|
+
destroy true
|
71
|
+
end
|
72
|
+
|
73
|
+
def incoming(types=nil, klass=nil)
|
74
|
+
node && self.class.wrap(node.incoming(types), klass)
|
75
|
+
end
|
76
|
+
|
77
|
+
def outgoing(types=nil, klass=nil)
|
78
|
+
node && self.class.wrap(node.outgoing(types), klass)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def destroy_associations include_associations
|
83
|
+
rels = node.rels
|
84
|
+
return false unless rels.empty? || include_associations
|
85
|
+
rels.each { |rel| rel.del }
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
def nullify_blanks! attrs
|
90
|
+
attrs.each { |k, v| attrs[k]=nil if attrs[k].blank? }
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_or_update
|
94
|
+
write; true
|
95
|
+
association_cache.values.each &:save
|
96
|
+
end
|
97
|
+
|
98
|
+
def write
|
99
|
+
if @node
|
100
|
+
nullify_blanks!(attributes).each { |k, v| @node[k]=v }
|
101
|
+
else
|
102
|
+
@node = Neography::Node.create nullify_blanks!(attributes).merge(type: self.class.type)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
# = Active Record Reflection
|
3
|
+
module Reflection # :nodoc:
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :reflections
|
8
|
+
self.reflections = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
# Reflection enables to interrogate Active Record classes and objects
|
12
|
+
# about their associations and aggregations. This information can,
|
13
|
+
# for example, be used in a form builder that takes an Active Record object
|
14
|
+
# and creates input fields for all of the attributes depending on their type
|
15
|
+
# and displays the associations to other objects.
|
16
|
+
#
|
17
|
+
# MacroReflection class has info for AggregateReflection and AssociationReflection
|
18
|
+
# classes.
|
19
|
+
module ClassMethods
|
20
|
+
def create_reflection(macro, name, options, model)
|
21
|
+
case macro
|
22
|
+
when :has_many, :has_one
|
23
|
+
klass = options[:through] ? ThroughReflection : AssociationReflection
|
24
|
+
reflection = klass.new(macro, name, options, model)
|
25
|
+
end
|
26
|
+
|
27
|
+
self.reflections = self.reflections.merge(name => reflection)
|
28
|
+
reflection
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns an array of AssociationReflection objects for all the
|
32
|
+
# associations in the class. If you only want to reflect on a certain
|
33
|
+
# association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
|
34
|
+
# <tt>:belongs_to</tt>) as the first parameter.
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# Account.reflect_on_all_associations # returns an array of all associations
|
39
|
+
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
40
|
+
#
|
41
|
+
def reflect_on_all_associations(macro = nil)
|
42
|
+
association_reflections = reflections.values.grep(AssociationReflection)
|
43
|
+
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
47
|
+
#
|
48
|
+
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
49
|
+
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
50
|
+
#
|
51
|
+
def reflect_on_association(association)
|
52
|
+
reflection = reflections[association]
|
53
|
+
reflection if reflection.is_a?(AssociationReflection)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
57
|
+
def reflect_on_all_autosave_associations
|
58
|
+
reflections.values.select { |reflection| reflection.options[:autosave] }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Base class for AggregateReflection and AssociationReflection. Objects of
|
63
|
+
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
64
|
+
#
|
65
|
+
# MacroReflection
|
66
|
+
# AggregateReflection
|
67
|
+
# AssociationReflection
|
68
|
+
# ThroughReflection
|
69
|
+
class MacroReflection
|
70
|
+
# Returns the name of the macro.
|
71
|
+
#
|
72
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
|
73
|
+
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
74
|
+
attr_reader :name
|
75
|
+
|
76
|
+
# Returns the macro type.
|
77
|
+
#
|
78
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
|
79
|
+
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
80
|
+
attr_reader :macro
|
81
|
+
|
82
|
+
attr_reader :scope
|
83
|
+
|
84
|
+
# Returns the hash of options used for the macro.
|
85
|
+
#
|
86
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
|
87
|
+
# <tt>has_many :clients</tt> returns +{}+
|
88
|
+
attr_reader :options
|
89
|
+
|
90
|
+
attr_reader :model
|
91
|
+
|
92
|
+
|
93
|
+
def initialize(macro, name, options, model)
|
94
|
+
@macro = macro
|
95
|
+
@name = name
|
96
|
+
@options = options
|
97
|
+
@model = model
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the class for the macro.
|
101
|
+
#
|
102
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
103
|
+
# <tt>has_many :clients</tt> returns the Client class
|
104
|
+
def klass
|
105
|
+
@klass ||= class_name.constantize
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the class name for the macro.
|
109
|
+
#
|
110
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
111
|
+
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
112
|
+
def class_name
|
113
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def direction
|
117
|
+
@direction ||= (options[:direction] || :outgoing)
|
118
|
+
end
|
119
|
+
|
120
|
+
def type
|
121
|
+
@type ||= (options[:type] || name.to_s.singularize)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +model+ attribute,
|
125
|
+
# and +other_aggregation+ has an options hash assigned to it.
|
126
|
+
def ==(other_aggregation)
|
127
|
+
super ||
|
128
|
+
other_aggregation.kind_of?(self.class) &&
|
129
|
+
name == other_aggregation.name &&
|
130
|
+
other_aggregation.options &&
|
131
|
+
model == other_aggregation.model
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
def derive_class_name
|
136
|
+
name.to_s.camelize
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# Holds all the meta-data about an association as it was specified in the
|
142
|
+
# Active Record class.
|
143
|
+
class AssociationReflection < MacroReflection #:nodoc:
|
144
|
+
# Returns the target association's class.
|
145
|
+
#
|
146
|
+
# class Author < ActiveRecord::Base
|
147
|
+
# has_many :books
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# Author.reflect_on_association(:books).klass
|
151
|
+
# # => Book
|
152
|
+
#
|
153
|
+
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
154
|
+
# a new association object. Use +build_association+ or +create_association+
|
155
|
+
# instead. This allows plugins to hook into association object creation.
|
156
|
+
#def klass
|
157
|
+
# @klass ||= model.send(:compute_type, class_name)
|
158
|
+
#end
|
159
|
+
|
160
|
+
def initialize(*args)
|
161
|
+
super
|
162
|
+
@collection = [:has_many].include?(macro)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns a new, unsaved instance of the associated class. +attributes+ will
|
166
|
+
# be passed to the class's constructor.
|
167
|
+
def build_association(attributes, &block)
|
168
|
+
klass.new(attributes, &block)
|
169
|
+
end
|
170
|
+
|
171
|
+
def through_reflection
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
|
175
|
+
def source_reflection
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# A chain of reflections from this one back to the owner. For more see the explanation in
|
180
|
+
# ThroughReflection.
|
181
|
+
def chain
|
182
|
+
[self]
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns whether or not this association reflection is for a collection
|
186
|
+
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
187
|
+
# +has_and_belongs_to_many+, +false+ otherwise.
|
188
|
+
def collection?
|
189
|
+
@collection
|
190
|
+
end
|
191
|
+
|
192
|
+
# Returns whether or not the association should be validated as part of
|
193
|
+
# the parent's validation.
|
194
|
+
#
|
195
|
+
# Unless you explicitly disable validation with
|
196
|
+
# <tt>validate: false</tt>, validation will take place when:
|
197
|
+
#
|
198
|
+
# * you explicitly enable validation; <tt>validate: true</tt>
|
199
|
+
# * you use autosave; <tt>autosave: true</tt>
|
200
|
+
# * the association is a +has_many+ association
|
201
|
+
def validate?
|
202
|
+
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
|
203
|
+
end
|
204
|
+
|
205
|
+
def association_class
|
206
|
+
case macro
|
207
|
+
when :has_many
|
208
|
+
if options[:through]
|
209
|
+
Associations::HasManyThroughAssociation
|
210
|
+
else
|
211
|
+
Associations::HasManyAssociation
|
212
|
+
end
|
213
|
+
when :has_one
|
214
|
+
if options[:through]
|
215
|
+
Associations::HasOneThroughAssociation
|
216
|
+
else
|
217
|
+
Associations::HasOneAssociation
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
def derive_class_name
|
224
|
+
class_name = name.to_s.camelize
|
225
|
+
class_name = class_name.singularize if collection?
|
226
|
+
class_name
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Holds all the meta-data about a :through association as it was specified
|
231
|
+
# in the Active Record class.
|
232
|
+
class ThroughReflection < AssociationReflection #:nodoc:
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ActiveNode
|
2
|
+
# = Active Node RecordInvalid
|
3
|
+
#
|
4
|
+
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
|
5
|
+
# +record+ method to retrieve the record which did not validate.
|
6
|
+
#
|
7
|
+
# begin
|
8
|
+
# complex_operation_that_calls_save!_internally
|
9
|
+
# rescue ActiveRecord::RecordInvalid => invalid
|
10
|
+
# puts invalid.record.errors
|
11
|
+
# end
|
12
|
+
class RecordInvalid < ActiveNodeError
|
13
|
+
attr_reader :record # :nodoc:
|
14
|
+
def initialize(record) # :nodoc:
|
15
|
+
@record = record
|
16
|
+
errors = @record.errors.full_messages.join(", ")
|
17
|
+
super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# = Active Node Validations
|
22
|
+
#
|
23
|
+
# Active Node includes the majority of its validations from <tt>ActiveModel::Validations</tt>
|
24
|
+
# all of which accept the <tt>:on</tt> argument to define the context where the
|
25
|
+
# validations are active. Active Node will always supply either the context of
|
26
|
+
# <tt>:create</tt> or <tt>:update</tt> dependent on whether the model is a
|
27
|
+
# <tt>new_record?</tt>.
|
28
|
+
|
29
|
+
module Validations
|
30
|
+
extend ActiveSupport::Concern
|
31
|
+
include ActiveModel::Validations
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
# Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
|
35
|
+
# so an exception is raised if the record is invalid.
|
36
|
+
def create!(attributes = nil, &block)
|
37
|
+
if attributes.is_a?(Array)
|
38
|
+
attributes.collect { |attr| create!(attr, &block) }
|
39
|
+
else
|
40
|
+
object = new(attributes)
|
41
|
+
yield(object) if block_given?
|
42
|
+
object.save!
|
43
|
+
object
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
|
49
|
+
# The regular Base#save method is replaced with this when the validations
|
50
|
+
# module is mixed in, which it is by default.
|
51
|
+
def save(options={})
|
52
|
+
perform_validations(options) ? super : false
|
53
|
+
end
|
54
|
+
|
55
|
+
# Attempts to save the record just like Base#save but will raise a +RecordInvalid+
|
56
|
+
# exception instead of returning +false+ if the record is not valid.
|
57
|
+
def save!(options={})
|
58
|
+
perform_validations(options) ? super : raise(RecordInvalid.new(self))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Runs all the validations within the specified context. Returns +true+ if
|
62
|
+
# no errors are found, +false+ otherwise.
|
63
|
+
#
|
64
|
+
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
|
65
|
+
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
|
66
|
+
#
|
67
|
+
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
|
68
|
+
# some <tt>:on</tt> option will only run in the specified context.
|
69
|
+
def valid?(context = nil)
|
70
|
+
context ||= (new_record? ? :create : :update)
|
71
|
+
output = super(context)
|
72
|
+
errors.empty? && output
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
def perform_validations(options={}) # :nodoc:
|
78
|
+
options[:validate] == false || valid?(options[:context])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/active_node.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "active_support/dependencies/autoload"
|
2
|
+
|
3
|
+
module ActiveNode
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
autoload :Base
|
6
|
+
autoload :Callbacks
|
7
|
+
autoload :Core
|
8
|
+
autoload :Persistence
|
9
|
+
autoload :Validations
|
10
|
+
autoload :Reflection
|
11
|
+
autoload :VERSION
|
12
|
+
|
13
|
+
eager_autoload do
|
14
|
+
autoload :ActiveNodeError, 'active_node/errors'
|
15
|
+
autoload :Associations
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveNode::Associations do
|
4
|
+
describe "#save" do
|
5
|
+
it "should have empty association" do
|
6
|
+
client = Client.new(name: 'a')
|
7
|
+
client.save
|
8
|
+
client.users.should be_empty
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should not have empty association" do
|
12
|
+
user = NeoUser.new(name: 'Heinrich')
|
13
|
+
user.save
|
14
|
+
client = Client.new(name: 'a', users: [user])
|
15
|
+
client.save
|
16
|
+
client.users.should == [user]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can set association by id" do
|
20
|
+
user = NeoUser.new(name: 'Heinrich')
|
21
|
+
user.save
|
22
|
+
client = Client.new(name: 'a', user_ids: [user.id])
|
23
|
+
client.save
|
24
|
+
client.users.should == [user]
|
25
|
+
client.user_ids.should == [user.id]
|
26
|
+
client.users.first.clients.first.should == client
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can remove associated objects" do
|
30
|
+
user = NeoUser.new(name: 'Heinrich')
|
31
|
+
user.save
|
32
|
+
client = Client.new(name: 'a', user_ids: [user.id])
|
33
|
+
client.save
|
34
|
+
client.user_ids = []
|
35
|
+
client.save
|
36
|
+
client.users.should be_empty
|
37
|
+
client.user_ids.should be_empty
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can remove some of the associated objects" do
|
41
|
+
child1 = Person.create!
|
42
|
+
child2 = Person.create!
|
43
|
+
person = Person.create! child_ids: [child1.id, child2.id]
|
44
|
+
person = Person.find(person.id)
|
45
|
+
person.children.count.should == 2
|
46
|
+
person.child_ids = [child2.id]
|
47
|
+
person.save
|
48
|
+
Person.find(person.id).children.should == [child2]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "can remove and add some of the associated objects" do
|
52
|
+
child1 = Person.create!
|
53
|
+
child2 = Person.create!
|
54
|
+
person = Person.create! child_ids: [child1.id, child2.id]
|
55
|
+
person = Person.find(person.id)
|
56
|
+
person.children.count.should == 2
|
57
|
+
child3 = Person.create!
|
58
|
+
person.child_ids = [child2.id, child3.id]
|
59
|
+
person.save
|
60
|
+
Person.find(person.id).children.should == [child2, child3]
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'can handle self referencing' do
|
64
|
+
person = Person.new
|
65
|
+
person.save
|
66
|
+
person.people = [person]
|
67
|
+
person.save
|
68
|
+
person.people.first == person
|
69
|
+
Person.all.count.should == 1
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'can handle reference to the same class' do
|
73
|
+
id = Person.create!(children: [Person.create!, Person.create!]).id
|
74
|
+
Person.find(id).children.size.should == 2
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveNode::Persistence do
|
4
|
+
describe "#save" do
|
5
|
+
it "should save not conventionally named object" do
|
6
|
+
NeoUser.new(name: 'Heinrich').save.should be_true
|
7
|
+
NeoUser.all.map(&:name).should == ['Heinrich']
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should destroy node' do
|
11
|
+
user = NeoUser.create!(name: 'abc')
|
12
|
+
NeoUser.all.count.should == 1
|
13
|
+
user.destroy.should be_true
|
14
|
+
NeoUser.all.count.should == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should not destroy node with relationships' do
|
18
|
+
person = Person.create! children: [Person.create!, Person.create!]
|
19
|
+
person.destroy.should be_false
|
20
|
+
Person.all.count.should == 3
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should destroy! node with relationships' do
|
24
|
+
person = Person.create! children: [Person.create!, Person.create!]
|
25
|
+
person.destroy!.should be_true
|
26
|
+
Person.all.count.should == 2
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveNode::Validations do
|
4
|
+
describe "#save" do
|
5
|
+
it "should not save invalid object" do
|
6
|
+
Client.new.save.should be_false
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should save invalid object" do
|
10
|
+
Client.new(name: 'abc7').save.should be_true
|
11
|
+
Client.all.first.name.should == 'abc7'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#require 'rubygems'
|
2
|
+
#require 'bundler/setup'
|
3
|
+
require 'active_node'
|
4
|
+
#
|
5
|
+
#require "active_node/version"
|
6
|
+
#require "active_model/version"
|
7
|
+
#require 'active_support'
|
8
|
+
#require 'active_model/validator'
|
9
|
+
#require 'active_model/validations'
|
10
|
+
#require 'active_model'
|
11
|
+
#require 'active_attr'
|
12
|
+
require 'neography'
|
13
|
+
require 'benchmark'
|
14
|
+
#require 'matchers'
|
15
|
+
require 'coveralls'
|
16
|
+
Coveralls.wear!
|
17
|
+
|
18
|
+
# If you want to see more, uncomment the next few lines
|
19
|
+
# require 'net-http-spy'
|
20
|
+
# Net::HTTP.http_logger_options = {:body => true} # just the body
|
21
|
+
# Net::HTTP.http_logger_options = {:verbose => true} # see everything
|
22
|
+
|
23
|
+
Dir[File.dirname(__FILE__) + '/models/*.rb'].each {|file| require file }
|
24
|
+
|
25
|
+
def generate_text(length=8)
|
26
|
+
chars = 'abcdefghjkmnpqrstuvwxyz'
|
27
|
+
key = ''
|
28
|
+
length.times { |i| key << chars[rand(chars.length)] }
|
29
|
+
key
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec.configure do |c|
|
33
|
+
c.filter_run_excluding :slow => true, :gremlin => true
|
34
|
+
#c.around(:each) do
|
35
|
+
# Neography::Rest.new.execute_query("START n0=node(0),nx=node(*) MATCH n0-[r0?]-(),nx-[rx?]-() WHERE nx <> n0 DELETE r0,rx,nx")
|
36
|
+
#end
|
37
|
+
c.before(:each) do
|
38
|
+
@neo=Neography::Rest.new
|
39
|
+
@neo.execute_query("START n0=node(0),nx=node(*) MATCH n0-[r0?]-(),nx-[rx?]-() WHERE nx <> n0 DELETE r0,rx,nx")
|
40
|
+
@neo.set_node_auto_index_status(true)
|
41
|
+
@neo.add_node_auto_index_property('type')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def json_content_type
|
47
|
+
{"Content-Type"=>"application/json"}
|
48
|
+
end
|
49
|
+
|
50
|
+
def error_response(attributes)
|
51
|
+
request_uri = double()
|
52
|
+
request_uri.stub(:request_uri).and_return("")
|
53
|
+
|
54
|
+
http_header = double()
|
55
|
+
http_header.stub(:request_uri).and_return(request_uri)
|
56
|
+
|
57
|
+
stub(
|
58
|
+
http_header: http_header,
|
59
|
+
code: attributes[:code],
|
60
|
+
body: {
|
61
|
+
message: attributes[:message],
|
62
|
+
exception: attributes[:exception],
|
63
|
+
stacktrace: attributes[:stacktrace]
|
64
|
+
}.reject { |k,v| v.nil? }.to_json
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
metadata
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_node
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2.alpha
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Heinrich Klobuczek
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: active_attr
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: neography
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activesupport
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activemodel
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '2.11'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '2.11'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: net-http-spy
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - '='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.2.1
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - '='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.2.1
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: coveralls
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: ActiveRecord style Object Graph Mapping for neo4j
|
127
|
+
email:
|
128
|
+
- heinrich@mail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- .gitignore
|
134
|
+
- .travis.yml
|
135
|
+
- Gemfile
|
136
|
+
- LICENSE
|
137
|
+
- Rakefile
|
138
|
+
- active_node.gemspec
|
139
|
+
- lib/active_node.rb
|
140
|
+
- lib/active_node/associations.rb
|
141
|
+
- lib/active_node/associations/association.rb
|
142
|
+
- lib/active_node/associations/builder/association.rb
|
143
|
+
- lib/active_node/associations/builder/collection_association.rb
|
144
|
+
- lib/active_node/associations/builder/has_many.rb
|
145
|
+
- lib/active_node/associations/collection_association.rb
|
146
|
+
- lib/active_node/associations/has_many_association.rb
|
147
|
+
- lib/active_node/base.rb
|
148
|
+
- lib/active_node/callbacks.rb
|
149
|
+
- lib/active_node/core.rb
|
150
|
+
- lib/active_node/errors.rb
|
151
|
+
- lib/active_node/persistence.rb
|
152
|
+
- lib/active_node/reflection.rb
|
153
|
+
- lib/active_node/validations.rb
|
154
|
+
- lib/active_node/version.rb
|
155
|
+
- spec/functional/associations_spec.rb
|
156
|
+
- spec/functional/persistence_spec.rb
|
157
|
+
- spec/functional/validations_spec.rb
|
158
|
+
- spec/models/client.rb
|
159
|
+
- spec/models/neo_user.rb
|
160
|
+
- spec/models/person.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
homepage: ''
|
163
|
+
licenses: []
|
164
|
+
post_install_message:
|
165
|
+
rdoc_options: []
|
166
|
+
require_paths:
|
167
|
+
- lib
|
168
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
none: false
|
176
|
+
requirements:
|
177
|
+
- - ! '>'
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: 1.3.1
|
180
|
+
requirements: []
|
181
|
+
rubyforge_project: active_node
|
182
|
+
rubygems_version: 1.8.25
|
183
|
+
signing_key:
|
184
|
+
specification_version: 3
|
185
|
+
summary: ActiveRecord style Object Graph Mapping for neo4j
|
186
|
+
test_files:
|
187
|
+
- spec/functional/associations_spec.rb
|
188
|
+
- spec/functional/persistence_spec.rb
|
189
|
+
- spec/functional/validations_spec.rb
|
190
|
+
- spec/models/client.rb
|
191
|
+
- spec/models/neo_user.rb
|
192
|
+
- spec/models/person.rb
|
193
|
+
- spec/spec_helper.rb
|