Yak of the week! Ruby 2.4 Pathname empty? changed to look at file size!

Yak of the week! Ruby 2.4 Pathname empty? changed to look at file size!

Pathname.present? is false for existing file with empty contents! and a good example of Pry saving me!

I had failing tests in React on Rails due Ruby 2.4.1 due to a change in the Pathname API. Previously, for Ruby 2.3, Pathname.empty? would return false if the file’s name is not empty, not the contents!

[43] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.blank?
true
[44] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.to_s
"/var/folders/rp/_k99k0pn0rsb4d3lm9l3dnjh0000gn/T/d20170603-96466-zk7di7"
[45] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.present?
false
[58] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.presence
nil

WTF?

[59] (pry) #<ReactOnRails::AssetsPrecompile>: 0> $ assets_path.presence

From: /Users/justin/.rvm/gems/ruby-2.4.1@react_on_rails/gems/activesupport-5.1.1/lib/active_support/core_ext/object/blank.rb @ line 43:
Owner: Object
Visibility: public
Number of lines: 3

def presence
  self if present?
end
[60] (pry) #<ReactOnRails::AssetsPrecompile>: 0> $ assets_path.present?

From: /Users/justin/.rvm/gems/ruby-2.4.1@react_on_rails/gems/activesupport-5.1.1/lib/active_support/core_ext/object/blank.rb @ line 23:
Owner: Object
Visibility: public
Number of lines: 3

def present?
  !blank?
end
[46] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.nil?
false
[52] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.exist?
true
[53] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.blank?
true

WTF!!!

The file exists, but blank? is true.

[54] (pry) #<ReactOnRails::AssetsPrecompile>: 0> $ assets_path.blank?

From: /Users/justin/.rvm/gems/ruby-2.4.1@react_on_rails/gems/activesupport-5.1.1/lib/active_support/core_ext/object/blank.rb @ line 16:
Owner: Object
Visibility: public
Number of lines: 3

def blank?
  respond_to?(:empty?) ? !!empty? : !self
end
[55] (pry) #<ReactOnRails::AssetsPrecompile>: 0> $ assets_path.empty?

From: ext/pathname/pathname.c (C Method):
Owner: Pathname
Visibility: public
Number of lines: 10

static VALUE
path_empty_p(VALUE self)
{

    VALUE path = get_strpath(self);
    if (RTEST(rb_funcall(rb_mFileTest, rb_intern("directory?"), 1, path)))
        return rb_funcall(rb_cDir, rb_intern("empty?"), 1, path);
    else
        return rb_funcall(rb_mFileTest, rb_intern("empty?"), 1, path);
}
[56] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.empty?
true
[57] (pry) #<ReactOnRails::AssetsPrecompile>: 0> assets_path.class
Pathname < Object

The reason why is that empty? is true!

These are my pry shortcuts! Pry is so useful!

See My .pryrc for debugging and productivity

Debugging Shortcuts
ss  :  step
nn  :  next
cc  :  continue
fin :  finish
uu  :  up
dd  :  down
bb  :  break
ww  :  whereami
ff  :  frame
sss :  show-stack
$   :  show whole method of context

Run 'pry_debug' or 'pd' to display shorter debug shortcuts

Utility method to solve the problem with tests

Utils.rb

module ReactOnRails
  module Utils
      # https://forum.shakacode.com/t/yak-of-the-week-ruby-2-4-pathname-empty-changed-to-look-at-file-size/901
    # return object if truthy, else return nil
    def self.truthy_presence(obj)
      if obj.nil? || obj == false
        nil
      else
        obj
      end
    end
end

Tests

module ReactOnRails
  RSpec.describe Utils do
    describe ".truthy_presence" do
      context "With non-empty string" do
        subject { "foobar" }
        it "returns subject (same value as presence) for a non-empty string" do
          expect(Utils.truthy_presence(subject)).to eq(subject.presence)

          # Blank strings are nil for presence
          expect(Utils.truthy_presence(subject)).to eq(subject)
        end
      end

      context "With empty string" do
        subject { "" }
        it "returns \"\" for an empty string" do
          expect(Utils.truthy_presence(subject)).to eq(subject)
        end
      end

      context "With nil object" do
        subject { nil }
        it "returns nil (same value as presence)" do
          expect(Utils.truthy_presence(subject)).to eq(subject.presence)

          # Blank strings are nil for presence
          expect(Utils.truthy_presence(subject)).to eq(nil)
        end
      end

      context "With pathname pointing to empty dir (obj.empty? is true)" do
        subject(:empty_dir) { Pathname.new(Dir.mktmpdir) }
        it "returns Pathname object" do
          # Blank strings are nil for presence
          expect(Utils.truthy_presence(empty_dir)).to eq(empty_dir)
        end
      end

      context "With pathname pointing to empty file" do
        let(:empty_dir) { Pathname.new(Dir.mktmpdir) }
        subject(:empty_file) { File.basename(Tempfile.new("tempfile",
                                                          empty_dir)) }
        it "returns Pathname object" do
          expect(Utils.truthy_presence(empty_file)).to eq(empty_file)
        end
      end
    end
  end
end