pplogの「ちょっと足跡が見えちゃう」機能を偲んで( ˘ω˘)

f:id:naoto5959:20151216001632p:plain

そこには足跡があった。

そう、名前の下に「最後に読んだよしたあしあと」が出ていたのだ。正確には、「最後のあしあとの最後の20文字」が出ていた。ウッカリおしりが見えちゃっている感じだ。

何故作ったか

ツイッターの名前欄にはロマンがあるんですよ。

名前欄とは、Koshikawa Naoto とか書いちゃうところである。しかし、なんでそれ名前欄に書いたのって言う文字列を名前欄に入れている人々がいるのだ。なんていうかMNS MessengerとかSkypeにあったアレ、ムードみたいなアレ。間違ってアレ目的で名前欄を書き換えている。それも頻繁に書き換えている。そんなNAMAE-RAN FREAKSがいるんですよ。

正直ツイッターにおける一番いい機能である名前欄で遊ぶという機能を、我々はそんな機能を、pplogに搭載したくなったのですよ。

(当時の会話です)

f:id:naoto5959:20151216002142p:plain

そうですね、一時期「コントレックス」という文字列を入力していたことがありますね。今はもう飲んでいないコントレックスよ。

どう作るか

名前欄を変更できる機能を作ろう。

違う、そんなんじゃない。pplogに欲しい機能はそんなんじゃない!設定なんてさせるなよ!何が名前の設定だ!アホか!ポエム書きにきてんだよ、ポエム読みに来てんだよ!ふざけんなよ!

そんな気持ちから産み出した機能。

「最後に読んだよしたあしあと」を表示する機能。

気づかない人には、うっかりおしりが見えてしまっているような。しかし気づいた人はふざけて遊べる、そんな機能。くすっと笑えるお遊び機能。コレだ!って思ったんですよ(当時)

どやPull Requestの様子です

どうですか。私のツイッターIDの下には「ほしぶどう」という称号のような何かが。何なんですか、これは!バカバカしい!

f:id:naoto5959:20151216001820p:plain 

成功かと思った

無駄に回遊率が上がった(感覚値です)表示したい文字列を探し集めるためにポエムをあさった。ホネを回し続けた。ホネをフォワードし続けたのだ。そして、皆の名前欄の下を見たくてたまらなくなった。無駄に人のページヘ行った。

f:id:naoto5959:20151216003023p:plain

ヨサソウ、ッテ思った。

いやまて

足跡を必要以上に選び始める自分が居た。今までと違う気持で「読んだよ」していた。違った。それは違った。なんなら最高の「読んだよしたあしあと」のあとは、「読んだよ」したくなくなったりしてた。

思ったのと違う行動をし始めていた。ポエムを読めよ。おもいっきり、心置きなく「読んだよ」しろよ。なにやってんだよ!!!

おれの作りたい世界はコレじゃない。

やめよう

f:id:naoto5959:20151216001632p:plain

ひっそりと始まった機能はひっそりと消えた。それは儚い。ポエムのように、その機能は消えた。pplogの機能はそのコンセプトのように、儚く去っていった。

記憶にだけ残る君よ。

「ちょっと足跡が見えちゃう」機能よ。安らかに眠れ。

nyauthという認証gemを作ってた

MoneyForward Advent Calendar 2015 - Qiitaの14日目」の記事です。

今年、一体何やっていたかなと振り返ってみたところ、nyauthというgemを作っていたことに気づいたので、雑に紹介します。

「お前エモい記事しか書かなくなったな」みたいな声もチラホラ聞くのでたまにはプログラミングの記事を。

f:id:naoto5959:20151213124149p:plain

こうして振り返ってみると、何故かコントリビューターも現れ始めて嬉しかったです。

何なの

一般ユーザー向けと管理者用向けといった複数のコンテクストに対応した認証gemです。

猫の「にゃん」(って何)と「Authentication」を合わせて「にゃおーす」つまりnyauthです。

なんで作ったの

個人プロジェクトでとあるWebアプリケーションを作る際に、せっかくだし deviseから卒業しようってのと、単純になるべくメタプロし過ぎないシンプルなものを作りたかったのです。

ただそれだけです。学びが多かった。それが収穫です。

どんなもの

ざっと

  • 登録
  • 認証
  • パスワード変更
  • 本人確認(メールが届くかどうか)
  • パスワード変更

といった機能があります。一般ユーザーにはこれら全てを提供して、管理者ユーザーには、「認証だけ」みたいなユースケースに対応してます。

一般ユーザーの認証URLは

/session/new

管理者ユーザーの認証URLは

/admin/session/new

と言った感じで、現在のコンテクストをURLのパスで判断してます。

ルーティング

config/routes.rb

Rails.application.routes.draw do
  mount Nyauth::Engine => "/"
end

/にマウントすると以下の様なroutesが定義されます。/にマウントした時にはデフォルトでUserモデルが認証の対象となります。

Prefix Verb URI Pattern Controller#Action
nyauth      /nyauth     Nyauth::Engine

Routes for Nyauth::Engine:
              registration POST   /registration(.:format)                             nyauth/registrations#create
          new_registration GET    /registration/new(.:format)                         nyauth/registrations#new
                   session POST   /session(.:format)                                  nyauth/sessions#create
               new_session GET    /session/new(.:format)                              nyauth/sessions#new
                           DELETE /session(.:format)                                  nyauth/sessions#destroy
             edit_password GET    /password/edit(.:format)                            nyauth/passwords#edit
                  password PATCH  /password(.:format)                                 nyauth/passwords#update
                           PUT    /password(.:format)                                 nyauth/passwords#update
     confirmation_requests POST   /confirmation_requests(.:format)                    nyauth/confirmation_requests#create
  new_confirmation_request GET    /confirmation_requests/new(.:format)                nyauth/confirmation_requests#new
              confirmation GET    /confirmations/:confirmation_key(.:format)          nyauth/confirmations#update
   reset_password_requests POST   /reset_password_requests(.:format)                  nyauth/reset_password_requests#create
new_reset_password_request GET    /reset_password_requests/new(.:format)              nyauth/reset_password_requests#new
       edit_reset_password GET    /reset_passwords/:reset_password_key/edit(.:format) nyauth/reset_passwords#edit
            reset_password PATCH  /reset_passwords/:reset_password_key(.:format)      nyauth/reset_passwords#update
                           PUT    /reset_passwords/:reset_password_key(.:format)      nyauth/reset_passwords#update

config/routes.rb

Rails.application.routes.draw do
  mount Nyauth::Engine => "/hoge"
end

/hogeにマウントすれば、Hogeモデルが認証の対象となります。

複数のモデルを認証したいときのルーティング

config/routes.rb

Rails.application.routes.draw do
  # for admin
  namespace :nyauth, path: :admin, as: :admin do
    # concerns :nyauth_registrable
    concerns :nyauth_authenticatable
    # concerns :nyauth_confirmable
  end

  # for user
  mount Nyauth::Engine => "/"
end

本当は、2箇所に違う名前でマウントしたいのだけども

Rails.application.routes.draw do
  # for admin
  mount Nyauth::Engine => "/admin", as: 'admin'

  # for user
  mount Nyauth::Engine => "/", as: 'user'
end

とは出来ません。Railsが2箇所にEngineをマウントはできてもURLヘルパーを上手く扱えず後で定義した方のasで上書いてしまうので、今回はmountは1つ、2つ目以降は

namespace :nyauth, path: :admin, as: :admin do
    # concerns :nyauth_registrable
    concerns :nyauth_authenticatable
    # concerns :nyauth_confirmable
end

と言った感じで定義します。もちろん、1つ目からこの形式でも大丈夫です。

ここで、nyauthでは以下のRouting Conrcenを使えます。

concern :nyauth_registrable do
  resource :registration, only: %i(new create)
end

concern :nyauth_authenticatable do
  resource :session, only: %i(new create destroy)
  resource :password, only: %i(edit update)
  resources :reset_password_requests, only: %i(new create)
  resources :reset_passwords, param: :reset_password_key, only: %i(edit update)
end

concern :nyauth_confirmable do
  resources :confirmation_requests, only: %i(new create)
  get '/confirmations/:confirmation_key' => 'confirmations#update', as: :confirmation
end

コントローラー

application_controller.rb

class ApplicationController < ActionController::Base
  include Nyauth::ControllerConcern
  before_action -> { require_authentication! as: :user }
  helper_method :current_user

  private

  def current_user
    current_authenticated(as: :user)
  end
end

使いたいコントローラーに、include Nyauth::ControllerConcernと書いておくと、require_authentication!というメソッドが生えるので、

before_action -> { require_authentication! as: :user }

てな感じで、フィルターをかけるイメージです。

管理者向けのコントローラーにも同じように、

admin/base_controller.rb

class Admin::BaseController < ActionController::Base
  include Nyauth::ControllerConcern
  before_action -> { require_authentication! as: :admin }
  helper_method :current_admin

  private

  def current_admin
    current_authenticated(as: :admin)
  end
end

テーブル定義

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :nickname
      # Authenticatable
      t.string :email, null: false
      t.string :password_digest, null: false
      t.string :password_salt, null: false
      t.string :reset_password_key
      t.datetime :reset_password_key_expired_at
      # Confirmable
      t.datetime :confirmed_at
      t.string :confirmation_key
      t.datetime :confirmation_key_expired_at

      t.timestamps null: false
    end
    add_index :users, :email, unique: true
  end
end

モデル

適切なカラムを定義した上で、モデルにmoduleincludeしておきます。

app/models/user.rb

class User < ActiveRecord::Base
  include Nyauth::Authenticatable
  include Nyauth::Confirmable
end

app/models/admin.rb

class Admin < ActiveRecord::Base
  include Nyauth::Authenticatable
end

なんとなく見れば分かるかな。更に詳しくは、READMEやソースコードを読んで頂けると良いかと嬉しいです。

学び

  • 同じEngineを複数のパスにマウントする事を想定していないようでつらかった。もうちょいソースを読み込んでコントリビュートしたい気持ちはある。
  • そのため、helperの生成がつらかった。実装的にもシンプルじゃないのでいけてない。
  • テスト時に、sign_in(user)みたいなヘルパーを使うために、wardenを大いに参考にさせていただいた。Rack層の段階で次のrequestにhookするという発想がとても勉強になった。
  • generator生成のノウハウを得た。
  • respondersの拡張ノウハウを得た。
  • gemのconfigを用意してゴニョゴニョカスタマイズするノウハウを得た。

個人プロジェクトの進捗どうですか

なんと肝心の個人プロジェクトのWebアプリは、このgemを作ることに寄り道したり、React.jsをこねくり回したり、RailsとJavaScriptのモダンな共存などを模索していたせいで公開がまで来てません。個人プロジェクトは、yak shavingばかりしていた1年でしたね。

github.com

今回の記事でnyauthにご興味持たれた方は是非Pull Requestでフィードバック頂けると嬉しいです。

iCloudをサインアウトする前にApple IDを変えてしまうとつらい

家族の携帯キャリアをIIJmioに統一し、iPhone4からiPhone6sへ機種変した。

LINEの過去トークを含めてすべてのデータをとっておきたいとのことで、

  • iPhone4をiTunes経由でMacにバックアップ(LINEのトーク移行には暗号化をONにするのがポイントらしい)
  • 買ってきたばかりのまったく新しいiPhone6sをiPhone4のバックアップから復元

という手順で移行する計画で進めた。

このとき、移行後のiPhone6sでiCloudがサインアウト出来ずにつらかった話。

Apple IDを変えた

今回、Apple IDがキャリアメールアドレスだったのでApple ID自体をgmailのアドレスに変える必要があった(hoge@carrier.example.netから、hoge@examle.comへ変更するものとするイメージ)。

Apple IDを変更したのでiPhone側のiTunesの設定とiCloudをサインアウトし、サインインしなおすことになる。このときiCloudで「iPhoneを探す」設定をしていると面倒くさい。

Apple ID変更前にiCloudをサインアウトしていれば、何の問題もないが、先にiCloudのサインアウトするのを忘れているとつらい。

iCloudからサインアウト出来ない

Apple IDを変えた後でも、iTunesは難なくサインアウト出来る。んが、iCloudをサインアウトしようとすると

「iPhoneを探す」をオフにするには、現在設定しているhoge@carrier.example.netのパスワードを入力してね

的なことを言われる。しかし、すでにAppleIDをhoge@carrier.example.netからhoge@example.comへ変更しているので自分のAppleIDを入力しても「そのIDはロックされています」と言われてどうにもならない。初期化しようとしても「iPhoneを探す」をオフにする過程で同じ状況。セキュリティ意識が高い!

解決策

(2015年11月29日現在はこの手順が有効だった)

解決策は、AppleのウェブサイトからAppleIDを一時的に前のアドレスに戻す こと。戻した際に確認のメールアドレスを送信云々表示されるが無視する。ていうか、無視したくなくてももうそのキャリアアドレスは受け取れないので無視せざるをえないので若干不安な気持ちになるが強い心で先へ進む。

この状態で、iCloudをサインアウトしようとすると先ほどと同じように

「iPhoneを探す」をオフにするには、現在設定しているhoge@carrier.example.netのパスワードを入力してね

と言われるので、パスワードを入力すると「iPhoneを探す」をオフに出来て、iCloudからもサインアウト出来る。

そして、ウェブサイトからAppleIDをまたhoge@example.comへ変更する。今度はこのメール宛の確認メールは踏んでおく。

こうして、ようやくiTunesとiCloudからサインアウト出来たので、一旦iPhone6sを初期化する。

サインアウトさえ出来れば計画通りの作業が可能となるので、iPhone4でも同じ手順で両方サインアウトして、バックアップを再取得し、iPhone6sを初期化して初めからやりなおして、うまくいった。

途中で諦めそうになった

これはApple IDのバグ?いや、仕様だと思い、もうiPhoneを交換するしか無いかとまで思ったが、ココで諦めてはいかん!という謎のモチベーションにより問題を解決できたの良かった。

なんせ、

SIMフリー版iPhoneをAppleで交換したらSIMロックが掛かっちゃって…もう大変さ。 | Tools 4 Hack

こういう話を知っていたので、SIMフリーiPhoneの交換はコミュニケーションミスが怖くてやりたくなかったのだ。とにかく解決してよかった。

Apple IDを変える際には事前に全デバイスのiCloudをサインアウトするんだ!

YAPC ASIA 2015の1日目に行ってきた。 #yapcasia

本日は、去年のYAPC::Asia Tokyo 2014 前夜祭に引き続き私にとっての2回目のYAPC、YAPC::Asia Tokyo 2015に参加して来ました。

参加したセッション

謎の写真です

f:id:naoto5959:20150821230425j:plain

旅行券、当たるかな?

esa.io - 趣味から育てたWebサービスで生きていく

さて、本日のお目当てのプログラムです。

esa.ioは「情報を育てる」という視点で作られた、 自律的なチームのためのドキュメント共有サービスです。

f:id:naoto5959:20150821230429j:plain

  • 今回は、界隈の仲間の発表ということで最前列で観ました。
  • イロイロ試すのは確かに大事。URLを持ち誰からもアクセスすることによってフィードバックをもらえるという点に激しく同意しました。
  • 仕事の後にこれだけのものを開発するパッションとスキルに尊敬の念を抱かずにはいられません。
  • サービスがイケるかも?と言ったタイミングで「責任をもつという意思表示のために会社を作る」という所にesa社の2人の覚悟と責任感を感じました。
  • 自分たちが楽しく開発することがユーザーに届ける価値を最大化する という点は私も大事にしている点です。楽しくない開発はどんなにがんばっても最高のパフォーマンスは出せません。楽しむことが何よりも大事だと思います。
  • Bug Fixタイムアタックに関しては、楽しむ為の工夫として素晴らしいなと感じます。
  • We're Not Hiringという方針を掲げる理由が「ユーザーサポートを作っている人たちがやることの強さ」を維持するためというあたりがクールです。
  • 開発スケジュールを決めない!!というのも私の大事にしている点で激しい同意で鼻血が出そうでした。モチベーションの赴くままやるのが最終的に効率がいいのです。モチベーションが全てです。デザインスプリントのようなあえて期限を決めることで自身を追い込む事は大事ですが、そこは 自身で追い込むことが大事 で、スケジュールを他者に握られないというのはすごい大事だと思います。
  • ふりかえりを壇上の上で行い、5年前の想いを実現している姿は素晴らしかったです。

胸を張って、趣味から育てたサービスで生きていく。誰にでも出来る事ではありません。謙虚な姿勢とふりかえりによる結果だと想いました。そしてあの2人だからこそ、という感じがします。esaはツイートの内容などゆふわな雰囲気がありますが、その雰囲気とは裏腹に確固たる信念の元、開発運営されているのでおいそれと真似できないなーと思いますが憧れますねこのチーム。とにかく今回の発表は、とても刺激を受けました。

fukayatsu氏の発表スライドはこちらです。

面白かったツイート

最後に

久々にカンファレンスに参加することで、大変刺激を受けました。自分のやりたいことと、世の中の人の役に立つことが一致させることがエンジニアとしての幸せなのかなと考えさせられる充実した1日でした。

YAPC::Asia Tokyo 2015のスタッフの皆様、運営ありがとうございました。