2012-07-02 4 views
4

Мне интересно, каков канонический путь в Ruby для создания пользовательских методов setter и getter. Обычно я делаю это через attr_accessor, но я в контексте создания DSL. В DSL, сеттера называются так (с помощью = знака будет создавать локальные переменные):Ruby get и set в одном методе

work do 
duration 15 
priority 5 
end 

Таким образом, они должны быть реализованы следующим образом:

def duration(dur) 
@duration = dur 
end 

Однако это делает реализацию геттер A бит сложный: создание метода с тем же именем, но без аргументов просто перезапишет setter.

Так что я написал пользовательские методы, которые делают как садилось, и получение:

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

Это хороший способ пойти об этом? Вот суть с тестами:

Ruby Custom Getters & Setters

Спасибо!

ответ

9

сложнее случай, если вы хотите установить продолжительность до нуля. Я могу думать о двух способов сделать это

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

Позвольте людям передать любое количество аргументов и решить, что делать на основе числа. Вы также можете создать исключение, если передано более одного аргумента.

Другой способ

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

Это использует аргументы по умолчанию немного: они могут быть в значительной степени любое выражение.

При вызове без аргументов getter устанавливается в значение true, но когда предоставляется аргумент (даже если он равен нулю) значение по умолчанию не оценивается. Из-за того, как работает локальная область видимости, getter заканчивается нулем.

Возможно, тоже умный, но сам метод сам чистит.

+0

Это может быть «умным», но это довольно известная идиома, поэтому я бы, конечно же, позволил ему перейти в обзор кода. –

+0

Спасибо, это отличные идеи. У меня нет варианта использования для установки значения в ноль, но хорошо знать, как его покрыть. – abbottjam

0

Это кажется мне хорошим, хотя кажется странным, что вы делаете ошибку, если значение не было установлено. Это то, что я обычно делаю:

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

Однако это упрощенный подход, потому что это означает, что вы не можете установить @duration обратно nil, используя только этот метод

+0

не ошибка (это было бы слишком резким), но исключение, то есть вход не является незаконным, а ложным. Причина, по которой я предпочитаю поднимать исключение к возврату nil, заключается в том, что он не распространяется на вызывающего и обнаруживается мгновенно, в отличие от того, когда метод вызывается на nil. [Смотрите этот скринкаст] (https://www.destroyallsoftware.com/screencasts/catalog/how-and-why-to-avoid-nil) – abbottjam

2

Для чего-то вроде этого, я предпочитаю отделить базовый класс от DSL. То есть, создайте класс Work, который имеет обычные аксессоры, duration и duration=. И использовать этот класс через DSL, оберните экземпляр работы с чем-то, что может ссылаться на аксессорах ситуативно, как это:

class AccessorMultiplexer 

    def initialize(target) 
    @target = target 
    end 

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

end 

Везде, где вы хотите использовать свой класс Работы через DSL, вы бы обернуть его AccessorMultiplexer.new(work).

Если вы против метапрограммирования в обертке, вы всегда можете создать определенную оболочку WorkDSL, которая делает то же самое, не используя method_missing. Но он будет поддерживать разделение и не позволит вашему классу Work быть загрязненными причудами синтаксиса DSL. Возможно, вы захотите использовать класс Work в другом месте вашего кода, где DSL будет в пути. В грабли или скрипт или - кто знает.

(Взято из моего ответа на codereview.)

+0

Мне нравится идея разделения, поэтому я буду преследовать эти идеи. Но что бы вы использовали вместо 'method_missing' в обертке WorkDSL? Я бы предпочел не использовать этот метод - кажется слишком «пойманным» для этой ситуации. Благодаря! – abbottjam

0

Я обычно делаю это

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

Вы можете заменить UNDEFINED с вашей любимой вещью

Смежные вопросы