- 投稿日:2021-01-03T23:40:40+09:00
モジュールとMixin メモ
調べながらメモする
モジュールとは
Rubyの基本概念は、クラスがありオブジェクトがある
ある一連の振る舞いを一箇所にまとめたものがモジュール(Module)しかし、クラスとは違いオブジェクトを生成することはできない
モジュールは、あくまで
一連の振る舞い(メソッドの集まり)、または処理をまとめたものそれをクラスで読み込んで使用する
基本形は、ほぼクラスと同じ
メソッドだけでなく、定数やクラスを宣言することも可能使いかた
使用したいクラスにて、取り込む(includeする)
そうすると、作成したオブジェクトからメソッドを読み込むことができる色々なクラスにて同じ振る舞いのメソッドを
簡単に追加することができる同じ名前をつけても衝突しない
それぞれの世界を名前空間といい
名前空間が異なれば、同じ名前でも良いMixin
クラスにモジュールを取り込むことをMixinと呼ぶ
多重継承もどき
Rubyは、Javaなどと同じように多重継承を許していない言語
つまり2つの親クラスを同時に継承できないよって、
1つのクラスを継承してしまった後、
変更を加えるには子クラスに手を加えるしかないところが
Mix-inを使うと、あたかも親クラスのように振る舞うことができるまた1度モジュールを定義すれば、他のクラスでも使えるのでとても便利
まとめ
・クラス→オブジェクトを生成することができる
・モジュール→オブジェクトを生成することができない
・モジュールはいわばメソッドの集まりのようなもので
様々なクラスにて読み込むことで使用することができる
・複数のモジュールを読み込んで使用することも可能
・クラスにモジュールを取り込むことをMixinと呼ぶ
・Mixinすることで、多重継承のような動きに見せることができる参考記事
現場Rails
https://style.potepan.com/articles/7667.html動作確認の際に参考にしようと思う記事
https://qiita.com/tanaka-t/items/32a3329fbc1eb0840ea0
https://qiita.com/pink_bangbi/items/2c2f17516cd3a7b4eeac
https://medium.com/@jiraffestaff/ruby-mixin%E3%81%AE%E4%BD%BF%E3%81%84%E3%81%A9%E3%81%93%E3%82%8D-%E8%A8%AD%E8%A8%88%E7%9A%84%E3%81%AA%E8%80%83%E3%81%88%E6%96%B9-9b8d2e529669
- 投稿日:2021-01-03T23:35:54+09:00
[Rails] pry-rails の導入〜 binding.pryのデバック用法
はじめに
この記事は下記を参考させていただきました。
https://pikawaka.com/rails/pryPryとは、Rubyのirbのようにrailsのコンソールでメソッドなどを使えることができるようになる機能です。
pry-railsの導入
pry-railsはコンソールを立ち上げたとき、irbでなくpryを起動させることができるgemです。
デフォルトでは、rails cコマンドでコンソールモードにした際、irbが起動します。
下記のようにgemを追加しbundle installを実行することで、pryを使えるようになります。Gemfile# 省略 gem 'pry-rails`binding.pry
記述した箇所で処理を止めることができます。
自分はデバックとテストに用いています。
デバッグとは、任意でない処理(バグ)を見つけることです。books_controller.rbdef create @book = BookOrder.new(bookorder_params) binding.pry if @book.save redirect_to root_path else render :index end end
binding.pryで処理を止めると、ターミナルが下記のようになりました。
コンソールが起動し、入力待ちの状態です。
ここで「params」「@book」と入力し、中身を確認することができます。
下記はbinding.pry実行中に使えるコマンドの一例です。
良かったら公式も見てみてください。
https://github.com/rweng/pry-rails
コマンド 内容 step ステップイン next ステップオーバー finish ステップアウト continue デバッグを終了する(中断していた処理を続行) [変数名] 変数の中身を出力 $ [メソッド名] メソッドの定義をみる show-stack スタックとレースをみる(要 pry-stack-explorer) !!! プロセスを終了する。 その後に何個 binding.pry があっても抜けられるが rails s などは終了する show-routes 現在のルーティングを確認 show-models 現在のモデルを確認 show-source クラスやモジュール、メソッドの定義を確認 show-doc クラスやモジュール、メソッドのドキュメントを確認 おわりに
Railsでは、「MVCの流れ」と、「今自分は何の為に何をやっているのか」を常に把握しておくことが伸びるコツだなあと日々感じております。
初学者ならではの辛さもあるかと思いますが、プログラミングは楽しいし、
未来の自分にとって最高級の投資だと思いますので、
学びを楽しんでいきましょう!
- 投稿日:2021-01-03T23:12:55+09:00
Railsのパンくずリストで2種類以上のトップページを実装する方法
こんばんは
アロハな男、やすのりです!今日はポートフォリオで使用しているパンくずリスト用Gem
gretel
を導入時に少し躓いた、『トップページを2種類以上実装する方法』を説明していきたいと思います!もし僕と同じことを実装したい方がいらっしゃったら参考にしてください!!
結論
2種類以上のトップページを実装する場合は、
config/breadcrumbs.rbcrumb :root do link "Home", root_path endという初期設定を
config/breadcrumbs.rbcrumb :top_page do link "トップページ", root_path end crumb :mypage_top do |user| link "マイページトップ", user_path(user.id) endという様な記述に修正し、
root
という設定を無くす!!なぜトップページが2種類以上必要?
まずそもそもなぜ通常だと1つでいいトップページが2種類以上必要なのかについてですが、これは私のポートフォリオの作り方で『その方が見栄えが良さそうだったから』です。
...ここに関しては理論立てた説明はありません
例えば、ポートフォリオでの
検索結果ページ
や詳細ページ
に遷移した際はもちろんトップページからの遷移歴が表示されます。
しかし、
マイページ
内のユーザー情報編集ページ
に遷移した場合の遷移歴は、
この様に、トップページ
へのリンクはなく、マイページトップ
へのリンクしかありません。これがもし遷移歴に
トップページ
も表示させる様にすると、
となってしまい、少し表示が多く見づらくなってしまうなと思いました。
※トップページへのリンクはヘッダー部分に設置してあるため、マイページから出られなくなる等といったことはありません。トップページを2種類用意するには?
gretel
を使用する際に基本は遷移先のリンクと表示用文字列
だけを記述し、親ページを設定したい時は別途設定を記述します。config/breadcrumbs.rbcrumb :root do link "トップページ", root_path end crumb :mypage_top do |user| link 'マイページトップ', user_path(user.id) end crumb :mypage_edit do |user| link '会員情報の確認・変更', edit_user_registration_path #ユーザー情報編集ページがマイページトップの中に位置する様に設定している。 parent :mypage_top, user endただし、何も設定をしていない場合でも
root
の設定と紐づいてしまいます。
そのためユーザー情報編集ページ
までの遷移歴を表示する際に紐づいているトップページ
まで表示されてしまっているわけです。『なら、
root
との紐付けを切り離すために、名前を変えちゃえばいいんだ!!』
ということで冒頭でお見せしたコードの出番ですが、config/breadcrumbs.rbcrumb :top_page do link "トップページ", root_path end crumb :mypage_top do |user| link "マイページトップ", user_path(user.id) end crumb :mypage_edit do |user| link '会員情報の確認・変更', edit_user_registration_path #ユーザー情報編集ページがマイページトップの中に位置する様に設定している。 parent :mypage_top, user endというコード構成になります。
このコード構成にして再度ユーザー情報編集ページ
を表示させることで、紐づいていないトップページ
の表示が消えてマイページトップ
からの遷移歴になるわけです。注意点
この実装方法をした場合に、1つ注意しなければならないことがあります。
それは、『
検索結果ページ
に親ページを設定する必要がある』ということです。config/breadcrumbs.rbcrumb :root do link "トップページ", root_path end crumb :search_word do |search_word| link "'#{search_word}' の検索結果", search_path end元々は
検索結果ページ
に何も設定しなくてもroot
に紐づいていたので自動的にトップページ
を表示してくれていましたが、名前を変更(今回であればtop_page
)に変更してしまっているので、紐付けが解除され親ページが存在しない状態になってしまっています。なので、
config/breadcrumbs.rbcrumb :top_page do link "トップページ", root_path end crumb :search_word do |search_word| link "'#{search_word}' の検索結果", search_path #新たに設定したtop_pageというページを親ページとして設定する。 parent :top_page endとコードを修正することで、元通りのパンくずリスト表示にすることができます。
最後に
トップページを2種類以上実装するということ自体そんなに頻繁にすることではないと思いますが、もし実装する際にこの記事が参考になれば幸いです!
この件に関するご指摘・アドバイスはどんどんいただきたいと思っていますので、コメントお待ちしています!!
- 投稿日:2021-01-03T23:03:24+09:00
RSpecにてmodelのテストを書く
modelテストの準備
今回はPostモデルにタイトルカラムがあるかどうかと文字数のテストをする。
app/models/post.rbclass Post < AplicationRecord belongs_to :user validates :title, presence: true, length: {maximum: 20} endファイルの作成
①spec配下にmodelsフォルダとfactoriesフォルダを作成し、テストしたいモデルのファイルを作成する。今回は spec/models/post_spec.rbを作成
②FactroyBotを使用可能にする。
使用すると user = create(:user)のようにDB登録やモデルのビルドができるようになる。
spec配下にsupportフォルダとfactory_bot.rbファイルを作成し、下記を記述。
ユーザーがログインしている状態でないと投稿出来ないことをテストするためpost_spec.rb user_spec.rbの2ファイルを作成する。spec/support/factroy_bot.rbRSpec.configure do |config| config.include FactroyBot::Syntax::Methods end③spec/rails_helper.rbの編集
spec/rails_helper.rbrequire 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__) abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' require 'support/factory_bot' begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e puts e.to_s.strip exit 1 end RSpec.configure do |config| config.fixture_path = "#{::Rails.root}/spec/fixtures" config.use_transactional_fixtures = true config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace! #今回の記事では関係ないがsystem_specにおいてヘッドレスモードでchromeを起動するための記述 config.before(:each) do |example| if example.metadata[:type] == :system if example.metadata[:js] driven_by :selenium_chrome_headless, screen_size: [1400, 1400] else driven_by :rack_test end end end #capybaraに関する記述 config.include Capybara::DSL endダミーデータの作成
spec/factories/user.rbFactoryBot.define do factory :user do name {Faker::Name.name} email {Faker::Internet.email} password {'password'} password_confirmation {'password'} end endspec/factories/post.rbFactoryBot.define do factory :post do body {Faker::Lorem.characters(number: 20)} user end endテストコードの作成
spec/models/post_spec.rbrequire 'rails_helper' RSpec.describe 'post model test', type: :model do describe 'validation test' do #factoriesで作成したダミーデータを使用する let(:user) {FactoryBot.create(:user)} let!(:post) {build(:post, user_id: user.id)} #test_postを作成し、空欄での登録ができるか確認 subject {test_post.valid?} let(:test_post) {post} it 'titleカラムが空欄でないこと' do test_post.title = '' is_expected.to eq false; end it '21文字以上であればfalse' do post.title = Faker::Lorem.characters(number: 21) expect(post).to eq false; end end endRSpecの実行
$ bin/rspec spec/models/post_spec.rb
- 投稿日:2021-01-03T22:58:44+09:00
【Rails】 PG::DuplicateTable: ERROR: relation "users" already existsの解消法
PG::DuplicateTable: ERROR: relation "users" already existsエラー
Railsでデータベースをmigrateしようとした時に出たエラーです。
エラーの内容は、「usersテーブルが既に存在してるよ!」ということでした。
はじめはよく理解していなかったので、マイグレーションファイルを削除して、
もう一回作成してみたりしてみたのですが、また同じエラーが出ました。rails db:migrate:resetを実行
エラー内容を検索してみた結果、リセットするのが一番良いようだったので、
rails db:migrate:reset
を実行。Dropped database 'rspec_sample_development' Dropped database 'rspec_sample_test' Created database 'rspec_sample_development' Created database 'rspec_sample_test' == 20210103133942 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0317s -- add_index(:users, :email, {:unique=>true}) -> 0.0051s == 20210103133942 CreateUsers: migrated (0.0371s) =============================このような表示が出て、みごとにデータベースのリセットが出来ました。
再度、rails db:migrate
でデータベースを反映させる事ができました。参考資料
- 投稿日:2021-01-03T22:56:12+09:00
Don't know how to build task 'credentials:edit'
問題
$ rails credentials:edit rails aborted! Don't know how to build task 'credentials:edit' (see --tasks)解決策
redentials.yml.encは、Rails5.2から追加されたので問題なければ5.2にしましょう。
gem 'rails', '~> 5.2'bundle update情報源
https://fuzzyblog.io/blog/rails/2020/09/14/deploying-to-hatchbox-with-rails-5-x.html
- 投稿日:2021-01-03T22:18:05+09:00
個人アプリ開発日記 #3
目次
1. はじめに
前回の個人アプリ開発日記#2では、このアプリを開発したいと思ったきっかけについて述べました。
そして3記事目の今回は、私が現在作っている「フレンドマッチングアプリ」と
既存のアプリについて比較します!理由は、将来的にリリースすることも視野に入れているので、
せっかく作ったものの「これって○○と似てない?」「○○と何が違うの?」
というような残念なことにしたくないからです。とは言えまだまだ詰めが甘い点は多いですが、自分なりに差別化を図りながら制作しているつもりなので、
思考整理も兼ねて投稿します。2. 内容
まずは、既存のアプリを友人、他人、1:1、1:nの4つの切り口で、簡単なマトリックスにしてみました。
友人 他人 1:1 チャット(LINE, WeChat, Snapchat) マッチング(Tinder, Pairs, with, タップル) 1:n SNS(Facebook, Instagram, Twitter) メディア(YouTube, TikTok, SHOWROOM LINEのオープンチャット、Twitterの趣味垢、InstagramやFacebookのDM/
メッセンジャーなどを含めると、もっと細分化してしまいますが、
ざっくりしたところでは上記のように大別できるのではないでしょうか。私のフレンドマッチングアプリは、1:1で他人がターゲットなのでマッチング系に属することになります。
しかし、既存のマッチング系は顔写真でスワイプで好みを選択したり、恋愛・婚活寄りが多いと感じています。
「つながりを、もっと身近に」というコンセプトは以前の記事にも書いたのですが、あくまで年齢や出身地、
血液型などの項目別もしくは、ハッシュタグ検索を主としています。また「友達が欲しいけど作る機会に恵まれていない人」をペルソナに設定しているため、友達や雑談相手
探しに利用することをイメージしています。ただし、フレンドマッチングアプリ自体は当然先行がいて
("Tantan"というアプリ名で顔写真でスワイプする点はTinderと同一)、マッチングアプリ世界王者の
Tinderも当初の恋愛系からライフスタイル全般のマッチングに裾野を広げていますので、
独自性を追求すべくこれからも動きを注視したいと考えています。5. おわりに
アプリ制作は初めてのため初心者ですが、中身の細かい実装をしながらでも、
視野が狭くなりすぎないように適度にアプリ全体を俯瞰することを意識しています!
- 投稿日:2021-01-03T21:42:32+09:00
Microposts機能(rails)
この記事では、Micropostsにおける投稿機能について、簡単に理解したのでその確認として理解を深めることを目的としている。
投稿機能は以下の手順デコーディングを行う
- Micropostモデル、contentカラム、user_idカラム(外部キー)を作る
- UserテーブルとMicropostテーブルを関連付ける
- UsersコントローラーのshowアクションにMicropostの中身を表示させるための「@microposts」を記載する
- Usersフォルダーのviewのshow.html.erbにも@micropostsを記載する
- Micropostsコントローラーを作り、_micropost.html.erbにmicropostテーブルのcontentカラムの中身を表示させるファイルを作る
- Homeページをアップデートする
1.Micropostモデル、contentカラム、user_idカラム(外部キー)を作る
$ rails g model Micropost content:string user_id:integer ↓ $ rails db:migrate:reset db:seed↑データベースをいじったので一度migrateファイルをリセットしておく
Microposは誰の投稿化を知るために必ずuser_idを持たなければいけないので以下のコードを打ち込む
class Micropost < ActiveRecord::Base *** validates :user_id, presence: true endUserテーブルとMicropostテーブルを関連付けをする
models/micropost.rb class Micropost < ApplicationRecord *** belongs_to :user validates :user_id, presence: true endmodels/user.rb class User < ApplicationRecord *** has_many :microposts, *dependent: :destroy ... end最新の投稿が投稿欄の一番上に表示されるようにする
class Micropost < ApplicationRecord belogns_to :user *** default_scope -> { order('created_at DESC')} validates :user_id, presence: true end*dependent: :destroyを使うことによりuserが退会した場合にそのuserが投稿した内容も同時に削除されるようにする
投稿文字数をTwitterのように140文字以内でしか投稿できないようにする
class Micropost < ApplicationRecord belongs_to :user default_scope -> {order('created_at DESC')} *** validates :content, presence: true, length: { maximum: 140 } validates :user_id, presence: true end3. UsersコントローラーのshowアクションにMicropostの中身(content)を表示させるための「@microposts」を記載する
class UsersController < ApplicationController /// def show @user = User.find(params[:id]) @microposts = @user.microposts.paginate(page: params[:page], per_page: 12) #userに紐づくすべての投稿をpaginateで取得する。 endviews/users/show.html.erb<% provide(:title, @user.name)%> <div class="container pt-4"> <div class="row"> <aside class="span"> <section> <h1> <%= gravatar_for @user %> <%= @user.name%> </h1> </section> </aside> <div class="col-9"> <div> <% if @user.microposts.any?%> #モデルにデータが存在する→true/存在しない→falseを返すメソッド <h3>Microposts (<%= @user.microposts.count %>)</h3> <ol class="microposts"> <%= render @microposts%> </ol> <%= will_paginate @microposts%> <% end %> </div> </div>5. Micropostsコントローラーを作り、_micropost.html.erbにmicropostテーブルのcontentカラムの中身を表示させるファイルを作る
terminalrails g controller Microposts=>createアクションとdestroyアクションを作るためにmicropostsコントローラーを作る
views/microposts/_micropost.html.erb<li> <h4><%= micropost.content%></h4> #micropostテーブルのcontentカラムの内容を表示 <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. #created_atで作成日時を表示する際に、Twitterのように「~前」と表示する方法 ※ex:約19時間前 </span> </li>=> showページでmicroposts投稿内容と作成日を表示させる
▼Fakerでダミーデータを作成する
db/seeds.rbusers = User.order(:created_at).take(7) 50.times do content = Faker::Lorem.sentence(3) users.each { |user| user.microposts.create!(content:content) } end$rails db:migrate:reset db:seed=> データベースの中身を変更したのでリセットする
createとdestroyのルーティングをresourcesメソッドを使って定義する
config/routes.rb/// resources :users resources :sessions, only: [:new, :create, :destroy] resources :microposts, only: [:create, :destroy] ///6. Homeページをアップデートする
views/static_pages/home.html.erb<% provide(:title, 'Home')%> <% if logged_in?%> <div class="row"> <aside class="col-3"> <section> <%= render 'shared/user_info' %> </section> <section> <%= render 'shared/micropost_form' %> </section> </aside> <!-- Microposts here --> </div> <% else %> <h1 style="text-align:center;">Awesome Blog</h1> <p> Welcome to Awesome Blog!<br> </p> <p> This Blog is made for all people who want to get good shape of the body!! </p>=>userがログインしているときは投稿機能とユーザーの情報を表示するがログインしていないときはAwesome Blogを表示するようにする
views/shared/_user_info.html.erb<%= link_to gravatar_for(@user, size: 52), @user%> <h1> <%= @user.name%> #usersデーブルのnameカラム </h1> <span> <%= pluralize(@user.microposts.count, "micropost")%> #pluralizeヘルパー...最初の引数に整数があると、それに基づいて2番目の引数である英単語が複数形に変化したものが渡される。 </span>views/shared/_micropost_form.html.erb<%= form_for(@micropost) do |f| %> #特定のモデルに特化したフォームを作りたい → form_for <div> <%= f.text_area :content, placeholder: "Compose new micropost...", class: "w-100"%> </div> <%= f.submit "Post", class: "btn btn-block btn-primary" %> <% end %>=>form_forメソッドを使うとモデルのインスタンスを渡すだけで自動的にRailsが処理してくれる(RESTfulなリソースを使っている場合)
form_for(@micropost)の@micropostがすでに保存されていた場合にはupdateアクション、未保存の時はcreateアクションを、Railsは自動的に選んでくれる※@micropostは後でhomeコントローラーで定義するmicropostsコントローラーのcreate,destroyアクションにアクセスできるのはログインを正しくしたユーザだけに制限する
controllers/microposts_controller.rbbefore_action :only_loggedin_users, only: [:create, :destroy] def create end def destroy end=>before_actionをすることによりURLから直接アクセスされることを防ぐ(ログインしていなければログインページにとぶ)
micropostsコントローラーの中身を記載していく
controllers/microposts_controller.rbbefore_action :only_loggedin_users, only: [:create, :destroy] def create @micropost = current_user.microposts.build(micropost_params) #microposts_paramsとは下のprivateで定義したもの #.build≒.new if @micropost.save flash[:success] = "Successfully saved!" redirect_to root_url else flash[:danger] = "Invalid content. Try again." redirect_to root_url end end def destroy end private def micropost_params params.require(:micropost).permit(:content) end=>投稿内容を@micropostに格納し保存するに成功したらflashメッセージを表示させ(flash[:success]=)homeページに投稿を反映させる(redirect_to root_url)
controllers/static_pages_controllerclass StaticPagesController < ApplicationController def home @micropost = current_user.microposts.build if logged_in? #1対多の場合では、「関連付けメソッド名.build」となる end def about end def contact end end=>現在ログインしているユーザーモデルに紐づく投稿のオブジェクトを作成する
▼すべての投稿を表示する
views/static_pages/home.html.erb<% if logged_in?%> <div class="row"> <aside class="col-3"> <section> <%= render 'shared/user_info' %> </section> <section> <%= render 'shared/micropost_form' %> </section> </aside> <!-- Microposts here --> <div class="col-9"> <h3>Micropost Feed</h3> *** <%= render 'shared\feed'%> </div> </div> <% else %>=>_feed.html.erbに投稿一覧を表示するようにする
controllers/static_pages_controllerdef home #@micropost = current_user.microposts.build if logged_in? if logged_in? @micropost = current_user.microposts.build @feed_items = current_user.feed.paginate(page: params[:page], per_page: 12) #user.rbで定義したfeedメソッドをの取得結果を@feed_itemsに格納する @user = current_user end end=>homeアクションのリファクタリングを行う。
models/user.rb#feed/// def feed Micropost.where("user_id = ?", id) end ///=>この部分がいまいち理解できない
views/shared/_feed.html.erb<% if @feed_items.any? %> <ol> <%= render partial: 'shared/feed_item', collection: @feed_items %> </ol> <%= will_pagenate @feed_items %> <% end %>views/shared/_feed_item.html.erb<li class="col-12 my-4"> <%= link_to gravatar_for(feed_item.user), feed_item.user %> <span class="user"> <%= link_to feed_item.user.name, feed_item.user %> </span> <h4><%= feed_item.content %></h4> <span class="timestamp"> Posted <%= time_ago_in_words(feed_item.created_at) %> ago. </span> </li>controllers/microposts_controller.rb#create/// def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Successfully saved!" redirect_to root_url else *** @feed_items = [] flash[:danger] = "Invalid content. Try again." redirect_to root_url end end ///=>@feed_items = []を加えることで@feed_itemsの中に何も入っていない(nil)の時に発生するエラーを防ぐ
削除機能を作る
views/microposts/_microposts.html.erb<li> <h4><%= micropost.content%></h4> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. </span> *** <% if current_user?(micropost.user) %> <%= link_to "delete", micropost, method: :delete, data: { confirm:"Are you sure?"} title: micropost.contentn %> <% end %> *** </li>=>ログインしているユーザーのshowページにだけ削除機能があらわれるようにする
views/shared/_feed_item.html.erb<li class="col-12 my-4"> <%= link_to gravatar_for(feed_item.user), feed_item.user %> <span class="user"> <%= link_to feed_item.user.name, feed_item.user %> </span> <h4><%= feed_item.content %></h4> <span class="timestamp"> Posted <%= time_ago_in_words(feed_item.created_at) %> ago. </span> *** <% if current_user?(feed_item.user) %> <%= link_to "delete", feed_item, method: :delete, data: { confirm: "You sure?"} title: feed_item.content %> <% end %> *** </li>destroyアクションの中身を記載する
controllers/microposts_controller.rb#destroydef destroy Micropost.find(params[:id]).destroy redirect_to root_url end
- 投稿日:2021-01-03T21:11:49+09:00
【Ruby】購入機能実装〜ログイン中・販売済商品に対しての分岐〜
解決したいこと
①出品者が自分の出品商品を購入できてしまう
②出品者が完売商品に対して商品の編集・削除ができてしまう
③完売商品に対してもう一度購入ができてしまうif文の分岐すごく苦手なんです。
どこをどう考えたらいいのかわからないので、まずは紙に書き出してみる。出品者
自分の出品物の販売中商品だけ編集&削除OK
購入は全てNG購入者
販売中商品のみ購入OK
編集&削除は全てNGだいぶ絞れたので実際にコードにしてみます。
1<% if user_signed_in? %> 2 <% if current_user.id == @item.user_id %> 3 <% unless @item.purchase.present? %> 4 <%= link_to '商品の編集', edit_item_path(@item.id), method: :get, class: "item-red-btn" %> 5 <p class='or-text'>or</p> 6 <%= link_to '削除', item_path(@item.id), method: :delete, class:'item-destroy' %> 7 <% end %> 8 <% end %>1行目
まずはログイン済ユーザーかどうかをチェック。2行目
そのユーザーが出品者かどうかをチェック。3行目
完売商品ではないかをチェック。4〜6行目
3行目がunlessなので完売商品ではなければ(偽)編集&削除ボタンを表示させる。時間がかかったのはこの後、どうしても購入ボタンが表示されてしまう。
elsifで分岐・・・いや違う。
もう一度if文で分岐・・・これも違う。
そもそも最初の分岐で売れてるか売れてないかを判断したらいいんじゃないか・・・?
そう思い、生み出した奇跡のコードがこちら。<% if current_user.id == @item.user_id && @item.purchase.present? %>わけがわからないですね?
もう一度書いた図を見てみる。
OKなのは販売中商品のみということか。
しかし販売中商品に購入ボタンをつけてしまうと出品者にも購入ボタンが表示されてしまう。
そこでハッと気付く。2行目
そのユーザーが出品者かどうかをチェック。ここでelseを使えばユーザーではあるけど出品者ではない=購入者ということではないのかな?
1<% if user_signed_in? %> 2 <% if current_user.id == @item.user_id %> 3 <% unless @item.purchase.present? %> 4 <%= link_to '商品の編集', edit_item_path(@item.id), method: :get, class: "item-red-btn" %> 5 <p class='or-text'>or</p> 6 <%= link_to '削除', item_path(@item.id), method: :delete, class:'item-destroy' %> 7 <% end %> 8 <% else %> 9 <% unless @item.purchase.present? %> 10 <%= link_to '購入画面に進む', item_orders_path(@item.id),class:"item-red-btn"%> 11 <% end %> 12 <% end %> 13<% end %>8行目
ログイン中ユーザーだけど出品者じゃない分岐。9行目
3行目と同じく出品物が完売ではないかをチェック。10行目
9行目がunlessなので完売商品ではなければ(偽)購入ボタンを表示させる。
うまいこといきました。
自己解決できるとまた一つ階段を昇れた気がします。
※違っている点、もっと簡単に書けるよというものがあればご指摘頂ければ幸いです。
- 投稿日:2021-01-03T21:11:49+09:00
【Ruby】購入機能実装〜ログイン中・完売商品に対しての分岐〜
解決したいこと
①出品者が自分の出品商品を購入できてしまう
②出品者が完売商品に対して商品の編集・削除ができてしまう
③完売商品に対してもう一度購入ができてしまうif文の分岐すごく苦手なんです。
どこをどう考えたらいいのかわからないので、まずは紙に書き出してみる。出品者
自分の出品物の販売中商品だけ編集&削除OK
購入は全てNG購入者
販売中商品のみ購入OK
編集&削除は全てNGだいぶ絞れたので実際にコードにしてみます。
1<% if user_signed_in? %> 2 <% if current_user.id == @item.user_id %> 3 <% unless @item.purchase.present? %> 4 <%= link_to '商品の編集', edit_item_path(@item.id), method: :get, class: "item-red-btn" %> 5 <p class='or-text'>or</p> 6 <%= link_to '削除', item_path(@item.id), method: :delete, class:'item-destroy' %> 7 <% end %> 8 <% end %>1行目
まずはログイン済ユーザーかどうかをチェック。2行目
そのユーザーが出品者かどうかをチェック。3行目
完売商品ではないかをチェック。4〜6行目
3行目がunlessなので完売商品ではなければ(偽)編集&削除ボタンを表示させる。時間がかかったのはこの後、どうしても購入ボタンが表示されてしまう。
elsifで分岐・・・いや違う。
もう一度if文で分岐・・・これも違う。
そもそも最初の分岐で売れてるか売れてないかを判断したらいいんじゃないか・・・?
そう思い、生み出した奇跡のコードがこちら。<% if current_user.id == @item.user_id && @item.purchase.present? %>わけがわからないですね?
もう一度書いた図を見てみる。
OKなのは販売中商品のみということか。
しかし販売中商品に購入ボタンをつけてしまうと出品者にも購入ボタンが表示されてしまう。
そこでハッと気付く。2行目
そのユーザーが出品者かどうかをチェック。ここでelseを使えばユーザーではあるけど出品者ではない=購入者ということではないのかな?
1<% if user_signed_in? %> 2 <% if current_user.id == @item.user_id %> 3 <% unless @item.purchase.present? %> 4 <%= link_to '商品の編集', edit_item_path(@item.id), method: :get, class: "item-red-btn" %> 5 <p class='or-text'>or</p> 6 <%= link_to '削除', item_path(@item.id), method: :delete, class:'item-destroy' %> 7 <% end %> 8 <% else %> 9 <% unless @item.purchase.present? %> 10 <%= link_to '購入画面に進む', item_orders_path(@item.id),class:"item-red-btn"%> 11 <% end %> 12 <% end %> 13<% end %>8行目
ログイン中ユーザーだけど出品者じゃない分岐。9行目
3行目と同じく出品物が完売ではないかをチェック。10行目
9行目がunlessなので完売商品ではなければ(偽)購入ボタンを表示させる。
うまいこといきました。
自己解決できるとまた一つ階段を昇れた気がします。
※違っている点、もっと簡単に書けるよというものがあればご指摘頂ければ幸いです。
2021 1/4
一部修正。1<% if user_signed_in? && @item.purchase == nil %> 2 <% if current_user.id == @item.user_id %> 3 <%= link_to '商品の編集', edit_item_path(@item.id), method: :get, class: "item-red-btn" %> 4 <p class='or-text'>or</p> 5 <%= link_to '削除', item_path(@item.id), method: :delete, class:'item-destroy' %> 6 <% else %> 7 <%= link_to '購入画面に進む', item_orders_path(@item.id),class:"item-red-btn"%> 8 <% end %> 9<% end %>1行目
unlessで繰り返されていたものを1行目に追記。
@item.purchase == nilで、「商品の購入情報がnil(まだ購入されていない)」と表現。4行ほど短く、コンパクトにすることができました。
生み出した奇跡のコードにちょっと近いかな?!と自分を褒め称えました。
奥が深い。
- 投稿日:2021-01-03T20:48:14+09:00
renderまたはredirect_toの指定パスに対して、引数が必要な場合
はじめに
現在、フリマサイトのwebアプリを、railsを用いて実装中です。
その中で、「商品を保存できたら商品一覧画面へ遷移する」という機能を実装する際に、redirect_toとrenderのURI(パス)に引数をつける必要性が分からなくなってしまった為、メンターさんに聞きました。
その回答内容を以下に整理します。1.redirect_toとrenderの違い
redirect_toとrenderのそれぞれの意味を、実際の使用例を参考にまとめます。
下部の参考サイトでは図でまとめている為、より理解しやすいかと思います。1-1. redirect_to : 「アクションの実行」
使用例は、商品を削除したら商品一覧画面へ遷移する(※今回root_pathは商品一覧画面)機能です。
items_controller.rbdef index @items = Item.all.order('created_at DESC') end def destroy item = Item.find(params[:id]) item.destroy redirect_to root_path #上記indexアクションを実行 endredirect_toは指定したURIのアクションの実行である為、今回、
「root_pathを実行する」
=「routes.rbに指定したrootを実行する」
=「itemsコントローラーのindexアクションを実行する」この場合、indexアクションを実行するため、indexアクション内の@itemsは再度生成されます。
もしここでrenderを用いてしまうと、@itemsは再度生成されない。削除することによって、@itemsに含まれるデータが変更されているが、変更前の@itemsを用いてviewへ遷移しているため、下の画像のようなNoMethodErrorが発生します。
1-2. render : 「viewの呼び出し」
使用例は、商品を保存したらredirect_toで商品一覧画面へ遷移し、保存できなかったらrenderで新規投稿画面を表示する(= new.html.erb を呼び出す)機能です。
items_controller.rbdef new @item = Item.new end def create @item = Item.new(item_params) if @item.save redirect_to root_path else render :new end endrenderは指定したviewの呼び出しである為、今回、
「new.html.erbを呼び出す」この場合、createアクション内でnew.html.erbを呼び出しており、newアクションは実行されません。即ち、newアクション内の@itemは生成されません。renderを用いる理由は、データの保存に失敗した場合に、入力していたデータを残したまま入力画面を再度表示できることです。
ちなみに、「createアクション内でnew.html.erbを呼び出している」ことは、商品の保存をあえて失敗すると、ドメイン表示からnewが消えるため、確認できます。もしここでredirect_toを用いると、@itemが再度生成されます。そのため、保存に失敗した場合、再度newアクションを実行するため、先ほど入力したデータが更新され、空になってしまいます。
2.URIに引数が必要な場合
2-1.redirect_toで引数が必要な場合
redirect_toを用いる際には、パスに引数を与える必要がある場合があります。
以下画像は、ターミナルでrais routesコマンドを実行した際の一例です。
この青部分の様に、URI Patternにidが含まれている場合、パスに引数を与える必要があります。その理由は、redirect先のパスが多数存在する場合は、引数によりアクションを実行する対象のパスを一つ指定しなければならないためです。
Ex) リダイレクト先がツイートのパスで、ツイートが100個ある場合、どのツイートかを引数により指定してあげる必要がある。2-2.renderで引数が必要な場合
renderで引数が必要な場合は、基本的には必要ありません。
その理由は、renderはviewの使い回しであり、コントローラのアクションは同一であるため、すでに生成したインスタンスを、render先のviewでも用いることができるからです。3.まとめ
- redirect_toは再度アクションを実行する
- renderはviewを切り出す
- redirect_toを用いる際に、URIにidが含まれている場合は、引数を設定する必要がある。
- renderでは基本的に引数は必要ない。
参考サイト
- レイアウトとレンダリング(Railsガイド)
renderとredirectの違い
https://qiita.com/january108/items/54143581ab1f03deefa
- 投稿日:2021-01-03T20:48:14+09:00
renderまたはredirect_toの違い、引数の必要性
はじめに
現在、フリマサイトのwebアプリを、railsを用いて実装中です。
その中で、「商品を保存できたら商品一覧画面へ遷移する」という機能を実装する際に、redirect_toとrenderのURI(パス)に引数をつける必要性が分からなくなってしまった為、メンターさんに聞きました。
その回答内容を以下に整理します。1.redirect_toとrenderの違い
redirect_toとrenderのそれぞれの意味を、実際の使用例を参考にまとめます。
下部の参考サイトでは図でまとめている為、より理解しやすいかと思います。1-1. redirect_to : 「アクションの実行」
使用例は、商品を削除したら商品一覧画面へ遷移する(※今回root_pathは商品一覧画面)機能です。
items_controller.rbdef index @items = Item.all.order('created_at DESC') end def destroy item = Item.find(params[:id]) item.destroy redirect_to root_path #上記indexアクションを実行 endredirect_toは指定したURIのアクションの実行である為、今回、
「root_pathを実行する」
=「routes.rbに指定したrootを実行する」
=「itemsコントローラーのindexアクションを実行する」この場合、indexアクションを実行するため、indexアクション内の@itemsは再度生成されます。
もしここでrenderを用いてしまうと、@itemsは再度生成されない。削除することによって、@itemsに含まれるデータが変更されているが、変更前の@itemsを用いてviewへ遷移しているため、下の画像のようなNoMethodErrorが発生します。
1-2. render : 「viewの呼び出し」
使用例は、商品を保存したらredirect_toで商品一覧画面へ遷移し、保存できなかったらrenderで新規投稿画面を表示する(= new.html.erb を呼び出す)機能です。
items_controller.rbdef new @item = Item.new end def create @item = Item.new(item_params) if @item.save redirect_to root_path else render :new end endrenderは指定したviewの呼び出しである為、今回、
「new.html.erbを呼び出す」この場合、createアクション内でnew.html.erbを呼び出しており、newアクションは実行されません。即ち、newアクション内の@itemは生成されません。renderを用いる理由は、データの保存に失敗した場合に、入力していたデータを残したまま入力画面を再度表示できることです。
ちなみに、「createアクション内でnew.html.erbを呼び出している」ことは、商品の保存をあえて失敗すると、ドメイン表示からnewが消えるため、確認できます。もしここでredirect_toを用いると、@itemが再度生成されます。そのため、保存に失敗した場合、再度newアクションを実行するため、先ほど入力したデータが更新され、空になってしまいます。
2.URIに引数が必要な場合
2-1.redirect_toで引数が必要な場合
redirect_toを用いる際には、パスに引数を与える必要がある場合があります。
以下画像は、ターミナルでrais routesコマンドを実行した際の一例です。
この青部分の様に、URI Patternにidが含まれている場合、パスに引数を与える必要があります。その理由は、redirect先のパスが多数存在する場合は、引数によりアクションを実行する対象のパスを一つ指定しなければならないためです。
Ex) リダイレクト先がツイートのパスで、ツイートが100個ある場合、どのツイートかを引数により指定してあげる必要がある。2-2.renderで引数が必要な場合
renderで引数が必要な場合は、基本的には必要ありません。
その理由は、renderはviewの使い回しであり、コントローラのアクションは同一であるため、すでに生成したインスタンスを、render先のviewでも用いることができるからです。3.まとめ
- redirect_toは再度アクションを実行する
- renderはviewを切り出す
- redirect_toを用いる際に、URIにidが含まれている場合は、引数を設定する必要がある。
- renderでは基本的に引数は必要ない。
参考サイト
- レイアウトとレンダリング(Railsガイド)
renderとredirectの違い
https://qiita.com/january108/items/54143581ab1f03deefa
- 投稿日:2021-01-03T19:22:31+09:00
RSpecの準備
この投稿は、自己学習の備忘録です。
railstutorialを一通り学習し、テストをminitestからRSpecへ書き換えようと思いたったことがこの記事を執筆しようとしたキッカケです。
私自身にとって初めての投稿ですが、もしこの投稿がご覧頂いた方のヒントになっていれば幸いです。
rails new時のオプション
$ rails new --skip-test --skip-bunldeデフォルのminitestとbundle install をスキップしておく。
gemの追加
Gemfilegroup :development, :test do gem 'rspec-rails' gem 'spring-commands-rspec' gem 'capybara' gem 'factory_bot_rails' gem 'faker' gem 'selenium-webdriver' gem 'database_cleaner' end$ bundle install --without production --path=vender/bundle↑PCないのシステムのgemと混同しないようにするため
RSpec用の初期ファイルのインストール
$ rails g rspec:install Running via Spring preloader in process 9045 create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rbSpringを使用したRSpecの導入
springを使用することでRSpecの実行時間を短縮することができる。
そのために先ほど追加したgem 'spring-commands-rspec'を使用する。
RSpecのstubファイル(./bin/rspec)を作成する。$ bundel exec spring binstub rspecbin/rspecファイルが作成され、RSpecを実行するとspringが使えるようになる。
*'spring-commands-rspec':bin/rspecのコマンドを実行するときに必要となる。RSpecはbin/コマンドをつけることでSpringと言うRailsに組み込まれているアプリを起動させて処理を高速化できる。RSpec自体はrspecと入力するだけで使用可能。
config/environments/test.rbの編集
config/environments/test.rbconfig.active_support.deprecation = :stderr ↓非推奨レポートをどこに出力するかの設定に変更 config.active_support.deprecation = :silenceとりあえずrails db:migrate RAILS_ENV=testを実行
$ rails db:migrate RAILS_ENV=test.rspecファイルの編集
.rspec--require spec_helper --format documentation :テストを通過した文言が表示されるようになる --color今回は以上!
Qiitaでの記事の書き方結構独特なんですね
最低でも月に2記事は投稿できるように頑張ります!
頑張るぞ、2021年!
- 投稿日:2021-01-03T18:52:55+09:00
[Rails]deviseのカスタマイズ。ユーザー登録、編集後のリダイレクトページを変える方法と、編集時のパスワード入力をスキップする方法
駆け出しエンジニアの備忘録です。初投稿です。
またいつか「あれどうやるんだっけ?」となった時に再度調べるのも効率が悪いので、ここに残していこうと思います。
間違いなどがあればご報告をお願い致します。デフォルトはrootパス
僕の環境だとrootはトップページにリダイレクトしてしまうので、どちらもユーザーの詳細画面に遷移するようにしたい。
deviseの導入などの準備は割愛いたします。1.コントローラーの作成
$ rails g devise:controllers usersいくつか作成されたコントローラーの中で今回使うのはregistrations_controller.rb
ここに追記する
controllers/users/registrations_controller.rbclass Users::RegistrationsController < Devise::RegistrationsController # 以下を追記 protected def after_sign_up_path_for(resource) flash[:notice] = '登録完了!編集ボタンからプロフィール画像と自己紹介文を登録できます。' user_path(resource) #ここにリダイレクト先のパスを指定 end def after_update_path_for(resource) user_path(resource) #ここにリダイレクト先のパスを指定 end endこれでコントローラーの準備はok
2.routes.rbの編集
作成したコントローラーに
class Users::RegistrationsController < Devise::RegistrationsController
という記述があるように、元々deviseで使われているDevise::RegistrationsControllerを継承した新しいUsers::RegistrationsControllerを作成した、ということなので、ここに記述をしてくことで追加の設定を反映していくことができる。
(継承してるということに全然気付いておらず、「コントローラーを新しく作るってことはこのコメントアウトされてるnewアクションとかupdateアクションとか全部自分で触る必要あるのか!?」って焦ったけどそのままでも大丈夫だった)
ただしこのままではUsers::RegistrationsControllerを使うことができないので、
以下を記述して使うことを明示する必要がある。routes.rbdevise_for :users, controllers: { registrations: 'users/registrations' }これにより作成したコントローラが使えるようになりました。
ちゃんと指定したパスにリダイレクトされるはずです。編集時のパスワードの入力をスキップする設定
デフォルトだと更新しようとしても設定中のパスワードを入力するように要求されてしまう。
パスワードの入力無しで更新できるようにします。
先程と同様に、コントローラーに以下を追記します。controllers/users/registrations_controller.rbdef update_resource(resource, params) resource.update_without_password(params) endあとはviewにあるcurrent_passwordのフォーム部分を削除すればokです。
参考にさせていただいた記事(参考というより殆どそのまま)
https://qiita.com/Tatty/items/a9759755e562ac4693ec
https://note.com/ruquia7/n/n4838547cb054
https://qiita.com/machamp/items/f6a7b003fcda3f04094a
- 投稿日:2021-01-03T18:44:47+09:00
【Rails】validatesエラー文を日本語化する方法
https://qiita.com/satreu16/items/a072a4be415f30087ed7
https://qiita.com/Ushinji/items/242bfba84df7a5a67d5b
上記2つの記事を参考にさせていただくことで、無事に日本語化することが出来ました。
- 投稿日:2021-01-03T18:41:44+09:00
【CircleCI】ECS自動デプロイ - CircleCI編 -
参考文献
- [Terraform][Backends][v0.9]tfstateファイルの管理方法
- グループ会社のインフラをECS/Fargateに移行して振り返る
- [AWS][Terraform][Fargate]ECSでコンテナをALB配下に置く
- circleci/aws-ecs@1.4.0
- AWS ECR/ECS へのデプロイ
CircleCIコマンド
◆ config.ymlチェック
$ yamllint .circleci/config.yml◆ CircleCI jobチェック
$ circleci orb validate .circleci/config.yml $ circleci local execute -c .circleci/config.yml --job build $ circleci build --job rspec .circleci/config.yml.circleci/config.yml
.circleci/config.ymlversion: 2.1 orbs: aws-ecr: circleci/aws-ecr@6.12.2 aws-ecs: circleci/aws-ecs@1.3.0 /// executors: ジョブのステップ実行する環境を定義 /// executors: default: docker: - image: circleci/ruby:2.7.1-node-browsers-legacy environment: BUNDLE_JOBS: 3 BUNDLE_RETRY: 3 BUNDLE_PATH: vendor/bundle RAILS_ENV: test DATABASE_HOST: '127.0.0.1' DB_USERNAME: 'root' DB_PASSWORD: 'XXXXXX' - image: circleci/mysql:5.7 environment: MYSQL_DATABASE: sample_dev MYSQL_USER: 'root' MYSQL_ROOT_PASSWORD: 'XXXXXX' docker_build: machine: docker_layer_caching: true /// commands: ジョブ内で実行する一連のステップをマップとして定義 /// commands: bundle_install_rspec: steps: - run: name: Which bundler? command: bundle -v /// ジョブのキャッシュを復元することで、ジョブ高速化 /// - restore_cache: keys: - cache-gem-{{ checksum "Gemfile.lock" }} - cache-gem- - run: name: Bundle Install command: bundle check || bundle install - save_cache: key: cache-gem-{{ checksum "Gemfile.lock" }} paths: - vendor/bundle - run: name: Database create command: DISABLE_SPRING=true bin/rake db:create --trace - run: name: Database setup command: DISABLE_SPRING=true bin/rake db:schema:load --trace - run: name: Run rspec command: | TZ=Asia/Tokyo \ bundle exec rspec --profile 10 \ --out test_results/rspec.xml \ --format progress \ $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) /// Vueインストール /// vue-installation: steps: - restore_cache: keys: - cache-yarn-{{ checksum "yarn.lock" }} - cache-yarn- - run: name: Yarn Install command: yarn install - save_cache: key: cache-yarn-{{ checksum "yarn.lock" }} paths: - node_modules /// jobs:実行処理 /// jobs: rspec: working_directory: ~/rspec executor: default steps: - checkout - bundle_install_rspec - vue-installation deploy_app: working_directory: ~/app executor: default steps: - setup_remote_docker - checkout /// workflows:全てのジョブのオーケストレーション /// workflows: version: 2 build-and-deploy: jobs: - rspec - deploy_app: requires: - rspec - aws-ecr/build-and-push-image: requires: - deploy_app account-url: AWS_ECR_ACCOUNT_URL aws-access-key-id: AWS_ACCESS_KEY_ID aws-secret-access-key: AWS_SECRET_ACCESS_KEY region: AWS_DEFAULT_REGION repo: "${AWS_RESOURCE_NAME_PREFIX}" dockerfile: docker/dev/app/Dockerfile tag: "${CIRCLE_SHA1}" - aws-ecs/deploy-service-update: requires: - aws-ecr/build-and-push-image aws-region: AWS_DEFAULT_REGION family: "${AWS_RESOURCE_NAME_PREFIX}-service" cluster-name: "${AWS_RESOURCE_NAME_PREFIX}-cluster" container-image-name-updates: "container=${AWS_RESOURCE_NAME_PREFIX}-container,image-and-tag=${AWS_ECR_ACCOUNT_URL}/${AWS_RESOURCE_NAME_PREFIX}:${CIRCLE_SHA1}" - aws-ecs/run-task: requires: - aws-ecs/deploy-service-update cluster: "${AWS_RESOURCE_NAME_PREFIX}-cluster" aws-region: AWS_DEFAULT_REGION task-definition: "${AWS_RESOURCE_NAME_PREFIX}-task-definition" count: 1 launch-type: FARGATE awsvpc: true subnet-ids: subnet-XXXXXX,subnet-XXXXXX security-group-ids: sg-XXXXXX,sg-XXXXXX overrides: "{\\\"containerOverrides\\\":[{\\\"name\\\": \\\"${AWS_RESOURCE_NAME_PREFIX}-container\\\",\\\"command\\\": [\\\"bundle\\\", \\\"exec\\\", \\\"rake\\\", \\\"db:migrate\\\", \\\"RAILS_ENV=test\\\"]}]}"
- 投稿日:2021-01-03T17:48:14+09:00
Sidekiq-limit_fetchが仕事しない
ruby on railsの開発において並列処理の実行数を管理したかったが、うまくいかなかったのでその原因のお話
Sidekiq-limit_fetchとは
Sidekiq-limit_fetchはsidekiqでの並列処理の実行数を管理するためのgemです。
https://github.com/brainopia/sidekiq-limit_fetch
Gemfilegem 'sidekiq-limit_fetch'sidekiq.yml:queues: - queue1 - queue2 :limits: queue1: 2 queue2: 5上記の場合、queue1は並列実行可能数が2、queue2は並列実行可能数が5となります。
Sidekiq-limit_fetchが仕事しなかったお話
上記のようにsidekiq-limit_fetchのgemを使用しているにも関わらず並行実行数が指定数を超えることが起きました。
この原因としては他のgemによるlimitの設定関数のオーバーライドでした。
Gemfilegem 'serverspec'上記のgemが原因でSidekiq-limit_fetchに記載されたset関数が正常に処理されませんでした。
serverspecはサーバーのテストを自動化するためのgemです。
そのため、serverspecをGemfileから削除することでsidekiq-limit_fetchは仕事をしてくれるようになりました。しかし、並列実行数が稀に超過する現象は引き続き発生したため、自身でジョブの実行数を管理するコードを書く方が安全だということがわかりました。
- 投稿日:2021-01-03T17:43:52+09:00
【Docker】ECS自動デプロイ - Docker編 -
参考文献
- [Terraform][Backends][v0.9]tfstateファイルの管理方法
- グループ会社のインフラをECS/Fargateに移行して振り返る
- [AWS][Terraform][Fargate]ECSでコンテナをALB配下に置く
- circleci/aws-ecs@1.4.0
- AWS ECR/ECS へのデプロイ
ツリー図
docker/dev├── app │ ├── Dockerfile │ ├── nginx │ │ ├── app.conf │ │ └── nginx.conf │ └── supervisor │ ├── app.conf │ └── supervisord.conf ├── db │ ├── data │ └── mysql_initDockerコマンド
◆ dockerコンテナ削除
$ docker rm -f `docker ps -a -q`◆ dockerイメージ削除
$ docker rmi `docker images -q`◆ dockerイメージ作成
$ docker build -f docker/dev/app/Dockerfile -t sample_dev .◆ ECRプッシュコマンド
$ docker tag XXXXXX sample/dev //Docker hubリポジトリ $ docker images $ docker push sample/dev $ docker tag sample/dev:latest XXXXXX.dkr.ecr.XXXXXX.amazonaws.com/ecs-sample:latest $ docker push XXXXXX.dkr.ecr.XXXXXX.amazonaws.com/ecs-sample:latest◆ dockerコンテナ内実行コマンド
$ bundle install $ bundle exec rake db:create db:migrate // supervisor起動 $ /usr/bin/supervisorctl restart app // production用データベース作成 $ bundle exec rails db:migrate RAILS_ENV=production // アセットプリコンパイル $ bundle exec rake assets:precompile RAILS_ENV=production◆ database.ymlコマンド
$ export RAILS_DATABASE_USERNAME=test $ export RAILS_DATABASE_PASSWORD=password $ export RAILS_DATABASE_HOST=rds.XXXXXX.XXXXXX.rds.amazonaws.com $ export RAILS_DATABASE_PORT=3306◆ nginx && pumaコマンド
// ポート確認 $ ps -ef | grep nginx $ ps aux | grep nginx // nginx停止コマンド $ nginx -s stop // PID関連コマンド $ touch /var/run/nginx.pid $ touch /run/nginx.pid // ポート占有確認 $ sudo lsof -i:80 $ ps ax | grep rails // puma起動 $ bundle exec puma -C config/puma.rb $ bundle exec pumactl start◆ supervisorコマンド
// supervisor起動 $ /etc/init.d/supervisor start $ supervisord -c /etc/supervisor/supervisord.conf // supervisorのsocketコマンド $ sudo touch /var/run/supervisor.sock $ sudo chmod 777 /var/run/supervisor.sock $ supervisorctl help stop // supervisordの設定の「/var/run」を「/dev/shm」 に変更する $ sed -i "s/\/var\/run/\/dev\/shm/g" /etc/supervisor/supervisord.confDockerfile
DockerfileFROM ruby:2.7.1 ENV APP_ROOT /var/www/sample_dev ENV LANG C.UTF-8 ENV TZ Asia/Tokyo /// ディレクトリ作成 /// RUN mkdir -p $APP_ROOT RUN mkdir -p /root/tmp WORKDIR $APP_ROOT /// Node.js、Nginx, supervisorインストール /// RUN apt-get update -y && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ bash \ build-essential \ git \ libcurl4-openssl-dev \ libghc-yaml-dev \ libqt5webkit5-dev \ libxml2-dev \ libxslt-dev \ libyaml-dev \ linux-headers-amd64 \ default-mysql-client \ nginx \ nodejs \ openssl \ ruby-dev \ ruby-json \ tzdata \ vim \ supervisor \ zlib1g-dev && \ apt-get clean -y && \ rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* /// supervisor用logディレクトリ作成 /// RUN mkdir -p /var/log/supervisor /// nginx.conf, conf.d/app.conf作成 /// COPY app/nginx/nginx.conf /etc/nginx/nginx.conf COPY app/nginx/app.conf /etc/nginx/conf.d/app.conf /// supervisord.conf, conf.d/app.conf作成 /// COPY app/supervisor/supervisord.conf /etc/supervisor/supervisord.conf COPY app/supervisor/app.conf /etc/supervisor/conf.d/app.conf /// シンボリックリンク(ヘルスチェックログ) /// /// 各nginxアクセスログ /// RUN ln -sf /dev/stdout /var/log/nginx/access.log RUN ln -sf /dev/stdout /var/log/nginx/app.access.log RUN ln -sf /dev/stderr /var/log/nginx/app.error.log /// アプリケーションログ(pumaログ) /// RUN ln -sf /dev/stdout $APP_ROOT/log/development.log RUN ln -sf /dev/stdout $APP_ROOT/log/production.log CMD [ "/usr/bin/supervisord" ]docker-compose.ymlversion: '3' services: app: build: context: . dockerfile: app/Dockerfile volumes: - ~/sample_dev:/var/www/sample_dev /// ホスト側で80番ポートの許可が必要 /// /// nginxでバーチャルホストを設定する /// ports: - 80:80 environment: MYSQL_ROOT_PASSWORD: XXXXXX depends_on: - db tty: true stdin_open: true db: image: mysql:5.7 ports: - 3306:3306 volumes: # mysql初期化 - ./db/mysql_init:/docker-entrypoint-initdb.d - ./db/data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: XXXXXXNginx
nginx.confuser root; worker_processes 1; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; }app.conf/// アクセスログ、エラーログ設定 /// access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log warn; upstream app { server unix:///var/www/sample_dev/tmp/sockets/puma.sock; } server { listen 80; server_name dev.sample.com; /// URLのパス設定 /// location / { /// リバースプロキシ設定 /// proxy_pass http://app; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; } }puma
config/puma.rbthreads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i threads threads_count, threads_count # ポートを開放しておかないと、socketのlistenが行われない #port ENV.fetch('PORT') { 3000 } environment ENV.fetch('RAILS_ENV') { 'development' } plugin :tmp_restart app_root = File.expand_path('../..', __FILE__) # nginxのhttpディレクティブでsocket通信を行う bind "unix://#{app_root}/tmp/sockets/puma.sock" stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", truesupervisor
supervisord.conf/// supervisor.sock作成 /// [unix_http_server] file=/var/run/supervisor.sock [supervisord] /// nodaemon=true: supervisorがforground(最前面)プロセスで振舞う /// nodaemon=true logfile=/var/log/supervisor/supervisord.log pidfile=/var/tmp/supervisord.pid /// rpcinterfaceを有効にすると、supervisorctlが有効になる /// [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface /// supervisorctlを使ってプロセス管理を可能にする /// [supervisorctl] serverurl=unix:///var/run/supervisor.sock [include] files = /etc/supervisor/conf.d/*.confapp.conf[program:app] command=bundle exec puma -C ./config/puma.rb autostart=true autorestart=true stopsignal=TERM user=root directory=/var/www/sample_dev/ stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:nginx] command=/usr/sbin/nginx -g "daemon off;" autostart=true autorestart=true stopsignal=TERM user=root stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0
- 投稿日:2021-01-03T17:25:00+09:00
【Rails】DeviseとOmniauthを使ってGoogle、Twitter、Facebook認証
DeviseとOmniauthを使って、Railsアプリにソーシャルログイン機能を実装する方法をまとめます。
例えばGoogleログインだけを使いたい場合は、TwitterとFacebook用の記述は無視してください。Deviseをインストール
Gemfilegem 'devise'$ bundle install $ rails g devise:installconfig/environments/development.rbconfig.action_mailer.default_url_options = { host: 'localhost', port: 3000 }app/views/layouts/application.html.erb<p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p>Modelを作成
$ rails g devise User $ rails db:migrateconfig/routes.rbRails.application.routes.draw do devise_for :users # 自動で追加される endViewを作成
$ rails g devise:views usersログインしないとアクセスできないようにする
app/controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :authenticate_user! endここまでできたらサーバーを再起動。
ログインページや新規登録ページ以外にアクセスしたらログイン画面に飛ばされること、
/users/sign_upにアクセスして、ユーザー登録ができることを確認。環境変数を設定
Gemfilegem 'dotenv-rails'$ bundle install $ touch .env.envHOST='hogehoge.com' TWITTER_API_KEY= TWITTER_API_SECRET= GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= FACEBOOK_KEY= FACEBOOK_SECRET=IDやSECRET等はあとで取得するので、今は空でOK。
omniauth系のgemをインストール
Gemfilegem 'omniauth-facebook' gem 'omniauth-twitter' gem 'omniauth-google-oauth2'$ bundle installUserテーブルにomniauth用のカラムを追加
$ rails g migration AddOmniauthToUsers provider:string uid:string $ rails db:migrateinitializerを編集
config/initializers/devise.rbconfig.omniauth :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], scope: 'email', info_fields: 'email', callback_url: "#{ENV['HOST']}/users/auth/facebook/callback" config.omniauth :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET'], scope: 'email', oauth_callback: "#{ENV['HOST']}/users/auth/twitter/callback" config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: 'email', redirect_uri: "#{ENV['HOST']}/users/auth/google_oauth2/callback" OmniAuth.config.logger = Rails.logger if Rails.env.development?OmniauthCallbacksControllerを作成
$ mkdir app/controllers/users/ $ touch app/controllers/users/omniauth_callbacks_controller.rbcontrollers/users/omniauth_callbacks_controller.rbclass Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook callback_for(:facebook) end def twitter callback_for(:twitter) end def google_oauth2 callback_for(:google) end # common callback method def callback_for(provider) @user = User.from_omniauth(request.env["omniauth.auth"]) if @user.persisted? sign_in_and_redirect @user, event: :authentication #this will throw if @user is not activated set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format? else session["devise.#{provider}_data"] = request.env["omniauth.auth"].except("extra") redirect_to new_user_registration_url end end def failure redirect_to root_path end endUserモデルを編集
app/models/user.rbclass User < ApplicationRecord # デフォルトの設定に、:omniauthable以下を追加 devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: %i[facebook twitter google_oauth2] # omniauthのコールバック時に呼ばれるメソッド def self.from_omniauth(auth) where(provider: auth.provider, uid: auth.uid).first_or_create do |user| user.email = auth.info.email user.password = Devise.friendly_token[0,20] end end endルーティングを編集
config/routes.rbRails.application.routes.draw do devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' } #省略 endログイン画面、サインアップ画面を確認
ソーシャルログイン用のリンクが追加されていることを確認します。
プラットフォームごとの設定
以下の記事等を参考に、Twitter Developersで
API key
とAPI key secret
を取得して、.env
にセット。
https://dev.classmethod.jp/articles/twitter-api-approved-way/.envTWITTER_API_KEY='hogetwkeyhoge' TWITTER_API_SECRET='fugatwsecretfuga'そして
Edit authentication settings
をいじって、アプリのドメイン/users/auth/twitter/callback
をセットして完成。
この辺の記事を見ながらGCPでアプリを作成し、
クライアント ID
とクライアント シークレット
を取得して、.env
にセット
https://qiita.com/nakanishi03/items/2dfe8b8431a044a01bc6#google.envOOGLE_CLIENT_ID='hogehogehoge.apps.googleusercontent.com' GOOGLE_CLIENT_SECRET='dgpV8aMmmDzfugafuga'
承認済みのリダイレクト URI
にドメイン/users/auth/google_oauth2/callback
をセット。
AppID
とAppSecret
を取得して環境変数にセット。.envFACEBOOK_KEY='foobarsdsfe' FACEBOOK_SECRET='bazdejnerf'
Valid OAuth Redirect URIs
にドメイン/users/auth/google_oauth2/callback
をセット。参考
- 投稿日:2021-01-03T17:08:23+09:00
【教材】Tailwind CSS & Rails6でQiitaを模写してみた【バックエンド演習に利用可】
この教材はこんな人におすすめ
- Tailwind CSSが流行っていると知っているけど学習が面倒くさい人
- Tailwind CSSを使ったビューの完成コードが見たい人
- フロント完成コードを元に機能実装(バックエンド)演習をしてみたい人
- 駆け出しエンジニアなので現役エンジニアが書いているコードが見たい人
完成コードをどうぞ
https://github.com/Fumiya-Soeno/tailwind
完成図
Tailwind CSSチートシート
こちらも一緒に眺めましょう。分からないクラス名はここで検索すれば載ってます。
https://nerdcave.com/tailwind-cheat-sheetFont Awesomeチートシート
fa fa-check
みたいなクラス名はTailwindCSSではなくFontAwesomeという別のCSSフレームワークです。アイコン表示に使ってます。
https://fontawesome.com/cheatsheetこれを教材として使う方法(インフラ・バックエンド向け)
問1:以下のQiita内部の仕組みを追加実装せよ
- 記事投稿/閲覧/編集/削除/LGTM(いいね)/記事コメント
- 記事キーワード検索/記事タグ付与/記事タグ検索
- ユーザー登録/プロフィール編集/ユーザーフォロー
- フォロー/記事コメント/LGTM(いいね)の通知
- 記事に画像添付/プロフィール画像設定
- 下書き保存/編集/削除
- 外部SNSリンク(API)
上記repoのようなビューのみコードを渡され『機能実装の部分を担当してね』みたいな仕事が現場によくあります。なので、上記の機能をMySQLやらActiveRecordなりで頑張って実装して下さい。該当箇所にはダミーコードがたくさん埋まってるのでこれをMySQLのデータに紐付ける作業です。
市場相場でいうとこの仕事で大体単価20~50万円くらいです。クラウドソーシングで受ければ多分20万円&納期1ヶ月だと思われます。経験1年のエンジニアなら20日くらいあれば納品可能な規模だと思います。これができれば平日稼働のみで月収20万はいけます多分。
問2:機能実装後、本番環境で動作確認せよ
インフラ学習したい方は、上記の機能実装後、これをAWSなりレンタルサーバFTPなどにあげてみると勉強になりそうです。画像投稿機能(AWSならバケット周り)もしっかり実装できていれば素晴らしいです。EC2のLinuxインスタンスとかdockerあたり使うと色々楽になるのでやってみましょう。こんだけできれば食うには困りません。
問3:広告(実物)を表示せよ
ここはおまけですが、画面右側の広告がダミー(画像)になっているので、これを実際の広告表示にできればあなたが個人サイトを開発すれば広告運用の練習にもなります。このスキルは独立に大変役立つので、余裕があればA8.netやGoogleアフィリエイトなどの広告表示に取り組んでみましょう。注意事項
- トラブル回避したいので、コードの再配布・自作発言はお控え下さい。
Railsを学びたい人向け
Mediumにてこの記事の筆者が学習ロードマップを無料公開中です
そもそもRailsが分からんという方は、上記を読んで頂ければ上に挙げた機能実装スキルは網羅できます。なお、Medium記事のclapボタンを50連打して頂けたら筆者は泣いて喜びます。
筆者連絡先
- 投稿日:2021-01-03T17:08:23+09:00
【無料教材】Tailwind CSS & Rails6でQiitaを模写してみた【バックエンド演習に利用可】
こんな人におすすめ
- Tailwind CSSが流行っていると知っているけど学習が面倒くさい人
- Tailwind CSSを使ったビューの完成コードが見たい人
- フロント完成コードを元に機能実装(バックエンド)演習をしてみたい人
- 駆け出しエンジニアなので現役エンジニアが書いているコードが見たい人
完成コードをどうぞ
https://github.com/Fumiya-Soeno/tailwind
完成図
これを教材として使う方法(インフラ・バックエンド向け)
問1:以下のQiita内部の仕組みを追加実装せよ
- 記事投稿/閲覧/編集/削除/LGTM(いいね)/記事コメント
- 記事キーワード検索/記事タグ付与/記事タグ検索
- ユーザー登録/プロフィール編集/ユーザーフォロー
- フォロー/記事コメント/LGTM(いいね)の通知
- 記事に画像添付/プロフィール画像設定
- 下書き保存/編集/削除
- 外部SNSリンク(API)
上記repoのようなビューのみコードを渡され『機能実装の部分を担当してね』みたいな仕事が現場によくあります。なので、上記の機能をMySQLやらActiveRecordなりで頑張って実装して下さい。該当箇所にはダミーコードがたくさん埋まってるのでこれをMySQLのデータに紐付ける作業です。
市場相場でいうとこの仕事で大体単価20~50万円くらいです。クラウドソーシングで受ければ多分20万円&納期1ヶ月だと思われます。経験1年のエンジニアなら20日くらいあれば納品可能な規模だと思います。これができれば平日稼働のみで月収20万はいけます多分。
問2:機能実装後、本番環境で動作確認せよ
インフラ学習したい方は、上記の機能実装後、これをAWSなりレンタルサーバFTPなどにあげてみると勉強になりそうです。画像投稿機能(AWSならバケット周り)もしっかり実装できていれば素晴らしいです。EC2のLinuxインスタンスとかdockerあたり使うと色々楽になるのでやってみましょう。こんだけできれば食うには困りません。
問3:広告(実物)を表示せよ
ここはおまけですが、画面右側の広告がダミー(画像)になっているので、これを実際の広告表示にできればあなたが個人サイトを開発すれば広告運用の練習にもなります。このスキルは独立に大変役立つので、余裕があればA8.netやGoogleアフィリエイトなどの広告表示に取り組んでみましょう。Tailwind CSSチートシート
こちらも一緒に眺めましょう。分からないクラス名はここで検索すれば載ってます。
https://nerdcave.com/tailwind-cheat-sheetFont Awesomeチートシート
fa fa-check
みたいなクラス名はTailwindCSSではなくFontAwesomeという別のCSSライブラリです。アイコン表示に使ってます。
https://fontawesome.com/cheatsheet注意事項
- トラブル回避したいので、コードの再配布・自作発言はお控え下さい。
Railsを学びたい人向け
Mediumにてこの記事の筆者が学習ロードマップを無料公開中です
そもそもRailsが分からんという方は、上記を読んで頂ければ上に挙げた機能実装スキルは網羅できます。なお、Medium記事のclapボタンを50連打して頂けたら筆者は泣いて喜びます。
筆者連絡先
- 投稿日:2021-01-03T16:05:05+09:00
【買付代行サービス個人開発 - No.012】ドロップダウンをJSで実装
概要
右上のドロップダウンメニューが常に表示されているので、ユーザーアイコンをクリック後、ドロップダウンメニューが表示されるようにする。
参考
tailwind ui
https://tailwindui.com/components/application-ui/elements/dropdowns
tippy.js
https://atomiks.github.io/tippyjs/v6/all-props/
https://on-ze.com/archives/7310
ToDo内容
Terminal# npm npm i tippy.js # Yarn yarn add tippy.js yarn add turbolinksapp/javascript/src/js/dropdown.jsimport tippy from 'tippy.js' import 'tippy.js/animations/perspective.css'; document.addEventListener('turbolinks:load', init) function init () { tippy('[data-dropdown]', { content (ref) { if (!ref.dataset.dropdownTemplate) { const id = ref.getAttribute('data-dropdown') const template = document.getElementById(id) ref.dataset.dropdownTemplate = template.innerHTML template.remove() } return ref.dataset.dropdownTemplate }, trigger: 'click', allowHTML: true, interactive: true, appendTo: () => document.body, animation: 'perspective', duration: 100, placement: 'bottom-end' }) }app/components/layout/navbar_component.html.slimbutton.flex.text-sm.border-2.border-transparent.rounded-full.focus:outline-none.focus:border-gray-300.transition.duration-150.ease-in-out( type="button" class="group h-full flex items-center px-4 text-left focus:outline-none focus:rounded-md" data-dropdown="navbar-dropdown" ) i.fas.fa-user-circle.fa-2x.text-gray-500 #navbar-dropdown.hidden .origin-top-right.absolute.right-0.mt-2.w-48.rounded-md.shadow-lg .py-1.rounded-md.bg-white.shadow-xs = link_to [:orgs], class: 'block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out' do | 会社切替 / TODO:リンクを@orgに書き換える = link_to [@org], class: 'block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out' do | 会社詳細動作確認
準備
Terminal-1bin/rails s
Terminal-2bin/webpack-dev-server
受入基準
- 画面右上のドロップダウンメニューが、ユーザーアイコンのクリック後、表示される
- 投稿日:2021-01-03T15:45:46+09:00
[Rails] 初心者がコーディングする際の事前知識(コントローラー作成、アクション作成、アクション以外のクラス作成、ルーティング設定)
背景
Rails環境構築ができた後のコーディングの進め方をまとめました。
また、Ruby自体初心者の方は下記の記事に目を通しておくといいと思います。
・Ruby基礎文法コントローラー作成
コントローラーは基本的にモデル単位(DBのテーブル単位)で作成します。コントローラー名は複数形にします。
下記コマンドでコントローラーとコントローラーに対応するViewファイルをまとめて作成できます。
ちなみに、Railsの命名規則でファイル名がスネークケース、クラス名はキャメルケースとなっています。rails generate controller コントローラー名上記コマンド実行時のコントローラー名はスネークケース、キャメルケースのどちらでも作成されるファイル名はスネークケースになります。
また、上記のコマンドを使わず手動でコントローラーを作成することもできます。その際はコントローラーに対応するViewファイルも作成しましょう。アクション作成
アクションとはController内のメソッドのことです。
アクション内で行う処理がない場合も空のメソッドを作成します。
下記のようにすると、コマンドでコントローラー作成と同時にアクションを作成できます。rails generate controller コントローラー名 アクション名1 アクション名2新しく作ったアクションにさせたい処理が既存アクションですでに実装されている場合、
render
を使用することで既存アクションを使いまわすことができます。下記は例です。def create #他アクションに対応するViewを返す render action: :new #他コントローラーのアクションを返す render template: "hoge/new" #jsonを返す render json: @hoge #テキストを返す render text: "hoge" #xmlを返す render xml: @hoge endアクションではActiveRecordを介してデータベースを操作し、Viewに値を渡します。
下記は7つのアクションについての記事です。
[Ruby on rails]7つのアクション以外のアクションの設定アクション以外のクラス作成
プロジェクト内の任意の場所にディレクトリを作成し、そこにクラスを作りましょう。(app/commonclass/Hoge.rbやlib/Hoge.rbなど)
作ったらコントローラーから下記のように読み込ませます。class HogeController < ApplicationController #クラス読み込み require './app/commonclass/hoge' def index end end上記のようにrequireを使う以外にも、
config/application.rb
に下記を追記すれば読み込ませることができます。config.autoload_paths += %W(#{config.root}/読み込ませたいディレクトリ #{config.root}/読み込ませたいディレクトリ)ルーティング設定
下記の記事を参考にしました。
Rails ルーティング 基礎 まとめ蛇足
下記は命名規則についての記事です。
・Railsにおける命名規則特に下記の記事を参考にさせていただきました。
・他言語経験者による初めてのrails
・【Rails】クラスファイルを用意してcontrollerから呼び出したい最後に、参考にさせていただいた記事の投稿者の皆様、ありがとうございます。
私の記事に不備などありましたらご指摘いただけると幸いです。
- 投稿日:2021-01-03T15:36:54+09:00
VirtualBox,Vagrant, cyberduck を用いたRuby on Rails の環境構築
はじめに
本格的にプログラミングを始めるために新しくMacbookproを買い環境構築を行ったので、次に行うときのための備忘録として記事を書かせていただきます。
初心者のため間違えているところなどある可能性があります(ご指摘いただけると非常にありがたいです)。その点ご留意の上お読みください。全体の流れ
以下のような流れで行いました。
- VirtualBoxとVagrantで仮想環境構築
- 1で構築した仮想環境にrbenvをインストールし、rubyとrailsを導入
- cyberduckを用いて仮想環境に接続、vscodeでファイル編集を可能にする
使用したツールのバージョン
参考までに僕が使用したマシンのスペックおよびツールのバージョンを記載します。
マシンスペック
macOS Cataline 10.15.7
MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ: 2 GHz クアッドコアIntel Core i5
メモリ: 16 GB 3733 MHz LPDDR4X各種ツール
VirtualBox: 6.1.16
Vagrant: 2.2.14
cyberduck: 7.7.2VirtualBox,およびVagrantの導入
https://www.sejuku.net/blog/39936 を参考に行いました。Vagrantにcentos7を導入するところまでは全く同様で行けました。
rbenv, ruby, rails の導入
次に、rbenvを導入しました。初めはxcode, homebrew を導入したのち、homebrew管理下でrubyをインストールする、という方法を参考にしていたのですが、xcodeコマンドラインツールを導入しているときにエラーが出て解決法が分からず詰んだので直接rbenvを導入する方法に切り替えました。
初めの方法の際に参考にしていたサイトは以下に貼っておきます。homebrewを用いる方法
https://qiita.com/TAByasu/items/47c6cfbeeafad39eda07rbenvを用いる方法は、Vagrantの導入で参考にした上記サイトの続きをそのまま用いました。これで、Railsを導入するところまでは問題なく行けました。しかし、仮想環境上における作業領域(僕の場合は home/vagrant/app/score_app)で以下のコマンド
home/vagrant/app/score_appbin/rails sを用いてもローカルサーバーへの接続が行えませんでした。これは以下の方法で解決しました。
仮想環境から出て、仮想マシンを設置しているフォルダ(僕の場合は、/ユーザー/ユーザー名/MyVagrant/centos7)で、以下のコマンド
/user/user_name/MyVagrant/centos7vi Vagrantfileを実行し、Vagrantfileを編集。ipアドレスの設定を行いました。内容は以下のサイトの行った解決方法①、②を参照。
https://qiita.com/Ago0727/items/325df5e39e3406fa16d2
①のみでは接続できなかったのですが、②で接続できました。ただし、①でも書かれていますがVagrantを用いる場合は接続の際にオプションとして以下を設定しないと接続できないようです。
user/user_name/MyVagrant/centos7bin/rails s -b 0.0.0.0cyberduckへの接続
ここも基本的に次のサイトを参考に行えました。
https://qiita.com/Lassieena/items/603fe89df26b59ca06f7
ただし、SSH private key の設定、送信方式をSFTPにすることに気を付けておけばいけます。最後に、cyberduckの環境設定タブ(macであれば画面上部に表示される)から外部エディタにインストールしたvscodeを指定します。
以上で完了です!最後に
環境構築は様々な方法があり、適切な方法で行うのは初心者にはかなりハードルが高かったです(これで完全にできているのかも不安です)。今回僕がこの方法にたどりつくまでに2回ほど失敗して、どうしたら良いかわからず仮想環境ごと廃棄するのを2回行ったので、もしどうしようもなくなったらもう一度仮想環境から作り直すのもありかもしれません。また、一通りしてみて感じたことは、コマンドの詳細などは理解できないところも多いとは思いますが自分がどこのフォルダにいて、どこに各パッケージをインストールすべきか(しているのか)を把握して構築することが大切だと感じました。仮想環境の廃棄に関してはvagrant コマンド などで検索すれば出てくるので、もし使うときは調べてみるといいかもです。
- 投稿日:2021-01-03T14:00:16+09:00
「え!?いつのまに新刊出てたの!?」を解消するアプリを作りました【ポートフォリオ解説】
はじめに
実務未経験者がどうやって・どんなポートフォリオを制作したのか
ポートフォリオの解説→意識したこと・苦労したことの順で解説します。よければ読んでみてください
コンセプト
「え!?いつのまに新刊出てたの!?」を解消する
好きなマンガや小説など・好きな作家を登録しておくと新刊の発売3日前にメールでお知らせします
ちなみにアプリ名はブクシルです。「ブック」+「(新刊の発売を)知る」ですね。
我ながら気に入ってます。寒くないです。だれですか寒いって言ったの。使用イメージ
書籍の検索ができます(楽天API使用)
お気に入り登録ができます(作品のシリーズと作者名)
発売3日前にメールでお知らせします
開発背景
せっかくポートフォリオをつくるんだったら、だれかの役に立つものが作りたいと思い、以下の流れで作り始めました
- 周囲の知人・友人に「どんなサービスがあったら便利か」「日常生活で不便に感じていることはないか」とヒアリング
- 「気づいたら新刊が発売されていることがあるから事前に知りたい」との意見を聞く
- 自分自身、読書が趣味で同じ悩みを持っていたこともあり、それを解決できるアプリを作りたいとの想いで製作開始
使用技術
フロントエンド
HTML/CSS(Sass)
Bootstrap 4.5.2
jQuery 3.5.1バックエンド
Ruby 2.6.6
Ruby on Rails 6.0.3.3API
楽天書籍ブックスAPI、楽天ジャンル検索APIDBMS
PostgreSQL 13.1インフラ
AWS(VPC・EC2・RDS・S3・IAM・Route53・ACM)
独自ドメイン
HTTPS化DB設計
一番苦労したのがDB設計です。
開発前に軽くER図を作ってからスタートしましたが、当初の構想ではうまくリレーションできず、何回も作り直すことになりました。
最終的に下図の通りになりました。
開発で意識したこと
1. エラー解決方法
実務に入ったときに先輩社員や上司の時間を無駄に奪うことにならないように
まずは自分の力で解決することを強く意識しました。
具体的には、前後することもありますがおおよそ次の順番です。
- エラーメッセージやログをよく読む
- どこの段階でエラーが出ているか推測する
- bindnig.pryでデバッグする
- エラー内容を調べる。(なるべく一次情報から読み解くことを意識。)
- どうしても解決できなければメンターの方に質問する
2. 質問の仕方
Slackを使ってだいたい以下の構成で質問していました。
なるべく少ないやり取りで済み、相手の人の時間を無駄に奪わない内容にすることを意識しました。
- やりたいこと・困っていることを端的に表現
- 自分なりに試したこと (書いたコード・コマンド)
- 試した結果 (エラーメッセージ)
- わからないなりに怪しいと思っている部分を述べる
・・・違っている場合にメンターの方から意見を頂ける
→理解が深まり、以降の質問を減らせる- GitHubのソースコードを記載・・・思わぬところにエラーの原因があることも多いため
実務において1人でずっと悩み続けることはかえって損失になってしまうと思うので
上記のステップである程度独力でやって解決できなかった場合には、端的かつ過不足ない内容で質問することで時間的な損失を最小限にすることを心がけていきたいと思います。3. 模擬チーム開発
実務を想定して模擬チーム開発を意識しました。
・ Git, GitHubを用いたソース管理
・ 必要要件をIssuesに列挙し、タスク消化
・ featureやfixのブランチを切ってプルリクベースで開発ただ、Issuesはプルリクと結びつけられると後で知ったので、使い方が甘かったなと反省しています。
開発で特に苦労したところ
DB設計
上でも述べましたが一番苦労しました。
リレーションについてわかったつもりでも、いざ自分のアプリに落とし込むとなるとうまくいかず、何度も紙に書いては関係性を整理しました。楽天書籍ブックスAPIの適用
公式ドキュメントだけではどう扱うのかのイメージがつかず、調べた限りでは使用例も少なかったです。
少ない情報の中からどう自分のアプリに落とし込むのか、使用例からどこが一般化できるのか、実際にコードを書きながら試行錯誤を繰り返しました。感想
エラー解決がめちゃくちゃ楽しい。
エラーを乗り越えたときに両手を突き上げるほど興奮したことが何回かあります。
ゲームで強いボスを倒したときみたいな感じですね。
負けては次の手を考え実行する、を繰り返して最終的に勝つみたいな。プログラミング学習をはじめたころはエラーが怖かったと思うのですが、成長したのかなあと。
あとは仮設と検証を繰り返す作業が現職の研究業務と似ているなあと思ったりしていました。課題/今後実装したいこと
やりたいことはまだ残っているので、ひとつずつ良くしていきたいと思います。
・レスポンシブWebデザイン(スマホ対応)
・お気に入り登録/解除時にフラッシュ表示
・お気に入り登録/解除時の非同期処理
・LINEログイン
・LINEのMessaging APIを使用してLINEで通知さいごに
長い文章をここまで読んでくださってありがとうございました
良かったと思った方はLGTMして頂けると嬉しいです!
どなたかの参考になれば幸いです!
- 投稿日:2021-01-03T13:03:10+09:00
uninitialized constant LikesControllerのエラーが出たときの対処法
Railsを使って"いいね"ボタンを実装している際に、こちらのエラーに遭遇しました。
原因は、likeコントローラを自作したときに、likes.controller.rbと記述したためです。
正解はlikes_controller.rbでした。
小さなミスがエラーにつながるので、慌てず、一つずつ確実に対処していきます。
- 投稿日:2021-01-03T12:32:36+09:00
ログイン要求処理の実装方法(Rails・初心者向け)
ログイン要求処理とは、ログインしていないユーザーに対して特定のページを見せないようにするための処理です。
この記事では、ログイン要求処理の実装方法を簡単に解決しています。
$ rails g helper sessions
でhelperを用意して、current_user
とlogged_in?
を実装します。app/helpers/sessions_helper.rbmodule SessionsHelper def current_user @current_user ||= User.find_by(id: session[:user_id]) end def logged_in? !!current_user end end
def current_user
は、現在ログインしているユーザーを取得するメソッドです。
@current_user ||= User.find_by(id: session[:user_id])
は
@current_user
に現在のログインユーザーが代入されている場合 → 何もしません。
@current_user
に現在のログインユーザーが代入されていない場合 →User.find_by(...)
からログインユーザーを取得し、@current_user
に代入します。
def logged_in?
はユーザーがログインしている時はtrueを、ログインしていなければfalseを返します。コントローラーについて考えていきます。
app/controllers/application_controller.rbclass ApplicationController < ActionController::Base include SessionsHelper private def require_user_logged_in unless logged_in? redirect_to login_url end end end
ApplicationController
に書いたメソッドは、すべてのControllerで使用できるようになります。
require_user_logged_in
メソッドを定義します。
require_user_logged_in
メソッドはログインの状態を確認して、ログインしていれば何もせず、ログインしていなければ強制的にログインページに戻します。
include SessionsHelper
を書いた理由としては、Helperで定義したlogged_in?
メソッドを使えるようにするためです。では
require_user_logged_in
メソッドをUsersController
で使ってみます。app/controllers/users_controller.rbclass UsersController < ApplicationController before_action :require_user_logged_in, only: [:index, :show] #以下省略before_actionで指定されたindexとshowは、事前処理として
require_user_logged_in
メソッドが実行されます。例えばある人がusers#indexにアクセスした際に、その人がログインしいればusers#indexが見れます。
しかし、ログインしていなければログインページに飛ばされます。
このようにして、会員制のサービスなどを作ることが出来ます。
以上です。
- 投稿日:2021-01-03T11:10:06+09:00
Rails5でSprocketsを使いつつ、npmを利用する
何をしたか
今までに作ったRailsアプリは
- Sprockets + JSのライブラリはCDNでレイアウトファイルから読み込み
- Webpacker + JSのライブラリはnpmで読み込み
のパターンしか作ったことがなかったのですが、この度、
- Sprockets + JSのライブラリはnpmで読み込み
というアプリを作ることになり、やり方をメモしようと思います。
具体的にはhttps://swiperjs.com/
というライブラリの導入プロセスを紹介していきます。npm + Rails の初め方
npm自体は、
node.js
をダウンロードするとついてきます。Railsの環境構築時にnode.jsはインストールされているはずなので、$ npm -vでバージョンが表示されたらOKです。
ライブラリのインストール
インストールしたいライブラリをnpmで導入します。
$ npm install swipernpmバージョン4からは
--save
オプションをつけなくてもpackage.json
を更新してくれます。アセットパイプラインにパスを追加
node_modules
配下のパスを見ながら、アセットパイプラインにパスを追加します。今回のswiper
の場合、必要なファイルswiper-bundle.js
は下記の位置にありました。node_modules └── swiper └── swiper-bundle.jsなので、
application.js
には、以下の様に記入します。assets/javascripts/application.js//= require rails-ujs //= require activestorage //= require swiper/swiper-bundle.js # 追記 //= require_tree .初期化に必要なファイルを追加
初期化に必要なコードがあれば、
assets/javascripts
内に別ファイルを作るか、application.js
内に、記載します。今回はapplication.js
内に記しました。assets/javascripts/application.js$(function() { new Swiper('.swiper-container', { loop: true, // If we need pagination pagination: { el: '.swiper-pagination', }, // Navigation arrows navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, }) })完成
これで、無事
swiper
が動作する様になりました^^感想・参考記事等
RailsとJSの最近の流れを振り返る形になって楽しかったです^^
以下、参考にした記事です。
- 投稿日:2021-01-03T07:47:14+09:00
Dockerfileについて
Dockerfile
dockerfileはdockerの新しいイメージを作成する際に使用するもので
この設定ファイルにはrailsアプリケーション実行に必要なファイルやパッケージを
イメージに含めるための定義が書かれている。Dockerfile.1 FROM ruby:2.4.5 2 RUN apt-get update -qq && apt-get install -y build-essential nodejs 3 RUN mkdir /app 4 WORKDIR /app 5 COPY Gemfile /app/Gemfile 6 COPY Gemfile.lock /app/Gemfile.lock 7 RUN bundle install 8 COPY . /app1 :から前の部分をリポジトリとよぶ。:から後の部分をタグという。
この場合rubyリポジトリの2.4.5タグを示している。2 ruby 2.4.5のイメージからコンテナを起動してコンテナ内で実行するコマンドを定義している
ubuntuのパッケージ管理システムであるapt-getでbuild-essential nodejsをインストールしてる
Railsの動作に必要3 ルートディレクトリにappディレクトリを作成
4 作業用ディレクトリをappディレクトリに移動
5,6 PC上にあるGemfileとGemfilelockをappディレクトリにコピー
7 gemのインストールコマンドを実行
8 dockerファイルの置いてあるフォルダの内容を全ての内容をappディレクトリにコピー
dockerfile→build→dockerimageが作成される
- 投稿日:2021-01-03T07:11:55+09:00
Ruby on Rails 基礎学習 ①
まず開発環境について
dockerを使うメリット
実際の現場では,Dockerを仮想環境として使っているのがほとんど。
dockerは起動スピードが早い。
AWSにはコンテナを実行するサービスがあるためサービスの公開が容易。dockerを使用した開発環境
dockerをインストールすると軽量なリナックス(Moby Linux)がインストールされる。
dockerでrubyのコンテナを立ち上げるとMoby Linux上でRubyの実行環境コンテナが立ち上がる。
コンテナの基になるものをimageという。