Beware of "nil" values in params

Recent rails security updates have shown that people make incorrect assumptions about the possible contents of the params hash.

Just don't make any! Treat it as what it is: potentially unsafe user input. For example:

/pages/edit?foo    --> params == {:foo => nil}
/pages/edit?foo[]  --> params == {:foo => [nil]} # at least in older rails 3 and in rails 2.x

Be especially wary about stuff like

User.find_by_password_reset_token(params[:password_reset_token])

If params[:password_reset_token] is nil, you'll...

Ruby blocks: Braces and do/end have different precedence

TL;DR {} binds stronger than do … end (as always in Ruby, special characters bind stronger than words)

Demo

Right way

names = ['bRUce', 'STaN', 'JOlIE']

# Blocks in braces are passed to the rightmost method
print names.map { |name| name.downcase }
print(names.map do |name| name.downcase end) # equivalent
=> ["bruce", "stan", "jolie"]

Wrong way

Avoid the examples below, as you pass at least one block to print and not to the enumerator.

names = ['bRUce', 'STaN', 'JOlIE'] 

# Blocks in do…end are pas...

Tod: A Gem for handling daytime without a date

Tod is a gem for working with daytimes.

Another additional gem?

Thus SQL has a time datatype for storing time of day in the format hh:mm:ss, neither Ruby nor Rails themselves offer an elegant way to deal with day times.

Time and DateTime both handle daytime values AND calendar date, using them to only store the time of day will end in inconsistent and thus confusing data, e. g. Time.new will initialize with the current Time in your Timezone, DateTime.new initializes at January 1, at an undefined year, without a timezone o...

Capistrano: Configure environment specific array attributes

Using Capistrano, we usually have some array configurations in the config/deploy.rb file, like set :linked_files, %w[config/database.yml], so in this case we don't have to manage the database configuration individually on every server.

In a specific case, one of our projects supports sign in by SAML, but not every deploy target has this feature activated. Here comes a nice handy Capistrano feature, which lets us modify the default configuration for individual env...

Rails I18n scope for humanized attribute names

ActiveModel classes have a class method .human_attribute_name. This returns a human-readable form of the attribute:

Person.human_attribute_name(:first_name) # => "First name"

By default Rails will use String#humanize to format the attribute name, e.g. by replacing underscores with spaces and capitalizing the first word. You can configure different translation in your I18n locales, e.g. in config/locales/en.yml:

en:
  activerecord:
    attributes...

Deployment: Merge consecutive commits without cherry-picking

You want to deploy new features but the latest commits are not ready for production? Then use git merge master~n to skip the n-last commits.

Tip

A big advantage of merging vs. cherry-picking is that cherry-picking will create copies of all picked commits. When you eventually do merge the branch after cherry-picking, you will have duplicate commit messages in your history.

Example

It's time for a production deployment!

git log --pretty=format:"%h - %s" --reverse origin/production..origin/master

0e6ab39f - Feature A
6396...

The Framework Field Guide - Fundamentals | Unicorn Utterances

I used two lab days to read the The framework field guide - Fundamentals, the first of a three part series to learn the basics of frontend technologies. I can highly suggest it for learning the fundamentals. 'The framework field guide' is written by Unicron Utterances and there side has many high quality articles on web development and computer science related to programming.

[The Framework Field Guide](https://unicorn-ut...

RSpec: Applying stubs only within a block

When you mocked method calls in RSpec, they are mocked until the end of a spec, or until you explicitly release them.

You can use RSpec::Mocks.with_temporary_scope to have all mocks applied inside a block to be released when the block ends.
Example:

RSpec::Mocks.with_temporary_scope do
  allow(Rails).to receive(:env).and_return('production'.inquiry)
  puts Rails.env # prints "production"
end
puts Rails.env # prints "test"

Note that, when overriding pre-existing mocks inside the block, they are not reverted to the previously ...

Configuring ActionMailer host and protocol for URL generation

When you generate a URL in a mailer view, ActionMailer will raise an error unless you previously configured it which hostname to use.

There are two options to set the default_url_options of ActionMailer:

  1. Hardcoded solution (preferred solution when using Rails with ActiveJob/Sidekiq or Cronjobs)
  2. Dynamic solution

1. Hardcoded solution

When you are sending mails from outside the request cycle, e.g. ActiveJob/Sidekiq or Cronjobs, y...

Modern HTTP Status codes for redirecting

Formerly 301 (Moved Permanently) and 302 (Found) were used for redirecting. Browsers did implement them in different ways, so since HTTP 1.1 there are some new status codes which allow for finer distinctions.

The interesting part is how non-GET requests are handled by the redirect. It is preferrable to use the newer status code to avoid unexpected behavior.

303 See Other

The response to the request can be found under anot...

How to list updateable dependencies with Bundler and Yarn

Bundler

bundle outdated [--filter-major|--filter-minor|--filter-patch]

Example output for bundle outdated --filter-major

Image

Other examples

A useful flag is --strict as it will only list versions that are allowed by your Gemfile requirements (e.g. does not show rails update to 6 if your Gemfile has the line gem 'rails', '~>5.2').

I also experienced that doing upgrades per group (test, development) are easier to do. Thus --groups might also be helpful.

$ bundle...

angular_xss 1.0 released (final release)

A bug report triggered us to

  1. improve support of angular_xss for HAML6 and Rails 7.1
  2. find out that the gem is no longer needed for any of our projects in active development

angular_xss 1.0 is thus the final release, it's now marked as unmaintained

1.0 2024-07-02

Compatible changes

  • Bump version to 1.0 as this gem has been production-ready for 10 years
  • Declare the gem to be unmaintained
  • Add compatibility with Rails 7.1
  • Add compatibility with HAML 6
    • NOTE: Don't use HAML 6.0.0. AngularXSS relies on a patch [introdu...

Updated: CarrierWave: Processing images with libvips

mutate blocks are only required for draw operations, and for writing metadata. You cannot call operations like scale() on mutable images.

Even though most libvips operations return a new, immutable Vips::Image, libvips uses a clever internal representation that avoids the need to allocate memory for every intermediate image. See the How it works section on libvips.org for details.

Updated: Don't name columns like counter_cache columns in Rails pre v4.2.4

Added a link to the commit resolving the issue and included the affected rails versions.

Caveat when using Rails' new "strict locals" feature

In Rails 7.1 it has become possible to annotate partials with the locals they expect:

# partial _user_name.erb
<%# locals: (user:) %>
<%= user.name %>

# view
<%= render 'user_name' %> <%# this raises an ArgumentError %>

Unfortunately, when some other code in that template raises an ArgumentError (for example an error in the User#name method) you will end up with a confusing stacktrace that looks like you have an error in your render call.

If th...

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...

Async control flow in JavaScript: Promises, Microtasks, async/await

Slides for Henning's talk on Sep 21st 2017.


Understanding sync vs. async control flow

Talking to synchronous (or "blocking") API

print('script start')
html = get('/foo')
print(html)
print('script end')

Script outputs 'script start', (long delay), '<html>...</html>', 'script end'.

Talking to asynchronous (or "evented") API

print('script start')
get('foo', done: function(html) {
  print(html)
})
print('script end')

Script outputs 'script start', 'script end', (long ...

Authorize allowed values with assignable_values

All our projects have enum-like requirements like this:

  • An attribute value must be included in a given set of values.
  • The list of allowed values must be retrievable in order to render <select> boxes.
  • Each value has a humanized label.
  • Sometimes there is a default value.

Most of the time, this requirement is also needed:

  • The list of assignable values depends on the user who is currently signed in.

In our past projects there are many different solutions for these related requirements, e.g. ChoiceTrait, methods like `available_...

rails_state_machine 3.0.0 released

We released a new version of our rails_state_machine gem. The release contains mainly a breaking change on how errors in state transitions are handled.

Please have a look at the changelog for more details and an upgrade guide:


3.0.0 2024-06-21

Breaking changes

  • Changed: Setting the <state_name>_event to an invalid event adds an error to the attribute instead of raising a `Transitio...

Updated: Breaking changes for boolean attributes in HAML 6

Starting with Unpoly 3.8, most Unpoly attributes can now be enabled with a value "true" and be disabled with a value "false". This pairs well with the default (unpatched) Haml 6 behavior.

capybara-lockstep 2.2.2 released

The capybara-lockstep gem synchronizes Capybara commands with client-side JavaScript and AJAX requests. This greatly improves the stability of an end-to-end ("E2E") test suite, even if that suite has timing issues.

Changes in 2.2.2

  • We now only wait for <script> elements with a JavaScript type
  • We only wait for <iframe> elements with a [src] attribute
  • We no longer wait for <video> and <audio> elements to load their metadata. This did not work consistently, and would sometime...

capybara-lockstep 2.2.1 fixes drivers with { browser: :remote }

Capybara-lockstep < 2.2.1 has a bug that makes it essentially not work when using browser: :remote in your Capybara driver. Please update to 2.2.1 to get the expected functionality.

The affected setups look like this:

chromedriver_url = ENV['CHROMEDRIVER_URL']

Capybara::Selenium::Driver.new(app,
  browser: chromedriver_url ? :remote : :chrome,
  options:,
  **(chromedriver_url ? { url: chromedriver_url } : {}),
)

Note that older Capybara versions used browser: :chrome, url: chromedriver_url, will work fine.

Searchkick: async reindexing fails for rails 7 with redis 4

After an upgrade to rails 7 I noticed that async reindexing jobs of Searchkick were failing for Model.reindex(mode: :async, wait: true):

/home/a_user/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/searchkick-5.3.1/lib/searchkick/relation_indexer.rb:142:in `block in batch_job': undefined method `call' for nil (NoMethodError)

    Searchkick.with_redis { |r| r.call("SADD", batches_key, [batch_id]) }
                                 ^^^^^
from /home/a_user/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/searchkick-5.3.1/lib/searchkick.r...

Why two Ruby Time objects are not equal, although they appear to be

So you are comparing two Time objects in an RSpec example, and they are not equal, although they look equal:

expected: Tue May 01 21:59:59 UTC 2007,
     got: Tue May 01 21:59:59 UTC 2007 (using ==)

The reason for this is that Time actually tracks fractions of a second, although #to_s doesn't say so and even though you probably only care about seconds. This means that two consecutive calls of Time.now probably return two inequal values.

Consider freezing time in your tests so it is not dependent on the speed of the executi...