Active Record like Search Results

29th August 2013

Active Records output may look like an array but its actually a special class that behaves like an array but has a lot of extensions attached to it.

For Adauth I want to provide results in something more than an array with the ability to order etc… but not over complicate things for the end user (if you just want an array you can still get it). So how do we make something like AR?

So straight away lets create a new class that inherits from Array and to begin with it needs no extra methods because can take an array and obviosuly that will return our new object acting as an Array.

This new class would look like this

1class SearchResults < Array
Language ruby

That now means that you can create your new class quickly from array returns by calling"foo")) instead of just SomeThing.query("foo").

Now that we have our class lets start using it! First and easiest functionality is limit(x) which is pretty simple 1 line function

1def limit(x)
2 return self[0..(x-1)]
Language ruby

This simply returns a new instance of self that is limited to the range of 0 to the supplied value.

The next function to bring to the table is order which should take a couple of options e.g. order(:name, :asc) which should look something like this

1def order(field, direction)
2 case direction
3 when :asc
4 return sort! { |x, y| x.send(field) <=> y.send(field) }
5 when :desc
6 return order(field, :asc).reverse!
7 else
8 raise "Invalid Order Provided, please use :asc or :desc"
9 end
Language ruby

This assumes that your search results are objects with the data retrievable via methods but you can sort your objects how ever you want as long as they match what your user is asking for. The advantage to this setup is that your code becomes very “englishy” and is very easy to read.

Taking my example from earlier of"foo")) you can now:"foo")).limit(3)"foo")).order(:age, :desc)"foo")).order(:name, :asc).limit(2)
Language ruby

Things look even nicer if your gem returns its data using your search results class as you wont need the .new so it looks like this:

1YourGem.some_dataset.order(:name, :asc)
Language ruby

The rest of the Array methods are still there and will function exactly the same.

Be warned, any none destructive methods (no !) will return a new Array not a new SearchResults

The finished class would look like this

1class SearchResults < Array
2 def limit(x)
3 return self[0..(x-1)]
4 end
6 def order(field, direction)
7 case direction
8 when :asc
9 return sort! { |x, y| x.send(field) <=> y.send(field) }
10 when :desc
11 return order(field, :asc).reverse!
12 else
13 raise "Invalid Order Provided, please use :asc or :desc"
14 end
15 end
Language ruby

Hardly a huge class is it? This is a little rough around the edges but it serves its purpose and when combined with AR like search methods will be incredibly easy to read and use.