ruby-on-railsrubydecoratoractivemodelvirtual-attribute

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


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.


Solution

  • 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.