In my show.html.erb file I have two links for next and previous like so
<%= link_to("Previous Post", #question.previous) if #question.previous %>
<%= link_to("Next Post", #question.next) if #question.next %>
.previous and .next are methods in my model which finds either the current id + 1 or - 1
like so in my model
def previous
Question.find_by_id(id - 1, :select => 'id')
end
def next
Question.find_by_id(id + 1, :select => 'id')
end
But rather than incrementing and decrementing blindly as I might have deleted a "Question" so i'll get an error, could I find the next/previous "Question" from the scope of the query the show action was accessed from?
Have you tried something like this?
def previous
Question.limit(1).order("id DESC").where("id < ?", id)
end
def next
Question.limit(1).order("id DESC").where("id > ?", id)
end
Try Act as Ordered.
https://github.com/imedo/acts_as_ordered
Related
I have created a multi find search, where I need to filter records by date / category / title. Searching by a category and/or title works, however, when date is typed it doesn't change anything (the results is the same like there was no date typed). I have no idea what else I could do to fix it, I am just a beginner in Ruby. Any idea?
Model:
def self.multi_find(cat_id, search, date_search)
search_condition = "%" + search + "%"
#date_condition = date_search
# test if cat_id is not blank
if not cat_id.blank?
# assign the value of cat_id to a ‘scope’ named :cat
scope :cat, -> { where('category_id = ?', cat_id) }
# using the ‘scope’ cat find where a search string is like a title or an author’s name
self.cat.where("title LIKE ? or event_date = ?", search_condition, date_search.to_date)
else
# find where a search string is like a title or an author’s name
self.where("title LIKE ? or event_date = ?", search_condition, date_search.to_date)
end
end
Controller:
def multi_find
# call an Event class method, using two parameters; a category unique identifier and a search string (author or title)
events = Event.multi_find(params[:cat_id], params[:search_string], params[:event_date_search])
# use Kaminari pagination ...
#events = Kaminari.paginate_array(events.order :title).page(params[:page]).per(6)
# if no products have been found
if #events.empty?
# display a notice
flash.now[:alert] = "No events found - so displaying all events"
# then display all products
#events = Event.order(:title).page(params[:page]).per(6)
end
# use the index view
render :action => "index"
end
The console outputs the SQL Query
Event Load (0.0ms) SELECT "events".* FROM "events" WHERE (category_id = '1') AND (title LIKE '%%' or event_date = '2018-02-14') ORDER BY "events"."title" ASC
View file:
<%= form_tag my_path, :method=>'post', :multipart => true do %>
<%= select_tag ('cat_id'),
options_from_collection_for_select(#categories, :id, :cat_name, 0 ),
:prompt => "Select a Category" %>
<div class="datepicker">
<% #event_date_format %>
<%= text_field_tag :event_date_search %>
</div>
<!-- Key word:-->
<%= text_field_tag :search_string %>
<%= submit_tag 'Search' %>
<% end %>
It's because you have an or in your sql statement. However you should also clean up your code a bit.
def self.multi_find(cat_id, search, date_search)
result = self.all
result = result.where(category_id: cat_id) if cat.id.present?
result = result.where('title LIKE ?', "%#{search}%") if search.present?
result = result.where(event_date: date_search) if date_search.present?
result
end
I'm trying to write a row for my index table that filters my objects regarding a specific value of a specific column. What I have until now is this:
pimps_controller.rb:
def index
#pimps = Pimp.search(params[:search])
end
pimp.rb:
def self.search( search)
if search
where('title LIKE ?', "%#{search}%")
else
scoped
end
end
A part of view:
<%= text_field_tag :search, params[:search] %>
That filters after the objects title only so I tried to alter it to make it functional for different search fields that can filter after different attributes. I want to pass a second parameter value if someone fires the search function to make sure it triggers for the right attributes. That's what I've tried:
pimps_controller.rb
#pimps = Pimp.search(params[:search_column],params[:search])
pimp.rb:
def self.search(search_column, search)
if search
col = "%#{search_column}"
s = "%#{search}%"
where(col 'LIKE ?', s)
else
scoped
end
end
The view:
<%= text_field_tag :search, params[:search], params[:search_column => title] %>
But it's not working. I get an error message for passing the both parameters in one search field I guess. How would you do it?
Here's a simple tutorial on how to do it:
https://we.riseup.net/rails/simple-search-tutorial
In the model, you will have to add the fields with or condition to the query.
def self.search(search)
search_condition = "%" + search + "%"
find(:all, :conditions => ['title LIKE ? OR description LIKE ?', search_condition, search_condition])
end
If you want to define the field to search in the params you can use string interpolation with simple quotes:
%q(text contains "#{search.query}")
You need 2 text fields, one for the column, one for the value:
# view
<%= text_field_tag :search_value, params[:search_value] %>
<%= text_field_tag :search_column, params[:search_column] %>
# controller
#pimps = Pimp.search(params[:search_column], params[:search_value])
# Pimp model
def self.search(search_column, search_value)
if search_value.present? && search_column.present?
column = self.column_names.include?(search_column.to_s) ? search_column : 'title'
value = "%#{search_value}%"
where("#{self.table_name}.#{column} LIKE ?", value)
else
scoped
end
end
The problem about this method is that if you don't type the exact name of the column, it will search the value in the column title. I think you should use a select_tag, listing all searchable columns of the model:
# view
<%= select_tag :search_column, options_for_select(Pimp.column_names.map { |col| [col, col] }, params[:search_column]) %>
This view code will display a select tag with the available columns of the Pimp model. You can easily limit the searchable columns by defining a class method on Pimp:
# Pimp model
def searchable_columns
self.column_names - ['id', 'created_at', 'updated_at']
end
# view
<%= select_tag :search_column, options_for_select(Pimp.searchable_columns.map { |col| [col, col] }, params[:search_column]) %>
I have images where you can click next and previous, but I'm having trouble because when I'm viewing the last image in my database (that is associated with user_id), it show's routing error with :id => nil.
This is happening because there's no more data after the image for that user. So how can I set it will rotate their images even if they get to their last image in database (if next), and vice versa, if previous.
This is in my model
def self.s_prev(img)
first(:conditions => ["created_at < ?", img.created_at], :order => "created_at desc")
end
def self.s_next(img)
first(:conditions => ["created_at > ?", img.created_at], :order => "created_at asc")
end
These is my links
<%= link_to "Previous", user_image_path(#image.user_id, #user.images.s_prev(#image)) if user_image_path(#image.user_id, #user.styles.s_prev(#image)) %>
<%= link_to "Next", user_image_path(#image.user_id, #user.images.s_next(#image)) if user_image_path(#image.user_id, #user.images.s_next(#image)) %>
Apparently the if statements don't help me, for some reason
Lets say there's these database id for images table:
id user_id
1 14
2 14
3 14
4 15
So say if I'm on this page: localhost:3000/users/14/images/2
The page will show, and the links will show, but when I click on next, I'll get an error because there is no user_id => 15 and id => 4, where the next button is trying to get localhost:3000/users/14/images/4 for the page in ...users/14/images/3
So how do I avoid this issue and only display next/previous links associated with user?
def self.s_prev(img)
ordered = scoped.order("created_at desc")
ordered.first(:conditions => ["created_at < ?", img.created_at]) || ordered.first
end
def self.s_next(img)
ordered = scoped.order("created_at asc")
ordered.first(:conditions => ["created_at > ?", img.created_at]) || ordered.first
end
I haven't used case statements before and was wondering how to do the following.
I have a number of news pages, each having posts relevant to department, so the pages
/tynewyddnews
/woodsidenews
/outreachnews
/sandpipernews
On my home page I am grabbing the latest post from each of these and displaying them.
Post Model
def self.top_posts
#Array with each of the 4 departments - first record
top_posts = [
self.tynewydd_posts.first,
self.woodside_posts.first,
self.sandpiper_posts.first,
self.outreach_posts.first
]
#remove entry if nil
top_posts.delete_if {|x| x==nil}
return top_posts
end
tynewydd_posts for example is a scope
scope :tynewydd_posts, :include => :department, :conditions => {"departments.name" => "Ty Newydd"}, :order => "posts.published_on DESC"
So if i am reading a post on the home page from tynewydd and want to create a link to /tynewyddnews or i am reading a post from woodside and want to link_to /woodside I have been advised that a case statement may help, but I am unsure on what to use as parameters, so my attempt so far is
def public_news
case Post.top_posts
when :tynewydd_posts == 'Ty Newydd'
link_to('...Read more', tynewyddnews_path)
when :woodside_posts == 'Woodside'
link_to('...Read More', woodsidenews_path)
end
end
And then in my view i can call the helper
<%= public_news %>
obviously a miserable attempt, firstly in examples I have seen a variable is being set when the case is being set? if someone could give some advice on how to achieve this it would be much appreciated
Try this variant:
def public_news
path = case
when Post.tynewydd_posts then tynewyddnews_path
when Post.woodside_posts then woodsidenews_path
...
else default_path end
end
link_to('...Read More', path)
end
when expression will fire up when expression is evaluated to true
I'm creating an application that'll display a random picture based upon a defined letter in a word.
Images are attached to a Pictures model (containing another "letter" field) using Paperclip, and will be iterated through in an each block.
How would I go about passing the letter back from the each block to the model for random selection.
This is what I've come up with so far, but it's throwing the following error.
undefined method `%' for {:letter=>"e"}:Hash
Model:
def self.random(letter)
if (c = count) != 0
find(:first, :conditions => [:letter => letter], :offset =>rand(c))
end
end
View:
<% #letters.each do |a| %>
<%= Picture.random(a).image(:thumb) %>
<% end %>
Thanks
One problem is your conditions has a syntax error. The hash notation is wrong:
:conditions => [:letter => letter]
should be
:conditions => {:letter => letter}
Also, it seems to me that your random scope will always exclude the first Picture if you don't allow an offset of 0. Besides that, do you really want to return nil if the random number was 0?
Picture.random(a).image(:thumb) would throw "undefined method 'image' for nil:NilClass" exception every time c==0. Can probably just use:
def self.random(letter)
find(:first, :conditions => {:letter => letter}, :offset =>rand(count))
end
EDIT: You'll either need to guarantee that your db has images for all letters, or tell the user no image exists for a given letter.
<% #letters.each do |a| %>
<% if pic = Picture.random(a).image(:thumb) %>
<%= pic.image(:thumb) %>
<% else %>
No image available for <%= a %>
<% end %>
<% end %>
Or the like...
EDIT: Actually I don't think your offset strategy will work. One other approach would be to return the set of images available for the given letter and randomly select from that collection, something like:
def self.random(letter)
pics = find(:all, :conditions => {:letter => letter})
pics[rand(pics.size)] if !pics.blank?
end