Ruby gets and sets one method

I am wondering what is the canonical path in Ruby for creating custom setter and getter methods. I usually do this through attr_accessor , but I'm in the context of creating a DSL. In DSL, setters are called like this (using the = sign creates local variables):

 work do duration 15 priority 5 end 

Therefore, they should be implemented as follows:

 def duration(dur) @duration = dur end 

However, this makes the getter implementation a bit complicated: creating a method with the same name but without arguments will simply overwrite setter.

So, I wrote my own methods that perform both tuning and getting:

 def duration(dur=nil) return @duration = dur if dur return @duration if @duration raise AttributeNotDefinedException, "Attribute '#{__method__}' hasn't been set" end 

Is this a good way? Here is the gist with test cases:

Ruby Custom Getters and Setters

Thanks!

+4
source share
4 answers

The more complicated case is that you want to set the duration to nil. I can imagine two ways to do this.

 def duration(*args) @duration = args.first unless args.empty? @duration end 

Allow people to pass any number of arguments and decide what to do based on the number. You can also throw an exception if more than one argument is passed.

Another way -

 def duration(value = (getter=true;nil)) @duration = value unless getter @duration end 

This makes little use of the default arguments: they can be almost any expression.

When called without arguments, getter set to true, but when an argument is provided (even if it is zero), the default value is not evaluated. Due to how the scope of the local getter variable works, zero ends.

Perhaps too smart, but the body of the method itself is cleaner.

+9
source

Something like this, I prefer to separate the base class from DSL. That is, create a Work class that has the usual accessors, duration and duration= . And to use this class through DSL, wrap the working instance with something that might cause access situations, for example:

 class AccessorMultiplexer def initialize(target) @target = target end def method_missing(method, *args) method = "#{method}=" unless args.empty? @target.send method, *args end end 

Wherever you want to use your working class through DSL, you wrapped it with AccessorMultiplexer.new(work) .

If you are opposed to metaprogramming in a wrapper, you can always create a specific WorkDSL wrapper that does the same without using method_missing . But it will maintain separation and will not allow your Work class to be contaminated with fads of DSL syntax. You might want to use the Work class elsewhere in your code where the DSL will be on the way. In rake or script or - who knows.

(adapted from my answer on codereview .)

+2
source

It seems wonderful to me, although it seems strange that you make a mistake if the value has not been set. This is what I usually did:

 def duration(dur=nil) @duration = dur if dur @duration end 

However, this is a simplified approach because it means that you cannot set @duration back to nil using only this method

0
source

I usually do it

 def duration dur='UNDEFINED' @duration = dur if dur != 'UNDEFINED' @duration end 

You can replace UNDEFINED with your favorite item.

0
source

All Articles