如何通过has_one和belongs_to关联获取方法?

时间:2012-05-29 02:43:45

标签: ruby-on-rails model rspec belongs-to has-one

我有几周的经验或接近一个月学习Ruby on Rails。我希望理解为什么我不能通过相关对象获取方法。

我正在尝试获取附加到用户的关联对象,而不是单独使用stane的Profile.new,例如简介及其方法如下所示。

user.profile.create

user.profile.create!

user.profile.build

而是我得到RSpec错误消息。 NoMethodError:nil的未定义方法`build':NilClass

非常感谢提前

# == Schema Information
#
# Table name: users
#
#  id         :integer         not null, primary key
#  email      :string(255)
#  created_at :datetime        not null
#  updated_at :datetime        not null
#

class User < ActiveRecord::Base
  attr_accessible :email
  has_one :profile

  before_save { |user| user.email = email.downcase }

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
end

    # == Schema Information
    #
    # Table name: profiles
    #
    #  id         :integer         not null, primary key
    #  given_name :string(255)
    #  surname    :string(255)
    #  user_id    :integer
    #  created_at :datetime        not null
    #  updated_at :datetime        not null
    #

    class Profile < ActiveRecord::Base
      attr_accessible :given_name, :surname #, :user_id
      belongs_to :user

      validates :user_id, presence: true
    end

    smileymike@ubuntu:~/rails_projects/bffmApp$ bundle exec guard
    Guard uses Libnotify to send notifications.
    Guard is now watching at '/home/smileymike/rails_projects/bffmApp'
    Starting Spork for RSpec
    Using RSpec
    Preloading Rails environment
    Loading Spork.prefork block...
    Spork is ready and listening on 8989!
    Spork server for RSpec successfully started
    Guard::RSpec is running, with RSpec 2!
    Running all specs
    Running tests with args ["--drb", "-f", "progress", "-r", "/home/smileymike/.rvm/gems/ruby-1.9.3-p194/gems/guard-rspec-0.7.2/lib/guard/rspec/formatters/notification_rspec.rb", "-f", "Guard::RSpec::Formatter::NotificationRSpec", "--out", "/dev/null", "--failure-exit-code", "2", "spec"]...
    ......FFFFFFFF...............

    Failures:

      1) Profile 
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

      2) Profile 
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

      3) Profile 
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

      4) Profile 
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

      5) Profile 
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

      6) Profile user 
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

      7) Profile when user_id is not present 
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

      8) Profile accessible attributes should not allow access to user_id
         Failure/Error: before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }
         NoMethodError:
           undefined method `build' for nil:NilClass
         # ./spec/models/profile_spec.rb:23:in `block (2 levels) in <top (required)>'

    Finished in 1.56 seconds
    29 examples, 8 failures

    Failed examples:

    rspec ./spec/models/profile_spec.rb:27 # Profile 
    rspec ./spec/models/profile_spec.rb:28 # Profile 
    rspec ./spec/models/profile_spec.rb:29 # Profile 
    rspec ./spec/models/profile_spec.rb:30 # Profile 
    rspec ./spec/models/profile_spec.rb:33 # Profile 
    rspec ./spec/models/profile_spec.rb:31 # Profile user 
    rspec ./spec/models/profile_spec.rb:37 # Profile when user_id is not present 
    rspec ./spec/models/profile_spec.rb:41 # Profile accessible attributes should not allow access to user_id
    Done.

    > 

require 'spec_helper'

describe Profile do

#  before do
    # This code is wrong but it works
#    @profile = Profile.new(given_name: "Michael", surname: "Colin", user_id: user.id)
#  end
#  but I am attempting to create a profile via User/Profile assoications
  let(:user) { FactoryGirl.create(:user) }
  before { @profile = user.profile.build(given_name: "Michael", surname: "Colin") }

  subject { @profile }

  it { should respond_to(:given_name) }
  it { should respond_to(:surname) }
  it { should respond_to(:user_id) }
  it { should respond_to(:user) }
  its(:user) { should == user }

  it { should be_valid }

  describe "when user_id is not present" do
    before { @profile.user_id = nil }
    it { should_not be_valid }
  end

  describe "accessible attributes" do
    it "should not allow access to user_id" do
      expect do
        Profile.new(user_id: user_id)
      end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
    end    
  end  
end

1 个答案:

答案 0 :(得分:3)

has_one会在用户上为您提供build_profile方法。有关详细信息,请参阅http://guides.rubyonrails.org/association_basics.html#has_one-association-reference

你不能做user.profile.build的原因是没有配置文件,所以user.build返回nil,并且要求nil构建配置文件是没有意义的。这与has_many情况不同,其中out总是返回一组事物 - 即使集合是空的 - 并且您可以要求集合创建另一个成员。很容易想象has_one访问器返回一个非零的“我不在这里”的值,这将启用你的场景,但会有其他问题(主要是这样的值不会是假的,导致可以说是更少的rubyesque代码)