我有这个类来存储禁令:
class Bans < Hash
def ban(ip, time, reason)
self[ip] = {time: time, reason: reason}
end
def banned?(ip)
if self.include?(ip)
return true
else
return false
end
end
end
我在另一个班级的其他地方使用它:
if @bans.banned?(@ip)
// code
end
问题是即使禁止IP地址,代码也不会被触发。删除if
子句并且只运行// code
,因此if
条款一定是问题,但我不明白我做错了什么(我是Ruby的新手)。
答案 0 :(得分:3)
self.keys.include? ip
应该有效:)
完整方法:
def banned?(ip)
keys.include? ip
end
修改强>
如何使用该课程:
@bans = Bans.new()
@bans.ban('123', 12, 'because')
@bans.banned?('123') #=> true
答案 1 :(得分:1)
class Bans < Hash
def ban(ip, time, reason)
puts "self before self[#{ip}]= #{self}"
self[ip] = {time: time, reason: reason}
puts "self after self[#{ip}]= #{self}"
end
def banned?(ip)
keys.include?(ip)
end
end
您已创建了一个类Bans
,它是Hash
的子类。如果我们看一下:
Bans.ancestors
#=> [Bans, Hash, Enumerable, Object, Kernel, BasicObject]
我们看到Bans
继承了其祖先的所有方法。我们可以在数组中返回所有Bans
'实例方法:
Bans.instance_methods
#=> [:ban, :banned?, :rehash,..., :to_h, :__send__, :__id__]
在我的电脑上(Ruby v2.2.1),Bans.instance_methods.size #=> 152
。有时它写得很方便:
Bans.instance_methods.sort
#=> [:!, :!=,..., :ban, :banned,..., :values_at, :zip]
或
Bans.instance_methods.include?(:banned?)
#=> true
您可以通过添加参数Bans
来获取false
(而不是祖先)上定义的实例方法:
Bans.instance_methods(false)
=> [:ban, :banned?]
这也可能有所帮助:
Bans.instance_method(:ban).owner
#=> Bans
Bans.instance_method(:values_at).owner
#=> Hash
Bans.instance_method(:chunk).owner
#=> Enumerable
所有这一切也可用于确定Bans
'类方法。只需将method
替换为instance_method
即可。例如:
Bans.methods.sort
# [:!, :!=,...:untrusted?]
Bans.methods(false)
#=> [] (since we didn't define any)
您看到我已添加行以在self
之前和之后打印self[ip] = {time: time, reason: reason}
的值。
要调用Bans
'实例方法之一,我们需要创建该类的实例:
b = Bans.new
#=> {}
然后将方法发送到实例:
b.ban(:wyatt, 'high noon', 'gunfight')
# self before self[wyatt]= {}
# self after self[wyatt]= {:wyatt=>{:time=>"high noon", :reason=>"gunfight"}}
这几乎与:
相同b.send(:ban, :wyatt, 'high noon', 'gunfight')
读取,“将带有参数:ban
,:wyatt
,'high noon'
的方法(或”消息“)'gunfight'
发送到实例b
。建议你这样想。(Ruby确实如此。)唯一的区别是send
必须是使用b.ban
的公共方法,但在使用send
时可以是私有方法。
理解Ruby的关键是要知道代码中不同点的self
。因此,我建议您使用puts self
行添加代码。
接下来,您要调用banned?
。你写道:
def banned?(ip)
if self.include?(ip)
return true
else
return false
end
end
我们试一试:
b.banned?(:wyatt)
#=> true
b.banned?(:doc)
#=> false
成功!但是,我们可以简化。
首先,你所拥有的是:
def banned?(ip)
self.include?(ip)
end
因为self.include?(ip)
返回true
或false
。现在什么是self
?我们在上面看到它等于:
s = {:wyatt=>{:time=>"high noon", :reason=>"gunfight"} }
因此banned?
的操作行评估为:
s.include?(ip)
s
是否有方法include?
?我们来看看:
s.methods(:include?)
#=> true
s.method(:include).owner
#=> Hash
因此我们可以查找Hash#include?的文档,该文档会告诉我们s
是否有一个键ip
。 (Hash#key?或Hash#has_key?执行相同的操作。在三者中,我说key?
最常用,include?
,最少。)
现在还有一件事。假设我们写道:
def banned?(ip)
key?(ip)
end
方法key?
没有明确的接收者(以前self
是明确的接收者)。当没有明确的接收者时,Ruby认为它是self
。这就是Ruby将key?(ip)
评估为self.key?(ip)
的原因。我建议你在不需要的时候排除self.
。警告:有时您需要包含self.
以避免歧义。例如,你不能写:
[ip] = {time: time, reason: reason}
作为ban
中的操作线。你会及时了解这一点。
答案 2 :(得分:0)
你有两个有趣的答案可以回答你的问题。
现在您可以创建一个Bans的全局静态实例,您可以在整个应用程序中轻松使用它来查看IP禁令:
class Bans < Hash
def ban(ip, time = Time.now, reason = nil)
puts "self before self[#{ip}]= #{self}"
self[ip] = {time: time, reason: reason}
puts "self after self[#{ip}]= #{self}"
end
def banned?(ip)
self.include?(ip)
end
end
#create BANS instance static variable
BANS = Bans.new
现在,您可以检查/设置IP禁止:
#set
BANS.ban '127.0.0.1', Time.now, :test
# check
BANS.banned? '127.0.0.1'
但是,我不确定您的代码的设计是否最佳。
在变量中保存禁止的IP地址(静态或其他)会出现以下问题:
每次应用程序重新启动时,禁用的IP列表都将丢失。
该列表不具备可扩展性(如果您的应用程序用于生产,则无法在应用程序的负载平衡实例之间共享该列表。)
在多线程环境中列表可能已损坏(我不认为您应该担心这一点,但这是可能的)。
我建议使用数据库来存储信息。如果您使用Rails,它应该像使用rails生成器添加模型类一样简单。
使用数据库应该有助于解决所有这些问题。