send() 方法有什么用途?

39

请问一下,下面列出的 'send()' 方法是用来做什么的?当我阅读这段代码时,它看起来毫无意义。

这是一个使用 Ruby 1.8.7 和 Rails 1.2.3 的 Rails 应用程序。请不要向我谈论升级,因为这是客户的环境,所以我没有那样的闲暇。

不用说,我指的语句类似于这样;

def do_schedule
  @performance = Performance.new(params[:performance])
  @performer = Performer.find(params[:performer_id])
  selected_track = params[:selected_track]
  if FileTest.exists?(File.expand_path(@performer.photo))
    @performance.photo = File.open(File.expand_path(@performer.photo))
  end

  @performance.audio = File.open(File.expand_path(@performer.send(selected_track)))

  if @performance.save
    flash[:notice] = 'Performer scheduled.'
    redirect_to :controller => :performer, :action => :index
  else
    render :action => 'schedule'
  end
end

表演者模型

class Performer < ActiveRecord::Base
  file_column :audio_one
  file_column :audio_two
  file_column :audio_three
  file_column :photo

  belongs_to :festival
  validates_presence_of :name, :first_name, :last_name, :address, :city, :state, :zip, :daytime_phone, :availability, :stages
  validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
  validates_confirmation_of :email

  validates_presence_of :audio_one, :audio_two, :audio_three, :photo, :if => :submitted

  after_create :salt_access_key
  serialize :availability
  serialize :stages

  attr_accessor :other_type_of_music
  before_save :set_other_type

  def set_other_type
    if type_of_music == 'Other'
      self.type_of_music = "Other - #{other_type_of_music}" unless other_type_of_music.blank?
    end
  end

  def salt_access_key
    update_attribute(:access_key, Digest::SHA1.hexdigest("--#{self.id}--#{self.name}--#{self.festival.year}"))
  end

  def preferred_stages
    stages = []
    festival = Festival.find(self.festival_id.to_i)
    self.stages.collect { | key, value |
      id = key.gsub(/[\D]/, '').to_i
      if id > 0
        stages << festival.performance_stages.find(id).name
      end
    }
    return stages
  end
end

这个控制器包含它是性能。我一直在搜索谷歌,试图弄清楚“@performer.send(selected_track)”的实际用途,但感觉自己像在旋涡中划船。

3个回答

73

send方法是用来向一个对象发送一个方法消息的Ruby实现,其工作方式如下:

class Car
  
  def start
    puts "vroom"
  end

  private

  def engine_temp
    puts "Just Right"
  end

end

@car = Car.new
@car.start # output: vroom
@car.send(:start) # output: vroom

那是基础知识,还有一条重要信息是:send 方法可以让你向私有方法发送消息,不仅限于公共方法。请注意保留 HTML 标签。
@car.engine_temp  # This doesn't work, it will raise an exception
@car.send(:engine_temp)  # output: Just Right

关于你的具体发送调用会做什么,很可能在Performer类中有一个def method_missing被设置为捕获并执行某些操作。


谢谢ctcherry!很棒的例子!至于你刚才提到的method_missing参考。不,Performer类实际上只有4个方法。3个(index、show和destroy)以及一个私有的lookup_festival。这是否意味着send无法正常工作? - Skittles
发送仍将起作用。它触发的内容取决于传递给它的selected_track变量是什么。您可以记录并向我们显示它,我们可能能够为您提供更多信息。 - Chris Cherry
看起来可能会加载其中一个 file_columns,例如 :audio_one,它被视为文件名,然后传递到 File.read 中,文件数据本身被分配给 @performance.audio - Chris Cherry
所以在这个例子中,任何能够发送请求到 do_schedule 操作的人都可以调用 Performer 上的任何方法?对我来说似乎是个坏主意... - Ajedi32
你是正确的,在生产代码中,你会想要白名单该参数以确保没有任何恶意行为。或者使用不同的方法。这个问题是关于解释现有代码的。 - Chris Cherry
显示剩余5条评论

15

send用于向对象传递方法(及其参数)。当您事先不知道方法名称时,它非常方便,因为它仅表示为字符串或符号。

例如:Performer.find(params[:performer_id])Performer.send(:find, params[:performer_id])相同。

请注意,当使用send时,仅仅依赖于params可能是危险的:如果用户传递destroydelete会实际上删除对象。


谢谢apneadiving!我明白了。现在有些东西开始变得更加清晰了。也许这部分解释了页面抛出错误的原因,因为据我所知,“selected_track”变量从未传递到“do_schedule”方法中。 - Skittles

6
send方法相当于在对象上调用给定的方法。因此,如果selected_track变量的值为1234,则@performer.send(selected_track)@performer.1234相同。或者,如果selected_track是"a_whiter_shade_of_pale",那么就像调用@performer.a_whiter_shade_of_pale一样。
因此,Performer类覆盖了method_missing,以便您可以使用任何轨道(名称或ID,从上面不清楚),并将其解释为在该表演者的曲目中搜索该曲目。

谢谢你的快速回复,JacobM!你的答案真的帮了我很多。我想让我有点困惑的是Performer类没有定义一个像你描述的那样被调用的方法。你的@performer.1234的例子是否表示通过send使用将调用Performer类的index方法或者其他?我注意到在Performer类中还使用了before_filter。 - Skittles
1
啊,你发布的代码澄清了一些事情。所谓的method_missing在ActiveRecord::Base中-- ActiveRecord的工作方式是我可以在模型上定义一个列(在数据库中),然后调用它,即使它不是模型中定义的方法。Method_missing捕获该方法调用,而ActiveRecord将我调用的“方法”与列/属性名称匹配起来。 - Jacob Mattison
现在一切开始变得更加清晰了。我来自编程的老派学派,使用PHP和Perl,现在才开始理解面向对象的严格ORM开发结构。感谢您的见解,非常感激 :) - Skittles
让我澄清一下,当我说“老派”时,我只是用它来描述那些语言的松散编码原则。我从80年代末开始编程。 :) - Skittles
“send”方法相当于在对象上调用给定的方法,这就是最好的答案。 - Michael Schmitz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接