create an instance of a model by clicking a button - ruby-on-rails

I am pretty new to Ruby and I am working on a hangman game .
What I am trying to do is create a new game when the user simply click on a button and I want that "click" to redirect to the show of that game.
Here are my models :
class Game < ApplicationRecord
has_many :guesses
end
class Guess < ApplicationRecord
belongs_to :game
end
Here are my controllers :
class GamesController < ApplicationController
skip_before_action :authenticate_user!
def index
#games = Game.all
end
def create
#game = Game.new(game_params)
#game.save!
redirect_to game_path(#game)
end
def show
#game = Game.new
#game = Game.last
end
def destroy
#game = Game.find(params[:id])
#game.destroy
redirect_to home_path
end
private
words =[
"spokesperson", "firefighter", "headquarters", "confession", "difficulty", "attachment", "mechanical",
"accumulation", "hypothesis", "systematic", "attraction", "distribute", "dependence", "environment",
"jurisdiction", "demonstrator", "constitution", "constraint", "consumption", "presidency", "incredible",
"miscarriage", "foundation", "photography", "constituency", "experienced", "background", "obligation",
"diplomatic", "discrimination", "entertainment", "grandmother", "girlfriend", "conversation", "convulsion",
"constellation", "leadership", "insistence", "projection", "transparent", "researcher", "reasonable","continental",
"excavation", "opposition", "interactive", "pedestrian", "announcement", "charismatic", "strikebreaker",
"resolution", "professional", "commemorate", "disability", "collection", "cooperation", "embarrassment",
"contradiction", "unpleasant", "retirement", "conscience", "satisfaction", "acquaintance", "expression",
"difference", "unfortunate", "accountant", "information", "fastidious", "conglomerate", "shareholder",
"accessible", "advertising", "battlefield", "laboratory", "manufacturer", "acquisition", "operational",
"expenditure", "fashionable", "allocation", "complication", "censorship", "population", "withdrawal",
"sensitivity", "exaggerate", "transmission", "philosophy", "memorandum", "superintendent", "responsibility",
"extraterrestrial", "hypothesize", "ghostwriter", "representative", "rehabilitation", "disappointment",
"understanding", "supplementary", "preoccupation"
]
#word_to_guess = words.sample
#health_bar = 5
#game_status = "Game not started yet"
def game_params
params.require(:game).permit(#word_to_guess, #health_bar, #game_status)
end
end
Here is what I have been trying to do
<%= link_to "nouvelle partie", game_path(game), method: :create %>
but the errors message is :
"undefined local variable or method `game' for #ActionView::Base:0x0000000000d4d0"

POST method won't work with link_to. Try using the to button_to instead of link_to like this:
<%= button_to "nouvelle partie", game_path(game), method: :post %>
I think the bug is due to the variable game not being passed to partial. You have to pass it on like this:
<%= render "game", game: #game %>
And you don't have to pass variables in game_params. You only need to set them in the show method where you will be redirected from the create method. Also, you don't set the current game like this Game.last, you just take id which is contained in the url.
def create
#game = Game.new
#game.save!
redirect_to game_path(#game)
end
def show
#game = Game.find(params[:id])
#word_to_guess = words.sample
#health_bar = 5
#game_status = "Game not started yet"
end

have you tried?
<%= link_to "nouvelle partie", game_path(#game), method: :create %>

Related

Issue with HomeController showing undefined method

I am trying to pass stored_products from shopify into a Rails app but keep getting a home controller error at https://f588240c.ngrok.io/ i have made updates, with no luck and restarted the server a number of times with no luck.
Any help would be welcomed. Heres the code
class Api::V1::HomeController < ShopifyApp::AuthenticatedController
def index
#products = ShopifyAPI::Product.find(:all, params: { limit: 10 })
#products.each do |product|
StoredProduct.where(shopify_id: product.id)
.first_or_create do |stored_product|
stored_product.shopify_id = product.id
stored_product.shopify_title = product.title
stored_product.shopify_handle = product.handle
stored_product.shopify_image_url = product.image.src
stored_product.shop_id = #shop.id
stored_product.save
product.images.each do |image|
ProductImage.where(shopify_id: image.id)
.first_or_create do |product_image|
product_image.image_url = image.src
product_image.stored_product_id = stored_product_id
product_image.shopify_id = image.id
end
end
end
end
#stored_products = StoredProduct.belongs_to_shop(#shop.id)
end
end
From the authenticated controller
private
def set_shop
#shop = Shop.find_by(id: session[:shopify])
set_locale
end
from the store_products.rb file
class StoredProduct < ApplicationRecord
belongs_to :shop
has_many :product_images
scope :belongs_to_shop, -> shop_id { where(shop_id: shop_id) }
end
For this specific issue/code tutorial, the private set_shop method should be set like follows:
def set_shop
#shop = Shop.find_by(id: session[:shop_id])
set_locale
end
The other answer has params instead of session
The problem is that #shop is nil. The error message says it cannot call the method .id on NilClass.
In the image I can see that you have a shop_id in the params so you might just need to change your code here:
def set_shop
#shop = Shop.find_by(id: params[:shop_id])
set_locale
end
But that depends on your code, so please double check.

Params issue with nested attributes

I'm having trouble with what I feel should be a simple issue. I'm trying to create a basic match score reporting app and I'm trying to create a match and the match players simultaneously.
My models:
class Match < ApplicationRecord
has_many :match_players
accepts_nested_attributes_for :match_players
end
class MatchPlayer < ApplicationRecord
belongs_to :player
belongs_to :match
end
My form:
.container
%h1 Log a completed match
= simple_form_for #match do |f|
= f.input :played_at, html5: true
= f.simple_fields_for :match_player do |mp|
= mp.input :player_id, collection: Player.all, label_method: lambda { |player| player.first_name + " " + player.last_name }
= f.submit "Submit Match Score"
My controller action and params:
def create
#match = Match.new(match_params)
if #match.save
player = MatchPlayer.create(player: current_player, match: #match)
opponent = MatchPlayer.create(player: Player.find(match_params[:match_player_attributes][:player_id], match: #match))
else
render :new
end
end
private
def match_params
params.require(:match).permit(:played_at, match_player_attributes: [:player_id])
end
Right now I'm getting a found unpermitted parameter: :match_player issue. If I change match_player_attributes to match_player in my params then I get unknown attribute 'match_player' for Match. The error is occurring on the first line of the create action (#match = Match.new(match_params))
Any help would be appreciated!
Edit following suggestions:
Controller:
def new
#match = Match.new
#match.match_players.build
end
def create
#match = Match.new(match_params)
if #match.save!
player = MatchPlayer.create(player: current_player, match: #match)
opponent = Player.find(match_params[:match_players_attributes]["0"][:player_id])
opponent_match_player = MatchPlayer.create(player: opponent, match: #match)
redirect_to(root_path, notice: "Success!")
else
render :new
end
end
private
def match_params
params.require(:match).permit(:played_at, match_players_attributes: [:player_id])
end
Form:
= simple_form_for #match do |f|
= f.input :played_at, html5: true
= f.simple_fields_for :match_players do |mp|
= mp.input :player_id, collection: Player.all, label_method: lambda { |player| player.first_name + " " + player.last_name }
= f.submit "Submit Match Score"
Now it's creating 3 match_players, one for the current_player and 2 of the opponent. What's going on?
Looks like the problem in the simple typo,
Try to change:
= f.simple_fields_for :match_player do |mp|
to
= f.simple_fields_for :match_players do |mp|
Also
def match_params
params.require(:match).permit(:played_at, match_player_attributes: [:player_id])
end
to
def match_params
params.require(:match).permit(:played_at, match_players_attributes: [:player_id])
end
Here is wiki with examples
UPD:
From wiki I shared with, you can find this point:
accepts_nested_attributes_for - an ActiveRecord class method that goes
in your model code - it lets you create and update child objects
through the associated parent. An in depth explanation is available in
the ActiveRecord documentation.
It means that you don't need to create opponent manually
opponent = Player.find(match_params[:match_players_attributes]["0"][:player_id])
opponent_match_player = MatchPlayer.create(player: opponent, match: #match)
Because when you send your params with nested attributes:
match_players_attributes: [:player_id] inside of match creation process match_player will be created automatically
I can see in your model, you have:
has_many :match_players
hence, in your controller and in your form, you must use match_players (plural not singular)
Thus, in your controller, you will have:
def match_params
params.require(:match).permit(:played_at, match_players_attributes: [:id, :player_id])
end
And, in your form:
...
= f.simple_fields_for :match_players do |mp|
...
Notice the last s of match_player in form and in controller.

NoMethodError undefined method `id' for nil:NilClass:

I know this kind of question is already answered multiple times but i seriously unable to figure it out what is causing a problem here, I am having trouble solving this problem. I keep getting the same error when i'm trying to create new registration ( http://localhost:3000/registrations/new?course_id=1 ) :
NoMethodError at /registrations
undefined method `id' for nil:NilClass
Here is my RegistrationsController:
class RegistrationsController < ApplicationController
before_action :set_registration, only: [:show, :edit, :update, :destroy]
def index
#registrations = Registration.all
end
def show
end
def new
#registration = Registration.new
#course = Course.new
#course = Course.find_by id: params["course_id"]
end
def create
#registration = Registration.new registration_params.merge(email: stripe_params["stripeEmail"], card_token: stripe_params["stripeToken"])
raise "Please Check Registration Errors" unless #registration.valid?
#registration.process_payment
#registration.save
redirect_to #registration, notice: 'Registration was successfully created.'
rescue Exception => e
flash[:error] = e.message
render :new
end
protect_from_forgery except: :webhook
def webhook
event = Stripe::Event.retrieve(params["id"])
case event.type
when "invoice.payment_succeeded" #renew subscription
Registration.find_by_customer_id(event.data.object.customer).renew
end
render status: :ok, json: "success"
end
private
def stripe_params
params.permit :stripeEmail, :stripeToken
end
def set_registration
#registration = Registration.find(params[:id])
end
def registration_params
params.require(:registration).permit(:course_id, :full_name, :company, :telephone, :email, :card_token)
end
end
My Registration Model:
class Registration < ActiveRecord::Base
belongs_to :course
def process_payment
customer_data = {email: email, card: card_token}.merge((course.plan.blank?)? {}: {plan: course.plan})
customer = Stripe::Customer.create customer_data
Stripe::Charge.create customer: customer.id,
amount: course.price * 100,
description: course.name,
currency: 'usd'
#Annotate Customer Id when Registration is Created
cusotmer_id = customer.id
end
def renew
update_attibute :end_date, Date.today + 1.month
end
end
Registration New.html.haml File :
%section#course-content
%section#ruby
%section.detailed-syllabus
.wrapper-inside
= form_for #registration, html: { class: "basic-grey" } do |f|
- if #registration.errors.any?
#error_explanation
%h2
= pluralize(#registration.errors.count, "error")
prohibited this registration from being saved:
%ul
- #registration.errors.full_messages.each do |message|
%li= message
.field
= f.hidden_field :course_id, value: #course.id
.field
= f.label :full_name
= f.text_field :full_name
.field
= f.label :company
= f.text_field :company
.field
= f.label :email
= f.text_field :email
.field
= f.label :telephone
= f.text_field :telephone
//‘Stripe.js’ will recognize the card data because we have marked the inputs with ‘data-stripe’ attribute as: number, cvv, exp-month and exp-year.
= javascript_include_tag "https://js.stripe.com/v2/"
:javascript
Stripe.setPublishableKey('#{Rails.application.secrets.stripe_publishable_key}');
= label_tag "Card Number", nil, required: true
.control-group
.controls
= text_field_tag :card_number, nil, class: "input-block-level", "data-stripe" => "number"
= label_tag "Card Verification", nil, required: true
.control-group
.controls
= text_field_tag :card_verification, nil, class: "input-block-level", "data-stripe" => "cvv"
= label_tag "Card Expires", nil, required: true
= select_tag :exp_month, options_for_select(Date::MONTHNAMES.compact.each_with_index.map { |name,i| ["#{i+1} - #{name}", i+1] }), include_blank: false, "data-stripe" => "exp-month", class: "span2"
= select_tag :exp_year, options_for_select((Date.today.year..(Date.today.year+10)).to_a), include_blank: false, "data-stripe" => "exp-year", class: "span1"
.actions
= f.submit "Registration Payment", class: "btn", style: "color: white;background: rgb(242, 118, 73);"
Does anyone know how to assist me in this? Greatly appreciate all the help.
Additional Can anyone please guide me through how to pass id between 2 models like this guy did between 2 models as he's creating a scaffold for one model but passing ID lets him create values for another model too without creating actions for another controller https://github.com/gotealeaf/stripe-basics.git
Edited:
GitHub Repository For This Code
https://github.com/ChiragArya/Stripe_CheckOut_Demo
From your comments, it appears the error is caused by :
#course.id being nil
The way to fix this is to ensure #course is defined properly. You need to do the following:
def new
#registration = Registration.new
#course = Course.find_by id: params["course_id"]
end
The other issue you have here is that your routes should be able to handle courses without having to append them with ?course_id=1:
#config/routes.rb
resources :registrations do
get :course_id, to: "registrations#new" #-> yoururl.com/registrations/:course_id
end
This will still give you the course_id param in the new action; just makes it more Rails.
--
Controller
You also need some structure in your code (you're aiming for fat model, thin controller). It looks like you're coming to Rails as a Ruby dev; you need to appreciate that Rails handles most of the exceptions etc for you.
Specifically, you need to look at how to remove code out of your actions:
def create
#registration = Registration.new registration_params
#registration.process_payment
if #registration.save
redirect_to #registration, notice: 'Registration was successfully created.'
else
# handle error here
end
end
private
def registration_params
params.require(:registration).permit(:course_id, :full_name, :company, :telephone, :email, :card_token).merge(email: stripe_params["stripeEmail"], card_token: stripe_params["stripeToken"])
end
-
`id' for nil:NilClass
Finally, you have to remember this error basically means the variable you're trying to invoke an action for is nil.
Ruby populates nil variables with a NilClass object, thus it's difficult to determine what the error actually is. All it means is that the variable you're trying to call a method on doesn't have the aforementioned method, as Ruby has populated it with the NilClass object.
Try changing Registration#new action to
def new
#course = Course.find(params[:course_id])
#registration = #course.registrations.new
end
add this in your def create
def create
#course = Course.find_by id: params["registration"]["course_id"]
#registration = Registration.new registration_params.merge(email: stripe_params["stripeEmail"], card_token: stripe_params["stripeToken"])
raise "Please Check Registration Errors" unless #registration.valid?
#registration.process_payment
#registration.save
redirect_to #registration, notice: 'Registration was successfully created.'
rescue Exception => e
flash[:error] = e.message
#course = Course.find_by id: params["registration"]["course_id"]
render :new
end

Rails Polymorphic - controller & views

Hi I'm trying to create an event depending on the eventable type. the eventable type is either group or shop.
Writing my code, I'm currently sure that their is a better way create my routes and controller (still a rails newbie)
Is there a way to create only one new and create method pass in the eventable type ?
Models:
class Event < ActiveRecord::Base
belongs_to :eventable, polymorphic: true
class Group < ActiveRecord::Base
has_many :events, as: :eventable
class Shop < ActiveRecord::Base
has_many :events, as: :eventable
Routes:
resources :events do
collection do
get :new_national_event
get :new_local_event
post :create_national_event
post :create_local_event
end
do
event-controller:
def index
#search = Search.new(params[:search])
#shop = find_user_shop(#search.shop_id)
#group = #shop.group
#shop_events = #shop.events
#group_events = #group.events
end
def new_national_event
#user = current_user
#event = #user.group.events.new
end
def new_local_event
#shop = find_user_shop(#search.shop_id)
#event = #shop.events.new
end
def create_national_event
user = current_user
#event = user.group.events
if #event.save!
flash.now[:notice] = "Votre événement national a bien été enregistré"
render :index
else
flash.now[:error] = "Erreur lors de l'enregistrement du événement national"
render :new
end
end
def create_local_event
user = current_user
#event = user.group.shop.events
if #event.save!
flash.now[:notice] = "Votre événement local a bien été enregistré"
render :index
else
flash.now[:error] = "Erreur lors de l'enregistrement du événement local"
render :new
end
end
views:
index.html.slim
= link_to new_national_event_events_path
= link_to new_local_event_events_path
new_national_event_events_path.html.slim
= form_for #event, :url => create_national_event_events_path, :method => :post do |f|
div class="field"
= f.text_field :title, :required => true
div class="field"
= f.text_field :threshold, :required => true
div class="form-actions"
=f.submit "Create", class: "btn blue"
If it is just the routing you are concerned with, and not the number of actions, you can use constraints to allow a single path variable send the request to one of a multiple number of actions, this can be useful in some cases where you may want to have multiple buttons to multiple actions all reading from a single form, or if you just want to simplify your routes variable naming.
use the commit_param_routing gem and in your routes file you can write something like:
resources :events do
collection do
post :save, constraints: CommitParamRouting.new(EventController::CREATENATIONAL), action: :create_national_event
post :save, constraints: CommitParamRouting.new(EventController::CREATELOCAL), action: :create_local_event
end
end
add the constants to your controller:
class EventController
CREATENATIONAL = "create national"
CREATELOCAL = "create local"
.....
end
and then all that is left is to add them to your view file submit buttons:
div class="form-actions"
.row
.col-xs-2
=f.submit EventController::CREATENATIONAL
.col-xs-2
=f.submit EventController::CREATELOCAL
sorry for if its not quite what you were looking for or unclear, my first answer!

NoMethodError in Lines#show

UPDATE: The answer bellow is correct. Just wanted to update what I did to solve the problem.
First I had to delete all my previous lines in the rails console.
Then I used the bye bug gem in my lines controller at the bottom of the create method to discover where the next bug occurred. I created a test line that I needed to delete again. so I ran
Line.last.delete in console.
This is the way my lines controller create method looks now (working no bugs)
def create
if user_signed_in?
#line = Line.create(line_params)
if #line
if params[:line][:previous_line_id].empty?
#line.story = Story.create
#line.save
else
#line.story = #line.previous_line.story
#line.save
end
redirect_to line_path(#line)
else
flash[:error] = #line.errors
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
end
else
Finally I ran #Lines.each { |line| line.update.attribute(:story_id: 3)}
This gave the necessary association between lines and story.
ORIGINAL POST BELLOW.
I'm getting this error in my rails app. I think that when I create a new line or start a story, it doesn't automatically add it to a story object. I've listed my show.html.erb file as well as my lines controller.rb file.
What am I missing? How do I get the controller to add data to the story object correctly?
Thanks!
I added a few lines of code to my lines controller:
class LinesController < ApplicationController
def new
params[:previous_line_id].nil? ? #line = Line.new : #line = Line.find(params[:previous_line_id]).next_lines.create
#lines = #line.collect_lines
#ajax = true if params[:ajax]
render :layout => false if params[:ajax]
if #line.previous_line
#line.update_attribute(:story_id, #line.previous_line.story.id)
else
story = Story.create
#line.story = story
#line.save
end
end
def create
if user_signed_in?
#line = Line.create(line_params)
if #line
redirect_to line_path(#line)
else
flash[:error] = #line.errors
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
end
else
flash[:error] = "Please sign in or register before creating a line!"
unless params[:line][:previous_line_id].empty?
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
else
redirect_to root_path
end
end
end
# params[:id] should correspond to the first line of the story.
# if params[:deeper_line_id] is not nil, that means that they want to render up to the nested line id
def show
#lines = Line.find(params[:id]).collect_lines
#next_lines = #lines.last.next_lines.ranked
#lines.last.update_attribute(:score, #lines.last.score + 1)
end
def select_next
#line = Line.find(params[:id])
#line.update_attribute(:score, #line.score + 1)
#lines = [#line]
#next_lines = #line.next_lines.ranked
render :layout => false
end
def send_invite
if user_signed_in?
UserInvite.send_invite_email(current_user,Line.find(params[:id]), params[:email]).deliver
flash[:notice] = "Your invite was sent!"
else
flash[:error] = "Please sign in"
end
redirect_to Line.find(params[:id])
end
private
def line_params
params.require(:line).permit(:text, :previous_line_id, :user_id)
end
end
I added these lines to the controller pictured above
if #line.previous_line
#line.update_attribute(:story_id, #line.previous_line.story.id)
else
story = Story.create
#line.story = story
#line.save
end
Here is my show.html.erb file
<div class="row">
<div class="col-lg-2">
</div>
<div class="box-container col-lg-7 ">
<div id="story" class="box">
<% #lines.each do |line| %>
<span class="story-line" data-id="<%=line.id%>"><%= link_to line.text, '#', :class=>"story-line" %></span>
<% end %>
</div>
<div id="next-steps">
<%= render 'next_steps' %>
</div>
<span style="font-size:.9em; margin-bottom:15px; display:block;">*If the links don't work, try refreshing.</span>
</div>
<div class="col-lg-2" style="padding-right:25px;">
<%= render 'invite' %>
Your Fellow Collaborators: <br />
<div class="collaborators">
<% #lines.last.story.collaborators.uniq.each do |collaborator| %>
<%= link_to profile_path(:id => collaborator.id) do %>
<%= image_tag collaborator.profile_image_uri, :class => "prof-icon" %>
<% end %>
<% end %>
</div>
Story model
class Story < ActiveRecord::Base
has_many :lines
has_and_belongs_to_many :collaborators, :class_name => "User", :join_table => "collaborators_stories", :association_foreign_key => :collaborator_id
def first_line
self.lines.first_lines.first_lines.first
end
end
Here is my lines.rb file
class Line < ActiveRecord::Base
scope :first_lines, -> { where previous_line_id: nil}
scope :ranked, -> { order("score + depth DESC")}
belongs_to :user
belongs_to :story
belongs_to :previous_line, :class_name => "Line", :foreign_key => "previous_line_id"
has_many :next_lines, :class_name => "Line", :foreign_key => "previous_line_id"
validates_presence_of :text
after_create :update_depths
def update_depths
line = self.previous_line
while !line.nil?
line.update_attribute(:depth, line.depth + 1)
line = line.previous_line
end
end
def first_line
line = self
while !line.previous_line.nil?
line = line.previous_line
end
line
end
def collect_lines
line = self
lines = [self]
while !line.previous_line.nil?
lines.unshift(line.previous_line)
line = line.previous_line
end
lines
end
end
Problem is orphaned lines in your database. Look for them and associate it to a story, or delete it:
How to find orphaned records:
http://antonzolotov.com/2013/01/26/how-to-find-and-delete-orphaned-records-with-ruby-on-rails.html
Then review the create method to ensure a line should be part of a story:
#short example review activerecord relations
#story = Story.find(params[:story_id])
story.lines.create(line_params)
That should work.
EDIT:
def self.find_orphan_ids
Lines.where([ "user_id NOT IN (?) OR story_id NOT IN (?)", User.pluck("id"), Story.pluck("id") ]).destroy_all
end

Resources