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>
完成です。
参考にしたページ
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"} ] },
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 # 下書きとして投稿する ---
技術的な事
- はてなブログAtomPubを利用して投稿したり、カテゴリ一覧の取得を行っている。
- SublimeTextで補完を呼び出す機構は下記が参考になった。
今後の展望
- 既存の記事を再編集出来るようにしたりしたい。
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
で一発でインストールできます。