16

After getting all values from model, I want to add another custom attribute to the ActiveRecord class (this attribute is not a column in db) so that I could use it in view, but rails does not allow me to add one. What should I add in its model class?

@test.all

@test.each do |elm|
    elm[:newatt] = 'added string'
end

error:

can't write unknown attribute `newatt'
Ladiko
  • 279
  • 2
  • 4
  • 10
  • What do you want to do with it? Only a scope or do you want so save it to the database? – Mindbreaker Aug 25 '13 at 13:03
  • I do not want to save it to database. I'm doing some processing in that each (see updated code), and I want to add some extra info to each element. It will be used in the view only. – Ladiko Aug 25 '13 at 13:21
  • Possible duplicate of [Adding extra run-time attribs to an activerecord object](http://stackoverflow.com/questions/7584592/adding-extra-run-time-attribs-to-an-activerecord-object) – mlt Feb 16 '17 at 21:47

8 Answers8

16

try this

class Test < ActiveRecord::Base
  attr_accessor :newattr
end

you can access it like

@test = Test.new
@test.newattr = "value"

As you may notice this a property, not a hash. so it uses . syntax. however, if you need it to behave like an hash you can do this without defining a new attribute

@test.all
@test.each do |elm|
    new_elm = {}
    new_elm[:newatt] = 'added string'
end

Lastly, I am not exactly sure what you are trying to do. if this doesn't make sense to you, kindly rephrase your question so we can understand the problem better.

CuriousMind
  • 33,537
  • 28
  • 98
  • 137
5

Define virtual attributes as instance variables:

attr_accessor :newattr
boulder
  • 3,256
  • 15
  • 21
5

If you want this only for your views and do not have any other purpose then you need not to add attr_accessor

@test.all.select('tests.*, "added string" as newattr')

here you are adding newattr attribute for query output of ActiveRecord with a value 'added string'

sajith
  • 2,564
  • 8
  • 39
  • 57
3

I think you mean to assign @test to the ActiveRecord query, correct? Try:

@test = MyARClass.select("*, NULL as newatt")
@test.each {|t| t[:newatt] = some_value}

Another related solution is to make it a singleton class method, though you'd have to jump though more hoops to make it writeable and I intuitively feel like this probably incurs more overhead

@test = MyARClass.all
@test.each do t
  def t.newatt
    some_value
  end
end

Using the second method, of course you'd access it via @test.first.newatt, rather than @test.first[:newatt]. You could try redefining t.[] and t.[]=, but this is starting to get really messy.

Andrew Schwartz
  • 4,440
  • 3
  • 25
  • 58
2

If it's really just temporary it doesn't have to be in the object:

@test.all
@test_temp = []

@test.each do |elm|
    @test_temp << {:elm => elm, :newatt => 'added string'}
end   

Otherwise, there are also good answers here.

Community
  • 1
  • 1
Chris Kerlin
  • 130
  • 1
  • 4
2

If it temporary, you can try this:

@test.all.map{ |t| t.attributes.merge({ newatt: "added string" }) }

Gundam Meister
  • 1,425
  • 2
  • 19
  • 29
0
@test.all

@test.each do |elm|
    write_attribute(:newatt, "added string")
end
Hugo
  • 134
  • 1
  • 10
0

I met the same issue. and successfully bypass using instance_eval

@test.all

@test.each do |elm|
  elm.instance_eval { @newatt = 'added string' }
end

normally it doesn't run into issue, when use attr_accessor. it appears when other DSL override "newattr=" which cause the issue. In my case, it's money-rails "monetize :newatt"

Explicitly use write_attribute doesn't work as it is the reason to raise exception in rails 4.x

roadtang
  • 1
  • 2