I have a question about process if i could be doing this a better way. I'm working on a rails app and i run into page errors when certian elements are not present. For example here is my show action
def show
#article = Article.friendly.find(params[:article_slug])
#section_slug = Section.find_by_id(#article.section_id).slug if #article.section_id.present?
#issue_slug = Issue.find_by_id(#article.issue_id).slug if #article.issue_id.present?
#next_article = #article.next_article if #article.next_article.present?
#prev_article = #article.prev_article if #article.prev_article.present?
#article_author = Author.find_by_id(#article.author_id)
render :layout => 'magazine'
session[:return_to] = request.referer
#if request.path != article_path(#article)
#return redirect_to #article, :status => :moved_permanently
#end
end
Should i be using .present? as much as i am? is there something better that will make pages not fail so completely if an element is not present? Just trying to learn rails in a way that will lead to better code.
First, why are you not using relations? If Article already has section_id then you should be getting to the Section using #article.section. belongs_to :section should be in your Article model.
The above is true for Issue and Author as well.
In answer to your question: No, you should not be using .present? as much as you are.
Here how I would code this action:
def show
#article = Article.friendly.find(params[:article_slug])
#section_slug = #article.section.try(:slug)
#issue_slug = #article.issue.try(:slug)
#next_article = #article.next_article
#prev_article = #article.prev_article
#author = #article.author
render :layout => 'magazine'
session[:return_to] = request.referer
end
There is no reason for #prev_article = #article.prev_article if #article.prev_article.present?. If your views depend on #prev_article they will see a nil value with or without the if #article.prev_article.present?. Your view is going to have to do .present? or similar anyways.
I might actually do away with all but #article here and have my views ask the article for its author, next_article, etc.
I think so you need to do only:
#next_article = #article.next_article
#prev_article = #article.prev_article
If next_article is not present then it will be nil so with present condition and without present condition the value of #next_article and #prev_article will be nil
welcome to RoR
if #article.prev_article.present?
is the same as
if #article.prev_article
and
#article_author = Author.find_by_id(#article.author_id)
looks like you are missing out on a belongs_to relationship. If in your model you define as follows:
class Author < ActiveRecord::Base
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :author
end
Then you can simply call
#article.author
to retrive the author object. Please do the same for section and issue.
When you want to query a database using ids, ActiveRecord allows you to do this:
#user = User.find(1)
It looks like you are trying to paginate articles. Give the Kaminari Gem (https://github.com/amatsuda/kaminari) and Will_Paginate Gem(https://github.com/mislav/will_paginate) a go, they come quite well built and maintained.
#issue_slug = Issue.find_by_id(#article.issue_id).slug if #article.issue_id.present?
In order to not error out when calling a method, the try method is apt.
#article.section.try(:slug)
Related
The error says
SystemStackError in FacilitatorController#index
but its not a problem with the controller because nothing happens if I change lines in there. It does get to the controller. I think it is probably a routing issue, but I'm not sure what is causing it.
Link
<%= link_to "Add Facilitators", facilitator_index_path(:course => #course.id), >:method => :get %>
Relevant routes
resources :facilitator
delete '/facilitator', to: 'facilitator#delete', as: 'facilitator_delete'
Some of Controller
class FacilitatorController < ApplicationController
def index
#course = Course.find(params[:course])
if params[:search_field]
#user = User.select{|user| user.email.downcase.include? params[:search_field].downcase}
else
#user = User.where('id not in (?)', #course.facilitators)
end
end
end
I think it might have something to do with the Courses model having facilitators through an alias, and that conflicting with the facilitator controller?
class Course < ActiveRecord::Base
...
has_many :facilitate_ownedcourses, foreign_key: :ownedcourse_id
has_many :facilitators, through: :facilitate_ownedcourses, source: :facilitator
Can anyone help ?
First, You have a typo in your link_to, should be :method and not >:method
Also in your controller
#user = User.select{|user| user.email.downcase.include? params[:search_field].downcase}
should be something like
#user = User.all.select{|user| user.email.downcase.include? params[:search_field].downcase}
or still better
#user = User.where('email like ?', "%#{params[:search_field].downcase}%")
To handle the case you can change like for ilike if you are using postgres, or you can use lower(email) if you are using mysql
First, you have a syntax error into "Add Facilitators" link, assuming it's typing mistake.
Secondly, back track would helpful to provide suggestion but couple of suggestion below:
Check any finder call back
Iteratively calling a method, may be during before_action from controller.
Ok, relying on guide's blog tutorial:http://edgeguides.rubyonrails.org/getting_started.html
Trying to learn how to write a method myself.
The guide did both article and comments ie, belongs_to & has_many relationship.
So, thought why not try to find out the totality of comments.
This is the method I wrote for Comments controller:
def total_number_of_comments
#article = Article.all
#comments_total = #article.comments.count
end
Then I put this in article's view index.html.erb
<p>Total number of comments:</p>
<%= #comments_total %>
On the index page, it doesn't show anything.
So, what am I doing wrong?
And, I don't want just a "correct" answer. I'd like to understand what I'm missing here.
But what I'm befuddled here is how to think this out.
I hesitate to do this because it would prolong the post, but I thought why not try to do count of articles too.
So, here's what I did:
In Article model
def self.total_number_of_articles
Article.all.count
end
In Article controller
def total_number_of_articles
#articles_total = Article.total_number_of_articles
end
Then in index.html.erb of Article View again, I put this:
<p>Total number of articles:</p>
<%= #total_number_of_articles %>
Again, nothing shows up in terms of count in either comment or article.
So .... clearly I'm missing something here.
EDIT
The comment (total_number_of_comments) method was sorta based on this: (from railsguide)
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
There are many things you missed, I would happy to explain you.
Here
def total_number_of_comments
#article = Article.all
#comments_total = #article.comments.count
end
You have to do this
def total_number_of_comments
#comments_total = Comment.all.count // simple solution
end
Next is , you didn't used proper instance variable.
def total_number_of_articles
#articles_total = Article.total_number_of_articles
end
See yourself
Total number of articles:
<%= #total_number_of_articles %> // this is wrong
You assigned #articles_total but used #total_number_of_articles. if you use #articles_total it will work fine.
You should define a function index in your controller.
calling a GET on /articles/index calls the controller function index, you should set #articles_total = Article.total_number_of_articles in your index function in controller. You have it in a function in your controller that is not getting called.
I'm trying to include a few other recent articles when someone views a particular article in my Rails app.
I have the following method in my controller:
def show
#article = Article.find(params[:id])
#recents = Article.where(!#article).order("created_at DESC").limit(4).offset(1)
end
As the expert eye might see, #recents isn't correct. It's my best guess. :)
How do I show some recent articles but not repeat the one they are currently viewing?
You should use a scope in the model, for it has a lot of advanteges. Learn about scopes here. Your case should be something like this:
In the model:
class Article < ActiveRecord::Base
scope :recent, ->(article_id) { where.not(id: article_id).order(created_at: :desc).limit(4) }
end
and in the controller:
def show
#article = Article.find(params[:id])
#recent = Article.recent(#article.id)
end
This way the recent scope will always get the four last articles leaving out the article you pass in as an argument. And scopes are chainable so you could do something like this as well:
def some_action
#user = User.find(params[:user_id])
#user_recent_articles = #user.articles.recent(0)
end
You are getting the user recent articles. I pass a zero because the scope asks for an argument. You could create a different scope if you want to do it the cleanest way.
This, assuming a user has_many articles.
Well, hope it helps!
try with #recents = Article.where.not(id: #article.id).order("created_at DESC").limit(4)
click here - section 2.4 :).
I think there is a way of only making one call instead of 2 the way you have it now.
I am new to ruby on rails and was going to some of the code already existing in the application.
The code is as follows(books):-
def index
#books = Book
#books = #books.select("books.*,
(select count(*) from book_issues where books.id = book_issues.book_id and book_issues.return_date is null) as issued_cnt,
(select count(*) from book_holds where books.id = book_holds.book_id) as hold_cnt")
#books = #books.joins("inner join book_issues on book_issues.book_id = books.id")
#books = #books.where('book_issues.return_date is null')
#books = #books.group('books.id')
#books.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #books }
end
end
I am finding this a little difficult to understand.Why is this code being used and why not use the below code:-
def index
if params[:book_id]
#book = Book.find(:all,
:conditions => ["book_id = ? ", params[:book_id] ],
:order => "action_date ASC")
end
end
Can someone please help me with this.
Read manuals and tutorials about associations and scoping in rails.
After that you should rewrite the code to something like this:
#model
class Book < ActiveRecord::Base
# Association for BookIssue, the BookIssue model should have a 'belongs_to :book'
has_one :book_issue
# Association for BookHold, the BookHold model should have a 'belongs_to :book'
has_one :book_hold
# Scope to get not returned books, it joins all issues that don't have a return date.
# All book with a return date will be ignored.
scope :not_returned, joins(:book_issue).where(:book_issues => { return_date: nil } )
end
#controller
def index
# Use the scope mentioned in the model, to get all not returned books.
#books = Book.not_returned.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #books }
end
end
The primary problem being solved here is "generate an array of books which aren't currently checked out and pass it to the template engine to render on the page". That SQL code is handling the first part. It's unfortunate that you have to join against book_issues to see if there are any available copies, but disregarding that for right now you'd want to define a method on Book like :available? that returned true when there's at least one copy not checked out and then use that in your controller.
As a further adjustment, I'd like to have a database column on the book records that let me know if they were available for checkout without joining against the book_issues table (BooksController#Index sounds like it'd be invoked an awful lot, and you don't want it to thrash the database). That'd mean updating your book checkout logic to tweak the master book record, maybe.
The code would be much happier if it looked like this:
#books controller
def index
#books = Book.available
respond_to ... # continue as before
end
# book model
scope :available, where(copies_available: true)
# book_issue model
belongs_to :book
after_save :update_parent_availability
def available?
return_date.nil?
end
def update_parent_availability
book.copies_available = book.issues.select(&:available?).any?
book.save if book.changed?
end
That :update_parent_availability action might be subject to race conditions. You should probably factor it out into a helper book availability management class and run it in a transaction.
I think you'll find the ActiveRecord sections of the Rails guide very helpful. I'd suggest giving the ActiveRecord querying docs a thorough read. Pay close attention to the general style of the examples. One of the most powerful aspects of the MVC (Model-View-Controller) pattern is that you can build very simple interfaces within your model that do the "heavy lifting" rather than cluttering up your controllers with logic that really doesn't belong there.
Rails newbie here, trying to get a new controller working.
When I try to show ann existing instance, I get an undefined method error on a helper method.
Code follows.
Any idea why getRecipes would be undefined?!
Controller:
def show
id = params[:id]
recipe_ids = ConcreteMenu.getRecipes(id)
respond_to do |format|
format.html
end
end
Model
require 'json/objects'
class ConcreteMenu < ActiveRecord::Base
has_many :menu_recipes
has_many :recipes, :through => :menu_recipes
belongs_to :menu
def self.getRecipes(id)
recipes = MenuRecipe.find(:all, :conditions => {:concrete_menu_id => id}, :select => 'id')
end
end
It would help if you pasted the error text, because your explanation leaves a lot of possibilities for what could be wrong. BUT, there is an easier way to get what you want. The value of defining "has_many" relationships is that instead of calling a class method and passing the id of a concrete menu to get its associated recipes, you can just do this:
def show
#concrete_menu = ConcreteMenu.find(params[:id], :include => :recipes)
end
Now you'll have the menu object, and #concrete_menu.recipes returns an array of recipes you need. This feature is already built in, no need to reinvent the wheel.
Also, I noticed you were attempting to collect id's in the controller instead of the objects themselves. This suggests that you're going back and actually retrieving the records in the view itself. This is less efficient, and more difficult to troubleshoot when things go wrong. My example above will do what you need in a better (and more rails-accepted) way.
As you have it defined there, it should be available. Is there a chance you have something else called ConcreteMenu defined, but in a different context?
To be sure you're calling the correct one, where there may be ambiguity, you can refer to the top-level class:
recipe_ids = ::ConcreteMenu.getRecipes(id)
The other way to check that the method is defined correctly via script/console:
ConcreteMenu.methods.grep(/getRecipe/)
# => ["getRecipes"]
This is presuming, of course, you're having trouble with the getRecipes method. There's a possibility you're mistaking how controller variables are passed to the view:
def show
#id = params[:id]
#recipe_ids = ConcreteMenu.getRecipes(#id)
respond_to do |format|
format.html
end
end
Any instance variables defined (#...) will be available within the context of the view, but any local variables will no longer be defined as they are out of scope.