In the discussion of the difference between include
and extend
in Ruby, there is a misconception that extend
would add methods to the singleton class of a ruby object as stated in many posts on this topic. But in fact, it is added to the ancestors chain of the singleton class! Even though it is technically not the same, practically this can be considered the same in most use cases.
This means, that we are able to overwrite these methods or call the parent version with super
depending in which order and in whi...
Most of our applications use CarrierWave for file uploads. CarrierWave has an integrated processing mechanism for different file versions with support for ImageMagick through CarrierWave::MiniMagick
(which requires the mini_magick
gem). In case your processing runs into an error, CarrierWave will just swallow it and rethrow an error with a very generic message like Processing failed. Maybe it is not an image?
which does not help you finding out what the actual problem is. CarrierWave probably does this for security purposes, but does n...
I recently stumbled over a problem that my feature tests broke in CI because of a mismatching chromedriver version.
In this specific project we have a fixed Chromium version in a Debian 12 environment instead of Chrome. The tests however used a recent chrome version instead.
$ chromedriver --version
ChromeDriver 117.0.5938.149 (e3344ddefa12e60436fa28c81cf207c1afb4d0a9-refs/branch-heads/5938@{#1539})
$ chromium --version
Chromium 117.0.5938.149 built on Debian 12.1, running on Debian 12.1
> WARN Selenium [:selenium_manager] The chromed...
Given you have an array column like this:
create_table "users", force: :cascade do |t|
t.integer "movie_ids", default: [], array: true
end
You might think that the following queries yield the same result:
User.where(movie_ids: [16, 17])
User.where(movie_ids: [17, 16])
Turn's out - they are not! They do care about array ordering more than I do.
To query for identical arrays independent of their order you have to either:
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.
In a [feature spec](https://relishapp.com/rspec/rspec-rails/docs/feature-spec...
Collection of useful tools in the Chrome JavaScript console.
This is not special to Chrome, but still a clever thing:
document.body.contentEditable=true
You can easily measure the time on the console with named timers:
console.time('myTime'); // Start timer
console.timeEnd('myTime'); // End timer and print the time
Variables $0
, $1
, ... $n
reference the nth-last inspected Element. $0
...
When you allow file uploads in your app, a user might upload content that hurts other users.
Our primary concern here is users uploading .html
or .svg
files that can run JavaScript and possibly hijack another user's session.
A secondary concern is that malicious users can upload executables (like an .exe
or .scr
file) and use your server to distribute it. However, modern operating systems usually warn before executing files that were downloaded from t...
You can use git worktree
to manage multiple working trees attached to the same repository. But why should I use git worktree
?
You can use more than one working tree to ...
... run tests while working on another branch
... compare multiple versions
... work on a different branch without disturbing your current branch
Creating a new working tree is as simple as creating a new branch. You only need to execute git worktree add <path> <branch>
. When you are done, you can remove the working tree with git worktree remove <Worktree>
...
TL;DR Use user.update!(remove_avatar: true)
to delete attachments outside of forms. This will have the same behavior as if you were in a form.
As you know, Carrierwave file attachments work by mounting an Uploader
class to an attribute of the model. Though the database field holds the file name as string, calling the attribute will always return the uploader, no matter if a file is attached or not. (Side note: use #present?
on the uploader to check if the file exists.)
class User < ApplicationRecord
mount :avatar, ...
Basically, you now need to know if your project uses a "real" time zone or :local
, and if config.active_record.time_zone_aware_attributes
is set to false
or not.
With time zones configured, always use .current
for Time
, Date
, and DateTime
.
ActiveRecord attributes will be time-zoned, and .current
values will be converted properly when written to the database.
Do not use Time.now
and friends. Timezone-less objects will not be converted properly when written to the database.
With no/local time zone use Time.now
, `...
View specs are a powerful tool to test several rendering paths by their cases instead of using a more costing feature spec. This is especially useful because they become quite convenient when used with Capybara::Node::Finders and Capybara::RSpecMatchers. This allows within view specs to isolate specific parts of the render...
rspec >= 3.1 brings a method and_wrap_original
. It seems a bit complicated at first, but there are use cases where it helps to write precise tests. For example it allows to add expectations on objects that will only be created when your code is called.
If you have older rspec, you could use expect_any_instance_of
, but with the drawback, that you can't be sure if it really was the correct instance which got the message.
The example model uses different validators based on a flag:
class MyModel < ApplicationRecord
...
Whenever is a Ruby gem that provides a nicer syntax for writing and deploying cron jobs.
Leading zeros are important for whenever if you use the 24-hours format!
This schedule.rb
:
every 1.day, at: '3:00', roles: [:primary_cron] do
runner 'Scheduler.delay.do_things'
end
will lead to this crontab entry (crontab -l
) with the default configuration:
0 15 * * * /bin/bash -l -c 'cd /var/www/my-project/releases/20180607182518 && bin/rails runner -e production '\''Scheduler.delay.do_things'\'''
Which would run on 3...
RSpec provides a nice diff when certain matchers fail.
Here is an example where this diff is helpful while comparing two hashes:
{a:1}.should match(a:1, b:2)
Failure/Error: {a:1}.should match(a:1, b:2)
expected {:a=>1} to match {:a=>1, :b=>2}
Diff:
@@ -1,3 +1,2 @@
:a => 1,
-:b => 2,
Unfortunately, this diff is not as clever as it would need to. RSpec's instance_of
matchers will look like errors in the diff (even if they are not), and time objects that differ only in milliseconds won't appear in the ...
The recommended additional setup of the spreewald gem, a useful set of cucumber steps, includes adding a file for defining custom selectors which can be used as prose within steps:
When I follow "Edit" within the controls section
Where the controls section
can be any arbitrary defined css selector within selectors.rb
Often it can be useful to select the nth element of a specific selector. Luckily, this can ...
We regularly have tasks that need to be performed around a deploy. Be it to notify operations about changed application behavior, be it to run a little oneline script after the deploy. Most database-related stuff can be handled by migrations, but every once in a while, we have tasks that are much easier to be performed manually.
Here is how we manage the deploy tasks themselves:
TLDR: A function is hard to use when it sometimes returns a promise and sometimes throws an exception. When writing an async function, prefer to signal failure by returning a rejected promise.
When your function returns a promise ("async function"), try not to throw synchronous exceptions when encountering fatal errors.
So avoid this:
function foo(x) {
if (!x) {
throw "No x given"
} else
return new Promise(funct...
When a Rails controller action should handle both HTML and JSON responses, do not use request.xhr?
to decide that. Use respond_to
.
I've too often seen code like this:
def show
# ...
if request.xhr?
render json: @user.as_json
else
# renders default HTML view
end
end
This is just plain wrong. Web browsers often fetch JSON via XHR, but they (should) also send the correct Accept
HTTP header to tell the server the data they expect to receive.
If you say request.xhr?
as a means for "wants JSON" you are ...
Note
Don't use reruns as a mean to work around flaky tests. You should always try to fix those instead of rerunning them regularly.
Configure RSpec to persist the result of your test runs to a file. This is necessary to be able to rerun examples.
Add this to your spec/spec_helper.rb
:
config.example_status_persistence_file_path = 'spec/examples.txt'
--only-failures
bundle exec rspec --only-failures
(or `...
Do you remember finding where a method is defined?
I recently learned from a senior colleague that Method objects are quite useful within a debugging feast to find out the currently defined internals of methods, because they are either called within the current context or because you want to learn something about the API of the current objects.
Why is this useful?
This is especially useful since Ru...
The linked rbenv plugin rbenv-each is very helpful to keep QoL gems up to date that are not part of the Gemfile.
For example, you can bump the geordi
version for all your rubies with the following command:
rbenv each gem update geordi
Another useful example would be to bulk-update bundler
or rubygems.
Note that rbenv-each
hasn't been updated since 2018, but it is fully functiona...
Imagine you have 2 HTML boxes. The first one has a margin-bottom
of let's say 30px
and the second one a margin-top
of 20px
. After rules of collapsing margins have been applied we have a margin of 30px
(not 50px
) between these two boxes . This is because no addition of both margins takes place but the maximum of both is applied. This behavior is called collapsing margins.
Oftentimes it is a good behavior but collapsing margins can be annoying, too. For example child el...
When your public-facing application has a longer downtime for server maintenance or long migrations, it's nice to setup a maintenance page to inform your users.
When delivering the maintenance page, be very careful to send the correct HTTP status code. Sending the wrong status code might get you kicked out of Google, or undo years of SEO work.
Here are some ways to shoot yourself in the foot during maintenance:
Ag
(aka "the silver searcher") is a very fast replacement for grep
.
It will parse your .gitignore
for additional speedup. To ignore even more files (node_modules
, *.min.js
etc), add an .ignore
with syntax identical to .gitignore
.
See Faster Grepping in Vim for hints about vim integration.