WYSIWYG with Action Text
Rails 6 includes a WYSIWYG editor, Action Text. It works out of the box quite well, but chances are that you want to add some custom functionality. This card contains some tips how to achieve this.
Setup
Basically, follow the guide in the Rails documentation. The automated script may not work with the way webpacker is configured in your project, but it should be easy to fix.
If you don't want the default c...
GoodJob: Ensure you get notified about background exceptions
GoodJob and ActiveJob rescue exceptions internally, preventing exception_notification from triggering. This can cause silent job failures.To get notified, subscribe to ActiveJob events and configure GoodJob's on_thread_error hook. This lets you manually call your exception notifier for every retry, discard, or internal GoodJob error.
# config/initializers/good_job.rb
# Manually notify on job failures, as they are handled internally by ActiveJob/GoodJob.
ActiveSupport::Noti...
How to enable pretty IRB inspection for your Ruby class
When Ruby objects are inspected in any modern IRB, some objects (like ActiveRecord instances) are rendered with neat colors and line breaks.
You will not get that for custom classes by default -- which can be annoying if your inspection contains lots of meaningful information.
Here is what you need to do if you want your objects to be inspected nicely.
Implement a pretty_print method
As an example, consider the following class.
class MyClass
# ...
def inspect
"#<#{self.class} attr1: #{attr1.inspect}, attr2: #{attr2...
How the Date Header Affects Cookie Expiration and Caching
tl;dr
When a cookie includes an
Expiresattribute or an HTTP response includes caching headers likeExpiresorCache-Control, their validity depends on the server'sDateheader if present. Otherwise, the browser uses its local time. This can lead to issues in tests with mocked time or inconsistent cache behavior.
Cookie Expires depends on the Date header or browser time
When a cookie includes an Expires attribute, the browser evaluates the expiration date relative to a reference time:
- If the HTTP response ...
has_one association may silently drop associated record when it is invalid
This is quite an edge case, and appears like a bug in Rails (4.2.6) to me.
Update: This is now documented on Edgeguides Ruby on Rails:
If you set the :validate option to true, then associated objects will be validated whenever you save this object. By default, this is false: associated objects will not be validated when this object is saved.
Setup
# post.rb
class Post < ActiveRecord::Base
has_one :attachment
end
# attachm...
Capybara: Preventing server errors from failing your test
When your Rails application server raises error, Capybara will fail your test when it clears the session after the last step. The effect is a test that passes all steps, but fails anyway.
Capybara's behavior will help you to detect and fix errors in your application code. However, sometimes your application will explode with an error outside your control. Two examples:
- A JavaScript library references a source map in its build, but forgets to package the source map
- CarrierWave cleans up an upload or cache file after the record was delet...
Center a float horizontally
This card shows you how to center a float horizontally in CSS. Also: find out what techniques are available for your use case and browser requirements in the card linked below.
Note: We have card with all CSS centering options. You probably want to head over there and get an overview over what techniques are available for your use case and browser requirements.
If you cannot use display: inline-block, centering a float ...
Switching the package manager from yarn to npm
We recently migrated a Rails application from yarn to npm. We decided to go this step instead of upgrading to > Yarn 2.0 to reduce the number of dependencies in our project.
Migration
- Remove the
yarn.lockfile - Remove the
node_modulesfolder - Run
npm install - Replace all occurrences of
yarnwithnpmin your project
Notes
- With
npmvendored packages with dependencies create their ownnode_modulesfolder within the vendor path. We...
Making minimal updates to DOM trees using morphdom / idiomorph
When you replace parts of the DOM with new HTML, using .innerHTML = newHtml is usually the simplest and fastest option. It comes at the price of your DOM elements losing state, like input values, scroll position, progress in a video player, or even more complex state for custom elements.
One option to avoid this are libraries like morphdom (as used by Phoenix Liveviews) or idiomorph (as used by Rails' Turbo).
It lets you write
morphdo...
JSON APIs: Default design for common features
When you build a JSON API you need to come up with a style to represent attributes, pagination, errors or including associated objects. Instead of reinventing the wheel, you may reuse successful API designs.
JSON API
JSON:API specifies common features found in most JSON APIs, like pagination, ordering and nested resources. The spec looks very similar to how one would build an API with Rails and uses similar patterns. Even if you don't plan on supporting the whole spec, it can still make sense to know how th...
How to turn images into inline attachments in emails
Not all email clients support external images in all situations, e.g. an image within a link. In some cases, a viable workaround is to turn your images into inline attachments.
Note
Rails provides a simple mechanism to achieve this:
This documentation makes it look like you have to care about these attachments in two places. You have to create the attachment in t...
ActiveRecord: count vs size vs length on associations
TL;DR: You should generally use #size to count associated records.
size
- Counts already loaded elements
- If the association is not loaded, falls back to a
COUNTquery
count
- If a counter cache is set up, returns the cached value
- Issues a
COUNTquery else
Caveats
- If you trigger a
COUNTquery for an association of an an unsaved record, Rails will try to load all children where the foreign keyIS NULL. This is not what you want. To prevent this behavior, you can useunsaved_record.association.to_a.size. - `c...
Disable PostgreSQL's Write-Ahead Log to speed up tests
The linked article suggests an interesting way to speed up tests of Rails + Postgres apps:
PostgreSQL allows the creation of “unlogged” tables, which do not record data in the PostgreSQL Write-Ahead Log. This can make the tables faster, but significantly increases the risk of data loss if the database crashes. As a result, this should not be used in production environments. If you would like all created tables to be unlogged in the test environment you can add the following to your...
Disable SimpleCov if you only run a fraction of your tests
Coverage reports are rarely useful if you run only small parts of your test suite.
Just do not load SimpleCov in this case, and you will see less noise in your test output:
if RSpec.configuration.files_to_run.count > 5
require "simplecov"
SimpleCov.start 'rails'
end
See also
How to tell ActiveRecord how to preload associations (either JOINs or separate queries)
Remember why preloading associations "randomly" uses joined tables or multiple queries?
If you don't like the cleverness of this behavior, you can explicitely tell ActiveRecord how to preload associations with either JOINs or separate queries.
This card gives an overview of the different options to preload associations, but
__Whic...
Ensure passing Jasmine specs from your Ruby E2E tests
Jasmine is a great way to unit test your JavaScript components without writing an expensive end-to-end test for every small requirement.
After we integrated Jasmine into a Rails app we often add an E2E test that opens that Jasmine runner and expects all specs to pass. This way we see Jasmine failures in our regular test runs.
RSpec
In a [feature spec](https://web.archive.org/web/20150201092849/http://www.rel...
PSA: Chrome and Firefox do not always clear session cookies on exit
Cookies without an expiration timestamp are called "session cookies". [1] They should only be kept until the end of the browsing session.
However, when Chrome or Firefox are configured to reopen tabs from last time upon start, they will keep session cookies when closing the browser. This even applies to tabs that were closed before shutting down the browser.
This is by design in Chrome and [Firefox](https://bugzilla.mozilla.org/buglist.cgi?bug_id=337551,345830,358042,362212,36...
Choosing the right gems for your project
Adding a gem means you take over the liability towards the external code.
Checklist
Based on "To gem, or not to gem":
- Gem is really needed (prefer writing your own code for simple requirements without many edge cases)
- Gem is tested well (coverage and quality)
- Gem has a good code quality
- Gem's licence fits to the project requirement
- Try to avoid gems that do much more than your requireme...
Verifying doubles in RSpec 3
RSpec 3 has verifying doubles. This breed of mock objects check that any methods being stubbed are present on an instance of a given class. They also check methods aren't called with the wrong number of arguments.
This dual approach allows you to move very quickly and test components in isolation, while
giving you confidence that your doubles are not a complete fiction.
You should always prefer using a verifying double to using an old-school mock...
How to find out which type of Spec you are
When you need to find out in which kind of spec you are during run-time, it's definitely possible. It's a lot easier in RSpec 2+.
For example, consider this global before block where you'd want to run some code for specific specs only:
config.before do
# stuff
that_fancy_method
# more stuff
end
RSpec 2+
If you want to run such a block for a specific type of specs, you can use filters:
config.before do
# stuff
# more stuff
end
config.before :type =...
RSpec: Inferring spec type from file location
RSpec Rails can automatically mix in different behaviors to your tests based on their type tag, for example enabling you to call get and
post in specs with the tag type: :request.
Alternatively you can skip these tags by setting the config config.infer_spec_type_from_file_location! in the spec_helper.rb. This will automatically choose the right type context based on the file location of the test.
For instan...
Heads up: pg_restore --clean keeps existing tables
When restoring a PostgreSQL dump using pg_restore, you usually add the --clean flag to remove any existing data from tables.
Note that this only removes data from tables that are part of the dump and will not remove any extra tables. You need to do that yourself.
When is this relevant?
As an example: You want to load a staging dump into your development machine. On your development machine, you have run migrations that introduced more tables which do not yet exist on staging. pg_restore with --clean will loa...
Rspec: around(:all) and around(:each) hook execution order
Background
-
before(:all)runs the block once before all of the examples. -
before(:each)runs the block once before each of your specs.
Summary
-
around(:suite)does not exist. -
around(:all)runs afterbefore(:all)and beforeafter(:all). -
around(:each)runs beforebefore(:each)and afterafter(:each).
As this is not 100% obvious (and not yet documented) it is written down in this card. In RSpec 3 :each has the alias :example and :all the alias :context.
Example
RSpec.configure do |config|
...
Don't require files in random order
A common pattern in Ruby is to to require all files in a specific diretory, using something like
Dir.glob(Rails.root.join('lib/ext/**/*.rb')).each do |filename|
require filename
end
However, this causes files to be required in an order determined by the file system. Since load order can be important, this may lead to different behavior on different machines which are hard to debug.
Simply add a .sort:
Dir.glob(Rails.root.join('lib/ext/**/*.rb')).sort.each do |filename|
require filename
end
Ruby 3
...