アクセスメソッドを使ってみる

アクセスメソッドとは?

オブジェクトからインスタンス変数にアクセスするためのメソッドのことです。
種類は以下の3つです。

  • attr_reader
  • attr_writer
  • attr_accessor

attr_readerを使ってみる

class SelfIntro
  def initialize(name='名無しさん')
    @name = name
  end
end

self_intro = SelfIntro.new('太郎')
puts self_intro.name

アクセスメソッドattr_readerを使っていないため、エラーが発生します。 f:id:maru877:20170727214931p:plain

class SelfIntro
  def initialize(name='名無しさん')
    @name = name
  end
  # 追記
  attr_reader :name
end

self_intro = SelfIntro.new('太郎')
puts self_intro.name

今回はattr_readerを追記したため、処理が成功します。
f:id:maru877:20170727215311p:plain

attr_writer を使ってみる

class SelfIntro
  def initialize(name='名無しさん')
    @name = name
  end

  attr_reader :name
end

self_intro = SelfIntro.new('太郎')
self_intro.name = 'たろう'
puts self_intro.name

アクセスメソッドattr_writerがないため、インスタンス変数を更新することができません。 f:id:maru877:20170727215735p:plain

class SelfIntro
  def initialize(name='名無しさん')
    @name = name
  end

  attr_reader :name
  # 追記
  attr_writer :name
end

self_intro = SelfIntro.new('太郎')
self_intro.name = 'たろう'
puts self_intro.name

今回はattr_writerを追記したため、処理が成功します。
f:id:maru877:20170727220001p:plain

attr_accessor を使ってみる

class SelfIntro
  def initialize(name='名無しさん')
    @name = name
  end

  attr_accessor :name
end

self_intro = SelfIntro.new('太郎')
self_intro.name = 'たろう'
puts self_intro.name

f:id:maru877:20170727220537p:plain attr_accessorattr_readerattr_writerの両方を有効にすることが出来ます。

これは直接インスタンス変数を操作できるようになったわけではなく、インスタンス変数を操作するメソッドが自動的に追加されているというイメージです。

Draperを使ってみる

Draperとは?

Draperはデコレーター(ビューとモデルの中間の処理を引き受ける)で、railsのプレゼンテーション層を担う。 表示に関するロジックをプレゼンテーション層に引き渡すことで、以下のようなことが避けられる。

  • モデルに表示ロジックを記述
  • ビューファイル内のif文多用
  • helperの名前空間の衝突

結果、コードの可読性、保守性を向上することができる。

draperをインストー

gem 'draper', '~> 1.3'

$ bundle install

土台となるアプリケーションを作成

$ rails g scaffold Blog title:string content:text f:id:maru877:20170727200954p:plain draperをインストールしているため、decoratorsも作成されます。

$ rails db:migrate

生成されたコード

class BlogDecorator < Draper::Decorator
  delegate_all

  # Define presentation-specific methods here. Helpers are accessed through
  # `helpers` (aka `h`). You can override attributes, for example:
  #
     def created_at
       helpers.content_tag :span, class: 'time' do
         object.created_at.strftime("%a %m/%d/%y")
       end
     end

end

delegate_allとは、decoratorに存在しないメソッドがコールされた場合、モデルの処理が実行される。

controllerにてdecoratorインスタンスを作成する。

オブジェクトをデコレートする(decorator内で利用できるようにする)ためには、以下の方法を利用します。 - 複数のオブジェクトの場合、decorate_collection - 単体のオブジェクトの場合、decorate

class BlogsController < ApplicationController
  before_action :set_blog, only: [:show, :edit, :update, :destroy]

  # GET /blogs
  # GET /blogs.json
  def index
    @blogs = BlogDecorator.decorate_collection(Blog.all)
  end

...

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_blog
      @blog = BlogDecorator.decorate(Blog.find(params[:id]))
      # これでもいける(model名からdecoratorを推察してくれる)
      # Blog.find(params[:id]).decorate 
    end
end

このようにデコレーターインスタンスを生成します。

ビューからdecoratorメソッドを利用する。

app/views/blogs/index.html.erbを編集。

<p id="notice"><%= notice %></p>

<h1>Blogs</h1>

<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Content</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @blogs.each do |blog| %>
      <tr>
        <td><%= blog.title %></td>
        <td><%= blog.content %></td>
        <td><%= blog.created_at %></td>
        <td><%= link_to 'Show', blog %></td>
        <td><%= link_to 'Edit', edit_blog_path(blog) %></td>
        <td><%= link_to 'Destroy', blog, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Blog', new_blog_path %>

表示を確認する

f:id:maru877:20170727200934p:plain

draperで定義したメソッドcreated_atがコールされていることが確認できる。

参考

こちらを参考にさせていただきました。
http://ruby-rails.hatenadiary.com/entry/20150415/1429031791

HashWithIndifferentAccessを使ってみる

HashWithIndifferentAccessとは?

HashWithIndifferentAccessを使うことで、シンボルでも文字列でもハッシュにアクセス可能となる。

hoge = ActiveSupport::HashWithIndifferentAccess.new(hoge: 'ほげ')

# シンボル
hoge[:hoge]
=> "ほげ"

# 文字列
hoge['hoge']
=> "ほげ"


hoge = {hoge: "ほげ"}.with_indifferent_access

# シンボル
hoge[:hoge]
=> "ほげ"

# 文字列
hoge['hoge']
=> "ほげ"

ActiveJobとは?

Active Jobとは?

バックグランドで実行するjobや、キューを操作するためのフレームワークのことです。

$ bin/rails g job hoge
上記のコマンドを実行すると
app/jobs/hoge.rbというjobが作成されます。

# app/jobs/hoge.rb
class HogeJob < ActiveJob::Base
  queue_as :default

  def perform(引数)
    # 後で行う処理を記述する
  end
end
# Jobを定義できたら以下のコードをトリガーとして実行する

# キューに空きができたら実行する
HogeJob.perform_later(引数)

# 1週間後に実行
HogeJob.set(wait: 1.week).perform_later(引数)

# 明日の正午に実行
HogeJob.set(wait_until: Date.tomorrow.noom).perform_later(引数)

アダプタがセットされていない場合は即座にJobが実行されます。

例外

ActiveJobの例外キャッチ方法はrescue_fromの1つのみ提供されている。

rescue_from(ActiveRecord::RecordNotFound) do |exception|
  # 例外処理を記述
end

def perform(引数)
  # 後で行う処理を記述する
end

Regexpを使ってみる

Regexpとは?

正規表現のクラスのことです。

hoge = Regexp.new('hogehoge')
# => /hogehoge/

このように正規表現のスラッシュで囲まれた形式でオブジェクトを作成します。

# 配列を用意
array = %w(hoge fuga)
# => ["hoge", "fuga"]

# 配列を|(OR条件)で結合した、正規表現を返す
regexp = Regexp.new(array.join('|'))
# => /hoge|fuga/

union

# 配列を用意
array = %w(hoge fuga)
# => ["hoge", "fuga"]

# 配列を|(OR条件)で結合した、正規表現を返す
regexp = Regexp.union(array)
# => /hoge|fuga/

配列操作をしなくても、unionを使うとこで、直感的に正規表現を作成することができます。

punditを使ってみる

punditとは

punditは認証系のgemの一種であり、対象のリソースに対して何を(誰を)許可するのかを定義することができます。

punditをインストー

gem 'pundit'

$ bundle install

punditの初期設定

app/controllers/application_controller.rbにPunditをincludeする。
(対象controllerの継承元でpunditをincludeしておく。)

include Pundit

$ rails g pundit:installを実行すると、app/policiesディレクトリが作成されます。

例えばhogehogeコントローラーへのアクセス制御を掛けたいとき、
$ rails g pundit:policy hogehogeを実行し、policyファイルapp/policies/hogehoge_policy.rbを作成されます。

認証に失敗する

hogehoge_controller.rbのindexアクションで、authorize貼ってみる。

# コントローラーのindexアクションにてauthorizeメソッドを呼び出す
def index
  authorize HogeHoge
end

# app/policies/hogehoge_policy.rbでindexをfalse(認証拒否)にしておく
class HogeHogePolicy < ApplicationPolicy
  def index?
    false
  end

認証拒否しておくとPundit::NotAuthorizedErrorが発生します。

認証に成功する

# hogehoges_controller
def index
  authorize HogeHoge
end

def new
  @hogehoge = HogeHoge.new
  authorize @hogehoge
end

def create
  @hogehoge = HogeHoge.new(permitted_attributes(HogeHoge))
end

def update
  @hogehoge = HogeHoge.find(params[:id])
  @hogehoge.update(permitted_attributes(@hogehoge))
end

policy(:hogehoge).fugafuga?(引数)




# app/policies/hogehoge_policy.rb
class HogeHogePolicy < ApplicationPolicy
  # indexアクション時のuser権限を確認しています。
  def index?
    user.staff? || user.admin?
  end

  # create, editアクションを実行できるかをチェックしています(newができればcreateもできる、editができればupdateもできる)
  def new?
    create?
  end

  def edit?
    update?
  end

  # 独自でメソッドを作ることもできる
  def fugafuga?(引数)
  end

  private
    # pundit側でストロングパラメータを設定できる
    def permitted_attributes
      [:strong_parameterA, :strong_parameterB]
    end 
end

Decoratorとは?

Railsでは責務を分割することによって、可読性、保守性の高いアプリケーションを作ることができます。helperにはビュー表示の責務を持たせ、modelには対象リソースに関する処理を責務として持たせます。
そのなかで、Decoratorにはhelperとmodelの中間のような責務を持たせます。モデルの内容を少し加工してビューに表示させる場合(model には表示に関する責務を持たせたくない、viewには表示に関するコード以外は書きたくない)などは、helperやmodelではなくDecoratorにコードを記述します。

active_decoratorをインストー

gem 'active_decorator'

$ bundle install

decoratorファイルを作成

$ rails g decorator question
‘app/decorators/question_decorator.rb'というファイルが作成されます。
ファイル名は対象のモデルに合わせてください。

module QuestionDecorator
  # 3日後の日付を返すメソッドを定義します。
  def three_days_later
    self.updated_at.since(3.days)
  end
end

3日後の日付を返すことに意味があるのか?ビューでsince(3.days)を使ってもいいのでは? そこらへんはひとまず置いておきます。

ビューから呼び出す

<% @questions.each do |question| %>
  <%= question.three_days_later %>
<% end %>

このようにDecoratorを使うことで、Railsに新たな責務を追加することができます。