Today I Learned Notes to self about software development

    Serving static files with Rack

    You can use a Proc like this to serve static files.

    # config.ru
    map '/' do
      path = '/index.html'
      default_homepage = File.read(path)
      app = proc do |env|
        [200, { 'Content-Type' => 'text/html' }, [default_homepage]]
        # last argument needs to be an array
      end
      run app
    end
    

    The proc keyword is the equivalent to Proc.new.

    Pushing gems to rubygems

    In your gem project run:

    rake build
    

    to create pkg/<gem-name>-0.1.0.gem

    Then run

    gem push pkg/<gem-name>-0.1.0.gem
    

    This should ask you to sign in, if you’re not already. Sign in credentials are stored in ~/.gem/credentials. So remove that if you want to “sign out” of rubygems.

    XCode Developer Cache

    In the eternal journey of freeing up more memory, I noticed that the storage management settings on my laptop (MacOS 12) had a new section for “developer cache”.

    xcode-cache.png

    (not actually my computer)

    Not only do I not know what is does, it also was taking up a lotta space!

    It turns out all of this “cache” is related to writing code for apple products which is not why I have xcode installed (I just need it for Ruby I think). So deleting the cache is safe to do, but it will keep coming back.

    To prevent that from happening, you need to open xcode (which actually made me install something else 😠) and delete all of the simulators.

    Menu > Window > Devices > Simulators

    then highlight and delete!

    Conditional Hash#merge

    I was in a situation where a key may or may not be present in a Hash (options), and I needed to do two different, but very similar things. The general if/else seemed like too much:

    if options[:uniqueness]
      if options[:scope]
        validates attribute, :uniqueness => { :case_sensitive => true, :scope => options[:scope] }
      else
        validates attribute, :uniqueness => { :case_sensitive => true }
      end
      # ...
    end
    

    So I found a nice one-liner that cleaned it up!

    if options[:uniqueness]
      uniqueness_options = {
        :case_sensitive => true,
        :scope => ( options[:scope] if options.has_key?(:scope))
      }.compact
    
      validates attribute, :uniqueness => uniqueness_options
    end
    

    Another approach was to use tap (which I need to lookup more about)

    { :a => 'animal' }.tap { |hash|
      hash[:b] = 'banana' if true
    }
    

    which maybe is better since it supports earlier Ruby version and is still very readable.

    Hash#merge and setting defaults

    When writing a method with an options Hash, how best do you specify default key/value pairs that can be safely overwritten?

    Well, if it’s just one this should be fine:

    def foo(k: 1)
      p k
    end
    
    foo({ k: 'apple'})
    

    but if you’re dealing with a double spat, Hash#merge is your friend.

    # Can also use a constant here to help document what the defaults are
    DEFAULTS = {
      fruit: 'apple',
      color: 'lavendar',
      feels: 'sleepy'
    }
    
    def blog(**options)
      options = DEFAULT.merge(options)
      # ...
    end
    

    merge works like this:

    h1 = { "a" => 100, "b" => 200 }
    h2 = { "b" => 254, "c" => 300 }
    h1.merge(h2)   #=> {"a"=>100, "b"=>254, "c"=>300}
    h1             #=> {"a"=>100, "b"=>200}
    

    prioritizing the values in the Hash argument.

    There’s also apparently Hash#reverse_merge in Rails, which handles duplicate values the other way— prioritizing the entries in the Hash calling the method.

    h1 = { "a" => 100, "b" => 200 }
    h2 = { "b" => 254, "c" => 300 }
    h1.reverse_merge(h2)   # => {"b"=>200, "c"=>300, "a"=>100}