Open a File Case-Insensitively in Ruby Under Linux

Open a file case-insensitively in Ruby under Linux

One approach would be to write a little method to build a case insensitive glob for a given filename:

def ci_glob(filename)
glob = ''
filename.each_char do |c|
glob += c.downcase != c.upcase ? "[#{c.downcase}#{c.upcase}]" : c
end
glob
end

irb(main):024:0> ci_glob('foo.txt')
=> "[fF][oO][oO].[tT][xX][tT]"

and then you can do:

filename = Dir.glob(ci_glob('foo.txt')).first

Alternatively, you can write the directory search you suggested quite concisely. e.g.

filename = Dir.glob('*').find { |f| f.downcase == 'foo.txt' }

Prior to Ruby 3.1 it was possible to use the FNM_CASEFOLD option to make glob case insensitive e.g.

filename = Dir.glob('foo.txt', File::FNM_CASEFOLD).first
if filename
# use filename here
else
# no matching file
end

The documentation suggested FNM_CASEFOLD couldn't be used with glob but it did actually work in older Ruby versions. However, as mentioned by lildude in the comments, the behaviour has now been brought inline with the documentation and so this approach shouldn't be used.

In Ruby, how can I interpret (expand) a glob relative to a directory?

If I were implementing that behaviour, I would go with filtering an array, returned by Dir#entries:

Dir.entries("#{target}").select { |f| f =~ /\A#{filename}\z/i }

Please be aware that on unix platform both . and .. entries will be listed as well, but they are unlikely to be matched on the second step. Also, probably the filename should be escaped with Regexp.escape:

Dir.entries("#{target}").select { |f| f =~ /\A#{Regexp.escape(filename)}\z/i }

case insensitive name, while preserving capitalization, in Devise

What you might try is to not set :name as case insensitive, which will properly save the case-sensitive name in the database:

config.case_insensitive_keys = []

Then, override the find_first_by_auth_conditions class method on User to find the user by their name. Note that this code will vary depending on the database (below is using Postgres):

def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where("lower(name) = ?", login.downcase).first
else
where(conditions).first
end
end

Doing this, a User.find_for_authentication(login: 'thefourthmusketeer') will properly return the record with a name of "TheFourthMusketeer".

See https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address for an explanation of overriding this method.

How to do case-insensitive order in Rails with postgresql

result = Users.find(:all, :order => "LOWER(name)")

To take a little bit from both Brad and Frank.

A lot of warnings after require 'Time'

There is no Time library in Ruby. There is, however, a time library.

It looks like you are using a case-insensitive file system, so when you require 'Time', the Operating System will "lie" to Ruby and tell it that Time.rb actually exists, even though there is really only a time.rb. (The OS will say the same thing about TIME.RB or tImE.rB or TiMe.Rb or …)

Therefore, Ruby will load Time.rb (which is really time.rb). However, internally, the time library will of course use require 'time' everywhere. Now, Ruby detects when a file has already been loaded and will just ignore it, BUT Time.rb and time.rb are two different file names, so Ruby will naturally load them both.

Since they are the same file, though, everything in time.rb will get executed twice, which means that you will get a warning for every single constant definition and every single method definition in that file.

The solution is simple: use require 'time' because that's the name of the library's entry file.

The alternative would be to use a case-sensitive file system, in which case you would simply get a LoadError exception telling you that there is no file named Time.rb.

cannot load such file -- Kconv

The name of the file is kconv.rb, not Kconv.rb. On your Mac, you are presumably using a case-insensitive file system such as FAT, NTFS or HFS, whereas on your CentOS box, you are using a case-sensitive file system such as ext4, btrfs or ZFS.

So, it should be

require 'kconv'

and the fact that

require 'Kconv'

works on a case-insensitive file system is just pure dumb luck.



Related Topics



Leave a reply



Submit