6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2022年2月12日 ActionMailer, whenever, 特にscopeの深堀りをしました。

ActionMailer

  • Railsに組み込まれているメール送信機能です。

コマンドで主要ファイルを作成することが出来ます。

rails g mailer UserMailer

whenever

Railsで定期的に実行したいタスクを設定することが出来るGemです。

スコープとメソッドの違い

  • 前提としてクエリメソッドとは、データベースに問い合わせするクエリを発行してくれるメソッドです。
  • 簡単に言うと、スコープとは複数のクエリメソッドをまとめて、複雑なデータをDBから取得したい時などに使われます。

スコープの定義方法

class モデル名 < ApplicationRecord
  scope :スコープの名前, -> { 条件式 }
  # ->ラムダリテラルはlambdaとも書けます。
  scope :スコープの名前,lambda { 条件式 }
end
#scopeメソッド   
scope(name, body, &block)

scope(name, body, &block)

以下はbinding.pryで止めて、scopeメソッドの中身を検証した結果です。

159:         # We are able to call the methods like this:
    160:         #
    161:         #   Article.published.featured.latest_article
    162:         #   Article.featured.titles
    163:         def scope(name, body, &block)
 => 164:           unless body.respond_to?(:call)
    165:             raise ArgumentError, "The scope body needs to be callable."
    166:           end
    167: 
    168:           if dangerous_class_method?(name)
    169:             raise ArgumentError, "You tried to define a scope named \"#{name}\" " \

[1] pry(Article)> name
=> :published_at_yesterday
[2] pry(Article)> body
=> #<Proc:0x0000000127da5b88@/Users/name/workspace/advanced/4916_name/app/models/article.rb:86 (lambda)>
[3] pry(Article)> &block
SyntaxError: unexpected &, expecting end-of-input
&block
^
[3] pry(Article)> block
=> nil

上記から分かることは、第一引数のnameにスコープ名、第二引数のbodyにprocオブジェクトが入っていることが分かります。第三引数&blockにはnilが入っていました。 第三引数に値を渡すには以下のコード例になります。

class Shirt < ActiveRecord::Base
  scope :red, -> { where(color: 'red') } do
    def dom_id
      'red_shirts'
    end
  end
end

do~endそのものが&blockに渡ります。

&blockの&とは… ブロックを引数として明示的に受け取る

スコープのブロックは第2引数のbodyに渡り第3引数はnilでしたが第3引数について触れます。&を仮引数の前につけるとブロックをメソッドの実引数として明示的に受け取ることができます。また、そのブロックを実行する場合はcallメソッドを使います。

スコープを定義する際に使われる->って何? lambdaリテラルのことです。ここでは、スコープ名という関数を使いたいので、lambdaリテラルを使って、スコープメソッドを定義しているというイメージです。

引数の持たせ方のコード例 ラムダリテラルを使用した場合は引数をブロックの外で()で仮引数を定義できます。lambdaを記述する書き方ではブロック内にブロック引数で仮引数を定義する必要があります。

-> (a) { a + b }
lambda { |a| a + b }

ラムダリテラルを使用した場合は引数の()を省略できます。

-> a { a + b }

lambdaとは?

lambdaとは、->構文またはlambdaメソッドで作られたProcオブジェクトです。Procオブジェクトとは、ブロックをオブジェクトとして扱えるようにしたオブジェクトです。オブジェクトとして扱えるので、変数に入れて別のメソッドに渡すことができます。

ブロックをオブジェクトとして扱うために、procオブジェクトにする=> procオブジェクトを作ります。

1.day.ago

昨日の日付を取得できます。

[1] pry(main)> 1.day.ago
=> Thu, 10 Feb 2022 23:39:38.257665000 UTC +00:00
[2] pry(main)> 1.day.ago.class
=> ActiveSupport::TimeWithZone
[2] pry(main)> 1.week.ago.beginning_of_day..Time.zone.now.end_of_day
=> Sat, 05 Feb 2022 00:00:00 JST +09:00..Sat, 12 Feb 2022 23:59:59 JST +09:00
先週の土曜日の0時〜今日(土曜日)の終わり23時59分

all_day

RailsActiveSupportに定義されているメソッドで、日付を範囲で取得することができます。

Rangeクラスになります。ActiveSupportとは、Rails拡張機能のことです。

ActiveSupportを使用することで様々なpresent?やblank?などのメソッドを使うことが出来るようになります。 今日の日付で、00:00:00から23:59:59までを取得範囲とします。

all_dayの前に取得したい日付を指定するとその日付の00:00:00から23:59:59までを取得します。

[11] pry(main)> Time.now.all_day
=> 2022-02-12 00:00:00 +0900..2022-02-12 23:59:59 +0900

実際の取得例 取得したい日付オブジェクト + all_day
今日 Time.zone.now.all_day
昨日 1.day.ago.all_day
1週間前 1.week.ago.all_day
1か月前 1.month.ago.all_day

begining_of_dayとend_of_day

begining_of_dayを使うことで、時間部分をその日の始まりに設定した日付を取得できます。end_of_dayは時間部分をその日の終わりに設定した日付を取得できます。

[5] pry(main)> 1.day.ago.beginning_of_day
=> Fri, 11 Feb 2022 00:00:00 JST +09:00
[6] pry(main)> 1.day.ago.end_of_day
=> Fri, 11 Feb 2022 23:59:59 JST +09:00
[7] pry(main)> 1.day.ago.beginning_of_day..1.day.ago.end_of_day
=> Fri, 11 Feb 2022 00:00:00 JST +09:00..Fri, 11 Feb 2022 23:59:59 JST +09:00

Active Supportとは

Active SupportはRuby on Railsコンポーネントであり、Ruby言語の拡張やユーティリティを提供します。 Active Supportは言語レベルで基本部分を底上げして豊かなものにし、Railsアプリケーションの開発とRuby on Railsそれ自体の開発に役立てるべく作られています。

以下はActiveSupportによって拡張されたメソッドの例です。 Rubyでは使えません。

  • blank?
  • present?
  • empty?
  • nil?

TimeとTimeWithZoneどちらを使うべきかについて

TimeとTimeWithZoneの違いについて

まず両者の明確な違いを記します。

Timeクラス

Rubyのクラスです。

TimeWithZone

RailsのActive Supportの拡張クラスです。Rubyでは、使用できません。

Railsの実装を見ていると日時関連の処理は TimeWithZone を積極的に使おうとしているように思います。 TimeWithZone はタイムゾーンRuby標準のTimeクラスよりも器用に扱えるので、国際的なwebアプリケーションをターゲットにするのであれば、TimeWithZoneクラスを積極的に使うのは確かに理にかなっています。 なので、我々も 極力 TimeWithZone を使うようにした方が良い 、と考えることができます。

RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い

最終的にどちらを使えばいいの?という結論につきましては、上記にもあるようにRailsを使用する場合は、Timeクラスではなく、TimeWithZoneを使用するのをオススメします。

まとめ

今日は4人で勉強会をしたRailsの内容を深掘りしました。