Railsでリクルートの1dayjobの問題を実装してみる。

あけましておめでとうございます。 久しぶりにブログを書きます。

最近はこたつに入ってだらだらとRuby on Railsの本を読んでみたり、 フロントエンドのモダンな開発環境の勉強をしてホームページを作ってみたり、 競技プログラミングに興味を持ってC++の勉強をしたりしていました。

先月、株式会社リクルートホールディングスのウィンターインターンシップの選考のひとつだった1dayjobを受けて来ました。 そこで課せられた課題は「URL短縮サービスを作れ」という物でした。Twitterなどでよく見る「http://goo.gl/tet7pg」みたいな奴ですね。

これを当時はPythonとFlaskで作って提出したのですが、 周りの参加者の話をきくとRailsで作ったという人が多かったのでRailsの復習もかねて作ってみようと思い立ちました。

要件

詳細な要件は色々ありましたが、忘れてしまったのでざっくりと。

URLを短縮させる

以下のようなリクエストがあったときに

$ curl http://{url短縮サービス}/api/v1/shortenurl -H 'Content-\
Type: application/json' -d '{"LongUrl": "http://www.google.co.jp/"}'

以下のようなレスポンスを返す。

{
    "Created": 1418454534,

    "LongUrl": "http://www.google.co.jp/",
    "ShortUrl": "http://{url短縮サービス}/4"
}

短縮したURLを使って元のURLにアクセスさせる

以下のようなリクエストがあったときに、

$ curl http://{url短縮サービス}/4

以下のようなレスポンスを返す。

<HTML>
<HEAD>
<TITLE>Moved Permanently</TITLE>
</HEAD>
<BODY>
<H1>Moved Permanently</H1>
The document has moved <A HREF="http://www.google.co.jp/">here</A>.
</BODY>
</HTML>

実装してみる

まずは新規アプリケーションの作成

Railsのインストールとかは省略します。

shorturlという名前でアプリケーションを作成します。

$ rails new shorturl
$ cd shorturl

modelの作成

簡単にデータベースに短縮前のURLを保持しておき、 そのインデックスを短縮後のURLにすれば良いでしょう。 今回は省略しますが本番ではURLをもっと短くする為にインデックスを[0-9a-zA-Z]の62種の文字を使い、 10進数から62進数への基数変換を行いました。

ではモデルを作ります。

$ rails generate model url longurl:text
      invoke  active_record
      create    db/migrate/20150103080833_create_urls.rb
      create    app/models/url.rb
      invoke    test_unit
      create      test/models/url_test.rb
      create      test/fixtures/urls.yml

$ rake db:migrate
== 20150103080833 CreateUrls: migrating =======================================
-- create_table(:urls)
   -> 0.0016s
== 20150103080833 CreateUrls: migrated (0.0017s) ==============================

これでurlsというテーブルが作成されました。 以下のようにrails dbconsoleを使う事でデータベースのコンソールが立ち上がります。

$ rails dbconsole
SQLite version 3.7.13 2012-07-17 17:46:21
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .schema urls
CREATE TABLE "urls" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "longurl" text, "created_at" datetime, "updated_at" datetime);

コントローラーの作成

controller一つでやってしまいます。 今回は使いませんが、綺麗な形でTwitterみたいなバージョンのあるAPIを実装したいならGrape[1]というgemを使うと楽な様です。

$ rails generate controller main

app/controllers/main_controller.rb

# coding: utf-8
class MainController < ApplicationController

  skip_before_filter :verify_authenticity_token ,:only => [:shortenurl]

  def url2dict(url_obj)
    return ['Created' => url_obj.created_at.to_i,
            'LongUrl' => url_obj.longurl,
            'ShortUrl' => 'http://localhost:3000/' + url_obj.id.to_s]
  end

  # urlを短縮させる
  def shortenurl
    longurl = params[:main][:LongUrl]
    fetched = Url.find_by(:longurl => longurl)
    # もし存在すればそのまま返す
    if fetched
      render json: url2dict(fetched)
      return
    end
    # 存在しなければ登録し、返す。
    url = Url.new(:longurl => longurl)
    url.save!
    render json: url2dict(url)
    return
  end

  def index
    fetched = Url.find_by(:id => params[:id])
    @longurl = fetched.longurl
    render(:layout => false)
  end
end

skip_before_filter :verify_authenticity_token ,:only => [:shortenurl] としないとpostする際エラーが出ました。

viewの作成

短縮されたアドレスでアクセスされた時にリダイレクトさせる為のhtmlページを作成します。

app/views/main/index.html.erb

<HTML>
<HEAD>
<TITLE>Moved Permanently</TITLE>
</HEAD>
<BODY>
<H1>Moved Permanently</H1>
The document has moved <A HREF="<%= @longurl %>">here</A>.
</BODY>
</HTML>

routingを行う

Rails.application.routes.draw do
  post 'api/v1/shortenurl/' => 'main#shortenurl'

  get '/:id' => 'main#index'
end

動作確認を行う

$ rails server
=> Booting WEBrick
=> Rails 4.1.8 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
=> Ctrl-C to shutdown server
[2015-01-04 20:48:56] INFO  WEBrick 1.3.1
[2015-01-04 20:48:56] INFO  ruby 2.1.2 (2014-05-08) [x86_64-darwin13.0]
[2015-01-04 20:48:56] INFO  WEBrick::HTTPServer#start: pid=32252 port=3000

これでサーバが立ち上がりました。http://localhost:3000/でアクセスする事が出来ます。

短縮アドレスの登録

$ curl http://127.0.0.1:3000/api/v1/shortenurl -H 'Content-Type: application/json' -d '{"LongUrl": "http://sachaos.net/"}'
[{"Created":1420370841,"LongUrl":"http://sachaos.net/","ShortUrl":"http://localhost:3000/1"}]%

短縮アドレスにアクセス

$ curl http://127.0.0.1:3000/1
<HTML>
<HEAD>
<TITLE>Moved Permanently</TITLE>
</HEAD>
<BODY>
<H1>Moved Permanently</H1>
The document has moved <A HREF="http://sachaos.net/">here</A>.
</BODY>
</HTML>

完成です。

参考にしたページ

[1] [Ruby on Rails]Grapeを使ってWeb APIを作成する