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を作成する

Pythonでスクリプトを書いて色々と楽を出来る様にする。

何処でも使えるような単純なスクリプトを書く時、 どこかにまとめて保存して何処でも呼び出せる様にしておくと便利だと気づきました。

例えば、「ちょっとこのテキストの単語の出現頻度とかみたいな〜」という時に

$ cat test.txt | frequency.py

みたいな感じで何処でも呼び出せたりしたら便利ですよね。 積極的に楽していきましょう。

スクリプトを保存する所を決める。

こういうのって皆してると思うのですが、 何処に保存しておくのが一般的なのでしょうか?

僕は~/src/scriptsというディレクトリを作ってそこに入れる事にしました。

$ mkdir -p ~/src/scripts
$ cd ~/src/scripts

PATHを通す

何処でも呼び出せる様にパスを通します。 僕はzshを使っているので.zshrcに書き込みます。

export PATH=$PATH:$HOME/src/scripts/

zshに補完される様にする。

frequency.py なんて、長くて覚えられないし、打つのも怠いのでzshに補完される様にします。

zstyle ':completion:*' command 'ls /Users/sakao/src/scripts'

スクリプトを作る

frequency.pyを作りましょう

#!/usr/bin/env python
# coding:utf-8

import sys
import os

from collections import Counter
import MeCab

m = MeCab.Tagger("-Owakati")

def tokenize(string):
    return m.parse(string).split()

def main():
    tokens = tokenize(sys.stdin.read())
    counter = Counter(tokens)
    for word, cnt in counter.most_common():
        print word, cnt

if __name__ == "__main__":
    main()

実行権限を与える

$ chmod u+x frequency.py

これで実行権限を与える事が出来ました。

試してみる

$ cd ~
$ echo "本日は晴天なり。\n本日は曇天なり。" | frequency.py
本日 2
は 2
。 2
なり 2
曇天 1
晴天 1

ちゃんと動くようです。便利。

SublimeTextで一つのキーバインドで複数のコマンドを実行する。Sidebarの操作をdirex+popwin的にする方法。

SublimeTextで一つのキーバインド複数のコマンドを実行

SublimeTextで一つのキーバインド複数のコマンドを実行するプラグインを見つけた。

sublime-text-multiple-commands

色々応用できて楽しそう。

Sidebarの操作をdirex+popwin的にする

emacsではディレクトリをsidebar的に表示してファイル選択できるdirexというパッケージがあります。

それが凄く便利で中毒になってしまったので それっぽい動作をSublimeTextでも出来ないかとmultiple commandsを使ってやってみました。

単純にキーバインドを設定するだけで大丈夫です。

    {
        "keys": ["ctrl+x", "ctrl+j"],
        "command": "run_multiple_commands",
        "args": {
            "commands": [
                {"command": "toggle_side_bar", "context": "window"},
                {"command": "focus_side_bar", "context": "window"}
            ]
        },
        "context": "window"
    },
    {
        "keys": ["enter"],
        "command": "toggle_side_bar",
        "context": [{"key": "control", "operand": "sidebar_tree"} ]
    },

C-x C-jでsidebarが開き、enterで選択し終えたらSidebarが閉じてくれます!便利!

SublimeText3ではてなブログに投稿するプラグインを作った

僕はブログが苦手で大体三日坊主で終わるんですが、 自分でツールを作れば愛着が湧いて積極的に使う様になるんじゃないかと思い、 大好きなSublimeTextではてなブログに投稿できるプラグインを作りました。

2014/09/27 追記: 友人に動かないと指摘されたので、アップデートしました

Git

https://github.com/sachaos/SublimeHatena

SublimeText

恋に落ちると話題のテキストエディタPythonで簡単にプラグインが作れる。

SublimeText2はPython2系列、 SublimeText3はPython3系列で動いています。

なにがうれしいのか?

  • SublimeTextで記事が書けてそのまま投稿できる。
  • カテゴリーを補完できるのが便利。
  • SublimeTextだとmarkdownが書きやすいのでその恩恵を受ける事が出来る。

使い方

インストール

$ cd {パッケージをおいてるところ}
$ git clone https://github.com/sachaos/SublimeHatena.git

accountの設定をする

インストールするとアカウントの設定がなされていない場合、 packages/User/以下にSublimeHatena.sublime-settingsというファイルが自動的に作成されます。

JSON形式でユーザー名、ブログID、APIキー(はてなブログの詳細設定に記載されている)を書き込んでください。

{
    "user_name": "your_user_name",
    "blog_id": "your_blog_id.hatenablog.com",
    "api_key": "your_api_key"
}

コマンドパレットに以下のコマンドが出ていれば正常に登録できています。

サンプル

command + shift + p

Hatena: New empty article

新しい記事を作成します

Hatena: Post this article

現在開いているタブをはてなブログに投稿します。 ヘッダがない場合、下書きとして投稿することになっています。

どんな物でも投稿してしまうので気をつけてください!

ヘッダの情報の追加

初期では以下のようになっていますが、 要素を加える事が可能です。

---
title: 
categories:
---

例えば、

---
title: this is title
name: hogehoge # author を設定
categories:PHP, Composer
draft: yes # 下書きとして投稿する
---

技術的な事

今後の展望

  • 既存の記事を再編集出来るようにしたりしたい。

PHP: Composerの使い方ゆるまとめ

インターン先ではPHPで開発し、 パッケージ管理にComposerを利用するらしいので 使い方をまとめておこうと思う。

Composer?

ライブラリを管理するもの。便利。

インストール

Composerはプロジェクト単位でライブラリを管理するのが主流らしい。 (もちろんシステム全体で使える様にする事も出来る。) ライブラリ管理を行うフォルダで

curl -s http://getcomposer.org/installer | php

して、composer.pharをゲットする。

ヘルプ

php composer.phar list

で観れる。

ライブラリを入れる

php composer.phar require "phpunit/phpunit=4.1.*"

phpunitのバージョン4.1なんとかを入れました。

{
    "require": {
        "phpunit/phpunit": "4.1.*"
    }
}

こんな風にcomposer.jsonに追加されるはずです。

プロジェクトを共有する事になったって場合

composer諸々があるならば

php composer.phar installで一発でインストールできます。