Im working on upgrading a legacy app to rails 4 and I´m getting an unexplainable (to me at least) ForbiddenAttributesError
.
I have whitelisted the params before using them to create a new instance of Station
but for some reason I´m still getting
ForbiddenAttributesError
. Is there some way to get more info about which attribute causes the error?
** EDIT. I am using Devise and CanCan. If I remove load_and_authorize_resource
the errors are gone (and the authorization as well!). I have tried the solutions on https://github.com/ryanb/cancan/issues/835 with no avail.
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
factories/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
models/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
This was totally an issue where CanCan is incompatible with Rails 4 and had nothing to do with my Station
model or controller.
My workaround was to skip_load_resource
for the create
action.
before_filter :authenticate_user!, :except => [:show, :index]
load_and_authorize_resource :except => [:show, :index]
skip_load_resource :only => [:create]
Bang! Everything turned green.
UPDATE. Check out CanCanCan it´s the continuation of the now dead cancan and features Rails 4 strong parameters support among other things.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With