20210505のRailsに関する記事は25件です。

Rails6でStripe Checkoutを実装 (開発環境/TESTモード)

背景 Stripe決済を初めて実装する機会があったので、備忘録がてらまとめる。 もし、Rails6でStripe決済を実装することになった駆け出しエンジニアがいたら是非参考にしてもらえると嬉しいです。 また、学習3~4ヶ月目のぺーぺーが書いた記事なので、理解が間違っている、リファクタリングなどあればなんなりと指摘していただけると幸いです。 Stripeには複数の決済手段があるが、今回は一番実装が簡単なCheckoutを実装した。 Checkoutとは?: Stripeが用意してくれている決済画面に遷移して決済を行う手段である。 実装内容 Stripe Checkout で決済機能を実装 決済完了時にユーザー情報を登録 支払完了後はWEBアプリに戻り、決済完了画面を表示 決済せずにStripe上で「戻る」をしたときに支払キャンセル画面を表示 Stripe側での設定 1. まずはStripeのサイトでアカウント作成 以下のリンクからアカウントを作成しましょう! Stripe公式サイト 2. ダッシュボード上の商品タブをクリックし、+ テスト商品を追加をクリック 3. 商品情報を入力 今回は一括で1,000円の商品を作成 4. 以下の4つのキーを使用 商品ページ内 詳細のID: prod_◯◯◯◯◯◯ 料金のAPI ID:price_◯◯◯◯◯◯◯◯◯◯ 開発者ページ内APIキーの標準キー 公開可能キー: pk_test_◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯…◯◯◯ シークレットキー: sk_test_◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯…◯◯◯ *第三者に漏洩しないようにご注意ください Rails側の手順 ■前提 deviseや必要なデータベースは作成済み。 今回、必要なデータベースは以下の感じ。 ユーザーテーブル: name, email, password, customer_id, plan_id + デフォルト プランテーブル: id, plan_name + デフォルト ■Gemのインストール Gemfile gem 'stripe' # Stripeを実装するためのgem gem 'dotenv-rails' # Stripeの重要な情報を記述するためのgem bundle installして準備完了 実装 少し長いので、コントローラを機能2つに分けて解説します。 ■決済画面への遷移~決済完了ページ or 決済キャンセルページの実装 payments_controller.rb class PaymentsController < ApplicationController  #決済後の画面遷移 def new @session = Stripe::Checkout::Session.create({ payment_method_types: ['card'], line_items: [{ price: 'price_XXXXXXXXXXX', # 料金API-IDを記述 quantity: 1, }], mode: 'payment', success_url: request.base_url + '/payments/after_payment_register?session_id={CHECKOUT_SESSION_ID}', # 決済成功後の遷移先 cancel_url: request.base_url + '/payments/payment_cancel', #決済キャンセルした際の遷移先 }) end #決済前のキャンセルのアクション def payment_cancel end end Stripeでは、Railsに実装する際はStripe::Checkout::Session.◯◯◯のような形でセッションオブジェクトを使用してデータを取得します。 参考:Stripe API reference (Create) /payments/new.html.erb <script src="https://js.stripe.com/v3"></script> <script> var stripe = Stripe('pk_test_XXXXXXXXXXXXXX'); // ()内には公開可能キーを記述 stripe.redirectToCheckout({ sessionId: '<%= @session.id %>' }).then(function (result) { }); </script> 上記ではnewアクションに対するビューの設定です。つまり、決済画面を作っています。 newアクションで作ったインスタンス変数@session(料金、数量等の情報)を決済ページに持っていってくれます。 今回のケースだと payments/new というパスで決済ページにアクセスできます。 ← 戻るの箇所には元々はショップ名が入ります。 カーソルを持っていくと← 戻るに変わり、クリックするとキャンセル扱いとなり画面遷移します。 ダミーカード番号 4242 4242 4242 4242と その他の情報を適当に入力して「支払う」ボタンを押すと決済完了画面に遷移するはずです! ■決済完了後、データベースにユーザー情報を登録 まず、Stripeの決済は色々な情報を保有しています。欲しいデータが何なのか、事前に整理しておきましょう! どのようなデータが取れるのかは以下を参考にしてください。 参考:Stripe API reference 今回、ユーザー情報としてStripeから引っ張りたかったのは以下の通り。 -顧客ID(customer_id) -メールアドレス(email) -商品ID(product_id) payments_controller.rb class PaymentsController < ApplicationController before_action :user_plan_judgement, except: [:new, :payment_cancel] INITIAL_USER_NAME = "新規ユーザー" STRIPE_PRODUCT_ID = "prod_XXXXXX" # 商品IDを記述 #(中略) # 初期パスワードを乱数生成、初期ユーザー名を新規ユーザー、customer_id, email, plan_idをユーザーデータテーブルに登録 def after_payment_register generated_initial_password = Devise.friendly_token.first(8) stripe_user_data = Stripe::Checkout::Session.retrieve(params[:session_id]) @user = User.new( customer_id: stripe_user_data.customer, name: INITIAL_USER_NAME, email: stripe_user_data.customer_details["email"], password: generated_initial_password, plan_id: user_plan_judgement ) @user.save send_notification_email end private # 1.プランIDを取得 def user_plan_judgement stripe_plan_data = Stripe::Checkout::Session.list_line_items(params[:session_id]) if stripe_plan_data[:data][0][:price]["product"] == STRIPE_PRODUCT_ID plan_id = 1 end end end ざっくりですが、以下のことを実行しています。 1. ユーザーが決済したプランはなにかを判定 2. 1.を含めたユーザー情報を登録 1. ユーザーが決済したプランはなにかを判定 Stripe::Checkout::Session.list_line_itemsでStripeの商品IDと引っ張ってきたデータのproductデータが一致していれば plan_idに1を代入するという処理にしました。 現状あまり意味はないのですが、プランの料金が変わったときでも新たに商品を作成する必要がなく、 同一のプランであることとして管理するために設定しました。 Stripe API reference (line_items) 2. 1.を含めたユーザー情報を登録 Devise.friendly_token.first(8)でランダムな8桁のパスワードを設定 Stripe::Checkout::Session.retrieveでレトリーブしてきてくれます。 参考:Stripe API reference (retrieve) かわいいレトリーバー犬はこのretrieveから来ているらしいです。 狩った獲物をとってくる(回収してくる)意味らしいです。 (今回のPJの恩師から教えてもらいました。) 参考:語源由来辞典 なので、欲しいデータをretrieve(回収)してきてもらうわけです。 retrieveしてもらった獲物(データ)を獲物箱(=変数)へ代入します。 あとは見慣れたUser.newとuser.saveでユーザーデータを保存してユーザー登録も完了! その他、ビューを自分のお好みで整えて完成! ■完成形 コントローラ payments_controller.rb class PaymentsController < ApplicationController before_action :user_plan_judgement, except: [:new, :payment_cancel] INITIAL_USER_NAME = "新規ユーザー" STRIPE_PRODUCT_ID = "prod_XXXXXX" # 商品IDを記述 def new @session = Stripe::Checkout::Session.create({ payment_method_types: ['card'], line_items: [{ price: 'price_XXXXXXXXXXX', # 料金API-IDを記述 quantity: 1, }], mode: 'payment', success_url: request.base_url + '/payments/after_payment_register?session_id={CHECKOUT_SESSION_ID}', cancel_url: request.base_url + '/payments/payment_cancel', }) end def payment_cancel end def after_payment_register generated_initial_password = Devise.friendly_token.first(8) stripe_user_data = Stripe::Checkout::Session.retrieve(params[:session_id]) @user = User.new( customer_id: stripe_user_data.customer, name: INITIAL_USER_NAME, email: stripe_user_data.customer_details["email"], password: generated_initial_password, plan_id: user_plan_judgement ) @user.save send_notification_email end private def user_plan_judgement stripe_plan_data = Stripe::Checkout::Session.list_line_items(params[:session_id]) if stripe_plan_data[:data][0][:price]["product"] == STRIPE_PRODUCT_ID plan_id = 1 end end end ビュー /payments/new.html.erb <script src="https://js.stripe.com/v3"></script> <script> var stripe = Stripe('pk_test_XXXXXXXXXXXXXX'); // ()内には公開可能キーを記述 stripe.redirectToCheckout({ sessionId: '<%= @session.id %>' }).then(function (result) { }); </script> after_payment_register.html.erb  <p>決済してユーザー登録したぞ</p> <p>決済後に表示されるページだよ</p> <p>必要に応じてhtmlで文章記述</p> payment_cancel.html.erb <p>キャンセルされたときに表示されるページだよ</p> その他(ルーティング、env) routes.rb (中略) resources :payments, only: [:new] get '/payments/after_payment_register', controller: 'payments', action: 'after_payment_register' get '/payments/payment_cancel', controller: 'payments', action: 'payment_cancel' (中略) .env *ここはGithubにpushされないファイルなので、隠したい定数はここに書きます。 //以下はシークレットキー STRIPE_TEST_SECRET_KEY = sk_test_XXXXXXXXXXXXXXXXXX 終わりに 長くなりましたが、ご覧いただきありがとうございました! 今回、初めてStripe決済を実装する機会をいただくことができ、自分で実装した中で理解したことをまとめてみました。 Stripe::Checkout::Session.◯◯◯で自在にデータを取得できるのは本当に便利だなあと実感しました。 また、MVC、データの取得~保存、データベースの構築まで自分でやれたのでデータ関連の理解が深まったと感じます。 次回は本番環境の実装についてまとめられたらまとめてみようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ユーザ登録機能の実装(deviseを使用して説明)インストールからdb作成まで

はじめに deviseを用いてユーザ登録の実装について書かせて頂きます。 自分でもまだまだ理解不十分な所もあるのでアウトプットできればと思います。 ログイン機能の実装 TwitterやInstagram、メルカリなど多くのウェブサイトにはログイン機能があります。 ユーザに対してアカウントを作り、ユーザを管理する機能です。 それではまずdeviseのインストールから始めます。 deviseのインストール ログイン機能を実装するにはまずdeviseというgemをインストールします。 deviseとはユーザー管理機能を簡単に実装するためのgemの事です。 gemってなんぞや?となるかもしれませんが、今回は割愛させて頂きます。?‍♂️ それではインストールします。 GemfileはRailsアプリで利用するgemの一覧を管理するファイルです。 様々な記述がされているのでまずは一番最後の行に記述しましょう。 Gemfile. gem 'devise' この時シングルクォーテーション忘れずに記述します。 記述し終えたらターミナルでコマンドを実行します。 ターミナル. bundle install これでインストールし終えました。 一旦サーバーを起動させます。理由はインストールしたGemの反映するタイミングが、サーバー起動時だからです。 ターミナル. rails s インストールできたかはGemfile.lockで確認できます。 (gemfileのレシートようなものと考えております。) devise設定ファイルを作成 下記のコマンドを実行しましょう。 これは「設定関連に使用するファイル」を自動で生成するコマンドです。 ターミナル. rails g devise:install deviseのUserモデルを作成 deviseを利用する際には、アカウントを作成するためのUserモデルを新しく作成する必要があります。 作成には通常のモデルの作成方法ではなく、deviseのモデル作成用コマンドでUserモデルを作成します。 アカウントを作成するためのUserモデルを作ります。この中にはアカウント名やemail、passwordなどのデータが入ります。 deviseモデルの作成は通常とはやり方ではありません。 #通常のモデル作成の場合(モデル名をuserとする) rails g model user #deviseのuserモデル作成の場合 rails d devise user このコマンドでユーザーに関する、モデルやマイグレーション、ルーティングも自動生成されています。 routes.rb Rails.application.routes.draw do devise_for :users #この行が追加されました root to: 'items#index' end テーブルの作成 すでに自動生成されているマイグレーションファイルに内容を記述し、テーブルを作成します。 db/migrate/20XXXXXXXXX_devise_create_users.rb 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 ##省略 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 end このような感じでマイグレーションファイルがあるかと思います。 5,6行目に注目してください。 db/migrate/20XXXXXXXXX_devise_create_users.rb t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" t.string :nickname, null: false #ニックネームを追加しました t.date :birth_day, null: false #誕生日を追加しました デフォルトでメールアドレスとパスワードはあります。 他に名前や誕生日などを追加していく事も可能です。 テーブルが設計できたのでマイグレーションを実行します。 rails g migrate id email encrypted_password nickname birth_day このようなdbが完成しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GraphQL RubyのResolverについて

概要 GraphQL Rubyの resolverについてまとめました。 開発環境 ruby 2.6.5 rails 5.2.4 graphql 1.10.10 https://graphql-ruby.org/getting_started resolverを使う前のGraphQL 実装 参考: https://graphql-ruby.org/schema/root_types.html Rails Model # == Schema Information # # Table name: subject_scores # # id :integer # subject_name(科目名) :string # user_name(ユーザー名) :string # score(点数) :integer class SubjectScore < ApplicationRecord end Types class module Types class QueryType < GraphQL::Schema::Object field :subject_scores, [SubjectScoreType], 'Returns all subject scores', null: true end end module Types class SubjectScoreType < GraphQL::Schema::Object field :id, Integer, null: true field :subject_name, String, null: true field :user_name, String, null: true field :score, Integer, null: true end end Request subjectScores: { id subjectName userName score } Response (データベースの値がそのまま返却される) "data": { "subjectScores": [ {"id": 1, "subjectName": "英語", "userName": "太郎1", "score": 25}, {"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50}, {"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60} ] } resolverで値を加工して返却する scoreが30点未満のユーザー名をマスキングしてみます。 resolver パターン1 参考: https://graphql-ruby.org/fields/introduction.html#field-resolution field名と同じメソッド名を定義するパターン resolverの定義 module Types class SubjectScoreType < GraphQL::Schema::Object field :id, Integer, null: true field :subject_name, String, null: true field :user_name, String, null: true field :score, Integer, null: true def user_name if object.score < 30 '***' else object.user_name end end end end Response(30点未満のユーザー名がマスキングされる) "data": { "subjectScores": [ {"id": 1, "subjectName": "英語", "userName": "***", "score": 25}, {"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50}, {"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60} ] } resolver パターン2 参考: https://graphql-ruby.org/fields/introduction.html#field-resolution fieldの resolver_method属性を使うパターン resolverの定義 module Types class SubjectScoreType < GraphQL::Schema::Object field :id, Integer, null: true field :subject_name, String, null: true field :user_name, String, null: true, resolver_method: :user_name_masking_resolver field :score, Integer, null: true def user_name_masking_resolver if object.score < 30 '***' else object.user_name end end end end Response(30点未満のユーザー名がマスキングされる) "data": { "subjectScores": [ {"id": 1, "subjectName": "英語", "userName": "***", "score": 25}, {"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50}, {"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60} ] } resolver パターン3 https://graphql-ruby.org/fields/resolvers.html#using-resolver fieldの resolver属性を使うパターン。 (resolverクラスを別で定義する必要があります) resolverクラスの定義 # app/graphql/resolvers/base.rb module Resolvers class Base < GraphQL::Schema::Resolver end end module Resolvers class MaskingScore < Resolvers::Base type String, null: true def resolve if object.score < 30 '***' else object.user_name end end end end user_namefieldのresolver属性に Resolvers::MaskingScoreを指定します。 module Types class SubjectScoreType < GraphQL::Schema::Object field :id, Integer, null: true field :subject_name, String, null: true field :user_name, String, null: true, resolver: Resolvers::MaskingScore field :score, Integer, null: true end end Response(30点未満のユーザー名がマスキングされる) "data": { "subjectScores": [ {"id": 1, "subjectName": "英語", "userName": "***", "score": 25}, {"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50}, {"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60} ] } まとめ この例ではパターン1で十分ですが、 resolverを共通処理化し、複数のfieldでそのresolverを使う場合もあると思います。(実際にありました) その場合はパターン2、3が有効かと。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で Date クラス を日本語に変換する方法

はじめに 共同開発の実装にて、created_at updated_at を日本語表示に変更したため、手順をドキュメントにまとめます。 手順 以下の手順で実装できます。 1. タイムゾーンを変更 2. config/initializers/time_formats.rbファイルを作成し、Time::DATE_FORMATS に独自の書式を追加 3. views の修正 これで完了です。 詳しくみていきましょう! タイムゾーンの変更 今回はすでに変更済みですが、詳しい手順としてはconfig/application.rbにタイムゾーンの設定を追加するだけです。 config/application.rb module WonderfulPortal class Application < Rails::Application config.time_zone = "Asia/Tokyo" end end これでタイムゾーンの変更は完了です! Time::DATE_FORMATSに独自で書式を定義 config/initializers/time_formats.rbファイルを作成し、以下のように設定を追加しましょう! config/initializers/time_formats.rb Time::DATE_FORMATS[:datetime_jp] = "%Y年%-m月%-d日" %Y や %-m などは指定の仕方によって表示が変わってきますので、お好みで変更すると良いでしょう! views の修正 最後にviews にて日付を表示させたい箇所に以下のように記述することで、xxxx年xx月xx日という表示になります。 今回はdocumentの作成日時を表示させたかったので、以下のように書きました。 html.erb <%= @document.created_at.to_s(:datetime_jp) %> 以上で実装完了です! 参考記事 【Rails】created_at、updated_atを日本時間に変更して良い感じに表示する方法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】jpostal+jp_prefectureで住所自動入力

サインアップにて住所自動入力機能をつける 前提 gem deviseをインストール済み useテーブルを作成済み nameカラムを追加している jpostalプラグインをDLしよう jquery.jpostal.jsをapp/assets/javascriptに配置する。 Gemfileに以下を追加 Gemfile gem 'jquery-rails' gem 'jp_prefecture' bundleも忘れずにね。 jQueryが動作するようにapplication.jsに記載 application.js //= require turbolinks //= require jquery // turbolinksより下に記述 Userに住所に使用するカラムを追加 $ rails g migration AddColumnsToUsers add_columns_to_users.rb def change add_column :users, :postcode, :integer //郵便番号 add_column :users, :prefecture_code, //都道府県一覧:integer add_column :users, :address_city, :string //市町区村 add_column :users, :address_street, :string //番地 add_column :users, :address_building, :string //建物 end rails db:migrateしちゃいましょう。 Modelの編集 ログイン時に必要な情報なので、user.rbに記載していきます。 user.rb include JpPrefecture jp_prefecture :prefecture_code //都道府県コードから都道府県名に自動変換 def prefecture_name JpPrefecture::Prefecture.find(code: prefecture_code).try(:name) end //~.prefecture_nameで都道府県名を参照可能にする。 //例)@user.prefecture.nameで該当ユーザーの住所表示 def prefecture_name=(prefecture_name) self.prefecture_code = JpPrefecture::Prefecture.find(name: prefecture_name).code end Controller編集 デフォルトのログインには住所入力機能はないので、通るように記述を追加する。 application_controller.rb def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [ :email, :name, :postcode, :prefecture_name, :address_city, :address_street, :address_building ]) end Viewファイル registrations/new.html.erb <div class="field"> <%= f.label :postcode,"郵便番号" %><br /> <%= f.text_field :postcode, autocomplete: 'postcode', class: "form-control" %> </div> <div class="field"> <%= f.label :prefecture_name,"都道府県" %><br /> <%= f.collection_select :prefecture_code,JpPrefecture::Prefecture.all, :code, :name,{ prompt: "選択してください" }, class: "form-control" %> </div> <div class="field"> <%= f.label :address_city,"市区町村" %><br /> <%= f.text_field :address_city,autocomplete: 'address_city', class: "form-control" %> </div> <div class="field"> <%= f.label :address_street,"番地" %><br /> <%= f.text_field :address_street,autocomplete: 'address_street',class: "form-control" %> </div> <div class="field"> <%= f.label :address_building,"建物" %><br /> <%= f.text_field :address_building,autocomplete: 'address_building',class: "form-control" %> </div> ※turbolinksを無効化しないと動かないこともあるので、そんな時は以下を追加してみよう。 applications.js document.addEventListener("turbolinks:load" ,function() { return $('#user_postcode').jpostal({ postcode: ['#user_postcode'], address: { '#user_prefecture_code': '%3', '#user_address_city': '%4', '#user_address_street': '%5%6%7', }, }); }); %3とか %4とかって一体なんぞや?問題を解決。 書式 意味 %3 都道府県 %4 市町区村 %5 町域 %6 大口事業所の番地 %7 大口事業所の名称 %8 都道府県カナ %9 市町区村カナ 都道府県欄、住所欄の2個 address : { '#prefecture' : '%3', '#address' : '%4%5', } 例2 都道府県欄、住所欄、番地欄の3個 address : { '#prefecture' : '%3', '#address1' : '%4', '#address2' : '%5', }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Heroku無料枠の限界に挑む!30分以上アクセスがなくてもスリープにならないようにやらしく設定する

Herokuの無料枠だと、30分アクセスがないとスリープしてしまう問題をなんとかしたい...!! (Herokuの無料枠では)Web アプリに 30 分間アクセスがないとスリープします。 次にアクセスがあったときに Dyno を起動する必要があるので数秒程度、レスポンスに時間が掛かります。 なぜやるのか 現在LINEボットを開発中で、無料で済ませたいためHerokuにデプロイしたのですが、Herokuの無料枠では30分何もアクセスがないとサーバーがスリープモードに入ってしまいます。一度スリープモードに入ってしまうと、再度起動までに予想外に時間がかかってしまい、ボットで返ってくるはずの返事が返ってこなかったり、LIFFブラウザを立ち上げてからページを開くまでに30秒ほどかかってしまったりと、このスリープが予想外に弊害になっていました。 ですが、ようは30分内になんらかのリクエストがアプリ側に送られればHerokuはスリープモードに入らないわけです。 ということで、Herokuのアドオン、「Scheduler」を使用して、10分に一回、Heroku側になんらかのリクエストを送る処理を実装することにしました。 やり方 参考: https://haayaaa.hatenablog.com/entry/2019/03/01/222221 ①Schedulerアドオンの追加 まず、ターミナルからheroku addons:add scheduler:standardをしてアドオンを追加します heroku addons:add scheduler:standard ※アドオンとは、プラグイン、拡張機能のようなものです。 ②Herokuのブラウザアプリにアクセスし、shedulerをクリック ③ Add jobをクリックし、10分毎にHerokuのデプロイ先URLにcurlコマンドでリクエストコマンドを送る処理を追加 (④ % heroku logs -tでログを確認) ターミナルで% heroku logs -tを叩き、10分毎に以下のようなログが残っていたら成功です 2021-05-05T13:22:03.309820+00:00 app[api]: Starting process with command `curl https://hoge.herokuapp.com/` by user scheduler@addons.heroku.com 2021-05-05T13:22:19.636248+00:00 heroku[scheduler.5024]: Starting process with command `curl https://hoge.herokuapp.com/` 簡単〜
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

checkboxでいい感じに、絞り込み検索してみた(Javascript備忘録)

はじめに ransackで、全件一覧表示を消してみた(備忘録)でも紹介していましたが、ransackというgemとJavaScriptを用いて、検索機能を実装した際に(勘違いして)いろいろ試したので、備忘録として残します。 【注意!】 ※JavaScriptの知識は、Progate1周程度。 ※ビュー&コードは、一部加工しています。(そのままコピペでは、動きません。) 環境 Rails 6.1.2 jquery 3.6.0 完成イメージ ・一覧テーブルがあり、2つのチェックボックスをクリックすると、絞り込み表示される  (データは、CSVファイルにあり。ransackで一覧表示&キーワード検索を実装。そこからチェックボックスで絞り込み表示させる) ・一覧ページ ・②をチェック後の一覧ページ 勘違いしたところ ①Railsで定義した変数をJavaScriptへ渡す データはCSVファイルに入っていて、ransack実装済みなのに 以下の内容を試していて、時間がかかる。 ・ransackで、全件一覧表示されているビューを消す ・gonを導入(Railsで定義した変数をJavaScriptへ渡すことができるGem) ・gonを用いて、実装 ②ransack検索とは別に、新たにJavaScriptでCSVファイルを読み込む ・JavaScriptで、CSVデータを読むこむ方法を調べる 大きくこの2つで、かなりの時間を費やしました アドバイス頂いた内容 行き詰まったので、メンターさんに度々アドバイスいただきました! このアドバイスのおかげで、軌道修正できました。 ①チェックボックスをクリックした際にコンソールに出力 ②チェックの入っている値を取得 ③チェックの入っている値のみ表示するようにする関数を作る ③が上手く実装できなかったので、さらに詳細なアドバイスを頂きました。 ④tableタグの中のtrタグに各行のデータが入っている。trタグを全て取得し,それぞれについて,値部分を取得して配列に含まれているかどうかを判定して表示・非表示を対応する trタグの中にデータがあるんだ!!と感動しました。(かなり知識浅く、すみません。) 苦戦したところ ③、④のtrタグの値を取得して、そこからの処理がどうすればよいのか?かなり苦戦しました。 (いろんなJavaScriptの記事を読みましたが、コードの理解が出来ず、1つ1つググりながらの実装だったので時間がかかりました。) 実装のポイント ①デフォルトで、チェックボックスに☑させる <label><input class="checkbox" type="checkbox" name="〇〇" value="①" checked> ①</label> checkedを入れることにより、デフォルトで☑できます。 ②チェックボックスをクリックすると処理が開始 $(".js-filter-form :checkbox").on("click", function(){ } ③チェックボックスに☑した値を配列に追加 //空の配列を作成 var checked = []; $(".js-filter-form :checked").each(function(){ // チェックボックスにチェックした値を配列に追加 checked.push($(this).val()); ④全てのtrを取得し処理 $('#list tbody tr').each(function(){ // html要素を取得 var txt = $(this).find("td:eq(0)").html(); // ①と②にチェックが入ると表示、チェックを外すと非表示できるよう条件分岐 if(txt.match(checked[0]) || (checked[1])){      //表示 $(this).show(); }else{      //非表示 $(this).hide(); } }); 最終コード(一部編集しております。Bootstrapは外しています。) index.html.erb <div> <h1>タイトル</h1>  <form class="js-filter-form :checkbox"> <label><input class="checkbox" type="checkbox" name="〇〇" value="①" checked> ①</label> <label><input class="checkbox" type="checkbox" name="〇〇" value="②" checked> ②</label> </form> <%= search_form_for @q do |f| %>    <%= f.label :title_cont, "タイトル" %> <%= f.search_field :title_cont %>    <%= f.label :content_cont, "キーワード" %>    <%= f.search_field :content_cont %> <%= f.submit "検索" %> <% end %> <div> <body> <div> <table id="list"> <thead> <tr> <th>ジャンル</th> <th>タイトル</th> </tr> </thead> <tbody> <% Railsの変数(複数形).each do |単数形| %> <tr> <td><%= 単数形.①②を示すジャンル %></td> <td><%= 単数形.タイトル %></td> </tr> <% end %> </tbody> </table> </div> </div> <script> $(".js-filter-form :checkbox").on("click", function(){ var checked = []; $(".js-filter-form :checked").each(function(){ // チェックボックスにチェックした値を配列に追加 checked.push($(this).val()); }); // 全てのtrを取得 $('#list tbody tr').each(function(){ var txt = $(this).find("td:eq(0)").html(); // ①と②にチェックが入ると表示、チェックを外すと非表示できるよう条件分岐 if(txt.match(checked[0]) || (checked[1])){ $(this).show(); }else{ $(this).hide(); } }); }); </script> </body> まとめ 実際に実装すると、想像以上に難しく感じました。 しかし、JavaScriptを学習できて、楽しかったです!(いろいろアプリ作ってみたいです。) 今回の反省点は、3つ。 ①「質問しないこと=自走力」ではないので、「何日も悩んでいることは、質問する」こと ②時間は有限なので、時間を区切ってスピード意識してすすめること ③メンターさんの時間を奪わないように、質問内容をはっきりすること 度々、質問に対応して頂いたメンターさんに感謝です。 コードについて、アドバイス等ございましたらコメント頂けると幸いです。 参考文献 jQueryで簡易的な検索機能(フィルター機能)を作る方法 jQueryAPI ドキュメント jQueryのhtml()で追加・取得・書き換えの方法まとめ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

0からのRubyインストールからRailsでアプリケーションフォルダ作成までの手順まとめ

これからRubyをインストールして、Railsを使ってアプリケーション開発をしていきたい初学者の方の役に立てるかもしれません。エラーで詰まっている方の参考になれたらいいなと思います。私はRuby:3.0.1とRails:6.1.3.1 をインストールしました、インストールまでの手順を残しておこうと思います(mac環境です) まずはRubyのインストール、macos(Big Sur)にも元から入っていますが、バージョンが 2.6.0 くらいだったため、最新の 3.0.1をインストールしました。 rbenvでRubyを入れる解説はこちらの記事がわかりやすかったです。 (ただHomebrewは入っている前提だったので、もし入れられてなければ https://qiita.com/zaburo/items/29fe23c1ceb6056109fd こちらの記事を参考にして下さい) Rubyのインストールまで終えたら次はRailsを入れていきます。 ターミナルで $ gem install rails を実行します。しかし、私の場合 ERROR: While executing gem ... (Gem::FilePermissionError) というエラーが出てしまいインストールできません これはmacにデフォルトで入っているバージョンのRubyを使おうとして、パーミッションエラーが出てしまっていからだと思います こちらの記事にPermissionErrorが出た時の対処法が載ってあります 先程 $ rbenv global 3.0.1 とした事で勝手に、インストールしたRubyのバージョンを使えるようになったと思っていましたが、どうやら違ったようです シェルがzshの方は ~/.zshrc に、bashの方は ~/.bash_profileにPATHを通さなければいけません PATHの通し方はこちらがわかりやすかったです。私の場合は .zshrc ファイルが無かったので作成しました ちなみに ~/.zshrc (~/.bash_profile)ファイルというのは、シェルを起動する時に、環境変数や関数などの定義を自動で行ってくれる設定ファイルです。ここにPATHを通しておくと、通したファイルのパスのRubyのバージョンが、毎回自動で認識されて使えるようになります。 $ open ~/.zshrc でファイルに記述する内容は [[ -d ~/.rbenv ]] && \ export PATH=${HOME}/.rbenv/bin:${PATH} && \ eval "$(rbenv init -)" こちらになります。記述し終わったらシェルを再起動して、もう一度 $ gem install rails をしてみるとインストールできました。 できたのですが、、、 $ rails new practice_app としてpractice_appフォルダを作成しようとしても Rails is not currently installed on this system. To get the latest version, simply type: $ sudo gem install rails You can then rerun your "rails" command. というエラーが出てしまいます。言われたとおり $ sudo gem install railsを実行してみます インストールできました。というメッセージが出てきました、しかし rails -v を実行するとまた上記のエラーと同じメッセージを返されます 調べてみると、これは先程変更した .zshrc の反映がうまくいってなかった事が原因みたいです そこで、ターミナルを再起動します。 解決できました。こんなに簡単に。笑 こちらが解決方法の載ってあった記事になります これで $ rails new practice_appとすると作成できました! 少し長かったですが、私と同じ環境の方のエラー解決の力に力になれればなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで自動計算

自動計算機能の実装 自分用のメモです〜 正解かどうかは分かりません!! が、ひとまず動いたので残しておきます。 function calc (){ const itemPrice = document.getElementById("item-price"); itemPrice.addEventListener("keyup",()=>{ //取得した要素が入力されたら(keyup)イベント発火 tax = parseInt(itemPrice.value * 0.1); const addTax = document.getElementById("add-tax-price"); addTax.innerHTML = `${tax}円` //taxはアイテムの値段.valudの10%を小数点以下切り捨て //表示させる要素を取得して、innnerHTMLで上書き prof = (itemPrice.value) - (tax) const priceContent = document.getElementById("profit"); priceContent.innerHTML = prof }) } window.addEventListener('load',calc); めちゃくちゃ頭抱えて悩んでたけど、めちゃくちゃ基礎でした。。 精進します。 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails6】結合テストコードエラー expected ◯◯◯◯, but was not given a block の解決

みなさんこんばんは! 今日も結合テストコードのエラー解決について記載していきます 本日対面したエラー ターミナル Failure/Error: expect(find_link('削除する', href: article_path(@article1)).click).to change{Article.count}.by(-1) expected ◯◯☆☆◯◯, but was not given a block ・・・「◯◯☆☆◯◯がブロックで渡されてないよ!」という日本語訳となります では、原因のテストコード記述を確認してみましょう! article_spec.rb # 投稿を削除するとレコードの数が1減ることを確認する expect(find_link('削除する', href: article_path(@article1)).click).to change{Article.count}.by(-1) きっと皆様であればどこが間違っているのか、すぐにわかりますよね… 間違っているのは、 expect( ) です、ここは expect{ } で結んであげないとダメなのです changeマッチャでモデルのカウントをする場合のみ、expect()ではなくexpect{}を使いましょう と教わっていました…この箇所を忘れてしまったということですね…反省です… では、直していきましょう article_spec.rb # 投稿を削除するとレコードの数が1減ることを確認する expect{find_link('削除する', href: article_path(@article1)).click}.to change{Article.count}.by(-1) ターミナルで実行します Finished in 12.24 seconds (files took 1.3 seconds to load) 3 examples, 0 failures うん、当然ですが無事にテスト終了しました! 続きを書いていくことにします
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsコードリーデイング④ route.rbのgetメソッドを深掘りしてみた 【備忘録】

はじめに MVCにおいてどのように内部処理が行われページ遷移に至るのかフレームワークを使用していると何も気にせずとも開発できてしまいます。 しかしこの基本的な動作の仕組みを理解することで、エンジニアとしての基本的な思考を理解できるのではと考えました。 今回は最も基本的なgetメソッドを見ていきます。 https://qiita.com/engineer_ikuzou/items/ef70de5de070b1094ab6 こちらの記事の続きです。 matchメソッド rails/actionpack/lib/action_dispatch/routing/mapper.rb module Resources # Matches a URL pattern to one or more routes. # For more information, see match[rdoc-ref:Base#match]. # # match 'path' => 'controller#action', via: :patch # match 'path', to: 'controller#action', via: :post # match 'path', 'otherpath', on: :member, via: :get def match(path, *rest, &block) if rest.empty? && Hash === path options = path path, to = options.find { |name, _value| name.is_a?(String) } raise ArgumentError, "Route path not specified" if path.nil? case to when Symbol options[:action] = to when String if /#/.match?(to) options[:to] = to else options[:controller] = to end else options[:to] = to end options.delete(path) paths = [path] else options = rest.pop || {} paths = [path] + rest end if options.key?(:defaults) defaults(options.delete(:defaults)) { map_match(paths, options, &block) } else map_match(paths, options, &block) end end pathのnilチェック rails/actionpack/lib/action_dispatch/routing/mapper.rb raise ArgumentError, "Route path not specified" if path.nil?  学び① 例外処理 raise throwと同じ役割のようです。 raise SomeException, 'message' 例外クラスとメッセージを引数として渡すことができます。 変数toの型に応じてoptionsハッシュに代入 rails/actionpack/lib/action_dispatch/routing/mapper.rb case to when Symbol options[:action] = to when String if /#/.match?(to) options[:to] = to else options[:controller] = to end else options[:to] = to end options.delete(path) paths = [path] 変数toは、"users#index"つまりStringなので、when Stringに入ります。 コントローラ#アクションで書かれるため、#がない場合は、controllerが書かれているというように処理します。 そして、 {"users"=>"users#index", :via=>:get, :to=>"users#index"} からpath(users)を削除。 {:via=>:get, :to=>"users#index"} 最後にpathsにpathを配列で代入。 学び② case whenは型の比較にも適用できる。 学び③ matchメソッド /#/.match?(to)で、toに#が含まれているかどうか判定しています。 https://docs.ruby-lang.org/ja/latest/method/String/i/match.html https://docs.ruby-lang.org/ja/latest/method/Regexp/i/match=3f.html デフォルト値のチェック rails/actionpack/lib/action_dispatch/routing/mapper.rb if options.key?(:defaults) defaults(options.delete(:defaults)) { map_match(paths, options, &block) } else map_match(paths, options, &block) end route.rbで get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' } のようにデフォルト値を設定している場合は、trueの方に入ります。 今回はデフォルト値の設定なしなので、elseです。 学び④ key?メソッド options.key?(:defaults)optionsが:defaultsというkeyを持つかどうかの判定です。 https://docs.ruby-lang.org/ja/latest/method/Hash/i/has_key=3f.html 処理本体 rails/actionpack/lib/action_dispatch/routing/mapper.rb def map_match(paths, options) if options[:on] && !VALID_ON_OPTIONS.include?(options[:on]) raise ArgumentError, "Unknown scope #{on.inspect} given to :on" end if @scope[:to] options[:to] ||= @scope[:to] end if @scope[:controller] && @scope[:action] options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}" end controller = options.delete(:controller) || @scope[:controller] option_path = options.delete :path to = options.delete :to via = Mapping.check_via Array(options.delete(:via) { @scope[:via] }) formatted = options.delete(:format) { @scope[:format] } anchor = options.delete(:anchor) { true } options_constraints = options.delete(:constraints) || {} path_types = paths.group_by(&:class) (path_types[String] || []).each do |_path| route_options = options.dup if _path && option_path raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings." end to = get_to_from_path(_path, to, route_options[:action]) decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints) end (path_types[Symbol] || []).each do |action| route_options = options.dup decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints) end self end ついに前処理が終わり、ルーティングの本体の処理が見えました。 一旦、最終的に残ったものが何か見てみます。 [11] pry(#<ActionDispatch::Routing::Mapper>)> self => #<ActionDispatch::Routing::Mapper:0x00007faf9452b930 @concerns={}, @draw_paths=[#<Pathname:/Users/username/codereading/CodeReading/config/routes>], @scope= #<ActionDispatch::Routing::Mapper::Scope:0x00007faf9452b8e0 @hash={:path_names=>{:new=>"new", :edit=>"edit"}}, @parent=#<ActionDispatch::Routing::Mapper::Scope:0x00007faf9f10bd90 @hash=nil, @parent=nil, @scope_level=nil>, @scope_level=nil>, @set=#<ActionDispatch::Routing::RouteSet:0x00007faf9e98ada0>> ActionDispatch::Routing::Mapperにインスタンス変数として、ルーティング情報を記録しているということでしょうか。 こちらのモジュールを継承することによってルーティング情報を取得できるものと思われます。 最後に 本日はここまでです。処理の詳細は引き続き読んでみようと思いますが、ルーティングの情報はインスタンス変数に保存されるということでした。考えたら当たり前な気もしますが、謎に包まれていたものが少し見えてきたため、嬉しいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

current_page?(controller: 'library', action: 'checkout')がワークしなくて困った件(Rails)

やりたいこと ページによって、検索フォームをビューで、表示する/しないを切り替えたいです。 そこで、current_page?()なるメソッドがあることを知りまして、カッコ内の引数には、Railsドキュメントによると、URLだけでなく、コントローラとアクションを指定することも可能とのこと。(2021/5/5執筆時時点) 開発環境 MacBook Pro 13 2020 macOS Catalina 10.15.7 ruby 3.0.0 Rails 6.0.3 問題 こちらのRailsドキュメントのお手本コードを真似して... current_page?(controller: 'library', action: 'checkout') こう書いてみます。 <%= render 'search_form' if current_page?(controller: 'expressions', action: 'index'} %> しかしながら、wrong number of arguments (given 0, expected 1)などとエラーが出てしまいます。given 0?いや、引数渡してるやん。なんなら、2つない? 余談ですが、URLの指定(例えばcurrent_page?('/'))はうまくいきました。 でも今やりたいことはそれではない! ググっても解決法が出てこなくて、みんなこの上のコードでできる!と断言しておられます。 いや、できねーし!(泣) シングルクオーテーション、ダブルクオテーションを試すという愚かな実験をするくらい、手当たり次第自分で色々試してみたところ、一つだけ見つけました。 それが下記です。 解決 <%= render 'search_form' if current_page?({controller: 'expressions', action: 'index'}) %> 丸括弧内で、ハッシュで一塊りにして渡してあげれば、引数argumentが1つあるとみなしてもらえるみたいです。 これで指定したコントローラとアクション以外で生成されたページでは、検索フォームが出なくなりました。 めでたし、めでたし...? 最後に と終わりたかったところなのですが、エラーこそ出なくなったのですが、検索後の画面では、'search_form'が消えてしまいます涙。同じexpressionsコントローラのindexメソッドのはずなのに...。 何かご指摘やアドバイスなどあれば、ぜひよろしくお願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]ノーエラーでherokuへデプロイしていく

はじめに 本記事ではローカル環境で開発したrailsアプリケーションをherokuへデプロイしていく方法いついてまとめてあります。 前提条件 ・Rails 6.0.0 ・herokuアカウントを所持している ・MySQLを使用 デプロイ方法 まずは、HerokuCLIをインストールしていきます。 なお、ここから以下のコマンドは自身のアプリケーションフォルダー内で行うことを推奨します。 % brew tap heroku/brew && brew install heroku インストール完了後に確認しましょう % heroku --version ここからherokuにログインします。 # Herokuへログインするためのコマンド % heroku login --interactive => Enter your Heroku credentials. # メールアドレスを入力し、エンターキーを押す => Email: # パスワードを入力して、エンターキーを押す => Password: さて、下準備はOKです。 ここから、実際にデプロイ手順を見ていきます。 まずは,heroku上でのアプリケーション名を作成します。ここでは、soccer_appにします。 % heroku create soccer_app さて、一応確認のため、以下のコマンドを打ちましょう % git config --list | grep heroku fatal: not in a git directoryが出ていなければOK!! さて、ここからデータベースの設定です。 今回は開発環境と同様にMySQLを使用するための設定を行います。ClearDBアドオンを追加することによって設定できます。 % heroku addons:add cleardb ClearDBアドオン(参考) ClearDBというデータベースサービスが提供しているアドオンで、これを追加することにより、HerokuでMySQLを使用できるようになります。 これでOK!!!と見せかけて、まだ設定は完璧ではありません。Ruby on Railsを使う場合は、MySQLに対応するGemについて考慮する必要があります。以下のコマンドでClearDBデータベースのURLを変数heroku_cleardbに格納します。 % heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL` 上記を踏まえ、今一度データベースのURLを再設定します。 % heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5} ゴールまで後少しです。 以下のコマンドで、credentials.yml.encをmaster.keyによって復号し、中身を確認します。 EDITOR="vi" bin/rails credentials:edit master.keyファイル credentials.yml.encの暗号文を復号する、鍵の役割を持ったファイルです。特定のcredentials.yml.encと対になっている。 credentials.yml.encファイル Railsにて、外部に漏らしたくない情報を扱う際に用いるファイル 次に、heroku上に環境変数としてmaster.keyの値を設置します。 (今は先ほど確認したようにローカルにある) % heroku config:set RAILS_MASTER_KEY=`cat config/master.key` 確認したい場合は以下のコマンドで確認 % heroku config 次にStackの準備です Stackとは、Herokuにおけるアプリケーションの動作環境のことです。Stackはデプロイされたアプリケーションを読み取り正常に稼働させるために用意されています。 heroku stack:set heroku-18 -a soccer_app いよいよラストスパート Herokuへアプリケーションの情報を追加しましょう。 % git push heroku master また、DBにもマイグレーションの情報を反映します   % heroku run rails db:migrate これで完了です!!!!!! あとは、URLを打ち込んで見てみましょう、、、 ってわからねえ!! そんなときは、以下のコマンドで確認してみましょう!! % heroku apps:info おわりに いかがでしたか ノーエラーでというのはなかなか大変かもしれませんが、頑張ってみてください!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows10で最小限のRails開発環境構築

目次 ・Git ・VSCode ・Ruby ・Node.js ・yarn ・Rails ・確認 Git こちらからダウンロード後、インストール。 セットアップでは何言ってるのかわからないのでそのままスキップを押しまくる。 終了後、Git Bashでユーザー名とEメールを登録する。 $ git config --global user.name "【ユーザー名】" $ git config --global user.email 【メールアドレス】 VSCode こちらからダウンロード。 インストールの際にVSCodeで開くやVSCodeを登録するにチェックを入れておくと後々便利。 完了したら設定をMSアカウントかGithubアカウントで紐付けておくと次からのインストールが楽になる。 Ruby こちらからWITH DEVKITの最新をダウンロード、インストール。 コマンドプロンプトが開いたらとりあえず1,2,3と入力しEnter。 Node.js こちらから適当にインストール。 yarn コマンドプロンプトで npm install --global yarn rails gem install rails でインストール。 確認 ターミナルで適当な場所に移動し、 rails new test_app を実行。完了後に cd test_app rails s を実行してブラウザで確認する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】新規アプリケーション作成の手順

はじめに 新規でアプリケーションを立ち上げ、データベースを作成するまでの手順をまとめようと思います。 手順 まずはディレクトリを移動します。 % cd ~/アプリケーションを作成するディレクトリ 次に新規アプリケーションを作成します。 % rails new アプリケーション名 アプリケーションを作成する際にバージョンやオプションを指定することも可能です。 以下の例ではバージョンを6.0.0、オプションをMySQLに指定しています。 % rails _6.0.0_ new アプリケーション名 -d mysql その次に作成したアプリケーションのディレクトリへ移動し、データベースを作成します。 % cd アプリケーション名 % rails db:create データベース作成までの手順は以上になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails g kaminari:views bootstrap4でNo such file or directoryと怒られた時の対処法

経緯 タイトルの件について、調べると/kaminari-core-1.2.1/lib/generators/kaminari/views_generator.rbにrequire 'open-uri'を追加する記事がすぐ見つかるが解決しなかった 結論 こちらのissue (https://github.com/kaminari/kaminari/issues/911) の言う通りにgemfileに以下のようにしてbundle installしなおすと解決した gem 'kaminari', :git => 'https://github.com/kaminari/kaminari' 補足 実行環境 # cat /etc/issue Debian GNU/Linux 10 \n \l # rails --version Rails 6.1.3.1 # ruby --version ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】中間テーブルの外部キーに紐づく、レコード一覧を取得する方法

はじめに Railsで特定の親テーブルのカラムのidに、紐づく子テーブルのレコードの一覧を取得したい時につまったのでメモとして残します。 経緯 親テーブルの特定のカラムのidに紐づく、中間テーブルのレコード一覧を取得します。 例題 ※前提としてarticlesテーブルに記事が投稿済みかを判定するカラムがあること articlesテーブルから投稿済みの記事を絞り込み、その投稿済みの記事に紐づくtag_article.rbテーブルのレコードを一覧を取得したい場合 tag.rb class Tag < ApplicationRecord has_many :tag_articles end tag_article.rb class Tag < ApplicationRecord belongs_to :tag belongs_to :tag_article end article.rb class Tag < ApplicationRecord belongs_to :tag_articles end 方法 以下のように関連テーブルを読み込んでおいてから、アソシエーションのカラムで絞り込むことで、親テーブルのレコードidにあった中間テーブルのレコード一覧を取得できます。 articles_controllers.rb . . . posted_articles = Article.preload(:tag_articles).where(posted: true) result = posted_articles.map(&:tag_artricles).flatten 補足 さらに絞り込んだレコード一覧をグループ分けも可能です。 group_idごとに、グループ分けをして一覧を取得することが可能です。 articles_controllers.rb result = posted_articles.map(&:tag_artricles).flatten.group_by(&:group_id) 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

devise導入後、開発途中からメール認証機能の追加

はじめに すでにdeviseがインストールされているアプリにメール認証機能を追加するやり方をメモします。 準備 メール送信にはGmail(2段階認証なし)を使用する。 そのためGoogleアカウントの設定で 「セキュリティ」から「安全性の低いアプリのアクセス」を「オン」にする必要がある。 マイグレーション 今回はすでにdeviseをインストールしているところにメール認証機能を追加するため、Userにいくつかのカラムを加える必要がある。 rails g migration add_confirmable_to_users 生成されたマイグレーションファイルを次の様に編集する。 class AddConfirmableToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :confirmation_token, :string add_column :users, :confirmed_at, :datetime add_column :users, :confirmation_sent_at, :datetime end end そしてdb:migrateする。 rails db:migrate Userモデルの編集 次はUserモデルに:confirmableを追加する。 app/models/user.rb class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable #追加部分 end Configの編集 config/environments/development.rbに次のコードをする。 config/environments/development.rb Rails.application.configure do   #省略 config.action_mailer.default_url_options = { host: 'localhost', port: 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 => "Gmailアドレス", :password => "Gmailパスワード", :authentication => :plain, :enable_starttls_auto => true } end そしてconfig/initializer/devise.rbを編集する。 config/initializer/devise.rb #ここのメールアドレスをGmailに編集する。 config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' #元々trueだったのをfalseにする。 config.reconfirmable = false これでユーザー登録時にメールが送信される様になる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

投稿した画像にリンクをつける

忘備録(自分用) 環境 Mac ruby 2.6.5 解決したい内容 <%= link_to image-tag(item.image, class: "item-img" ),item_path(item) do %>の記述を追加すると、「NoMethodError in Items#index」を表示する。 投稿した画像をクリックすると、画像の詳細画面に遷移したい。 解決策 link_toの記載を見直す。 <% @items.each do |item| %> <li class='list'> <%= link_to image-tag(item.image, class: "item-img" ),item_path(item) do %> <div class='item-img-content'> <% end %> <% end %> 上記を下記に修正 <% @items.each do |item| %> <li class='list'> <%= link_to item_path(item) do %> <div class='item-img-content'> <%= image_tag item.image, class: "item-img" %> <% end %> <% end %> link_to とimage_tagを一緒にせず、do〜endの中に分けて記述することで、解決。 一緒に記述する方法もありそうだが、現時点では、エラーが出るため、上記にて対応。 参考にした記事 https://ja.stackoverflow.com/questions/30523/image-tag%E3%81%A8link-to%E3%81%AE%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E3%81%A7%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E8%B5%B7%E3%81%8D%E3%81%BE%E3%81%99)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

名前空間を用いている場合のform_withの引数に関して

はじめに クチコミサイト作成中にadminユーザーにて、商品の新規登録をしようとするとルーティングエラーが発生しました。 かなり初歩的なミスで恐縮ですが、備忘録も兼ねて投稿します。 routes namespace :admin do resources :items, only:[:new, :create, :show,:edit, :update, :destroy] end このような形で管理者ユーザー(admin)と商品(item)を名前空間にて記述していました。 これは管理者ユーザーでのみ、商品の新規登録や削除等の操作をしたかったためです。 エラー内容 エラーが発生している当時は以下のように記載していました。 views/admin/items/admin_items.erb <%= form_with(model: @item, local: true) do |f| %> そして商品を新規登録しようとすると、「そんなルーティングない」とエラーで弾かれてしまいます。 解決 正しくは以下のように記載する必要がありました。 views/admin/items/admin_items.erb <%= form_with(model:[:admin, @item], local: true) do |f| %> modelの引数にadminを追記してあげる必要があり、たったこれだけのことでした。 ちなみにこれはrailsガイドにまんま記載があります…。 https://railsguides.jp/form_helpers.html まとめ 名前空間を利用している際のform_withの引数は注意が必要と分かりました。 また、まずrailsガイドを確認するといった習慣がついていなかったので、気をつけていこうと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】"RubyOnRails"色々な機能まとめ

はじめに 始めまして、hikaruです。記事を読んで頂きありがとうございます。 今回初投稿です。これから頑張って記事を投稿していこうと思います。 みなさんのお役にたてればと、自身が調べてわかりやすい!と思った記事をまとめてみました。 実際に私は某プログラミングスクールの生徒ということもあり、初心者の目線で記事をピックアップしています。 ※注意点:わかりやすいと思ったのは私の主観です。 この記事は、どんな人の役にたてるの? ・Railsの基礎を勉強中の人。 ・スクールの課題が終わって新しいことに挑戦したい人。 ・検索がうまくできなかった人(理解しやすい記事が見つからない)。 ・各機能のまとめがみたかった人。 どんなものをピックアップしたの? 【ピックアップした内容】 ・いいね機能、コメント機能 ・フォロー/フォロワー機能 ・検索機能について ・いいね機能、コメント機能の非同期 ・住所検索機能 ・メール送信機能 ピックアップした記事一覧 いいね機能、コメント機能(同期) :【Rails、JavaScript】いいね機能を実装(同期・非同期通信) 備考:同期と非同期を両方記載してくれていますが、非同期についてはもっとわかりやすいものがあったので、後述紹介。 フォロー/フォロワー機能:【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】 備考:アソシエーションの事を分かりやすくまとめてくれています。 図がある為、理解しやすく初心者目線で書いてくれている記事だと思いました。 検索機能:Rails 検索機能の実装 備考:解説がきちんとされていて、わかりやすい記事です! いいね機能、コメント機能の非同期:【Rails×Ajax】いいね機能ハンズオン 備考:フォロー/フォロワー機能の記事を書いてくれている人と同じ人です!こちらも図があり、理解しやすいのが特徴です。 住所検索機能:【Rails】jpostalとjp_prefectureを用いて住所自動入力の実装 備考:住所検索機能についてわかりやすくまとまっています! こちらの記事にでてくるjp_prefecture gemのことも調べたのでよかったらはっときます。  jp_prefecture gem とは? メール送信機能:【Rails】 Action Mailerとdeviseをつかって登録完了メールを自動送信してみる 備考:図があり、要点がよくまとめられていた為、理解しやすかったです! こちらも絡んでくるので参考までに。 【Rails】configのenvironments配下のファイルの意味と記述内容を理解する。 さいごに 記事はあくまでも理解する為の参考です。思考停止でコピペはやめましょう。 そしてこちらの記事のまとめは仕掛中です。なにかわかれば随時更新していき、皆さんのお役にたてるように情報発信していきます。 記事を見て頂きありがとうございました!  ※ミスや質問などありましたら、コメント下さい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] RSpec ver.3.9.1 で遭遇したエラーへの対処談

■ 本記事の概要 本記事はRSpec ver.3.9.1 を下記開発環境で試した際に遭遇したエラーの紹介と、その時の対処体験談になります。 ■ 開発環境 OS:macOS Big Sur 11.3 Ruby:3.0.0 Ruby on Rails:6.1.3 ローカル環境DB:Mysql テキストエディタ:Visual Studio Code ■ 導入を試みた際のGemfile(該当箇所のみ) 上記の開発環境に、RSpec ver.3.9.1を導入するべく、Gemfileを下記のように記載してbundle installを実行しました。 Gemfile group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] ####### ↓追加↓ ####### gem 'rspec-rails', '~> 3.7' gem 'factory_bot_rails', '~> 4.11' ####### ↑追加↑ ####### end ターミナル $ bundle install bundle installも無事済んだので、テストコードを書いてbundle exec rspecを実行してテストを走らせました。 ターミナル $ bundle exec rspec ■ 起きたエラー(LoadError: ...) すると、見るからにエラーが発生したと思われる赤字の出力が返ってきました。 エラー文上部にあるLoadError: cannot load such file -- rexml/documentから察するに、rexml/documentが存在しないのか、上手く読み込めなかったことによるエラーなのかもしれません ■ エラー内容をGoogleで検索 まずは、同じようなエラーが起きていないかGoogleで検索してみました。 検索キーワードは至って簡単にLoadError: cannot load such file -- rexml/documentとして検索 すると、RSpec ではありませんが、今回と同じようにLoadError: cannot load such file -- rexml/documentというエラーが発生していると書かれたIssuesがあったので、ざっと目を通してみました。 英語ができる方ではない私ですが、中段辺りの記述にRuby 3.0.0からrexmlはデフォルトではなくなったので、gem 'rexml'を加えれば問題が解決するかもといった記載を見つけることができました (公式ドキュメントも確認してみたら、ちゃんと書いてありました) 現時点ではそれで問題が解決できるかは分かりませんでしたが、試してみる価値はあると思って、gem 'rexml'を加えてみることに。 ■ gem 'rexml'を追加 development環境では、rexmlが無くてもアプリケーション自体は問題なく動いていたので、test環境のみにrexmlを追加して、bundle installを実行してからのbundle exec rspecの実行 Gemfile group :test do gem 'capybara', '>= 3.26' gem 'selenium-webdriver' gem 'webdrivers' ##### ↓追加↓ ##### gem 'rexml' ##### ↑追加↑ ##### end ターミナル $ bundle install $ bundle exec rspec ■ 成功か...はたまたエラーか... 結果は赤字でした エラー分にある'name'が原因かと思って、重点的にチェックを行いましたが、書いたコード自体には不備はなさそうだったので、raise WrongScopeError rspecで検索してみることに すると、@aotaro1994さんが書かれた【Rspec】Error: raise WrongScopeError の解消【原因:rspec_railsのバージョン】の記事がトップに表示されたので拝読しました。'name'がエラーの原因になっているところも同じだったので、@aotaro1994さんの対処法を試させていただきました。 (@aotaro1994さん、ありがとうございました!) 参考記事:【Rspec】Error: raise WrongScopeError の解消【原因:rspec_railsのバージョン】 :@aotaro1994さん著 ■ テスト成功も...違和感 @aotaro1994さんの対処法を実行後、bundle installを実行してからのbundle exec rspecの実行 結果は... ...なんか...以前触った時と違くない?以前はもっとテスト項目も表示されてたような... ■ オプションの追加 どうやって以前テスト項目も表示させていたのか失念してしまったので、取り敢えずオプションを追加して表示させることに。 ターミナル $ bundle exec rspec -f d すると...上記の画像では隠してしまいましたが、テスト項目も一緒に表示されました ■ 最後に 以上が今回私が行った対処になります。 同じようなエラーが発生した方の参考になれば嬉しいです! 最後に ここまで読んでいただき、ありがとうございました!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】マイグレーション実行時にMysql2::Error: Duplicate entry for key..が出た

Railsでテーブルのデータに対して一意性制約を付与するマイグレーションファイルを実行しようとした際に上記エラーが発生 class AddIndexToUserBooks < ActiveRecord::Migration[6.0] def change add_index :user_books, [:user_id, :book_id], unique: true end end マイグレーションの内容としては中間テーブルのデータの組み合わせに対して一意性制約を設けるもの。 原因と解決策 すでにテーブルに存在しているデータが一意性制約に反していた。 上記例でいれば、同じuserとbookの組み合わせがすでにテーブルに挿入されていることが原因で一意性制約を付与できない状態だった。 これをテーブルから直接削除して解決 所感 Duplicate entryはマイグレーション実行時には発生しないと思っていたので一瞬焦りました。 マイグレーションが実行される場合はすでにテーブルに存在しているデータとの整合性も判定されているということですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【本番環境】【備忘録】rails db:create RAILS_ENV=production でrails aborted! Mysql2::Error::ConnectionError: Access denied for user 'root'@'localhost' (using password: NO)とエラーが出る問題の対処法

AWSのサービスである、EC2とRDSを用いてローカルで作成したRailsのアプリをデプロイしようと作業を進めています。 https://qiita.com/naoki_mochizuki/items/5a1757d222806cbe0cd1 上記記事に従って rails db:create RAILS_ENV=production rails db:migrate RAILS_ENV=production を行ったのですがタイトルのエラーが発生・・・ エラー文 rails aborted! Mysql2::Error::ConnectionError: Access denied for user 'root'@'localhost' (using password: NO) /var/www/rails/アプリ名/bin/rails:9:in `<top (required)>' /var/www/rails/アプリ名/bin/spring:15:in `<top (required)>' bin/rails:3:in `load' bin/rails:3:in `<main>' Tasks: TOP => db:create (See full trace by running task with --trace) 試したこと ①$ mysql -u root -p の実行 MySQL Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 12 Server version: 5.7.34 MySQL Community Server (GPL) Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 特に問題なく入れた。 ②config/database.ymlを開き、パスワードを記載 いつも使っているのがVSコードなので、VSコードにてdatabase.ymlを開く。 本番環境用のパスワードの欄が空欄だったため、記載する。 しかし全く同じエラー。どうして・・・・・? 結論 ローカル環境ではなく、「本番環境(EC2)」にてconfig/database.ymlを開き、パスワードを記載すると解決します! (ローカル環境にてdatabase.ymlを編集していたので、本番環境への影響がなく、全く意味のないことをしていたようです。。) 手順(注意:すべて本番環境の中で実行する) asami|アプリ名 $ vi config/database.yml database.yml production: <<: *default database: アプリ名_production username: root password: XXXXX # ←ここに記述 無事に通りました! 本当に単純すぎる間違いでかなり時間をロスしました・・・・・
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubocup導入

Rubocopとは Rubocop(ルボコップ)はRubyの静的コード解析ツール(Gem)です。 「インデントが揃っていない」「余分な改行・スペースがある」などの指摘をRubyStyleGuideに基づいて行ってくれます。 Rubocopを導入することにより、レビューに掛かる時間を減らし、コードの品質を担保できるようになります。 Rubocopの指摘は設定で変更が可能です。(下記は一例) インストール Gemfile group :development do gem 'rubocop', require: false end ターミナル bundle install 上記コマンドで、Rubocopを導入できました。 Rubocopの設定 アプリ直下(Gemfileなどと同じ場所)に 「.rubocop.yml」 ファイルを作ります。 ターミナル touch .rubocop.yml ここでは対象から除外するファイル・ディレクトリの指定だったり、無視するエラーの指定だったりのカスタマイズをします。 .rubocop.yml AllCops: # 除外するディレクトリ(自動生成されたファイル) # デフォルト設定にある"vendor/**/*"が無効化されないように記述 Exclude: - "vendor/**/*" # rubocop config/default.yml - "db/**/*" - "config/**/*" - "bin/*" - "node_modules/**/*" - "Gemfile" # 1行あたりの文字数をチェックする Layout/LineLength: Max: 130 # 下記ファイルはチェックの対象から外す Exclude: - "Rakefile" - "spec/rails_helper.rb" - "spec/spec_helper.rb" # RSpecは1つのブロックあたりの行数が多くなるため、チェックの除外から外す # ブロック内の行数をチェックする Metrics/BlockLength: Exclude: - "spec/**/*" # Assignment: 変数への代入 # Branch: メソッド呼び出し # Condition: 条件文 # 上記項目をRubocopが計算して基準値を超えると警告を出す(上記頭文字をとって'Abc') Metrics/AbcSize: Max: 50 # メソッドの中身が複雑になっていないか、Rubocopが計算して基準値を超えると警告を出す Metrics/PerceivedComplexity: Max: 8 # 循環的複雑度が高すぎないかをチェック(ifやforなどを1メソッド内で使いすぎている) Metrics/CyclomaticComplexity: Max: 10 # メソッドの行数が多すぎないかをチェック Metrics/MethodLength: Max: 30 # ネストが深すぎないかをチェック(if文のネストもチェック) Metrics/BlockNesting: Max: 5 # クラスの行数をチェック(無効) Metrics/ClassLength: Enabled: false # 空メソッドの場合に、1行のスタイルにしない NG例:def style1; end Style/EmptyMethod: EnforcedStyle: expanded # クラス内にクラスが定義されていないかチェック(無効) Style/ClassAndModuleChildren: Enabled: false # 日本語でのコメントを許可 Style/AsciiComments: Enabled: false # クラスやモジュール定義前に、それらの説明書きがあるかをチェック(無効) Style/Documentation: Enabled: false # %i()構文を使用していないシンボルで構成される配列リテラルをチェック(無効) Style/SymbolArray: Enabled: false # 文字列に値が代入されて変わっていないかチェック(無効) Style/FrozenStringLiteralComment: Enabled: false # メソッドパラメータ名の最小文字数を設定 Naming/MethodParameterName: MinNameLength: 1 Rubocopには、他にも以下のような標準設定がしてあります Rubocop 標準設定 備考 文字列 シングルクォーテーション 式展開や文字列内にシングルクォーテーションがある場合はダブルクォーテーションを使う  変数名とメソッド名 キャメルケースで定義する 修正の流れ ターミナル #修正内容を確認 bundle exec rubocop ターミナル # 修正内容を自動修正 bundle exec rubocop -a # または bundle exec rubocop --auto-correct 以上で、Rubocopが自動でチェックしながらコードを勝手に綺麗にしてくれます。 とてもありがたい機能ですね!! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む