Rails 4中的ForbiddenAttributesError

时间:2013-11-08 09:55:58

标签: ruby-on-rails ruby rspec 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)>'

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

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

2 个答案:

答案 0 :(得分:18)

这完全是一个问题,CanCan与Rails 4不兼容,与我的Station模型或控制器无关。
对于skip_load_resource操作,我的解决方法是create

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

砰!一切都变绿了。

UPDATE。查看CanCanCan这是现在已经死亡的cancan的延续,并提供Rails 4强大的参数支持等功能。

答案 1 :(得分:0)

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

  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是一个与站点有某种关系类型的模型,它也不应该在强参数中。