rspec 具有友好 id 自定义 slug 的 shoulda 匹配器不工作
rspec shoulda matchers with friendly id custom slug not working
我有一个带有友好 ID 的自定义 slug 模型:
# == Schema Information
#
# Table name: blogs
#
# id :integer not null, primary key
# title :string not null
# content :text not null
# is_published :boolean default("false"), not null
# slug :string
# publishing_date :datetime
# tags :text default("{}"), not null, is an Array
# meta_keywords :text default("{}"), not null, is an Array
# meta_description :text
# page_title :string(70)
# author_id :integer default("1")
# blog_category_id :integer default("1")
# author_type :integer default("0"), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Blog < ActiveRecord::Base
enum author_type: [:content_contributor]
enum publishing_status: [:reviewing, :accepted, :rejected]
include FriendlyId
friendly_id :title_and_category, use: [:slugged, :history]
has_attached_file :featured_image, styles: { large: '600x600>', medium: '300x300>', thumb: '100x100>' }, default_url: 'No_image.jpg.png'
validates_attachment_content_type :featured_image, content_type: /\Aimage\/.*\Z/
belongs_to :author, class_name: 'ContentContributor', foreign_key: 'author_id', inverse_of: :blogs
belongs_to :featured_product, class_name: 'Product', foreign_key: 'featured_product_id', inverse_of: :blogs
belongs_to :blog_category, inverse_of: :blogs
belongs_to :deleted_by, class_name: 'User', foreign_key: 'deleted_by_id'
validates :author, presence: true
validates :author_type, presence: true
validates :title, presence: true
validates :content, presence: true
validates :tags, presence: true
validates :meta_keywords, presence: true
def title_and_category
"#{blog_category.name} #{title}"
end
end
我已经为模型编写了这样的规范:
it { should define_enum_for(:author_type) }
it { should define_enum_for(:publishing_status) }
it { should belong_to(:author).class_name("ContentContributor").with_foreign_key("author_id").inverse_of(:blogs) }
it { should belong_to(:featured_product).class_name("Product").with_foreign_key("featured_product_id").inverse_of(:blogs) }
it { should belong_to(:deleted_by).class_name("User").with_foreign_key("deleted_by_id") }
it { should belong_to(:blog_category).inverse_of(:blogs) }
it { should validate_presence_of(:author) }
it { should validate_presence_of(:author_type) }
it { should validate_presence_of(:title) }
it { should validate_presence_of(:content) }
it { should validate_presence_of(:tags) }
it { should validate_presence_of(:meta_keywords) }
以上失败并出现以下错误:
失败:
1) Blog should require author to be set
Failure/Error: it { should validate_presence_of(:author) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:33:in `block (2 levels) in <top (required)>'
2) Blog should require author_type to be set
Failure/Error: it { should validate_presence_of(:author_type) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:34:in `block (2 levels) in <top (required)>'
3) Blog should require title to be set
Failure/Error: it { should validate_presence_of(:title) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:35:in `block (2 levels) in <top (required)>'
4) Blog should require content to be set
Failure/Error: it { should validate_presence_of(:content) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:36:in `block (2 levels) in <top (required)>'
5) Blog should require tags to be set
Failure/Error: it { should validate_presence_of(:tags) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:37:in `block (2 levels) in <top (required)>'
6) Blog should require meta_keywords to be set
Failure/Error: it { should validate_presence_of(:meta_keywords) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:38:in `block (2 levels) in <top (required)>'
我删除了自定义名称构建的代码,并且以上所有都通过了,但没有构建自定义 slug。应该改变什么?
这是因为 FriendlyId
使用 before_validation callback
设置了一个 slug。在您在 class.
中定义的任何 before_validation 回调之前调用此方法
title_and_category
方法在您的其他验证之前被调用。
要解决此问题,您可以在设置 before_validation 回调后调用 friendly_id,或者您可以在 friendly_id 之前添加您的方法:
解决方案 1
假设您有一个 title_and_category
列而不是一个方法。
# Make sure to prepend it so that it runs before Friendly_id's own callback
# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
before_validation :set_title_and_category, prepend: true
def set_title_and_category
self.title_and_category = "#{blog_category.name} #{title}"
end
解决方案 2
def title_and_category
"#{blog_category.name} #{title}" if blog_category
end
解决方案 3
我看到您正在使用 shoulda-matchers
,您可以使用 shoulda callback matchers 并在您的测试中添加此代码
it { should callback(:generate_slug).before(:validation) }
我有一个带有友好 ID 的自定义 slug 模型:
# == Schema Information
#
# Table name: blogs
#
# id :integer not null, primary key
# title :string not null
# content :text not null
# is_published :boolean default("false"), not null
# slug :string
# publishing_date :datetime
# tags :text default("{}"), not null, is an Array
# meta_keywords :text default("{}"), not null, is an Array
# meta_description :text
# page_title :string(70)
# author_id :integer default("1")
# blog_category_id :integer default("1")
# author_type :integer default("0"), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Blog < ActiveRecord::Base
enum author_type: [:content_contributor]
enum publishing_status: [:reviewing, :accepted, :rejected]
include FriendlyId
friendly_id :title_and_category, use: [:slugged, :history]
has_attached_file :featured_image, styles: { large: '600x600>', medium: '300x300>', thumb: '100x100>' }, default_url: 'No_image.jpg.png'
validates_attachment_content_type :featured_image, content_type: /\Aimage\/.*\Z/
belongs_to :author, class_name: 'ContentContributor', foreign_key: 'author_id', inverse_of: :blogs
belongs_to :featured_product, class_name: 'Product', foreign_key: 'featured_product_id', inverse_of: :blogs
belongs_to :blog_category, inverse_of: :blogs
belongs_to :deleted_by, class_name: 'User', foreign_key: 'deleted_by_id'
validates :author, presence: true
validates :author_type, presence: true
validates :title, presence: true
validates :content, presence: true
validates :tags, presence: true
validates :meta_keywords, presence: true
def title_and_category
"#{blog_category.name} #{title}"
end
end
我已经为模型编写了这样的规范:
it { should define_enum_for(:author_type) }
it { should define_enum_for(:publishing_status) }
it { should belong_to(:author).class_name("ContentContributor").with_foreign_key("author_id").inverse_of(:blogs) }
it { should belong_to(:featured_product).class_name("Product").with_foreign_key("featured_product_id").inverse_of(:blogs) }
it { should belong_to(:deleted_by).class_name("User").with_foreign_key("deleted_by_id") }
it { should belong_to(:blog_category).inverse_of(:blogs) }
it { should validate_presence_of(:author) }
it { should validate_presence_of(:author_type) }
it { should validate_presence_of(:title) }
it { should validate_presence_of(:content) }
it { should validate_presence_of(:tags) }
it { should validate_presence_of(:meta_keywords) }
以上失败并出现以下错误:
失败:
1) Blog should require author to be set
Failure/Error: it { should validate_presence_of(:author) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:33:in `block (2 levels) in <top (required)>'
2) Blog should require author_type to be set
Failure/Error: it { should validate_presence_of(:author_type) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:34:in `block (2 levels) in <top (required)>'
3) Blog should require title to be set
Failure/Error: it { should validate_presence_of(:title) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:35:in `block (2 levels) in <top (required)>'
4) Blog should require content to be set
Failure/Error: it { should validate_presence_of(:content) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:36:in `block (2 levels) in <top (required)>'
5) Blog should require tags to be set
Failure/Error: it { should validate_presence_of(:tags) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:37:in `block (2 levels) in <top (required)>'
6) Blog should require meta_keywords to be set
Failure/Error: it { should validate_presence_of(:meta_keywords) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./app/models/blog.rb:41:in `title_and_category'
# ./spec/models/blog_spec.rb:38:in `block (2 levels) in <top (required)>'
我删除了自定义名称构建的代码,并且以上所有都通过了,但没有构建自定义 slug。应该改变什么?
这是因为 FriendlyId
使用 before_validation callback
设置了一个 slug。在您在 class.
title_and_category
方法在您的其他验证之前被调用。
要解决此问题,您可以在设置 before_validation 回调后调用 friendly_id,或者您可以在 friendly_id 之前添加您的方法:
解决方案 1
假设您有一个 title_and_category
列而不是一个方法。
# Make sure to prepend it so that it runs before Friendly_id's own callback
# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
before_validation :set_title_and_category, prepend: true
def set_title_and_category
self.title_and_category = "#{blog_category.name} #{title}"
end
解决方案 2
def title_and_category
"#{blog_category.name} #{title}" if blog_category
end
解决方案 3
我看到您正在使用 shoulda-matchers
,您可以使用 shoulda callback matchers 并在您的测试中添加此代码
it { should callback(:generate_slug).before(:validation) }