20200729のRubyに関する記事は18件です。

【Rails】メソッドの分類

一口にメソッドと言っても様々なメソッドがあるので、整理する為に記事にしました。

Railsのメソッドの原則

  • Railsではメソッドをdef〜endで定義します。
def メソッド名
end

上記以外にも以下のようなメソッドがある。

Rubyが用意してくれるメソッド

  • newメソッド、eachメソッド、sortメソッドなど

Railsが用意してくれるメソッド

Railsが用意してくれるメソッドには主に以下の2つに別れる。

①ActiveSupportコア拡張機能

  • present?メソッド、tryメソッド、時間計算を行うメソッドなど

②ActiveRecordのメソッド

  • allメソッド、saveメソッド、orderメソッドなど

オブジェクト自身が持っているメソッド

@user.nameのように、インスタンスから値を取り出すのにオブジェクト自身がもっているメソッド(ここではnameのこと)を使うことができる。

参考サイト:https://diveintocode.jp/blogs/Technology/RailsMethod01

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不動点コンビネータを用いた無名再帰関数の実行方法まとめ

諸般の理由で『Pythonのlambda式を用いたラムダ計算の基礎表現』を書いた後にHaskellに触れたところ,無名再帰関数を実行する不動点コンビネータがとんでもなく簡単に書けたため,同じ方法でSchemeやPythonでもできないか試したところ,これまたあっさりできたので,まとめメモ的に新しく記事にした.

このような内容がQiitaや書籍,ネット上に星の数の更に星の数乗ほどあることは承知しているが,この手の話はYコンビネータが大きな割合を占めており(実際,元記事でも取り上げている),関心のある人々の数多ある参考資料のひとつ程度に捉えてもらえると幸いである.ツッコミ,編集リクエスト歓迎.

不動点コンビネータの定義

Haskell,Scheme,Python,Rubyでの実行例を述べる.なお,不動点コンビネータとは,$f(g(f))=g(f)$が成り立つ関数$g$を指す.

Haskell(GHC)

式に従い,不動点コンビネータを定義する.

Prelude> g f = f (g f)

フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

Prelude> g (\fib -> \x -> \f1 -> \f2 -> if x == 0 then f1 else fib (x-1) f2 (f1+f2)) 50 0 1
12586269025
Prelude> map (\n -> g (\fib -> \x -> \f1 -> \f2 -> if x == 0 then f1 else fib (x-1) f2 (f1+f2)) n 0 1) [0..10]
[0,1,1,2,3,5,8,13,21,34,55]
Prelude> map (\n -> g (\fib -> \x -> \f1 -> \f2 -> if x == 0 then f1 else fib (x-1) f2 (f1+f2)) n 0 1) [0,10..50]
[0,55,6765,832040,102334155,12586269025]

Scheme(Gauche)

式に従い,不動点コンビネータを定義する.

gosh> (define (g f) (f (g f)))
g

フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.※注意:実行環境によっては危険!

gosh> ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) 50) 0) 1)
; いつまでも表示されず → 強制終了

引数評価を先に行うという仕様上,実際の階乗計算の前に自己適用(g f)を繰り返して無限ループに陥り,メモリを食い潰していった模様.

同様の現象はYコンビネータでも起こるため,実際に稼働するZコンビネータへの変換と同じく,(g f)を,自由変数yを引数にもつlambda式でくくることで(ラムダ計算で言うη簡約の逆適用であるη展開,実装上はクロージャの活用),呼び出されるまで実行を据え置くようにする.なお,Haskellの引数は実際に必要となるまで評価されない(遅延評価機能のひとつ)ため,このような問題が起こらない.

gosh> (define (g f) (f (lambda (y) ((g f) y))))
g

書き直した不動点コンビネータについて,フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

gosh> ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) 50) 0) 1)
12586269025
gosh> (map (lambda (x) ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) x) 0) 1)) '(0 1 2 3 4 5 6 7 8 9 10))
(0 1 1 2 3 5 8 13 21 34 55)
gosh> (map (lambda (x) ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) x) 0) 1)) '(0 10 20 30 40 50))
(0 55 6765 832040 102334155 12586269025)

Python(Python3)

式に従って不動点コンビネータを定義…したいところだが,Schemeと同じく引数評価を先に行うことがわかっているので,書き直したSchemeの定義と同じ内容の定義を行う.なお,lambda式を直接変数に代入するのはPEP8非推奨であり,内部で(クロージャで)定義した関数を返す関数を返す,という記述方法が一般的であるため,それに従った定義を行う.

>>> def g(f):
...     def ret(y):
...         return g(f)(y)
...     return f(ret)
...
>>>

フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

>>> g(lambda fib: lambda n: lambda f1: lambda f2: f1 if n == 0 else fib(n-1)(f2)(f1+f2))(50)(0)(1)
12586269025L
>>> map(lambda x: g(lambda fib: lambda n: lambda f1: lambda f2: f1 if n == 0 else fib(n-1)(f2)(f1+f2))(x)(0)(1), range(11))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>> map(lambda x: g(lambda fib: lambda n: lambda f1: lambda f2: f1 if n == 0 else fib(n-1)(f2)(f1+f2))(x)(0)(1), range(0,51,10))
[0, 55, 6765, 832040, 102334155, 12586269025L]

Ruby(Ruby 2.5.5,JRuby 9.1.17)

書き直したSchemeの定義と同じ内容の不動点コンビネータの定義,および,フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

g = -> (f) { f.( -> (y) { g.(f).(y) }) }

p g.(-> (fib) { -> (n) { -> (f1) { -> (f2) { n == 0 ? f1 : fib.(n-1).(f2).(f1+f2) } } } }).(50).(0).(1)
# => 12586269025
p (0..10).map { |x| g.(-> (fib) { -> (n) { -> (f1) { -> (f2) { n == 0 ? f1 : fib.(n-1).(f2).(f1+f2) } } } }).(x).(0).(1) }
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
p (0..50).step(10).map { |x| g.(-> (fib) { -> (n) { -> (f1) { -> (f2) { n == 0 ? f1 : fib.(n-1).(f2).(f1+f2) } } } }).(x).(0).(1) }
# => [0, 55, 6765, 832040, 102334155, 12586269025]

補足説明

無名関数,高階関数

プログラミングにおける『無名関数』とは,関数名を持たずに引数のみを持つ関数定義のことを指す.関数定義構文を用いる必要がないため,関数定義を引数として受け取れる『高階関数』への小さな任意処理の埋め込み追加などによく用いられる.なお,高階関数は,関数定義を戻り値として返すことができるものも含まれる.

再帰関数,無名再帰

無名関数の最大の欠点は,関数名がないために,『再帰関数』を定義するのも実行するのも,特別な方法が必要となることである.その方法としては,自身の引数の名前で自身を呼び出せるようにしてくれる高階関数を別途用意したり,実行環境が無名関数の場所を自動的に覚えて特別な名前で自身を呼び出せるようにしたりといったものがある(Wikipedia『無名再帰』を参照).

不動点コンビネータ

この記事では,無名再帰のための高階関数として『不動点コンビネータ』を定義する方法に焦点を当てている.不動点コンビネータには様々な関数があり,その中でも有名なのが,自身も無名関数として示されることが多いYコンビネータである.この記事では,Yコンビネータを特に意識しなくとも定義できる不動点コンビネータについて述べている.

備考

記事に関する補足

  • 概説に留めたいため,この記事では正確な定義や位置付けは述べていない.
  • PEP8非推奨は高階関数大好き人間にはやっぱりつらたん.

変更履歴

  • 2020-07-30:無名関数の例をフィボナッチ数計算に変更,Rubyの例を追加
  • 2020-07-29:初版公開
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不動点コンビネータを用いた無名再帰関数の実行まとめ

諸般の理由で『Pythonのlambda式を用いたラムダ計算の基礎表現』を書いた後にHaskellに触れたところ,無名再帰関数を実行する不動点コンビネータがとんでもなく簡単に書けたため,同じ方法でSchemeやPythonでもできないか試したところ,これまたあっさりできたので,まとめメモ的に新しく記事にした.

このような内容がQiitaや書籍,ネット上に星の数の更に星の数乗ほどあることは承知しているが,この手の話はYコンビネータが大きな割合を占めており(実際,元記事でも取り上げている),関心のある人々の数多ある参考資料のひとつ程度に捉えてもらえると幸いである.ツッコミ,編集リクエスト歓迎.

不動点コンビネータの定義

Haskell,Scheme,Python,Rubyでの実行例を述べる.なお,不動点コンビネータとは,$f(g(f))=g(f)$が成り立つ関数$g$を指す.

Haskell(GHC)

式に従い,不動点コンビネータを定義する.

Prelude> g f = f (g f)

フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

Prelude> g (\fib -> \x -> \f1 -> \f2 -> if x == 0 then f1 else fib (x-1) f2 (f1+f2)) 50 0 1
12586269025
Prelude> map (\n -> g (\fib -> \x -> \f1 -> \f2 -> if x == 0 then f1 else fib (x-1) f2 (f1+f2)) n 0 1) [0..10]
[0,1,1,2,3,5,8,13,21,34,55]
Prelude> map (\n -> g (\fib -> \x -> \f1 -> \f2 -> if x == 0 then f1 else fib (x-1) f2 (f1+f2)) n 0 1) [0,10..50]
[0,55,6765,832040,102334155,12586269025]

Scheme(Gauche)

式に従い,不動点コンビネータを定義する.

gosh> (define (g f) (f (g f)))
g

フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.※注意:実行環境によっては危険!

gosh> ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) 50) 0) 1)
; いつまでも表示されず → 強制終了

引数評価を先に行うという仕様上,実際の階乗計算の前に自己適用(g f)を繰り返して無限ループに陥り,メモリを食い潰していった模様.

同様の現象はYコンビネータでも起こるため,実際に稼働するZコンビネータへの変換と同じく,(g f)を,自由変数yを引数にもつlambda式でくくることで(ラムダ計算で言うη簡約の逆適用であるη展開,実装上はクロージャの活用),呼び出されるまで実行を据え置くようにする.なお,Haskellの引数は実際に必要となるまで評価されない(遅延評価機能のひとつ)ため,このような問題が起こらない.

gosh> (define (g f) (f (lambda (y) ((g f) y))))
g

書き直した不動点コンビネータについて,フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

gosh> ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) 50) 0) 1)
12586269025
gosh> (map (lambda (x) ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) x) 0) 1)) '(0 1 2 3 4 5 6 7 8 9 10))
(0 1 1 2 3 5 8 13 21 34 55)
gosh> (map (lambda (x) ((((g (lambda (fib) (lambda (n) (lambda (f1) (lambda (f2) (if (= n 0) f1 (((fib (- n 1)) f2) (+ f1 f2)))))))) x) 0) 1)) '(0 10 20 30 40 50))
(0 55 6765 832040 102334155 12586269025)

Python(Python3)

式に従って不動点コンビネータを定義…したいところだが,Schemeと同じく引数評価を先に行うことがわかっているので,書き直したSchemeの定義と同じ内容の定義を行う.なお,lambda式を直接変数に代入するのはPEP8非推奨であり,内部で(クロージャで)定義した関数を返す関数を返す,という記述方法が一般的であるため,それに従った定義を行う.

>>> def g(f):
...     def ret(y):
...         return g(f)(y)
...     return f(ret)
...
>>>

フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

>>> g(lambda fib: lambda n: lambda f1: lambda f2: f1 if n == 0 else fib(n-1)(f2)(f1+f2))(50)(0)(1)
12586269025L
>>> map(lambda x: g(lambda fib: lambda n: lambda f1: lambda f2: f1 if n == 0 else fib(n-1)(f2)(f1+f2))(x)(0)(1), range(11))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>> map(lambda x: g(lambda fib: lambda n: lambda f1: lambda f2: f1 if n == 0 else fib(n-1)(f2)(f1+f2))(x)(0)(1), range(0,51,10))
[0, 55, 6765, 832040, 102334155, 12586269025L]

Ruby(Ruby 2.5.5,JRuby 9.1.17)

書き直したSchemeの定義と同じ内容の不動点コンビネータの定義,および,フィボナッチ数計算を行う無名関数を用いた動作確認を行った結果は次の通り.

g = -> (f) { f.( -> (y) { g.(f).(y) }) }

p g.(-> (fib) { -> (n) { -> (f1) { -> (f2) { n == 0 ? f1 : fib.(n-1).(f2).(f1+f2) } } } }).(50).(0).(1)
# => 12586269025
p (0..10).map { |x| g.(-> (fib) { -> (n) { -> (f1) { -> (f2) { n == 0 ? f1 : fib.(n-1).(f2).(f1+f2) } } } }).(x).(0).(1) }
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
p (0..50).step(10).map { |x| g.(-> (fib) { -> (n) { -> (f1) { -> (f2) { n == 0 ? f1 : fib.(n-1).(f2).(f1+f2) } } } }).(x).(0).(1) }
# => [0, 55, 6765, 832040, 102334155, 12586269025]

補足説明

無名関数,高階関数

プログラミングにおける『無名関数』とは,関数名を持たずに引数のみを持つ関数定義のことを指す.関数定義構文を用いる必要がないため,関数定義を引数として受け取れる『高階関数』への小さな任意処理の埋め込み追加などによく用いられる.なお,高階関数は,関数定義を戻り値として返すことができるものも含まれる.

再帰関数,無名再帰

無名関数の最大の欠点は,関数名がないために,『再帰関数』を定義するのも実行するのも,特別な方法が必要となることである.その方法としては,自身の引数の名前で自身を呼び出せるようにしてくれる高階関数を別途用意したり,実行環境が無名関数の場所を自動的に覚えて特別な名前で自身を呼び出せるようにしたりといったものがある(Wikipedia『無名再帰』を参照).

不動点コンビネータ

この記事では,無名再帰のための高階関数として『不動点コンビネータ』を定義する方法に焦点を当てている.不動点コンビネータには様々な関数があり,その中でも有名なのが,自身も無名関数として示されることが多いYコンビネータである.この記事では,Yコンビネータを特に意識しなくとも定義できる不動点コンビネータについて述べている.

備考

記事に関する補足

  • 概説に留めたいため,この記事では正確な定義や位置付けは述べていない.
  • 各言語の例は,高階関数記述のチートシート代わりになるかもしれない.
  • PythonのPEP8非推奨は高階関数大好き人間にはやっぱりつらたん.

変更履歴

  • 2020-07-31:無名関数の例をフィボナッチ数計算+mapに変更,タイトル微修正
  • 2020-07-30:無名関数の例をフィボナッチ数計算に変更,Rubyの例を追加
  • 2020-07-29:初版公開
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

wdayメソッドを使い指定した曜日を取得する方法

指定した日付の曜日を取得する方法について学んだのでまとめてみたいと思います。

例えば今日が何曜日かを設定したいとしてまずDateクラスtodayメソッドを使用して日付を取得します。

require "date"
today = Date.today 

ここで注意しなければならないのがDateクラスを使う際にはrequireで呼び出さなければならないという点です。

次に日曜から土曜日までの配列を取得します。

wdays =  ["日", "月", "火", "水", "木", "金", "土" ]

最後にwdayメソッドで配列から今日の日付の曜日を取得します。ここでなぜ配列を日曜日から順番に定義したかというとwdayメソッドは曜日を0(日曜日)から6(土曜日)の整数で取得するからなんです。

puts wdays[today.wday] + '曜日'
火曜日

繰り返しになりますがwdayメソッドは整数で値を取得しているので[today.wday](中身は本日の場合火曜日なので2)という番号で取り出す→つまり配列の中の2番目の要素である「火」が引き出されているということになります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】i18nによる日本語化

はじめに

  • Railsのデフォルト言語の日本語化(2パターン)
  • 多言語のymlファイルを導入(2パターン)
  • config/localesで階層的に多言語ファイルの管理

  • 筆者のスペック
    • Rails学習歴:4ヶ月
  • 条件
    • Rails 6.0.3.2
    • Ruby 2.6.6

そのため間違いなどがある可能性があるので鵜呑み厳禁!

Railsのデフォルト言語の日本語化(2パターン)

どちらか片方の設定で日本語の言語設定は完了します。

1. config/application.rbに設定情報を追加

  • config/application.rbconfig.i18n.default_locale = :jaを記入することで日本語を利用できます。
  • :jaの部分を他をローケルキーに変えることで他の言語に設定することもできます。
config/application.rb
module App
  class Application < Rails::Application
...
    config.i18n.default_locale = :ja
...
  end
end

2.config/initializers/locale.rbに設定情報を追加

  • config/initializers/locale.rbRails.application.config.i18n.default_locale = :jaを記入することで日本語を利用できます。
config/initializers/locale.rb
Rails.application.config.i18n.default_locale = :ja

結局どちらの方法で設定するべきなのか

結果どちらでもいい
まずRailsの実行の順序としconfig/application.rbが先に実行されて、その後にconfig/initializersが実行されます。
もっと具体的に言うとconfig/initializers以下は全てのフレームワークとgemが読み込まれた後に実行されます。
そのため完璧を期すために、何らかの理由でapplication.rbファイルを使用したくない場合には後者を使った方がいいということですね。

参考文献
Railsの設定 Ruby STUDIO
Railsの多言語対応 Ruby STUDIO

多言語の言語ファイルを導入(**.yml)(2パターン)

1.Gemの導入(Rails-i18n 等)

Rails-i18nをGemfileに追加してbundle(※bundle installの省略形)でOKです。
Gemで導入するとファイルが圧迫しない&手軽に数十種類の言語を導入できるのでオススメです。

Gemfile
 gem 'rails-i18n'
ターミナル
$ bundle install

2.手動で言語ファイル(**.yml)をダウンロードしてくるor作成する

今回はGithubからRails-i18nのja.ymlをダウンロードしてくる。
※Github上からファイルを取ってくる際はRawボタンを押すと出てくるURLを使用する。

ターミナル
## wget 【ダウンロードしたいファイルのURL】 -P 【ダウンロードしたファイルを格納したい場所】
$ wget https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml -P config/locales/default

参考文献
Rails-i18nの使用例(英語)
[初学者]Railsのi18nによる日本語化対応(Qiita)

config/localesで階層的に多言語ファイルの管理

  • config/application.rbconfig.i18n.load_pathの設定を行う。

デフォルトの設定だとI18n.load_path += Dir[Rails.root.join('config', 'locale', '*.{rb,yml}')]になっています。
この設定だとconfig/localesに存在する**.ymlファイルしか読み込みません。
そのためconfig/locales以下のすべてのディレクトリに入ってる**.ymlを読み込むようにします。

config/application.rb
module Myapp
  class Application < Rails::Application
...
    # 言語ファイルを階層ごとに設定するための記述
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
...
  end
end

設定が完了すると、下記のディレクトリ構造を読み込むことができるようになります。

config/locales
|-00_defaults
|---es.rb
|---ja.rb
|-01_models
|---book
|-----es.rb
|-----ja.rb
|-99_views
|---defaults
|-----es.rb
|-----ja.rb

参考文献
Rails 国際化 (i18n) API Railsガイド

補足

引用:Railsで日本語サービスを作るときのテンプレート

各言語用ymlファイルは、01_modelsというように数値のprefixをつけたディレクトリに入れ、以下の様な構成にする。

  • config/locales/00_defaults/ja.yml
  • config/locales/01_models/ja.yml
  • config/locales/02_views/ja.yml

YAMLは同じキーがあった場合は内容を上書きするので、00などをつけて順番を選べるようにしておくと都合が良い。
辞書順に上から読み込まれて行くので、内容がコンフリクト(競合)した場合は数値が大きいほうの設定が使われる様になる。

参考するべき文献

特定のviewファイル専用の訳文を簡単に参照する
Ruby on RailsのI18nで使用する名前空間に関してのまとめと、ベストプラクティスの検討。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsプロジェクトのRubyバージョンを変更する

はじめに

Railsの既に作成済みのプロジェクトのRubyバージョンが誤っており、Rubyを再度インストールしたが途中エラーが発生したので、備忘録として残しておきます。

今回は、プロジェクトのバージョンを「2.6.3」→「2.5.3」にバージョン変更しました。
もし「間違っている」とか「もっと簡単にできる」とかあれば、ご指摘いただけますと助かります。:bow_tone1:

環境

  • macOS Catalina 10.15.6(19G73)
  • Rails 5.2.4.3
  • Ruby 2.6.3 (※変更前)
  • Ruby 2.5.3 (※変更後)

Rubyのバージョンを確認する

$ ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]

バージョンが2.6.3であることを確認。

Rubyをインストールする

$ rvm install 2.5.3

インストールが完了するまで待ちます。

RVMのRubyバージョンを設定

rvm --default use 2.5.3
Using /home/ec2-user/.rvm/gems/ruby-2.5.3

RVMのデフォルトバージョンを2.5.3に変更しておきます。

Railsサーバーを立ち上げてみる

$ rails s
Your Ruby version is 2.5.3, but your Gemfile specified 2.6.3

どうやら、「Rubyのバージョンは2.5.3やのにGemfileは2.6.3やぞ?」と言うてるみたいですね。
Gemfileのバージョンを合わせます。

Gemfileを修正

Gemfile
 source 'https://rubygems.org'
 -ruby '2.6.3'
 +ruby '2.5.3'

ファイル上部のRubyバージョンを書き換え。

bundle installする

$ bundle install

無事にインストールが完了すれば、再度動作確認をします。
「Rails s」」でサーバーが立ち上がればOK。

ちなみにダメな場合「Rails」と付くコマンドは全部アウトです。

最後に

思ったよりも簡単にバージョンを変更することができて、ちょっぴり驚き。
Railsのエラーは内容が分かりやすくて、ちょっと英語が分かる人ならだいたい解決できてしまうからスゴイ。

無事バージョンを変更できたので良かったですが、できれば初めからバージョンを確認してプロジェクトをスタートさせるに越したことはない笑
今後は気をつけていこう(戒め

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Super-Linterを使ってGitHub ActionsでRubyのlintを実行する

前提

Super-Linterのv3を使います。

特定の言語のlintだけ実行する

Super-Linterは、デフォルトではリポジトリに含まれるファイルのうち、Super-Linterでlintできる言語1すべてについてlintを実行する。特定の言語についてだけlintを実行するときは、次のようにVALIDATE_<言語名>: trueenvとして指定する。

例えば、Rubyのlint(すなわちRuboCop)だけ実行したいときの設定ファイルは次のとおり:

.github/workflows/linter.yml
name: linter

on: [push, pull_request]

jobs:
  build:
    name: Lint code base
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Lint Code Base
        uses: docker://github/super-linter:v3
        env:
          VALIDATE_RUBY: true
          DEFAULT_BRANCH: master
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

lintのルールファイルを置くパス

例えばRuboCopのルールだと、.rubocop.ymlという名前のファイルをリポジトリのルートに置くことが多い。しかし、Super-Linterがデフォルトで読むルールファイルのパスは.github/linters/.ruby-lint.ymlである2

LINTER_RULES_PATHRUBY_CONFIG_FILEを設定すればプロジェクトのルートの.rubocop.ymlを直接読むように設定できるが、.github/linters/.ruby-lint.yml.rubocop.ymlへのシンボリックリンクとしておくこともできる。


  1. https://github.com/github/super-linter#supported-linters 

  2. LINTER_RULES_PATHのデフォルト値が.github/lintersRUBY_CONFIG_FILEのデフォルトが.ruby-lint.ymlであることによる 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】Rubyにおける二次元配列の初期化と扱い方

はじめに

Rubyのアルゴリズムを本気で学び始めた当初、二次元配列に苦しめられましたが、少しずつ扱いに慣れてきたので、備忘録&誰かの助けになればな...と思い書かせていただきます。

二次元配列とは?

配列の中に、配列が格納された形の配列のことです。
また2よりも更に大きい次元の配列を多次元配列と言います。

arr.rb
#一次元配列(通常の配列)
colors = ["Red", "Green", "Blue"]

#二次元配列
colors = [["Red", "Green", "Blue"], ["Yellow", "Purple", "Orange"]]

二次元配列の初期化の方法

二次元配列を初期化(生成)する上で一つ注意点があります。

以下を参照ください。

arr.rb
arr = Array.new(2, Array.new(3, 0))
#=> [[0, 0, 0], [0, 0, 0]]

一見、二次元配列になっているのですが、実はこれ、二次元目の配列が全て同一のオブジェクトとして生成されてしまっています。
試しに配列の値を変更してみます。

一つ目の要素(配列)の中の、真ん中の要素に「1」を代入してみます。

arr.rb
arr = Array.new(2, Array.new(3, 0))
p arr
#=> [[0, 0, 0], [0, 0, 0]]

arr[0][1] = 1
p arr
#=> [[0, 1, 0], [0, 1, 0]]

#期待していた出力
#=> [[0, 1, 0], [0, 0, 0]]

このようにarr[0]に対して行った代入が、別の要素にまで代入されています。

これは最初の初期化の時点で、別々のオブジェクトとして定義できていないことによって起こります。

個別のオブジェクトとして生成する方法

ブロックを渡すことにより生成します。

arr.rb
arr = Array.new(2) { Array.new(3,0) }
p arr
#=> [[0, 0, 0], [0, 0, 0]]

arr[0][1] = 1
p arr
#=> [[0, 1, 0], [0, 0, 0]]

※補足
よりわかりやすい説明のコメントいただきましたのでそのまま引用させていただきます。
@kts_h さんありがとうございます。

メソッドの引数は、メソッド呼び出しの際に一度だけ評価されるので、Array.new(3, 0) を評価して得られた同一のArray オブジェクトが外側の配列の全ての要素になってしまうのに対し、ブロック付きメソッドのブロックは、反復するたびに評価されることがあり、Array.new { |i| ... } はその一例であるため、外側の配列の要素を作るたびに Array.new(3, 0) が評価されて、その都度、異なる Array オブジェクトが作成され、外側の配列の要素になることを明示すると、ほかの初心者の人たちに対し、より親切な説明になるのではないかと思いました。

二次元配列の操作について

一次元配列とは違い、二次元配列は次元が深くなっていて、一見複雑にも見えるのですが、非常に直感的に操作できます。

代入

初期化の際にも行いましたが、代入は以下のように行います。

arr.rb
arr = Array.new(2) { Array.new(3,0) }

arr[0][0] = 1
p arr
#=> [[1, 0, 0], [0, 0, 0]]

arr[1][2] = 1
p arr
#=> [[0, 0, 0], [0, 0, 1]]

繰り返し処理(each)

次に繰り返し処理での、扱い方です。
今回は「eachメソッド」を使用します。

arr.rb
arr = Array.new(3) { Array.new(3,0) }
arr.each do |p_a|
  p p_a
end

#=>
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]

#一番深い次元の要素を一つずつ出力する場合
arr = Array.new(3) { Array.new(3,0) }
arr.each do |p_a|
  p_a.each do |c_a|
    p c_a
  end
end

#=>
0
0
0
0
0
0
0
0
0

このように繰り返し処理を二重にかけることで、一番深い要素一つ一つにアクセスできました。

二次元配列は慣れるとロジックを組む上で便利です。
例えば、配列の中の数値でFizzBuzzを行う場合

arr.rb
int = 1
arr = Array.new(10) { Array.new(3, 0) }
arr.each_with_index do |p_a, p_index|
  p_a.each_with_index do |c_a, c_index|
    if int % 15 == 0
      arr[p_index][c_index] = "FizzBuzz"
    elsif int % 5 == 0
      arr[p_index][c_index] = "Buzz"
    elsif int % 3 == 0
      arr[p_index][c_index] = "Fizz"
    else
      arr[p_index][c_index] = int
    end
    int += 1
  end
end

p arr
#=>
[[1, 2, "Fizz"], [4, "Buzz", "Fizz"], [7, 8, "Fizz"], 
["Buzz", 11, "Fizz"], [13, 14, "FizzBuzz"], [16, 17, "Fizz"], 
[19, "Buzz", "Fizz"], [22, 23, "Fizz"], ["Buzz", 26, "Fizz"], 
[28, 29, "FizzBuzz"]]

このように非常に直感的に操作することができます。

※コードは非常に助長だと思います。ゴメンナサイ

ご指摘等ありましたらズバズバとお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 割と使うのにど忘れするコトをまとめてみた。

Railsを約3年程やってみて、それなりに使うけど毎日使うわけでもないのでいざ書くとなると「どう書くんだっけ?」とついド忘れしてしまう項目をまとめてみた。

Enum

個人的に「あれ・・・思い出せない」になる回数1位

参考:【Rails】Enumってどんな子?使えるの?

記述例

menu.rb
class Menu < ApplicationRecord
  enum category: { japanese: 0, chinese: 1, western: 2, other: 3 }
end
ja.yml
ja:
  enums:
    menu:
      category:
        japanese: 和食
        chinese: 中華
        western: 洋食
        other: その他

よく使うメソッド

# 設定を確認する
[18] pry(main)> Menu.categories
=> {"japanese"=>0, "chinese"=>1, "western"=>2, "other"=>3}
[19] pry(main)> Menu.categories["japanese"]
=> 0
[20] pry(main)> Menu.categories[:japanese]
=> 0

# 設定(日本語)を確認する
[21] pry(main)> Menu.categories_i18n
=> {"japanese"=>"和食", "chinese"=>"中華", "western"=>"洋食", "other"=>"その他"}

# 個々のインスタンスの値を確認する
[22] pry(main)> menu = Menu.last
[23] pry(main)> menu.category
=> "japanese"
[24] pry(main)> menu.category_i18n
=> "和食"
[25] pry(main)> menu.category_before_type_cast
=> 0

Scope

JavaScriptのアロー関数の様な -> や、引数ありの場合の () を「どこに書くだっけ」となってしまう

参考:Railsでよく利用する、Scopeの使い方。

記述例

menu.rb
class Menu < ApplicationRecord
  enum category: { japanese: 0, chinese: 1, western: 2, other: 3 }

  # 引数なしの場合
  scope :asian_food, -> { where(category: [Menu.categories[:japanese], Menu.categories[:chinese]]) }
end

呼び出し方

[10] pry(main)> Menu.asian_food
=> [#<Menu:0x0000560df31d6058 id: 1, category: "japanese", name: "出し巻き卵">, #<Menu:0x0000560df31d5f18 id: 2, category: "japanese", name: "味噌汁">]

ActiveRecordのSQLベタ書き

ActiveRecordのメソッドだけでは取得できないデータが欲しい時に部分的にSQLをベタ書きする場合の書き方。プレイスホルダーをどう書くのか忘れてしまう。

[13] pry(main)> Menu.where('created_at >= ?', Date.today)
=> [#<Menu:0x0000561ef0c68c38
  id: 3,
  category: "japanese",
  name: "お好み焼き",
  created_at: Sat, 25 Jul 2020 15:43:24 JST +09:00,
  updated_at: Sat, 25 Jul 2020 15:43:24 JST +09:00>]

routes

membercollection はどっちがidありでどっちがid無しか、とっさに分からなくなってしまう。

参考:railsのroutes.rbのmemberとcollectionの違いをわかりやすく解説してみた。〜rails初心者から中級者へ〜

routes.rb
resources :menus do
  collection do
    get :photo
  end

  member do
    get :reviews
  end
end

結論、id があるのが member 、無いのが collection

 photo_menus GET    /menus/photo(.:format)          menus#photo
 reviews_menu GET   /menus/:id/reviews(.:format)    menus#reviews                                                         
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails turorial 11章あたりでハマるだろうこと

送信するメールがエンコードされててリンクを踏めない

サインアップ時、nameに日本語を入れる

スクリーンショット 2020-07-29 18.53.57.png

railsのログをみてみると

スクリーンショット 2020-07-29 18.52.59.png

http://localhost:3000/account_activations/AOSiSUDhma-B1bwZe3Wd_g/edit?email=3Dhogehoge%40gmail.com=0D

クエリのemailに謎の"3D"がついている。
その他所々で謎の0Dや3Dが、、、

ログからこのリンクを踏もうとすると

User.find_by(email: '3Dhogehoge@gmail.com')

を探査してしまいrootに飛ばされる。

quoted-printable

スクリーンショット 2020-07-29 18.57.10.png

Content-Transfer-Encoding: quoted-printableが原因で
=が=3Dに変換される。

メール本文に日本語があると、7bit文字に変換できないため、
Content-Transfer-Encoding: quoted-printableが登場する。
Content-Transfer-Encoding: quoted-printabl
は8bit文字を7bit文字に変換してエンコードする。

What does 3D mean in this html email?
quoted-printable

quoted-printablがデコードされる前のurlを踏んでいるので、正しいリンクに飛ばない。

解決策

本番環境ではちゃんとデコードされたリンクを踏むので、解決策とまでは言わないが一応

mail-iso-2022-jpを使う

group :development, :test do
       ・
       ・
       ・
  gem 'mail-iso-2022-jp'
end

bundle installして

application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: 'noreply@example.com',
  charset: 'ISO-2022-JP'
  layout 'mailer'
end

最後に

本番環境ではちゃんとデコードしてくれるので、charset指定は変えずにokです。

参考
日本語メールの仕組み

日々精進

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

resourcesとresourceの違いについてまとめてみた

Railsで何らかのリソースに対するCRUD処理を行うためのルーティングを生成する方法として、resourcesメソッドとresourceメソッドがあるので、今回はその違いについてまとめてみました。

resourcesとは

resourcesメソッドは、コントローラーの7つのアクション(index、show、new、create、edit、update、destroy)をひとまとめにしたメソッドで、複数のリソースに対するCRUD処理を行うためのルーティングを生成します。

config/routes.rb
Rails.application.routes.draw do
 resources :games
end

#リソース名が複数形

上記のように記載すると、7つのアクション(index、show、new、create、edit、update、destroy)全てを設定したことと同じになります。
確認のため、ターミナルにてrails routesコマンドを入力してみると、以下のルーティングが生成されています。

ターミナル
   Prefix Verb   URI Pattern               Controller#Action
    games GET    /games(.:format)          games#index
          POST   /games(.:format)          games#create
 new_game GET    /games/new(.:format)      games#new
edit_game GET    /games/:id/edit(.:format) games#edit
     game GET    /games/:id(.:format)      games#show
          PATCH  /games/:id(.:format)      games#update
          PUT    /games/:id(.:format)      games#update
          DELETE /games/:id(.:format)      games#destroy

リソースが複数なので、/gamesに対するGETリクエストが返すのはリソースの一覧画面(indexアクション)であることが分かると思います。

resourceとは

resourceメソッドは、コントローラの7つのアクション(index、show、new、create、edit、update、destroy)に対して、indexとid付きのパスが生成されません。
resourceは、ただ1つのリソースに対するCRUD処理を行うためのルーティングを生成します。

config/routes.rb
Rails.application.routes.draw do
 resource :game
end

#リソース名が単数形

こちらも確認のため、ターミナルにてrails routesコマンドを入力してみると、以下のルーティングが生成されています。

ターミナル
   Prefix Verb   URI Pattern          Controller#Action
 new_game GET    /game/new(.:format)  games#new
edit_game GET    /game/edit(.:format) games#edit
     game GET    /game(.:format)      games#show
          PATCH  /game(.:format)      games#update
          PUT    /game(.:format)      games#update
          DELETE /game(.:format)      games#destroy
          POST   /game(.:format)      games#create

こちらの場合は、リソースは1つだけなのでurlに:idを含む必要がありません。そして、一覧画面(indexアクション)が必要ないので、/gameに対するGETリクエストはそのリソースの表示(showアクション)であることが分かると思います。

まとめ

「resourcesメソッド」はidが付与されているので、複数あるリソースの中からidを用いて特定のリソースを絞り込むことができます。

その一方で「resourceメソッド」にはidが付与されないので、リソースを絞り込まない状況で使用します。

まとめると、複数存在するリソース(商品やユーザーなど)では「resources」を使用し、1つしか存在しないリソースには「resouece」を使用します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

hashについて ruby

ハッシュとは

ハッシュは配列のインデックス部分をキーというものに置き換わり、バリュー(値)をそのキーに紐づいてデータを管理してくれるものです。

簡単にデータの集計とかをやってくれるので、配列でデータ集計するよりお手軽です。
例えば、下記のような処理を書いて、ユーザーのリストのようなプレーンテキストがあったとします。

hash.rb
name_count = {}
STDIN.each do |line|
    line.chomp!
    count = name_count[line] ||  0

    name_count[line] = count + 1
end

p name_count

name_count.keys.sort.each do |name|
    value = name_count[name]
    puts "#{name}#{value}個発見されました"
end
Joutarou 
Noriaki
Polnareff
Avdol
Joutarou 
Noriaki
Joseph
Igy
Joutarou 
Noriaki
Joutarou 
Polnareff
Avdol
Joutarou 
Joseph
Joseph
Polnareff
Avdol
Joseph
Igy
Joutarou 
Igy
Joutarou 
Igy
Joutarou 
Noriaki
Joseph
Igy
Noriaki
Joseph
Igy

実行してみます。

$ ruby hash.rb < logfile
{"Joutarou "=>8, "Noriaki"=>5, "Polnareff"=>3, "Avdol"=>3, "Joseph"=>6, "Igy"=>6}
Avdolが3個発見されました
Igyが6個発見されました
Josephが6個発見されました
Joutarou が8個発見されました
Noriakiが5個発見されました
Polnareffが3個発見されました

出力結果の1番上にあるのが、ハッシュのデータそのものになります。キーとバリューがセットになって作られているのがわかります。

ハッシュで複数のバリューを持たせたい・・・

 データを管理する方法として、先のコードでやったように集計も楽であることから配列のように一気に順番に出す以外で使うことがあるようなデータを取り扱う場合、ハッシュの方が便利にように思います。
 配列でインデックスに複数配列を作ることもできますが、データ内へのアクセスの面倒さや、コードにした時の可読性にも難があります。ハッシュであれば、その両方をクリアできるように思います。例えば、ユーザー名に対して、今月のログイン数と、累計のログイン数のデータをセットで持たせたい場合、ハッシュの中にハッシュを組むことで、分かりやすくデータを管理することができます。1つのキーにハッシュが紐づいているというイメージです。

hash_hash.rb
access_hash = {
    "Joutarou"  => 1,
    "Avdol" => 3,
    "Kakyouin" => 2,
    "Joseph"  => 8
}

  ruikei_hash = {
    "Joutarou" => 12,
    "Avdol" => 12,
    "Kakyouin" => 100,
    "Joseph" => 22
  }

  matome_hash = {}

  access_hash.keys.each do |name|
    matome_hash[name] = {
      "access" => access_hash[name],
      "ruikei" => ruikei_hash[name]
    }
  end

  p matome_hash

  p matome_hash["Joutarou"]["ruikei"]

  matome_hash.keys.sort.each do |name|
    value = matome_hash[name]["access"]
    puts "#{name}#{value}です"
  end

出力結果は下記になります。

$ ruby hash_hash.rb
{"Joutarou"=>{"access"=>1, "ruikei"=>12}, "Avdol"=>{"access"=>3, "ruikei"=>12}, "Kakyouin"=>{"access"=>2, "ruikei"=>100}, "Joseph"=>{"access"=>8, "ruikei"=>22}}
12
Avdolが3です
Josephが8です
Joutarouが1です
Kakyouinが2です

まとめ

データのアクセスのしやすさと、見易さが際立っていると思います。便利ですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数モデルにデータを保存したい

某メルカリのコピーサイトを作成中。
商品の画像登録にて、複数の画像登録となるとカラムが増えて美しくないので
別モデルでimageテーブルを作成することに

fields_for

form_for内で異なるモデルを編集できるようになる。

例:Productモデルに紐付くImageモデル

これを使うとform_withとかで別モデルに保存とかできるようになるっぽい
アソシエーションとかで紐づいてることが前提

Model

product(商品)とimageは1対多
accepts_nested_attributes_for で一緒に保存したいモデルを指定

product.rb
class Product < ApplicationRecord
  has_many :images
  accepts_nested_attributes_for :images
end



imageとproduct(商品)は多対一となってます
複数保存する場合は mount_uploaders と複数形にする必要があるので注意

image.rb
class Image < ApplicationRecord
  belongs_to :product
  mount_uploaders :image, ImageUploader
end

Controller

newアクションで build を使用することでfield_forが使用可能に

build

親モデルとアソシエーションを組んでいる子モデルのインスタンスを生成できるメソッド

みたいです。ちなみにhas_many(1対多)であれば

インスタンス変数.子モデル.build

で問題ありませんが、belongs_to や has_one では .build の表記では使えないらしく

親モデル.build_子モデル  例) @product.build_image

となるそうです

products.controller.rb
  def new
    @product = Product.new
    @product.images.build
  end

  def create
    @product = Product.create(product_params)
  end

  def product_params
    params.require(:product).permit(images_attributes: {image: []})
  end

またパラメーター内では
モデル名_attributes: と記述します

これも送るデータが一つか複数かで記述が変わり、一つの場合は

images_attributes: [:image]

こんな感じ

View

html.haml
    .Contents
      = form_with model: @product, html: {class: "Form"}, local: true do |f|
        .InputPhoto
          .InputPhoto__DropArea
            = f.fields_for :images do |c|
              = c.label :image
              = c.file_field :image, multiple: true

これでとりあえず画像の保存は完了!

次はjqueryか...


参考記事
https://qiita.com/kouuuki/items/5daf2b5f34273d8457f7
https://qiita.com/nakasato_minami/items/5015319292c9f8a93f34
https://qiita.com/mihou/items/2d7504c0bf98d07a95c3
ありがとうございました!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ruby で GoogleDriveにアクセスする方法 2020年度版

GoogleDriveへのAPIアクセスがしょっちゅう変わってるので、2020年度版をご紹介。

サマリー

  • QuickStart のページだけで完結できる!
  • クライアントタイプは「Desktop app」を選べ!
  • 旧バージョンの方法はもはや使えないので、適当にググると死ぬ!QuickStart と公式ドキュメント以外見るな!

GoogleCloudの管理画面にアクセスして、やれプロジェクト作れだとかやれサービスアカウントを作れだとかいろいろ選択肢が多いので、「このルート」というのを用意しました。

QuickStart のページにアクセス

https://developers.google.com/drive/api/v3/quickstart/ruby

このページの上から行くのが一番素直です。

Enable the Drive API のボタンをクリック

本当はGoogleCloudのコンソールから通るのがいいはずなのですが、めちゃくちゃ面倒なので、QuickStartには近道が用意されています。

image.png

「Enable Drive API」をクリック(このドキュメントからしかたどるルートが見つからない謎のボタン)。

image.png

プロジェクト名はなんでもいいので、そのままでNEXTをクリック。

image.png

APIクライアントの選択肢が色々あるんだけど、Rubyプログラムから使うときには、「Desktop App」を選択した状態で、CREATE。

これで、credentials.json というファイルがダウンロードできる。
中身は

credentials.json
{"installed":{"client_id":"...

ではじまるJSONファイル。

あとは、QuickStartの言うなりで大丈夫。

どういうAPIがあるかは、本家APIリファレンスの

https://github.com/googleapis/google-api-ruby-client

を丹念にみればOK。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails 5.0】モデルにカスタムバリデーターを実装する

はじめに

Railsのバリデーションですが、自分で作ることができます。

モデル内に自作する

Personのname属性に"ウンコ"という文字列が含まれていたらDBに登録できないようにします。
下品な名前は許しません()

person.rb
class Person < ApplicationRecord
  before_save :include_unko

  private
  # Personモデルでしか使わないのでPrivateメソッドにしておく
  def include_unko
    if name.include?("ウンコ")
      errors.add(:name, "に下品な言葉が含まれています")
    end
  end

end

# キャンチョメとウンコティンティンをPersonに登録してみましょう。
person_1 = Person.create(name: "キャンチョメ")
person_1.valid? # => true
person_1.erros # => nil

person_2 = Person.create(name: "ウンコティンティン")
person_2.valid? # => false
person_2.errors.full_message
# => ["Name に下品な言葉が含まれています"]

カスタムバリデーターの戻り値について

valid?(もしくはinvalid?)メソッドを使って、バリデーションを手動でトリガできます。
バリデーションメソッドを実行した後、オブジェクト(今回ではperson_1とperson_2)にエラー(errors)がない場合はtrueが返され、そうでなければfalseが返されます。
したがって、カスタムバリデーターを実装したら必ずerrors.addを用いてエラーを格納するようにします。
truefalseを返すメソッドではないということに注意しましょう。

参考

Active Record バリデーション - Railsガイド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 5 cloud9で誤って作成したマイグレーションファイルを削除する方法

記事の趣旨 ※あくまで個人で開発されている方を対象としています

削除したマイグレーションファイルが生きている
編集してマイグレーションファイルを作成しなおした

対処

299b83e2ab2fec0c25f32edba537db10.png

どうしよう。。。。強行手段

1.$ rails db:migrate:status

a51f3909b9dfe0d66a0e5c910853ba46.png

1.モデルに作成されたファイルを削除しましょう。(例です)
 
(例)complete.rb  削除

2.不要なマイグレーションファイル削除

(例)20200729035325_create_complete.rb 削除

補足
削除はファイルを選択し削除してください

658fb17c679b9dcd39054c5d26cf884a.png

3.再度作り直したマイグレーションファイルを作成

(例)$ rails g model Complete user:references task:references

4.不要なファイルを削除しリセット

$ rails db:migrate:reset

補足 もしそれでもできなければ1~4の手順の後でデータベースをリセットし再度リセット

$ rails db:reset

$ rails db:migrate:reset

1ff2a9671ba84f19a7f238ce246b20d0.png

No-Fileは削除され必要なファイルが更新されました。

適切な方法は一度不要なファイルを落とし、確認した後に削除されることをお勧めします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Effective Rubyを読んで勉強になったこと

はじめに

Effective Rubyを私が読んで勉強になったことを書いてます。

本書を読んだのは2回目ですが、1回目は何がどう役立つのか全くわかりませんでした。
2回目の今回もいまだに「これって一体どういった場面で効果的なの?」、「そもそも何いってんのこれ?」って部分は多数というかそっちの方が多い気もしますが、以前より「なるほど!」と思った部分があったので、「なんかコーディング力上がってない気がする」って悶々としている人は読んでみると発見があるかもしれません。

本全体の内容を紹介するものではなく
RoRを書いていて、「あ〜こう書けば良かったんだ!」と自分のアンテナに引っかかったところだけを書いていくので、本全体の内容を知りたい方は以下の記事など参照すると良いと思います。
https://qiita.com/fujigaki/items/9d64fa49efcbe7017d8c

勉強になったこと

項目2 オブジェクトを扱うときにはnilかもしれないことを忘れないようにしよう

undefined method 'XXXX' for nil:NilClass(NoMethodError)

以前は、RoRしか経験がなく特に気になってませんでしたが、Typescriptではじめて静的型付き言語を触ってから嫌いになったエラーの1つです。
上記のエラーが意図せず発生しないように、nilになる可能性をできる限り排除しましょうってことが大事で、すぐにできることとして

[4] pry(main)> nil.to_a
=> []
[5] pry(main)> nil.to_s
=> ""
[6] pry(main)> nil.to_i
=> 0

があげられます。
このように型付けがないのをカバーするためにto_aやto_sに代表される変換メソッドを入れておくことでnilが排除されるので、よりエラーが発生しにくいコードになるってことですね。

項目8 サブクラスを初期化するときはsuperを呼び出そう

[7] pry(main)> class Parent
[7] pry(main)*   attr_accessor(:name)  
[7] pry(main)*   def initialize  
[7] pry(main)*     @name = "サンプル太郎"    
[7] pry(main)*   end    
[7] pry(main)* end  
=> :initialize
[8] pry(main)> 
[9] pry(main)> class Child < Parent
[9] pry(main)*   attr_accessor(:age)   
[9] pry(main)*   def initialize  
[9] pry(main)*     @age = 14    
[9] pry(main)*   end    
[9] pry(main)* end  
=> :initialize
[10] pry(main)> 
[11] pry(main)> child  = Child.new
=> #<Child:0x00007fcf69ed6a08 @age=14>
[12] pry(main)> child.name
=> nil

上記のようにChildクラスにParentを継承していた時にChildをnewしてもParentのnameにはアクセスできないんですよね。
最近、これと全く同じことをしてなんでアクセスできない?って思って調べた結果superが必要だったと思い出しました。
「あーこれこの間ハマったやつや!」って思ったので改めて。

[13] pry(main)> class Child < Parent
[13] pry(main)*   attr_accessor(:age)   
[13] pry(main)*   def initialize  
[13] pry(main)*     super()    
[13] pry(main)*     @age = 14    
[13] pry(main)*   end    
[13] pry(main)* end  
=> :initialize
[14] pry(main)> Child.new
=> #<Child:0x00007fcf5cfda718 @age=14, @name="サンプル太郎">

項目11 構造化データの表現にはHashではなくStructを使おう

Typescriptを触っていて例えばUserの配列を作ろうとしたとき

type User = {
  id: string
  name: string
  address: string
}
const users: User[] = []

Userって型を作ってその配列だよ!って型宣言してたんですが、これと似たようなことだと認識してます。

[2] pry(main)> sample_users = [
[2] pry(main)*   { id: "1", name: "太郎", age: 8, nickname: "とっとこ太郎" }, 
[2] pry(main)*   {id: "2", name: "花子", age: 11, nickname: "トイレの花子"} 
[2] pry(main)* ]  
=> [{:id=>"1", :name=>"太郎", :age=>8, :nickname=>"とっとこ太郎"}, {:id=>"2", :name=>"花子", :age=>11, :nickname=>"トイレの花子"}]
[3] pry(main)> 
[4] pry(main)> array = []
=> []
[5] pry(main)> 
[6] pry(main)> sample_users.each do |user|
[6] pry(main)*   array << { name: user[:name], nickname: user[:nickname] } 
[6] pry(main)* end  
=> [{:id=>"1", :name=>"太郎", :age=>8, :nickname=>"とっとこ太郎"}, {:id=>"2", :name=>"花子", :age=>11, :nickname=>"トイレの花子"}]
[7] pry(main)> 
[8] pry(main)> array[0][:missing]
=> nil

見て分かる通り、missingなんてkeyがないので、本来ならエラー発生してほしいんですが、nilが返ってきてしまいます。
これだと仮にタイポしてたとしても気づかないってことが。
それを防ぐために、Structで型を定義してそれをarrayに詰めようって話ですね。

[19] pry(main)> SampleUser = Struct.new(:name, :nickname)
=> SampleUser
[23] pry(main)> array = []
=> []
[24] pry(main)> sample_users.each do |user|
[24] pry(main)*   array << SampleUser.new(user[:name], user[:nickname])
[24] pry(main)* end  
=> [{:id=>"1", :name=>"太郎", :age=>8, :nickname=>"とっとこ太郎"}, {:id=>"2", :name=>"花子", :age=>11, :nickname=>"トイレの花子"}]

[26] pry(main)> array
=> [#<struct Sample name="太郎", nickname="とっとこ太郎">, #<struct Sample name="花子", nickname="トイレの花子">]
[28] pry(main)> array[0]
=> #<struct Sample name="太郎", nickname="とっとこ太郎">
[29] pry(main)> array[0][:missing]
NameError: no member 'missing' in struct
[30] pry(main)> array[0][:name]
=> "太郎"
[31] pry(main)> array[0].name
=> "太郎"
[32] pry(main)> array[0].missing
NoMethodError: undefined method `missing' for #<struct Sample name="太郎", nickname="とっとこ太郎">
from (pry):36:in `<main>'

なるほど、nilが返ってこなくなりエラーが発生!これはいいですね!

項目19 reduceを使ってコレクションを畳み込む方法を身に付けよう

users = [{ name: "太郎", age: 22 }, { name: "花子", age: 11 }, { name: "大吉", age: 55 }]

ここから21才以上ののname取り出したい時って

[45] pry(main)> users.select { |u| u[:age] >= 21}.map{|u| u[:name]}
=> ["太郎", "大吉"]

って書くと思うのですが、これってselectで21才以上の新しい配列を作ってさらにそこにnameだけを抽出した新しい配列をつくるという無駄な処理があります。

ここでreduce使うと

[48] pry(main)> users.reduce([]) do |names, user|
[48] pry(main)*   names << user[:name] if user[:age] >= 21  
[48] pry(main)*   names
[48] pry(main)* end  
=> ["太郎", "大吉"]
[49] pry(main)> 

うん。シンプル。注意としてはアキュムレーターををしっかりとブロックの中で返してあげることですね。

感想

2回目読んでみましたが、リファクタリングに使えそうなTipsがあって読んで良かったと思います。
正直、第5章メタプログラミングと第8章メモリ管理とパフォーマンスは、どこで使うのか?、そもそも何いってるかようわからんって部分が大半だったので次本書を手にするときは「なるほど!」って思いたいところです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsのlocal環境をhttpsにしてみた。

環境

ruby 2.5.1
rails 6.0.3.2
mac catalina 10.15.5

mkcert install

Git mkcert

$ brew install mkcert

localにCAを作成

$ mkcert -install

証明書とキー発行

作成中のapplicationのフォルダへ移動
該当アプリの.gitignoreに下記追加。※Gitに挙げないため。

localhost.pem
localhost-key.pem

以下を実行

$ mkcert localhost

これで、
localhost.pem
localhost-key.pem
の2つのファイルが出来上がります。

config/puma.rbを編集

config/puma.rb
if ENV.fetch('RAILS_ENV') { 'development' } == 'development'
  ssl_bind '0.0.0.0', '9292', key: './localhost-key.pem', cert: './localhost.pem'
end

以上で準備終了

rails起動して以下をポチっ
https://localhost:9292/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む