Case-insensitive single attribute method

Main question

This is a really terrible method that checks for equality based on code, but not agnostic

def ==(another_country) (code.nil? ? nil : code.downcase) == (another_country.code.nil? ? nil : another_country.code.downcase) unless another_country.nil? end 

Can you point me in the right direction, how to write it more elegantly, without relying on ugly, if still structures?

This is the solution as a result of which I used (+ RSpecs)

 # Country model class Country < ActiveRecord::Base attr_accessible :code def ==(another_country) code.to_s.downcase == another_country.code.to_s.downcase rescue false end end 

Extensive tests:

 # RSpec describe Country do describe 'equality based solely on Country.code' do before do @country_code_de = FactoryGirl.build(:country, :code => 'de') end it 'should be equal if Country.code is equal' do other_country_code_de = FactoryGirl.build(:country, :code => 'de') @country_code_de.should == other_country_code_de end it 'should be not equal if Country.code is not equal' do country_code_usa = FactoryGirl.build(:country, :code => 'usa') @country_code_de.should_not == country_code_usa end it 'should be case insensitive' do country_code_de_uppercase = FactoryGirl.build(:country, :code => 'DE') @country_code_de.should == country_code_de_uppercase end it 'should not rely on id for equality' do @country_code_de.id = 0 country_code_usa = FactoryGirl.build(:country, :code => 'usa', :id => 0) @country_code_de.should_not == country_code_usa end it 'should be not equal if Country.code of one Country is nil' do country_code_nil = FactoryGirl.build(:country, :code => nil) @country_code_de.should_not == country_code_nil end it 'should be equal if Country.code for both countries is nil' do country_code_nil = FactoryGirl.build(:country, :code => nil) other_country_code_nil = FactoryGirl.build(:country, :code => nil) country_code_nil.should == other_country_code_nil end it 'should be not equal if other Country is nil' do @country_code_de.should_not == nil end it 'should be not equal if other object is not a Country' do @country_code_de.should_not == 'test' end it 'should be equal for descendants of Country with same Country.code' do class CountryChild < Country end country_child = CountryChild.new(:code => 'de') @country_code_de.should == country_child end end end 
+4
source share
7 answers

How about this

 def ==(another_country) return false if code.blank? # Remove this line if you want to return true if code and antoher_country.code are nil code.to_s.downcase == another_country.to_s.code.downcase rescue false end 

Here, if any of the code , another_country or another_country.code is nil, it will throw an exception, and the rescue false statement will return false .

If all goes well, the comparison will happen and true or false will be returned based on input.

+1
source

If you use Rails:

 def ==(another_country) return nil unless another_country code.try(:downcase) == another_country.code.try(:downcase) end 
+1
source

nil has a to_s method:

 def ==(another_country) #return nil if another_country.nil? self.code.to_s.downcase == another_country.code.to_s.downcase end 
+1
source

Perhaps you can break the logic into two methods: one returns the identifier of the object, the other to check for equality:

 class MyClass def identity return nil if code.nil? code.downcase end def ==(other) return false unless other.is_a?(MyClass) self.identity == other.identity end end 
+1
source

Since any value that is not nil or false acting as true in the conditions, there are some tricks you can do with the code.

Type expression

 (code.nil? ? nil : code.downcase) 

can be replaced painlessly with

 (code.downcase if code) # or by this one (code && code.downcase) 

Second

 (do_something) unless another_country.nil? 

as well as

 (do_something) if another_country # or another_contry && (do_something) 

So in the end you can turn your method into this

 def ==(another_country) code && another_country.code && code.downcase == another_country.code.downcase end 

Some tests

 class Country attr_accessor :code def initialize(code) @code = code end def ==(another_country) code && another_country.code && code.downcase == another_country.code.downcase end end p Country.new("FOObar") == Country.new("fooBAR") # => true p Country.new(nil) == Country.new(nil) # => nil p Country.new("XXX") == Country.new(nil) # => nil p Country.new(nil) == Country.new("XXX") # => nil 
+1
source
 def == (another_country) if code.nil? || another_country.nil? || another_country.code.nil? return nil end code.downcase == another_country.code.downcase end 

Thus, you can immediately see what you are doing - nil verification and comparison.

0
source
 def == (another_country) return unless another_country.is_a?(Country) return if code.nil? || another_country.code.nil? code.casecmp(another_country.code).zero? end 

Class checking is good practice if you end up with an array of mixed types.

If you are not worried about the "vs nil" case, you can compress it a bit. I don't think it's worth it.

 def == (another_country) code.try(:casecmp, another_country.code.to_s).try(:zero?) if another_country.is_a?(Country) end 

Note that if you override ==, should you also override eql? and a hash otherwise you may get unexpected results using hashes and enumerated methods.

Ruby Monk - Equality of Objects

0
source

Source: https://habr.com/ru/post/1397287/


All Articles