Is is possible to return virtual attributes automatically in Rails models?

1.2k Views Asked by At

Given:

class Foo
  has_one :bar

  def bar_name
    bar.name
  end
end

class Bar
  belongs_to :foo
end

In the console or in a view, I can @foo.bar_name to get 'baz'.

I'm aware that I can @foo.as_json(methods: :bar_name) to get {"id"=>"abc123", "bar_name"=>"baz"}.

I could also denormalize the attribute and make it non-virtual, but I would rather not do that in this case.

Is it possible to automatically return the model with the virtual attribute included?

#<Foo id: "abc123", bar_name: "baz">

I want to do this because I am constructing a large object with nested collections of models, and the as_json call is abstracted away from me.

2

There are 2 best solutions below

3
engineersmnky On BEST ANSWER

Not 100% sure I understand if your concern is related to as_json but if so this will work

class Foo
  has_one :bar

  def bar_name
    bar.name
  end
  def as_json(options={})
    super(options.merge!(methods: :bar_name))
  end
end

Now a call to @foo.as_json will by default include the bar_name like your explicit example does.

Ugly would not recommend but you could change the inspection of foo e.g. #<Foo id: "abc123", bar_name: "baz"> as follows

class Foo
  def inspect
    base_string = "#<#{self.class.name}:#{self.object_id} "
    fields = self.attributes.map {|k,v| "#{k}: #{v.inspect}"}
    fields << "bar_name: #{self.bar_name.inspect}"
    base_string << fields.join(", ") << ">"
  end
end

Then the "inspection notation" would show that information although I am still unclear if this is your intention and if so why you would want this.

2
vich On

You could use attr_accessor - per the Rails docs:

Defines a named attribute for this module, where the name is symbol.id2name, creating an instance variable (@name) and a corresponding access method to read it. Also creates a method called name= to set the attribute.