Updates from June, 2021 Toggle Comment Threads | Keyboard Shortcuts

  • ThomasPowell 5:14 am on June 29, 2021 Permalink | Reply
    Tags: map, , , transform   

    Rails’ and Ruby’s Hash transform_values 

    Rails: 4.2.1-5.2.3 and Ruby >= 2.5.5 have a transform_values method on Hash that allows you to pass a block to the method and transform the values of the key-value map in the hash based on the block contents. Essentially, it’s map but for the Hash values only, and with no weird Hash/array-element syntax. From the linked API documents above:

    h = { a: 1, b: 2, c: 3 }
    h.transform_values {|v| v * v + 1 }  #=> { a: 2, b: 5, c: 10 }
    h.transform_values(&:to_s)           #=> { a: "1", b: "2", c: "3" }
    h.transform_values.with_index {|v, i| "#{v}.#{i}" }
                                         #=> { a: "1.0", b: "2.1", c: "3.2" }

  • ThomasPowell 4:27 am on June 29, 2021 Permalink | Reply
    Tags: , , ,   

    NoMethodError undefined method `shared_examples_for’ for main:Object for bundle gem rspec 

    If you create a gem stub using bundle gem thing and select rspec as your test suite, you may get an error similar to the following:

    ❯ bundle exec rspec
    An error occurred while loading ./spec/thing_spec.rb.
      shared_examples_for 'saying hello' do
        puts "hi"
      undefined method `shared_examples_for' for main:Object
    # ./spec/thing_spec.rb:1:in `<top (required)>'
    No examples found.

    whenever using describe, shared_examples, and shared_examples_for, etc…

    After a lot of diving into rspec source code to verify where shared_examples_for was defined (rspec-core so…) I noticed the following code in the stubbed spec_helper.rb:

      # Disable RSpec exposing methods globally on `Module` and `main`

    If you comment out config.disable_monkey_patching!, then those methods will be included at a top level.

  • ThomasPowell 9:43 am on June 26, 2021 Permalink | Reply
    Tags: , ,   

    Ruby: Enumerable grep, grep_v 

    UPDATE: After I wrote this, I started finding myself doing a lot of caller.grep(/(project_directory|suspected_gem)/) to aid in debugging obscure interactions with internal gems and projects.

    In looking at a pull request and noticing some awkward “first” and “last” iteration detection which also required each_with_index, I started looking into what would be a cleaner way, and my first step was trying to figure out if there was an enumerable context.

    Ultimately I landed on Enumerable#grep and Enumerable#grep_v, which somewhat perplexed me. It’s not really a “grep” unless your collection’s values respond to that:

    irb(main):056:0> 1000.times.map(&:to_s).grep(/00/)
    => ["100", "200", "300", "400", "500", "600", "700", "800", "900"]

    Maybe you want to look for Classes in the ObjectSpace… The argument to #grep is compared against an implicit element for each iteration with the === operator. So you could list everything in the ObjectSpace that’s a Class:

    ObjectSpace.each_object.grep(Class) # too long to include here

    One situation that I thought of that might be especially useful is Dir globbing:

    irb(main):064:0> Dir['*'].grep(/(yarn|json)/)
    => ["package-lock.json", "package.json", "yarn.lock"]

    If you were trying to the Ruby REPL as a shell, you could even:

    irb(main):005:0> Dir['*/'].grep_v(%r{/packs/}).grep(/(.js$|.erb$|.rb$|.json$|.lock$)/)
    => ["app/controllers/application_controller.rb", "app/controllers/posts_controller.rb", "app/helpers/application_helper.rb", "app/helpers/posts_helper.rb", "app/javascript/src/jets/crud.js", "app/jobs/application_job.rb", "app/models/application_item.rb", "app/models/application_record.rb", "app/models/post.rb", "app/views/layouts/application.html.erb", "app/views/posts/edit.html.erb", "app/views/posts/index.html.erb", "app/views/posts/new.html.erb", "app/views/posts/show.html.erb", "app/views/posts/_form.html.erb", "babel.config.js", "config/application.rb", "config/environments/development.rb", "config/environments/production.rb", "config/environments/test.rb", "config/routes.rb", "config/webpack/development.js", "config/webpack/environment.js", "config/webpack/production.js", "config/webpack/test.js", "db/migrate/20210610023540_create_posts.rb", "db/schema.rb", "Gemfile.lock", "postcss.config.js", "spec/controllers/posts_controller_spec.rb", "spec/fixtures/payloads/posts-index.json", "spec/fixtures/payloads/posts-show.json", "spec/spec_helper.rb"]

    And, as with .each, .map, etc… you can pass a block interact with each element. Your return from each iteration will map back to the output.

    # array containing the contents of all the files matching:
    Dir['*/'].grep_v(%r{/packs/}).grep(/(.js$|.erb$|.rb$|.json$|.lock$)/) { |f| File.open(f).read }

    I’m not sure if I’ve seen any code that would be made cleaner by grep (unless for utility scripting), and there’s always the risk of lowering maintainability of the code by using features no one else uses, but it’s nice to know that Ruby always has something more even after using it for many years.

Compose new post
Next post/Next comment
Previous post/Previous comment
Show/Hide comments
Go to top
Go to login
Show/Hide help
shift + esc
%d bloggers like this: