rspec + factorygirl:堆栈级别太深
rspec + factorygirl : stack level too deep
我有一个帐户和一个用户模型。 Account: has_one :owner, class_name: 'User'
和 has_many :users
.
我的第一个规范失败了:
1) User has a valid factory
Failure/Error: Unable to find matching line from backtrace
SystemStackError:
stack level too deep
# /usr/local/rvm/gems/ruby-2.1.1/gems/activerecord-4.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:222
我正在寻找一个循环,但我没有看到它!用户工厂调用帐户工厂,帐户工厂调用所有者工厂...有什么问题吗?
## /spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, :type => :model do
it "has a valid factory" do
expect(FactoryGirl.create(:user)).to be_valid
end
end
我的 2 个工厂:
## /spec/factories/accounts.rb
require 'faker'
FactoryGirl.define do
factory :account do
name { Faker::Company.name }
subdomain { Faker::Internet.domain_word }
association :owner, factory: :owner, strategy: :build
end
end
## /spec/factories/users.rb
require 'faker'
FactoryGirl.define do
factory :user do
firstname { Faker::Name.first_name }
lastname { Faker::Name.last_name }
email { Faker::Internet.email }
password { Faker::Internet.password(6) }
association :account, factory: :account, strategy: :build
role {"employee"}
end
factory :owner, class: User do
firstname { Faker::Name.first_name }
lastname { Faker::Name.last_name }
email { Faker::Internet.email }
password { Faker::Internet.password(6) }
role {"owner"}
end
end
编辑,添加 2 个模型:
class User < ActiveRecord::Base
include ActiveModel::Dirty
include PublicActivity::Common
##
# user ROLES can be owner, manager or employee
ROLES =%w[owner manager employee]
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
acts_as_messageable
acts_as_tenant :account
accepts_nested_attributes_for :account
######################################################################
# Relations #
######################################################################
belongs_to :team
has_many :posts
has_many :shifts
######################################################################
# Validations #
######################################################################
validates :lastname, presence: true, allow_nil: false
validates :firstname, presence: true, allow_nil: false
validates :email, presence: true, allow_nil: false
validates_uniqueness_of :email, scope: :account_id
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates :password,
presence: true,
length: { :in => 2..20 },
:if => :password_required?, unless: :skip_password_validation
validates :role, presence: true
######################################################################
# Callbacks #
######################################################################
before_save :delete_avatar, if: -> { remove_avatar == '1' && !avatar_updated_at_changed? }
before_create :default_values
######################################################################
# Scope #
######################################################################
scope :active, -> (active) { where active: active }
scope :active_without_owner, -> { active(true).where.not(role: 'owner') }
scope :active_grouped, -> { where(active: true).group("id","team_id") }
scope :team, -> (team_id) { where team_id: team_id }
scope :starts_with, -> (lastname) { where("lastname like ?", "#{lastname}%") }
scope :sort_lastname, -> { order("users.lastname ASC") }
scope :account_id, -> { where account_id: account_id }
attr_accessor :is_manager, :last_current_user, :skip_password_validation
def activebydefault
self.active = true if self.active.nil?
end
private
def default_values
self.active ||= 'true'
self.save
true
end
end
class Account < ActiveRecord::Base
RESTRICTED_SUBDOMAINS = %w(www)
######################################################################
# Relations #
######################################################################
has_one :owner, class_name: 'User'
has_many :users
has_many :teams
accepts_nested_attributes_for :owner, :teams
######################################################################
# Validations #
######################################################################
validates :owner, presence: true
validates :name, presence: true
validates :subdomain, presence: true,
uniqueness: { case_sensitive: false, message: 'already used' },
format: { with: /\A[\w\-]+\Z/i, message: 'contains invalid characters' },
exclusion: { in: RESTRICTED_SUBDOMAINS, message: 'restricted' }
validates :time_zone, allow_nil: true,
inclusion: {
in: ActiveSupport::TimeZone.zones_map(&:name).keys
}
######################################################################
# Callbacks #
######################################################################
before_validation :downcase_subdomain
after_create :add_owner_to_team, :default_values, :send_notification
private
def downcase_subdomain
self.subdomain = subdomain.try(:downcase)
end
def default_values
self.publish_to_wall_new_employee = true
self.publish_to_wall_published_schedule = true
self.publish_to_wall_modified_schedule = true
self.publish_to_wall_new_document = true
self.save
true
end
end
编辑 2:添加了尾巴
ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
(0.2ms) BEGIN
(0.3ms) SAVEPOINT active_record_1
User Exists (1.0ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" IS NULL) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" IS NULL) LIMIT 1
Account Exists (0.5ms) SELECT 1 AS one FROM "accounts" WHERE LOWER("accounts"."subdomain") = LOWER('jean') LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'thomas.benoit@bourgeois.net' AND "users"."account_id" IS NULL) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'thomas.benoit@bourgeois.net' AND "users"."account_id" IS NULL) LIMIT 1
SQL (0.4ms) INSERT INTO "accounts" ("created_at", "name", "subdomain", "updated_at") VALUES (, , , ) RETURNING "id" [["created_at", "2015-02-02 19:58:20.000241"], ["name", "Pierre EURL"], ["subdomain", "jean"], ["updated_at", "2015-02-02 19:58:20.000241"]]
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
走啊走...
我想这只是因为 before_create
回调:
User
型号:
def default_values
self.active ||= 'true'
self.save
true
end
尝试替换为:
def default_values
self.active ||= 'true'
end
您不需要手动调用 save
(因为它正在循环您的回调)
我有一个帐户和一个用户模型。 Account: has_one :owner, class_name: 'User'
和 has_many :users
.
我的第一个规范失败了:
1) User has a valid factory
Failure/Error: Unable to find matching line from backtrace
SystemStackError:
stack level too deep
# /usr/local/rvm/gems/ruby-2.1.1/gems/activerecord-4.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:222
我正在寻找一个循环,但我没有看到它!用户工厂调用帐户工厂,帐户工厂调用所有者工厂...有什么问题吗?
## /spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, :type => :model do
it "has a valid factory" do
expect(FactoryGirl.create(:user)).to be_valid
end
end
我的 2 个工厂:
## /spec/factories/accounts.rb
require 'faker'
FactoryGirl.define do
factory :account do
name { Faker::Company.name }
subdomain { Faker::Internet.domain_word }
association :owner, factory: :owner, strategy: :build
end
end
## /spec/factories/users.rb
require 'faker'
FactoryGirl.define do
factory :user do
firstname { Faker::Name.first_name }
lastname { Faker::Name.last_name }
email { Faker::Internet.email }
password { Faker::Internet.password(6) }
association :account, factory: :account, strategy: :build
role {"employee"}
end
factory :owner, class: User do
firstname { Faker::Name.first_name }
lastname { Faker::Name.last_name }
email { Faker::Internet.email }
password { Faker::Internet.password(6) }
role {"owner"}
end
end
编辑,添加 2 个模型:
class User < ActiveRecord::Base
include ActiveModel::Dirty
include PublicActivity::Common
##
# user ROLES can be owner, manager or employee
ROLES =%w[owner manager employee]
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
acts_as_messageable
acts_as_tenant :account
accepts_nested_attributes_for :account
######################################################################
# Relations #
######################################################################
belongs_to :team
has_many :posts
has_many :shifts
######################################################################
# Validations #
######################################################################
validates :lastname, presence: true, allow_nil: false
validates :firstname, presence: true, allow_nil: false
validates :email, presence: true, allow_nil: false
validates_uniqueness_of :email, scope: :account_id
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates :password,
presence: true,
length: { :in => 2..20 },
:if => :password_required?, unless: :skip_password_validation
validates :role, presence: true
######################################################################
# Callbacks #
######################################################################
before_save :delete_avatar, if: -> { remove_avatar == '1' && !avatar_updated_at_changed? }
before_create :default_values
######################################################################
# Scope #
######################################################################
scope :active, -> (active) { where active: active }
scope :active_without_owner, -> { active(true).where.not(role: 'owner') }
scope :active_grouped, -> { where(active: true).group("id","team_id") }
scope :team, -> (team_id) { where team_id: team_id }
scope :starts_with, -> (lastname) { where("lastname like ?", "#{lastname}%") }
scope :sort_lastname, -> { order("users.lastname ASC") }
scope :account_id, -> { where account_id: account_id }
attr_accessor :is_manager, :last_current_user, :skip_password_validation
def activebydefault
self.active = true if self.active.nil?
end
private
def default_values
self.active ||= 'true'
self.save
true
end
end
class Account < ActiveRecord::Base
RESTRICTED_SUBDOMAINS = %w(www)
######################################################################
# Relations #
######################################################################
has_one :owner, class_name: 'User'
has_many :users
has_many :teams
accepts_nested_attributes_for :owner, :teams
######################################################################
# Validations #
######################################################################
validates :owner, presence: true
validates :name, presence: true
validates :subdomain, presence: true,
uniqueness: { case_sensitive: false, message: 'already used' },
format: { with: /\A[\w\-]+\Z/i, message: 'contains invalid characters' },
exclusion: { in: RESTRICTED_SUBDOMAINS, message: 'restricted' }
validates :time_zone, allow_nil: true,
inclusion: {
in: ActiveSupport::TimeZone.zones_map(&:name).keys
}
######################################################################
# Callbacks #
######################################################################
before_validation :downcase_subdomain
after_create :add_owner_to_team, :default_values, :send_notification
private
def downcase_subdomain
self.subdomain = subdomain.try(:downcase)
end
def default_values
self.publish_to_wall_new_employee = true
self.publish_to_wall_published_schedule = true
self.publish_to_wall_modified_schedule = true
self.publish_to_wall_new_document = true
self.save
true
end
end
编辑 2:添加了尾巴
ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
(0.2ms) BEGIN
(0.3ms) SAVEPOINT active_record_1
User Exists (1.0ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" IS NULL) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" IS NULL) LIMIT 1
Account Exists (0.5ms) SELECT 1 AS one FROM "accounts" WHERE LOWER("accounts"."subdomain") = LOWER('jean') LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'thomas.benoit@bourgeois.net' AND "users"."account_id" IS NULL) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'thomas.benoit@bourgeois.net' AND "users"."account_id" IS NULL) LIMIT 1
SQL (0.4ms) INSERT INTO "accounts" ("created_at", "name", "subdomain", "updated_at") VALUES (, , , ) RETURNING "id" [["created_at", "2015-02-02 19:58:20.000241"], ["name", "Pierre EURL"], ["subdomain", "jean"], ["updated_at", "2015-02-02 19:58:20.000241"]]
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'lola.francois@legrand.com' AND "users"."account_id" = 131) LIMIT 1
走啊走...
我想这只是因为 before_create
回调:
User
型号:
def default_values
self.active ||= 'true'
self.save
true
end
尝试替换为:
def default_values
self.active ||= 'true'
end
您不需要手动调用 save
(因为它正在循环您的回调)