【Rails】英語を日本語化! Gem i18n
Ruby on Railsで英語を簡単に日本語表示してくれるGem「i18n
」の紹介です。
事前準備
Railsアプリ全体の言語設定を日本語に変更します。
# config/application.rb config.i18n.default_locale = :ja
Gemfile
にrails-i18n
を設定してbundle install
します。
# Gemfile gem 'rails-i18n'
ベースとなる日本語辞書ファイルをダウンロードします。
config/locales/ja.yml
を作成しrails-i18n/ja.yml at master · svenfuchs/rails-i18n · GitHubの内容を全てコピペします。
# config/locales/ja.yml --- ja: activerecord: errors: messages: record_invalid: 'バリデーションに失敗しました: %{errors}' restrict_dependent_destroy: has_one: "%{record}が存在しているので削除できません" has_many: "%{record}が存在しているので削除できません" date: abbr_day_names: - 日 - 月 - 火 - 水 - 木 - 金 - 土 . . .
これで完了です。
基本的な使い方
先ほどのja.yml
に追加します。
ja: views: hoges: hello: 'こんにちは'
この状態で、view側のファイルで= t('views.hoges.hello')
と書くと「こんにちは」と表示されていると思います。
色々な使い方
モデル名を日本語表示
# ja.yml ja: activerecord: models: user: "ユーザー"
# コンソール > User.model_name.human => "ユーザー"
カラム名を日本語表示
# ja.yml ja: activerecord: attributes: user: name: 名前
# コンソール > User.human_attribute_name :name => "名前"
日付フォーマットを操る
日付を色々なパターンで表示する
ja.yml
にformats
が定義されていて、それを利用すると状況に応じて色々なフォーマットで日付を表示することが可能です。
# ja.yml la: date: formats: default: "%Y/%m/%d" long: "%Y年%m月%d日(%a)" short: "%m/%d"
# 指定なし > I18n.l date => "2019年09月26日(木)" # :short > I18n.l date, format: :short => "9月26日(木)" # :long > I18n.l date, format: :long => "2019年09月26日(木)" # viewで使うとき <%= l date, format: :short %>
曜日部分を日本語化する
# ja.yml ja: data: abbr_day_names: - 日 - 月 - 火 - 水 - 木 - 金 - 土
元々曜日に関する日本語が定義されていますので、この状態で下記のように曜日部分を日本語表示することが可能です。
# コンソール > date = Date.today => Thu, 26 Sep 2019 > date.strftime("%Y/%m/%d(%a)") => "2019/09/26(Thu)" > date.strftime("%Y/%m/%d(#{I18n.t('date.abbr_day_names')[date.wday]})") => "2019/09/26(木)"
%w(日 月 火)
って定義しておいてDate.today.wday
でindexを取得してやる方法が調べるとよく出てきますが、こっちの方が余計な記述も不要でスッキリですね。
【Rails】簡単にformが作成できるsimple_form
Ruby on Railsで登録機能や更新機能を作る際に必要なform。
このformの作成を簡単に作成することが出来る便利なライブラリ「simple_form gem」のご紹介。
インストール手順
Gemfile
に追加して、bundle install
する。
gem 'simple_form'
モデルの作成
generate
コマンドを使ってモデルを作ります。
$ bundle exec rails g model hoge name:string age:integer gender:string birthday:date email:string
rake db:migrate
してDBにテーブルを作成します。
$ bundle exec rake db:migrate
こちらで作成したhoges
テーブルです。
Table "public.hoges" Column | Type | Collation | Nullable | Default ------------+-----------------------------+-----------+----------+----------------------------------- id | bigint | | not null | nextval('hoges_id_seq'::regclass) name | character varying | | | age | integer | | | gender | character varying | | | birthday | date | | | email | character varying | | | created_at | timestamp without time zone | | not null | updated_at | timestamp without time zone | | not null | Indexes: "hoges_pkey" PRIMARY KEY, btree (id)
コントローラーの作成
new
アクションはモデルをnewしてオブジェクトする。
class HogesController < ApplicationController def index end def new @hoge = Hoge.new end end
※create
アクションは今回は割愛します...
画面の作成
form_tag
の代わりにsimple_form_for
を使います。
ざっくり使い方
# haml .hoges__new .hoges__new-title %h2 新規登録画面 %p ご依頼・お問い合わせなどございましたら、下記よりお気軽にご連絡ください。 .hoges__new-form = simple_form_for(@hoge, url: hoges_path) do |f| = f.input :name = f.input :age = f.input :birthday .hoges__new-form-btn = f.submit # html <div class="hoges__new"> <div class="hoges__new-title"> <h2> 新規登録画面 </h2> <p> ご依頼・お問い合わせなどございましたら、下記よりお気軽にご連絡ください。 </p> </div> <div class="hoges__new-form"> <form class="simple_form new_hoge" id="new_hoge" novalidate="novalidate" action="/hoges" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="authenticity_token" value="6VTA5orkwF0ot26n+iZKp6+OeokxeTOxTnAm79HSX1RFx9LM9OMnevbi1HP7YoCl3cjJGrAa24rGhf6NFIRb0Q=="> <div class="form-group string optional hoge_name"><label class="control-label string optional" for="hoge_name">お名前</label><input class="form-control string optional" type="text" name="hoge[name]" id="hoge_name"></div> <div class="form-group integer optional hoge_age"><label class="control-label integer optional" for="hoge_age">ご年齢</label><input class="form-control numeric integer optional" type="number" step="1" name="hoge[age]" id="hoge_age"></div> <div class="form-group date optional hoge_birthday"><label class="control-label date optional" for="hoge_birthday_1i">誕生日</label> <select id="hoge_birthday_1i" name="hoge[birthday(1i)]" class="form-control date optional"> <option value="2014">2014</option> <option value="2015">2015</option> <option value="2016">2016</option> <option value="2017">2017</option> <option value="2018">2018</option> <option value="2019" selected="selected">2019</option> <option value="2020">2020</option> <option value="2021">2021</option> <option value="2022">2022</option> <option value="2023">2023</option> <option value="2024">2024</option> </select> <select id="hoge_birthday_2i" name="hoge[birthday(2i)]" class="form-control date optional"> <option value="1">1月</option> <option value="2">2月</option> <option value="3">3月</option> <option value="4">4月</option> <option value="5">5月</option> <option value="6">6月</option> <option value="7">7月</option> <option value="8">8月</option> <option value="9" selected="selected">9月</option> <option value="10">10月</option> <option value="11">11月</option> <option value="12">12月</option> </select> <select id="hoge_birthday_3i" name="hoge[birthday(3i)]" class="form-control date optional"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> <option value="6">6</option> <option value="7">7</option> <option value="8">8</option> <option value="9">9</option> <option value="10">10</option> <option value="11">11</option> <option value="12">12</option> <option value="13">13</option> <option value="14">14</option> <option value="15">15</option> <option value="16">16</option> <option value="17">17</option> <option value="18">18</option> <option value="19">19</option> <option value="20">20</option> <option value="21">21</option> <option value="22">22</option> <option value="23">23</option> <option value="24">24</option> <option value="25" selected="selected">25</option> <option value="26">26</option> <option value="27">27</option> <option value="28">28</option> <option value="29">29</option> <option value="30">30</option> <option value="31">31</option> </select> </div> <div class="hoges__new-form-btn"> <input type="submit" name="commit" value="登録する" data-disable-with="登録する"></div> </form> </div> </div>
formの種類
simple_formの便利なところでカラムの型に応じてformの種類の自動で判断し生成してくれます。
- string型: テキストフィールド
- integer型: 数値フィールド
- date型: 年月日のセレクトボックス
- boolean型: チェックボックス
# テキストフィールドの表示 = f.input :name # ラジオボタンの表示 = f.input :inquiry_type, as: :radio_buttons # セレクトボタンの表示 class User GENDERS = %w(男性 女性 性別無回答).freeze end = f.input :gender, collection: User::GENDERS, label: false, hint: '感想レビューに表示されます' # hidden項目 = f.input :not_delivery, as: :hidden, input_html: { value: false } # ボタンの表示 = f.submit class: 'btn btn-primary', data: {disable_with: t('text.disable_with')}
便利な機能
# ラベル名の変更(label) = f.input :email, label: false = f.input :email, label: 'メールアドレス' # 未入力時に表示する文字列(placeholder) = f.input :email, placeholder: 'メールアドレス' # classやidを指定する(class, id) = f.input :email, class: 'form-control' # 備考欄を表示する(hint) = f.input :tel, hint: '半角数字、-(ハイフン)' # セレクトボタンで初期選択を空にする = f.input :birthday, start_year: Date.current.year - 100, end_year: Date.current.year - 10, include_blank: true ※初期文字列を表示したい時は文字列を設定して下さい。
日本語化
ラベル名の部分など所々デフォルトだと英語表示されますが、これらは「i18n」というgemを利用すると日本語表示することが可能です。
# config/locales/ja.yml ja: activerecord: attributes: hoge: name: 'お名前' age: 'ご年齢' birthday: '誕生日'
こんな感じでja.yml
ファイルを作成しモデル名とカラム名に紐づく日本語を定義しておくと自動で日本語表示されます。
色々なことが出来るので、これはまた別の記事でまとめたいと思います。
【Rails】バリデーションをまとめてみる
データを登録する際に利用するフォームですが、条件に応じて必須だったり制限したりする仕組みを「バリデーション」といいます。 Ruby on Railsでは以下のような感じで設定することができます。
class Hoge < ApplicationRecord validates :name, presence: true end
# validationがない場合 > hoge = Hoge.new => #<Hoge:0x00007f803eabca78 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> > hoge.save (0.3ms) BEGIN Hoge Create (2.8ms) INSERT INTO "hoges" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2019-09-24 11:19:16.757343"], ["updated_at", "2019-09-24 11:19:16.757343"]] (0.8ms) COMMIT => true # validationがある場合 > hoge = Hoge.new => #<Hoge:0x00007fc554e8fd48 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> [2] pry(main)> hoge.save (0.3ms) BEGIN (0.3ms) ROLLBACK => false [3] pry(main)> hoge.errors.full_messages => ["Name入力してください。"]
このようにvalidationを設定することで、条件が満たされていないとデータが保存されないように出来ます。
今回は入力が必須の設定であるpresence
を使ったので、最後のエラー文に"Name入力してください。"
といったエラーが自動的に設定されます。
これらの設定が色々とありますので、そちらをまとめていきたいと思います!
今回使うテーブルはこちら↓↓
Table "public.hoges" Column | Type | Collation | Nullable | Default -------------+-----------------------------+-----------+----------+----------------------------------- id | bigint | | not null | nextval('hoges_id_seq'::regclass) name | character varying | | not null | ''::character varying age | integer | | not null | 0 gender | character varying | | not null | 'man'::character varying birthday | date | | not null | '1900-01-01'::date email | character varying | | not null | postal_code | character varying | | not null | created_at | timestamp without time zone | | not null | updated_at | timestamp without time zone | | not null | Indexes: "hoges_pkey" PRIMARY KEY, btree (id)
バリデーションが実行されるタイミング
バリデーションが実行されるタイミングは色々とあるのですが、例えばこの二つです!
hoge.save
hoge.valid?
save
はその名の通りデータを保存する際に使い、valid?
は保存せずにvalidationが成功するかどうかを判断する時に使います。
また、save(validate: false)
とするとvalidationしないようにすることも可能です。
他のものは、また別の機会でまとめてみたいと思います。
バリデーションのエラー
バリデーションに失敗すると勝手にエラーに関する情報が保存されます。
> hoge.errors => #<ActiveModel::Errors:0x00007fe8e626a940 @base=#<Hoge:0x00007fe8ca962638 id: nil, name: "ai", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil>, @details={:name=>[{:error=>:too_short, :count=>10}]}, @messages={:name=>["は10文字以上で入力してください。"]}> > hoge.errors.full_messages => ["Nameは10文字以上で入力してください。"]
errors
とすると、モデルの詳細な情報が取得できて、errors.full_messages
とするとエラーメッセージだけ取得できます。
使われ方としては、このfull_messages
を画面側で表示させてエラー内容を表示させたりします。
ここでName
という形で英語で表示されてしまってますが、これもi18n
という仕組みを使うことで日本語表示させることも可能です。
が、ここでは説明しません。別の機会に使い方をまとめたいと思います。
バリデーションの種類
空かどうか(presence)
validates :name, presence: true > hoge = Hoge.new => #<Hoge:0x00007fc556fe6618 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> > hoge.valid? => false
文字数(length)
# 最小の数 validates :name, length: { minimum: 10 } # 最大の数 validates :name, length: { maximum: 20 } # 数の範囲 validates :name, length: { in: 10..20 } > hoge.name = 'ai' => "ai" > hoge.name.length => 2 > hoge.valid? hoge=> false > hoge.errors.full_messages => ["Nameは10文字以上で入力してください。" > hoge.name = 'aiueokakikukekosashisuseso' => "aiueokakikukekosashisuseso" > hoge.name.length => 26 > hoge.valid? => false > hoge.errors.full_messages => ["Nameは20文字以内で入力してください。"]
ユニーク(uniqueness)
# name単体でユニーク validates :name, uniqueness: true # name/emailでユニーク validates :name, uniqueness: { scope: [:email] } > Hoge.all Hoge Load (0.5ms) SELECT "hoges".* FROM "hoges" => [#<Hoge:0x00007fe60d860628 id: 1, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: Tue, 24 Sep 2019 20:19:16 JST +09:00, updated_at: Tue, 24 Sep 2019 20:19:16 JST +09:00>, #<Hoge:0x00007fe60d831c38 id: 2, name: "opiyo", age: 30, gender: "man", birthday: Mon, 01 Jan 1900, created_at: Tue, 24 Sep 2019 20:58:20 JST +09:00, updated_at: Tue, 24 Sep 2019 20:58:20 JST +09:00>] > hogehoge = Hoge.new => #<Hoge:0x00007fe604662938 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> > hogehoge.name = 'opiyo' => "opiyo" > hogehoge.age = 20 => 20 > hogehoge.save (0.3ms) BEGIN Hoge Exists (0.4ms) SELECT 1 AS one FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiyo"], ["LIMIT", 1]] (0.3ms) ROLLBACK => false > hogehoge.errors.full_messages => ["Nameはすでに存在します。"]
数値のみ(numericality)
validates :age, numericality: true > hoge.age = 'opiyo' => "opiyo" > hoge.valid? => false > hoge.errors.full_messages => ["Ageは数値で入力してください。"]
正規表現(format)
validates :postal_code, format: { with: /\A[0-9]{3}-[0-9]{4}\z/ } > hoge.postal_code = '1234-567' => "1234-567" > hoge.valid? => false > hoge.errors.full_messages => ["Postal codeは不正な値です。"]
条件付き(if:、on:)
# カラムの値やメソッドなどで条件を設定する時 validates :name, presence: true, if: Proc.new { age < 20 } # アクションに応じて設定する時 validates :postal_code, format: { with: /\A[0-9]{3}-[0-9]{4}\z/ }, on: update > hoge.age = 18 => 18 > hoge.save (0.3ms) BEGIN (0.3ms) ROLLBACK => false > hoge.errors.full_messages => ["Name入力してください。"] > hoge.age = 30 => 30 > hoge.valid? => true
バリデーションを自分で作る(validate、with_options)
# メソッドを作ってチェックする validate :enter_name_if_minor def enter_name_if_minor if self.age < 20 errors[:base] << '未成年の場合は名前を入力してください!' end end > hoge.age = 18 => 18 [3] pry(main)> hoge.valid? => false [4] pry(main)> hoge.errors.full_messages => ["Age未成年の場合は名前を入力してください!"] # 別のバリデーションを作る(メール送る処理の時だけ使うバリデーション) with_options on: :send_mail do validates :email, presence: true end > hoge.valid?(:send_mail) => false > hoge.errors.full_messages => ["Email入力してください。"] > hoge.valid? => true # saveを使うときは`context`を使う > hoge.save(context: :send_mail)
その他
その他にも複雑なvalidationが必要な場合に色々なgemがあったりします。
- phonelib(電話番号のvalidation)
- email_validator(メールアドレスのvalidation)
上記で紹介した正規表現(:format)を使えば実現できるのですが、電話番号やメールアドレスは結構考えることが多く大変です。 こういう場合は、だいたいgemがあるのでそれを使えばokだと思います!
Rails最高ですね。
【Rails】調整さんのようなカレンダーを表示する
調整さんのようなカレンダーを作ると表示する内容が固定されていればtableタグなどで出来るが、 DBに保存された内容だとどうhtmlを組めば分からない。
調整さんってご存知ですか?
ちょっとした打合せとかする時に皆の予定を確認するのにめちゃくちゃ便利な調整さん。
これと似たような画面をrailsでどうやって作るのかな〜と思い試してみました。
こちらが完成イメージです。それではカレンダーを表示してみましょう!
この記事を読めば以下2つが分かるようになります。
調整さんを実現するモデル
キャラクターモデル
# character.rb class Character < ApplicationRecord extend Enumerize has_many :deathblows has_many :character_magics, dependent: :destroy has_many :magics, through: :character_magics accepts_nested_attributes_for :deathblows, allow_destroy: true enumerize :name, in: [:terra, :celes, :locke, :sabin] enumerize :sex, in: [:male, :female] validates :name, presence: true validates :sex, presence: true def build_default_deathblows (self.deathblows.count..7).each do self.deathblows.build end end end
魔法モデル
# magic.rb class Magic < ApplicationRecord has_many :character_magics, dependent: :destroy has_many :characters, through: :character_magics validates :name, presence: true end
キャラクター魔法モデル
# character_magic.rb class CharacterMagic < ApplicationRecord belongs_to :character belongs_to :magic end
調整さんのようなカレンダー表示
ルーティングや、コントラーラーは今回割愛しますが、画面の作り方になります!
view
イメージとしてはキャラクターごとに1行にまとめ、それを横に並べていく感じです。
# index.haml %h1 調整さんのようなカレンダー .tyousei__calendars %ul.tyousei__calendars-body %li.tyousei__calendars-item %p - - @magics.each do |magic| %p = magic.name - @characters.each do |character| %li.tyousei__calendars-item %p = character.name_text - @magics.each do |magic| %p - if character.character_magics.find_by(magic_id: magic.id).present? ●
css
横並びはdisplay: flex
を使えばもう簡単にできますね。
後は、文字に応じて高さや幅が変わってしまうので一つ一つのセルに指定してやればokです!
.tyousei__calendars-body display: flex .tyousei__calendars list-style: none .tyousei__calendars-item list-style: none width: 100px text-align: center &:first-child font-weight: 700 p height: 30px &:first-child font-weight: 700
そして、完成したカレンダーがこちら!
【Rails】DBに保存する前のデータを削除する方法は`delete`
オブジェクトをnewしたけど、条件に応じて内容が空になることがわかった。
この状態でsaveしちゃうとvalidationに引っかかるしオブジェクトを削除したい。
が、DB保存前なのでActiveRecordで削除する感じで
delete
はできないしどうすれば...
そんな疑問に今日は応えていきます!
今日のお題はこちら
- DB保存前のオブジェクトを削除する方法
今日親に紐づく子のデータを複数同時に保存する処理を作っていたんですが、 formが空の場合カラムにnilがせっとされたまま送られてくるのでvalidationに引っかかるし不要なデータなので削除した。
だけど、DBに保存前だしこれどうやって削除するんだ?ってなって調べてみて解決したので、それを共有できればと思います。
モデルの構成
親モデル: character(キャラクター) 子モデル: deathblow(必殺技)
ER図
# character.rb class Character < ApplicationRecord extend Enumerize has_many :deathblows accepts_nested_attributes_for :deathblows, allow_destroy: true enumerize :sex, in: [:male, :female] validates :name, presence: true validates :sex, presence: true end
# deathblow.rb class Deathblow < ApplicationRecord belongs_to :character validates :name, presence: true validates :acquisition_level, presence: true end
DB保存前のオブジェクトを削除する方法
結論としては、親モデル.子モデル達.delete(削除する子モデル)
でokです!
つまり
character.deathblows(deathblow)
では実際に画面のキャプチャー/コード/ログを見ながら解説して行きます。
必殺技登録: edit
マッシュのレベルが6になりましたので、魔列車で大活躍の「オーラキャノン」を習得させてあげましょう!w
# characters_controller.rb class CharactersController < ApplicationController def edit @character = Character.find params[:id] @character.build_default_deathblows end def update @character = Character.find params[:id] @character.update(character_params) end private def character_params params.require(:character).permit(:name, :sex, deathblows_attributes: [:name, :acquisition_level]) end end
# character.rb class Character < ApplicationRecord def build_default_deathblows (self.deathblows.count..7).each do self.deathblows.build end end end
# edit.haml %h1 キャラクターの登録 = simple_form_for(@character, url: characters_path) do |f| = f.input :name = f.input :sex, collections: Character.sex.values.delete_if {|v| v == 'male'} = f.fields_for :deathblows do |cf| = cf.input :name = cf.input :acquisition_level = f.submit
必殺技登録: update
マッシュは、まだレベル6なので「オーラキャノン」までしか覚えられません。
だけど、「むげんとうぶ」まで覚える事を考慮してform自体は最大の8つまで準備しているせいで 無駄なデータが送られきてしまいます。
ここで、今日の本題。こいつらも一緒にsaveしちゃうとvalidationに引っかかり弾かれちゃいます。
なので、データを削除します!
def update @character = Character.find params[:id] @character.attributes = character_params @character.build_default_deathblows.each do |deathblow| @character.build_default_deathblows.delete(deathblow) unless deathblow.valid? end @character.update(character_params) end
attributes
でデータを親モデルにセット- 子モデルをeachで1つ1つデータをチェックする
valid
してvalidationが問題ないかチェック- エラーならdeleteする
こんな感じです。
まとめ
本来ならば、動的にformが追加できるようにするべきです。 削除しまっているので、validationで引っかかった場合は、editに戻りますがbuildする処理を通らないので formが消えっぱなしになりますので。
では最後に、もう一度おさらいです。
# 親モデル.子モデル達.delete(子モデル) ↓ @character.build_default_deathblows.delete(deathblow)
ps.
やってて気づきましたが、deleteする処理はmodelでbefore_save
使えばコントローラー側は2行で済むかもですね。
def update @character = Character.find params[:id] @character.update(character_params) end
【Rails】enumerizeで定義したフォームのセレクトタグをアクション別に変える
今日やりたかったのは、新規作成時と編集時で同一カラムなんだけどセレクトタグに表示する内容を変えたいってのがあった。
編集の場合は色々な条件があって変更されると困るってのがあったので、悩んでたんだけど結構簡単に出来たのでその方法を紹介します。
新規作成時のview
# new.haml %h1 キャラクターの登録 = simple_form_for(@character, url: characters_path) do |f| = f.input :name = f.input :sex
編集時のview
第二引数にあたる、collections
にセレクトタグに表示する内容を設定することができます。
# edit.haml %h1 キャラクターの登録 = simple_form_for(@character, url: characters_path) do |f| = f.input :name = f.input :sex, collections: Character.sex.values.delete_if {|v| v == 'male'}
コンソールで実行するとこんな感じで、条件に応じて合致したものを削除してくれるってのがdelete_if
になります。
Character.sex.values => ["male", "female"]
Character.sex.values.delete_if {|v| v == 'male'} => ["female"]
これだけだなのですが、環境周りとかも紹介させてもらえればと思います。
環境/使い方紹介
Gemfileの設定
先ずはenumが使えるように、gem enumrize
を設定します。
# Gemfile gem 'enumerize'
modelの設定
次は対象のカラムに対してenumerizeの設定をします。
# character.rb class Character < ApplicationRecord extend Enumerize enumerize :sex, in: [:male, :female] validates :name, presence: true validates :sex, presence: true end
viewの設定
formの生成はsimple_form
を使います。こちらの使い方はまた別の機会に。
enumを設定をしたのが:sex
ですが、この記述だけで勝手にセレクトタグを生成してくれます!クソ楽チン。
# new.haml %h1 キャラクターの登録 = simple_form_for(@character, url: characters_path) do |f| = f.input :name = f.input :sex
日本語化の対応
ymlにenumerizeを定義してあげて、対象モデル名とカラム名に対して日本語を設定してやれば表示上は日本語で、データ保存時は英語でってのが簡単に実現できる
# ja.yml ja: enumerize: character: sex: male: '男性' female: '女性'
【Rails】営業日を考量して日付を操るGem business_time
回答期限に応じて何かを処理するって時に営業日を考慮したい時があり色々調べているとスンバラシイですね。
business_time
っていうgemがあったので簡単な使い方を紹介します!
導入方法
Gemfile
を設定してbundle install
します。
# Gemfile gem 'business_time'
使い方
bundle exec rails c
で色々検証してみました。
日付の操作
現在日付
Time.current.to_s => "2019-07-09 21:48:18 +0900"
1日後
1.business_day.from_now.to_s => "2019-07-10 21:48:23 +0900"
1日前
1.business_day.ago.to_s => "2019-07-08 21:55:19 +0900"
1時間前
1.business_hour.ago.to_s => "2019-07-09 20:54:58 +0900"
営業日チェック
今日が営業日か
Date.today.to_s => "2019-07-09"
Date.today.workday? => true
7/9が営業日か
Date.parse("2019-07-09").workday? => true
7/13(土)が営業日か
Date.parse("2019-07-13").workday? => false
自分の誕生日が休日か?
my_birthday = Date.new(2019, 8, 4) => Sun, 04 Aug 2019
my_birthday.to_s => "2019-08-04"
my_birthday.workday? => false
注意点
Timezoneはきちんと設定しましょう!
TimeZoneはきちんとtokyoにしておかないと計算がおかしくなるので、注意です!
# confing/application.rb class Application < Rails::Application config.time_zone = 'Asia/Tokyo' end
きちんと設定されていれば、今の日付が表示されると思います。
Time.current.to_s => "2019-07-09 21:54:34 +0900"
1.business_hour.from_now.to_s => "2019-07-10 10:53:59 +0900"
が、この設定がないとおかしくなります。
Time.current.to_s => "2019-07-09 13:08:15 UTC"
1.business_hour.from_now.to_s => "2019-07-09 14:08:19 UTC"
営業時間の判定もされる
普通にやってると2日後とかになってあれっなると思います。
Time.current.to_s => "2019-07-09 22:09:30 +0900"
1.business_hour.from_now.to_s => "2019-07-10 11:00:00 +0900"
この場合は、1時間後が表示されるはずです。つまり2019-07-09 23:09:30 +0900
ですね。
が、このgemは営業時間を持っているのでその時間も考慮して計算しているので気をつけましょう。
こちらの設定については、こちらのコマンドを実行することでymlが作られそこに定義されています。
$ rails generate business_time:config
business_time: beginning_of_workday: 10:00 am end_of_workday: 10:00 pm holidays: - Jan 01, 2010 - July 4th, 2010 - December 25th, 2010 work_week: - mon - tue - wed - thu - fri
- beginning_of_workday: 開始時間
- end_of_workday: 終了時間
【Rails】データが1件でもあるかどうかチェックするには`Model.exists?`
Ruby on Railsでデータベースにデータが存在するかどうかチェックしたい場合があると思います。
- そもそもデータがあるかどうか知りたい時
- ログインしたユーザーが管理者ユーザーがどうかをチェックしたい
そんな時はModel.exists?()
を使いましょうって話です。
データが存在するかチェックする
> User.exists? User Exists (3.0ms) SELECT 1 AS one FROM "users" LIMIT $1 [["LIMIT", 1]] => true
Userモデルにデータが1件でも存在するかのチェックです。
条件付きで存在をチェックする
> User.exists?(id: [1, 2, 3], name: 'opiyo', role: 'admin') User Exists (0.6ms) SELECT 1 AS one FROM "users" WHERE "users"."id" IN ($1, $2, $3) AND "users"."name" = $4 AND "users"."role" = $5 LIMIT $6 [["id", 1], ["id", 2], ["id", 3], ["name", "opiyo"], ["role", "admin"], ["LIMIT", 1]] => false
Userモデルから管理者ユーザーを探すような条件を渡してます。条件は何個でもつけれますし、配列も渡せちゃいます。
find_byも同じ使い方出来るじゃね?
データを1件だけ取得するのに便利なのがModel.find_by()
だと思うのですが、exists?
と同じような使い方ができますね。
> User.find_by(id: [1, 2, 3], name: 'opiyo', role: 'admin') User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2, $3) AND "users"."name" = $4 AND "users"."role" = $5 LIMIT $6 [["id", 1], ["id", 2], ["id", 3], ["name", "opiyo"], ["role", "admin"], ["LIMIT", 1]] => nil
さっきのと全く同じ条件ですが戻り値が違いますね。今回はnil
です。
取得できた場合はデータが1件取れますね。
> User.find_by(name: 'opiyo', role: 'admin') User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."name" = $1 AND "users"."role" = $2 LIMIT $3 [["name", "opiyo"], ["role", "admin"], ["LIMIT", 1]] => #<User id: 175, created_at: "2018-05-28 02:59:58", updated_at: "2019-06-18 05:42:19", name: "opiyo", role: "admin">
データも欲しいけど存在チェックもしたいっていう贅沢な事をする場合は、こんな書き方で叶えることが出来るので便利!
> if user = User.find_by(name: 'opiyo', role: 'admin') * puts user.name * puts user.role * end User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."name" = $1 AND "users"."role" = $2 LIMIT $3 [["name", "opiyo"], ["role", "admin"], ["LIMIT", 1]] opiyo admin
取得に失敗した場合はuser = nil
になりますが、nil
はfalse
なのでif文に入りません。
> if user = User.find_by(name: 'opiyoopiyo', role: 'admin') * puts user.name * puts user.role * end User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."name" = $1 AND "users"."role" = $2 LIMIT $3 [["name", "opiyoopiyo"], ["role", "admin"], ["LIMIT", 1]] => nil
さっき出力された、opiyo
とadmin
が表示されませんね。
参考サイト
rake db:migrationがどこまで実行されてるのか確認したり戻したり
あるデータベースのカラムを追加したくと色々やっていたら、今どんな状況なのか良く分からなくなったので整理する。
migration実行状況確認
$ rake db:migrate:status Running via Spring preloader in process 1796 database: hogehoge Status Migration ID Migration Name -------------------------------------------------- up 20170509072520 Create events up 20170510082334 Add details to event # Statusがup = 実行済みの状態
migrationの実行を元に戻す
$ rake db:rollback Running via Spring preloader in process 1817 == 20170510082334 AddDetailsToEvent: reverting ================================ == 20170510082334 AddDetailsToEvent: reverted (0.0399s) ======================= $ rake db:migrate:status Running via Spring preloader in process 1832 database: hogehoge Status Migration ID Migration Name -------------------------------------------------- up 20170509072520 Create events down 20170510082334 Add details to event # Statusがdownになった
migrationファイルを削除する
$ rake db:migrate:status Running via Spring preloader in process 1852 database: hogehoge Status Migration ID Migration Name -------------------------------------------------- up 20170509072520 Create events # Add details to eventの行がなくなった
Googleフォームで回答数と回答された合算を表示する方法
前回も似たような話題を取り上げたのですが、Googleフォームを使って回答数を表示させる方法です。
ざっくり手順紹介
- 数値を入れる項目を作成
- スクリプトエディタを起動する
- 入力された数をカウントして説明欄に表示するスクリプト作成
- トリガーを設定
スクリプト部分はこんな感じですね。
form.setDescription('申し込んだ子どもの数: ' + sum_child_count + '人');
本当にコレだけなんですが、これじゃ分からないのでもう少しだけ細かく。
回答された数を表示する
単純に回答された数を表示するだけであれば、非常に簡単です。
function resultCount() { resultCount = 0; var form = FormApp.getActiveForm(); //アクティブフォームを取得 resultCount = form.getResponses().length; // 全回答内容を取得 // 説明欄に申し込み済み子供の数を表示する form.setDescription('申込まれた数: ' + resultCount + '人'); }
これで実行すると
めちゃくちゃ簡単ですね。
アンケート項目の数値を合算した数を表示する
今回僕がやってたのが、まさにこれなんですが少し複雑になります。
ですが、やってることは上と同じです。
function myFunction() { //子どもの数上限を設定 var LIMIT_COUNT = 4; sumChildCount = 0; var form = FormApp.getActiveForm(); //アクティブフォームを取得 var formResponses = form.getResponses(); // 全回答内容を取得 for (var i = 0; i < formResponses.length; i++) { var formResponse = formResponses[i]; // 回答ひとつ分を取得 var itemResponses = formResponse.getItemResponses(); // 質問項目を取得 for (var j = 0; j < itemResponses.length; j++) { // 回答内容をひとつずつチェック var itemResponse = itemResponses[j]; var question = itemResponse.getItem().getTitle(); var answer = itemResponse.getResponse(); // 申込み数カウント if( question == '子どもの数' ){ sumChildCount += Number(answer); } } } // 説明欄に申し込み済み子供の数を表示する form.setDescription('申し込んだ子どもの数: ' + sumChildCount + '人'); }
ざっと説明すると、さっきのと違うのが回答数ではなくて子供の数はに答えてくれた総数を表示している点ですね。
なので、回答された子供の数を見つけ出して変数に合算して行ってる感じです。
for (var i = 0; i < formResponses.length; i++) {
全ての回答内容を先ずは取得して、for文でグルグル回します
for (var j = 0; j < itemResponses.length; j++) { // 回答内容をひとつずつチェック
1件の回答に対する全ての項目を更にグルグル回します
var question = itemResponse.getItem().getTitle();
項目のタイトルを取得します
var answer = itemResponse.getResponse();
回答された数を取得します
if( question == '子どもの数' ){ sumChildCount += Number(answer); }
さっき取得したタイトルを使って条件文を作り、そこに回答された数を合算していく処理です。
あとは、説明欄に合算した値を渡してあげれば出来上がりーです。
まとめ
結構色々できるなーってのが正直、面白いですね。
だけど意外と情報が少ないので、ハマると結構苦労します。
今ハマっているがTwitterでも呟いちゃったんですが
Googleフォームのスクリプトで条件に当てはまらなかったら再入力促すようにしたいんだけど、どうすんだろう。
— 中野 拓 (@opiyo_taku) 2019年6月14日
- 人数を入れる項目がある
- 10人超えたらエラーにして最大数超えたってメッセージ出す
現在8人の状態で3人だったらエラーみたいな。
単純な回答数ならばアドオン「formLimiter」とかで対処できそうなのですが、今回みたいにGAS側で数を計算してその結果から判断するみたいなことができないのかな〜と。
知っている方がいれば是非!教えていただけると嬉しいです。
ということで話が少し脱線しましたが、以上でございます。