active_node 0.0.2.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|