LINUX.ORG.RU

define_method context

 ,


0

2
#!/usr/bin/env ruby

class C
    def self.bparam(sym)
        define_method sym do 
            yield
        end
    end

    def initialize(x)
        @x = x
    end

    bparam(:x) {@x}
    bparam(:xx) {x + x}
end

a = C.new(10)

puts a.x
puts a.xx
true
./1.rb:15:in `block in <class:C>': undefined local variable or method `x' for C:Class (NameError)
        from ./1.rb:6:in `block in bparam'
        from ./1.rb:21:in `<main>'

Можно ли сделать так, чтобы yield выполнялся не в контексте класса С, а экземпляра a? Реальный bparam сложнее приведенного.

★★

#!/usr/bin/env ruby

class C
    def self.bparam(sym, &block)
        define_method sym, &block
    end

    def initialize(x)
        @x = x
    end

    bparam(:x) {@x}
    bparam(:xx) {x + x}
end

a = C.new(10)

puts a.x
puts a.xx

noomorph
()
Ответ на: комментарий от kelyar

убери self и запихни вызовы в конструктор. на вид должно работать

class C
    def bparam(sym, &block)
        self.class.define_method sym do
            block.call
        end
    end

    def initialize(x)
        @x = x
        bparam(:x) {@x}
        bparam(:xx) {x + x}
    end

end

a = C.new(10)

puts a.x.nil?
puts a.xx
./1.rb:5:in `bparam': private method `define_method' called for C:Class (NoMethodError)
        from ./1.rb:12:in `initialize'
        from ./1.rb:18:in `new'
        from ./1.rb:18:in `<main>'
ival ★★
() автор топика
Ответ на: комментарий от ival

На реальный bparam обобщить не получилось.

ival ★★
() автор топика
Ответ на: комментарий от kelyar

Всем еще раз спасибо, private можно вызвать через send. И того.

#!/usr/bin/env ruby

$call_flag = false

class C
    def bparam(sym, default, &block)
        new_block = Proc.new { 
            if $call_flag 
                block.call 
            else 
                default 
            end
        }   
        self.class.send(:define_method, sym, new_block)
    end

    def initialize(x)
        @x = x
        bparam(:x, 1) {@x}
        bparam(:xx, 2) {x + x}
    end

end

a = C.new(10)

puts a.xx
$call_flag = true
puts a.xx
2
20
ival ★★
() автор топика
Ответ на: комментарий от ival

Да ты же наркоман и для каждого экземпляра класса заново определяешь эти методы. Не надо так. Можно как-нибудь вот так, например:

[1] pry(main)> class C
[1] pry(main)*   methods = {
[1] pry(main)*     one: ->(x) { x + 1 },
[1] pry(main)*     two: ->(x) { x + x }
[1] pry(main)*   }  
[1] pry(main)*   
[1] pry(main)*   methods.each do |name, block|
[1] pry(main)*     define_method(name, &block)
[1] pry(main)*   end  
[1] pry(main)* end  
=> {:one=>#<Proc:0x00000001475af8@(pry):3 (lambda)>,
 :two=>#<Proc:0x00000001475ad0@(pry):4 (lambda)>}
[2] pry(main)> C.new.one(5)
=> 6
[3] pry(main)> C.new.two(5)
=> 10

Вместо

$call_flag
можно использовать
block_given?
, кстати, если он тебе нужен только для того, что проверять, не передан ли блок.

S-Mage ★★
()
Ответ на: комментарий от S-Mage

$call_flag должен менять поведение метода даже после его определения. Нужно иметь функцию, которая получает блок в качестве параметра, конструирует на его основе новый и вот его уже передает в define_method.

Да ты же наркоман и для каждого экземпляра класса заново определяешь эти методы

Да, это явный overhead, но почему-то в ruby без него не получается.

ival ★★
() автор топика
Ответ на: комментарий от ival

явный overhead, но почему-то в ruby без него не получается.

обычно это значит, что ты что-то не так делаешь или хочешь странного )

kelyar ★★★★★
()
Ответ на: комментарий от S-Mage

Чего? Какой метод ты хочешь модифицировать (и зачем)?

Ну это довольно частая задача. Вот есть у меня, например, блок, который что-то там вычисляет. Часто по нему хочется построить блок, который вычисляет нужное значение только один раз, на подобии

def obj
     if @obj .nil? 
         @obj = calc_obj
     end
    @obj
end 

Если таких блоков с десяток, хорошо бы иметь возможность писать что-то типа

cached_object(:obj) { calc_obj body }

и ruby сам оборачивал данный блок в if @obj.nil?

Во втором примере подразумевается, что методов, переключаемых через $call_flag много.

ival ★★
() автор топика
Последнее исправление: ival (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.