Today I Learned

A Hashrocket project

Ready to join Hashrocket? Find Openings here and apply today.

31 posts by philcapel

How to conditionally add a value in array literals

I really like declarative things. It’s probably why I like React, and really dig functional programming approaches. So when I’m writing Ruby, sometimes I find myself wanting to delcare some array of values to use in a map, reduce, or each. The problem is, sometimes I only want a particular value give a condition. One pattern that I’ve started to try on for size is

def things
  [something, (something_else if reasons)]
end

Then, you can use it like so:

things.compact.map(&:cool_method)

The conditional will evaluate and leave a nil in the array, which isn’t my favorite. However, I’ve found that this is a very useful pattern for simplifying certain methods.

How using fzf in new places rocks

Thanks to joshbranchaud and gabrielreis for these:

Fuzzy git checkout (alias in git config):

[alias]
    fco = !git branch | fzf +m | awk '{print $1}' | xargs git checkout

Then just use git fco and get an interactive fzf session to look through the available branches!

Fuzzy rails routes:

rails routes | fzf

Fuzzy search your routes without needing to run rails routes multiple times!

How to show constraints in MySQL

Working in Postgres, I’ve gotten used to seeing the contraints on a table listed with \d. Working in MySQL I wasn’t seeing similar with describe <table>;. Turns out you can see all the constraints by selecting from information_schema.referential_constraints:

SELECT * 
  FROM information_schema.referential_constraints 
  WHERE constraint_schema='db_name' 
  AND table_name='table_of_interest';

About cool ways to use file marks

File marks in vim are just marks made with [A-Z]. They have the delightful property that they persist (via .viminfo) across sessions without needing a session (depending on your settings). I’ve known about them for a while, but I wasn’t sure if there was really a way that I could come up with the use them. So I went out looking and found something that I like!

`T -> project todo file
`V -> .vimrc

I’ll probably come up with some other ideas based on the invariant files that I frequently visit, but these two are definitely going into my rotation. I’m also thinking that at least one more should be added:

`L -> TIL file (ABT - always be TILin)

cReddit where credit is due

About rspec test tags

You want a way to run a specific sub-set of your specs. Well RSpec has a tag metadata that you can add to make things a lot nicer:

describe "my awesome code", :awesome do
  # some set of awesome tests
end

Now I just run rspec --tag awesome and you’re just going to run your block!

It also takes a negation, which means that if you want to run everything else you just use rspec --tag ~awesome.

You can also use this to add behavior to the way examples are dealt with via RSpec.configure

RSpec.configure do |config|
  config.append_after : each do |ce|
    if ce.metadata[:awesome]
         # do something awesome to the example
    end
  end
end

About `:aggregate_failures` for rspec

When writing rspec tests you can separate your test cases as in:

describe "foo" do
  specify { expect(some_behavior).to be_well_behaved }
  specify { expect(some_other_behavior).to be_well_behaved }
end

Or if you prefer to write:

describe "foo" do
  it "handles some behaviors" do
    expect(some_behavior).to be_well_behaved
    expect(some_other_behavior).to be_well_behaved
  end
end

The second example has the downside that it will fail out of the it block if the first expect doesn’t pass. To get around this, you can use:

describe "foo" do
  it "handles some behaviors", :aggregate_failures do
    expect(some_behavior).to be_well_behaved
    expect(some_other_behavior).to be_well_behaved
  end
end

Which will run the specs and collect their failures.

About nvim SHADA (Shared Data) file and Sessions

Ever stop your vim and get really depressed that you just lost your buffers? Cause I’ve done it twice today. The relevant details about sessions:

   The following command creates a session file:

    :mksession vimbook.vim

Later if you want to restore this session, you can use this command:

    :source vimbook.vim

If you want to start Vim and restore a specific session, you can use the
following command:

    vim -S vimbook.vim

This tells Vim to read a specific file on startup.  The 'S' stands for
session (actually, you can source any Vim script with -S, thus it might as
well stand for "source").

Your sessionoptions needs to include buffers. The SHADA file is like adding steroids. You can keep marks, register contents, and command line history.

Relevant documentation

Use Enzyme's `wrappingComponent` option in `mount`

Also works with shallow:

const provided = {super: 'cool', object: ['of', 'things']};

// This means that the root of the tree is going to be the provider
describe('Some Component', () => {
  it('does cool things when props change', () => {
    const target = mount(
      <CoolProvider thing={provided}
        <Component changeMe={false} />
      </CoolProvider>
  })
})

// This means that the root of the tree will be your Component
describe('Some Component', () => {
  it('does cool things when props change', () => {
    const target = mount(<Component changeMe={false} />, {
      wrappingComponent: CoolProvider,
      wrappingComponentProps: {super: 'cool', object: ['of', 'things']}
  })
})

This pattern is particularly meaningful when you want to call setProps or other methods that are only valid on the root of the component tree, because dive can’t help you there. If you want to change the props on both, you can use target.getWrappingComponent() to get at the wrapping component in the same way!

https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/getWrappingComponent.html

Rails `travel_to` changes the value of `usec` to 0

https://github.com/rails/rails/blob/4dcc5435e9569e084f6f90fcea6e7c37d7bd2b4d/activesupport/lib/active_support/testing/time_helpers.rb#L145

In and of itself, not a huge deal. However, when you combine it with the way that active record quotes times:

https://github.com/rails/rails/blob/4dcc5435e9569e084f6f90fcea6e7c37d7bd2b4d/activesupport/lib/active_support/testing/time_helpers.rb#L145

It can lead to some incredibly subtle bugs in your tests that use queries in their assertions. Long story short, if your query relies on a time comparison, you may not return anything. :|

Change up to next underscore "_" in vim

ct_ will change up to the underscore and leave it be.

cf_ will change up to the underscore and eat it as well.

Alternatively, you can use set iskeyword-=_ which will make the “_” character a valid word boundary. This might be preferable if you, like me, tend to use ciw more often than just cw.

EDIT: Because iskeyword is how the syntax highlighting is managed, you’ll probably notice that for methods/functions that contain keywords the highlighting is strange after playing with this. I found that I like nnoremap <leader>e :set iskeyword-=_<cr>diw:set iskeyword+=_<cr>i as a way to allow me to more easily edit the words I want without messing with the highlighting.

Default Hash values are super easy in Ruby

Want a list?

list_default = Hash.new([])

That’s pretty darn easy.

Edit: Thanks to @kaokun on twitter for catching this and correcting me. This will return the same array each time (having flashbacks to the first time I used mutable keyword args in python). To get a different array, you need need to pass a block with the behavior:

list_default = Hash.new { |hash, key| hash[key] = [] }

Just because your language hides pointers and references doesn’t mean you can ignore them I guess.

AddEventListener doesn't duplicate named functions

When adding an event listener to a target in javascript, using the EventTarget.addEventListener function allows you to add a listener without clobbering any others that may be registered to that target. This is really nice and all, but if you have a script that may or may not be reloaded multiple times you can end up with this function getting called quite a bit. If you’re like me and you use anonymous functions like they’re going out of style, then you can end up attaching multiple handlers, because they aren’t recognized as the same function, eg

const div = document.createElement('div');
div.addEventListener('click', event => (fetch('mutate/thing'));

This could end up running the fetch the same number of times that the script is loaded. If instead you do:

function mutateMyThings(event) { fetch('mutate/thing') };
div.addEventListener('click', mutateMyThings);

Then you don’t have to worry about it, since adding a named function will automatically only add it once.

Proc Composition Operator in Ruby #functionalruby

Function composition is really nice, and having a clean way to do it is important. Ruby 2.6 introduced a really neat way of handling it with procs:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self << mult_self
add_and_mult[3] # 18

If you want the application to be reversed:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self >> mult_self
add_and_mult[3] # 36

This can be made into a way of building up pipelines for single argument procs by declaring them in an Array and using #inject with an identity proc:

calculations = [
->x { x / 3 },
->x { x + 5 },
->x { x * 2 }
]
pipeline = calculations.inject(->x {x}) do |built, func|
build << func
end
pipeline[3] # 8

This can be super cool for things like ActiveRecord objects where you want to chain certain operations without necessarily making them explicit methods via scopes or similar.

Tmux Shortcuts I use

Create a new window

<tmux-leader>c

Think ‘c’ as in create. Rename window

<tmux-leader>,

That’s a comma. I don’t have anything clever for this one. Create a new pane

<tmux-leader>%

Maybe you’re clever enough to come up with something for this? I just memorized it. Kill a window ( like with a hung terminal )

<tmux-leader>&

You can also create a new session without calling new-session in full, you just use

tmux new -s <session-name>

Which is a bit quicker to type. Unless you like to type, you know, you do you.

Variable arguments and map in JS can hurt

Given some array of numbers as strings:

const someNumStrings = ['1', '2', '05', '68'];

If you want them to be numbers, then you might be tempted to do something like:

someNumStrings.map(parseInt)

Which would be fine if parseInt didn’t allow multiple arguments, and if map only sent in the single element. But that’s not how it works, so what you end up getting is a bit of a mess.

[1, NaN, 0, NaN]

The parseInt function takes a radix as the second argument (and realistically anything else you want to pass to it won’t cause it to explode). The Array.map method takes a callback (in this case parseInt) and gives that little sucker all the data you could want! In this case, the index is being passed as the radix, and parseInt doesn’t care about the others.

TL;DR: map(el => parseInt(el)) >>>>>>>>>> map(parseInt) and if you ever intentionally encode the radix as the index of the element you’re parsing… may god have mercy on your soul.

Access a shadowDOM node from Capybara

Using selenium_headless you can access a node from the shadowDOM using evaluate_script. The trick is knowing where in the showdowRoot.childNodes it’s going to be. If you have a web component called my-dope-component then you could do something like this:

shadow_host = find('my-dope-component')
shadow_child = evaluate_script('arguments[0].shadowRoot.childNodes[<magic-index>]', shadow_host)

The value for <magic-index> depends on the structure of the template that you’re using, as well as the library (if any) that you’re using. You could also write a find style script that iterates the children and locates the correct one based on the tagName property or similar.

For example:

FIND_NODE = <<-SCRIPT
Array.from(arguments[0].shadowRoot.childNodes).map(element => {
  if (element.tagName === arguments[1].toUpperCase()) return element;
}).filter( value => value != undefined)[0]
SCRIPT

Ergodox vim input for numpad keys can be wonky

I use the ergodox ez keyboard. When you’re setting it up you can select either the numpad value of a key, or the shifted/regular value. These will behave slightly differently depending on your settings for application keypad mode. This is something that is set at the application layer, so you may end up dealing with vim recieing <esc>Ok if you are trying to type +. You can either try to find the way to get your application (in my case Manjaro’s terminal emulator) to change the keypad mode (I couldn’t find that), or you can setup an innoremap section in your vimrc to cover the cases where you definitely don’t want the escape sequence.

:inoremap <Esc>Oq 1
:inoremap <Esc>Or 2
:inoremap <Esc>Os 3
:inoremap <Esc>Ot 4
:inoremap <Esc>Ou 5
:inoremap <Esc>Ov 6
:inoremap <Esc>Ow 7
:inoremap <Esc>Ox 8
:inoremap <Esc>Oy 9
:inoremap <Esc>Op 0
:inoremap <Esc>On .
:inoremap <Esc>OQ /
:inoremap <Esc>OR *
:inoremap <Esc>Ol +
:inoremap <Esc>OS -
:inoremap <Esc>OM <Enter>

This sequence is for PuTTY, but you can see which key to put after the <Esc>O by looking at what registers on keydown events. Alternatively, for ergodox users, only use the shifted/regular values for keys.

Couldn't decrypt `config/credentials.yml.enc`

Rails 5.2 introduced the credentials system for managing secure credentials in your application. You can edit the file with rails credentials:edit, which is pretty dope. But if you do like I did and you start your project on one machine and go to try and edit that file on a different machine after you pull down the repository from Github, it will generate a new master.key file for you. This will not work to decrypt the file, and you’ll get the error above. You need to make sure that you grab the file from wherever you started your application from. In my case it was as simple as using scp to grab the file from a different machine.

Search and Replace Control Characters In Vim

In vim, if you want to search and replace non-printable control characters (for example you have ^M scattered throughout your file and it’s messing up an import) you can use ctrl+v+<char>. The ctrl+v allows you to type the control character, but you must hold down control for both of the key presses.

:%s/ctrl+v+M/

This would search and replace all non-printable ^M characters in your text.

Slack Hot-Keys Rock

Do you like slack? Chances are it doesn’t really matter whether you do or don’t. It’s a matter of course at this point that you will probably work with it. Because that’s the case, you should at least strive to be as aware of neat features as possible!

To that end, you should check out the built-in hot-key help using:

MacOS: Command + / Linux/Windows: Ctrl + /

I’m particularly fond of the “Navigation” and “Channels & Direct Messages”!

Seeing tmux messages easier

Using tmux is pretty slick, until something doesn’t work and you want to see the error message for longer that about 750ms. That’s the default setting for the messages to be displayed.

Set a new display time

For the current session

:set-option display-time <milliseconds>

So set-option display-time 4000 would set your display time to 4 seconds.

Globally

Add the -g flag.
Or add it to your tmux.conf to hold on to the configuration:

set-option -g display-time <milliseconds>

View all messages

If you want to view all the recent messages, then you can use

:show-messages

Default hot-key

<tmux-leader>~

To close out of the messages, just press <enter>.

Rails CollectionProxy #length vs #count

When testing a controller, it’s not unreasonable to right a test something like this:

expect(post_to_endpoint).to change { some.objects.length }.from(0).to(1)

Because of the details of CollectionProxy, this will not work as expected. Instead, you need to write:

expect(post_to_endpoint).to change { some.objects.count }.from(0).to(1)

The length method will evaluate to the same value, while the count method will reflect changes to the underlying data since the last call.