Skip to content

Commit

Permalink
Add old article draft.
Browse files Browse the repository at this point in the history
  • Loading branch information
olivierlacan committed Jul 27, 2015
1 parent b10cc79 commit c3e0ff3
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 0 deletions.
@@ -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.
23 changes: 23 additions & 0 deletions _posts/2015-04-03-autojump-around-your-command-line.md
@@ -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
18 changes: 18 additions & 0 deletions _posts/2015-04-08-forcing-ssl-on-a-github-pages-site.md
@@ -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
@@ -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.

0 comments on commit c3e0ff3

Please sign in to comment.