Forum: Ruby on Rails More dynamic way? Search Pagination

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
5565e460a42fbe5669b458b2753f8d34?d=identicon&s=25 codeslush (Guest)
on 2005-11-12 12:49
(Received via mailing list)
All,

I'm new to ruby and to rails!  :-)   This will give you all a chance to
tear my code apart!  Feel free to offer better techniques as necessary!

I have a need to implement somewhat dynamic search across several
different entities.

In one of my controllers, I have an action called search.  I have some
code working to do a search based on parameters from a form submit.
Now, I want to make the search routine available to all controllers.
I'm trying to figure out if I can dynamically name variables in ruby or
not based on input args to a function.

Here is the code I have in the controller:

   @whereClause = ""
   @params[:price_quote].each { | key, value |
     if value.length > 0
       @whereClause = @whereClause.length == 0 ? "#{key} = #{value}" :
@whereClause << " and #{key} = #{value}"
     end
   }

   if @whereClause.length > 0 then
        @price_quote_pages, @price_quotes = paginate :price_quote,
:per_page => 10, :conditions => @whereClause
  else
        @price_quote_pages, @price_quotes = paginate :price_quote,
:per_page => 10
  end

I would like to have a function called "getSearchResults" or something
similar where I pass in the name of the controller and the param hash.
Based on the input args, it would find the proper hash and create the
page objects with the controller name.

Any suggestions?

Michael
A4b45b0467e28925b5103b50f7d39e5d?d=identicon&s=25 beattie.mark (Guest)
on 2005-11-12 12:49
(Received via mailing list)
On Friday 11 November 2005 7:27 am, Michael wrote:
> I would like to have a function called "getSearchResults" or something
> similar where I pass in the name of the controller and the param hash.
> Based on the input args, it would find the proper hash and create the page
> objects with the controller name.
>
> Any suggestions?

Hi Michael,

I'm quite new to Rails myself, but I'll try to help.

Start by putting a function in app/controllers/application.rb:

	private

	def dynamic_search(
		options = {
			:controller_name => "default_controller",
			:search_params => {}
		})
		controller_name = options[:controller_name]
		search_params = options[:search_params]

		# (your search code here)

	end

Then in any of your app's controllers you can call it using:

@results = dynamic_search( :controller_name =>
"some_controller", :search_params => { :key1 => "value1", :key2 =>
"value2",
etc})

Using this technique, you could omit either of the parameters passed to
the
function by replacing "default_controller" with the name of, well, your
default controller, and adding code to your search routine to handle the
parameters. Here's a hint:

	for column in Price_quote.content_columns
		...
	end

Will let you iterate through the column names of the db table.

Good luck. (The more experienced of you please correct me where I'm
wrong)
5565e460a42fbe5669b458b2753f8d34?d=identicon&s=25 codeslush (Guest)
on 2005-11-12 12:49
(Received via mailing list)
Mark,

Thank you for the input.

I already have the column names and values to search on - they were in
the params hash from my original sample. I get them when the user
submits the search form.


@params[:price_quote].each { | key, value | ....



What I really would like to know how to do, though, is somehow
dynamically name the objects.  For example, If I tell the function that
this is for the price_quote controller, then I want it to create the
proper pagination objects for me.  Taking the sample from below, I want
to be able to name "@price_quote_pages" based on the input of the
function.  It could be "@lead_pages", "@activity_pages", etc...



if @whereClause.length > 0 then

@price_quote_pages, @price_quotes = paginate :price_quote, :per_page =>
10, :conditions => @whereClause



...



The views rely on the name of these pagination objects to navigate
forward and backward through the records.



All in all, this search actually sucks pretty badly!  It isn't flexible
at all.  I had a bug in my original post whereby I didn't surround the
param values with single quotes (worked great for numeric fields
though)..hahaha.  I just need something basic for simple search on
multiple fields without having to hard code the names (e.g.
ActiveRecord.find_all_by...and....and...) and get the results into a
pagination object!



Thanks,



Michael




Mark Beattie <beattie.mark@gmail.com> wrote:
On Friday 11 November 2005 7:27 am, Michael wrote:
> I would like to have a function called "getSearchResults" or something
> similar where I pass in the name of the controller and the param hash.
> Based on the input args, it would find the proper hash and create the page
> objects with the controller name.
>
> Any suggestions?

Hi Michael,

I'm quite new to Rails myself, but I'll try to help.

Start by putting a function in app/controllers/application.rb:

private

def dynamic_search(
options = {
:controller_name => "default_controller",
:search_params => {}
})
controller_name = options[:controller_name]
search_params = options[:search_params]

# (your search code here)

end

Then in any of your app's controllers you can call it using:

@results = dynamic_search( :controller_name =>
"some_controller", :search_params => { :key1 => "value1", :key2 =>
"value2",
etc})

Using this technique, you could omit either of the parameters passed to
the
function by replacing "default_controller" with the name of, well, your
default controller, and adding code to your search routine to handle the
parameters. Here's a hint:

for column in Price_quote.content_columns
...
end

Will let you iterate through the column names of the db table.

Good luck. (The more experienced of you please correct me where I'm
wrong)
A4b45b0467e28925b5103b50f7d39e5d?d=identicon&s=25 beattie.mark (Guest)
on 2005-11-12 12:49
(Received via mailing list)
On Friday 11 November 2005 10:38 am, Michael wrote:
> What I really would like to know how to do, though, is somehow dynamically
> name the objects.  For example, If I tell the function that this is for the
> price_quote controller, then I want it to create the proper pagination
> objects for me.  Taking the sample from below, I want to be able to name
> "@price_quote_pages" based on the input of the function.  It could be
> "@lead_pages", "@activity_pages", etc...

I don't think the controller is determined by the paginator. I'm
thinking that
you can pass whatever controller you want to the links generated from
the
paginator in your view:

<%= link_to 'Previous page', {:controller => @whatever,  :page =>
@price_quote_pages.current.previous, :search => @params['search'],
:order_by
=> @params['order_by'], :order_dir => @params['order_dir'] } if
@price_quote_pages.current.previous %>

and

<%= link_to 'Next page', {:controller => @whatever,  :page =>
@price_quote_pages.current.next, :search => @params['search'], :order_by
=>
@params['order_by'], :order_dir => @params['order_dir'] } if
@price_quote_pages.current.next %>

Check out the docs for the link_to helper:

http://api.rubyonrails.com/classes/ActionView/Help...

link_to probably defaults to the current controller unless told
otherwise.
Cb33b32bd5d675db7be148542976e510?d=identicon&s=25 sam (Guest)
on 2005-11-22 06:34
(Received via mailing list)
Hi Michael,

I'm also fairly new to rails, and I have been looking at this same
issue.  With some help from the people on rubyonrails irc I put together
the following in application.rb

  def search
    conditions = "0"
    params.delete("controller")  # is there a better way to extract
these terms???
    params.delete("action")
    params.delete("page")
    params.each {|key, value| conditions+= " OR #{key} LIKE
'%#{value}%'" }
    @name = self.controller_name()
    @item_pages, @items = paginate @name.to_sym, :per_page => 5,
:conditions => conditions
    @model = Kernel.const_get(@name.camelize)
    render :layout => 'mylayout'
  end

and then I drop the following into each view as search.rhtml

<% for item in @items %>
<div id="item<%= item.id %>" class="orangebox">
<% for column in @model.content_columns %>
<%=h item.send(column.name) %><br>
<%end%>
    <%= link_to 'Show', :action => 'show', :id => item %>
    <%= link_to 'Edit', :action => 'edit', :id => item %>
    <%= link_to_remote "Remove",
    :url=>{
        :controller=>"items",
        :action=>"destroy",
        :id=>item},
    :loading=>"status('item#{item.id}')",
    :complete=>"new Effect.Fade('item#{item.id}')"
%>
</div>
<% end %>

<%= pagination_links(@item_pages, {:params=>params}) %>
<br />

and now every one of my controllers that has an associated model name
(e.g. recipes_controller and recipe) has search available, e.g.

http://rails/recipes/search?title=trifle

I wonder if this meets all your needs.

CHEERS> SAM
58c44a4a506d878f9a112f1d7b7cb87e?d=identicon&s=25 jeremyevans0 (Guest)
on 2005-11-22 18:51
(Received via mailing list)
On 11/21/05, Sam Joseph <sam@neurogrid.com> wrote:
> I'm also fairly new to rails, and I have been looking at this same
> issue.  With some help from the people on rubyonrails irc I put together
> the following in application.rb

I really hope nobody on IRC actually told you to use the code below.
While the method they gave you will work it has an obvious SQL
injection vulnerability.

> :conditions => conditions
>     @model = Kernel.const_get(@name.camelize)
>     render :layout => 'mylayout'
>   end

You should not be trusting the data given in params.  You should be
checking key against a list of known good keys, and using ? as
placeholders for values.  Maybe something like:

def search
  valid_keys = %w'foo bar baz'
  conditions = ['0']
  valid_keys.each do |key|
    if params.include?(key)
      conditions[0] << " OR #{key} LIKE ?"
      conditions << "%#{params[key]}%"
    end
  end
  @name = self.controller_name()
  @item_pages, @items = paginate @name.to_sym, :per_page => 5,
 :conditions => conditions
  @model = Kernel.const_get(@name.camelize)
  render :layout => 'mylayout'
end

That's untested, but it shouldn't be vulnerable.

You should never, ever, under any circumstances, trust data that is in
params.  You should be checking, filtering, and/or scrubbing all
client data before using it in your SQL queries (protection against
SQL injection) or displaying it on your page (protection against cross
site scripting).  Rails makes SQL injection protection easy, by
submitting an array for conditions using ? as placeholders for
variables.  I haven't read the AWDR book, but I'm guessing (hoping)
there is a section on this.
This topic is locked and can not be replied to.