offline_lookup 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|