Rails - Problems Passing Params Through link_to - ruby-on-rails

I'm trying to update the game_started attribute through a link_to. I've also tried using a form_for via hidden_field with no luck.
I also keep getting the below errors
ArgumentError in GamesController#update
When assigning attributes, you must pass a hash as an argument.
Using Rails 5 and Ruby 2.4
Any explanation would be greatly appreciated!
show.html.erb
<% if #game.game_started %>
# some code
<% else %>
<%= link_to "Start The Game", game_path(#game, :game_started => true), :method => :put %>
<% end %>
GamesController
def edit
end
def update
#game = Game.find(params[:id])
if #game.update_attributes (params[:game_started])
redirect_to #game
end
end
def game_params
params.require(:game).permit(:game_type, :deck_1, :deck_2, :user_1, :user_2, :game_started)
end

Change it to
if #game.update_attributes (game_started: params[:game_started])
redirect_to #game
end

In show.html.erb should change to
<%= link_to "Start The Game", game_path(#game, :game => {:game_started => true}), :method => :put %>
In Controller should be
if #game.update_attributes (game_started: params['game']['game_started'])
redirect_to #game
end

The error is telling you that you're passing the wrong arguments into the update_attributes method call. It's expecting a hash like {game_started: params['game_started']}, while you're just giving it the value of params['game_started']. When you just give it a value, it won't know which field in the model to update. So change your code to:
```
if #game.update_attributes(game_started: params[:game_started])
redirect_to #game
end
```

Related

Why is my form not saving? form_for question

I have this form. I am new to rails and I am trying to write a simple ecommerce site. This is the only part not working. (It worked 2 days ago I sear)
<%= form_tag line_items_path do%>
<%binding.pry%>
<%= hidden_field_tag :lite_item, :order_id, #order.id%>
<%= hidden_field_tag :line_item, :menu_item_id, #menu_item.id%>
<%= number_field_tag :line_item, :quantity, 1 %>
<%= submit_tag "Add to Cart"%>
<% end %>
It gives params that look like:
#<ActionController::Parameters {"authenticity_token"=>"VECKnS5SBot1rCyekepPXZa7TyTYkfFi0KdNRTB617ZnelmQo8Lkz_cJmQ8nAmCHUdDlPu1mpkhrPvMKysfjew", "order_id"=>"1", "menu_item_id"=>"1", "quantity"=>"1", "commit"=>"Add to Cart", "controller"=>"line_items", "action"=>"create"} permitted: false>
The controller for the view looks like this:
class MenusController < ApplicationController
def index
#menu_items = MenuItem.all
end
def show
#menu_item = MenuItem.find(params[:id])
#line_items = current_order.line_items.build
end
end
The form is really going through the line_items controller
def create
binding.pry
#line_item = LineItem.create(line_item_params)
if #line_item.save
#order.line_item_id = #line_item.id
#order.save
redirect_to cart_path(#current_cart), notice: "Item added to cart."
else
redirect_to menu_path(#menu_item), alert: "Item did not add to cart."
end
end
With strong params like this
def line_item_params
params.require(:line_item).permit(:menu_item_id, :quantity, :order_id)
end
It should use the line_items_path POST>
If anything else is needed just ask. Thanks in advance.
There are a lot of problems here.
The signature is hidden_field_tag(name, value = nil, options = {}). So the parameters you would actually be creating with that form is:
{
"lite_item" => "order_id", # check your spelling...
"line_item" => "quantity"
}
Oops. And that not even going to happen as <%= number_field_tag :line_item, :quantity, 1 %> will raise since you're passing an integer where the method expects a hash.
If you really have to create the inputs manually you would want:
<%= hidden_field_tag "line_item[order_id]", #order.id %>
But since you actually have a model there is no reason why you should be using form_tag instead of form_for(#line_item) or form_with(model: #line_item).
<%= form_for(#line_item) do |form| %>
<%= form.hidden_field :order_id %>
<%= form.hidden_field :menu_item_id %>
<%= form.number_field :quantity %>
<%= form.submit_tag "Add to Cart"%>
<% end %>
The controller should also use the correct pluralization for the instance variable:
def show
#menu_item = MenuItem.find(params[:id])
#line_item = current_order.line_items.build
end
Your create method is also pretty questionable. All you should need is:
def create
# use .new not .create
#line_item = LineItem.new(line_item_params)
if #line_item.save
redirect_to cart_path(#current_cart), notice: "Item added to cart."
else
redirect_to menu_path(#menu_item), alert: "Item did not add to cart."
end
end
I have no idea why you think you need to update #order here. Your controller should just really be adding a row to what is essentially a join table.

Edit page to display all category names with edit inputs on each

I have categories nested inside of guides. I'm building an app to learn rails better and I'm trying to make a page that will display all categories that belong to a guide and have edit inputs under them and a save button next to it so the user can edit the names of the categories they want to change.
Bit stuck on how exactly how get this done.
here is the category_item_keys controller
def edit
#guide = Guide.friendly.find(params[:guide_id])
#category = Category.friendly.find(params[:category_id])
#key = #category.category_item_keys
end
def update
#guide = Guide.friendly.find(params[:guide_id])
#category = Category.friendly.find(params[:category_id])
#key = #category.category_item_keys.friendly.find(key_params) # no idea what to make this equal because it isn't one set key being edited on the page
if #key = #category.category_item_keys.update_attributes(key_params)
flash[:success] = "Key updated"
redirect_to #guide
else
render 'edit'
end
end
private
def key_params
params.require(:category_item_key).permit(:key, :slug)
end
routes
match '/guides/:guide_id/:category_id/keys/edit' => 'category_item_keys#edit', :via => :get
match '/guides/:guide_id/:category_id/keys/' => 'category_item_keys#update', :via => :post, as: :category_item_keys_update
edit.html.erb
<ul>
<% #key.each do |key| %>
<li><%= key.key #just shows key name %><br>
<%= form_for([#category, #keys], url: category_item_keys_create_path) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :key, "Key name" %>
<%= f.text_field :key %>
<%= f.submit "Save" %>
<% end %>
</li>
<% end %>
</ul>
This just gives me an error of:
undefined method `to_key' for #<CategoryItemKey::ActiveRecord_Associations_CollectionProxy:0x007fe20a86b480>
Later I plan on using an in-place editor gem but i would like to learn how this can be done fist.
EDIT:
Fixed the error ( changed form_for([#category, #keys] to form_for([#category, key] and turns out this way works for displaying and allowing all categories to be edited... to an extent.
I get another error when i submit a form
undefined method 'update_attribute'
EDIT 2
slowly getting there. I Changed the update #key variable to #key = #category.category_item_keys.all to fix the error. But this line is now giving me problems
if #key = #category.category_item_keys.update_attributes(key_params)'
THIRD EDIT
ERROR
Couldn't find CategoryItemKey without an ID
on line
#key = #category.category_item_keys.find params[:id]
paramaters:
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"egj/OebdSbxxaoaTkr46WVIOIIu4Ezijzu45kqxLT0krjFWHqi67SRJDSgV7bcL6SeoGpUSYsrolspylCXBu9g==",
"category_item_key"=>{"name"=>"def1111"},
"commit"=>"Save",
"guide_id"=>"dbz",
"category_id"=>"characters"}
Here's how to clean up the code:
#config/routes.rb
resources :guides do
resources :categories, path: "" do
resources :category_item_keys, path: "keys", only: [:update] do
get :edit, on: :collection #-> url.com/guides/:guide_id/:category_id/keys/edit
end
end
end
#app/controllers/keys_controller.rb
class KeysController < ApplicationController
def edit
#guide = Guide.find params[:guide_id]
#category = Category.find params[:category_id]
#keys = #category.category_item_keys
end
def update
#guide = Guide.find params[:guide_id]
#category = Category.find params[:category_id]
#key = #category.category_item_keys.find params[:id]
if #key.update key_params
redirect_to #guide, success: "Key Updated"
else
render 'edit'
end
end
private
def key_params
params.require(:category_item_key).permit(:key)
end
end
#app/views/keys/edit.html.erb
<% #keys.each do |key| %>
<%= form_for [#guide, #category, key] do |f| %>
<%= f.text_field :key %>
<%= f.submit %>
<% end %>
<% end %>
If you wanted to use an in-place editor gem, I'd recommend looking at X-Editable, as we've applied it here (its only a demo app, just sign up for free and go to profile):
Looks like you are trying to do update_attributes on a collection instead of an object. Try to first fetch the key object
#key = #category.category_item_keys.friendly.find(params[:id])
and then try to update its attributes
if #key.update_attributes(key_params)
...
end
use nested forms available in rails 4.

ajax request through link_to with params

I'm trying to do an AJAX request, form a link_to helper, that also contains params.
<%= link_to 'Bookmark', bookmarks_path(user_id: #user.id, week_number: #week_number, year: #answers.last.created_at.year), remote: true %>
the error I get in my console is:
ActionController::ParameterMissing - param is missing or the value is empty: bookmark:
the other issue is that it fires off a POST and GET request, both return in a 404.
My bookmarks controller:
def create
#bookmark = current_user.bookmarks.create(bookmark_params)
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
private
def bookmark_params
params.require(:bookmark).permit(:user_id, :week_number, :year)
end
is this the incorrect way to go about doing this, and how do I fix the issues I'm having?
For creating bookmark through ajax, you need to make form like:-
<%= form_for #bookmark, remote: true do |f|%>
<%= f.select :weak_number, collection_of_values %>
<%= f.select :year, collection_of_values %>
<% end %>
Also, there is no need to permit user_id.So, remove from bookmark_params method. current_user.bookmarks will set user_id in bookmark record.

Is there a way to do this in Rails without mass assignment?

Members create votes that both belong to them and to another model, Issues. Currently I'm doing this with a hidden form and passing the appropriate parameters. Here's the code on the issues index view:
<%= form_for(#vote) do |f| %>
<%= f.hidden_field "issue_id", :value => issue.id %>
<%= f.hidden_field "member_id", :value => session[:member_id] %>
<%= f.hidden_field "type", :value => :Upvote %>
<%= f.label issue.upvotes_count(issue.id) %>
<%= submit_tag "Up", :class => 'up-vote' %>
<% end %>
This doesn't seem ideal as it leaves issue_id and member_id open to mass assignment. Is there a better way to do this with a button_to tag or something?
Here's the controller code:
class VotesController < ApplicationController
#GET
def new
#vote = Vote.new
end
# POST
def create
#vote = Vote.new(params[:vote])
#vote.member_id = current_member
if #vote.save
redirect_to issues_path
else
redirect_to issues_path, notice: "you must be logged in to vote"
end
end
end
and
class IssuesController < ApplicationController
# GET
def index
#issues = Issue.find(:all)
#vote = Vote.new
end
# GET
def show
#issue = Issue.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
end
Use scope in the controller:
#issue = Issue.find(params[:issue_id])
#vote = #issue.votes.new(params[:vote])
#vote.save
and do not pass member_id and issue_id to hidden fields.
If you have proper nested RESTful routes you should be able to get params[:issue_id] directly.
If issue and member_id are available before you vote.save! in the controller, you can set them manually there.
Normally you get values like member_id from current_user in the controller rather than passing it via form parameters. How you have it currently does expose you to mass-assignment.
Do members have to login before voting? If so, then you don't need to include member_id as a hidden field because you can grab current_user in the controller and this will provide good protection since there wouldn't be any advantage for a member to hack issue_id or type.

Rails f.error_messages alwais empty

What did I do :)? In rails form has f.error_messages alwais empty. How can I fix/check this?
Thx
The AR#validate method fills the model's error hash with validation errors.
If between instantiating the model and the call f.error_messages you do not call validate (via AR#save or directly) the #errors hash never gets filled and the errors are never shown).
Also make sure you do not redirect ( the validated object gets lost and a new one is created and has no "filled" #errors hash ), but call render :action => ...
Are you looking for error_messages_for :model?
After validation, this function will build a list of error messages for your view.
For example:
# users_controller.rb
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :action => 'new'
end
end
# view/users/new.html.erb
<%= error_messages_for :user %>
<% form_for #user do |f| %>
...
<% end %>

Resources