Rails 4中的ForbiddenAttributesError

max*_*max 12 ruby rspec ruby-on-rails ruby-on-rails-4

我正在努力将遗留应用程序升级到rails 4,我得到了一个无法解释的(至少对我来说)ForbiddenAttributesError.

在使用它们创建一个新的实例之前,我已将这些参数列入白名单Station但由于某种原因我仍然可以使用 ForbiddenAttributesError.有没有办法获得有关哪个属性导致错误的更多信息?


**编辑.我正在使用Devise和CanCan.如果我删除load_and_authorize_resource错误消失了(和授权一样!).我在https://github.com/ryanb/cancan/issues/835上尝试过这些解决方案但没有用.


rspec spec/controllers/stations_controller_spec.rb

  1) StationsController POST 'create' invalid should not create a new record
     Failure/Error: post :create, :station => { :name => 'foo' }
     ActiveModel::ForbiddenAttributesError:
       ActiveModel::ForbiddenAttributesError
     # ./spec/controllers/stations_controller_spec.rb:67:in `block (5 levels) in <top (required)>'
     # ./spec/controllers/stations_controller_spec.rb:66:in `block (4 levels) in <top (required)>'

  2) StationsController POST 'create' valid should create a new record
     Failure/Error: before { post :create, :station => FactoryGirl.attributes_for(:station, :hw_id => rand(1000)) }
     ActiveModel::ForbiddenAttributesError:
       ActiveModel::ForbiddenAttributesError
     # ./spec/controllers/stations_controller_spec.rb:73:in `block (4 levels) in <top (required)>'

  3) StationsController POST 'create' valid
     Failure/Error: before { post :create, :station => FactoryGirl.attributes_for(:station, :hw_id => rand(1000)) }
     ActiveModel::ForbiddenAttributesError:
       ActiveModel::ForbiddenAttributesError
     # ./spec/controllers/stations_controller_spec.rb:73:in `block (4 levels) in <top (required)>'
Run Code Online (Sandbox Code Playgroud)

schema.rb

  create_table "stations", :force => true do |t|
    t.string   "name"
    t.string   "hw_id"
    t.float    "lat"
    t.float    "lon"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
    t.string   "timezone"
    t.float    "balance"
    t.integer  "user_id"
    t.boolean  "down"
  end
Run Code Online (Sandbox Code Playgroud)

station_controller.rb

      ...
      # POST /stations
      # POST /stations.xml
      def create
        @station = Station.new(station_params)
        @station.set_timezone!
        logger.debug("@station " + @station.inspect)
        respond_to do |format|
          if @station.save
            format.html { redirect_to(@station, :notice => 'Station was successfully created.') }
            format.xml  { render :xml => @station, :status => :created, :location => @station }
            format.yaml { render :status => :ok, :nothing => true }
          else
            # create users list if the station cannot be created
            @users = User.all_users_select(current_user)
            format.html { render :action => "new" }
            format.xml  { render :xml => @station.errors, :status => :unprocessable_entity }
            format.yaml { render :status => :unprocessable_entity, :nothing => true }
          end
        end
      end

      ...

      # whitelists params for mass assignment
      def station_params
        params.require(:station).permit(
            :user_id, :name, :hw_id, :measures, :lat, :lon, :authenticity_token, :commit
        )
      end
Run Code Online (Sandbox Code Playgroud)

station_controller_spec.rb

      describe "POST 'create'" do
        describe "invalid" do
          it "should not create a new record" do
            expect do
              post :create, :station => { :name => 'foo' }
            end.to_not change(Station, :count)
          end
        end

        describe "valid" do
          before { post :create, :station => FactoryGirl.attributes_for(:station, :hw_id => rand(1000)) }
          it "should create a new record" do
            expect do
              post :create, :station => FactoryGirl.attributes_for(:station, :hw_id => rand(1000))
            end.to change(Station, :count)
          end
          subject { response }
          it { should redirect_to station_path(assigns(:station)) }
        end
      end
Run Code Online (Sandbox Code Playgroud)

工厂/ station.rb

    FactoryGirl.define do
      factory :station do
        name "Test Staton"
        hw_id Time.now.to_s
        lat 63.401839
        lon 13.072183
      end
      factory :invalid_station, class: Station do
        name nil
      end
    end
Run Code Online (Sandbox Code Playgroud)

车型/ station.rb

#include Geokit::Geocoders
class Station < ActiveRecord::Base
  #acts_as_mappable :default_units => :kms,
   #                  :lat_column_name => :lat,
   #                  :lng_column_name => :lon
  belongs_to :user
  has_many :measures, :dependent => :destroy
  has_one :current_measure, :class_name => "Measure"

  # arduino client has not memory enough to post the station name so it cannot be required!
  validates :hw_id, :presence => true # must have a hw_id
  validates :hw_id, :uniqueness => true # and the hw_id must be unique

  # Geocoder gem attribute mapping
  geocoded_by :name, :latitude  => :lat, :longitude => :lon

  def owned_by?(owner)
    user==owner
  end

  def calibrate_speed(speed)
    speed/250
  end

  def self.send_low_balance_alerts
    stations = Station.find(:all)
    stations.each do |station|
      logger.info "Checking station #{station.name}"
      logger.info "Last measure at #{station.current_measure.created_at}"
      if station.balance/100 < 15 && !station.down
        if !station.user.nil?
          logger.info "Send reminder to owner #{station.user}"
          UserMailer.notify_about_low_balance(station.user, station).deliver
        end
      end
    end
  end

  def self.send_down_alerts
    stations = Station.find(:all)
    stations.each do |station|
      logger.info "Checking station #{station.name}"
      logger.info "Last measure at #{station.current_measure.created_at}"
      if station.current_measure.created_at < 15.minutes.ago && !station.down
        station.down = true
        station.save
        if !station.user.nil?
          logger.info "Send reminder to owner #{station.user}"
          UserMailer.notify_about_station_down(station.user, station).deliver
        end
      end
    end
  end

  def self.get_timezone(lat, lon)
    timezone = Timezone::Zone.new :latlon => [lat, lon]
    timezone.active_support_time_zone
  end



  def set_timezone!
    if (!self.lat.nil? && !self.lon.nil?)
      self.timezone  = Station.get_timezone(self.lat, self.lon)
    end
  end

end
Run Code Online (Sandbox Code Playgroud)

max*_*max 18

这完全是一个问题,CanCan与Rails 4不兼容,与我的Station模型或控制器无关.
我的解决方法是skip_load_resourcecreate行动.

before_filter :authenticate_user!, :except => [:show, :index]
load_and_authorize_resource :except => [:show, :index]
skip_load_resource :only => [:create]
Run Code Online (Sandbox Code Playgroud)

砰! 一切都变绿了.

UPDATE.查看CanCanCan它是现在死亡康康的延续,并具有Rails 4强大的参数支持等功能.