How do I implement next and previous links along with their permalink - ruby-on-rails-3

I have a simple blog app. On my index page, where all the posts are show. What I want do is show only one post on one page and have next and previous links. When clicked on next, it should go to the next post based on created date and the url in the browser should be the actual link /problems/:id .
Any help of suggestion is appreciated.
Thanks

Duplicate answer from Stack Overflow another question
If each title is unique and you need alphabetical, try this in your Post model.
def previous_post
self.class.first(:conditions => ["title < ?", title], :order => "title desc")
end
def next_post
self.class.first(:conditions => ["title > ?", title], :order => "title asc")
end
You can then link to those in the view.
<%= link_to("Previous Post", #post.previous_post) if #post.previous_post %>
<%= link_to("Next Post", #post.next_post) if #post.next_post %>
Untested, but it should get you close. You can change title to any unique attribute (created_at, id, etc.) if you need a different sort order.

Related

Editing multiple records at once - how to update without IDs

Still new to Rails. I'll try to provide as much detail as possible.
I have a form that lets me update multiple records at one time.
It's based off the 'Editing Multiple Individually' Railscast episode.
<%= form_tag(auction_clerk_path(#auction), :method => :put) do %>
<% #lots.each do |lot| %>
<%= fields_for "lots[]", lot do |f| %>
<%= f.number_field :sale_price %>
<% end %>
<% end %>
<% end %>
(Simplified to just include a single input for each instance)
An Auction contains multiple Lots (items for sale).
The auction_clerk_path is the route I'm using to just show all lots on one auction.
Everything is working just fine... until I use try to customize my lot paths...
I've added the following to my lot.rb file to be able to use:
/auctions/:auction_id/lots/:lot_number
instead of /auctions/:auction_id/lots/:id
def to_param
lot_number
end
So, in the form mentioned earlier, the fields render with name="lots[12][sale_price]" where 12 is the id.
However with the to_param change, now the fields render with name="lots[1][sale_price]" where 1 is the lot_number.
When I save, the submitted parameters are lot_numbers instead of ids.
So obviously when it tries to update, it won't find the correct records.
My method definition looks like this:
def save_clerking
#updated_lots = Lot.update(params[:lots].keys, params[:lots].values).reject { |l| l.errors.empty? }
if #updated_lots.empty?
flash[:notice] = "Lots updated"
redirect_to auction_clerk_path(#auction)
else
render :action => "clerk"
end
end
I either need to change my method definition to lookup by lot number, or change the form to somehow output IDs in the first place... but I don't know how.
Any help is appreciated. Thanks!
Fixed this through some help on another question.
I changed my method def to
#updated_lots = []
params[:lots].each do |lot_number, attributes|
lot = Lot.where("lot_number = ? AND auction_id = ?", lot_number, params[:auction_id]).first
if lot.update_attributes(attributes)
#updated_lots << lot
end
end
You could fetch the ids by lot number in the controller action and feed those to the update method instead of the params keys.

Post of form in table

I have a #company known in my view and also #shops which is displayed on a table.
Each row of the table contains a button for the user to click on.
The click on that button should trigger a create in Clients and client belongs to both a Shop and a Company. The shop_id I want to send with a post request together with the company.
I need to generate a post request per line in the table. How do I do that? With a form_for? And how do I do it per row? And how do I send the shop_id (of that row) and company_id (in the view) to the post? I don't want to use params[:shop_id] because the user can change that right?
Found the solution. You need to use a hidden field:
<%= form_for '', :url => company_clients_url(shop), :html => {:method => :post} do %>
<%= hidden_field_tag 'shop_id', shop.id %>
Later in the controller you can pick it up with params[:shop_id]
Hope this helps somebody else out.

Rails filter by SQL search in different model

In my Photos view there is an option to search using a text_field. I wanted to add an option to "search" by clicking on predefined tags. Perhaps a search is not the best way to do this.
This is the method for search in my model:
def self.search(search)
if search
where('name or description or tag.name LIKE ?', "%#{search}%")
else
scoped
end
end
I'm not sure that I am calling this correctly either... But these are the links I create in the index view for photos:
<% #tags.each do |tag| %>
<%= link_to tag.name, :search => tag.name, :class => "tag" %>
<% end %>
Tags are not in the photo table, but have HABTM relation with photos. I can normally call them though simply with #photo.tags.name or something similiar.
Anyway, when I click a tag it spits back a SQLite3::SQLException: no such column: tag.name: SELECT COUNT(*) FROM "photos" WHERE (name or description or tag.name LIKE '%landscape%'). Any thoughts? Perhaps there is a better way to do this in the first place?
In order to find by a field that is in the tags table you have to make a join with it. To make it change your search method to this:
def self.search(search)
if search
joins(:tags).where('name or description or tags.name LIKE ?', "%#{search}%")
else
scoped
end
end
Coming with your own solution to common problems is a great way to learn, however, I'd recommend that you delegate the tags job to a specialized gem like: https://github.com/mbleigh/acts-as-taggable-on, so you can keep focused on your business.
Also, using a relational model to save tags is somewhat overkill: a comma-separated list of tags should be enough for most cases.

Beginner struggling with update_attribute command

I am in the process of trying to use the update_attribute command, but struggling to get it working (at all) and hoped someone could point me in the right direction?
I have previously posted a question about this issue, it was very useful in terms of giving a feel for the mechanics of what is going on, but unfortunately it didn't actually get it working.
I have a database of items (Items), which among other things contains ':item_name', ':click_count' and ':external_url'.
Currently I have a view (Showselecteditems) in which there is a list of all the items, when a user clicks on an item name, they are directed to the appropriate external url. This works using the code:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url %>
I would now like to add the ability to count the number of times a particular item name has been clicked on (i.e. in total for all users, not individual users) and therefore the number of times each external url has been visited in order to work out which is most popular.
Reading around, I believe i need to modify the code above to something of the form:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And need to define this function somewhere - it seems to only be found if located in 'application_helper'?
def clickcountplusone
clickeditem = Items.find(params[:identifier])
clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end
Needless to say, I cannot get this to work... My question is therefore, how can I set things up correctly so that when the link is clicked on the count is incremented? The other common problem people seem to report is that the number will be incremented each time the page is refreshed, which I would like to avod if possible.
Previously people have suggested adding to the 'show' section of the 'Items' controller, however, i don't know how this would work as the links are being clicked on the Showselecteditems view page, not the database itself where you get the show, edit, destroy commands. Any advice greatly appreciated.
This
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
will not point user to the some_controller#clickcountplusone, because you already specified an external link.
The easiest way to do this job is to modify your link_to like:
<%= link_to selecteditem.item_name.to_s, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And then to modify your actions source:
def clickcountplusone
clickeditem = Items.find(params[:identifier])
redirect_to clickeditem.external_url if clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end

How can I make this Ruby on Rails page more efficient?

I'm building a site where users can track their collection of figures for Dungeons & Dragons (www.ddmdb.com). The models/relationships involved in this funcitonality are the following:
User:
id
login (username)
a bunch of other fields
Miniature:
id
name
number (# in the set, not count)
release_id (foreign key)
a bunch of other fields and foreign keys
Ownership:
id (is this really even needed?)
user_id
miniature_id
have_count
favorite (boolean)
The pertinent relationships I have set up are as follows:
User:
has_many :ownerships
has_many :miniatures, :through => :ownerships, :uniq => true, :conditions => "ownerships.have_count > 0"
has_many :favorites, :through => :ownerships, :source => :miniature, :uniq => true, :conditions => "ownerships.favorite = true"
Miniatures:
has_many :ownerships
has_many :owners, :through => :ownerships, :source => :user, :uniq => true, :conditions => "ownerships.have_count > 0"
Ownership:
belongs_to :user
belongs_to :miniature
I have a page where user's can both view and update their collection, as well as view other user's collections. It contains a list of all the miniatures on the site and a text box next to each where the user can enter how many of each miniature they have. This functionality also exists in sub-lists of miniatures (filtered by type, release, size, rarity, etc.)
When a user creates an account they have no entries in the ownership. When they use the collection page or sub-list of miniatures to update their collection, I create entries in the ownership table for only the miniatures on the submitting page. So if it's the full Collection list I update all minis (even if the count is 0) or if it's a sub-list, I only update those miniatures. So at any time a particular user I may have:
- no entries in ownership
- entries for some of the miniatures
- entries for all the miniatures.
The problem I'm having is that I don't know how to query the database with a LEFT JOIN using a "Rails method" so that if a user doesn't have an entry for a miniature in Ownerships it defaults to a have_count of 0. Currently I query for each user_id/miniature_id combination individually as I loop through all miniatures and it's obviously really inefficient.
View:
<% for miniature in #miniatures %>
<td><%= link_to miniature.name, miniature %></td>
<td><%= text_field_tag "counts[#{miniature.id}]", get_user_miniature_count(current_user, miniature), :size => 2 %></td>
<% end %>
Helper:
def get_user_miniature_count(user, miniature)
ownerships = user.ownerships
ownership = user.ownerships.find_by_miniature_id(miniature.id)
if ownership.nil?
return 0
else
return ownership.have_count
end
end
An alternate solution would be creating entries for all miniatures when a user signs up, but then I would also have to add a 0 have_count for all users when a new miniature is added to the database after they sign up. That seems like it could get a bit complex, but perhaps it's the right way to go?
Is there a way to do the join and supply a default value for miniatures where there's no entries in the Ownership table for that particular user?
The first thing I would say is that the User model should own the code that works out how many of a given miniature the user owns, since it seems like "business logic" rather than view formatting.
My suggestion would be to add a method to your User model:
def owns(miniature_id)
o = ownerships.detect { |o| o.miniature_id == miniature_id }
(o && o.have_count) || 0
end
Dry-coded, ymmv.
Edit: Note that ownerships is cached by Rails once loaded and detect is not overridden by ActiveRecord like find is, and so acts as you would expect it to on an Array (ie no database operations).
Using fd's suggestion and information found at http://www.ruby-forum.com/topic/52385, I created the following method:
def miniature_count(miniature_id)
if #counts.nil?
#counts = Hash.new
ownerships.collect{|o| #counts[o.miniature_id] = o.have_count }
end
count = #counts[miniature_id] || 0
end
This ends up being faster than the detect approach.
I picked miniature_count over owns for the name because owns sounds like a method that should return a boolean instead of an integer.
Query Every Entry
Completed in 2.61783 (0 reqs/sec) | Rendering: 1.14116 (43%) | DB: 1.34131 (51%) | 200 OK [http://ddmdb/collection/1]
Detect Methods
Completed in 2.20406 (0 reqs/sec) | Rendering: 1.87113 (84%) | DB: 0.21206 (9%) | 200 OK [http://ddmdb/collection/1]
Hash Method
Completed in 0.41957 (2 reqs/sec) | Rendering: 0.19290 (45%) | DB: 0.10735 (25%) | 200 OK [http://ddmdb/collection/1]
I will definitely need to add caching, but this is definitely an improvement. I also suspect I am prematurely optimizing this code, but it's a small site and a 2.5 second load time was not making me happy.
Maybe I'm missing something, but the way you've specified the relationships seems sufficient for rails to figure out the counts on its own? Have you tried that?
edit:
Re the discussion in the comments...how about this:
<% ownerships=current_user.ownerships %>
<% for miniature in #miniatures %>
<td><%= link_to miniature.name, miniature %></td>
<td><%= text_field_tag "counts[#{miniature.id}]", get_miniature_count(ownerships, miniature), :size => 2 %></td>
<% end %>
Where get_miniature_count() just iterates through the supplied ownerships and returns 0 or the count if the miniature appears in the list? I think this will avoid going back to the DB again in each iteration of the 'for'.
edit 2: I'd also suggest firing up script/console and trying to do what you want in ruby directly, i.e. test for the miniatures membership in the ownerships list thinking in terms of ruby not SQL. Often, rails and activerecord is smart enough to do the necessary SQL black magic for you behind the scenes, given it knows the relationships. If you find a user and then do user.methods you'll see what is available.