š Iāve spent a decent chunk of today reading (listening to) Heads You Win by Jeffrey Archer.
Iām about 2/3 through and can highly recommend it! (Just donāt click on the above link if you donāt want a mild spoiler shoved in your face)
Book a private car 24x7 with no notice. Track it in real time. Shows up within a minute of scheduled time.
Public buses and trains run night and day. You can track them in real time. They show up within a minute or two of the scheduled time (or a replacement comes soon after).
Book an ISP technician for a suitable day (weeks in advance and only during work hours). ISP actually schedules it several days later (without asking, even though you arenāt available). You organise someone else to be on site for a 5 hour(!!!) window (canāt track technician in real time) but technician never shows (and you donāt even get an explanation until the next day). ISP reschedules for āearliest possible appointmentā… a week and a half later (almost 4 weeks after original request, not on a suitable day and they didnāt even provide a window, this time someone needs to be on standby all day).
To make it worse, this is for a business (i.e. costs extra), stock standard DSL connection (no bleeding edge technologies to figure out), from the second biggest ISP in the country!
My wife and I have been trying to not let busyness be an idol.
I like the premise behind āNo points for busyā but I think we can do better than āpoints for efficiency and productivityā…
Points for being present? For actually listening? For putting othersā needs first?
Iām looking forward to the inaugural RunWest 12km fun run tomorrow. My goal is 54 minutes or less.
After that itās time for some longer runs with only 61 days until my second marathon.
Passion is a consequence of effort, not just a cause
ā Adam Grant on the WorkLife podcast (overcast link)
In Advanced ActiveRecord Querying I learned you can nest joins.
I knew about joins(:suburb)
but didn’t realise you can join further, e.g. joins(suburb: :state)
.
You could even joins(suburb: { state: { country: { planet: { solar_system: :galaxy }}}})
!
N.B. Could ā Should
I know itās trendy to pick on Apple but Iām really impressed by how smooth the Apple ecosystem is these days:
I think thereās probably others but more and more I just do them without thinking (whereas I used to wait with bated breath to see if it would be successful). It seems to me like Apple has just been quietly iterating and improving each of these features to the point where they now feel rock solid and indispensable!
Mind. Blown.
Double-click a curly, square or round brace in the Mac terminal and it automatically selects the braces and everything inside them!
Great for quickly grokking complex objects in a Ruby console or large slabs of JSON!
Most problems donāt require more data. – Seth Godin - Data Into Information
Iām sure big data has uses but Iām yet to work in an org that has truly solved little data. Why make it any harder?
Data + Structure = Info
Info + Insight = Knowledge
Knowledge + Judgement = Wisdom
Today I encountered an issue where 2 + 2 was only equalling 3 when fetching data using ActiveRecord in Rails.
def permanent_staff
People.staff_like.where(
contract_type: Settings.permanent_staff_contract_types
)
end
def non_permanent_staff
People.staff_like.where.not(
contract_type: Settings.permanent_staff_contract_types
)
end
People.staff_like.count == permanent_staff.count + non_permanent_staff.count
=> false # (huh!?)
After a little bit of digging I realised some People were being excluded because they didn’t have a contract type:
People.staff_like.count == permanent_staff.count + non_permanent_staff.count +
People.staff_like.where(contract_type: nil).count
=> true
I assumed those with a NULL
contract type would be included in non_permanent_staff
but it turns out that’s not how databases work (at least not how PostrgreSQL or Sequel Server work, but I’m pretty sure this is “standard” behaviour). As soon as the query optimiser sees contract_type NOT IN (...
it filters out any results that don’t have a contract_type
.
My solution doesn’t feel like idiomatic Ruby or Rails but I solved it with the following method:
def non_permanent_staff
People.staff_like.where.not(
contract_type: Settings.permanent_staff_contract_types
).or(People.staff_like.where(contract_type: nil))
end
The generated SQL is a bit ugly (the staff_like
scope conditions gets included twice) but I’m sure the database can optimise that… better the DB deal with the data collection than Ruby.
If this method proves confusing to future-me I may just put a NOT NULL
constraint on contract_type
in the database and be done with it. It’s a string we’re importing from a legacy system so the default value would just be “.
When I was looking for information on Rails Metal/ActionController::Metal the other day I was struck once again by what a great resource RailsCasts is.
The Rails Metal episode is still up even though it’s no longer applicable, providing a useful historical perspective. Even better, there’s a link right at the top to an updated video released 3 years later that explains how to achieve the same thing following the removal of Rails Metal.
It’s clear Ryan Bates put a lot of love and attention into the whole site!
Git is great at comparing lines of code that have changed but not so good at individual words within a line or paragraph of text.
When diffing you can use git diff --word-diff
to get more granular word and punctuation changes but you wonāt see white space changes.
Use git diff --word-diff-regex=.
to see all changes at a more granular level.
You can see the changes that way but Iām yet to find an easy way to split out individual changes on the same line into separate commits. Patch mode doesnāt work for this (itās still whole lines at a time).
My goal was to take a fairly long document that Iād made a number of different changes to (punctuation, grammar, spelling mistakes, wording changes, whitespace errors, etc) and commit the changes by type… one commit for all the spelling mistakes, one for all the whitespace changes and separate ones for each of the suggested wording changes. The reason for this is to allow the different commits to be accepted or rejected individually.
It gets very tricky when one line has multiple changes… a typo, some whitespace errors and a suggested change. I want each of those in different commits but I canāt easily just grab one word of the line.
So far, the best method I have found (e.g. to just fix the spelling mistakes) is to use git diff --word-diff-regex=.
in one terminal window to see the individual changes, copy a whole line in my editor, discard the changes to that line, fix the spelling mistake again, save and stage that line, then paste the whole line back in with its other changes and save again.
The end result is the spelling fix is staged and the other changes to that line are saved in my working tree. Once Iāve repeated that process for all the spelling mistakes Iāll be able to commit and move on to one of the other fixes.
Overall Iām not happy with this technique so Iām going to research other options tomorrow.
After an interesting article on memory usage and bloating in Ruby I came across another article by the same author about the Annotate gem.
Annotate puts the relevant schema in your Rails models and tests as a comment so that you don’t have to keep referencing db/schema.rb
Today I Learned that to include a Ruby symbol in a YAML file it needs to be prefixed with !ruby/symbol
For example:
my_list_of_symbols:
- !ruby/symbol one
- !ruby/symbol two
becomes:
{ my_list_of_symbols: [:one, :two] }
Sometimes I’m happy to be a late adopter of Ruby and Rails, other times I feel like I missed out on all the fun.
Anyway, today I heard of Rails Metal for the first time.
Yep, I’m linking to a blog post from 10.5 years ago! How’s that for a hot take?
Today I started learning about ActiveJob.
Thanks to a deprecation warning I’m also learning about Sidekiq, Redis and how (not) to store data in Redis :)
Short version:
Various (but widespread) adoption problems with Teams, SharePoint and OneDrive are a good reminder that less is more…
Googleās offerings may have a shorter feature checklist but in my mind thatās a good thing. The fundamentals are solid and the features they do have, work.
Nate Berkopecās email series on practical Sidekiq has been really good.
The most recent one delved into Ruby memory usage in Sidekiq and in general and I found it really informative!
Ruby 2.6.2 is out (and 2.5.4) with some security fixes.
My upgrade steps (fish, homebrew and rbenv) were:
brew update; and brew upgrade ruby-build
rbenv install 2.6.2
I’m still amazed at how much effort people pour into these open source languages and tools!
Bundler is a very helpful tool for managing third party dependencies in Ruby.
Bundler takes a “Gemfile” where you specify which gems you want to use (and potentially which version). When you run bundle
(or bundle install
) it reads the Gemfile and automagically figures out dependencies, sub-dependencies, sub-sub-dependencies (you get the idea) and then tries to find mutually compatible versions of each of them. Once it’s done that it downloads them all, installs them and then records which versions it chose in a Gemfile.lock. You then commit your Gemfile and Gemfile.lock to version control to make sure your collaborators (and your deployed application!) are all using the same trusted versions.
Bundler is itself a gem so you do need to install it before you can use it to install (and manage) all your other gems. Assuming you already have ruby installed it’s as simple as:
gem install bundler
Recently, I was deploying an application to Heroku and I noticed a warning from the remote system. It’s only a warning but wherever possible I like to treat warnings as errors, otherwise they tend to build up and accumulate and hide real problems in the noise. Anyway the warning was:
Warning: the running version of Bundler (1.15.2) is older than the version that created the lockfile (1.17.1). We suggest you upgrade to the latest version of Bundler by running
gem install bundler
On a local machine that message (and the following two lines I snipped) is quite helpful but not so much when it’s coming from a server I can’t control. Thankfully, Heroku have documented all this very thoroughly. You should definitely read their documentation but the short version is that they only support a limited number of “carefully curated” bundler versions.
The best way around such warnings is to match your local version of Bundler to Heroku’s carefully curated version(s). That page above links to another page with the currently supported versions:
https://devcenter.heroku.com/articles/ruby-support#libraries
As of today that’s version 2.0.1 for Gemfile.locks bundled with 2.x and 1.15.2 for everything else.
I ended up upgrading to Bundler 2.0.1 but I could just as easily have reverted to 1.15.2.
Below are some instructions on how to manage which version(s) of Bundler you have installed and how to massage your environment to use a particular version of Bundler.
# To check which version(s) of bundler you currently have installed:
$ gem list | grep bundler
bundler (1.17.1)
# To install an older version
$ gem install bundler -v 1.15.2
Fetching: bundler-1.15.2.gem (100%)
...
1 gem installed
# To install currently supported 2.x version (currently 2.0.1)
$ gem install bundler -v 2.0.1
Fetching: bundler-2.0.1.gem (100%)
...
1 gem installed
# Check again:
$ gem list | grep bundler
bundler (2.0.1, 1.17.1, 1.15.2)
# Bundle with the latest installed version (now 2.0.1)
$ bundle install
# Try to bundle with an older version (may break if your Gemfile.lock was built with 2.x)
$ bundle _1.15.2_ install
Traceback...
Could not find 'bundler' (2.0.1) required by your Gemfile.lock (Gem::GemNotFoundException)
# Actually bundle with an older version
$ rm Gemfile.lock
$ bundle _1.15.2_ install
Prompted by Ruby Rogues episode 403 (Overcast link) I finally deployed my very unfinished Parkrun tracking app to Heroku today.
The premise of the episode is that Rails needs an “Active Deployment” gem built in to ease deployment of new apps to a variety of different services.
Our apps at work run an AWS EC2. To spin up a new one we currently need to create a new VM in AWS, somehow bootstrap the SSH certificates for ansible, run ansible to turn it into a Rails server and then run capistrano to deploy the application. It’s all documented in Confluence but I wouldn’t even know where to begin to try and create a similar set up for my own app(s).
The Heroku process was quite painless, their documentation is very thorough, but there were still a few sharp edges.
One thing that tripped me up for a while was getting Bundler versions in sync. I wrote up what I did on StackOverflow but I’ll cross post a lightly edited version on here for posterity.
Yesterday I read (listened to) The Fox by Frederick Forsyth (read by David Rintoul) š
It was an easy listen and a nice way to while away some hours. Thankfully, it didnāt distract by trying to be too technical with its description of firewalls and security š
Model View Controller (MVC) is a design pattern in which an applicationās code is divided by responsibility.
The āModelā refers to the underlying objects and code that represent (model) the business processes and logic. This includes the actual business objects themselves, stored data, schemas (including relationships, constraints/validations and indices) and operations specific to manipulating and storing the data. In a Rails app this usually consists of model objects, each of which inherits from ActiveRecord, the schema and migrations.
The āViewā refers to the parts of the application a user sees and interacts with. Pages, forms, tables, visualisations, feeds and summaries all form part of the view layer. In a Rails app the user usually interacts with HTML pages (with supporting CSS and JS) but other āviewsā of the application could include JSON/XML feeds or APIs. In Rails, these various views are often delivered using ERB templates & partials and the supporting assets are delivered using Sprockets and Webpack/Webpacker.
The āControllerā is responsible for mediating between the Model & the View and between the user & the application as a whole. In a Rails app the former is accomplished with ācontrollersā, which inherit from Action Controller, while the latter is handled by the routes file and routing system. Additional responsibilities usually handled at the Controller layer include caching and session management.
Some of the benefits of separating these responsibilities include the ability to test and refactor different parts in isolation, the option to reuse objects or even use the same Model (backend) with different Views (e.g. a web app, a RESTful API and an iOS app) and better designed interfaces which make for easier collaboration within (or between) teams.