Easy Screenshots in Your CLI Pull Requests

2018-08-14

Are you using hub to create your Pull Requests? How are you handling embedding images? Wait... you are embedding images, aren't you? For my money, a screenshot is one of the most important parts of a PR body. The context provided by an image goes a long way towards understanding a proposed change. A picture is worth a thousand words and all that.

Until recently, I'd been using hub to create a pull request, and then editing the PR on GitHub to embed screenshots. This always felt a little clunky and I assumed I'd make it more seamless some day.

Cue the cheesy dramatic music: That day has arrived.

Some instructions here will be Mac-specific but I'm sure you can figure out how to tweak it for your setup.


Since a PR body is just markdown, the mechanics of embedding a screenshot are well understood:

  1. Take a screenshot

    • To avoid littering the desktop or other folders, I prefer to take the screenshot to the clipboard (by default, add CTRL to your shortcut)
  2. Upload the screenshot somewhere that is world-readable

    • For longevity, you should probably use one of your company's s3 buckets for work-related PRs.
  3. Add the markdown to render the screenshot to the PR body

Assuming you did step 1 correctly and have a screenshot in your clipboard, how do we upload it somewhere world readable?

We'll use two new commands (both available via homebrew). pngpaste is a great utility for pasting an image to a file. awscli has an s3 subcommand for uploading files to a bucket.

Besides that, we'll do a little work to make the filename unique and then massage the resulting url into the proper markdown for an image and we're golden:

And there ya go. No more editing the PR after the fact.

If you use vim, you're now a quick :r !s3_image away from embedding a screenshot straight from the clipboard to your PR body. Make it a proper mapping. Declare victory.


Executable Note-Taking — Writing Automated Tests as a Learning Method

2017-11-27

I'm trying a new (to me) approach to learning programming languages: writing automated tests as a learning method. This surely isn't a new idea but I wanted to share my experience.

The concept is pretty straightforward:

  1. Discover how something works
  2. Write a test to illustrate your understanding (aim for concise examples)
  3. Add more tests as-needed to probe the boundaries of your understanding

These tests are effectively executable note-taking. You should try it.

Benefits

  • As with traditional note-taking, the process of writing the tests help commit the concept to memory.
  • The tests are there to reference in the future if I forget how something works.
  • The tests' passing shows that my understanding is correct.
  • I can update the tests when my understanding improves or I find a better approach.
  • I can re-run the tests when a major version of the language is released to see what is deprecated.

You might worry that this effort is duplicating existing tests from the language itself. Right! But the purpose here is to solidify and exercise your understanding. (Do feel encouraged to consult the official tests afterwards as you can learn even more.)

An example

I needed to write a method in Elixir that could take an optional block as an argument (this is a common thing in Ruby/Rails). I did some digging and found the do: block approach (example). Next I wrote a couple tests in my local elixir_learning app to document my understanding:

describe "methods that take an optional block" do
  defmodule OptionalBlock do
    @moduledoc """
    This works by specifying a method with and without the `do: block`
    argument.
    """

    def foo(arg1, arg2) do
      [arg1, arg2]
    end

    def foo(arg1, arg2, do: block) do
      [arg1, arg2, block]
    end
  end

  test "without a block" do
    assert OptionalBlock.foo("test", %{a: 1}) == ["test", %{a: 1}]
  end

  test "with a block" do
    x = :example_result_of_block

    result =
      OptionalBlock.foo "test", %{a: 1} do
        x
      end

    assert result == ["test", %{a: 1}, :example_result_of_block]
  end
end

Now I understand the behavior. Whenever I forget how this works, I can come back to it for reference.

Further exploration

After writing the initial examples, I try to probe the edges of my understanding.

Looking at the code now, it isn't clear when block execution happens. Is it lazy (like in Ruby) or does it happen at function call time?

Let's write another test to find out:

# higher in the test file
import ExUnit.CaptureIO

test "time of block execution" do
  defmodule UnusedBlock do
    def foo(do: _block) do
      # intentionally not using _block
    end
  end

  assert capture_io(fn ->
           IO.puts("Before")

           UnusedBlock.foo do
             IO.puts("Block")
           end

           IO.puts("After")
         end) == "Before\nAfter\n"
end

Here we're making a guess at what the output might be. UnusedBlock.foo never uses block. If the do/end block is lazily-evaluated then we will only see the "Before" and "After" lines.

Running the test shows:

1) test methods that take an optional block time of block execution (OptionalBlockTest)
   test/optional_block_test.exs:36
   Assertion with == failed
   code:  assert capture_io(fn ->
            IO.puts("Before")
            UnusedBlock.foo() do
              IO.puts("Block")
            end
            IO.puts("After")
          end) == "Before\nAfter\n"
   left:  "Before\nBlock\nAfter\n"
   right: "Before\nAfter\n"
   stacktrace:
     test/optional_block_test.exs:43: (test)

Our guess was incorrect: the "Block" line is present. This means the block is not lazily-evaluated. It is evaluated at the time the function is invoked.

This behavior is good to remember for the future and worth codifying in the test. We update our assertion to include the "Block" output line (we do expect it now) and update the test name to mention how the block is always invoked even if it goes unused.

Now I know a little more about how Elixir handles block arguments by default and I have executable notes to look back on.

If you want to learn more, Henrik Nyh has helpful background info and discusses using macros for lazy evaluation in his excellent Elixir block keywords article.


I've only been using this approach for a short while so I can't comment on the long-term benefits or downsides. I can say that I've really been enjoying the benefits mentioned above — particularly being able to experiment with my understanding in a reproducible way.

See also:


Rails Route Completion with fzf in Vim

2017-11-14

My work codebase has a lot of routes. If your Rails app has been around awhile, it likely does too.

You probably know this routine:

  • start to type a route and realize you don't remember it precisely
  • leave your editor to run rake routes (maybe with grep)
  • comb through the output to find the route you wanted
  • copy and paste it back into your editor
  • promptly forget the route so you can do the same thing next time

I decided to fix this using fzf completion in Vim so I never have to leave the editor.

Now the routine is:

  • start to type a route and realize I don't remember it precisely
  • hit <c-x><c-r> and fuzzy-complete the route
  • promptly forget the route so I can do the same thing next time

Here's what this looks like.

And here's the code (using fzf#complete from fzf.vim):

function! s:parse_route(selected)
  let l:squished = substitute(join(a:selected), '^\s\+', '', '')
  return split(l:squished)[0] . '_path'
endfunction

inoremap <expr> <c-x><c-r> fzf#complete({
  \ 'source':  'rake routes',
  \ 'reducer': '<sid>parse_route'})

The code is a bit naive but it solves the problem well enough for now.

Note: Example routes are from Thredded ("The best Rails forums engine ever").