mrzasa
mrzasa

Reputation: 23347

Delegate all methods but setters

In my Rails application I need a class that wraps an ActiveRecord object and disable all its setters. Other methods should be delegated to inner object.

Example:

class Person < ActiveRecord::Base
  attr_accessible :name, :age
  def adult?
    age >= 18
  end
end

class PersonWrapper
  def initialize(person)
    @person = person
  end
end

person = Person.new(name: "John", age: 12)
wrapper = PersonWrapper.new(Person)

wrapper.age # => 12
wrapper.adult? # => false
wrapper.age = 123 # error

Is there any convenient way to do it besides method_missing?

Upvotes: 0

Views: 1378

Answers (2)

Stefan
Stefan

Reputation: 114268

If you just want to prevent updates to the database you can use readonly!:

person = Person.new(name: "John", age: 12)
person.readonly!
person.age = 123  #=> works
person.save       #=> raises ActiveRecord::ReadOnlyRecord

Or you could freeze the record:

person = Person.new(name: "John", age: 12)
person.freeze
person.age = 123  #=> raises RuntimeError: can't modify frozen Hash

Upvotes: 1

Slicedpan
Slicedpan

Reputation: 5015

class Wrapper
  def self.create(ar_model)
    klass = Class.new
    ar_model.attributes.each do |k, v|
      klass.send(:define_method, k) { @obj[k] }
    end
    klass.send(:define_method, :initialize) {|obj| @obj = obj}
    klass.new(ar_model)
  end
end

Usage

p = Person.first
wrapper = Wrapper.create(p)
wrapper.adult? # => false
wrapper.age = 123 #method not defined

Probably not as convenient as method_missing but it works

[EDIT]

This way means that if the wrapped object does not have some attributes set, then getters will not be created in the wrapping class, if you swap the line

ar_model.attributes.each do |k, v|

for this one

ar_model.class.column_names.each do |k|

then a getter will be created for each column whether the wrapped object has the corresponding attribute set or not

Upvotes: 0

Related Questions