1

I have three models "Input", "Mechanism", and "Output".

Mechanism 'has_one' :input and 'has_one' :output.

I want it make it so that a Mechanism object has the name attribute "The effect of input X on output Y".

Here is what I tried:

class Mechanism
  include Neo4j::ActiveNode
  property :name, default: 'NewMechanism#{self.class.count}'
  has_one :in, :input,  class_name: 'Input'
  has_one :out, :output,  class_name: 'Output'
  after_create :name_mechanism
  def name_mechanism
     self.update_attributes(name: "Effect of #{self.input.name} on #{self.output.name}")
  end
end

But when I initialize an object in the console, I get the error

NoMethodError: undefined method `name' for nil:NilClass from app/models/mechanism.rb:12:in 'name_mechanism'

So yeah I am using Neo4j as a database, but I suspect this isn't a neo4j issue, but rather it is my weak understanding of callbacks in Rails. Any advice?

Count Zero
  • 630
  • 1
  • 6
  • 15

2 Answers2

2

Your code is assuming that every mechanism will always have an associated input and output. You need to cater to situations where it does not. You could do something like this

class Mechanism
  include Neo4j::ActiveNode
  property :name, default: 'NewMechanism#{self.class.count}'
  has_one :in, :input,  class_name: 'Input'
  has_one :out, :output,  class_name: 'Output'
  before_create :name_mechanism

  def name_mechanism
    if self.name.blank?
      self.name = self.default_name
    end
  end

  def default_name
    "Effect of #{self.input ? self.input.name : "<input not set>"} on #{self.output ? self.output.name : "<output not set>"}"
  end

end

note i've changed the callback to a before_create since this is a better place to set a default name. Note also that name_mechanism keeps the name if it's already got a non-blank one.

Max Williams
  • 32,435
  • 31
  • 130
  • 197
2

Solved it. This assumes that Input and Output objects exist before creating a Mechanism that ties the two.

class Mechanism
  include Neo4j::ActiveNode
  property :name, default: 'New Mechanism #{self.class.count}'
  has_one :in, :input,  class_name: 'Input'
  has_one :out, :output,  class_name: 'Output'
  after_save :name_mechanism
  def name_mechanism
    unless (self.input.nil?) || (self.output.nil?)
      self.name = "Effect of #{self.input.name} on #{self.output.name}"
    end
  end
end

Key takeaway: When using Neo4j, callbacks are methods on ActiveNode, not ActiveRecord. They have the same names and usage as the ActiveRecord callbacks, but because the "object life cycle" can be a bit different in Neo4j, the typical use of callbacks may not always apply.

Count Zero
  • 630
  • 1
  • 6
  • 15
  • 1
    I think Max's factoring is worth looking at, but I'm also curious about the underlying issue. What do your create/save statements look like? If you are creating, for example, and specifying the input and the output at that time, then maybe there's an argument for having `after_save` happen after all relationship creations. I think it could be worth adding an issue to the `neo4j` repo: github.com/neo4jrb/neo4j/issues/new – Brian Underwood Jul 14 '15 at 16:44