- 投稿日:2020-07-29T23:55:10+09:00
【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
- 投稿日:2020-07-29T23:11:42+09:00
不動点コンビネータを用いた無名再帰関数の実行方法まとめ
諸般の理由で『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:初版公開
- 投稿日:2020-07-29T23:11:42+09:00
不動点コンビネータを用いた無名再帰関数の実行まとめ
諸般の理由で『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:初版公開
- 投稿日:2020-07-29T23:08:54+09:00
wdayメソッドを使い指定した曜日を取得する方法
指定した日付の曜日を取得する方法について学んだのでまとめてみたいと思います。
例えば今日が何曜日かを設定したいとしてまずDateクラスtodayメソッドを使用して日付を取得します。
require "date" today = Date.todayここで注意しなければならないのがDateクラスを使う際にはrequireで呼び出さなければならないという点です。
次に日曜から土曜日までの配列を取得します。
wdays = ["日", "月", "火", "水", "木", "金", "土" ]最後にwdayメソッドで配列から今日の日付の曜日を取得します。ここでなぜ配列を日曜日から順番に定義したかというとwdayメソッドは曜日を0(日曜日)から6(土曜日)の整数で取得するからなんです。
puts wdays[today.wday] + '曜日' 火曜日繰り返しになりますがwdayメソッドは整数で値を取得しているので[today.wday](中身は本日の場合火曜日なので2)という番号で取り出す→つまり配列の中の2番目の要素である「火」が引き出されているということになります。
- 投稿日:2020-07-29T23:03:48+09:00
【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.rb
にconfig.i18n.default_locale = :ja
を記入することで日本語を利用できます。:ja
の部分を他をローケルキーに変えることで他の言語に設定することもできます。config/application.rbmodule App class Application < Rails::Application ... config.i18n.default_locale = :ja ... end end2.
config/initializers/locale.rb
に設定情報を追加
config/initializers/locale.rb
にRails.application.config.i18n.default_locale = :ja
を記入することで日本語を利用できます。config/initializers/locale.rbRails.application.config.i18n.default_locale = :ja結局どちらの方法で設定するべきなのか
結果どちらでもいい
まずRailsの実行の順序としconfig/application.rb
が先に実行されて、その後にconfig/initializers
が実行されます。
もっと具体的に言うとconfig/initializers
以下は全てのフレームワークとgemが読み込まれた後に実行されます。
そのため完璧を期すために、何らかの理由でapplication.rbファイルを使用したくない場合には後者を使った方がいいということですね。多言語の言語ファイルを導入(**.yml)(2パターン)
1.Gemの導入(Rails-i18n 等)
Rails-i18nをGemfileに追加して
bundle
(※bundle installの省略形)でOKです。
Gemで導入するとファイルが圧迫しない&手軽に数十種類の言語を導入できるのでオススメです。Gemfilegem 'rails-i18n'ターミナル$ bundle install2.手動で言語ファイル(**.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
config/locales
で階層的に多言語ファイルの管理
config/application.rb
にconfig.i18n.load_path
の設定を行う。デフォルトの設定だと
I18n.load_path += Dir[Rails.root.join('config', 'locale', '*.{rb,yml}')]
になっています。
この設定だとconfig/locales
に存在する**.ymlファイルしか読み込みません。
そのためconfig/locales
以下のすべてのディレクトリに入ってる**.ymlを読み込むようにします。config/application.rbmodule 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補足
各言語用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で使用する名前空間に関してのまとめと、ベストプラクティスの検討。
- 投稿日:2020-07-29T22:47:49+09:00
RailsプロジェクトのRubyバージョンを変更する
はじめに
Railsの既に作成済みのプロジェクトのRubyバージョンが誤っており、Rubyを再度インストールしたが途中エラーが発生したので、備忘録として残しておきます。
今回は、プロジェクトのバージョンを「2.6.3」→「2.5.3」にバージョン変更しました。
もし「間違っている」とか「もっと簡単にできる」とかあれば、ご指摘いただけますと助かります。環境
- 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を修正
Gemfilesource 'https://rubygems.org' -ruby '2.6.3' +ruby '2.5.3'ファイル上部のRubyバージョンを書き換え。
bundle installする
$ bundle install
無事にインストールが完了すれば、再度動作確認をします。
「Rails s」」でサーバーが立ち上がればOK。ちなみにダメな場合「Rails」と付くコマンドは全部アウトです。
最後に
思ったよりも簡単にバージョンを変更することができて、ちょっぴり驚き。
Railsのエラーは内容が分かりやすくて、ちょっと英語が分かる人ならだいたい解決できてしまうからスゴイ。無事バージョンを変更できたので良かったですが、できれば初めからバージョンを確認してプロジェクトをスタートさせるに越したことはない笑
今後は気をつけていこう(戒め
- 投稿日:2020-07-29T22:22:40+09:00
Super-Linterを使ってGitHub ActionsでRubyのlintを実行する
前提
Super-Linterのv3を使います。
特定の言語のlintだけ実行する
Super-Linterは、デフォルトではリポジトリに含まれるファイルのうち、Super-Linterでlintできる言語1すべてについてlintを実行する。特定の言語についてだけlintを実行するときは、次のように
VALIDATE_<言語名>: true
をenv
として指定する。例えば、Rubyのlint(すなわちRuboCop)だけ実行したいときの設定ファイルは次のとおり:
.github/workflows/linter.ymlname: 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_PATH
とRUBY_CONFIG_FILE
を設定すればプロジェクトのルートの.rubocop.yml
を直接読むように設定できるが、.github/linters/.ruby-lint.yml
を.rubocop.yml
へのシンボリックリンクとしておくこともできる。
LINTER_RULES_PATH
のデフォルト値が.github/linters
、RUBY_CONFIG_FILE
のデフォルトが.ruby-lint.yml
であることによる ↩
- 投稿日:2020-07-29T21:14:25+09:00
【Ruby】Rubyにおける二次元配列の初期化と扱い方
はじめに
Rubyのアルゴリズムを本気で学び始めた当初、二次元配列に苦しめられましたが、少しずつ扱いに慣れてきたので、備忘録&誰かの助けになればな...と思い書かせていただきます。
二次元配列とは?
配列の中に、配列が格納された形の配列のことです。
また2よりも更に大きい次元の配列を多次元配列と言います。arr.rb#一次元配列(通常の配列) colors = ["Red", "Green", "Blue"] #二次元配列 colors = [["Red", "Green", "Blue"], ["Yellow", "Purple", "Orange"]]二次元配列の初期化の方法
二次元配列を初期化(生成)する上で一つ注意点があります。
以下を参照ください。
arr.rbarr = Array.new(2, Array.new(3, 0)) #=> [[0, 0, 0], [0, 0, 0]]一見、二次元配列になっているのですが、実はこれ、二次元目の配列が全て同一のオブジェクトとして生成されてしまっています。
試しに配列の値を変更してみます。一つ目の要素(配列)の中の、真ん中の要素に「1」を代入してみます。
arr.rbarr = 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.rbarr = 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.rbarr = 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.rbarr = 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.rbint = 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"]]このように非常に直感的に操作することができます。
※コードは非常に助長だと思います。ゴメンナサイ
ご指摘等ありましたらズバズバとお願いいたします。
- 投稿日:2020-07-29T20:58:45+09:00
Rails 割と使うのにど忘れするコトをまとめてみた。
Railsを約3年程やってみて、それなりに使うけど毎日使うわけでもないのでいざ書くとなると「どう書くんだっけ?」とついド忘れしてしまう項目をまとめてみた。
Enum
個人的に「あれ・・・思い出せない」になる回数1位
記述例
menu.rbclass Menu < ApplicationRecord enum category: { japanese: 0, chinese: 1, western: 2, other: 3 } endja.ymlja: 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 => 0Scope
JavaScriptのアロー関数の様な
->
や、引数ありの場合の()
を「どこに書くだっけ」となってしまう記述例
menu.rbclass 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
member
とcollection
はどっちがidありでどっちがid無しか、とっさに分からなくなってしまう。参考:railsのroutes.rbのmemberとcollectionの違いをわかりやすく解説してみた。〜rails初心者から中級者へ〜
routes.rbresources :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
- 投稿日:2020-07-29T20:38:21+09:00
rails turorial 11章あたりでハマるだろうこと
送信するメールがエンコードされててリンクを踏めない
サインアップ時、nameに日本語を入れる
railsのログをみてみると
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
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-printablequoted-printablがデコードされる前のurlを踏んでいるので、正しいリンクに飛ばない。
解決策
本番環境ではちゃんとデコードされたリンクを踏むので、解決策とまでは言わないが一応
mail-iso-2022-jpを使う
group :development, :test do ・ ・ ・ gem 'mail-iso-2022-jp' endbundle installして
application_mailer.rbclass ApplicationMailer < ActionMailer::Base default from: 'noreply@example.com', charset: 'ISO-2022-JP' layout 'mailer' end最後に
本番環境ではちゃんとデコードしてくれるので、charset指定は変えずにokです。
参考
日本語メールの仕組み日々精進
- 投稿日:2020-07-29T20:24:41+09:00
resourcesとresourceの違いについてまとめてみた
Railsで何らかのリソースに対するCRUD処理を行うためのルーティングを生成する方法として、resourcesメソッドとresourceメソッドがあるので、今回はその違いについてまとめてみました。
resourcesとは
resourcesメソッドは、コントローラーの7つのアクション(index、show、new、create、edit、update、destroy)をひとまとめにしたメソッドで、複数のリソースに対するCRUD処理を行うためのルーティングを生成します。
config/routes.rbRails.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.rbRails.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」を使用します。
- 投稿日:2020-07-29T20:04:04+09:00
hashについて ruby
ハッシュとは
ハッシュは配列のインデックス部分をキーというものに置き換わり、バリュー(値)をそのキーに紐づいてデータを管理してくれるものです。
簡単にデータの集計とかをやってくれるので、配列でデータ集計するよりお手軽です。
例えば、下記のような処理を書いて、ユーザーのリストのようなプレーンテキストがあったとします。hash.rbname_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}個発見されました" endJoutarou 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.rbaccess_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ですまとめ
データのアクセスのしやすさと、見易さが際立っていると思います。便利ですね。
- 投稿日:2020-07-29T18:41:49+09:00
複数モデルにデータを保存したい
某メルカリのコピーサイトを作成中。
商品の画像登録にて、複数の画像登録となるとカラムが増えて美しくないので
別モデルでimageテーブルを作成することにfields_for
form_for内で異なるモデルを編集できるようになる。
例:Productモデルに紐付くImageモデル
これを使うとform_withとかで別モデルに保存とかできるようになるっぽい
アソシエーションとかで紐づいてることが前提Model
product(商品)とimageは1対多
accepts_nested_attributes_for
で一緒に保存したいモデルを指定product.rbclass Product < ApplicationRecord has_many :images accepts_nested_attributes_for :images end
imageとproduct(商品)は多対一となってます
複数保存する場合はmount_uploaders
と複数形にする必要があるので注意image.rbclass Image < ApplicationRecord belongs_to :product mount_uploaders :image, ImageUploader endController
newアクションで
build
を使用することでfield_forが使用可能にbuild
親モデルとアソシエーションを組んでいる子モデルのインスタンスを生成できるメソッド
みたいです。ちなみにhas_many(1対多)であれば
インスタンス変数.子モデル.build
で問題ありませんが、belongs_to や has_one では .build の表記では使えないらしく
親モデル.build_子モデル
例) @product.build_imageとなるそうです
products.controller.rbdef 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
ありがとうございました!!!
- 投稿日:2020-07-29T17:34:11+09:00
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には近道が用意されています。
「Enable Drive API」をクリック(このドキュメントからしかたどるルートが見つからない謎のボタン)。
プロジェクト名はなんでもいいので、そのままでNEXTをクリック。
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。
- 投稿日:2020-07-29T16:23:33+09:00
【Rails 5.0】モデルにカスタムバリデーターを実装する
はじめに
Railsのバリデーションですが、自分で作ることができます。
モデル内に自作する
Personのname属性に"ウンコ"という文字列が含まれていたらDBに登録できないようにします。
下品な名前は許しません()person.rbclass 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
を用いてエラーを格納するようにします。
true
やfalse
を返すメソッドではないということに注意しましょう。参考
- 投稿日:2020-07-29T14:15:36+09:00
Rails 5 cloud9で誤って作成したマイグレーションファイルを削除する方法
記事の趣旨 ※あくまで個人で開発されている方を対象としています
削除したマイグレーションファイルが生きている
編集してマイグレーションファイルを作成しなおした対処
どうしよう。。。。強行手段
1.$ rails db:migrate:status
1.モデルに作成されたファイルを削除しましょう。(例です)
(例)complete.rb 削除2.不要なマイグレーションファイル削除
(例)20200729035325_create_complete.rb 削除
補足
削除はファイルを選択し削除してください3.再度作り直したマイグレーションファイルを作成
(例)$ rails g model Complete user:references task:references
4.不要なファイルを削除しリセット
$ rails db:migrate:reset
補足 もしそれでもできなければ1~4の手順の後でデータベースをリセットし再度リセット
$ rails db:reset
$ rails db:migrate:reset
No-Fileは削除され必要なファイルが更新されました。
適切な方法は一度不要なファイルを落とし、確認した後に削除されることをお勧めします。
- 投稿日:2020-07-29T13:04:07+09:00
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章メモリ管理とパフォーマンスは、どこで使うのか?、そもそも何いってるかようわからんって部分が大半だったので次本書を手にするときは「なるほど!」って思いたいところです
- 投稿日:2020-07-29T05:06:52+09:00
railsのlocal環境をhttpsにしてみた。
環境
ruby 2.5.1
rails 6.0.3.2
mac catalina 10.15.5mkcert install
$ brew install mkcertlocalにCAを作成
$ mkcert -install証明書とキー発行
作成中のapplicationのフォルダへ移動
該当アプリの.gitignoreに下記追加。※Gitに挙げないため。localhost.pem localhost-key.pem以下を実行
$ mkcert localhostこれで、
localhost.pem
localhost-key.pem
の2つのファイルが出来上がります。config/puma.rbを編集
config/puma.rbif 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/