如何在Rails中为我的控制器和模型提供方法?

时间:2010-08-25 00:54:39

标签: ruby-on-rails ruby oop

我在Rails应用程序中有一个私有方法连接到Amazon S3,执行传递的代码块,然后关闭与S3的连接。它看起来像这样;

def S3
  AWS::S3::Base.establish_connection!(
    :access_key_id     => 'Not telling',
    :secret_access_key => 'Really not telling'
  )
  data = yield
  AWS::S3::Base.disconnect
  data
end

这样称呼(作为例子);

send_data(S3 {AWS::S3::S3Object.value("#{@upload_file.name}",'bucket')}, :filename => @upload_file.name)

我在我的控制器和模型中以多种方式调用此方法,因此将它作为私有方法包含在两个类中。这很好用,我很满意,但它不是很干。

如何让我的模型和控制器都能访问此方法,但只能让代码出现一次?这是一个Ruby问题而不是Rails问题,反映了我对OOP的新见解。我猜一个模块或混合就是答案,但到目前为止我还没有真正使用过这两个模块,需要一点点手持。

感谢。

3 个答案:

答案 0 :(得分:9)

模块用于ruby中的3种不同的东西。首先是命名空间。在模块内部具有类或常量定义不会与该模块之外的类或常量发生冲突。像这样的东西

class Product
  def foo
    puts 'first'
  end
end

module Affiliate
  class Product
    puts 'second'
  end
end

p = Product.new
p.foo # => 'first'

p = Affiliate::Product.new
p.foo # => 'second'

模块的第二个用途是作为粘贴方法的地方,这些方法在其他任何地方都没有。您也可以在类中执行此操作,但使用模块类型可以告诉读取代码的人员,这些代码并不是要实现的。像这样的东西

module Foo
  def self.bar
    puts 'hi'
  end
end

Foo.bar #=> 'hi'

最后(也是最令人困惑的)是模块可以包含在其他类中。以这种方式使用它们也被称为mixin,因为您将所有方法“混合”到您所包含的任何内容中。

module Foo
  def bar
    puts 'hi'
  end
end

class Baz
  include Foo
end

b = Baz.new
b.bar #=> 'hi'

Mixins实际上是一个比较复杂的主题,然后我在这里讨论,但更深入可能会让人感到困惑。

现在,对我来说,S3似乎真的属于控制器,因为控制器通常是处理传入和传出连接的东西。如果是这种情况,我只会在应用程序控制器上有一个受保护的方法,因为所有其他控制器都可以访问它,但仍然是私有的。

如果你确实有充分的理由将它放入模型中,我会选择mixin。像

这样的东西
module AwsUtils
private
  def S3
    AWS::S3::Base.establish_connection!\
      :access_key_id     => 'Not telling',
      :secret_access_key => 'Really not telling'

    data = yield
    AWS::S3::Base.disconnect
    data
  end
end

如果您将其放在lib/aws_utils.rb中,则应该可以通过在控制器和模型中添加include AwsUtils来使用它。 Rails知道在lib中查找类和模块,但只有在名称匹配时(大例)。我称之为AwsUtils,因为我知道当它看到(aws_utils.rb)时会看到什么轨道,说实话,我不知道S3Utils需要什么; - )

如果我不清楚某些事情,请随时询问更多信息。模块往往是红宝石中的那些东西之一,虽然令人惊讶,但对于新手来说却是令人费解的。

答案 1 :(得分:3)

你的预感是正确的:你可以把一个模块放在lib目录中。在 为了使这些方法可用于您的模型,只需包含它即可 用:

class Model < ActiveRecord::Base
  include MyModule
end

包含模块的实例方法将成为您班级的实例方法。 (这被称为mixin)

module MyModule
  def S3
    #...
  end
end

答案 2 :(得分:1)

您可以将模块编写为:

module MyModule
  def self.S3(args*)
    AWS::S3::Base.establish_connection!(
      :access_key_id     => 'Not telling',
      :secret_access_key => 'Really not telling'
    )
    data = yield
    AWS::S3::Base.disconnect
    data
  end
end

然后在控制器或模型中将其称为

MyModule.S3(PARAMS *)