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|
...
Ruby: Different ways of assigning multiple attributes
This card is a short summary on different ways of assigning multiple attributes to an instance of a class.
Using positional parameters
Using parameters is the default when assigning attributes. It works good for a small number of attributes, but becomes more difficult to read when using multiple attributes.
Example:
class User
def initialize(salutation, first_name, last_name, street_and_number, zip_code, city, phone_number, email, newsletter)
@salutation = salutation
@first_name = first_name
@last_name = last_nam...
Chrome: disable "Choose your search engine" popup in tests
Fresh Chrome installations now show a "Choose your search engine" popup in Europe. This might make your Cucumber tests fail.
Fortunately there is a flag to disable the popup. Add the following option to your chromedriver setup code:
options.add_argument('--disable-search-engine-choice-screen')
I found this flag in Peter Beverloo's list.
Background: This was experienced locally with google-chrome 127.0.6533.72
. In CI I did not get the popup.
cucumber_factory 1.14 lets you set array fields, has_many associations, numbers without quotes
Setting array columns
When using PostgreSQL array columns, you can set an array attribute to a value with square brackets:
Given there is a movie with the tags ["comedy", "drama" and "action"]
Setting has_many associations
You can set has_many
associations by referring to multiple named records in square brackets:
Given there is a movie with the title "Sunshine"
And there is a movie with the title "...
Customizing Rails error messages for models and attributes
Rails has generic error messages you can define in your locale .yml
files. You may override those application-wide error messages using model or attribute scope like this:
en:
activerecord:
errors:
messages:
invalid: is invalid # used for any invalid attribute in the application
presence: etc
models:
car:
invalid: does not work # used for invalid car attributes
attributes:
driver:
invalid: not allowed to drive # used if the car's driver attribut...
Load order of the environment
Rails 3, 4, 5, 6
config/application.rb
-
config/environment.rb
before theinitialize!
call (we don't usually edit this file) - The current environment, e.g.
environments/production.rb
- Gems
- Vendored plugins
- All initializers in
config/initializers/*.rb
-
config/environment.rb
after theinitialize!
call (we don't usually edit this file) - Your own code from
app
Rails 2
- Code in
config/preinitializer.rb
(if it exists) -
environment.rb
, code above theRails::Initializer.run
blo...
Beware of params with non-string values (nil, array, hash)
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 => ""}
/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])
I...
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 ar...
Tod: A Gem for handling daytime without a date
Tod is a gem for working with daytimes. That's a tuple of (hour, minute second)
without a day, month or year.
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.n...
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:
- Hardcoded solution (preferred solution when using Rails with ActiveJob/Sidekiq or Cronjobs)
- 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
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...
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_...
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...
Don't name columns like counter_cache columns in Rails pre v4.2.4
< Rails v4.2.4
ActiveRecord has a feature called counter caching where the containing record in a has_many
relationship caches the number of its children. E.g. when you have House has_many :rooms
, Rails can cache the number of rooms in House#rooms_count
.
Mind that when a model has a column that looks to Rails like a counter-cache column, Rails will apply counter-cache logic to your model, even if you're not using counter caches.
E.g. you have a house with 12...