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(因为它正在循环您的回调)