Skip to content

Commit

Permalink
Stolen associated validation from active record
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasronge committed Apr 14, 2011
1 parent 188053c commit b236bf1
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 18 deletions.
1 change: 1 addition & 0 deletions lib/neo4j/rails/rails.rb
Expand Up @@ -4,6 +4,7 @@
require 'neo4j/rails/railtie'
require 'neo4j/rails/validations/uniqueness'
require 'neo4j/rails/validations/non_nil'
require 'neo4j/rails/validations/associated'
require 'neo4j/rails/finders'
require 'neo4j/rails/mapping/property'
require 'neo4j/rails/validations'
Expand Down
3 changes: 3 additions & 0 deletions lib/neo4j/rails/relationship.rb
Expand Up @@ -52,8 +52,11 @@ def reset_attributes
end

def valid_relationships?(*)
raise "REMOVE THIS METHOD" if true
puts "valid_relationships? #{caller.inspect}"
true
end

# --------------------------------------
# Public Class Methods
# --------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions lib/neo4j/rails/relationships/decl_relationship_dsl.rb
Expand Up @@ -4,11 +4,17 @@ module Relationships
# This is the Neo4j::Rails version of the Neo4j::HasN::DeclRelationshipDsl class used by Neo4j::NodeMixin#has_n and has_one
#
class DeclRelationshipDsl #:nodoc:
attr_accessor :dir

def initialize(storage, dir)
@storage = storage
@dir = dir
end

def rel_type
@storage.rel_type
end

def single_relationship(*)
@storage.single_relationship(@dir)
end
Expand Down
7 changes: 4 additions & 3 deletions lib/neo4j/rails/relationships/relationships.rb
Expand Up @@ -9,9 +9,10 @@ def write_changed_relationships #:nodoc:
end
end

def valid_relationships?(context, validated_nodes) #:nodoc:
validated_nodes ||= Set.new
!@relationships.values.find { |storage| !storage.valid?(context, validated_nodes) }
def valid_relationships?(context, validated_origins) #:nodoc:
puts " -- valid_relationships? for #{self}"
validated_origins ||= [Set.new, self]
!@relationships.values.find { |storage| !storage.valid?(context, validated_origins) }
end

def _decl_rels_for(rel_type) #:nodoc:
Expand Down
28 changes: 19 additions & 9 deletions lib/neo4j/rails/relationships/storage.rb
Expand Up @@ -129,32 +129,42 @@ def rm_incoming_rel(rel)
@incoming_rels.delete(rel)
end

def valid?(context, validated_nodes)
def valid?(context, validated_origins)
validated_nodes = validated_origins[0]
return true if validated_nodes.include?(@node)
all_valid = true

@outgoing_rels.each do |rel|
error_node = validated_origins[1] # node where we store the error array
puts "STORE ERROR ON #{error_node}"
start_node = rel.start_node
end_node = rel.end_node

validated_nodes << start_node << end_node
if !end_node.valid?(context, validated_nodes)
if !end_node.valid?(context, validated_origins)
all_valid = false
start_node.errors[@rel_type.to_sym] ||= []
start_node.errors[@rel_type.to_sym] << end_node.errors.clone
elsif !start_node.valid?(context, validated_nodes)
error_node.errors[@rel_type.to_sym] ||= []
error_node.errors[@rel_type.to_sym] << end_node.errors.clone
elsif !start_node.valid?(context, validated_origins)

all_valid = false
end_node.errors[@rel_type.to_sym] ||= []
end_node.errors[@rel_type.to_sym] << start_node.errors.clone
error_node.errors[@rel_type.to_sym] ||= []
error_node.errors[@rel_type.to_sym] << start_node.errors.clone
end

all_valid = false if !rel.valid?
if !rel.valid?
all_valid = false
puts "ERROR on relationship #{rel}"
error_node.errors[@rel_type.to_sym] ||= []
error_node.errors[@rel_type.to_sym] << rel.errors.clone # TODO CLONE ???
end
end
puts "all_valid=#{all_valid} for #{validated_origins[1]}"
all_valid
end

def persist
puts "PERSIST BEGIN"
success = true
@outgoing_rels.each do |rel|
success = rel.save
Expand All @@ -174,7 +184,7 @@ def persist
@incoming_rels.clear
success
end

puts "PERSIST #{success} END #{caller.inspect}"
success
end
end
Expand Down
7 changes: 3 additions & 4 deletions lib/neo4j/rails/validations.rb
Expand Up @@ -13,11 +13,10 @@ def save(options={})
perform_validations(options) ? super : false
end

def valid?(context = nil, validated_nodes=nil)
def valid?(context = nil)
context ||= (new_record? ? :create : :update)
output = super(context)
output_rels = valid_relationships?(context, validated_nodes)
errors.empty? && output && output_rels
super(context)
errors.empty?
end

private
Expand Down
53 changes: 53 additions & 0 deletions lib/neo4j/rails/validations/associated.rb
@@ -0,0 +1,53 @@
module Neo4j
module Rails
module Validations
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
puts "validate_each #{record}, attr: #{attribute}, val: #{value}"
value.each {|x| puts "a NODE #{x}"}
return if (value.is_a?(Array) ? value : [value]).collect { |r| r.nil? || r.valid? }.all?
puts "Add errors"
record.errors.add(attribute, :invalid, options.merge(:value => value))
end
end

module ClassMethods
# Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
#
# class Book < Neo4j::Rails::Model
# has_n :pages
# belongs_to :library
#
# validates_associated :pages, :library
# end
#
# Warning: If, after the above definition, you then wrote:
#
# class Page < Neo4j::Rails::Model
# has_n(:book).from(:pages)
#
# validates_associated :book
# end
#
# this would specify a circular dependency and cause infinite recursion.
#
# NOTE: This validation will not fail if the association hasn't been assigned. If you want to
# ensure that the association is both present and guaranteed to be valid, you also need to
# use +validates_presence_of+.
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_associated(*attr_names)
validates_with AssociatedValidator, _merge_attributes(attr_names)
end
end
end
end
end
4 changes: 2 additions & 2 deletions spec/spec_helper.rb
Expand Up @@ -98,9 +98,9 @@ def self.to_s
end
end

def create_model(base_class = Neo4j::Model, &block)
def create_model(base_class = Neo4j::Model,name=nil, &block)
klass = Class.new(base_class)
TempModel.set(klass)
TempModel.set(klass, name)
klass.class_eval &block if block
klass
end
Expand Down

0 comments on commit b236bf1

Please sign in to comment.