ActiveRecord Scopes can be nice to use and a useful way to think about data. Chaining methods together in logical filters to get the desired results without having to think about the structure of the data. ActiveRecord is tided to doing database queries, but to do that you must have a database. Have you ever wanted use this kind of syntax on a set of custom in-memory objects or an array of hashes? Now let’s not let ActiveRecord have all the fun we can do the same thing in plain old Ruby and I’ll show you how.

Here I’ve got an ActiveRecord Person, notice that it is singular because in ActiveRecord a Class represent a collection of people and a single person. We can’t very well just create new collections because the source is a single database instance.

class Person < ActiveRecord :: Base scope :minors , -> { ... } scope :adults , -> { ... } scope :tall , -> { ... } scope :short , -> { ... } end Person . tall . minors # => <Relations ...>

In our Ruby world why not have many different collections instance we are not limited by a single database. So in this example I have made a distinction between a collection of People and a single person.

The People class, defined below, is going to represent the definition of collection of persons. Here I stub out some methods that I will define later just to get the shape of what the classes interface is going to look like.

class People def minors ; end def adults ; end def tall ; end def short ; end end

Here is a person with the attributes of age and height. I set the #to_s method so that when the objects are sent to #puts it displays more than just the object id, but lists out the attributes.

class Person attr_reader :height , :age def initialize ( age :, height :) @age = age @height = height end def to_s "<#Person age: #{ age } , height: #{ height } >" end end

I create the initializer so that it sets the collection array to an attr_reader :to_a . This will allow conversion of this object into a simple array if need. First let’s start with the create method. For this to have chainable methods ie. People#adults#tall each method call needs to return a new instance of a People collection. Here I’ve filled out the operation needed to return the desired result using #reject and #select on the Array of Persons.

class People attr_reader :to_a def initialize ( collection = []) @to_a = collection end def minors create to_a . select { | person | person . age < 19 } end def adults create to_a . reject { | person | minors . to_a . include? ( person ) } end def tall create to_a . select { | person | person . height >= 5 } end private def create ( collection ) self . class . new ( collection ) end end

Here I’ve created an array that will be used as the input into our new People class.

person_array = [ Person . new ( age: 10 , height: 4 ), Person . new ( age: 19 , height: 4 ), Person . new ( age: 45 , height: 6 ), Person . new ( age: 16 , height: 5 ), Person . new ( age: 32 , height: 5 ), ]

tall_people = People . new ( person_array ). tall puts tall_people . to_a # => [<#Person age: 45, height: 6> # <#Person age: 16, height: 5> # <#Person age: 32, height: 5>] puts tall_people . minors . to_a #=> [<#Person age: 16, height: 5>]

Check out the ActiveEnumerable Gem for a DSL to do the same thing and a whole lot more ActiveRecord query like methods on custom Ruby Collections.