Ruby on rails Rails/Ruby、混合模块和保持干燥
我正在尝试为我的rails应用程序编写更多的模块化代码,因此我开始更多地在类中包含模块。我对它们的功能有基本的了解,但我发现在保持干燥的同时很难保持它们的灵活性 下面是一个最新的例子 我有一个叫做可接触的模块。它有两个非常基本的功能Ruby on rails Rails/Ruby、混合模块和保持干燥,ruby-on-rails,ruby,Ruby On Rails,Ruby,我正在尝试为我的rails应用程序编写更多的模块化代码,因此我开始更多地在类中包含模块。我对它们的功能有基本的了解,但我发现在保持干燥的同时很难保持它们的灵活性 下面是一个最新的例子 我有一个叫做可接触的模块。它有两个非常基本的功能 确保数据库中存在正确的联系人列 验证某些字段 给你 module Contactable extend ActiveSupport::Concern ERROR = 'please ensure necessary fields are in place'
module Contactable
extend ActiveSupport::Concern
ERROR = 'please ensure necessary fields are in place'
included do
REQUIRED_DATABASE_FIELDS.map { |rdf| raise "#{rdf} not included. #{ERROR}" unless column_names.include?(rdf)}
REQUIRED_INPUT_FIELDS.map { |rif| validates rif.to_sym, presence: true}
end
end
我希望contactable由其他三个模块(电话、电子邮件和地址)组成,其中包含需要的列数组和需要验证的字段。我现在正在研究的一个是“可寻址”
module Addressable
extend ActiveSupport::Concern
ERROR = 'please ensure necessary fields are in place'
REQUIRED_DATABASE_FIELDS = %w{address1
address2
address3
town
county
country
postcode}
REQUIRED_INPUT_FIELDS = %w{address1 postcode}
included do
REQUIRED_DATABASE_FIELDS.map { |rdf| raise "#{rdf} not included. #{ERROR}" unless column_names.include?(rdf)}
REQUIRED_INPUT_FIELDS.map { |rif| validates rif.to_sym, presence: true}
end
end
显然这里有重复。但是,如果我将此模块包括在contactable中,我就不需要重复,但这意味着contactable将始终包括Phoneable和Emailable。在某些情况下,我可能不想验证或要求这些特性。有什么方法可以实现这种灵活性吗?您应该在这里使用单元测试。通过检查模型(或它们包含的模块)中的数据库模式,您并没有真正实现任何目标。如果列不存在,应用程序将抛出NoMethodError或数据库驱动程序错误 最好是实际进行单元测试,以覆盖您的模型并确保它们按预期工作
require 'rails_helper'
describe User
# Tests the presence of the database column indirectly.
it { should respond_to :email }
# Explicit test - there a very few good reasons to actually do this.
it "should have the email column" do
expect(User.column_names).to have_key :email
end
end
如果您使用的是RSpec,那么您可以使用共享示例来减少规范中的重复量
# support/example_groups/addressable.rb
require 'spec_helper'
RSpec.shared_examples_for "an addressable" do
it { should respond_to :address1 }
it { should respond_to :address2 }
it { should respond_to :address3 }
it { should respond_to :county }
it { should respond_to :postcode }
# ...
end
有关如何使用测试单元/minitest实现相同功能的示例,请参见。您可以执行以下操作: 添加/app/models/concerns/fields_validator.rb 添加/app/models/concerns/contact.rb 添加/app/models/concerns/address.rb 在模型中
class Promotion < ActiveRecord::Base
include Address
include Contact
validate_required_attributes
end
要检查这是否有效
Promotion.new.save!
"ActiveRecord::RecordInvalid: Validation failed: Sku can't be blank, Amount can't be blank, Observations can't be blank, Product details can't be blank, Offer details can't be blank"
注意事项:
- 将模块保留在自定义命名空间中。现有的
模块将出现问题。例如:可寻址
module MyApp module Addressable # code... end end class Promotion < ActiveRecord::Base include MyApp::Addressable validate_required_attributes end
模块MyApp 模块可寻址 #代码。。。 结束 结束 类升级
- 您需要首先加载所有属性,然后应用验证。如果不这样做,如果模块共享属性,则可以重复验证
- 共享逻辑进入
字段验证模块
MyApp
)中。然后,需要使用名称空间包含模块。您可以使用包含MyApp::Addressable
来代替包含Address
module Address
extend ActiveSupport::Concern
include FieldsValidator
included do
puts "include address..."
load_required_attributes(:sku, :amount, :observations)
end
end
class Promotion < ActiveRecord::Base
include Address
include Contact
validate_required_attributes
end
include address...
loading attrs: [:sku, :amount, :observations]
include contact...
loading attrs: [:product_details, :observations, :offer_details]
adds validation for sku
adds validation for amount
adds validation for observations
adds validation for product_details
adds validation for offer_details
Promotion.new.save!
"ActiveRecord::RecordInvalid: Validation failed: Sku can't be blank, Amount can't be blank, Observations can't be blank, Product details can't be blank, Offer details can't be blank"
module MyApp
module Addressable
# code...
end
end
class Promotion < ActiveRecord::Base
include MyApp::Addressable
validate_required_attributes
end