TECH EXPERT 61日目

TECH::EXPERT

個人的な就職活動を本格化させたので、課題に対するやる気が急速に減退。
というか課題も必要最低限の機能は動いているし、これ以上はやるだけ無駄。勉強したい分野だけ実装して、どうでもいい箇所は放置しないと時間が勿体ない。

rails

htmlエスケープ

古いver.のrailsを除き、通常エスケープされる。≒ 変換される。
これはuserの入力文字列を扱う場合に、そのまま扱うとhtmlタグや属性として認識されるような文字列が含まれていた場合に、それを認識させないようにするための措置である。
ただし、link_toやform_for等のヘルパーメソッドではhtml_safeとして扱われているようで、エスケープ処理が行われない。

controller
@a = "<h1>hogehoge</h1>"

view
= @a
=> &lt;h1&gt;hogehoge&lt;/h1&gt;

.each_slice()

n個の要素をまとめて取り出してブロック内の処理を行う。

items.each_slice(2){ |hoge| hoge.join(",")}

viewだとこんな感じ
- items.each_slice(3) do |elements|
 - elements.each do |e|
  = e.name

オープンクラス

hash 既存のクラス

.select

配列に対して用いる。
ブロックがtrueになる配列のみを抽出する。

array.select{ |n| 条件}

reject

.selectと逆。
ブロック内がfalseになる配列のみを抽出する。

.class

型を判定できる。

arr=[]
arr.class
=> array

.instance_of?()

引数のクラスのインスタンスかどうか判定できる。

@users = User.all
@users.class
=> array

@users[0].class
=> User(id: integer, name: string, created_at: datetime, updated_at: datetime)

@users[0].instance_of?(User)
=> true

where

配列を渡すこともできる。

where(id: [1,2])

link_to派生

link_to_if
link_to_unless
link_to_unless_current?

例)下記の場合、indexページの場合は単なる文字列で”index”が出力され、
indexページ以外の場合はaタグでindexが出力される。その場合のhrefは/items/index

= link_to_unless_current 'index', '/items/index'

current_page?

current_page?(root_path)
current_page?(action: 'index')
current_page?(controller: 'user', action: 'show')
current_page?('/users/sign_up')

reject

ブロック内の戻り値がtrueの時、key,valueを削除する。

戻り値がハッシュを返すが、変更がなかった場合はnilを返す。

hash.reject {|key, value| block }

delete_if

ブロック内の戻り値がtrueの時、key,valueを削除する。

rejectとほぼ同様だが、変更がない場合もハッシュを返す。

hash.reject {|key, value| block }

空白演算子

posixの空白演算子[:blank:]は正規表現中で通用する。
searchの空白区切で配列に分解する時に便利そう。

"あああ いいい ううう".split(/[[:blank:]]+/)
#=> ['あああ', 'いいい', 'ううう']

@keyword = params[:keyword].to_s.split(/[[:blank:]]+/) 

配列の掛け算

a = ["ggg", "ggf"]
a*2
=> ["ggg", "ggf", "ggg", "ggf"]

クエリ

find_each > all.each

find_eachはDBから分割してデータを取得し、メモリ上に展開するのでメモリ効率が良い。
all.eachすると最初に全てのデータを展開するためメモリ効率が悪い。

Hoge.find_each do |fuga|
  # 処理
end

キャッシュについて

.cache.fetch

引数で指定したキーに対応するキャッシュがあれば返す、なければ作成して返す。

# ./app/controllers/articles_controller.rb

  # 追加したメソッドからarticlesを取得するように変更
  def index
    # @articles = Article.all
    @articles = cache_articles
  end

  private
    def cache_articles
      Rails.cache.fetch("cache_articles", expired_in: 60.minutes) do
        # Article.all
        Article.all.to_a
      end
    end
Railsでクエリ結果をキャッシュしてDB負荷を軽減する - Qiita
はじめにRubyonRailsを使ったサービス運用時に(RubyonRailsに限った話ではありませんが)サービスの成長に連れてサイトへのアクセス増大によりDBへの負荷が原因でサイトのレス…

ActiveRecord

ActiveRecord::FinderMethods

クエリを即発行する。

ActiveRecord::FinderMethods

find, find_by, take, first, last, exists?
ActiveRecord::FinderMethods

ActiveRecord::Relation

ActiveRecord::Relationのオブジェクトを返す。
遅延評価(Lazy Evaluation)のため、実際にデータが必要になるまではクエリを投げない。
whereやlimitがこれに該当する。

内部実装的にはarelというライブラリで、SQL文を合成する。

@user = User.where('id > ?', 5).limit(3)
@user = @user.where(name: "tanaka")

これはチェーン可能で、分けて書いても
@user = User.where('id > ?', 5).where(name: "tanaka").limit(3)
と解釈されて、SQL文が書かれる。

逆に
@user = User.where('id > ?', 5).take(3)
@user = @user.where(name: "tanaka")
とした場合、takeはActiveRecord::FinderMethodsなのでSQL文をすでに発行しており、内部的にto_aされているため、where句は続けられず、エラーとなる。

@user = User.where('id > ?', 5).to_a
も同様にto_aの時点でクエリが発行されるので、後ろにActiveRecord:Relationメソッドは書けない。

ActiveRecord::Calculations

countやaverageが属す。
詳しく調べていないが、おそらくこれもクエリ即時発行型と思う。
記載順序で挙動が変わるので少なくともRelationとは別の動きをする。

ancestory gem

include他、eager_loadできないため、ツリー一覧を表示する際にN+1問題を生じやすい。
include自体はアソシエーションに対してしか効かないため、ancestoryの.childrenや.parentには効かせられない。

Model.allで取得したインスタンスの配列に対して、selectメソッドで処理するのがクエリ発生数を抑えられるため好ましい。
逆にこれ以外の方法は、別のモデルからアソシエーションでancestoryを使っているモデルを取得するときにincludeするしかない。この場合、カテゴリー一覧のような全件取得はおそらく無理。

%ul.left-box__category
  - @category.select{|n| n.ancestry == nil}.each do |big_category|
    = link_to big_category.name, search_items_path
    %ul.left-box
      - @category.select{|n| n.ancestry == big_category.id.to_s}.each do |middle_category|
        = link_to middle_category.name, search_items_path
        %ul.left-box
          - @category.select{|n| n.ancestry == big_category.id.to_s + "/"+middle_category.id.to_s}.each do |small_category|
            = link_to small_category.name, search_items_path(category_id: small_category.id)

コメント