README.md
# Looksist
[![Build Status](https://travis-ci.org/jpsimonroy/looksist.png?branch=master)](https://travis-ci.org/jpsimonroy/looksist)
[![Coverage Status](https://img.shields.io/coveralls/jpsimonroy/looksist.svg)](https://coveralls.io/r/jpsimonroy/looksist?branch=master)
[![Code Climate](https://codeclimate.com/github/jpsimonroy/looksist/badges/gpa.svg)](https://codeclimate.com/github/jpsimonroy/looksist)
[![Gem Version](https://badge.fury.io/rb/looksist.svg)](http://badge.fury.io/rb/looksist)
looksist (adj) - forming positive prejudices based on appearances
Use this gem when you have to lookup attributes from a key-value store based on another attribute as key. This supports redis out-of-the-box and it's blazing fast!
## Installation
Add this line to your application's Gemfile:
gem 'looksist'
## Supported Ruby Versions
ruby 2.1
ruby 1.9.3
jruby 1.7
## Dependencies
JsonPath - required by the gem.
ActiveSupport - external dependency, make sure to require it in your project.
## Usage
### With Object Models (Her, Active Resource or any of your choice)
* Add an initializer to configure looksist
``` ruby
Looksist.configure do |looksist|
looksist.lookup_store = Redis.new(:url => ENV['REDIS_URL'], :driver => :hiredis)
looksist.driver = Looksist::Serializers::Her
end
```
You need to specify the driver to manage the attributes. In this case, we use [HER](https://github.com/remiprev/her). You can add support for ActiveResource or ActiveRecord as needed (also refer to specs for free form usage without a driver).
* Please find the sample rspec to understand the usage and internals
``` ruby
it 'should generate declarative attributes on the model with simple lookup value' do
module SimpleLookup
class Employee
include Looksist
attr_accessor :id
lookup :name, using: :id
def initialize(id)
@id = id
end
end
end
expect(Looksist.lookup_store).to receive(:get).with('ids/1').and_return('Employee Name')
e = SimpleLookup::Employee.new(1)
expect(e.name).to eq('Employee Name')
end
```
lookup can take the following forms:
``` ruby
# will lookup "employees/#{employee_id}" from the store
lookup :name, using: :employee_id
# will lookup "stars/#{employee_id}" from the store
lookup :name, using: :employee_id, bucket_name: "stars"
# will lookup "stars/#{employee_id}" from the store
# for an object with two attributes (name, location)
lookup [:name, :location], using: :employee_id
# will lookup "stars/#{employee_id}" from the store
# for an object with two attributes (name, location) and expose name as nome
lookup [:name, :age], using: :id, as: {name: 'nome'}
```
### With Plain Hashes
#### Array Of Hashes
```ruby
it 'should inject single attribute into array of hashes' do
class HashService
include Looksist
def metrics
[
{
employee_id: 5
},
{
employer_id: 3
}
[
end
inject after: :metrics, at: '$',
using: :employee_id, populate: :employee_name
end
# Removed mock expectations, look at the tests for actuals
expect(HashService.new.metrics).to eq([{employeed_id: 5, employee_name: 'Emp 5'},{employeed_id: 3, employee_name: 'Emp 3'}])
end
end
```
#### Columnar Hashes
* First Level look ups
```ruby
it 'should inject multiple attribute to an existing hash' do
class HashService
include Looksist
def metrics
{
table: {
employee_id: [5, 6],
employer_id: [3, 4]
}
}
end
inject after: :metrics, at: :table,
using: :employee_id, populate: :employee_name
inject after: :metrics, at: :table,
using: :employer_id, populate: :employer_name
end
# Removed mock expectations, look at the tests for actuals
expect(HashService.new.metrics).to eq({table: {
employee_id: [5, 6],
employer_id: [3, 4],
employee_name: ['emp 5', 'emp 6'],
employer_name: ['empr 3', 'empr 4']
}})
end
end
```
#### Plain Hash
```ruby
it 'should inject single attribute into a plain hash' do
class FirstLevelHash
include Looksist
def metrics
{employee_id: 5}
end
inject after: :metrics, using: :employee_id, populate: :employee_name
end
# Removed mock expectations, look at the tests for actuals
expect(FirstLevelHash.new.metrics).to eq({employeed_id: 5, employee_name: 'Emp 5'})
end
end
```
* Inner Lookups using [JsonPath](https://github.com/joshbuddy/jsonpath)
```ruby
it 'should inject multiple attribute to an existing deep hash' do
class EmployeeHash
include Looksist
def metrics
{
table: {
database: {
employee_id: [15, 16],
employer_id: [13, 14]
}
}
}
end
inject after: :metrics, at: '$.table.database',
using: :employee_id, populate: :employee_name
inject after: :metrics, at: '$.table.database',
using: :employer_id, populate: :employer_name
end
# Mocks removed to keep it simple.
expect(EmployeeHash.new.metrics).to eq({table: {
database: {
employee_id: [15, 16],
employer_id: [13, 14],
employee_name: ['emp 15', 'emp 16'],
employer_name: ['empr 13', 'empr 14']
}
}})
end
```
* Inner Lookups using [JsonPath](https://github.com/joshbuddy/jsonpath)
```ruby
it 'should inject multiple attribute to an existing deep hash for class methods' do
class EmployeeHash
include Looksist
def self.metrics
{
table: {
database: {
employee_id: [15, 16],
employer_id: [13, 14]
}
}
}
end
inject after: :metrics, at: '$.table.database',
using: :employee_id, populate: :employee_name
end
# Mocks removed to keep it simple.
expect(EmployeeHash.metrics).to eq({table: {
database: {
employee_id: [15, 16],
employer_id: [13, 14],
employee_name: ['emp 15', 'emp 16'],
employer_name: ['empr 13', 'empr 14']
}
}})
end
```
#### Non Columnar Hashes
```ruby
it 'should be capable to deep lookup and inject' do
class Menu
include Looksist
def metrics
{
table: {
menu: [
{
item_id: 1
},
{
item_id: 2
}
]
}
}
end
inject after: :metrics, at: '$.table.menu',
using: :item_id, populate: :item_name
end
expect(Menu.new.metrics).to eq({
table: {
menu: [{
item_id: 1,
item_name: 'Idly'
},
{
item_id: 2,
item_name: 'Pongal'
}]
}
})
end
```
### Controlling the L2 cache
Looksist has support for an in memory L2 cache which it uses to optimize redis lookups. To disable L2 cache initialize looksists as below.
* Note that in no L2 cache mode, all lookups would go to redis and the gem would not optimize redundant lookups.
* Hash based lookups would still see optimizations which come from performing unique on keys when injecting values.
```ruby
Looksist.configure do |looksist|
looksist.lookup_store = Redis.new(:url => ENV['REDIS_URL'], :driver => :hiredis)
looksist.driver = Looksist::Serializers::Her
looksist.l2_cache = :no_cache
end
```