How do I make my objects less messy? - ruby-on-rails

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

Related

Probleme on nested ressources rails

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>

Rails - Problem with passing local variables

I'm running Rails 6.0.3.2 and I want to render a partial passing a local variable to another controller view:
My routes:
Rails.application.routes.draw do
devise_for :users
root to: 'notebooks#index'
resources :notebooks, only: [:index, :new, :show, :create, :edit, :update, :destroy] do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
collection do
get "list"
end
end
resources :tags
end
Notebook Model:
class Notebook < ApplicationRecord
has_one_attached :photo
validates :asin, presence: true, uniqueness: true
after_initialize :init
acts_as_list column: :position, add_new_at: :bottom
has_many :taggings
has_many :tags, through: :taggings
def init
self.edited ||= false
end
end
Tag Model
class Tag < ApplicationRecord
has_many :taggings
has_many :notebooks, through: :taggings
end
In Tag controller:
def index
#tags = Tag.all.order(created_at: :asc)
end
I tried to follow this guide and render the "index view" from Tags Controller on the "list view". The application finds the tags/_index.html file, but return the error undefined method `each' for nil:NilClass. Bellow my views code:
In app/views/notebooks/list.html.erb:
<%= render :partial => "tags/index" , locals: {tags: #tags}%>
In app/views/tags/_index.html.erb
<% tags.each do |tag| %>
<div>
<div class="d-flex">
<p><%= tag.id %></p>
<p><%= tag.name %></p>
</div>
<p>tag.taggings.count</p>
</div>
<% end %>
Anyone can point me what i'm doing wrong? I read the Layouts and Rendering in Rails documentation, but i don't have a clue why the instructions won't work on my project...
Thanks in advance!
The rails way to do this is just to create a partial for a single record:
# app/views/tags/_tag.html.erb
<div>
<div class="d-flex">
<p><%= tag.id %></p>
<p><%= tag.name %></p>
</div>
<p>tag.taggings.count</p>
</div>
You can then pass the collection to render and Rails will look up the partial and render it for each member in the collection:
<%= render #tags %>
This is short for:
<%= render partial: 'tag', collection: #tags %>
See Rendering Collections.

param is missing or the value is empty:

When going on the view page, the error "param is missing or the value is empty: restaurant" is displayed.
- I solve the problem by deleting "require(:restaurant)" but don't really understand the trick...do you ?
- I am looking for a solution that allows me to keep the "require(:restaurant)... Any Idea ?
restaurants_controller.rb
class RestaurantsController < ApplicationController
before_action :set_restaurant, only: [:show, :edit, :update]
def index
#restaurants = Restaurant.all
end
def show
end
def new
#restaurant = Restaurant.new(params.permit(:name, :address, :category))
end
def create
#restaurant = Restaurant.new(restaurant_params)
#restaurant.save
redirect_to restaurant_path(#restaurant)
end
private
def set_restaurant
#restaurant = Restaurant.find(params[:id])
end
def restaurant_params
params.require(:restaurant).permit(:name, :address, :category)
end
end
new.html.erb
<%= simple_form_for(#restaurant) do |f| %>
<% if #restaurant.errors.any? %>
<div class="errors-container">
<ul>
<% #restaurant.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.input :name %>
<%= f.input :address %>
<%= f.input :category %>
<%= f.submit "add a resto", class: "btn btn-primary" %>
<% end %>
restaurant.rb
class Restaurant < ApplicationRecord
validates :name, :address, :category, presence: true
validates :category, inclusion: { in: ["chinese", "italian", "japanese", "french", "belgian"]}
has_many :reviews, dependent: :destroy
end
routes
Rails.application.routes.draw do
resources :restaurants, only: [:new, :create, :show, :index] do
resources :reviews, only: [ :new, :create ]
end
end
The inputs of your form should be named like this:
restaurant[name]
restaurant[address]
restaurant[category]
It could be that it is something like is:
name
address
category
You can debug your params by inspecting it.
You could do something like this in your restaurant_params in the controller:
raise params.inspect
Or open your debug console of the browser and see what is posted.
And as mentioned in the comments you can use the restaurant_params in your new method but I don't think that fixes you issue it is more likely that the form is the problem.
# instead of
Restaurant.new(params.permit(:name, :address, :category))
# do
Restaurant.new(restaurant_params)
I assume you're trying to auto-populate the form on your new view with some passed attributes.
Your restaurant#new is looking for a params[:restaurant] hash to create a new Restaurant object and can't find it. Look at how you're calling the new action and what parameters are being passed -- my guess is that you're not passing any or you're passing them in the root params hash like params[:name], params[:address] instead of params[:restaurant] => { :name => 'name'}.
Most new actions are a GET so if you're using inline URL parameters put them in like http://address.com/restaurant/new?restaurant[name]=Bistro&restaurant[category]=burgers
In restaurants_controller.rb, we can declare a method for show like:
def show
#restaurant = Restaurant.find(params[:id])
end
In the View Section where we want to link with a button, we can paste
<td><%= link_to "Show", restaurant %> </td>
I hope it will work

How to load all associations across all models

I'm making a simple website to learn RoR. It allows users to post reviews for movies, but I want to implement a link in my root view that shows ALL of the reviews in the database. How do I do that?
I want to be able to <%= link_to 'Reviews', reviews_path %> but my reviews#index URI pattern is /movies/:movie_id/reviews
What do I put in my reviews model in order to extract all the reviews in the database?
My reviews controller:
class ReviewsController < ApplicationController
def create
#movie = Movie.find(params[:movie_id])
#review = #movie.reviews.create(review_params)
redirect_to movies_path(#movie)
end
private
def review_params
params.require(:review).permit(:email, :comment)
end
end
Review model:
class Review < ApplicationRecord
belongs_to :movie
end
And my root view:
<h3>All Reviews:</h3>
<!-- put link here -->
<h3>Sort By:</h3>
<p>
<%= link_to 'Release', movies_path(:sort_param => "release") %>
<%= link_to 'Title', movies_path(:sort_param => "title") %>
</p>
<h1>Popular Movies</h1>
<% #movies.each do |m| %>
<h2>Title</h2>
<%= m.title %>
<%= m.release %>
<%= link_to 'Show', movie_path(m) %>
<% end %>
Edit: I looked at this solution but I'm not sure where to put that code. I tried to put it in the Review's index method but I got an error:
Couldn't find all Reviews with 'id': (all, {:order=>"created_at DESC", :limit=>10})
In your routes.rb
resources :reviews, only: [:index]
reviews_controller.rb
If you want to display all the reviews from any movie, in most recent order, you could do:
class ReviewsController < ApplicationController
def index
#reviews = Review.order('created_at DESC').limit(10)
end
def create
...
end
end
You can add a route to reviews only in your routes.rb file.
resources :reviews, only: [:index]

Displaying random products in ruby on rails app

In a app I'm building, a customer selects a product and in the products/show.html.erb he is supposed to be able to see related products in a below div.
category.rb and product.rb are related:
cateory.rb
has_many :products, :dependent => :nullify
product.rb
belongs_to :category
belongs_to :label
So Im thinking the best way to do that is to display random products from the same category as the selected product belongs_to?
At the moment this what I have in the def show line in the products_controller.rb
#products_rand = Product.offset(offset).limit(6)
I need it to grab only products in the same category as the selected product?
products_controller.rb
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
def show
offset = rand(100)
#meta_title = "Mypage #{#product.title}"
#meta_description = #product.description
#products_rand = Product.offset(offset).limit(6)
end
private
def set_product
#product = Product.find(params[:id])
end
def product_params
params.require(:product).permit(:title, :description, :image, :category_id, :stock_quantity, :label_id, :query, :slug)
end
end
And the random products are displayed in this the views/products/show.html.erb
<div class="row product-teaser">
<h4 class="text-center teaser-text"> Products from same category </h4>
<% #products_rand.each do |product| %>
<div class="col-sm-2 col-xs-3 center-block product-thumbs-product-view" >
<%= link_to product_path (product) do %>
<%= image_tag product.image.url, :size => "100%x100%", class: "img-responsive center-block" %>
<% end %>
<h5 class="text-center"><%= link_to product.title, product, class: "text-center" %></h5>
</div>
<% end %>
</div>
Here is a link to previous question about this Not able to display random images below product image in Ruby on rails app?
You can use the following (for Postgres/SQLite):
#products_rand = Product.where(category_id: #product.category_id).order("RANDOM()").limit(6)
Or the following for MySQL:
#products_rand = Product.where(category_id: #product.category_id).order("RAND()").limit(6)

Resources