I am coding a Rails app, where I would like to show specific restaurants, hotels and bars for a specific city.
My problem is when I want to show a specific instance of a Restaurant (for now), I always end-up on the page of the Restaurant with the id 1.
Here is my routes file :
Rails.application.routes.draw do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
root to: 'cities#index'
resources :cities, only: %i[index show] do
resources :restaurants, only: [:show]
resources :hotels, only: [:show]
resources :clubs, only: [:show]
end
end
Here my show for a specific city:
<h1><%= #city.name %></h1>
<div class="row">
<div class="col-4">
<h2>Restaurants</h2>
<% #restaurants.each do |restaurant| %>
<h3><%= link_to restaurant.name, [#city, #restaurant] %></h3>
<% end %>
</div>
<div class="col-4">
<h2>Hotels</h2>
<% #hotels.each do |hotel| %>
<h3><%= hotel.name %></h3>
<% end %>
</div>
<div class="col-4">
<h2>Bars</h2>
<% #clubs.each do |club| %>
<h3><%= club.name %></h3>
<% end %>
</div>
</div>
Here my Cities controller:
class CitiesController < ApplicationController
def index
#cities = City.all
end
def show
#city = City.find(params[:id])
#restaurants = Restaurant.all
#hotels = Hotel.all
#clubs = Club.all
end
end
Here my restaurant controller:
class RestaurantsController < ApplicationController
before_action :find_city
before_action :set_restaurant, only: [:show]
def index
#restaurants = #city.restaurants
end
def show; end
private
def find_city
#city = City.find(params[:city_id])
end
def set_restaurant
#restaurant = #city.restaurants.find(params[:id])
end
end
On the city show, whatever the Restaurant I click on, I always end up on the page of the restaurant with the id 1 and with this path: http://localhost:3000/cities/1/restaurants/1 and I really don't understand why...
I have used this tutorial to help me but I am still missing something apparently.
Change this line:
<h3><%= link_to restaurant.name, [#city, #restaurant] %></h3>
to this:
<h3><%= link_to restaurant.name, restaurant %></h3>
Related
I need help with this my code,that displays list of posts of users along with their username.What i want is, when i click on a username of a particular post, it should send me to that user's profile. instead, it send's to my own profile or the current user's profile whiles i want it to link me to the username i have clicked profile. (e.g like when you click on a username on Instagram, it links you to the user's profile so you can follow or unfollow and see their post)
Please i need help. what i'm i not doing right in my code.
i'm on rails 5.2.3 & ruby 2.3.3
Home
<div class="posts">
<% #posts.each do |post| %>
<section class="post">
<div class="user">
<div class="avatar">
<img src="assets/avater.jpg">
</div>
<%= link_to post.user.username, user_path(post.user), class: 'username' %>
</div>
<%= image_tag post.image, class: 'main-image' %>
<div class="description">
<% post.description.to_s.split(' ').each do |word| %>
<% if word.start_with?('#') %>
<%= link_to word, search_index_path(query: word) %>
<% else %>
<%= word %>
<% end %>
<% end %>
</div>
</section>
<% end %>
<%= paginate #posts %>
</div>
routes
Rails.application.routes.draw do
get 'search/index'
devise_for :users
get 'home/index'
resources :posts
root 'home#index'
resources :users, only: [:show, :edit, :update]
resources :posts, only: [:new, :create, :show, :destroy]
end
users controller
class UsersController < ApplicationController
before_action :find_user
def show
#user = User.find(params[:id])
#posts = current_user.posts.order(created_at: :desc)
end
def edit
end
def update
current_user.update(user_params)
redirect_to current_user
end
private
def find_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:username, :name, :website,:bio, :email, :phone, :gender, :avatar)
end
end
post controller
class PostsController < ApplicationController
def new
#post = current_user.posts.build
end
def create
#post = Post.create(post_params)
redirect_to root_path
end
def show
#post = Post.find(params[:id])
end
def destroy
#post = current_user.posts.find(params[:id])
#post.destroy
redirect_to user_path(current_user)
end
private
def post_params
params.require(:post).permit(:description, :image, :user_id)
end
end
home controller
class HomeController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.order(created_at: :desc).page(params[:page]).per(5)
end
end
I guess the problem is that on the show method from users_controller, you are getting the posts from current_user instead of the user, it should be #posts = #user.posts.order(created_at: :desc)
My project has three main parts to it:
Pages (similar to articles)
Categories (the pages have a category associated with them)
Tags (each Page can have several different tags)
I have a sidebar which uses #categories to pull through a list of all the current categories in my project:
<div class="col-md-3">
<p class="lead">Categories</p>
<div class="list-group">
<%= link_to 'All articles', pages_path(#page), :class => 'list-group-item' %>
<% #categories.each do |category| %>
<%= link_to category.name, category_path(category), :class => 'list-group-item' %>
<% end %>
</div>
</div>
But currently I need to include
#categories = Category.all
In my index and show actions in my controllers for both pages and categories so that the sidebar loads (I only use the sidebar in these two parts of the project).
Is there an easier way of doing this than including the above code in every action in the controller?
Also with the tags controller after creating a page and going to the tag's show page to view any pages associated with those tags, I get an error saying 'Couldn't find page with 'id'=2.
class TagsController < ApplicationController
def index
#tags = Tag.all
end
def show
#tag = Tag.find(params[:id])
#page = Page.find(params[:id])
#categories = Category.all
end
-
<% #tag.pages.each do |page| %>
<div class="thumbnail">
<div class="text">
<article class="clearfix">
<%= link_to page.title, url_for_page(page), class: "h1" %>
<p class="pull-right"><span class="glyphicon glyphicon-time"></span> Posted on <%= page.created_at.to_formatted_s :long %></p>
<hr />
<%= page.body.html_safe %>
<hr />
<div class="btn-group btn-group-xs" role="group" aria-label="...">
<% page.tags.each do |tag| %>
<%= link_to tag.name, tag_path(tag), class: "btn btn-info" %>
<% end %>
</div>
</article>
</div>
</div>
<% end %>
Anyone got any ideas? Any help would be greatly appreciated :)
Thanks!
Update:
Routes file:
Rails.application.routes.draw do
resources :categories
resources :pages
resources :tags
Models:
Category.rb
class Category < ActiveRecord::Base
has_many :pages
Page.rb
class Page < ActiveRecord::Base
include Bootsy::Container
belongs_to :category
has_many :taggings
has_many :tags, through: :taggings
def tag_list
self.tags.collect do |tag|
tag.name
end.join(", ")
end
def tag_list=(tags_string)
tag_names = tags_string.split(", ").collect{ |s| s.strip.downcase }.uniq
new_or_found_tags = tag_names.collect { |name| Tag.find_or_create_by(name: name) }
self.tags = new_or_found_tags
end
end
Tag.rb
class Tag < ActiveRecord::Base
include Bootsy::Container
has_many :taggings
has_many :pages, through: :taggings
def to_s
name
end
end
Tagging.rb
class Tagging < ActiveRecord::Base
include Bootsy::Container
belongs_to :tag
belongs_to :page
end
You could add a before_action for the controller limited to only index and show actions, as below:
class TagsController < ApplicationController
before_action :load_categories, only: [:index, :show]
# Your code
private
def load_categories
#categories = Category.all
end
end
That will load the categories into the instance variable for both the index and show actions.
For the error you're getting, if I'm reading it right you have nested routes? You need to be getting the correct ID for the tag, which is :tag_id:
def show
#tag = Tag.find(params[:tag_id])
#page = Page.find(params[:id])
#categories = Category.all
end
You were getting :id for both. If that doesn't work, are your routes nested? If not post your routes and I'll update the answer.
You can DRY out your Controller to set page and categories in a callback. But take care: you may omit the categories query in the categories#index action, but your page#... actions may skip the callback at all skip_before_action set_page or better overwrite the method and use a correct handling.
class ApplicationController
before_action set_page, only: [:index, :show]
before_action set_categories, only: [:index, :show]
private
def set_page
#page = Page.find params[:page_id]
end
def set_categories
#categories = Category.all
end
end
I am trying to access an "edit" link to edit an object, but I'm getting this error:
Param is missing or the value is empty: preview
Basically, I have 2 models that I linked through association:
Game model
Review model
I'm rendering reviews in the Game's show page. When I try to edit a review, it's saying I'm missing params or the value is empty in the Reviews controller.
The routes are also nested. How can I fix this?
Thanks in advance :)
routes.rb
Rails.application.routes.draw do
devise_for :users
root "games#index"
resources :games do
resources :news
resources :reviews, except: [:show, :index]
resources :previews, except: [:show, :index]
end
resources :platforms
resources :genres
end
show.html.erb (Linked to Games controller)
<% if #news.last.created_at > preview.updated_at %>
<p><%= link_to "edit", edit_game_preview_path(#game.id, preview.id) %></p>
<% end %>
<p><%= link_to "delete", game_preview_path(#game.id, preview.id), method: :delete %></p>
<% end %>
Reviews partial (Form)
<%= form_for [#game, #previews.new] do |r| %>
<h3 class="post_review">Preview this game</h3>
<p><%= flash[:notice_submit] %></p>
<p><%= r.text_field :title, placeholder: "Enter your tagline" %></p>
<p><%= r.text_area :content, placeholder: "Enter your review here" %></p>
<p><%= r.text_area :vote %></p>
<p><%= r.hidden_field :game_id, value: #game.id %></p>
<%= r.submit %>
<% end %>
Reviews controller
class PreviewsController < ApplicationController
before_action :authenticate_user!
before_action :set_preview, only: [:show, :edit, :update, :destroy]
before_action :set_game
def new
#preview = Preview.new
end
def create
#preview = Preview.new(preview_params)
#preview.user_id = current_user.id
#preview.game_id = #game.id
#preview.username = current_user.username
if #preview.save
redirect_to :back
flash[:notice_submit] = "Thanks for you comment!"
else
redirect_to :back
flash[:notice_submit] = "Either you've already voted, or you're not filling in all forms."
end
end
def edit
#preview.update(preview_params)
redirect_to #game
end
def destroy
#preview.destroy
redirect_to #game
end
private
def set_preview
#preview = Preview.find(params[:id])
end
def set_game
#game = Game.find(params[:game_id])
end
def set_user
#user = User.find(params[:user_id])
end
def preview_params
params.require(:preview).permit(:title, :content, :vote)
end
end
You are getting this error because in your preview_params you are requiring a preview object.
I think your controller logic for the edit action is invalid. For the edit action, you just need to set_preview and then render the edit template. The current logic in your edit action should go in an update action.
def edit
end
def update
#preview.update(preview_params)
redirect_to #game
end
Also the first line of your form should be:
<%= form_for [#game, #preview] do |r| %>
so I'm trying to create a video game review website for practice.
A game has many reviews, and votes. The idea is, in order to post a review, you must vote "Good" or "Bad" first, THEN submit a review. You can't post a text review without voting.
I'm trying to do this without the acts_as_voteable gem...
The data format for votes is boolean. "Good" is true, "Bad" is false.
How do I get the votes to save? below are my routes.rb, _review partial, reviews controller, and show page.
many thanks guys :)
edit****: also I'm trying to only one vote per user. I was thinking of using a token variable which equals to 1, and when a vote is cast, the token is -1. Is that a good approach? But the data type for vote is boolean, so how would that work -- or should I change the data type for vote from boolean to integer?
edit#2 -- so I added :vote into my params.
routes.rb
upvote_game_review_path
POST /games/:game_id/reviews/:id/upvote(.:format) reviews#upvote
downvote_game_review_path
POST /games/:game_id/reviews/:id/downvote(.:format) reviews#downvote
Rails.application.routes.draw do
devise_for :users
root "games#index"
resources :games do
resources :news
resources :reviews, except: [:show, :index] do
member do
post "upvote"
post "downvote"
end
end
end
resources :platforms
resources :genres
end
reviews_controller.rb
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :update, :edit, :destroy]
before_action :set_game
before_action :authenticate_user!
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
#review.game_id = #game.id
if #review.save
redirect_to #game
else
render "review"
end
end
def upvote
#review.vote.create = true
redirect_to #game
end
def downvote
#review.vote.create
#review.vote = false
redirect_to #game
end
def edit
#review.update(review.params)
end
def destroy
#review.destroy
redirect_to #game
end
private
def set_review
#review = Review.find(params[:id])
end
def set_game
#game = Game.find(params[:game_id])
end
def review_params
params.require(:review).permit(:comment, :vote)
end
end
_review partial <-- to create a new review
<%= form_for [#game, #reviews.new] do |r| %>
<h3 class="post_review">Review this game</h3>
<p>
<%= r.text_area :comment %>
</p>
<p>
<%= button_to "Good", upvote_game_review_path(#game.id, r) %>
</p>
<p>
<%= button_to "Bad", downvote_game_review_path(#game.id, r) %>
</p>
<p>
<%= r.hidden_field :game_id, value: #game.id %>
<p>
<%= r.submit %>
<% end %>
show.html.erb
<p><%= link_to "<< Home", games_path %></p>
<span><%= link_to "Edit", edit_game_path(#game) %></span>
<span><%= link_to "Delete", game_path(#game), method: :delete %></span>
<div class="game_summary">
<h2><%= #game.title %></h2>
<%= image_tag #game.image %>
<p>Release Date: <%= #game.release_date %> </p>
<p>Genre: <%= #game.genre_id %> </p>
<p>Platforms: <%= #game.platform_id %></p>
</div>
<%= link_to "Add News", new_game_news_path(#game) %>
<h2>News & Articles</h2>
<%= link_to "view all", game_news_index_path(#game) %>
<% #news.each do |n| %>
<ol>
<li><%= link_to n.title, game_news_path(#game.id, n.id) %></li>
</ol>
<% end %>
<div class="game_review submit">
<%= render "review" %>
</div>
<% #reviews.each do |review| %>
<p><%= review.comment %></p>
<p><%= link_to "delete", game_review_path(#game.id, review.id), method: :delete %></p>
<% end %>
You don't specify which review you're loading in. The reason is here:
before_action :set_review, only: [:show, :update, :edit, :destroy]
You don't pull in the request's review instance when you go to either of those actions. Further, it doesn't look like you're actually saving them.
So, two things I'd recommend:
Add those methods to your before_action:
before_action :set_review, only: [:show, :update, :edit,
:destroy, :upvote, :downvote]
(May not be necessary, write tests to confirm this!) Actually save the entity after you've changed its value.
def upvote
#review.vote.create = true
#review.save
redirect_to #game
end
def downvote
#review.vote.create unless #review.vote
#review.vote = false
#review.save
redirect_to #game
end
so I'm trying to create a videogame review website for practice. each game would have it's own page, and on each page, i'd like to have a form where you can input reviews, and have it show up on the same page when submitted.
I'm not sure how to handle this. but I managed to create two controllers -- one for creating games, and one for creating reviews.
I use devise for user logins/registration
I'll post the codes below: please let me know if you need to see other files.
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :games
resources :reviews
root "games#index"
end
games_controller.rb -- to create new games
class GamesController < ApplicationController
before_action :set_game, only: [:show, :edit, :update, :destroy]
def index
#games = Game.all
end
def new
#game = Game.new
end
def create
#game = Game.create(game_params)
redirect_to #game
end
def edit
end
def update
#game.update(game_params)
redirect_to #game
end
def destroy
end
private
def game_params
params.require(:game).permit(:title, :image, :developer, :genre, :release_date, :platform)
end
def set_game
#game = Game.find(params[:id])
end
end
reviews_controller.rb -- to create reviews
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
def index
end
def show
end
def new
#review = Review.new
end
def create
#review = Review.create(review_params)
redirect_to #review
end
private
def review_params
params.require(:review).permit(:review)
end
def set_review
#review = Review.find(params[:id])
end
end
show.html.erb for games
<% if user_signed_in? %>
<p class=""><%= link_to "Update Details", edit_game_path %></p>
<p class=""><%= link_to "Update News", new_game_path %></p>
<% end %>
<%= flash[:notice] = "Successfully updated." %>
<hr>
<div class="game_summary">
<%= image_tag #game.image %>
<div class="game_details">
<h3 class="game_title"><%= #game.title %></h3>
<ul class="game_info">
<li>Developer: <%= #game.developer %></li>
<li>Release Date: <%= #game.release_date %></li>
<li>Genre: <%= #game.genre %></li>
<li>Platform(s): <%= #game.platform %></li>
</ul>
<button class="buttons" id="buy">Buy</button>
<button class="buttons" id="rent">Rent</button>
</div>
</div>
<div class="game_news">
<h2 class="game_news_title">News & Articles</h2>
</div>
<div class="game_reviews">
<%= link_to "Add Review", new_review_path %>
<h2>Reviews</h2>
<ul>
<li><%= %></li>
<li><%= %></li>
<li><%= %></li>
<li><%= %></li>
</ul>
</div>
In your games_controller.rb, you'd need a show method (which is created by default when you use scaffolding, but which you somehow missed). This show method would gather all the reviews pertaining to the game in question, assuming your Review model has a reference to its corresponding Game model (both as a foreign key specified in schema.rb and via belongs_to and has_many in their respective models).
The show method would look like this:
def show
#reviews = Review.where("game_id = ?", #game.id)
end
Then in your view, you'd have something like this:
<% #reviews.each do | review | %>
<%= review.title %>
<%= review.content %>
<% end %>
I have no idea what fields are contained in your Review object, but you can extrapolate from this example to get what you want.