Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b10cc79
commit c3e0ff3
Showing
4 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
_posts/2015-03-30-better-feedback-on-ruby-2-2-keyword-argument-errors.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
layout: post | ||
title: "Better Feedback on Ruby 2.2 Keyword Argument Errors" | ||
date: 2015-03-30 19:21 | ||
location: "Paris, France" | ||
categories: ruby | ||
--- | ||
|
||
I was building a class yesterday and I wanted the instantiation process of the class | ||
to be self-documenting so I decided to use a keyword argument in the `initializer` method. | ||
|
||
```ruby | ||
class HallMonitor | ||
def initialize(user: nil) | ||
@user = user | ||
end | ||
end | ||
``` | ||
|
||
As you can see, I also decided to make the `user` keyword argument optional by | ||
making it default to nil. | ||
|
||
Later while pairing and refactoring the implementation of the class and the | ||
associated specs, I forgot about my fancy keyword argument and simply wrote | ||
the following: | ||
|
||
```ruby | ||
HallMonitor.new(user) | ||
``` | ||
|
||
This raised the following error: | ||
|
||
```ruby | ||
ArgumentError: wrong number of arguments (1 for 0) | ||
``` | ||
|
||
I was confused for a second. I hesitated, then said "it makes no sense" out loud | ||
because I remembered making the `user` argument optional. Of course I had misread | ||
the exception as "wrong number of arguments (**0 for 1**)", which it wasn't. | ||
|
||
Then I went back to the class, looked up the method definitation and went "aaaahhh!". | ||
|
||
But here, Ruby could have been far more helpful to me. It made sense before we | ||
had keyword arguments for Ruby to respond with an exception that would just compare | ||
the number of provided arguments against the number of required arguments. Nowadays | ||
we have keyword arguments, Ruby can tell us exactly what argument is missing and | ||
what its name is (if we use keyword arguments of course), so why not throw this exception | ||
instead: | ||
|
||
```ruby | ||
ArgumentError: wrong number of arguments (1 for 0), keyword arguments: [user: nil] | ||
``` | ||
|
||
This kind of simple error feedback improvements, similar to [one proposed by | ||
Richard Schneeman](https://bugs.ruby-lang.org/issues/10982) earlier this year | ||
would probably save a Rubyists time debugging simple mistakes. And that's bound | ||
to make them happier, isn't it? | ||
|
||
If you agree or disagree, let me know on [Twitter](http://twitter.com/olivierlacan) or by | ||
email. I'll submit a feature suggestion to [bugs.ruby-lang.org](http://bugs.ruby-lang.org) | ||
if nobody objects for a sensible reason. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
layout: post | ||
title: "Autojump Around your Command Line" | ||
date: 2015-04-03 8:40 | ||
location: "Paris, France" | ||
categories: development | ||
draft: true | ||
--- | ||
|
||
Pairing with someone for the first time is always an exciting time for me. It | ||
used to be anxiety-inducing when I was still convinced that pairing with | ||
someone I considered more experienced was an opportunity for them to discover | ||
that I had no idea what I was doing. | ||
|
||
Of course, while some pairing sessions go better than others, that didn't happen. | ||
Or at least people I paired with didn't tell me that they thought I was a fraud. | ||
I didn't get fired right after my early pairing session either. So I found solace | ||
in the fact that had I been *shockingly bad* they might have taken the time to | ||
tell someone else how much I sucked. | ||
|
||
I call that dealing with impostor syndrome through rationalizations. | ||
|
||
Back to pairing. Even when I was far less experienced than I am now |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
layout: post | ||
title: "How to setup free SSL on a Static GitHub Pages site" | ||
date: 2015-04-08 9:32 | ||
location: "Paris, France" | ||
categories: development | ||
draft: true | ||
--- | ||
|
||
Monday morning I made [a website](https://cantheyseemydick.com) on a whim after watching [an impressive interview of Edward Snowden from John Oliver](https://youtu.be/XEVlyP4_11M?t=14m9s). | ||
|
||
I took me 27 minutes to create the basic shell of the website, transcribe the relevant portions of the interview, tweak the styles and remove unnecessary things. | ||
|
||
A few hours later a security engineer at "Large Tech Company" [made a great suggestion on Twitter](https://twitter.com/bcrypt/status/585151317779165184): | ||
|
||
> .@olivierlacan can you pls support SSL on this very important website so they can't see if I'm seeing if they can see my hypothetical dick? | ||
The irony of having a website about warrantless privacy intrusion being succeptible to warrantless privacy intrusion was too great not to tackle. I asked for help from a few other people on Twitter who were in agreement since I had never setup SSL/HTTPS |
151 changes: 151 additions & 0 deletions
151
_posts/2015-04-16-migrating-an-ad-hoc-url-slug-system-to-friendly-id.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
--- | ||
layout: post | ||
title: "Migrating an ad-hoc URL slug system to FriendlyId" | ||
date: 2015-04-16 13:42 | ||
location: "Paris, France" | ||
categories: development | ||
--- | ||
|
||
I recently decided to migrate from a ad-hoc solution for URL slugs on [Orientation](https://github.com/orientation/orientation) to [Norman Clarke](https://github.com/norman)'s excellent [FriendlyId gem](https://github.com/norman/friendly_id). | ||
|
||
The main reason was that wanted to keep a slug history in order to avoid `404 File Not Found` | ||
errors when users decide to change an article title. I have to be able to guarantee that you will | ||
find a piece of documentation regardless of how many time its title has changed in the past. | ||
|
||
## Before | ||
|
||
This is how slugs were generated originally in the Article model: | ||
|
||
```ruby | ||
# app/models/article.rb | ||
class Article < ActiveRecord::Base | ||
|
||
# ... | ||
|
||
before_validation :generate_slug | ||
|
||
# ... | ||
|
||
validates :slug, uniqueness: true, presence: true | ||
|
||
# ... | ||
|
||
def to_param | ||
slug | ||
end | ||
|
||
# ... | ||
|
||
private | ||
|
||
def generate_slug | ||
if self.slug.present? && self.slug == title.parameterize | ||
self.slug | ||
else | ||
self.slug = title.parameterize | ||
end | ||
end | ||
end | ||
``` | ||
|
||
And this is how they were used in the controller: | ||
|
||
```ruby | ||
# app/controllers/articles_controller.rb | ||
class ArticlesController < ApplicationController | ||
respond_to :html, :json | ||
|
||
# ... | ||
|
||
def show | ||
@article = Article.find_by(slug: params[:id]) || Article.find(params[:id]) | ||
respond_with @article | ||
end | ||
end | ||
``` | ||
|
||
One important note here about `find_by`. Unlike the regular ActiveRecord `find`, `find_by` does not raise a `RecordNotFound` exception when it finds no matches. Instead it returns `nil` which is falsey. This is why it comes before the `find` call, which was only present as a legacy concern to allow people to access `/articles/33` instead of using Article 33's slug. | ||
|
||
## After | ||
|
||
After installing FriendlyId and creating the `friendly_id_slugs` table, I replaced the code above with this: | ||
|
||
```ruby | ||
# app/models/article.rb | ||
|
||
class Article < ActiveRecord::Base | ||
include Dateable | ||
extend ActionView::Helpers::DateHelper | ||
extend FriendlyId | ||
|
||
friendly_id :title, use: [:slugged, :history] | ||
|
||
def should_generate_new_friendly_id? | ||
!has_friendly_id_slug || title_changed? | ||
end | ||
|
||
def has_friendly_id_slug? | ||
slugs.where(slug: slug).exists? | ||
end | ||
end | ||
``` | ||
|
||
```ruby | ||
# app/controllers/articles_controller.rb | ||
class ArticlesController < ApplicationController | ||
respond_to :html, :json | ||
|
||
# ... | ||
|
||
def show | ||
@article = Article.friendly.find(params[:id]) | ||
respond_with_article_or_redirect | ||
end | ||
|
||
# ... | ||
|
||
private | ||
|
||
def respond_with_article_or_redirect | ||
# If an old id or a numeric id was used to find the record, then | ||
# the request path will not match the post_path, and we should do | ||
# a 301 redirect that uses the current friendly id. | ||
if request.path != article_path(@article) | ||
return redirect_to @article, status: :moved_permanently | ||
else | ||
return respond_with @article | ||
end | ||
end | ||
end | ||
``` | ||
|
||
The useful method here is `has_friendly_id_slug?` because it allows me to check | ||
whether an article has a slug that was generated by FriendlyId or by my old | ||
ad-hoc system. | ||
|
||
FriendlyId creates a simple `has_many` association between the model and `FriendlyId::Slug`. I could have used `friendly_id` (the dynamic reader method) because the default database column name for the locally-stored (on the `articles` table instead of the `friendly_id_slugs` table) slug is... `slug`. That's configurable of course, but I know for a fact that the column name I had used with my ad-hoc system was `slug` so there's no point in relying on FriendlyId to figure out the actual column name for me. | ||
|
||
## Why bother? | ||
|
||
So anyway, say we have an article titled "Banana". That article's slug column should already be set to `banana` (per my ad-hoc slug system) but a freshly installed FriendlyId should mean that there is not `friendly_id_slugs` record for `banana`. Thanks to `has_friendly_id_slug?`, we can check. | ||
|
||
And thanks to that check, we can decide to easily migrate all the cached slugs (i.e. on the `articles` table) to FriendlyId. All it takes is: | ||
|
||
```ruby | ||
Article.all.each(&:save!) | ||
``` | ||
|
||
Why bother? Because if we're using FriendlyId's History module, we need its `friendly_id_slugs` table to contain the original slug. Without doing this, the first slug stored in FriendlyId would be a future one (when an article's title is eventually modified) and not the current one stored inside of the `articles.slug` column. | ||
|
||
``` | ||
a = Article.friendly.find("banana") | ||
a.title = "Pamplemousse" | ||
a.save! | ||
a.friendly.find("banana") | ||
=> #<Article:0x0000010b6913b8 | ||
``` | ||
|
||
Everybody's happy and nobody bumps into 404s! The end. | ||
|
||
PS: Thanks to Normal Clarke for [the tip that helped me](https://twitter.com/compay/status/588696133817520129) find this solution. |