Ruby On Rails, Design, Simplicity, Web 2.0, Ajax, Mac and Tons of Pizza.

Apr 15

Create getter and setter on a valorized variable

Posted by Sandro Paganotti in Ruby on Rails - 3 comments digg this add to delicious

I noticed that none of the attr_* (attr_reader, attr_accessor and attr_writer) has the capability to let you assign a value to the variable you’re creating.

This can be solved using the Class constructor method but it can lead you to define the initialize function just to make these assignements.

So I created an attr_with_value method that can be added to the Object class in order to make it avaiable to all the Classes and that can let you use the following sintax:


class Printer
  attr_with_value :pippo, 5
  attr_with_value :printer, Proc.new{|a| p "saying: #{a}" }
end

pr = Printer.new
pr.printer.call("hello!")
# "saying: hello!" 
p pr.pippo
# 5

pr.printer = Proc.new{|a| p "whispering #{a}"}
pr.printer.call("hello!")
# "whispering hello!" 

In the following snippet you can find the method:


class Object
  def self.attr_with_value(name,value)

    class_eval <<-EOS

      define_method(name.to_s) do      
        @#{name}_starter.nil? ? value : @#{name} 
      end

      define_method(name.to_s+"=") do |val|
        @#{name}_starter = true; @#{name} = val
      end

    EOS

  end  
end

I’m looking for a way to improve it (in particular I’m trying to find a clever way to handle the initial assignment), so every suggestion is welcome!

Comments

  • Anny

    Posted on April 19

    I have wanted one of these forever! THANKS for the great work
  • Simon

    Posted on April 21

    How about changing the getter to this:
      defined?(@#{name})? @#{name} : value
    
    And replacing the setter with just the normal:
      @#{name} = val
    
  • Matt Williams

    Posted on May 09

    Here's another variation; it allows you to pass in a block as the "default" value (it's based off my solution to the metakoans ruby quiz): --- snip --- class Object def self.attr_with_value(*arg,&block) arg = arg[0] if arg.length == 1 name = arg if arg.instance_of? String name = arg.to_s if arg.instance_of? Symbol if arg.instance_of? Array name = arg[0] value = arg[1] end self.send(:define_method, name) { if instance_variables.include? "@#{name}" self.instance_eval "@#{name}" else if block_given? instance_eval &block else value end end } self.send(:define_method, "#{name}="){ |value| self.instance_eval "@#{name} = value" } end end ----- Here's a test class: class Foo attr_accessor :bar attr_with_value(:fud) {instance_variables.include?("@bar") ? @bar : ' '} attr_with_value :fi, 10 end I could probably optimize it some, but it's working ;-)

Post a comment

Categories:

Tags:

Powered by Mephisto, Valid XHTML 1.1, Valid CSS - Supported by Wave Factory