- 投稿日:2020-04-04T23:40:02+09:00
ラベル機能実装後、検索のテストをしていたらPG::AmbiguousColumn: ERROR
ラベルでの検索も実装し、そのSystem Spec中にタイトルのエラーが発生
System Spec中に……と書いてあるものの、テストを書き色んなパターンを試した結果、見落としていたエラーを発見した、というのが正しいです。
前提
タスク管理アプリを作成中。
タスク名、ステータス(未着手、着手、完了)での検索は実装済み。
今回、タスクにラベル(タグのようなもの)を付けられるようにし、そのラベルでもタスクを検索できるように実装。
その機能のテストを書いている時に起こったエラー。環境
Rails 5.2.3
Ruby 2.6.5テーブル構成
TaskテーブルとLabelテーブルがあって、多対多を実現するためのLabelingという中間テーブルがある。
schema.rb(一部略)create_table "labelings", force: :cascade do |t| t.bigint "task_id" t.bigint "label_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["label_id"], name: "index_labelings_on_label_id" t.index ["task_id"], name: "index_labelings_on_task_id" end create_table "labels", force: :cascade do |t| t.string "name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "tasks", force: :cascade do |t| t.string "name", null: false t.text "content", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.datetime "deadline", null: false t.integer "priority", default: 0, null: false t.integer "status", default: 0, null: false t.bigint "user_id" t.index ["name"], name: "index_tasks_on_name" t.index ["user_id"], name: "index_tasks_on_user_id" end検索のロジック
モデルにscopeを書いて実装。
app/models/task.rb(一部略)scope :default_order, -> { order(created_at: :desc) } # 作成日を降順に並べるscopeの名前を変更し分かりやすく scope :sort_deadline, -> { order(deadline: :desc) } scope :search_with_name, -> (name) { return if name.blank? where('name LIKE ?', "%#{name}%") } scope :search_with_status, -> (status) { return if status.blank? where(status: status) } scope :search_with_label, -> (label) { return if label.blank? joins(:labels).where('labels.id = ?', label) # where(id: label)じゃだめ }app/controllers/tasks_controller.rbdef index @tasks = current_user.tasks @tasks = @tasks .search_with_name(params[:name]) .search_with_status(params[:status]) .search_with_label(params[:label]) # 中略 end検索を実装してるviewのコード
app/views/tasks/index.html.slim= form_with(method: :get, local: true, url: tasks_path) do |f| = f.label :name_search, t('.name_search'), value: params[:name] = f.search_field :name, placeholder: t('.name_search'), class: 'form-control' = f.label :status_search, t('.status_search'), value: params[:status] = f.select :status, Task.enum_options_for_select(:status), class: 'form_control', include_blank: true, selected: '' / = f.select :status, [Task.human_attribute_name('status.waiting'), Task.human_attribute_name('status.working'), Task.human_attribute_name('status.completed')], class: 'form-control', include_blank: true, selected: '' = f.label :label_search, t('.label_search'), value: params[:label] = f.select :label, Label.pluck(:name, :id), { include_blank: true } = f.hidden_field :search, value: true div.search_button = f.submit(t('.search'), class: 'btn btn-secondary')実際のページはこんな感じ
ラベルでの検索も問題なく実装できていた。ように見えていた。
System Specを書いていると……
ラベルでの検索機能のSystem Spec。ラベルのみでの検索は無事テストを書けたので、タスク名、ステータス、ラベルの3つ全てで検索をするテストを書いてみた。
spec/system/task_spec.rbcontext 'タスク名、ステータス、ラベルの全てで検索した場合' do before do fill_in 'タスク名で検索', with: "TEST_TASK" select '未着手', from: :status select '勉強', from: :label click_button '検索' end example 'マッチしたタスクのみが表示される' do within ('tbody') do expect(page).to have_text "TEST_TASK", "未着手", "勉強" end end endFactoryBotで2つのデータを用意している。検索した結果、このタスクのみが表示されるはずだった。
しかしここで問題発生。
テストが失敗してエラー時のスクリーンショットも見たことないような画面に……。コンソールのログを見てみると、
Failures: 1) Tasks タスク一覧画面 タスクの検索機能 タスク名、ステータス、ラベルの全てで検索した場合 マッチしたタスクのみが表示される Failure/Error: - @tasks.each do |task| ActionView::Template::Error: PG::AmbiguousColumn: ERROR: column reference "name" is ambiguous LINE 1: ...ngs"."label_id" WHERE "tasks"."user_id" = $1 AND (name LIKE ... ^ : SELECT "tasks".* FROM "tasks" INNER JOIN "labelings" ON "labelings"."task_id" = "tasks"."id" INNER JOIN "labels" ON "labels"."id" = "labelings"."label_id" WHERE "tasks"."user_id" = $1 AND (name LIKE '%TEST_TASK%') AND "tasks"."status" = $2 AND (labels.id = '1') ORDER BY "tasks"."created_at" DESC LIMIT $3 OFFSET $4
PG::AmbiguousColumn: ERRORで検索。ActionView::Template::Error:PG::AmbiguousColumn: ERROR…でRSpecが失敗して困った。 - Qiita
Taskモデルに、TaskモデルとアソシエーションしているLabelモデルをjoinsで結合しています。
それによりorder("created_at desc")が、tasksテーブルにあるcreated_atを指しているのか、labelsテーブルにあるcreated_atを指しているのか、曖昧( = AmbiguousCoulumn )になっていることがエラーの原因でした。これを見てもう一度コンソールのログを見直すと、
column reference "name" is ambiguousと文句言われてる。そこで気付いた。LabelテーブルにもTaskテーブルにもnameカラムがあることに。
その上、検索を実装してるscopeの中身を見ると、app/models/task.rbscope :search_with_name, -> (name) { return if name.blank? where('name LIKE ?', "%#{name}%") }と、
'name LIKE?'だけで何のテーブルかを指定していないのが原因そうだ。解決法
where('name LIKE ?', "%#{name}%")を、where('tasks.name LIKE ?', "%#{name}%")に。何のnameかを指定してあげる。
app/models/task.rbscope :search_with_name, -> (name) { return if name.blank? where('tasks.name LIKE ?', "%#{name}%") }これで無事テストも通ったし、タスク名、ステータス、ラベル全てでの検索をしても問題なく動くようになった。
関連付けしたテーブルで同じカラム名がある場合は注意する!
- 投稿日:2020-04-04T23:32:05+09:00
[*Rails*] deviseの使い方(rails6版)
はじめに
Rails6での再再検証版です。
以下のRails5のときのものをRails6で実行し、一部変更になっている部分を反映したバージョンです。
[Rails] deviseの使い方(rails5版)【前回】 rails : 5.0.0.1 ruby : 2.3.1 devise: 4.2.0 ↓ 【今回】 rails : 6.0.2.2 ruby : 2.7.1 devise: 4.7.1deviseとは
ユーザー登録して、送られてきたメールのリンクをクリックして本登録して、ログインして、パスワード忘れたら再設定して、何回もログインミスったらアカウントロックして…などといった認証系アプリに必要な機能を簡単に追加できる便利なgemです。
1. gemのインストール
1. プロジェクトの作成
新しいプロジェクトを作ります。
$ rails new devise_rails6 $ cd devise_rails6/2. Gemfileの編集とインストール
以下ファイルに
deviseとomniauth-twitterを追加します。source 'https://rubygems.org' # (省略)... # Devise gem 'devise' gem 'omniauth-twitter'gemをインストール。
$ bundle install2. deviseの設定
devise関連ファイルを追加。
すると以下のような英文が表示されます。1から4まで順番に見ていきます。$ rails generate devise:install Running via Spring preloader in process 30440 create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Some setup you must do manually if you haven't yet: 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. 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" 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> 4. You can copy Devise views (for customization) to your app by running: rails g devise:views ===============================================================================1. デフォルトURLの指定
英文の例に書いてあった
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }を以下のファイルに追加しました。
config.action_mailer.default_url_options = { host: 'localhost:3000' }でもOKです。config/environments/development.rbRails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # (省略)... # mailer setting config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } # config.action_mailer.default_url_options = { host: 'localhost:3000' } end2. root_urlの指定
1番で指定した
http://localhost:3000/にアクセスした際に表示されるページを指定します。
現状ページは1つも作っていないため、先に追加します。Pagesコントローラーと、indexページとshowページを追加してみます。
$ rails g controller Pages index show
routes.rbに以下を指定します。config/routes.rbRails.application.routes.draw do root 'pages#index' get 'pages/show' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end3. flashメッセージの設定
ログインした時などに上の方に「ログインしました」みたいなメッセージが出るようにします。
以下のファイルの<body>タグのすぐ下に指定されたタグを挿入します。app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title>DeviseRails6</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <!-- ここに追加 --> <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> <%= yield %> </body> </html>4. deviseのViewを生成
deviseの導入で追加されるViewは、以下のコマンドを実行しなければデザインを変更できないので、デザインをカスタマイズするためにも実行します。
$ rails g devise:viewsすると以下の様なファイルが生成されます。
app/views/devise/shared/_error_messages.html.erb (エラーメッセージ用パーシャル) app/views/devise/shared/_links.html.erb (リンク用パーシャル) app/views/devise/confirmations/new.html.erb (認証メールの再送信画面) app/views/devise/passwords/edit.html.erb (パスワード変更画面) app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面) app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面) app/views/devise/registrations/new.html.erb (ユーザー登録画面) app/views/devise/sessions/new.html.erb (ログイン画面) app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面) app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文) app/views/devise/mailer/email_changed.html.erb (メール用メールアドレス変更完了文) app/views/devise/mailer/password_change.html.erb (メール用パスワード変更完了文) app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文) app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)3. Userモデルの設定
1. Userモデルの作成
以下を実行。
$ rails g devise Userマイグレーションファイルができます。
デフォルトではこんな感じになってます。db/migrate/20200404040003_devise_create_users.rb# frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable # t.integer :sign_in_count, default: 0, null: false # t.datetime :current_sign_in_at # t.datetime :last_sign_in_at # t.string :current_sign_in_ip # t.string :last_sign_in_ip ## Confirmable # t.string :confirmation_token # t.datetime :confirmed_at # t.datetime :confirmation_sent_at # t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true # add_index :users, :confirmation_token, unique: true # add_index :users, :unlock_token, unique: true end endUserモデルはこんな感じになっています。
デフォルトではdatabase_authenticatable、registerable、recoverable、rememberable、validatableが使えるようになっています。app/models/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable end2. deviseモジュール概要
各モジュールについて以下に紹介します。
機能 概要 database_authenticatable サインイン時にユーザーの正当性を検証するためにパスワードをハッシュ化してDBに登録します。認証方法としてはPOSTリクエストかHTTP Basic認証が使えます。 registerable 登録処理を通してユーザーをサインアップします。また、ユーザーに自身のアカウントを編集したり削除できるようにします。 recoverable パスワードをリセットし、それを通知します。 rememberable 保存されたcookieから、ユーザーを記憶するためのトークンを生成・削除します。 trackable サインイン回数や、サインイン時間、IPアドレスを記録します。 validatable Emailやパスワードのバリデーションを提供します。独自に定義したバリデーションを追加することもできます。 confirmable メールに記載されているURLをクリックして本登録を完了する、といったよくある登録方式を提供します。また、サインイン中にアカウントが認証済みかどうかを検証します。 lockable 一定回数サインインを失敗するとアカウントをロックします。ロック解除にはメールによる解除か、一定時間経つと解除するといった方法があります。 timeoutable 一定時間活動していないアカウントのセッションを破棄します。 omniauthable intridea/omniauthをサポートします。TwitterやFacebookなどの認証を追加したい場合はこれを使用します。 3. Userモデルの編集
今回はデフォルトではないものも触ってみたいと思うので全部入れてみます。
Twitter認証を使うのでTwitterを指定しています。app/models/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :lockable, :timeoutable, :trackable, :omniauthable, omniauth_providers:[:twitter] end4. マイグレーションファイルの編集
上に合わせて使用モジュールに対応する部分のコメントアウトを外します。今回は全部使うので全部外します。
全部を使用しない場合は 3. で使うことにしたものに関連する項目だけコメントアウトを外してください。db/migrate/20200404040003_devise_create_users.rb# frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable t.integer :sign_in_count, default: 0, null: false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip ## Confirmable t.string :confirmation_token t.datetime :confirmed_at t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable ## Lockable t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts t.string :unlock_token # Only if unlock strategy is :email or :both t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true add_index :users, :confirmation_token, unique: true add_index :users, :unlock_token, unique: true end end5. omniauth-twitter用カラムの追加
ついでに
omniauth-twitterで使うproviderとuid、それとTwitter認証の場合はアカウント名を保存しておきたいのでusernameもUserテーブルに追加します。
その他、保存したい項目があったら arunagw / omniauth-twitter の認証時のハッシュ情報を参考に保存用カラムを追加してください。$ rails g migration add_columns_to_users provider uid username以下のようなマイグレーションファイルができます。
db/migrate/20200404042756_add_columns_to_users.rbclass AddColumnsToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :provider, :string add_column :users, :uid, :string add_column :users, :username, :string end endここまで出来たら以下を実行します。
$ rake db:migrate4. viewの編集
以下のファイルを編集して、ページ上部にメニューが出るようにします。
user_signed_in?はdeviseのHelperメソッドです。
ログインしているかしてないかで上部のメニューの表示が変わるようになります。
current_userで現在サインインしているユーザーの情報を取得できます。app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title>DeviseRails6</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <header> <nav> <% if user_signed_in? %> <strong><%= link_to current_user.username, pages_show_path %></strong> <%= link_to 'プロフィール変更', edit_user_registration_path %> <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %> <% else %> <%= link_to 'サインアップ', new_user_registration_path %> <%= link_to 'ログイン', new_user_session_path %> <% end %> </nav> </header> <!-- ここに追加 --> <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> <%= yield %> </body> </html>以下の2ページも修正。
indexの方がトップページ、showの方がログインしているユーザー用のページになる予定です。app/views/pages/index.html.erb<h1>ようこそ</h1> <p>トップページです。</p>app/views/pages/show.html.erb<h1>こんにちは、<%= current_user.username %>さん</h1> <p>ユーザー用ページです。</p>サーバーを立ち上げて、「サインアップ」を押下すると、
app/models/user.rbでomniauthableを設定しているのに何の設定もしていないので現状ではエラーになります。$ brew install yarn $ rails webpacker:install $ rails s
omniauthableを設定していない場合はサインアップページが表示されます。Twitterで認証する
1. 設定
1. Twitter Developerの登録
Twitter Developerにアクセスし、
Create an appをクリックし(ツイッターにログインしてないとボタンが出ません)、情報を入力にします。
審査がいるようになってしまったので、私がやったときは何週間かかかった気がします。
私は今回、rails5版のこの記事を書いたときのものを使用しています。新規申請する場合は、申請時に
Enable Sign in with Twitterにチェックを入れておいてください。2. 設定ファイルの編集
Keys and tokensタブを開き、API keyとAPI secret keyを以下の該当箇所にコピーして貼り付けます。
callback_urlをhttp://127.0.0.1:3000/users/auth/twitter/callbackにし、Twitter Developer側のコールバックの設定も変更する必要があるようです。
なので、以下の用に修正しました。config/initializers/devise.rbDevise.setup do |config| # (省略)... config.omniauth :twitter, 'API Key', 'API Secret', callback_url: 'http://127.0.0.1:3000/users/auth/twitter/callback' end2. 動作確認
サーバーを立ち上げてサインアップページの下の方にあるSign in with Twitterというリンクをクリックします。
すると以下の様な画面が開くのでログインして
連携アプリを認証をクリックします。こんな画面が出るはずです。
3. コールバック用コントローラーの作成
Twitter認証後適切に画面が遷移するように以下を実行してコントローラーを作ります。
$ rails g controller omniauth_callbacks作成したコントローラーの中身を以下のように修正します。
継承するのがDevise::OmniauthCallbacksControllerになっていることに注意です。
omniauth.authという環境変数に認証に関する情報が入っています。
その情報を使ってユーザーが登録されているかを検証し、登録してる場合はログイン、登録されてない場合は登録用ページに遷移します。app/controllers/omniauth_callbacks_controller.rbclass OmniauthCallbacksController < Devise::OmniauthCallbacksController def twitter @user = User.from_omniauth(request.env["omniauth.auth"].except("extra")) if @user.persisted? sign_in_and_redirect @user else session["devise.user_attributes"] = @user.attributes redirect_to new_user_registration_url end end endちなみに
omniauth.authの中にはこんなものが入ってます。(一部の値は適当に変えてあります。){ "provider"=>"twitter", "uid"=>"0123456789", "info"=>{ "nickname"=>"manycicadas", "name"=>"芭蕉", "location"=>"関東", "image"=>"http://pbs.twimg.com/profile_images/483964583371997185/2ZqzhzKV_normal.png", "description"=>"JavaEE/Ruby(Rails)/HTML/CSS/JavaScript/Raspberry Pi などなどが好き。", "urls"=>{ "Website"=>nil, "Twitter"=>"https://twitter.com/manycicadas" } }, "credentials"=>{ "token"=>"xxx", "secret"=>"xxxxxx" } (省略)... }Userモデルに
self.from_omniauthとself.new_with_sessionを作ります。
self.from_omniauthではuidとproviderで検索してあったらそれを、無かったらレコードを作ります。
self.new_with_sessionについては、もしこのメソッドを追加しておかなければ、Twitter認証後サインアップページで登録を行っても、認証情報として取ってきたuidやproviderなどが登録されません。それらが登録されないのでTwitterで認証しても登録されてないユーザーとして毎回サインアップページに飛ばされます。app/models/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :lockable, :timeoutable, :trackable, :omniauthable, omniauth_providers:[:twitter] def self.from_omniauth(auth) find_or_create_by(provider: auth["provider"], uid: auth["uid"]) do |user| user.provider = auth["provider"] user.uid = auth["uid"] user.username = auth["info"]["nickname"] end end def self.new_with_session(params, session) if session["devise.user_attributes"] new(session["devise.user_attributes"]) do |user| user.attributes = params end else super end end end以下ファイルを編集して、コールバック用のコントローラーとしてさっき作ったコントローラーが呼ばれるようにします。これを書かないとdevise側のコントローラーが呼ばれます。
config/routes.rbRails.application.routes.draw do devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks" } root 'pages#index' get 'pages/show' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html endこれでTwitter認証ができるようになりました。
初回、Twitter認証を行うと、サインアップページに飛ばされ、そこでメールアドレスやパスワードを入力して登録するとユーザー情報が登録されます。
今回はcomfirmable機能を入れているので、登録したら確認メッセージを送ったとのメッセージが出て、そのままログインすることはできません。
この機能を入れてなかった場合、登録すると即ログインします。アカウント登録確認メールを送る
1. comfirmable概要
多くの登録系サイトで採用されている、登録すると仮登録状態になり、届いたメールのリンクをクリックするとログイン可能になるという仕組みを追加できるのがcomfirmableです。
今回はGmailのアカウントを使って実際にメールが届くように設定します。2. ログを見る
log/deployments.logDevise::Mailer#confirmation_instructions: processed outbound mail in 8.8ms Delivered mail 5e881d3772753_89a53b9c70510@SampleMacBook-Pro.local.mail (22.3ms) Date: Sat, 04 Apr 2020 14:37:59 +0900 From: please-change-me-at-config-initializers-devise@example.com Reply-To: please-change-me-at-config-initializers-devise@example.com To: sample@example.com Message-ID: <5e881d3772753_89a53b9c70510@SampleMacBook-Pro.local.mail> Subject: Confirmation instructions Mime-Version: 1.0 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit <p>Welcome sample@example.com!</p> <p>You can confirm your account email through the link below:</p> <p><a href="http://localhost:3000/users/confirmation?confirmation_token=1vus7S8UesPoay5yz2je">Confirm my account</a></p>3. メールが実際に届くようにする
1. 設定ファイルの編集
今回はgmailを使うのでメールアドレスにはgmailを設定します。
デフォルトの設定があると思うので、それを変更してください。config/initializers/devise.rbDevise.setup do |config| # (省略)... config.mailer_sender = "メールアドレス" endgmailの場合はGメールアドレスとGメールパスワードの部分を自分のアカウントのものに変更します。
config/environments/development.rbRails.application.configure do # (省略)... # mailer setting config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } # config.action_mailer.default_url_options = { host: 'localhost:3000' } config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :address => 'smtp.gmail.com', :port => 587, :user_name => 'Gメールアドレス', :password => 'Gメールパスワード', :authentication => :plain, :enable_starttls_auto => true } end2. Gmailの設定
1. とりあえず試したい場合
デフォルトでは安全性の低いアプリケーションからGmailへのアクセスが制限されています。
よってその制限を解除することで動くようにできます。
ただ、安全性の低いアプリからのアクセスを許可するということはセキュリティ的にはいまいちなのであくまで動作確認ように利用することをおすすめします。
安全性の低いアプリがアカウントにアクセスするのを許可する の安全性の低いアプリを許可というリンクをクリックして、以下のように設定します。2. 2段階認証がオンの場合
2段階認証をオンにしている場合、1の設定はできないはずなのでこちらをやってください。
また、1のセキュリティ的にイマイチな方をやりたくない方もこの際2段階認証をオンにしてこの設定をやってください。アカウントへのアクセスとセキュリティ設定の管理を開きます。
2段階認証がオフの場合は2段階認証プロセスから2段階認証の設定をしてください。(2 段階認証プロセスを有効にする)
2段階認証設定を行っている場合はアプリパスワードを開きます。
アプリを選択でその他を選択します。適当に名前をつけます。
パスワードが生成されます。
生成されたパスワードを、1の設定の
:password => "Gメールパスワード",のGメールのパスワードの部分に設定します。3. 届いたメールを確認
これでアプリ側からサインアップすると、以下の様なメールが届くようになります。
Confirm my accountをクリックするログイン画面からログインが可能になります。メールで送られる文章は以下のファイルを編集することで可能です。
app/views/devise/confirmation_instructions.html.erbアカウントをロックする
1. lockable概要
アカウントの認証を一定回数間違うと、アカウントをロックするようにする機能です。
2. 設定
1. 設定ファイルの編集
以下ファイルを修正して、アカウントの認証を5回失敗します。
config/initializers/devise.rbDevise.setup do |config| # (省略)... # lock sessings config.unlock_strategy = :email config.maximum_attempts = 4 endするとアカウントがロックされてこのようなメールが届きます。
メールの中身は
app/views/devise/mailer/unlock_instructions.rbファイルを修正すれば変わります。2. 設定値について
- lock_strategy(ロック方法)
属性 説明 :failed_attempts 失敗回数によってロック。 :none ロックしない。
- unlock_strategy(ロック解除方法)
属性 説明 :time 指定時間でロックを解除する。 メールでロックを解除する。 :both :timeと:emailの両方。 :none 解除させない。
:noneを指定した場合、ユーザーのレコードのlocked_atカラムをnilにアップデートしたらロックが解除できます。
- unlock_in(ロック解除時間)
2時間で解除するなら
config.unlock_in = 2.hoursといった具合に指定。
- unlock_keys
アカウントをロックまたは解除するときに使用するキーを定義するらしいです。
config.unlock_keys = [:username]という感じで指定できます。
- maximum_attempts(失敗可能回数)
アカウントの認証を失敗して良い回数を指定します。
config.maximum_attempts = 4と指定した場合、4回目までは失敗しても大丈夫ですが、5回目を失敗した時点でアカウントがロックされます。セッションをタイムアウトする
1. timeoutable概要
一定時間活動がない場合にセッションをタイムアウトさせるのがtimeoutableです。
2. 設定
以下ファイルを修正するとタイムアウトまでの時間を指定できます。
以下の場合だと3分後にセッションがタイムアウトします。
デフォルトは30分だそうです。config/initializers/devise.rbDevise.setup do |config| (省略)... # timeout setting config.timeout_in = 3.minutes endその他の設定
1. ログイン後のページを変更する
ログインすると、デフォルトでは
root_urlに飛ばされます。
これをapp/views/pages/show.html.erbになるように修正します。
after_sign_in_path_forメソッドを追加します。ここにログイン後に遷移したいページを指定します。
あとsign_in_requiredも追加します。showページはログインしているユーザーだけにアクセスさせ、ログインしてない場合はログインページに遷移させます。app/controllers/application_controller.rbclass ApplicationController < ActionController::Base def after_sign_in_path_for(resource) pages_show_path end private def sign_in_required redirect_to new_user_session_url unless user_signed_in? end endPagesコントローラーに
before_actionを追加します。app/controllers/pages_controller.rbclass PagesController < ApplicationController before_action :sign_in_required, only: [:show] def index end def show end endこれでログイン後showページに遷移するようになります。
2. サインアップする際に登録するパラメーターを増やす
ユーザーを登録するときにデフォルトではEmailとパスワードだけですが、
ユーザー名も登録させたい、などの場合があると思います。まずはサインアップページにユーザー名を入力するエリアを追加します。
ユーザーのプロフィール変更ページ(views/devise/registrations/edit.html.erb)にも同様に追加しときます。app/views/devise/registrations/new.html.erb<h2>Sign up</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> # (省略)... <!-- ユーザー名を追加 --> <div class="field"> <%= f.label :username %><br /> <%= f.text_field :username %> </div> # (省略)... <%= render "devise/shared/links" %>次に
ApplicationControllerに以下を追加します。
テンプレートを変えて、ユーザー名を入力するようにしただけでは実際に登録されないからです。
詳しくはstrong_parametersについて調べてください。
簡単に言えばよく分からんパラメーターは渡せないようになってるので渡せるようにします。app/controllers/application_controller.rbclass ApplicationController < ActionController::Base protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? # (省略)... protected def configure_permitted_parameters devise_parameer_sanitizer.permit(:sign_up, keys: [:username]) devise_parameter_sanitizer.permit(:account_update, keys: [:username]) end end最後に
作ったプロジェクト全体は以下です。
cigalecigales/devise_rails6
- 投稿日:2020-04-04T23:02:55+09:00
MVCとは
MVC
Model(モデル)
View(ビュー)
Controller(コントローラー)
の頭文字です。
役割としては
Model(モデル)
ビジネスロジック(データ処理)を行う
View(ビュー)
表示処理を行う
Controller(コントローラー)
リクエストに応じて適切な処理を呼び出すMVCの流れ
ルーティング=>コントローラー
リクエストに対応したルーティングが読まれ、それに対応したコントローラーのアクションが動きます。
コントローラー=>モデル
コントローラーで呼び出したアクションをモデルを通してデータベースから必要なデータを呼び出します。
コントローラー=>ビュー
呼び出したデータをビューに受け渡し、受け取ったデータをWebページに表示させます。
- 投稿日:2020-04-04T22:42:29+09:00
[Rails]本番環境でSeed-fuを使う
本記事投稿のいきさつ
railsでアプリを作成している中で、Seed_fu Gem を使用しました。
その際、開発環境では上手くテーブルにデータを格納することができたのですが、本番環境で苦戦したため備忘録として書きます。
ここでは、自分が苦戦したEC2内でのコマンドのみを書きます。エラー(異なるディレクトリでコマンドを入力していたため)
自分が作成したアプリのディレクトリへ移動しコマンドを入力したところ
Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)と、mysplに接続ができないというエラーが発生。
これはコマンドを入力するディレクトリが誤っていたため発生したエラーでした。
実は開発環境とは異なり、本番環境ではcurrentディレクトリ内のデータが本番環境で動いているフォルダ群になります。
そのためcurrentディレクトリへ移動した後、再度コマンドを入力します。cd current rails db:seed_fuこれで大丈夫かと思われましたが、またしてもエラーが。
エラー(コマンド誤り)
Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfilelisten gem がないとのこと。
まさかと思いgemfileを確認したところちゃんと書かれていました。
調べたところ、seedでデータを投入する際は環境の指定をする必要があるようです。ということで以下を再度実行。rails db:seed_fu RAILS_ENV=production今度は成功しました。初めての経験で苦戦しましたが、とても勉強になりました。
ちなみに、test環境でも同じように以下を実行すれば大丈夫です。rails db:seed_fu RAILS_ENV=testおわり
最後まで見ていただきありがとうございました。
- 投稿日:2020-04-04T22:41:52+09:00
【rails】ユーザに紐づくデータをユーザ登録時に生成するための実装方法
新規ユーザ登録時に、ユーザに紐づくデータを生成するよう実装した。
具体的には、新規ユーザが登録されたときに、外部キーとしてuser_idを持つFolderモデルとTagモデルのデータが生成されるようにした。Gemfile
gem 'devise'devise導入
bundle Install rails g devise:install rails g devise UserDeviseControllerのカスタマイズ
※認証ユーザーのモデルはUser
rails g devise:controllers users # rails g devise:controllers [scope]app/controllers/users 以下に次のコントローラが作成される。
・confirmations_controller.rb
・omniauth_callbacks_controller.rb
・passwords_controller.rb
・registrations_controller.rb
・sessions_controller.rb
・unlocks_controller.rbルーティング設定
registration_controller.rbのルーティング設定を行う。
config/routes.rbRails.application.routes.draw do # devise_for :users devise_for :users, controllers: { registrations: 'users/registrations' }registration_controller設定
newアクションのコメントアウトを外す。
createアクションにデータを生成する処理を記述する。app/controllers/users/registrations_controller.rb# frozen_string_literal: true class Users::RegistrationsController < Devise::RegistrationsController # before_action :configure_sign_up_params, only: [:create] # before_action :configure_account_update_params, only: [:update] # GET /resource/sign_up def new super end # POST /resource def create super @default_folder = Folder.create( name: 'DefaultFolder', user_id: current_user.id ) Tag.create( name: 'Temporary', user_id: current_user.id, folder_id: @default_folder.id ) end # GET /resource/edit # def edit # super # end ...上記では、user_IDを外部キーとして持つFolderモデルとTagモデルのデータを生成している。
新規ユーザ登録により、登録したユーザーのuser_idを外部キーとして持つFolderモデルとTagモデルのデータが生成されるようになった。参考文献
- 投稿日:2020-04-04T22:28:22+09:00
[Docker] webコンテナからdbコンテナへの接続方法
docker-compose.yml:docker-compose.yml version: '3' services: db: container_name: local-mysql image: mysql:5.7 volumes: - ./db/mysql_data:/var/lib/mysql environment: MYSQL_DATABASE: 'local-mysql-db' MYSQL_PASSWORD: password MYSQL_USER: root ports: - "3306:3306" networks: - app-net web: container_name: "local-application" build: . command: rails s -p 3000 -b '0.0.0.0' volumes: - .:/fishingshares ports: - "3000:3000" depends_on: - db links: - db networks: - app-net networks: app-net: driver: bridge$ mysql -h local-mysql -u root -D local-mysql-db -p詳しく解説すると、
mysql -h [host名] -u [ユーザ名] -D [database名] -p
ということになる。以上のコマンドをwebコンテナ内で実行すると、mysqlに接続できる。
以上
- 投稿日:2020-04-04T22:08:31+09:00
selfを使って、undefined methodエラーを解消
はじめに
Modelで定義したメソッドがControllerで呼び出せなくなったとき、
selfを使って2パターンの解決法を見つけたため、記録として残しておく。今回は、helloという名前のメソッドをモデルで定義するという設定とする。
解決法1(特異メソッド方式)
post.rbclass Post def self.hello puts 'hello' end endメソッド名の前にself.をつけるやり方
ruby初学者なら何度も見たことのある形だ。
解決法2(特異クラス方式)
post.rbclass Post class << self def hello puts 'hello' end end endこちらでも、きちんとcontrollerで呼び出すことができた。
この場合だと、メソッド名の前にself.を書く必要もないし、class << self と end内であれば、
複数のメソッドを同じように定義できるのがメリットだ。post.rbclass Post class << self def hello puts 'hello' end def goodmorning # 二つ定義しても self つけなくて良い puts 'Good morning' end end endそもそも
ここからは、特異メソッド方式や特異クラス方式、selfについてもう一歩踏み込んだところまで理解したい方々に読んで欲しい。
class << selfの仕組み
特異メソッド
hello = 'hello' def hello.say puts hello + 'world' end hello.say #=> hello world another_hello = 'hello' another_hello.say #=> NoMethodError: undefined method `say' for "hello":Stringこのように、特異メソッド(hello.say)とは、オブジェクト(hello)に直接固有のメソッドを定義したものだ。
なので、上記のように別のオブジェクト(another_hello)には使うことはできない。これに加えて、もう一つ特異メソッドを定義する方法がある。
hello = 'hello' class << hello def say_world puts "#{self}, world" end end hello.say_world #=> hello, worldこれも << hello というところで helloオブジェクトの特異クラスを引き出しており、あくまでオブジェクトに対しての特異メソッドの定義となっている。
つまり、一番最初のこの部分、
class Post class << selfクラス定義のコンテキストでの self とは、Classクラスのインスタンス Post class だ。class Post とはクラスを定義するときの記法だが、Ruby の中での理解としては、Class クラスのオブジェクトを生成し Post というグローバルな定数へ代入している。
なので、以下の2つの記述は同じ意味だ。
class Post def hello puts 'hello' end end post = Post.new post.hello #=> helloPost = Class.new do def hello puts 'hello' end end post = Post.new post.hello #=> hello定数 Post に入っている Class クラスのオブジェクトへ特異メソッドを定義してみる。
Post = Class.new do def hello puts 'hello' end end def Post.bye puts 'good bye' end Post.new.hello #=> hello Post.bye #=> good byePost.bye という呼出しができた。これはクラスメソッドの呼出しと同じ。
つまり、クラスメソッド Post.bye というのは、Post に入っている Class クラスのオブジェクトへの特異メソッドの定義として読むことができる。これにオブジェクトの特異クラスの引き出しの記法 << をあわせて考えると、一番冒頭のコードでやろうとしていることがわかってくる。
特異クラス
クラスメソッドの定義は特異メソッド形式と特異クラス形式があった。
特異メソッド形式で定義した def Post.bye を特異クラス形式へ書きかえてみる。
Post = Class.new do def hello puts 'hello' end end class << Post def bye puts 'good bye' end end Post.new.hello #=> hello Post.bye #=> good byeさらに書き換えると下記にようになる
class Post def hello puts 'hello' end class << self def bye puts 'good bye' end end end Post.new.hello #=> hello Post.bye #=> good byeクラスメソッドのための記法があるわけではなく、特異メソッドという仕組みを使って巧みにクラスメソッドが実現されていることがわかった。
おわりに
今まで何気なく使っていたself。
というかなんとなく理解できたようでできていなかった部分だった。私が参考にした、というかこの記事はほぼ下記の記事のコピペなので、私の記事がわかりにくかったという方は、ぜひ下のURLの記事を参考にして欲しい。
参考記事
https://magazine.rubyist.net/articles/0046/0046-SingletonClassForBeginners.html
- 投稿日:2020-04-04T21:50:07+09:00
Active jobでの非同期処理
非同期処理とActive job
非同期処理とはAという作業と同時に別のBという作業を行うこと(マルチタスク、並列作業)。通常はAという作業の後にBという作業をするように一つ一つしか行えない非同期処理と同期処理の実装パターンと特徴。しかしrailsではActive jobを用いて必要なツールのインストールを行って実行する。ここではsidekiqの例をあげます。
準備、設定
sidekiqによる非同期処理を行うにはredisサーバーが起動していないといけないので、まずredisをマシーンにインストール。
↓
redisのサーバーを起動
↓
gemを使ってsidekiqをアプリにインストール
↓
bundle exec sidekiqでsidekiqを起動
↓
config/environmentsでその環境でsidekiqを使用することを記述ジョブを作成、実行
ジョブとはメインの処理とは別に並行的にバックグラウンドで行わせる処理のこと。ジョブを実行するにはジョブを定義し、読み出さないといけない
rails g job ~でジョブを記述するためだけのファイルを作る
↓
app/jobs/~job.rbにジョブの内容を記述
↓
非同期処理を行いたいアクションの中でジョブの読み出しを行うことで非同期処理される厳密にはジョブの読み出しをするとすぐに実行されるわけではない。予約をするイメージ。忙しくてできない時は開始されないし、処理できる状態になれば実行されるって感じ
参考文献
現場で使える Ruby on Rails 5速習実践ガイド
Railsガイド
Rails 4.2で導入されたActive Jobを使ってみよう
- 投稿日:2020-04-04T21:37:57+09:00
Rails5.2以降でのcredentials.yml.encの読み込み方
PAYJPのAPIが読み込めず、クレジットカードの登録ができない
PAYJPを使ってクレジット決済の導入をしたところ、
クレジットカードの登録をしようとすると下記のようなエラーがでましたPAYJPの導入はQiita記事参照してください
RailsでPayjpを使った購入機能を実装する
PAYJPと連携するためのキーが読み込めていない
Rails5.2以降からはAPIのキーなど環境変数に設定するためのものは
credentilas.yml.encに記載するようになりました詳しくはこちら
Rails5.2から追加された credentials.yml.enc のキホンこちらを参照に
credentials.yml.encに記載したはず・・・
もしかしてちゃんと読み込めていなそう?
rails cで調べてみるターミナルrails c [1] pry(main)> Rails.application.credentials.PAYJP_PRIVATE_KEY => "sk_test_XXXXXXXXXXXXXXXXXXXX"どうやらちゃんと読み込めるのでちゃんと環境変数は設定できていそう
credentials.yml.encを読み込む書き方が必要どうやら他の参考記事の通りに
card_controller.rbdef pay Payjp.api_key = ENV["PAYJP_PRIVATE_KEY"] 中略と書いてしまっていたのが原因。
ENV["PAYJP_PRIVATE_KEY"]で読み込んでいたのはcredentials.yml.encを使っていないRails.5.2以前の書き方で、credentials.yml.encを読み込むためにはcard_controller.rbdef pay Rails.application.credentials.dig(:payjp, :PAYJP_PRIVATE_KEY) 中略と書かないと
credentials.yml.encに記載した環境変数の読み込みができないということです。
記事によってRailsのバージョンが違うので安易にそのまま記載するのはいけないですね。。。。参考
- 投稿日:2020-04-04T21:17:04+09:00
rails 通知機能
はじめに
今回は通知機能を作成しました。
こちらの記事を参考にさせていただいております。
https://qiita.com/nekojoker/items/80448944ec9aaae48d0a
https://qiita.com/yuto_1014/items/2db1dd4fcd7945b980f7作るもの
コメント・いいね・フォローされた時に通知を作成して確認・削除できる機能を作成します。すでに上記の機能はあるものとして作成してます。どれか一つでも機能があればOK。参考に過去に投稿したコメント・フォロー機能についてリンクを載せておきます。
コメント機能:https://qiita.com/E6YOteYPzmFGfOD/items/ef776d34908872ea19f7
フォロー機能:https://qiita.com/E6YOteYPzmFGfOD/items/ec0492b509962c3b7ae4ER図
参考に今回作成する通知機能のER図です。(今回関係ないタグ機能がありますが無視してください。)
作成の流れ
1.Notificationモデルの作成
2.各モデルの関連づけ
3.通知メゾットの作成
4.通知機能をコントローラーへ埋め込む
5.通知一覧画面と通知削除作成モデルの作成
rails g model Notification visitor:references visited:references micropost:references comment:references action:string checked:booleandb/migrate/class CreateNotifications < ActiveRecord::Migration[5.1] def change create_table :notifications do |t| t.references :visitor, foreign_key:{ to_table: :users }, null: false t.references :visited, foreign_key:{ to_table: :users }, null: false t.references :micropost, foregin_key: true t.references :comment, foregin_key: true t.string :action, null: false t.boolean :checked, null: false, default: false t.timestamps end end endvistor_id
→通知するユーザー(いいね、コメント、フォローする側)
visted_id
→通知をうけるユーザー
rails g model カラム名:references とすると自動で
・カラム名にidをつけた形でカラムを作成。
・インデックス付与。
foreign_key:true
・外部キー制約を付与
to_table: :user
・visitorとvisitedカラムの参照テーブルを指定してます。
nul: false
・空の状態で保存できなくする。モデルの関連付け
app/models/user.rbhas_many :active_notifications, foreign_key:"visitor_id", class_name: "Notification", dependent: :destroy has_many :passive_notifications, foreign_key:"visited_id", class_name: "Notification", dependent: :destroyapp/models/micropost.rbhas_many :notifications, dependent: :destroyapp/models/comment.rbhas_many :notifications, dependent: :destroyapp/models/notification.rbclass Notification < ApplicationRecord default_scope -> { order(created_at: :desc) } belongs_to :visitor, class_name: "User", optional: true belongs_to :visited, class_name: "User", optional: true belongs_to :micropost, optional: true belongs_to :comment, optional: true validates :visitor_id, presence: true validates :visited_id, presence: true ACTION_VALUES = ["like", "follow", "comment"] validates :action, presence: true, inclusion: {in:ACTION_VALUES} validates :checked, inclusion: {in: [true,false]} end各オプション
foreign_key:"vistor_id", class_name: "Notification"
→関連づけるカラム名とテーブル名を指定します。
dependent: :destroy
→投稿が削除された際に関連付いている通知を削除。
default_scope -> { order(created_at: :desc) }
→通知が新しい順に並ぶ。
optional: true: nullの値を許可する。
inclusion
→保存できる値を制限しています。通知メゾットの作成
いいね
app/models/micropost.rbdef create_notification_like!(current_user) temp = Notification.where(["visitor_id = ? and visited_id = ? and micropost_id = ? and action = ? ", current_user.id, user_id, id, 'like']) if temp.blank? notification = current_user.active_notifications.new( micropost_id: id, visited_id: user_id, action: 'like' ) if notification.visitor_id == notification.visited_id notification.checked = true end notification.save if notification.valid? end end始めに通知が作成済みでないか確認してます。(何度も作成しないように)登録済みの場合と自分の投稿に対するいいねの場合は通知を確認済みとして作成します。(checkedをtrueにする。)active_notificationsはuserモデルでhas_many関連付けを行った際に名付けた関連名です。
コメント
app/models/micropost.rbdef create_notification_comment!(current_user, comment_id) #同じ投稿にコメントしているユーザーに通知を送る。(current_userと投稿ユーザーのぞく) temp_ids = Comment.where(micropost_id: id).where.not("user_id=? or user_id=?", current_user.id,user_id).select(:user_id).distinct #取得したユーザー達へ通知を作成。(user_idのみ繰り返し取得) temp_ids.each do |temp_id| save_notification_comment!(current_user, comment_id, temp_id['user_id']) end #投稿者へ通知を作成 save_notification_comment!(current_user, comment_id, user_id) end def save_notification_comment!(current_user, comment_id, visited_id) notification = current_user.active_notifications.new( micropost_id: id, comment_id: comment_id, visited_id: visited_id, action: 'comment' ) if notification.visitor_id == notification.visited_id notification.checked = true end notification.save if notification.valid? endコメントの場合は同じ投稿に対してコメントしているユーザーにもコメントを送るようにします。マイクロポストの投稿者には必ず通知が1件作成されるようにします。
「where.(micropost_id: id)」
→同じ投稿にコメント
「where.not("user_id=? or user_id=?", current_user.id,user_id)」
→コメントしたユーザーと投稿者は除く。
distinct
→複数回コメントしている人も通知は一件のみにする
select(:user_id)
→user_idのみ取得しています。取得したuser_idを下で定義している通知作成メゾットに渡して繰り返し通知を作成します。
フォロー
app/models/user.rbdef create_notification_follow!(current_user) #すでに通知が作成されているか確認 temp = Notification.where(["visitor_id = ? and visited_id = ? and action = ? ",current_user.id, id, 'follow']) if temp.blank? notification = current_user.active_notifications.new( visited_id: id, action: 'follow' ) notification.save if notification.valid? end end通知機能を各コントローラーへ埋め込み
各通知メゾットをコメント等のアクションを起こした際に起動するようにコントローラー内へ埋め込んでいきます。
コメント
app/controlloers/comments_controller.rbdef create @comment = current_user.comments.build(comment_params) @comment.micropost_id = params[:micropost_id] if @comment.save flash[:success] = 'コメントしました' #通知機能用 @micropost=@comment.micropost @micropost.create_notification_comment!(current_user, @comment.id) #ここまで通知機能 redirect_to @comment.micropost else comments_get render template: 'microposts/show' endフォロー
app/controllers/follow_controlloer.rbdef create @user =User.find(params[:follow_relationship][:following_id]) current_user.follow(@user) #通知機能追加 @user.create_notification_follow!(current_user) respond_to do |format| format.html {redirect_back(fallback_location: root_url)} format.js end endいいね
app/controllers/like_controlloer.rbdef create @user = current_user @micropost = Micropost.find(params[:micropost_id]) current_user.like(@micropost) #通知機能追加 @micropost.create_notification_like!(current_user) respond_to do |format| format.html { redirect_back(fallback_location: root_url) } format.js end end通知一覧画面と通知削除作成
まずは通知ページ表示ようのコントローラ、ルーディングの設定です。
rails g controller notificationsconfig/routes.rbresources :notifications, only: [:index, :destroy]app/controllers/notifications_controlloer.rbdef index @notifications = current_user.passive_notifications #通知画面を開くとcheckedをtrueにして通知確認済にする @notifications.where(checked: false).each do |notification| notification.update_attributes(checked: true) end end def destroy @notifications =current_user.passive_notifications.destroy_all redirect_to notifications_path end続いて通知一覧画面の作成です。
app/views/notifications/index.html.erb<h3 class="text-center">通知</h3> <%= link_to "通知削除", notification_path(@notifications), method: :delete ,class: "fas fa-trash" %> <% if @notifications.exists? %> <div class="notification-index"> <%= render @notifications %> </div> <% else %> <p>通知はありません</p> <% end %>app/views/notifications/_notification.html.erb<div class="notification-view"> <%= notification_form(notification) %><span class="moderate-font"><%= " (#{time_ago_in_words(notification.created_at)}前)" %></span> <br> <% if !@comment.nil? %> <p class="notification-comment"><%= @comment %></p> <% end %> </div>いいね、フォロー、コメントの通知によって表示する内容を変更するために、
ヘルパーメゾットを作成してviewで呼び出します。ruby/app/helprs/notifications_helper.rbmodule NotificationsHelper def notification_form(notification) #通知を送ってきたユーザーを取得 @visitor = notification.visitor #コメントの内容を通知に表示する @comment = nil @visitor_comment = notification.comment_id # notification.actionがfollowかlikeかcommentかで処理を変える case notification.action when 'follow' #aタグで通知を作成したユーザーshowのリンクを作成 tag.a(notification.visitor.name, href: user_path(@visitor)) + 'があなたをフォローしました' when 'like' tag.a(notification.visitor.name, href: user_path(@visitor)) + 'が' + tag.a('あなたの投稿', href: micropost_path(notification.micropost_id)) + 'にいいねしました' when 'comment' then #コメントの内容と投稿のタイトルを取得 @comment = Comment.find_by(id: @visitor_comment) @comment_content =@comment.content @micropost_title =@comment.micropost.title tag.a(@visitor.name, href: user_path(@visitor)) + 'が' + tag.a("#{@micropost_title}", href: micropost_path(notification.micropost_id)) + 'にコメントしました' end end以上となります。
だいぶ長くなりましたがここまでお読みいただきありがとうございました!!
- 投稿日:2020-04-04T20:18:24+09:00
【VScode】なぜか自動保存できねえ時の解決法、そしてパーミッション関連のことも【備忘録】
【VScode】なぜか自動保存できねえ時の解決法
設定でAfterdelayなどにしてファイルの自動保存機能をつけているVScodeで開発しているときに、
以下のようなエラーが出て、自動が保存ができないような事態の解決法を紹介します
[アクセス権限が不十分です。[Sudo 権限で再試行]を選択してスーパーユーザーとして再試行してください。]
と出ております。
つまり、現在、Sudoユーザーとして、ファイルにアクセスできていない状態なので、
・スーパーユーザーにモードを変える
・ファイル、あるいはフォルダのアクセス権限(パーミッション)を変更する
方法があります。今回は後者の手段を用いて、このエラーを解決します。
terminalsudo chmod -R 777 ファイルのディレクトリ位置(Ex. /Users/kazuhitoNakayama/Desktop/index.html.erb)この一行で、全てのユーザーに対してのファイルの実行権限を与えられるので、
上記エラーが解決されます。解説
chmodとは
?ファイルやディレクトリのアクセス権限を変更する際に使うコマンドです
(UNIX系のOSで使用できます。Ex.Macなど)使い方は、chmod [オプション] [与える権限] [対象のファイル、ディレクトリのディレクトリ位置]
オプションは私は普段 「-R」を使ってますが、他にもたくさんあるので
「chmod オプション」とかでお調べください!777とは
?先ほどchmod [オプション] [与える権限] [対象のファイル、ディレクトリのディレクトリ位置]
とお伝えしました。
ここでの[与える権限]について説明すると、アクセス権の記法には2種類あり、
・アルファベット
・数字
があります。アルファベットでの記法
あるフォルダの中に入って「ls -l」をすることで、各ファイルの権限を参照できるので、みてみると、
アルファベットの羅列が見えます。terminal$ ls -l drwxr-xr-x 2 user_name group 68 8 30 15:53 css -rw-r--r-- 1 user_name group 0 8 30 15:53 index.htmlそれぞれの文字は
r:(read)読み込み権限
w:(write)書き込み権限
x:(execute)実行権限
こんな風に対応しております。例えば上の、drwxr-xr-xをいかに読むかというと
d/rwx/r-x/r-x
↑こんな風に区切られます。また、それぞれの区切りの中で、
ディレクトリなのかファイルなのか/所有者に対する権限/グループに対する権限/その他の者に対する権限/
という風に区切られており、それぞれの対象に対して、権限をr・w・xで与えます。
(1文字目に関してはdならば当該ファイルはディレクトリとわかります。-ならばファイルです。)よって、
d/rwx/r-x/r-xは
ーディレクトリであり
ー自分に対して、読み込み、書き込み、実行の全ての権限が与えられており、
ーグループに対して、読み込み、実行権限が与えられており、
ーその他の者に対して、読み込み、実行権限が与えられています。数字の記法
シンプルで、上記、r・w・xがそれぞれ数字に変わるだけですね!
r(読み込み権限):4
w(書き込み権限):2
x(実行権限):1そして今回は3つの数字で権限対象、与える権限を表現します
例えば、7311つ目の数字:所有者
2つ目の数字:グループ
3つ目の数字:その他の者
という風になっているので、731は、
所有者は、7=4+2+1なので読み込み、書き込み、実行権限が与えられ、
グループは、3=2+1なので書き込み、実行権限が与えられ、
その他の者は、1なので実行権限が与えられます。総括
冒頭の振り返りで行くと
terminalsudo chmod -R 777 ファイルのディレクトリ位置(Ex. /Users/kazuhitoNakayama/Desktop/index.html.erb)これによりindex.html.erbのファイルのパーミッションを、誰でも実行できるようにしたので、自分がファイルを変更してもパーミッションで怒られることがなくなったのです!
パーミッションは自分的に忘れやすい!
必覚!
- 投稿日:2020-04-04T20:12:08+09:00
【AWS】デプロイ関連で詰まった箇所
デプロイ
bundler: failed to load command: puma
解決法
AssetsPrecompile関連エラー
Precompile = 1つのファイルにまとめる(圧縮する作業)
ターミナル# アセッツプリコンパイル $ bundle exec rake assets:precompile RAILS_ENV=production解決法
yarnのインストール
ターミナル$ curl -o- -L https://yarnpkg.com/install.sh | bash $ source .bashrc # 反映 $ yarn -V # yarnインストールconfig/environments/production.rbconfig.assets.js_compressor = :uglifier # 変更 config.assets.js_compressor = Uglifier.new(harmony: true)config/environments/production.rbconfig.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # 変更 config.public_file_server.enabled = truecapistrano関連エラー
SSHKit::Runner::ExecuteError
config/deploy/production.rbset :ssh_options, { keys: %w(~/.ssh/id_rsa) } # 変更(例) set :ssh_options, { keys: %w(/Users/hogehogehoge/.ssh/hogehoge_key_rsa) } # ①ssh接続の秘密鍵にはデフォルトでid_rsaを探しにいくため、使用する秘密鍵の名前に変更 # ②使用する秘密鍵の場所までのpathを通すGem::LoadError : "ed25519 is not part of the bundle. Add it to your Gemfile."
Gemfilegem 'ed25519' gem 'bcrypt_pbkdf'ターミナル$ bundle使ってるパソコンの環境によってエラーが出たり出なかったりするらしい。
bundle exec cap production deploy(git:checkで落ちる)
Gemfile# ssh形式で接続 set :repo_url, "git@github.com:hogehogehoge.git" # 変更 # https形式で接続 set :repo_url, "https://github.com/hoge/hogehogehoge.git" set :git_http_username, "gitに登録しているusername" set :git_http_password, "gitに登録した公開鍵パスワード"bundle exec cap production deploy(deploy:symlink:linked_filesで落ちる)
master.key database.yml settings.ymlの配置を変更
/var/www/rails/アプリケーション/shared/config配下に移動bundle exec cap production deploy(yarn:installで落ちる)
nodeのバージョンが古かったらしい
bundle exec cap production deploy(nginx:restartで落ちる)
ターミナル$ ps aux | grep nginx →プロセスを確認してnginxが起動していないか確認 $ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: [emerg] open() "/var/www/rails/アプリケーション名/log/nginx.error.log" failed (2: No such file or directory) →nginx.conf(nginx設定ファイル)は問題ない →/var/www/rails.......にnginx.error.logがあるか確認してくださいと言われるが、nginx.error.logは/var/www/rails/アプリケーション名/sheard/log/nginx.error.logに配置したいため/etc/nginx/sites-enabled/アプリケーション名を書き換えるターミナル[~]$ cd /etc/nginx/sites-enabled $ sudo vim アプリケーション名sites-enabled/アプリケーション名error_log /var/www/rails/アプリケーション名/sheard/log/nginx.error.log access_log /var/www/rails/アプリケーション名/sheard/log/nginx.access.logターミナル$ sudo service nginx start → 起動確認bundle exec cap production deploy(全て通ったのにブラウザで画面表示されない)
nginx.error.log
puma.error.log
production.log
を確認してエラーが出ているか確認。
nginx.error.log場所確認ターミナル/etc/nginx/sites-enabled/アプリケーションvimで開いて中身を確認
自分の場合は/var/www/rails/アプリケーション名/shared/log/nginx.error.logに配置注意))あくまで読み込むのは
/etc/nginx/nginx.confであるためnginx.confにinclude /etc/nginx/sites-enabled/*のようにincludeされているかを確認。nginx関連で見るファイルは基本的にnginx.confとsites-enabled配下のファイルの2つ
puma.error.log場所確認ローカル環境の
config/deploy.rbset :puma_error_log の後にパスが記述自分の場合は
/var/www/rails/アプリケーション名/shared/log/puma.error.logに配置error.logは他の開発者とも共有しやすいようにshared(シェアード)配下に置くのが一般的なのでそのように設定しておくのがベター
production.logもshared配下に配置今回のエラーは
/etc/nginx/sites-enabled配下にcapistranoでデプロイ以前に作成した.confファイルが残っておりそちらのファイルがnginx.confにinclude
されており、間違った設定が適用されていたことが原因だった。
sites-enabled/.confを削除し、nginx.confに記載のincludeを削除することで解決。AWSアーキテクチャ関連
複数サーバーにcapistranoでデプロイしたい時
元々あるWebサーバーからイメージの作成、EC2インスタンスを起動し、ローカルの
config/deploy/production.rbrole app, 'ユーザー名@パブリックIP' role web, 'ユーザー名@パブリックIP' role db, 'ユーザー名@パブリックIP'を追加して
ターミナル$ bundle exec cap production deployで、OK
SSL証明書発行(albに付与)
参考記事の7まで進めたら、最下部の
新しい ACM 証明書をリクエストをクリック。
*.ドメイン名とドメイン名を記入し、次へ
検証方法はDNS
証明書が認証されるまで待って、リスナー画面へ戻り、発行された証明書を選択して保存※ Udymy
AWS:ゼロから実践するAmazon Web Services。手を動かしながらインフラの基礎を習得のセクション8の62.CloudFrontを設定して高速化しようが参考になる。ローカルのconfig/deploy/templates/nginx_conf.erb# bundle exec rails g capistrano:nginx_puma:configをした時に自動生成されるファイル # デプロイすると、このファイルを元にEC2インスタンスの/etc/nginx/nginx.confや/etc/nginx/sites-enabled/アプリケーションに設定が反映される location @puma_<%= fetch(:nginx_config_name) %> { ... <% if fetch(:nginx_use_ssl) -%> proxy_set_header X-Forwarded-Proto https; <% else -%> proxy_set_header X-Forwarded-Proto http; <% end -%> ↓ 変更 proxy_set_header X-Forwarded-Proto https;この作業をしないとPOSTリクエストがhttpsの時はできないので要注意
https://www.cotegg.com/blog/?p=1850Git
プッシュ済みコミット取り消し、ローカルの状態をプッシュ
プッシュ済みコミット取り消し
ターミナル$ git log 戻したいコミットの地点のIDをコピー $ git reset --hard IDローカルの状態をプッシュ
リモートの方がコミットが進んでるためrejectするのでターミナル$ git push -f origin master ローカルの状態を最新としてリモートを更新チーム開発時の
-fは要注意
- 投稿日:2020-04-04T19:34:17+09:00
初心者の気持ちがわかる!! Railsチュートリアル第1章(1.3)
初心者の気持ちがわかる!!Railsチュートリアル 1.3
Railsチュートリアル (1.3)
1.3 最初のアプリケーション
さっそくWebページに「hello,world!」を表示してみましょう。
と、その前に今回のRailsプロジェクトで使うためのenvironmentディレクトリを作成しましょう。(Cloud9 IDEを使用している人は元からあるので不要)
リスト1.2$ cd # ホームディレクトリに移動する $ mkdir environment # 'environment' ディレクトリを作成する $ cd environment/ # 'environment' ディレクトリに移動する実際に使いながら学びたい人は
ドットインストールのUNIXコマンド入門 一般ユーザー編 (全24回)
をやってみよう。Q.UNIXコマンドとは?
A.
簡単に言うと、マウスで視覚的にフォルダを開いたり削除したりしているものをターミナルにコマンドを入力することで実行することが出来るもの。次にリスト1.1でインストールしたRailsを使って最初のアプリケーションを作成します。
リスト1.3$ cd ~/environment (cdのディレクトリに移動) $ rails _5.1.6_ new hello_app (hello_appディレクトリを作成)environmentディレクトリの中にhello_appというディレクトリが作成され、hello_appの中に大量のディレクトリができている。これはrails5.1.6を使うことで一気に生成されている。(時短、効率化に繋がっているということ?)
本来Webアプリケーションのディレクトリをどう構成するかは自由であるが、このように標準化されていることで、他の開発者が見ても、コードが読みやすくなっている。1.3.1 Bundler
アプリケーションに必要なgem(アプリ作成に必要な便利グッツ?)をインストールします。
その際、リスト1.3で生成されたhello_appの中にあるgemfileを変更しインストールしていきます。それをbundlerを実行するといいます。(terminalにはbundle installと記載し実行する。)実は既にrailsアプリケーションを新規作成すると自動でbundlerを実行し、既存のgemfile(リスト1.4)をインストールしている。
それをリスト1.5のように書き換え、bundlerを実行し、インストールする。
リスト1.5以下のように書き換えるsource 'https://rubygems.org' gem 'rails', '5.1.6' gem 'puma', '3.9.1' gem 'sass-rails', '5.0.6' gem 'uglifier', '3.2.0' gem 'coffee-rails', '4.2.2' gem 'jquery-rails', '4.3.1' gem 'turbolinks', '5.0.1' gem 'jbuilder', '2.6.4' group :development, :test do gem 'sqlite3', '1.3.13' gem 'byebug', '9.0.6', platform: :mri end group :development do gem 'web-console', '3.5.1' gem 'listen', '3.1.5' gem 'spring', '2.0.2' gem 'spring-watcher-listen', '2.0.1' end # Windows環境ではtzinfo-dataというgemを含める必要があります gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]ターミナルで以下をbundlerを実行
$ cd hello_app/ (hello_appディレクトリへ移動) $ bundle install$ bundle updateを実行する。
1.3.2 rails server
本文の写真を見ながら、新しいターミナルを開き、サーバーを起動します。
(hello_appディレクトリに移動するのを忘れないように!)リスト1.6$ cd ~/environment/hello_app/ $ rails serverここで私はpreviewボタンがないじゃないか〜!!と思いました。
画面幅を広げると出てきますよ 笑
演習
1.$ ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]ruby 2.6.3である。
2.$ rails -v Rails 5.1.61.3.3 Model-View-Controller (MVC)
Railsアプリケーションの全体的な仕組みについてざっくり見ておきます。
後で、しっかり理解しまーす。1.3.4 Hello,world!
hello,world!を表示させよう!
コントローラーアクションを追加することで表示させてみます。まずコントローラーの確認
$ ls app/controllers/*_controller.rb app/controllers/application_controller.rb(結果一つしかない)app/controllers/の中にある~~controller.rbというファイルを表示して〜、とお願いしている。
今は一つしかありません。それではapp/controllers/application_controller.rbを開いて記入してみる。
ファイルは以下の場所にあります。
以下のように記載する。
リスト1.7(app/controllers/application_controller.rb)class ApplicationController < ActionController::Base protect_from_forgery with: :exception def hello render html: "hello, world!" end end簡単に説明すると、defの右にある”hello"が呼び出されると、renderメソッドにより「hello,world!」をテキストとして表示してください。って感じです。
現状は書き方など深く理解しなくてもいいですよ〜
(defで定義した"hello"はなんでもいい"world"でもなんでもいい。定義したものを呼び出す時のために名前をつけている。)それでは先ほどサーバーで呼び出したデフォルトページを表示するのではなく、定義したものを呼び出して「hello,world!」を表示してみます。
そのために、Railsのルーター (router) を編集します。
リスト1.9(config/routes.rb)Rails.application.routes.draw do root 'application#hello' end上記の内容はrootURLにアクセスした場合、applicationコントローラーのhelloというアクションを呼び出し表示する。
以上で先ほどのdefaltページでリロードボタンを押すか[command+r]を押すとページが更新され、「hello,world!」が表示される。
演習
1.解答
1.(app/controllers/application_controller.rb)class ApplicationController < ActionController::Base protect_from_forgery with: :exception def hello render html: "hola, mundo!" end end上記のようにアクション内容を書き換える
2.解答
2.(app/controllers/application_controller.rb)class ApplicationController < ActionController::Base protect_from_forgery with: :exception def hello render html: "¡Hola, mundo!" end end3.解答
3.(app/controllers/application_controller.rb)class ApplicationController < ActionController::Base protect_from_forgery with: :exception def hello render html: "goodbye, world!" end end3.(config/routes.rb)Rails.application.routes.draw do root 'application#goodbye' end上記のように書き換える。
筆者コメント
普段文章を書かないせいか、理解不足のせいかなかなか時間がかかります。どこまで理解して、どこまで書こうか、線引きが曖昧なのが問題??
しかし、まとめるには今まで以上の理解が必要で、なんとなくわかった気になって進んでしまうのが防げるのでいいかなと思っています。
とりあえず出来るところまでやってみよう!!
- 投稿日:2020-04-04T19:25:09+09:00
railsで行うページネーション
やり方
kaminariをgemでインストール
↓
ページ移行を使うviewのアクションの中で、ページネーションを実施
↓
viewの方でページ移行を記述
・今どのページか
・他のページに移動するためのリンク
・全部でデータ数はどれだけかを示すデザイン
bootstrapをパーシャルテンプレートを追加する。パーシャルテンプレートとは、renderのためのテンプレートで共通で、使用することでアプリ内でデザインを統一できる。
1ページの表示件数を変更する
perスコープを使用する
kaminariの設定
デフォルトで1ページの表示件数を変更したければ、config/initializers/kaminari_config.rbで設定をする
参考文献
- 投稿日:2020-04-04T18:45:40+09:00
素人がWebサービスを自分で作る備忘録(前準備編)1
この記事の目的
この記事は自分が初めてWebサービスを作るにあたっての備忘録的な意味合いが強いです。
その為、エンジニアリング経験者の方にとっては特に為になる内容ではないと思われますが、ズブの素人が初めてサービスを開発しようと思ってからの成長日記だと思って見てもらえると嬉しいです。また、同じような未経験者の方がWebサービスを開発しようとしたとき、何かの手助けになれれば嬉しいと思います。
現在のレベル
・Progate学習済み(HTML&CSS、Javascript、React、Ruby、RubyonRails、Git、CommandLine、SQL)
・Railsチュートリアル 二周個人でWebサービスを作る際にはまず、何のために作るのか決めそれに適した基礎知識を習得する必要が出てきます。
今回は、第一の目標として今まで学習してきた内容のアウトプット、第二の目標はモダンなIT企業へのジョブチェンジするためのポートフォリオ作成ということで行っていきます。
その為、Web業界で今現在多く使われているRailsを選択しました。
Rails学習のバイブルとして多くに人に利用されているRailsチュートリアルですが、これを内容を理解するためには学習のための学習が必要となってくる(Reactを除く)。その学習にはProgateがおすすめ!Webサービスの企画
上記の学習を終えたところで実際に、Webサービスの開発に入っていきます
しかし、いきなりコードを書けと言われて書くことはできないはずです!なぜなら何を作ろうとか、どのような機能を持たせようとか、そういったところの話を先にやらないと作れるわけがないんです!1. 何のために作成するのか
2. どのような物を作るのか
3. どういった機能を盛り込むのかこのようなところから考えていきたいと思います。
1に関しては上でも書いた通り、知識のアウトプット、ポートフォリオ作成です。
2に関しては
・今まで学んだ知識 +αで作れる物(大きなことを言うと後で後悔しそうなので)
・未経験からの転職で有利になりそうな機能を実装すること
・自分が作りたい物(コロナとかで外に出れないので他の人と繋がれるサービスとか)を条件とします。
3に関しては・ユーザー管理機能
・投稿機能
・投稿一覧、投稿詳細機能
・画像ファイルアップロード機能
・ページネーション機能or無限スクロール機能
・DBテーブルのリレーション管理
・単体、統合テスト
+αの内容
・ReactによるJavascriptライブラリ
を取り入れていこうと思います。次では具体的なサービスの形を考えていくことについて書いていこうと思います。
- 投稿日:2020-04-04T18:26:19+09:00
【Rails】enum_helpを用いてセレクトボックスを作成
はじめに
こんな感じのフォームを作ります。
観測した限りでは一番シンプルな書き方です。問題などございましたらご指摘をお願いします。環境
ruby 2.5.3
Rails 5.2.4.2
enum_help (0.0.17)
rails-i18n (5.1.3)作成法
enumの設定
models/user.rbclass User < ApplicationRecord validates :name, presence: true, length: { maximum: 30 } validates :description, length: { maximum: 300 } validates :privacy, presence: true enum privacy: { published: 0, closed: 1 } endbooleanにしていないのは、今後限定公開などの機能を追加することを想定しているためです。
このようにenumを設定することで、データベースでは数値の値を文字列として擬似的に扱うことができます。
また、この値は文字列でもシンボルでも指定することができます。user.privacy => "closed" user.privacy = 0 => 0 user.privacy => "published" user.privacy = :closed => :closed user.privacy => "closed"i18nの設定
Gemfilegem 'rails-i18n' gem 'enum_help'必要なgemを記述して、
$ bundle installでインストールします。
そして、application.rbに以下の設定を追加します。config/application.rbmodule TestApp class Application < Rails::Application # 言語・タイムゾーンを日本に設定 config.i18n.default_locale = :ja config.time_zone = 'Tokyo' config.active_record.default_timezone = :local # config/locales/配下の全てのrb, ymlファイルを読み込み対象とする config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s] end end
config/locales配下に日本語化用のファイルを用意します。(ファイルの場所や名前は自由)config/locales/models/ja.ymlja: activerecord: models: user: ユーザー attributes: id: ID created_at: 作成日時 updated_at: 更新日時 user: name: 名前 description: 自己紹介 privacy: 公開設定 enums: user: privacy: published: 公開 closed: 非公開formの作成
form.html.slim= form_with model: @user, local: true do |f| = f.label :name = f.text_field :name br = f.label :description = f.text_area :description br = f.label :privacy = f.select :privacy, User.privacies_i18n.invert br = f.submitこんな感じです。ポイントはセレクトボックスの値です。
= f.select プロパティ名, 選択肢の配列とすればいいので、= f.select :privacy, [['公開', 0], ['非公開', 1]]これでも可能ですが、なんか嫌です。モデルと関連付けたい。
そこで、gemの出番です。'enum_help'によってそれぞれ以下のようなインスタンスメソッドとクラスメソッドが使えるようになります。user.privacy_i18n => "公開" User.privacies_i18n => {"published"=>"公開", "closed"=>"非公開"}これを利用して
[[表示される選択肢, 渡される値], [表示される選択肢, 渡される値]]の形の配列に置き換えます。User.privacies_i18n.map { |k, v| [v, k] } => [["公開", "published"], ["非公開", "closed"]] User.privacies_i18n.invert.to_a => [["公開", "published"], ["非公開", "closed"]]どちらも同じですが、後者の方が文字数が少なくシンプルかなと思います。
(追記)
これだけ書いておいて何ですが、配列に変更する必要はありませんでした。ハッシュのままでも行けました。
ということで、User.privacies_i18n.invertが最短です。
そして五年前の記事で既に言及していたので、ここに恥を晒しておきます。リンク
Enums | Active Record クエリインターフェイス - Railsガイド
Rails 国際化 (i18n) API - Railsガイド
- 投稿日:2020-04-04T18:11:02+09:00
【Rails】Railsアプリでfont-awesome(5系)を使えるようにするには
Font-awesomeとは
Web上で無料で使えるアイコンを提供しているツール
Font-awesomeをアプリに読み込む
gemでインストールする
fontawesome4系では「gem 'font-awesome-rails'」でインストールできていたが、fontawesome5系には対応していないとのこと。よって最新版を使用するために次のgemをGemfileに追加しましょう。
gem 'font-awesome-sass'gemを追加したらbundle install
$ bundle installその後はapp/assets/stylesheets/application.scssにfont-awesomeを引き込んでおきましょう。
今回はscssを使う場合を想定しています。@import 'font-awesome-sprockets'; @import 'font-awesome';これで5系に対応しているfontawesomeがアプリ内で使えるようになる。
参考
gem 'font-awesome-rails'は最新版に対応してない... 代わりにfont-awesome-sassを使おう!
- 投稿日:2020-04-04T17:37:13+09:00
クラスとインスタンスで使うメソッドなど
ゲッター
あるクラスのインスタンスのインスタンス変数の値を返すだけのメソッドのことです。これを定義することで、インスタンスから自身の持つインスタンス変数の値を取得することができます。
【例】MovieクラスとTicketクラスがあるとします。movie.rbclass Movie def initialize(title, fee, start_date, end_date) 〜省略〜 end def title #ここから return @title end def fee return @fee end #ここまでがゲッターの定義です endticket.rbclass Ticket def initialize(movie) @title = movie.title #movieクラスのインスタンス.titleで参照できます @fee = movie.fee #movieクラスのインスタンス.feeで参照できます end endインスタンスは自身のインスタンス変数titleやインスタンス変数feeの値を返します。つまり、movieクラスのインスタンス.titleなどとすることで、それぞれの値を取り出せるようになったのです。
require
あるファイルから他のファイルの記述内容を参照するためのメソッドです。
例えばhoge.rbとfuga.rbいうファイルがあった時、fuga.rbにrequire "./hoge"と書くことでfuga.rbにhoge.rbの記述が書かれていることにしてくれます。
なぜまとめて書かないかというと、クラスの中身が大きくなってきた時に不便で、細かく分けると管理しやすいからです。
【例】xxx.rbrequire './movie' require './ticket'Dateクラス
【例】
xxx.rbrequire './movie' require './ticket' 〜省略〜 oceans_eleven = Movie.new( "Ocean's Eleven", 1800, Date.new(2020, 4, 4), Date.new(2020, 6, 4) )Date.new()は日付を表現するためのクラスです。Dateクラスを使って日付を扱えば、うるう年など日付関連の計算が楽になります。Dateクラスを利用するには明示的にrequireを使って読み込む必要があります。
Dateクラスのインスタンスには、第一引数から順に年、月、日の順で数字で値を渡します。
Rubyの公式ドキュメントセッター
インスタンス変数の値を更新するためのインスタンスメソッドです。
【例】セッターを使用せずに値を更新する場合yyy.rbdef initialize @movies = [] endxxx.rb@movies = 更新したい値【例】セッターを使用し値を更新する場合
yyy.rbdef initialize @movies = [] end def movies = (movies) #セッターを定義してます @movies = movies endxxx.rbyyy.moveis = [A,B,C]「=が一つの代入式」に見えるのは、メソッド名に=が含まれていたからです。Rubyはメソッド名であっても半角スペースを無視するため、このようなことができます。
self
例えば映画の選択肢が出て、どれかを選択できる状態にできるとします(今回は1つしか設定していませんが、)。メッセージを出すためにputsメソッドを、ユーザーが選択肢を選べるようにgetsメソッドを使います。
puts "どの映画を見ますか?" i = 0 self.movies.each do |movie| puts "#{i} #{movie.title}: #{movie.fee}円" i += 1 end getsインスタンスメソッドの中でselfと書くと、そのインスタンスメソッドを使っているインスタンス自身を参照できます。
つまり、self.moviesの返り値は今回の場合xxx.rbにある[oceans_eleven]となります。その後、eachメソッドで一つ一つのmovieが参照され、それぞれのタイトルと金額が並んで表示される形になります。yyy.rbclass Yyy def initialize @movies = [] end def movies @movies end def movies=(movies) @movies = movies end def display_menu puts "どの映画を見ますか?" i = 0 self.movies.each do |movie| puts "#{i} #{movie.title}: #{movie.fee}円" i += 1 end gets end endattr_accessor
ゲッター、セッターを簡単に定義できるメソッドです。
【例】class Dog attr_accessor :name, :type, :age end以下の定義と同様の意味合いです。
【例】class Dog def name @name end def name=(name) @name = name end def type @type end def type=(type) @type = type end def age @age end def age=(age) @age = age end end
- 投稿日:2020-04-04T17:19:24+09:00
railsでのcsv形式ファイルの出力、入力
CSVファイルとは
CSVファイルとはモデルのインスタンスの中身を外部へ出力したり、外部から入力するのに必要。具体的にいうと、出力は他のソフトウェアに出力するために使い、入力は他のソフトウェアからの大量データの一括などに使われる。
準備
config/application.rbrequire 'csv' #アプリ全体でcsvが使えるようにライブラリを取り入れるインスタンスの中身をCSV出力する方法
今回の例ではtaskモデルの全インスタンスの中身をCSVファイルで出力する
models/task.rb#この関数ではcsv_attributesという配列変数を定義 def self.csv_attributes ["name", "description", "created_at", "updated_at"] end #CSV形式でインスタンスの中身を出力できるようにする def self.generate_csv #ここでcsv形式の配列csvを定義する、csv形式の配列に代入していくと列ベクトルができる? CSV.generate(headers: true) do |csv| #まずこのcsvに上記で定義したcsv_attributesを1行目として代入 csv << csv_attributes all.each do |task| #taskを一つ一つ取り出して #csvの次に行にtaskの値を代入していく csv << csv_attributes.map{ |attr| task.send(attr) } end end end↓
定義したgenerate_csvを読み出すためにcontrollerに記述するtasks_controller.rbdef index #名前付きURLは/tasks . . . respond_to do |format| #この意味はURLのformatによって、新たに変更を加えるよという意味 format.html #format.htmlの場合は、つまりURLが/tasksの時はという意味。format.htmlの後に処理が書かれていないため何もせず画面遷移する format.csv { send_data @tasks.generate_csv, filename: "tasks-#{Time.zone.now.strftime('%Y%m%d%S')}.csv" } #format.csvの場合は、つまり/task.csvになっている場合はという意味。その場合は後述の処理をする。ここではsend dataをしているので、データをブラウザからダウンロードされるようにしている end end↓
では/task.csvでアクセスするにはどうしたらいいのか。以下のようなリンクならアクセス可能= link_to 'エクスポート', tasks_path(format: :csv), class: 'btn btn-primary mb-5'CSVデータを入力してデータ保存する方法
モデルファイルの中で受け取った値をデータとして保存する関数を定義する
models/task.rbdef self.import(file) CSV.foreach(file.path, headers: true) do |row| #受け取ったCSVファイルを行ごとに取り出す、その時1行目headerには項目が書いてあるので、1行目は無視する task = new #Task.newと同価 task.attributes = row.to_hash.slice(*csv_attributes) #taskの属性に順番にデータを格納していく。詳しくは調べてください task.save! end end↓
controllerの中で入力保存してリダイレクトするためのアクションを作るtasks_controller.rbdef import current_user.tasks.import(params[:file]) #現在のuserのtaskにimportを発動させる redirect_to tasks_url, notice: "タスクを追加しました" endちなみにimportの読み出し方がcurrent_user.tasks.importの理解が意外と難しいのでメモ。モデルファイルの中でself.メソッド名で定義したメソッドはクラスメソッドとなるため、インスタンスではなくても使用できる。
↓
現在taskモデルはuserモデルに従属しているのcurrent_user.tasksとすることで、今のユーザーのタスクモデル(クラス)のメソッドとして使用している。参考文献はRailsのmodelクラスのselfが曖昧だったので↓
ルーティング設定routes.rbresources :tasks do post :import, on: :collection endc写真など大きなデータはURLにidとして挟み込めないので、collecionを使用することで、URLの引数として受け取れるようになる
↓
入力画面設定= form_tag import_tasks_path, multipart: true, class: 'mb-5' do = file_field_tag :file = submit_tag "インポート", class: 'btn btn-primary'この入力でファイルを追加する時は拡張子が.csvになっていることを確認してからやりましょう。
- 投稿日:2020-04-04T17:02:07+09:00
【備忘録】カラムとプロパティの違いについて
はじめに
カラムとプロパティって言葉でてきて混乱したのでめっちゃ簡単にまとめてみました~?
カラムとプロパティの違い?
カラム:データーベース側の呼び方
プロパティ:サーバーサイド側の呼び方
結論:呼び方が違うだけで大体一緒のこと
カラムちょっと詳しく?
画像(データーベース)の id や user_id 、 name などのこと。
プロパティちょっと詳しく?
例:form_for<%= form_for @user do |form| %> <%= form.label :name %> <%= form.text_field :name %> <%= form.label :address %> <%= form.text_area :address %> <%= form.submit %> <% end %>このようなフォームを作るときに出てくる
<%= form.text_field :name %> <%= form.text_area :address %>この部分の :name 、 :address のこと
さいごに
form.text_fieldの引数に、『カラム名を指定する』っていうのはあまり正しくなくて、『プロパティを指定する』っていうのが正しいみたいです~?
- 投稿日:2020-04-04T16:12:49+09:00
【HTTP/Rails】PATCHとPUTについて
はじめに~
Railsで作成したアプリで、投稿したものを更新するよ~ってときに使うHTTPがPATCHとPUT!
ページを表示するときはGET、投稿をするときはPOST、投稿とかを削除するときはDELETE、
あれ、なんで更新だけ2つも(PATCHとPUT)あるんだろ、、、
と気になってしまったので調べてみました〜?♀️?♂️そもそもHTTPとは?
HTTP = Hyper Text Transfer Protocol(ハイパーテキスト転送プロトコル)、、、???
めっちゃざっくり言うと、
『クライアント(ユーザー)とサーバ間のやりとりを可能にする約束事!』なんで約束事があるのかというと、たくさんのサイトが好き勝手にサーバーとやりとりする方法決めたら管理とかめっちゃ大変だから!
あとはHTTPとは?のおすすめ記事みてみてください~?
PATCHとPUTの違いとは?
PATCH:リソースの部分更新
PUT:リソースの完全な置き換え
もともとRails3まではPUTで更新を行っていたみたいです!
しかし一般的な更新は全てのリソース(投稿とかのデータ)を更新することはほとんどないのでRails4以降ではPATCHを使った更新が主流になりました?最後に
PUTで更新できるならなんでもいいや~?じゃなく、
もっと効率的に更新するには??って頭のいい人が考えてPATCHが生まれたのかなと思うとなんかすげって思いました(小者感)
- 投稿日:2020-04-04T15:54:58+09:00
2つの条件の組み合わせで検索する
リレーションのできている異なる2つのテーブルのカラムを用いて検索出来るようにする。
viewで検索窓を実装
areaテーブルのarea_name(地域)とshopテーブルのname(お店の名前)の組み合わせで検索できるよう実装する。
~.html.erb#フォームで:search_area, :search_shopを受け取れるようにする。 #shops_pathへ送り、 shops/indexページで検索結果を表示させた <%= form_tag(shops_path, :method => "get") do %> <%= text_field_tag :search_area,"",placeholder: "地域" %> <%= text_field_tag :search_shop,"",placeholder: "お店の名前" %> <%= submit_tag "検索", :name => nil %> <% end %>フォームで入力された情報をコントローラで受け取る
searchというメソッドにフォームで入力された:search_areaと:search_shopの2つの引数を渡す。shops_controller.erbdef index @shops = Shop.search(params[:search_area],params[:search_shop]) end現段階ではこの
searchというメソッドが定義されていない。
その為、このsearchメソッドをshopモデルで定義する必要がある。検索するための
searchメソッドをモデルに定義するshop.rb#引数にsearch_area と search_shopの2つを取るsearchメソッドを定義 def self.search(search_area,search_shop) #どちらも空白の場合は全ての店舗情報を返す if search_area.blank? and search_shop.blank? Shop.all #地域が無効な入力の時は店舗名だけで検索する elsif Area.find_by(area_name: search_area).nil? Shop.where("shops.name LIKE ?", "%#{search_shop}%") else #上記以外の場合は地域と店舗名両方に合致するものを検索する search_area_id = Area.find_by(area_name: search_area).id #引数として受け取ったsearch_areaの地域名を地域idに変換する ⬆️ Shop.where(area_id: search_area_id).where("shops.name LIKE ?", "%#{search_shop}%") #shopテーブルのarea_idとnameが一致する店舗の一覧を取得する⬆️ end end
LIKE ?って何?→ https://qiita.com/seri1234/items/765423c2c46ca4114da0これで先ほどコントローラに記述していた
searchメソッドが使えるようになりました。おわり
joinsメソッドとかgemのransackを使えばもっとsearchメソッドを綺麗にできそうです。なるべくgemに頼りたくないなと考えつつ、いろいろ調べたもののどうにも上手くできず
searchメソッドの中身がかなり力技になってしまいました。もっとスマートな記述ができると思うので後々更新したいと思います。
- 投稿日:2020-04-04T15:43:47+09:00
deviseを使ったユーザー管理機能の実装
deviseを使ったユーザー管理機能(ログイン・ログアウト)の実装手順
今回はトップページの作成も合わせて行います。
トップページを既に作成済みの方は、ログイン機能の実装のみお読みください。作業の流れ
・トップページの作成
- homesコントローラ作成
- homesコントローラの編集
- トップページのビュー作成
- ルーティングの編集
・ログイン機能の実装
- gem 'devise' のインストール
- deviseの設定ファイル作成
- ログイン機能付きUserモデルの作成
- データベースに反映
トップページの作成
早速作業に入りましょう
1. homesコントローラの作成
今回は例として
homesコントローラを作成します。ターミナル$ rails g controller homes *コマンドを実行すると以下のようなものが作成されます。 Running via Spring preloader in process **** create app/controllers/homes_controller.rb invoke erb create app/views/homes invoke test_unit create test/controllers/homes_controller_test.rb invoke helper create app/helpers/homes_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/homes.coffee invoke scss create app/assets/stylesheets/homes.scss2. homesコントローラの編集
続いて先ほど作成された、
homes_controller.rbにindexアクションを定義します。app/controllers/homes_controller.rbclass HomesController < ApplicationController def index end end3. トップページのビュー作成
次に
homesコントローラのindexアクションが呼ばれた際に返すビューを用意します。
app/views内のhomesディレクトリにindex.html.erbを新規作成してください。
後でちゃんとこのページが呼ばれているか確認するため、index.html.erbにテキストを入力しておきます。
4. ルーティングの編集
トップページを表示するためのルーティングを設定します。
config/routes.rbRails.application.routes.draw do root to: "homes#index" endこれで トップページの作成 は完了です。
最後にローカルサーバを起動して、ちゃんと表示されるか確認しておきましょう。
rails sを実行してからこちらをクリック➡️ http://localhost:3000/
白くて分かりづらいですが、以下のような画面が表示されればOKです。
ログイン機能の実装
それでは ログイン機能の実装 に入ります。
1. gem 'devise' のインストール
まずは今回ログイン機能の実装に使用する
'devise'というgemをインストールします。
Gemfileの最後の行にgem 'devise'を追記してください。Gemfile====== 省略 ====== gem 'devise'続けてターミナルでgemのインストールを実行します。
ターミナル$ bundle install補足)
gem listコマンドを実行し、きちんとgemがインストールされているか確認しておきましょう。ターミナル$ gem list *** LOCAL GEMS *** ==== 省略 ==== debug_inspector (0.0.3) devise (4.7.1) ⬅️deviseがインストールされていればOK did_you_mean (1.2.0) diff-lcs (1.3) domain_name (0.5.20190701) ==== 省略 ====2. deviseの設定ファイル作成
次にdeviseを使用する際に必要な設定ファイルを作成します。
deviseに関するファイルなのでdevise専用コマンドを使用します。ターミナル$ rails g devise:install Running via Spring preloader in process **** create config/initializers/devise.rb create config/locales/devise.en.yml3. ログイン機能付きUserモデルの作成
ユーザー情報を登録するためのモデル、マイグレーションファイルなどを作成します。
ここでもdevise専用コマンドを使用します。ターミナル$ rails g devise user Running via Spring preloader in process **** invoke active_record create db/migrate/20200404053603_devise_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml insert app/models/user.rb route devise_for :users ※注意:userの前にmodelをつけるとmodelモデルが作成されてしまいます。上記コマンドを実行すると、自動的に以下のようなルーティングが生成されます。
config/routes.rbRails.application.routes.draw do devise_for :users ⬅️自動で生成 root to: "homes#index" end4. データベースに反映
最後にユーザーの情報を登録できるようにするため、マイグレーションファイルをデータベースに反映します。
ターミナル$ rails db:migrate == 20200404053603 DeviseCreateUsers: migrating ================================ -- create_table(:users) -> 0.0122s -- add_index(:users, :email, {:unique=>true}) -> 0.0113s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.0128s == 20200404053603 DeviseCreateUsers: migrated (0.0366s) =======================以上でログイン機能の実装は完了です。
早速確認してみましょう。ローカルサーバを起動し、URLの末尾に
users/sign_upと入力して、新規登録画面を確認しましょう。以下のような画面が表示されます。
続いて、URLの末尾をusers/sign_inに変更するか、sign_up画面のlog inのリンクをクリックして、ログイン画面を確認しましょう。
画面が確認できればOKです。お疲れ様でした。
また、ここまでお読み頂きありがとうございました。追加補足
補足1) 4. データベースに反映 を忘れていた場合、以下のようなPendingMigrationErrorというエラーが発生してしまいます。要は、データベースに反映されていないマイグレーションファイルが残ってるよ。という意味です。
補足2)もし rails db:migrate を実行したはずなのに上記のエラー画面が表示される場合は、
rails db:migarate:statusコマンドを実行してマイグレーションの状況を確認しましょう。ターミナル$ rails db:migrate:status database: sample_app_development Status Migration ID Migration Name -------------------------------------------------- down 20200404053603 Devise create users補足3)上記のように Status が
downの場合はきちんとマイグレーションファイルの反映が行われていません。再度db:migrateを実行してください。Status がUPになればOKです。補足4)上記のエラーとは別に、もし以下のようなNoMethodErrorというエラーが発生した場合は、ローカルサーバを再起動しましょう。
追加実装
ログイン画面へのリンクをつくるのが面倒なので、ログインしていないとトップページにいけないようにしてしまいましょう。追記するのは以下の一点です。
app/controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :authenticate_user! ⬅️この一行を追記 end補足)このままではログアウトができないので、ログアウトのリンクだけ作成しておきます。
index.html.erbを以下のように編集しましょう。app/views/homes/index.html.erbこれは"homes#index"ページです。 <br> ログアウトしますか? <%= link_to "はい", destroy_user_session_path, method: :delete %>
- 投稿日:2020-04-04T15:42:40+09:00
ログアウト時に確認ダイアログを表示させたい…!(Rails)
はじめに
ログアウトボタンや、削除ボタンを押したとき、本当に実行するのか確認するアラートを表示させたいなって思うこと、ありますよね!
やり方を調べてみたら、めちゃめちゃ簡単だったので、ご紹介します。今回やりたかったこと
こんな感じで、ログアウトボタンを押した際に、確認のアラートを表示させたい…!
「でもどうせ、JavaScriptの知識が必要なんでしょ…?」
そう思っている方、多いと思います。私もそうでした。
安心してください。
JavaScriptの知識は必要ありません。
方法
早速やり方です。
アラートなしの場合 <%= link_to destroy_user_session_path, method: :delete do %> <i class="fas fa-sign-out-alt">ログアウト</i> <% end %>アラートなしのhtml.erbはこんな感じになってます。
ここに少し付け足すだけで、アラートが実装出来ちゃいます。アラートありの場合 <%= link_to destroy_user_session_path, method: :delete, data: {confirm: "ログアウトしますか?"} do %> <i class="fas fa-sign-out-alt">ログアウト</i> <% end %>link_to の行に、
data: {confirm: "ログアウトしますか?"}
を追加しました。これで終了です。
data: {confirm: ""} は、Railsのlink_toのオプションで、
"" の中に、アラートで表示させたいメッセージを入力する事ができます。え、こんなに簡単なの…? って、私は知ったときに思いました(笑)
皆さんも是非お試しあれ!
- 投稿日:2020-04-04T15:21:10+09:00
Font-awesomeでcouldn't find file 'font-awesome' with type 'text/css'が出たときにやったこと
couldn't find file 'font-awesome' with type 'text/css'がエラーが発生
font-awesome便利ですよね〜
今回はfont-awesomeをインストール後に発生したエラーに対してやったことをまとめました。
font-awesomeをインストールしたとき、、、
gem 'font-awesome-rails'$ bundle install/* *= require font-awesome */
application.css内にも記載し、ページを開くと、、、上記のようなエラーが発生
エラー文からfont-awesomeが読み込めていないと予想application.css内の記述を確認する
割ととんでもない記述をしていたので、修正*= require treeの下に記述をrequire_treeの上へ移動
また、同じエラー文、、、
その後、何度もエラー内容を調べるも解決の糸口は見えず
gem 'font-awesome-sass'のエラー対処は大量に出てくるのにな、、、
なんなら自分もsassでfont-awesome使っていたので、sassに切り替えようかとまで思ったり、、、
ある程度悩んだ後、もしやと思ってrails sを再起動してみる
すると、、、
起動した
なんでもっと早く再起動しておかなかったんだと
どうやらPCやターミナルをずっと動かしておくと、予期せぬエラーが発生することがあるらしい
人間と一緒で定期的に休ませてあげるのが大事だなと
さて、気を取り直して頑張ろう
- 投稿日:2020-04-04T15:17:35+09:00
RSpecの起動をスピードアップする files took n seconds to load が気になるあなたへ
dockerでrspec実行してたのですが、毎回アホみたいにファイルのロードが長かったのでなんとかしようと思いました。
Springを活用する
spring-commands-rspec(https://github.com/jonleighton/spring-commands-rspec) を使えばOKgroup :development do 略 gem "spring-commands-rspec"$ bundle install $ bundle exec spring binstub rspec $ bin/rspecspringに
bin/rspecを作ってもらい、それ経由でrspecを実行します。できるファイル
bin/rspec#!/usr/bin/env ruby begin load File.expand_path('../spring', __FILE__) rescue LoadError => e raise unless e.message.include?('spring') end require 'bundler/setup' load Gem.bin_path('rspec-core', 'rspec')一回目の実行は0から立ち上げるので普通の早さですが、二回目以降はspringがロードしてくれてるので早くなります。
僕の環境での恩恵はこんな感じでした。
before
Finished in 10.9 seconds (files took 1 minute 34.43 seconds to load) 37 examples, 0 failuresafter
Finished in 6.71 seconds (files took 4.68 seconds to load) 37 examples, 0 failuresファイルのロード時間が1分半 → 5秒弱に短縮されました。まじか。
補足
Springはapplication preloaderです。
Spring is a Rails application preloader. It speeds up development by keeping your application running in the background so you don't need to boot it every time you run a test, rake task or migration.
(https://github.com/rails/spring#spring より)
バックグラウンドで走らておくことで、スピードアップを図るものですね。
いまSpringが仕事中かどうかは、$ bin/spring statusでわかります。
rspecだけでなく、railsやrakeコマンドも早くしてくれるので、活用するとハッピーになれるかもしれません。
- 投稿日:2020-04-04T15:16:22+09:00
railsでhaml変換した後にmissing a templateエラーが発生した際の対処方法
railsでhaml:erb2hamlを実行した後にindex.html.hamlのmissing a templateが発生したので解決方法をメモ。
環境
- rails 5.2.4.2
haml-railsでrails haml:erb2hamlを実行
post_app % rails haml:erb2haml -------------------------------------------------------------------------------- Generating HAML for app/views/posts/index.html.erb... Generating HAML for app/views/posts/_nav.html.erb... Generating HAML for app/views/posts/_main_top.html.erb... Generating HAML for app/views/posts/_main_bottom.html.erb... Generating HAML for app/views/posts/_sidebar.html.erb... Generating HAML for app/views/layouts/application.html.erb... Generating HAML for app/views/layouts/mailer.html.erb... Generating HAML for app/views/layouts/mailer.text.erb... -------------------------------------------------------------------------------- HAML generated for the following files: app/views/posts/index.html.erb app/views/posts/_nav.html.erb app/views/posts/_main_top.html.erb app/views/posts/_main_bottom.html.erb app/views/posts/_sidebar.html.erb app/views/layouts/application.html.erb app/views/layouts/mailer.html.erb app/views/layouts/mailer.text.erb -------------------------------------------------------------------------------- Would you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n) y Deleting original .erb files. -------------------------------------------------------------------------------- Task complete! No .erb files found. Task will now exit.rails haml:erb2hamlは成功したにもかかわらずmissing a templateエラーが発生しました。
missing a template
ActionController::UnknownFormat (PostsController#index is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot.):テンプレートがないと怒られたのでファイル名やパスを確認するも間違っているところはなさそう。
ここでindex.html.hamlのファイル名をindex.html.erbに変更してみるとエラーが消えました。
ちゃんとコントローラはviews/posts/index.html.erbを呼び出しているようです。解決方法
rails s でローカルサーバを再起動するだけ!
rails sいくらファイル名やパスを確認してもおかしなところは見あたらず、1時間くらいなんでやー!っとなっていたので同じ境遇の人に役に立てば幸いです。
参考記事
- 投稿日:2020-04-04T14:53:53+09:00
ActiveStorageで画像をアップロードする方法
Active Storage
Active Storageとは
Active Storageとはファイルをアップロードして[undefined]()
モデルに添付してくれるやつ。Active Storageをインストールして使用することで本番環境でクラウドストレージサービス(S3、GCSとか)にファイル(画像とか)をアップロードして、データベース上でActiveRecordモデル(アプリで使うモデル)のインスタンスに紐付けできるということ。手順
Active Storageをgemを使ってインストール
↓
するとmigrationファイルができる。これは2つのテーブル(モデル)を作るためにできた(active_storage_blobsとactive_storage_attachmentsという名前)。一つ目のactive_storage_blobsは画像ファイルを管理するためのモデル。
二つ目のactive_storage_attachmentsはActiveRecordモデル(アプリで使うモデル)とactive_storage_blobsの関係性を表すためのモデル。つまりこの二つのモデルの間に存在し、二つのモデルに従属(belong_to)しているテーブル。上記で説明をしてきたがActiveStorageで画像を紐づける際にこの二つのモデルを意識することは少ない?
↓
db:migrateする
↓
ActiveRecordモデル(appで使用するモデル)ファイルに写真を持つことを記述するmodels/task.rbhas_one_attached :image #一つのtaskに一つのimageがつく。 imageという名前は入力時のform fieldでのタグ名。tasks/new.html.slim= form_with model: task, local: true do |f| . . . .form-group = f.label :image = f.file_field :image, class: 'form-control' #imageというタグ名で値を送信している他にもモデルファイルで以下のように記述するとモデルは複数の画像を持つことができる。
models/message.rbclass Message < ApplicationRecord has_many_attached :images end参考文献はRailsガイドと【Rails 5.2】 Active Storageの使い方
設定
・画像ファイルを管理する場所を指定したい
そんな時はconfig/environmentsで各環境での管理場所を指定する。development.rbconfig.active_storage.service = :local #開発環境ではファイルを保存する場所をlocalという設定にしている。localがどういう設定になっているかを知りたい場合は
config/storage.ymllocal: service: Disk root: <%= Rails.root.join("storage") %> #localという設定はファイルの保存はアプリのstorogeディレクトリで行われる
- 投稿日:2020-04-04T14:53:53+09:00
ActiveStorageについてまとめさせていただきました
Active Storage
Active Storageとは
Active Storageとはファイルをアップロードして[undefined]()
モデルに添付してくれるやつ。Active Storageをインストールして使用することで本番環境でクラウドストレージサービス(S3、GCSとか)にファイル(画像とか)をアップロードして、データベース上でActiveRecordモデル(アプリで使うモデル)のインスタンスに紐付けできるということ。手順
Active Storageをgemを使ってインストール
↓
するとmigrationファイルができる。これは2つのテーブル(モデル)を作るためにできた(active_storage_blobsとactive_storage_attachmentsという名前)。一つ目のactive_storage_blobsは画像ファイルを管理するためのモデル。
二つ目のactive_storage_attachmentsはActiveRecordモデル(アプリで使うモデル)とactive_storage_blobsの関係性を表すためのモデル。つまりこの二つのモデルの間に存在し、二つのモデルに従属(belong_to)しているテーブル。上記で説明をしてきたがActiveStorageで画像を紐づける際にこの二つのモデルを意識することは少ない?
↓
db:migrateする
↓
ActiveRecordモデル(appで使用するモデル)ファイルに写真を持つことを記述するmodels/task.rbhas_one_attached :image #一つのtaskに一つのimageがつく。 imageという名前は入力時のform fieldでのタグ名。tasks/new.html.slim= form_with model: task, local: true do |f| . . . .form-group = f.label :image = f.file_field :image, class: 'form-control' #imageというタグ名で値を送信している他にもモデルファイルで以下のように記述するとモデルは複数の画像を持つことができる。
models/message.rbclass Message < ApplicationRecord has_many_attached :images end参考文献はRailsガイドと【Rails 5.2】 Active Storageの使い方
設定
・画像ファイルを管理する場所を指定したい
そんな時はconfig/environmentsで各環境での管理場所を指定する。development.rbconfig.active_storage.service = :local #開発環境ではファイルを保存する場所をlocalという設定にしている。localがどういう設定になっているかを知りたい場合は
config/storage.ymllocal: service: Disk root: <%= Rails.root.join("storage") %> #localという設定はファイルの保存はアプリのstorogeディレクトリで行われる
- 投稿日:2020-04-04T14:15:52+09:00
【Rails】現場Railsメモ
執筆中
- 復習
- アウトプット前提のインプット
環境
Ruby 2.6.5
Rails 5.2.4.2
MySQL 5.7.28Chapter1 RailsのためのRuby入門
オブジェクト
- 言葉によって区別できるあらゆる「モノ」
- 全てのオブジェクトは
object_idという固有番号をもち、固有の存在として認識される- 仕事は何らかの振る舞い(メソッド)をすること
クラス
- オブジェクトの原型になるもの(設計図)
人間
太郎くん
花子さん
hogeくん
fugaくん人間がクラスに該当。
インスタンス
- クラスを元に作成された実態
人間
太郎くん
花子さん
hogeくん
fugaくん人間未満(言葉不適切?)がインスタンスに該当。
変数
- 何かのオブジェクトをさし示すことができる、ラベルのような存在
ローカル変数
- プログラム中の一定の処理の範囲で使われ、その範囲が終わったら捨てられる
インスタンス変数
- オブジェクトの内部に保持されてオブジェクトが存在する限り一緒に存在する
メソッド
- オブジェクトの何らかの振る舞い
- クラスメソッドとインスタンスメソッド
人間
- 名前
- 体
- 喜怒哀楽太郎くん
- 1秒間にタイピングを10回打てる
花子さん
- 3桁の数字の15個の和を2秒で求められる
hogeくん
- 100mを9.58で走れる
fugaくん
- 1時間で料理を10品作れる人間クラスのインスタンスである4人は
歩く、食べる、笑うことができる。
名前、体、喜怒哀楽がクラスメソッドに該当。人間クラスのインスタンスである4人のそれぞれにしかできない
特殊能力がインスタンスメソッドに該当。レシーバー
- メソッドを呼ぶ元のオブジェクト
hoge = 'ほげ'
hoge.length
hoge.class
hoge.lengthのhogeがレシーバーに該当。
.lengthはStringクラス(前述の人間クラス)がもつメソッドに該当。返り値(戻り値)
- メソッドが呼ばれた時に帰ってくる値のこと
属性(Attribute)
- オブジェクトの抱えるデータ













































