Rails, list by integer - ruby-on-rails

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

Related

How do I use a button to update the order of a filtered category?

I am new to Rails, but slowly making progress. I can't quite wrap my head around how to achieve my next task.
I have a controller (IdeasController) with an index that looks like this:
def index
if params[:round].blank? && params[:challenge].blank?
#ideas = Idea.all.order(params[:sort])
# #ideas = Idea.all.order(created_at: :desc, cached_votes_up: :desc)
end
if params[:round].present?
#round_id = Round.find_by(name: params[:round]).id
#ideas = Idea.where(round_id: #round_id).order("created_at DESC")
end
if params[:challenge].present?
#challenge_id = Challenge.find_by(name: params[:challenge]).id
#ideas = Idea.where(challenge_id: #challenge_id).order("created_at DESC")
end
end
I am updating the view and filtering by category with the above :round and :challenge with the code below in my index.html.erb:
<%= link_to "All", ideas_path %>
<% Round.all.each do |round| %>
<%= link_to round.name, ideas_path(round: round.name) %>
<% end %>
<% Challenge.all.each do |challenge| %>
<%= link_to challenge.name, ideas_path(challenge: challenge.name) %>
<% end %>
Now, my problem is that I want to create a button that orders by created_at DESC or ASC. I want the button to essentially be a toggle. I also want another button to order by cached_weighted_average DESC or ASC. This is from acts_as_votable so I can sort by vote counts.
The problem I am running into is that I can create a link or button that orders by created_at or cached_weighted_average, but it replaces all of the URL that was previously filtered by :round or :challenge. For example, if a user clicks "Round 1" and sees all ideas marked for "Round 1" and then they click the link to order by cached_weighted_average, the URL replaces:
/ideas?round=Round+1
With this:
/ideas?sort=cached_weighted_average+ASC
What I want is:
/ideas?round=Round+1&?sort=cached_weighted_average+ASC
I know this is a very new question, but everything I have tried has failed so far. It feels like I am missing something very easy. What I noticed I can do easily is inside the controller I can do something like:
if params[:round].present?
#round_id = Round.find_by(name: params[:round]).id
#ideas = Idea.where(round_id: #round_id).order("cached_weighted_average DESC")
end
Which is perfect. This button just needs to switch between cached_weighted_average DESC and created_at DESC.
Any help is appreciated, thanks.
passing multiple parameters is one way to handle:
<%= link_to object.name, object_path(first: something, second: something_else) %>
then alter your conditionals to contemplate presence of multiple params.
to differentiate between round and challenge when attempting to allow the user to choose how they'd like to sort you could use the same name and then pass it different values.
something like:
params["round_or_challenge"]
this would change your conditional to something like:
if params["round_or_challenge"] == "round" && params["asc_or_desc"] == "asc"
# query
elsif params["round_or_challenge"] == "challenge"
# query
end
or whatever. it's basically the same...just pass the values you need. you can also pass the existing parameters from the view the same way you access them in the controller.
Thanks for the response, #toddmetheny. I didn't implement your solution, but your solution helped me understand passing multiple parameters a bit more.
I ended up creating a helper, sortable. I also used the url_for to append at the end of whatever the current URL might be. I liked this approach because it meant I could sort on any parameter. I'm not sure that it's the best solution, but it works.
def sortable (name, sort)
link_to name, url_for(params.merge(sort: sort))
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

Generating URLs when not using an integer as an id?

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.

Resources