我有一个类,它带有指向该类中方法的lambda哈希。
Thing.new.call
=> undefined local variable or method `do_foo' for Thing:Class
我在等
Thing.new.call
=> foo
=> bar
lambda似乎调用do_foo
和do_bar
作为类方法,而不是实例方法。我能修一下吗?我不想使用send(command)
,因为命令可以由用户更新,并且可能存在黑客攻击风险。
链接到replit
class Thing
def initialize
@commands = [:foo, :bar]
end
SAFE_COMMANDS = {
foo: -> { do_foo },
bar: -> { do_bar }
}
def call
@commands.each { |command| SAFE_COMMANDS[command].call }
end
private
def do_foo
puts 'foo'
end
def do_bar
puts 'bar'
end
end
这里的问题是SAFE_COMMANDS
数组和其中的lambda是在类上下文中定义的,因此它试图调用类方法。
您可以将实例传递给lambdas,以便调用其实例方法。
class Thing
def initialize
@commands = [:foo, :bar]
end
SAFE_COMMANDS = {
foo: -> (obj){ obj.do_foo },
bar: -> (obj){ obj.do_bar }
}
def call
@commands.each { |command| SAFE_COMMANDS[command].call(self) }
end
private
def do_foo
puts 'foo'
end
def do_bar
puts 'bar'
end
end
然而,你会得到以下错误:
private method `do_foo' called for #<Thing:0x00007fdc018399f8 @commands=[:foo, :bar]> (NoMethodError)
这是因为这些方法是私有的,因此Lambda无法访问。
以下是我的建议。使用#send
,但首先过滤命令,如果发出非法命令,则引发异常(或类似情况(。
class Thing
def initialize
@commands = [:do_foo, :do_bar, :do_bad_stuff, :do_really_bad_stuff]
end
SAFE_COMMANDS = %i{
do_foo
do_bar
}
def call
illegal_commands = @commands - SAFE_COMMANDS
raise("illegal commands: #{illegal_commands.join(', ')}") unless illegal_commands.empty?
@commands.each { |command| self.send(command) }
end
private
def do_foo
puts 'foo'
end
def do_bar
puts 'bar'
end
end
这将创建一个不在SAFE_COMMANDS
数组中的任何命令的数组。如果该数组为空,那么我们只收到了合法的命令,然后继续。否则,我们会引发一个异常,并很好地格式化这些非法命令,告诉用户他们"搞砸了">
illegal commands: do_bad_stuff, do_really_bad_stuff (RuntimeError)
但我建议在初始化时检查一下。我想您最终会以某种方式将这些命令传递给实例,所以我进行了更改。
class Thing
def initialize( *commands )
commands = commands.map(&:to_sym)
illegal_commands = commands - SAFE_COMMANDS
raise("illegal commands: #{illegal_commands.join(', ')}") unless illegal_commands.empty?
@commands = commands
end
SAFE_COMMANDS = %i{
do_foo
do_bar
}
def call
@commands.each { |command| self.send(command) }
end
private
def do_foo
puts 'foo'
end
def do_bar
puts 'bar'
end
end
# the exception is raised here
thing = Thing.new(:do_foo, :do_bar, :do_bad_stuff, :do_really_bad_stuff)
# rather than here
thing.call
我认为用lambdas过滤命令列表而不是恶作剧是方式更安全的安全方式,也不那么复杂。