- 投稿日:2020-11-27T23:45:57+09:00
ActiveHashを使ってカテゴリーの選択を実装
ActiveHashとは「基本的に変更されないデータの取り扱い」があったとします。このデータはデータベースに保存する必要がありません。一方、ビューファイルなどにそれらのデータを直接書いてしまうと、可読性に欠けます。そのようなケースでは、ActiveHashが有用です。
ActiveHashをインストール
gem 'active_hash'モデル生成(今回はカテゴリー選択を"genre"とします)
rails g model genre --skip-migrationこの時のモデル作成には「--skip-migration」を使用しております。
これはモデルファイルを作成するときに、マイグレーションファイルの生成を行わないためのオプションです。
選択したジャンルの情報はデータベースに保存しないため、マイグレーションファイルを作成する必要がないためです。ActiveHash::Base
ActiveHash::Baseは、あるモデル内(クラス内)でActiveHashを用いる際に必要となるクラスです。
app/models/genre.rb
class Genre < ActiveHash::Base self.data = [ { id: 1, name: '--' }, { id: 2, name: '経済' }, { id: 3, name: '政治' }, { id: 4, name: '地域' }, { id: 5, name: '国際' }, { id: 6, name: 'IT' }, { id: 7, name: 'エンタメ' }, { id: 8, name: 'スポーツ' }, { id: 9, name: 'グルメ' }, { id: 10, name: 'その他' } ] include ActiveHash::Associations has_many :articles endマイグレーションファイル内
class CreateArticles < ActiveRecord::Migration[6.0] def change create_table :articles do |t| t.string :title , null: false t.text :text , null: false t.integer :genre_id , null: false t.timestamps end end endターミナル
% rails db:migrate親モデルのアソシエーションを設定
app/models/article.rb
class Article < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :genre validates :title, :text. presence: true validates :genre_id, numericality: { other_than: 1 } endapp/views/articles/new.html.erb
<%= form_with model: @article, url:articles_path, local: true do |f| %> <div class="article-box"> 記事を投稿する <%= f.text_field :title, class:"title", placeholder:"タイトル" %> <%= f.text_area :text, class:"text", placeholder:"テキスト" %> <%= f.collection_select(:genre_id, Genre.all, :id, :name, {}, {class:"genre-select"}) %> <%= f.submit "投稿する" ,class:"btn" %> </div> <% end %>以上備忘録でした。
- 投稿日:2020-11-27T23:38:04+09:00
deviseを使って、ユーザー情報の編集・更新について
はじめに
昨日、deviseでユーザーマイページの詳細ページがない、ということを投稿したが、アプリの制作を進めていると、編集ページは見つけたので、記録しておく。
ユーザー情報の編集ページはdeviseにあった!
ターミナルで
rails routesをすると、
edit_user_registration GET (URI略) users/registrations#edit user_registration PATCH (URI略) users/registrations#update
views/devise/registrations/edit.html.erb
ファイルに、ユーザーの編集ページとして、デフォルトであり、パスワードやメールアドレスの編集ができるようになっている。このファイルにlink_to
で飛ばすと、簡単に編集・更新できる。
デフォルトであるビューで、更新機能も備わっている。
更新が成功すると、ルートパスにリダイレクトもされる。最後に
おそらく、コントローラーに記述があるんだろうけど、どの部分か見つけられない今日この頃。
- 投稿日:2020-11-27T23:21:26+09:00
Rails RSpec による画像添付結合テスト
はじめに
RSpecによるオリジナルアプリの結合テストを行っています。画像投稿機能において、「テスト用の画像を添付する」、「添付したテスト用の画像が画面上にあるか」の2つを忘れていたので、備忘録として書き記します。
#目次
1.テスト用の画像を添付する
2.添付したテスト用の画像が画面上にあるかテストする1.テスト用の画像を添付する
ここではimage_pathという変数にテスト用画像(test_image.jpg)を添付する。この時相対パスで指定する。
attach_fileメソッドはアップロードのinput要素にテスト用画像を添付することができる。第一引数にアップロードするinput要素のname属性の値、第二引数にアップロードする画像のパス、第三引数にオプション(ここではmake_visible: trueで一時的に表示)を設定する。spec/system/◯◯_spec.rb# 添付する画像を定義する image_path = Rails.root.join('public/images/test_image.jpg') # 画像選択フォームに画像を添付する attach_file('desk[image]', image_path, make_visible: true)2.添付したテスト用の画像が画面上にあるかテストする
画像を投稿すると、画像一覧ページに遷移する。遷移先で投稿した画像があるかの確認は下記コードに記す。have_selectorで要素があるか判断する。ちなみにhave_content()で文字列があるかのマッチャになる。
spec/system/◯◯_spec.rbexpect(page).to have_selector("img[src$='test_image.jpg']")以上
- 投稿日:2020-11-27T21:41:56+09:00
【Rails】Ajaxでいいねの数を表示する方法
概要
前回の続きでいいねカウントの実装をしていいねの数を表示していきたいと思います。
APIの作成
まずJavaScriptからリクエストが来たときに返したいデータを定義していきます。
JavaScriptでリクエストを送ったら現在のいいね数をresponseで返すことができれば、実装できると思いました。いいねの数を取得するために、
LikesControllerのshowアクション内でlike_countを定義
しました。app/controllers/likes_controller.rbdef show like_count = @team.likes.count render json: { likeCount: like_count } endただ、私が実装したいことは
現在のいいね数を
DOMContentLoaded
イベント時に取得することだけでなく、POSTとDELETEのリクエストの時にも取得したいと考えています。
そのため、showアクション内にだけ定義するのではなくcreate
,destroy
にも定義していく必要があります。create
とdestroy
でもrender json
にlikeCount
を入れないとJavaScriptからのリクエストに対応するデータがないのでundefinedになって返ってきてしまいます。前回と重複するところもありますが、
LikesController
の記述はこのようになると思いました。before_action :like_counts
でアクションの最初に読み込むようにしています。LikesController
app/controllers/likes_controller.rbclass LikesController < ApplicationController before_action :authenticate_user! before_action :set_like before_action :like_counts def show like_status = current_user.has_liked?(@team) render json: { hasLiked: like_status, likeCount: like_counts }.to_json end def create @team.likes.create!(user_id: current_user.id) render json: { status: 'ok', likeCount: like_counts }.to_json end def destroy like = @team.likes.find_by!(user_id: current_user.id) like.destroy! render json: { status: 'ok', likeCount: like_counts }.to_json end private def set_like @team = Team.find(params[:team_id]) end def like_counts @like_count = @team.likes.count end endどこにいいねの数を表示するのか
show.html.haml.star_counter 現在 %span がいいねをしています。今回、star_counterクラス内のspanタグ内にいいねの数を入れていこうかと思います。
span
を入れた理由はいいねの数をscss
でスタイルを調整しようと考えているからです。たとえば、以下のようにすればいいねの数だけ文字が赤く太くなります。SCSS
scss.star_counter { span { color: red; // 好きな色 font-weight: bold; // 線を太くして強調 } }いいねの数を取得(GET)
JavaScriptで実装したいこととしてはまず、GETリクエストで現在のいいねの数を取得することです。
そのため、GETリクエストが成功したらいいねの数を表示させていきます。コードにするとこのようになると思いました。 ※前回と重複するところは省略しております。
jQuery// GET, POST, DELETEリクエストで使うので処理をまとめています。 const likeCountCalculation = (likeCount) => { // いいねの数を表示させたいところを取得。今回はstar_counterクラス内のspan要素を取得しています。その後appendで追加していくという処理です。 $('.star_counter > span').append( `${likeCount}人` ) } // axiosでGETリクエスト成功 .then((response) => { // response内に先程Controllerで書いたlikeCountがjson形式で返されるので取得。 const likeCount = response.data.likeCount // 上で定義してあるlikeCountCalculation()が呼び出されて、span要素内に現在のいいねの数が表示されるようになります。現在のいいねの数はlikeCountに入っているので関数を呼び出すときにを引数にいれています。 likeCountCalculation(likeCount) })いいねの数の増減 (POST, DELETE)
はじめは、
likeのcountを+1, -1
することで実装すればと思っていたのですが、それよりもGETの処理と同じ考え方で現在のいいねの数を取得すれば良いのでは?と考えました。現在のというのは+1や-1されたあとのいいねの数のことです。要するに、POSTやDELETEリクエストが成功すればいいねの数は増減しているのでその値を取得することで実装できると思いました。
コードにするとこのようになると思います。POSTもDELETEも同じ記述なのでまとめて書かせていただきます。GETの処理と違う点は、リクエストが成功したらspan要素の中身を空にするということです。これをしないとリクエストの度に要素が追加されていってしまいます。jQuery// axiosでリクエスト(POST or DELETE)成功 .then((response) => { // responseに入ってくるいいねの数を取得 const likeCount = response.data.likeCount // star_counterクラス内のspan要素の中身を空にする。 $('.star_counter > span').html('') // 現在いいねの数を表示する。 likeCountCalculation(likeCount) })まとめ
- 前回同様やりかたは一つではないこと。
- 親要素内の子要素を取得する方法もひとつではなく選択肢がたくさんあり、わかりやすいものを使う。(読みやすいコードだったり状況に応じて判断)
- jsonで複数の値を返すには記述を変える必要がある。
最後に
自分の実装したいことをどうやって実現していくのか、ここの言語化が大事だと感じました。実装したいことに対して、いくつもの方法があるということは、簡単に書けるものだったり逆に複雑になってしまったりすることがあると思います。こういったところもたくさんコードを書いたり技術書を読んだり、記事を見たりして学習していくことで力をつけていきます。
参考文献
- length、size、count メソッドの違いまとめ【Ruby】
- Ruby 2.7.0 リファレンスマニュアル
- 【Rails】JSON形式のデータを返却する方法とは?
- jQueryで子要素を取得するいくつかの方法〜children,find,contents
- jQuery で innerHTML の書き換え/取得/追記/削除を行う方法
前回書いた記事です。いいねの実装をこちらでしているのでよろしければご覧ください!
- 投稿日:2020-11-27T20:28:43+09:00
Ruby on Railsでdeviseとscaffoldを組み合わせた際に調べたことをまとめてみた。
はじめに
Ruby on Railsでdeviseとscaffoldを使って簡易アプリを作っているときに分からないコードなど、調べたことを自分用にまとめてみました。
環境
MacOS Catalina version 10.15.7
Rails 6.0.3.4
devise 4.7.3
前提
$ rails new アプリ名
でアプリができている状態
deviseの導入
以下のurlのページを参考にdeviseでログイン機能等を作成する。
rails g devise:install
でファイルの作成を終えた後、下のコードを記述する。config/environments/development.rbconfig.action_mailer.default_url_options = { host: 'localhost', port: 3000 }こちらのページ⇨ https://qiita.com/Orangina1050/items/a16e655519a60f35b394
scaffoldの導入
既に作ってあるアプリにディレクトリを移動した後、以下の形式でscaffoldを実行する。
$ rails g scaffold モデル名 カラム1:データ型1 カラム2:データ型2
自分は自己紹介をする簡易アプリなので以下のカラムを持たせた。
$ rails g scaffold intro name:string hometown:string text:contentscaffold実行後以下を行う。
$ rails db:migrateカラムをつけ忘れて追加したい場合
rails g migration 行なう処理Toテーブル名 カラム名:データ型
の形式で実行する。カラムを加えるなら、AddColumnToテーブル名
というように書く。自分は、user_idのカラムをつけ忘れたため、
$ rails g migration AddColumnToIntros user_id:integerを行った。
rails db:migrate
も忘れずに。コントローラーに設定を加える
scaffoldによってできたコントローラー(intros_controller.rb)の一番上に、
before_action :authenticate_user!
を記入する。before_action :authenticate_user!とは
authenticate_user!はdeviseを入れる事で使えるようになるヘルパーの一つで、
コントローラーに設定してログイン済ユーザーのみにアクセスを許可する。
参考 https://qiita.com/ryuuuuuuuuuu/items/bf7e2ea18ef29254b3dd
次に、newアクション内に下記を追加する。
app/controllers/intros_controller.rb@intro = Intro.find_or_create_by(:user_id => current_user.id)find_or_create_byとは
は引数の条件に該当するデータがあればそれを返し
find_by(attributes)
、なければ新規作成create(attributes, &block)
します。今回は、user_idがcurrent_user.idの場合、それを取得、なければ作成を行っている。
参考 https://qiita.com/taimuzu/items/0a21738d018f475d63ae自己紹介を既に作成しているユーザーが新規作成のボタンを押したときに編集ページにとんでほしいのでnewアクションに
app/controllers/intros_controller.rbredirect_to edit_intro_url(@intro)を追記する。これは
redirect_to edit_intro_url(@user.id)
と同じことをやっている。
参考 https://qiita.com/Kawanji01/items/96fff507ed2f75403ecbredirect_toのパス確認
さっき指定したリダイレクト先は、以下のコマンドを打つと様々なパスが出てくるので、そこから対象のものを探す。
edit_intro_url
の部分。$ rails routes最後に、editアクションにidがcurrent_user.idかそうでないか分岐する処理を書く。
app/controllers/intros_controller.rbif @intro.user_id != current_user.id flash[:notice] = "他のユーザーの編集はできません。" redirect_to intros_path endその際に、もとからindex.html.erbとshow.html.erbに書かれている、
<p id="notice"><%= notice %></p>
をコメントアウトすることで、flash[:notice]の内容を表示することができる。
- 投稿日:2020-11-27T20:28:43+09:00
Ruby on Railsでdeviseとscaffoldを組み合わせた際に調べたところをまとめてみた。
はじめに
Ruby on Railsでdeviseとscaffoldを使って簡易アプリを作っているときに分からないコードなど、調べたことを自分用にまとめてみました。
環境
MacOS Catalina version 10.15.7
Rails 6.0.3.4
devise 4.7.3
前提
$ rails new アプリ名
でアプリができている状態
deviseの導入
以下のurlのページを参考にdeviseでログイン機能等を作成する。
rails g devise:install
でファイルの作成を終えた後、下のコードを記述する。config/environments/development.rbconfig.action_mailer.default_url_options = { host: 'localhost', port: 3000 }こちらのページ⇨ https://qiita.com/Orangina1050/items/a16e655519a60f35b394
scaffoldの導入
既に作ってあるアプリにディレクトリを移動した後、以下の形式でscaffoldを実行する。
$ rails g scaffold モデル名 カラム1:データ型1 カラム2:データ型2
自分は自己紹介をする簡易アプリなので以下のカラムを持たせた。
$ rails g scaffold intro name:string hometown:string text:contentscaffold実行後以下を行う。
$ rails db:migrateカラムをつけ忘れて追加したい場合
rails g migration 行なう処理Toテーブル名 カラム名:データ型
の形式で実行する。カラムを加えるなら、AddColumnToテーブル名
というように書く。自分は、user_idのカラムをつけ忘れたため、
$ rails g migration AddColumnToIntros user_id:integerを行った。
rails db:migrate
も忘れずに。コントローラーに設定を加える
scaffoldによってできたコントローラー(intros_controller.rb)の一番上に、
before_action :authenticate_user!
を記入する。before_action :authenticate_user!とは
authenticate_user!はdeviseを入れる事で使えるようになるヘルパーの一つで、
コントローラーに設定してログイン済ユーザーのみにアクセスを許可する。
参考 https://qiita.com/ryuuuuuuuuuu/items/bf7e2ea18ef29254b3dd
次に、newアクション内に下記を追加する。
app/controllers/intros_controller.rb@intro = Intro.find_or_create_by(:user_id => current_user.id)find_or_create_byとは
は引数の条件に該当するデータがあればそれを返し
find_by(attributes)
、なければ新規作成create(attributes, &block)
します。今回は、user_idがcurrent_user.idの場合、それを取得、なければ作成を行っている。
参考 https://qiita.com/taimuzu/items/0a21738d018f475d63ae自己紹介を既に作成しているユーザーが新規作成のボタンを押したときに編集ページにとんでほしいのでnewアクションに
app/controllers/intros_controller.rbredirect_to edit_intro_url(@intro)を追記する。これは
redirect_to edit_intro_url(@user.id)
と同じことをやっている。
参考 https://qiita.com/Kawanji01/items/96fff507ed2f75403ecbredirect_toのパス確認
さっき指定したリダイレクト先は、以下のコマンドを打つと様々なパスが出てくるので、そこから対象のものを探す。
edit_intro_url
の部分。$ rails routes最後に、editアクションにidがcurrent_user.idかそうでないか分岐する処理を書く。
app/controllers/intros_controller.rbif @intro.user_id != current_user.id flash[:notice] = "他のユーザーの編集はできません。" redirect_to intros_path endその際に、もとからindex.html.erbとshow.html.erbに書かれている、
<p id="notice"><%= notice %></p>
をコメントアウトすることで、flash[:notice]の内容を表示することができる。
- 投稿日:2020-11-27T20:14:00+09:00
GitHub Actionsを使ってRailsアプリをデプロイしてみた
GitHub Actions初めて使ってみました。設定楽でいいですね。
使い方(ざっくり)
- プロジェクトルートに
.github/workflow
ディレクトリを作成- その中にymlファイルを作って、実行したい処理を書く
- 環境変数を使いたい場合はGitHubにアクセスして
Settings→Secrets
で設定する
- ymlファイル内で
${{ secrets.HOGEHOGE }}
でアクセスできる使い方(実際やったこと)
masterブランチが更新された時にRailsアプリをEC2にデプロイする設定をしました。
まずGitHub上で3つの環境変数(PRIVATE_KEY
、USER_NAME
、HOST_NAME
)を登録。
その後アプリに戻り以下を実行。$ midir -p ./github/workflow $ touch ./github/workflow/rails.yml/github/workflow/rails.ymlname: Rails CI/CD ## ↓実行タイミング on: push: branches: [master] pull_request: branches: [master] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Deploy # ↓環境変数 env: PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} USER_NAME: ${{ secrets.USER_NAME }} HOST_NAME: ${{ secrets.HOST_NAME }} # ↓実行したいコマンド run: | echo "$PRIVATE_KEY" > private_key && chmod 600 private_key ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOST_NAME} 'cd sample_app && git pull origin master && ~/.rbenv/shims/bundle install && ~/.rbenv/shims/bundle exec rails assets:precompile RAILS_ENV=production && ~/.rbenv/shims/bundle exec rails db:migrate RAILS_ENV=production && kill $(cat tmp/pids/puma.pid) && ~/.rbenv/shims/rails s -e production'masterブランチが更新された時、GitHubのActionsタブにアクセスすると、作成したワークフローが正常に動いているか確認することができます。
- 投稿日:2020-11-27T17:22:07+09:00
Rails seedファイルによる初期データの設定
HerokuでRailsアプリをデプロイした際に自身のアプリケーションでは管理権限のあるユーザーしかユーザーを作成出来ない仕様のアプリケーションなのですが、root画面がログイン画面なので操作出来ないと思い色々考えていたらseedファイルという便利なものがあるのを知り導入しました。
seedファイルとは
初期データを記述できるファイルです。これがある事で上の件であったり、データベースを作り直す度にテストユーザーの作成時間を短縮する事が出来ます。
db/seeds.rbに記述します。
User.create!( name: 'テスト', email: 'test@test.com', password: 'xxxxxx', admin: true )開発環境に反映する
$rails db:seedHeroku: 本番環境に反映する
heroku run rake db:seed↑がないと本番環境に反映されません。
ちなみになんですが恥ずかしながら、HerokuでもRails cが使えるのを知らなかったです。
- 投稿日:2020-11-27T17:04:43+09:00
[Rails]deleteメソッドとdestroyメソッドの違いについて!![初心者]
はじめに
先日、とあるアプリケーションを作成していて、
deleteメソッド
とdestroyメソッド
のどちらを使うべきか、分からなくなってしまったので、備忘録も兼ねてアウトプットします!!deleteメソッドについて
指定した条件のレコードを、SQLを直接実行することで削除します。
ActiveRecordを介さないということなので、モデルを経由しません。
また、モデルで関連付けされているレコードがあったとしても、そちらは削除されません。User.find(id: 1).deletedestroyメソッドについて
ActiveRecordを介して、指定した条件のレコードを削除するメソッドです。
モデル内で、dependent: :destory
が設定されていれば、関連付けされているデータも併せて削除されます。
ActiveRecordを介しているので、callback
メソッドやバリデーションを機能させることもできます。メソッドの実行時にエラーが発生し、削除ができなかった場合は、
false
を返すだけで、例外は返却されません。
なので、「とりあえずデータを削除したいが、失敗した場合はエラーを返却してほしいな...」という場合は、destroy!メソッド
を使用すれば大丈夫です。User.find(id: 1).destroy例えば、Userモデルに多数のBookモデルが関連付けされている場合、
id: 1
のユーザーが投稿したbook
に関しても、同時に削除がされることになります。ActiveRecordって何なん??
ActiveRecordとは、
RubyとSQLの翻訳機
のようなイメージです。本来、DBで使用されるDB言語には、SQLが使用されています。
しかし、Railsが開発してくれているActiveRecord
のおかげで、我々はRuby
を使用して、DBからデータを探したり、持って来れたり出来るというわけなのです!!
非常に便利な機能ですね。delete_allとdestroy_allについて
まとめて紹介しますが、こっちを使用すると、条件に合致する
複数
のレコードを同時に削除することが可能です。
whereメソッド
と同時に使われることが多いです!!Book.where(user_id: 1).delete_all Book.where(user_id: 1).destroy_allおわりに
Railsには、似通っている機能が多数存在するので、混乱することも多いですが、一つ一つ丁寧に理解していくことが、上達への近道になりそうですね。
- 投稿日:2020-11-27T16:20:42+09:00
railsでマイグレーションファイルを編集する際に行う事(rails db:rollback)
マイグレーションファイルを編集したい
マイグレーションファイルの記述を間違えてしまった、コードを付け加えたいと思った時は、行う手順があります。
まずはマイグレーションファイルの状態を確認します。
% rails db:migrate:status Status Migration ID Migration Name -------------------------------------------------- up 20201119073257 Devise create users up 20201120031505 Create coordinations up 20201120032421 Create active storage tablesactive storage今回はusersのマイグレーションファイルを編集を行いますので次のコマンドを実行します。
rails db:rollback再度statusコマンドでマイグレーションファイルの状態を確認します。
% rails db:migrate:status Status Migration ID Migration Name -------------------------------------------------- up 20201119073257 Devise create users up 20201120031505 Create coordinations down 20201120032421 Create active storage tablesactive storageこれではusersのマイグレーションファイルがdownになっていないので編集出来ません。
ロールバックコマンドを行うと直近のマイグレーションファイルのみがdownになり編集出来る状態になります。
今回のusersマイグレーションファイルのように、最後から3つ目のマイグレーションファイルを編集、変更したい場合はrails db:rollback STEP=3とSTEP=○を追記すると一度でusersマイグレーションファイルまでdownにすることが出来ます。
rails db:migrate:status Status Migration ID Migration Name -------------------------------------------------- down 20201119073257 Devise create users down 20201120031505 Create coordinations down 20201120032421 Create active storage tablesactive storageSTEP=○ ←○の中には最後に行ったマイグレーションファイルからrollbackを行いたいマイグレーションファイルまでいくつのマイグレーションファイルが含まれているか数えてその数字を入れてあげればうまくいきます。
最後を含め3つ目ならrails db:rollback STEP=3
4つ目ならrails db:rollback STEP=4最後に
これでマイグレーションファイルの変更を行うことが出来ます。
変更が完了したらマイグレーションを実行をお忘れずに!
- 投稿日:2020-11-27T13:58:29+09:00
ActiveSupport::Callbacksを読む
- 明日の朝までに読まないといけない
背景
- とあるコントローラの処理が重い
- 調査の結果、before_actionとかafter_actionとかで指定されているfilterに原因があることが分かった
- そこで、それぞれのCallbackの実行にかかる時間を書き出したい
- そのために、該当するControllerのrun_callbacksメソッドを黒魔術で上書きする
ActiveSupport::Callbacksについて
- ApplicationControllerやActiveRecordがこれをincludeして、run_callbacksなどのメソッドを使用可能になる。
今回読むコード
activesupport/lib/active_support/callbacks.rb# Runs the callbacks for the given event. # # Calls the before and around callbacks in the order they were set, yields # the block (if given one), and then runs the after callbacks in reverse # order. # # If the callback chain was halted, returns +false+. Otherwise returns the # result of the block, +nil+ if no callbacks have been set, or +true+ # if callbacks have been set but no block is given. # # run_callbacks :save do # save # end # #-- # # As this method is used in many places, and often wraps large portions of # user code, it has an additional design goal of minimizing its impact on # the visible call stack. An exception from inside a :before or :after # callback can be as noisy as it likes -- but when control has passed # smoothly through and into the supplied block, we want as little evidence # as possible that we were here. def run_callbacks(kind) callbacks = __callbacks[kind.to_sym] if callbacks.empty? yield if block_given? else env = Filters::Environment.new(self, false, nil) next_sequence = callbacks.compile invoke_sequence = Proc.new do skipped = nil while true current = next_sequence current.invoke_before(env) if current.final? env.value = !env.halted && (!block_given? || yield) elsif current.skip?(env) (skipped ||= []) << current next_sequence = next_sequence.nested next else next_sequence = next_sequence.nested begin target, block, method, *arguments = current.expand_call_template(env, invoke_sequence) target.send(method, *arguments, &block) ensure next_sequence = current end end current.invoke_after(env) skipped.pop.invoke_after(env) while skipped && skipped.first break env.value end end # Common case: no 'around' callbacks defined if next_sequence.final? next_sequence.invoke_before(env) env.value = !env.halted && (!block_given? || yield) next_sequence.invoke_after(env) env.value else invoke_sequence.call end end endbinding.pryで実験
[9] pry(#<Api::V2::AppleInAppPurchaseController>)> callbacks.class => ActiveSupport::Callbacks::CallbackChain [10] pry(#<Api::V2::AppleInAppPurchaseController>)> callbacks.compile.class => ActiveSupport::Callbacks::CallbackSequencecallbacks
=> #<ActiveSupport::Callbacks::CallbackChain:0x00007f907ffdd998 @callbacks= #<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064416350 @after=[], @before=[], @call_template= #<ActiveSupport::Callbacks::CallTemplate:0x00007f9064416378 @arguments=[], @method_name=:set_controller_action, @override_block=nil, @override_target=nil>, @nested= #<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064417ca0 @after= [#<Proc:0x00007f90644175e8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:232>, #<Proc:0x00007f9064416c60@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>, #<Proc:0x00007f9064416a08@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>], @before= [#<Proc:0x00007f9064416468@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f90644165f8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f9064416850@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f9064416e68@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:175>, #<Proc:0x00007f9064417188@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f9064417368@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f90644178e0@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>], @call_template=nil, @nested=nil, @user_conditions=nil>, @user_conditions=[]>, @chain= [#<ActiveSupport::Callbacks::Callback:0x00007f90643c1738 @chain_config= {:scope=>[:kind], :terminator=> #<Proc:0x00007f9072ce44d8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:34 (lambda)>, :skip_after_callbacks_if_terminated=>true}, @filter=:set_controller_action, @if=[], @key=:set_controller_action, @kind=:around, @name=:process_action, @unless=[]>, #<ActiveSupport::Callbacks::Callback:0x00007f90643add28 @chain_config= {:scope=>[:kind], :terminator=> #<Proc:0x00007f9072ce44d8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:34 (lambda)>, :skip_after_callbacks_if_terminated=>true}, @filter=:init_i18n_debugger, @if=[], @key=:init_i18n_debugger, @kind=:before, @name=:process_action, @unless=[]>, #<ActiveSupport::Callbacks::Callback:0x00007f9072cfd938 @chain_config= {:scope=>[:kind], :terminator=> #<Proc:0x00007f9072ce44d8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:34 (lambda)>, :skip_after_callbacks_if_terminated=>true}, @filter= #<Proc:0x00007f9072cfdb90@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/config-2.2.1/lib/config/integrations/rails/railtie.rb:28>,callback_chains
=> #<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064416350 @after=[], @before=[], @call_template= #<ActiveSupport::Callbacks::CallTemplate:0x00007f9064416378 @arguments=[], @method_name=:set_controller_action, @override_block=nil, @override_target=nil>, @nested= #<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064417ca0 @after= [#<Proc:0x00007f90644175e8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:232>, #<Proc:0x00007f9064416c60@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>, #<Proc:0x00007f9064416a08@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>], @before= [#<Proc:0x00007f9064416468@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f90644165f8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f9064416850@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f9064416e68@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:175>, #<Proc:0x00007f9064417188@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f9064417368@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>, #<Proc:0x00007f90644178e0@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>], @call_template=nil, @nested=nil, @user_conditions=nil>, @user_conditions=[]>
- 投稿日:2020-11-27T12:52:57+09:00
Railsの基本を振り返ってみる
今更ながら、Railsの基本をもう一度振り返っておさらいしてみる
Railsとは
Rubyで作られたフレームワークのこと
「MVC」というアーキテクチャパターンを採用しているアーキテクチャパターンとは
一言でいうならば、「ソフトウェアアーキテクチャで発生する問題の解決策」
を指す。似た表現である「アーキテクチャスタイル
」も同意と考えてよさそう「MVC」とは
アプリケーションを以下の3つで考えること
M Model
…主にシステムの処理を行う場所
V View
…画面の描画を担当する場所
C Controller
…ModelとView間の間にたち、データの受け渡しを行う場所以下で更に詳しくみていきます。
Modelとは
主に、DBとのやりとりを行ってデータを取得したり格納したりします。
役割としては、ビジネスロジック
を担う
例)給与計算などViewとは
アプリケーションの見た目を担う
Modelを介して、取得したデータを受け渡し用の変数等に格納して
動的にテンプレートで表示する
ロジック等も記述することができるが、うまくModelとControllerを活用するCotrollerとは
ユーザーのリクエストを受けて、モデルに適切な動作を要求する
リクエストはURLとして届くため、届いたURLを分析し適切なアクションへと
導くのが役目
さらに、Webに関する一般的な仕事を受け持つという側面がある
例)
- セッション管理URLの解釈
- HTTPリクエスト
- レスポンスの処理
- クッキーの管理などを担当参考記事
MVC
https://kitsune.blog/rails-summary
https://www.atmarkit.co.jp/ait/articles/1102/23/news109.htmlアーキテクチャパターン
https://qiita.com/cocoa-maemae/items/f276fe14b9700fdae7bc
- 投稿日:2020-11-27T12:52:26+09:00
【Rails】AWSデプロイ中にCould not find aws-eventstream-1.1.0 in any of the sources Run `bundle install` to install missing gems.
EC2内でDBを作成しようとしたところエラーが出て詰まったので忘備録として書きます
EC2内でデータベースを作成しようと下記のコマンドを実行したところ
ターミナル(EC2内で実行)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ rails db:create RAILS_ENV=production
Could not find aws-eventstream-1.1.0 in any of the sources
Run `bundle install` to install missing gems.
とエラー
どうやらどのソースでもaws-eventstream-1.1.0が見つかりませんでしたbundle install
を実行して、不足しているgemをインストールします。と言われているよう...
まずGemfileをのぞくが
Gemfile
gem "aws-sdk-s3", require: false
一番下に記述はしっかりあることを確認。
Gemfile.lockをのぞきにいくが
63行目に以下の記述は入っている
Gemfile.lock
aws-eventstream (1.1.0)
bundleinstallのし忘れかと思い履歴を見るがしっかりEC2内にて参考資料通り実行済み、
作業を一度ふり返るが特にミスした形跡も見当たらずもちろんググってみるがあまり参考になる記事も見つけられず。
どうやら開発環境ではaws-eventstream (1.1.0)は存在するが本番環境では適用されてないのだとあたりをつけて念のためもう一度bundle install
ターミナル(EC2内)
[ec2-user@ip-10-0-0-25 <リポジトリ名>]$ bundle install
Using actionmailer 6.0.3.3
Using actiontext 6.0.3.3
Using public_suffix 4.0.6
Using addressable 2.7.0
Using ast 2.4.1
Fetching aws-eventstream 1.1.0
Installing aws-eventstream 1.1.0
Fetching aws-partitions 1.399.0
Installing aws-partitions 1.399.0
Fetching aws-sigv4 1.2.2
Installing aws-sigv4 1.2.2
Fetching jmespath 1.4.0
Installing jmespath 1.4.0
Fetching aws-sdk-core 3.109.3
Installing aws-sdk-core 3.109.3
Fetching aws-sdk-kms 1.39.0
Installing aws-sdk-kms 1.39.0
Fetching aws-sdk-s3 1.85.0
Installing aws-sdk-s3 1.85.0
と、どうやらinstall されていなかったようです。
再度
ターミナル(EC2内)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ rails db:create RAILS_ENV=production
Created database '<データベース名>'
と成功!!
今回原因はbundleinstallし忘れだったようだが、他のgemはしっかり反映されてたのでそれが原因突き止められなかったがしっかり一つ一つ検証していき可能性を消していく作業が大事だと感じた。
同じような症状で悩まれるかたに是非参考になればと思い書かせていただきました。最後まで読んでいただきありがとうございました。
- 投稿日:2020-11-27T10:54:47+09:00
Railsのrouting設定について
本投稿の目的
・Rails学習の議事録です。
学習に使った教材
Udemyの以下2つの教材を参考にまとめました。
・"はじめてのRuby on Rails入門-RubyとRailsを基礎から学びWebアプリケーションをネットに公開しよう"
・"フルスタックエンジニアが教える 即戦力Railsエンジニア養成講座"
○RESTfull
・HTTPメソッドにそってWebのリソースを設計する設計思想
・RailsのRouting設定はこの思想を表現したようなもの【HTTPメソッド】
・GET リソースの取得
・POST リソースの作成
・PATCH/PUT リソースの更新
・DELTE リソースの削除○Routingとは?
以下の①~④を指定する設定
①どのHTTPメソッドで
②どのurlへアクセスした際に
③どのcontrollerの
④どのアクションを実行するか○Routing設定方法
・/config/routes.rb を開く
・このファイルを編集することでrouting変更が可能
*(rails g controller アクション名で記述したroutingが自動記述済み)【rootの設定】
・urlに"/"でアクセスした際のrouting設定
・以下を記述 (*アクション名は"#"で記載することに注意)qiita.rbroot 'controller名#アクション名''【例:rootでquestions controllerのindexアクションを実行したい場合】
qiita.rbroot 'questions#index'【書き方①】
・HTTPメソッドでPrefix_pathのurlへアクセスするとtoo以降のcontrollerでアクションを実行
・という設定を意味する
・以下を記述 (Prefixの部分は _pathを除くことに注意)qiita.rbHTTPメソッド 'Prefix(_pathは除く)', too: 'controller名#アクション名'【例:boards controllerのnewアクションを想定】
qiita.rbget 'boards/new', too: 'boards#new'【書き方②】
【基本的な記述】
・Railsでdefaultで設定された分のroutingが自動で設定可能
・以下を記述qiita.rbresources :controller名【応用的な記述】
・指定したアクション名のみをroutingで設定可能
・以下を記述qiita.rbresources :controller名, only: [:アクション名1, :アクション名2]【例:questions controller を想定】
・questions controller のindex,create,new,showに関連するroutingのみ自動設定qiita.rbresources :questions, only:[:index :create :new :show]○Routing確認方法
【確認方法①(ターミナル)】
・ターミナルに以下を記述rails routes*【出力結果】
・今回は 【例:questions controller を想定】 の条件下での場合の出力結果Prefix Verb URI Pattern Controller#Action questions GET /questions(.:format) questions#index POST /questions(.:format) questions#create new_question GET /questions/new(.:format) questions#new question GET /questions/:id(.:format) questions#show【解説】
・Prefix_pathで定義されたurlへアクセスした際に,それぞれのcontrollerのアクションを実行する
・これらは,routes.rbで設定したため作成された設定である【確認方法②(Webブラウザ)】
・/rails/info/routes のpathにアクセス
・設定済みのルート情報を確認
- 投稿日:2020-11-27T10:13:03+09:00
【Rspec Capybara】confirmダイアログが2回表示されるsystem specを書く
やり方
単純にダイアログの回数分、
accept_confirm をネストすればOK。
# 1回目のダイアログ page.accept_confirm do # 2回目のダイアログ page.accept_confirm do # ダイアログが全てOKなら実施したい何らかの処理 first('div[data-cell="sample"]>div>form>button').click end end
- accept_confirmリファレンス
[https://www.rubydoc.info/github/jnicklas/capybara/Capybara%2FSession:accept_confirm:embed:cite]
- 投稿日:2020-11-27T05:59:07+09:00
Sorceryで複数モデルを扱う
Sorcery
https://github.com/Sorcery/sorcery
How
例: AdminモデルとUserモデルが存在して、両方でSorceryを使いたい場合
前提として、AdminかUserのどちらかでSorceryの導入が完了しているusers_controllerdef current_user User.find_by(id: session[:user_id]) endconfig/initializer/sorcery.rbconfig.user_class = 'Admin'こうすると指定したcontroller内だけでcurrent_userの中身がuserになり、それ以外ではadminになる
上記のuserとadminを逆にしてもいいので、あまり使わない方をcontrollerに指定するとよさそうcurrent_userがセットされているので、
before_action :require_login
も普通に動くSingle Table Inheritanceを使うやり方もあるらしいがこっちがシンプル
- 投稿日:2020-11-27T01:44:54+09:00
RuntimeErrorで画像投稿ができなかった場合の対処法
背景
Refileなどの画像投稿が出来るgemを導入し、画像を実際に格納してみたら
RuntimeErrorとされた。よくあることらしいので備忘録も含めて。使用環境
ruby 2.7.2 Rails 6.0.3.4対処法
エラー文をよく読むとそこまで難しくない。そのままの通り実行することする。
RuntimeError at / Refile.secret_key was not set. Please add the following to your Refile configuration and restart your application: Refile.secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'このRefile.secret_key='XXX'をそのままコピー。
時間を置くとkeyが変わるので注意。/config/initializers/application_controller_renderer.rb# Be sure to restart your server when you modify this file. # ActiveSupport::Reloader.to_prepare do # ApplicationController.renderer.defaults.merge!( # http_host: 'example.org', # https: false # ) # end #以下を貼り付ける Refile.secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'Railsを再起動し、リロードし直すと無事画像が投稿されたのが確認された。