Displaying random products in ruby on rails app - ruby-on-rails

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)

Related

Rails 5 - undefined local variable or method `post'

I'm new to Rails. I've created a category model for my posts but when I go to display all posts associated with a particular category I get a NameError page
Here's my categories show.html.erb file:
<h1> <%= "Category: " + #category.name %></h1>
<div align="center">
<%= will_paginate #category_posts %>
</div>
<%= render 'posts/post', obj: #category_posts %>
<div align="center">
<% will_paginate #category_posts %>
</div>
I'm rendering the _post.html.erb partial to display posts that was defined in my post folder.
Looks like the issue is linked to the first line in the code below because the error message points to <li id="post-<%= post.id %>"> in the _post.html.erb code:
<li id="post-<%= post.id %>">
<span class="title"> <%=link_to post.title, post_path(post) %> </span>
<span class="content"><%= truncate(post.content, length: 100) if post.content? %></span>
<span class="content"> <%= image_tag post.picture.url if post.picture? %> </span>
<span class="content">
<% if post.category.any? %>
<p><%= render post.category %></p>
<% end %>
</span>
</li>
And this is my category controller file where I definite the "show" method:
class CategorysController < ApplicationController
before_action :require_admin, except: [:index, :show]
def index
#categories = Category.paginate(page: params[:page])
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
if #category.save
flash[:success] = "Category created successfully"
redirect_to categories_path
else
render 'new'
end
end
def show
#category = Category.find(params[:id])
#category_posts = #category.posts.paginate(page: params[:page], per_page: 5)
end
The Post model:
class Post < ApplicationRecord
belongs_to :user
has_many :post_categories
has_many :categories, through: :post_category
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :title, presence: true
validate :picture_size
private
# validates the size of an upload picture
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB")
end
end
end
The general idea is that when I go to localhost/categories/1 for example, I should have all the posts associated with that category.
Can anyone help me resolve this issue?
You probably mean to render a partial using a collection:
render(partial: 'posts/post', collection: #category_posts)
Where that should expand that partial to repeat once for each post and assign the local post variable.
obj isn't a valid argument, but object is if you want to render the content once with a given object.

How can I put image attached to comments to index.html.erb in songs?

There is a comment in song, it has a one-to-many relationship, and each comment has an image,
I would like to put each image which have the best number of good in songs list.
<% #songs.each do |song| %>
<div class="col-md-4 col-sm-6 portfolio-item">
<a class="portfolio-link" data-toggle="modal" href="#portfolioModal1">
<div class="portfolio-hover">
<div class="portfolio-hover-content">
<i class="fa fa-plus fa-3x"></i>
</div>
</div>
<%= image_tag song.comments.image.url(:medium).order(:cached_votes_up => :desc).limit(1) %>
<img class="img-fluid" src="/assets/noimage.jpg" alt="">
</a>
<div class="portfolio-caption">
But I get an error:
I do not know why image becomes a NoMethodError.
Also, the association between songs and comment should have been made.
class Song < ActiveRecord::Base
acts_as_votable
belongs_to :user
has_many :comments, :dependent => :destroy
end
class Comment < ActiveRecord::Base
acts_as_votable
belongs_to :user
belongs_to :song
has_attached_file :image, styles: { :medium => "400x400#", :thumb =>
"180x180#" }, :default_url => "noimage.jpg"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
How can I solve this?
NoMethodError in Songs#index
undefined method `each' for nil:NilClass
<% #songs.each do |song| %>
<div class="col-md-4 col-sm-6 portfolio-item">
<%= link_to like_song_path(song), method: :put, class: "portfolio-link" do %>
<div class="portfolio-hover">
<div class="portfolio-hover-content">
<i class="fa fa-plus fa-3x">
</i>
</div>
</div>
<% #comments.each do |comment| %>
<%= image_tag song.comments.order(:cached_votes_up => :desc).limit(1).first.image.url(:medium) %>
<% end %>
<% end %>
song.controoler↓
before_action :set_song, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /songs
# GET /songs.json
def index
if params[:user_id]
#user = User.find(params[:user_id])
#songs = #user.songs.order(:cached_votes_up => :desc)
else
#songs = Song.all.order(:cached_votes_up => :desc)
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_song
#song = Song.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def song_params
params.require(:song).permit(:title, :body, :get_upvotes)
end
I think the problem is #comments wasn't defined in index action.
I know I didn't define #comment in index action , but I'm not sure how to code.
The exception you're seeing is due to #image being called on the collection of comments instead of an individual comment object. Even though #limit(1) is being applied to the query, the returned result is still a collection. To fix the error, you can modify your existing query to the following:
<%= image_tag song.comments.order(:cached_votes_up => :desc).limit(1).first.image.url(:medium) %>
Hope this helps!
You can do this
<%= image_tag song.comments.order(:cached_votes_up => :desc).limit(1).first.image.url(:medium) if song.comments.order(:cached_votes_up => :desc).limit(1).present? %>
I hope this help you.

Ruby on Rails: how to make a form for associated models (nested)

First of all I have this:
https://polar-scrubland-30279.herokuapp.com/ - my project which is deployed on heroku (Captain Obvious)
I've got projects and todos inside them.
For this moment I show all projects using this way:
------index.html.erb------
<%= render #projects %>
------_project.html.erb-----
<div class="project">
<div class="project-header">
<h2><%= project.title %></h2>
</div>
<div class="project-todos">
<% project.todos.all.each do |todo| %>
<p><%= check_box('tag', todo.__id__, {class: 'icheckbox_square-blue', checked: todo.isCompleted}) %> <%= content_tag :todotext, todo.text %></p>
<% end %>
</div>
</div>
And as you understand it doesn't allow me to change my todo's status when checkbox is checked. So that's why I need a form that will allow me to track all the checkboxes. Also I wanna make text-decoration: line-through when checkbox is pressed, but don't get how to.
Is there a way to creat a form which will satisfy my needs? Please can you help me, Any information will be appreciated.
ADDITIONAL INFORAMTION:
GitHub - https://github.com/NanoBreaker/taskmanager
project.rb
class Project < ActiveRecord::Base
has_many :todos
end
todo.rb
class Todo < ActiveRecord::Base
belongs_to :project
end
Lets start with the models:
class Project < ApplicationRecord
has_many :todos
accepts_nested_attributes_for :todos
end
class Todo < ApplicationRecord
belongs_to :project
end
accepts_nested_attributes_for lets you create or modify several nested Todo records at once when creating or updating a Project.
# will update 2 todos at once
#project.update(
todos_attributes: [ { id: 1, isComplete: true }, { id: 2, isComplete: false }]
)
We can use fields_for to create nested inputs for todos:
<%= f.form_for(#project) do |f| %>
<%= f.fields_for(:todos) do |tf| %>
<%= tf.check_box :isCompleted %>
<% end %>
<% end %>
This generates fields for todos nested under the key todos_attributes. We can whitelist them by using a hash key containing a array of permitted attributes.
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
def new
#project = Project.new
# this seeds the project with 3 empty tasks
# otherwise we don't have any inputs.
3.times { #project.todos.new }
end
def create
#project = Project.new(project_params)
if #project.save
# ...
else
# ...
end
end
def update
if #project.update(project_params)
# ...
else
# ...
end
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project)
.permit(:foo, :bar,
todos_attributes: [:isCompleted, :text]
)
end
end
You can create a form for each project by creating a partial which uses a local instead of an instance variable:
# app/views/projects/_form.html.erb
<%= f.form_for(local_assigns[:project] || #project) do |f| %>
<%= f.fields_for(:todos) do |tf| %>
<%= tf.check_box :isCompleted %>
<% end %>
<% end %>
# app/views/projects/index.html.erb
<% #projects.each do |project| %>
<%= render partial: 'projects/form', project: project %>
<% end %>
You can reuse the same partial for the other views as well:
# app/views/projects/new.html.erb
<%= render partial: 'projects/form' %>
# app/views/projects/edit.html.erb
<%= render partial: 'projects/form' %>

How do I make my objects less messy?

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

Rails ajax form for products

I've been working with app for pizzeria where customers could order pizzas through their website. I currently working with the product page where I try to submit products to shopping cart through ajax, but I'm really stuck. I haven't been able to build a shoppingcart which would accept product-id, product-size-id, extra-toppings as an array and quantity. I decided to try to go with session-store where all the order-row ids are stored and on menu page every product has a form where user could add product, size and quantity to shoppingcart but I keep getting this error in server logs:
Started POST "/order_row" for ::1 at 2015-08-03 11:18:21 +0300
Processing by OrderRowsController#create as JS
Parameters: {"utf8"=>"✓", "order_row"=>{"product"=>"1", "size"=>"0", "quantity"=>"2"}, "commit"=>"Tilaa"}
Completed 500 Internal Server Error in 2ms (ActiveRecord: 0.0ms)
ActiveRecord::AssociationTypeMismatch (Product(#70158072501800) expected, got String(#70158039566200)):
app/controllers/order_rows_controller.rb:4:in `create'
I have models Product, ProductCategory, Order, OrderRow and my session stores order-row-ids as mentioned. My menu page is actually product_categories#show -view where products belonging to that category are listed.
#order_rows_controller.rb
class OrderRowsController < ApplicationController
respond_to :html, :js
def create
#orow = OrderRow.new(order_rows_params)
if #orow.save
session[:order_row_ids] << #orow.id
flash[:notice] = "Lisättiin ostoskoriin!"
else
flash[:error] = "Tuotteen lisääminen ostoskoriin epäonnistui."
redirect :back
end
end
def update
#orow = OrderRow.find(params[:id])
if #orow.update_attributes(params[:order_row])
flash[:notice] = "Ostoskori päivitetty."
else
flash[:error] = "Ostoskorin päivitys epäonnistui."
end
end
def destroy
#orow.find(params[:id]).destroy
flash[:notice] = "Tuote poistettu onnistuneesti"
end
private
def order_rows_params
params.require(:order_row).permit(:product, :size, :quantity) #, :extras => []
end
end
ProductCategories-controller
class ProductCategoriesController < ApplicationController
before_action :set_product_category, only: [:edit, :update, :destroy]
respond_to :html, :js
def index
#product_categories = ProductCategory.all
end
def show
#product_category = ProductCategory.friendly.find(params[:id])
#product_categories = ProductCategory.all
#products = #product_category.products
#order_row = OrderRow.new(order: nil, product: nil, size: nil, extras: nil, quantity: nil)
end
And menu-page in product_categories/show.html.erb
#product_categories#show -view
<!--- category descriptions -->
<div class="container">
<% #products.each do |product| %>
<div class="col-sm-6 col-md-4">
<div class="product well">
<h3><%= product.name %></h3>
<span><%= product.description %></span>
<p class="prices">
<%= price(product.normal_price) %> | <%= price(product.plus_size_price) %> | <%= price(product.lunch_price) %>
</p>
<br>
<div id="form-<%= product.id %>">
<%= simple_form_for #order_row, :url => url_for(:controller => 'order_rows', :action => 'create'), remote: true do |f| %>
<%= f.hidden_field :product, :value => product.id %>
<h5>Koko</h5>
<div style="padding-left: 13px">
<%= f.input :size, collection: OrderRow.sizes, as: :radio_buttons, label: false, item_label_class: "radio-inline", item_wrapper_tag: false %>
</div>
<h5>Määrä</h5>
<div style="width: 8%; padding-left: 13px;">
<%= f.input :quantity, as: :string, label: false %>
</div>
<p>
<%= f.submit "Tilaa", class: "btn btn-success btn-lg" %>
</p>
<% end %>
</div>
</div>
</div>
<% end %>
</div>
Create.js.erb in order_rows#create action
#create.js.erb
$("#form-<%= params[:product] %>").load(document.URL + "#form-<%= params[:product]");
Associations:
#order_row
belongs_to :order
belongs_to :product
#product
belongs_to :product_category
has_one :campaign_producte
belongs_to :dish_type
#product_categories
has_many :products
has_many :campaign_products
has_many :product_extras
has_many :dish_types, through: :products
#product_extra
belongs_to :product_category
Link to github-repo: https://github.com/casualCodeAndDesign/ravintolamammamia
What's the reason for this server error and why it doesn't store my order_row to the database?
ActiveRecord::AssociationTypeMismatch (Product(#70158072501800)
expected, got String(#70158039566200))
You need to change
<%= f.hidden_field :product, :value => product.id %>
to
<%= f.hidden_field :product_id, :value => product.id %>
and product to product_id in create.js.erb and order_rows_params

Resources