未経験からエンジニアになって初めて知ったRailsの知識(第1週目)

未経験からRailsエンジニアになりました。
もともと1年弱railsを学んではいたのですが実務に入ってみると案外知らないことが多いです。
業務中にわからないことがあるたびにメモって、業務終わりにアウトプットしているのでそれをqiitaにも拙い文章ではありますが記事として投稿します。
エンジニアの方は「未経験者はこういうのがわかっていないんだなぁ」という目で御覧ください。技術的に間違ってたらコメントいただけると嬉しいです。


## begin ~ rescue ~ else ~ end

簡単に言うとif文のちょっとデカイバージョン。
if文はtrue/falseに分かれるものを扱うのに対し、begin ~ rescueはtrue/falseにわかれない処理(railsのエラー画面が出てくるようなもの)に対して成功/失敗の分岐を行う。

```ruby
def create
  begin
    @item = Item.create!(item_params)  # 保存に失敗した際にエラーを出力
  rescue => e
    errors.add(:base, e.message)
  end
end
```

例えばこのコードだと、保存に失敗した際にエラーが発生する可能性がある処理を噛ましているが
これをbegin~rescueなしで書くと

```ruby
def create
  @item = Item.create!(item_params)  # 保存に失敗した際にエラーを出力
end
```

このようになる。これだとエラーが発生した段階で処理が止まってしまう。
これを防ぎ、エラーが発生した段階でrescue文を実行させてそのまま処理を続けてくれるのがbegin~rescueの役割。
こういう使い方はしないと思うけどあくまで一例として書いてみた。

## transaction

begin~rescueと一緒に使われることが多い。
複数のレコードの処理がすべて成功したかどうかを判定する際に使う。

```ruby
def save
  Hoge.create(hoge: hoge)
  Fuga.create(fuga: fuga)
end
```

みたいに複数のレコードを操作するメソッドを定義した際にif文やbegin ~ rescueだけだと例外処理ができない。
具体的にはどの部分でエラーが出ているのかの特定が難しい。よってここにTransactionを使う。

```ruby
def save
  begin
  ActiveRecord::Base.transaction do
    Hoge.create!(hoge: hoge)
    Fuga.create!(fuga: fuga)
  rescue => e
  	return e.message
  end
end
```

上記のようにすることでcreateのどちらかに失敗した際にエラーを出すことができる。
このとき発生したエラーの情報はeという変数に格納され、`e.message` でエラー文を出力できる。

## banken

ユーザーごとの権限管理を行うgem。bundle install後

```
rails g banken:install
```

を実行すると `app/loyalities` ディレクトリが作成される。権限の設定ファイルは

```
rails g banken:loyalty books
```

とすればloyalities以下にファイルが作成される。権限の設定方法は以下のようにLoyaltyクラスに書く。

```ruby
class BooksLoyalty < ApplicationLoyalty
  def create?
    user.admin? || record.unpublished?
  end
end
```

このような権限ファイルが存在すると同じ名前のコントローラーにて

```ruby
class BooksController < ApplicationController

  # 他の処理は省略

  def create
    authorize! @post

    if @post.update(post_params)
      redirect_to @post, notice: 'Post was successfully updated.'
    else
      render :edit
    end
  end

  # 他の処理は省略
end
```

のように`authorize!` メソッドで同じ名前のコントローラー&&アクションに対応する権限の判定を行うことができる。
権限周りで弾かれた際はloyaltiesディレクトリを見に行くようにする。

## scope

whereメソッド等でのモデルの検索処理に名前をつけたもの。例えば

```ruby
User.where(age: 10..19)
```

と書かずに

```ruby
class User < ActiveRecord::Base
  scope :teenager -> { where(age: 10..19) }
end
```

のようにかけば

```ruby
User.teenager
```

で10ageが10~19のユーザーを指定できる。

参考…https://qiita.com/ozin/items/24d1b220a002004a6351

## enum

かんたんに言えばActiveHashをモデル内部に埋め込んだもの。

例えばこんなactive_hashがあったとする

```ruby
class Category < ActiveHash::Base
  self.data = [
      { id: 0, name: 'メンズ'}
      { id: 0, name: 'レディース'}
      { id: 0, name: 'キッズ'}
    ]
end
```

これをItemモデル内に埋め込んでしまおう、というのがenumを使うとできる。

```ruby
class Item < ActiveRecord::Base
  enum categoy: { mens: 0, redies: 1, kids: 2}
end
```

このようにenumを定義しておくと category=1 のItemを保存した際に

```ruby
   item.mens? # メンズかどうかを判定
   # => true
   item.category # enumに設定された文字列が出力される 
   # => mens 
```

のような記述が使えるようになる。

```
gem 'enum_help'
```
というgemを使うことで日本語化にも対応してja.ymlに

```yml
ja:
  enums:
    item:
      category:
        mens: メンズ
```
のように定義しておけば

```ruby
 item.category
 # => メンズ
```

と日本語で出力できる。

参考…https://qiita.com/punkshiraishi/items/799bef63607e03262644

## lambda

ブロックをオブジェクト化したもの例えば

```ruby
lambda2 = lambda { |s| p "Hello, #{s}!" }
```

のような形でlambdaを定義しておいて

```ruby
lambda2 = lambda { |s| p "Hello, #{s}!" }
lambda.call("test")

=> "test"
```

のような形で呼び出す、という使い方をする。
これはここからここまでが一連の処理の流れですよ〜という意味をもたせられることに価値があると思う。

例えば親モデルの一部をコピーした子モデルを作る際に

```ruby
class Parent < ApplicationRecord
  after_create :child_initialize
  
  def child_initialize
    self.child.create(fisrt_name: self.first_name, last_name: self.last_name…)
  end
  
end
```

のようにしてもいいけど、一つのメソッドの処理が長くなってしまうのはあまり好ましくない。これを解決するために
ブロックをオブジェクト化してブロックを引数として渡す。

```ruby
class Parent < ApplicationRecord
  after_create :child_initialize
  
  child_params_lambda = lambda {
   first_name: |parent| parent.first_name
   nationality: |parent| parent.nationality
  }
  
  def child_initialize
    self.child.create(child_params_lambda.call(self))
  end
  
end
```

ちょっとテキトーだけどこんな感じでlambdaとしてブロックを事前に作っておく。
lambcaがcallされると「お、なんかブロックでもあるのかな」となるのでちょっとコードが長くても
落ち着くと思う。ここはもう少し深ぼりたいところ。

## ActiveSupport::Concern

かんたんにいうとコントローラーで使われる共通の処理をモジュールとしてまとめておく場所。
ビューにおけるhelperのような立ち位置。

```ruby
class Hoge
  def test
    puts 'test'
  end
end
```

```ruby
class Fuga
  def test
    puts 'test'
  end
end
```

こんな感じで複数のクラスに共通した処理が合ったときにこれを一つにまとめる。

```ruby
module Test
  extend ActiveSupport::Concern
  def test
    puts 'test'
  end
end
```

Concernにまとめておくと各クラスでincludeしてあたかもそのクラスで定義したメソッドかのように使える。

```ruby
class Hoge
  include Test
  def hoge
    test
  end
end

class Fuga
  include Test
  def fuga
    test
  end
end
```

Concernを使う場面としては、コントローラーに記述するまでもないけど共通してよく使われる処理があるとき。
モジュール化して切り離しておくことで複数のコントローラーでまたいで使うことができる。
なおconcernに書かれた処理は別途concern_specでテストも実装するべし。

## trait

FactoryBoyで複数のカラムと値の組み合わせを一つにまとめるために使う。例えば

```ruby
FactoryBot.define do
  factory :user do
    nickname { Faker::Name.name }
    email { Faker::Internet.free_email }
    password { 'Pass1234' }
    password_confirmation { 'Pass1234' }
    profession { 'student' }
    age { 24 }
  end
end
```

こんな感じのuserデータが有ったときに、未成年の学生、というFactoryを生成したい。このとき

```ruby
build(:user, age: ..19, profession: 'student' )
```

としてもいいけどちょっと記述が長くなってしまう。また意図している内容を記述から読み取る必要がある。これを

```ruby
FactoryBot.define do
  factory :user do
    nickname { Faker::Name.name }
    email { Faker::Internet.free_email }
    password { 'Pass1234' }
    password_confirmation { 'Pass1234' }
    
    trait :young_student do
      profession { 'student' }
	    age { Faker::Number.number(from: 7, to: 19) }
    end
  end
end
```

としておけば

```ruby
build(:user, :young_student)
```

これだけで未成年の学生、を抽出可能である。

## decorator

モデルのデータ整形を専門に扱う。よくあるのはDB上は姓名別で管理しているけどブラウザ上では一緒に扱いたいというようなケース

```ruby
class User < ApplicationRecord
  # first_name:string last_name:string
end
```

このようなUserモデルが存在していた際に、姓名をくっつけて表示させることを考える。

```ruby
module UserDecorator
  def full_name
    "#{first_name} #{last_name}"
  end
end
```

こんな感じでfull_nameっていうメソッドを定義しておくと

```ruby
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end
end
```

とした際に

```erb
<%= @user.full_name %>
```

で姓名を合体した記述が取り出せる。