ブラウザから返ってくるenumの値はシンボルじゃなくて文字列だった

こんにちは!kossyです!




さて、今回はenumで定義した値をブラウザからパラメータとして受け取る場合、シンボルの形式ではなく文字列で
返ってくるのに気づかずハマったので、備忘録としてブログに残してみたいと思います。





enumのステータスによって処理を分岐させたいというシチュエーションでハマりました、、、

コードは例としてこんなんです。

  enum status: { draft: 1, published: 2 }

hogehoge_controller.rb

if params[:status] == :draft
~~
else
~~
end


ここでbinding.pryで処理を止めてparamsをみます。
(省略しまくってすみません。エスパーしてください笑)

コンソール

[1] pry(#<DraftsController>)> params                                                                                                                                     
=> <ActionController::Parameters {"utf8"=>"", "authenticity_token"=>"Rl3FrY+0vYyOoCFv2mRhKYs2Gc3ptfCfMAicrzwECs0YpW8ZHOTGWQ6hHVjepT3K1fADkCZ/7nYHmEp6YusqFQ==", "article"=><ActionController::Parameters {"title"=>"test", "description"=>"test", "body"=>"test", "category_id"=>"1", "status"=>"draft"} permitted: false>, "commit"=>"SAVE", "controller"=>"drafts", "action"=>"create"} permitted: false>

statusのハッシュの値が"draft"と文字列で来ています。
文字列とシンボルを比較すれば当たり前ですがfalseが返ります。これで悶々としてました。

解決策は、

if params[:status].to_sym == :draft

とすればtrueを返すことができます。

コンソールで試してみましょう。

[1] pry(main)> string = "string"                                                                                                                                         
=> "string"
[2] pry(main)> symbol = :string                                                                                                                                          
=> :string
[3] pry(main)> string == symbol                                                                                                                                          
=> false
[4] pry(main)> _string = string.to_sym                                                                                                                                   
=> :string
[5] pry(main)> _string == symbol                                                                                                                                         
=> true

こんな感じですね。


ブラウザから送られてくるenumのパラメーターは文字列型で返ると覚えましょう。(自戒を込めて)

特定のテストケースを実行したい時のfocus: true

こんにちは!kossyです!




さて、今回は、特定のテストケースを実行したい時に便利な、
focus: trueオプションの使い方について、ブログに残してみたいと思います。




環境
Rails 5.1.6
Ruby 2.5.1
rspec 3.8.0
MacOS Mojave




まずはspec_helper.rbに設定

以下の記述を追記します。

/spec/spec_helper.rb

RSpec.configure do |config|

省略

  config.filter_run :focus

end


これでfocus: trueを使えるようになります。




後は実行したいブロックに記述するだけ


例えばこんなテストがあったとします。

  describe 'Validation of create User' do
    describe 'blank' do
      it '名前が空白だとエラーになる' do
        user = User.new(name: '', email: 'example@gmail.com', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:name]).to include('を入力してください')
      end
      it 'メールアドレスが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:email]).to include('を入力してください')
      end
      it 'パスワードが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '')
        user.valid?
        expect(user.errors[:password]).to include('を入力してください')
      end
    end

では、一つ目のitブロックにfocus: trueを設定します。すると、

$ bundle exec rspec spec/models/user_spec.rb


User
  Validation of create User
    blank
      名前が空白だとエラーになる

Finished in 0.24428 seconds (files took 10.93 seconds to load)
1 example, 0 failures


一つ目のテストブロックのテストのみ実行されます。


describeのブロックにも設定することができます。

$ bundle exec rspec spec/models/user_spec.rb



  describe 'Validation of create User', focus: true do
    describe 'blank' do
      it '名前が空白だとエラーになる' do
        user = User.new(name: '', email: 'example@gmail.com', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:name]).to include('を入力してください')
      end
      it 'メールアドレスが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:email]).to include('を入力してください')
      end
      it 'パスワードが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '')
        user.valid?
        expect(user.errors[:password]).to include('を入力してください')
      end
    end


以下はログ
User
  Validation of create User
    blank
      名前が空白だとエラーになる
      メールアドレスが空白だとエラーになる
      パスワードが空白だとエラーになる

Finished in 0.15704 seconds (files took 8.86 seconds to load)
3 examples, 0 failures

Validation of create Userのdescribeブロックのテストが全て実行されているのがわかります。




テストケースが増えてくると実行して終了するまで待つのが
とても面倒になってきます。

focus: trueはそんなシチュエーションで活躍間違いなしのオプションです。




参考にさせていただいた記事
RSpecで特定のテストケースのみを実行する方法 - TIM Labs
今日から使える! RSpec でテストの実行サンプル it を素早く絞り込み・スキップする方法 - Qiita

Rail5.2系でreferencesカラムを設定しようとした時のエラー

こんにちは!kossyです!





さて、今回は、Rails5.2系で外部キーを設定しようとした時に遭遇したエラーについて、
ブログに残してみたいと思います。



環境
Rails 5.2.2
Ruby 2.5.1






エラーの状況

2019****_create_category.rb

class CreateCategories < ActiveRecord::Migration[5.2]
  def change
    create_table :categories do |t|
      t.string :name, null: false
      t.text :content, null: false
      t.string :image, null: false

      t.timestamps
    end
  end
end


2019****_create_article.rb

class CreateArticles < ActiveRecord::Migration[5.2]
  def change
    create_table :articles do |t|
      t.string :title, null: false, index: true
      t.string :image
      t.text :description, null: false
      t.text :body, null: false
      t.integer :status, null: false
      t.references :user_id, foreign_key: true
      t.references :category_id, foreign_key: true

      t.timestamps
    end
  end
end

この内容でrails db:migrateを実行してみると、
以下のエラーが発生しました。

rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Table 'cms_development.articles' doesn't exist: SHOW FULL FIELDS FROM `articles`

Caused by:
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'cms_development.articles' doesn't exist: SHOW FULL FIELDS FROM `articles`

Caused by:
Mysql2::Error: Table 'cms_development.articles' doesn't exist

Caused by:
Mysql2::Error: Cannot add foreign key constraint




対処(ベストプラクティスかは怪しい)



外部キーを追加できないと怒られていたので、該当部分をコメントアウト

class CreateArticles < ActiveRecord::Migration[5.2]
  def change
    create_table :articles do |t|
      t.string :title, null: false, index: true
      t.string :image
      t.text :description, null: false
      t.text :body, null: false
      t.integer :status, null: false
      # t.references :user_id, foreign_key: true
      # t.references :category_id, foreign_key: true

      t.timestamps
    end
  end
end

そして、rails db:migrateすると、ひとまず成功。

次に、

$ rails g migration AddReferencesToArticle user:references category:references


2019****_add_refereces_to_article.rb

class AddReferenceToArticle < ActiveRecord::Migration[5.2]
  def change
    add_reference :articles, :user, foreign_key: true
    add_reference :articles, :category, foreign_key: true
  end
end

この状態でrails db:migrateしたら、無事にreferenceカラムを追加できました。


これ本番環境でmigrationする時も起きるのでしょうか?
だとすれば、またそのうち対処法をブログに残すことになるかもしれません、、、笑


1/16 追記
t.references :user, foreign_key: true
t.references :category, foreign_key: true
とすればエラーは起きないみたいです、、、
もう同じミスはやらないと心に誓いました。

link_toでページ内ジャンプ機能を設定してみた

こんにちは!kossyです!




さて、Railsのビューヘルパーであるlink_toでページ内リンク機能の設定方法について、
ブログに残してみました。

HTMLではaタグで実現できますが、link_toを使う場合はどのように実装すればいいのか、
少し詰まったので、備忘録として残しておきます。



環境
Rails 5.2.2
Ruby 2.5.1
Haml




anchorオプションで明示的にリンク先を指定する


HTMLではジャンプしたい箇所にidを設定し、aタグのパスにidを指定すれば
ページ内ジャンプ機能を実現できましたが、
link_toの場合はanchorを使えばページ内ジャンプ機能ができます。

app/views/home/index

%header
  = link_to "TOPIC", { anchor: "topic" }



.article#topic
  %h2
    トピック

こんな感じですね。

anchorオプションにidであるtopicを指定しています。

挙動はこんな感じになるかと。
https://gyazo.com/d9a9d15af906e90c9103d45832b27e39




参考にさせていただいたサイト
link_toでページ内リンクとclass指定を共存させる方法 - Qiita

link_toで画像付きのリンクを生成してみた

こんにちは!kossyです!




さて、今回は、Railsのビューヘルパーであるlink_toで、画像付きのリンクを生成する方法を
ブログに残してみたいと思います。




環境
Rails 5.2.2
Ruby 2.5.1
Haml




link_toの引数にimage_tagを使えばOK


同じくRailsのビューヘルパーであるimag_tagを使えば簡単に実現できます。

app/views/samples/index.html.haml


link_to image_tag("画像名", class:"クラス名"), パス


例
link_to image_tag("sample.png", class: "image"), samples_path

これで実現できます。



参考にさせていただいたサイト
https://dotinstall.com/lessons/basic_rails_v3

TECH CAMPの夜間コースを卒業して即戦力エンジニアになれたのか

こんにちは!kossyです!





早いものでWebエンジニアとして働き出してから1ヶ月が経過しました。
スクールで勉強していたRailsと、全く触れたことのなかったAngularを使って、
既存アプリの改修業務を主として行なっています。



さて、私が卒業したプログラミングスクールでは、
「プログラミング未経験者が短期間で即戦力エンジニアへ」
というフレーズが謳い文句になっています。


そこで、実務に携わるようになって1ヶ月経った今、
本当に即戦力エンジニアになれたのか?
について、ブログに残してみたいと思います。









1. 入社して日が浅い段階でタスクを振って頂けた

初日は入社手続きと開発環境の構築に追われ(課題は与えられたが仕様の把握に時間取られる)、
2日目はスプリントレビューだったのでほとんどコードを書かず。
3日目から本格的に開発業務に携わることになりました。


上司に仕様を説明してもらいながら、
まずはコードを記述して、レビューを頂く形のタスクを振ってもらえました。


仕様を把握するのが大変でしたが、サーバーサイドのコードの変更自体は、
見本になるコードがそこかしこに書いてあるので、
なんとか対処できました。(Angularは上司につきっきりで指導してもらいましたが、、、)


「即戦力」の定義によるとは思いますが、
「入社から日が浅い段階でタスクを振られても、ある程度の成果物を提出できる」ことが
即戦力の定義とするならば、該当していると考えてもいいと思っています。









2. 即戦力になれるかどうかは会社で使う言語次第


入社してすぐにタスクを振っていただけたのも、私が経験のある言語と
会社が使っている言語が一致していたから、という理由が非常に大きいと思っています。


私の会社で使う言語は、スクールで学習していたRubyで、
サーバーサイドのフレームワークも同じくRailsです。


自分の経験のある言語と、会社で使用する言語の一致も、
即戦力エンジニアとして活躍するための条件かもしれません。









まとめ

繰り返しますが、即戦力として入社してすぐにチームにジョインしてコードを書くには、
スクールで学んでいた技術と会社で使う技術が一致していないと難しいと思いました。


TECH CAMPの求人はRubyだけでなく、javaPHPpythonなどの言語、
フレームワークcakePHPDjango、Flaskなど、言語・フレームワーク・更にはOSに拘らず紹介されます。
また、フロントエンドエンジニアとしての求人もあったりします。(React, Vue.js, Angular、Nuxt.js等)


全く触れたことのない技術を使う会社で、入社して日が浅い段階で成果を出せと言われても、
正直無理がありますよね、、、笑
算数できないのに数学で満点取って見ろと言われているようなものです、、、


なので、スクールを卒業した全ての方が、
即戦力として活躍するのは難しいのではないかと思いました。


とはいえ、スクールを卒業すればWebアプリ、通信周り、設計の技法の基礎が身につくので、
新しい言語、フレームワークの習得にはさほど時間をかけずに習得できるような素地がある状態で、
業務に入ることができるのではないでしょうか。



TECH CAMPを卒業すれば、フレームワークRailsに限れば、
即戦力エンジニアになれると思います。



ActiveModelを使ってDBと関係ないFormの構築してみた

こんにちは!kossyです!




さて、今回はActiveModelを使ったフォームの構築についてブログに残してみたいと思います。



そもそもActiveModelって??

DBと関係ないFormを構築できるスグレモノです。
Active Recordを使った時と同じ振る舞いを実現できます。

Formに関して責務の分離を行うことができるので、
ばらつきがちなロジックをまとめられたり、検索周りの機能の共通化ができたりします。





実装方法

例として、運営に問い合わせを行うようなフォームを構築してみます。

app/forms/inquiryforms.rb

class InquiryForm
  include ActiveModel::Model
  include ActiveModel::Attributes
  attr_accessor :subject, :content, :accepted

  attribute :accepted, :boolean, default: false

  validates :subject, presence: true, length: { maximum: 100 }
  validates :content, presence: true

  def save
    return false if invalid?
    ActionMailer.inquiry_form(subject, content).deliver_later
    true
  end

end




app/controllers/inquiries_controller.rb

class InquiriesController < ApplicationController

  def new
    @inquiry_form = InquiryForm.new
  end

  def create
    @inquiry_form = InquiryForm.new(inquiry_form_params)
    if @inquiry_form.save
      redirect_to home_path, notice: '問い合わせを送信しました。'
    else
      render :new
    end
  end

  private

  def inquiry_form_params
    params.require(:inquiry_form).permit(:subject, :content, :accepted)
  end

end

include ActiveModel::ModelでActiveModelの機能を利用できるようになります。
attr_accessorで使用する属性を定義します。

include ActiveModel::Attributesは属性のキャストを簡単に行えるようになる機能です。
acceptedはrailsのビューヘルパーであるcheck_boxを介して値が送られてくることを想定している(Stringでくる)ため、
booleanにキャストしたいので定義しています。

saveメソッドはバリデーションに引っかかっていないか検証し、値が正しければメールを送信しています。


応用すれば商品検索フォームや投稿フォームにも使えそうです。



参考にさせていただいたサイト
ActiveModel::Modelで簡単に検索機能追加 - Qiita
ActiveModelのベストプラクティスを考える - Lチカ開発ブログ
form objectを使ってみよう - メドピア開発者ブログ
ActiveModel::Attributes が最高すぎるんだよな。 - Qiita
ActiveModel::Model で簡単に ActiveModel の機能を利用する - happy lie, happy life

sprintfでランダムな4桁の数字を生成してみた

こんにちは!kossyです!




さて、今回はrubyのメソッドであるsprintfを使って、ランダムな4桁の数字の生成方法を
ブログに残してみたいと思います。




sprintfメソッドって?
docs.ruby-lang.org
引数にフォーマットと文字列や数値を指定すると、
フォーマットで指定した返り値を作成できるメソッド、と言う感じですかね。
詳しくはドキュメントをみてください()


ランダムな数値を生成してみよう

これで生成できます。

[1] pry(main)> sprintf("%.4d", rand(10000))
=> "9347"
[2] pry(main)> sprintf("%.4d", rand(10000))
=> "4849"
[3] pry(main)> sprintf("%.4d", rand(10000))
=> "3760"
[4] pry(main)> sprintf("%.4d", rand(10000))
=> "1695"
[5] pry(main)> sprintf("%.4d", rand(10000))
=> "0318"


使うシチュエーションがパッと思いつきませんが、、、
Railsでランダムなパスワードとか生成したい時に応用できそうな気もします。

RailsAPIモードを使っているときにrequest_specでログイン・ログアウトを実装

こんにちは!kossyです!



さて、今回はRailsAPIモードで認証機能周りのrequest_specでログイン・ログアウト状態を再現したい時の方法を
ブログに残してみたいと思います。




環境
Rails 5.1.6
Ruby 2.5.1
rspec 3.8.0
devise_token_auth 1.1.3




support直下にmoduleを定義したファイルを作成し、rails_helper.rbに読み込ませるだけ



supports直下にファイルを作成します。

spec/supports/authentication_helper.rb(名前は任意)

module AuthenticaitionHelper
  HTTP_HELPERS_TO_OVERRIDE = [:get, :post, :patch, :put, :delete]

  # ログイン状態にする
  def login(user)
    @user = user
    @auth_token = @user.create_new_auth_token # devise_token_authのメソッド 参考: https://www.rubydoc.info/gems/devise_token_auth/0.1.37/DeviseTokenAuth%2FConcerns%2FUser:create_new_auth_token
  end

  # ログアウトする
  def logout
    @user = nil
    @auth_token = nil
  end

  # 参考: https://gist.github.com/blaze182/3a59a6af8c6a7aaff7bf5f8078a5f2b6
  HTTP_HELPERS_TO_OVERRIDE.each do |helper| # 定数に対して繰り返し処理を実行
    define_method(helper) do |path, **args| # def get(path) のようなメソッドを動的に定義 
      add_auth_headers(args) # privateメソッドの呼び出し
      args == {} ? super(path) : super(path, **args) # argumentsが空ハッシュなら定義元のメソッドを実行、空でなければ定義元のメソッドをargumentsを引き連れて実行
    end
  end

  private
   # tokenをセットするprivateメソッド
    def add_auth_headers(args)
      return unless defined? @auth_token # @auth_tokenが定義されていなければ処理終了 https://www.xmisao.com/2014/03/30/checking-if-a-variable-is-defined.html
      args[:headers] ||= {} # args[:headers]がnilなら{}を代入
      args[:headers].merge!(@auth_token) # args[:headers]に@auth_tokenを代入
    end

end

作ったモジュールをrails_helper.rbで読み込む記述を追加します。

RSpec.configure do |config|

  省略

  Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } #support directoryをrequire
  config.include AuthenticationHelper, type: :request #type: :requestのときにRequestHelperをinclude
end


これで例えばFactoryBot等でuserを定義している場合、

user = FactoryBot.create :user
login user

等でログイン状態を再現できます。





参考にさせていただいた記事
type: :requestのテストでsign_in/sign_outする - Qiita

Railsのgem 'active_hash'で都道府県データを作成してみた

こんにちは!kossyです!





さて、今回はRailsでモデルに都道府県データを持たせずに、
active_hashで持たせる方法について、ブログに残してみたいと思います。




環境
Rails 5.2.3
Ruby 2.6.3
MacOS Mojave
active_hash 2.2.0

事前準備

例によってプロジェクトを新規作成して試してみます。

$ rails new active_hash_test

$ cd active_hash_test

次にactive_hashを導入します。

./Gemfile

gem 'active_hash'

で、bundle install。

$ bundle install



次にモデルを作成します。

$ rails g model Address prefecture_id:integer city:string

Running via Spring preloader in process 21996
      invoke  active_record
      create    db/migrate/20190108110055_create_addresses.rb
      create    app/models/address.rb
      invoke    test_unit
      create      test/models/address_test.rb
      create      test/fixtures/addresses.yml

そのままrails db:migrateします。

$ rails db:create
$ rails db:migrate

次はモデル周りを記述します。





Addressモデルの編集とPrefectureモデルの作成

まず、Prefectureモデルを作成してみます。
モデルの作成、と言うと、rails g modelしたくなりますが、
今回はActiveHash::Baseを継承したPrefectureモデルを自作します。
ActiveHash::Baseを継承したモデルを作成すると、ActiveRecordのメソッド(allとか)が使えるようになります。
他にどんなメソッドが使えるようになるか気になる人は、下の手順でPrefectureモデルを定義して、コンソールで、

$ Prefecture.methods

と入力してみてください。




以下実装です。

app/models/prefecture.rb

class Prefecture < ActiveHash::Base
  self.data = [
      {id: 1, name: '北海道'}, {id: 2, name: '青森県'}, {id: 3, name: '岩手県'},
      {id: 4, name: '宮城県'}, {id: 5, name: '秋田県'}, {id: 6, name: '山形県'},
      {id: 7, name: '福島県'}, {id: 8, name: '茨城県'}, {id: 9, name: '栃木県'},
      {id: 10, name: '群馬県'}, {id: 11, name: '埼玉県'}, {id: 12, name: '千葉県'},
      {id: 13, name: '東京都'}, {id: 14, name: '神奈川県'}, {id: 15, name: '新潟県'},
      {id: 16, name: '富山県'}, {id: 17, name: '石川県'}, {id: 18, name: '福井県'},
      {id: 19, name: '山梨県'}, {id: 20, name: '長野県'}, {id: 21, name: '岐阜県'},
      {id: 22, name: '静岡県'}, {id: 23, name: '愛知県'}, {id: 24, name: '三重県'},
      {id: 25, name: '滋賀県'}, {id: 26, name: '京都府'}, {id: 27, name: '大阪府'},
      {id: 28, name: '兵庫県'}, {id: 29, name: '奈良県'}, {id: 30, name: '和歌山県'},
      {id: 31, name: '鳥取県'}, {id: 32, name: '島根県'}, {id: 33, name: '岡山県'},
      {id: 34, name: '広島県'}, {id: 35, name: '山口県'}, {id: 36, name: '徳島県'},
      {id: 37, name: '香川県'}, {id: 38, name: '愛媛県'}, {id: 39, name: '高知県'},
      {id: 40, name: '福岡県'}, {id: 41, name: '佐賀県'}, {id: 42, name: '長崎県'},
      {id: 43, name: '熊本県'}, {id: 44, name: '大分県'}, {id: 45, name: '宮崎県'},
      {id: 46, name: '鹿児島県'}, {id: 47, name: '沖縄県'}
  ]
end


app/models/address.rb

class Address < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :prefecture
end

belongs_to_active_hashメソッドでアソシエーションも組めるってわけです。
extend ActiveHash::Associations::ActiveRecordExtensionsしないとprefectureモデルを使えません。
詳しく知りたい方はGemの中を覗いてみてください。
参考: active_hash/associations.rb at master · zilkey/active_hash · GitHub




これで準備完了です。
では、コンソールで試してみます。

$ rails c

# 以下はログです

[1] pry(main)> @address_1 = Address.create(prefecture_id: 1, city: '函館市')                                                                                       
   (0.2ms)  SAVEPOINT active_record_1
  Address Create (41.0ms)  INSERT INTO `addresses` (`prefecture_id`, `city`, `created_at`, `updated_at`) VALUES (1, '函館市', '2019-01-08 11:41:41', '2019-01-08 11:41:41')
   (0.3ms)  RELEASE SAVEPOINT active_record_1
=> #<Address:0x00007fc9f8cbf420
 id: 1,
 prefecture_id: 1,
 city: "函館市",
 created_at: Tue, 08 Jan 2019 11:41:41 UTC +00:00,
 updated_at: Tue, 08 Jan 2019 11:41:41 UTC +00:00>
[2] pry(main)> @address_2 = Address.create(prefecture_id: 13, city: '新宿区新宿')                                                                                        
   (0.3ms)  SAVEPOINT active_record_1
  Address Create (0.3ms)  INSERT INTO `addresses` (`prefecture_id`, `city`, `created_at`, `updated_at`) VALUES (13, '新宿区新宿', '2019-01-08 11:42:05', '2019-01-08 11:42:05')
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> #<Address:0x00007fc9f8a7cb40
 id: 2,
 prefecture_id: 13,
 city: "新宿区新宿",
 created_at: Tue, 08 Jan 2019 11:42:05 UTC +00:00,
 updated_at: Tue, 08 Jan 2019 11:42:05 UTC +00:00>
[3] pry(main)> @address_1.prefecture.name                                                                                                                                
=> "北海道"
[4] pry(main)> @address_2.prefecture.name                                                                                                                                
=> "東京都"

って感じで、prefecuture_idにPrefectureモデルに定義したハッシュのidを渡して、@address.prefecture.nameで名前を取れちゃいます。
もちろんPrefecture.find でハッシュを取ってこれます。

都道府県を選択するセレクトボックスを生成したい時は、

# hamlで書いてます

= f.collection_select :prefecture_id, Prefecture.all, :id, :name

って感じで、
f:id:kossy-web-engineer:20190307221245p:plain
こんな感じで設定できます。

検索機能も、詳細はこちらの記事に譲るとして、
Rails初心者が「form_with」を使って検索機能を実装してみた。 - Qiita
form_with でシンプルなサーチフォームを作成する | deadwood


f:id:kossy-web-engineer:20190512134125p:plain
こんな感じのチェックボックス式の検索フォームがあったとして、

view

<%= f.collection_check_boxes :prefecture_id, Prefecture.all, :id, :name %>

controller

@results = Address.where('prefecture_id IN(?)', params[:prefecture_id])

IN検索機能を使って、受け取ったprefecture_idを持つAddressテーブルのレコードを取得しています。



蛇足ですが、delegateを使えば@address.nameでも県名が取れたりしちゃいます。
Active Support コア拡張機能 - Railsガイド

app/models/address.rb

class Address < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :prefecture
  delegate :name, to: :prefecture
end

ログ

@address = Address.create(prefecture_id: 1, city: '猿払村') 

 @address.name                                                                                                                                             
=> "北海道"


あとは、app/models/address.rbに、

def full_address
  "#{name}#{city}"      #delegateしているのでnameだけで都道府県名が取れる
end

メソッドを定義すると、
@address.full_addressで、
都道府県名と市町村を連名で表示するような実装もできます。




active_hashさん、Prefectureモデルに静的データを持たせるよりも楽でいいですね。




ちなみに、active_hashで静的データを持つことの是非についてはこちらが詳しかったです。
blog.sesere.net




参考にさせていただいたサイト
https://blog.sesere.net/entry/2017/07/23/180000_1
GitHub - zilkey/active_hash: A readonly ActiveRecord-esque base class that lets you use a hash, a Yaml file or a custom file as the datasource
https://blog.otsukasatoshi.com/entry/2017/04/20/003756
Active Support コア拡張機能 - Railsガイド