Mastodonを支えるgems

[Mastodon](https://github.com/tootsuite/mastodon)のコードを読んでみることにしました。

知らないgemが一杯あったのでまずは調べてみることにしました。標準添付ライブラリ、Railsにデフォルトで含まれているものは除きます。また、`group :development`とか、group指定ありのは疲れたのでまた今度。

[今回確認したGemfile](https://github.com/tootsuite/mastodon/blob/58bcd50f7f8c187f8c0bd4a1d52aecafd76979fc/Gemfile)

実際のコードに目を通しながら調べましたが、勘違いなどあればご指摘頂けるとうれしいです。

## [hamlit-rails](https://rubygems.org/gems/hamlit-rails)
hamlit-rails provides generators for Rails 4.

Railsのジェネレータが生成するビューテンプレートをHaml化する[haml-rails](https://github.com/indirect/haml-rails)の[hamlit](https://github.com/k0kubun/hamlit)版のようです。Hamlという点では同じですがhaml gemへの依存をなくせますね。


## [pg](https://rubygems.org/gems/pg)
Pg is the Ruby interface to the PostgreSQL RDBMS.

RubyのPostgreSQLインタフェース。


## [pghero](https://rubygems.org/gems/pghero)
A performance dashboard for Postgres

スロークエリのチェックやインデックスの状態などPostgreSQLの管理に役立つダッシュボードを提供するEngineのようです。
mastodonではDeviseのauthenticated routesを使ってマウントすることで管理者がアクセスできるようにしているようですね。
https://github.com/tootsuite/mastodon/blob/fe1ddc3b9614380367fa55f13df033e21c96747e/config/routes.rb#L11

```ruby
authenticate :user, lambda { |u| u.admin? } do
  mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
  mount PgHero::Engine, at: 'pghero', as: :pghero
end
```


## [dotenv-rails](https://rubygems.org/gems/dotenv-rails)
Autoload dotenv in Rails.

`.env` ファイルに環境変数を書いておくと自動的に環境変数`ENV` に読み込んでくれるようです。読み込みのタイミングは[before_configuration](https://railsguides.jp/configuring.html#初期化イベント)なので、configで使いたいときはそれより前に`Dotenv::Railtie.load` とする必要があるようです。
https://github.com/tootsuite/mastodon/blob/fe1ddc3b9614380367fa55f13df033e21c96747e/config/application.rb#L13

```ruby
Dotenv::Railtie.load
```

## [paperclip](https://rubygems.org/gems/paperclip)
Easy upload management for ActiveRecord

RailsのActiveRecordでファイルアップロード機能を書くのによく使われるgem
。最近は[shrine](https://github.com/janko-m/shrine)など後発のものに押されてる感ありますがよく見ますね。
https://github.com/tootsuite/mastodon/blob/e09ab2c0bdc0822886fa98b0019d4a447a2ee0d6/app/models/account.rb#L15

```ruby
# Avatar upload
has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' }
```

## [paperclip-av-transcoder](https://rubygems.org/gems/paperclip-av-transcoder)
Audio/Video Transcoder for Paperclip using FFMPEG/Avconv

[Paperclip](https://rubygems.org/gems/paperclip)でアップロードした動画をFFmpegやlibav(avconv)を使って変換するためのようです。
mastodonではContent-Typeをみて使うprocessorを切り換えるようにして使っていました。
https://github.com/tootsuite/mastodon/blob/bfbc2ca0d8dcef47f8581585b42f13b6b4c933d9/lib/paperclip/video_transcoder.rb
https://github.com/tootsuite/mastodon/blob/bfbc2ca0d8dcef47f8581585b42f13b6b4c933d9/app/models/media_attachment.rb#L88

```ruby
def file_processors(f)
  if f.file_content_type == 'image/gif'
    [:gif_transcoder]
  elsif VIDEO_MIME_TYPES.include? f.file_content_type
    [:video_transcoder]
  else
    [:thumbnail]
  end
end
```

## [aws-sdk](https://rubygems.org/gems/aws-sdk)
The official AWS SDK for Ruby. Provides both resource oriented interfaces and API clients for AWS services.

公式のRuby AWS SDKですね。
mastodonではアップロードファイルの保管先としてS3を使うときぐらいでしょうか。
https://github.com/tootsuite/mastodon/blob/6c83fec9715340bd178de0a8ba595bb4ad10eb1a/config/initializers/paperclip.rb#L10


## [addressable](https://rubygems.org/gems/addressable)
Addressable is a replacement for the URI implementation that is part of Ruby's standard library.

標準ライブラリのURIのようにパースしたり、名前解決を行ってくれたり、正規化したりしてくれるようです。
mastodonではURLのバリデーションに使っているようです。
https://github.com/tootsuite/mastodon/blob/b330d1f000ef354a856c43aeaa27e079c89fc822/app/validators/url_validator.rb#L11

```ruby
def compliant?(url)
  parsed_url = Addressable::URI.parse(url)
  !parsed_url.nil? && %w(http https).include?(parsed_url.scheme) && parsed_url.host
end
```

## [bootsnap](https://rubygems.org/gems/bootsnap)
Boot large ruby/rails apps faster

Ruby/Railsアプリの起動高速化するgem。
`Kernel#load`、`Kernel#require`を書き換えてパス探索を高速化したり、コンパイル結果をキャッシュしたりするようです。

[Adds bootsnap. Faster boot time #3176](https://github.com/tootsuite/mastodon/pull/3176)

## [cld3](https://rubygems.org/gems/cld3)
Compact Language Detector v3 (CLD3) is a neural network model for language identification.

CLDというオープンソースの言語解析ライブラリのRubyバインディングのようです。

mastodonではトゥートの言語の自動検出に利用しています。

[Language improvements, replace whatlanguage with CLD](https://github.com/tootsuite/mastodon/pull/2753)

[Use CLD3 #2949](https://github.com/tootsuite/mastodon/pull/2949)

https://github.com/tootsuite/mastodon/blob/58bcd50f7f8c187f8c0bd4a1d52aecafd76979fc/app/lib/language_detector.rb#L9

```rb
class LanguageDetector
  attr_reader :text, :account

  def initialize(text, account = nil)
    @text = text
    @account = account
    @identifier = CLD3::NNetLanguageIdentifier.new(1, 2048)
  end

# (snip)

@identifier.find_language(text_without_urls)
```
https://github.com/tootsuite/mastodon/blob/88725d6ce85115ea3b0652007db5d40a1c069be3/app/services/post_status_service.rb#L30

```rb
    ApplicationRecord.transaction do
      status = account.statuses.create!(text: text,
                                        thread: in_reply_to,
                                        sensitive: options[:sensitive],
                                        spoiler_text: options[:spoiler_text] || '',
                                        visibility: options[:visibility],
                                        language: detect_language_for(text, account),
                                        application: options[:application])
      attach_media(status, media)
    end
```

```rb
  def detect_language_for(text, account)
    LanguageDetector.new(text, account).to_iso_s
  end
```

## [devise](https://rubygems.org/gems/devise)
Flexible authentication solution for Rails with Warden

認証機能の手抜きによく使われる（僕もよく使ってしまう）そしてブラックボックスが嫌われがちなgemです。手早く一般的な機能を実装するのには便利だと思います。


## [devise-two-factor](https://rubygems.org/gems/devise-two-factor)
Barebones two-factor authentication with Devise

Deviseで二段階認証（二要素認証）に対応する最小限の実装とのことです。Google Authenticatorなどに簡単に対応できるそうです。
https://github.com/tootsuite/mastodon/blob/93c13fe691750e8c7f0c90091bca2564b97ccba7/app/models/user.rb#L8


## [doorkeeper](https://rubygems.org/gems/doorkeeper)
Doorkeeper is an OAuth 2 provider for Rails and Grape.

OAuth2プロバイダの実装のようで、[このあたりの記事](http://qiita.com/kyonsuke19101/items/407f3cdfec38d1108e9d)が参考になりそうです。
mastodonのAPI認可に利用しているようです。
[Search: doorkeeper](https://github.com/tootsuite/mastodon/search?l=Ruby&q=doorkeeper&type=&utf8=✓)


## [fast_blank](https://rubygems.org/gems/fast_blank)
Provides a C-optimized method for determining if a string is blank

Cで書かれた高速な`String#blank?` で置き換えるようです。


## [goldfinger](https://rubygems.org/gems/goldfinger)
A Webfinger utility for Ruby

[WebFinger](https://webfinger.net)クライアントのRuby実装のようです。
mastodonでは別のサーバー（mastodonインスタンス）のアカウントのフォローに利用しているようです。
https://github.com/tootsuite/mastodon/blob/0687ab8ae3c2573ba2aa1d37f62e3583d0c7ab01/app/controllers/remote_follow_controller.rb#L18

```ruby
if @remote_follow.valid?
  resource          = Goldfinger.finger("acct:#{@remote_follow.acct}")
  redirect_url_link = resource&.link('http://ostatus.org/schema/1.0/subscribe')
```

## [hiredis](https://rubygems.org/gems/hiredis)
Ruby wrapper for hiredis (protocol serialization/deserialization and blocking I/O)

RedisのCクライアント[hiredis](https://github.com/redis/hiredis)のRubyラッパーライブラリのようで、これ単体でも利用できるようですが、[redis-rb](https://rubygems.org/gems/redis)とあわせて使うことでredis-rbの高速化が望めるようです。


## [htmlentities](https://rubygems.org/gems/htmlentities)
A module for encoding and decoding (X)HTML entities.

[文字実体参照や数値文字参照](https://ja.wikipedia.org/wiki/文字参照)のエンコード・デコードができます。


## [http_accept_language](https://rubygems.org/gems/http_accept_language)
Find out which locale the user preferes by reading the languages they specified in their browser

ブラウザが送出したAccept-Languageヘッダーの扱いを簡単にします。リストアップした中から互換性のある言語を取り出したりできます。
mastodonでは`I18n.available_locales` の中から使えそうなlocaleを選ぶのに使っています。
https://github.com/tootsuite/mastodon/blob/f5cd1383231af6922dbab4f54b7d29eacfec9d9e/app/controllers/concerns/localized.rb#L35

```ruby
def user_supplied_locale
  http_accept_language.language_region_compatible_from(I18n.available_locales)
end
```

## [httplog](https://rubygems.org/gems/httplog)
Log outgoing HTTP requests made from your application.

アプリケーションからの外部HTTP通信のログを記録してくれるので、たとえばAPI呼び出しの追跡などに役立つようです。


## [kaminari](https://rubygems.org/gems/kaminari)
Kaminari is a Scope & Engine based, clean, powerful, agnostic, customizable and sophisticated paginator for Rails 4+

ページネーションを実装する時にはまず使う、そんなgemです。
ActiveRecordだけでなく、Arrayなども同じようにページングできるのもよいです。


## [link_header](https://rubygems.org/gems/link_header)
Converts conforming link headers to and from text, LinkHeader objects and corresponding (JSON-friendly) Array representations, also HTML link elements.

HTTPのLinkヘッダのパースや生成を簡単にするgemのようです。
mastodonではAPIでページネーションを実装するにあたり、前後のパスをクライアントに伝える際などに使っているようです。
https://github.com/tootsuite/mastodon/blob/e2a1b574ab4ec5dd07dbc5c317bcc3718f13c19b/app/controllers/api_controller.rb#L63

```ruby
def set_pagination_headers(next_path = nil, prev_path = nil)
  links = []
  links << [next_path, [%w(rel next)]] if next_path
  links << [prev_path, [%w(rel prev)]] if prev_path
  response.headers['Link'] = LinkHeader.new(links)
end
```

## [nokogiri](https://rubygems.org/gems/nokogiri)
Nokogiri (鋸) is an HTML, XML, SAX, and Reader parser.

XMLビルダー、パーサーを提供します。
mastodonではどちらの用途としても使っているようです。
https://github.com/tootsuite/mastodon/blob/137100dcf38c0da0fe7044a4c92aa06eae02c420/app/views/well_known/webfinger/show.xml.ruby#L1

```ruby
Nokogiri::XML::Builder.new do |xml|
  xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
    xml.Subject @canonical_account_uri
    xml.Alias TagManager.instance.url_for(@account)
    xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: TagManager.instance.url_for(@account))
    xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom'))
    xml.Link(rel: 'salmon', href: api_salmon_url(@account.id))
    xml.Link(rel: 'magic-public-key', href: "data:application/magic-public-key,#{@magic_key}")
    xml.Link(rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_follow_url}?acct={uri}")
  end
end.to_xml
```
https://github.com/tootsuite/mastodon/blob/08faeedff7838e339488cfcddf02d95241557ffb/app/services/fetch_remote_resource_service.rb

```ruby
xml = Nokogiri::XML(body)
xml.encoding = 'utf-8'

if xml.root.name == 'feed'
```

## [oj](https://rubygems.org/gems/oj)
The fastest JSON parser and object serializer.

高速なJSONライブラリ。


## [ostatus2](https://rubygems.org/gems/ostatus2)
Toolset for interacting with the OStatus2 suite of protocols

[PubSubHubbub](https://ja.wikipedia.org/wiki/PubSubHubbub)を使ってフィードの更新通知したり、Salmon（サーモン）Protocolでフィードにコメントを付けたりするのに使えるようです。
[コメント分散問題は“サーモン・プロトコル”で解決!? － ＠IT](http://view-source:http//www.atmarkit.co.jp/news/201002/26/salmon.html)
[Mastodon OStatus API の叩き方](https://gist.github.com/okapies/60d62d0df0163bbfb4ab09c1766558e8)
mastodonではgoldfingerとあわせて別のサーバー（mastodonインスタンス）のユーザーのfollow/unfollow/like/unlikeなどの実装に使っているようです。
https://github.com/tootsuite/mastodon/blob/6d6a429af8fe4bd92ed497f401676353fdc603e0/app/services/favourite_service.rb#L16
https://github.com/tootsuite/mastodon/blob/f722bd2387df9163760014e9555928ec487ae95f/app/workers/notification_worker.rb#L9
https://github.com/tootsuite/mastodon/blob/50660d54e8cabd08fee649a6abc26f35a8d7a82c/app/services/send_interaction_service.rb

```ruby
# Send an Atom representation of an interaction to a remote Salmon endpoint
# @param [String] Entry XML
# @param [Account] source_account
# @param [Account] target_account
def call(xml, source_account, target_account)
  envelope = salmon.pack(xml, source_account.keypair)
  salmon.post(target_account.salmon_url, envelope)
end
```

## [ox](https://rubygems.org/gems/ox)
A fast XML parser and object serializer that uses only standard C lib. 

Cだけで書かれたlibxmlなどに依存しない、高速なXMLパーサ＆オブジェクトシリアライザのようです。
XMLパーサとしてNokogiriより２倍以上高速で、＆オブジェクトシリアライザとしてMarshal.dumpより最大６倍高速で、SAXではNokogiriよりも40倍、LibXMLよりも13倍高速という謳い文句です。
mastodonではAtomの生成に利用しているようです。
https://github.com/tootsuite/mastodon/blob/15ec4ae07b17821625bd2ca1088a7573a7ed128c/app/lib/atom_serializer.rb#L8

```ruby
document = Ox::Document.new(version: '1.0')
document << element
('<?xml version="1.0"?>' + Ox.dump(element, effort: :tolerant)).force_encoding('UTF-8')
```

## [rabl](https://rubygems.org/gems/rabl)
General ruby templating with json, bson, xml and msgpack support

API応答をDSLで柔軟に書けるというテンプレートシステムのようです。JSONだけでなくXML、MessagePackなど複数のフォーマットに対応しています。
mastodonでもAPI応答など利用されています。
[Search: extension:rabl](https://github.com/tootsuite/mastodon/search?utf8=✓&q=extension%3Arabl&type=)


## [rack-attack](https://rubygems.org/gems/rack-attack)
A rack middleware for throttling and blocking abusive requests

ミドルウェアレベルでセーフリスト、ブラックリスト、スロットリングなど不正なリクエストからの防御を実装できるようです。
mastodonではAPIや、ログイン、ユーザー登録などのレート制限の実装に利用しているようです。
https://github.com/tootsuite/mastodon/blob/ff5baa5349ca7a5391cfa9e283d2f5e653f876ee/config/initializers/rack_attack.rb

```ruby
# Rate limits for the API
throttle('api', limit: 300, period: 5.minutes) do |req|
  req.ip if req.path =~ /\A\/api\/v/
end

# Rate limit logins
throttle('login', limit: 5, period: 5.minutes) do |req|
  req.ip if req.path == '/auth/sign_in' && req.post?
end
```
https://github.com/tootsuite/mastodon/blob/e2a1b574ab4ec5dd07dbc5c317bcc3718f13c19b/app/controllers/api_controller.rb#L52

```ruby
now        = Time.now.utc
match_data = request.env['rack.attack.throttle_data']['api']

response.headers['X-RateLimit-Limit']     = match_data[:limit].to_s
response.headers['X-RateLimit-Remaining'] = (match_data[:limit] - match_data[:count]).to_s
response.headers['X-RateLimit-Reset']     = (now + (match_data[:period] - now.to_i % match_data[:period])).iso8601(6)
```

## [rack-cors](https://rubygems.org/gems/rack-cors)
Middleware that will make Rack-based apps CORS compatible.

ミドルウェアレベルで[HTTPアクセス制御（CORS: Cross-Origin Resource Sharing）](https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control)を実装できるようです。
mastodonではAPIのクロスオリジンアクセスの許可や、ヘッダのホワイトリストの指定などに利用しているようです。
https://github.com/tootsuite/mastodon/blob/0a1b0246003e19e80241d8711276db790a88235d/config/application.rb#L58

```ruby
config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins  '*'

    resource '/api/*',       headers: :any, methods: [:post, :put, :delete, :get, :options], credentials: false, expose: ['Link', 'X-RateLimit-Reset', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-Request-Id']
    resource '/oauth/token', headers: :any, methods: [:post], credentials: false
  end
end
```

## [rack-timeout](https://rubygems.org/gems/rack-timeout)
Rack middleware which aborts requests that have been running for longer than a specified timeout.

ミドルウェアレベルでリクエストを受け取ってから結果を返すまでの待機時間や、POSTデータを受け取る際の受信待ち時間などを設定できるようです。
mastodonではproductionでのみサービスタイムアウトを設定しているようで、これにより長時間応答が返せない場合に処理を中断することができます。
https://github.com/tootsuite/mastodon/blob/5b12624847f6a599e1bbb3b24fc87c3b222e6716/config/initializers/timeout.rb#L5

```ruby
Rack::Timeout::Logger.disable
Rack::Timeout.service_timeout = false

if Rails.env.production?
  Rack::Timeout.service_timeout = 90
end
```

## [rails-i18n](https://rubygems.org/gems/rails-i18n)
A set of common locale data and translations to internationalize and/or localize your Rails applications.

Railsのデフォルト文字列を翻訳した辞書一式のようです。使いたい言語だけを選ぶこともできます。


## [rails-settings-cached](https://rubygems.org/gems/rails-settings-cached)
This is improved from rails-settings, added caching.

アプリケーション設定をドットファイルなどではなくデータベースに記録し、変更できるようにしたいとき簡単に実装できるgemです。
mastodonでは「サイト名」や「自由にユーザー登録できるようにするか」などの設定に利用しているようです。
https://github.com/tootsuite/mastodon/blob/d87ee1167e0f11d94a20e70e9228a3398e202bc8/app/controllers/auth/registrations_controller.rb#L32


## [redis](https://rubygems.org/gems/redis)
A Ruby client library for Redis

Key-Value Storeのひとつ[Redis](https://redis.io)のRubyクライアントライブラリで、Redis APIと１対１で対応するようです。[hiredis](https://github.com/redis/hiredis)と組み合わせて使うことで高速化するようです。
mastodonではsidekiqのバックエンドとして非同期処理の他、ActionCable（これは設定だけかも）、Node.jsによるStreaming APIなどに利用しているようです。
https://github.com/tootsuite/mastodon/blob/323671a653e22704f0d50be13d67bf6fe6ee3a2a/app/services/fan_out_on_write_service.rb#L61

```ruby
def deliver_to_hashtags(status)
  Rails.logger.debug "Delivering status #{status.id} to hashtags"

  status.tags.pluck(:name).each do |hashtag|
    Redis.current.publish("timeline:hashtag:#{hashtag}", @payload)
    Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if status.local?
  end
end
```

## [redis-namespace](https://rubygems.org/gems/redis-namespace)
Adds a Redis::Namespace class which can be used to namespace calls to Redis. This is useful when using a single instance of Redis with multiple, different applications.

Redisキーにnamespaceを使用できるようになるgemのようです。複数のアプリから1つのRedisサーバを使う場合など役立ちそうです。

https://github.com/tootsuite/mastodon/blob/df92f010ad5acd72b28e6d49c028f9123e92cf06/config/initializers/redis.rb#L11

```rb
namespace = ENV.fetch('REDIS_NAMESPACE') { nil }

if namespace
  Redis.current = Redis::Namespace.new(namespace, redis: redis_connection)
else
  Redis.current = redis_connection
end
```


## [rqrcode](https://rubygems.org/gems/rqrcode)
rQRCode is a library for encoding QR Codes.

QRコード生成のライブラリのようです。
mastodonでは二段階認証（二要素認証）の際にスマートフォンに読み取らせるQRコードを生成するのに利用しているようです。
https://github.com/tootsuite/mastodon/blob/a8e1afc30a1f843943bb29e7385b8611ea3c29f4/app/controllers/settings/two_factor_auths_controller.rb#L50


## [ruby-oembed](https://rubygems.org/gems/ruby-oembed)
oEmbed for Ruby

URLから埋め込み用コードを取得できる[oEmbed](http://oembed.com)という仕様のRuby実装のようです。oEmbedクライアントとしては、任意のoEmbedプロバイダのURLを指定するだけで、サムネイルや埋め込み用HTMLなどを取得できるようです。また、独自のoEmbedプロバイダを作るのにも使えるようです。
mastodonでは[現在](https://github.com/tootsuite/mastodon/tree/389f8f824909d60b7560248465416bd4bed92b6b)のところGemfileにはあるものの使われていなさそうでした。
個別のトゥートページのLinkタグで示される`application/json+oembed` はありますが、
https://github.com/tootsuite/mastodon/blob/389f8f824909d60b7560248465416bd4bed92b6b/app/views/stream_entries/show.html.haml#L3

```haml
- content_for :header_tags do
  %link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
  %link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/
```

その実装は、rablで書かれていました。
https://github.com/tootsuite/mastodon/blob/389f8f824909d60b7560248465416bd4bed92b6b/app/views/api/oembed/show.json.rabl

```ruby
# frozen_string_literal: true
object @stream_entry

node(:type) { 'rich' }
node(:version) { '1.0' }
node(:title, &:title)
node(:author_name) { |entry| entry.account.display_name.blank? ? entry.account.username : entry.account.display_name }
node(:author_url) { |entry| account_url(entry.account) }
node(:provider_name) { site_hostname }
node(:provider_url) { root_url }
node(:cache_age) { 86_400 }
node(:html) { |entry| "<iframe src=\"#{embed_account_stream_entry_url(entry.account, entry)}\" style=\"width: 100%; overflow: hidden\" frameborder=\"0\" width=\"#{@width}\" height=\"#{@height}\" scrolling=\"no\"></iframe>" }
node(:width) { @width }
node(:height) { @height }
```

これによりoEmbedに対応したサービスでmastodonのトゥートのURLを貼り付けると、特別な設定をせずとも埋め込み表示されます。
たとえばはてなブログなどは対応しています。

## [sanitize](https://rubygems.org/gems/sanitize)
Sanitize is a whitelist-based HTML and CSS sanitizer. Given a list of acceptable elements, attributes, and CSS properties, Sanitize will remove all unacceptable HTML and/or CSS from a string.

ホワイトリスト方式のサニタイズができるgem。許可する要素や属性など細かく設定できるようです。

mastodonでは用途ごとにサニタイズ設定を用意し、ヘルパーやサービスクラスで使い分けているようです。

https://github.com/tootsuite/mastodon/blob/b5a9c6b3d292abc7e47d8a6f830f6b5589c04862/app/lib/formatter.rb

https://github.com/tootsuite/mastodon/blob/58bcd50f7f8c187f8c0bd4a1d52aecafd76979fc/app/lib/sanitize_config.rb

```rb
    MASTODON_OEMBED ||= freeze_config merge(
      RELAXED,
      elements: RELAXED[:elements] + %w(audio embed iframe source video),

      attributes: merge(
        RELAXED[:attributes],
        'audio'  => %w(controls),
        'embed'  => %w(height src type width),
        'iframe' => %w(allowfullscreen frameborder height scrolling src width),
        'source' => %w(src type),
        'video'  => %w(controls height loop width),
        'div'    => [:data]
      ),
```

https://github.com/tootsuite/mastodon/blob/79ef8b3653a6ff7edbc1af0741dff36522262c07/app/services/fetch_link_card_service.rb#L69

```rb
card.html   = Formatter.instance.sanitize(response.html, Sanitize::Config::MASTODON_OEMBED)
```

## [sidekiq](https://rubygems.org/gems/sidekiq)
Simple, efficient background processing for Ruby.

非同期処理を実現するgemです。
Rails 4.2以上ではActive Jobというフレームワークが導入され、Sidekiqもそのバックエンドとして選択できるという扱いになりましたが、mastodonの場合はSidekiqをそのまま利用しているようです。
https://github.com/tootsuite/mastodon/blob/389f8f824909d60b7560248465416bd4bed92b6b/app/workers/after_remote_follow_request_worker.rb#L4

```ruby
class AfterRemoteFollowRequestWorker
  include Sidekiq::Worker

  sidekiq_options queue: 'pull', retry: 5

  def perform(follow_request_id)
    follow_request  = FollowRequest.find(follow_request_id)
    updated_account = FetchRemoteAccountService.new.call(follow_request.target_account.remote_url)
```
https://github.com/tootsuite/mastodon/blob/6d6a429af8fe4bd92ed497f401676353fdc603e0/app/services/follow_service.rb#L31

```ruby
if target_account.local?
  NotifyService.new.call(target_account, follow_request)
else
  NotificationWorker.perform_async(build_follow_request_xml(follow_request), source_account.id, target_account.id)
  AfterRemoteFollowRequestWorker.perform_async(follow_request.id)
end
```

## [sidekiq-scheduler](https://rubygems.org/gems/sidekiq-scheduler)
Light weight job scheduling extension for Sidekiq that adds support for queueinga jobs in a recurring way.

SidekiqでCRONなどのような定期処理を実装するgemです。
複数のサーバインスタンスがあるとき、多重実行されるのかどうか？そのケアは必要か？はちょっと試してないのでよくわかりません。（誰か詳しい方教えてください…）

https://github.com/tootsuite/mastodon/blob/58bcd50f7f8c187f8c0bd4a1d52aecafd76979fc/config/sidekiq.yml#L8
```yaml
:schedule:
  subscriptions_scheduler:
    cron: '0 5 * * *'
    class: Scheduler::SubscriptionsScheduler
```

https://github.com/tootsuite/mastodon/blob/58bcd50f7f8c187f8c0bd4a1d52aecafd76979fc/app/workers/scheduler/subscriptions_scheduler.rb

```rb
# frozen_string_literal: true
require 'sidekiq-scheduler'

class Scheduler::SubscriptionsScheduler
  include Sidekiq::Worker

  def perform
    logger.info 'Queueing PuSH re-subscriptions'

    expiring_accounts.pluck(:id).each do |id|
      Pubsubhubbub::SubscribeWorker.perform_async(id)
    end
  end

  private

  def expiring_accounts
    Account.expiring(1.day.from_now).partitioned
  end
end
```

## [sidekiq-unique-jobs](https://rubygems.org/gems/sidekiq-unique-jobs)
Handles various types of unique jobs for Sidekiq

Sidekiqで同じジョブが同時に複数実行されないようにできるようです。
mastdonの場合は、２週間ぶりにログインしたユーザーのメンションをDBから探すという重めの処理について１回だけ実行したい、ということで利用しているようです。
https://github.com/tootsuite/mastodon/blob/5d710b1139b34f2ed6bd556f448fa7248354e758/app/controllers/application_controller.rb#L44

```ruby
# If the sign in is after a two week break, we need to regenerate their feed
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
```
https://github.com/tootsuite/mastodon/blob/82aaedec467815c2947a11651d5216bb88ce4038/app/workers/regeneration_worker.rb#L6

```ruby
class RegenerationWorker
  include Sidekiq::Worker

  sidekiq_options queue: 'pull', backtrace: true, unique: :until_executed
```

## [simple-navigation](https://rubygems.org/gems/simple-navigation)
A ruby gem for creating navigations (with multiple levels) for your Rails, Sinatra or Padrino applications. Render your navigation as html list, link list or breadcrumbs.

ナビゲーションやパンくずリストを簡単に作れるgemのようです。
mastodonでは管理画面のナビゲーションに利用しているようです。
https://github.com/tootsuite/mastodon/blob/55e150352204bc790345cf81ef02bfcb1d7e0594/config/navigation.rb
https://github.com/tootsuite/mastodon/blob/0d83569899155e5d5c17fff0c69e533df2f34d42/app/views/layouts/admin.html.haml#L11


## [simple_form](https://rubygems.org/gems/simple_form)
Forms made easy!

Railsのフォームをスッキリ書けるDSLを提供するgemのようです。
mastodonではログインページ等各種フォームで利用しているようです。
https://github.com/tootsuite/mastodon/blob/23b997ae55655ccae6a9a9e424a3596c9165a481/app/views/auth/sessions/new.html.haml#L4

```haml
= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
  = f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
  = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }

  .actions
    = f.button :button, t('auth.login'), type: :submit
```

## [statsd-instrument](https://rubygems.org/gems/statsd-instrument)
A StatsD client for Ruby apps.

[StatsD](https://github.com/etsy/statsd)ののRubyクライアントで、アプリケーションのメトリクスの取得と計測を行えるようです。
mastodonでは[ActiveSupport::Notifications](https://railsguides.jp/active_support_instrumentation.html#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E3%82%B5%E3%83%96%E3%82%B9%E3%82%AF%E3%83%A9%E3%82%A4%E3%83%96)を使ってActionControllerの処理を時間を計測しているようです。
https://github.com/tootsuite/mastodon/blob/f6a5977f0b2986b15ca8254123053e1a59647cd1/config/initializers/statsd.rb

```ruby
    ActiveSupport::Notifications.subscribe(/performance/) do |name, _start, _finish, _id, payload|
      action      = payload[:action] || :increment
      measurement = payload[:measurement]
      value       = payload[:value]
      key_name    = clean_name("#{name}.#{measurement}")
    
      StatsD.send(action.to_s, key_name, (value || 1))
    end
```
https://github.com/tootsuite/mastodon/blob/d567f21d4f4c3feb09a08443cf75eb583904093b/config/initializers/instrumentation.rb#L14

```ruby
ActiveSupport::Notifications.subscribe(/process_action.action_controller/) do |*args|
  event      = ActiveSupport::Notifications::Event.new(*args)
  # (snip)

  ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.total_duration", value: event.duration
  ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.db_time", value: event.payload[:db_runtime]
  ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.view_time", value: event.payload[:view_runtime]
  ActiveSupport::Notifications.instrument :performance, measurement: "#{key}.status.#{status}"
end
```

## [twitter-text](https://rubygems.org/gems/twitter-text)
A gem that provides text handling for Twitter

TwitterにツイートするテキストのURL自動リンク、文字数の正確なカウントなどの面倒をみてくれるようです。
mastodonではトゥートの自動リンクに利用しているようです。
https://github.com/tootsuite/mastodon/blob/70891a99a97bc1ca14a8ded13a5cd45b648b92b3/app/lib/formatter.rb
https://github.com/tootsuite/mastodon/blob/70891a99a97bc1ca14a8ded13a5cd45b648b92b3/spec/lib/formatter_spec.rb

## [webpacker](https://rubygems.org/gems/webpacker)
Use Webpack to manage app-like JavaScript modules in Rails

[アセット管理をSprockets/BrowserifyからWebpackに切り替えた](https://github.com/tootsuite/mastodon/commit/f5bf5ebb82e3af420dcd23d602b1be6cc86838e1)ことで追加されました。

https://github.com/tootsuite/mastodon/tree/58bcd50f7f8c187f8c0bd4a1d52aecafd76979fc/app/javascript

Webpacker gemについて調べる時間がなかったのでとりあえずほかの記事

http://qiita.com/search?q=webpacker&sort=stock


# 今は支えていないgems

記事作成時点で使われていたけど、その後Gemfileから消えたgemはここに残しておきます。

## [font-awesome-rails](https://rubygems.org/gems/font-awesome-rails)
I like font-awesome. I like the asset pipeline. I like semantic versioning. If you do too, you're welcome.

[font-awesome](http://fontawesome.io)とAsset Pipelineと[セマンティック・バージョニング](http://semver.org/lang/ja/)が好きな人に。

[アセット管理をSprockets/BrowserifyからWebpackに切り替えた影響で削除した](https://github.com/tootsuite/mastodon/commit/f5bf5ebb82e3af420dcd23d602b1be6cc86838e1)ようです。

## [best_in_place](https://rubygems.org/gems/best_in_place)
A RESTful unobtrusive jQuery Inplace-Editor and a helper as a Rails Gem

編集したいところをページ遷移なしにそのまま編集できる機能を簡単に実現できるようにするEngineのようです。mastodonでは管理画面のサイト設定で使っているようです･･･が、そのうち無くなる気がします。
https://github.com/tootsuite/mastodon/blob/d90d23699ca611f05d8def40109505251507fa46/app/views/admin/settings/index.html.haml#L15

```haml
%td= best_in_place @settings['site_contact_username'], :value, url: admin_setting_path(@settings['site_contact_username']), place_holder: t('admin.settings.contact_information.username')
```

## [autoprefixer-rails](https://rubygems.org/gems/autoprefixer-rails)
Parse CSS and add vendor prefixes to CSS rules using values from the Can I Use website.

CSSベンダープレフィックスを付与してくれるAutoprefixerを、Sprocketsで使ってくれるようになるようです。

## [react-rails](https://rubygems.org/gems/react-rails)
Render components in views or controller actions.

RailsのViewとReact Componentの橋渡しをしてくれます。
mastodonでは、[app/assets/javascripts/components/containers/mastodon.jsx](https://github.com/tootsuite/mastodon/blob/a3486e0543f76581441380fdf2975d596a6be1c6/app/assets/javascripts/components/containers/mastodon.jsx)をホームのViewに置くことでRails View内にTweetDeckっぽいSPAを置いています。react-rails単体でもJSXのプリコンパイルも対応しているはずですが、それについてはbrowserify-railsで行っていました。
https://github.com/tootsuite/mastodon/blob/0d83569899155e5d5c17fff0c69e533df2f34d42/app/views/home/index.html.haml#L6

```haml
- content_for :header_tags do
  %script#initial-state{:type => 'application/json'}!= json_escape(render(file: 'home/initial_state', formats: :json))

  = javascript_include_tag 'application', integrity: true, crossorigin: 'anonymous'

= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false
```

## [browserify-rails](https://rubygems.org/gems/browserify-rails)
Browserify + Rails = CommonJS Heaven

SprocketsでCommonJSを使えるようにする方法の１つです。
mastodonではBabelifyと組み合わせてBabelのes2015とreactのプリセットを利用しているようです。
https://github.com/tootsuite/mastodon/blob/0a1b0246003e19e80241d8711276db790a88235d/config/application.rb#L71

```ruby
config.browserify_rails.source_map_environments << 'development'
config.browserify_rails.commandline_options   = '--transform [ babelify --presets [ es2015 react ] ] --extension=".jsx"'
config.browserify_rails.evaluate_node_modules = true
```
https://github.com/tootsuite/mastodon/blob/a3486e0543f76581441380fdf2975d596a6be1c6/app/assets/javascripts/components/containers/mastodon.jsx

```js
const Mastodon = React.createClass({
  # (snip)
});

export default Mastodon;
```
https://github.com/tootsuite/mastodon/blob/0a1b0246003e19e80241d8711276db790a88235d/app/assets/javascripts/components.js#L15

```js
//= require_tree ./components

window.Mastodon = require('./components/containers/mastodon');
```

[アセット管理をSprockets/BrowserifyからWebpackに切り替えた影響で削除した](https://github.com/tootsuite/mastodon/commit/f5bf5ebb82e3af420dcd23d602b1be6cc86838e1)ようです。

## [whatlanguage](https://rubygems.org/gems/whatlanguage)
WhatLanguage rapidly detects the language of a sample of text

文字列からどの言語で書かれているか検出できるそうです。
mastodonではトゥートの内容から言語を検出するのに利用しているようです。
https://github.com/tootsuite/mastodon/blob/297c11dba2864b20992cd3f98282f5ce35d5d144/app/lib/language_detector.rb#L12

```ruby
def to_iso_s
  WhatLanguage.new(:all).language_iso(text) || default_locale.to_sym
end
```
https://github.com/tootsuite/mastodon/blob/297c11dba2864b20992cd3f98282f5ce35d5d144/app/services/post_status_service.rb#L22
https://github.com/tootsuite/mastodon/blob/297c11dba2864b20992cd3f98282f5ce35d5d144/app/services/post_status_service.rb#L56

```ruby
def call(account, text, in_reply_to = nil, options = {})
  media  = validate_media!(options[:media_ids])
  status = account.statuses.create!(text: text,
                                    thread: in_reply_to,
                                    sensitive: options[:sensitive],
                                    spoiler_text: options[:spoiler_text] || '',
                                    visibility: options[:visibility],
                                    language: detect_language_for(text, account),
                                    application: options[:application])
  # (snip)
end


def detect_language_for(text, account)
  LanguageDetector.new(text, account).to_iso_s
end
```

検出精度の低さが問題になってCLDというオープンソースの言語解析ライブラリベースの`cld2`gemに置き換えられ、その後さらに`cld3`gemに置き換えられたようです。

[Language improvements, replace whatlanguage with CLD](https://github.com/tootsuite/mastodon/pull/2753)

[Use CLD3 #2949](https://github.com/tootsuite/mastodon/pull/2949)