Shoulda Matchers: how to test conditional validations
Shoulda Matchers don't provide canditional validations (validations with if: option). Here is how to write tests for the condition:
Class:
class Employee < ActiveRecored::Base
validates :office, presence: true, if: manager?
def manager?
...
end
end
Test:
describe Employee do
describe '#office' do
context 'is a manager' do
before { allow(subject).to receive(:manager?).and_return(true) }
it { is_expected.to validate_presence_o...
MySQL 5.7.5 enables `ONLY_FULL_GROUP_BY` mode per default
When using GROUP BY, MySQL now complains if the SELECT includes columns which are not part of the GROUP BY.
Reason:
There could be multiple values for those columns per group but only one value can be picked for the results.
The default behaviour of MySQL prior to version 5.7 will not complain and arbitrarily choose a value. But this leads to non-deterministic results. So MySQL now has enabled the only_full_group_by setting by default to prevent this.
In Rails this could lead to some trouble, because scopes do not have sp...
Rails: How to write custom email interceptors
Nowadays it is fairly easy to intercept and modify mails globally before they are sent. All you have to do is register an interceptor class which responds to .delivering_email(message). This card will show you two common use cases.
Subject prefix:
Usually you want to prefix the subject line of emails with the current environment (except production) so you can differentiate between production mails and mails from other environments. Of course a...
Capistrano 2: How to deploy a single server
When you have a multi-server setup, you'll be adding a new server from time to time. Before doing a full deploy, you might want to test that server in an isolated deploy. There is a single way to do this: the HOSTFILTER env variable.
Commenting out "server" lines in the Capistrano deploy config will raise a Capistrano::NoMatchingServersError with <task> is only run for servers matching {:roles=> <role>}, but no servers matched. Instead, specify the server-under-test like this:
HOSTFILTER=separate-sidekiq.makandra.de cap productio...
Working with or without time zones in Rails applications
Rails supports time zones, but there are several pitfalls. Most importantly because Time.now and Time.current are completely different things and code from gems might use one or the other.
Especially configuring an application that cares only about one time zone is a bit tricky.
The following was tested on Rails 5.1 but should apply to Rails 4.2 as well.
Using only local time
Your life will be easier if your application does not need to support time zones. Disable them like this:
config.time_zone = 'Berlin' # Your local ...
How to define height of a div as percentage of its variable width
This is useful if, for example, you want to use a background-image that has to scale with the width and the div should only have the height of the picture.
html:
<div class="outer">
<div class="inner">
</div>
</div>
css:
.outer {
width: 100%;
background-image: image-url('background.png');
background-size: cover;
}
.inner {
padding-top: 60%;
}
How does it work?
There are several CSS attributes that can handle values as percentage. But they use different other attributes as "reference value...
Git: Rebasing dependent feature branches
This card will show you how to use git rebase --onto without confusion.
Use case:
You've got two feature branches (one and two), where two depends on one. Now commits of branch one have changed after you branched two from it (i.e. after code review the commits of branch one are squashed). Thus the commit history of branch one has changed. Branch two's history however didn't change.
Solution:
To make the branches share the same commit history again you will have to rebase and replay (attach) the a...
Webmock's hash_including doesn't parse query values to string
Webmocks hash_including is similar to RSpec::Mocks::ArgumentMatchers#hash_including. Be aware that hash_including (webmock v3.0.1) doesn't parse integer values to String.
Without hash including you would say:
uri = URI('http://example.com/?foo=1&bar=2')
stub_request(:get, 'example.com').with(query: {foo: 1, bar: 2})
Net::HTTP.get(uri) # ===> Success
If you only want to check if foo is present you can use hash_including:
uri = URI('http://example.com/?foo=1&bar=2')
stub_request(:get, 'example.com').with(query: hash_i...
Webmock normalizes arrays in urls
Typhoeus has a different way of representing array params in a get request than RestClient.
Typhoeus: http://example.com/?foo[0]=1&foo[1]=2&foo[2]=3
RestClient: http://example.com/?foo[]=1&foo[]=2&foo[]=3
Webmock normalizes this url when matching to your stubs, so it is always http://example.com/?foo[]=1&foo[]=2&foo[]=3. This might lead to green tests, but in fact crashes in real world. Rack::Utils.build_nested_query might help to build a get re...
Middleman configuration for Rails Developers
Middleman is a static page generator that brings many of the goodies that Rails developers are used to.
Out of the box, Middleman brings Haml, Sass, helpers etc. However, it can be configured to do even better. This card is a list of improvement hints for a Rails developer.
Gemfile
Remove tzinfo-data and wdm unless you're on Windows. Add these gems:
gem 'middleman-livereload'
gem 'middleman-sprockets' # Asset pipeline!
gem 'bootstrap-sass' # If you want to use Bootstrap
gem 'byebug'
gem 'capistrano'
gem 'capistrano-mid...
Capistrano 3: How to deploy when a firewall blocks your git repo
Sometimes, through some firewall or proxy misconfiguration, you might have to deploy to a server that cannot access the git repository.
Solution 1: HTTP Proxy (this is the preferred fix)
SSH can be tunneled over an HTTP Proxy. For example, when the repo is on github, use this:
-
Install
socat -
Add a
~/.ssh/configon the target server(s) with permission 0600 and this content:Host github.com ssh.github.com User git Hostname ssh.github.com Port 443 ProxyCommand socat - PROXY:<your proxyhost>:%h:%p,...
Quickly printing data in columns on your Ruby console
Dump this method into your Ruby console to quickly print data in columns. This is helpful for e.g. comparing attributes of a set of Rails records.
def tp(objects, *method_names)
terminal_width = `tput cols`.to_i
cols = objects.count + 1 # Label column
col_width = (terminal_width / cols) - 1 # Column spacing
Array(method_names).map do |method_name|
cells = objects.map{ |o| o.send(method_name).inspect }
cells.unshift(method_name)
puts cells.map{ |cell| cell.to_s.ljust(col_width) }.join ' '
end
nil
end
Usag...
Style Guide for Git commit messages
- Separate subject from body with a blank line
- Limit the subject line to 50 characters (max. 72), include reference (unique story ID) to requirements tracker (Linear in our case)
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how.
Tip
As an alternative, use a commit message that refers to a GitHub issue (
fixes #321) or [story ID](https://makandracards.com/makandra/620718-b...
Using ActiveRecord with threads might use more database connections than you think
Database connections are not thread-safe. That's why ActiveRecord uses a separate database connection for each thread.
For instance, the following code uses 3 database connections:
3.times do
Thread.new do
User.first # first database access makes a new connection
end
end
These three connections will remain connected to the database server after the threads terminate. This only affects threads that use ActiveRecord.
You can rely on Rails' various clean-up mechanisms to release connections, as outlined below. This may...
RSpec: Debug flickering test suites with rspec --bisect
In modern default RSpec configurations, your tests are usually run in random order. This helps to detect "flickering" tests that only fail when run in a certain order.
The reason for this are tests that have side effects causing other tests to fail later. The hard part is to find the offending test.
Enter rspec --bisect:
- Say you have a flickering test that passes on its own, but you just saw it fail in a full test run. At the top of the RSpec output, you will see a message like
Randomized with seed 12345. Take a note of the number....
Howto: Require a gem that is not in Gemfile
In case you want to require a gem, that is not in the Gemfile of you bundle and therefore not in your loadpath, you need to add the path manually and require the gem afterwards.
Expected error
Requiring a gem, that is not in the Gemfile or .gemspec, will cause an LoadError exception:
require 'example_gem' => LoadError: cannot load such file -- example_gem
Adding a gem to the loadpath temporary
- You need to install the
gem
gem install 'example_gem'
- Then you need to require the path where the gem was install...
Storing trees in databases
This card compares patterns to store trees in a relation database like MySQL or PostgreSQL. Implementation examples are for the ActiveRecord ORM used with Ruby on Rails, but the techniques can be implemented in any language or framework.
We will be using this example tree (from the acts_as_nested_set docs):
root
|
+-- Child 1
| |
| +-- Child 1.1
| |
| +-- Child 1.2
|
+-- ...
How to use Parallel to speed up building the same html partial multiple times (for different data)
The parallel-gem is quite easy to use and can speed up rendering time if you want to render the same partial multiple times (e.g. for rendering long lists of things).
Parallel supports execution using forked processes (the default), threads (mind the GVL) and Ractors (some limitations for data sharing).
If your parallelized code talks to the database, you should [ensure not to leak database connections](https://makandracards.com/makandra/45360-using-activerecord-with-threads-will-leak-database-connect...
How to disable Chrome's save password bubble for Selenium tests
When filling out forms in Selenium tests, Chrome shows the (usual) bubble, asking to store those credentials.
While the bubble does not interfere with tests, it is annoying when debugging tests. Here are two ways to disable it:
Option 1: prefs
You can set profile preferences to disable the password manager like so:
prefs = {
'credentials_enable_service' => false,
'profile.password_manager_enabled' => false
}
Capybara::Selenium::Driver.new(app, browser: :chrome, prefs: prefs)
Sadly, there are no command line s...
CSS: Don't target multiple vendor-prefixed pseudo-elements in a single rule
Some pseudo-elements need to be addressed with vendor prefixes. E.g. ::selection is not supported by Firefox, you need to use ::-moz-selection instead.
What you cannot do is to define a single CSS rule to address both the standard and vendor-prefixed form:
::selection, ::-moz-selection {
background-color: red;
}
This rule will be ignored by all browsers. The reason is that if a browser doe...
How to make http/https requests yourself with nc/telnet/openssl
Sometimes you want/have to send specific http(s) requests. You can do that easy with curl or just write the request yourself.
make a http request with nc
nc example.com 80
GET / HTTP/1.1
Host: example.com
# press enter
make a http request with telnet
telnet example.com 80
GET / HTTP/1.1
Host: example.com
# press enter
make https request with openssl
openssl s_client -connect example.com:443
GET / HTTP/1.1
Host: example.com
# press enter
You can specify more headers if you want:
nc example.c...
Sending errors to sentry from development
For the initial setup or changes in the sentry reporting it might be useful to enabled reporting of sentry in development. Don't commit these changes and prefer to report to the staging environment. As other developers might be confused of these errors try to given them a proper message and delete them afterwards.
- Add
config.raven_dsn = 'your-dns'inconfig/environments/development.rb. - Add development to existing environments in the
Raven.configureblock:config.environments = ['development', 'staging', 'production']. - ...
How to view Image Metadata on the Linux Command Line with ImageMagick
ImageMagick has a command line tool called identify which can read image metadata:
>identify -verbose DSC00136.JPG
Image: DSC00136.JPG
Format: JPEG (Joint Photographic Experts Group JFIF format)
Class: DirectClass
Geometry: 5472x3648+0+0
Resolution: 350x350
Print size: 15.6343x10.4229
Units: PixelsPerInch
Type: TrueColor
Endianess: Undefined
Colorspace: sRGB
Depth: 8-bit
Channel depth:
red: 8-bit
green: 8-bit
blue: 8-bit
Channel statistics:
Red:
min: 0 (0)
max: 255 (1)
mean: 11...
Beware: Nested Spreewald patiently blocks are not patient
Note: The behaviour of Spreewald's within step is as described below for version < 1.9.0; For Spreewald >= 1.9.0 it is as described in Solution 1.
When doing integration testing with cucumber and selenium you will often encounter problems with timing - For example if your test runs faster than your application, html elements may not yet be visible when the test looks for them. That's why Spreewald (a collection of cucumber steps) has a concept of doing things patiently, which means a given b...