12

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

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


** 编辑。我正在使用设计和 CanCan。如果我删除load_and_authorize_resource错误就消失了(授权也消失了!)。我已经尝试了https://github.com/ryanb/cancan/issues/835上的解决方案,但无济于事。


rspec 规范/控制器/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)>'

架构.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

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

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

工厂/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

模型/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
4

2 回答 2

18

Station这完全是 CanCan 与 Rails 4 不兼容且与我的模型或控制器无关的问题。
我的解决方法是skip_load_resource采取create行动。

before_filter :authenticate_user!, :except => [:show, :index]
load_and_authorize_resource :except => [:show, :index]
skip_load_resource :only => [:create]

砰! 一切都变成了绿色。

更新。查看CanCanCan它是现已死去的 cancan 的延续,并具有 Rails 4 强大的参数支持等特性。

于 2013-11-10T22:42:09.190 回答
0

电台是否属于用户?我认为您不应该为 user_id 设置强参数。user_id 应该通过模型设置的方式自动生成。所以你强大的参数代码应该是:

  def station_params
    params.require(:station).permit(
         :name, :hw_id, :measures, :lat, :lon, :authenticity_token, :commit #user_id taken out
    )
  end

另外,你的意思是让 hw_id 成为一个字符串吗?通常当某些东西说 id 它是一个整数时。此外,如果 hw 是一个与站有某种关系的模型,它也不应该在强参数中。

于 2013-11-10T21:41:08.467 回答