1

I'm trying to override a setter method of one declared relationship to perform a custom behavior and some validation on the old node and the new node.

For example, take the next example:

class FantasticTeam 
  include Neo4j::ActiveNode

  has_one :in, :active, model_class: Whatever
  has_many :in, :not_active, model_class: Whatever

  def active=(whatever)
    # return if whatever is not include in :not_active
    # add the old value to :not_active
    # delete the relationship :not_active that belongs to whatever (the param)
    # add :active relationship to whatever
  end
end

Whats the proper way to do something like this?

tehAnswer
  • 960
  • 1
  • 13
  • 28

1 Answers1

3

There are two ways to handle this, both easier than overriding the setter method.

The first is to use callbacks within your ActiveNode model.

class FantasticTeam 
  include Neo4j::ActiveNode

  has_one :in, :active, before: :before_callback, model_class: Whatever
  has_many :in, :not_active, model_class: Whatever

  private

  def before_callback(other)
    return false unless self.not_active.include?(other)
    self.not_active(:w, :r).where(uuid: other.uuid).pluck(:r).each { |r| r.destroy }
    self.not_active << other unless self.active.nil?
  end
end

I personally never use that process. I always use ActiveRel models for any relationship logic.

class FantasticTeam 
  include Neo4j::ActiveNode

  has_one :in, :active, model_class: 'Whatever', rel_class: 'FantasticActiveWhatever'
  has_many :in, :not_active, model_class: 'Whatever'

end

class FantasticActiveWhatever
  include Neo4j::ActiveRel
  from_class FantasticTeam
  to_class Whatever
  type 'ACTIVE'

  validate      :inactive
  before_create :clear_inactive

  private

  def inactive
    self.errors.add(:inactive, 'Destination node must be declared inactive') unless from_node.not_active.include?(to_node)
  end

  def clear_inactive
    from_node.not_active(:w, :r).where(uuid: to_node.uuid).pluck(:r).each { |r| r.destroy }
  end
end

Then you'd create that:

rel = FantasticActiveWhatever.new(from_node: team, to_node: whatever) if rel.save # move on end

ActiveRel requires some extra setup but it's so much more powerful.

Either way, this is something that needs to be wrapped in a transaction because the potential exists for you to remove the not_active relationship but be unable to set the active.

begin
  tx = Neo4j::Transaction.new
  # the whole process
rescue StandardError
  tx.failure
  # additional failure behavior, if any
ensure
  tx.close
end

Basic relationship callbacks are documented at https://github.com/neo4jrb/neo4j/wiki/Neo4j-v3-Declared-Relationships#relationship-callbacks.

ActiveRel is documented at https://github.com/neo4jrb/neo4j/wiki/Neo4j%3A%3AActiveRel.

Transactions are documented at https://github.com/neo4jrb/neo4j/wiki/Transaction.

subvertallchris
  • 5,282
  • 2
  • 25
  • 43
  • 1
    One more thing: `from_node.not_active(:w, :r).where(uuid: to_node.uuid).pluck(:r).each { |r| r.destroy }` In here, what's the meaning of :w and why do you need to declare it if you don't gonna use it? Thanks Chris for keeping it real. – tehAnswer Oct 18 '14 at 18:07
  • 2
    It's always `query_start_node.association(:node_id, :rel_id)`. If you want to work with a relationship in a QueryProxy object, you must always set a node identifier first. – subvertallchris Oct 18 '14 at 18:19