繁体   English   中英

使用 Rails 和 RSpec 自动加载后重新实例化 class

[英]Re-instancing a class after autoloading with Rails and RSpec

我有这个 Class,我在其中根据数据库表创建了一些属性:

class Form
 include ActiveAttr::Model

 attribute :type, type: String
 attribute :default_name, type: String

 Languages.all.each do |lang|
  attribute :"name_#{lang}", type: String
 end
end

这工作正常,但后来我用 RSpec 做了两个测试:

单元测试:

require 'rails_helper'

RSpec.describe Form, type: :class do
  ...
end

端到端测试:

require 'rails_helper'

RSpec.describe 'forms', type: :system do
  let!(:languages) do
    create(:language, name: 'es')
    create(:language, name: 'en')
  end

  scenario 'accessing the page where I can see all the attributes of the Form' do
    @form = create(:form, :with_languages)
    visit form_page(@form)
  end

  ...

当我运行rspec时,Rails 自动加载所有内容,并且创建的表单 class 还没有数据库中的任何语言,因此它没有任何name_属性。 第一个测试工作正常,但第二个测试失败,因为加载了表单 Class 时没有模拟语言:

undefined method `name_en' for #<Form:0x000000014766c4f0>

这是因为为了加载视图,我们在 controller 中执行@view = Form.new(@form) 。显然它不会创建新的 object。

如果我只运行第二个测试,它就像一个魅力,我尝试使用 DatabaseCleaner,但它是一样的。

有没有办法在不禁用自动加载的情况下执行此操作? 我尝试使用config.autoload_paths禁用它,但它给了我数千个错误,而且它是一个巨大的应用程序。

我尝试了几种解决方案,但都没有用,我需要的是重新创建 class。

@view = Form.new(@form)确实创建了一个新的 object 但它不会重新加载 class,因此不会再次定义属性。 attribute是一个 class 方法,它在加载Form class 时运行,在调用Form.new时不运行。

我不确定您仅使用Form class 做什么,通常每个表格都有一个特定的 class。 您可以稍后添加属性,但这是对Form class 的永久更改:

>> Form
# Form class is autoloaded and all attributes are defined
 Language Load (0.9ms)  SELECT "languages".* FROM "languages"
=> Form(default_name: String, type: String)

>> Language.create(name: :en)
>> Form
# will not add language attributes
=> Form(default_name: String, type: String)

>> Form.attribute :name_en, type: String
=> attribute :name_en, :type => String
>> Form
=> Form(default_name: String, name_en: String, type: String)

您可以在初始化新的 object 时更改Form class:

class Form
  include ActiveAttr::Model
  attribute :type,         type: String
  attribute :default_name, type: String

  def self.lang_attributes languages = Language.pluck(:name)
    Array.wrap(languages).each do |lang|
      attribute :"name_#{lang}", type: String
    end
  end

  lang_attributes

  def initialize(...)
    self.class.lang_attributes
    super
  end
end
>> Language.create(name: :en)
>> Form # first call to Form autoloads it
  Language Pluck (0.8ms)  SELECT "languages"."name" FROM "languages"
=> Form(default_name: String, name_en: String, type: String)

>> Language.create(name: :es)
>> Form.lang_attributes
>> Form
=> Form(default_name: String, name_en: String, name_es: String, type: String)

>> Language.create(name: :fr)
>> my_form = Form.new
  Language Pluck (0.6ms)  SELECT "languages"."name" FROM "languages"
=> #<Form default_name: nil, name_en: nil, name_es: nil, name_fr: nil, type: nil>
# NOTE: but this changes the class itself

如果您希望实际的 class 是动态的,那么 class 的定义必须是动态的:

class FormClass
  def self.new langs: Language.pluck(:name)
    Class.new do
      include ActiveAttr::Model
      attribute :type,         type: String
      attribute :default_name, type: String
      Array.wrap(langs).each do |lang|
        attribute :"name_#{lang}", type: String
      end
    end
  end
end
>> form_class = FormClass.new    # aka Form
  Language Load (0.5ms)  SELECT "languages".* FROM "languages"
=> (default_name: String, name_en: String, name_es: String, name_fr: String, type: String)
>> form_object = form_class.new  # aka Form.new
=> #< default_name: nil, name_en: nil, name_es: nil, name_fr: nil, type: nil>

>> Language.destroy_all
>> form_object = FormClass.new.new
  Language Load (0.7ms)  SELECT "languages".* FROM "languages"
=> #< default_name: nil, type: nil>
# no more language attributes ^

# and you can override language attribute(s) 
>> FormClass.new(langs: :en).new
=> #< default_name: nil, name_en: nil, type: nil>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM