Rails

  1. 総論・概念
      1. railsの命名規則
    1. convention over configuration
  2. Routes
    1. resoucesメソッド
      1. resourcesメソッドのネスト
      2. prefix
      3. resourcesとresource
      4. namespace do
  3. Controller
    1. コントローラーの作成・削除
      1. rails gで不要なファイル群を作成しないように設定変更
      2. redirect_toメソッド
      3. before_actionメソッド
      4. layout ‘レイアウトファイル名’
      5. リレーションのCreate
  4. View
    1. .erbで使うメソッド ヘルパーメソッド
      1. link_toメソッド
      2. simple_format
      3. img_tag
    2. validation(検証)
      1.  validates :カラム名, presence: true
      2. validationとnew/save
    3. errorsメソッド
      1. .errors.full_messages
    4. renderメソッド
      1. render partialオプション
      2. render localsオプション
      3. render collection
      4. renderメソッドの省略記法
  5. one more step
    1. 複数のコントローラに同じ処理が記述されている場合(concerns)
    2. 複数のアクションに同じ処理が記述されている場合
    3. コントローラに複雑な処理を記述している場合
    4. モデル
      1. Decorator
    5. Callback
    6. Modelメソッド
  6. 頻出メソッド
    1. ActiveRecordメソッド
      1. findメソッド
      2. whereメソッド
      3. あいまい検索
      4. 複数条件検索
      5. where.not
      6. destroyメソッド
      7. updateメソッド
      8. orderメソッド
      9. limitメソッド
      10. mergeメソッド
      11. new_record?
      12. .last()
    2. first_or_initializeとその類似
      1. first_or_initializeメソッド
      2. .find_or_initialize_by()
      3. .first_or_create()
      4. .find_or_create_by()
      5. enum
    3. ActiveRecord Relationクラス
    4. ActiveRecord 集計関数
      1. averageメソッド
      2. groupメソッド
      3. countメソッド
      4. order(‘count_カラム名’).count(カラム名)
      5. .group.countと併用した場合
    5. dateクラス
      1. Date.today
    6. その他
      1. include?()
      2. .slice() or []
      3. dig
      4. .delete()
      5. ワンライナー
      6. delete_if
      7. 配列から複数の要素を削除
    7. 数値の桁区切り、通過表示
      1. to_s(:delimited), number_with_delimiter
      2. to_s(:currency)
  7. task
  8. helperメソッド
  9. よく使うGem
    1. pry-rails
    2. ページネーション
  10. その他
      1. インストール済みのGemを探す
      2. ローカルサーバーの立ち上げ
      3. Rails console
      4. フォントファイルの置き場は
      5. ファイルのかき分けの問題
    1. Rubyのモジュール
    2. README
      1. rake db:create と rails db:create
      2. Ynaqdh
      3. 複数形
    3. asset pipeline
      1. sprockets
      2. プリコンパイル
    4. scaffold
    5. ベーシック認証 Basic認証
      1. authenticate_or_request_with_http_basic
  11. ancestry
  12. session
      1. paramsの内容を一旦、sessionに保存する場合
    1. 関連記事

総論・概念

とっても重要な概念。railsの流れは常にこう。
ルーティング → コントローラー → ビュー

アクセスしたアドレス(パス)を元に、次のアクションを起こすためにどのコントローラーを使うかルーティングを行う。
指定されたコントローラーに所属するアクションを用いてデータの取得等を行う。
取得したデータをビューとしてhtmlに返す。

get 'パス' => 'コントローラー名#アクション名'
get 'tweets' => 'tweets#index'  

railsの命名規則

controller → 複数形
- ファイル名:users_controller.rb
- class名:UsersController

model → 単数形
- ファイル名:user.rb
- class名:User

データベース → 複数形
- table名:users

view → 複数形
- ディレクトリ名:users/

convention over configuration

規約が最初。規約に従わない場合は自分で設定してね、という概念。
初めてのプログラミングの勉強をrailsでやる場合に、学習を阻害する根源。

規約の中身が多すぎるため把握しきれず、意図しない動作をしてしまう。(規約通りなのだが)
初学者にとって規約を理解することそのものが困難であり、膨大すぎる規約のためそもそも把握できるようなものではない。なにより規約があることを知らない。

私が10日余りrailsを勉強して引っかかった部分はほぼ全てrails内部での処理である。
読んでないけどきっと規約に書いてある。
rubyに関しては書いた通りに動かせるのでロジックが組める。でもrailsは書いた内容が出力される時に、”いいように”して出力してくれるので私の意図と違ったりする。
なのでrailsは覚えゲーだと思う。「AをしたらBになる」という入出力を覚えることはできても、理解したいのならrailsではない方が好ましいかもしれない。

Routes

Rails.application.routes.draw do
  devise_for :users
  root  'tweets#index'
  resources :tweets do
    resources :comments, only:[:create]
  end
  resources :users, only:[:show]

  # get   'tweets'     => 'tweets#index'  
  # get   'tweets/new' => 'tweets#new'
  # post  'tweets'     => 'tweets#create'
  # delete 'tweets/:id' => 'tweets#destroy'
  # get    'tweets/:id' =>  'tweets#show'
  # get   'tweets/:id/edit' => 'tweets#edit'
  # patch 'tweets/:id'  =>  'tweets#update'
  # get   'users/:id'  => 'users#show'
end

resoucesメソッド

自動でルーティングしてくれる。
7種類。only等で使うものだけ指定する。
new, create, show, destroy, index, update/put, update/patch

resources:reviews, only:[:new, :create]
上は下と同義
get 'products/:product_id/reviews/new' => 'reviews#new'
post 'products/:product_id/reviews' => 'reviews#create'

resourcesメソッドのネスト

resources :books do
  resources :reviews
end
          Prefix Verb   URI Pattern                                Controller#Action
    book_reviews GET    /books/:book_id/reviews(.:format)          reviews#index
                 POST   /books/:book_id/reviews(.:format)          reviews#create
 new_book_review GET    /books/:book_id/reviews/new(.:format)      reviews#new
edit_book_review GET    /books/:book_id/reviews/:id/edit(.:format) reviews#edit
     book_review GET    /books/:book_id/reviews/:id(.:format)      reviews#show
                 PATCH  /books/:book_id/reviews/:id(.:format)      reviews#update
                 PUT    /books/:book_id/reviews/:id(.:format)      reviews#update
                 DELETE /books/:book_id/reviews/:id(.:format)      reviews#destroy
           books GET    /books(.:format)                           books#index
                 POST   /books(.:format)                           books#create
        new_book GET    /books/new(.:format)                       books#new
       edit_book GET    /books/:id/edit(.:format)                  books#edit
            book GET    /books/:id(.:format)                       books#show
                 PATCH  /books/:id(.:format)                       books#update
                 PUT    /books/:id(.:format)                       books#update
                 DELETE /books/:id(.:format)  

prefix

prefixに_pathを付けるとURI Patternのパスを示す。
book_review_pathで”/books/:book_id/reviews”を示す。
:book_idに関しては、_pathの引数にインスタンスを渡すことで補われる。
なおprefixが空欄のものは無いのではなく、上と同じため省略されているだけである。

@review = Review.find(1)
book_review_path(@review)
=> /books/:book_id/reviews/1
prefixを用いる場合には''で囲う必要はない。囲うと/new_post_pathはno roote matchesと怒られる。
<li><%= link_to 'New', new_post_path %></li>
<li><%= link_to 'New', 'posts/new' %></li>

resourcesとresource

単数形と複数形で挙動が異なる。
複数形だとindexとcreate以外のアクションに:idが付与される。
そして単数形を用いる場合、リソースが1つと判断でき、indexする必要がないため自動的にindexアクションは省かれる。

単純なページ等でresourceと単数形で用いたい場合、resource :bookのようにどちらも単数形で指定しても返されるコントローラー名(controllers#show)などは複数形が指定される。railsのルールとしてコントローラー名は複数形で使われるから。
ただし、resources bookとした場合にはbook#indexという風にそのまま作成される。

処理としてはresourceに与えられる値は複数形化され、resourcesはもともと複数形として信頼されているのでそのままでコントローラー名として扱われる?
resource 単数形 複数形化#アクション  => 明らかな介入あり
resource 複数形 そのまま#アクション  => 介入あるか不明
resources 単数形 そのまま#アクション  => 介入なし
resources 複数形 そのまま#アクション  => 介入あるか不明

resourece :tweets

new_tweets GET    /tweets/new(.:format)   tweets#new
edit_tweetsGET    /tweets/edit(.:format)  tweets#edit
    tweets GET    /tweets(.:format)       tweets#show
           PATCH  /tweets(.:format)       tweets#update
           PUT    /tweets(.:format)       tweets#update
           DELETE /tweets(.:format)       tweets#destroy
           POST   /tweets(.:format)       tweets#create
resources :tweets

tweets      GET    /tweets(.:format)         tweets#index
            POST   /tweets(.:format)         tweets#create
new_tweet   GET    /tweets/new(.:format)     tweets#new
edit_tweet  GET    /tweets/:id/edit(.:format)tweets#edit
tweet       GET    /tweets/:id(.:format)     tweets#show
            PATCH  /tweets/:id(.:format)     tweets#update
            PUT    /tweets/:id(.:format)     tweets#update
            DELETE /tweets/:id(.:format)     tweets#destroy

namespace do

namespaceに付けた名前がurlの先頭に付与される。

namespace :admin do
  namespece :users do
    resources :hoges, only[:index]
  end
end

admin/users/hoges

Controller

コントローラーの作成・削除

rails g controller コントローラ名
  # コントローラーを作成 gはgenerateの略
rails d controller コントローラ名
  # 作成したコントローラを削除

作成した場合、app/controllerにコントローラー名フォルダとコントローラー名.rbファイルが作成される。
対応するviewに関しては、app/viewにコントローラー名フォルダが作成されるが、中身となるアクション名.html.erbは自動的には作成されないので必要に応じて自分で新規作成する。(コントローラー作成時にアクションまでは指定しないので当然ではあるが)

rails gで不要なファイル群を作成しないように設定変更

config/application.rbに以下を記述

module ChatSpace
  class Application < Rails::Application
    config.generators do |g|
      g.stylesheets false
      g.javascripts false
      g.helper false
      g.test_framework false
    end
  end
end

redirect_toメソッド

redirect_to controller: :コントローラー名, action: :アクション名

アクション → ビューが通常の流れだが、アクション内でredirect_toメソッドを行うことで別のアクションを実行したり、別のビューへ誘導できる。

def move_to_index
  redirect_to action: :index
  # indexアクションを強制的に実行する
end

引数にはaction: :indexという形で、キーがaction:バリューが:indexであるハッシュを指定する。
つまりredirect_to{action: :index}が精確だが、railsではハッシュの{}は省略しても良い。

before_actionメソッド

コントローラーでbefore_action :メソッド名 と指定することによってアクションが実行される前に、特定のメソッドを実行できる。
なおonlyやexceptを用いて制限することもできる。

before_action :hoge, except: :index
# indexアクション以外が実行される前にhogeが実行される。
例
class MessagesController < ApplicationController
  before_action :set_group

  private
  def set_group
    @group = Group.find(params[:group_id])
  end
end

layout ‘レイアウトファイル名’

コントローラ内でlayout ‘レイアウトファイル名’と書くと、そのコントローラでのアクションが呼ばれたあとに表示するビューのレイアウトファイルを指定できる。
仮にlayout ‘movie’に指定すると、MovieControllerのindexアクションが呼ばれたときに表示されるレイアウトはmovie.html.erbとなる。なにも指定しないとレイアウトファイルはapplication.html.erbとなる。

リレーションのCreate

current_user.reviews.create(create_params)とするとcurrent_userの入ったテーブルが作成される。その他の情報はparamsから。

def create
  current_user.reviews.create(create_params)
  redirect_to controller: :products, action: :index
end

private
  def create_params
    params.require(:review).permit(:rate, :review).merge(product_id: params[:product_id])
  end

View

コントローラーのアクションが実行された後、アクションと同名のviewファイルが展開される。
railsではviewファイルに.html.erbファイルが用いられ、embedded Rubyの略でHTMLへrubyスクリプトを埋め込んだ形式の物である。

コントローラーのアクションで定義したメソッドを使うには
<%= メソッド名 %>とする。

  • <% ~ %>  ・・・ 処理を行うのみで、htmlへ値を代入しない。
  • <%= ~ %>  ・・・ 処理を行い、その場に値を代入する。(htmlに埋め込む)
  • <%= ~ -%>  ・・・ 最後の改行を除いてその場に値を代入する。

実はRailsでは、ルーティングとルーティングに対応するビューさえあれば、明示的にコントローラのアクションを定義しなくても、ビューを表示することができる。
静的なページを構築する場合にはアクションを定義しなくてよい。

.erbで使うメソッド ヘルパーメソッド

link_toメソッド

rubyタグ内で使用できる。htmlのaタグへ変換される。
例として上のメソッドはhtml.erbからhtmlへ変換される際に自動的に変換される。

<%= link_to '記事一覧へ', '/hogehoge', class: 'sample' %>
↓へ変換される
<a class="sample" href="/hogehoge">記事一覧へ</a>

simple_format

rubyタグ内では改行が無視されるため、改行も出力したい場合に使う。

<%= simple_format(text) %>

img_tag

<%= img_tag 'cat1.png'%>
<img src="images/cat1.png">

validation(検証)

入力フォームを通じてビューからサーバー側へパラメーターが送られてきた際、正常な値か検証する。未入力を撥ねるために使う。

 validates :カラム名, presence: true

フォームの中身の有無を検出してくれる。ない場合には保存処理をしない。

例えば、userのemailを入力必須にしたい場合、以下のように書く。
user.rb

class User < ApplicationRecord
 validates :email, presence: true 

ユーザーの新規登録時にemailを入力しなかった場合、userを登録することができなくなる。

validationとnew/save

validationで未入力があった場合にどうやって再処理を促すか?
仮にformから返る値をcreateで作った場合、validationで撥ねた場合に処理が分岐できない。

実はsaveメソッドは成功するとインスタンスが返り値となるが、失敗すると(validationで撥ねる)とfalseが返ります。これを利用して、if インスタンス.saveと書くことで再入力を促すことができる。

if tweet.save
  redirect_to root_path
else
  render :new
end

errorsメソッド

validationで失敗した場合、保存しようとしたインスタンスはnilではなく、エラー情報を含んだインスタンスになる。(情報が追加される)
これをrenderで再度同じページに飛ばすことで、エラー情報を持つ場合の分岐が作れる。

.errors.full_messages

エラーメッセージを配列で取得できる。

- if @group.errors.any?
  .chat-group-form__errors
     %h2= "#{@group.errors.full_messages.count}件のエラーが発生しました。"
     %ul
       - @group.errors.full_messages.each do |message|
         %li= message

renderメソッド

文字通りレンダリングするときに使う。
部分テンプレートを呼び出したり、アクションをせずに別のビューページに飛ばす際に用いる。

類似の機能にredirect_toがある。
redirect_toの場合はアクションを実行後にビューに移るのに対し、
renderの場合はアクションを実行せずに直接ビューに移る。
具体的な使い方としては、静的なページにアクセスさせる際にはアクションを実行する必要がないためrenderを使う。

viewファイル内だけではなく、controller側でも使う。
その際にはhtmlを指定するだけでなく、actionを指定もできる。(当然だが、actionに対応するviewが出るだけで、actionは実行されない)

render :show
render action: :show
render "show"
render "show.html.erb"
render action: "show"
render action: "show.html.erb"

render partialオプション

renderメソッドに :partialというオプションをつけることで、明示的に部分テンプレート名を指定し、部分テンプレートを表示することができます。
なお同名かつ_を付与してファイルを作成している場合には記述を省略できる。

sample.html.erb
_sample.html.erb 部分テンプレート名に_を付ける

render :partial => "sample", :locals => { :val1 => xxx, :val2 => xxx }

render localsオプション

部分テンプレート内に変数を渡す。

render partial: "sample", locals: { hogehoge: "hello!" }
<% @product.reviews.each do |review|%>
  <%= render partial: "review", locals: { review: review } %>
<% end %>

render collection

render partial:tweet, collection @tweets

@tweets.each do |tweet|
  render partial:tweet
end

上記の上と下はやっていることは同じであるが、処理速度が違う。
理由はeachで回す場合に部分テンプレートをeachの回数だけ繰り返し呼んでしまうからだ。
collectionで渡す場合には1度しか呼び出されないので通常は上の書き方をする。

renderメソッドの省略記法

<% render partial: "hogehoge", collection: @hogehoges %>
↑↓は同義。collection記法であれば大半を省略できる。
<% render @hogehoges %>

collection記法は、モデル名sと_モデル名.html.erbの規則であればpartial部分を省略可能。部分テンプレート内ではインスタンス名(単数形)が用いられる。加えてcollectionという表記も省略できるので上記のように簡潔に記載できる。
この規則は、@hogehogesの文字列由来ではなく、@hogehogesのモデルに由来する。
このため、下記のような記載では、参照される部分テンプレートや部分テンプレート内のインスタンス変数名はモデル由来のものが利用される。(ここの記載はちょっと怪しいです。 ローカル変数を省略すると、参照先テンプレート名が変数名に格納される?)
これは規約なのでどうしようもない。

def index
  @hogehoges = Post.all.page(params[:page]).per(5)
end

posts/index.html.erb
<%= render @hogehoges %>

posts/_post.html.erb
<%= post.text %>

なお変数名を指定する場合には次のようにasで指定する。

= render partial: "irs", collection: @companies, as: :company

one more step

複数のコントローラに同じ処理が記述されている場合(concerns)

規模が大きくなるにつれ複数のコントローラに同じような処理が繰り返し記述されることがある。

  • app/controllers/concernsにファイルを追加し、必要箇所で読み込ませる
  • 親コントローラにメソッドを定義する

などの方法でコントローラの記述を共通化する。
concerns ディレクトリに共通するコードをモジュールとして定義し、ソースコードの可読性をあげる。

app/controllers/concerns/current_cart.rb

module CurrentCart
  extend ActiveSupport::Concern

  private

  def set_cart
    @cart = Cart.find_by(id: session[:cart_id])
    if @cart.nil?
      @cart = Cart.create
      session[:cart_id] = @cart.id
    end
  end
end
class ProductsController < ApplicationController
  include CurrentCart
  before_action :set_cart
end

同じ親コントローラを継承していれば、親コントローラに記述するだけでOK。

def ApplicationController < ActionController::Base

  private

  def authorize_owner
    redirect_to root_path unless current_user.owner?
  end
end

複数のアクションに同じ処理が記述されている場合

同じコントローラ内で同じような処理が繰り返し記述されている場合はcallbackを用いて共通化する。before_actionなどのcallbackを使うと良い。

注)諸説あり。before_action等が増えるとメソッド内の処理が見通せなくなる。
callbackの順番によって意図しないエラーが起こる可能性がある。

コントローラに複雑な処理を記述している場合

コントローラのコード量が多くなっている場合、本来モデルで行うべき処理がコントローラに書かれている可能性が高い。

モデル

ビューやコントローラから呼び出される様々な処理はモデルに集約されていくために、特にモデルは肥大化し易い。幾つかの観点で切り分ける。

Decorator

Decorator(デコレーター)とはビューとモデルの中間に位置し、モデルやビューなどに実装されやすい表示ロジックやフォーマットなどを担当する。

モデルにビューでしか使用しないメソッドが増えていくことがある。
full_nameやfull_name_kanaと言ったメソッドがその例。
こうしたメソッドをデコレーターへ移動する。

app/models/user.rb

class User < ActiveRecord::Base
  def full_name
    "#{family_name} #{first_name}"
  end

  def full_name_kana
    "#{family_name_kana} #{first_name_kana}"
  end
end

Railsでデコレーターを使用する場合にはdraperやactive_decoratorと言ったgemを使う方法が一般的。
以下はdraperを使った場合。

app/decorators/user_decorator.rb

class UserDecorator < Draper::Decorator
  delegate_all

  def full_name
    "#{family_name} #{first_name}"
  end

  def full_name_kana
    "#{family_name_kana} #{first_name_kana}"
  end

end

app/controllers/users_controller.rb

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

app/views/users/show.html.erb

<%= @user.full_name %>
<%= @user.full_name_kana %>

Callback

コントローラ同様にモデルにもvalidationの直前に実行されるbefore_validationであったり、saveの直後に実行されるafter_saveなど様々なタイミングで実行されるコールバックが存在する。

開発が大規模になるとコールバックのメソッドが増え、メソッド同士の関係性がわかりづらくなる。callbackの引数に、そのコールバックと同名のメソッドを持つインスタンスを渡すことで、コールバックの処理を別のクラスに移動できる。

class BankAccount < ActiveRecord::Base
  before_save      EncryptionWrapper.new
  after_save       EncryptionWrapper.new
  after_initialize EncryptionWrapper.new
end

class EncryptionWrapper

  def before_save(record)
    record.credit_card_number =  encrypt(record.credit_card_number)
  end

  def after_save(record)
    record.credit_card_number = decypt(record.credit_card_number)
  end

  def after_initialize(record)
    record.credit_card_number = decypt(record.credit_card_number)
  end

  private

    def encrypt(value)
      # 暗号化の処理
    end

    def decrypt(value)
      # 解読の処理
    end
end

Modelメソッド

モデル内に定義したメソッドはモデルに対して使う。
selfを用いてクラスメソッド風に書くが、インスタンスに使う。

モデル
def add_tax
  self.price = (price * 1.08).round
end

コントローラ
def create
  @product = Product.new(product_params)
  @product.add_tax
end

頻出メソッド

ActiveRecordメソッド

最終的にSQL分を発行してDBから情報を抜いてくれる。

findメソッド

whereメソッド

モデル.where(条件)のように引数部分に条件を指定することで、テーブル内の条件に一致したレコードのインスタンスを配列型で取得できる。

例
user.where('id < ?', 20)
Product.where('title LIKE(?)', "test%")
Review.where(name: "test")

あいまい検索

文字列意味
%任意の文字列(空白文字列含む)
_任意の1文字
実行例詳細
where(‘title LIKE(?)’, “a%”)aから始まるタイトル
where(‘title LIKE(?)’, “%b”)bで終わるタイトル
where(‘title LIKE(?)’, “%c%”)cが含まれるタイトル
where(‘title LIKE(?)’, “d_”)dで始まる2文字のタイトル
where(‘title LIKE(?)’, “_e”)eで終わる2文字のタイトル

複数条件検索

User.where(‘id = ? or name = ?‘, 5, ‘satou’)
User.where(‘id = ? and name = ?‘, 5, ‘satou’)

where.not

@users = User.where('name LIKE(?)', "%#{keyword}%").where.not(id: current_user.id)

destroyメソッド

インスタンスに対してそのレコードを削除する際に使うことができる。

updateメソッド

updateメソッドはモデルのインスタンスに対して使用でき、引数内の情報にレコードを更新できる。

update(カラム名: 更新する情報, カラム名2: 更新する情報2、、、)

orderメソッド

コンテンツの並び替えをする。すべてのインスタンスを取得する場合は.allを省略できる。

@contents = Content.all.order("id DESC")

昇順.order(“id ASC”) もしくは降順 .order(“id DESC”)で並べ替えます。
id部分には任意のカラム名を指定します。

limitメソッド

取得するインスタンスの数を制限する。
すべてのインスタンスを取得する場合は.allを省略できる。

@ranking = Product.limit(5)

mergeメソッド

フォームから値を受けとって、統合したりする時などに使う。

params.permit(:image, :text).merge(user_id: current_user.id)

new_record?

モデル.new_record?と言う感じで使う。
返り値はtrue/false

.last()

Items.last
id順で最後のidのインスタンスを取得

Items.last(4)
最後の4つのインスタンスを取得

first_or_initializeとその類似

個人的にはfind_or_create_byの方が検索と作成保存まで1メソッドで済むので良さそう。

first_or_initializeメソッド

user = User.where(name: "hogehoge").first_or_initialize(name: "fugafufa")

whereメソッドと使うことで、whereで検索した条件のレコードがあればそのレコードのインスタンスを返す。なければ新しくインスタンスを作る。


user = User.where(nickname: "Tanaka").first_or_initialize
=> #<User id: nil, nickname: "Tanaka">
p user.nickname
=> "Tanaka" # "Tanaka"がnicknameカラムに代入されている

上記だけだと保存されてないので、最後に保存する。

user.save

.find_or_initialize_by()

引数で検索してなければnew()で作ってくれる。

user = User.find_or_initialize_by(name: "fugafufa")
if user.new_record?
  user.save
end

.first_or_create()

.first_or_initializeとほぼ同じ。whereと併用する。
違いはcreate()かnew()かの違い。
生成したインスタンスに追加情報を加えるときはinitializeで、加えないときはcreateの方がsaveしないでいいので楽。

user = User.where(name: "hogehoge").first_or_create(name: "hogehoge")

.find_or_create_by()

.find_or_initialize_by()とほぼ同じ。
違いはcreate()かnew()かの違い。
createなのでsaveしなくていい。

enum

カラム名に対して設定する。モデルファイルに記述する。
カラムはinteger型かboolean型のものが対象。

nationテーブルにprefectureカラムがある場合
enum prefecture: [:tokyo, :saitama, kanagawa]

enum prefecture: { tokyo: 5, saitama: 10, kanagawa: 3 }

[]で指定すると0から順にintが割り当てられる。
{}で渡すと番号も指定できる。ただし、自動追加はできない。

ActiveRecord Relationクラス

whereメソッドやアソシエーションを利用してDBから複数のレコードをインスタンスとして取得した場合、取得した配列はActiveRecord Relationクラスに属す。
ActiveRecord Relationクラスには、whereを始めとして複数のメソッドが準備されている。

products = Product.all
products.class #=> ActiveRecord::Relation::ActiveRecord_Relation_Product

whereメソッドやアソシエーションで、DBに当てはまるレコードが存在しない場合、返り値は空の配列になる。

modelからインスタンスを取得すると配列になる。配列内の個々のモデルにはハッシュ?

ActiveRecord 集計関数

averageメソッド

取得したモデルインスタンスの平均を出力できる。
以下の書き方をするが、カラム名をシンボルで指定する。
例)reviews.average(:rate)

モデル.average(カラム名 [, オプション])

配列に対して1つずつ中身(ハッシュ)を取り出して、シンボル指定で値を抜いている印象。
コードの実態が何をしているのかはわからないが、eachを当てているように見える。

groupメソッド

テーブルのレコードを指定したカラムでまとめる。
.group(:key)のように使う。

Review.group(:product_id)
  Review Load (10.5ms)  SELECT `reviews`.* FROM `reviews` GROUP BY product_id
=> [#<Review id: 1, nickname: "まいき", rate: 1, review: "おもしろ", product_id: 21, user_id: 4>,
  #<Review id: 3, nickname: "ちゃん", rate: 10, review: "感動", product_id: 22, user_id: 4>,
  #<Review id: 13, nickname: "ごとう", rate: 10, review: "良かった", product_id: 23, user_id: 4> ・・・

返る値はテーブル情報のハッシュ(?)が入った配列?
product_idで情報がまとめられるが、それぞれのreviewの詳細を失うわけではない。
1つ1つのproduct_idのテーブル内にネストされて格納されるイメージ。(正しいかは知らない)

countメソッド

array.count(hogehoge)

count (Array)
配列の中で引数hogehogeと同じ要素の数を返す。
引数を省略すると、要素の数を返す。

order(‘count_カラム名’).count(カラム名)

countメソッドの引数にカラム名を指定できる。
こうするとorderメソッドでcount_カラム名でのソートが可能になり、そのカラムを持つレコードの数でソートする。

product_ids = Review.group(:product_id).order('count_product_id DESC').limit(5).count(:product_id).keys

例として、reviewをproduct_idでまとめたレコードをレコード数でソートしてカウントする。
返り値として、カラム名とレコード数のハッシュで返す。
ハッシュからkeyだけ取り出すという処理がされる。

.group.countと併用した場合

ActiveRecordにおけるcountは返り値がハッシュになる。

Review.group(:product_id).count
=> {21=>2, 22=>1, 23=>4}

limit()を併用したい場合にはcount()の前につける。
count(:product_id)の後に行うと返り値がハッシュになるため使えない。

dateクラス

date = Date.new(2011, 4, 1) 

Date.today

今日を取得

date = Date.today

その他

include?()

文字列に対して使う。
()内の文字を含む場合にはtrue、含まなければfalseを返す。

str.include?("hogehoge")

.slice() or []

.sliceに引数を1つだけ与えた場合はN番目の文字を取り出す。(0始まり)
負の数を与えた場合には後ろからN番目。(こっちは-1始まり)

str = "abcdef54321"
str.slice(1) = b
str[1] = b
str.slice(-1) = 1
str[-1] = 1

引数を2つ与えた場合にはN番目からM個の文字を取り出す。
負の値の場合でも後ろからN番目から後ろ方向へM個なので注意。

str = "abcdef54321"
str[1,3] = bcd
str[-5,3] = 543

dig

ハッシュに対して使う。
ネストされたハッシュに対してキーを順に参照して値を返す。

hash={key1: {key2: {key3: "hoge"}}}
hash.dig(:key1, :key2, key3)
=> hoge

.delete()

stringに対して用い、特定の文字を削除する。

"hoge".delete("h")
=> oge

破壊的な変更はdelete!とする。
注意点としては、delete!(“hogehoge”)でhogehogeが含まれない場合、nilが返る。

ワンライナー

array = [1, 2, 3, 4, 5].map do |el| 
  if el.odd?
    el 
  end
end.compact!
array = [1, 2, 3, 4, 5].map { |el| el if el.odd? }.compact!
array = (1..5).to_a.delete_if { |el| el.even? }
array = (1..5).to_a.delete_if(&:even?)
array = [1, 2, 3, 4, 5].select{ |el| el.odd?}

delete_if

hash.delete_if(|key, value| 条件 )
array.delete_if(|element| 条件 )

ブロックの戻り値がtrueの時、要素を削除する。
ハッシュに使う場合はキーと値を削除する。

配列から複数の要素を削除

「-」メソッドで差集合が取れる。(左辺の重複要素は削られない)

a = [hoge, hoge, fuga, fuga]
b = [hoge]
c = a - b
=> [fuga, fuga]

他にも|で和集合、&で積集合(どちらも重複要素は削られます)

a = [hoge, hoge, fuga, fuga]
b = [hoge]
c = a | b
=> [hoge, fuga]
d = a & b
=> [hoge]

数値の桁区切り、通過表示

to_s(:delimited), number_with_delimiter

to_s(:delimited)だけで桁区切りは, 小数点区切りは.が与えられる。
桁区切りはdlimiterを指定することで記号を変更できる。
小数点区切りはseparator。

1000.to_s(:delimited, delimiter: ' ', separator: ',')

to_s(:currency)

数字を通貨表示に変更してくれる。

12345.to_s(:currency) #=> "12,345円"

task

定期的に実行したい処理は、lib/tasks以下のディレクトリに、rakeタスクを作成して処理を記述する。

taskの作成

rails g task task名

以下のファイルが作成される。

lib/tasks/task名.rake

namespace :task_sample do
end
namespace :user do
  desc "ユーザー情報をリセットする"
  task reset_flag: :environment do
    User.update_all(flag: false)
  end
end

実行する。

rake task user:reset_flag

helperメソッド

app/helpers/product_helper.rb

module ProductsHelper
  def converting_to_jpy(price)
    "#{price.to_s(:delimited, delimiter: ',')}円"
  end
end

よく使うGem

gemをインストールする際にはgemfileに gem ‘hogehoge’と書いて、bundle installを実行。

pry-rails

デバッグツール。
binding.pryをソースコードの中に記述することで処理を止めることができる。
止めた処理はexitで再開。exit!でサーバーを止める。
止めた時点での変数を出力したりすることで処理が正しく行われているか確認できる。

ページネーション

ページ分割のこと。
gemのkaminariを利用して実装する。

gemfileに以下を追記してbundle install. 
インストール後はrails sでサーバーを再起動します。

gem 'kaminari'

kaminariをインストールすると、pageメソッドとperメソッドを利用できる。
さらにビューのリクエストの際paramsの中にpageというキーが追加されて、その値がビューで指定したページ番号となります。

.pageでページ番号を、.perで一度に表示するデータ数を指定する。

@hogehoge = Modelclass.all.page(params[:page]).per(5)

paginateメソッドはページリンクを作る。
html.erbの必要な箇所に以下を追記する。

<%= paginate(@tweets) %>

その他

インストール済みのGemを探す

list または search は、ローカルもしくはリモートにあるGemパッケージを検索する。

gem list

ローカルサーバーの立ち上げ

rails s

サーバーを終了する場合にはcontrol+C。
ターミナルをタブで分けた方が使いやすいので、command+Tでタブを分ける。

Rails console

rails c

コンソールを起動するとターミナルからrailsのメソッドを使うことができる。
終了するときはexit

フォントファイルの置き場は

app/assets/fonts/
fontsフォルダは存在しないので作成する。

ファイルのかき分けの問題

カリキュラムでScraping用のscraping.rbはデータテーブルを扱うモデルとは別に作った。
テーブルに関するメソッドはModelファイルに書くべきでは?
=> 1度しか使わないようなメソッド等は別ファイルに書き分けるのはよくある。
モデルファイルには別ファイルのメソッドを呼び出す記述だけにとどめて可読性をあげた方がよい。
=> カリキュラムではモデルフォルダにscraping.rbを作ったことが腑に落ちない。というかスッキリしない。ファイルが増えると管理しにくくなるはず。
=> モジュールで管理するのが普通なのかも?

Rubyのモジュール

モジュールとクラスはよく似ている。クラスはデータと処理を、モジュールは処理を担当する。
モジュールを用いることで、モジュール内に定義したメソッドをインスタンスメソッドとして取り込んだり(include)、クラスメソッドとして取り込んだり(extend)できる。

名前空間の提供というもっと重要そうな点については理解が追いつかないので割愛。

README

マークダウンで記述され、ソフトウェアの仕様、規格、インストール方法などを文書化したもの。rails newをした際に自動的に作成される。

## membersテーブル

|Column|Type|Options|
|------|----|-------|
|user_id|integer|null: false, foreign_key: true|
|group_id|integer|null: false, foreign_key: true|

### Association
- belongs_to :group
- belongs_to :user

rake db:create と rails db:create

rails5からはrakeではなくrailsコマンドを使うように変更された。
rakeコマンドも使えるが、あえて使う意味はない。
rails4以前ではバージョン情報をgemファイル内に記載していたため、bundle exec rake db:**としていたが、rails5からはbundle execは不要となり、単にrails db:**でよくなった。

第2章 Toyアプリケーション - Railsチュートリアル
SNS 開発を題材にした大型チュートリアル。プロダクト開発の 0→1 を創りながら学びます。電子書籍や解説動画、質問対応、法人向けサービスも提供しています。

コラム 2.1. Rakeについてを参照。

Ynaqdh

rails gでconflictした際にでる。

Y - yes, overwrite
n - no, do not overwrite
a - all, overwrite this and all others
q - quit, abort
d - diff, show the differences between the old and the new
h - help, show this help
m - merge, run merge tool

複数形

sをつける以外の複雑な複数形にも対応されている。
company -> companies

asset pipeline

アセットファイルを管理する仕組み。
アセットファイル = javascriptやcssなどの付帯情報

sprockets

Sprocketsは、そのAsset Pipelineの基盤となるgem。
アセットファイルのパス管理やコンパイルをしてくれる。

コンパイルする際にはマニュフェストファイルと呼ばれるapplication.cssやapplication.jsなどを元にファイルを参照する。

プリコンパイル

rake assets:precompile

public/assets/以下にコンパイルされたファイルが配置される。
production環境ではassetフォルダではなく、こちらを読みに行く。

scaffold

rails g scaffold product name:string price:integer

これを実行すると以下が作成される。
要点はmigrationファイル、コントローラ、ビュー(jsonも)
さらにルーティングやアセットファイルの作成まで。

Running via Spring preloader in process 56483
      invoke  active_record
      create    db/migrate/20190820033625_create_products.rb
      create    app/models/product.rb
      invoke    test_unit
      create      test/models/product_test.rb
      create      test/fixtures/products.yml
      invoke  resource_route
       route    resources :products
      invoke  scaffold_controller
      create    app/controllers/products_controller.rb
      invoke    erb
      create      app/views/products
      create      app/views/products/index.html.erb
      create      app/views/products/edit.html.erb
      create      app/views/products/show.html.erb
      create      app/views/products/new.html.erb
      create      app/views/products/_form.html.erb
      invoke    test_unit
      create      test/controllers/products_controller_test.rb
      create      test/system/products_test.rb
      invoke    helper
      create      app/helpers/products_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/products/index.json.jbuilder
      create      app/views/products/show.json.jbuilder
      create      app/views/products/_product.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/products.coffee
      invoke    scss
      create      app/assets/stylesheets/products.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss

ベーシック認証 Basic認証

HTTP通信の規格に備え付けられているユーザー認証

authenticate_or_request_with_http_basic

下記の例ではusernameとpasswordを設定している。
ソースにベタ書きではなく、普通は環境変数を使う。

authenticate_or_request_with_http_basic do |username, password|
  username == 'admin' && password == 'password'
end

実際に動かすにはbefore actionで呼び出す。
before_actionに付けるif文は、rails5.2以前はif: “Rails.env.production?”という記法が通用したが、5.2〜はproc{}で囲まないと弾かれるようになった。

class ApplicationController < ActionController::Base
  before_action :basic_auth, if: proc{Rails.env.production?}

  private
  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == 'admin' && password == '2223'
    end
  end
end

もしくはメソッドを定義してif文に書き込む。

  before_action :basic_auth, if: :production?  

  private
  def production?
    Rails.env.production?
  end
  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end

ancestry

カテゴリ等の多階層にわたるテーブルの設計を行うgem。
インスタンスに対して.childrenを繰り返すことで多階層なテーブルを作成できる。

category = Category.create( name: "レディース")
child = category.children.create(name: "トップス")
child.children.create([{name: "Tシャツ/カットソー"},{name: "パーカー"}}])

逆に親を参照する際には.parentを使えば参照できる。

child.parent.name

session

ハッシュで情報を保持している。pramsと違いページ遷移でデータは消えない。
deviseの機能としては主にuser_idを持たせているが、他にも任意の値を入れることができる。

sessionに情報を保存する場合は以下のようにする。

session[:name] = "田中"

session自体はハッシュと似た構造なのだが、以下で代入すると単なるハッシュと見なされ、sessionとしては保存されない。(実は手抜き代入しようとしてココで嵌った)

session = {name: "satou", email: "ggg@ggg"}

paramsの内容を一旦、sessionに保存する場合

paramsは以下のクラス(ハッシュクラスからの派生クラス)

<ActionController::Parameters>

sessionは以下のクラス

#<ActionDispatch::Request::Session:0x00007fe98b371ff0 ...>

session = params.permit()
とすると、sessionという変数は<ActionController::Parameters>になってしまう。
ActionDispatchクラスのsessionとは別物な感じ。
これではページ遷移後に情報が保持されない。

paramsを.to_hでハッシュに変えて代入してやればSessionクラスとして代入できる?

session = user_information_params.to_h

なおparams.to_hでハッシュ化されるのはpermitされた項目のみ。
params.permit.to_hとしないと空のハッシュになる。

これをやって動くかと思ったが、結局sessionは維持されず。
これじゃダメみたい。

session[:key] = valueに立ち返って、eachで回して解決した。

def sign_up 
  user_params.each do |key, value|
    session[:"#{key}"] = value
  end
end

コメント