ActiveRecord::Store: migrate data in store
When you need to store structured data (like Ruby hashes) in a single database column with ActiveRecord, a simple way is to use PostgreSQL's jsonb columns. ActiveRecord will automatically serialize and deserialize your Hash to and from JSON, and you can index JSON paths for fast reads.
As an alternative, ActiveRecord::Store offers a way to store hashes in a single database column. This card will show you how to migrate those hashes in an ActiveRecord::Migration by example:
...
RSpec: Expect one of multiple matchers to match
RSpec let's you chain a matcher with .or. The expectation will then pass if at least one matcher matches:
expect(color).to eq("red").or eq("green")
Real-world example
A real-world use case would be to test if the current page has a button with the label "Foo". There are many ways to render a button with CSS:
<input type="button" value="Foo">
<input type="submit" value="Foo">
<button>Foo</button>
We cannot express it with a single have_css() matcher, since we need the { text: 'Foo' } optio...
CSS: Using the current text color for other color properties
There is a kinda secret, yet well supported CSS feature called currentColor. It's like a special CSS variable that has been supported in almost all browsers for almost all time (see linked Caniuse).
Usage
The currentColor value can be used in CSS to indicate the current value of color should be used. A common use case is setting a border color:
a.ghost
color: white
border: 1px solid currentColor
&:hover
color: red // Border color will change as well
Note that in many cases, you can simply omit the color to ac...
Jasmine: Adding custom matchers
Definition
A matcher is a function that returns an object with a compare key. Usually it is registered with beforeEach:
beforeEach(() => {
jasmine.addMatchers({
// Example matcher
toBeAnything() {
return {
compare(actualValue, ...matcherArguments) {
// Do some computations here ...
// Return whether the actualValue matches the expectation
return {pass: true}
}
}
}
})
})
Usage
expect(actualValue).toBeAnything(...matcherArg...
Rails: Including HTML in your i18n locales
TL;DR
I18n keys ending with
_htmlare automatically marked as HTML-safe when translating witht('.your_key_html').
When you're localizing a Rails application, some localized texts need to contain HTML. Be it some localized link, or some tags for formatting that you wantto include in your translated text. Example:
# e.g. config/locales/en.yml
en:
page:
text: 'Please visit our <a href="https://www.example.com/">corporate website</a> to learn more about <strong>the corporation</strong>.'
When your view tran...
screenfull.js: Simple wrapper for cross-browser usage of the JavaScript Fullscreen API
Using the JS fullscreen API is painful because all browers use different methods and events and you need to use lots of boilerplate code to make your application work in all browsers.
The "screenfull" library wraps that for you, including events.
Examples
The linked GitHub repo contains some information. You basically use the library like this:
// Make an element go fullscreen
screenfull.request(element)
// Leave fullscreen
screenfull.exit()
...
jQuery: How to replace DOM nodes with their contents
You know that you can use jQuery's text() to get an element's contents without any tags.
If you want to remove only some tags, but keep others, use contents() and unwrap(). Here is how.
Consider the following example element.
$container = $('<div><strong>Hello</strong> <em>World</em></div>')
Let's say we want to discard any <em> tags, but keep their contents.
Simply find them, then dive into their child nodes via contents, and use unwrap replace their ...
ActionMailer: How to send a test mail directly from the console
If your rails application is unable to send mails, it might be useful to debug your settings using the rails console. Here is a snippet that shows the current settings and lets you send a test mail directly from the console:
mailer = ActionMailer::Base.new
# check settings:
mailer.delivery_method # -> :smtp
mailer.smtp_settings # -> { address: "localhost", port: 25, domain: "localhost.localdomain", user_name: nil, password: nil, authentication: nil, enable_starttls_auto: true }
# send mail:
mailer.mail(from: 'sender@example.com', ...
PostgreSQL: How to show table sizes
When you have a large PG database, you may want to find out which tables are consuming the most disk space.
You can easily check this using the following SQL statement from the PostgreSQL wiki.
SELECT nspname || '.' || relname AS "relation",
pg_size_pretty(pg_total_relation_size(C.oid)) AS "total_size"
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind <> 'i'
AND nspname !~ '^pg_toast'
ORDER BY pg_tot...
Webpacker: Configuring browser compatibility
Webpacker uses Babel and Webpack to transpile modern JavaScript down to EcmaScript 5. Depending on what browser a project needs to support, the final Webpack output needs to be different. E.g. when we need to support IE11 we can rely on fewer JavaScript features. Hence our output will be more verbose than when we only need support modern browsers.
Rails 5.1+ projects often use Webpacker to preconfigure the Webpack pipeline for us. The default configuration works something like this:
- Webpack checks w...
Carrierwave: Built-in RSpec matchers
CarrierWave comes with some RSpec matchers which will make testing more comfortable. Let's say you have an Uploader like this:
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
# Create different versions of your uploaded files:
version :small do
process resize_to_fill: [100, 100]
end
version :medium do
process resize_to_fit: [200, nil]
end
version :large do
process resize_to_limit: [400, 400]
end
end
Imagine you have a class Movie with an attribute poster. In ...
Bundler error: Downloading gem revealed dependencies not in the API
Recent Bundler (1.16.1) started complaining about missing dependencies in the Gemfile. This is due to a stricter handling of specifications (see attached link).
The error message looks like this:
Downloading example-gem-1.2.3 revealed dependencies not in the API or the lockfile (other-gem (< 3)).
Either installing with `--full-index` or running `bundle update example-gem` should fix the problem.
However, bundle install --full-index did not any better for me, and bundle update is not always a viable solution.
Easiest solut...
Generating test images on the fly via JavaScript or Ruby
When you need test images, instead of using services like lorempixel or placehold.it you may generate test images yourself.
Here we build a simple SVG image and wrap it into a data: URI. All browsers support SVG, and you can easily adjust it yourself.
Simply set it as an image's src attribute.
JavaScript
Simple solution in modern JavaScript, e.g. for use in the client's browser:
function svgUri(text) {
let svg = `
<svg wid...
RSpec: Expecting multiple calls of the same method
If the argument list is the same every time:
expect(object).to receive(:foo).with('argument').and_return('response 1', 'response 2')
If the argument list differs between invocations:
expect(object).to receive(:foo).with('argument 1').ordered.and_return('response 1')
expect(object).to receive(:foo).with('argument 2').ordered.and_return('response 2')
Running Rails 2 apps with modern MariaDB SQL server
You might have some trouble running a Rails LTS 2 app with MySQL 5.7.
If you don't want to hack Mysql 5.6 into your modern Ubuntu or use the MySQL sandbox, you might want to try MariaDB 10.x.
MariaDB 10.x should work with both old and new Rails applications.
[Switch to MariaDB](https://makandracards.com/makandra/468343-how-...
Understanding SQL compatibility modes in MySQL and MariaDB
MySQL and MariaDB have an SQL mode setting which changes how MySQL behaves.
The SQL mode value is comprised of multiple flags like "STRICT_TRANS_TABLES, NO_ZERO_IN_DATE". Each flag activates or disables a particular behavior.
The default SQL mode varies widly between versions of MySQL and MariaDB. In general, more recent versions of MySQL and MariaDB have stricter settings than older versions, and MySQL has stricter settings than the more liberal MariaDB.
If your app explodes ...
Rails: Overriding view templates under certain conditions only
Rails offers a way to prepend (or append) view paths for the current request. This way, you can make the application use different view templates for just that request.
Example
A use case of this is a different set of view templates that should be used under certain circumstances:
class UsersController < ApplicationController
before_action :prepare_views
def index
# ...
end
private
def prepare_views
if <condition>
prepend_view_path Rails.root.join('app', 'views', 'special')
end
end
...
VNC browser disappears while typing
We often use the Then console step from spreewald in combination with geordi vnc from geordi to debug tests within a real browser. Sometimes when you type in the browser it suddenly disappears. You will only see a grey screen then.
This will always happen if you press the d key. Press the d key again and the browser will appear again.
Nested Spreewald patiently blocks are now patient
In Spreewald 1.10.4+, nested patiently blocks are now patient.
Here is an example:
patiently do
outer_code
patiently do
inner_code
end
end
On spreewald 1.11.2+ the inner block will wait for the full configured wait time (by default 5 seconds). The outer patiently block would now be out of time, but it will always be retried at least a second time. This behavior allows with_scope to be patient, and it must be patient, as explained below.
In versions 1.10.4 - 1.11.1, inner blocks would keep giving the ou...
How to: Solve gem loaded specs mutex
Use bundler > 1.15 to fix Gem::LOADED_SPECS_MUTEX (NameError).
Given the following project:
ruby -v
ruby 1.8.7
bundler -v
Bundler version 1.13.7
gem -v
1.8.30
rails -v
Rails 3.2.22.1
Running specs or features resulted in:
uninitialized constant Gem::LOADED_SPECS_MUTEX (NameError)
The previous settings described in Maximum version of Rubygems and Bundler for Ruby 1.8.7 and Rails 2.3 (even the rails version was rails 3.2 and not 2.3) seems not to work here, so I used (also described in the ca...
How to control Chromedriver using curl
Here is how to use Chromedriver without libraries like selenium-webdriver. This can be useful for debugging.
The following example visits a web page and reads the a headline's text contents.
-
Create a session. You will get a JSON response containing lots of information about your Chrome session, including a
sessionId. Use that to send any future commands to your chromedriver session.$ curl -XPOST http://localhost:9515/session -d '{"desiredCapabilities":{"browserName":"chrome"}}' {"sessionId":"your-session-id-here","sta...
RSpec expects pending tests to fail
When flagging a spec that will be implemented later as pending, include a failing spec body or RSpec 3 will consider the pending test as failing.
The reasoning is: If the spec is flagged as pending but passes, it should not be pending. So these will fail:
it 'does something' do
pending
end
it 'does something else' do
pending
expect(1).to eq(1)
end
The first case may be unexpected, if you just wanted to write down that something should eventually happen that will be implemented later.
Instead, ...
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 ...
Think twice before using application names in your variables and methods
I recently had fun with replacing a custom video plattform ("mycustomtv") in one of our applications. I learned a lot about naming stuff.
youtube_mycustomtv_chooser is a bad name for a directive.
Why?
- if you add, for example, other video systems like vimeo, the name of the method either lies or will get longer.
- if you replace one system, you will have to change the method and all of its occurrences (and you're very lucky if it is only one poorly named thing in your application)
What would be a better solution?
Abstract fro...