【Rails】Deviseを使って管理側のログインURLをスマートにする

はじめに

Webサービスを開発していて、ハマったところなのでメモ。

環境

前提条件

  1. ローカル環境でDeviseが正常に動作すること。
  2. ApplicationControllerを管理画面用のコントローラーを使っている。
  3. 管理者テーブルは「admin_users」という名前で定義している。

上記内容は、各アプリごとに違うので適度読み替えてください。

詳細

app/controller/admin/admin_controller.rb

class Admin::AdminController < ApplicationController
  layout "admin/layouts/application"
   
  before_action :authenticate_admin_admin_user!
end

config/routes.rb

# home/indexはダッシュボードみたいなところとして定義している。
namespace :admin do
  get '/', :to => 'home#index'
  # controllersは独自でカスタマイズしているため、明示的に書いている。
  devise_for :admin_users, controllers: { sessions: 'admin/devise/sessions', registrations: 'admin/devise/registrations', passwords: 'admin/devise/passwords' }
end

rake routesでパスを確認すると以下の通りとなる。

        new_admin_admin_user_session GET    (/:locale)/admin/admin_users/sign_in(.:format)           admin/devise/sessions#new
            admin_admin_user_session POST   (/:locale)/admin/admin_users/sign_in(.:format)           admin/devise/sessions#create
    destroy_admin_admin_user_session DELETE (/:locale)/admin/admin_users/sign_out(.:format)          admin/devise/sessions#destroy

改修した経緯

  1. URLがまったくスマートではなく、モデル名が露出していたため。
  2. adminが2つ並んでいて、よろしくない。

解決策

以下のように記述すれば、スマートになります。

config/routes.rb

# home/indexはダッシュボードみたいなところとして定義している。
namespace :admin do
  get '/', :to => 'home#index'
end
# controllersは独自でカスタマイズしているため、明示的に書いている。
devise_for :admin_users, path: :admin, controllers: { sessions: 'admin/devise/sessions', registrations: 'admin/devise/registrations', passwords: 'admin/devise/passwords' }

app/controller/admin/admin_controller.rb

class Admin::AdminController < ApplicationController
  layout "admin/layouts/application"
   
  before_action :authenticate_admin_user!
end

rake routesでパスを確認すると以下の通りとなる。

        new_admin_user_session GET    (/:locale)/admin/sign_in(.:format)                       admin/devise/sessions#new
            admin_user_session POST   (/:locale)/admin/sign_in(.:format)                       admin/devise/sessions#create
    destroy_admin_user_session DELETE (/:locale)/admin/sign_out(.:format)                      admin/devise/sessions#destroy

参考

github.com

【書籍】GILT(ギルト)という本を読みました。

はじめに

ひとりのプログラマとして、技術書を読み漁るのもいいですが
ビジネスにコミットしていく上でビジネス書も読まないとダメだな
と思い、書店の「起業コーナー」を眺めていると以下の本を発見しました。

ハードカバーであり、結構なページ数だったので
読めるか心配だったのですが、内容はそこまで小難しくなく
書かているようなので、朝読書をメインに読み続けました。

5月終わりぐらいに読み終えて、得られるものが結構あったので
読書メモとしてここに書く。

読書メモ

スタートアップのメンバーとして

スタートアップのビジネスは、1人ひとりが臨機応変に判断し
柔軟に独創的に行動して、いくつもの役割を果たすことが大事。

反対意見

反対意見は価値がある。
否定的意見と計画していたことを照らしあわせて検討した。
否定的な見方をする人の耳障りな意見や疑問に
耳を傾けることで、起こりうるあらゆる状況が想定できるだけでなく
潜在的なライバルがわかってくる。

人脈

立ち上げ準備時の人脈ネットワークを作る。
人付き合いが苦手な人でも、アドバイザー、客の紹介
取引先を探す労をとってくれる支援者を自分の人脈リストの中に
少なくても、2,3人は見つけておくこと。

スタートアップに重要なこと

スタートアップにとって、1番重要なのは
プレゼンテーション、自分の見せ方にこそ投資をする。
強烈な第一印象を与えることにお金を使わないといけない。

エンジニア

エンジニアが「この人のために仕事をしたい」と思うような
リーダーシップとカリスマ性を備えたトップがいることが大事である。

トップクラスのエンジニアを採用し、採用者に満足して働いてもらうためのノウハウ。

  1. 前もってふるいをかける
    オフィスで会う前に筆記試験を受けさせる。
    仕事のスピード、経験と技術スキルを判断するためだが試験をするのは、
    スキルよりむしろ人柄を見るほうが大きい
  2. エンジニアとしての能力だけでなく、企業文化に合う人を採用する。
    直近3ヶ月間に手助けしてくれる人より、今後2〜3年勤めて、会社に貢献してくれる人を待望する。
    面接の時には、エンジニアとしての優秀さと、一緒にやっていける人柄かを五分五分で見る。
  3. 外部に誇れるテクノロジー・ブランドをつくり上げる
    トップクラスのエンジニアに来てもらうためには、トップクラスのエンジニアが在籍し、すばらしい問題解決能力を
    発揮している会社として認められることが重要。大きな会議のスポンサーになり、講演したり、ブログを開設して、
    エンジニアの質の高さを示す。こういう形でアピールすることにより「一緒に働きたい、仕事に興味がある」と言う人が増える。
  4. トップクラスの才能を引き入れるための工夫を凝らす
    最高のエンジニアは職探しなんてしない。口説き落とすには、オフィスに呼んで気軽におしゃべりしたり、
    エンジニアに引き合わせたりすることが役立つ。
  5. 創造する時間を与える
    エンジニアを本来の仕事以外のことで悩ませてはいけない。エンジニアが関心を持つ製品の開発に、
    長時間にわたって邪魔されずに集中して取り組めるようなハッカソンを定期的に開催したりする。

トップに立つ人へのアドバイス

  1. 怒りを感じたときには絶対に人と話さない
    大きく深呼吸して、むずかしい話ができる準備が整うまで気持ちを落ち着かせる。
  2. 取り返しのつかない失敗はないと覚えておくこと
    一生懸命に働き、かなりの能力もある人が下す決断は、いいことが悪いほうを上回るもの。
    失敗から立ち直るには、いさぎよく失敗を認め、どうすれば取り戻せるかをすばやく説明し、そして行動すること。
  3. 相手に花を持たせることも忘れない
    完勝はいつもいいことだとは限らない。
  4. 何が人を発奮させるかに気づく
    数字を見ると興奮する人もいれば、創造的な仕事に満足感を得る人もいる。
    どんな仕事に高いモチベーションを持つかは、人によって違う。年齢や性別では決めつけられない。
    人を理解するための唯一の方法は、何が相手を動かすかを理解しようと働きかけ、一緒に時間を過ごすこと。
  5. 内なる声に耳を澄ます時間を取る
    どんなに多忙でも一人きりになる時間を取る。精神を落ち着かせることがある。
    ノート一冊持っていかず、ただ座って、とりとめのないことを考える。
    そんなときに、放置しておいては解決できない問題について、ベストなアイデアを思いついたりする。

まとめ

スタートアップについて書かれた内容だったので、エンジニアについても
しっかりと書かれていたのでかなりためになりました。
特に上に書いたエンジニアの採用に関しては頷ける人がたくさんいると考えています。

結局「新しいことをやりたい」と思ってもなかなかできず
いざそのチャンスが来た時も準備できておらず「どうしよう」となってしまい
動けない人では意味がないです。

また、社内で新規事業が立ち上がりそのチームのメンバーとして動くことに
スタートアップと似た何かがあるように思います。

ちなみに本の中に書いてあったことですが
2008年とかそのぐらいの話なのに、立ち上げ時はRuby on Railsを使う
ということになっていたようです。
やはり、Ruby(Ruby on Rails)はビジネスと密接しているなぁと思いました。

【Rails】【Capistrano】Staging環境で「bundle exec rails console」しても動作しない

はじめに

Webサービスを開発していて、ハマったところなのでメモ。

環境

詳細

CapistranoVPSサーバなどにデプロイした後
サーバ側で「bundle exec rails console」を実行した際に 以下の様な情報が出力されてしまいました。

$ bundle exec rails c
Usage:
  rails new APP_PATH [options]

Options:
  -r, [--ruby=PATH]                                      # Path to the Ruby binary of your choice

...以下略...

また、config/deploy.rb は以下の通り設定していました。

set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets bundle public/system public/assets vendor/assets/bower_components}

なお、おさらいとしてですが
rails consoleを起動するとき、bin/railsの内容が呼び出されるようになっています。
この設定は、新規でプロジェクトを作ったときから有効です。
元にローカル環境では問題なく使えていました。

bin/rails

#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application',  __FILE__)
require_relative '../config/boot'
require 'rails/commands'

解決策

config/deploy.rb に以下の記述を追加する。

set :bundle_binstubs, -> { shared_path.join('bin') }

また、「set :linked_dirs」から「bin」を削除する。

上記のように定義した後、Capistranoでデプロイして bin配下にファイルが設定されていればOKです。

原因

1つ目は、binをsharedディレクトリにしていたのにも 関わらず、bin配下の内容を配置していなかったことです。

set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets bundle public/system public/assets vendor/assets/bower_components}

2つ目は、capistrano-bundler 1.1.4から仕様が変更になったようです。

qiita.com

そして、GitHubにあるドキュメントのUsageを見ると、bin配下のものは
以下のように定義するように書いてありました。

set :bundle_binstubs, -> { shared_path.join('bin') }

github.com

【Rails】【Capistrano】Carrierwave (fog)を含んだ形でデプロイするとエラーになってしまう

はじめに

Webサービスを開発していて、ハマったところなのでメモ。

環境

詳細

Carrierwaveの動作確認がローカルでできたのですが
Capistranoを使ってデプロイしようとしたところ
以下の様なエラーが発生しました。

rake aborted!
ArgumentError: Missing required arguments: aws_access_key_id, aws_secret_access_key

「idとkeyが必須だが、設定されていないよ」というエラー内容。
実装は以下の通りとなっている。

config/initializer/carrierwave.rb

CarrierWave.configure do |config|
  config.fog_credentials = {
      provider:              'AWS',
      aws_access_key_id:     ENV['IMG_UP_AWS_S3_ACCESS_KEY_ID'],
      aws_secret_access_key: ENV['IMG_UP_AWS_S3_SECRET_ACCESS_KEY'],
      region:                'ap-northeast-1'
  }

実装方法としては、環境変数から取ってくるようにしていました。

さらにCapistranoを動かすホスト側には環境変数を定義せず、dotenv管理。
クライアント環境には環境変数定義済みです。

解決策

Capistranoでデプロイ時に使用する環境変数を明示的に設定するようにします。

# Default value for default_env is {}
set :default_env, {
  rbenv_root: "/usr/local/rbenv",
  path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH",
  IMG_UP_AWS_S3_ACCESS_KEY_ID: ENV["IMG_UP_AWS_S3_ACCESS_KEY_ID"],
  IMG_UP_AWS_S3_SECRET_ACCESS_KEY: ENV["IMG_UP_AWS_S3_SECRET_ACCESS_KEY"]
}

こうすることにより、デプロイ時のみ使われる環境変数を定義することができます。
また、ホスト側に環境変数を定義せず実行できるので、無駄に変数を書かなくても済みます。

以下、Capistrano実行時のログ。

Command: ( RBENV_ROOT=/usr/local/rbenv PATH=/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH IMG_UP_AWS_S3_ACCESS_KEY_ID= IMG_UP_AWS_S3_SECRET_ACCESS_KEY= RBENV_VERSION=2.3.0 /usr/bin/env mkdir -p /tmp/sample/

原因

Capistranoでは、ホスト、クライアントの環境変数を読まずに実行するようです。
そのため、明示的に指定してあげないといけないようです。

【Ruby】ruby-filemagicのインストールでエラーになる

はじめに

画像チェックの処理を実装したいと思い
ruby-filemagicというライブラリが良さそうだったので
インストールしようとしたところ、エラーになったのでメモ。

結論

GitHubのREADMEを読みましょう。

github.com

解決策

Mac

brew install libmagic

CentOS

yum install file-devel

【Gem】【Rails】RubyGemsにあるcarrierwaveをインストールすると最新バージョンなのに古い内容になってしまう

はじめに

Webサービスを開発していて、ハマったところなのでメモ。

環境

詳細

Amazon S3に対して、画像アップロード機能を作成したかったため
「carrierwave」を追加しました。

バリデーションエラーがなければ、指定したディレクトリに画像がアップロードされ
問題なかったのですが、バリデーションに引っかかった場合、画像が消えないように
一時ディレクトリに格納されるようにしていました。

しかし、画像が格納されず機能が実装されていない?と思い、ライブラリを調査しました。

コードに機能が実装されているか確認

GitHubに公開されているので、ソースを確認します。

github.com

途中、調査していたところ、0.10でAmazon S3に一時的にファイルが
アップロードされるようになったようなので機能はありそうです

github.com

バージョンの確認

bundle installした際に古いライブラリがインストールされている可能性があるため
アンインストールして、再度実行する。

...中略...
Installing carrierwave 0.11.2
...中略...

RubyGemsのサイトを見に行くと0.11.2が最新っぽいので大丈夫そうです。

carrierwave | RubyGems.org | your community gem host

ここまでやっても解決しません。

インストールされたライブラリを確認

実装したロジックがおかしいのかな?と考え、1つ1つ確認したところ問題なし。
そして、せっかくRubyMineを使っているのでIDEから該当箇所のロジックが
ないかどうかを確認しました。

確認したところ、追加されたロジックに関する内容がすべてありませんでした。
バージョンは確かに0.11.2というのに。

解決策

Gemfile

gem 'carrierwave'

上記の記述を以下のように修正

gem 'carrierwave', github: 'carrierwaveuploader/carrierwave'

ライブラリを直接GitHubから取ってくるようにしました。

Install carrierwave 0.11.0 from git://github.com/carrierwaveuploader/carrierwave.git (at master@b31f7ce)

原因

原因を探ろうと思ったのですが、検討がつかず時間が経ってしまったので
一旦前に進むようにします。謎すぎる。。。

【Rails】Capistranoでデプロイするとき「Devise.secret_key was not set. Please add the following to your Devise initializer:」が発生する

はじめに

Webサービスを開発していて、ハマったところなのでメモ。

環境

前提条件

エラーが出るところ以外、デプロイできる状態とします。

原因(なぜ、今までできていたか?)

config/secret.ymlに文字列を直接記述していたため。

staging:
  secret_key_base: 'test'

さらにdevise.rbの記述でsecret_key_baseを使うようにしていたため。

Devise.setup do |config|
  # The secret key used by Devise. Devise uses this key to generate
  # random tokens. Changing this key will render invalid all existing
  # confirmation, reset password and unlock tokens in the database.
  # config.secret_key = aqwsedrftgyhujikolp;''

  ...以下略....

改修した経緯

さすがに直接書くのはどうなの?という疑問からとなります。

改修

config/secret.yml

staging:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

config/deploy.rb

# Default value for default_env is {}
set :default_env, {
  rbenv_root: "/usr/local/rbenv",
  path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH",
  DEVISE_SECRET_KEY: ENV["DEVISE_SECRET_KEY"]
}

devise.rb

Devise.setup do |config|
  # The secret key used by Devise. Devise uses this key to generate
  # random tokens. Changing this key will render invalid all existing
  # confirmation, reset password and unlock tokens in the database.
  config.secret_key = ENV["DEVISE_SECRET_KEY"]

  ...以下略...

さらにWebサーバの環境変数にSECRET_KEY_BASE, DEVISE_SECRET_KEYの内容を記述する。

例:.bash_profile

export SECRET_KEY_BASE='secret_key'
export DEVISE_SECRET_KEY='devise_secret_key'

なぜ、このようにしたか?

改修は、見た通りやればいいと思うのですが、上記のようにした理由を書きます。
ある意味、ここからが本題です。

まず、すぐに思いつくのが以下のGemです。

github.com

自分も「これが手っ取り早いよなぁ?」と思っていたのですが、以下のエントリーを発見。

t-cyrill.hatenablog.jp

記事の最後の方に書いてありますが、StagingやProductionでは使われることは
望まれていないようです。確かに管理が簡単で.envに書けばすべて解決して、
わざわざ環境変数を設定する手間も省けてとても魅力的なものです。

ただ、運用するという面で見るとProduction環境は楽するよりも、
強固なシステムを作ることにウェイトを置くべきだと思うので、
これが今のところベストかな?と考えています。