Or, that is what seems to be happening. I'm getting errors in my production environment that i can't recreate in development. Basically, i'm trying to associate a Booker to a Booking. If the Booker doesn't exist I'd like to invite him/her.
# GET /bookings/new
def new
#booking = Booking.new
authorize #booking
#booking.venue = Venue.new
#booker = User.new
end
Relevant create code;
def create
#booking = Booking.new(booking_params)
authorize #booking
booker_found = set_booker
And the set_booker private method
def set_booker
logger.info "booker: #{booking_params.inspect}"
# set existing user as booker or prepare inviting a new user
booker_found = false
#booker = User.find_by_email(booking_params[:user][:email])
etc.
The last line is where I get errors in production, because booking_params[:user] does not exist. I tried resetting my database in the development ENV and the code works fine. Yet in production I will always get NoMethodError (undefined method '[]' for nil:NilClass)
This is my relevant form code;
<%= simple_form_for(#booking, :wrapper => :bootstrap3 ) do |f| %>
<%= f.simple_fields_for #booker do |user_form| %>
<%= user_form.input :email, label: "Booker e-mail" %>
<% end %>
This is what logger shows in development;
Parameters: {"utf8"=>"✓", "authenticity_token"=>"lala", "booking"=>{"name"=>"Mah Booking", "date"=>"02-10-2014", "venue_name"=>"Meeeeeeeeeeee", "venue_id"=>"", "user"=>{"email"=>"booker#boekkoek.nl"}, "artist_fee"=>"0.00"}, "commit"=>"Create Booking", "locale"=>"en"}
booker: {"date"=>"02-10-2014", "name"=>"Mah Booking", "artist_fee"=>"0.00", "venue_id"=>"", "venue_name"=>"Meeeeeeeeeeee", "user"=>{"email"=>"booker#boekkoek.nl"}}
And this is from my production.log
Parameters: {"utf8"=>"✓", "authenticity_token"=>"yada", "booking"=>{"name"=>"tttt", "date"=>"02-10-2014", "venue_name"=>"meee", "venue_id"=>"", "user"=>{"email"=>"info#blabl.nl"}, "artist_fee"=>"0.00"}, "commit"=>"Create Booking", "locale"=>"en"}
booker: {"name"=>"tttt", "date"=>"02-10-2014", "artist_fee"=>"0.00", "venue_id"=>""}
I have no idea why the order is different, also, it seems to but "cutting off" after the venue_id, which is obviously causing the error. Has anyone seen behavior like this before?
Edit:
Here's my booking_params private method
def booking_params
params.require(:booking).permit(*policy(#booking || Booking).permitted_attributes)
end
And the pundit policy;
def permitted_attributes
[:status,
:date,
:name,
:get_in_time_string,
:soundcheck_time_string,
:dinner_time_string,
:show_time_string,
:show_time_end_string,
:artist_fee,
:venue_id,
:venue_name,
:act_ids => [],
user: [ :id, :email ]
]
end
Like I said, same code works fine in development. The only way I can sort of reproduce the problem is by removing the user params from my permitted_attributes method, but then I actually get an "unpermitted parameters" error. What would be the right way to define the permitted attributes for "user"? Totally lost on this one.
I suspect your strong parameters are incorrect/are missing the nested user. If you post them I can fix that side of it, but an easy temporary fix is to simply use
params[:user][:email]
i.e access the parameters directly. Going back to the issue, have a look here for using nested parameters in the strong parameters:
Rails 4 - Strong Parameters - Nested Objects
Never found out what the problem really was. Not using Pundit for the strong params fixed it though.
Related
I have this link_to helper passing an :email_sequence instance and an extra :set_active params.
I then try to update the :email_sequence instance in the controller using strong params but I'm getting an error saying:
param is missing or the value is empty: email_sequence
link_to:
<%= link_to "Activate", admin_email_sequence_path(base_email.email_sequence, set_active: :true), method: :patch %>
Controller:
class Admin::EmailSequencesController < AdminController
before_action :set_email_sequence
def update
if #email_sequence.update(active: email_sequence_params[:set_active])
flash[:success] = "Sequence updated succesfully"
redirect_to_forwarder_or(params[:forwarding_uri], admin_account_emails_path)
end
end
private
def set_email_sequence
#email_sequence = current_account.email_sequences.find(params[:id])
end
def email_sequence_params
params.require(:email_sequence).permit(:set_active)
end
end
This is what gets sent in the params:
{"_method"=>"patch", "authenticity_token"=>"[FILTERED]", "set_active"=>"false", "id"=>"1"}
Can anybody tell me what am I doing wrong?
By params.require(:email_sequence).permit(:set_active) you expect parameters to be { email_sequence: {set_active: "ANY SCALAR VALUE HERE"} } but you pass only set_active you can fix it by permitting the only one parameter
params.permit(:set_active)
You don't need strong parameters here in the first place. Contrary to popular belief strong parameters does not magically sanitize the parameters. It just prevent mass assignment vulnerabilities by requiring that you whitelist the parameters when passing a hash of parameters. Since you are only using a single parameter there is no mass assignment vulnerability:
class Admin::EmailSequencesController < AdminController
before_action :set_email_sequence
def update
if #email_sequence.update(active: params[:set_active])
flash[:success] = "Sequence updated succesfully"
redirect_to_forwarder_or(params[:forwarding_uri], admin_account_emails_path)
else
# provide an error response!
end
end
private
def set_email_sequence
#email_sequence = current_account.email_sequences.find(params[:id])
end
end
If you later want to use multiple parameters nested in a hash the use of link_to is pretty questionable even if it can be done.
<%= link_to "Activate",
admin_email_sequence_path(
base_email.email_sequence,
"email_sequence[set_active]" => true,
"email_sequence[foo]" => "bar"
),
method: :patch
%>
Use button_to or form_with/form_for to create a form element and style the button to look the way you want instead as this places the parameters in request body instead of the query string.
I have the following routes:
shallow do
resources :countries do
resources :airports
end
end
I'm having trouble with invoking two of the routes.
The airports_controller.rb file begins
def create
Rails::logger.debug "!!! Building airport with parameters #{params}"
#country = Country.find(params[:country_id])
Rails::logger.debug "!!! Found airport #{#country.name}"
#airport = #country.airports.build(params[:airport])
The last line gives the error Minitest::UnexpectedError: ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError, but the params I'm calling with are
!!! Building airport with parameters {"airport"=>{"iata_code"=>"CCC",
"icao_code"=>"CCCC", "name"=>"Airport 3", "city"=>"City 3", "latitude"=>"1.5",
"longitude"=>"1.5"}, "country_id"=>"980190962", "controller"=>"airports",
"action"=>"create"}
and as far as I can see all of those are in my permitted parameters:
def airport_params
params.require(:airport).permit(:iata_code, :icao_code, :name, :city, :latitude, :longitude, :notes, :country_id)
end
Secondly, my airports\_form.html.erb begins <%= form_for [#country, #airport] do |f| %>, but that gives an error Minitest::UnexpectedError: ActionView::Template::Error: undefined method 'airports_path' for #<#<Class:0x4b0ba30>:0x55a2e48>. Yes, that path is undefined, but I was trying to get to path country_airports_path, which is defined.
So what am I missing here?
In "def create"
def create
...
#airport = #country.airports.build(airport_params)
You've correctly created the "airport_params" function, however you're not calling it. That should explain the ForbiddenAttributesError.
As for the undefined method, at first glance it looks like an incorrect association, are your models correctly related (i.e. belongs_to and has_many)? If so, you could try adding url: country_airports_path to the form_for field.
Attributes
The attributes error you've received is basically caused by your non-use of strong_params.
Rails 4 introduced strong_params to give you the ability to define specific attributes to pass to your model (prevents mass assignment). The problem you have is that you're using the Rails 3 way - passing all of the attributes without whitelisting them
As pointed out, you'll be better doing this:
#app/controllers/airports_controller.rb
Class AirportsController < ApplicationController
def create
#airport = Airport.new(airport_params)
end
private
def airport_params
params.permit(:airport).permit(:your, :params).merge(country_id: params[:country_id])
end
end
However, I believe there's something deeper you need to consider.
You're currently using .build for your airport model object. Whilst there's nothing wrong with this, you need to consider what you're trying to achieve...
The Airport model object is a standalone object. You can easily associate it with your Country model by setting the foreign_key in your strong params (demonstrated above)
If you use this line: #airport = #country.airports.build, you're working with the Country object, which IMO will open yourself up to errors down the line. As mentioned, there's nothing "wrong" with what you're doing; it's just that I'd either work with the Airport model directly (as written above), or use accepts_nested_attributes_for to work with the Country model
--
Route
Secondly, your path error is going to be caused by your form_for
form_for builds a form from an ActiveRecord object - which it does by taking arguments such as model_name to build the "url" / "action" for the form.
This means every time you populate a form_for object with an ActiveRecord object (variable), Rails will build a route based off of what you pass it (it does not know whether the object is nested or not)
<%= form_for #airport do |f| %>
# builds route using "airport_path"
<% end %>
If you want to create a nested form, you'll be much better using an array of ActiveRecord objects, as to provide Rails with the knowledge you're using a nested resource:
<%= form_for [#country, #airport] do |f| %>
# builds route using "country_airport_path"
<% end %>
This question is about problems I ran into after asking my previous question Rails - How do I refresh a page without reloading it?
Have been googling but cannot find a solution.
I'm building a function that gives out a random record every time people go to that page but the random record will change again if the user refresh the page.
Therefore I have tried using cookies
#posts_controller.rb
def new
#random = cookies[:stuff] ||= Stuff.random
end
and call #random.content in my posts/new view since I want only the content of the Stuff model, when I go to the page for the first time is fine but when I refresh the page, it gives me this error
undefined method 'content' for "#<Stuff:0x0000010331ac00>":String
Is there a way to resolve this?
Thanks!
----Update----
I have fixed the content missing problem using <%= show_random(#random)['content'] %> and the Internal Server Error expected Hash (got Array) for param 'post'using
<%= f.hidden_field :stuff_id , :value => show_random(#random)[:id] %>
stuff/new.html.erb
# app/views/stuff/new.html.erb
<%= show_random(#random)[:content] %>
<div>
<%= f.hidden_field :stuff_id , :value => show_random(#random)[:id] %><br>
</div>
But when creating the Post without meeting the validation, it gives me
no implicit conversion of Stuff into String
Extracted source (around line #2):
1
2 <%= show_random(#random)['title'] %>
I think it has something to do with my create action in my Posts_controller.erb
Please have a look below of my Posts_controller.erb
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Post created successfully!"
else
#random = Stuff.where(id: params[:post][:stuff_id]).first
render 'new'
end
end
The first time , as cookies[:stuff] is null, so a new Stuff instance is assigned to both cookies[:stuff] and #random, so the content method call on #random will be fine. But as you store an object into the cookies[:stuff], the value will be converted into a string by rails automatically.
The second time, you visit the page, the cookies[:stuff] is not empty, and is assigned to the #random variable. But as previous saying, the content inside the cookies is a string, so calling content method on a string can not work.
Make your .random method defined on Stuff return a serialized record, which you can then deserialize in your views (possibly with a helper method) to make it a hash:
# app/models/stuff.rb
def self.random
random_record_magic.to_json
end
end
# app/views/stuff/index.html.erb
<%= show_random(#random)['content'] %>
# app/helpers/stuff_helper.rb
def show_random(random)
JSON.parse(random)
end
I'm new at Ruby on Rails and have been struggling through for a while now. Specifically my issue is that I have two tables: appointments and service1s. Service1 has_many :appointments and Appointment belongs_to :service1.
On my site, users can sign up for an appointment. In that form they can select a service as such:
<%= f.label "Service" %>
<%= f.select :service1, options_from_collection_for_select(Service1.all, :id, :name), :prompt => true %>
In the appointments controller I have:
def create
#stylists = ["Zoe Andreadis", "Amanda Giorgi", "Michelle Oda", "Danny Quaranta", "Demi Tsionis", "Christina Vicencio", "Nancianne Warren"]
#appointment = Appointment.new(params.require(:appointment).permit(:date, :start, :stylistpref))
#appointment.service1 = Service1.find_by_name(:service1).id
#appointment.user = current_user
if #appointment.save
flash[:success] = "Appointment Booked for " + #appointment.date + #appointment.time
redirect_to(root_url)
else
render 'new'
end
end
The f.select stores the name as string, and I'm pretty sure the appointment model requires the id.
With that code I get this error message on the #appointment.service1 line: undefined method `id' for nil:NilClass
If i get rid of that line, then I get the flash error message that service1 was left blank.
Please help me solve this!
Thank you!
You don't have a Service1 with the name 'service1'. find_by_name is coming back with nil
Update in response to your comment:
That first code snippet you posted: that's within a form, correct? I think you want params[:object_key][:service1] where :object_key is the name of the record the form is based on.
I'm pretty sure there's an easier way to do what you're doing though. I'd have to see more code though.
I have the following in my products index page:
<%= button_to "Add", user_order_orderitems_path(user_id: current_user.id, item_id: x.id, order_id: current_user.group.current_order.id), class: "btn btn-mini" %>
Which I can see from the logs is being picked up by my Orderitems#create action in my controller ok. This looks like:
def create
#orderitem = Orderitem.new(orderitem_params)
if #orderitem.save
redirect_to items_path
else
redirect_to items_path
end
end
private
def orderitem_params
params.require(:orderitem).permit(:user_id, :order_id, :item_id)
end
end
The params specified in the button_to call are being created and are showing up in the logs as:
Started POST "/users/1/orders/1/orderitems?item_id=2264" for 127.0.0.1 at 2013-07-03 22:45:24 +0100
Processing by OrderitemsController#create as HTML
Parameters: {"authenticity_token"=>"blah=", "item_id"=>"2264", "user_id"=>"1", "order_id"=>"1"}
Fnally - the problem - my strong_params method can't process these params as the three params I care about are not nested in a hash with 'Orderitem's as a key. I would expect, for my create action to work, I need something like:
Parameters: {"authenticity_token"=>"blah=", "orderitems"=>{"item_id"=>"2264", "user_id"=>"1", "order_id"=>"1"}}
but I can't for the life of me work out how, with button_to I am able to do this - I have tried a form_for, but this also failed to work. Banging my head on a brick wall for a couple of days on this one...So, how can post my three ids to my OrderItemsController create action from an index view for Products but bypassing any form_for or new actions? Is it possible?
Please let me know if I am approaching this scenario (adding an item to a basket) in completely the wrong way.
This way you can treat a standard hash as one supported by strong parameters
raw_parameters = {"authenticity_token"=>"blah=", "item_id"=>"2264", "user_id"=>"1", "order_id"=>"1"}
parameters = ActionController::Parameters.new(raw_parameters)
parameters.permit(:user_id, :order_id, :item_id)