0.02
No commit activity in last 3 years
No release in over 3 years
QueryableArray is intended to store a group of objects which share the same attributes allowing them to be searched with a simplified DSL
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0
>= 0
>= 0

Runtime

 Project Readme

A QueryableArray inherits from Array and is intended to store a group of objects which share the same attributes allowing them to be searched. It overrides [], find_all and method_missing to provide a simplified DSL for looking up objects by querying their attributes.

View the full documentation over at rubydoc.info.

Installation¶ ↑

gem install queryable_array

Requirements¶ ↑

Ruby 1.9+

Usage¶ ↑

Basic¶ ↑

Initialize the QueryableArray with a collection of objects e.g. Page objects from a JSON response or database query (although you should probably restrict database queries with WHERE conditions instead if you have the opportunity)

pages = QueryableArray.new Page.all

The pages object can then be queried by passing a search hash to the [] method

pages[uri: '/']                    # => #<Page @uri='/' @name='Home'>
pages[name: 'About']               # => #<Page @uri='/about' @name='About'>
pages[uri: '/', name: 'Home']      # => #<Page @uri='/' @name='Home'>
pages[uri: '/', name: 'Mismatch']  # => nil

Notice that it only returns the first matching object or nil if one is not found. If you’d like to find all matching objects, simply wrap your search hash in an array

pages[[published: true]]  # => [#<Page @uri='/' @name='Home' @published=true>, #<Page @uri='/about' @name='About' @published=true>, ...]
pages[[uri: '/missing']]  # => []

Attributes may also be searched by regular expressions

pages[name: /home/i]   # => #<Page @uri='/' @name='Home'>
pages[[uri: /users/]]  # => [#<Page @uri='/users/bob' @name='Bob'>, #<Page @uri='/users/steve' @name='Steve'>, ...]

The methods find_by and find_all behave as aliases for [search_hash] and [[search_hash]] respectively

pages.find_by(name: 'Home')     # => #<Page @uri='/' @name='Home'>
pages.find_by(name: 'Missing')  # => nil
pages.find_all(uri: /users/)    # => [#<Page @uri='/users/bob' @name='Bob'>, #<Page @uri='/users/steve' @name='Steve'>, ...]

The existing block form for those methods work as well

pages.find_all { |page| page.uri =~ /users/ }  # => [#<Page @uri='/users/bob' @name='Bob'>, #<Page @uri='/users/steve' @name='Steve'>, ...]

A Proc object may be passed to [] as well

pages[uri: proc { |uri| uri.split('/').size > 1 }]  # => #<Page @uri='/users/bob' @name='Bob'>
pages[proc { |page| page.uri == '/' }]              # => #<Page @uri='/' @name='Home'>

Lookups by index or ranges still behave exactly as they do in regular Array objects

pages[0]     # => #<Page @uri='/' @name='Home'>
pages[-1]    # => #<Page @uri='/zebras' @name='Zebras'>
pages[99]    # => nil
pages[0..1]  # => [#<Page @uri='/' @name='Home'>, #<Page @uri='/about' @name='About'>]

Default finders¶ ↑

A QueryableArray object can be initialized with a default_finder to make lookups even simpler

pages = QueryableArray.new Page.all, :uri

Now the pages object can be searched easily by uri

pages['/']         # => #<Page @uri='/' @name='Home'>
pages['/about']    # => #<Page @uri='/about' @name='About'>
pages['/missing']  # => nil

You can even specify multiple default_finders

pages = QueryableArray.new Page.all, [:uri, :name]

pages['/about']  # => #<Page @uri='/about' @name='About'>
pages['About']   # => #<Page @uri='/about' @name='About'>
pages[/home/i]   # => #<Page @uri='/' @name='Home'>

Wrapping your search inside an array still returns all matches

pages[[/users/]]  # => [#<Page @uri='/users/bob' @name='Bob'>, #<Page @uri='/users/steve' @name='Steve'>, ...]

Dynamic attribute-based finders¶ ↑

QueryableArray#method_missing allows you to lookup objects using a notation like the ActiveRecord dynamic finders

pages.find_by_uri('/')                   # => #<Page @uri='/' @name='Home'>
pages.find_by_uri_and_name('/', 'Home')  # => #<Page @uri='/' @name='Home'>
pages.find_by_uri('/missing')            # => nil

pages.find_all_by_uri('/')               # => [#<Page @uri='/' @name='Home'>]
pages.find_all_by_uri(/users/)           # => [#<Page @uri='/users/bob' @name='Bob'>, #<Page @uri='/users/steve' @name='Steve'>, ...]

Dot notation finders¶ ↑

If any default_finders are defined you may even use dot notation to lookup objects by those attributes

pages = QueryableArray.new Page.all, :name

pages.sitemap               # => #<Page @uri='/sitemap' @name='Sitemap'>
pages.missing               # => NoMethodError
QueryableArray.new.missing  # => NoMethodError

Calling pages.sitemap behaves the same as pages[/sitemap/i]

To perform a case-sensitive search, simply append a ! to the end of your method call e.g. pages.sitemap! which calls pages['sitemap']

You may also query to see if a match exists by appending a ? to your search

pages.sitemap?  # => true
pages.missing?  # => false

Composable¶ ↑

Functionality for QueryableArray has been separated out into individual modules containing their own features which allows you to create your own objects and only include the features you care about

  • QueryableArray::DefaultFinder - Allows objects to be searched by default_finders thru []

  • QueryableArray::DotNotation - Allows objects to be searched using dot notation thru method_missing which behaves like an alias to QueryableArray::DefaultFinder#[]

  • QueryableArray::DynamicFinder - Allows objects to be searched by dynamic finders thru method_missing similar to the ActiveRecord dynamic attribute-based finders e.g. find_by_email or find_all_by_last_name

  • QueryableArray::Queryable - Allows find_by and find_all to accept search hashes which are converted into Proc searches and passed as the block arguments for find and find_all respectively

  • QueryableArray::Shorthand - Makes [search_hash] and [[search_hash]] behave as an alias for find_by and find_all respectively

Try making your own classes with them

class Collection < Array
  include QueryableArray::Queryable
end

pages = Collection.new Page.all
pages.find_all(published: true)  # => [#<Page @uri='/' @published=true>, #<Page @uri='/about' @published=true>]

Real world example¶ ↑

Try using it inside of your templates:

<div class="posts">
  <%- posts[[published: true]].each do |post| -%>
    <div class="post">
      <h2><a href="<%= post.url -%>"><%= post.title -%></a></h2>
      <div class="excerpt"><%= post.excerpt -%></div>
      <a href="<%= post.url -%>#comments"><%= post.comments[[approved: true]].size -%> comments</a>
    </div>
  <%- end -%>
</div>

Testing¶ ↑

bundle exec rake