Strong parameters in test issue - ruby-on-rails

Ruby 2.1.1p76 on Rails 4.1.1.
Please check out my controller:
def update
begin
current_user.update_settings user_settings_params unless params[:user_setting].blank?
current_user.update_attribute :district_id, params[:user][:district_id] unless params[:user].blank? || params[:user][:district_id].blank?
flash[:success] = "Preferencje zostały zaktualizowane"
redirect_to subscription_index_path
rescue UserLevelException => exception
flash[:alert] = "Sprytnie, za karę zostałeś wylogowany ;)"
session[:user_id] = nil
redirect_to root_path
return
end
end
private
def user_settings_params
params.require(:user_setting).permit(
:inquiry_subject, :inquiry_body,
:offer_subject, :offer_body,
:only_companies_with_email,
{:district_ids => []},
# {:district_ids => params[:user_setting][:district_ids].try(:keys)},
:delivery_address,
)
end
See the commented line? In the form above - user_settings_params will not return :district_ids array of ids, and this is fine, since I can use the line below instead to have them (got it from guides).
The problem I have is when running this test:
test 'should set user level10 districts' do
user = login_user :paid10
post :update, :user_setting => {:district_ids => [districts(:zachodniopomorskie).id, districts(:slaskie).id]}
assert_equal nil, flash[:alert]
assert_equal 'Preferencje zostały zaktualizowane', flash[:success]
db_user_districts = User.find(user.id).settings.districts.all
assert db_user_districts.include? districts(:zachodniopomorskie)
assert db_user_districts.include? districts(:slaskie)
assert_equal 2, db_user_districts.count
end
It passes. When debugging user_settings_param has :district_ids available as if strong parameters were disabled or something. I wanted to submit an issue to rails but most probably I'm doing something wrong and can't figure it out.

I've found it - it was because of quirky way I was creating checkboxes for HABTM
= check_box_tag "user_setting[district_ids][#{district.id}]", district.id, user.settings.district_ids.include?(district.id)
= label_tag "user_setting[district_ids][#{district.id}]", district.name
For no particular reason I've inserted ids into params keys AND values. And because of that those were passed to params object as hash. In test though those were sent as array. So it was the view to blame.
= check_box_tag "user_setting[district_ids][]", district.id, user.settings.district_ids.include?(district.id)
= label_tag "user_setting[district_ids][]", district.name

Related

Rails 5 testing controller unfiltered params

I have recently upgraded my application to Rails 5 and when I am testing my controller I am getting the following error:
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash.
My controller code looks like this:
def bid
widget_mode = params.include?(:widget)
if !#auction.published?
redirect_to '/' #go to the homepage
elsif #auction.third_party?
redirect_to #auction.third_party_bidding_url
elsif current_user && current_user.clerk? &&
!#auction.listing? &&
(#auction.items_count == 1 || params["section"] == "auction") &&
!widget_mode
redirect_to action: 'clerk', id: #auction.id, params: params.slice(:item, :section)
else
# Make sure the auction is in firebase
exists = #auction.rt_get('updated_at').body.to_i > 0 rescue false
#auction.queue_realtime_update unless exists
end
end
and my test code looks like this:
test "should redirect to powerclerk if multi item auction and params section = auction" do
sign_in users(:clerk)
a = auctions(:kenwood_dr)
assert a.items.count > 1, "Expected auction to have more than one item"
get :bid, params: {id: a.id, item: a.items.first.id, section: "auction"}
assert_redirected_to "/clerk/1?item=1&section=auction"
end
I tried adding:
params.permit(:item, :section, :id, :controller, :action, :widget)
to the beginning of my bid controller method and that didn't make a difference. Any insight would be appreciated.
This error occurs when calling to_h or to_hash on an instance of ActionController::Parameters that doesn't have any permitted keys (documentation).
Since ActionController::Parameters#slice returns an instance of the same, this code does not give you a hash like it would seem: params.slice(:item, :section).
In most cases you can use permit instead of slice on parameters instances. If you ever want to bypass the safe access whitelisting of ActionController::Parameters you can use permit! and use ActionController::Parameters#slice, or if you want to convert to a hash without sanitization you can use to_unsafe_h.
I ended up solving this by switching:
redirect_to action: 'clerk', id: #auction.id, params: params.slice(:item, :section)
to
redirect_to action: 'clerk', id: #auction.id, params: params.permit(:item, :section)

Ruby why is my object saving without an ID?

I feel like I'm missing something very basic, and someone can look at this and very quickly telling me what that is...
def create
#user = User.find_by_id(create_params[:user_id])
#plan = #user.plans.new(create_params)
if #plan.save
puts #plan.inspect
end
end
Right now the puts statement returns this:
#<Plan id: nil, zipcode: "02114", selected_plan: 5, user_id: 1>
So basically.. all the attributes are there, but there's no id...
More info:
create_params = {zipcode:"02114", selected_plan:"5", user_id: 1}
And if it's helpful to know, save! does work and successfully saves the plan with an id... and plan.save == true returns true
Your code should have the following pattern.
#user = User.find(create_params[:user_id]) # see changes
#plan = #user.plans.new(create_params)
if #plan.save
puts #plan.reload.inspect #reload object to see if the object has been saved!
else
puts #plan.errors.messages.inspect # if object is not saved there should be Object#errors.messages should have messages
end

Resque can't update objects - rails

I am trying to make a simple import background job to import some csv information using resque.
The job runs, but once I edit an object it seems like its ruined or something...
Once this job runs, it finds the user with the first lin... then at #user.name it doesn't get set, and then it can't save. I can run this code in my console and it works great, only broken in resque. Is there a limitation that you can't work with object or write to a database with resque? wasted 6 hours so far trying myriads of things...please help.
def self.perform(chunk)
chunk.each do |i|
#user = User.where(:email => i[:email].to_s).first_or_initialize
#user.name = i[:name].to_s
if #user.save
puts #user.email
entity = Entity.find_by_email_domain(#user.email)
eur = EntityUser.where(:entity_id => entity.id, :user_id => #user.id).first_or_initialize
if eur.save
puts "Start: BLARGITY End"
topic = Topic.where(:number => i[:course_number].to_s).first_or_initialize
eu = TopicUser.where(:topic_id => topic.id, :user_id => user.id, :role_i => 1).first_or_initialize
else
eu = TopicUser.where(:topic_id => topic.id, :user_id => user.id).first_or_initialize
end
eu.save
end
end
end
end
I just tried with doing a find instead like this, and you can see the error now..
NoMethodError: undefined method `name=' for nil:NilClass
#user = User.find_by_email(i[:email])
puts "sdfdsf"
#user.name = i[:name]
puts #user.name
I was using smarter_csv to send in 'chunk' which was a hash. I guess when redis serializes a hash, you need to symbolize it before you start using it again.
chunk.each do |i|
i.symbolize_keys!
#user = User.find_by_email(i[:email])
#user.name = i[:name]
works after symbolizing it!

What's the right way to modify the params passed to controller in rails 3.1.0?

In our Rails 3.1.0 app, we need to modify params passed to rfq controller in create and update. For example, we want to record the current user id under input_by_id. What we did was:
#rfq.input_by_id = session[:user_id]
It worked as expected. Also when need_report field is false, then report_language field should be nil. We decide to add the following line in rfq controller to make sure the nil is passed to report_language when need_report is false:
#rfq.report_language = nil unless params[:need_report]
However this addition causes the rspec case failure (in create/update of the controller) because of the data validation failure. However when we fire up the app, it behaves fine without saving the report_language when need_report is false. I am wondering if the line above is not the right way to use params[:need_report] for #rfq updating.
Thanks so much.
UPDATE:
Controller code:
def create
if has_create_right?
#rfq = Rfq.new(params[:rfq], :as => :roles_new )
#rfq.input_by_id = session[:user_id]
#save sales_id selected
if sales? && member? && !team_lead?
#rfq.sales_id = session[:user_id]
end
#view page may carry the hidden report language even if need_report == false
#rfq.report_language = nil unless params[:need_report]
#save into join table rfqs_standards
params[:rfq][:standard_ids].each do |sid|
#rfq.standards << Standard.find(sid.to_i) if !sid.nil? && sid.to_i > 0
end unless params[:rfq][:standard_ids].nil?
#save into join table rfqs_test_items
params[:rfq][:test_item_ids].each do |tid|
#rfq.test_items << TestItem.find(tid.to_i) if !tid.nil? && tid.to_i > 0
end unless params[:rfq][:test_item_ids].nil?
if #rfq.save!
redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
else
flash.now[:error] = "RFQ not saved!"
render 'new'
end
else
redirect_to URI.escape("/view_handler?index=0&msg=No rights!")
end
end
Test case failed after addition of #rfq.report_language = nil unless params[:need_report]
it "should be successful for corp head" do
session[:corp_head] = true
session[:user_id] = 1
s = Factory(:standard)
rfq = Factory.attributes_for(:rfq, :need_report => true, :report_language => 'EN')
rfq[:standard_ids] = [s.id] # attach standard_id's to mimic the POST'ed form data
get 'create', :rfq => rfq
#response.should redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
response.should render_template('new')
end
the problem ist that you are simply not looking at the right value.
get 'create', :rfq => rfq will result in a params-hash like {:rfq => {...}}
so you need to #rfq.report_language = nil unless params[:rfq][:need_report] == 'true'

Rails validations running against original record during update

I'm trying to figure out an inconsistency between what's happening in a functional test and what is happening in my development environment. I have a custom validation method unique_entry that is essentially a specialized version of validates_uniqueness_of. It looks like this:
def unique_entry
matched_entry = Entry.first(:conditions => ['LOWER(field_one) = LOWER(?) AND LOWER(field_two) = LOWER(?)', self.field_one, self.field_two])
errors.add_to_base('Duplicate detected') if matched_entry && (matched_entry.id != self.id)
end
The update action in the controller is very basic:
def update
if #entry.update_attributes(params[:entry])
flash.now[:success] = 'Success'
render :action => 'show'
else
flash.now[:error] = 'Error'
render :action => 'edit'
end
end
This works just fine when I'm creating a new record. When I update a record, however, I get inconsistent behavior. If I test it from a browser in my development environment, it correctly renders the edit action with an error message, but in my functional test, it accepts the update as successful. Here is the test:
test "should not update entry and should render edit view if invalid update" do
put :update, { :id => 1, :field_one => 'new_value', :field_two => 'new_value' } # 'new values' are the same as another existing record to trigger the duplication check
assert_template :edit
assert_not_nil flash[:error]
end
I looked at the test log and discovered that the values unique_entry is using are the record's original values instead of the values it should be attempting to update with. That is, the first line of unique_entry generates an SQL query like this:
SELECT * FROM "entries" WHERE (LOWER(field_one) = LOWER('original_value_of_field_one') AND LOWER(field_two) = LOWER('original_value_of_field_two')) LIMIT 1
What am I missing here? Why do my validations seem to be running against the original record instead of the new values only in the test environment?
In your test, shouldn't there be some reference to :entry, since that is what you are looking for in the controller params[:entry] ?

Resources