Ruby has a safe operator, no need to try anymore
Wow, how did I miss this memo? Ruby 2.3 introduced a safe operator
Instead of
current_user.try(:profile).try(:bio)
you can now do
current_user&.profile&.bio
Wow, how did I miss this memo? Ruby 2.3 introduced a safe operator
Instead of
current_user.try(:profile).try(:bio)
you can now do
current_user&.profile&.bio
If you're testing a ActiveRecord model mixin in your application, you might be tempted to unit test it in the context of one of your app's models. However, that would violate your test isolation and introduce complexities related to the behavior of the model.
Better solution is to make an Active Record class just for your test, and the fact that you can invoke schema definitions on the fly makes it super easy. Here's the top of one of my specs, illustrating the technique.
require 'rails_helper'
ActiveRecord::Schema.define do
create_table :test_objects, force: true do |t|
t.jsonb :jobs, null: false, default: {}
end
end
class TestObject < ApplicationRecord
include WorkerRegistry
end
RSpec.describe WorkerRegistry do
let(:test_object) { TestObject.create }
...
An aspect of Rails that I adore is how it has a place for nearly everything you need to do. One of those things is to format dates/times using the strftime method. Instead of tucking away custom strftime patterns in constants, you can configure them onto the native Rails formatter, accessed via time.to_s(:format_name)
DateTime formats are shared with Time and stored in the Time::DATE_FORMATS hash. Use your desired format name as the hash key and either a strftime string or Proc instance that takes a time or datetime argument as the value.
# config/initializers/time_formats.rb
Time::DATE_FORMATS[:month_and_year] = '%B %Y'
Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
Here's one of the formats that I've been using lately, to get my times into a more familiar form.
Time::DATE_FORMATS[:short_time] =
lambda { |time| time.strftime('%I:%M%p').gsub('AM','am').gsub('PM','pm').gsub(':00','') }
I learned about Toastr JavaScript library last week and have been delighted to use it instead of more traditional flash messaging.
First of all get the Toastr sources. I opted to link to them on CDNJS:
= stylesheet_link_tag 'https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css'
= javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js'
Next I defined some extra flash types in my application_controller.rb file to match Toastr's native notification types and enable use of the built-in styling.
class ApplicationController < ActionController::Base
add_flash_types :success, :info, :warning, :error
...
Finally, add the following block of JavaScript to the bottom of a layout template (or whatever shared partial that contains your JS and CSS includes.
- flash.keys.each do |key|
- toastr_key = key
- toastr_key = 'info' if key == 'notice'
- toastr_key = 'warning' if key == 'alert'
:javascript
$(function() {
toastr["#{toastr_key}"]("#{flash[key]}");
});
Lines 2 and 3 establish a mapping from conventional Rails notice and alert so that I don't have to hack libraries like Devise which rely on them.
Easy.
This finding was a pleasant surprise. For years, I've been writing the same kind of boilerplate code to override to_param on my model classes and generate unique slugs. Turns out there's a really well-written library that does that, with some worthwhile additional functionality. Check out FriendlyId for easy slug generation and even the ability to preserve history of slugs after changes, so that it's possible to do 301 redirects with just a couple lines of code.
It's better for at least a couple of reasons, eloquently stated in this blog post by Michal Orman
...we can set default values or handle - providing a block - gracefully missing keys. Also using
fetchwith unknown key will raiseKeyErrorthat will tell us which exactly key is missing. That is in fact the behavior we are expecting from the app. Without required settings is just not working and complaining about missing setting and not about some random nil references.
Got that? Instead of this:
AWS.config(
access_key_id: ENV['S3_ACCESS_KEY'],
secret_access_key: ENV['S3_SECRET_KEY'],
region: ENV['S3_REGION']
)
Do this:
AWS.config(
access_key_id: ENV.fetch('S3_ACCESS_KEY'),
secret_access_key: ENV.fetch('S3_SECRET_KEY'),
region: ENV.fetch('S3_REGION')
)
Make sure to pass the migration a native Ruby hash as the default value. DO NOT pass it a string representation of an hash, thinking that it'll work (as valid JSON).
DO THIS
t.jsonb :preferences, default: {}, null: false
NOT
t.jsonb :preferences, default: '{}', null: false
It'll break in a maddeningly non-obvious way. Take my word for it. Also there is this relevant StackOverflow post which saved my ass.
The handy-dandy Nodemon tool is not just for Node. Today I whipped up an invocation that can restart my Rails server whenever there are changes in the config directory tree. Super useful when working heavily with i18n, since changing translation files requires bouncing the server to see changes reflected in the view.
$ nodemon --watch config -e rb,yml --exec "rails server"
Ruby 2 gained keyword arguments while I was away in Javascript-land the last few years, and somehow I missed the memo. Loving the elegance of how you specify defaults and required parameters.
Thoughtbot has the best writeup.
I suspect I will not be using non-keyword arguments in my Ruby code anymore.
Today I woke up to an exception RuntimeError: can't modify frozen String
I looked at the code that had raised the exception. It was doing a gsub! on a value out of params. It took me a bit of digging to figure out why it failed. Turns out there's an edge case that causes one of those values to get pulled out of ENV instead of the normal flow where it originates from the user.
Strings coming out of ENV are frozen. D'oh!
In normal unit testing, you say expect(person.calculate_bmi).to eq(21) or something like that. Approvals allow you to assert the state of complex objects against a known state.
For example, you can say, Approvals.verify(person)
I had not heard of this approach, which is posed as an alternative to traditional TDD. http://approvaltests.com
File this one under huge pain in the ass... Dropbox is ending support for public folders as of September 17th, 2017. Luckily that's a lot of lead time.
Description of the new Shared Files functionality that replaces public folders.
You are using HTML5 typed text input fields, aren't you?
If you make an input element with type="email" and you make it required, then the browser will take care of making sure that the email is valid on form submit. Automatically.
Doesn't eliminate the need for validation on the server side, or does it?
This product just blew my mind, since I know how difficult it is to keep up with Node dependencies. You could manually track updates of your dependencies and test whether things continue to work. But it takes a lot of effort. So most of the time, your software is in a Schrödinger state of being potentially broken, and you have no idea until you run npm install and try the next time.
Greenkeeper handles the chores of dependency management. npm install and npm test is called immediately after an update. There is no doubt about the state of your software.
Rails 4 added support for enumerations in Active Record classes. That's cool, but what's cooler is how it has been reimagined by Foraker Labs in Denver, based on the seriously underrated gem Enumerated Type.
Please go read the blog post about it right now, it'll take 5-10 minutes and I promise you won't regret it.