looksist 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +44 -11
- data/lib/looksist.rb +3 -1
- data/lib/looksist/hashed.rb +42 -0
- data/lib/looksist/redis_service.rb +54 -0
- data/lib/looksist/version.rb +1 -1
- data/spec/hashed_spec.rb +120 -0
- data/spec/looksist_spec.rb +14 -4
- data/spec/spec_helper.rb +2 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 453912d57be9e4fc38f6616ffdce38e38f0385bb
|
4
|
+
data.tar.gz: 8f43edb7bf0cfd70addeabfc8fd999045d253599
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b7044cc4641fa67c4842a4c0a534a9182a6a3518764927aec3087041fbbdc8916597af8b98b018e480f2ef01daf07211bbe34e974c8affaf7e850ea22377d10
|
7
|
+
data.tar.gz: fb977003fff907b27036af080f3a870b0b5a798f6ca668a63fa75b3441251bdbb9d2d93d4488543d0786b8ea351e324cd27776b8a114c0e66b4560fc08bcec60
|
data/README.md
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
#
|
1
|
+
# Looksist
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/jpsimonroy/herdis.png?branch=master)](https://travis-ci.org/jpsimonroy/herdis)
|
4
4
|
|
5
|
-
|
5
|
+
looksist (adj) - forming positive prejudices based on appearances
|
6
|
+
|
7
|
+
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!
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
9
11
|
Add this line to your application's Gemfile:
|
10
12
|
|
11
|
-
gem '
|
13
|
+
gem 'looksist'
|
12
14
|
|
13
15
|
And then execute:
|
14
16
|
|
@@ -16,16 +18,47 @@ And then execute:
|
|
16
18
|
|
17
19
|
Or install it yourself as:
|
18
20
|
|
19
|
-
$ gem install
|
21
|
+
$ gem install looksist
|
20
22
|
|
21
23
|
## Usage
|
22
24
|
|
23
|
-
|
25
|
+
* Add an initializer to configure looksist
|
26
|
+
|
27
|
+
``` ruby
|
28
|
+
Looksist.lookup_store_client ||= Redis.new(:url => (ENV['REDIS_URL'], :driver => :hiredis)
|
29
|
+
Looksist.driver = Looksist::Serializers::Her
|
30
|
+
```
|
31
|
+
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).
|
32
|
+
|
33
|
+
* Please find the sample rspec to understand the usage and internals
|
34
|
+
|
35
|
+
``` ruby
|
36
|
+
it 'should generate declarative attributes on the model with simple lookup value' do
|
37
|
+
module SimpleLookup
|
38
|
+
class Employee
|
39
|
+
include Looksist
|
40
|
+
attr_accessor :id
|
41
|
+
lookup :name, using= :id
|
42
|
+
|
43
|
+
def initialize(id)
|
44
|
+
@id = id
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
expect(Looksist.lookup_store_client).to receive(:get).with('ids/1').and_return('Employee Name')
|
50
|
+
e = SimpleLookup::Employee.new(1)
|
51
|
+
expect(e.name).to eq('Employee Name')
|
52
|
+
end
|
53
|
+
```
|
54
|
+
lookup takes the following form:
|
55
|
+
|
56
|
+
``` ruby
|
57
|
+
lookup :name, using = :employee_id # will lookup "employees/#{employee_id}" from the store
|
58
|
+
|
59
|
+
lookup :name, using = :employee_id, bucket_name="stars" # will lookup "stars/#{employee_id}" from the store
|
60
|
+
|
61
|
+
lookup [:name, :location], using = :employee_id # will lookup "stars/#{employee_id}" from the store for an object with two attributes (name, location)
|
24
62
|
|
25
|
-
|
63
|
+
```
|
26
64
|
|
27
|
-
1. Fork it ( https://github.com/[my-github-username]/herdis/fork )
|
28
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
-
5. Create a new Pull Request
|
data/lib/looksist.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'looksist/version'
|
2
|
+
require 'looksist/redis_service'
|
3
|
+
require 'looksist/hashed'
|
2
4
|
|
3
5
|
module Looksist
|
4
6
|
extend ActiveSupport::Concern
|
@@ -19,7 +21,7 @@ module Looksist
|
|
19
21
|
what.each do |method_name|
|
20
22
|
define_method(method_name) do
|
21
23
|
key = [bucket, '/', self.send(using).try(:to_s)].join('')
|
22
|
-
JSON.parse(send(:memoized, key))[method_name.to_s]
|
24
|
+
JSON.parse(send(:memoized, key) || '{}')[method_name.to_s]
|
23
25
|
end
|
24
26
|
self.lookup_attributes << method_name
|
25
27
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Looksist
|
2
|
+
module Hashed
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
# inject after: :assortment, at: 'table', using: :supplier_id, populate: :supplier_name, bucket_name: 'suppliers'
|
6
|
+
|
7
|
+
class << self;
|
8
|
+
attr_accessor :redis_service
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def inject(opts)
|
13
|
+
raise 'Incorrect usage' unless [:after, :using, :populate].all? { |e| opts.keys.include? e }
|
14
|
+
@rules ||= {}
|
15
|
+
@rules[opts[:after]] ||= []
|
16
|
+
@rules[opts[:after]] << opts
|
17
|
+
return if @rules[opts[:after]].length > 1
|
18
|
+
|
19
|
+
define_method("#{opts[:after]}_with_inject") do
|
20
|
+
hash = send("#{opts[:after]}_without_inject".to_sym)
|
21
|
+
self.class.instance_variable_get(:@rules)[opts[:after]].each do |opts|
|
22
|
+
keys = hash[opts[:at]][opts[:using]]
|
23
|
+
entity_name = entity(opts[:using])
|
24
|
+
values = Hashed.redis_service.send("#{entity_name}_for", keys)
|
25
|
+
hash[opts[:at]][opts[:populate]] = values
|
26
|
+
end
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
|
30
|
+
alias_method_chain opts[:after], :inject
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
included do |base|
|
35
|
+
base.class_attribute :rules
|
36
|
+
end
|
37
|
+
|
38
|
+
def entity(entity_id)
|
39
|
+
entity = entity_id.to_s.gsub('_id', '')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Looksist
|
2
|
+
class RedisService
|
3
|
+
attr_accessor :client, :buffer_size, :cache
|
4
|
+
|
5
|
+
def self.instance
|
6
|
+
@_instance_ ||= new
|
7
|
+
@_instance_.cache ||= {}
|
8
|
+
yield @_instance_ if block_given?
|
9
|
+
@_instance_.buffer_size ||= 50000
|
10
|
+
@_instance_
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(m, *args, &block)
|
14
|
+
if m.to_s.ends_with?("_for")
|
15
|
+
entity = m.to_s.gsub('_for', '')
|
16
|
+
args.first.is_a?(Array) ? find_all(entity, args.first) : find(entity, args.first)
|
17
|
+
else
|
18
|
+
super(m, args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find(entity, id)
|
25
|
+
key = redis_key(entity, id)
|
26
|
+
hit_or_miss(key) do
|
27
|
+
@client.get(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def find_all(entity, ids)
|
33
|
+
@client.pipelined do
|
34
|
+
ids.uniq.each do |id|
|
35
|
+
find(entity, id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
ids.each_with_object([]) { |k, acc| acc << cache[redis_key(entity, k)] }
|
39
|
+
end
|
40
|
+
|
41
|
+
def hit_or_miss(key, &block)
|
42
|
+
@cache[key] ||= lru(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def lru
|
46
|
+
@cache.shift if @cache.length >= @buffer_size
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
|
50
|
+
def redis_key(entity, id)
|
51
|
+
"#{entity.pluralize}/#{id}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/looksist/version.rb
CHANGED
data/spec/hashed_spec.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Looksist::Hashed do
|
4
|
+
before(:each) do
|
5
|
+
class MockRedis
|
6
|
+
def pipelined
|
7
|
+
yield
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
@mock = MockRedis.new
|
12
|
+
Looksist::Hashed.redis_service = Looksist::RedisService.instance do |lookup|
|
13
|
+
lookup.client = @mock
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
context 'inject ' do
|
19
|
+
it 'should inject single attribute to an existing hash' do
|
20
|
+
class HashService1
|
21
|
+
include Looksist::Hashed
|
22
|
+
|
23
|
+
def metrics
|
24
|
+
{
|
25
|
+
table: {
|
26
|
+
employee_id: [1, 2]
|
27
|
+
}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
inject after: :metrics, at: :table, using: :employee_id, populate: :employee_name
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(@mock).to receive(:get).with('employees/1').and_return('emp 1')
|
35
|
+
expect(@mock).to receive(:get).with('employees/2').and_return('emp 2')
|
36
|
+
|
37
|
+
HashService1.new.metrics.should eq({table: {
|
38
|
+
employee_id: [1, 2],
|
39
|
+
employee_name: ['emp 1', 'emp 2']
|
40
|
+
}})
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should inject multiple attribute to an existing hash' do
|
44
|
+
class HashService
|
45
|
+
include Looksist::Hashed
|
46
|
+
|
47
|
+
def metrics
|
48
|
+
{
|
49
|
+
table: {
|
50
|
+
employee_id: [5, 6],
|
51
|
+
employer_id: [3, 4]
|
52
|
+
}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
inject after: :metrics, at: :table, using: :employee_id, populate: :employee_name
|
57
|
+
inject after: :metrics, at: :table, using: :employer_id, populate: :employer_name
|
58
|
+
end
|
59
|
+
|
60
|
+
expect(@mock).to receive(:get).with('employees/5').and_return('emp 5')
|
61
|
+
expect(@mock).to receive(:get).with('employees/6').and_return('emp 6')
|
62
|
+
|
63
|
+
expect(@mock).to receive(:get).with('employers/3').and_return('empr 3')
|
64
|
+
expect(@mock).to receive(:get).with('employers/4').and_return('empr 4')
|
65
|
+
|
66
|
+
HashService.new.metrics.should eq({table: {
|
67
|
+
employee_id: [5, 6],
|
68
|
+
employer_id: [3, 4],
|
69
|
+
employee_name: ['emp 5', 'emp 6'],
|
70
|
+
employer_name: ['empr 3', 'empr 4']
|
71
|
+
}})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
context 'multiple methods and injections' do
|
77
|
+
it 'should inject multiple attribute to an existing hash' do
|
78
|
+
class HashServiceSuper
|
79
|
+
include Looksist::Hashed
|
80
|
+
|
81
|
+
def shrinkage
|
82
|
+
{
|
83
|
+
table: {
|
84
|
+
shrink_id: [1, 2]
|
85
|
+
}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def stock
|
90
|
+
{
|
91
|
+
table: {
|
92
|
+
dc_id: [7, 8]
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
inject after: :shrinkage, at: :table, using: :shrink_id, populate: :shrink_name
|
98
|
+
inject after: :stock, at: :table, using: :dc_id, populate: :dc_name
|
99
|
+
end
|
100
|
+
|
101
|
+
expect(@mock).to receive(:get).with('shrinks/1').and_return('shrink 1')
|
102
|
+
expect(@mock).to receive(:get).with('shrinks/2').and_return('shrink 2')
|
103
|
+
|
104
|
+
expect(@mock).to receive(:get).with('dcs/7').and_return('dc 7')
|
105
|
+
expect(@mock).to receive(:get).with('dcs/8').and_return('dc 8')
|
106
|
+
|
107
|
+
hash_service_super = HashServiceSuper.new
|
108
|
+
hash_service_super.shrinkage.should eq({table: {
|
109
|
+
shrink_id: [1, 2],
|
110
|
+
shrink_name: ['shrink 1', 'shrink 2']
|
111
|
+
}})
|
112
|
+
|
113
|
+
hash_service_super.stock.should eq({table: {
|
114
|
+
dc_id: [7, 8],
|
115
|
+
dc_name: ['dc 7', 'dc 8']
|
116
|
+
}})
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
data/spec/looksist_spec.rb
CHANGED
@@ -71,39 +71,49 @@ describe Looksist do
|
|
71
71
|
module SimpleLookup
|
72
72
|
class Employee
|
73
73
|
include Looksist
|
74
|
-
attr_accessor :id
|
74
|
+
attr_accessor :id, :employee_id
|
75
75
|
lookup :name, using= :id
|
76
|
+
lookup :unavailable, using= :employee_id
|
76
77
|
|
77
78
|
def initialize(id)
|
78
|
-
@id = id
|
79
|
+
@id = @employee_id = id
|
79
80
|
end
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
83
84
|
expect(Looksist.lookup_store_client).to receive(:get).with('ids/1').and_return('Employee Name')
|
85
|
+
expect(Looksist.lookup_store_client).to receive(:get).with('employees/1').and_return(nil)
|
84
86
|
e = SimpleLookup::Employee.new(1)
|
85
87
|
expect(e.name).to eq('Employee Name')
|
88
|
+
expect(e.unavailable).to be(nil)
|
86
89
|
end
|
87
90
|
|
88
91
|
it 'should generate declarative attributes on the model with object based lookup value' do
|
89
92
|
module CompositeLookup
|
90
93
|
class Employee
|
91
94
|
include Looksist
|
92
|
-
attr_accessor :id
|
95
|
+
attr_accessor :id, :employee_id
|
93
96
|
|
94
97
|
lookup [:name, :location], using=:id
|
98
|
+
lookup [:age, :sex], using=:employee_id
|
95
99
|
|
96
100
|
def initialize(id)
|
97
|
-
@id = id
|
101
|
+
@id = @employee_id = id
|
98
102
|
end
|
99
103
|
end
|
100
104
|
end
|
101
105
|
|
102
106
|
expect(Looksist.lookup_store_client).to receive(:get).with('ids/1')
|
103
107
|
.and_return({name: 'Employee Name', location: 'Chennai'}.to_json)
|
108
|
+
expect(Looksist.lookup_store_client).to receive(:get).twice.with('employees/1')
|
109
|
+
.and_return(nil)
|
104
110
|
e = CompositeLookup::Employee.new(1)
|
111
|
+
|
105
112
|
expect(e.name).to eq('Employee Name')
|
106
113
|
expect(e.location).to eq('Chennai')
|
114
|
+
|
115
|
+
expect(e.age).to be(nil)
|
116
|
+
expect(e.sex).to be(nil)
|
107
117
|
end
|
108
118
|
end
|
109
119
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: looksist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RC
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-10-
|
12
|
+
date: 2014-10-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -155,8 +155,11 @@ files:
|
|
155
155
|
- README.md
|
156
156
|
- Rakefile
|
157
157
|
- lib/looksist.rb
|
158
|
+
- lib/looksist/hashed.rb
|
159
|
+
- lib/looksist/redis_service.rb
|
158
160
|
- lib/looksist/version.rb
|
159
161
|
- looksist.gemspec
|
162
|
+
- spec/hashed_spec.rb
|
160
163
|
- spec/looksist_spec.rb
|
161
164
|
- spec/spec_helper.rb
|
162
165
|
homepage: https://github.com/jpsimonroy/herdis
|
@@ -184,5 +187,6 @@ signing_key:
|
|
184
187
|
specification_version: 4
|
185
188
|
summary: Redis backed lookup for your her models
|
186
189
|
test_files:
|
190
|
+
- spec/hashed_spec.rb
|
187
191
|
- spec/looksist_spec.rb
|
188
192
|
- spec/spec_helper.rb
|