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

ArgumentError in GroupsController#create ,wrong number of arguments (given 0, expected 1)の一例

1. どんなエラー?

「GroupsControllerのcreateの部分で引数が1つ返ってくると予想される(記述されている)のに、実行時には0件でしたよ」というエラーです

<エラー文>
スクリーンショット 2020-01-14 22.14.02.png

<エラーに関係したファイル>

groups_controller.rb
class GroupsController < ApplicationController
  def index
  end

  def new
    @group = Group.new
    @group.users << current_user
  end

  def create
    @group = Group.new(group_params)
    if @group.save
      redirect_to root_path, notice:'グループを作成しました'
    else
      render :new
    end
  end

  def edit
    @group = Group.find(params[:id])
  end

  def update
    @group = Group.find(params[:id])
    if @group.update(group_params)
      redirect_to root_path, notice: 'グループを更新しました'
    else
      render :edit
    end
  end

  private
  def group_params
    params.require.permit(:name,user_ids:[])
  end
end

2.原因と解決方法

まず結論から言いますと、'require'の後ろにハッシュ指定がないため、'(:group)'をつけることにより解決します。

groups_controller.rb
  params.require(:group).permit(:name,user_ids:[])

3.原因の見つけ方

 このエラーに対する正攻法として、引数の数がおかしいというエラーに対し、じゃあ〇〇行目(今回は35行目)では何を呼び出すことが可能な状態なのかを調べるという方法があります。(実践方法は下記に記載しますので必要な方はご覧いただければと思います。)

 調べた内容より、間違いの確認と、呼び出したい内容がどのハッシュに格納されているか確認ができます。これにより'require'で呼び出すハッシュを決めることができます。

(※requireを使用するときは多重ハッシュとなっている場合となるため、値の直前のハッシュを指定します。これを使わないと値を格納してほしいところにハッシュ値が入ってしまいエラーとなります。今回の例がまさにそれに当たります。)

<実践方法>
①pry-rails(デバック(確認)用のツール)をインストールするため、Gemfileの最後の行に下記のコマンドを追記する

gem 'pry-rails'

②作業中のファイルのディレクトリで bundle installする(このコマンドがわからない方は検索をかけてもらえばいっぱい出てきます)

Neverland:chat-space-kai kontatomoya$ bundle install

③指定の行を改行してその行に binding.pry を記述する(筆者の場合35行目を改行して、新しい35行目に書きます)

groups_controller.rb
#1~32行目は上記の内容から変化がないので省略
33 private
34   def group_params
35     binding.pry
36     params.require.permit(:name,user_ids:[])
37   end
38 end

④rails s します

Neverland:chat-space-kai kontatomoya$ rails s

⑤エラーを吐いてしまうページにつなげます。(筆者の場合は下記の写真の登録するボタンを押すとエラーページ繋がります。)すると、次のページに飛ばずに読み込み中のままとなります。

スクリーンショット 2020-01-14 22.53.53.png

⑥この状態のままターミナルを確認します。すると下記のようになっています。

#上にもっと記述が出ますが直接関係ないので省略します

From: /Users/kontatomoya/projects/chat-space-kai/app/controllers/groups_controller.rb @ line 35 GroupsController#group_params:

    34: def group_params
 => 35:   binding.pry
    36:   params.require.permit(:name,user_ids:[])
    37: end

[1] pry(#<GroupsController>)> 

⑦ここで [1] pry(#<GroupsController>)>  の隣に params と打ちましょう。すると下記のような回答が返ってきます

#続き
[1] pry(#<GroupsController>)> params   #paramsを打ちました
=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"/vrx3ZW1g+kcEgeh8V+FJlqUnBNEIR9jyvGeSl1r22IcMk+1F0I7zRhnKKXWpLNMtwjbmWxBBBaNR9phNk3KOg==", 
"group"=>{"name"=>"", "user_ids"=>["", "2"]},     ←この部分だけ{}で囲まれており、二重(多重)ハッシュとなっています
"commit"=>"登録する", "controller"=>"groups", "action"=>"create"} permitted: false>
[2] pry(#<GroupsController>)> 

 まずこれを見ると呼び出すことができるハッシュがわかります。その中でハッシュ'group'のハッシュだけ2重ハッシュ(ActionController::Parametersに二回囲まれている状態)となっております。
 今回筆者はその中の"name", "user_ids"がほしいと考えています。よって、こちらで'permit'したいハッシュ(:name,:user_ids[])の一つ上のハッシュ(今回は'group')を要求すれば良いとわかるいう流れとなります

(※今回の場合そのほかの値が欲しければ'require'をつけずに'permit(:先ほど調べたハッシュ名)'とすればエラーは無くなります。)

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

【翻訳】Testing best practices - GitLab

GitLabのテストにおけるベストプラクティス記事を日本語訳したものです。

https://docs.gitlab.com/ee/development/testing_guide/best_practices.html

最終閲覧日時: 2020/01/06

途中、おかしな日本語があるかもしれませんので原文と照らし合わせてお読みください

Test Design

GitLabではテストは最優先事項[1]です。機能の設計と同様に、テストの設計を考慮することは重要と考えています。

機能を実装するとき、我々は適切な機能を適切な方法で開発することを検討します。これにより、範囲を管理可能なレベルに絞り込むことができます。機能のテストを実装する場合、適切なテスト実装を検討する必要がありますが、重要な箇所をすべてカバーするテストは難しく、すぐに管理困難な段階にまで拡大するでしょう。

テストヒューリスティック[2]は、この問題の解決に役立ちます。これは、我々のコードに潜むバグを明らかにし、簡潔に対処します。テストを設計する時、既知のテストヒューリスティックの確認に時間をかけ、我々のテスト設計手法を周知してください。Test Engineering sectionで有用なヒューリスティックのドキュメントが読めます。

Test speed

GitLabには大規模なテストスイートがあり、並列化しないと実行に数時間かかることがあります。正確で効果的かつ高速なテストを書く努力をすることが重要です。

テストのパフォーマンスに関する注意事項を次に示します。

  • doublespyFactoryBot.build(...)より速い
  • FactoryBot.build(...).build_stubbed.createより速い
  • build, build_stubbed, attributes_for, spy, doubleを使う時、createでオブジェクトを作成しないこと。DBの永続化は遅い。
  • 本当にテストする必要がある場合以外は、JavaScriptを必要とする機能のテスト(RSpecの:jsのような)を行わないこと。ヘッドレスブラウザでのテストは遅い。

RSpec

rspecでテストするには

# run all tests
bundle exec rspec

# run test for path
bundle exec rspec spec/[path]/[to]/[spec].rb

guardを使用して変更を継続的に監視し、変更されたテストのみを実行します。

bundle exec guard

springとguardを一緒に使用する場合は、代わりにSPRING = 1 bundle exec guardとしてspringを使用してください。

General guidelines

  • トップレベルにはdescribe ClassNameを一つだけ定義すること
  • describe内では、クラスメソッドを.method、インスタンスメソッドを#methodと表記すること
  • ロジック的に分岐する場合はcontextを使うこと
  • テスト項目の順序はプロダクトコードクラス内の順序と一致させること
  • 改行を使用してフェーズを分離し、4フェーズのテストパターンに従うこと
  • localhostのようなハードコーディングはせず、Gitlab.config.gitlab.hostのように設定変数を使うこと
  • sequenceによって生成された変数のような値に対してテストを行わないこと(落とし穴を参照)
  • beforeafterなどのフックの引数に:each(alias :example)与えてもデフォルトで効いているため引数に指定しないこと
  • beforeafterのフックは:allのスコープよりも:contextのスコープのほうが望ましい
  • 指定した要素に作用するevaluate_script("$('.js-foo').testSomething()") (もしくはexecute_script)を使うときは、Capybaraのマッチャー(例えばfind('.js-foo'))であらかじめ要素が確実に存在することを確かめる
  • focus: trueを使ってテストしたい範囲を分離すること
  • テストに複数の期待値がある場合はaggregate_failuresを使用すること

System / Feature tests

Note: 新しいSystem / Feature testを書く前に一度、書かないことを検討してください

  • feature specのファイル名はuser_changes_password_spec.rbのようにROLE_ACTION_spec.rbとすべき
  • シナリオタイトルには成功ケースと失敗ケースを記載すること
  • 「successfully」など、情報がないようなシナリオタイトルは避けること
    • 何がsuccessfullyなのか明記すること
  • 機能のタイトルを繰り返すだけのシナリオタイトルは避けること
  • データベースには必要なレコードのみを作成すること
  • Happy path[3]とless happy pathだけをテストします。
  • 可能な限り単体テストまたは統合テストでテストする必要がある
  • ActiveRecord内部ではなくページに表示されるものを評価すること
    • もしレコードが作成されたことを確認したかったら、Model.count等でモデルが増えたことをテストするのではなく、その項目がページに表示されるというテストを追加すること。
  • DOM要素を探してもよいがテストをより脆弱にするため乱用はしないこと

Live debug

live_debugメソッド[4]を使えば、Capybaraを一時停止して、ブラウザでウェブサイトを表示できます。デフォルトブラウザで開きます。テストの実行を再開するには、任意のキーを押します。

以下のような感じです。

$ bin/rspec spec/features/auto_deploy_spec.rb:34
Running via Spring preloader in process 8999
Run options: include {:locations=>{"./spec/features/auto_deploy_spec.rb"=>[34]}}

Current example is paused for live debugging
The current user credentials are: user2 / 12345678
Press any key to resume the execution of the example!
Back to the example!
.

Finished in 34.51 seconds (files took 0.76702 seconds to load)
1 example, 0 failures

Note: live_debugはJavaScriptが使える場合でのみ動きます。

Run :js spec in a visible browser

以下のようにCHROME_HEADLESS=0を付けてspecを実行します。

CHROME_HEADLESS=0 bundle exec rspec some_spec.rb

このテストはすぐに終わりますが、これにより何が起こっているのかわかります。CHROME_HEADLESS=0を付け live_debugを使って開いているブラウザを一時停止し、再び開くことができません。これは要素のデバッグと検査に使用できます。

byebugまたはbinding.pryを追加して、実行を一時停止し、テストをステップ実行することもできます。

Screenshots

capybara-screenshotgemを使用して失敗時に自動的にスクリーンショットを撮ります。 CIでは、これらのファイルをジョブアーティファクトとしてダウンロードできます。

また、以下のメソッドを追加することにより、テストの任意の時点でスクリーンショットを手動で取得できます。不要になったら削除してください!詳細については、ここを参照してください。

  • screenshot_and_save_page
    • Capybaraが「見ているもの」のスクリーンショットを作成し、ページソース(htmlファイル等)を保存します。
  • screenshot_and_open_image
    • Capybaraが「見ているも」のをスクリーンショット化し、画像を自動的に開きます。

これにより作成されたHTMLダンプにはCSSがありません。これにより、実際のアプリケーションとは大きく異なった外観になります。デバッグを容易にするCSSを追加するsmall hackがあります。

Fast unit tests

一部のクラスはRailsから十分に分離されており、RailsやBunlderの:defaultグループのgem等によって追加されたオーバーヘッドなしでそれらをテストできるはずです。このような場合、テストファイルでspec_helperをreuqireする代わりにfast_spec_helperをreuqireできます。次の理由からテストは非常に高速に実行されるはずです。

  • gemのロードをスキップ
  • Railsアプリの起動をスキップ
  • GitLab ShellとGitallyのスキップ
  • テストリポジトリのセットアップをスキップ

fast_spec_helperはlibディレクトリ配下にある自動ロードクラスもサポートします。つまり、クラス/モジュールがlibディレクトリ配下のコードのみを使用している限り、依存関係を明示的にロードする必要はありません。fast_spec_helperは、Rails環境で一般的に使用されるコア拡張機能を含む、すべてのActiveSupport拡張機能もロードします。

コードがgemを使用している場合、または依存関係がlibにない場合、require_dependencyを使用して依存関係をロードする必要があることに注意してください。

たとえば、Gitlab::UntrustedRegexpクラスを呼び出しているコードをテストする場合は、内部でre2ライブラリを使用します。re2 gemを必要とするライブラリ内のファイルにrequire_dependency 're2'を追加して、この要件を作成する必要があります 明示的に指定するか、仕様自体に追加することもできますが、前者が優先されます。

spec_helperの場合に30秒以上かかるloadが、fast_spec_helperを使うことで1秒程度のロードですみます。

let variables

GitLabのRSpecスイートでは、重複を減らすためにlet(それに加えて、厳密な非遅延バージョンlet!)変数を広範囲に使用しています。しかしながらこれは時々コードを分かりにくくします。そのため、今後の使用に関するガイドラインを設定する必要があります。

  • let!はインスタンス変数よりも好ましい。letlet!よりも好ましい。ローカル変数はletよりも望ましい
  • letを使用してspecファイル全体の重複を減らせる
  • 単一のテストでのみ使用される変数はletを使わずにitブロック内でローカル変数として定義すること
  • 最上位の記述ブロック内でlet変数を定義しないこと。これは、より深くネストされたコンテキストまたは記述ブロックでのみ使用される。定義は使用する場所にできるだけ近づけること。
    • 解せない
  • あるlet変数の定義を別のlet変数の定義で上書きしないようにする
    • これもスコープが異なっていればいいのでは?
  • 別で定義されているlet変数を定義するな(たぶん糸の異なる重複定義するなってことだと思われ)
    • 代わりにヘルパーメソッドを使うべき
  • let!変数は定義された順序が重要な場合にのみ使用すること。それ以外はletで十分
    • letは遅延評価であり、参照されるまで評価されないことに注意して

Common test setup

場合によっては、各exampleでテスト用に同じオブジェクトを生成する必要はありません。たとえば、プロジェクトとのそのプロジェクトのゲストは、プロジェクトとゲストが関係するすべてのファイルに対してテストするために必要となります。これは、test-profgemで導入できるlet_it_be変数とbefore_allフックを使うことで達成できます。

let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }

before_all do
  project.add_guest(user)
end

これにより、このcontextに対して作成されるProject, User, ProjectMemberが一つだけになります。

let_it_beおよびbefore_allは、ネストされたcontext内でも使用できます。トランザクションロールバックにより、contextが処理された後に自動的にクリーンアップされます。

let_it_beブロック内で定義されたオブジェクトを変更する場合、必要に応じてオブジェクトをリロードするか、すべての例でリロードするリロードオプションを指定する必要があることに注意してください。

let_it_be(:project, reload: true) { create(:project) }

また、再検索オプションを指定して、新しいオブジェクトを完全にロードすることもできます。

let_it_be(:project, refind: true) { create(:project) }

set variables

Note: Gitlabではlet_it_beを支持しているため、setを削除しつつあります。詳細はこちら参照してください。

場合によっては、各exampleでテスト用に同じオブジェクトを生成する必要はありません。たとえば、プロジェクトとのそのプロジェクトのゲストは、プロジェクトとゲストが関係するすべてのファイルに対してテストするために必要となります。これは、letを使用するのと同じ方法でsetを使用することで実現できます。

rspec-setはActiveRecordオブジェクトでのみ機能し、新しいサンプルの前に必要な場合にのみモデルをリロードまたは再作成します。つまり、プロパティを変更したときまたはオブジェクトを破棄したときです。

setブロック内のletブロックで定義されたモデルを参照することはできませんので注意してください。

また、:jsスペックでは、各exampleの後にデータベースの状態をクリーンアップするためにトランザクションを使用しないため、setはサポートされていません。

Time-sensitive tests

TimecopはRubyベースで利用でき、時間依存のテストケースに有効です。時間に依存するテストを実行・検証する場合、Timecopを使用して一時的なテストの失敗を防ぎます。

例えば、

it 'is overdue' do
  issue = build(:issue, due_date: Date.tomorrow)

  Timecop.freeze(3.days.from_now) do
    expect(issue).to be_overdue
  end
end

Feature flags in tests

すべての機能フラグはRubyベースのテストにおいて、デフォルトで有効になるようにスタブ化されています。

テストで機能フラグを無効にするには、stub_feature_flagsヘルパーを使用します。たとえば、テストでci_live_trace機能フラグをグローバルに無効にするには、

stub_feature_flags(ci_live_trace: false)

Feature.enabled?(:ci_live_trace) # => false

一部のアクターに対して機能フラグを無効にし、他のアクターに対しては無効にしないテストを設定する場合、ヘルパーに渡すオプションでこれを指定できます。たとえば、特定のプロジェクトのci_live_trace機能フラグを無効にするには、

project1, project2 = build_list(:project, 2)

# Feature will only be disabled for project1
stub_feature_flags(ci_live_trace: { enabled: false, thing: project1 })

Feature.enabled?(:ci_live_trace, project1) # => false
Feature.enabled?(:ci_live_trace, project2) # => true

Pristine test environments

1つのGitLabテストで実行されるコードは、多くのデータにアクセスして変更する場合があります。テストを実行する前に慎重に準備し、その後クリーンアップしないと、データはテストによって変更され、次のテストの動作に影響を与える可能性があります。これは必ず避けてください。幸いなことに、既存のテストフレームワークのほとんどはこのようなケースを回避しています。

テスト環境が汚染されると、一般的には不安定なテストになります。テスト環境汚染の多くの場合は、スペックAの後にスペックBを実行すると確実に失敗するが、スペックBの後にスペックAを実行すると確実に成功するといった、順序の依存関係として現れます。このような場合、rspec --bisect5を使用して、どのスペックに問題があるかを判断できます。問題を解決するには、テストスイートで環境がどのように維持されているかをある程度理解する必要があります。続きを読んで各データストアの詳細をご覧ください。

SQL database

database_cleanergemによって管理しています。各スペックはトランザクションに囲まれ、テストが完了するとロールバックされます。特定のスペックでは、完了後にすべてのテーブルに対してDELETE FROMクエリが発行されます。これにより複数のデータベース接続(ブラウザからの操作やマイグレーションスペックなどのスペックにとって重要な)から作成された行を表示できます。

よく知られているTRUNCATE TABLESアプローチの代わりにこれらの戦略を使用した結果の1つに、主キーと他のシーケンスがスペック間でリセットされないことがあります。したがって、スペックAでプロジェクトを作成してからスペックBでプロジェクトを作成すると、最初のプロジェクトはid = 1になり、2番目のプロジェクトはid = 2になります。

これは、スペックがIDの値またはその他のシーケンス生成列に依存しないことを意味します。偶発的な競合を避けるため、スペックではこれらの種類の列に値を手動で指定することも避けてください。代わりに、未指定のままにして、行の作成後に値を検索します。

Redis

GitLabではRedisに、キャッシュされたデータとSidekiqジョブの2つのデータカテゴリを保存します。殆どのスペックではRailsキャッシュはメモリ内に存在しています。これはスペック間で置き換えられるため、Rails.cache.readRails.cache.writeの呼び出しは安全です。ただし、スペックが直接Redis呼び出しを行う場合は、必要に応じて:clean_gitlab_redis_cache:clean_gitlab_redis_shared_state:clean_gitlab_redis_queuestraitを適切に使用する必要があります。

Background jobs / Sidekiq

デフォルトでは、Sidekiqジョブはジョブ配列にキューイングされ、処理されません。テストがSidekiqジョブをキューに入れて処理する必要がある場合は、:sidekiq_inlinetraitを使用できます。

:sidekiq_might_not_need_inlinetraitは、Sidekiqのインラインモードがフェイクモードに変更されたときに、Sidekiqが実際にジョブを処理するのに必要なすべてのテストに追加されました。このtraitを持つテストは、Sidekiq処理ジョブに依存しないように修正するか、バックグラウンドジョブの処理が必要/予想される場合、:sidekiq_might_not_need_inlinetraitを:sidekiq_inlineに更新する必要があります。

Note: ワーカーはApplicationJob/ActiveJob::Baseを継承していないため、perform_enqueued_jobsは現在使用できません。

Filesystem

ファイルシステムのデータは、「リポジトリ」と「その他すべて」に大まかに分けることができます。リポジトリはtmp/tests/repositoriesに保存されます。このディレクトリは、テストが実行される前、およびテストが終了した後に空になります。スペック間では空にならないため、作成されたリポジトリはプロセスの存続期間中、このディレクトリ内に蓄積されます。それらを削除するのはコストがかかりますが、注意深く管理しないと汚染につながる可能性があります。

これらを回避するには、テストスイートでハッシュストレージを有効にします。つまり、リポジトリにはプロジェクトのIDに依存する一意のパスが与えられます。プロジェクトIDはスペック間でリセットされないため、各スペックがディスク上の独自のリポジトリを取得することが保証され、スペック間で変更が表示されないようにします。

スペックでプロジェクトIDを手動で指定する場合、またはtmp/tests/repositories/ディレクトリの状態を直接検査する場合、実行の前後にディレクトリをクリーンアップする必要があります。一般的に、これらのパターンは完全に回避する必要があります。

アップロードなど、データベースオブジェクトにリンクされた他のクラスのファイルは、通常同じ方法で管理されます。スペックでハッシュストレージが有効になっている場合、IDによって決定される場所のディスクに書き込まれるため、競合は発生しません。

一部のスペックでは、projectsfactoryに:legacy_storagetraitを渡すことで、ハッシュストレージを無効にします。これを行うスペックは、プロジェクトまたはそのグループのパスをオーバーライドしてはなりません。デフォルトのパスにはプロジェクトIDが含まれているため、競合しません。ただし、2つの仕様が同じパスを持つ:legacy_storageプロジェクトを作成する場合、ディスク上の同じリポジトリを使用し、環境汚染をテストします。

その他のファイルは、スペックによって手動で管理する必要があります。たとえば、tmp/test-file.csvファイルを作成するコードを実行する場合、スペックでは、クリーンアップの一環としてファイルが削除されるようにする必要があります。

Persistent in-memory application state

Rspecによるスペックはすべて同じRubyプロセスを共有します。つまり、スペック間でアクセス可能なRubyオブジェクトを変更することで、互いに影響を与えることができます。これはグローバル変数、および定数(クラス、モジュールなどを含む)であることを意味しています。

通常、グローバル変数は変更しないでください。どうしても必要な場合、以下のようなブロックを使用して、変更を後でロールバックできます。

around(:each) do |example|
  old_value = $0

  begin
    $0 = "new-value"
    example.run
  ensure
    $0 = old_value
  end
end

スペックで定数を変更する必要がある場合は、stub_constヘルパーを使用して、変更が確実にロールバックされるようにする必要があります。

ENV定数を変更する必要がある場合は、代わりにstub_envヘルパーメソッドを使用できます。

ほとんどのRubyインスタンスはスペック感で共有されませんが、クラスとモジュールは一般的に共有されます。クラスおよびモジュールのインスタンス変数、アクセサー、クラス変数、およびその他のステートフルイディオムはグローバル変数と同じように扱われるべきです。必要がない限り変更しないでください。とくに、変更の必要性を排除するために、expectまたはstubに沿った依存関係の代入かを使うのことが望ましいです。他に選択肢がない場合は、上記のグローバル変数と同様にaroundブロックが使用できますが、可能な限り回避する必要があります。

Table-based / Parameterized tests

このスタイルのテストは、包括的な入力範囲で1つのコードを実行するために使用されます。テストケースを1回指定するだけで、入力のテーブルとそれぞれの予想出力とともに、テス​​トを読みやすく、コンパクトにすることができます。

GitLabではrspec-parameterizedgemを使っています。テーブル構文を使用し、Rubyの入力範囲をチェックする短い例は、次のようになります。

describe "#==" do
  using RSpec::Parameterized::TableSyntax

  where(:a, :b, :result) do
    1         | 1        | true
    1         | 2        | false
    true      | true     | true
    true      | false    | false
  end

  with_them do
    it { expect(a == b).to eq(result) }

    it 'is isomorphic' do
      expect(b == a).to eq(result)
    end
  end
end

Caution: whereブロックの入力として単純な値のみを使用します。プロシージャ、ステートフルオブジェクト、FactoryBotで作成されたオブジェクトなどを使用すると、予期しない結果が生じる可能性があります。

Prometheus tests

Prometheusメトリクス[6]は、テストの実行ごとに保持される場合があります。各サンプルの前にメトリクスが確実にリセットされるようにするには、Rspecテストに:prometheusタグを追加します。

Matchers

カスタムマッチャーを作成して、意図を明確にし、RSpecの予想の複雑さを隠す必要があります。これはspec/support/matchers/に配置する必要があります。マッチャーは、特定のタイプのスペック(機能スペック、リクエストスペックなど)にのみ適用される場合はサブフォルダーに配置できますが、複数のタイプの仕様に適用する場合は配置しないでください。

be_like_time

データベースから返される時間は、Rubyの時間オブジェクトと精度が異なる場合があります。そのため、スペックを比較する際に柔軟な許容範囲が必要です。be_like_timeを使用して、時間が1秒以内であることを比較できます。

expect(metrics.merged_at).to be_like_time(time)

have_gitlab_http_status

have_http_statusよりもhave_gitlab_http_statusをお勧めします。have_gitlab_http_statusは、ステータスが一致しない場合に常に応答本文も表示できるためです。これは、テストが落ちたときにソースコードを編集せず、テストを再実行せずとも落ちた原因を知るのに非常に役に立ちます。

特に500サーバーエラーが表示されている場合に便利です。

Shared contexts

すべてのshared contextは、spec/support/shared_contexts/に配置する必要があります。shared contextは、特定のタイプのスペック(機能スペック、リクエストスペックなど)にのみ適用される場合サブフォルダーに配置できますが、複数のタイプの仕様に適用される場合はそうではありません。

各ファイルにはコンテキストが1つだけ含まれ、わかりやすい名前を付ける必要があります。
(e.g. spec/support/shared_contexts/controllers/githubish_import_controller_shared_context.rb.)

Shared examples

すべてのshared exampleは、spec/support/shared_exampless/に配置する必要があります。shared examplesは、特定のタイプのスペック(機能スペック、リクエストスペックなど)にのみ適用される場合サブフォルダーに配置できますが、複数のタイプの仕様に適用される場合はそうではありません。

各ファイルにはコンテキストが1つだけ含まれ、わかりやすい名前を付ける必要があります。
(e.g. spec/support/shared_exampless/controllers/githubish_import_controller_shared_example.rb.)

Helpers

ヘルパーは通常、特定のRSpecのexampleの複雑さを隠すためのメソッドを提供するモジュールです。他のスペックと共有することを意図していない場合、RSpecファイルでヘルパーを定義できます。それ以外の場合は、spec/support/helpers/に配置する必要があります。

特定のタイプのスペック(機能スペック、リクエストスペックなど)のみに適用される場合、ヘルパーはサブフォルダーに配置できます。

ヘルパーはRailsの命名規則/名前空間規則に従う必要があります。たとえば、spec/support/helpers/cycle_analytics_helpers.rbは以下のように定義する必要があります。

module Spec
  module Support
    module Helpers
      module CycleAnalyticsHelpers
        def create_commit_referencing_issue(issue, branch_name: random_git_name)
          project.repository.add_branch(user, branch_name, 'master')
          create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name)
        end
      end
    end
  end
end

ヘルパーでRSpecの設定を変更しないでください。たとえば、上記のヘルパーモジュールには以下を含めないでください。

RSpec.configure do |config|
  config.include Spec::Support::Helpers::CycleAnalyticsHelpers
end

Factories

GitLabはテスト用のFixture[7]の代替としてfactory_botを使用します。

  • Factoryはspec/factories/で定義し、対応するモデルの複数形を使用して命名します(Userのfactoryはusers.rb)。
  • ファイルごとにトップレベルのファクトリ定義は1つだけにする必要があります。
  • FactoryBotメソッドは、すべてのRSpecグループに混在しています。つまりFactory.create(...)の代わりにcreate(...)を呼び出すことができます(そして呼び出す必要があります)。
  • traitを使用して定義と使用方法をクリーンアップします。
  • ファクトリを定義するとき、モデルに関係のないカラムを定義しないでください。
  • ファクトリをインスタンス化するときは、不要なカラムを指定しないでください。
  • ファクトリはActiveRecordオブジェクトに限定される必要はありません。を参照してください。

Fixtures

すべてのFixtureはspec/fixtures/の下に配置する必要があります。

Repositories

マージリクエストのマージなどの一部の機能をテストするには、特定の状態のGitリポジトリがテスト環境に存在する必要があります。GitLabは、特定の一般的なケースに対してgitlab-testリポジトリを維持します。プロジェクトファクトリの:repositoryトレイトでリポジトリのコピーが使用されていることを確認できます

let(:project) { create(:project, :repository) }

可能な場合は、:repositoryではなく:custom_repoトレイトの使用を検討してください。これにより、プロジェクトのリポジトリのmasterブランチに表示されるファイルを正確に指定できます。

let(:project) do
  create(
    :project, :custom_repo,
    files: {
      'README.md'       => 'Content here',
      'foo/bar/baz.txt' => 'More content here'
    }
  )
end

これにより、デフォルトの権限と指定されたコンテンツを持つ2つのファイルを含むリポジトリが作成されます。

Config

RSpecの設定ファイルは、RSpecのコンフィグ(すなわちRSpec.configure do |config|ブロック)を変更するファイルです。これはspec/support/に配置する必要があります。

各ファイルには特定のドメインに関連する必要があります。たとえば、spec/support/capybara.rb, spec/support/carriewave.rbなどです。

ヘルパーモジュールが特定の種類のスペックにのみ適用される場合、config.include呼び出しに修飾子を追加する必要があります。たとえば、spec/support/helpers/cycle_analytics_helpers.rb:libおよびtype: :modelスペックにのみ適用される場合、次のように記述します。

RSpec.configure do |config|
  config.include Spec::Support::Helpers::CycleAnalyticsHelpers, :lib
  config.include Spec::Support::Helpers::CycleAnalyticsHelpers, type: :model
end

構成ファイルがconfig.includeのみで構成されている場合、これらのconfig.includespec/spec_helper.rbに直接追加できます。

汎用的なヘルパーについては、spec/fast_spec_helper.rbファイルで使用されるspec/support/rspec.rbファイルに含めることを検討してください。spec/fast_spec_helper.rbファイルの詳細については、高速ユニットテストを参照してください。

注釈

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

トランザクションとは

IT用語トランザクションについて

複数の処理を1つにまとめたもの。
ただし、これら「複数の処理」は分離させることはできないこと。

具体例

  1. Aさんの口座から100円分差し引き、残高1,000 - 100 = 900円にする。
  2. Bさんの口座へ100円分プラスし、残高2,000 + 100 = 2100円にする。

1.の処理がうまくいって、2.の処理が何らかの原因で失敗したとする。そうなると、
Aさんの口座残高は5,000円になっているいる一方で、Bさんの口座残高は20,000円のままで、5000円が「消失」してしまうことになる。

まとめ

トランザクションは成功か失敗かの二択

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

お名前.comを使い、独自ドメイン取得し、herokuデプロイする

こんにちは。
初学者の方で転職活動などの為にポートフォリオを作成している方も多いと思います。

その際にherokuを使ってデプロイする人が多いと思うのですが、

URLが、
〜herokuapp.com

となってしまい、なんかカッコ悪いなあって思いまして、
AWSを使って色々やろうとしたのですが、

ポートフォリオだからそこまで口数をかけたくないと思い

独自ドメインを使い、herokuで、デプロイすることにしました。

その手順を載せさせていただきます。
初学者の方への参考になれたら幸いです。

※前提として
Herokuにアプリをデプロイ済みであることとします

1.お名前.comで独自ドメインを取得する

https://www.onamae.com/

にて
取得したいドメイン名を入れて検索する

スクリーンショット 2020-01-14 12.17.33.png

追加できるものを探し、

スクリーンショット 2020-01-14 12.19.16.png

個人情報、クレジット情報を入力する

ここでは
test.work
を取得したことにして進めます。

2.herokuの設定

herokuはhobbyプラン(無料の一つ上の7$のプラン)以上じゃないと独自ドメインが使えないためです。

ちなみにこちらは
7$/月の料金がかかってしまいます。

Herokuのプランはアカウントごとではなく、アプリケーションごとに設定します。例えば、2つのアプリケーションをHerokuにデプロイしている場合、それぞれのアプリケーションに対して無料・有料プランにするかどうかを設定することになります。

herokuをhobbyプランにする

https://dashboard.heroku.com/apps

より、
デプロイするアプリを押下、

詳細画面で、
Resources > [Change Dyno Type]を押下し、
[Hobby]を選択し[save]を押下します。

スクリーンショット 2020-01-14 20.37.33.png

スクリーンショット 2020-01-14 20.40.34.png

↓のように表示されたら、完了です
スクリーンショット 2020-01-14 20.41.32.png

heroku側のドメイン設定とSSL設定

[Settings]を押下し、[Add domain]を押下します。

スクリーンショット 2020-01-14 20.47.46.png

お名前.comで取得したドメインに
www.
を追加し入力し、[Next]を押下します。

スクリーンショット 2020-01-14 20.54.38.png

DNS target
が表示されるので、コピーします。

スクリーンショット 2020-01-14 20.56.08.png

次に[Configure SSL]を押下し、

Automatic Certificate Management (ACM)
を選択し、[Next]を押下します。
スクリーンショット 2020-01-14 20.58.41.png

確認画面になるので、もう一度[Next]を押下します。

最後に[Finish]を押下します。

3.お名前.comの設定

再びお名前.comにいき、
[ドメイン設定]を押下、
[DNS関連機能の設定]を押下、
[DNS設定/転送設定]を押下します。

ドメイン名を選択し[次へ]を押下します。
スクリーンショット 2020-01-14 21.07.32.png

DNSレコード設定を利用するの
[設定する]を押下します。
スクリーンショット 2020-01-14 21.08.40.png

TYPE→CNAME
ホスト名→wwwを入力
VALUEには、先程herokuでコピーしたDNS Targetを貼り付け
[追加]を押下します。

スクリーンショット 2020-01-14 21.14.06.png

画面下部まで進み確認へ進み、
[確認内容へ進む]を押下します。

内容を確認し、[設定する]を押下します。
スクリーンショット 2020-01-14 21.16.53.png

これで、設定は完了です!!

※UI上でうまくいかない場合は
コマンドでの設定をおすすめします
こちらを参考に!
https://medium.com/@kjmczk/heroku-cdomain-ssl-1b4cae424e61

反映まで数時間かかるようなので、
メールが来るまで待ちましょう!!

ドメインが反映される前に取得したドメインにアクセスするとお名前.comのページに飛ばされ『このドメインは取得されています』と表示される思いますが、心配せず気長に待ってください。

24〜72時間の間でドメインが反映されるらしいのですが、
自分の場合は数十分で
反映されました!

無事に公開されました!!

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

Rails エラーメッセージの表示と日本語化

エラーメッセージの表示

image.png

上記の画像のようにタイトルとブログ本文が空だった場合はエラー表示される様に実装する事。

モデルにバリデーションを設定

バリデーションを付けたいモデル(今回はpost.rb)に記載。

post.rb
class Post < ApplicationRecord
  validates :title, :content, presence: true
end

titleカラムとcontentカラムが空を防ぐ。

エラーメッセージのファイルの作成

エラーメッセージはエラーが発生すると、_error_messages.html.erb内に格納される。

layouts/_error_messages.html.erb
% if model.errors.any? %>
  <div class="alert alert-warning">
    <ul>
      <% model.errors.full_messages.each do |message| %>
        <li><%= message %></li> 
      <% end %>
    </ul>
  </div>
<% end %>

フォーム(form_with)の中にrenderで挿入

posts/new.html.erb
<%= form_with model: @post, class: :form, local: true do |form| %>
 #renderメソッドで_error_messages.html.erbを呼び出す。
  <%= render 'layouts/error_messages', model: form.object %> 
  <%= form.text_field :title, placeholder: :タイトル, class: :form__title %>
  <%= form.text_area :content, placeholder: :ブログ本文, class: :form__text %>
  <%= form.submit '投稿する', class: :form__btn %>
<% end %>

これで表示は完了。
次は日本語化実装へ。

エラーメッセージの日本語化

Gemfile
gem 'rails-i18n'

Gemfileに以下の一文を追加して、bundle install。

日本語化の基となるファイルを作成する

Railsの多言語化対応は、ymlファイルで管理。
config/locales ディレクトリ直下に、ja.ymlを作成。

ターミナル
$ touch config/locales/ja.yml

カラム名の日本語化

config/locales/models/ja.yml
ja:
  activerecord:
    attributes:
      post:
        title: 名前
        content: ブログ本文

ja.ymlの注意点

Railsはymlファイルの改行とインデントで日本語化のパスを参照している為、
attributes: => モデル名 => カラム名 の順に改行とインデントを入れる必要がある。

config/locales/models/ja.yml
attributes:         # attributes:の直下に
  post:            # モデル名を指定し
    title: 名前        # カラム名を指定する。
   content:ブログ本文    # カラム名を指定する。

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

次のunless文をif文に書き換えてください、という問題。

問題

1 if a + b > 0
2  puts "計算結果は0より大きいです"
3 end

模範回答

1 unless a + b <= 0
2  puts "計算結果は0より大きいです"
3 end

解説

 unless 条件式 then
  条件式が偽の時に実行する処理
 end
上記が私の参考にしたunless文の使い方です。

今回の場合でいうと、
 aとbの合計値が0以下でない時 = aとbの合計値が0より大きい時
           unless文 = if文
どちらも同じ条件の内容を示します。


※表現に誤りのある箇所があった為、訂正済みです。


間違った解釈の見える箇所がありましたら、コメントしていただきたいです。今後の参考にさせていただきたいと考えております。

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

次のif文をunless文に置き換えてください、という問題。

問題

1 if a + b > 0
2  puts "計算結果は0より大きいです"
3 end

模範回答

1 unless a + b <= 0
2  puts "計算結果は0より大きいです"
3 end

解説

 unless 条件式 then
  条件式が偽の時に実行する処理
 end
上記が私の参考にしたunless文の使い方です。

今回の場合でいうと、
 aとbの合計値が0以下でない時 = aとbの合計値が0以上である時
           unless文 = if文
どちらも同じ条件の内容を示します。




間違った解釈をしている箇所がありましたら、コメントしていただきたいです。今後の参考にさせていただきたいと考えております。

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

初心者によるプログラミング学習ログ 210日目

100日チャレンジの210日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

210日目は

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

【Ruby】Seleniumで露骨な人間アピール

はじめに

seleniumを使用してサイト調査をしておりまして、
昨今のスクレイピング禁や例の図書館事件も含めビビり倒しながらの調査の私です。
問題が発生したわけではないのですが、
露骨に人間アピールをしようと思って下記のようなメソッドを作ってみました。
多分みんなやっていると思いますが。

該当ソースコード

def rndSleep
rnd = rand(5) + 1
sleep rnd.to_i
end

rand(5) + 1で1~5のランダムな数字をrudに代入して、
動作を〇秒間停止するsleepメソッド、〇秒部分に当てています。

人間アピールしたい箇所にrndSleepを入れると1~5秒おいて実行してくれます。
私は繰り返し処理の、処理開始前に一息ついてもらう感じにしてます。

while true do
    index = index + 1
    rndSleep # <= ここで一息
    begin
        news = driver.find_element(:xpath, "/html/body/div[3]/div[1]/div[2]/div[2]/div/table/tbody/tr[#{index}]/td[2]/a")
        href = news.attribute('href')
        newsUrl << href
    rescue Selenium::WebDriver::Error::NoSuchElementError
        break
    end
end

おわりに

実際にBANされたりとか、ブロックされたりはないのですが、
されたら怖いのでちょっとでも抗いたい。
中身の調査をして抜け漏れ予防したいので、負荷かけたいわけではない。
つまり共存したいのです。
なので、プログラム実行側でうまいこと負荷を分散させて、
「人間ですよ~いいコンテンツですね~たくさん回遊しちゃうな~」
的なアピールをして乗り切る必要があるのではないかと感じました。

ちなみに、要素が表示されるまで待つことにsleepメソッドを使用するのは効率が悪いので、
下記の記事を参考にしていただいた方が良いかと思います。

参考記事

seleniumで要素を待つ時にsleepを使うのはオススメしない

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

超初心者がwebpackerについて調べてみたぞ

Ruby on Rails を使って開発を始めた今日この頃。
サーバーサイドはそれとなく出来てきたからそろそろフロントも固めて行こうかしら。なんて思っていた僕はBootstrapをRailsで使おうと考えた。Bootstrapを使うのはいたって簡単。htmlのhead内に以下のリンクをぶち込めばいいだけ

application.html.erb
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
    integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

なんて便利だこと。
しかし、Bootstrapを使うには他にも方法はある。それは以下のリンクにある通りだ。
https://qiita.com/rhistoba/items/f724dae231d7e28bf477
つまり、railsにBootstrapをインストールしてしまう方法である。
基本的には、リンクの記事通りに手順を踏めばインストールできるのであるが、超初心者の僕はいくつかのワカラナイ点があったので自分用のメモとして残すことにした。

前提

僕のスペック

  • プログラミング学習を始めて1ヶ月
  • HTML/CSS/Javascript/Ruby/Railsをprogateで一応学習済
  • Ruby on rails でポートフォリオを作成中

開発環境

  • 端末 : LENOVO ideapad 530S-14ARR
  • OS : Windows 10 Home ver.1809
  • シェル : PowerShell 5.1.17763.771
  • Ruby : 2.6.4
  • rails : 5.2.4.1

そもそもwebpackerってなんだ?

webpackerとはwebpackをRubyで使えるようにしたものらしい。
僕「webpackかぁ~…聞いたことあるよ。…でもよく知らない。」

webpackとは?

調べたところ、webpack というのは

Webpackとは、CSS、JavaScript、画像などを1つのファイルとしてまとめるためのモジュールバンドラーで、node.jsのモジュールの1つです。

とのこと。(引用元:https://www.sejuku.net/blog/68146)
僕「モジュールバンドラーってなんだよぉ(´;ω;`)」
僕「node.jsもよく分かんねーよぉ(´;ω;`)」
調べても分からないが続く。まさに分からないリレー状態。

モジュールバンドラーとは?

次にモジュールバンドラーについて知らべてみた。
モジュールバンドラーとはその名の通り、モジュール(部品)をまとめたものをいうらしい。ここでモジュールとはあるプログラムと考えるのが良さげ。

例えば、現在時刻を表示するプログラムを作るとしよう。ここで、現在時刻を取得するコードと、それを表示するコードと分けて作ったとする。これらのコードを合体させたいのだが、その方法として、表示するためのコードに現在時刻を取得するコードをインポートすることにしよう。この時、インポートされる側のコードをモジュールと呼ぶ

このような便利なコード集をモジュールバンドラーというみたい。
(参考:https://note.com/billion_dollars/n/n596fecfdeb2e)

node.jsとは?

node.jsとはサーバーサイドJavascriptである。
Javascriptは本来、ブラウザで動き見た目を作る(フロントで活躍する)言語なのだが、「この言語でサーバーサイドも作れたら嬉しくね?」という発想から作られたJavaScript 環境なんだとか。

僕「なるほどぉ。すごいじゃんnode.js」

(参考:https://eng-entrance.com/what-is-nodejs#Nodejs-2)

改めてwebpackって何ぞや?

ここまで調べたことをまとめて、もう一度考えるとwebpackとは、

CSS、JavaScript、画像などの部品を上手にまとめて1つにするnode.js(Javascript環境の1つ)のモジュールだ!

ということですね。要するにまとめ上手なお兄さんみたいな(雑)

とはいえ、なんでそんなにまとめたがるのか?

なんとなくwebpackについて分かったけど、まとめることにどんなメリットがあるのかしら?調べてみると通信との関係があるみたいだ。
僕たちがwebサイトを閲覧する時は、自分のPCからサーバーにリクエストを送り、そのレスポンスとしてあるwebページ(htmlファイルとか)を返してくれる。
しかし、そのhtmlが様々なコード(モジュール)や画像、css、javascriptなどを引用してきている時には、それらも同時に送ってやらないと不完全なwebページしか閲覧できない。だからそういう関係するファイルも一緒に送ってやるのだが、これらがバラバラだとサーバーがPCに送るのに時間が掛かってしまうみたい。
ここでそれらのファイルが1つにまとまっていることで素早く通信できるようだ。

まとめ

webpackerのことを調べて、本来の目的であるBootstrapのインストール忘れてた(/ω\)

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

ruby console をするとエラーが出ちゃう

僕「ruby console が使いたいなぁ」
僕「progate先生がターミナルにruby consoleって入力すれば使えるって言ってたな」
…入力
ターミナル「エラーですわ」
僕「まじか」←今ここ

前提

僕のスペック

  • プログラミング学習を始めて1ヶ月
  • HTML/CSS/Javascript/Ruby/Railsをprogateで一応学習済
  • Ruby on rails でポートフォリオを作成中

開発環境

  • 端末 : LENOVO ideapad 530S-14ARR
  • OS : Windows 10 Home ver.1809
  • シェル : PowerShell 5.1.17763.771
  • Ruby : 2.4.9
  • rails : 5.2.4.1

本題

Ruby on rails を使ってアプリを作っていた僕は、データーベースの内容が知りたくなって、ターミナルから以下のコマンドを打ち込んだ。

ruby console

その結果、以下のエラーが…

Loading development environment (Rails 5.2.4.1)
This version of IRB is drastically different from the previous version.
If you hit any issues, you can use "irb --legacy" to run the old version.
If you want to just erase this message, please use "irb --multiline".
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/reline-0.0.7/lib/reline/line_editor.rb:1020:in `calculate_width': undefined method `grapheme_clusters' for "":String (NoMethodError)
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/reline-0.0.7/lib/reline/line_editor.rb:349:in `rerender'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/reline-0.0.7/lib/reline.rb:210:in `inner_readline'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/reline-0.0.7/lib/reline.rb:160:in `readmultiline'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb/input-method.rb:259:in `gets'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:518:in `block (2 levels) in eval_input'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:694:in `signal_status'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:517:in `block in eval_input'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb/ruby-lex.rb:164:in `lex'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb/ruby-lex.rb:136:in `block (2 levels) in each_top_level_statement'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb/ruby-lex.rb:133:in `loop'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb/ruby-lex.rb:133:in `block in each_top_level_statement'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb/ruby-lex.rb:132:in `catch'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb/ruby-lex.rb:132:in `each_top_level_statement'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:536:in `eval_input'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:471:in `block in run'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:470:in `catch'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:470:in `run'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/irb-1.1.0/lib/irb.rb:399:in `start'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/railties-5.2.4.1/lib/rails/commands/console/console_command.rb:64:in `start'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/railties-5.2.4.1/lib/rails/commands/console/console_command.rb:19:in `start'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/railties-5.2.4.1/lib/rails/commands/console/console_command.rb:96:in `perform'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/thor-1.0.1/lib/thor/command.rb:27:in `run'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/thor-1.0.1/lib/thor.rb:392:in `dispatch'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/railties-5.2.4.1/lib/rails/command/base.rb:69:in `perform'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/railties-5.2.4.1/lib/rails/command.rb:46:in `invoke'
        from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/railties-5.2.4.1/lib/rails/commands.rb:18:in `<top (required)>'
        from bin/rails:4:in `require'
        from bin/rails:4:in `<main>'

なんてこった。ちょこっとコマンドを打ち込んだだけなのに、これほどの長文で怒られるとは。しくしく…

とりあえず適当に検索して直そうと試みる

なんでエラーが出ちゃうのか。検索してみると以下の記事が見つかった。

https://qiita.com/roki1801/items/7e4a371ae0b9ba928f55

この記事に従ってrb-readlineをインストールしてみた。その上で再びトライ。

僕「今度こそいけるやろ…」
…入力
ターミナル「またエラーですわw」
僕「ムキー!」

もう、打つ手が無くなった…。もう僕はrails consoleが使えないRubyエンジニアとして生きて行くしかないのか…と、絶望してふて寝しようとした時、エラー文に

Loading development environment (Rails 5.2.4.1)
This version of IRB is drastically different from the previous version.
If you hit any issues, you can use "irb --legacy" to run the old version.

と書いてあることに気付く。和訳すると
「お前が使ってるのはRails 5.2.4.1だわ。そんで、IRBのバージョンは以前のと全然違うから、必要であればIRBのバージョン落とせば?」
そう。問題はどうやらバージョンに起因するもののようだ。上記のリンクに示した解決法と今回の件は全然関係ないのである。ちゃんとエラー文を読まないせいで余計なことをしてしまった。

僕「…ところでIRBってなんやねん。」
そう思った僕は謎のIRBを調べてみた。するとIRBは”Interactive Ruby”の略で、対話的にプログラムを実行するためのシステムなんだとか。
僕「ほー。ってことはこいつはそもそもRuby consoleを動かす中身的な奴じゃね?」
僕「つまり、このIRBのバージョンとrailsのバージョンがマッチしていないのね」
僕「ほいじゃ、railsのバージョンを最新にしてみようかしら。」
そうして、RubyとRailsをアップデートしてみる。
(RubyとRailsのアップデートに関しては長くなるので別の記事で)

アップデートしRubyとRailsはそれぞれ以下のバージョンに。

  • Ruby 2.4.9 → 2.6.4
  • rails 5.2.4.1 → 5.2.4.1

ここで、railsのバージョンが変わっていないことに気づく。
あれ?railsのバージョンが古いからrails consoleが使えなかったのではないのか…?
まぁとりあえず、rails consoleを実行できるか確かめてみるか。
そうしてターミナルにrails consoleを入力すると…

Loading development environment (Rails 5.2.4.1)
irb(main):001:0>

どうやら上手いこと動いている。
この結果からみるとrubyのバージョンのせいでconsoleが使えなかったのか??

まとめ

理由はよく分かっていないが、上記のようなエラーが出た場合にはRubyのアップデートが有効そう。同じ境遇の人は是非一度試してみて欲しい。また、この問題を理解できている人がいおられれば是非ご教授を願いたい。
あと、当たり前だがエラー文はちゃんと読もう(/ω\)

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

Ruby|クラスメソッド内の super について

はじめに

本稿は super について書いています。普段はインスタンスメソッドで super を利用するシーンがあると思いますが、クラスメソッドだと混乱することがあったのでまとめになります。

super is 何?

super をメソッドで呼び出すと、親クラスにある同名のメソッドを呼び出します。メソッドがなければ例外を起こします。

class Parent
  def do(something)
    p something
  end
end

class Child < Parent
  def do
    super('something')
  end
end

c = Child.new

インスタンスメソッドだと、ancestors を使えばどのように探索をするかヒントになります。先の例だと Child クラスの継承関係に Parent クラスがあるので、そこからメソッドを探すことが出来ています。

もし、期待するメソッドが Parent クラスになければ更に親のクラスを探索します。さらに親の Object クラスを探索してなければ、Kernel クラスを探索します。最後は BasicObject クラスですね。

p Child.ancestors
#=> [Child, Parent, Object, Kernel, BasicObject]

ハマったこと

仕事で pundit の中を調査することがあり、次のような記述を見つけました。included はモジュールがインクルードされたときのコールバックで base クラスに対して更に拡張をする場合(クラスメソッドの追加など)によく使われます。

module PolicyExampleGroup
  include Pundit::RSpec::Matchers

  def self.included(base)
    base.metadata[:type] = :policy
    base.extend Pundit::RSpec::DSL
    super # これ
  end
end

さて、この super はどのクラスで定義している included メソッドでしょうか。はじめは ancestors で調べればわかると思っていたのですがモジュールは親クラスを持たないのです。。。それでも例外にならない、なぜ? :thinking:

p Pundit::RSpec::PolicyExampleGroup.ancestors
#=> [Pundit::RSpec::PolicyExampleGroup]

Ruby はクラスメソッドは特異クラスに定義されているので、クラスメソッドで super を使うと探索は特異クラスの継承関係で行われます。

:warning: 見つける対象になるメソッドはインスタンスメソッドであることに注意

p Pundit::RSpec::PolicyExampleGroup.singleton_class.ancestors
#=> [#<Class:Pundit::RSpec::PolicyExampleGroup>, Module, ..., Kernel, BasicObject]

今回は Module クラスにあるインスタンスメソッドにマッチしたのでそれが実行されています。オーバーライドしなければ nil を返すだけのメソッドなので削除しても良さそうですね :thinking:

ひとまず、拙い英語で pull request を作成しましたがどうなることやら。。。 :eyes:

https://github.com/varvet/pundit/pull/635

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

AWS Cloud9 へ mysql2 のgemを導入する時にエラーが出てなんとか解決した

Rubyの学習でデータベースを使おうと思って、

データベースといえばMySQLと思いつきでインストールしてしまい、

そのまま使えないじゃん!って思い、

mysql2というgem使えば使えるのか!と調べて気づき

mysql2がエラー出て入らねぇよ!!!と困り、

google大先生とQiitaの各先人様のおかげで導入できた

この流れの記録を書いておきます。

とりあえずスタートはmysql2の導入エラーから

まぁ困った

実際にはこんな感じ

ec2-user:/ $ sudo gem install mysql2
Building native extensions.  This could take a while...
ERROR:  Error installing mysql2:
        ERROR: Failed to build gem native extension.

    /usr/bin/ruby2.0 extconf.rb
mkmf.rb can't find header files for ruby at /usr/share/ruby/include/ruby.h


Gem files will remain installed in /usr/local/share/ruby/gems/2.0/gems/mysql2-0.5.3 for inspection.
Results logged to /usr/local/share/ruby/gems/2.0/gems/mysql2-0.5.3/ext/mysql2/gem_make.out

そもそもまずこれやってる時点の私はCUIが苦手で、
エラーに書いてあるディレクトリまで移動してもあまり意味わかっておらず、
usr/local/share/の中にrubyフォルダ無かったりと散々でしたw

結論は

https://qiita.com/higeo_kk/items/828d9f11cdc69c85c078
この方の記事通りでさくっと解決してしまった・・・
ruby-develがなかったのかな?
develってことはディベロッパーツール?
今は腹落ちしてないけどこの辺は環境構築で絶対にハマるところだと
私の未熟な勘でも感じているので、また後日解明したい。

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

処理の前後で作成されたObject数を数える

GC.start
allocated_before = GC.stat(:total_allocated_objects) #=> ex: 108391
# ..何らかの処理..
allocated_after = GC.stat(:total_allocated_objects) #=> ex: 108871

puts "Object count: #{allocated_after - allocated_before}"

Ref. https://gist.github.com/apauly/3f547f06e83b66e4187e48519b426464

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

#Rspec + #Ruby / overwrite class method argument or method return value

https://relishapp.com/rspec/rspec-mocks/v/3-9/docs/configuring-responses/wrapping-the-original-implementation

# https://relishapp.com/rspec/rspec-mocks/v/3-9/docs/configuring-responses/wrapping-the-original-implementation


class You
  def self.say(message: )
    {
      message: message
    }
  end
end

RSpec.describe do
  it do
    expect(You.say(message: 'YES')).to eq({message: 'YES'})
  end

  it 'adds hash key value to method return hash' do
    allow(You).to receive(:say).and_wrap_original { |method, *args|
      method.call(*args).merge(hello: 'EVERYONE')
    }

    expect(You.say(message: 'YES')).to eq({message: 'YES', hello: 'EVERYONE'})
  end

  it 'overwrites method receives argument' do
    allow(You).to receive(:say).and_wrap_original { |method, *args|
      method.call(message: 'OH')
    }

    expect(You.say(message: 'YES')).to eq({message: 'OH'})
  end
end





class API
  def self.solve_for(x)
    (1..x).to_a
  end
end

RSpec.describe "and_wrap_original" do
  it "can be overridden for specific arguments using #with" do
    allow(API).to receive(:solve_for).and_wrap_original { |m, *args| m.call(*args).first(5) }
    allow(API).to receive(:solve_for).with(2).and_return([3])

    expect(API.solve_for(20)).to eq [1,2,3,4,5]
    expect(API.solve_for(2)).to eq [3]
  end
end


# $ rspec /Users/yumainaura/.ghq/github.com/YumaInaura/YumaInaura/rspec/wrapping.rb

# and_wrap_original
#   can be overridden for specific arguments using #with


#   is expected to eq {:message=>"YES"}
#   adds hash key value to method return hash
#   overwrites method receives argument

# Finished in 0.01419 seconds (files took 0.1395 seconds to load)
# 4 examples, 0 failures

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2944

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

【Ruby on Rails】デフォルトメッセージの日本語化

エラーメッセージなどのデフォルトメッセージを日本語化する設定です。

バージョン情報

Ruby 2.6.3
Ruby on Rails 5.2.3

設定方法

config/initializers/配下にlocale.rbというファイルを作成します。ファイル名は何でも良いですが習慣上locale.rbにしてあります。

下記の2文を記載します。

config/initializers/locale.rb
I18n.config.available_locales = :ja
I18n.default_locale = :ja

日本語の辞書ファイルを作成します。
config/locales/ja.ymlというファイルを作成し、下記のURLの本文を作成したファイルにすべてコピペします。

https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml

モデル名などはまだ英語のままなので、個別で日本語化していきます。
config/locales/model.ja.ymlというファイルを作成します。

config/locales/model.ja.yml
ja:
  activerecord:
    models:
      post: 投稿
      user: ユーザー
    attributes:
      post:
        content: 内容
        image: 画像
      user:
        name: 名前
        email: メールアドレス
        current_password: 現在のパスワード
        password: パスワード
        password_confirmation: 確認用パスワード

以上です。

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

インクリメント・デクリメントの書き方のまとめ(Scala、Java、Rust、C言語、C++、Go言語、PHP、Perl、Python、Ruby、JavaScript)

いろんな言語を触っていると、言語の細かい仕様がだんだんごっちゃになってきてしまいますので、メモです。

インクリメント・デクリメントの有無

あり: Java、C言語、C++、Go言語△、PHP、Perl、JavaScript
なし: Scala、Rust、Python、Ruby

Go言語は式を構成する演算子ではなく文(statement)という扱いにすることで、インクリメントの演算子としての問題を回避していて、個人的にはちょうどいい仕様に感じます。

ついでに代入演算子も確認しましたが、こちらはだいたいの言語にあるようです。

Scala

  • インクリメント・デクリメント演算子はない
  • 代入演算子はある
i += 1
i -= 1

i += 1 などは i = i + 1 などのシンタックスシュガー。

参考

Assignment Operators - Expressions | Scala 2.13

Scalaでは、なぜインクリメントやデクリメントができないのか?

Java

  • インクリメント・デクリメント演算子は前置・後置ともにある
  • 式であり値を返す
  • 代入演算子もある
++i;
--i;
i++;
i--;
i += 1;
i -= 1;

参考

Prefix Increment Operator ++ - Java Language Specification

Rust

  • インクリメント・デクリメント演算子はない
  • 代入演算子はある
i += 1;
i -= 1;

参考

Compound assignment expressions - Operator expressions - The Rust Reference

なぜインクリメント演算子がないのか?
Why doesn't Rust have increment and decrement operators?

C言語、C++

  • インクリメント・デクリメント演算子は前置・後置ともにある
  • 式であり値を返す
  • 代入演算子もある
++i;
--i;
i++;
i--;
i += 1;
i -= 1;

Go言語

  • C言語でいうインクリメント・デクリメント演算子は後置のみ
  • 式ではなく文の扱いなので、式の中には埋め込めない
  • 代入演算子もある
i++
i--
i += 1
i -= 1

参考

IncDec statements - The Go Programming Language Specification

++--が演算子ではない件
演算子とステートメント — プログラミング言語 Go | text.Baldanders.info

PHP

  • インクリメント・デクリメント演算子は前置・後置ともにある
  • 式であり値を返す
  • 代入演算子もある
++$i;
--$i;
$i++;
$i--;
$i += 1;
$i -= 1;

参考

加算子/減算子 | PHP Manual

Perl

  • インクリメント・デクリメント演算子は前置・後置ともにある
  • 式であり値を返す
  • 代入演算子もある
++$i;
--$i;
$i++;
$i--;
$i += 1;
$i -= 1;

参考

インクリメントとデクリメント - perlop - Perl の演算子と優先順位 - perldoc.jp

Python

  • インクリメント・デクリメント演算子はない
  • 代入演算子はある
i += 1
i -= 1

累算代入文というらしい。

参考

累算代入文 (augmented assignment statement) - 単純文 (simple statement) — Python 3.8.0 ドキュメント

Ruby

  • インクリメント・デクリメント演算子はない
  • 代入演算子はある
i += 1
i -= 1

自己代入というらしい。

参考

演算子式 (Ruby 2.6.0)

Ruby にインクリメント演算子のようなものが無い理由 - fugafuga.write

Rubyのインクリメント速度のバージョンごとの比較 - Qiita

JavaScript

  • インクリメント・デクリメント演算子は前置・後置ともにある
  • 式であり値を返す
  • 代入演算子もある
++i;
--i;
i++;
i--;
i += 1;
i -= 1;

参考

Update Expressions - ECMAScript® 2019 Language Specification

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

ActionView::MissingTemplate in Groups#newの一例

1.どんなエラー?

renderメソッドで表示しようとしている、viewファイルがありませんという内容です

筆者の場合は同じgroupsフォルダ内のformというファイルで記載したものを引用しnewファイルで表示しようとしていました。(下記参照)

<エラー文>

スクリーンショット 2020-01-14 2.23.39.png

<エラーに関係したgroupsフォルダ内のファイル>
new.html.haml
.chat-group-form
  %h1 新規チャットグループ
  = render partial: 'form', locals: { group: @group }
form.html.haml
= form_for group do |f|
  .chat-group-form__errors
    %h2 10件のエラーが発生しました
    %ul
      %li nameを入力してください
  .chat-group-form__field
    .chat-group-form__field--left
      = f.label :name, class: 'chat-group-form__label'
    .chat-group-form__field--right
      = f.text_field :name, class: 'chat__group_name chat-group-form__input', placeholder: 'グループ名を入力してください'
  .chat-group-form__field.clearfix
    / この部分はインクリメンタルサーチ(ユーザー追加の非同期化のときに使用します
  .chat-group-form__field.clearfix
    .chat-group-form__field--left
      %label.chat-group-form__label{:for => "chat_group_チャットメンバー"} チャットメンバー
    .chat-group-form__field--right
      / グループ作成機能の追加時はここにcollection_check_boxesの記述を入れてください
      = f.collection_check_boxes :user_ids, User.all, :id, :name
      / この部分はインクリメンタルサーチ(ユーザー追加の非同期化のときに使用します
  .chat-group-form__field.clearfix
    .chat-group-form__field--left
    .chat-group-form__field--right
      = f.submit class: 'chat-group-form__action-btn'

2.原因

 構文を確認してみるとどこにも間違いがないため、悩んでいたところ、ありました、partial: 'form'という一文が。
 partialというのはフォルダ内の"部品名となっているファイル"を引用しますよという意味があり、通常のファイルに対してpartialという縛りを増やすと読み込みを行いません。
 つまるところpartialで参照したファイルのファイル名は"部品"であることを意味する'_(アンダーバー)'から始めなければならないのです。

3.解決方法

結論としてはpartialを使って呼び出すファイルは"_(アンダーバー)"から始まるファイル名とすればエラー要因の一つが取り除かれるということになります。

ちなみに筆者の場合の例を確認してみましょう。上記に添付した筆者のファイル名を見てみるとformのファイル名が部品の形となっていませんね。なのでform.html.haml→_form.html.hamlとすることによりエラー文は解消されます。

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