lookup_by 0.1.3 → 0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 170c2ef2408cf8495f0cfe3f30cd4811ee17cd6f
4
- data.tar.gz: 14e0c54b5ec327fa87cd0044b67cd921df3a002b
3
+ metadata.gz: c32a15a02ac5c67549366075fac6d76db2122564
4
+ data.tar.gz: 07aa69751c19f10a96b573eb08d562e7290aee47
5
5
  SHA512:
6
- metadata.gz: 258e5adbe68b2c181613e08e5cb59e6d8044e1b28cea20dffb282d3489826aa9061e884cd200844953e95a23cc4e9286595f0a3e849205faa4ba6a102bd27d71
7
- data.tar.gz: 76c03e4a60019234ba204c47d65520076b5dad3705a765523be4706b9caa78ae5199d9f9f992dca7e7c3ef65a48f8dc0d1563bdaf666bcc970e5343e29f7b0fd
6
+ metadata.gz: 14f87c363d0c346dafb9f37a1b015a721786c0ba4223ddfa1bb656adfe0dbf1d9764ea3724ac3330b336719382c5f014ae393b97f0c6eeeff3a936844d825af5
7
+ data.tar.gz: c1cca0a1500aedf3a1f10ffaceca6dbe5d1fd870b86c9a0b23858f5c075aeaf6901c84f0fd73559a31c60a5cc64149c2f1925af69ab57935fa56d623f8550662
@@ -16,14 +16,31 @@ module LookupBy
16
16
  def lookup_for field, options = {}
17
17
  return unless table_exists?
18
18
 
19
+ options.symbolize_keys!
20
+ options.assert_valid_keys(:class_name, :foreign_key, :symbolize, :strict, :scope)
21
+
19
22
  field = field.to_sym
20
23
 
21
- %W(#{field} raw_#{field} #{field}= #{field}_before_type_cast).map(&:to_sym).each do |method|
24
+ %W(#{field} raw_#{field} #{field}= #{field}_before_type_cast #{field}?).map(&:to_sym).each do |method|
22
25
  raise Error, "method `#{method}` already exists on #{self.inspect}" if instance_methods.include? method
23
26
  end
24
27
 
25
- options.symbolize_keys!
26
- options.assert_valid_keys(:class_name, :foreign_key, :symbolize, :strict)
28
+ singleton_class.class_eval do
29
+ attr_reader :lookups
30
+ end
31
+
32
+ @lookups ||= []
33
+ @lookups << field
34
+
35
+ scope_name = "with_#{field}" unless options[:scope] == false
36
+
37
+ if scope_name
38
+ single_scope = scope_name
39
+ plural_scope = scope_name.pluralize
40
+
41
+ raise Error, "#{single_scope} already exists on #{self}. Use `lookup_for #{field}, scope: false` if you don't want scope :#{single_scope}" if respond_to?(single_scope)
42
+ raise Error, "#{plural_scope} already exists on #{self}. Use `lookup_for #{field}, scope: false` if you don't want scope :#{plural_scope}" if respond_to?(plural_scope)
43
+ end
27
44
 
28
45
  class_name = options[:class_name] || field
29
46
  class_name = class_name.to_s.camelize
@@ -31,22 +48,21 @@ module LookupBy
31
48
  foreign_key = options[:foreign_key] || "#{field}_id"
32
49
  foreign_key = foreign_key.to_sym
33
50
 
51
+ raise Error, "foreign key `#{foreign_key}` is required on #{self}" unless attribute_names.include?(foreign_key.to_s)
52
+
34
53
  strict = options[:strict]
35
54
  strict = true if strict.nil?
36
55
 
37
- raise Error, "foreign key `#{foreign_key}` is required on #{self}" unless attribute_names.include?(foreign_key.to_s)
38
-
39
- lookup_field = class_name.constantize.lookup.field
56
+ class_eval <<-SCOPES, __FILE__, __LINE__.next if scope_name
57
+ scope :#{scope_name}, ->(name) { where(#{foreign_key}: #{class_name}[name]) }
58
+ scope :#{scope_name.pluralize}, ->(*names) { where(#{foreign_key}: #{class_name}[*names]) }
59
+ SCOPES
40
60
 
41
61
  cast = options[:symbolize] ? ".to_sym" : ""
42
62
 
63
+ lookup_field = class_name.constantize.lookup.field
43
64
  lookup_object = "#{class_name}[#{foreign_key}]"
44
65
 
45
- class << self; attr_reader :lookups; end
46
-
47
- @lookups ||= []
48
- @lookups << field
49
-
50
66
  class_eval <<-METHODS, __FILE__, __LINE__.next
51
67
  def raw_#{field}
52
68
  #{lookup_object}
@@ -57,16 +73,20 @@ module LookupBy
57
73
  value ? value.#{lookup_field}#{cast} : nil
58
74
  end
59
75
 
76
+ def #{field}?(name)
77
+ raise ArgumentError, "Invalid #{field} \#{name.inspect}" unless object = #{class_name}[name]
78
+ #{foreign_key} == object.id
79
+ end
80
+
60
81
  def #{field}_before_type_cast
61
- value = #{lookup_object}
62
- value.#{lookup_field}_before_type_cast
82
+ #{lookup_object}.#{lookup_field}_before_type_cast
63
83
  end
64
84
 
65
85
  def #{field}=(arg)
66
86
  value = case arg
67
87
  when "", nil
68
88
  nil
69
- when String, Fixnum
89
+ when String, Integer
70
90
  #{class_name}[arg].try(:id)
71
91
  when Symbol
72
92
  #{%Q(raise ArgumentError, "#{foreign_key}=(Symbol): use `lookup_for :column, symbolize: true` to allow symbols") unless options[:symbolize]}
@@ -75,7 +95,7 @@ module LookupBy
75
95
  raise ArgumentError, "self.#{foreign_key}=(#{class_name}): must be saved" unless arg.id
76
96
  arg.id
77
97
  else
78
- raise TypeError, "#{foreign_key}=(arg): arg must be a String, Symbol, Fixnum, nil, or #{class_name}"
98
+ raise TypeError, "#{foreign_key}=(arg): arg must be a String, Symbol, Integer, nil, or #{class_name}"
79
99
  end
80
100
 
81
101
  #{%Q(raise LookupBy::Error, "\#{arg.inspect} is not in the <#{class_name}> lookup cache" if arg.present? && value.nil?) if strict}
@@ -23,7 +23,7 @@ module LookupBy
23
23
  when true
24
24
  @type = :all
25
25
  @read ||= false
26
- when ::Fixnum
26
+ when ::Integer
27
27
  raise ArgumentError, "`#{@klass}.lookup_by :#{@field}` options[:find] must be true when caching N" if @read == false
28
28
 
29
29
  @type = :lru
@@ -62,7 +62,7 @@ module LookupBy
62
62
  def fetch(value)
63
63
  increment :cache, :get
64
64
 
65
- value = normalize(value) if @normalize && !value.is_a?(Fixnum)
65
+ value = normalize(value) if @normalize && !value.is_a?(Integer)
66
66
 
67
67
  found = cache_read(value) if cache?
68
68
  found ||= db_read(value) if @read
@@ -108,7 +108,7 @@ module LookupBy
108
108
  end
109
109
 
110
110
  def cache_read(value)
111
- if value.is_a? Fixnum
111
+ if value.is_a? Integer
112
112
  found = @cache[value]
113
113
  else
114
114
  found = @cache.values.detect { |o| o.send(@field) == value }
@@ -138,7 +138,7 @@ module LookupBy
138
138
  end
139
139
 
140
140
  def column_for(value)
141
- value.is_a?(Fixnum) ? @primary_key : @field
141
+ value.is_a?(Integer) ? @primary_key : @field
142
142
  end
143
143
 
144
144
  def cache?
@@ -71,14 +71,19 @@ module LookupBy
71
71
  @lookup.cache.values.map { |o| o.send(column_name) }
72
72
  end
73
73
 
74
- def [](arg)
75
- case arg
76
- when nil, "" then nil
77
- when String then @lookup.fetch(arg)
78
- when Symbol then @lookup.fetch(arg.to_s)
79
- when Fixnum then @lookup.fetch(arg)
80
- when self then arg
81
- else raise TypeError, "#{name}[arg]: arg must be a String, Symbol, Fixnum, nil, or #{name}"
74
+ def [](*args)
75
+ case args.size
76
+ when 0 then raise ArgumentError, "#{name}[*args]: at least one argument is required"
77
+ when 1
78
+ case arg = args.first
79
+ when nil, "" then nil
80
+ when String then @lookup.fetch(arg)
81
+ when Symbol then @lookup.fetch(arg.to_s)
82
+ when Integer then @lookup.fetch(arg)
83
+ when self then arg
84
+ else raise TypeError, "#{name}[arg]: arg must be at least one String, Symbol, Integer, nil, or #{name}"
85
+ end
86
+ else return args.map { |arg| self[arg] }
82
87
  end
83
88
  end
84
89
  end
@@ -86,10 +91,10 @@ module LookupBy
86
91
  module InstanceMethods
87
92
  def ===(arg)
88
93
  case arg
89
- when Symbol, String, Fixnum, nil
94
+ when Symbol, String, Integer, nil
90
95
  return self == self.class[arg]
91
96
  when Array
92
- return !!arg.detect { |i| self === i }
97
+ return arg.any? { |i| self === i }
93
98
  end
94
99
 
95
100
  super
@@ -1,3 +1,3 @@
1
1
  module LookupBy
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -17,13 +17,25 @@ describe ::ActiveRecord::Base do
17
17
  public :define_method, :remove_method
18
18
  end
19
19
 
20
- [:foo, :foo=, :raw_foo, :foo_before_type_cast].each do |method|
20
+ [:foo, :foo=, :raw_foo, :foo_before_type_cast, :foo?].each do |method|
21
21
  subject.define_method(method) { }
22
22
 
23
23
  expect { subject.lookup_for :foo }.to raise_error LookupBy::Error, /already exists/
24
24
 
25
25
  subject.remove_method(method)
26
26
  end
27
+
28
+ class << subject.singleton_class
29
+ public :define_method, :remove_method
30
+ end
31
+
32
+ [:with_foo, :with_foos].each do |method|
33
+ subject.singleton_class.define_method(method) { }
34
+
35
+ expect { subject.lookup_for :foo }.to raise_error LookupBy::Error, /already exists/
36
+
37
+ subject.singleton_class.remove_method(method)
38
+ end
27
39
  end
28
40
 
29
41
  it "requires a foreign key" do
@@ -33,6 +45,18 @@ describe ::ActiveRecord::Base do
33
45
  it "rejects unsaved lookup values" do
34
46
  expect { subject.new.city = City.new(name: "Toronto") }.to raise_error ArgumentError, /must be saved/
35
47
  end
48
+
49
+ context "scope: nil" do
50
+ it { should respond_to(:with_city).with(1).arguments }
51
+ it { should respond_to(:with_cities).with(2).arguments }
52
+ end
53
+
54
+ context "scope: false" do
55
+ it { should_not respond_to(:with_postal_code) }
56
+ it { should_not respond_to(:with_postal_codes) }
57
+ end
58
+
59
+ its(:lookups) { should include(:city) }
36
60
  end
37
61
  end
38
62
 
@@ -46,7 +70,7 @@ describe LookupBy::Association do
46
70
  context "Address.lookup_for :city, strict: false" do
47
71
  it_behaves_like "a lookup for", :city
48
72
 
49
- it "accepts Fixnums" do
73
+ it "accepts Integers" do
50
74
  subject.city = City.where(city: "New York").first.id
51
75
  subject.city.should eq "New York"
52
76
  end
@@ -92,7 +116,7 @@ describe LookupBy::Association do
92
116
 
93
117
  context "Missing.lookup_for :city" do
94
118
  it "does not raise foreign key error when table hasn't been created" do
95
- expect { require "missing"; }.to_not raise_error
119
+ expect { require "missing" }.to_not raise_error
96
120
  end
97
121
  end
98
122
  end
@@ -1,6 +1,6 @@
1
1
  class Address < ActiveRecord::Base
2
2
  lookup_for :city, strict: false
3
3
  lookup_for :state, symbolize: true
4
- lookup_for :postal_code
4
+ lookup_for :postal_code, scope: false
5
5
  lookup_for :street
6
6
  end
@@ -3,24 +3,34 @@ shared_examples "a lookup" do
3
3
  it { should respond_to :lookup }
4
4
  it { should respond_to :lookup_by }
5
5
  it { should respond_to :lookup_for }
6
+
6
7
  its(:is_a_lookup?) { should be_true }
7
8
 
9
+ it "raises with no args" do
10
+ expect { subject[] }.to raise_error ArgumentError
11
+ end
12
+
8
13
  it "returns nil for nil" do
9
14
  subject[nil].should be_nil
15
+ subject[nil, nil].should == [nil, nil]
10
16
  end
11
17
 
12
18
  it "returns nil for empty strings" do
13
19
  subject[""].should be_nil
20
+ subject["", ""].should == [nil, nil]
14
21
  end
15
22
 
16
23
  it "returns itself" do
17
- first = subject.first
18
- subject[first].should eq first if first
24
+ if first = subject.first
25
+ subject[first].should eq first
26
+ subject[first, first].should == [first, first]
27
+ end
19
28
  end
20
29
 
21
30
  it "rejects other argument types" do
22
- [1.00, true, false, Address.new].each do |value|
23
- expect { subject[value] }.to raise_error TypeError
31
+ [1.00, true, false, Rational(1), Address.new, Array.new, Hash.new].each do |value|
32
+ expect { subject[value] }.to raise_error TypeError
33
+ expect { subject[value, value] }.to raise_error TypeError
24
34
  end
25
35
  end
26
36
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lookup_by
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Peterson