20210228のRubyに関する記事は30件です。

Association ; 1対1、1対多、多対多について

Association

Rails には関連 (Association) という仕組みがある。関連付けを用いて二つのモデルをつなぐことで、コードをシンプル、かつ簡単に書くことができる。
関連付けの関係は大きく分けて以下の三種類に分類される。

  • 1 対 1
  • 1 対 多
  • 多 対 多

1 対 1 (has_one, belongs_to)

例えば、Teacher, Class, Student, Subject のモデルで以下の図のような関係性があったとします。
image.png

まずはTeacherとClassの関係性を見てみます。
image.png

ここで表されているのは、

  • 原田先生 は 2-1 を
  • 山川先生 は 2-2 を

担任しているということがわかります。
この場合、Teacherモデルのデータひとつに対し、Classモデルのデータがひとつだけ割り当てられているため、1対1の関係性となっているということになります。
この関係性をコードで表すと以下のようになります。

class Teacher < ApplicationRecord
    has_one :Class
end

class Class < ApplicationRecord
    belongs_to :Teacher
end

1 対 多 (has_many, belongs_to)

次に ClassとStudentの関係性を見てみます。
image.png

ここで表されているのは

  • 2-1 には 田中さんと矢島さん
  • 2-2 には 中村さんと木下さん

が属しているということがわかります。
この場合、Classモデルのデータひとつに対し、Studentモデルのデータが複数割り当てられているため、1対多の関係性になっているということになります。
この関係性をコードで表すと以下のようになります。

class Class < ApplicationRecord
    has_many :Students
end

class Student < ApplicationRecord
    belongs_to :Class
end

多 対 多 (has_and_belongs_to_many)

最後に、StudentとSubjectの関係性を見てみます。
image.png

線が多いため、Studentモデルのデータごとに色分けしております。
ここで表されているのは

  • 田中さんは 国語、英語、数学 を受講する
  • 矢島さんは 国語、英語、数学 を受講する
  • 中村さんは 国語、英語、数学 を受講する
  • 木下さんは 国語、英語、数学 を受講する

ということがわかります。
ここで、線の色分けをSubjectモデルのデータごとに変えてみます。
image.png

この場合、表されているのは

  • 国語を受講するのは 田中さん、矢島さん、中村さん、木下さん
  • 英語を受講するのは 田中さん、矢島さん、中村さん、木下さん
  • 数学を受講するのは 田中さん、矢島さん、中村さん、木下さん

ということになります。
つまり、Studentモデルのデータひとつに対し、Subjectモデルのデータが複数割り当てられており、またSubjectモデルのデータひとつに対し、Studentモデルのデータが複数割り当てられています。そのため、この関係性は多対多ということになります。
この関係性をコードで表すと以下のようになります。

class Student < ApplicationRecord
    has_and_belongs_to_many :Subjects
end

class Subject < ApplicationRecord
    has_and_belongs_to_many :Students
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】 Deviseを複数導入する場合

はじめに

deviseを導入すると、簡単に認証機能が実装できて本当に便利なgemですよね!
Ruby on Railsを学び始めて2ヶ月目の頃、ユーザーと管理者に分けてdevise機能を2つ実装したい、こういった時はどうしたら良いのか、また複数のdevise間で両者とも使用するモデルに関してはどうしたら良いのか、と少し悩んだことがありましたので、今回の記事のテーマに選定致しました。

前提

今回説明するモデルを以下の三点に絞らせて頂いております。
● devise User
● devise Admin
● 両者に共通してItemモデルを使用


開発環境

ruby 2.6.3
Rails 5.2.4.4

gemを導入

Gemfile.
gem 'devise'
terminal.
$ bundle install


ユーザー用、管理者用のdevise機能実装

deviseを複数実装することは意外と簡単です!
User、Adminのように適宜名前を付け、各コマンドを実行すればモデル、ビュー、コントローラーは作成出来ます。

terminal.
$ rails g devise:install

# ユーザー用のdevise作成
$ rails g devise User name:string 
$ rails db:migrate
$ rails g devise:views users
$ rails g devise:controllers users

# 管理者用のdevise作成
$ rails g devise Admin
$ rails db:migrate
$ rails g devise:views admins
$ rails g devise:controllers admins


model / controller

ここで少し頭を悩ませることになります。
ECサイトを例にあげると、Item(商品)モデルを必ず作成するかと思います。このItemモデルの働きをユーザーと管理者それぞれの立場で考えてみましょう。

● ユーザー
 (例)商品を閲覧する、商品の詳細画面へ飛ぶ、注文画面へ遷移する

● 管理者
 (例)商品を登録する

つまり、Itemモデルひとつをとっても、ユーザーと管理者で見る画面も違ければ、コントローラーの記述も異なるわけです。では、ビューとコントローラーをそれぞれで作成するにはどうしたら良いのかをご説明致します。答えは、ビューとコントローラーで「user」、「admin」という親ディレクトリを用意して、その中に各モデルのビューやコントローラーを作成するという形になります。

※ 「users」、「admins」というディレクト名だと先ほど作成したdivise用のものと被ってしまい作成できません。そのため単数形の名称にしてあります。どうしても違和感があり気に入らないという場合でしたら、userの代わりに「customers」、「members」などにしても良いかと思います。

terminal.
$ rails g model Item

$ rails g controller user::items
$ rails g controller admin::items

(補足)
● モデルは共通で使用できるので、1つのみの作成で大丈夫です。

rails g controller (親ディレクトリ名)::(その中に作成するディレクトリ名)
  上記のように記述することで、自動的に親ディレクトを作成し、その中にコントローラーを作成してくれます。

user/items_controller.rb
class User::ItemsController < ApplicationController

end

コントローラーのディレクトリを確認すると、userディレクトリ内にitems_controller.rb、adminディレクトリ内にitems_controller.rbとひとつのモデルに対し、2つのコントローラーを作成することが出来ました。
ちなみに、コントローラーを作成するときのコマンドでコロンを2つ(::)入力しましたが、これらはコントローラーのファイルのClass名の部分にもきちんと反映されています。どのディレクトリに準ずるコントローラーか、ということが明確になっております。

View

先ほどコントローラーを作成した際に自動的にuserディレクトリadminディレクトリが作成されましたが、それに伴い、viewの方にも空のuser/itemsディレクトリadmin/itemsディレクトリが作成されています。
それぞれのディレクトリの上で右クリックを押し、必要なファイルを作成してください。

ルーティング

次に悩んだポイントがルーティングの作成です。
共通で使用しているモデルのルーティングに関しても、別々に作成する必要があります。
ルーティング作成時のポイントを3つ簡単に押さえておきます。

scope

URL: 変える   (例) user/items, user/item/:id
ファイル構成: 変えない

namespace

URL: 変える   (例) user/items, user/item/:id
ファイル構成: 指定のパスに変える

module

URL: 変えない  (例) /items, /item/:id
ファイル構成: 指定のパスに変える

config/routes.rb
Rails.application.routes.draw do
  root 'homes#top'

  devise_for :admins
  namespace :admin do
    resources :items
  end

  devise_for :users
  namespace: :user do
    resources :items
  end
end

(補足)
今回は、自分自身が分かりやすようにディレクトリを分け、またURLにもuserとadminと表示させ分かりやすくしたかった為、namespaceを採用しました。
ご自身の用途に合わせて選択していただければ良いかと思います。
scopenamespacemoduleについて分かりやすくまとめてくださっているサイトがあり、参考にさせていただきましたので、下記にURLを掲載しておきます。

(参考)
・Railsのroutingにおけるscope / namespace / module の違い
https://qiita.com/ryosuketter/items/9240d8c2561b5989f049

終わり

今回は以上になります。
ざっくりとした説明で分かりにくい点もあるかと思いますが、申し訳ありません。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になればと思っています。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。

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

【Ruby on Rails】before_actionを使って全てのアクションで同じ処理を行う

同じ処理をbefore_actionで定義する。

あるコントローラー内の全てのアクションで1つの処理を使い回したい時。

class Api::V1::Admin::CompanyAdminsController < ApplicationController

  def index
    company_admins = CompanyAdmin.where(company_id: params[:company_id])
    json_response(200, CompanyAdminIndexSerializer.new(company_admins).to_h)
  end

  def show 
    company_admin = CompanyAdmin.find params[:id]
    json_response(:ok, serialize_data(CompanyAdminIndexSerializer, company_admin))
  end

  def destroy
    company_admin = CompanyAdmin.find params[:id]
    company_admin.destroy!
    json_response(204)
  end

end

このコントローラー内を見てみると、show・destroyアクションで同じ処理が書かれている。

company_admin = CompanyAdmin.find params[:id]

これをまとめるために、before_actionと記述する。
これによって、全てのアクションで最初に行いたい処理を実行できる。

  def load_company_admin
    @company_admin = CompanyAdmin.find params[:id]
  end
end

例えば、上のようなメソッドをコントローラー内に記述する。
そして、コントローラー内の一番上にbefore_actionの記述を加える。

最終的なコード

class Api::V1::Admin::CompanyAdminsController < ApplicationController
  before_action :load_company_admin, only: [:show, :destroy] 
  ##onlyオプションを付け加えることで、showとdestroyアクションのときと指定ができる。

  def index
    company_admins = CompanyAdmin.where(company_id: params[:company_id])
    json_response(200, CompanyAdminIndexSerializer.new(company_admins).to_h)
  end

  def show 
    json_response(:ok, serialize_data(CompanyAdminIndexSerializer, @company_admin))
  end

  def destroy
    @company_admin.destroy!
    json_response(204)
  end

  private
  def load_company_admin ##処理の名前を定義する。
    @company_admin = CompanyAdmin.find params[:id]
  end
  ##ここで同じ処理を記述する。
end

少しだけ、このファイルの記述がスッキリした気がする。
何度も同じ記述を書くのはエンジニアとして、あまり優秀ではない気がしているので、できるだけ同じ処理はまとめていきましょう。

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

【Railsチュートリアル】第10章 ユーザーの更新・表示・削除②

10.3 すべてのユーザーを表示する

indexアクションを追加して、すべてのユーザーを一覧表示する。データベースにサンプルデータを追加する方法や、将来ユーザー数が膨大になってもindexページを問題なく表示できるようにするためのユーザー出力のページネーション(pagination=ページ分割)の方法を学ぶ。

10.3.1 ユーザーの一覧ページ

ユーザーのindexページはログインしたユーザーにしか見せないようにし、未登録のユーザーがデフォルトで表示できるページを制限する。

indexアクションが正しくリダイレクトするか検証するテストを書く。

test/controllers/users_controller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

  def setup
    @user       = users(:michael)
    @other_user = users(:archer)
  end

  test "should get new" do
    get signup_path
    assert_response :success
  end

  test "should redirect index when not logged in" do
    # ログインしていない場合はインデックスをリダイレクトするテストを行う
    get users_path
    assert_redirected_to login_url
  end
  .
  .
  .

beforeフィルターのlogged_in_userにindexアクションを追加して、このアクションを保護。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update]
    # 「indexアクションが呼び出されたらlogged_in_userアクションを実行する」を追加
  before_action :correct_user,   only: [:edit, :update]

  def index
  end

  def show
    @user = User.find(params[:id])
  end
  .
  .
  .
end

indexビューの実装。User.allを使ってデータベース上の全ユーザーを取得し、ビューで使えるインスタンス変数@usersに代入する。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update]
  .
  .
  .
  def index
    @users = User.all
  end
  .
  .
  .
end
app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>

<ul class="users">
  <% @users.each do |user| %>
    <li>
      <%= gravatar_for user, size: 50 %>
      <%= link_to user.name, user %>
    </li>
  <% end %>
</ul>

users_helper.rb, custom.scss, _header.html.erbにコードを追加してrails.test

演習 1

レイアウトにあるすべてのリンクに対して統合テストを書いてみましょう。ログイン済みユーザーとそうでないユーザーのそれぞれに対して、正しい振る舞いを考えてください。ヒント: log_in_asヘルパーを使ってリスト 5.32にテストを追加してみましょう。

test/integration/site_layout_test.rb
require 'test_helper'

class SiteLayoutTest < ActionDispatch::IntegrationTest

  test "layout links" do
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
    assert_select "a[href=?]", login_path
    get contact_path
    assert_select "title", full_title("Contact")
    get signup_path
    assert_select "title", full_title("Sign up")
  end

  def setup
    @user = users(:michael)
  end

  test "layout links when loged in" do
    log_in_as(@user)
      # => テストユーザーでログイン
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", users_path
      # => /users ユーザー一覧ページ
    assert_select "a[href=?]", user_path(@user)
      # => /users/:id Profileページ
    assert_select "a[href=?]", edit_user_path(@user)
      # => /users/1/:id Settingsページ
    assert_select "a[href=?]", logout_path
  end
end

10.3.2 サンプルのユーザー

indexページに複数のユーザーを表示させる。

演習 1

試しに他人の編集ページにアクセスしてみて、10.2.2で実装したようにリダイレクトされるかどうかを確かめてみましょう。
確認のみなので省略。

10.3.3 ページネーション

1つのページに一度に30人だけユーザーを表示するというようなページネーション(pagination)を実装する。

演習 1

Railsコンソールを開き、pageオプションにnilをセットして実行すると、1ページ目のユーザーが取得できることを確認してみましょう。

>> User.paginate(page: nil)
   (0.1ms)  begin transaction
  User Load (2.9ms)  SELECT "users".* FROM "users" LIMIT ? OFFSET ?  [["LIMIT", 11], ["OFFSET", 0]]
   (1.7ms)  SELECT COUNT(*) FROM "users"
=> #<ActiveRecord::Relation [#<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2021-02-26 14:35:28", updated_at: "2021-02-26 14:35:28", password_digest: [FILTERED], remember_digest: nil>, #<User id: 2, name: "William Bartell", email: "example-1@railstutorial.org", created_at: "2021-02-26 14:35:30", updated_at: "2021-02-26 14:35:30", password_digest: [FILTERED], remember_digest: nil>, #<User id: 3, name: "Carter Feest", email: "example-2@railstutorial.org", created_at: "2021-02-26 14:35:30", updated_at: "2021-02-26 14:35:30", password_digest: [FILTERED], remember_digest: nil>, #<User id: 4, name: "Arlinda Douglas Sr.", email: "example-3@railstutorial.org", created_at: "2021-02-26 14:35:30", updated_at: "2021-02-26 14:35:30", password_digest: [FILTERED], remember_digest: nil>, #<User id: 5, name: "Dr. Monika Goyette", email: "example-4@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 6, name: "Mr. Hilda Prohaska", email: "example-5@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 7, name: "Theo Russel", email: "example-6@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 8, name: "Robbie Littel", email: "example-7@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 9, name: "Azzie Walter", email: "example-8@railstutorial.org", created_at: "2021-02-26 14:35:32", updated_at: "2021-02-26 14:35:32", password_digest: [FILTERED], remember_digest: nil>, #<User id: 10, name: "Shanell Nicolas", email: "example-9@railstutorial.org", created_at: "2021-02-26 14:35:32", updated_at: "2021-02-26 14:35:32", password_digest: [FILTERED], remember_digest: nil>, ...]>

演習 2

先ほどの演習課題で取得したpaginationオブジェクトは、何クラスでしょうか? また、User.allのクラスとどこが違うでしょうか? 比較してみてください。

class: User::ActiveRecord_Relation
User.allと同じ。

10.3.4 ユーザー一覧のテスト

ページネーションに対する簡単なテストも書いておく。
ログイン→indexページにアクセス→最初のページにユーザーがいることを確認→ページネーションのリンクがあることを確認の手順でテストしていく。

演習 1

試しにリスト 10.45にあるページネーションのリンク(will_paginateの部分)を2つともコメントアウトしてみて、リスト 10.48のテストが red に変わるかどうか確かめてみましょう。

 FAIL["test_index_including_pagination", #<Minitest::Reporters::Suite:0x000056137403b7a8 @name="UsersIndexTest">, 14.158692726000027]
 test_index_including_pagination#UsersIndexTest (14.16s)
        Expected at least 1 element matching "div.pagination", found 0..
        Expected 0 to be >= 1.
        test/integration/users_index_test.rb:13:in `block in <class:UsersIndexTest>'

red

演習 2

先ほどは2つともコメントアウトしましたが、1つだけコメントアウトした場合、テストが green のままであることを確認してみましょう。will_paginateのリンクが2つとも存在していることをテストしたい場合は、どのようなテストを追加すれば良いでしょうか? ヒント: 表 5.2を参考にして、数をカウントするテストを追加してみましょう。

test/integration/users_index_test.rb
require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "index including pagination" do
    log_in_as(@user)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination', count: 2 # 「count: 2」を追記
    User.paginate(page: 1).each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
    end
  end
end
 FAIL["test_index_including_pagination", #<Minitest::Reporters::Suite:0x0000561374244360 @name="UsersIndexTest">, 16.980905499999608]
 test_index_including_pagination#UsersIndexTest (16.98s)
        Expected exactly 2 elements matching "div.pagination", found 1..
        Expected: 2
          Actual: 1
        test/integration/users_index_test.rb:13:in `block in <class:UsersIndexTest>'

10.3.5 パーシャルのリファクタリング

一覧ページのリファクタリングを行う。

演習 1

リスト 10.52にあるrenderの行をコメントアウトし、テストの結果が red に変わることを確認してみましょう。

REDになりました。

10.4 ユーザーを削除する

削除を実行できる権限を持つ管理(admin)ユーザーのクラスを作成する。
次にユーザーを削除するためのリンクを追加し、削除を行うのに必要なdestroyアクションも実装する。

10.4.1 管理ユーザー

Boolean型 とは?
真理値の「真 = true」と「偽 = false」という2値をとるデータ型のこと。

toggle!メソッド とは?
toggle!(:flag)と書き、属性(:flag)を反転し保存するメソッド。saveに成功したらtrueを返す。

演習 1

Web経由でadmin属性を変更できないことを確認してみましょう。具体的には、リスト 10.56に示したように、PATCHを直接ユーザーのURL(/users/:id)に送信するテストを作成してみてください。テストが正しい振る舞いをしているかどうか確信を得るために、まずはadminをuser_paramsメソッド内の許可されたパラメータ一覧に追加するところから始めてみましょう。最初のテストの結果は red になるはずです。最後の行では、更新済みのユーザー情報をデータベースから読み込めることを確認します( 6.1.5)。

test/controllers/users_controller_test.rb
  test "should not allow the admin attribute to be edited via the web" do
    log_in_as(@other_user)
      # テストユーザー(@other_user)でログイン
    assert_not @other_user.admin?
      # @other_userは管理者か?=> 管理者ではない => false
      # @other_user.admin?がfalse => 成功
    patch user_path(@other_user), params: { user: { password: "password", password_confirmation: "password", admin: true } }
      # @other_userに管理者権限を持たせる => admin: true
    assert_not @other_user.reload.admin?
      # 更新した@other_userは管理者か? => 管理者ではない => false
      # @other_user.reload.admin?がfalse => 成功
  end

assert_not とは?
assert_not hogeの場合 => hoge が trueなら失敗、falseなら成功。

10.4.2 destroyアクション

destroyアクションへのリンクを追加する。
ユーザーindexページの各ユーザーに削除用のリンクを追加し、続いて管理ユーザーへのアクセスを制限する。

演習 1

管理者ユーザーとしてログインし、試しにサンプルユーザを2〜3人削除してみましょう。ユーザーを削除すると、Railsサーバーのログにはどのような情報が表示されるでしょうか?

Started DELETE "/users/4" for 10.0.2.2 at 2021-02-28 19:28:49 +0900
Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.0/127.255.255.255, ::1
Processing by UsersController#destroy as HTML
  Parameters: {"authenticity_token"=>"ZUaUvmxMfqGHERKzsE1flF1BHJkxUkLesSKKx047CAkzYAOz6jB3wQrDRQ2xHloAba4P/r9CRI1XAATfArRZTw==", "id"=>"4"}
  User Load (0.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   app/helpers/sessions_helper.rb:18:in `current_user'
  User Load (1.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 4], ["LIMIT", 1]]
  ↳ app/controllers/users_controller.rb:52:in `destroy'
   (0.1ms)  begin transaction
  ↳ app/controllers/users_controller.rb:52:in `destroy'
  User Destroy (7.1ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 4]]
   app/controllers/users_controller.rb:52:in `destroy'
   (13.6ms)  commit transaction
  ↳ app/controllers/users_controller.rb:52:in `destroy'
Redirected to http://localhost:3000/users
Completed 302 Found in 46ms (ActiveRecord: 22.7ms | Allocations: 4317)

10.4.3 ユーザー削除のテスト

演習 1

試しにリスト 10.59にある管理者ユーザーのbeforeフィルターをコメントアウトしてみて、テストの結果が red に変わることを確認してみましょう。

確認のみなので省略。

さいごに

Webアプリケーションとしての基礎を整えられたようです。
Railsチュートリアルをはじめたばかりの頃はエラーが出ただけで焦っていました。
ですが今では冷静にエラーを読む、わからなければ検索するというのが自然とできるようになりました。
コードもだんだんと読み下せるようになってきました。一歩ずつ前に進んでいる気がします。次章もがんばります!

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

一覧表示での進捗率ゲージの作成

はじめに

オリジナルアプリでクラウドファンディングサイトを作成した際に、
jQueryを使って進捗率ゲージを作成したので紹介します。
前回、詳細画面での作成方法を紹介しましたが
今回は一覧表示画面での作成方法です。

Image from Gyazo

詳細画面での作成はこちら→
https://qiita.com/takiguchiharuna1221/items/df517f2a9e4b74299328#comment-bd0d8efa2c7c3dc7651e
※こちらではlinear-gradientを使った別の方法も投稿していただいています。

初学者なのでより良い方法が他にもあるかもしれませんが、どなたかの参考になれば嬉しいです。

環境

・Rails 6.0.0
・Ruby 2.6.5
・jQuery

作成手順

【view】

index.html.haml
.Contents__projects
  - @projects.each do |project| 
  # 一部省略
    .Projects 
      = link_to display_project_path(project.id) , class: "Box3"do
        .Box1
          = image_tag project.image.url.to_s, class: "Box1__image"
        .Box2
          %p.Box2__title= project.title
          %p.Box2__percent__contents 進捗率
          - @percent = number_to_percentage(@total.to_f/project.target_amount*100,precision: 1)
          = @percent
          .contents-pacent__box2__graph1
            .contents-pacent__box2__graph2
              %input{name: "percent", type: "hidden", value:@percent, class: 'percent' }

①ゲージを挿入したい部分にクラスを設定します。
 →.contents-pacent_box2_graph2の部分

②inptを用いてDBのインスタンス変数の値を取得します。
 →inputのvalueに変数を設定することでjQueryで変数を使えるようにします。
  ※今回は変数の詳細は割愛します。
 参考記事:https://qiita.com/Kohei_Kishimoto0214/items/d919b00d75dec0699cf0

【CSS】

_project.scss
.contents-pacent__box2__graph2{
  background-color: #ea662d;
  height: 15px;
  border-radius: 6px;
  max-width: 100%;
}

③CSSは以上の通りです。
 進捗率が100%以上になっても突き抜けてしまわないように
 max-width: 100%;を設定しています。

【jQuery】

project.js
$(function() {
  $('.percent').each(function(i){
    $(this).attr('id', `percent_${i}`);
  });
  $('.contents-pacent__box2__graph2').each(function(i){
    $(this).attr('id', `graph_${i}`);
  });

  (window.onload = function() {
    var ele = document.getElementsByClassName("Projects");
    for(var i = 0; i < ele.length; i++){
      $("#graph_"+i).css({ 'width' : $( "#percent_"+ i).val() } );
    };
  });
});

④.percentと.contents-pacent_box2_graph2のそれぞれにidを振ります。
⑤リロードのタイミングでProjectsの数を数え、その回数だけ
”.contents-pacent
_box2_graph2のwidthの値を@percentからとる”
を繰り返すように①で付与したidを使ってfor文で設定します。

最後に

idの使い方やfor文の設定が自分では使ったことがなかったので苦労しました。
日々勉強します。

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

RSpec:モデルの単体テストの記述例

今回は前回の記事「RSpecの導入&関係の深いgemについて」の続きで、モデルの単体テストを行います。

前回の記事の各gemのインストール、初期設定が完了していることを前提としています。

モデルのテストファイルを作成

ターミナルでrails g rspec model モデル名コマンドを入力してテストファイルを生成します。

ターミナル
$ rails g rspec model モデル名
#以下のように表示されればテストファイルの生成が成功しています。
Running via Spring preloader in process 1234
      create  spec/models/モデル名_spec.rb
      invoke  factory_bot
      create    spec/factories/モデル名の複数形.rb

たとえばuserモデルのテストファイルを生成するとこうなります。

ターミナル
$ rails g rspec:model user
Running via Spring preloader in process 1234
      create  spec/models/user_spec.rb
      invoke  factory_bot
      create    spec/factories/users.rb #Factory Botをインストールしておくと自動生成される

ちなみに事前にFactory Botもインストールしておくと、spec/factories/users.rbは自動生成されます。

生成したuser_spec.rbにはこのような記述がされています。

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

do〜endの間にテストコードを記述していきます。

また1行目のrequire 'rails_helperは、前回の記事で行ったrails g rspec:installコマンドで生成したrails_helper.rbを読み込んでいます。

FactoryBotの設定

まずは、FactoryBotでUserモデルのテストで使用するインスタンスを設定してまとめます。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name { Faker::Name }
    email { Faker::Internet.free_email }
    password = Faker::Internet.password(min_length: 6)
    password { password }
    password_confirmation { password }
  end
end

passwordカラムとpassword_confirmationカラムは同じ値を入れるため、変数passwordに値を代入して、各カラムに変数passwordを入れます。

Faker::の後ろに続くNameInternet.free_emailでどんな値をランダムに生成するかを指定しています。前者なら名前、後者ならメールアドレスですね。

Fakerでは色んなタイプの値を生成してくれるので、Fakerの公式GitHubで見てみてください。

テストコードの設定

Userモデル(usersテーブル)にnameカラム、emailカラム、passwordカラムがあると想定して、
たとえば以下のようにユーザー新規登録機能のテストコードを記述します。

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    # 各テストコードが実行される前にFactoryBotで生成したインスタンスを@userに代入
    @user = FactoryBot.build(:user)
  end

  # describeでテスト対象をグループ分け
  describe '新規登録' do
    # contextでどのような状況なのかをグループ分け
    context '新規登録できるとき' do
      # itで何をテストしているのかをグループ分け
      it '全ての項目が入力されていれば登録できる' do
        expect(@user).to be_valid
      end
    end

    context '新規登録できないとき' do
      it 'nameが空では登録できない' do
        @user.name = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Name 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
      it '重複したemailが存在する場合登録できない' do
        @user.save
        another_user = FactoryBot.build(:user)
        another_user.email = @user.email
        another_user.valid?
        expect(another_user.errors.full_messages).to include('Email has already been taken')
      end
      it 'emailに@が含まれていないと登録できない' do
        @user.email.slice!('@')
        @user.valid?
        expect(@user.errors.full_messages).to include('Email is invalid')
      end
      it 'passwordが空では登録できない' do
        @user.password = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Password can't be blank")
      end
      it 'passwordが5文字以下では登録できない' do
        @user.password = '123ab'
        @user.valid?
        expect(@user.errors.full_messages).to include('Password is too short (minimum is 6 characters)')
      end
      it 'passwordが存在してもpassword_confirmationが空では登録できない' do
        @user.password_confirmation = ''
        @user.valid?
        expect(@user.errors.full_messages).to include("Password confirmation doesn't match Password")
      end
    end
  end
end

before do ~ end

テストコードの最初のbefore do ~ endは、describe以下の各テストコードが実行される前に処理される共通の内容を記述します。
今回の場合は登録するユーザー情報を変数@userに代入する処理を記述しています。
これで各テストコードでユーザー情報が入った変数@userを使えるようになりました。

describeメソッド、contextメソッド、itメソッド

テストコードのグループ分けに使用するメソッドで、

  • describeメソッドはどのような機能なのか(テスト対象)
  • contextメソッドはどのような状況なのか
  • itメソッドは何をテストしているのか

をそれぞれグループ分けしています。

expectation(エクスペクテーション)

一番最初の「describe新規登録、context新規登録できるとき、it全ての項目が入力されていれば登録できる」の中にあるような以下のような記述の部分をexpectationといい、テストで得られた結果が期待通りなのかを確認する構文です。

expect(@user).to be_valid

上記の記述のうち、be_validの部分をmatcher(マッチャ)といいます。

matcher

「expectの引数(カッコの中の記述)」と「想定した挙動」が一致しているか判断します。
expectの引数にはテストで得られた実際の挙動を指定し、matcherにはどのような挙動を想定しているかを記述します

単体テストでよく使うmatcherの例

matcher 想定する挙動 記述例
include Xの中にYという文字列が含まれているか expect(X).to include(Y)
be_valid expectのインスタンスが正しく保存される expect(X).to be_valid

テストコードの実行

少し補足説明がなりましたが、上記のテストコードの実行をします。
ターミナルで以下のコマンドを入力します。

ターミナル
$ bundle exec rspec spec/models/user_spec.rb

# 実行結果
User
  新規登録
    新規登録できるとき
      全ての項目が入力されていれば登録できる
      passwordが6文字以上の半角英数字であれば登録できる
      identityが空で登録できる
    新規登録できないとき
      nameが空では登録できない
      emailが空では登録できない
      重複したemailが存在する場合登録できない
      emailに@が含まれていないと登録できない
      passwordが空では登録できない
      passwordが5文字以下では登録できない
      passwordが存在してもpassword_confirmationが空では登録できない

Finished in 0.07492 seconds (files took 1.3 seconds to load)
10 examples, 0 failures

上記ではエラーなくテストが完了しましたが、expectationに記述されているmatcherのエラーメッセージなどに記述ミスがある場合は修正する必要があります。

テストコードの修正にはpry-railsというデバッグ用のgemを使用するのですが、それは別の記事で紹介したいと思います。

RSpecのシリーズ記事

第1回:RSpecの導入&関係の深いgemについて
第2回:RSpec:モデルの単体テストの記述例 ※この記事

参考資料

RSpec: Behaviour Driven Development for Ruby
Qiita:
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」

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

[解決] Railsの言語設定エラー 'translation missing: ja.datetime.distance_in_words.x_days ago' 

現状確認

ruby 2.6.6
Rails 6.1.0

インスタンス変数は、正しく定義されている。

[1] pry(#<#<Class:0x00007ff69c4e9978>>)> @post.created_at
=> Sun, 31 Jan 2021 08:28:26.434149000 UTC +00:00
[2] pry(#<#<Class:0x00007ff69c4e9978>>)> time_ago_in_words(@post.created_at)
=> "translation missing: ja.datetime.distance_in_words.x_days"

ファイルを見ると、日本語になっている(自分がen→jaに変えたことに気づいていなかった)

application.rb
config.i18n.default_locale = :ja

解決

今までtime_ago_in_wordsは英語設定だったから、上記の日本語変更でエラーが起きたっぽい

time_ago_in_wordsを日本語化するする

locales/ja.ymlにこれを書く

ja:
  datetime:
    distance_in_words:
      half_a_minute: "30秒前後"
      less_than_x_seconds:
        one:   "1秒"
        other: "%{count}秒"
      x_seconds:
        one:   "1秒"
        other: "%{count}秒"
      less_than_x_minutes:
        one:   "1分"
        other: "%{count}分"
      x_minutes:
        one:   "約1分"
        other: "%{count}分"
      about_x_hours:
        one:   "約1時間"
        other: "約%{count}時間"
      x_days:
        one:   "1日"
        other: "%{count}日"
      about_x_months:
        one:   "約1ヶ月"
        other: "約%{count}ヶ月"
      x_months:
        one:   "1ヶ月"
        other: "%{count}ヶ月"
      almost_x_years:
        one:   "1年弱"
        other: "%{count}年弱"
      about_x_years:
        one:   "約1年"
        other: "約%{count}年"
      over_x_years:
        one:   "1年以上"
        other: "%{count}年以上"

ブラウザで確認した、エラーは無くなっていた。

参考

【Rails】time_ago_in_words メソッドで created_atを「〜前」と表示する | RemoNote

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

【Rails】Capistrano main以外のブランチをデプロイする方法

通常、mainブランチをcapistranoでデプロイする時、このように入力すると思います。

$ bundle exec cap production deploy

ただ、開発中にmain以外のブランチをデプロイして確認したい時もあると思います。
そんな時は、

config/deploy.rbに追記して

$ set :branch, ENV['BRANCH'] || "main"

デプロイする。

bundle exec cap production deploy BRANCH=ブランチ名

特に指定しなければmainが、main以外ならブランチ名を指定してやれば、それぞれデプロイできます。

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

ActiveRecord includesとselectの別名カラム

概要

ActiveRecord includesを使う際、selectに別名カラムの記述があると
その別名カラムの参照ができない事象があったので、記事にしました。

開発環境

  • ruby 2.6.5
  • Rails 5.2.4.4

ActiveRecordクラス 定義

class User < ApplicationRecord
  has_one    :house
end

class House < ApplicationRecord
  belongs_to    :user
  belongs_to    :house_info
end

class HouseInfo < ApplicationRecord
  has_one    :house
end

問題ない場合のincludes構文

  • 問題なくuserインスタンスが取得できた (preloadの挙動になる)
User.includes(house: :house_info).select('users.id, users.created_at as user_registed_at')

SELECT  users.id, users.created_at as user_registed_at FROM "users"
SELECT "houses".* FROM "houses" WHERE "houses"."user_id" = $1

 [#<User:0x000055a87e374e58 id: 1>,
 #<User:0x000055a87e3742c8 id: 2>,
 #<User:0x000055a87e37f858 id: 3>,
 #<User:0x000055a87e37ecc8 id: 4>,
 #<User:0x000055a87e37e1b0 id: 5>]
  • selectで指定した別名カラムの値も取得できる
User.includes(house: :house_info).select('users.id, users.created_at as user_registed_at').first.user_registed_at

SELECT  users.id, users.created_at as user_registed_at FROM "users"
SELECT "houses".* FROM "houses" WHERE "houses"."user_id" = $1
=> "2021-02-05T13:02:17.754+09:00"

問題ありのincludes構文

includesしたテーブルに対し、検索条件(where)を追記してみた

  • house_infoのownerカラムをwhere条件にいれると left outter joinとなり、テーブルそれぞれのカラムが別名のsqlが発行される(eager_loadの挙動になる)
User.includes(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at')

SELECT  users.id, users.created_at as user_registed_at, "users"."id" AS t0_r0, "users"."name" AS t0_r1, "interviews"."creator_id" AS t0_r2, "houses"."user_id" AS t0_r3, "houses"."house_info_id" AS t0_r4, "house_infos"."address" AS t0_r5, "house_infos"."owner" AS t0_r6
FROM "users" LEFT OUTER JOIN "houses" ON "houses"."interview_id" = "users"."id" LEFT OUTER JOIN "house_infos" ON "house_infos"."id" = "houses"."house_info_id" WHERE "house_infos"."owner" = '太郎'

[#<User:0x000055a879e296c8
  id: 1,
  created_at: 2020/12/12>
]
  • select で別名指定した user_registed_atはsql上、存在するが、モデルから参照できない。
User.includes(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at').first.user_registed_at

NoMethodError: undefined method `user_registed_at' for #<User:0x000055a87c1a93c8>
from /bundle/gems/activemodel-5.2.4.4/lib/active_model/attribute_methods.rb:430:in `method_missing'

includesをやめて解決

  • househouse_infoを joinし、preloadでキャッシュするようにした
User.joins(house: :house_info).preload(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at')

SELECT users.id, users.created_at as user_registed_at FROM "users" INNER JOIN "houses" ON "houses"."user_id" = "users"."id" INNER JOIN "house_infos" ON "house_infos"."id" = "houses"."house_info_id" WHERE "house_infos"."owner" = '太郎'

SELECT "houses".* FROM "houses" WHERE "houses"."user_id" IN ($1, $2, $3, $4, $5)

SELECT "house_infos".* FROM "house_infos" WHERE "house_infos"."id" IN ($1, $2, $3, $4, $5)

[#<User:0x000055a87d6fb410 id: 1>,
 #<User:0x000055a87d6fa8f8 id: 2>,
 #<User:0x000055a87d6f9de0 id: 3>,
  .
  .
  • 各モデルから user_registed_atが参照できるようになった。
User.joins(house: :house_info).preload(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at').first.user_registed_at

=> "2021-02-05T13:05:47.225+09:00"

includesの挙動について調べてみた

  • includesはjoinsがなければpreloadと同じ挙動となる
  • 明示的にeager_loadと同じ挙動にするためにはreferencesというメソッドを併用する必要

User.includes(house: :house_info).references(house: :house_info).select('users.id, users.created_at as user_registed_at')

SELECT  users.id, users.created_at as user_registed_at, "users"."id" AS t0_r0, "users"."name" AS t0_r1, "interviews"."creator_id" AS t0_r2, "houses"."user_id" AS t0_r3, "houses"."house_info_id" AS t0_r4, "house_infos"."address" AS t0_r5, "house_infos"."owner" AS t0_r6
FROM "users" LEFT OUTER JOIN "houses" ON "houses"."interview_id" = "users"."id" LEFT OUTER JOIN "house_infos" ON "house_infos"."id" = "houses"."house_info_id"

なぜ?別名カラムをモデルから参照できなかったのか?

https://github.com/rails/rails/issues/34889
バグだったらしく、Rails6.0.3で治っているよう。

最後に

includesはケースによって
preloadまたはeager_loadの挙動をするのは知っていたが、
selectで指定した別名カラムの参照問題は知らず、、1日ハマっていた。
やっぱり、ActiveRecordはハマりポイントが多そう。

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

RubyでHit&Blow書いてみた

ここの数当てゲーム2を解いてみました

コード

class Answer
  def initialize(digits)
    num = %w[1 2 3 4 5 6 7 8 9]

    first_num     = num.delete(num.sample)
    remaining_num = num.push("0").sample(digits - 1)

    @answer             = [first_num, *remaining_num]
    @count_hit_and_blow = Hash.new(0)
    @digits             = digits
  end

  def judge_hit(entry)
    entry_judeged_hit = entry.dup

    entry.zip(@answer) do |n|
      if n.uniq!
        @count_hit_and_blow[:hit] += 1
        entry_judeged_hit.delete(n[0])
      end
    end

    entry_judeged_hit
  end

  def judge_blow(entry_judeged_hit)
    blow_count     = entry_judeged_hit.intersection(@answer).size
    entry_has_blow = blow_count != 0

    @count_hit_and_blow[:blow] = blow_count if entry_has_blow
  end

  def judge_game
    @count_hit_and_blow.each { |k, v| print "#{v}#{k} " }
    print "\n"

    if @count_hit_and_blow[:hit] == @digits
      sleep 0.5
      puts "game clear", "congratulations!!!!!"

      exit
    end

    @count_hit_and_blow.clear
  end
end

def input_digits
  loop do
    print "答えの桁数を入力してください: "
    digits = gets
    unless digits.match?(/^[0-9]+$/) && digits.to_i <= 10
      puts "不正な値であるか""別の値を入力してください\n\n"
      next
    end

    break digits.to_i
  end
end

def input_entry(digits)
  loop do
    print "回答を入力してください: "
    entry = gets.chomp
    exit if entry.empty?

    entry_is_suitable_number = entry.match?(/^[0-9]+$/)  && entry.size == digits
    entry_is_uniqe           = (ary_entry = entry.chars) && !ary_entry.uniq!

    unless entry_is_suitable_number && entry_is_uniqe
      puts "不正なであるか重複した数字がある値です", "別の値を入力して下さい\n\n"
      next
    end

    break ary_entry
  end
end

# --------------実行部分--------------
puts "数当てゲーム、Hit&Blowを始めます(空行を入力すると終了します)\n\n"
sleep 1

digits = input_digits
answer = Answer.new(digits)

puts "\nゲームスタート!\n\n"
loop do
  entry = input_entry(digits)

  entry_judeged_hit = answer.judge_hit(entry)
  answer.judge_blow(entry_judeged_hit)
  answer.judge_game
end

完走した感想

難しかった(小並感)

num = %w[1 2 3 4 5 6 7 8 9]

first_num     = num.delete(num.sample)
remaining_num = num.push("0").sample(digits - 1)

@answer = [first_num, *remaining_num]

簡単にググってみた所、答えの生成にこれよりスリムなコードが無かったので嬉しかった

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

【Rubyで検証】普通な人はあまり存在しない件 〜 普通の人とマッチングする確率とは?な話

kataomoi_woman.png

「私、結婚する人の条件はふつうな人(年収700万)でいいの〜」
(なお、サラリーマンの平均年収は約400万)

みたいな話は有名かと思いますが、ほんとうに普通の人ってなかなかいないよねというお話です
(半分真面目に、半分ふざけて計算してみた)

今回のメインテーマ

ざっくり話をまとめると、次のようなことをお伝えする記事となっております

  • 普通の人ってどのくらいの確率で存在するの?
  • ↑の計算するのにRubyでどうプログラムすれば良いの?

この記事をきっかけに、統計Rubyについて親しみを持ってもらえればと思います

前提

まずは前提条件を整理しておかないと変な文句を言われかねないので、整理しておきます(というかこれをしないと計算できない)

普通の人の定義
以下の各項目の偏差値が50以上とします(これは普通ぐらいでいてほしいとみんなが思いそうなことをあげてみました)

  • 身長
  • 頭脳(学歴とも言う)
  • 性格
  • 年収
  • ファッション

分布
全てのステータスは正規分布に従うと仮定する

その他補足
それぞれのステータスは独立しており、他のステータスに影響を与えない
(高身長だと高年収になるというような関係は無視する)

計算してみる

各ステータスについて、偏差値50以下と50以上で数が半々になるので、偏差値50以上となる確率は1/2です

ステータスは6つを想定しているので、全て50以上となる確率は単純に

\frac{1}{2}^6=\frac{1}{64}≒0.0156=1.56 [\%]

つまりたったの2%程度です?

普通というぐらいだから最低でも40〜50%程度には落ち着いてもらいたいところ

ではどこまで妥協すれば良いのでしょうか?

やり方としては、次の2つがありそうです?

1. 普通であってほしいステータスを絞る
2. 普通という基準を下げる(基準の偏差値を50より小さくする)

検証①

1つ目の方法は一瞬で終わります

ステータスが1つの場合は確率が50%
ステータスが2つの場合は50% × 50%25%

25%(4人に1人)を普通というのは苦しいので、この方法を取る場合は、普通であって欲しいステータスは1つまでということになります(うーん厳しい)?

検証②

こちらは期待する偏差値をどこまで下げたら良いの?ということですが、やや面倒です?

計算で出そうとすると、次のような手順になりましょう
(話が多少面倒なので、(ここから)(ここまで)間は読み飛ばしてもOKです)

(ここから)
ある偏差値s以上の確率をpとしたときに、全てのステータスがある偏差値s以上になる確率が50%になれば良い

全てのステータスがs以上になる確率は次のように計算できる

\begin{align}
\qquad &p^6 = 0.5\\
  \therefore &p = 0.89 = 89 [\%]
\end{align}

つまり存在確率が89%となる偏差値sを求めれば良いということになる

これを計算するには正規分布の確率密度関数をsから無限大の範囲で積分したときに89%となるsを求めれば良いということになる(タブン)
(ここまで)

しかしそんな計算したくない(というか自分には多分ムリ)

なのでプログラミングで概算値をざっと算出してみることにしました

※ この手の計算はPythonRを使ったほうが楽に計算できるのかと思いますが、あいにく筆者はそのへん使い慣れてないので、今回はRubyで無理やりやることにしています(汗)

プログラムの概要

次の手順で算出していこうと思います
(ゴールはここまでは妥協すべきという偏差値を求めることです)

  • 人物のモデルを作成
  • インスタンス作成時に6つのステータスを付与
  • ステータスは正規分布にしたがってランダムに与えられる
  • 10000人分のインスタンスを作成し、全てのステータスが偏差値50以上となる人数をカウントし、その存在確率を算出する
  • 存在確率が50%を下回ったら、期待する偏差値を1下げて再び存在確率を算出
  • 50%以上となる偏差値を求める

※ 10000という試行回数については、このぐらいやれば問題ないだろうという勝手な数値としています。何度かこの条件で計算して、そこまで数値がぶれなかったのでまあ問題ないと判断しています

プログラムの内容

まずはPersonモデルを作成します

person.rb
require './rand'

class Person
  attr_accessor :height, :face, :brain, :personality, :income, :fashion

  MU_SIGMA = { mu: 50, sigma: 10 }

  def initialize(expectation)
    @height = Random.new.normal_rand(**MU_SIGMA)
    @face = Random.new.normal_rand(**MU_SIGMA)
    @brain = Random.new.normal_rand(**MU_SIGMA)
    @personality = Random.new.normal_rand(**MU_SIGMA)
    @income = Random.new.normal_rand(**MU_SIGMA)
    @fashion = Random.new.normal_rand(**MU_SIGMA)
    @expectation = expectation
  end

  def nomal?
    height >= @expectation &&
    face >= @expectation &&
    brain >= @expectation &&
    personality >= @expectation &&
    income >= @expectation &&
    fashion >= @expectation
  end
end

  • initialize内ではRandomクラス(後述)によって、単純な乱数から正規分布に従う確率変数を発生させ、偏差値を付与しています。あと期待する偏差値をインスタンス変数として生成しています。

↑良い感じのライブラリがあればよかったんですが、見つからなかったのでこのような手法をとっています。

  • normal?メソッドでは、全てのステータスが期待する偏差値(expectation)以上かどうかの判定をしています

↑ちなみにmuは母集団の平均値、sigmaは標準偏差であり、偏差値の定義上平均が50、標準偏差を10にしております

続いて実際に計算するプログラムです(以下)

calc_expectation.rb
equire './person'

SAMPLE_NUMBER = 10_000

def main
  expectation = 50
  hit_count = 0

  probability = calc_probability(SAMPLE_NUMBER, expectation)

  while probability < 50 do
    puts "確率が#{probability}%なので再計算します(偏差値: #{expectation})"
    expectation -= 1
    probability = calc_probability(SAMPLE_NUMBER, expectation)
  end

  puts '計算が完了しました'
  puts "期待する偏差値が #{expectation}以上であれば、#{probability}%の確率で存在します"
end

def calc_probability(sample_number, expectation)
  hit_count = 0
  sample_number.times do
    hit_count += 1 if Person.new(expectation).nomal?
  end

  (hit_count.to_f / sample_number.to_f * 100).round(4)
end

main

概要にも書いたとおり、試行回数を1万回として、期待する偏差値の人が存在する確率を求め、50%以下であれば期待値を1下げて再度確率を求めています

いずれ存在確率が50%以上になったところで計算をやめるようにしています

補足的に、正規分布を発生させるクラスを載せておきます(以下)

rand.rb
class Random
  include Math

  # ボックス―ミューラー法による正規分布乱数発生
  def normal_rand(mu: 0, sigma: 1.0)
    a, b = self.rand(), self.rand()
    (sqrt(-2 * log(a)) * sin(2 * PI * b) * sigma) + mu
  end
end

ボックス―ミューラー法なるもので、正規分布に従う乱数を作っています

以下Wikipedia参照

ボックス=ミュラー法(ボックス=ミュラーほう、英: Box–Muller's method)とは、一様分布に従う確率変数から標準正規分布に従う確率変数を生成させる手法。計算機シミュレーションにおいて、正規分布に従う擬似乱数の発生に応用される。統計学者ジョージ・ボックス(英語版)とマーヴィン・マラー(ミュラー)によって考案された。

実行結果

スクリプトを実行すると以下の結果が得られました

$ ruby calc_expectation.rb
確率が1.65%なので再計算します(偏差値: 50)
確率が2.52%なので再計算します(偏差値: 49)
確率が3.88%なので再計算します(偏差値: 48)
確率が5.64%なので再計算します(偏差値: 47)
確率が7.52%なので再計算します(偏差値: 46)
確率が11.28%なので再計算します(偏差値: 45)
確率が14.9%なので再計算します(偏差値: 44)
確率が19.08%なので再計算します(偏差値: 43)
確率が23.47%なので再計算します(偏差値: 42)
確率が29.69%なので再計算します(偏差値: 41)
確率が35.24%なので再計算します(偏差値: 40)
確率が41.68%なので再計算します(偏差値: 39)
確率が48.16%なので再計算します(偏差値: 38)
計算が完了しました
期待する偏差値が 37以上であれば、53.84%の確率で存在します

50%くらいとなると偏差値37~38あたりという結果となりました
40%でも偏差値39ぐらいですね?

つまりステータスが6つの場合であれば、期待する偏差値は37〜40ぐらいになりましょう

まとめ

ふつうの人という存在がいかに稀有であるかお分かりいただけたでしょうか?

今回はステータスが6つを仮定しましたが、今回想定していないステータス(例えば体型や趣味・嗜好など)を追加すればするだけ、存在確率は天文学的な確率に近づいていきます?

とはいえ頭が良ければ年収が高くなるなど、お互い相関する部分もあるので、実際にはもう少しマシな確率にはなると思いますが・・・

普通の人はたくさんいるだろうという夢から覚めて、現実を見つめる一助となれば幸いです

(Rubyの学習にも一役買ってくれると幸いです)

参考

国税庁の平均給与情報
ボックス―ミューラー法

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

2重ハッシュ

user_data = [
 {user: {profile: {name: 'Choco'}}},
 {user: {profile: {name: 'Ramune'}}},
 {user: {profile: {name: 'Sakura'}}},
]

ここからeachメソッドを使って全てのユーザーの名前を出力したいと思います。

ハッシュから特定の値を取得する場合はその値に対応するキーを指定します。

ハッシュ[取得したい値のキー]

2重ハッシュから特定の値を取得する場合は、取得したい値のキーまでを連続して指定します。

ハッシュ[取得したい値のキー][取得したい値のキー]

冒頭の記述では配列の中にハッシュが格納されています。
ハッシュの1つ1つを取り出すためにeach文を用いて記述します。

user_data.each do |x|
  puts x[:user][:profile][:name]
end

ブロック変数をxとしました。
Rubyファイルの記述をまとめるとこのようになります。

user_data = [
 {user: {profile: {name: 'Choco'}}},
 {user: {profile: {name: 'Ramune'}}},
 {user: {profile: {name: 'Sakura'}}},
]

user_data.each do |x|
  puts x[:user][:profile][:name]
end

ターミナルでファイルを実行すると、順番にブロック変数xに代入されていき、全てのユーザーの名前が出力されます。

Choco
Ramune
Sakura

補足

※digメソッドを使って取り出す方法もあります。その場合は以下のような記述になります。

user_data.each{ |x| puts x.dig(:user, :profile, :name) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

renderにおけるlocalsとcollectionについて

localsとcollectionのオプションについての理解を深めようとこの記事を書くことにしました。

1.localsについて

仮にposts_controllersに以下の記述があるとして

posts_controllers.rb
def show
  @post = Post.find(params[:id)
end
posts/foobar.html.erb
<%= render partial: 'post_partial', locals: { post: @post } %>

または省略形で
<%= rendner 'post_partial', post: @post %>
posts/_post_partila.html.erb
<%= post.title%>
<%= post.body %>
公式(省略形)
<%= render 'パーシャル名', ローカル変数: インスタンス変数名 %>

つまりlocalsオプションを使用することで
@postというインスタンス変数をpostというローカル変数に変換することができる。

2.collectionについて

仮にposts_controllersに以下の記述があるとして

posts_controllers.rb
def index 
  @posts = Post.all
end 
posts/index.html.erb
<%= render partial: 'each_post', collection: @posts %>

応用形 asオプションでローカル変数を指定することができる
<%= render partial: 'each_post', collection: @posts, as: :post%>

collectionオプションを使用しなかった場合は以下の記述になるが

posts/_each_post.html.erb
#collectionを使わなければ
<% @posts.each do |post| %>
  <%= post.body%>
<% end %>

collectionオプションを使用するで
eachメソッドを省くことができるため簡潔に記述することができる

posts/_each_post.html.erb
#collectionを使用することで
<%= post.body%>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文字列のなかで変数を

html.erb形式にて、
文字列のなかに変数を含めたい

例えばpost変数の作成日を書くとき、

<%= "作成日:#{post.create_at}"  %>

初心者にとってはこれでもすごい

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

文字列のなかで変数を使いたい

html.erb形式にて、
文字列のなかに変数を含めたい

例えばpost変数の作成日を書くとき、

<%= "作成日:#{post.create_at}"  %>

初心者にとってはこれでもすごい

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

Railsでリンクを押すとなぜかcssが適用されてしまう??

概要

現在Railsでアプリを作成しているのですが、
Vue.jsを導入してから、リンクを押すとcssのスタイルが適用されてしまうバグが起きるようになりました。

試したこと

HTML,CSS,Vue.jsを少しずつ削って、どこが原因か探る作業を行いました。
(めちゃくちゃ時間かかった、、、)

結局解決できず。

解決策

application.jsのTurbolinksを無効化することで解決しました。

application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
import "bootstrap/dist/js/bootstrap"

Rails.start()
// ↓Turbolinksを無効化
// Turbolinks.start()
ActiveStorage.start()

require("../stylesheets/application.scss")

参考文献の方のように、自分も「turbolinks」と仲良くならないとなと実感しました。

参考文献

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

Boolean型からString型にする方法

問題

bにboolean型でtrueかfalseが与えられる時、string型に変更せよ。

def boolean_to_string(b)
  return
end

解答1

def boolean_to_string(b)
  b ? "true" : "false"
end

三項演算子なるほど。

解答2

def boolean_to_string(b)
  b.to_s
end

解答3

def boolean_to_string(b)
  b.inspect
end

inspect使ったことないですができるんですね!

感想

Codewarsは他の人が書いたコードが読めるのですごく勉強になりますね!

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

Railsのdeviseを使用して複数のログイン画面を実装する方法

deviseとは

Railsのgem(ライブラリ)の1つ。
ログイン機能の実装を簡単にしてくれるgemです。
これからdeviseを使用して複数のログイン画面を実装していきます。

インストール・準備

Gemfileに追記

Gemfile
gem 'devise'

bundleまたはbundle install実行

$ bundle install

deviseを使用できるようにインストールします。

$ rails g devise:install

上記のコマンドを実行すると、ターミナルに下記の文字が表示されます。

===============================================================================

Depending on your application's configuration some manual setup may be required:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

     * Required for all applications. *

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

     * Not required for API-only Applications *

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

     * Not required for API-only Applications *

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

     * Not required *

===============================================================================

では1から順に見ていきましょう!
config/environments/development.rbのendの上に記述してください。

config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

続いて2です。ルーティングを指定しましょう。

config/routes.rb
root to: "home#index"

続いて3です。こちらはログインした時にお知らせをしてくれます。

app/views/layouts/application.html.erb
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

ここで一つ注意なのですが、複数のログイン画面を実装するときは4は実行しないようにしましょう。

Modelを生成する

ここで2つのモデルを生成します。
モデルを生成するときは$ rails g devise モデル名です。

$ rails g devise A
$ rails g devise B

db/migrateに2つのファイルが生成されます。
設定を加えたい場合は今のうちに書き換えておきましょう。
そして $ rails db:migrate をお忘れなく!

Controllerを生成する

$ rails g devise:controllers As(複数形)
$ rails g devise:controllers Bs(複数形)

複数形にするのをお忘れなく!

設定ファイルを変更する

config/initializers/development.rbを開いて下記を見つけ出す。Macの方はコマンド⌘ + Fで簡単に探せますよ!

config/initializers/devise.rb
# config.scoped_views = false

上記のコメントアウトを外して、falseをtrueに書き換えます。

config/initializers/devise.rb
config.scoped_views = true

こうすることで複数モデルを可能にするようです。

Viewを生成する

rails g devise:views As(複数形)

rails g devise:views Bs(複数形)

ルーティングを生成する

config/routes.rbを以下のように書き換えてください。

config/routes.rb
Rails.application.routes.draw do
  devise_for :B, controllers: {
                         sessions: "Bs/sessions",
                         passwords: "Bs/passwords",
                         registrations: "Bs/registrations",
                       }
  devise_for :As, controllers: {
                       sessions: "As/sessions",
                       passwords: "As/passwords",
                       registrations: "As/registrations",
                     }
  root to: "home#index"
end

その他

それぞれの新規登録画面・ログイン画面を編集したい場合は、
[Aの新規登録画面]
app/views/As/registrations/new.html.rb
[Aのログイン画面]
app/views/As/sessions/new.html.rb
[Bの新規登録画面]
app/views/Bs/registrations/new.html.rb
[Bのログイン画面]
app/views/Bs/sessions/new.html.rb

rails routesでを見たらURLの指定もわかりやすいかと思います。

rails sでサーバーを起動して以下のようにURLを打ち込んで確認してください。
[Aの新規登録画面]
http://localhost:3000/As/sign_up
[Aのログイン画面]
http://localhost:3000/As/sign_in
[Bの新規登録画面]
http://localhost:3000/Bs/sign_up
[Bのログイン画面]
http://localhost:3000/Bs/sign_in

 参照

https://qiita.com/4npei/items/9066afb75d6a2ad9c661
https://qiita.com/Densetsu/items/2dc6a010809ae70c079e

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

通知機能

参考にしたサイト

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

Ruby 基本 コメントについて

コメントについて

  Rubyでコメントを書くにはいくつか書き方があります。
   ・1行コメント
    → 行の先頭に「#」を書く。これだけでその行がコメント扱いになる。

#コメント
def  ~

end

   ・複数行コメント
    → =beginと=endで囲まれた部分がコメント扱いになる。

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

【Ruby】Hashをまとめてみた

Hashまとめ

PaizaでHashを使うことがあったので、整理がてらまとめました。

Hashとは

  • みなさんお馴染みのKey,Value配列
    • なんだかんだでよく使う。実務でも割と。
    • 2次元配列が個人的には嫌いなので、Hashに逃げがち
  • Hash使えたら、Rubyを「完全に理解した」レベル。チョットデキルには程遠い。

Hash作成

hash = {}

要素の検索

Key検索

hash = { "Apple" => 1, "Orange" => 5 }
print hash.find {|k,v| k == "Apple"}
# { "Apple" => 1 }

Value検索

  • やり方は上と同じなので割愛。気が向いたら書く

要素の追加

hash = { "Apple" => 1, "Orange" => 5 }
hash.store( "Grape" , 3 )
print hash
# { "Apple" => 1, "Orange" => 5, "Grape" => 3 }

要素の削除

hash = { "Apple" => 1, "Orange" => 5 }
hash.delete("Apple")
print hash
# { "Orange" => 5}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】Hashまとめ

Hashまとめ

PaizaでHashを使うことがあったので、整理がてらまとめました。

Hashとは

  • みなさんお馴染みのKey,Value配列
    • なんだかんだでよく使う。実務でも割と。
    • 2次元配列が個人的には嫌いなので、Hashに逃げがち
  • Hash使えたら、Rubyを「完全に理解した」レベル。チョットデキルには程遠い。

Hash作成

hash = {}

要素の検索

Key検索

hash = { "Apple" => 1, "Orange" => 5 }
print hash.find {|k,v| k == "Apple"}
# { "Apple" => 1 }

Value検索

  • やり方は上と同じなので割愛。気が向いたら書く

要素の追加

hash = { "Apple" => 1, "Orange" => 5 }
hash.store( "Grape" , 3 )
print hash
# { "Apple" => 1, "Orange" => 5, "Grape" => 3 }

要素の削除

hash = { "Apple" => 1, "Orange" => 5 }
hash.delete("Apple")
print hash
# { "Orange" => 5}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

heroku上にアプリケーションをアップする

herokuへアップする!

まず自身のherokuアカウントへログインします。

% heroku login --interactive

カレントディレクトリがアップするアプリケーション上であることを確認。

% heroku create アプリケーション名
% git config --list | grep heroku

上記のコマンドで正しく設定できていることを確認する

% heroku addons:add cleardb

MySQLを使うためにアドオンを追加します。

% heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL`

MySQLを使うためにGemに考慮した設定の変更を行います。

% heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5}

ClearDBデータベースのURLを変数heroku_cleardbに格納。

config/credentials.yml.encの中身を確認します。

% EDITOR="vi" bin/rails credentials:edit

確認後esc, :qで脱出できます。
環境変数をセットしていきます。

環境変数
外部に漏らしたくない情報を環境変数にセットします。

% heroku config:set RAILS_MASTER_KEY=`cat config/master.key`

上記の記述でRAILS_MASTER_KEYを設定。この時公開鍵や秘密鍵を扱う場合は一緒にセットしておきます。

% heroku config:set PAYJP_SECRET_KEY= 'sk_test_-----'
% heroku config:set PAYJP_PUBLIC_KEY= 'pk_test_-----'

今回はpayjpを用いた開発だったので環境変数上にセットします。

% heroku config

上記で環境変数が正しく反映されているかを確認します。

アプリケーション情報をプッシュする

% git push heroku master

上記でherokuにアプリケーションを追加。

マイグレーションファイルを実行
この段階ではデータベースにはマイグレーションの情報が反映されていません。

% heroku run rails db:migrate

ここまでで正しい情報になっているかを確認します。

% heroku apps:info

この時表示されるURLへアクセス!

変更をプッシュする
その後変更などを加える必要がある場合は以下の処理を行う

% git add .
% git commit -m "コミット名"
% git push heroku master

処理を反映します。

stackの変更を加える必要がある場合

% heroku stack:set heroku-18 -a アプリケーション

今回の場合だとheroku-18に変更。
Stackとは、Herokuにおけるアプリケーションの動作環境のこと。Stackはデプロイされたアプリケーションを読み取り正常に稼働させるために用意されています。

以上で無事herokuへのデプロイ完了しました。

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

「action_args」Gemから学ぶ「Ruby」③ ソース分析2日目

本日はparams_handler.rbのstrengthen_params!メソッド分析

「action_args」Gem分析2日目です。
strengthen_params!メソッドソースを見てみます〜
Railsのストロングパラメーターについて知らない方は事前に読んだ方がいいです。

学習ポイント1

Object#instance_variable_defined?(var)
インスタンス変数varが定義されていたら真を返す

Object#instance_variable_get(var)
オブジェクトのインスタンス変数の値を取得して返します。
インスタンス変数が定義されていなければnilを返します

strengthen_params!
permitting_model_name = self.class.instance_variable_defined?(:@permitting_model_name) && self.class.instance_variable_get(:@permitting_model_name)

self.class.instance_variable_get(:@permitting_model_name)だけでもいい気がしているがなぜself.class.instance_variable_defined?も条件に追加されているのか気になる。

学習ポイント2

singularize:複数形で書かれた英語表記の文字列を単数形に変換

singularize
irb(main):009:0> "words".singularize
=> "word"

pluralize:単数形で書かれた英語表記の文字列を複数形に変換

pluralize
irb(main):010:0> "word".pluralize
=> "words"

underscore:キャメルケース文字列をスネークケース文字列に変更

underscore
irb(main):015:0> "ApplicationController".underscore
=> "application_controller"

tr(pattern, replace): pattern文字列に含まれる文字を検索し、それを replace文字列の対応する文字に置き換えます。

tr
irb(main):019:1* "applications_controller/test".tr('/','_')
=> "applications_controller_test"
strengthen_params!
 target_model_name = (permitting_model_name || self.class.name.sub(/.+::/, '').sub(/Controller$/, '')).singularize.underscore.tr('/', '_').to_sym

Controller名からController文字列を削除してキャメルケースをアンダースコアに変更したらモデル名になる。

学習ポイント3

%i: 要素がシンボルの配列を作る

irb(main):001:0> %i[req keyreq]
=> [:req, :keyreq]

require, permitについてはこちら

parametersについてはこちら

tryメソッド:オブジェクトがnilでない場合のみ、そのオブジェクトのメソッドを実行したい時に使うメソッド

tryメソッド
irb(main):005:0> user
=> #<User id: 1, uuid: nil, name: "tester", created_at: "2021-02-28 03:57:31.626363000 +0000", updated_at: "2021-02-28 03:57:31.626363000 +0000">
irb(main):006:0> user.try(:name) || 'なし'
=> "tester"
strengthen_params!
method_parameters.each do |type, key|
    trimmed_key = key.to_s.sub(/_params\z/, '').to_sym

    if (trimmed_key == target_model_name) && permitted_attributes
    # ストロングパラメータの場合
    # メソッドのパラメーターが必須の引数・必須のキーワード引数
     params.require(trimmed_key) if %i[req keyreq].include?(type)
    # paramsハッシュで宣言されたモデル属性を許可してる
     params[trimmed_key] = params[trimmed_key].try :permit, *permitted_attributes if params.key? trimmed_key
    end
end

テストコード

学習ポイント4

params生成方法

@params = ActionController::Parameters.new(x: '1', y: '2', foo: {a: 'a', b: 'b'}, bar: {a: 'a', b: 'b'}, baz: {a: 'a', b: 'b'}, hoge: {a: 'a', b: 'b'}, fuga: {a: 'a', b: 'b'}, foo_foo: {a: 'a', b: 'b'}, hoge_hoge: {a: 'a', b: 'b'}, fuga_fuga: {a: 'a', b: 'b'})

Class.new: 新しく名前の付いていない superclass のサブクラスを生成

test_case
# params生成
setup do
   @params = ActionController::Parameters.new(x: '1', y: '2', foo: {a: 'a', b: 'b'}, bar: {a: 'a', b: 'b'}, baz: {a: 'a', b: 'b'}, hoge: {a: 'a', b: 'b'}, fuga: {a: 'a', b: 'b'}, foo_foo: {a: 'a', b: 'b'}, hoge_hoge: {a: 'a', b: 'b'}, fuga_fuga: {a: 'a', b: 'b'})
end

# strengthen_params!を実行するメソッド
def execute_strengthen_params!(controller)
    c = controller.new
    c.instance_variable_set :@_params, @params
   #メソッド名パラメータに:aを渡してParamsのRequire,Permit処理
    c.strengthen_params! :a
end

test 'requiring via :req, permitting all scalars' do
    execute_strengthen_params! FooController ||= Class.new(ApplicationController) { permits :a, :b; def a(foo) end }
   # params[:foo] => {a: 'a', b: 'b'}
    assert @params[:foo].permitted?
    assert_not_nil @params[:foo][:a] # 'a'
    assert_not_nil @params[:foo][:b] # 'b'
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ポートフォリオ要件定義

ポートフォリオ事前レビュー

【テーマ】
働く上で発生する様々な悩みを解決する。

【ターゲット】
悩みをもつ働く人々

【サービス内容】
・悩みを投稿して、それに対してコメント(解決方法)をいただき、悩みを解決することができる。
・過去に同じような悩みを経験した人や、同じような悩みを解決した人に対して、個別相談することができる。
・悩み検索によって、過去の悩みから自分が抱えている悩みと似通った悩みを探すことができる。
・悩みへの解決方法が浮かばず、ユーザーの力になってあげることができなくても、悩みを応援することで、「よく応援されている悩み一覧」へ表示され、他のユーザーの目にとまりやすくなり、悩み解決率を上げることができる。

【テーマ背景】
私は現在、金融及び保険の営業をしています。しかし、先日、右膝の古傷が悪化したことで歩くことが難しくなり、営業活動が長期間できないという状態です。そんな時に色々と悩みが出てきました。
①怪我した状態で出勤してよいのか?
②出勤しない場合、どういった休暇扱いになるのか?
③補助金のような援助はでるのか?
④有給を使い切ってしまった場合はどうなるのか?
⑤結局どのように活動するのが、会社と自分にとって一番よいのか?
もちろん会社の人たちにも相談はしましたが、私のように仕事が長期間できなくなるという経験した方はいないので、解決方法を見出すことはできませんでした。
このような時に、自分の会社内の方だけではなく、会社外の方からも意見を頂戴することができれば、一番ベストな解決方法にたどり着くことができるのでは?と思いました。
その他にも、転職を考えている方であれば、具体的な退職手続きや退職前後の流れなど、社内の方には相談しにくいことが多々出てくると思います。そんなとき、自社とは全く関係のない社外の方であれば、気軽に相談することができると思います。
まとめると、悩みを解決する時に、情報収集の一つとしてご活用いただけるようなサービスになればと思い、今回「働く上で発生する様々な悩みを解決する。」というテーマに決定いたしました。
また、「悩み 社会人」とGoogleで検索したとき、転職サービスを運営している企業が作成したまとめサイトのようなものしかなく、生のユーザーの声をそのまま使ったサイトはあまり見られませんでした。なので、生のユーザーの声を大切にしたサービスにしたいと強く思っています。

【機能一覧】
《基本機能》
・ユーザー新規登録/ログイン/ログアウト(devise)
・ユーザープロフィール登録/編集/詳細機能(名前/プロフィール文)
・プロフィール画像アップロード機能(carrierwave)
・悩み新規投稿/編集/削除/詳細機能
・悩みへのタグ付け機能
・この悩み応援します機能(いいね)
・よく応援されている悩み順に一覧表示
・悩みを新着順に一覧表示
・悩みへのコメント機能
・個別相談機能(非同期通信)
・悩みキーワード検索機能(ransack)
・悩みをタグから検索できる機能(ransack)

《応用機能》
・かんたんログイン
・悩み一覧にページネーションを使う
・レスポンシブ対応
・通知機能

《その他》
・単体テスト
・結合テスト

【使用技術一覧】
《フロントエンド》
・HTML/CSS
・JavaScript
・Bootstrap(5.0)(カスタマイズして使用する)

《バックエンド》
・Ruby(3.0.0)
・Ruby on Rails(6.0.3.1)
・MySQL(8.0)

《インフラ》
・heroku(開発初期)
・AWS(VPC/EC2/S3/RDS)
・Nginx(Webサーバー)
・Puma(アプリケーションサーバー)

《テスト》
・RSpec

《CI/CD》
・CircleCI(自動テスト)
・Capistrano(自動デプロイ)
・Rubocop(コーディングチェック)

《バージョン管理》
・Git/GitHub

《開発環境》
・VScode
・Docker

【開発フェーズ】
《第一段階》
・基本機能実装
・テストの作成
・UI/UXのブラッシュアップ
・コーディングチェック
・herokuへのデプロイ

《第二段階》
・応用機能実装
・UI/UXのブラッシュアップ
・コーディングチェック

《第三段階》
・インフラをherokuからAWSに変更(VPC/EC2/S3/RDS)
・Docker/docker-composeの導入
・CI/CDの導入

《第四段階》
・EC2からECSに変更

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

DM機能

参考にしたサイト

新しく覚えたこと

アソシエーション

Image from Gyazo

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

ActiveRecordまとめメモ

はじめに

 最近Railsの独学をはじめたので備忘録として記事にしていきます。普段は業務でphpを使っていて、データのやりとりにはSQL文を書いてるんですが、SQLを書かずに簡単にデータ操作ができる「ActiveRecord」に感動しました!!その感動が伝わるように記事を書いていきたいと思います。アドバイスなどがあればコメントお願いします。

ActiveRecordとは?

結論から言うとActiveRecordとは、「DB操作を行うSQLを使わず、Rubyでデータ操作を可能にする仕組み」です。例えば、Userテーブルからidが1の顧客を取得する場合、SQLでは

「SELECT * FROM users WHERE id = 1」

と長いコードを書かなければいけないんですが、ActiveRecordであれば

「User.find(1)」

と書けばID=1のユーザー情報を簡単に取得することができます。

ActiveRecordを使用するメリット

■短いコードで直感的に書くことができる

 上記の例のように、ActiveRecordのメソッドを使えば簡単にDB操作を行うことができ、僕みたいな駆け出しエンジニアや初学者の方の心理的なハードルは下がると思います。まずはActiveRecordを使えばこういうことができるというのを経験的に学んで、そこから深く掘り下げてSQLなどを学んでいくと理解が深まると思うのでおすすめです。

よく使うメソッド

作成系(Create)

■newメソッド

インスタンスの生成を行う。newメソッドは新しいオブジェクトを作成するだけで、データベースへの保存はできない。

# インスタンスの生成
user = User.new
user.name = "yamada"
user.email = "example@mail.co.jp"

■saveメソッド

上記のnewメソッドでインスタンスを作成し、代入した値をデータベースに保存するのがsaveメソッド。
保存に成功すればtrue、失敗すればfalseを返す。

# 作成したオブジェクトの保存
user.save

■createメソッド

 インスタンスの生成とデータベースの保存を行うメソッド。newメソッドとsaveメソッドが行なっている処理を一括で行う。Userモデルにnameとemail属性があればデータベースに新しいレコードを作成する。

# インスタンスの生成 + オブジェクトの保存
user = User.create(name: "yamada", email: "example@mail.co.jp")

取得系(Read)

■allメソッド

指定したモデルの全てのオブジェクトを取得する。

# ユーザーモデルの全てのレコードを取得
users = User.all

■findメソッド

指定したidから一件のレコードを取得する。

# ユーザーモデルからidが1のレコードを取得
user = User.find(1)

■find_byメソッド

指定した属性、値の最初のレコードを一件取得する。

# ユーザーモデルからname属性が山田のレコードの最初の一件を取得
user = User.find_by(name: "yamada")

■firstメソッド

指定したモデルの最初のレコードを一件取得する。

# ユーザーモデルの最初のレコードを取得
user = User.first

■whereメソッド

指定した属性、値に一致するレコードを全て取得する。先ほどのfind_byメソッドは一致するレコードを一件取得するが、whereメソッドは全て取得する。

# ユーザーモデルのname属性で値が山田のレコードを全て取得
users = User.where(name: "yamada")

更新系(Update)

■updateメソッド

指定したレコードの値を変更する。

# idが1のレコードのname属性を田中に変更する
user = User.find(1)
user.update(name: "tanaka")

下記のコードと上記のコードは同じ意味。

user = User.find(1)
user.name = 'tanaka'
user.save

■update_all

指定した属性のレコードを全て更新する。一回のSQLで全て更新できるので一括で値を更新する場合に便利。

# ユーザーモデルの性別属性を全て男に更新
user.update_all(sex: "male")

削除系(Delete)

■destroyメソッド

指定したレコードを削除する。

# idが1のレコードを削除
user = User.find(1)
user.destroy

■destroy_byメソッド

指定した属性、値と一致するレコードを全て削除する。

# name属性の値が山田のレコードを全て削除
User.destroy_by(name: "yamada")

■destroy_all

指定したモデルの全てのレコードを削除する。

# ユーザーモデルのレコードを全て削除
User.destroy_all

参考記事

Raileガイド Active Record の基礎

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

Ruby on Railsの環境構築

私の今現在の環境の構築方法を忘れないためのメモです。

参考記事

ありがとうございます!

バージョン

Mac OS Catalina 10.15.17

MojaveやM1だと違う可能性があるのでご注意ください。

準備

Catalinaではzshというシェルを利用するので以下コマンドをターミナルで実行し、zshをデフォルトに設定します。
パスワードを求められる場合はPCのパスワードを入力します。

% chsh -s /bin/zsh

以下のコマンドでログインシェルを確認します。

% echo $SHELL

# 以下のように表示されればOK
/bin/zsh

Command Line Toolsをインストール

開発に必要なもろもろのソフトウェアをダウンロードするためにはCommand Line Toolsが必要なためインストールしていきます。

ターミナルに以下コマンドを入力します。

% xcode-select --install

下記画像のような画面になるのでインストールを選択します。

スクリーンショット 2021-02-28 11.01.39.png

下記画像のような画面になるので同意するを選択します。

スクリーンショット 2021-02-28 11.02.10.png

ちょっと時間かかりますので待ちます。

Homebrewのインストール

ソフトウェア管理ツールであるHomebrewをインストールします。

以下の公式サイトのインストールの下のスクリプトをコピーし貼り付けします。
Homebrew

私の場合は以下です。

# ホームディレクトリで行います
% /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

パスワードを求められたり、注意書きが出たりします。
Press RETURN to continue or any other key to abortと出たら続けるにはエンターキーを、やめるにはそれ以外の入力をという意味なのでエンターキーを押下します。

以下コマンドでインストールできたか確認できます。

% brew -v

# 以下のようになればOK(数字は違う可能性があります)
Homebrew 2.5.1

以下コマンドで最新バージョンにします。

% brew update

以下コマンドで権限を変更します。

%  sudo chown -R `whoami`:admin /usr/local/bin

パスワードを求められるのでPCのパスワードを入力します。

Rubyをインストール

Rubyをインストールしていきます。
このためにHomebrewが必要でした。

以下のコマンドでRubyの土台となるrbenvruby-buildをHomebrewを用いてインストールします。

% brew install rbenv ruby-build

以下のコマンドでPCのどこからでもrbenvを利用できるようにします。

% echo 'eval "$(rbenv init -)"' >> ~/.zshrc

以下のコマンドでzshrcを再読み込みし変更を反映させます。

% source ~/.zshrc

以下のコマンドでターミナルのirb上で日本語入力を可能にする設定を行います。readlineをインストールします。

% brew install readline

以下のコマンドでreadlineをPCのどこからでも利用できるようにします。

% brew link readline --force

以下のコマンドでrbenvを利用してRubyをインストールします。
今回は2.6.5のバージョンです。

% RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)"

# バージョンを指定しインストール
% rbenv install 2.6.5

# インストールしたRuby 2.6.5を使用するために以下コマンドを実行
% rbenv global 2.6.5

# 以下コマンドでrbenvを読み込む
% rbenv rehash

# 以下コマンドでバージョンの確認ができる
% ruby -v

MySQLのインストール

以下のコマンドを実行して、MySQLをインストールします。

% brew install mysql@5.6

以下のコマンドで自動で起動するように設定します。

% mkdir ~/Library/LaunchAgents 
% ln -sfv /usr/local/opt/mysql\@5.6/*.plist ~/Library/LaunchAgents
% launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.6.plist

以下のコマンドでPCのどこからでもMySQLが使えるようにします。

# mysqlのコマンドを実行できるようにする設定
% echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc

#  設定を読み込むコマンド
% source ~/.zshrc

# mysqlのコマンドが打てるか確認する
% which mysql
# 以下のように表示されればOK
/usr/local/opt/mysql@5.6/bin/mysql

# MySQLの状態を確認するコマンド
% mysql.server status
# 以下のように表示されればOK
SUCCESS! MySQL running

Railsのインストール

以下コマンドでgemを管理するためのbundlerバンドラーをインストールします。

% gem install bundler --version='2.1.4'

以下コマンドでRailsをインストールします。

% gem install rails --version='6.0.0'

# rbenvの再読み込みをして設定を反映させる
% rbenv rehash

# バージョン確認
% rails -v
# 以下のように表示されればOK
Rails 6.0.0

Node.jsのインストール

RailsでJavaScriptを動かすために必要なNode.jsをインストールします。
以下コマンドを実行します。

% brew install node@14

# パスを設定する
% echo 'export PATH="/usr/local/opt/node@14/bin:$PATH"' >> ~/.zshrc
% source ~/.zshrc

# バージョンを確認
% node -v
#以下のように表示されればOK
v14.15.3

yarnをインストール

yarnについてよくわかっていなかったのですが、以下の記事がわかりやすかったです。
ありがとうございます!
npmとはyarnとは

以下コマンドを実行します。

% brew install yarn

# バージョンを確認
% yarn -v

以上です。

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

【Ruby】【paiza】繰り返し処理

繰り返し処理

  • 繰り返し処理を使って、スマートに(エンジニアぽく)問題を解きましょう。

サンプル問題

  • 配列array = [10,22]
  • 配列の中身の合計値を算出

普通の解き方

sum = array[0] + array[1]
print sum // 32

繰り返しを使ってスマートに

sum = 0
for number in line
 sum = sum + number
end

print sum // 32

もしも

もしも配列が3つになったらどうする?

  • 繰り返しを使えば、配列の増減に勝手に対応してくれる(エンジニアぽくなる)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails I18nを便利に使おう

アプリケーションを色々な国に展開する場合やメッセージを日本語化する際にI18nを使用すると思います。

下記のようなパターンではuser_nameをtranslateに渡すと、"太郎"が返ってきます。

config/locales/ja.yml
ja:
  user_name: "太郎"
> I18n.t(:user_name)
=> "太郎"

しかし、userが、太郎だけであればこれでも良いのですが、複数いる場合がたいていのパターンだと思います。
これを少し拡張させて、どのuserでも対応できるようにしていきます。

config/locales/ja.yml
ja:
  user_name: "%{name}"
> I18n.t(:user_name, name: "太郎")
=> "太郎"
> I18n.t(:user_name, name: "次郎")
=> "次郎"

上記のように変更すると、引数に名前を渡せば、どんな名前でも表示できるようになりました。
これで使いやすくなりました。

さらにtranslateではこのようなこともできます。

config/locales/ja.yml
ja:
  user_name_html: <span class="user">"%{name}"</span>

このように最後に_htmlをつけることで、htmlごと(今回はspanタグごと)返すこともできます。
view自体が複雑でごちゃごちゃしている場合,こうゆうパターンで書くのも一つの手かもしれません。

translateは調べるといろんなことができるので、色々試してやってみましょう!!
案外今やりたいことと合致するかもしれません

 参考記事

あなたはいくつ知っている?Rails I18nの便利機能大全!

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