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...
Things you probably didn’t know you could do with Chrome’s Developer Console
Collection of useful tools in the Chrome JavaScript console.
Make the whole page editable
This is not special to Chrome, but still a clever thing:
document.body.contentEditable=true
Taking time
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
Reference previously inspected elements (from the Elements panel)
Variables $0
, $1
, ... $n
reference the nth-last inspected Element. $0
...
Preventing users from uploading malicious content
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...
Parallelize Development Using Git Worktrees
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>
...
Carrierwave: Deleting files outside of forms
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, ...
Use Time.current / Date.current / DateTime.current on projects that have a time zone
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
forTime
,Date
, andDateTime
.ActiveRecord attributes will be time-zoned, and
.current
values will be converted properly when written to the database.
Do not useTime.now
and friends. Timezone-less objects will not be converted properly when written to the database. -
With no/local time zone use
Time.now
, `...
RSpec: Leverage the power of Capybara Finders and Matchers for view specs
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 to wirte view unit specs as you can isolate specific part...
RSpec: Ensuring a method is called on an object that will be created in the future
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.
Example
The example model uses different validators based on a flag:
class MyModel < ApplicationRecord
...
Whenever: Don't forget leading zeros for hours!
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...
Heads up: RSpec's diffs may not tell the truth
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 ...
Spreewald, Cucumber: Selector for the nth element
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 ...
How to make sure that manual deploy tasks (scheduled in Pivotal Tracker) are executed on deploy (with Capistrano)
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.
Writing deploy tasks
Here is how we manage the deploy tasks themselves:
- Deploy tasks are written inside the Pivotal Tracker story description, clearly marked (e.g. with a headline "Deploy task")
- We disting...
JavaScript: Don't throw synchronous exceptions from functions that return a Promise
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.
The full story
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...
XHR is not JSON
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 ...
RSpec: Efficiently rerunning failed examples during development
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.
Setup
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'
Rerun all failed examples using --only-failures
bundle exec rspec --only-failures
(or `...
Using Ruby's Method objects for inspecting methods
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...
Use rbenv-each to run a command for every installed Ruby version
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...
What collapsing margins are, how they work and when margins do not collapse
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...
Be careful to use correct HTTP status codes for maintenance pages
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.
Popular footguns
Here are some ways to shoot yourself in the foot during maintenance:
- If all your routes send a "200 OK" with a HTML body "We're b...
Ag: Very fast grep replacement
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.
Devise: How to send asynchronous emails
By default, Devise sends all emails synchronously with deliver_now
.
To change that, Devise's readme suggests overwriting the send_devise_notification
method like this:
class User
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
end
However, there is one problem: When deliver_later
enqueues the mail with ActiveJob, the job arguments are logged. In case of a password reset, this includes the password reset token, which should not be logged.
A...
Tasks, microtasks, queues and schedules - JakeArchibald.com
The way that Javascript schedules timeouts and promise callbacks is more complicated than you think. This can be the reason why callbacks are not executed in the order that they are queued.
Please read this article!
This is an extract of the example in the article which demonstrates the execution order of tasks and microtasks.
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
})...
Restarting all God tasks
To restart all tasks monitored by God, don't use god restart
. This command is only meant to soft-restart a given process or group.
Instead you should:
god stop
god terminate
god start -c yourgodconfig.god
Rails: Talking to the database without instantiating ActiveRecord objects
Instantiating ActiveRecord objects comes expensive. To speed up things, you can choose a more direct way to talk to your database: the ActiveRecord::ConnectionAdapters::DatabaseStatements
module.
Using the module and its methods is not suggested in the usual in-app workflow, as validations, callbacks, custom getters/setters etc. are ignored. However, for database-centered stuff like migrations, these fill the gap between writing pure SQL and full...