Does Rails need more useful URL helpers?

Similar topic to my last post, but a different brand of methods. In
particular, the url_for and link_to methods have come to bother me at
times. I will mention one instance in particular:

I am designing a high-end administrative interface. This interface
supports paging, sorting and limiting to specific field values. Thus I
have params named page, sort and limit.

The lack of elegance I see comes when I make new links or add new
parameters. To keep the administrator on the same page time and time
again, I have to add “:page => params[:page]” to every link in my
scaffold’s edit.rhtml, list.rhtml and new.rhtml as well as to my helpers
that generate paging, sorting and limiting links. As I add more
parameters, the link_to commands in these files grow more and more
unreadable and the number of places I have to explicitly specify every
parameter in the query string increases.

I attempted to use :overwrite_params, but then all of a sudden link_to
does not seem to recognize my routes. It starts dumping URL-defined
params into the query string.

When I read about link_to, the description said that it intuitively
understood the hierarchy of parameters. If the :action changed, the :id
was nulled. If the :controller changed, :action and :id were nulled. And
so forth.

The problem is once you get into params in the query string such as
params[:page]. It seems to me that a link_to specifying only :sort =>
sort should not overwrite :page or :limit. Params in the query string
should act as brothers. No one param should be the parent of another;
setting one should not unset the others. They are not in a hierarchy.
They merely exist side-by-side. Changing the way rails nulls query
string parameters would keep those params out of my paging, sorting and
limiting helpers, which I feel should ideally not have to know about
each other. And if there is some reason this should not be the default,
perhaps it should at least be an option. Or perhaps :overwrite_params
needs to be fixed. Or perhaps I am using it incorrectly?

This also wouldn’t address every repeated instance of “:page =>
params[:page]” in my views. Changing :action still needs to null query
string parameters. Or does it? Should query string parameters be
considered children of even :action or :controller? Is :page necessarily
subordinate to :action => ‘list’? To me it seems :page should be
subordinate to the :controller but not the action. Perhaps some method
of explicitly specifying query string parameter hierarchies is in order.

That’s definitely a very nice solution! Obviously it breaks down when
users do not have cookies enabled. In the case of an administrative
panel, we are free to require our customer to enable cookies. For the
similar public environment to come, this solution would become less
desirable.

One thought, though: why use the params array at all? Why not just leave
the variables in the session? The default paginator looks for its page
number in the params variable, but I believe you can roll-your-own
Paginator and tell it to use the session variable:

@element_count = Element.count (or count_by_sql or parent.element.count
as needed)
@element_pages = Paginator.new self, @element_count, 20,
@session[self.name][:page]

I haven’t actually tried this, though I assume it’d work fine. I had to
generate my own paginators for this same administrative panel because I
designed it to operate both with and without a limiting id at each
descent into the hierarchy. This complicated matters and forced me to
use my own paginator, but the benefits were definitely worth it, and
doing so is hardly difficult.

Using a session for paging is usually a bad idea.

  1. Multiple windows result in unexpected results.

  2. Bookmarks don’t reference the right page and GETS aren’t idempotent.

If you want reliable paging that always works you need to include
everything in the URL. If you add sorting and filtering/limiting, make
sure to mark those links nofollow and/or use javascript, because Google
will kill you when it indexes every possible sort and filter
combination.

Hello Stephen,

2006/4/25, Stephen F. [email protected]:

I am designing a high-end administrative interface. This interface
supports paging, sorting and limiting to specific field values. Thus I
have params named page, sort and limit.

I see your point. The way I coded around that is to store the page,
sort and limit in the session. If the incoming request has those
parameters, I overwrite the session, else I use the session’s values.
Here’s how I do it:

class UsersController < ApplicationController
before_filter :process_filters

def index
@users, @user_pages = paginate(:users, :limit => @limit, :page =>
@page, :order => @sort)
end

def process_filters
# That generates a namespace for the common parameters
session[self.name] ||= Hash.new

@page = params[:page] || session[self.name][:page] || 1
session[self.name][:page] = @page

# Same kind of code for limit and order/sort

end
end

Hope that helps !

Here’s another approach…

create a model to hold the page state.

The way I did this goes something like this…

user has_many :page_views
PageView belongs_to :user

page_view
id :integer
page_url :string
setting :text

The ‘setting’ attribute just holds a yaml encoding of the params hash.
the ‘page_url’ is the uri for the request without the params
Then in a before filter, you can load the previous params and overwrite
the bits that changed.
in an ‘after’ filter, you write the modified params back to the table.

The nice part here is that it remembers settings for each user, it
doesn’t forget the settings when it logs off, and it keeps your session
from getting cluttered.

On Wednesday, April 26, 2006, at 5:33 PM, Guest wrote:

combination.


Posted via http://www.ruby-forum.com/.


Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails

_Kevin

Kevin O. wrote:

Here’s another approach…

create a model to hold the page state.

user has_many :page_views
PageView belongs_to :user

page_view
id :integer
page_url :string
setting :text

The default_url_options method I mentioned last failed terribly.
Clashing variable names and an inability to define where they should
expire caused params to persist longer than they should have. My company
has decided to lean towards params lasting too short than too long and I
tend to agree with them.

The above version also seems quite excellent, but it won’t work when a
view is not tied to a user object, as with the Admin panels in which I
was trying to implement sorting.

After more months of Rails use and experience, I continue to think param
overriding needs a review. Where :controller, :action and :id do not
change, link_to’s should retain the current URL’s params except where
explicitly and individually overridden. It makes sense for :id to be
nulled when :action is set because of the hierarchy of the route. But
params in not specified in a route should not be overridden in the same
manner. They should all be treated as siblings at the same hierarchical
depth. They should only be nulled when a param from the route changes,
not when any of their siblings changes. To do otherwise continues to
seem unintuitive to me.

While searching for the solution to another problem, I came across the
following URL: Sessions w/o cookies still broken - Rails - Ruby-Forum. Based on the
example code given in this article, I implemented the following method
in my ApplicationController class:

def default_url_options(options)
{
:page => params[:page],
:sort => params[:sort],
:limit => params[:limit]
}
end

And it works like a charm! I just didn’t know the method existed. Next
time I will examine the base classes like ActionController::Base more
carefully.

I wonder if a review of how parameters override other parameters is
still in order or whether the above method solves all the needed
problems? It effectively provides a single location for the developer to
specify all parameters that should remain from one link to another
regardless of any other parameter modifications. This can even be
specified on a per-controller basis.