mattak's blog

気の向くままに書く

mongoを使ってurl shortenerつくる

mongoぇ

基本的な使い方はわかったけど、アプリどう組み込むのかの経験をつまぬば。。 簡単そうなアプリケーションとして、url shortenerを作ってみる。

rubyでmongo

とりあえず、rubyで使えるかをためす。 ちゃんと、利用できるgemがあるみたい。。

$ gem i mongo
$ gem i bson_ext

bson_extは

For optimal performance, use of the BSON extension is recommended.

っていわれたので、入れた。 早速使ってみる。

$ pry 
> connect = Mongo::Connection.new
> connect.database_names
=> ["local", "mydb", "test"]

データベースの選択

> connect.db("mydb")

コレクションの選択

> coll = db.collection('things')

コレクションの表示

> coll.find.each { |doc| puts doc }

{"_id"=>BSON::ObjectId('514d9d5d41e70994840e6c01'), "name"=>"mongo"}
{"_id"=>BSON::ObjectId('514d9d6541e70994840e6c02'), "x"=>3.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c03'), "x"=>1.0, "y"=>1.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c04'), "x"=>1.0, "y"=>2.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c05'), "x"=>1.0, "y"=>3.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c06'), "x"=>1.0, "y"=>4.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c07'), "x"=>1.0, "y"=>5.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c08'), "x"=>1.0, "y"=>6.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c09'), "x"=>1.0, "y"=>7.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c0a'), "x"=>1.0, "y"=>8.0}
{"_id"=>BSON::ObjectId('514d9f6c41e70994840e6c0b'), "x"=>1.0, "y"=>9.0}

ドキュメントの挿入

> doc = { x: 1, y: 10 }
> id = coll.insert(doc)

基本はOK。。

url shortener

urlを受け取って、短くしたurlを返すアプリ。 代表的なサービスはbit.ly とかgoo.glとか。

仕様

基本的にコマンドラインからも利用できて、

登録 (POST) :

curl -d url="http://www.google.com/search?q=hello" http://localhost:4567/
> http://localhost:4567/x12d

参照 (GET) :

curl http://localhost:4567/x12d
> http://www.google.com/search?q=hello

見たいなかんじで参照できる。

4桁の数字で短くしてくれるようにする。 小文字アルファベットと数字を使えば、 (26 + 10) = 36文字 364 = 1679616

168万件は許容できる. リリースするようなサービスじゃないので十分。

短縮urlの作成

シンプルに乱数を作る

def strand(n)
   char = (("a".."z").to_a + (0..9).to_a)
   n.times.collect { |i| char.shuffle[0] }.join
end

アプリを作る

ひな形作成

$ bundle gem shurl

ディレクトリ

$ tree .
.
├── Gemfile
├── README.md
├── Rakefile
├── app.rb
├── config.ru
├── lib
│   ├── shurl
│   │   └── version.rb
│   └── shurl.rb
└── shurl.gemspec

ロジック部分

mongodbにurlデータとそのハッシュ値を入れるだけ。

require 'mongo'

module Shurl
  class Model
    # mongo db setup
    def initialize()
      connect = Mongo::Connection.new
      db      = connect.db("shurl")
      @coll   = db.collection("urls")
    end

    def strand(n)
      char = (("a".."z").to_a + (0..9).to_a)
      n.times.collect { |i| char.shuffle[0].to_s }.join
    end

    def to_shorturl(request, code)
      if request.port == 80
        request.scheme.to_s + "://" \
          + request.host.to_s + "/" \
          + code.to_s
      else
        request.scheme.to_s + "://" \
          + request.host.to_s + ":" + request.port.to_s + "/" \
          + code.to_s
      end
    end

    def to_longurl(code)
      doc = @coll.find_one({code: code})
      doc["url"] if doc != nil
    end

    def find_or_create_url(request, longurl)
      doc = @coll.find_one({url: longurl})

      # create
      if doc == nil 
        code = strand(4)
        doc = { "url" => longurl, "code" => code }
        @coll.insert(doc)
      end

      to_shorturl(request, doc["code"])
    end
  end
end

ルーティング部分

require 'sinatra'
require 'shurl'

#
# routing
#

shurl = Shurl::Model.new

get '/' do
  "please post url"
end

# show raw url
get '/s/:code' do
  url = shurl.to_longurl(params[:code])

  if url == nil
    "no url found"
  else
    url
  end
end

# redirect
get '/:code' do
  url = shurl.to_longurl(params[:code])

  if url == nil
    redirect '/'
  else
    redirect url
  end
end

# shorten
post '/' do
  if params[:url] == nil
    halt 400, "please specify url param"
  else 
    short_url = shurl.find_or_create_url(request, params[:url])
    short_url
  end
end

実行

$ bundle install --path vendor/install
$ bundle exec rackup

試す

長いURLの記録

$ LONG_URL="https://maps.google.co.jp/maps?q=35.710158,139.812784&num=1&vpsrc=0&hl=ja&brcurrent=3,0x60188ed7f6bdc0a3:0xaa8fcf79ad1e5576,0&ie=UTF8&ll=35.71258,139.812741&spn=0.010088,0.01781&t=m&z=16&iwloc=A"

$ curl --data-urlencode url="$LONG_URL" http://localhost:4006/
http://localhost:4006/tklk

短縮urlのアクセス

$ curl -L http://localhost:4006/tklk

リンク先の中身を見る

$ curl -L http://localhost:4006/s/tklk
https://maps.google.co.jp/maps?q=35.710158,139.812784&num=1&vpsrc=0&hl=ja&brcurrent=3,0x60188ed7f6bdc0a3:0xaa8fcf79ad1e5576,0&ie=UTF8&ll=35.71258,139.812741&spn=0.010088,0.01781&t=m&z=16&iwloc=A

ポストしつつ、飛ぶ。

$ curl -L `curl --data-urlencode url="$LONG_URL" http://localhost:4006/`

さくっとかけていいね!

せっかくなので

githubにあげた。

http://github.com/mattak/shurl