offline_lookup 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/lib/offline_lookup.rb +2 -2
- data/lib/offline_lookup/active_record.rb +4 -41
- data/lib/offline_lookup/core.rb +44 -0
- data/lib/offline_lookup/dynamic_module_builder.rb +115 -0
- metadata +4 -4
- data/lib/offline_lookup/base.rb +0 -9
- data/lib/offline_lookup/builder.rb +0 -109
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9df8ec8a2ac9076d35a4965141e8f7ddd64ef41e
|
4
|
+
data.tar.gz: cf5881b25c71c21378b012024dd6f9615264f5bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0183aafc5868444dfe5d8452f43c65e02cd0ceee4abc50b93ac87ad9526d6ac376046576d83a4270e1fbcb9c68c9f6bc7aa34f783c30ad940db6f3e1cb9baa3b
|
7
|
+
data.tar.gz: a3ba577eb3860d3b906fe87984d003f4004b1c8495978695fba245f4637e8831b120e75df38a510154f40375d20282d6e881c95315781bf83660e9ed4c9fb68e
|
data/README.md
CHANGED
@@ -31,6 +31,11 @@ Add `lookup` method to allow lookup by key'd name without risking bad / reserved
|
|
31
31
|
|
32
32
|
You can now specify multiple columns for lookup! The values are by default joined with a " " (note this translates to "_" for method names). You can configure this delimiter and what to do with `nil` values.
|
33
33
|
|
34
|
+
v1.1.0
|
35
|
+
I forget. Probably a big bugfix.
|
36
|
+
|
37
|
+
v1.2.0
|
38
|
+
Definitely a big bugfix. The core OfflineLookup module was getting included in all of ActiveRecord, which mistakenly included callback methods that depended on the existence of `self.offline_lookup_options`. This has been fixed and this behavior is now only exhibited on models that explicitly call `use_offline_lookup`
|
34
39
|
|
35
40
|
|
36
41
|
## How To Use It
|
data/lib/offline_lookup.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# TODO: support scope arg in use_offline_lookup (partial index)
|
3
3
|
|
4
4
|
require 'offline_lookup/active_record.rb'
|
5
|
-
require 'offline_lookup/
|
6
|
-
require 'offline_lookup/
|
5
|
+
require 'offline_lookup/core.rb'
|
6
|
+
require 'offline_lookup/dynamic_module_builder.rb'
|
7
7
|
|
8
8
|
ActiveRecord::Base.include OfflineLookup::ActiveRecord
|
@@ -2,27 +2,9 @@ module OfflineLookup
|
|
2
2
|
module ActiveRecord
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
included do
|
6
|
-
after_create :get_offline_lookup_values, :add_offline_lookup
|
7
|
-
after_destroy :get_offline_lookup_values
|
8
|
-
end
|
9
|
-
|
10
|
-
def get_offline_lookup_values
|
11
|
-
self.class.get_offline_lookup_values
|
12
|
-
end
|
13
|
-
|
14
|
-
def offline_lookup_value
|
15
|
-
self.class.offline_lookup_value(*offline_lookup_options[:fields].map{|f| self.attributes[f.to_s]})
|
16
|
-
end
|
17
|
-
|
18
|
-
def add_offline_lookup
|
19
|
-
builder = OfflineLookup::Builder.new(self.class, self.offline_lookup_options)
|
20
|
-
builder.add_lookup(self.attributes[builder.key], offline_lookup_value)
|
21
|
-
end
|
22
|
-
|
23
5
|
module ClassMethods
|
24
6
|
def use_offline_lookup(*fields, key: "id", identity_methods: false, lookup_methods: false, compact: false, delimiter: " ", name: fields.join(delimiter), transform: nil)
|
25
|
-
class_attribute :
|
7
|
+
class_attribute :offline_lookup_options
|
26
8
|
self.offline_lookup_options = {
|
27
9
|
fields: fields.map(&:to_s),
|
28
10
|
key: key.to_s,
|
@@ -32,29 +14,10 @@ module OfflineLookup
|
|
32
14
|
delimiter: delimiter.to_s,
|
33
15
|
name: name,
|
34
16
|
transform: transform
|
35
|
-
}
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
include OfflineLookup::Base
|
40
|
-
end
|
41
|
-
|
42
|
-
def offline_lookup_value(*fields)
|
43
|
-
if offline_lookup_options[:transform].present?
|
44
|
-
offline_lookup_options[:transform].call(*fields.map(&:to_s))
|
45
|
-
else
|
46
|
-
fields.map(&:to_s).join(offline_lookup_options[:delimiter])
|
47
|
-
end
|
17
|
+
}
|
18
|
+
include OfflineLookup::Core
|
19
|
+
include OfflineLookup::DynamicModuleBuilder.new(self, self.offline_lookup_options).build_module
|
48
20
|
end
|
49
|
-
|
50
|
-
def get_offline_lookup_values
|
51
|
-
self.offline_lookup_values = self.all.pluck(offline_lookup_options[:key], *offline_lookup_options[:fields]).map do |key, *fields|
|
52
|
-
fields.compact! if offline_lookup_options[:compact]
|
53
|
-
value = offline_lookup_options[:transform].present? ? offline_lookup_options[:transform].call(*fields.map(&:to_s)) : fields.map(&:to_s).join(offline_lookup_options[:delimiter])
|
54
|
-
[key, value.to_s]
|
55
|
-
end.to_h.freeze
|
56
|
-
end
|
57
|
-
|
58
21
|
end
|
59
22
|
end
|
60
23
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module OfflineLookup
|
2
|
+
module Core
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :offline_lookup_values
|
7
|
+
self.offline_lookup_values = get_offline_lookup_values
|
8
|
+
|
9
|
+
after_create :set_class_offline_lookup_values, :add_to_offline_lookup
|
10
|
+
after_destroy :set_class_offline_lookup_values
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def offline_lookup_value(*field_values)
|
15
|
+
field_values.compact! if offline_lookup_options[:compact]
|
16
|
+
if offline_lookup_options[:transform].present?
|
17
|
+
offline_lookup_options[:transform].call(*field_values.map(&:to_s))
|
18
|
+
else
|
19
|
+
field_values.map(&:to_s).join(offline_lookup_options[:delimiter])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_offline_lookup_values
|
24
|
+
self.all.pluck(offline_lookup_options[:key], *offline_lookup_options[:fields]).map do |key_value, *field_values|
|
25
|
+
[key_value, offline_lookup_value(*field_values)]
|
26
|
+
end.to_h.freeze
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_class_offline_lookup_values
|
31
|
+
self.class.offline_lookup_values = self.class.get_offline_lookup_values
|
32
|
+
end
|
33
|
+
|
34
|
+
def offline_lookup_value
|
35
|
+
self.class.offline_lookup_value(*offline_lookup_options[:fields].map{|f| self.attributes[f.to_s]})
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_to_offline_lookup
|
39
|
+
builder = OfflineLookup::DynamicModuleBuilder.new(self.class, self.offline_lookup_options)
|
40
|
+
builder.add_dynamic_lookup_methods(self.attributes[self.offline_lookup_options[:key]], offline_lookup_value)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module OfflineLookup
|
2
|
+
class DynamicModuleBuilder
|
3
|
+
def initialize(model, options)
|
4
|
+
@model = model
|
5
|
+
@fields = options[:fields].map(&:to_s)
|
6
|
+
@key_name = options[:key]
|
7
|
+
@name = options[:name]
|
8
|
+
@build_identity_methods = options[:identity_methods]
|
9
|
+
@build_lookup_methods = options[:lookup_methods]
|
10
|
+
@modyule = get_module || create_module
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_module
|
14
|
+
@model.const_defined?("OfflineLookupMethods") && @model::OfflineLookupMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_module
|
18
|
+
modyule = Module.new
|
19
|
+
modyule.extend ActiveSupport::Concern
|
20
|
+
modyule.const_set("ClassMethods", Module.new)
|
21
|
+
@model.const_set("OfflineLookupMethods", modyule)
|
22
|
+
return modyule
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_module
|
26
|
+
add_all_dynamic_lookups
|
27
|
+
add_name_for_key_method
|
28
|
+
add_key_for_name_method
|
29
|
+
add_static_lookup_method
|
30
|
+
return @modyule
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_all_dynamic_lookups
|
34
|
+
@model.offline_lookup_values.each{|key_value, value| add_dynamic_lookup_methods(key_value, value)}
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_dynamic_lookup_methods(key_value, value)
|
38
|
+
add_key_lookup_method(key_value, value)
|
39
|
+
add_identity_method(key_value, value) if @build_identity_methods
|
40
|
+
add_lookup_method(key_value, value) if @build_lookup_methods
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get key value (e.g. FooType.bar_id)
|
44
|
+
def add_key_lookup_method(key_value, value)
|
45
|
+
key_lookup_method_name = sanitize("#{value}_#{@key_name}")
|
46
|
+
@modyule::ClassMethods.instance_exec(key_lookup_method_name, key_value, value) do |method_name, key_value|
|
47
|
+
define_method method_name do
|
48
|
+
key_value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return true iff instance is of named type (e.g. FooType.first.bar?)
|
54
|
+
def add_identity_method(key_value, value)
|
55
|
+
identify_method_name = sanitize(value) + "?"
|
56
|
+
@modyule.instance_exec(identify_method_name, @key_name, key_value) do |method_name, key_name, key_value|
|
57
|
+
define_method method_name do
|
58
|
+
self.attributes[key_name] == key_value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get instance by named method (e.g. FooType.bar) (Not offline, but syntactic sugar)
|
64
|
+
def add_lookup_method(key_value, value)
|
65
|
+
lookup_method_name = sanitize(value)
|
66
|
+
@modyule::ClassMethods.instance_exec(lookup_method_name, @key_name, key_value, value) do |method_name, key_name, key_value|
|
67
|
+
define_method method_name do
|
68
|
+
find_by(key_name => key_value)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# e.g. FooType.name_for_id(1)
|
74
|
+
def add_name_for_key_method
|
75
|
+
method_name = sanitize("#{@name}_for_#{@key_name}")
|
76
|
+
@modyule::ClassMethods.instance_exec(method_name) do |method_name|
|
77
|
+
define_method method_name do |key|
|
78
|
+
self.offline_lookup_values[key]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# e.g. FooType.id_for_name("Bar")
|
84
|
+
def add_key_for_name_method
|
85
|
+
method_name = sanitize("#{@key_name}_for_#{@name}")
|
86
|
+
@modyule::ClassMethods.instance_exec(method_name) do |method_name|
|
87
|
+
define_method method_name do |value|
|
88
|
+
self.offline_lookup_values.key(value.to_s)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# e.g. FooType.lookup("Bar")
|
94
|
+
def add_static_lookup_method
|
95
|
+
@modyule::ClassMethods.instance_exec(@key_name) do |key_name|
|
96
|
+
define_method "lookup" do |value|
|
97
|
+
find_by(key_name => self.offline_lookup_values.key(value.to_s))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def sanitize(string)
|
103
|
+
#1. Replace illegal chars and _ boundaries with " " boundary
|
104
|
+
string = string.to_s.gsub(/[^a-zA-Z\d]+/," ").strip
|
105
|
+
#2. Insert " " boundary at snake-case boundaries
|
106
|
+
string.gsub!(/([a-z])([A-Z])/){|s| "#{$1} #{$2}"}
|
107
|
+
#3. underscore
|
108
|
+
string.gsub!(/\s+/, "_")
|
109
|
+
string.downcase!
|
110
|
+
#4. Append underscore if name begins with digit
|
111
|
+
string = "_#{string}" if string.length == 0 || string[0] =~ /\d/
|
112
|
+
return string
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: offline_lookup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Schwartz
|
@@ -19,9 +19,9 @@ files:
|
|
19
19
|
- README.md
|
20
20
|
- lib/offline_lookup.rb
|
21
21
|
- lib/offline_lookup/active_record.rb
|
22
|
-
- lib/offline_lookup/
|
23
|
-
- lib/offline_lookup/
|
24
|
-
homepage:
|
22
|
+
- lib/offline_lookup/core.rb
|
23
|
+
- lib/offline_lookup/dynamic_module_builder.rb
|
24
|
+
homepage: http://github.com/ozydingo/offline_lookup
|
25
25
|
licenses:
|
26
26
|
- MIT
|
27
27
|
metadata: {}
|
data/lib/offline_lookup/base.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
module OfflineLookup
|
2
|
-
class Builder
|
3
|
-
attr_reader :fields, :key, :name
|
4
|
-
def initialize(model, options)
|
5
|
-
@model = model
|
6
|
-
@fields = options[:fields]
|
7
|
-
@key = options[:key]
|
8
|
-
@name = options[:name]
|
9
|
-
@build_identity_methods = options[:identity_methods]
|
10
|
-
@build_lookup_methods = options[:lookup_methods]
|
11
|
-
@modyule = get_module || create_module
|
12
|
-
end
|
13
|
-
|
14
|
-
def get_module
|
15
|
-
@model.const_defined?("OfflineLookupMethods") && @model::OfflineLookupMethods
|
16
|
-
end
|
17
|
-
|
18
|
-
def create_module
|
19
|
-
@modyule = Module.new
|
20
|
-
@modyule.extend ActiveSupport::Concern
|
21
|
-
@modyule.const_set("ClassMethods", Module.new)
|
22
|
-
@model.const_set("OfflineLookupMethods", @modyule)
|
23
|
-
end
|
24
|
-
|
25
|
-
def build
|
26
|
-
@model.offline_lookup_values.each do |key, value|
|
27
|
-
add_lookup(key, value)
|
28
|
-
end
|
29
|
-
add_name_for_key_method
|
30
|
-
add_key_for_name_method
|
31
|
-
add_static_lookup_method
|
32
|
-
@model.include @modyule
|
33
|
-
end
|
34
|
-
|
35
|
-
def add_lookup(key, value)
|
36
|
-
add_key_lookup_method(key, value)
|
37
|
-
add_identity_method(key, value) if @build_identity_methods
|
38
|
-
add_lookup_method(key, value) if @build_lookup_methods
|
39
|
-
end
|
40
|
-
|
41
|
-
def sanitize(string)
|
42
|
-
#1. Replace illegal chars and _ boundaries with " " boundary
|
43
|
-
string = string.to_s.gsub(/[^a-zA-Z\d]+/," ").strip
|
44
|
-
#2. Insert " " boundary at snake-case boundaries
|
45
|
-
string.gsub!(/([a-z])([A-Z])/){|s| "#{$1} #{$2}"}
|
46
|
-
#3. underscore
|
47
|
-
string.gsub!(/\s+/, "_")
|
48
|
-
string.downcase!
|
49
|
-
#4. Append underscore if name begins with digit
|
50
|
-
string = "_#{string}" if string.length == 0 || string[0] =~ /\d/
|
51
|
-
return string
|
52
|
-
end
|
53
|
-
|
54
|
-
# Get key value (e.g. FooType.bar_id)
|
55
|
-
def add_key_lookup_method(key, value)
|
56
|
-
@modyule::ClassMethods.instance_exec(self, key, value) do |builder, key, value|
|
57
|
-
define_method builder.sanitize("#{value}_#{builder.key}") do
|
58
|
-
key
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Return true iff instance is of named type (e.g. FooType.first.bar?)
|
64
|
-
def add_identity_method(key, value)
|
65
|
-
@modyule.instance_exec(self, key, value) do |builder, key, value|
|
66
|
-
define_method builder.sanitize(value) + "?" do
|
67
|
-
self.attributes[builder.key] == key
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Get instance by named method (e.g. FooType.bar) (Not offline, but syntactic sugar)
|
73
|
-
def add_lookup_method(key, value)
|
74
|
-
@modyule::ClassMethods.instance_exec(self, key, value) do |builder, key, value|
|
75
|
-
define_method builder.sanitize(value) do
|
76
|
-
find_by(builder.key => self.offline_lookup_values.key(value.to_s))
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# e.g. FooType.name_for_id(1)
|
82
|
-
def add_name_for_key_method
|
83
|
-
@modyule::ClassMethods.instance_exec(self) do |builder|
|
84
|
-
define_method builder.sanitize("#{builder.name}_for_#{builder.key}") do |key|
|
85
|
-
self.offline_lookup_values[key]
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# e.g. FooType.id_for_name("Bar")
|
91
|
-
def add_key_for_name_method
|
92
|
-
@modyule::ClassMethods.instance_exec(self) do |builder|
|
93
|
-
define_method builder.sanitize("#{builder.key}_for_#{builder.name}") do |value|
|
94
|
-
self.offline_lookup_values.key(value.to_s)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# e.g. FooType.lookup("Bar")
|
100
|
-
def add_static_lookup_method
|
101
|
-
@modyule::ClassMethods.instance_exec(self) do |builder|
|
102
|
-
define_method "lookup" do |value|
|
103
|
-
find_by(builder.key => self.offline_lookup_values.key(value.to_s))
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|