8
4

More than 3 years have passed since last update.

Gem 探報録 Shortener: Rails側で短縮URLを生成しよう!

Last updated at Posted at 2020-03-08

【概要】

初めて自分主導で Gem を導入するため、ざっと調べてみた。

【本文】

□ shortner とは?

  • Rails アプリケーション内で短縮URLを生成できる!
  • 短縮 URL を利用した計測もできる!(ユーザー等のモデル毎の計測も!)
  • めっちゃ長いパラメータ付きのURLも省略できる!
  • アプリケーション内でも外部でも、どこでもリダイレクトするよ!
短縮URLとは
# 長いURLが...
"http://localhost:3000/account_activations/C7EYgDf43N5OOh0S92wJlg/edit?email=user%40example.com"

# 短いURLに...!?
"http://localhost:3000/rd/fisafaf"

■ 概要

  • Shortener は 大別して3つの要素で構成される
    • Model では、短縮 URL の情報を保存
    • Controller では、短縮 URL のアクセスが来たら、元の URL へリダイレクト
    • Helper では、controllers と views で短縮 URL を生成
  • Shortener のイカす所
    • 301 でリダイレクトさせる(ページランクに影響するそうな)。1
    • 短縮 URL の英数字は ユニークである。
    • 短縮前 URL の使用回数を記録する。
    • リンクは、ユーザに関連付けが可能で、特定ユーザとリンク情報との統計情報が取得できる。


■ 初期設定

○ 1: Gemfile に追加

gem 'shortener'
bash
bundle install

○ 2: generator を実行して短縮URL用のテーブル生成23

bash
rails generate shortener
〜 migratation ファイルを以下の通り変更する〜
rails db:migrate
失敗した場合のlog
# MySQL におけるデフォルトのキー長は、 767 bytes であるため、生成ファイルだとエラーが発生する。
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Specified key was too long; max key length is 3072 bytes: CREATE  INDEX `index_shortened_urls_on_url`  ON `shortened_urls` (`url`(2083))
~timestamp~_create_shortened_urls_table.rb
# MySQLの設定によりキー長を 3072bytes まで拡張できるが、デフォルト値を使用する。
...
-  t.text :url, null: false, length: 2083
+  t.text :url, null: false, length: 767
...
-  add_index :shortened_urls, :url, length: 2083
+  add_index :shortened_urls, :url, length: 767
...

○ 3: ルーティング設定

routes.rb
...
+ get 'rd/:id', to: "shortener/shortened_urls#show", as: "shortend"
...

○ 4: 短縮URLを生成

bash
Shortener::ShortenedUrl.generate("https://www.tbs.co.jp/anime/machikado/")
=> #<Shortener::ShortenedUrl:0x00005593275cf228
 id: 1,
 owner_id: nil,
 owner_type: nil,
 url: "https://comic-fuz.com/series/1583",
 unique_key: "d91z2",
 category: nil,
 use_count: 0,
 expires_at: nil,
 created_at: Sun, 08 Mar 2020 04:31:43 UTC +00:00,
 updated_at: Sun, 08 Mar 2020 04:31:43 UTC +00:00>

token = Shortener::ShortenedUrl.find(1).unique_key
=> "d91z2"

Rails.application.routes.url_for(host: "localhost:3000", controller: "shortener/shortened_urls", action: :show, id: token)
=> "http://localhost:3000/rd/d91z2"

○ 5: 短縮URLにアクセスしてみると...

bash
# `use_count`の通りアクセス数がカウントされる
Shortener::ShortenedUrl.find(1)
=> #<Shortener::ShortenedUrl:0x0000562cdbe787c8
 id: 1,
 owner_id: nil,
 owner_type: nil,
 url: "https://comic-fuz.com/series/1583",
 unique_key: "d91z2",
 category: nil,
 use_count: 1,
 expires_at: nil,
 created_at: Sun, 08 Mar 2020 04:31:43 UTC +00:00,
 updated_at: Sun, 08 Mar 2020 04:31:43 UTC +00:00>

○ 6: その他

bash
# Railsアプリケーション内のルーティングへも設定可能
Shortener::ShortenedUrl.generate("/login")

# unique_key を元に登録済みのURLを検索可能
Shortener::ShortenedUrl.fetch_with_token(token: "d91z2")

■ 設定変更

○ unique_key の長さを変更

  • デフォルトでは5であるが、設定ファイルを生成して、次の通り長さを変更できる。
  • しかし、767bytes制限では大きく設定できない(15で失敗確認)。
config/initializers/shortener.rb
Shortener.unique_key_length = 10

○ token が一致しない場合のリダイレクト先を変更

config/initializers/shortener.rb
Shortener.default_redirect = "https://www.nintendo.co.jp/switch/acbaa/index.html"

○ unique_key の生成文字を変更

config/initializers/shortener.rb
# デフォルトがこれ
Shortener.charset = :alphanum

# ライブラリ内にある他の設定
## alphanum: ('a'..'z').to_a + (0..9).to_a,
## alphanumcase: ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a

# 新たに数字やハイフン等を追加できる...らしいが失敗する...
Shortener.charset = ("a".."z").to_a + (0..9).to_a + ["-", "_"]

■ short_url の可用性

  • 短縮 URL を生成する方法として、ヘルパーメソッドも定義されている。
  • console で試す場合は、事前に次の通りincludeしておくこと
    • include Shortener::ShortenerHelper
    • include Rails.application.routes.url_helpers
    • default_url_options[:host] = "localhost:3000"
  • 参考として、ライブラリのコードを次の通り記載する。
# shortener/app/helpers/shortener/shortener_helper.rb
  def short_url(url, owner: nil, custom_key: nil, expires_at: nil, fresh: false, category: nil, url_options: {})
    short_url = Shortener::ShortenedUrl.generate(
      url,
      owner:      owner,
      custom_key: custom_key,
      expires_at: expires_at,
      fresh:      fresh,
      category:   category
    )

    if short_url
      if subdomain = Shortener.subdomain
        url_options = url_options.merge(subdomain: subdomain)
      end

      options = { controller: :"/shortener/shortened_urls", action: :show, id: short_url.unique_key, only_path: false }.merge(url_options)
      url_for(options)
    else
      url
    end
  end

# shortener/app/models/shortener/shortened_url.rb
  def self.generate!(destination_url, owner: nil, custom_key: nil, expires_at: nil, fresh: false, category: nil)
    # if we get a shortened_url object with a different owner, generate
    # new one for the new owner. Otherwise return same object
    if destination_url.is_a? Shortener::ShortenedUrl
      if destination_url.owner == owner
        destination_url
      else
        generate!(
          destination_url.url,
          owner:      owner,
          custom_key: custom_key,
          expires_at: expires_at,
          fresh:      fresh,
          category:   category
        )
      end
    else
      scope = owner ? owner.shortened_urls : self
      creation_method = fresh ? 'create' : 'first_or_create'

      scope.where(url: clean_url(destination_url), category: category).send(
        creation_method,
        custom_key: custom_key,
        expires_at: expires_at
      )
    end
  end

○ サブドメイン付きでリダイレクト用URLが作成可能

bash
short_url("https://yahoo.co.jp/", url_options: { subdomain: "organization", host: "localhost:3000", protocol: "http" } )
=> "http://organization.localhost:3000/rd/BCDWTVbXwa"

○ 特定のモデルとも関連付けが可能

user.rb
# 関連付けして...
class User < ActiveRecord::Base
  has_shortened_urls
end
bash
# 生成する!
Shortener::ShortenedUrl.generate("https://comic-fuz.com/series/1583", owner: user)
short_url("https://comic-fuz.com/series/1583", owner: user)
=> #<Shortener::ShortenedUrl:0x00005600092337f0
 id: 6,
 owner_id: 1,
 owner_type: "User",
 url: "https://comic-fuz.com/series/1583",
 unique_key: "IUYIpUvD9O",
 category: nil,
 use_count: 0,
 expires_at: nil,
 created_at: Sun, 08 Mar 2020 07:33:10 UTC +00:00,
 updated_at: Sun, 08 Mar 2020 07:33:10 UTC +00:00>

User.first.shortened_urls.first.url
=> "https://comic-fuz.com/series/1583"

○ unique_key を独自に設定可能

  • 使用時はunique_key_lengthの設定等に注意すること。
bash
short_url("https://comic-fuz.com/series/261", custom_key: 'Original')
=> "http://localhost:3000/rd/Original"

○ 短縮URLの有効期限を設定可能

  • 切れたらデフォルト設定のURLへ遷移
bash
short_url("https://comic-fuz.com/series/261", expires_at: 1.minutes.since)

○ 短縮URLの再生製が可能

  • デフォルトで、短縮URLはユニークであるが、fresh: trueオプションで、再生製が可能となる。
bash
short_url("https://comic-fuz.com/series/261", fresh: true)

○ 生成される token に使用不可の文字列を設定可能である。

  • 例えば、http://localhost:3000/rd/termsと別のURLがルーティングしていた場合、token がその値に生成されたら、ルーティングが競合してしまう。
  • そのため、生成してはいけない token を設定可能である。(本記事ではディレクトリを切っているので、競合する心配はない)
config/initializers/shortener.rb
Shortener.forbidden_keys.concat %w(terms promo)

○ クローラー等に対する短縮URLカウント

  • デフォルトでは、クローラー等の訪問をカウントしている。
  • 次の通り設定することで、無視することができる。
config/initializers/shortener.rb
Shortener.ignore_robots = true

まだ機能があるけど割愛

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4