Bob
Bob

Reputation: 8504

Rails 3 add virtual attribute dynamically

My setup: Rails 3.0.9, Ruby 1.9.2

I have my reasons for doing this but what I need is a way to add a virtual attribute to an activerecord resultset dynamically. That means I am not using attr_accessor in the model and instead wish to dynamically add virtual attributes to a resultset.

For example,

users = User.all
#a user has following attributes: name, email, password

What I like to do is say add (without using attr_accessor) a virtual attribute status to users, is that possible?

Upvotes: 10

Views: 6333

Answers (4)

Rafael Oliveira
Rafael Oliveira

Reputation: 2923

You could simply do:

users.each { |user| user.class_eval { attr_accessor :status } }

All users will have user.status and user.status = :new_status available.

Upvotes: 1

Daniel Rikowski
Daniel Rikowski

Reputation: 72544

If your attributes are read-only, you can also add them by selecting them in your database query. Fields which appear in the SQL result result will automatically be add as attributes.

Examples:

User.find_by_sql('users.*, (...) AS status')
User.select('users.*, joined_table.status').join(...)

With these examples you'll have all User attributes and an additional status attribute.

Upvotes: 1

Tilo
Tilo

Reputation: 33752

you can do the following:

add an attribute "extras" which will be accessed as a Hash, and which will store any additional / dynamic attributes -- and then tell Rails to persist that Hash via JSON in ActiveRecord or Mongo or whatever you use

e.g.:

class AddExtrasToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :extras, :text    # do not use a binary type here! (rails bug)
  end
  ...
end

then in the model add a statement to "serialize" that new attribute -- e.g. that means it's persisted as JSON

class User < ActiveRecord::Base
...
serialize :extras
...
end

You can now do this:

   u = User.find 3

   u.extras[:status] = 'valid'
   u.save

You can also add a bit of magic to the User model, to look at the extras Hash if it gets a method_missing() call

See also: Google "Rails 3 serialize"

Upvotes: 2

apneadiving
apneadiving

Reputation: 115541

You should do this:

users.each do |user|
  user.instance_eval do
    def status
      instance_variable_get("@status")
    end        
    def status=(val) 
      instance_variable_set("@status",val)
    end
  end
end

Upvotes: 16

Related Questions