ActiveRecord: Named bindings in conditions
In Active Record you can use named bindings in where-conditions. This helps you to make your code more readable and reduces repetitions in the binding list.
Example without named bindings
User.where(
'name = ? OR email = ?',
params[:query],
params[:query]
)
Example with named bindings
User.where(
'name = :query OR email = :query',
query: params[:query]
)
Rails: Concurrent requests in development and tests
With puma you can have concurrent requests. There are two concepts on how Puma can handle two incoming requests: Workers and Threads.
Workers
Puma can have multiple workers. Each worker is a process fork from puma and therefore a very heavy instance and can have multiple threads, that handle the incoming requests.
Example: A Puma server with 2 workers and 1 thread each can handle 2 request in parallel. A third request has to wait until the thread of one of the workers is free.
Threads
Rails is thread-safe since version 4 (n...
Test-Driven Development with integration and unit tests: a pragmatic approach
Test-Driven Development (TDD) in its most dogmatic form (red-green-refactor in micro-iterations) can be tedious. It does not have to be this way! This guide shows a pragmatic approach with integration and unit tests, that works in practice and improves on productivity.
Advantages
- No added effort: tests need to be written anyway.
- Test heads serve as todo lists. You'll always know what is finished and what is left to do.
- Big tasks are broken down into smaller tasks that can be processed one by one.
- You will not forget a test.
- You...
How to use Simplecov to find untested code in a Rails project with RSpec and Cucumber
Simplecov is a code coverage tool. This helps you to find out which parts of your application are not tested.
Integrating this in a rails project with rspec, cucumber and parallel_tests is easy.
-
Add it to your Gemfile and bundle
group :test do gem 'simplecov', require: false end -
Add a
.simplecovfile in your project root:SimpleCov.start 'rails' do # any custom configs like groups and filters can be here at a central place enable_cov...
Bundler in deploy mode shares gems between patch-level Ruby versions
A recent patch level Ruby update caused troubles to some of us as applications started to complain about incompatible gem versions. I'll try to explain how the faulty state most likely is achieved and how to fix it.
Theory
When you deploy a new Ruby version with capistrano-opscomplete, it will take care of a few things:
- The new Ruby version is installed
- The Bundler version stated in the Gemfil...
How to: Throttle CPU in Google Chrome
Chrome allows you to throttle the Network and the CPU. Both settings are useful to measure the performance of you application and reproduce performance issues (Example Debugging frontend performance issues with Chrome DevTools).
You find the settings in: DevTools > Performance > Capture Settings (Gear icon in the right corner) > `CPU: No...
Video transcoding: Web and native playback overview (April 2020)
Intro
Embedding videos on a website is very easy, add a <video> tag to your source code and it just works. Most of the time.
The thing is: Both the operating system and Browser of your client must support the container and codecs of your video. To ensure playback on every device, you have to transcode your videos to one or more versions of which they are supported by every device out there.
In this card, I'll explore the available audio and video standards we have right now. The goal is to built a pipeline that...
How to write good code comments
Code comments allow for adding human readable text right next to the code: notes for other developers, and for your future self. You can imagine comments as post-its (or sometimes multi-sheet letters ...) on real-world objects like cupboards, light switches etc.
As always, with power comes responsibility. Code comments can go wrong in many ways: they may become outdated, silently move away from the code they're referring to, restate the obvious, or just clutter files.
Good Comments
Here are some simple rules to keep your comments help...
Capybara: How to find a hidden field by its label
To find an input with the type hidden, you need to specify the type hidden:
find_field('Some label', type: :hidden)
Otherwise you will see an exception :
find_field('Some label')
# => Capybara::ElementNotFound: Unable to find field "Some label" that is not disabled`.
Note: Usually you don't need to check the input of hidden fields in an integration test. But e.g. waiting for a datepicker library to write the expected value to this field before continuing the test, which prevents flaky tests, is a valid use case.
Quick HTML testing with RubyMine
If you need to test some HTML, e.g. an embed code, you can use RubyMine's "scratch files":
- File > New Scratch File (or
Ctrl + Shift + Alt + Ins) - Select "HTML" as file type
- Write or paste the HTML
- Move your mouse to the upper right corner of the scratch file editor. Pick a browser to instantly open your file.
ActiveRecord: Specifying conditions on an associated table
We can use ActiveRecord's where to add conditions to a relation. But sometimes our condition is not on the model itself, but on an associated model. This card explains multiple ways to express this condition using ActiveRecord's query interface (without writing SQL).
As an example we will use a User that has many Posts:
class User < ApplicationRecord
has_many :posts
scope :active, -> { tra...
Letting a DOM element fade into transparency
You can use the CSS property mask-image to define an "alpha channel" for an element.
E.g. to let an element start at full opacity at the top and gradually fade into transparency at the bottom:
.box {
-webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
}
- A fully opaque black pixel will render the masked pixel fully opaque
- A fully transparent black pixel will render the ...
Ruby: How to fetch a remote host's TLS certificate
TLS/SSL certificates are often used for HTTPS traffic. Occasionally a service may also use their TLS certificate to support public-key encrypting data (e.g. when it is part of the URI and visible to the user, but contains sensitive information).
Here is how to easily fetch such certificate data.
certificate = Net::HTTP.start('example.com', 443, use_ssl: true) { |http| http.peer_cert }
# => #<OpenSSL::X509::Certificate: subject=#<OpenSSL::X509::Name CN=www.example.org,...>
certificate.public_key
# => #<OpenSSL::PKey::RSA:0x...
How to write modular code
Or: How to avoid and refactor spaghetti code
Please note that I tried to keep the examples small. The effects of the methods in this card are of course much more significant with real / more complex code.
What are the benefits of more modular code?
Code is written once but read often (by your future self and other developers who have to understand it in order to make changes for example). With more modular code you reduce the scope of what has to be understood in order to change something. Also, naming things gives you the opportunity t...
How to make a cucumber test work with multiple browser sessions
Imagine you want to write a cucumber test for a user-to-user chat. To do this, you need the test to work with several browser sessions, logged in as separate users, at the same time.
Luckily, Capybara makes this relatively easy:
Scenario:
Scenario: Alice and Bob can chat
Given Alice, Bob, and a chat session
When I am signed in as "Alice"
And I go to the chat
And I am signed in as "Bob" [session: bob]
And I go to the chat [session: bob]
And I send the message "Hello, this is Alice!"
Then I should see "Hello, this ...
Rails: Rest API post-mortem analysis
This is a personal post-mortem analysis of a project that was mainly build to provide a REST API to mobile clients.
For the API backend we used the following components:
- Active Model Serializer (AMS) to serializer our Active Record models to JSON.
- JSON Schema to test the responses of our server.
- SwaggerUI to document the API.
It worked
The concept worked really good. Here are two points that were extraordinary compared to normal Rails project with many UI components:
- Having a Rails application, that has no UI components (only...
How to: Validate dynamic attributes / JSON in ActiveRecord
PostgreSQL and ActiveRecord have a good support for storing dynamic attributes (hashes) in columns of type JSONB. But sometimes you are missing some kind of validation or lookup possibility (with plain attributes you can use Active Record's built-in validations and have your schema.rb).
One approach about being more strict with dynamic attributes is to use JSON Schema validations. Here is an example, where a project has the dynamic attributes analytic_stats, that we can use to store analytics from an external measurement tool.
- A g...
Structuring Rails applications: the Modular Monorepo Monolith
Root Insurance runs their application as a monolithic Rails application – but they've modularized it inside its repository. Here is their approach in summary:
Strategy
- Keep all code in a single repository (monorepo)
- Have a Rails Engine for each logical component instead of writing a single big Rails Application
- Build database-independent components as gems
- Thus: gems/ and engines/ directories instead of app/
- Define a dependency graph of components. It should have few edges.
- Gems and Engines can be extracted easier once nece...
Capybara 'fill_in': Ambiguous match for different input names
When you have two inputs, where one contains the name of the other (eg. Name and Name with special treatment), Capybara's fill_in method will fail with the following message:
Ambiguous match, found 2 elements matching visible field "Name" that is not disabled (Capybara::Ambiguous)
You can force Capybara to match exactly what you are typing (which makes your tests better anyways) with match: :prefer_exact:
name = 'Name'
value = 'Bettertest Cucumberbatch'
fill_in(field, with: value, match: :prefer_exact)
Furthermore...
Always convert and strip user-provided images to sRGB
Debugging image color profiles is hard. You can't trust your eyes in this matter, as the image rendering depends on multiple factors. At least the operation system, browser or image viewer software and monitor influence the resulting image colors on your screen.
When we offer our users the possibility to upload images, they will most likely contain tons of EXIF metadata and sometimes exotic color profiles like eciRGB. We want to get rid of the metadata, as it might contain sensitiv...
Jasmine: Test that an object is an instance of a given class
To test that an object was constructed by a given constructor function, use jasmine.any(Klass):
describe('plus()', function() {
it ('returns a number', function() {
let result = plus(1, 2)
expect(result).toEqual(jasmine.any(Number))
})
})
Defining new elements for your HTML document
Browsers come with a set of built-in elements like <p> or <input>. When we need a new component not covered by that, we often build it from <div> and <span> tags. An alternative is to introduce a new element, like <my-element>.
When a browser encounters an unknown element like <my-element>, the browser will proceed to render <my-element>'s children. The visual rendering of your page will not be affected.
If you care about their HTML being valid, your new element should contain a dash character (-) to mark it as a *custom el...
How to set up SMTP email delivery with a Gmail account
If you want to make your Rails application be capable of sending SMTP emails, check out the action mailer configuration section in the Ruby on Rails guide.
TL;DR you will end up having an smtp_settings hash that looks something like this:
smtp_settings = {
address: ...,
domain: ...,
port: ...,
user_name: ...,
password: ...,
authentication: ...,
tls: ...,
enable_starttls_auto: ...,
}
This hash can be set as the `delivery_me...
Rspec 3: what to do when `describe` is undefined
When tests might not run with skipping RSpec in the RSpec.describe failing with the error undefined method 'describe' for main:Object this card will help you out!
In RSpec 3 the DSL like describe is exposed globally by default. Therefore it is not necessary to write Rspec.describe.
However, there is a config option to disable this beavior, which also disables the old should-syntax:
RSpec.configure do |config|
config.disable_monkey_patching!...