为什么这个Ruby代码没有被触发?

时间:2015-05-05 19:40:23

标签: ruby

我有这个类来存储禁令:

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的新手)。

3 个答案:

答案 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)

@Piotr对你的问题给出了一个很好的答案。考虑到你是Ruby新手,我想扩展他所说的内容。首先,让我们看看你的代码,稍作修改。

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)返回truefalse。现在什么是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地址(静态或其他)会出现以下问题:

  1. 每次应用程序重新启动时,禁用的IP列表都将丢失。

  2. 该列表不具备可扩展性(如果您的应用程序用于生产,则无法在应用程序的负载平衡实例之间共享该列表。)

  3. 在多线程环境中列表可能已损坏(我不认为您应该担心这一点,但这是可能的)。

  4. 我建议使用数据库来存储信息。如果您使用Rails,它应该像使用rails生成器添加模型类一样简单。

    使用数据库应该有助于解决所有这些问题。