Rails: When defining scopes with class methods, don't use `self`
Sometimes it is useful to define a named scope by implementing a static method with the scope's name on the scoped class. For instance, when a method should decide which existing scope should be the next link in the scope chain. Take this class for example:
class Meal < ActiveRecord::Base
named_scope :for_date, lambda { |date| :conditions => { :date => date }}
named_scope :with_meat, :conditions => { :meat => true }
named_scope :without_meat, :conditions => { :meat => false }
def self.suitable_for(user)
if user.vegetar...
How to grep through the DOM using the Capybara API
When your Cucumber feature needs to browse the page HTML, and you are not sure how to express your query as a clever CSS or XPath expression, there is another way: You can use all
and find
to grep through the DOM and then perform your search in plain Ruby.
Here is an example for this technique:
Then /^I should see an image with the file...
Ruby / Rails: clone vs. dup vs. deep_dup
Ruby and Rails have several methods for creating a new object that looks like another: clone
, dup
, deep_dup
. When using them you should be aware of their differences so that you can select the method you really need.
clone
- Shallow copy: references to other objects/values are copied (instead of cloning those objects/values)
- Clones the object and all its "special object attributes" like
frozen
,tainted
and modules that the object has been extended with - [Ruby 2.6 documentation for clone](https://devdocs.io/ruby~2.6/obj...
Rubygems: Installing the last version of rubygems that has no rubyforge_project deprecation warning
You can install rubygems 3.0.8
(released on February 18, 2020) to keep all the Gem::Specification#rubyforge_project
deprecation warnings away from your development log. With Rubygems >= 3.1
this deprecation warning was introduced. While maintaining older projects this could get quite annoying and the fix below might okey, for newer projects the right ways is to upgrade the gems.
gem update --system 3.0.8
Example message:
NOTE: Gem::Specification#rubyforge_project= is deprecated with no replacement. It will be removed o...
Git: How to add changes matching a regular expression
When you have many changes, and you want to spread them across different commits, here is a way to stage all changes matching a given regular expression for a single commit.
Example
Consider the following git diff
output.
diff --git a/file1.rb b/file1.rb
index 806ca88..36d536b 100644
--- a/file1.rb
+++ b/file1.rb
@@ -1,7 +1,5 @@
-# Here is a useless comment.
-# It will be removed.
class File1
- def foo
+ def bar
# ...
end
end
diff --git a/file2.rb b/file2.rb
index 550e1c6..600f4e3 100644
--- a/file2.rb
+++ b/file2...
PSA: "index: true" in Rails migrations does not work as you'd expect
Several Rails migration methods accept index: true
as an option to create an index. In some cases (like #add_column
), this option is silently discarded. Know what you are doing, or use #add_index
instead.
Example
Consider the following migration.
class CreateExamples < ActiveRecord::Migration
def change
create_table :examples do |t|
t.references :category, index: true
t.boolean :positive, index: true
t.integer :number_of_participants, index: true
end
add_reference :examples, :user, index: tr...
Five years of "Today I Learned" from Josh Branchaud
The linked GitHub repository is a bit like our "dev" cards deck, but groomed from a single person (Josh Branchaud). It includes an extensive list of over 900 TILs on many topics that might be interesting for most of us. (e.g. Ruby, Rails, Git, Unix..)
Ruby
Here is an excerpt of all the Ruby TILs that were new to me. I encourage you to take your time to skim over the original list as well!
-
Assoc For Hashes
- `Hash#ass...
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...
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
.simplecov
file in your project root:SimpleCov.start 'rails' do # any custom configs like groups and filters can be here at a central place enable_cov...
Using #deep_dup for copying whole hashes and array
"Everything in Ruby is an object". This is also true for nested hashes and arrays. If you copy a hash with #clone
or #dup
and you modify the copy, you will run into the following behavior:
original_hash = { foo: { bar: 'original value' } }
copied_hash = original_hash.dup
copied_hash[:foo][:bar] = 'changed value'
original_hash # => { foo: { bar: "changed value" }
This is, because { bar: 'baz' }
is an object, which is referenced in :foo
. The copy of original_hash
still holds the reference to the same object, so alterin...
Async control flow in JavaScript: Promises, Microtasks, async/await
Slides for Henning's talk on Sep 21st 2017.
Understanding sync vs. async control flow
Talking to synchronous (or "blocking") API
print('script start')
html = get('/foo')
print(html)
print('script end')
Script outputs 'script start'
, (long delay), '<html>...</html>'
, 'script end'
.
Talking to asynchronous (or "evented") API
print('script start')
get('foo', done: function(html) {
print(html)
})
print('script end')
Script outputs 'script start'
, `'...
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...
Devise: How to allow only HTTP Basic Auth and disable the HTML sign-in form
By default, Devise redirects to a sign-in form when accessing a route that requires authentication. If for some reason you do not want this, but use Basic Authentication (and the corresponding browser username/password dialog) instead, this is a simple change.
Note that Devise's default configuration actually only redirects requests for HTML content (as requested by the HTTP Accept
header).
For all other formats (like JSON) it would use Basic Auth if the http_authenticatable
setting was enabled. So you can simply enable that flag and cl...
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.
As always, with great power comes great 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 helpful:
Avoid stating what some code does – prefer the Why
Some say, perfect code would not need a single comment. Th...
Writing a README for a project
Rails applications and ruby gems should have a README that gives the reader a quick overview of the project. Its size will vary as projects differ in complexity, but there should always be some introductory prose for a developer to read when starting on it.
Purpose
That's already the main purpose of a project README: Give a new developer a quick overview of the project. In sketching this outline, the README should notify the reader of any peculiarity he needs to know of.
Remember that in a few months, you'll be a kind of "new ...
Heads up: Ruby's Net::HTTP silently retries a failing request
Ruby's Net::HTTP library repeats a failing request once, as long as it deems it idempotent (GET, HEAD etc). Both requests will use the configured timeout. Hence, if both requests time out, Net::HTTP will only return after twice the configured timeout.
This can become an issue if you rely on the timeout to strike precisely.
HTML/CSS: "transparent" is not a color
Heads up: transparent
is not a real color, but black
with 0% opacity.
In Safari, this makes e.g. gradients between transparent and white have some gray tones in the middle.
/* Unexpected in Safari */
linear-gradient(to right, white 0%, transparent 100%)
/* Correct */
linear-gradient(to right, white 0%, rgba(255,255,255,0) 100%)
Best Practice: Creating User Accounts Without Sending the Password
In applications without a sign-up, user accounts are usually created by an admin. This imposes two challenges:
- How to transmit the password securely and
- How to make the user change the initial password immediately
There is a simple solution: create the account with a secret password, then ask the user to use the password reset with his user name.
How to avoid ActiveRecord::EnvironmentMismatchError on "rails db:drop"
After loading a staging dump into development, you might get an ActiveRecord::EnvironmentMismatchError
when trying to replace the database (like rails db:drop
, rails db:schema:load
).
$ rails db:drop
rails aborted!
ActiveRecord::EnvironmentMismatchError: You are attempting to modify a database that was last run in `staging` environment.
You are running in `development` environment. If you are sure you want to continue, first set the environment using:
bin/rails db:environment:set RAILS_ENV=development
Starting with R...
Event order when clicking on touch devices
Touch devices have their own set of events like touchstart
or touchmove
. Because mobile browsers should also work with with web applications that were build for mouse devices, touch devices also fire classic mouse events like mousedown
or click
.
When a user follows a link on a touch device, the following events will be fired in sequence:
touchstart
touchend
mousemove
mousedown
mouseup
click
Canceling the event sequence
-------------------...
Bash: How to use colors in your tail output
Sometimes it's nice to have some coloring in your logs for better readability. You can output your logs via tail
and pipe this through sed
to add ANSI color annotations (which your console then interprets).
To print a log (e.g. rails log) and color all lines containing "FATAL" in red and all lines with "INFO" in green:
tail -f /path/to/log | sed --unbuffered -e 's/\(.*INFO.*\)/\o033[32m\1\o033[39m/' -e 's/\(.*FATAL.*\)/\o033[31m\1\o033[39m/'
Here are the ...
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.