Ruby Rake中变量和符号之间的联系是什么?

时间:2013-02-13 01:43:58

标签: ruby-on-rails ruby rake symbols

我是Ruby编程的新手,但我认为我或多或少得到了一个符号(一个不可变的值,占用更少的空间,一个枚举等等)。这就是困扰我的 - 从一个Hartl Ruby on Rails教程中的Rake测试:

describe "something" do
  let(:user) { FactoryGirl.create(:user) } 
  before { sign_in user }
...
end

调用FactoryGirl.create(:user)对我来说是有意义的 - 在这里我们将一种标志(:user)传递给FactoryGirl来告诉它要制作什么样的对象(或者更确切地说,是什么要设置的变量,因为它只创建User对象)。但是在let(user)中,user是一个变量,而不是一个符号(或至少它应该是)。我只能假设let方法正在秘密地交换符号:user以获取同名变量 - 但为什么呢? let(user)不是一个更直观的语法吗?我在这里缺少什么?

此外,还有其他地方在Ruby中出现这种模式吗?到目前为止,我只在rake测试中看到它。

2 个答案:

答案 0 :(得分:2)

就像Dave已经提到的那样,提供的代码不是rake任务,而是RSpec规范。

但是让我在这里关注真正的问题。

如果您从未接触过具有类似功能的任何其他语言,那么符号有点难以掌握。有些语言称之为Atom。

http://en.wikipedia.org/wiki/Symbol_(programming)

使符号变形的想法是提供一种人类可读但计算上便宜的原始类型。

在Ruby中,当编译器/解释器看到符号时,它会创建一个Symbol类型的对象并将其存储在内存中。在ruby中,符号是单例,因此任何其他对相同符号的使用都会返回完全相同的对象,这使得它在空间方面非常便宜并且比较便宜,因为您可以只比较内存地址而不是内容。 / p>

例如,如果比较两个符号,如下所示:

:foo == :foo

你几乎在比较同一个对象,这意味着只需要比较内存地址。

现在,当你比较两个字符串时:

"foo" == "foo"

它创建两个具有相同内容的String实例,并且需要比较字符串的每个字节以确保它们相等。

此属性使符号非常适合散列中的标识符或键。

现在,到RSpec。

我们来看下面的例子:

describe Authenticator do
  let(:user) { Factory.create(:user) )

  it "authenticate" do
    auth_user = subject.authenticate(user.login, user.password)
    auth_user.should == user
  end
end

Factory.create将符号作为要使用的工厂的标识符。你需要自己定义工厂,所以它真的只是一个名字。你可以使用一个字符串,但使用符号更便宜和最好的做法,但老实说,除非你多次调用Factory.create百万次,否则它不会有太大的区别。

let不是定义一个变量,它实际上定义了一个做一些事情的方法:

  1. 第一次在规范(块)中调用该方法时,它将执行块,缓存结果并将其返回
  2. 同一规范(它阻止)内的任何其他调用将只返回缓存的结果
  3. 完成规范后,它会删除缓存的结果,以便在下次调用时重新评估下一个规范
  4. 这允许仅在需要时延迟创建对象,并允许将任何状态更改限制为当前规范。

    因此,底线是:RSpec使用该符号作为方法名称的标识符,该名称将被生成以抽象掉某些事物,使您的生活更轻松。 RSpec只是一种BDD领域特定语言,它使用元编程来构建测试套件。

    使用以下测试用例可以实现相同的行为:

    class AuthenticatorTest < Test::Unit::TestCase
      def user
        return @user if @user
        @user = Factory.create(:user)
      end
    
      def subject
        return @subject if @subject
        @subject = Authenticator.new
      end
    
      def teardown
        @subject = nil
        @user = nil
      end
    
      def test_authenticate
        auth_user = subject.authenticate(user.login, user.password)
        assert_equal auth_user, user
      end
    end
    

    请注意,您可能不应该编写这样的测试用例,但它(粗略地)说明了RSpec的用途。

    我希望有所帮助。

答案 1 :(得分:0)

一些小的更正/澄清:

  1. 这是'rspec'测试,而不是rake测试。 Rake是一种自动化工具(类似于'make'),但rspec是一种测试工具。
  2. FactoryGirl可以创建许多不同类型的对象。如果定义多个工厂,则传递给#create的符号会告诉它使用哪个工厂。它没有告诉它在你的程序中设置什么变量。
  3. let()调用定义了一个由提供的符号名称给出的辅助方法,例如: :用户。该方法返回由传递给它的块确定的值,例如, FactoryGirl将创建的用户对象。
  4. 我已经掩盖了let()内容的一些细节。真实的文档在这里:

    https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let

    根据我个人的经验,这个特定的模式是nc,但是单元测试往往与常规代码不同,因为它们的想法是运用代码,而不是解决实际问题。要做到这一点,你往往希望一次改变一件事情,所以事情可能会重复 - 但ruby提供了许多工具来减少打字并保持可读性。

    编辑:有关使用此模式的示例,请参阅注释。它在我的鼻子下......:)