HashedRecord
HashedRecord allow to filter in-memory records with ActiveRecord like interface.
example:
We have users with countries we group them into hash and then access by country code as:
users = [
{ username: 'bob', country: 'GB' },
{ username: 'alise', country: 'UA' }
]
hash = users.group_by {|r| r[:country] }
hash['GB'] # => [{:username=>"bob", :country=>"GB"}]
With HashedRecord
:
users = HashedRecord.new([
{ username: 'bob', country: 'GB', group_id: 1 },
{ username: 'alise', country: 'UA', group_id: 1 },
{ username: 'serg', country: 'UA', group_id: 2 }
])
users.where(country: 'GB') # => [{:username=>"bob", :country=>"GB", :group_id=>1}]
It supports multiple filters:
users.where(country: 'UA', group_id: 2) # => [{:username=>"serg", :country=>"UA", :group_id=>2}]
It supports array of ids as params:
users.where(country: 'UA', group_id: [1,2]) # => [{:username=>"alise", :country=>"UA", :group_id=>1}, {:username=>"serg", :country=>"UA", :group_id=>2}]
It supports negative filters:
users.not(country: 'UA') # => [{:username=>"bob", :country=>"GB", :group_id=>1}]
It supports chained filters:
users.where(group_id: 1).not(country: 'UA') # => [{:username=>"bob", :country=>"GB", :group_id=>1}]
That allow to make intersections as:
orders = HashedRecord.new([
{ name: 'ball', user_id: 1, category_id: 3 },
{ name: 'rocket', user_id: 1, category_id: 3 }
])
users = HashedRecord.new([
{ id: 1, username: 'bob', country: 'GB' },
{ id: 2, username: 'alise', country: 'UA'}
])
user_ids = orders.where(category_id: 3).map{ |r| r[:user_id] }
users.where(id: user_ids) # => [{:id=>1, :username=>"bob", :country=>"GB"}]
And difference as:
orders = HashedRecord.new([
{ name: 'ball', user_id: 2, category_id: 3 },
{ name: 'rocket', user_id: 2, category_id: 3 }
])
users = HashedRecord.new([
{ id: 1, username: 'bob', country: 'GB' },
{ id: 2, username: 'alise', country: 'UA'}
])
user_ids = orders.where(category_id: 3).map{ |r| r[:user_id] }
users.not(id: user_ids) # => [{:id=>2, :username=>"alise", :country=>"UA"}]
Records data types
It works hashes with string as key:
users = HashedRecord.new([
{ 'username' => 'bob', 'country' => 'GB' },
{ 'username' => 'alise', 'country' => 'UA' }
])
users.where(country: 'GB') # => [{"username"=>"bob", "country"=>"GB"}]
It works with objects:
users = HashedRecord.new([
OpenStruct.new(username: 'bob', country: 'GB'),
OpenStruct.new(username: 'alise', country: 'UA')
])
users.where(country: 'GB') # => [#<OpenStruct username="bob", country="GB">, #<OpenStruct username="alise", country="UA">]
It allow to specify custom access method:
users = HashedRecord.new([
{ attributes: { username: 'bob', country: 'GB' } },
{ attributes: { username: 'alise', country: 'UA' } }
], access_method: ->(record, key) { record[:attributes].send(:[], key) })
users.where(country: 'GB') # => [{:attributes=>{:username=>"bob", :country=>"GB"}}]
Performance
Under the hood HashedRecord use ruby hashes, and that makes record access as fast as O(1)
Activerecord and Enumerable
You can extend Enumerable as:
module Enumerable
def to_hashed
HashedRecord.new(to_a)
end
end
That allow to filter as:
users = [
{ 'username' => 'bob', 'country' => 'GB' },
{ 'username' => 'alise', 'country' => 'UA' }
]
users.to_hashed.where(username: 'bob') # => [{:username=>"bob", :country=>"GB"}]
And handle activerecord relation as:
Users.where(country: 'GB').to_hashed.where(group_id: [...])
Install
gem install hashedrecord
or in your Gemfile
gem 'hashedrecord'