lookup_by 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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