Understanding will_paginate arguments - ruby-on-rails

Total Rails noob, working through the Rails Tutorial videos. I'm all the way to the last lesson, and there's something I don't understand:
#users = #user.followed_users.paginate(page: params[:page])
Specifically, the bit I'm not tracking on is paginate(page: params[:page]). I looked at the paginate docs and I understand the paginate method can take three params, :page being one of them. I think this parameter means "current page," but the will_paginate docs say it defaults to 1.
I also know (think) that params[:page] refers to the built-in Rails params hash, meaning, current session params. Right?
So... I don't get it. Why do I need it? How does the :page symbol get into the params hash? What does this really do?
For additional context, see listing 11.30 on the Ruby Tutorial book. Any help would be much appreciated.

I think what you might be misunderstanding is how Ruby arguments work in this case. paginate does not actually take 3 arguments, but instead takes a single hash argument with three options (key/value pairs).
In Ruby, when you pass key/value pairs as the last set of arguments, they are automatically converted to a hash. For example, the following are equivalent:
paginate({page: 1})
is the same as:
paginate(page: 1)
So really what you are doing is passing a single argument, which is a hash that has multiple key/value pairs.
Now to specifically answer your questions:
Why do I need it?
You need to pass this value in so that will_paginate knows which page you are currently on. It defaults to page one because on the initial page load, you will not have ?page=x in your URL. After you change to a different page, it takes the page value from the URL and passes that to the paginate method.
How does the :page symbol get into the params hash?
Any argument that is part of the query params in the URL will get automatically passed to the params hash by Rails (more likely Rack which Rails is built upon)
What does this really do?
I'm hoping the above answered this, but if not, maybe it provided you with enough info to come up with a more specific question.

Related

ruby on rails iterating params

I have a client that is sending params such as age, gender, name and so on.
I need to retrieve data from the table based on the params, but first I need to check for the presence of the param(to avoid a null param and therefore an empty result). The params are working as filters, so they can be triggered or they can be left blanck.
What I am doing right now is
#retieve = Student.all
unless params[:age].nil?
#retrieve = #retrieve.where(age: params[:age])
end
unless params[:gender].nil?
#retrieve = #retrieve.where(gender: params[:gender])
end
and so on for every param I receive. This way I check if the filter has been selected, and if it has I use the selection as a parameter for the query
It works, but as Ruby is known for the DRY statement, I am pretty sure someone out there knows a better way for putting this and to make this flexible.
Thank you for whatever answer or suggestion you will provide!
This will work best if all of these filters were in a subhash of params that you can iterate over without including unwanted parameters (eg the :action and :controller parameters that rails adds)
Once you've done that you could do
(params[:filters] || {}).inject(Student.all) do |scope, (key, value)|
scope.where(key => value)
end
There's a few ways to do this sort of thing and you have options for how far you want to go at this stage.
Two big things I'd consider -
1) Make nice scopes that allow you to send a param and ignore it if it's nil. That way you can just append another scope for each param from the form and it will be ignored without using if or unless
2) Move the search into a separate class (a concern) to keep your controller clean.
Here's a blog post that talks about some of the concepts (too much to post in this answer). There is lots of info on the web about this, I searched on the web under "rails search filter params concern" to get an example for you.
http://www.justinweiss.com/blog/2014/02/17/search-and-filter-rails-models-without-bloating-your-controller/

"colon" notation in Ruby

I'm new to Ruby, and trying to read/understand some rb files. I've come across this line. Could anyone explain what it is doing (and the gramatical meaning behind it, too)?
#account = current_user.accounts.find(params[:id])
1: what is current_user? I grepped the entire file, but didn't see where it was declared. (Well, I know variables don't need to be declared in Ruby, but it is not referred to with an # sign, so this might not be a variable?)
Where should I expect to find current_user? (ie., in app/model, app/view, etc?)
2: What is the meaning of :id?
params looks like an array, so I guess :id somehow means the index, but why is there the colon before id?
Thanks
what is current_user?
It's often hard to tell the difference between a local variable and a method being called. This is because the () is optional, and often omitted. In this case, current_user is a method, declared in a mixin or superclass somewhere, which is why it's not in this file. Think of current_user.accounts as current_user().accounts, which should illustrate what's going on there.
Where should I expect to find current_user?
Most likely, it provided in a plugin or gem that handles authentication for your project. It's hard to advise more than that without knowing more about your project.
What is the meaning of :id? params
It's pulling a value from a hash, by it's key. In irb, try this:
params = { :id => 123 }
puts params[:id] # => prints 123
So params is a hash (some languages call this datatype an associative array, or a dictionary). It's a way to store a set of key/value pairs. The somehash[somekey] notation returns the value for somekey in somehash.
:id itself is a symbol. It's sort of like a string that never changes. Do some research on ruby symbols to learn more on that.
And in rails, params is a special hash. Values passed form the request show up in this hash. So the route /books/:id would active when you load /books/123, which would set params[:id] to 123 in the controller.
Current user is likely provided by a gem. Example of a gem would be Devise.
"params" is a hash of the parameters passed as the query string on the URI. So, ":id" would reference the value of the id parameter.
Example:
www.domain.com?id=abc would yield params[:id] as "abc".

How to build sort urls on Rails?

I am working on the index page of a listing controller, which needs several sort options. Query string is needed to determine the sort option that is active for the current page. I have used a workaround for this problem by hardcoding the query string into the sort links:
=link_to "Lowest Price","/listings?sort_by=price&order=asc", :class=>"#{'active' if request.query_string =~ /sort_by=price&order=asc/ }"
But there are two problems with this. First, this is too fragile. Second, it doesn't support a search query nor any other parameters -- otherwise it breaks.
What I need is a way to change the sort options without assuming that the query string will stay intact...
Not sure if there is a best practice for doing this. I'm taking the long road and just adding helpers to parse url to hash, hash to url, and I still don't know what to do about the active link problem. It could be a while to do all that.
Any suggestions would be appreciated.
You can do this by providing key/value pairs to any URL helper. For example:
listings_url(:sort_by => "asc", :order => "asc")

Ruby on Rails - passing hashes

I have a piece of controller code where some values are calculated. The result is in the form of an array of hashes. This needs to get into a partial form somehow so that it may be retrieved later during commit (which is through the Submit button).
The questions is how do we pass the array of hashes?
thanks.
Is there a reason it has to be through the form? This is the type of thing I usually use the session for.
I can't really think of a nice way to do what you're asking with forms. I guess you could create hidden fields for each key in your hash in the form with hidden_field_tag as an alternative. Then you run into problems translating it (what if a key's value is an array or another hash?).
You could easily store the hash in the session and then on each page load, check to see if there is a hash where you expect it. On calculating values:
session[:expected_info] = results
And each page load, something like this:
if session.has_key?(:expected_info)
results = session.delete(:expected_info)
# you already calculated the results, just grab them and
# do what you need to do
else
# you don't have the expected info
end
You should be able to pass it as a string to your partial:
[{}].inspect
and eval it when it is submitted back through the form:
eval("[{}]"))
but that would be really dirty…

Can I access the collection an instance method was called on in ruby on rails

I'm working on implementing a search form in a ruby on rails application. The general idea is to use form_tag to submit the search fields (via params) to a search function in the model of the class I'm trying to search. The search function will then iterate through each of the params and execute a scoping function if the name of the function appears in params.
The issue is that when I call the search on a collection like so:
#calendar.reservations.search({:search_email => "test"})
I don't know how to refer to the collection of #calendar.reservations from within the search function.
Additionally I'm confused as to why #calendar.reservations.search(...) works, but Reservations.all.search gives me an error saying you can't call an instance method on an array.
I've got the details of the search method over here: https://gist.github.com/783964
Any help would be greatly appreciated!
I don't know how to refer to the
collection of #calendar.reservations
from within the search function.
If you use self (or Reservation, it's the same object) inside the classmethod, you will access the records with the current scope, so in your case you will see only the reservations of a particular calendar.
[edit] I looked at you search function, and I think what you want is:
def self.search(search_fields)
search_fields.inject(self) do |scope, (key, value)|
scope.send(key, value)
end
end
Additionally I'm confused as to why
#calendar.reservations.search(...)
works, but Reservations.all.search
gives me an error saying you can't
call an instance method on an array.
#calendar.reservations does not return a standard array but a (lazy) AssociationCollection, where you can still apply scopes (and classmethods as your filter). On the other hand Reservation.all returns a plain array, so you cannot execute search there (or any scope, for that matter).
You don't really need a search method at all, as far as I can tell.
Simply use where:
#calendar.reservations.where(:search_email => 'test')
I would strongly encourage you to look at the MetaSearch GEM by Ernie Miller. It handles the kind of thing you're working on very elegantly and is quite easy to implement. I suspect that your view code would almost accomplish what the GEM needs already, and this would take care of all your model searching needs very nicely.
Take a look and see if it will solve your problem. Good luck!
Reservation.all.search doesn't work because it returns all the results as an array, while Reservation.where(..) returns an ActiveRecord object (AREL). Reservation.all actually fetches the results instead of just building the query further, which methods like where, limit etc do.

Resources