Friday, June 30, 2017

11 Small Improvements For Ruby - followup

Jo Jo by Garen M. from flickr (CC-NC)

Last week I posted a list of 11 things I'd like to see changed about Ruby.

Time for some followup, as I have workarounds for at least some of the issues.

Pathname#glob - now in a gem

I wrote a gem pathname-glob which provides the missing method so you can code:

Pathname("foo").glob("*.txt")

This doesn't just save you a few characters, it's the only way to get reliable globbing when the path contains special characters like [, ], {, }, ?, *, or \ - which is pretty likely if you're dealing with users' files.

Support for special characters can't be fixed in Dir.glob("foo/*.txt") style API, as it doesn't know where name of the directory ends and globbing pattern starts.

Another workaround would be to use Dir.chdir("foo"){ Dir.glob("*.txt") } - that'd deal with special characters in folder names, but would cause issues with threads.

Of all the issues, this one is probably most useful to get into ruby standard library. It's like jQuery for Pathnames.

Hash#zip - now in a gem

I wrote a gem hash-zip.

You can use it for zipping any number of Hashes together, and it will pad any missing values with nils. Usually the next step is to merge them in some meaningful way (for simple case when one overrides the other Hash#merge already exists).

default_options.zip(user_options).map{|key, (default_value, user_value)| ... }

Technically it overwrites existing #zip method Hash inherits from Enumerable, but Enumerable#zip really doesn't make any sense when applied to Hashes, so it's better this way than introducing a new name.

Missing Hash methods - now in a gem

I wrote a gem hash-polyfill, which contains a bunch of simple methods Hash is bound to get eventually.

These are:
  • Hash#compact
  • Hash#compact!
  • Hash#select_values
  • Hash#select_values!
  • Hash#reject_values
  • Hash#reject_values!
  • Hash#transform_values
  • Hash#transform_values!
As I assume these will be added eventually, gem only adds methods which don't exist yet. Gem skips already defined methods.

In particular the last two already exist in 2.4, but you can get access to them from older Ruby versions with this polyfill.

Naming convention for #compact, #compact! follows Array, and for #select_values etc. follows 2.4's #transform_values and existing Enumerable#select/reject.

Names Ruby will end up using might be different, but these are as good guesses as any.

Enumerable#count_by - easier with 2.4

The example I had last time:

posts.count_by(&:author)

is actually not too bad with 2.4:

posts.group_by(&:author).transform_values(&:size)

For very large collections (like all bytes in some huge file), group_by / transform_values is going to take a lot more memory than counting things directly, but I ran benchmarks and it seems it's usually a lot faster than Ruby-based loop implementation.

If you're not on 2.4, check out hash-polyfill gem.

2 comments:

  1. Anonymous01:38

    Nice! Do you think the gems are suitable to be added to "Awesome Ruby" ruby.libhunt.com ?

    ReplyDelete
  2. Anonymous: Maybe? I don't really know what criteria they use. They're very tiny gems.

    ReplyDelete