だいぶ前に、マルチテナントの Rails アプリをテストするために Pow を導入した。
便利に使っていたけど、もう用済みになったのでアンインストール。
$ curl get.pow.cx/uninstall.sh | sh
Pow は Mac 以外で使えないのがもったいない。 Node ではなく Go で作られていたら Windows でも使えたりしたかもな。
Rails アプリ開発中、メール送信機能をテストするには Gmail の SMTP サーバーを使うのが手っ取り早い。
その際、ActionMailer の設定に Gmail のアカウントとパスワードを書く必要があるけど、 開発者ごとに違うし、そもそもアカウントとパスワードをソースコードに直接書きたくない。
そういった環境や開発者ごとに異なる情報を記述するために、 dotenv(dotenv-rails) を導入してみた。
Gemfile に
gem "dotenv-rails", groups: [:development, :test]
を追加して bundle install
。
プロジェクトのルートディレクトリに .env ファイルを作成し、環境変数にセットしたい情報を記述する。
GMAIL_ADDRESS="your-address@gmail.com" GMAIL_PASSWORD="your-password"
すると、config/environment/development.rb の ActionMailer の設定が次のように書ける。
config.action_mailer.delivery_method = :smtp config.action_mailer.raise_delivery_errors = true config.action_mailer.smtp_settings = { enable_starttls_auto: true, address: "smtp.gmail.com", port: 587, domain: "smtp.gmail.com", authentication: "plain", user_name: ENV["GMAIL_ADDRESS"], password: ENV["GMAIL_PASSWORD"] }
bin/rails server
で開発サーバーを起動すれば、.env ファイルに書いた環境変数が自動で読み込まれて ENV にセットされる。
.env ファイルをリポジトリから除外するように .gitignore に追加しておけば、 Gmail のアカウントとパスワードをソースコードに記述せずに、 心置きなくメール送信をテストできる。
Rails アプリの開発環境を Vagrant + Chef で構築していて、 PostgreSQL のインストールでつまづいたのでメモ。
まず Berkshelf で postgresql のクックブックをダウンロード。
echo cookbook "postgresql" >> Berksfile berks vendor cookbooks
postgresql クックブックはそのまま使うと、どうしてもエンコーディングに UTF-8 を指定してデータベースを作成できなかった。 対策として、postgresql をインポートするクックブックを新規作成する。
knife cookbook create postgresql_server_utf8
デフォルトのレシピを記述。
ENV["LANGUAGE"] = ENV["LANG"] = ENV["LC_ALL"] = "en_US.UTF-8" include_recipe "postgresql::server"
環境変数をセットしているのがポイント。
あとは Vagrantfile に
VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "hashicorp/precise64" config.vm.provision "chef_solo" do |chef| chef.cookbooks_path = ["./chef/cookbooks", "./chef/site-cookbooks"] chef.add_recipe "postgresql_server_utf8" chef.json = { "postgresql" => { "password" => { "postgres" => "postgres" }, "initdb_locale" => "en_US.UTF-8", "config" => { "lc_messages" => "en_US.UTF-8", "lc_monetary" => "en_US.UTF-8", "lc_numeric" => "en_US.UTF-8", "lc_time" => "en_US.UTF-8" } } } end end
を書いて、vagrant up/provision を実行すれば、 PostgreSQL をインストールできた。 エンコーディングに UTF-8 を指定してデータベースを作成できるようにもなった。
パスワード変更機能を、Devise が提供するコントローラーを使わずに、自前で実装したいときのためのメモ。
現在のパスワード・新しいパスワード・新しいパスワード(確認用) を入力して変更する場合、 update_with_password を使う。
current_user.update_with_password( password: "new_password", password_confirmation: "new_password", current_password: "old_password" )
新しいパスワードで強制的に変更する場合、update を使う。 管理者がユーザーのパスワードを再発行するときなんかに使ったりする。
current_user.update( password: "new_password", password_confirmation: "new_password" )
注意点としては、現在ログインしているユーザーのパスワードを変更すると、 直後ログアウトした状態になってしまうので、 再ログインする処理が必要。
class SettingsController < ApplicationController # ... 省略 ... def update respond_to do |format| if current_user.update_with_password(user_params) # パスワードを変更するとログアウトしてしまうので、再ログインが必要 sign_in(current_user, bypass: true) format.html { redirect_to edit_setting_path } else format.html { render :edit } end end end end
$ bin/rails generate kaminari:views bootstrap
で Boostrap 用の kaminari のビューをダウンロードできる。 ただ、Bootstrap 2.0 のときのテーマなので、最新の 3.2 で使うには修正が必要。
app/views/kaminari/_pagenator.html.erb を次のように修正すれば使えた。
<%= paginator.render do -%> <ul class="pagination"> <%= first_page_tag unless current_page.first? %> <%= prev_page_tag unless current_page.first? %> <% each_page do |page| -%> <% if page.left_outer? || page.right_outer? || page.inside_window? -%> <%= page_tag page %> <% elsif !page.was_truncated? -%> <%= gap_tag %> <% end -%> <% end -%> <%= next_page_tag unless current_page.last? %> <%= last_page_tag unless current_page.last? %> </ul> <% end -%>
amatsuda/kaminari_themes には Bootstrap 3 対応のプルリクエストがいくつもあるけど、どれもマージされていない。まぁ、これくらいの修正で使えるからいいけど。
Devise ではメールアドレスとパスワードを使ってサインインするのがデフォルト。
メールアドレスではなくユーザー名、それとパスワードでサインインしたい場合はどうすればいいか? Devise が呼び出すモデルとコントローラーのメソッドを上書きするしかない。
やったことをメモしておく。
すでに users テーブルを作っていたので、users テーブルに列を追加する。
$ rails generate migration AddUsernameToUsers
マイグレーションファイルの中身は次の通り。
class AddUsernameToUsers < ActiveRecord::Migration def change add_column :users, :username, :string, null: false, default: "" add_index :users, :username, unique: true end end
あとはマイグレーションを実行。
ユーザー名を使ってサインインするのは User だけなので、 User クラスで認証に使うキーを指定する。
class User < ActiveRecord::Base # 認証で使うキーを指定 devise :database_authenticatable, :trackable, :validatable, authentication_keys: [:username] # ユーザー名で検索 def self.find_first_by_auth_conditions(warden_conditions) conditions = warden_conditions.dup if username = conditions.delete(:username) where(conditions).where(username: username).first else where(conditions).first end end # 登録時に email を不要にする def email_required? false end def email_changed? false end end
ユーザー名が Strong Parameters ではじかれないように対策が必要。
class ApplicationController < ActionController::Base before_filter :configure_permitted_parameters, if: :devise_controller? # ...省略... protected def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:subdomain, :username, :email, :password, :password_confirmation) } devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:subdomain, :username, :email, :password, :remember_me) } end end
ユーザー名とパスワードでサインインするのは User だけなので、 異なるビューを使えるように initializers/devise.rb で設定する。
Devise.setup do |config| # ...省略... config.scoped_views = true end
ビューを生成。
$ rails generate devise:view users
app/views/users/sessions/new.html.erb を修正し、ユーザー名とパスワードを入力できるようにする。
<%= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "form-signin", role: "form" }) do |f| %> <h2 class="form-signin-heading"> サインイン </h2> <%= f.text_field :username, class: "form-control", placeholder: "ユーザー名" %> <%= f.password_field :password, autocomplete: "off", class: "form-control", placeholder: "パスワード" %> <%= f.submit "サインイン", data: { disable_with: "サインイン中..." }, class: "btn btn-lg btn-primary btn-block" %> <% end %>
なお、今回作っているアプリは「ユーザー登録を行うのは管理者ユーザーのみ」という仕様だったので、 ユーザー名とパスワードでのサインアップは省いた。