Larry
Larry

Reputation: 397

Making virtual attributes be part of one Hash

I have a model which has one actual column in the database. This column is stored as a JSON string of configuration. I use a bunch of virtual attributes which I want to map inside of this configuration JSON attribute. I basically dont want to create a bunch columns in the db, but rather use this one JSON attribute to contain everything. Is there a cleaner way than the below defs of achieving this?

class Device < ActiveRecord::Base
  attr_accessible :configuration
  serialize :configuration, JSON

  attr_accessor :background_color, :title

  # below is ew
  def background_color; self.configuration["background_color"]; end
  def background_color=(value); self.configuration["background_color"] = value; end

  def title; self.configuration["title"]; end
  def title=(value); self.configuration["title"] = value; end
end

Ideally i'd be looking for something like attr_maps_to_hash :configuration, [:background_color, :title]. Does something like this exist?

Upvotes: 0

Views: 653

Answers (3)

Matt Knox
Matt Knox

Reputation: 11

Rails as of 3.2 has key-value stores built into ActiveRecord-see here:

Where can i read more about Rails 3.2's Data Store with key-value in textfield?

in your case you can have a text field named configuration and then do this:

class Device < AR::Base store :configuration, accessors: [:title, :background_color, ...] ...

This should work fine with forms, etc..

Upvotes: 1

Matt Helm
Matt Helm

Reputation: 1

Two methods come to mind.

First, you could have an array of your attributes [:background_color, :title] and then iterate over them while calling define_method. You would defined two methods, define(method_name) and define("#{method_name}=").

Second, a similar idea but using method missing.

def method_missing(method_name, *args, &block)
  ...see if it's a get or set...
  ...do your stuff...
  ...rain dance...
  ...yay...
end

Upvotes: 0

siong1987
siong1987

Reputation: 1053

You can use ActiveRecord::Store for this.

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ]
end

u = User.new(color: 'black', homepage: '37signals.com')
u.color                          # Accessor stored attribute
u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor

# Add additional accessors to an existing store through store_accessor
class SuperUser < User
  store_accessor :settings, :privileges, :servants
end

If you are using PostgreSQL, check out HStore.

Upvotes: 1

Related Questions