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.
Related
I am trying to figure out how to make my categories linkable to their entries.
I have only one model, Entry which has scrapped data from Reddit (title, link, and category)
Now have the data displayed on the index.html.erb page alongside with the category in a sidebar ... but it's not linked.
How do I make it linkable to the entries it came from?
The most I've come up with which is in the index.html.erb as well but it trails off because I'm not sure how to call the specific entry that I'm clicking on the column for ... ie, if I click on jokes, I want to get all entries that have that specific category show up ...
Here's the incomplete link_to <p><%= link_to '#lala.join(" ")', :action %></p>
Do I put in the parameter of [:category] in the path above?
Entries Controller
class EntriesController < ApplicationController
def index
#entries = Entry.all
#lala = #entries.select(:category).map(&:category).uniq
##entries = Entry.all.group_by{ |entry| entry.category }
end
def scrape
RedditScrapper.scrape
respond_to do |format|
format.html { redirect_to entries_url, notice: 'Entries were successfully scraped.' }
format.json { entriesArray.to_json }
end
end
end
index.html.erb
<div class="container-fluid">
<div class="row">
<div class="col-md-8">
<div class="card-columns">
<% #entries.reverse.each do |entry| %>
<div class="card">
<div class="card-block">
<p class="card-title"><b><%= entry.title %></b></p>
<p class="card-text"><%= entry.link %></p>
<p class="card-type"><%= cat(entry.category) %></p>
</div>
</div>
<% end %>
</div>
</div>
<div class="col-md-4">
<p><%= link_to '#lala.join(" ")', :action %></p>
</div>
</div>
scraper
require 'open-uri'
module RedditScrapper
def self.scrape
doc = Nokogiri::HTML(open("https://www.reddit.com/"))
entries = doc.css('.entry')
entries.each do |entry|
title = entry.css('p.title > a').text
link = entry.css('p.title > a')[0]['href']
category = entry.css('p.tagline > a.subreddit')[0]['href']
Entry.create!(title: title, link: link, category: category )
end
end
end
Entry model
class Entry < ApplicationRecord
validates :title, presence: true
validates :link, presence: true
validates :category, presence: true
end
routes
Rails.application.routes.draw do
#root 'entry#scrape_reddit'
root 'entries#index'
resources :entries
#get '/new_entries', to: 'entries#scrape', as: 'scrape'
end
Can't seem to find an answer for my specific problem.
I have a rails 4 application which displays both a blog (model is post) and a portfolio (model is pletool). My post model is working fine, as is my pletool model except that I am unable to replicate my index controller from posts to pletools.
I'm using act as taggable to tag and have two separate tag types - "tags" for the post and "pletags" for the pletools.
For both index views I want to be able to display a tag cloud of the most popular tags, which if the user then clicks on will filter to only posts or pletools of that particular tag.
I get an 'undefined method nil class' error when I try to alter the index definition in my pletools_controller.rb from "tags" to "pletags".
pletools_controller.rb
class PletoolsController < ApplicationController
layout 'application'
before_action :find_pletool, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :tag_cloud
def index
if params[:pletag].present?
#pletool = Pletool.tagged_with(params[:pletag]).paginate(:page => params[:page], :per_page => 20)
else
#pletools = Pletool.all.order('created_at DESC').paginate(page: params[:page], per_page: 20)
end
end
def tag_cloud
#pletags = Pletool.tag_counts_on(:pletags, :limit => 10, :order => "count desc")
end
def new
#pletool = Pletool.new
end
def show
end
def edit
end
def create
#pletool = Pletool.new(pletool_params)
if #pletool.save
redirect_to #pletool, notice: "Tool succesfully saved!"
else
render 'new', notice: "Try Again. I was unable to save your PLE Tool."
end
end
def update
if #pletool.update(params[:pletool].permit(:title, :description, :link, :image, :pletag_list))
redirect_to #pletool, notice: "PLE Tool succesfully edited!"
else
render 'edit'
end
end
def destroy
#pletool.destroy
respond_to do |format|
format.html { redirect_to pletools_url, notice: 'Ple Tool was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def pletool_params
params.require(:pletool).permit(:title, :link, :description,:image, :image_url, :slug, :pletag, :pletag_list)
end
def find_pletool
#pletool = Pletool.friendly.find(params[:id])
end
end
index.html.erb
<% #pletools.each do |pletool| %>
<li class="post msnry-blog-entry #">
<% if pletool.image.exists? %>
<div class="click-slide click-top">
<%= link_to image_tag pletool.image.url(:large) %>
<div class = "text-center">
<%= link_to '<span class = "ti ti-eye"></span>'.html_safe, pletool %>
</div>
</div>
<% end %>
<div class="msnry-blog-entry-text">
<h4 class"h5"><%= link_to pletool.title, pletool %></h4>
<p><%= markdown truncate(pletool.description, :length => 150) %></p>
<ul class="msnry-blog-entry-meta list-inline">
<li>
<span class="ti ti-pencil-alt">
</span>
by Anthony Llewellyn
</li>
<li>
<span class="ti ti-calendar">
</span>
<%= pletool.created_at.strftime('%A, %B %d') %>
</li>
<li class="entry-tags hidden-xs">
<% pletool.pletags.any? %>
<% pletool.pletags.each do |tag| %>
<li><a href="#">
<%= link_to tag.name, pletag_path(tag.name) %>
</a></li>
<% end %>
</li>
</ul>
</div>
</li>
<% end %>
</li>
</ul>
ok after much searching (with no real answers that I could find online) and playing around with various parameters, it turns out i had some very minor issues in a couple of my scripts.
firstly for pletools_controller.rb
i needed to change #pletool to plural in a couple of lines e.g.
#pletools = Pletool.tagged_with(params[:pletag]).paginate(:page => params[:page], :per_page => 20)
also in my routes.rg i have configured two separate index routes and again i needed to make a minor change from get 'pletag:tag' to get 'pletag:pletag'
here is the relevant section of my final routes.rb
get 'pletags/:pletag', to: 'pletools#index', as: :pletag, layout: 'application'
get 'tags/:tag', to: 'posts#index', as: :tag, layout: 'application'
Hope that this helps someone else out one day / some day!
I have a situation where I have #challenge and #idea instances from Challenge and Idea model responsively.
I have one partial which is going to used in many controllers, so based on controllers I want to generate path.
e.g
So when I am in IdeasController polymorphic_path([#challenge, #idea]) will generate "/challenges/12/ideas/45"
but if I am in any other controller suppose RefinementsController, I want generate path as "challenges/12/refinements/45"
How can I generate it using polymorphic_path or url_for
code:
app/controllers/ideas_controller.rb
class IdeasController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge
load_and_authorize_resource :idea, through: :challenge
def index
end
def show
end
end
app/controllers/refinements_controller.rb
class RefinementsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge, parent: true
load_and_authorize_resource :idea, through: :challenge, parent: false
def index
#ideas = #ideas.refined
render 'ideas/index'
end
def show
render 'ideas/show'
end
end
app/views/ideas/index.html.erb
<div class="ideas">
<% #ideas.each do |idea| %>
<div class="idea">
<div class="title">
<%= link_to idea.title, polymorphic_path([#challenge, idea]) %>
</div>
</div>
<% end %>
</div>
app/views/ideas/show.html.erb
<div class="idea">
<div class="title">
<%= link_to idea.title, polymorphic_path([#challenge, #idea]) %>
</div>
<div class="descriptio">
<%= #idea.description %>
</div>
</div>
Partial
Without having your partial code here, I'm going to make an assumption that you are calling polymorphic_path inside your partial, hence wanting to change the path as per the controller it's loaded from
If you're calling a partial, you have to remember its been designed to be an independent, modular element of your application, and so including #instance variables in your partial will likely be an antipattern
What I would recommend is to populate your partial with local variables, allowing you to populate those accordingly:
#app/controllers/ideas_controller.rb
Class IdeasController < ApplicationController
def show
#challenge = Challenge.find params[:challenge_id] if params[:challenge_id].present?
#idea = Idea.find params[:id]
end
end
#app/views/ideas/show.html.erb
<%= render "shared/partial", locals: {challenge: #challenge, child: #idea} %>
#app/views/shared/_partial.html.erb
<%= link_to "Test", polymorphic_path([challenge, child]) %>
--
Update
Thanks for your code
You'll be totally better be using the following:
#app/views/ideas/index.html.erb
<%= render "ideas/item", collection: #items, as: :item, locals: {challenge: #challenge} %>
#app/views/ideas/show.html.erb
<%= render "ideas/item", object: #item, as: :item, locals: {challenge: #challenge} %>
This will allow you to call:
#app/views/ideas/_idea.html.erb
<div class="idea">
<div class="title">
<%= link_to idea.title, polymorphic_path([challenge, idea]) %>
</div>
<% if action_name =="show" %>
<div class="descriptio">
<%= idea.description %>
</div>
<% end %>
</div>
Although it's not a good practice but if it's only a minor change and rest of your partial is same in all cases then what you do is use some generic variable in your partial. In your case your polymorphic url depends on the initialized value of second variable i.e #idea(as first part challenges/12/ is same) so you can do something like this:
polymorphic_path([#challenge,#something_generic])
and now depending on your controllers action you can change #something_generic to make proper url for your form
class IdeasController < ApplicationController
def new
#challenge = Challenge.find(params[:challenge_id])
#something_generic = #challenge.ideas.build
end
end
class RefinementsController < ApplicationController
def new
#challenge = Challenge.find(params[:challenge_id])
#something_generic = #challenge.refinements.build
end
end
Update:
Change your code to this:
class IdeasController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge
load_and_authorize_resource :idea, through: :challenge
def index
end
def show
#something_generic = Idea.find(params[:id])
end
end
class RefinementsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge, parent: true
load_and_authorize_resource :idea, through: :challenge, parent: false
def index
#something_generic = Refinement.find(params[:id])
render 'ideas/index'
end
def show
#something_generic = Refinement.find(params[:id])
render 'ideas/show'
end
end
Since you are using cancancan and have proper filters so #idea and #challenge will be defined automatically in your actions.
For index.html i think you'll be better of using separate templates but if you still want to use a single template then you can do something like this:
app/views/ideas/index.html.erb
<div class="ideas">
<% #ideas.each do |idea| %>
<div class="idea">
<div class="title">
<% if controller_name == "ideas" %>
<%= link_to idea.title, polymorphic_path([#challenge, idea]) %>
<% else %>
<%= link_to idea.title, polymorphic_path([#challenge, #something_generic]) %>
<% end %>
</div>
</div>
<% end %>
</div>
and show.html.erb
<div class="idea">
<div class="title">
<%= link_to #idea.title, polymorphic_path([#challenge, #something_generic]) %>
</div>
<div class="description">
<%= #idea.description %>
</div>
</div>
I've run into an issue with my Ruby on Rails app. I've a page called /discussion which contains discussions that users can comment on with microposts. The goal here is to have a form below each micropost that, when submitted, will put the text in the discussion. The form shows up - but whenever I click submit I get the error:
NoMethodError in MicropostsController#create
undefined method `micropost' for #<User:0x007f9744091680>
Rails.root: /home/nick/Documents/RailsProjects/buon
Application Trace | Framework Trace | Full Trace
app/controllers/microposts_controller.rb:8:in `create'
the micropost controller
class MicropostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
def index
end
def create
#micropost = current_user.micropost.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
end
the _discusions.html
<% content_for :script do %>
<%= javascript_include_tag 'hover_content' %>
<% end %>
<li>
<div class = "intro-bar"><span class = "intro"><%=discussion.intro %></span></div>
<div class = "content-bar"><span class = "content"><%= discussion.content %></span></div>
<span class = "timestamp">
Posted <%= time_ago_in_words(discussion.created_at) %> ago.
</span>
</li>
<% if signed_in? %>
<div class = "row">
<aside class = "span4">
<section>
<%= render 'shared/micropost_form', :locals => {:discussion => discussion }%>
</section>
</aside>
</div>
<% end %>
the micropost model
class Micropost < ActiveRecord::Base
attr_accessible :break_votes, :content, :not_votes
belongs_to :user
belongs_to :discussion
validates :content, presence: true, length: { maximum: 200 }
validates :user_id, presence: true
default_scope order: 'microposts.created_at DESC'
end
any ideas?
If this is a has_many association you need to pluralize, so your code should read:
#micropost = current_user.microposts.build(params[:micropost])
I have a table of venues and offers. Each venue can have have many offers.
I would like to be able to add the offers to the venues from the venues edit page. So far I have this (code below) but its giving a "NoMethodError in Venues#edit, undefined method `model_name' for NilClass:Class" error.
venues edit page
(the div id="tabs-3" is a container in an accordion)
<div id="tabs-3">
<%= form_for [#venue, #offer] do |f| %>
<h2 class="venue_show_orange">Offers</h2>
<%= f.fields_for :offers do |offer| %>
<div class="add_offer">
<%= offer.text_field :title %><br>
</div>
<div class="button"><%= submit_tag %></div>
<% end %>
<% end %>
</div>
offers controller
class OffersController < ApplicationController
def new
#offer = Offer.new
end
def create
#offer = #venue.offers.create!(params[:offer])
#offer.venue = #venue
if #offer.save
flash[:notice] = 'Offer added'
redirect_to offers_path
else
render :action => :new
end
end
def edit
#offer = Offer.find(params[:id])
end
def update
#offer = Offer.find(params[:id])
#offer.attributes = params[:offer]
if #offer.save!
flash[:notice] = 'Offer updated successfully'
redirect_to offers_path(#offer)
end
end
end
venues controller
(nothing offer related in here - is this where I'm going wrong?)
class VenuesController < ApplicationController
protect_from_forgery :only => [:update, :delete, :create]
load_and_authorize_resource
def new
#venue = Venue.new
5.times { #venue.venuephotos.build }
end
def create
#venue = Venue.new params[:venue]
if #venue.save
flash[:notice] = 'Venue added'
redirect_to venues_path
else
render :action => :new
end
end
def edit
#venue = Venue.find(params[:id])
5.times { #venue.venuephotos.build }
end
def update
#venue = Venue.find(params[:id])
#venue.attributes = params[:venue]
if #venue.save!
flash[:notice] = 'Venue updated successfully'
redirect_to :back
end
end
end
Any help is much appreciated thanks very much!
edit
venues edit page
<div id="tabs-3">
<%= form_for #venue do |f| %>
<div class="edit_venue_details">
<h2 class="venue_show_orange">Offers</h2>
<%= render :partial => 'offers/offer', :collection => #venue.offers %>
<div class="clearall"></div>
<h2 class="edit_venue_sub_header">Add a new offer</h2>
<%= f.fields_for :offers do |offer| %>
<% if offer.object.new_record? %>
<p class="edit_venue">title: <br>
<%= offer.text_field :title, :class => "edit_venue_input" %></p>
<% end %>
<% end %>
</div>
<button class="submit_button" type="submit"> Save changes</button>
<% end %>
</div>
whats being displayed
however if I add a new offer, that will display correctly:
Some remarks:
1) Replace:
<%= form_for [#venue, #offer] do |f| %>
with:
<%= form_for #venue do |f| %>
Because offers data will be updated through the related venue, only one controller action will handle the form.
2) If you want to add some unexisting offers in this form, you shoud instantiate them the way you did with venuephotos
3) Show your Venue model. You should have at least:
accepts_nested_attributes_for :offers
Fixed with:
<%= f.fields_for :offers, #venue.offers.build do |offer| %>