感嘆符(!)をつけるメソッド
- 戻り値で自身を変更する破壊的なメソッド
- 処理失敗時に例外を発生させるほうのメソッド
- ( 同じ処理をするメソッドが2つある場合に、プログラマにより注意を促したい場合 )
ユーザープロフィールの作成
この記事で行うこと
ユーザーが現在のパスワード(current_password)なしでプロフィールを登録・編集できるようにする。
使うもの
前提
予約機能の作成 - さがえもん まで完了していること。
現場では?
ユーザープロフィールには登録される項目が多いため、別テーブルで切り分けて保存します。
実装手順
プロフィールテーブルの作成
userテーブルとは別にprofileテーブルを作成します。 userとprofileテーブルは以下のような関連性を持ちます。
- profileの作成
$ rails g model Users::Profile
- モデルとマイグレーションに関連づけを記述
app/models/users/profile.rb
class Users::Profile < ApplicationRecord # ------------------------------------------------------------------------------- # Relations # ------------------------------------------------------------------------------- belongs_to :user end
app/models/user.rb
# ------------------------------------------------------------------------------- # Relations # ------------------------------------------------------------------------------- has_many :deliveries, dependent: :destroy has_one :profile, class_name: 'Users::Profile', dependent: :destroy accepts_nested_attributes_for :profile
profileはmodel/users/profile.rbのようにmodel直下にはないので class_name
で指定します。
accepts_nested_attributes_for
でuserを編集する時に一緒にprofile(子レコード)を保存・更新することができるようになります。
db/migrate/20180728140656_create_users_profiles.rb
class CreateUsersProfiles < ActiveRecord::Migration[5.1] def change create_table :users_profiles do |t| t.belongs_to :user, foreign_key: true t.integer :gender t.timestamps end end end
データを作成しましょう。
$ rails db:migrate
プロフィール入力フォームの作成
プロフィール変更ページにgender(性別)用のフォームを作成します。
- enumの設定
性別は男性・女性どちらかをgenderカラムに持ちます。 この際genderはbooleanでもいいのですが、enumという便利な機能があるのでこちらを使っていきます。
enumの詳細は enumまとめ - さがえもん を読んでみてください。
app/models/users/profile.rb
belongs_to :user # ------------------------------------------------------------------------------- # Enumerables # ------------------------------------------------------------------------------- enum gender: { woman: 1000, man: 2000 }
プロフィール画面へ遷移する前にprofileをbuildさせてあげます。
buildしてあげないと field_for
のフォームが表示されません。
app/controllers/users/registrations_controller.rb
def edit resource.build_profile if resource.profile.nil? super end
フォームは以下のように書くことでシンプルに表現することができます。 app/views/users/registrations/edit.html.haml
・・・・・ .form-group = f.label :password_confirmation, class: 'col-sm-2 control-label' .col-sm-10= f.password_field :password_confirmation, class: 'form-control', autocomplete: 'off' // プロフィール = f.fields_for :profile do |profile_f| .form-group = profile_f.label :gender, class: 'col-sm-2 control-label' .col-sm-9.col-xs-12 - Users::Profile.genders_i18n.invert.each do |key_i18n, value| %label.radio-inline = profile_f.radio_button :gender, value = key_i18n .form-group = f.label :current_password, class: 'col-sm-2 control-label' ・・・・・
- current_passwordなしで登録できるように変更する
こちらは railsのdeviseまとめ - 現場の開発 で説明しています。ご覧ください。
app/views/users/registrations/edit.html.haml
.form-group # 削除 = f.label :current_password, class: 'col-sm-2 control-label' # 削除 .col-sm-10= f.password_field :current_password, class: 'form-control', autocomplete: 'off' #削除
参考
ソースコード
質疑応答
コメントしてください。 こんなコード見たいというような要望あれば記事にしますのでそちらもコメントかメッセージください。
railsのdeviseまとめ
アカウント登録時に許可するパラメータの設定
def sign_up_params params.require(:user).permit(:email, :password, :password_confirmation) end
リソースの更新時のストロングパラメータを変更する
app/controllers/users/registrations_controller.rb
# protected ・・・・ def account_update_params params.require(:user).permit( :email, :password, :password_confirmation, :current_password, profile_attributes: %i(id gender) ) end ・・・
パスワードなしでリソースを更新する
app/controllers/users/registrations_controller.rb
# protected ・・・・ # # アカウント情報の更新時にはパスワードは不要 # def update_resource(resource, params) resource.update_without_password(params) end ・・・
日本語化
config/locales/devise.ja.yml
ja: devise: confirmations: confirmed: "アカウントが確認されました。ログインしています。" send_instructions: "アカウントの確認方法を数分以内にメールでご連絡します。" send_paranoid_instructions: "ご登録のメールアドレスが保存されている場合、アカウントの確認方法をメールでご連絡します。" failure: already_authenticated: "既にログインしています。" inactive: "Your account is not activated yet." invalid: "Invalid email or password." locked: "アカウントがロックされています。" last_attempt: "あなたのアカウントがロックされる前に、もう1つの試みを持っています。" not_found_in_database: "メールアドレスまたはパスワードが無効です。" timeout: "一定時間が経過したため、再度ログインが必要です" unauthenticated: "ログインまたは登録が必要です。" unconfirmed: "本登録を行ってください。" mailer: confirmation_instructions: subject: "アカウントの登録方法" reset_password_instructions: subject: "パスワードの再設定" unlock_instructions: subject: "アカウントのロック解除" omniauth_callbacks: failure: "%{kind} から承認されませんでした。理由:%{reason}" success: "%{kind} から承認されました。" passwords: no_token: "このページにアクセスする事が出来ません。正しいURLでアクセスしている事を確認して下さい。" send_instructions: "パスワードのリセット方法を数分以内にメールでご連絡します。" send_paranoid_instructions: "" updated: "パスワードを変更しました。" updated_not_active: "パスワードを変更しました。" registrations: destroyed: "アカウントを削除しました。またのご利用をお待ちしております。" signed_up: "アカウント登録を受け付けました。" signed_up_but_inactive: "アカウントは登録されていますが、アクティブになっていないため利用できません。" signed_up_but_locked: "アカウントは登録されていますが、ロックされているため利用できません。" signed_up_but_unconfirmed: "確認メールを登録したメールアドレス宛に送信しました。リンクを開いてアカウントを有効にして下さい。" update_needs_confirmation: "アカウント情報が更新されました。更新の確認メールを新しいメールアドレス宛に送信しましたので、リンクを開いて更新を有効にして下さい。" updated: "アカウントが更新されました。" sessions: signed_in: "ログインしました。" signed_out: "ログアウトしました。" unlocks: send_instructions: "アカウントのロックを解除する方法を数分以内にメールでご連絡します。" send_paranoid_instructions: "アカウントが存在する場合、ロックを解除する方法をメールでご連絡します。" unlocked: "アカウントのロックが解除されました。ログインしています。" errors: messages: already_confirmed: "は既に登録済みです。ログインしてください" confirmation_period_expired: "%{period}以内に確認する必要がありますので、新しくリクエストしてください。" expired: "有効期限切れです。新規登録してください。" not_found: "は見つかりませんでした。" not_locked: "ロックされていません。" not_saved: one: "エラーにより、この %{resource} を保存できません:" other: "%{count} 個のエラーにより、この %{resource} を保存できません:"
プロフィールとは別でパスワードのみ編集する場合
# PUT /resource def update if params[:user][:password] if resource.update_with_password(user_password_params) sign_in(resource, bypass: true) redirect_to users_settings_password_url, notice: t('devise.passwords.updated') else render 'users/settings/password' end else super end end ・・・・・ # # パスワード更新時のパラメータ # def user_password_params params.require(:user).permit( :password, :password_confirmation, :current_password ) end
参考
ローカライズ(i18n)まとめ
localeを日本語(:ja)に変更する
config/application.rb
config.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
翻訳ファイルの作成と翻訳の記入
以下のファイルを作成しましょう。
config/application.rbで日本語設定を施した後にja.ymlファイルが存在しないと I18n::InvalidLocale: :ja is not a valid locale
エラーが発生しますので気をつけてください。
config/locales/ja.yml
ja: activerecord: errors: messages: record_invalid: "バリデーションに失敗しました: %{errors}" restrict_dependent_destroy: has_one: "%{record}が存在しているので削除できません" has_many: "%{record}が存在しているので削除できません" date: abbr_day_names: - 日 - 月 - 火 - 水 - 木 - 金 - 土 abbr_month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 day_names: - 日曜日 - 月曜日 - 火曜日 - 水曜日 - 木曜日 - 金曜日 - 土曜日 formats: default: "%Y/%m/%d" long: "%Y年%m月%d日(%a)" short: "%m/%d" month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 order: - :year - :month - :day datetime: distance_in_words: about_x_hours: one: 約1時間 other: 約%{count}時間 about_x_months: one: 約1ヶ月 other: 約%{count}ヶ月 about_x_years: one: 約1年 other: 約%{count}年 almost_x_years: one: 1年弱 other: "%{count}年弱" half_a_minute: 30秒前後 less_than_x_minutes: one: 1分以内 other: "%{count}分未満" less_than_x_seconds: one: 1秒以内 other: "%{count}秒未満" over_x_years: one: 1年以上 other: "%{count}年以上" x_days: one: 1日 other: "%{count}日" x_minutes: one: 1分 other: "%{count}分" x_months: one: 1ヶ月 other: "%{count}ヶ月" x_years: one: 1年 other: "%{count}年" x_seconds: one: 1秒 other: "%{count}秒" prompts: day: 日 hour: 時 minute: 分 month: 月 second: 秒 year: 年 errors: format: "%{attribute}%{message}" messages: accepted: を受諾してください blank: を入力してください present: は入力しないでください confirmation: と%{attribute}の入力が一致しません empty: を入力してください equal_to: は%{count}にしてください even: は偶数にしてください exclusion: は予約されています greater_than: は%{count}より大きい値にしてください greater_than_or_equal_to: は%{count}以上の値にしてください inclusion: は一覧にありません invalid: は不正な値です less_than: は%{count}より小さい値にしてください less_than_or_equal_to: は%{count}以下の値にしてください model_invalid: "バリデーションに失敗しました: %{errors}" not_a_number: は数値で入力してください not_an_integer: は整数で入力してください odd: は奇数にしてください required: を入力してください taken: はすでに存在します too_long: は%{count}文字以内で入力してください too_short: は%{count}文字以上で入力してください wrong_length: は%{count}文字で入力してください other_than: は%{count}以外の値にしてください template: body: 次の項目を確認してください header: one: "%{model}にエラーが発生しました" other: "%{model}に%{count}個のエラーが発生しました" helpers: select: prompt: 選択してください submit: create: 登録する submit: 保存する update: 更新する number: currency: format: delimiter: "," format: "%n%u" precision: 0 separator: "." significant: false strip_insignificant_zeros: false unit: 円 format: delimiter: "," precision: 3 separator: "." significant: false strip_insignificant_zeros: false human: decimal_units: format: "%n %u" units: billion: 十億 million: 百万 quadrillion: 千兆 thousand: 千 trillion: 兆 unit: '' format: delimiter: '' precision: 3 significant: true strip_insignificant_zeros: true storage_units: format: "%n%u" units: byte: バイト gb: GB kb: KB mb: MB tb: TB percentage: format: delimiter: '' format: "%n%" precision: format: delimiter: '' support: array: last_word_connector: 、 two_words_connector: 、 words_connector: 、 time: am: 午前 formats: default: "%Y年%m月%d日(%a) %H時%M分%S秒 %z" long: "%Y/%m/%d %H:%M" short: "%m/%d %H:%M" pm: 午後
以下のサイトから入手できます。 github.com
config/locales/resource/users.ja.yml
ja: activerecord: models: user: ユーザー user_profile: プロフィール attributes: user: id: ユーザーID email: メールアドレス password: パスワード password_confirmation: パスワード(確認用) sign_in_count: ログイン回数 created_at: 登録日時 last_sign_in_at: 前回ログイン時刻 users/profile: user: ユーザー gender: 性別
Railsサーバの再起動をしてください。
動作確認
> I18n.t('activerecord.models.user') => "ユーザー"
enumまとめ
enumとは?
enumは列挙型は列挙型と呼ばれています。 列挙型は整数が割り当てられた文字を順番に出力していくことができます。
属性名、属性の値(ハッシュ)で指定することでenumを定義することができます。
enumのすごいところ
可読性が上がります。 例えば、statusカラムがあるとして、そのstatusカラムは以下の属性と値を持つと表現できます。
pending: 1000 # 審査待ち active: 2000 # 登録済み quit: 3000 # 退会後
以上の値を属性(pending, active, quit)からでも値(1000, 2000, 3000)を取得・更新することができます。 viewやcontroller、model内で属性名で表現できれば見やすさは高まります。
実例
コンソールで試します。
booleanで検証できる
> User.create(email: 'hoge@example.com', password: 'hogehoge') > profile = User.first.build_profile > profile.gender = 'man' > profile.man? => true > profile.woman? => false
破壊的メソッドで更新できる。
> profile.man! > profile.gender => "man"
ハッシュで取得できる
> Users::Profile.genders
属性名(キー)で設定できる。
> profile = Users::Profile.new(gender: :woman) > profile.woman? => true
値(バリュー)で設定できる。
> profile = Users::Profile.new(gender: Users::Profile.genders[:woman]) > profile.woman? => true
検索ができる
> profile = Users::Profile.new(gender: Users::Profile.genders[:woman]) > profile.user = User.first > profile.save > Users::Profile.woman => #<ActiveRecord::Relation [#<Users::Profile id: 2, user_id: 1, gender: "woman", created_at: "2018-07-28 15:33:28", updated_at: "2018-07-28 15:33:28">]>
属性名を日本語で取得することができる
enum_helpというgemを使うことで属性名を日本語にすることができます。
Gemfile
gem 'enum_help'
bundle install
ここでi18nを使います。i18nは ローカライズ(i18n)まとめ - 現場の開発 でまとめています。
config/locales/resource/users.ja.yml
ja: activerecord: models: user: ユーザー user_profile: プロフィール attributes: user: id: ユーザーID email: メールアドレス password: パスワード password_confirmation: パスワード(確認用) sign_in_count: ログイン回数 created_at: 登録日時 last_sign_in_at: 前回ログイン時刻 users/profile: user: ユーザー gender: 性別 enums: users: profile: gender: woman: 女性 man: 男性
コンソールで確認します。
> User.first.profile.gender_i18n => "男性"
参考
Railsアプリにwebpackerの導入
この記事で行うこと
webpackerの導入
使うもの
Ruby 2.4.2
Rails 5.1.6
前提
(Tutorialをしている人は)予約機能の作成 - さがえもん まで完了していること。
webpackerとは
Webpackerとは、Webpackを用いてRails上でJavaScript開発をするために必要な一連の流れを提供してくれる、Rails organizationで開発されているgemです。
Railsの開発プロセスに沿ったJSのパッケージ管理・エントリーポイントの制御ができることが導入のメリットになります。
現場では?
以下の記事がうまくまとまっています。というか、参考にさせていただきました。 techlife.cookpad.com
RailsにおいてJavaScriptのパッケージ管理方法はたくさんありますが、 現状もっともRailsの開発プロセスにのっとった管理方法を採用しているのがwebpackerになります。
実装手順
インストール
Gemfile
gem 'webpacker'
webpackerの設定ファイルをインストール
bundle exec rails webpacker:install
するとapp配下にjavascriptフォルダが作成されます。
今後、Javascriptは、app/javascript/packs配下に拡張子.js
でファイルを作成してコードを書いてください。
動作確認
webpackerが正常に動作するかどうかを確認します。
- applicationが動作しないことを確認する
option(alt)+ command(⌘)+ i
でデベロッパーコンソールを開きます。 「Console」タブに何も表示されないことを確認します。
application.html.hamlに javascript_pack_tag
を書いてください。
app/views/layouts/application.html.haml
・・・・・ = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application' # 追加 %body ・・・・・
ページをリロードするとデベロッパツールのConsoleに「Hello World from Webpacker」が表示されます。 これで動作確認完了です。
動作確認で記述した以下のコードは消しておいてください。このコードをapplication.html.hamlで書いてしまうと、全てのファイルでapplication.jsが呼び出されてしまいます。
必要なhamlファイルにのみ javascript_pack_tag
でjsファイルを呼びましょう。
app/views/layouts/application.html.haml
・・・・・ = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application' # 削除 %body ・・・・・
参考
github.com techlife.cookpad.com github.com
質疑応答
コメントしてください。