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 ...
xlsxtream: Streaming & Fast XLSX Spreadsheet Writer for Ruby
When writing XLSX files, there are gems like rubyXL or axlsx. While they do offer features like formatting or graphs, to represent spreadsheet cells, they have to keep several Ruby objects in memory. When writing huge files, both will become slow and consume lots of memory.
Enter Xlsxtream, a Ruby XLSX library with less features (e.g. no individual cell styles) but which does away with the memory issue by streaming ...
PostgreSQL's OVERLAPS operator is not fully inclusive
PostgreSQL supports the SQL OVERLAPS operator. You can use it to test if two date ranges overlap:
=> SELECT ('2001-02-16'::date, '2001-12-21'::date) OVERLAPS
('2001-12-20'::date, '2002-10-30'::date);
overlaps
--------
true
An important caveat is that the date ranges are defined as start <= time < end. As such the later date is not included in the range:
=> SELECT ('2001-02-16'::date, '2001-12-21'::date) OVERLAPS
('2001-12-21'::date, '2002-10-30'::date);
overlaps
--------
false
Also compar...
chromedriver-helper gem in Gemfile might break you selenium tests (of other projects)
Summary: Don't add chromedriver-helper to the Gemfile
- the executables might break your tests in projects where
chromedriver-helperis not in the Gemfile - developers with different chrome versions will have problems using the same
chromedriver-helperversion
Background
If you install the chromedriver-helper gem, but don't have it in you Gemfile, your selenium tests might fail with:
Selenium::WebDriver::Error::WebDriverError: unable to connect to chromedriver 127.0.0.1:9515
The reason is that chromedriver-helper ov...
Fix for: "can't convert nil to String" when running "gem update --system"
When attempting to update RubyGems, depending on updates your previously performed, you might run into an error
ERROR: While executing gem ... (TypeError)
can't convert nil into String
Fix this by running
gem uninstall rubygems-update -a -x
Don't require files in random order
A common pattern in Ruby is to to require all files in a specific diretory, using something like
Dir.glob(Rails.root.join('lib/ext/**/*.rb')).each do |filename|
require filename
end
However, this causes files to be required in an order determined by the file system. Since load order can be important, this may lead to different behavior on different machines which are hard to debug.
Simply add a .sort:
Dir.glob(Rails.root.join('lib/ext/**/*.rb')).sort.each do |filename|
require filename
end
Ruby 3
...
JavaScript: Testing the type of a value
Checking if a JavaScript value is of a given type can be very confusing:
- There are two operators
typeofandinstanceofwhich work very differently. - JavaScript has some primitive types, like string literals, that are not objects (as opposed to Ruby, where every value is an object).
- Some values are sometimes a primitive value (e.g.
"foo") and sometimes an object (new String("foo")) and each form requires different checks - There are three different types for
null(null,undefinedandNaN) and each has different rules for...
Capybara: Accessing the parent of an element
If you already selected an element and want to get its parent, you can call find(:xpath, '..') on it.
To get the grand-parent element, call find(:xpath, '../..').
Example
Find a link which contains a twitter icon and check that it links to the correct page:
<a href="http://twitter.com/">
<i class="icon is-twitter"></i>
</a>
link = page.find("a .icon.is-twitter").find(:xpath, '..')
expect(link[:href]).to eq("http://twitter.com/")
About XPath
There is a good overview on XPath syntax on [w3schools](htt...
Fixing flaky E2E tests
An end-to-end test (E2E test) is a script that remote-controls a web browser with tools like Selenium WebDriver. This card shows basic techniques for fixing a flaky E2E test suite that sometimes passes and sometimes fails.
Although many examples in this card use Ruby, Cucumber and Selenium, the techniques are applicable to all languages and testing tools.
Why tests are flaky
Your tests probably look like this:
When I click on A
And I click on B
And I click on C
Then I should see effects of C
A test like this works fine...
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', 'script end', (long ...
How to add a custom dictionary to Ruby Mine
- Download the dictionary from http://www.winedt.org/dict.html, e.g.
http://www.winedt.org/dict/de_neu.zip unzip de_neu.zipmkdir ~/Documents/diciconv -f UTF-16 -t UTF-8 de_neu.dic -o ~/Documents/dic/de_neu_utf8.dic- In RubyMine: Go to Settings (CTRL + ALT + S) > Editor > Spelling > Dictionaries and add the folder
~/Documents/dic
Bundler 2 will rename Gemfile and Gemfile.lock
TL;DR: Bundler 2.0 will rename Gemfile to gems.rb and Gemfile.lock to gems.locked (sic).
The old filenames will be supported until the release of Bundler 3.0.
RSpec: How to define classes for specs
RSpec allows defining methods inside describe/context blocks which will only exist inside them.
However, classes (or any constants, for that matter) will not be affected by this. If you define them in your specs, they will exist globally. This is because of how RSpec works (short story: instance_eval).
Negative example:
describe Notifier do
class TestRecord < ApplicationRecord # DO NOT do this!
# ...
end
let(:record) { TestRecord.new }
it { ... }
end
# TestRecord will exist here, outside of the spec!
D...
Import Excel files without running into memory limitations
There are several gems that make it easy to read and process xlsx files. Parsing the entire file at once however is error-prone since each cell is transformed to a ruby object - sometimes including thousands of formatted but empty cells.
As of today, I found two promising alternatives that provide a stream-based access to spradsheet rows:
- Roo supports multiple spreadsheet types like ODS or CSV and has a quite large contributor base
- [Creek](https://github.com/pythonicrubyis...
Ruby: A small summary of what return, break and next means for blocks
Summary
- Use
returnto return from a method.returnaccepts a value that will be the return value of the method call. - Use
breakto quit from a block and from the method that yielded to the block.breakaccepts a value that supplies the result of the expression it is “breaking” out of. - Use
nextto skip the rest of the current iteration.nextaccepts an argument that will be the result of that block iteration.
The following method will serve as an example in the details below:
def example
puts yield
puts ...
Custom Ruby method Enumerable#count_by (use for quick statistics)
I frequently find myself needing a combination of group_by, count and sort for quick statistics. Here's a method on Enumerable that combines the three:
module Enumerable
def count_by(&block)
group_by(&block)
.transform_values(&:count)
.sort_by(&:last)
.to_h
end
end
Just paste that snippet into a Rails console and use #count_by now!
Usage examples
- Number of email addresses by domain:
> User.all.count_by { |user| user.email.sub /^.*@/, '' }
=> { "sina.cn"=>2, ..., "hotmail.co...
Understanding Scope in Ruby
Scope is all about where something is visible. It’s all about what (variables, constants, methods) is available to you at a given moment. If you understand scope well enough, you should be able to point at any line of your Ruby program and tell which variables are available in that context, and more importantly, which ones are not.
The article gives detailed explanation on the variable scope in ruby with examples that are easy to understand. Every ruby developer should at least know the first part of the article by heart. The second half ...
How to use a local gem in your Gemfile
You can use local copies of gems in your Gemfile like this:
gem 'spreewald', path: '~/gems/spreewald'
As soon as you have bundled your project with the local copy of the gem, all code changes in the copy will be available on your project. So you can for example set a debugger or add console output in the gem and use it from your project.
If you checked out the gem with your versioning tool, you can easily reset your changes afterwards or make a pull request for the gem if you improved it.
Don't commit a Gemfile with local path...
IRB: last return value
In the ruby shell (IRB) and rails console the return value of the previous command is saved in _ (underscore). This might come in handy if you forgot to save the value to a variable and further want to use it.
Example:
irb(main):001:0> 1 + 2
=> 3
irb(main):002:0> _
=> 3
irb(main):003:0> a = _
=> 3
Caution when using the || operator to set defaults
I often see the use of || to set a default value for a variable that might be nil, null or undefined.
x = x || 'default-value'
This pattern should be avoided in all languages.
While using || works as intended when x is null or an actual object, it also sets the default value for other falsy values, such as false. false is a non-blank value that you never want to override with a default.
To make it worse, languages like JavaScript or Perl have [many more fal...
ActiveRecord::RecordNotFound errors allow you to query the :name and :id of the model that could not be found
ActiveRecord::RecordNotFound errors provide quite meaningful error messages that can provide some insight on application details. Consider the following:
ActiveRecord::RecordNotFound: Couldn't find Organisation::Membership with 'id'=12 [WHERE "organisation_memberships"."user_id" = 1]
You should probably not simply render those error messages to the user directly. Instead you you might want to re-raise your own errors. ActiveRecord::RecordNotFound provides you with methods :model and :id where you can get information about w...
Ruby: define a class with Struct.new
This card will show you a cool way to define a class using Struct.new.
A common usecase for Structs are temporary data structures which just hold state and don't provide behaviour. In many cases you could use a simple hash as a data structure instead. However, a Struct provides you with a nice constructor, attribute accessors and complains if you try to access undefined attributes. Structs are easy to compare (by attributes). A struct gives meaning to the data.
Disclaimer
Structs are great...
Speed up better_errors
If you use the Better Errors gem, you will sometimes notice that it can be very slow. This is because it sometimes renders a huge amount of data that will actually be hard to render for your browser.
You can significantly improve performance by adding this to config/initializers/better_errors:
if defined?(BetterErrors) && Rails.env.development?
module BetterErrorsHugeInspectWarning
def inspect_value(obj)
inspected = obj.inspect
if inspected.size > 20_000
inspec...
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...