Today I Learned

hashrocket A Hashrocket project

15 posts by vidalekechukwu twitter @vidalekechukwu

RSpec's Render Template is Permissive

In a controller test, when you expect a request to render a template, you'd write something like this:

expect(response).to render_template(new_user_path)
Ruby

If your controller action looked like the following, the expectation would pass:

class UsersController < ActionController::Base
  def action
    ...
    render :new
  end
end
Ruby

render_template delegates to ActionController::TemplateAssertions#assert_template. What I didn't know, however, is how permissive (magical) it is.

Let's say instead of the action's render :new you returned a json object with the template, rendered to a string, as one of its values:

def action
  response_body = render_to_string(layout: false, template: "users/new.html.haml")
  render json: { page: response_body }
end
Ruby

expect(response).to render_template(new_user_path) still passess!!!

Also if you say expect(response).to render_template("new.html.haml"), it also passes. How does it know you meant the user's new.html.haml and not some other arbitrary one? Maybe because you're in the UsersControllerSpec. You can also expect...render_template for templates unrelated to the controller's scope.

I suspect that this permissiveness is caused by Rails registering the rendering of templates, irrespective of whether they are rendered to strings or directly as a response to a controller action. expect...render_template maybe then looks through the registered renders 🤷🏾‍♂️. Either way, it's magical!

React Fragments

Ordinarily React requires that each component returns a single child component. Sibling components cannot be returned in the render function:

// Valid
const MyComponent = () => (
  <div>
    <span>Chicken</span>
    <span>and</span>
    <span>Farm</span>
  </div>
);

// Invalid
const MyComponent = () => (
  <span>Chicken</span>
  <span>and</span>
  <span>Farm</span>
);
JavaScript

Sometimes, however, you have no need for the wrapper component and want to just render the siblings. React allows you to do this with the Fragment component, which satisfies the single child requirement without actually rendering a div in the DOM:

import {Fragment} from "react";

// Valid
const MyComponent = () => (
  <Fragment>
    <span>Chicken</span>
    <span>and</span>
    <span>Farm</span>
  </Fragment>
);

// Renders =>
<span>Chicken</span>
<span>and</span>
<span>Farm</span>
JavaScript

Read more about Fragments here: https://reactjs.org/docs/fragments.html

Ruby #try Operator

In Rails, there's a #try method that will attempt to call a method on an object. If the caller is not nil, it will return the result of the "tried" method. If the caller is nil, try will return nil:

chicken.try(:farm) # => "delicious" (assuming chicken is an object whose #farm function returns "delicious")

# otherwise

chicken.try(:farm) => nil
Ruby

Ruby 2.3.0 introduced the Safe Navigation Operator &. that is a shorthand for the try method.

chicken&.farm # => "delicious" (assuming chicken is an object whose #farm function returns "delicious")

# otherwise

chicken&.farm => nil
Ruby

(Shouts out to Dorian)

Spreading nil Into a Ruby Array

Spreading nil into an array doesn't add an entry into a Ruby array:

[*nil] # => []
a = [1, 2, 3]
[*nil, *a] # => [1, 2, 3]
Ruby

One might expect this to insert nil in the array, but this isn't the case. This feature could be useful when passing dynamically assigned, nullable items into an array.

def foo(arg)
  [*arg]
end

nullable_var = params[:foo]
foo(nullable_var).each { |x| act_on(x) } # => [] if nullable_var is nil
Ruby

Rendering Emojis With Unicodes in Javascript

Ordinarily you can render an emoji in Javascript by using its four-character Unicode code point as follows:

(React JS [JSX] Sample)

<span>{'\u263B'}</span> => <span></span>
JavaScript

Sometimes, however, the Unicode code point for an emoji is more than four characters long and the above method for rendering doesn't work. In ECMA6, the following syntax will work for emojis whose Unicode code point length is more than four (1):

<span>{'\u{1F4A9}'}</span> => <span>💩</span>
JavaScript

Note:

  1. This has only been tested with a five-character Unicode code point.

CSS Variables

CSS3 supports native variables that are assigned with the following syntax:

html {
  --foo-color: #FF0000;
  --bar-color: #00FF00;
}
CSS

Note the -- that precedes the variable's name. Now, the <html> element and all its descendants will be able to access the variables with the following syntax.

a {
  color: var(--foo-color);
}

button {
  background: var(--bar-color);
}
CSS

CSS uses the var function to look up variables.

You don't have to assign the variable to the <html> tag. It can be assigned to any element or selector's style block. Just remember, only that element and its children will have access to the variable ;-)

Futhermore, you can access the variables in Chrome's DevTool/Element Inspector!

NOTE: This feature is not supported at all in Internet Explorer and only has partial support in Microsoft Edge 15, the most recent Edge at this time of writing. Opera Mini users are out of luck as well :-(

Browser Support Chart

Accept Your Own Changes During Git Rebase

During a git rebase you may encounter conflicts in files between your current, HEAD, branch and the branch you're rebasing. Ordinarily, you'll want to go through each file and individually resolve each conflict to ensure that the proper changes are preserved.

Sometimes, however, you already know that you want to accept ALL the changes in a file on your local branch and discard the other branch's edits. Instead of opening the file, finding the conflict regions, then making the appropriate changes, you can more succinctly prefer your changes with the following command:

git checkout --ours /path/to/file.js
Sh

Conversely, if you want to keep the other branch's changes, run:

git checkout --theirs /path/to/file.ex
Sh

You can also do this for an entire directory:

git checkout --ours /path/to/dir/
git checkout --theirs . # Current working directory
Sh

When you [re]open the conflict files, you'll see that your preferred branch's changes have been written and the other branch's have been discarded.

After you've finished, stage the the conflict files, and continue your rebase:

git add /path/to/conflict_file.rb
git rebase --continue # or --skip
Sh

Combine Multiple Rake Commands Without &&

Ordinarily, to successively run commands whose execution depends on the previous command's success, you would separate the commands with two &'s. This includes rake commands:

rake db:migrate && rake db:redo && rake db:test:prepare
Ruby

The downside to this, however, is that the Rails environment is loaded with each rake invocation. Thus, in this example the Rails environment is loaded three times.

This is slow. To speed up the process, we can load the Rails environment just once as follows:

rake db:migrate db:redo db:test:prepare
Ruby

We still run all the tasks but don't have to wait an eternity for Rails to get its act together and load rake. Hooray!

Hat-tip @mrmicahcooper

Get a Random Record from an ActiveRecord Model

Let's say you have an events table with a model name Event. If you want to get a random event from the table, you could run

Event.find_by_sql(
  <<-SQL
    SELECT * FROM events ORDER BY random() LIMIT 1
  SQL
).first
Ruby

The functional part of this query is the ORDER BY random() bit. For every row that postgres is sorting, it generates a random number (between 0.0 and 1.0 by default). Then it sorts the rows by their randomly generated number. Read more about the postgres random() function at the documentation page.

Rails: Start Resque Worker for Async Mail Delivery

QUEUE=mailers rake resque:work
Ruby

Assuming you have properly configured Resque to work with ActiveJob, you will now have a worker waiting to execute mail delivery requests. Read more about how to execute these deliveries to take full advantage of the feature.

If you want to run the worker as a background process simply set the BACKGROUND environment variable before running the rake task:

BACKGROUND=1 QUEUE=mailers rake resque:work
Ruby

App-Wide Search-Replace in Vim

  1. In command-line mode, load all desired files into your argument list (arglist)
:args `git grep -l <string>`
vim

where <string> is the string you want to replace.

  1. Run a global search-replace across all the files in your arglist
:argdo %s/<string>/<replacement>/gce | update
vim

Note:

  • Search-replace flags:

    • g[lobal]: replace all matches
    • c[onfirm]: ask for confirmation before replacing
    • e[rror]: don't halt operation at errors
  • update saves each file after the replacement is made.

  • The search-replace will only apply to files checked into your git history. Be sure to commit the files you want searched before attempting this.