【rails】Docker開発環境でのrails credentials:edit使用方法
参考:【Rails 5.2.2】Rails on Dockerでcredentialsをeditしたい - Qiita
【Rails 5.2.2】Rails on Dockerでcredentialsをeditしたい - Qiita
docker内にvimを導入
#Dockerfile RUN apt-get install -y vim
そしてdocker-compose build
実際に使用する
↓コマンドはこれです
$ docker-compose run -e EDITOR="vim" web rails credentials:edit
※Railsが動くコンテナのサービス名がwebの前提です。
ほぼコピペですですみません。
【rails】いいね機能 非同期編
やっとできたので書いていきます。
↓前回まで
【rails】いいね機能実装 (追記あり) - 技術系ブログ
参考: 第14章 ユーザーをフォローする - Railsチュートリアル
[https://qiita.com/tambo/items/45211df065e0c037d032:title]
【Rails初心者】Ajaxの実装方法を理解する(非同期通信) - Qiita
導入手順
①jqueryを使えるようにします。
②remote: true 追加
③.js.erbファイル作成
④コントローラー側編集です。
そもそも非同期通信とは、
参考:非同期処理:コールバック/Promise/Async Function · JavaScript Primer #jsprimer
初心者目線でAjaxの説明 - Qiita
その前に同期通信は 同期処理ではコードを順番に処理していき、ひとつの処理が終わるまで次の処理は行いません。
同期処理では実行している処理はひとつだけとなるため、とても直感的な動作となります。
つまり、 同期処理ではひとつの処理が終わるまで、次の処理へ進むことができない。
非同期通信は、ひとつの非同期処理が終わるのを待たずに次の処理を評価します。
つまり、非同期処理では同時に実行している処理が複数あります。
一部分のみの更新とう言う感じですかね。
Ajaxとは
Asynchronous JavaScript + XML (AJAX) はそれ自体がある種の技術というわけではありませんが、
Jesse James Garrett によって 2005 年に作られた言葉で、既存の技術同士を組み合わせた新しいアプローチを意味します。
Ajax - Wikipedia
準備
jqueryを使えるようにします。
gem 'jquery-rails'
#app/asssets/javascripts/application.js -# バージョンがRails5.1以降の場合 //= require jquery //= require rails-ujs -# Rails5.1より前の場合 //= require jquery //= require jquery-ujs
view側
#app/views/likes/_like.html.slim -if current_user.likes.find_by(post_id: @post.id) #liked = link_to post_like_path(@post, current_user.likes.find_by(post_id: @post.id)), method: :delete, remote: true do p いいね済 - else #likable = link_to post_likes_path(@post,current_user.likes.build),method: :post, remote: true do p いいね
(@post, current_user.likes.find_by(post_id: @post.id))
,(@post,current_user.likes.build)
のように書いてアクション側にid
を送ります。非同期導入前の段階では書かなくてもエラーが出ませんでしたが、
非同期通信をする際には必要なようです。
またremote: true
とそれぞれにidを追加しています
idを追加するとこで、jqueryのセレクタとして特定することができます。
remote: trueとは
参考:Rails で JavaScript を使用する - Railsガイド
フォームの送信がブラウザによる通常の送信メカニズムではなくAjaxによって送信されるようになります。
追加するだけでAjaxが使えるようになると考えてください。
そして、リクエストがhtml形式からjs形式になります。
つまり.js
ファイルを探すようになります。
詳しく説明すると、link_to post_like_path(@post)
ではhtml形式なら
controllerは「app/views/controller名/アクション名.リクエストの形式.erb」というファイルを探しに行きます。
しかし、今回のpostリクエストでいうと、html形式だとapp/views/likes/create.html.slim
を探しますが,
js形式だとapp/views/likes/create.js.erb
を読み込みます。
.js.erbファイルを作成
.js.erbファイルの作成にはファイル名を注意してください。
link_to先のアクション名に合わせてください。
app/views/controller名/アクション名.js.erb
で作成します。
今回は、create
とdestroy
2つ作ります。
touch app/views/likes/create.js.erb
touch app/views/likes/destroy.js.erb
#app/views/likes/create.js.erb $("#likable").html("<%= escape_javascript(render('likes/like')) %>");
#app/views/likes/destroy.js.erb $("#liked").html("<%= escape_javascript(render('likes/like')) %>");
escape_javascriptメソッドは、JavaScriptファイル内にHTMLを挿入するときに実行結果をエスケープするために必要です。
コントローラー側
下記をcreate,destroyアクションにそれぞれ追加します。
respond_to do |format| format.html { redirect_to @post } format.js end
respond_toメソッド
とは、
上の (ブロック内の) コードのうち、いずれかの1行が実行されます。
#app/controllers/likes_controller.rb class LikesController < ApplicationController before_action :set_post, only: [:create, :destroy] def create @like = current_user.likes.create(like_params) respond_to do |format| format.html { redirect_to @post } format.js end end def destroy @like = Like.find_by(like_params, user_id: current_user.id) @like.destroy respond_to do |format| format.html { redirect_to @post } format.js end end private def set_post @post = Post.find(params[:post_id]) end def like_params params.permit(:post_id) end end
おしまい。
rails db:seed バリデーションエラー
seedファイルを書いて、rails db:seed
したらタイトル通りのエラーになった。
rails db:reset
のみでも解決しなかった。
以下解決手順
$ rails db:drop $ rails db:create $ rails db:schema:load $ rails db:seed
bin/rails db:resetコマンドは、データベースをdropして再度設定します。このコマンドはrails db:drop db:setupと同等です。
Active Record マイグレーション - Railsガイド
rails db:reset
からcreate
で解決せずに、順番にdrop,create,shema:load,seed
で解決した理由がよくわかりませんが、うまくは行きます。
参考:
rake db:schema:load の挙動について - Qiita
Railsの rake db コマンドリスト - Qiita
rails db:migrate:resetできなかったのでrails db:resetした - Qiita
rake db:seedでActiveRecord::RecordInvalid: Validation failedって怒られたとき - Qiita
【rails】Docker環境でSystemSpecの導入の仕方。
解決したのでまとめておきます。
導入前に
docker,rspec導入済みですすめます。 つまり単体テストはできるということです。
system導入するには
dockerコンテナの中へ新たにchromedriverを準備なければなりません。
実際に進めていきます。
gem
gem 'rails', '~> 5.1.0' ... ... group :development, :test do gem "rspec-rails" end group :test do gem 'capybara' gem 'selenium-webdriver' end
Dockerfileに書き込む
FROM ruby:2.3.7 ENV LANG C.UTF-8 RUN apt-get update -qq && apt-get install -y build-essential node.js RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ && apt-get install -y nodejs # chromeの追加 RUN apt-get update && apt-get install -y unzip && \ CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \ wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \ unzip ~/chromedriver_linux64.zip -d ~/ && \ rm ~/chromedriver_linux64.zip && \ chown root:root ~/chromedriver && \ chmod 755 ~/chromedriver && \ mv ~/chromedriver /usr/bin/chromedriver && \ sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \ apt-get update && apt-get install -y google-chrome-stable RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /app CMD ["rails", "server", "-b", "0.0.0.0"]
参考:Headless ChromeをDocker上で動かして、E2Eのテスト - Qiita
詳しくは、参考のリンク先見てください。
linuxコマンドをまとめておきます。
curl HTTPアクセスをしてコンテンツを取得できるコマンド wget HTTPアクセスをしてコンテンツをファイルに保存するコマンド unzip ZIPファイルからファイルを取り出す
そのあとdocker-compose build
設定を書く。
mkdir spec/system; mkdir spec/support; touch spec/support/capybara.rb
#spec/support/capybara.rb require 'capybara/rspec' require 'selenium-webdriver' Capybara.register_driver :selenium_chrome_headless do |app| options = ::Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--window-size=1400,1400') driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) end Capybara.javascript_driver = :selenium_chrome_headless
・Capybaraにドライバを登録 ・Seleniumのoptionを指定する ・js使いたいときだけheadlessモードで起動する
#spec/rails_helper.rb Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } #↑コメントアウトする これによりspec/support/capybara.rbが読み込まれます。 RSpec.configure do |config| 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 end #type: :systemのときのみchromeを使うという設定です。
テストの実行
spec/system
の下にテストファイルを作って書く。
docker-compose run web rspec spec
おしまい
参考:
RailsのSystemTest(headless chrome)をDocker上で動かす - Qiita
RailsのSystemTest(headless chrome)をDocker上で動かす - Qiita
Rails + Selenium + DockerでSystemSpecの環境構築 - Qiita
【rails】フォロー機能。追記あり
整理していなかったのでまとめておきます。
参考:第14章 ユーザーをフォローする - Railsチュートリアル
※devise導入済みですすめます。
この機能を導入したら
①view側にフォロー中とフォロワーの数の表示
②フォローボタンの作成
③実際にフォロー中の人、フォロワーを表示
この3つをview側に追加することを目指して作っていきます。
導入前に
twitterのようなフォロー機能は非対称性があります。つまり自分がフォローしていたとしても、
フォローが返されない、とう言う状況もあります。
このことを頭に入れなければいけません。
モデルの導入
rails generate model Relationship follower_id:integer followed_id:integer
そして、 できたファイルに
class CreateRelationships < ActiveRecord::Migration[5.0] def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end end
これでfollower_idとfollowed_idの組み合わせが必ずユニークであることを保証します。
マイグレーションファイルに書いてある add_index - Qiita
データベースにindexを張る方法 - Qiita
DBのインデックスと複合インデックス - Qiita
次に$ rails db:migrate
関連付け
userとreleationshipは1対多の関係にあります。
class User < ApplicationRecord has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy end
いつもの関連付けなら、has_many :relationships
とすることろを、自分がフォローした(能動関係)を作るためにhas_many :active_relationships
にして、
次に、has_many :active_relationships
というモデルは無いので
class_name: "Relationship"
と書いて、Railsに探して欲しいモデルのクラス名を明示的に伝える。
更に、外部キーを先程作った foreign_key: "follower_id",
で指定します。
(ユーザーを削除したら、ユーザーのリレーションシップも同時に削除される必要があります。
そのため、関連付けにdependent: :destroyも追加しています。)
class Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" end
本来ならbelongs_to :user
、外部キーがuser_id
としますが、今回は仮想的にフォローする人される人に分けたいのでfollower_id
、followed_id
として関連付けます。
belongs_to :follower, class_name: "User"
とすることで
userと関連付けるのをfollower_id
と指定します。
また、follower
、followerd
というクラスは存在しないのでclass_name: "User"
として明記します。
このように書くと、今回はuser_id
がfollwer_id
、followed_id
として認識されます。
つまり、relationshipモデルのfollower_idやfollowed_idとuserモデルのidがひも付きます。
この結果、下記のメソッドが使えるようになります。
active_relationship.follower フォロワーを返します active_relationship.followed フォローしているユーザーを返します user.active_relationships.create(followed_id: other_user.id) userと紐付けて能動的関係を作成/登録する user.active_relationships.create!(followed_id: other_user.id) userを紐付けて能動的関係を作成/登録する (失敗時にエラーを出力) user.active_relationships.build(followed_id: other_user.id) userと紐付けた新しいRelationshipオブジェクトを返す
バリテーションの追加
class Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" validates :follower_id, presence: true validates :followed_id, presence: true end
現時点では、フォローするuserとの1対多の関係ができました。この時点で②フォローボタンの作成 のみならほとんどてきてしまいます。
多対多
一人のユーザーはいくつもフォローするしされる、つまり多対多の関係にあります。
class User < ApplicationRecord has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy has_many :following, through: :active_relationships, source: :followed #追加したのを和訳すると userはactive_relationshipsを通ってfollowingをたくさん持っている。 さらに細かく言うと、外部キーがfollower_idと紐付いているuserはactive_relationshipsを通ってfollowed_idをたくさん持っている end
Active Record の関連付け - Railsガイド
:sourceパラメーター を使って、「following配列の元はfollowed idの集合である」ということを明示的にRailsに伝えます。
この結果、user.following
でユーザーがフォロー中の人がわかります。 つまりfollowed_id
を抜き出すことができます。
さらに、user.following.count
でフォロー中の人数がわかるようにます。
follow、unfollow、following?メソッドを作成
followingで取得した集合をより簡単に取り扱うために、followやunfollowといった便利メソッドを追加します。
class User < ApplicationRecord # ユーザーをフォローする #followed_idにother_userを追加 def follow(other_user) following << other_user end # ユーザーをフォロー解除する # followed_idを削除 def unfollow(other_user) active_relationships.find_by(followed_id: other_user.id).destroy end # 現在のユーザーがフォローしてたらtrueを返す def following?(other_user) following.include?(other_user) end end
フォロワー側
今まではすべて自分からフォローした側でしたが、自分をもフォローされます。
しかし、これはいままでとすべて反対のことをすればいいだけです。
class User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy has_many :following, through: :active_relationships, source: :followed has_many :followers, through: :passive_relationships, source: :follower end
この結果、user.followers
でユーザーをフォロワーがわかります。 つまりfollower_id
を抜き出すことができます。
さらに、user.followers.count
でフォロワー数がわかるようにます。
view側
①view側にフォロー中とフォロワーの数の表示
②フォローボタンの作成
③実際にフォロー中の人、フォローされた人を表示
をいよいよ作っていきます。
①view側にフォロー中とフォロワーの数の表示
touch app/views/devise/shared/_stats.html.slim
この中にフォロー中とフォロワーの数の表示を書きます。
#app/views/devise/shared/_stats.html.slim - @user ||= current_user .stats | フォロー中のユーザー = link_to following_user_path(@user) div#following = @user.following.count | フォロワーリスト = link_to followers_user_path(@user) div#followers =@user.followers.count
そして、 表示させます。
#app/views/users/show.html.slim(わたしの場合) = render 'devise/shared/stats'
これで、①view側にフォロー中とフォロワーの数の表示 ができました。 また、
= link_to following_user_path(@user) = link_to followers_user_path(@user)
次にこのリンクから③実際にフォロー中の人、フォローされた人を表示のviewを作っていきます。
③実際にフォロー中の人、フォローされた人を表示
まずはルーティングから
config/routes.rb Rails.application.routes.draw do resources :users do member do get :following, :followers end end end
このURLで下記のようになります。
HTTPリクエスト URL アクション 名前付きルート GET /users/1/following following following_user_path(1) GET /users/1/followers followers followers_user_path(1)
次に、コントローラー
class UsersController < ApplicationController def following @user = User.find(params[:id]) @users = @user.following render 'show_follow' end def followers @user = User.find(params[:id]) @users = @user.followers render 'show_follow' end end
render 'show_follow
を2つのアクションで使い回しても@users
の中身は違うので問題有りません。
show_follow
のviewを作っていきます。
touch app/views/users/ahow_follow.htnl.slim
#follow_show .container - @users.each do |user| .user_content = link_to user_path(user) = user.username #←.usernameはuserモデルの中のカラム名に合わせてください
②フォローボタンの作成
これで最後です。
まずはコントローラーを作っていきます。
rails generate controller Relationships
次にルーティングです。
フォロー関係を作るか、消すかだけなので
Rails.application.routes.draw do resources :users do member do get :following, :followers end end resources :relationships, only: [:create, :destroy] end
次に、コントローラーを書きます
#app/comtroller/relationships_controller.rb class RelationshipsController < ApplicationController def create @user = User.find(params[:followed_id]) current_user.follow(@user) redirect_to @user end def destroy @user = Relationship.find(params[:id]).followed current_user.unfollow(@user) redirect_to @user end end
さらにボタンを作っていきます。
フォローするボタン
touch app/views/relationships/_follow.html.slim
=button_to current_user.active_relationships.build, params: { followed_id: @user.id } | フォローする
フォローを解除するボタン
touch app/views/relationships/_unfollow.html.slim
=button_to current_user.active_relationships.find_by(followed_id: @user.id), method: :delete | 解除する
そして上記のボタンをまとめます。
touch app/views/users/_follow_form.htnl.slim
- unless @user == current_user #follow_form - if current_user.following?(@user) = render 'relationships/unfollow' - else = render 'relationships/follow'
最後にこれを表示させます。
#app/views/users/show.html.slim(わたしの場合) = render 'devise/shared/stats' = render 'users/follow_form'
おしまい。
追記
【rails】任意のviewのみで特定のjsを読み込む。
とっても簡単です。
参考:
Rails 必要なJavaScriptのみを読み込む | | KeruuWeb
RailsがJavaScriptやスタイルシートを読み込む仕組み - りょうたくの技術ブログ
Asset Pipelineとは
そもそも、任意のveiwのみでjsが読み込んでくれないとは、Asset Pipelineがしっかり機能しているからです。
アセットパイプラインがアセットを連結するとこが第一の機能なのです。
すべてのJavaScriptファイルを1つのマスター.jsファイルに連結し、すべてのCSSファイルを1つのマスター.cssファイルに連結します。
そのため任意のviewのみで読み込むためには手を加える必要があります。
ツリー読み込みの停止
すべて自動で読み込むのを書き換えます。
#app/assets/javascripts/application.js - //= require_tree . + // require_tree .
読み込ませたいjsファイルを記述
#config/initializers/assets.rb Rails.application.config.assets.precompile += %w( test.js )
最後に表示させたいviewに読み込ませます
= javascript_include_tag "test.js"
おしまい
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale.
参考:config.i18n.fallbacksのメッセージについて - Qiita
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application. Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x. For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0
上記のようなエラーが出てきました。
ちなみにconfig/environments/produciton.rb
の74行目
に'config.i18n.fallbacks = true'.
と私の場合書いてあります。
gemfile.lockを見ると、
i18n (1.7.0) rails (5.2.4)
です。
If you're using I18n (>= 1.1.0) and Rails (< 5.2.2),
にはあたりません。
i18nのバージョンを下げたりもしましたが、注意が出ます。
これは何もしない感じで