Generating URLs when not using an integer as an id? - ruby-on-rails

So I'm building a blog engine which has /articles/then-the-article-permalink as it's URL structure. I need to have prev and next links which will jump to the next article by pub_date, my code looks like this:
In my articles#show
#article = Article.find_by_permalink(params[:id])
#prev_article = Article.find(:first, :conditions => [ "pub_date < ?", #article.pub_date])
#next_article = Article.find(:first, :conditions => [ "pub_date > ?", #article.pub_date])
And in my show.html.erb
<%= link_to "Next", article_path(#next_article) %>
<%= link_to 'Prev', article_path(#prev_article) %>
In my articles model I have this:
def to_param
self.permalink
end
The specific error message I get is:
article_url failed to generate from {:action=>"show", :controller=>"articles", :id=>nil}, expected: {:action=>"show", :controller=>"articles"}, diff: {:id=>nil}
Without the prev and next everything is working fine but I'm out of ideas as to why this isn't working. Anyone want to help?

Solved my own problem, because I only had 2 records it was always finding a nil record. I changed the code in the views to:
<%= link_to "Next", article_path(#next_article) if !#next_article.nil? %>
<%= link_to 'Prev', article_path(#prev_article) if !#prev_article.nil? %>
Stupid and overblown problem, but I thought I'd add the solution for anyone that comes across this in future.

#next_picture = Article.find(:first, :conditions => [ "pub_date > ?", #article.pub_date])
should probably be this:
#next_article = Article.find(:first, :conditions => [ "pub_date > ?", #article.pub_date])
(I changed #next_picture to #next_article)

Stick in a <% debugger %> in your template and then check what the value of the #next_article.permalink is? I suspect the permalink is blank (either empty string or nil).
Also, just in general, can I recommend the friendly_id as a more robust solution to this problem (including changing permalinks and other features).

You could use will_paginate for this problem and set the number of of articles per page to 1, and use a named_scope to order the articles by pub_date. Then your next and previous links would just work. The URLs for next and previous would show the page number rather than the date, but you could probably modify the behaviour of the params sent to the action to use the date instead of the page number.

Related

Rails, list by integer

Right now I have a link which routes to a view that lists all of my Users by a given letter, if i click 'A' it displays all my users whose names start with the letter a. How do I do this for users that start with an integer? I assume I'd have to change my params value but I dont know how to do this. this is my code:
<%= link_to 'A', users_charlist_path(:char => "A") %>
right now I have this but it doesnt work:
<%= link_to '#', users_charlist_path(:char => /[0-9]+(\%7C[0-9]+)*/) %>
this is my controller:
def charlist
#a = User.all(:conditions => "goal like '#{params[:char]}%'")
end
I think everything must work. Only few moments.
SELECT * FROM users WHERE email LIKE '7%' ... Works so I think problem with link generation.
never pass parameters like this "#{params}". Because it's not secure. Use :conditions => ['goal like ?", "#{params[:char]}%"]
try your code in console and use pry for debugging.
You could use [0123456789] as wildcard that can be used by any database (source).
<%= link_to '#', users_charlist_path(:char => '[0123456789]' %>
In the controller, as explained by Igor, do not include params directly in the string, that's a security issue
def charlist
#a = User.where('goal like ?', "#{params[:char]}%").to_a
end

How do I set my rails app up so that I can click a letter and see only the entries starting with that letter?

My rails app basically displays a dictionary of sorts, which is stored in a database. I've set up a basic rails app with scaffold to display this on a webpage, but I can't for the life of me figure out how to set this next bit up. Basically I want a row of the 26 alphabetical letters, and when you click on a letter (say 'A'), I only want to see the dictionary entries of that letter.
My index.html.erb file:
<% for char in 'A'..'Z' %>
<%= link_to( "#{char}", "/words/lettersearch", :id => "#{char}") %>
<% end %>
my words_controller.rb file method for doing this:
def lettersearch
#word = Word.find(:all, :conditions => ["word LIKE ?", "#{params[:word]}%"])
end
It then outputs the #word to another page.
My problem is that it just outputs the entire dictionary again, no matter what letter I click on the index page. I know it's to do with the routing, as it never seems to actually run the 'lettersearch' method - it's always trying to run through the 'Show' method that's defined by default earlier in the controller.
Anyone able to give me a quick hand with how to route this thing through? I'm pretty new to rails and I don't understand the workings of link_to very well at all.
routes.rb:
Dictionary::Application.routes.draw do
resources :words do
post 'search', :on => :collection
post 'lettersearch', :on => :collection
end
#Rest of routes.rb still commented apart from
match ':controller(/:action(/:id))(.:format)'
You are passing in a param called :id but in your controller you use a param called :word
Also, update your code to use newer syntax
World.where(["word like ?", "#{params[:id]}%"])
And your view code can be cleaned as well
<% for char in 'A'..'Z' %>
<%= link_to char, "/words/lettersearch", :id => char %>
<% end %>
Your routes file only has a POST route to letter search, but a link_to is a GET request. So whats happening is the GET request is hitting /words/:id via GET which is the show action by default, and the params[:id] inside that request will be "lettersearch"
You could run the search in your index. I think that might be what you want to do. In your index controller you could put:
#orders = Order.lettersearch(params[:word])
and then in your model:
def lettersearch(word)
if lettersearch
#word = Word.find(:all, :conditions => ["word LIKE ?", "#{word}%"])
else
all
end
end

ruby on rails search form

I'm new to RoR and I've managed to make a basic search form but keep getting errors when trying to expand the search tags (name).. I have a model with various data (location, website, email, telephone) and was wondering how I can add these to my current search code.
/models/ciir.rb
def self.search(search)
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
static_pages_controller.rb
def home
#ciirs = Ciir.search(params[:search])
end
/home.html.erb
<%= form_tag ciirs_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag " Search Database Records ", :name => nil %>
</p>
<% end %>
When clicking the submit button (no search terms) the url is:
ciirs?utf8=✓&search=
but when modifying the name condition to something like 'website' the url changes to
ciirs?utf8=✓&search=&commit=+Search+Database+Records+ –
Since you mentioned you are new to RoR, I must share the way I learned RoR was reading, using and analyzing one issue at a time. I would suggest you to take a look at following points one at a time and try & learn how RoR treats them and how these fit your question:
How form_tag works?
How text_field_tag works?
Once you have understood form_tag, difference between text_field_tag and f.text_field?
How params objects are created, and it uses names of form controls?
How and when to use GET and/or POST form methods? Inadvertently, what are different types of method and when to use them?
How URL are used in the form_tag and what components are they made of?
Sprinkle a bit of knowledge of Ruby language by learning between Arrays and Hashes? In fact, learn Ruby as much as you can.
Answering your question,
/home.html.erb
<%= form_tag "/static_pages/home", :method => 'post' do %>
<p>
<%= text_field_tag "search[name]", params.has_key?("search") && params[:search].has_key?("name") ? params[:search][:name] : "" %>
<%= submit_tag " Search Database Records " %>
</p>
<% end %>
/models/ciir.rb
def self.search(search)
if search
find(:all, :conditions => ["name LIKE '%?%'", search[:name]])
else
find(:all)
end
end
So I modified your form, and told RoR about search params containing data for name.
params is a Hash (which is a key-value pair) having key named search, which further is a Hash having key named name.
The same principle is followed in the model code. We passed the Hash of key search to the function and in there, used the value of key named name.
I also updated the url in form_tag, to point it to home action of your controller. Assuming that you have added it to your routes.rb file, it usually follows the pattern controller_name/action_name or the function name action_name_controller_name_path or action_name_controller_name_url. Run rake routes command at your root directory to list out all paths in your application.
Also note, I used POST method instead of original GET. You may wish to use GET here, so please change it back.
I hope this works.
I found no error in your code. the url changed to ciirs?utf8=✓&search=&commit=+Search+Database+Records+ is normal. submit_tag generates a button named "commit" defaultly, it will be parsed in the params. I see you add :name => nil , it will fix the problem, the other part of your code needn't to be modified. I copied your code and tested it, it ran smoothly.

Get url for current page, but with a different format

Using rails 2. I want a link to the current page (whatever it is) that keeps all of the params the same but changes the format to 'csv'. (setting the format can be done by having format=csv in the params or by putting .csv at the end of the path). Eg
posts/1
=> posts/1.csv OR posts/1?format=csv
posts?name=jim
=> posts.csv?name=jim OR posts?name=jim&format=csv
I tried this as a hacky attempt
request.url+"&format=csv"
and that works fine if there are params in the current url (case 2 above) but breaks if there aren't (case 1). I could come up with more hacky stuff along these lines, eg testing if the request has params, but i'm thinking there must be a nicer way.
cheers, max
EDIT - btw, it's not guaranteed that the current page could have a named route associated with it, in case that's relevant: we could have got there via the generic "/:controller/:action/:id" route.
<%= link_to "This page in CSV", {:format => :csv } %>
<%= link_to "This page in PDF", {:format => :pdf } %>
<%= link_to "This page in JPEG", {:format => :jpeg } %>
EDIT
Add helper
def current_url(new_params)
url_for :params => params.merge(new_params)
end
then use this
<%= link_to "This page in CSV", current_url(:format => :csv ) %>
EDIT 2
Or improve your hack:
def current_url(new_params)
params.merge!(new_params)
string = params.map{ |k,v| "#{k}=#{v}" }.join("&")
request.uri.split("?")[0] + "?" + string
end
EDIT
IMPORTANT! #floor - your approach above has a serious problem - it directly modifies params, so if you've got anything after a call to this method which uses params (such as will_paginate links for example) then that will get the modified version which you used to build your link. I changed it to call .dup on params and then modify the duplicated object rather than modifying params directly. – #Max Williams
You can use:
link_to "text of link", your_controller_path(format:'csv',params: request.query_parameters)
#floor's answer was great, I found it very useful.
Although the method can be improved by using the to_params method rather than contructing your own, like so:
def current_url(new_params)
params.merge!(new_params)
"#{request.uri}#{params.to_params}"
end

Link for action problem

I have problem with my application.
I have table report, there are 2 column , user_id and comment_id
I created link on article comment view
<%= link_to "[ ! ]", report_comment_url(comment) %>
class CommentsController < ApplicationController
def report
#comment = Comment.find(params[:id])
#comment = CommentReport.new(params[:comment_report, :comment_id])
if #comment_report.save
redirect_to :back
end
redirect_to :back
end
end
but it was error
ActionController::MethodNotAllowed
Only post requests are allowed.
Do you have any suggestion how to post current_user id and comment_id to report table ?
Given what I assume you're trying to accomplish, I'd suggest you use link_to_remote.
params[:comment_report] is nil because there's no reference to that in your link_to statement. Since you mention in the comment that your view is:
<%= link_to "[ ! ]", report_comment_url(comment), :comment_id => comment.id, :user_id => comment.user_id %>
Then you need this in your controller:
#comment_report = CommentReport.new(:user_id => params[:user_id], :comment_id => params[:comment_id])
But I agree with NSD that link_to_remote would work better for what you want to accomplish (which is create a new record and return the user to the page). You would also eliminate the need for your #comment = Comment.find(params[:id]) statement.
Here's what's going on:
Using Restful routes, you've set up report as a post operation. Which seems reasonable because report is performing the create action.
Unfortunately link_to doesn't know or even care about that. Links in general only perform get requests. Forms produce post requests, but they seem unnecessary in this case.
You have four options.
Make the [ ! ] link a button on a form submitting to report.
Break RESTful guidelines and redefine report receive get requests.
Make this a link_to_remote call. N.B. This relies on javascript and will not work at all if Javascript is disabled.
Add the method options to the link_to call. N.B. This also relies on javascript and will fall back to a get request if javascript is disabled.
<%= link_to "[ ! ]", report_comment_url(comment), :method => :post %>
However none of these solutions will solve all your problems. There are a couple of bugs with your posted code that you may not have realized yet.
First:
#comment = CommentReport.new(params[:comment_report, :comment_id])
is bad syntax and will fail. There are a number of ways to fix this, the preferred method is to to roll :comment_id into the params[:comment_report] hash to fix this.
Ie pass the params as:
params = {
:id => 4, # done by report_comment_url
:comment_report => {
:attribute1 => value1,
...
:comment_id => 4
}
}
Now you can use
#comment = CommentReport.new(params[:comment_report])
for the desired effect.
Second:
report_comment_url does not pass along the additional parameters, so your controller will try to save an empty record. Adding the comment_report to the arguments of report_comment_url will fix this problem.
This will perform a remote call requesting the report action in the comments controller, with the parameter hash required to fix the other problem.
<%= link_to_remote "[ ! ]", report_comment_url(comment,
:comment_report => {:attribute1 => value1, ..., :comment_id => comment.id}),
:method => :post %>

Resources