20210512のRubyに関する記事は23件です。

[Ruby]リフレクションマスターになろう!

リフレクションとは? プログラム実行中に、そのプログラムの情報を取得したり操作したりすること。 デバッグや、正しく定義できてるかの確認など開発をする上で欠かせないものだと思うので、きっちり使えるようにした方が開発スピードは向上します。 オブジェクトについて調べる オブジェクトが持つインスタンス変数にアクセス class Foo def initialize @foo = 1 @bar = 2 end end foo = Foo.new # インスタンス変数名の確認 foo.instance_variables #=>[:@foo, :@bar] # インスタンス変数の存在確認(@は必須) foo.instance_variable_defined?(:@foo) #=> true # インスタンス変数の値を参照 foo.instance_variable_get(:@foo) #=> 1 # インスタンス変数の値を更新(存在しない場合は新しく定義) foo.instance_variable_set(:@foo, 2) #=> 2 オブジェクトが持つメソッドにアクセス # 親クラスを定義 class ParentClass def super_public; end private def super_private; end protected def super_protected; end end # 子クラスを定義 class ChildClass < ParentClass def sub_public; end def hello puts 'Hello' end private def sub_private; end protected def sub_protected; end end # 特異クラスを定義 def Child.new.singleton; end child = Cgild.new # プライベートメソッド以外を取得 child.methods #=> [:singleton, :sub_public, sub_protected, ...] # パブリックメソッドを取得 child.public_methods #=> [:singleton, :sub_public, ...] # プライベートメソッドを取得 child.private_methods #=> [:sub_private, :super_private, ...] # プロテクテッドメソッドを取得 child.protected_methods #=> [:sub_protected, :super_protected] # 特異メソッドを取得 child.singleton_methods #=> [:singleton] child.methods(false) #=> [:singleton] # メソッドが定義されている確認 child.respond_to?(:sub_public) #=> true # メソッドの呼び出し child.send(:hello) #=> "Hello" クラスについて調べる クラスが持つ値にアクセス class Foo @@foo = 'foo' FOO = 'foo' def bar @@bar = 'bar' end end # クラス変数を取得する Foo.class_variables #=> [:@@foo] # クラス変数が定義されているか確認(@@必須) Foo.class_variable_defined?(@@foo) #=> true # クラス変数の値を取得 Foo.class_variable_get(@@foo) #=> "foo" # クラス変数に値を設定(存在しない場合は新しく作成) Foo.classvariable_set(@@foo, 'set_foo') #=> "set_foo" # クラス内に定義されている定数を確認 Foo.constants #=> [:FOO] # 定数の値を取得する Foo.const_get(:FOO) #=> "foo" # 定数に値を設定(存在しない場合は新しく作成) Foo.const_set(:FOO, 'set_foo') #=> "set_foo" クラスが持つメソッドにアクセス # 親クラスを定義 class ParentClass def super_public; end private def super_private; end protected def super_protected; end end # 子クラスを定義 class ChildClass < ParentClass def sub_public; end def hello puts 'Hello' end private def sub_private; end protected def sub_protected; end end # プライベートメソッド以外のインスタンスメソッドを取得 ChildClass.instance_methods #=> [:sub_public, :sub_protected, :super_public, ...] # パブリックメソッドを取得 ChildClass.public_instance_methods #=> [:sub_public, :super_public, ...] # プライベートメソッドを取得 ChildClass.private_instance_methods #=> [:sub_private, :super_private, ...] # プロテクテッドメソッドを取得 ChildClass.protected_instance_methods #=> [:sub_protected, super_protected] # 指定されたメソッドがプライベートメソッド以外に定義されているか確認 ChildClass.method_defined?(:sub_public) #=> true # 指定されたメソッドがパブリックメソッドに定義されているか確認 ChildClass.public_method_defined?(:sub_public) #=> true # 指定されたメソッドがプライベートメソッドに定義されているか確認 ChildClass.private_method_defined?(:sub_private) #=> true # 指定されたメソッドがプロテクテッドメソッドに定義されているか確認 ChildClass.protected_method_defined?(:sub_protected) #=> true # クラスの継承構造を参照 ChildClass.ancestors #=> [ChildClass, ParentClass, ...] # スーパークラスを参照 ChildClass.superclass #=> ParentClass おわりに 「リフレクション」という聞き慣れない言葉ではあったかと思いますが、内容は結構日頃から似たようなことをしているかと思います。 いろんな操作があるので、必要な情報を取得したり設定できるようにしておきましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails DM機能 メッセージ編

class MessagesController < ApplicationController before_action :authenticate_user!, :only => [:create] def create #room_idには@room.idを渡される(二人がDM時に開いているルームのID) if Entry.where(:user_id => current_user.id, :room_id => params[:message][:room_id]).present?  #ログインしているユーザーの、二人がDM時に開いているルームに入った情報があるか[:message]には作ったメッセージに渡されたルームIDとコンテンツが入っている @message = Message.create(params.require(:message).permit(:user_id, :content, :room_id).merge(:user_id => current_user.id)) redirect_to "/rooms/#{@message.room_id}" else redirect_back(fallback_location: root_path) end end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsにてlink_toの文字色を変える

link_toにて各リンク先を作成したが、デフォルトの色が青色なのでこの色を変えたいと思います。 これはスキンケア用品に関するクチコミサイトですが、色合いがおかしいです。 やり方は簡単で、link_toの末尾にclass指定して、cssで変更します。 view <%= link_to category.category_name, categories_path(category_id: category.id) %> これを、 <%= link_to category.category_name, categories_path(category_id: category.id, class:link) %> scss .link { text-decoration: none; color: white ; } これで文字色の指定ができます。 文字色以外もこのscss上で編集できます。 以上、link_to内にclassを作るのがポイントでした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails でDM機能 rooms編

rooms コントローラー class RoomsController < ApplicationController before_action :authenticate_user! def create @room = Room.create  エントリー情報は一つのルームにつき二つ @entry1 = Entry.create(:room_id => @room.id, :user_id => current_user.id) #ログインしているユーザーのエントリー情報を登録 @entry2 = Entry.create(params.require(:entry).permit(:user_id, :room_id).merge(:room_id => @room.id)) #showで受け取った情報 redirect_to "/rooms/#{@room.id}" #roomのshowへ end def show @room = Room.find(params[:id]) #二通りのもらい方 新しいのか前のか if Entry.where(:user_id => current_user.id, :room_id => @room.id).present? #エントリー情報があるか @messages = @room.messages #ルームが持つメッセージ @message = Message.new  @entries = @room.entries #ルームはエントリーを二つもっている。 else #なければ違う人のルームに入ろうとしているということ redirect_back(fallback_location: root_path) end end end ルームのshowビュー <% @entries.each do |e| %> 二つのエントリー情報をもとに、アソシエーションつたってIDとe-mailを取得 <% end %> <% if @messages.present? %> #messagesはルームにあるすべてのメッセージ <% @messages.each do |m| %> <%= m.content %> by メッセージはまだありません <% end %> <%= form_for @message do |f| %> # @messageはカラのメッセージ <%= f.text_field :content, :placeholder => "メッセージを入力して下さい" , :size => 70 %> <%= f.hidden_field :room_id, :value => @room.id %> <%= f.submit "投稿する" %> <% end %> <%= link_to "ユーザー一覧に戻る", users_path %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby技術者認定試験silverで中々覚えにくいやつメモ

表題の通りです。Rubysilver取得の学習中ですが、なんか覚えにくいなあというものをここに吐き出せばきっと覚えられるんじゃないか、と思ったので。 RExとかで9割超えてきたし、合格教本の問題も9割くらいできるようになったのでそろそろ受けようかなと思っているが、まあ最後の詰めです。 Stringオブジェクト strip 文字列の先頭と末尾の空白文字(\t\r\n\f\v)を取り除く。 chomp 末尾から改行コードを取り除く。 chop 末尾の文字を取り除きます。ただし、文字列の末尾が"\r\n"であれば、2文字とも取り除く。 上記について破壊的に実行したい場合は!をつける Arrayオブジェクト pop selfの末尾より1要素を取り出す(LIFO)。 push | selfの末尾に引数の値を追加します(LIFO)。 unshift | selfの先頭へ引数の値を追加します(FIFO)。引数が指定されていない場合は何もしない shift | selfの先頭より1要素を取り出します(FIFO)。 上記はすべて破壊的に実行される。 ファイル操作関連 File.openの2番目の引数について r 読み込み。 w 書き込み。既存ファイルの場合はファイルの内容が空になる a 追記。追加位置はファイルの末尾 r+ 読み書き。ファイルの読み書き位置は先頭 w+ 読み書き。既存ファイルの場合はファイルの内容が空になる a+ 読み書き。ファイルの読み込み位置は先頭、書き込み位置は末尾 ファイルポインタのseekメソッドの引数について IO::SEEK_SET ファイルの先頭からの位置を表す IO::SEEK_CUR 現在のファイルポインタからの位置を表す IO::SEEK_END ファイルの末尾からの位置を表す
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの配列について

配列とは複数のデータ(要素)をまとめて格納できるオブジェクトのことです。 配列内のデータは順番に並んでおり添字を指定することでデータを取り出すことができます。 添字とは配列の各要素に割り振られた番号のことです。これは0から始まります。 書き方としては [データ1,データ2,データ3] と書きます。 データの取り出し方としては ターミナル irb(main):002:0> a = [1,2,3,"aaa"] => [1, 2, 3, "aaa"] まずirbモードにして変数aに配列を定義します。 次に先頭の1を取り出します。 ターミナル irb(main):004:0> a[0] => 1 配列の添字は0から始まるのでa[0]となります。 次に配列aは空であるかを確認する方法としては ターミナル irb(main):005:0> a.empty? => false aには要素が入っているのでfalseと返ってきます。 aの中に特定の値が入っているか確認するときは ターミナル irb(main):007:0> a.include?('aaa') => true aaaは入っているのでtrueが返ってきます。 ターミナル irb(main):008:0> a.reverse => ["aaa", 3, 2, 1] irb(main):009:0> a => [1, 2, 3, "aaa"] irb(main):010:0> a.reverse! => ["aaa", 3, 2, 1] irb(main):011:0> a => ["aaa", 3, 2, 1] reverseと打つと配列の要素を逆に返すことはできますが、並び替えられていません。 reverse!と打つことで配列内の要素を逆に並び替えることができます。 ターミナル irb(main):012:0> a.shuffle => [3, 1, "aaa", 2] shuffleと打つとランダムに順番を並び替えることができます。 配列内に多数の要素を定義したいときは範囲オブジェクトというものがあります。 ターミナル irb(main):013:0> (0..25).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] 0..25とすることでこの範囲を一気に指定することができます。 ターミナル irb(main):014:0> aa = (0..99).to_a.shuffle => [76, 62, 67, 35, 60, 65, 26, 68, 69, 58, 25, 39, 73, 55, 43, 41, 54, 0, 37, 70, 1, 34, 95, 27, 92, 53, 56, 82, 18, 36, 44, 49, 84, 94, 59, 33, 6, 38, 64, 87, 15, 10, 32, 97, 96, 79, 5, 21, 46, 45, 89, 22, 16, 98, 42, 80, 12, 40, 4, 61, 30, 72, 47, 50, 8, 75, 83, 48, 11, 51, 78, 52, 24, 85, 19, 63, 28, 90, 2, 77, 20, 3, 88, 99, 17, 91, 57, 13, 14, 7, 81, 23, 93, 74, 71, 31, 29, 66, 9, 86] このようにshuffleも記述すると0から99 までの数字をランダムに配列することができます。 ターミナル irb(main):015:0> z = (0..10).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] irb(main):016:0> z << 20 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20] irb(main):017:0> z.push(30) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30] 配列の末尾に加えたいときはz<<20、またはpushメソッド z.push(30)と書きます。 配列の要素を削除したいときは ターミナル => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30] irb(main):018:0> z.pop => 30 irb(main):019:0> z.shift => 0 irb(main):020:0> z => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20] z.popで末尾の要素を、z.shiftで先頭の要素を削除します。 重複する要素を削除したいときは ターミナル => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 3, 6] irb(main):023:0> z.uniq => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20] irb(main):024:0> z => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 3, 6] irb(main):025:0> z.uniq! => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20] irb(main):026:0> z => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20] irb(main):027:0> z.uniqで重複する要素を削除し、完全に配列から削除するときはz.uniq!と書きます。 ターミナル irb(main):027:0> s = ['my','name','is','nanashi'] => ["my", "name", "is", "nanashi"] irb(main):028:0> s.join => "mynameisnanashi" irb(main):029:0> s.join(' ') => "my name is nanashi" irb(main):030:0> s.join(' ') => "my_name_is_nanashi" 次に結合の方法です。 s.joinと書くことで書く要素を結合することができます。 s.join(' ')やs.join(' ')とすることで各要素間にスペースなどを入れることができます。 ターミナル irb(main):031:0> s.sort => ["is", "my", "name", "nanashi"] s.sortで若い順にソートすることができます。 :ターミナル irb(main):032:0> s.size => 4 s.sizeで要素の個数を返すことができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsでDM機能搭載 userコントローラー&ビュー

忘れないための備忘録です。 railsのDM機能 (参考:https://qiita.com/nojinoji/items/2b3f8309a31cc6d88d03) アソシエーションは ルームとユーザーが エントリーとメッセージに対して1:多の関係 まずユーザーコントローラーのShow def show @user=User.find(params[:id]) #詳細ページのメイン(DMしたい相手) @currentUserEntry=Entry.where(user_id: current_user.id) #ログインしている人が入っているルーム情報 @userEntry=Entry.where(user_id: @user.id) #詳細ページの人が入っているルーム情報 if @user.id == current_user.id #ログインしている人が自分のページを見ている else @currentUserEntry.each do |cu| #二人のルーム情報の中に @userEntry.each do |u| if cu.room_id == u.room_id then #二人のルームがあるなら @isRoom = true   @roomId = cu.room_id #この時にはcu,uともにお互いのルーム end end end if @isRoom #ないなら else @room = Room.new @entry = Entry.new end end end user/show.html.erb のビュー <% if @user.id == current_user.id %>  #自分が自分のマイページをみている <% else %> <% if @isRoom == true %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyのハッシュでno implicit conversion of symbol into integerがでたがintegerでなく自分のミスだった件について

はじめに 今回は初のRubyの記事を書きたいと思います。 Rubyは記事がたくさんあるイメージですが、この問題の原因はみつからなかったため記事にします。 いろいろな言語をやっていたりすると起きるミスなのかなとも思いました。 問題 以下のハッシュを定義して"サイン"を表示しようとしました。 def subject { math: { sin: "サイン", cos: "コサイン", tan: "タンジェント", }, science: { viology: "植物学", biology: "生物学", } } } # サインと表示したい select = subject[:math[:sin]] すると以下のエラーが発生します。 no implicit conversion of symbol into integer 文字列にしろとでているので、subject["math"["sin"]]など確かめたが表示されませんでした。 解決策 そもそもハッシュの表示の仕方を間違っていました。 エラー文が原因を表していると思っていましたが、それ以前の問題でした。 以下のコードで動きました。 select = sebject[:math][:sin] puts(select) # サインと表示される この原因によるエラーのほうが多いかも 先ほども述べましたが、このエラーが出た場合は多くは文字型で指定すると治るかと思います。 def subject { "math": { "sin": "サイン", "cos": "コサイン", "tan": "タンジェント", }, "science": { "viology": "植物学", "biology": "生物学", } } } # keyを文字型で定義している (ex "math"など) エラー通り文字型でハッシュを表示します。 シンボルでなく文字列で指定しなければならないので以下のようにする select = subject["math"]["sin"] さいごに エラーの内容に気を取られて、文字列にしたりシンボルにしたりを繰り返していましたが、まさかの表記の仕方自体が違っていたことにはなかなか気づきませんでした。リストやらハッシュやらわからなくなることがあります。エラー文を気にする前に表記が正しいか確認するのも大事です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyドリルを解く時に使うおすすめサイト

はじめに テックキャンプ で学習する際、毎朝rubyドリルを時ます。 その際に私が挙動を確認する際に使うサイトをご紹介します。 おすすめサイト paiza.IOは、ブラウザ上でコードを書け、書いたコードをその場で実行できるオンラインのプログラム実行環境です。 最初はrubyドリルを解く際にターミナルで記述し、確認をしていたましたがこのサイトを見つけ使っております。 このように言語を選択して記述ができます。 実行をクリックすると実際の挙動を確かめることできます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

モデル単体テストコードの書き方

はじめに プログラミング初学者のため、自分の理解できている範囲内で言語化しています。 何か間違っている情報や改善点などありましたら、コメントいただけますと幸いです。?‍♂️ 手順 導入/設定 ・ RSpecの導入 ・ FactoryBotの導入 下準備 ・ テストコード記述ファイルの作成 FactoryBot ・ ファクトリーボットファイル記述 Spec ・ テストコード記述 実装 導入/設定 ポイントは:development,:testのところに書くこと Gemfile group :development, :test do gem 'factory_bot_rails' gem 'rspec-rails', '~> 4.0.0' end の後にbundle installを忘れない 次に、RSpecのインストール ターミナル % rails g rspec:install ここでインストールができないなどのエラーが出る場合はこの記事を参照 https://qiita.com/tech_hack_Rich/items/c8e12f5d9d3e9d677ed7 次に、テスト結果をターミナルで可視化するための設定をする .rspec --require spec_helper --format documentation ---追記する これで導入と設定は完了 下準備 テストコードファイルの作成 ターミナル % rails g rspec:model user これでテストコードを書くためのファイルを作成する、 同時にfactorybotのファイルもここで生成される 下準備OK FactoryBot こんな感じで記述 spec/factories/users.rb FactoryBot.define do factory :user do nickname {'test'} email {'test@example'} password {'000000'} password_confirmation {password} end end Spec spec/models/user_spec.rb require 'rails_helper' RSpec.describe User, type: :model do before do @user = FactoryBot.build(:user) end describe 'ユーザー新規登録' do it 'nicknameが空では登録できない' do @user.nickname = '' @user.valid? expect(@user.errors.full_messages).to include "Nickname can't be blank" end it 'emailが空では登録できない' do @user.email = '' @user.valid? expect(@user.errors.full_messages).to include "Email can't be blank" end end end 完成?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【RSpec】基本的な書き方

はじめに この記事は、使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」を参考(引用)してます。 describe / it / expect の役割 RSpecでよく使うdescribe, it, expectについて。 describe テストのグループ化 ・ describeはテストのグループ化を宣言する。 ・ describeの中では複数のexample(it end ... do)を記述できる。 it テストをexampleにまとめる ・ itはテストをexampleという単位にまとめる役割をする。 ・ it do ... endの中のエクスペクテーション(期待値と実際の値の比較)が全てパスすればそのexampleはパスしたことになる。 ・ it do ... endのなかには一つのエクスペクテーション(expect)を描く。(原則なので破っても良い) expect エクスペクテーション itの部分で述べたエクスペクテーションがexpectにあたり、 expect(X).to eq Yのような形で描く。 使えると便利なcontext / before context contextは条件でグループ化するときに使う。 RSpec.describe User do describe '#greet' do context '12歳以下の場合' do it 'ひらがなで答えること' do user = User.new(name: 'たろう', age: 12) expect(user.greet).to eq 'ぼくはたろうだよ。' end end context '13歳以上の場合' do it '漢字で答えること' do user = User.new(name: 'たろう', age: 13) expect(user.greet).to eq '僕はたろうです。' end end end end before ・ beforeは共通の前準備をする時に使える。 ・ before do ... endに記述したものは、  example(it do ... end)の実行前に呼ばれる。 ・ beforeはテスト実行前の共通処理やデータのセットアップ等を行うことが多い。 RSpec.describe User do describe '#greet' do before do @params = { name: 'たろう' } end context '12歳以下の場合' do it 'ひらがなで答えること' do user = User.new(@params.merge(age: 12)) expect(user.greet).to eq 'ぼくはたろうだよ。' end end context '13歳以上の場合' do it '漢字で答えること' do user = User.new(@params.merge(age: 13)) expect(user.greet).to eq '僕はたろうです。' end end end end beforeではインスタンス変数を使用している。 これはbeforeとitでは変数のスコープが異なるため。 ネストしたdescribe, contextの中でのbefore beforeはdescribeやcontextごとに用意できる。 呼ばれる順番は、親、子の順。 RSpec.describe User do describe '#greet' do before do @params = { name: 'たろう' } end context '12歳以下の場合' do before do @params.merge!(age: 12) end it 'ひらがなで答えること' do user = User.new(@params) expect(user.greet).to eq 'ぼくはたろうだよ。' end end context '13歳以上の場合' do before do @params.merge!(age: 13) end it '漢字で答えること' do user = User.new(@params) expect(user.greet).to eq '僕はたろうです。' end end end end letの役割 インスタンス変数の代わり これまではbeforeブロックに@paramsという形でインスタンス変数を利用してきました。これをletを使って次のように書き換えられる。 #これを before do @params = { name: "たろう" } end #このように let(:params) { { name: "たろう" } } ローカル変数の代わりにも インスタンス変数だけでなく、ローカル変数もletでかける。 #これを user = User.new(params) #このように let(:user) = User.new(params) letのメリット beforeがexpample(it do ... end)の前に呼ばれるのに対し、letは遅延評価される。letは必要になるまで呼ばれない。 RSpec.describe User do describe '#greet' do let(:user) { User.new(params) } let(:params) { { name: 'たろう', age: age } } context '12歳以下の場合' do let(:age) { 12 } it 'ひらがなで答えること' do expect(user.greet).to eq 'ぼくはたろうだよ。' end end context '13歳以上の場合' do let(:age) { 13 } it '漢字で答えること' do expect(user.greet).to eq '僕はたろうです。' end end end end ここでは、 1. expect(user.greet).to eqの部分でuserをさがす。 2. let(:user) { User.new(params) }が呼ばれ、   ここでparamsをさがす。 3. let(:params) { { name: 'たろう', age: age } }が呼ばれ、次はageをさがす。 4. let(:age) { 12 }がよばれる。 のようにしてletが呼び出される。 事前に実行されるlet! let!を使うとexample*の実行前にlet!で定義した値が作られる。 RSpec.describe Blog do #事前にblogが作られるためexpectでエラーにならない。 let!(:blog) { Blog.create(title: 'RSpec必勝法', content: 'あとで書く') } it 'ブログの取得ができること' do expect(Blog.first).to eq blog end end subjectの使い方 subjectを使えば同じエクスペクテーションを一つにまとめることができる。 #12歳以下のとき expect(user.greet).to eq 'ぼくはたろうだよ。' #13歳以上のとき expect(user.greet).to eq '僕はたろうです。' #上の二つを次のようにまとめる subject { user.greet } #subjectで宣言し、 is_expected.to eq 'ぼくはたろうだよ。' #is_expected.toでテスト さらに、itを省略して、次のようにかける。 RSpec.describe User do describe '#greet' do let(:user) { User.new(name: 'たろう', age: age) } subject { user.greet } context '12歳以下の場合' do let(:age) { 12 } it { is_expected.to eq 'ぼくはたろうだよ。' } end context '13歳以上の場合' do let(:age) { 13 } it { is_expected.to eq '僕はたろうです。' } end end end itのエイリアス specify, example itの代わりにspecifyとexampleが使える。 # それ(it)はユーザー名を返す it 'returns user name' do # ... end # 会社は社員を持つことを仕様として明記(specify)する specify 'Company has employees' do # ... end # fizz_buzzメソッドの実行例(example) example '#fizz_buzz' do # ... end RSpecの高度な機能 shared_example と it_behaves_like shared_example と it_behaves_likeを使えば、exampleの再利用ができる。  RSpec.describe User do describe '#greet' do #省略 shared_examples '大人のあいさつ' do it { is_expected.to eq '僕はたろうです。' } end context '13歳の場合' do let(:age) { 13 } it_behaves_like '大人のあいさつ' end context '100歳の場合' do let(:age) { 100 } it_behaves_like '大人のあいさつ' end end end shared_example "foo" do ... endで再利用したいexampleを定義し、it_behaves_like "foo"で呼び出す。 shared_context と include_context shared_exampleとit_behaves_likeのように、 contextの再利用には、shared_contextとinclude_contextが使える。 RSpec.describe User do let(:user) { User.new(name: 'たろう', age: age) }  #これらの記述でlet(:age)の繰り返しをなくす shared_context '12歳の場合' do let(:age) { 12 } end shared_context '13歳の場合' do let(:age) { 13 } end describe '#greet' do subject { user.greet } context '12歳以下の場合' do include_context '12歳の場合' it { is_expected.to eq 'ぼくはたろうだよ。' } end context '13歳以上の場合' do include_context '13歳の場合' it { is_expected.to eq '僕はたろうです。' } end end describe '#child?' do subject { user.child? } context '12歳以下の場合' do include_context '12歳の場合' it { is_expected.to eq true } end context '13歳以上の場合' do include_context '13歳の場合' it { is_expected.to eq false } end end end shared_context "foo" do ... endで再利用したいコンテキスト宣言し、contextの中でinclude_context "foo"を使って呼び出す。 スキップさせたいとき RSpecではテストを一旦置いときたいときにスキップできる。 xを使う itやdescribeの前にxと書きxit, xdescribeとすることで、そのテストをスキップできる。(pending扱いになる。) RSpec.describe '何らかの理由で実行したくないクラス' do #xexample, xspecify も使える。 xit '実行したくないテスト' do expect(foo).to eq bar end end #describeをスキップ xdescribe '四則演算' do end # contextをスキップ xcontext '管理者の場合' do end テストは後で書く、中身のない it it "foo" do ... endのdo ... endを省略することでpendingにすることができる。 RSpec.describe User do describe '#good_bye' do context '12歳以下の場合' do #do ... end を省略 it 'ひらがなでさよならすること' end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]Basic認証について

はじめに こんにちは、docker導入によってテストコードが動かなくなるというエラーのために寝れなくなるほど悩んでいるとちです。息抜きに、ちょっと全然違うことについてまとめてみました。ぜひご覧ください Basic認証とは Basic認証とは、HTTP通信の規格に備え付けられている、ユーザー認証の仕組みのことです。閲覧できるユーザーを制限するために使用します。 少ない手間で認証を実装できるため便利ですが、安全性という観点から、完全に信頼できる認証方式ではありません。 HTTP通信で定義されている仕様上、ユーザー名とパスワードが通信経路上にそのまま送られるため、漏洩のリスクがあります。また、ログアウトの概念が定義されていないため、もし必要になる場合は自力で実装する必要があるほか、複数のサーバーをまたいだ認証が難しいといった特徴も問題になります。 実装について簡単にまとめ はい、これで完成なんですね、実は。 app/controllers/application_controller.rb class ApplicationController < ActionController::Base before_action :basic_auth private def basic_auth authenticate_or_request_with_http_basic do |username, password| username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"] # 環境変数を読み込む記述に変更 end end end 一応、味気なさすぎるので解説します。 そんなに難しいわけではないですが、、鬼長い名前のメソッドがありますね!そう!authenticate_or_request_with_http_basicメソッドです。 RailsアプリケーションでBasic認証を実装するために使用する、Railsのメソッドで、ブロックを開き、ブロック内部でusernameとpasswordを設定することでBasic認証を利用できます。 ただし、ここで注意なのは、それぞれの値は環境変数で設定してください。でないと、github上にあげてしまうと誰でも認証できてしまうということになります! ちなみにローカル、AWS、herokuそれぞれで環境変数は設定する必要があります herokuの場合 herokuの場合は、heroku configで設定できます。以下のコマンドで設定しましょう % heroku config:set BASIC_AUTH_USER="admin" % heroku config:set BASIC_AUTH_PASSWORD="2222" ローカルの場合 macOSがCatalina以降の方は、以下のコマンドです。 % ~/.zshrc Mojave以前の方は以下のコマンドです。 % source ~/.bash_profile (以下、macOSがCatalina以降の方と仮定して説明、基本変わりないです) 以下のように環境変数を設定してやってパスを通しましょう export BASIC_AUTH_USER='admin' export BASIC_AUTH_PASSWORD='2222' % source ~/.zshrc とこんな感じです!! 参考 zsh 「zsh」はログインシェルと呼ばれるもので、プログラムを実行する時に、ユーザーの要求に一番最初に対応する役割を担います。隠しファイルなので、特別な設定なしではFinderなどには表示されていません。 環境変数を記載する場所は、設定ファイルである「.zshrc」の中です。 bash 「bash」とは、zsh同様、ログインシェルの1つです。zshとの違いは、OSがCatalina以降であれば「zsh」、Mojave以前であれば「bash」が自動で適用されます。 環境変数を記載する場所は、設定ファイルである「.bash_profile」の中です。 おわりに いい感じに息抜きできたので再度エラーと戦ってきます!行ってきます!笑
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyの演算子まとめ

毎回調べてる気がする。 演算子 記述例 意味 別の演算子 && a && b aとbが共に真の場合に真 and || a || b aかbの少なくとも1つが真の場合に真 or ! !a aが真の時に偽、偽の時に真 not 演算子 記述例 意味 別の演算子 && a && b aとbが共に真の場合に真 and 11 a 11 b aかbの少なくとも1つが真の場合に真 or ! !a aが真の時に偽、偽の時に真 not ||がマークダウン的に書けなかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

完全数またはほぼ完全数 [ruby]

あるプログラミングテスト問題に出会い、とても参考になったので、その学びを納めておきたいと思います。自身の回答に関しては、繰り返し処理が2重で存在するなど、まだまだ稚拙な回答ですが、この問題自体はとても参考になりました。 問題  Xを2 以上の整数とし、X の約数のうち X 自身を除いたものの和を Y とします。  このとき X = Y となるような X を完全数 |X-Y| = 1 となるような X をほぼ完全数 (|X-Y| は X-Y の絶対値を意味する) と言うことにする。 たとえば、X = 28 のとき 28 の約数は 1, 2, 4, 7, 14, 28 なので、Y = 1+2+4+7+14 = 28 となります。 従って、28 は完全数です。 また、X = 16 のとき Y = 1+2+4+8 = 15 となるので、|16 - 15| = 1 従って、16 はほぼ完全数です。 入力された整数が完全数かほぼ完全数かそのいずれでもないかを判定するプログラムを作成する 入力される値 入力は以下のフォーマットで与えらる Q X_1 . . X_Q1 1行目のQには、判定したい整数の個数 続く Q 行には整数 X_1, ..., X_Q が入力されます。 期待する出力 各 X_i に対して X_i が完全数であれば "完全数" X_i がほぼ完全数であれば "ほぼ完全数" どちらでもなければ "どちらでもない"と 1 行に出力してください。 条件 すべてのテストケースで以下の条件を満たします。 1 ≦ Q ≦ 50 2 ≦ X_i ≦ 1000 (i=1, 2, ..., Q) 自分の回答 input_lines = readlines.map(&:to_i) count = input_lines[0] given_numbers = input_lines[1..count] given_numbers.each do |num| divide_numbers = [*1..num-1] # 解説1 sum = 0 divide_numbers.each do |divide| sum += divide if num % divide == 0 end if sum == num puts "完全数" elsif (sum - num).abs == 1 # 解説2 puts "ほぼ完全数" else puts "どちらでもない" end end 入力される数字の例 3 4 28 777 出力される例 ほぼ完全数 完全数 どちらでもない 解説 ちっぽけな解説ですが、一応載せておきます。 解説1 divide_numbers = [*1..num-1] これは数学の話ではありますが、この問題は約数が重要です。また今回はその中でも、その数字自体は約数に含めてはいけないので、上記の構文で、1から始まり自分の一つ下の数字までの配列が格納されます。この配列は、今回の条件で約数になりうる数字の配列です。 # 28の場合 [1, 2, 3, 4, 5, 6, .. , 26, 27] 解説2 (sum - num).abs == 1 absはrubyで絶対値を出力するメソッドです。 (-5).abs = 5 (5).abs = 5 解説は以上です。 余談 余談ですが、この問題を見て小川洋子さんの「博士の愛した数式」を思い出しました。 記憶が短時間しか続かない数学者の博士と主人公である家政婦のやりとりを基本とした人間ドラマです。 その中で、博士が主人公とコミュニーションを取る上で、「友愛数」という数学の面白さを基に家政婦とコミュニケーションを取るシーンがありました。博士らしい他者との距離の取り方で、とても印象的でした。 この完全数の考え方は「友愛数」の考え方に似ています。なので思い出しました。コーディングテストの練習中に、博士の愛した数式を思い出して、少し悲しくなる、という謎現象でした。 最後までお読み頂きありがとうございました!(笑
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クッキーの仕組み

クッキーとは ユーザーの情報を保存しておくためのファイルのことです。 仕組み 初回アクセス時サーバーがユーザーにクッキーを発行します。 クッキーデータは一定期間ユーザーのブラウザに保存されて次回以降のアクセス時に利用されます。 どのように利用されるのか たとえば、多くのショッピングサイトではユーザーのIDやカートの情報をクッキーで保存しています。 これにより1回サイトを離れてしまったユーザーがまたサイトを訪れた際に、毎回IDを入力したり商品をカートに入れ直したりすることなくスムーズにショッピングの続きができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails初学者によるRailsチュートリアル学習記録⑨ 第7章

目次 1. はじめに 2. 第7章の概要 3. 学習内容 4. 終わりに 1. はじめに この記事は、Rails初学者の工業大学三年生がRailsチュートリアルの学習記録をつけるための記事です。 筆者自体がRailsやWebについて知識が少ないので、内容の解釈などに間違いがある可能性があります。(その時はコメントで指摘してくださると助かります!) Railsチュートリアル内ではRailsの内容以外にも、gitでのバージョン管理やHerokuを使ったデプロイも学習しますが、gitに関しては既に私が学習済みのため学習記録には記述しません。 演習の記録も省略します。 2. 第7章の概要 この章では、ユーザー登録機能を実装して、ブラウザからユーザー登録をできるようにしていきます。 RESTアーキテクチャに基づいたユーザーのページも使用できるようにして、より実際のwebアプリケーションに 近づいていく1章です。 ユーザー登録の準備 RESTアーキテクチャによるルート デバッグ gravatarを使用する ビューにフォームを作成する フォームの処理の流れ Strong Parameters フォーム送信後の処理 flashでウェルカムメッセージを表示する 3. 学習内容 1. ユーザー登録の準備 1-1. RESTアーキテクチャによるルート RESTアーキテクチャは、GET / POST / PATCH / DELETE の4つのHTTPメソッドに対応したアクションやリソースをデフォルトで提供します。 提供されるアクションの一覧を以下の表に示します。 HTTPリクエスト URL アクション名 用途 GET /users index ユーザー一覧を表示するページ GET /users/1 show 特定のユーザーを表示するページ GET /users/new new ユーザーを新規作成するページ POST /users create ユーザーを作成するアクション GET /users/1/edit edit id=1 のユーザーを編集するページ PATCH /users/1 update ユーザーを更新するアクション DELETE /users/1 destroy ユーザーを削除するアクション これらのアクションを使用するにはroutesファイルにresources :usersという一行を追加する必要があります。 また上記の表の用途の列で、○○するページと○○するアクションという表記が混在していますが、 ○○するページと表記されたアクションは、それに対応するビューを作成するのに対し、 ○○するアクションと表記されたアクションはビューを必要とせず、処理を行った後に別のビューにリダイレクトします。 1-2. デバッグ この章で作成するプロフィルページはこのアプリケーション内での初めての動的なページです。 そのページではデータベースからユーザーのデータを取り出し、それを表示します。 ここでは、そのページが正しく動作しているかを確認できるようにしていきます。 デバッグ情報を表示させるには、ビューファイルにdebugメソッドとparams変数を使います。 この時、本番環境ではデバッグ情報を表示させたくないので、if Rails.env.development?を使うことで、 開発環境でのみデバッグ情報を表示させます。 以下のコードがデバッグ情報を挿入したビューファイルです。 app/views/layoouts/application.html.erb <!-- body部分のみを表示 --> <body> <%= render 'layouts/header' %> <div class="container"> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> <!-- 開発環境でのみデバッグ情報を挿入する --> </div> </body> 1-3. gravatarを使用する gravatarとは、メールアドレスと画像を紐づけることで、ユーザーのアイコンとしてその画像を表示できる機能です。 Railsでgravatarを使用するには、gravatar_forというヘルパーメソッドを使用します。 このヘルパーメソッドはUsersコントローラのヘルパーファイルに定義して以下のように使用します。 app/helpers/users_helper.rb module UsersHelper # 引数で与えられたユーザーの Gravatar 画像を返す def gravatar_for(user) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end end app/views/users/show.html.erb <% provide(:title, @user.name) %> <h1> <%= gravatar_for @user %> <!-- 第一引数にユーザーオブジェクトを渡す--> <%= @user.name %> </h1> 2. フォームを作成する 2-1. フォームの処理の流れ Railsでフォームを実装するにはform_withヘルパーメソッドを使用する。 フォームを作るユーザー登録ページはUsersコントローラのnewアクションに対応させて、 newアクションで@user = User.newを実行することでオブジェクトを生成します。 以下のコードが登録フォームのコードです。 app/views/users/new.html.erb # フォーム部分のみを抜粋 <%= form_with(model: @user, local: true) do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation %> <%= f.submit "Create my account", class: "btn btn-primary" %> <% end %> 上のコードのようにlabelと入力部分(input)をペアで作成し、 ペアは引数のシンボルで判断されます。 このシンボルはフォームに入力されたデータを扱うときにも使用します。 ボタンを押すとform_withの引数になっている@userが新しいユーザーであるとRailsが判断して、 POSTリクエストを/users、つまりcreateアクションに入力された値を送信します。 上記の処理の流れは <%= form_with(model: @user, local: true) do |f| %>というRubyのコードが <form action="/users" class="new_user" id="new_user" method="post">というHTMLを生成していることを意味します。 入力された値はcreateアクションにparamsハッシュとして渡されます。 paramsハッシュには各リクエストの情報が含まれていますがユーザー登録の場合は、 userハッシュが格納されています。つまり、ハッシュが入れ子になっている状態です。 そして、userハッシュの中にフォームに入力された値がフォームのシンボルとともに格納されています。 よって、createアクションで@user = User.new(params[:user])と書くことで、 フォームに入力された値を持ったユーザーが作成できます。 上記の@userにsaveメソッドを用いればユーザー登録ができるようになります。 しかし、上記のparamsを渡す方法ではユーザーが送信したデータをそのままUserオブジェクトに渡しており、 この方法はセキュリティ上の欠陥があります。 具体的な例としては、Userモデルにadmin属性という属性があり、サイトの管理者はその属性の値が1になるという運用をした場合、 admin='1'という値をprams[:user]に紛れ込ませて渡すことで、誰でもサイトの管理者権限を得ることができてしまいます。 このような欠陥を次節のStrongParametersで解消します。 2-2. Strong Parameters Strong Parametersはparamsハッシュで必須の値や許可された値を決めることができます。 よって、許可されていない値を受け取らないことで、先述した例のような欠陥を解消できます。 この機能を実装するにはuser_paramsというメソッドを使用するのが慣習です。 このメソッドはUsersコントローラに書き、外部ユーザーから隠すためにprivateキーワードを使用します。 app/views/users/new.html.erb # user_paramsメソッドの部分のみを抜粋 private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end <% end %> そして、createアクションでのオブジェクト生成にこのメソッドを使用します。 @user = User.new(user_params)このように記述して、paramsハッシュを安全に扱っています。 2-3. フォーム送信後の処理 ① 登録に成功したときの処理 ユーザー登録に成功した場合は、自分自身のプロフィールページにリダイレクトし、 そこでウェルカムメッセージを表示させます。 まず、リダイレクトはredirect_toメソッドで実装します。 @user.saveが成功したときにredirect_to @userというコードを実行すると、 railsが自動で判断してuser_url(@user)というプロフィールページにリダイレクトするコードに変換します。 ウェルカムメッセージはflashという特殊な変数で実装します。 登録に成功したときに、flash[:success] = "Welcome to the Sample App!"と書くことで、 flash変数にメッセージを代入します。 :successというキーを使用しているのはRailsの慣習で、成功時のメッセージを意味します。 メッセージが代入されたflash変数を、ビューで使用することでウェルカムメッセージを表示します。 app/views/layouts/application.html.erb <div class="container"> <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> flashの機能とは直接関係ないのですが、<div class="alert alert-<%= message_type %>"><%= message %></div> この行でのclassの設定にRubyが用いられています。 現状ではウェルカムメッセージしかflashで扱っていませんが、他のメッセージも使うことになった場合に メッセージの種類に合わせてcssを切り替えることができるようになります。 ② 登録に失敗したときの処理 ユーザー登録に失敗した場合は、エラーメッセージを表示してユーザー登録に失敗したことを ユーザーに分かりやすく伝える必要があります。 Railsはエラーの発生時に表示させたいメッセージを自動で生成してくれています。 それはuser.errors.full_messageというオブジェクトに配列として格納されています。 それをビューに表示できるようなパーシャルを作成しましょう app/views/layouts/application.html.erb # エラーがあるときのみパーシャルを表示 <div class="container"> <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> 4. 終わりに この章で実際のアプリケーションのようにブラウザからユーザー登録ができるようになったので、 Herokuに上げて開発環境でなく本番環境でアプリケーションを動かすことができました。 実装したユーザー登録ではユーザー自身の情報を扱うということもあり、Railsチュートリアルでも セキュリティに関する内容が増えてきました。 この記事では書いていませんが、本番環境でSSLを使用したり本番環境用のWebサーバーを使用したりなども行っています。 セキュリティに関する知識は資格試験の勉強などで勉強しましたが、実際に扱うことは初めてなのでセキュリティの内容は 注意深く実践していきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

attr_accessorで定義したセッターをクラス内で使う際の落とし穴

TLDRはこちら。 attr_accessorとは attr_accessor はインスタンス変数に対するゲッター・セッターを同時に定義するための便利メソッド。 例えば、以下のコードでは @earth_shape というインスタンス変数に対する #earth_shape (ゲッター) 、#earth_shape= (セッター) を同時に定義してくれる。 class TopSecret attr_accessor :earth_shape def initialize @earth_shape = 'flat' end end secret = TopSecret.new # ゲッターを使って、インスタンス変数の値を得る puts secret.earth_shape # 'flat' を出力 # セッターを使って、インスタンス変数の値を変える secret.earth_shape = 'round' puts secret.earth_shape # 'round' を出力 定義されるのは通常のメソッドと変わりないので、クラス内でゲッター・セッターメソッドを呼ぶこともできる。 class TopSecret attr_accessor :earth_shape def initialize @earth_shape = 'flat' end def reveal # クラス内からゲッターを呼ぶ puts "The Earth is #{earth_shape}." end end secret = TopSecret.new secret.reveal # 'The Earth is flat.' と出力 セッターを使う際の落とし穴 しかし、クラス内でセッターを使う際には落とし穴がある: class TopSecret attr_accessor :earth_shape def initialize @earth_shape = 'flat' end def reveal # クラス内からセッターを呼ぶ? earth_shape = 'round' # クラス内からゲッターを呼ぶ? puts "The Earth is #{earth_shape}." end end secret = TopSecret.new secret.reveal # 'The Earth is round.' と出力 上の #reveal メソッドは一見セッターとゲッターを呼んでいるように見えるが、実はそうではない。 コードの末尾に以下を追加すれば、何かがおかしいことが分かるはず: puts secret.earth_shape # 'flat' と出力 #reveal メソッドでセッターが呼ばれて @earth_shape の値が 'round' に変わったのであれば、上のコードは 'round' と出力するはずである。 では、どこがおかしいのか? 問題は、 #reveal メソッド内の以下の行だ: # クラス内からセッターを呼ぶ? earth_shape = 'round' 実は、Rubyはこの式をローカル変数の代入式として解釈するのである。 つまり、この時点でローカル変数 earth_shape が誕生し、その値に 'round' が入ったということだ。 さらに、 attr_accessor で定義されたゲッターメソッド、 #earth_shape は、「たまたま」そのローカル変数 earth_shape と名前が同じだったことで、ローカル変数の背後に隠れてしまう (この挙動は「シャドーイング」と呼ばれる) 。 よって、以下の # クラス内からゲッターを呼ぶ? puts "The Earth is #{earth_shape}." も、実はゲッターを呼び出しているのではなく、ローカル変数の値を参照しているのだ。 このため、 #reveal メソッド内ではインスタンス変数 @earth_shape には指一本触れていないので、その値は元々の 'flat' のままということだ。 puts secret.earth_shape # 'flat' と出力 さらなる落とし穴: 条件分岐 この程度の落とし穴にハマるものかという方に、以下のコードを見ていただきたい: class TopSecret attr_accessor :earth_shape def initialize @earth_shape = 'flat' end def reveal(redact:) earth_shape = 'round' if redact puts "The Earth is #{earth_shape}." end end secret = TopSecret.new secret.reveal(redact: true) # 何が出力される? puts secret.earth_shape # 何が出力される? secret.reveal(redact: false) # 何が出力される? puts secret.earth_shape # 何が出力される? 4つの質問の答え、分かるだろうか? 最初の2つに関してはif節がない版のコードと同様で、ゲッターとセッターがローカル変数 earth_shape に隠されて、インスタンス変数 @earth_shape は変化しない。 secret.reveal(redact: true) # 'The Earth is round.' と出力 puts secret.earth_shape # 'flat' と出力 残りの2つが異端児である。 先に解答を見てみよう: secret.reveal(redact: false) # 'The Earth is .' と出力 puts secret.earth_shape # 'flat' と出力 インスタンス変数 @earth_shape の値が 'flat' のままなのは分かるとして、 'The Earth is .' という出力は予想外ではないだろうか。 理由は、条件分岐内での変数代入の意外ともいえる挙動にある。 #reveal メソッドを redact: false で呼んだ場合、以下のコードでローカル変数への代入が行われないのはお分かりだろう。 earth_shape = "round" if redact 変数への代入が行われなかったので、シャドーイングは発生せずに earth_shape でゲッターメソッドが呼ばれると考えた方もいるはずだ: # ゲッターを呼んでいる? puts "The Earth is #{earth_shape}." # 'The Earth is flat.' と出力? 残念ながらそうではない。 変数への代入が行われなかったのは事実だが、 earth_shape はどのみちローカル変数を指すのである。 なぜなら、if節・case節などの分岐の中で変数への代入を行った場合、たとえその分岐が実行されなくとも変数の「作成」は行われるというRubyの言語上の決まりがあるからだ。 そして、作成直後の変数の値は nil となるため、文字列に埋め込まれたときに空文字列に置換され、 'The Earth is .' という出力になったのである。 どうすれば回避できるか 前述の通り、クラス内でゲッター・セッターメソッドを使う場合「シャドーイング」による落とし穴に気をつけなければならない。 予期せぬシャドーイングを未然に防ぐには、「self キーワードを使う方法」や「インスタンス変数を直接使う方法」などがある。 self キーワードを使う方法 self キーワードをインスタンスメソッド内で使うと、そのメソッドを呼び出す際に使ったクラスのインスタンスを指す (「レシーバー」という言い方もある) 。 ゲッター・セッターメソッドの呼び出しの前に self. をつけることで、「ローカル変数ではなくインスタンスメソッドを参照しています」という意図をRubyに伝えることができる: class TopSecret attr_accessor :earth_shape def initialize @earth_shape = 'flat' end def reveal self.earth_shape = 'round' puts "The Earth is #{self.earth_shape}." end end secret = TopSecret.new secret.reveal # 'The Earth is round.' と出力 puts secret.earth_shape # 'round' と出力 実行結果から、ローカル変数の作成は行われずにインスタンス変数が直接変更されていることが分かる。 ただし、Rubocopを使ってリントをしている方は、以下のようなお節介アドバイスをされるかもしれない: super_secret.rb:10:26: C: [Correctable] Style/RedundantSelf: Redundant self detected. puts "The Earth is #{self.earth_shape}." ^^^^^^^^^^^^^^^^ Rubocopののデフォルト設定では、余計な self を書くと怒られるようになっている。 理由としては、メソッド内に earth_shape というローカル変数がないのだから、わざわざ self. をつけなくともゲッターの呼び出しだと分かるというところだろう。 Rubocopに逆らわない善良な市民であれば、以下のように書くこととなる: def reveal self.earth_shape = 'round' puts "The Earth is #{earth_shape}." end 個人的に、セッターの前にだけ self. が付いていることに不釣り合いさを感じる。 さらには、メソッドが長くなった場合、一目見てローカル変数かゲッターメソッドかが分からないという問題もあるだろう。 あくまでデフォルト設定なので、Rubocopの設定ファイルをいじってRubocopの権力乱用に反旗を翻すのも一つの手である。 ただ、その前に2つ目の方法を見ていただきたい。 インスタンス変数を直接使う方法 attr_accessor を使うと、ゲッターと同じ名前のインスタンス変数が作られることはご存知だろう。 インスタンスメソッド内であればインスタンス変数を使えるのだから、以下のように書くことができる: class TopSecret attr_accessor :earth_shape def initialize @earth_shape = 'flat' end def reveal @earth_shape = 'round' puts "The Earth is #{@earth_shape}." end end secret = TopSecret.new secret.reveal # 'The Earth is round.' と出力 puts secret.earth_shape # 'round' と出力 この方法であれば、ローカル変数とインスタンス変数の混乱もなく分かりやすいだろう。 こちらのほうがむしろ自然という見方もできる。 attr_accessor はあくまでクラスの外部からインスタンス変数にアクセスするセッターとゲッターを定義するためのものと考えるのであれば、クラスの内部でそれらを使う必要はないからだ。 ただし、こちらはこちらでいくつか考察に値する点がある。 まず、インスタンス変数の性質上、名前を間違えてもエラーが検知され辛いという点がある: class TopSecret attr_accessor :earth_shape def initialize @earth_shape = 'flat' end def reveal # つづりこれで合ってたっけ? @earf_shape = 'round' puts "The Earth is #{@earf_shape}." end end secret = TopSecret.new secret.reveal # 'The Earth is round.' と出力 puts secret.earth_shape # 'flat' と出力 ここでは、 @earth_shape とは別のインスタンス変数 @earf_shape が作成され、その値が読み書きされている。 単体テスト等を念入りに書いていれば検知はできるだろうが、ゲッター・セッターを使っていた場合は存在しないメソッドを呼び出した時点でランタイムエラーとなることと比較してほしい。 「間違っているけど何故か動く」コードよりは「間違っていたからエラー終了する」コードのほうが、長期的に見れば負債は少ないことも合わせて考えたいところだ。 また、 attr_accessor で定義されるゲッター・セッターは値を直接読み書きするだけの単純なメソッドであるが、仮にゲッターの初回呼び出し時に値を初期化したい場合や、セッターにバリデーション処理を加えた場合などはどうなるだろう? class TopSecret def earth_shape @earth_shape ||= 'flat' end def earth_shape=(shape) raise "We can't allow that." if shape == 'round' @earth_shape = shape end def reveal # `self.earth_shape` を経由しないので、インスタンス変数は初期化されない puts "The Earth is #{@earth_shape}." # 'The Earth is .' と出力 # `self.earth_shape=` を経由しないので、バリデーションは実行されない @earth_shape = 'round' end end (もはや attr_accessor の面影がなくなってしまっているが、実は主役はゲッター・セッターなので気にしないでほしい。) インスタンス変数に直接アクセスすることによって、ゲッター内の「インスタンス変数の初期化」、セッター内の「値のバリデーションによる例外」といった「副作用」を通り越してしまっている。 ゲッター・セッターが副作用を持っていることが問題なのだろうか、それとも副作用を持っているゲッター・セッターを無視してインスタンス変数にアクセスしていることが問題なのだろうか。 深追いするとなかなか奥が深そうなので、余力があれば機会を改めて考えてみたい。 ひとまずは、 attr_accessor で事足りないようなゲッター・セッターの場合は、前述の self キーワードを使う方法に頼らざるを得ないというところか。 まとめ インスタンスメソッド内でセッター earth_shape= を呼びたい場合、 self.earth_shape = 'round' のように self を使わないとローカル変数が作られてしまう self を使いたくない場合、 @earth_shape = 'round' のようにインスタンス変数に直接アクセスすることもできる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails6.0 + jQuery + Bootstrap] d-flexが有効だと style="display: none;" が効かない。

課題 Bootstrapのd-flexが有効な場合 $element.fadeOut() が効かなかった。 **子要素の列が消えてくれない。 原因 どうやらこいつだ。 結論 d-flex を外すと効いた。 jquery $element.removeClass('d-flex') $element.fadeOut(300)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rspecのrspecに学ぶ、ネストの深いrspecを書かない方法

ネストの深いrspec rspecはcontextのネストが深くなることがある。 BDDのテストケースは下記の形式をとるので、これをcontextで表現しようとすると難しいことがある。 Given:最初の文脈(前提)があって、 When:イベントが発生した場合、 Then:なんらかのアウトプットを保証する。 例えば、fizz_buzz問題でfizzを出力する振る舞いを確認する場合はこうなる。事前条件は必要ないので書いていない。シンプルで、contextとitをつなげてみたときにわかりやすい英文として記述できる。 describe "#fizz_buzz" do subject { fizz_buzz(input) } context "when input is multiple of 3" do let!(:input) { 3 } it { is_expected to eq "fizz" } end end ここで、3の倍数に対する入力がfizzであることを確認するのに、3だけを確認するのは心細いことに気づく。 上述のフォーマットを崩さないように、itの中身を変えないことを意識するとこうなる。 describe "#fizz_buzz" do subject { fizz_buzz(input) } context "when input is multiple of 3" do let!(:input) { 3 } it { is_expected to eq "fizz" } end context "when input is multiple of 3" do let!(:input) { 12 } it { is_expected to eq "fizz" } end context "when input is multiple of 3" do let!(:input) { 303 } it { is_expected to eq "fizz" } end end 同じcontextが横並びしてしまった。5の倍数や3と5の倍数のパターンも記述することを考えると、3の倍数のパターンはひとくくりにしたほうがわかりやすそうだ。 describe "#fizz_buzz" do subject { fizz_buzz(input) } context "when input is multiple of 3" do context "inputting 3" do let!(:input) { 3 } it { is_expected to eq "fizz" } end context "inputting 12" do let!(:input) { 12 } it { is_expected to eq "fizz" } end context "inputting 303" do let!(:input) { 303 } it { is_expected to eq "fizz" } end end end ネストが深くなってしまった。context "inputting 3" doというのは、letで与えている数字と重複しているし、そもそも3を選んだことにはあまり意味はない。3の倍数であれば何でも良かったのだが、contextにまで顔を出してしまっている。 愚直にこうしてはいけないのだろうか?is_expectedのようなおしゃれな構文を手放した代わりに、ネストが浅く、単純で理解しやすいexpectが並んでいる。 describe "#fizz_buzz" do context "when input is multiple of 3" do it "returns fizz" do expected = "fizz" expect(fizz_buzz(3)).to eq expected expect(fizz_buzz(12)).to eq expected expect(fizz_buzz(303)).to eq expected end end end rspecの書き方に関する議論 どうも、Qiitaなどで見かける記事では、subjectを使おうとか、contextを細かく分けていたり、またそのcontextの中にちょっとずつbeforeが入っていてなんかしている事が多い。[要出典] 私は異端者なのだろうか。 なんか腑に落ちないので、rspecのrspecを読んでみた。 rspecのrspecの流儀 rpec-coreのrspecを読んだところ、指針らしきものが見えてきた。結論から言うと、rspecのrspecではとても愚直にrspecを記述している。 contextにbefore、letが無くてもいい contextのbeforeやletは無理して使わない。 itの中で事前条件のためのセットアップをすることが多い itの中身は一行でなくてもいい 事前条件のセットアップを含めて、そこそこ長い記述をすることもある ただし、これが許されるのはあくまで1つの振る舞いを確認している場合。複数の振る舞いを1つのitで確認することは無い itの中身に事前条件のセットアップを含んでもいい というかほとんどそうしている is_expectedは無理に使わない ほぼ見かけなかった itに複数のexpectを書いていい 1つの振る舞いに関することであれば1つのitに複数のexpectを書く 1つの振る舞いを観測するために複数の検証が必要なことは普通にある itは振る舞いで分割する expectを複数書くのを許容しているが、何でもかんでも書いているわけではない。 外から見た動きとして特徴づけられることごとにitを分ける context内でメソッド定義してitの中身を短くする subjectやletは無理に使わず普通にメソッド定義する ちなみに、context内でのメソッド定義はcontextローカルなヘルパーとして登録される。 rspecに学ぶrspecの書き方のミソ どうやら、rspecを書く上で重要なのは下記二点のよう。subjectとかis_expectedとかはこれらを満たした上で使えたら使おう。 1つの振る舞いにつき1つのit(振る舞いの単位は観測者によって変わる) contextとitに書いてあることが英文として読みやすいように構成する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails6】deviseで独自カラムを追加して使用する方法 ④

これまで これまでに、deviseのインストール・Userモデルの作成・Viewの作成・Controllerの作成・Modelのカスタマイズ・Routesの編集を行いました。 今回は、Deviseの設定 ( 独自カラムでログインできるようにする ) を行っていきたいと思います。 ▽前回の記事はこちら▽ 【Rails6】deviseで独自カラムを追加して使用する方法 ① 【Rails6】deviseで独自カラムを追加して使用する方法 ② 【Rails6】deviseで独自カラムを追加して使用する方法 ③ ① Devise の設定 /config/initializers/devise.rb ファイルを編集していきます。 今回編集する内容は、ログインの認証するキーを email から user_code にするようにします。 コメントアウトされているので、#を外します。 devise.rb_編集前 # ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating a user. The default is # just :email. You can configure it to use [:username, :subdomain], so for # authenticating a user, both parameters are required. Remember that those # parameters are used only when authenticating and not when retrieving from # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. # config.authentication_keys = [:email] devise.rb_編集後 # ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating a user. The default is # just :email. You can configure it to use [:username, :subdomain], so for # authenticating a user, both parameters are required. Remember that those # parameters are used only when authenticating and not when retrieving from # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. config.authentication_keys = [:user_code] 上記に編集すると、ログイン認証のキーが変更されます。 前回まで、ファイルをまるまる載せていましたが、長くなるため編集する部分のみを記載します。 ② ストロングパラメータ 追加 今回の肝と思われるのは、ここの部分です。 今までのカスタマイズ・設定で、画面の表示は正常に行われるのですが、 このままでは、独自カラムが画面に入力した値が正常にコントローラへ渡せません。 Deviseでは、ストロングパラメータを追加する必要があります。 ストロングパラメータの追加は、 /app/controllers/application_controller.rbを編集します。 application_controller.rb_編集前 class ApplicationController < ActionController::Base end application_controller.rb_編集後 class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? # before_action でメゾット呼び出し protected # protected内にメゾットを記述 def configure_permitted_parameters # メゾット devise_parameter_sanitizer.permit(:sign_up, keys: [:user_code, :user_last_name, :user_first_name] ) devise_parameter_sanitizer.permit(:sign_in, keys: [:user_code]) devise_parameter_sanitizer.permit(:account_update, keys: [:user_code, :user_last_name, :user_first_name]) end end No 種類 説明 1 sing_up 登録時 2 sing_in ログイン時 3 account_update ユーザ情報更新時 上記のパターンの説明を表にしてみました。 3種類の処理時にストロングパラメータが通るように、追加します。 devise_parameter_sanitizer.permit(:処理種類, keys: [:カラム名, :カラム名]) ここまで行うと、独自カラムを追加してdeviseを使用することができるようになります。 あとは、ログインページ等のHTMLとCSSを作り、実用性のある画面を作成すると完成です。 最後に deviseの使用方法などはたくさんインターネットに情報がありますが、今回は「独自カラムの追加とログイン時に独自のカラムで行えるようにする」というテーマで、一からご説明してきました。 どちらかというと、Rails初心者の方向けに近かったのかなと思いますが、deviseのカスタマイズをされたい方も参考になるかなと思いますので、ぜひ参考にしてみてください。 記事を複数回分けましたが以上、deviseで独自カラムを追加して使用する方法のご紹介でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails】PV数の導入(impressionist)

実装すること gem impressionistを使用して、投稿ページにPV(ページビュー)を設置します。 同じユーザーがPV数を稼ぐ、増やせないように同じセッションはカウントしないようにします。 impressionist: https://github.com/charlotte-ruby/impressionist こちらのgithubを参考に実装していきます。 完成形 目のアイコンがPV数を表示しており、 投稿詳細に行くと数字がカウントされています。 前提 下記の機能実装済み。 ・devise(今回は、memberモデル) ・投稿機能(今回は、questionモデル) schema.rb ActiveRecord::Schema.define(version: 2021_05_05_122222) do create_table "members", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.datetime "remember_created_at" t.string "name", default: "", null: false t.text "introduction" t.string "image_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["email"], name: "index_members_on_email", unique: true"index_members_on_reset_password_token", unique: true end create_table "questions", force: :cascade do |t| t.integer "member_id", null: false t.string "title", default: "", null: false t.text "content", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["member_id"], name: "index_questions_on_member_id" end end 手順 ①gem"impressionist"の導入 ②modelへの記載 ③controllerへの記載 ④viewへの記載 実装 1. impressionistのインストール railsのバージョンに注意。(6と5以下で異なる) 私は5なので、下記を記載。 Gemfile #rails 5 or lower gem 'impressionist', '~>1.6.1' インストールします。 terminal bundle install impressionistのマイグレーションファイルを作成します。 terminal rails g impressionist 下記が自動的に作成されます。 *user_idとありますが、ここは特にいじる必要はありません。  私はuserではなく、memberを使用していたのでmember_idに修正したところエラーが出ました。   私自身が作成したmemberモデルを表すidではなかったです。 create_impressions_table.rb class CreateImpressionsTable < ActiveRecord::Migration[5.2] def self.up create_table :impressions, :force => true do |t| t.string :impressionable_type t.integer :impressionable_id t.integer :user_id t.string :controller_name t.string :action_name t.string :view_name t.string :request_hash t.string :ip_address t.string :session_hash t.text :message t.text :referrer t.text :params t.timestamps end add_index :impressions, [:impressionable_type, :message, :impressionable_id], :name => "impressionable_type_message_index", :unique => false, :length => {:message => 255 } add_index :impressions, [:impressionable_type, :impressionable_id, :request_hash], :name => "poly_request_index", :unique => false add_index :impressions, [:impressionable_type, :impressionable_id, :ip_address], :name => "poly_ip_index", :unique => false add_index :impressions, [:impressionable_type, :impressionable_id, :session_hash], :name => "poly_session_index", :unique => false add_index :impressions, [:controller_name,:action_name,:request_hash], :name => "controlleraction_request_index", :unique => false add_index :impressions, [:controller_name,:action_name,:ip_address], :name => "controlleraction_ip_index", :unique => false add_index :impressions, [:controller_name,:action_name,:session_hash], :name => "controlleraction_session_index", :unique => false add_index :impressions, [:impressionable_type, :impressionable_id, :params], :name => "poly_params_request_index", :unique => false, :length => {:params => 255 } add_index :impressions, :user_id end def self.down drop_table :impressions end end dbに反映させましょう。 terminal rails db:migrate 2. modelへの記載 models/question.rb class Question < ApplicationRecord is_impressionable belongs_to :member validates :title, presence: true validates :title, length: { minimum: 5, maximum: 50 }, allow_blank: true validates :content, presence: true validates :content, length: { minimum: 20 }, allow_blank: true end 下記を一番上に追加しています。 is_impressionable or is_impressionable :counter_cache => true これを追加することで、questionモデルでimpresionistを使う事ができます。 3. controllerへの記載 questions_controller.rb class Public::QuestionsController < ApplicationController impressionist :actions => [:show] def show @member = current_member @question = Question.find(params[:id]) impressionist(@question, nil, unique: [:session_hash.to_s]) end それぞれ説明します。 ① impressionist :actions => [:show] ⇨ PV数を計測したいので、アクションを指定します。  ・投稿詳細(questions#show)で計測したい。  ・[ ]内は、計測したいアクションを記載。 ② impressionist(@question, nil, unique: [:session_hash.to_s]) ⇨ showアクション内にも記載します。  ・@questionを計測する対象として設定します。   今回は、投稿詳細なので@question。showにある変数を入れましょう。  ・uniqueでは、どの値で計測するかを書きましょう。今回は、sessionで計測したいので、   先ほど作成した、マイグレーションファイルのカラム名を書きましょう。(参照: t.string :session_hash) 4.viewへの記載 *わかりやすくするために余計な箇所は省いてあります。 questions/show.html.erb <div class="container"> <div class= "row"> <div class="col-md-3 pt-5">    <i class="fas fa-eye">     <%= @question.impressionist_count(:filter=>:session_hash) %>     </i> </div> </div> </div> ここで計測(いわゆるcount)したいので、下記を記載します。 <%= @question.impressionist_count(:filter=>:session_hash) %> ・ impressionist_countで計測。 ・ (:filter=>:session_hash)でフィルターをかけ、どれで計測するか設定。   今回は、sessionで計測なので、記載の通り。 これで問題なく計測できるかと思います。 もし、エラーや上手く動かない。などあればコメント頂けたらと思います。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】ゲッター/セッターとは

ゲッター 通常、インスタンス変数はクラス内でないと参照できないが、 クラス外で参照したい時に使うのがゲッターメソッド。 ゲッター使い方 class Animal def initialize(name) @name = name end def getName #「ゲッター」メソッド @name end end dog = Animal.new('ライオン') #ゲッターメソッドを定義することで、クラスの外でも@nameを取得できる p dog.getName #=> 'ライオン' セッターとは? クラス外でインスタンス変数の値を変更したい時に使うのがセッターメソッド。 インスタンス変数の更新もクラス内でしかできませんので、 インスタンス変数の内容を更新する専用のメソッド「セッター」を定義します。 セッター使い方 class Animal def initialize(name) @name = name end def setName=(name) #「セッター」メソッド @name = name end end dog = Animal.new('ライオン') #セッターメソッドを定義することで、クラスの外でも@nameを変更できる dog.setName = 'トラ' p dog.getName #=> 'トラ' まとめ クラス外でインスタンス変数を使用するときはゲッターを定義する クラス外でインスタンス変数の値を変更するときはセッターを定義する。 最後まで見ていただきありがとうございました!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【devise】アカウント情報変更機能実装

こんばんは。 現在オートバイのレビューサイトを作成中のプログラミング初学者です。 マイページに、アカウント編集機能を実装するにあたって、いくつか壁にぶち当たったので、実装までの流れをまとめたいと考え、投稿します。 目的 deviseを用いて、マイページ内にアカウント情報編集機能を実装する 環境 Ruby on rails ver.6.1.3.1 Ruby ver.2.6.5 Docker ver.20.10.5 Docker compose ver.1.28.5 前提条件 ・gem Devise導入済み 公式Github ・マイページ実装済み(参考:私はusersコントローラーを自作し、showアクションにてマイページを実装しました。) 実装 ①ルーティング設定 devise_for :users, controllers: { registrations: 'users/registrations' } devise導入で、すでにdevise_for :usersまでは記載済みかと思いますが、 controllersオプションを追記し、deviseで標準装備されているregistrationsコントローラーを用いてアカウント情報編集機能を実装していきます。 rails routesでPrefixパスの確認などをしておいてもいいかと思います。 ②application_controller.rb編集 class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? private def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname]) devise_parameter_sanitizer.permit(:account_update, keys: [:nickname]) end end ストロングパラメーターを実装します。 deviseを導入済みであれば、アカウント新規登録時に用いるストロングパラメーターconfigure_permitted_parametersメソッド(メソッド名は任意、慣習的に使用。)を実装済みかと思いますが、アカウント情報編集のためにdevise_parameter_sanitizer.permit(:account_update, keys: [:nickname])を追記します。 ※アカウント情報にemail、password以外のカラムを用意している場合。私はnicknameカラムを追加しています。 ③ビューファイルにアカウント情報変更画面への遷移パスを設置 <div class="user-edit"> <% if user_signed_in? && @user == current_user %> <%= link_to "アカウント情報編集", edit_user_registration_path, class: "user-edit-btn" %> <% end %> </div> classなどは自由ですが、if user_signed_in? && @user == current_userでユーザーとしてサインイン済みであること、遷移しようとするアカウント情報変更画面は今ログインしているユーザーであることを前提にリンクを設置します。 ※インスタンス変数userは事前にusersコントローラーのshowメソッド内で、@user = User.find(params[:id])などと定義しておきましょう。 ④アカウント情報変更画面の実装 <h2>Edit <%= resource_name.to_s.humanize %></h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> <% end %> <div class="field"> <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> <%= f.password_field :password, autocomplete: "new-password" %> <% if @minimum_password_length %> <br /> <em><%= @minimum_password_length %> characters minimum</em> <% end %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> <%= f.password_field :current_password, autocomplete: "current-password" %> </div> <div class="actions"> <%= f.submit "Update" %> </div> <% end %> <h3>Cancel my account</h3> <p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p> <%= link_to "Back", :back %> ビューファイルはviews/devise/registrations/edit.html.erbを使います。 初期は上記のように必要十分の装備と、味気ない見た目となりますので、これを編集していきます。 <div class="user-edit-contents"> <h1>プロフィール編集</h1> <div class="user-edit-form"> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <div class="field"> <%= f.label :email %> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <div class="field"> <%= f.label :nickname %> <%= f.text_field :nickname, autofocus: true, autocomplete: "nickname" %> </div> <div class="field"> <%= f.label :password %> <%= f.password_field :password, autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, autocomplete: "new-password" %> </div> <div class="actions"> <%= f.submit "保存", class:"user-update-btn" %> </div> <% end %> <%= link_to "戻る", user_path(current_user.id), class:"redirect-btn" %> </div> </div> 編集後がこちらになります。昼下がりの料理番組のようですね。 ポイントは下記の通りです。 ①メール認証部分を削除 ②nickname変更フォームを追加 ③パスワード最小文字表示部分の削除 ④:current_passwordフォーム削除(後ほど解説します) ⑤アカウント削除ボタン削除 このままでは、正しくフォームを入力しても更新されませんし、エラーが出ることがあります。 ⑤registrations_controller.rb編集 deviseで標準装備されているコントローラーcontrollers/users/registrations_controller.rbに必要なメソッドを定義していきます。 ※標準ではコメントだらけのコントローラーファイルになっているかと思います。 protected #コメントアウト外す def update_resource(resource, params) resource.update_without_current_password(params) #独自のメソッド。解説は下記にて。 end #更新後のパスを指定。マイページに戻るように設定。 def after_update_path_for(resource) user_path(@user.id) end update_without_current_passwordメソッドについて deviseでアカウント情報を変更する際に、現在のパスワードが必須となります。 毎回パスワードを入力しないといけないのは面倒なので、省くメソッドを作ります。 deviseに標準であるメソッドはupdate_without_passwordであり、アカウント情報変更時にパスワードそのものを必要としなくすることは可能ですが、 今回は現在のパスワードは不要でも、パスワードの変更は可能とするように実装していきます。 ⑥Userモデルにupdate_without_current_passswordメソッドの定義 def update_without_current_password(params, *options) params.delete(:current_password) if params[:password].blank? && params[:password_confirmation].blank? params.delete(:password) params.delete(:password_confirmation) end result = update(params, *options) clean_up_passwords result end これにて現在のパスワードはパラメーター内で不要となり、かつパスワードとパスワードの確認を空とした場合、パスワードの更新はなされなくなります。 参考にした記事の多くはupdate_attributesメソッドを使っていましたが、おそらくrailsの6.1以降はこのエイリアスの使用はできずNoMethodErrorを返しますので、同義のupdateメソッドを採用します。 これで実装完了です。お疲れ様でした。 ※上記例はdeviseの日本語化をしています。CSSはご自由に。 補足 正規表現のバリデーションを設けている場合 userモデルでPASSWORD_REGEXのようなバリデーションを設けている場合、これが反応してどれだけパスワードを入力しても、バリデーションに引っかかるエラーが出現するかと思います。(実際私はこれにかなり苦戦しました。) user.rb PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze validates_format_of :password, with: PASSWORD_REGEX, message: 'には英字と数字の両方を含めて設定してください' この場合、このバリデーションに下記1点メソッドを追記してください。 user.rb PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze validates_format_of :password, with: PASSWORD_REGEX, message: 'には英字と数字の両方を含めて設定してください', if: :password_required? deviseの標準メソッドのうちの1つです。 参考:varidatavle.rb これによりpasswordの有無を判定します。その上で、正規表現バリデーションを適応させます。 ゲストログイン機能を実装している場合 ポートフォリオでよくあるゲストログイン。ワンタップでログインでき、アプリの機能を試せるのは魅力的ですよね。 今回アカウント情報編集機能の実装ができましたが、ゲストユーザーに限っては誰でもログインできる特性上、簡単に編集できるのは望ましくありません。 コントローラーに壁を張ります。 registrations_controller.rb before_action :ensure_normal_user, only: [:edit, :update] #メソッド名は任意 protected #ゲストユーザーはアカウント情報を編集・更新できない def ensure_normal_user if resource.email == 'guest@example.com' redirect_to root_path, alert: 'ゲストユーザーは編集できません。' end end ゲストユーザーではないことを確認するメソッドを追加しました。 ゲストログインに使用されるメールアドレス(今回はguest@example.com)でeditアクション、updateアクションを実行しようとした場合、ルートパスにリダイレクトさせ、アラートを表示させるようにしました。 これで、ゲストユーザーはそもそもアカウント情報編集画面にすら遷移できなくなります。 まとめ ここまでご覧いただきありがとうございました。 何か間違いなどがあれば遠慮なくご指摘いただけますと幸いです。 参考記事 公式Github [Rails] deviseの使い方(rails5版) devise ユーザーのプロフィール画面作成と編集(デフォルトをカスタマイズ) deviseで現在のパスワード無しでuserを更新する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む