- 投稿日:2021-03-08T23:57:20+09:00
【Rails】モデルのアソシエーションの書き方
環境
macOS: Big Sur Ver11.2.2
Rails: 6.0.0
Ruby: 2.6.5困ったこと
モデルを作成したときにモデル名をアッパーキャメルケースで記述したとき、アソシエーションを書くときはどういう形式(アッパーキャメル?、キャメルケース?、スネークケース?)で書くのか迷いが生じた。
実際、書き方が違うだけでエラーが出て2時間も解決に時間を要したことから注意したいところ。実例
作成したモデルは以下の通り。
Acitive Hashのクラスも含んでます。class OrderDetail < ApplicationRecord belongs_to :order extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :prefecture belongs_to :visit_time belongs_to :visit_day # バリデーションはフォームオブジェクトクラスに記述 endclass VisitDay < ActiveHash::Base self.data = [ { id: 0, name: '--' }, { id: 1, name: '本日' }, { id: 2, name: '明日' }, { id: 3, name: 'あさって' } ] include ActiveHash::Associations has_many :order_details endclass VisitTime < ActiveHash::Base self.data = [ { id: 0, name: '--' }, { id: 1, name: '7時〜8時' }, { id: 2, name: '8時〜9時' }, { id: 3, name: '9時〜10時' }, { id: 4, name: '10時〜11時' }, { id: 5, name: '11時〜12時' }, { id: 6, name: '13時〜14時' }, { id: 7, name: '14時〜15時' }, { id: 8, name: '15時〜16時' }, { id: 9, name: '16時〜17時' }, { id: 10, name: '17時〜18時' }, { id: 11, name: '18時〜19時' }, { id: 12, name: '19時〜20時' }, { id: 13, name: '20時〜21時' } ] include ActiveHash::Associations has_many :order_details end見ていただければ分かる通り、3つのモデル(クラス)は全てアッパーキャメルケースで定義しています。
こういう場合、アソシエーションでモデル名を記述するときはスネークケースで記述すること!
初心者の自分はアソシエーションを書くときになんとなくキャメルケースで書いてしまい、それがエラーの原因となっていました。皆様はどうぞお気をつけください。
- 投稿日:2021-03-08T23:07:05+09:00
【Stripe Rails】決済モーダル内のemailにJavaScriptで値を設定する
- 投稿日:2021-03-08T21:49:11+09:00
【Ruby on Rails】フォロー・フォロワー機能の実装
はじめに
Railsで作成中のアプリにフォロー・フォロワー機能を実装しました。
理解できていなかったことも多かったので、理解を深めるために投稿します。
解説が長すぎるので、全てリンクにしています。
参考にされる際は必要なところだけご覧いただいても構いません。前提条件と作成物
前提条件
- rails 5.2.4
- devise導入済み
- userテーブルは作成済み←Userに対しフォロー機能を設定します
作成物
- フォロー機能(follow,unfollow)
- フォロー・フォロワー一覧ページ
流れ
- Relationshipモデルとカラムを作成
- アソシエーションを記述
- Userモデルに
「フォロー機能」「フォロー解除機能」「フォローしているかどうかメソッド」を記述- Relationshipコントローラを作成・編集、ルーティング編集
- viewページを作成(フォローボタン)
- viewページを作成(フォロー・フォロワー一覧)
1. Relationshipモデルを作成
ターミナル$ rails g model Relationship follower_id:integer followed_id:integer # follower_id:フォローするユーザーのid, followed_id:フォローされるユーザーのid $ rails db:migrate # マイグレーション実行まずはRelationshipモデルとカラムを作ります。
今回はRelationshipで作りましたが、他のモデルの場合は置き換えてください。
カラム データ型 備考 relationship_id integer 記述不要、自動作成される follower_id integer フォローするユーザーのid followed_id integer フォローされるユーザーのid
【解説】なぜRelationshipモデルのカラムはuser_idではないのか?(クリック)
Relationshipモデルは中間テーブルの役割を担っています。
中間テーブルとは、多 対 多のリレーションを構築するとき、
2つのテーブルを関連づけるために各テーブルの外部キー(FK)をもつテーブルのことです。
今回のフォロー・フォロワー機能は、
・1ユーザーはたくさんのユーザーにフォローできる(1:N)
・1ユーザーはたくさんのユーザーにフォローされる(1:N)
つまり多 対 多のリレーション状態を作る機能です。
通常であれば、参照するテーブルのid(この場合はuser_id)を中間テーブルのカラムに当てることが望ましいですが、
今回の場合は参照されるのは両方ともuserテーブルとなり、重複してしまいます。
そのため、最終的に参照するのはuser_idですが、
分かりやすいように別のカラム名で定義をしてあげる必要があります。
今回は、
follower_id : フォローするユーザーのid
followed_id : フォローされるユーザーのid
としてカラムを設定しました。
中間テーブル?についてはこちら↓
Active Record の関連付け2. アソシエーションを記述
app/models/relationship.rbclass Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" # class_name: "User"を定義することでUserテーブルのレコードを参照する endRelationshipモデルにアソシエーションを記述します。
belongs_to :follower
の後にclass_name: "User"
を忘れないようにします。
【解説】なぜbelongs_to :follower(followed)なのか?class_nameはなぜ必要?(クリック)
belongs_to :follower, class_name: "User"
を元に見ていきます。
belongs_to = 従属 の意味です。
belongs_to :follower
でfollowerテーブルのfollower_idを探しにいきます。
ただ、今回はfollowerテーブルは作成していないのと、
参照してもらいたいのはfollower_idに格納されている値と同じuser_idです。
(user_idはフォロー・フォロワー関係にありますが、
relationshipテーブルの中で重複するため定義できません。
そのため、このような現象が起こります。)
このままではエラーになってしまうため、
class_name: "User"
でUserテーブルを参照するように定義します。
これを行うことで、例えば
「relationship_idが(1)の時follower_idに格納されている値と
同じuser_idをUserテーブルから探して」
というように関連づけることができます。(あくまで私の捉え方です)
followed_idに関しても同じなので、同様に定義します。
app/models/user.rb(フォロー機能部分のみ抜粋)class User < ApplicationRecord has_many :reverse_of_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy has_many :followers, through: :reverse_of_relationships, source: :follower # 被フォロー関係を通じて参照→followed_idをフォローしている人 has_many :relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy # 【class_name: "Relationship"】は省略可能 has_many :followings, through: :relationships, source: :followed # 与フォロー関係を通じて参照→follower_idをフォローしている人 endUserモデルにもRelationshipとのアソシエーションを記述します。
くどいようですが、user_idを中間テーブルで直接定義していないため、
Relationshipテーブルを参照した時にuser_idが参照できるよう、定義します。
また、今回はフォロー・フォロワー一覧ページも作成するため、2、4行目も記述します。
(フォロー・フォロワー数をカウントする時も使えます。)
【解説】has_many 1行目と3行目の意味は?(クリック)
has_many :reverse_of_relationships, class_name: "Relationship"
先程のRelationshipモデルと同じ意味です。
見分けやすいようにreverse_of_relationshipsと定義しました。
このままだと、reverse_of_relationship_idを探してしまいます。
それを防ぐために
foreign_key: "followed_id"
でどのカラムを参照して欲しいのかを定義します。
(foreign_keyは外部キーの意味です)
has_many :relationships, class_name: "Relationship"
の場合、class_nameは不要ですが、見た目統一のためにコードを残しています。
【解説】has_many 2行目と4行目の意味は?(クリック)
2行目のhas_many :followers, through: :reverse_of_relationships, source: :follower
を基準に見ていきます。
1行目で定義したのは、「どのカラムを参照するか」でした。
2行目と4行目では、参照したカラムを元に、関連するuser_idも参照できるように記述します。
1行目で定義したreverse_of_relationships
モデルをthrough
してfollower(_id)
というカラムを参照(source)
してね、と記述しています。
これを記述することで、@user.followersという記述がコントローラーで使えるようになります。
回りくどい説明になってしまいますが、2行目の場合は、
followed_idにあたるユーザーをフォローしているユーザー(つまりfollower_idにあたるユーザー)を参照することができます。
これはフォロー・フォロワー一覧ページを作成する時に使います。
3. Userモデルにメソッドを記述
app/models/user.rb(フォロー機能部分のみ抜粋)class User < ApplicationRecord def follow(user_id) relationships.create(followed_id: user_id) end def unfollow(user_id) relationships.find_by(followed_id: user_id).destroy end def following?(user) followings.include?(user) end end4. Relationshipコントローラを作成・編集
まずはrelationshipsコントローラを作成します。
ターミナル$ rails g controller relationships作成したコントローラにフォロー機能を作成・保存・削除するアクションと、
フォロー・フォロワー一覧を表示するアクションを記述していきます。app/controllers/relationships_controller.rbclass RelationshipsController < ApplicationController # ——————フォロー機能を作成・保存・削除する———————————— def create current_user.follow(params[:user_id]) redirect_to request.referer end def destroy current_user.unfollow(params[:user_id]) redirect_to request.referer end #————————フォロー・フォロワー一覧を表示する-———————————— def followings user = User.find(params[:user_id]) @users = user.followings end def followers user = User.find(params[:user_id]) @users = user.followers end
【解説】フォローを作成・保存・削除する(クリック)
current_user.follow(params[:user_id])
、
current_user.unfollow(params[:user_id])
では、先程userモデルで定義したfollow、unfollowメソッドを使っています。
relationshipsとusersはネストの関係のため、userモデルからメソッドの呼び出しも可能です。
モデルに記述する理由はこちらが参考になりました↓
Railsのモデルに書いたメソッドってどうやってコントローラで使うの?
【解説】フォロー・フォロワー一覧を表示する(クリック)
@users = user.followings
、
@users = user.followers
では、
先程userモデルで定義したアソシエーションを利用します。
これによりuser = User.find(params[:user_id])
で取得したユーザーのidが、
フォローしている もしくは フォローされている ユーザーのid一覧を
取得することができます。
ルーティングも記述します。
config/routes.rb(関連部分のみ)resources :users, only: [:index, :show, :edit, :update] do # ——————————————— ここから ——————————————— resource :relationships, only: [:create, :destroy] get 'followings' => 'relationships#followings', as: 'followings' get 'followers' => 'relationships#followers', as: 'followers' # ——————————— ここまでネストさせる ——————————— end
resources :users
の最後にdo
を記述するのを忘れないようにします。
relationships は中間テーブルなので、usersモデルにネストさせます。
followingsとfollowersは一覧ページ用に定義したアクションです。5. viewページを作成(フォローボタン)
view/users/_info.html.erb(フォローボタン部分のみ記述)<% if current_user.following?(user) %> <%= link_to "Unfollow", user_relationships_path(user.id), method: :delete %> <% else %> <%= link_to "Follow", user_relationships_path(user.id), method: :post %> <% end %>今回はuserページの部分テンプレート内に埋め込みましたが、
view/relationships/_follow_button.html.erbなど、
ボタン部分だけ記述したviewを作成してもOKだと思います。(そちらの方が見やすいです)
【解説】<% if current_user.following?(user) %>(クリック)
if current_user.following?(user)
では、
userモデルで定義したfollowing?メソッドを使っています。
これにより、「今ログインしているユーザーは今参照しているユーザーをフォロしているか?」が条件となります。
trueの場合は、フォローしている状態なので、フォロー解除(unfollow)できるような表示を、
falseの場合は、フォローしていない状態なので、フォローする(follow)できるように表示します。
6. viewページを作成(フォロー・フォロワー一覧)
view/relationships/followers.html.erbもしくはview/relationships/followings.html.erb<h2>Follower Users</h2>または<h2>Follow Users</h2> <% if @users.exists? %> <thead> <tr> <th>name</th> <th></th> <th></th> </tr> </thead> <tbody> <% users.each do |user| %> <tr> <td><%= user.name %></td> <td>フォロー数: <%= user.followings.count %></td> <td>フォロワー数: <%= user.followers.count %></td> </tr> <% end %> </tbody> </table> <% else %> <p>ユーザーはいません</p> <% end %>フォロー・フォロワー一覧ともにほとんど同じ記述です。
タイトルを<h2>Follower Users</h2>
なのか、
<h2>Follow Users</h2>
なのか選択します。
<% if @users.exists? %>
がtrueの場合は、ユーザー一覧を表示させます。
すでにユーザー一覧(index)を作成していれば、部分テンプレート化して埋め込みでOKです。
例:<%= render '/users/index', users: @users %>
などです。
作成していない場合は、上記のような形で表記できます。
tableタグだけで整えていますが、bootstrapなどを使えばより綺麗に整えられると思います。これでフォロー・フォロワー機能の実装は完了です!
おわりに
今回は、フォロー・フォロワー機能の実装を、手順と内容についてまとめました。
1度の実装だけでは理解が難しく、何度もパターンを試して、
アウトプットすることでまた少し理解が深まったような気がします。
フォロー・フォロワー機能の実装方法はたくさんあると思うので、
よりスマートで効率の良いコードを書けるように精進していきたいです^^内容が盛りだくさんのため、抜け漏れがあったら申し訳ありません。
また、理解が乏しい箇所・用語の使い方が間違っている点も多々あるかと思います。コメント欄でご指摘いただければ幸いです。こちらも参考になりました↓ありがとうございました。
https://qiita.com/mitsumitsu1128/items/e41e2ff37f143db81897
- 投稿日:2021-03-08T21:04:46+09:00
Railsポートフォリオで詰まった箇所まとめ(その都度追加)
Railsポートフォリオ作成中、時間を取られた箇所まとめ。精神的に来たもの一覧でもある。ほぼ自分への戒め用。もう少し早く書き始めればよかった・・・
【ページ内リンク】
0.環境
Q1.フォルダ全消しした後、再度同じものを作り直したときに途中でエラーの嵐になる
Q2.テーブルにDateTimeが追加できない!0.環境
・AWS
・heroku/7.48.0 linux-x64 node-v12.16.2
・Rails 5.2.4.5
・ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
・MySQL 5.7.31
・PostgreSQL 9.2.24Q1.フォルダ全消しした後、再度同じものを作り直したときに途中でエラーの嵐になる
A.前に作ったデータベース消してなかった。そりゃ同名のDB作ろうとしたらエラー出ますよね。
【解決策】
mysql> drop database 消したいDB名;・消したいDB名の確認方法は
mysql> show database;
Q2.テーブルにDateTimeが追加できない!
A.諦めてdateクラスとtimeクラスに分けた・・・(後に、
DateTime
って大文字D
とT
を使ってたからだと判明。これは酷い。)【経緯】
・日付と時刻を入れたかったので、それら二つとも代入できるDateTimeクラスって便利じゃん!ということで$ rails g model テーブル名 カラム名:DateTime
をやってみたが$ rails db:migrate
でエラー。
・エラー文読んでみたらDateTimeがダメみたい
・Time
でもDate
でも出来なかった
・色々考えた結果、$ rails g model テーブル名 カラム名:date ~
としたら出来た
・普通に$ rails g model テーブル名 カラム名:datetime
としたら出来るのでは・・・?
・出来た。【解決策】
$ rails g model テーブル名 カラム名:datetime
- 投稿日:2021-03-08T20:28:06+09:00
interactor gem についてまとめてみた (2/2)
Interactor のまとめ記事第2弾です。
全2部作となっていて、
1. Interactor 概論
2. ActiveInteractor Gem で Interactor を使いやすく(今回)
という構成でお届けしています。
今回は、 activeinteractor の wiki をベースに、 activeinteractor 特有の機能について説明します。前回のおさらい
- ビジネスロジックをカプセル化するためのオブジェクトとして Interactor というものがある
- それぞれの Interactor は、自身が所持する Context を変更する形で外部に影響を及ぼす
- フックを使って、 Interactor の実行前後に実施する処理をまとめることができる
- Interactor を使うことで、動作が明快になったり、コントローラの肥大化を防いだりすることができる
- Interactor には、普通の Interactor と、それを束ねる Organizer という2種類がある
- Interactor が行う動作がきちんと1つに制限されていれば、テストを書くのは簡単
ActiveInteractor
概要
Interactor Gem を改良した Gem として、今回は activeinteractor1 を紹介します。
基本的な使い方は同じですが、以下の点を中心に書き方に違いが見られます。
- Interactor は
ActiveInteractor::Base
を、 Context はActiveInteractor::Context::Base
を継承する.call
ではなく.perform
- バリデーションが使える
- コールバックやヘルパーメソッドが便利
Interactor と Organizer
Interactor や Organizer はクラスの継承で表現されます。
# interactor gem class AuthenticateUser include Interactor def call # ... end end # activeinteractor gem class AuthenticateUser < ActiveInteractor::Base def perform # ... end end# interactor gem class PlaceOrder include Interactor::Organizer organize CreateOrder, ChargeCard, SendThankYou end # activeinteractor gem class PlaceOrder < ActiveInteractor::Organizer::Base organize :create_order, :charge_card, :send_thank_you end
organize
メソッドはブロックを引数にとることができ、 Interactor を organize するかどうか決める条件やコールバックを指定することができます。class PlaceOrder < ActiveInteractor::Organizer::Base organize do add :create_order, if :user_registered? add :charge_card, if: -> { context.order } add :send_thank_you, if: -> { context.order } end private def user_registered? context.user&.registered? end endclass PlaceOrder < ActiveInteractor::Organizer::Base organize do add :create_order, before: -> { puts context.order }, after: :print_order_id add :charge_card add :send_thank_you end private def print_order_id puts context.order.id end end一方の Interactor の Context が他方の Interactor の実行に依存しないとき、Organizer で
.perform_in_parallel
メソッドを使うことで、 Interactor を並行して動作させることができます。class CreateNewUser < ActiveInteractor::Base def perform context.user = User.create( first_name: context.first_name, last_name: context.last_name ) end end class LogNewUserCreation < ActiveInteractor::Base def perform context.log = Log.create( event: 'new user created', first_name: context.first_name, last_name: context.last_name ) end end class CreateUser < ActiveInteractor::Organizer::Base perform_in_parallel organize :create_new_user, :log_new_user_creation end CreateUser.perform(first_name: 'Aaron', last_name: 'Allen') #=> <#CreateUser::Context first_name='Aaron' last_name='Allen' user=>#<User ...> log=<#Log ...>>Context
Context と Interactor の紐付け
Context は
ActiveInteractor::Context::Base
を継承します。それぞれの Interactor は、自身に紐づく Context を以下の順番で探索します。
InteractorName::Context
InteractorNameContext
該当するクラスが存在しない場合は、
InteractorName::Context < ActiveInteractor::Context::Base
が自動的に作成されます。class MyInteractor < ActiveInteractor::Base; end class MyInteractor::Context < ActiveInteractor::Context::Base; end MyInteractor.context_class #=> MyInteractor::Contextclass MyInteractorContext < ActiveInteractor::Context::Base; end class MyInteractor < ActiveInteractor::Base; end MyInteractor.context_class #=> MyInteractorContextclass MyInteractor < ActiveInteractor::Base; end MyInteractor.context_class #=> MyInteractor::Context
.contextualize_with
メソッドを使って、紐付ける Context を明示的に指定することもできます。class MyGenericContext < ActiveInteractor::Context::Base; end class MyInteractor contextualize_with :my_generic_context end MyInteractor.context_class #=> MyGenericContext明示的にプロパティを指定する
それぞれの Context は、 Interactor が動作した後に自身にどんなプロパティが指定されているべきか明示的に定義することができます。
.attributes
メソッドを使うことで、 Context にどのようなプロパティが割り当てられているか確認することができます。class MyInteractorContext < ActiveInteractor::Context::Base attributes :first_name, :last_name, :email, :user end class MyInteractor < ActiveInteractor::Base; end result = MyInteractor.perform( first_name: 'Aaron', last_name: 'Allen', email: 'hello@aaronmallen.me', occupation: 'Software Dude' ) #=> <#MyInteractor::Context first_name='Aaron' last_name='Allen' email='hello@aaronmallen.me' occupation='Software Dude'> result.attributes #=> { first_name: 'Aaron', last_name: 'Allen', email: 'hello@aaronmallen.me' } result.occupation #=> 'Software Dude'バリデーション
ActiveModel::Validations
で定義されているバリデーション用のメソッドは、 Context でも使用することができます。 Interactor 内でもcontext_
という接頭辞をつけることでバリデーションメソッドを使うことができますが、 Context クラスの内部でバリデーションを完結させることが推奨されます。
バリデーションを行うタイミングは、以下の2通りに指定できます。
:calling
:#perform
の前にバリデーションを行う:called
:#perform
の後にバリデーションを行うclass MyInteractorContext < ActiveInteractor::Context::Base attributes :first_name, :last_name, :email, :user # #perform の前でのみバリデーションされる validates :first_name, presence: true, on: :calling # #perform の前後でバリデーションされる validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } # #perform の後でのみバリデーションされる validates :user, presence: true, on: :called validate :user_is_a_user, on: :called private def user_is_a_user return if user.is_a?(User) errors.add(:user, :invalid) end end class MyInteractor < ActiveInteractor::Base def perform context.user = User.create_with( first_name: context.first_name, last_name: context.last_name ).find_or_create_by(email: context.email) end end result = MyInteractor.perform(last_name: 'Allen') #=> <#MyInteractor::Context last_name='Allen> result.failure? #=> true result.valid? #=> false result.errors[:first_name] #=> ['can not be blank'] result = MyInterator.perform(first_name: 'Aaron', email: 'hello@aaronmallen.me') #=> <#MyInteractor::Context first_name='Aaron' email='hello@aaronmallen.me' user=<#User ...>> result.success? #=> true result.valid? #=> true result.errors.empty? #=> trueコールバック
バリデーション前後のコールバック
.before_context_validation
でバリデーション前に実行されるコールバックを、.after_context_validation
でバリデーション後に実行されるコールバックを指定することができます。class MyInteractorContext < ActiveInteractor::Context::Base attributes :first_name, :last_name, :email validates :last_name, presence: true end class MyInteractor < ActiveInteractor::Base before_context_validation { context.last_name ||= 'Unknown' } end result = MyInteractor.perform(first_name: 'Aaron', email: 'hello@aaronmallen.me') result.valid? #=> true result.last_name #=> 'Unknown'class MyInteractorContext < ActiveInteractor::Context::Base attributes :first_name, :last_name, :email validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } end class MyInteractor < ActiveInteractor::Base after_context_validation { context.email&.downcase! } end result = MyInteractor.perform(first_name: 'Aaron', last_name: 'Allen', email: 'HELLO@AARONMALLEN.ME') result.valid? #=> true result.email #=> 'hello@aaronmallen.me'
#perform
前後のコールバック
#perform
前後に実行されるコールバックは、.before_perform
.after_perform
.around_perform
で指定することができます。class MyInteractor < ActiveInteractor::Base before_perform :print_start def perform puts 'Performing' end private def print_start puts 'Start' end end MyInteractor.perform # Start # Performing #=> <#MyInteractor::Context...>class MyInteractor < ActiveInteractor::Base around_perform :track_time def perform sleep(1) end private def track_time context.start_time = Time.now.utc yield context.end_time = Time.now.utc end end result = MyInteractor.perform result.start_time #=> 2019-01-01 00:00:00 UTC result.end_time #=> 2019-01-01 00:00:01 UTCclass MyInteractor < ActiveInteractor::Base after_perform :print_done def perform puts 'Performing' end private def print_done puts 'Done' end end MyInteractor.perform # Performing # Done #=> <#MyInteractor::Context...>ロールバック前後のコールバック
同様に、
.before_rollback
.after_rollback
.around_rollback
で指定することができます。具体例は省略します。Organizer のコールバック
Organizer が organize するそれぞれの Interactor について、それぞれの実行前後に実行するコールバックを
.before_each_perform
.after_each_perform
.around_each_perform
で指定することができます。.before_each_perform
のみ具体例を見てみます。class MyInteractor1 < ActiveInteractor::Base def perform puts 'MyInteractor1' end end class MyInteractor2 < ActiveInteractor::Base def perform puts 'MyInteractor2' end end class MyOrganizer < ActiveInteractor::Organizer::Base before_each_perform :print_start organized MyInteractor1, MyInteractor2 private def print_start puts "Start" end end MyOrganizer.perform # Start # MyInteractor1 # Start # MyInteractor2 #=> <MyOrganizer::Context...>Rails で便利なコマンド
rails g
rails g
を使ってファイルを作成することができます。# ActiveInteractor の導入 rails generate active_interactor:install # 各種ファイルの生成 rails generate interactor NAME [context_attributes] [options] rails generate interactor:organizer NAME [interactor interactor] [options] rails generate interactor:context NAME [attribute attribute] [options]ジェネレーターの設定を以下のように行うことができます。
# config/application.rb module MyApplication class Application < Rails::Application config.generators.interactors :active_interactor, dir: 'my_directory', # ファイルを生成するディレクトリを指定 generate_context: false # Context のクラスを自動生成するかどうかを指定 end end endActiveRecord のモデルを Context として使用する
ActiveRecord モデルを Context として使用するには、 モデル内で
acts_as_context
を使用した後、 Interactor でcontextualize_with
メソッドを使用します。# app/models/user class User < ApplicationRecord acts_as_context end # app/interactors/create_user class CreateUser < ApplicationInteractor contextualize_with :user def perform context.email&.downcase! context.save end end CreateUser.perform(email: 'HELLO@AARONMALLEN.ME') #=> <#User id=1 email='hello@aaronmallen.me'>
Copyright (c) 2019 Aaron Allen, Released under the MIT license: https://github.com/aaronmallen/activeinteractor/blob/main/LICENSE ↩
- 投稿日:2021-03-08T19:22:09+09:00
RSpecにて引数が必要なsubjectの使い方
はじめに
RSpecで、同じメソッドに対してのテストを複数回書くシーンでは、subjectを使うことでDRYなコードを書くことができます。
ここでは引数が必要なメソッドのテストをsubjectを用いて書きます。実装
ユーザー名(user_name)、自己紹介(pr)カラムがあるユーザーを、フリーワードで検索するuser_search(free_word)メソッドをテストするとします。普通に書くと以下のように繰り返し同じ処理が出てきます。
spec/models/user_spec.rbdescribe "#user_search" do context "when 'user_name1' is given" do it "return 1 result" do expect(Job.user_search("user_name1").count).to eq 1 end end context "when 'pr1' is given" do it "return 1 result" do expect(Job.user_search("pr1").count).to eq 1 end end endこれは以下のようにsubjectでまとめて処理を書くことができます。
spec/models/user_spec.rbdescribe "#user_search" do subject { User.user_search(field).count } context "when user_name1 is given" do let(:field) { "user_name1" } is_expected.to eq 1 end context "when pr1 is given" do let(:field) { "pr1" } is_expected.to eq 1 end end引数はletで定義することができます。
- 投稿日:2021-03-08T18:06:50+09:00
tiktokでogp相当の情報をAPIで取得する(ruby)
tiktokのスクレイピングをしてogp情報をとってくる処理を書いていたのだがいつのまにか下記のようにstatus code1000で弾かれるようになってしまっていた。
[25] pry(main)> html = open(movie_url) do |f| f.read; end => "{\"statusCode\":10000,\"verifyConfig\":{\"code\":10000,\"type\":\"verify\",\"subtype\":\"slide\",\"fp\":\"verify_5f101e29a0ad54278347a24437cfcb93\",\"region\":\"sg\",\"detail\":\"2SdJGObfW85-lX4dEGohkmh8YbPmZYiXwIh1pqrEUuohsJqr4t3XjgXHtkBHWheNJbj4MB*CVwF1ayKFmhUFcJY2yAgBxyatF0XXzv0*xk*DmEz7Og6oE0nS1mFuZXdkJE*Z05uOnCgTkh4d4yy7xfv6EiiQJGBxdHDxp21vXCZjphcNMaY6YoCrq-5wRulWVle0lP3RJyqJQUFbtIP8pH0LICH8SRGn9a6mItvTEiEwiHCfJHBxhTUf1kFCYkGm1gBpVE1s45qbaNPeVELCCU0732nlU1VLR7Jm16yTIne64NABwJSaXFAl19NrjsfHrV-Cxq*d4kuVUBERzDIIncNGgQ3m6MLdwGLw*H4xfl94O1LId2V5zlOltVL1gYtral9VUYNMskUBWclUpWt2zrUfpbVzuUg.\"}}"どうしようかなと思ったが、調べてみたらTikTokのAPIでogp相当の情報をとってこれるようだったので、apiを通して取得してくるように修正した。ドキュメントもあるので別に何も大変なことはない。
https://www.tiktok.com/oembed?url=https://www.tiktok.com/@scout2015/video/6718335390845095173このような形で情報を取得したいtiktokのURLをurlパラメータに渡してhttps://www.tiktok.com/oembed を叩くだけでいい。そうすると下記のようなレスポンスが返ってくる。
{ "version": "1.0", "type": "video", "title": "Scramble up ur name & I’ll try to guess it?❤️ #foryoupage #petsoftiktok #aesthetic", "author_url": "https://www.tiktok.com/@scout2015", "author_name": "Scout & Suki", "width": "100%", "height": "100%", "html": "<blockquote class=\"tiktok-embed\" cite=\"https://www.tiktok.com/@scout2015/video/6718335390845095173\" data-video-id=\"6718335390845095173\" style=\"max-width: 605px;min-width: 325px;\" > <section> <a target=\"_blank\" title=\"@scout2015\" href=\"https://www.tiktok.com/@scout2015\">@scout2015</a> <p>Scramble up ur name & I’ll try to guess it?❤️ <a title=\"foryoupage\" target=\"_blank\" href=\"https://www.tiktok.com/tag/foryoupage\">#foryoupage</a> <a title=\"petsoftiktok\" target=\"_blank\" href=\"https://www.tiktok.com/tag/petsoftiktok\">#petsoftiktok</a> <a title=\"aesthetic\" target=\"_blank\" href=\"https://www.tiktok.com/tag/aesthetic\">#aesthetic</a></p> <a target=\"_blank\" title=\"♬ original sound - ???????\" href=\"https://www.tiktok.com/music/original-sound-6689804660171082501\">♬ original sound - ???????</a> </section> </blockquote> <script async src=\"https://www.tiktok.com/embed.js\"></script>", "thumbnail_width": 720, "thumbnail_height": 1280, "thumbnail_url": "https://p16.muscdn.com/obj/tos-maliva-p-0068/06kv6rfcesljdjr45ukb0000d844090v0200010605", "provider_url": "https://www.tiktok.com", "provider_name": "TikTok" }コード(ruby)
uri = URI.parse("https://www.tiktok.com/oembed?url=#{params[:tiktok_url]}") response = Net::HTTP.get_response(uri) res_json = JSON.parse(response.body) html_doc = res_json["html"] title = res_json["title"] ogp_image = res_json["thumbnail_url"]
- 投稿日:2021-03-08T18:03:50+09:00
Ruby2.3.4+Rails5.0.4からRuby3.0.0+Rails6.1.3にバージョンアップした話
はじめに
Heroku-16 Stackで動いているアプリケーションがあり、こちらが2021/6にEOLを迎える。
それに伴いHeroku Stackを上げるとRubyのバージョンも上げないといけなかった。
せっかくだったらこの機会に最新にしようと思い、バージョンを上げたのでその時の注意事項をメモ書き程度に残しておく。Gemfile
まず元々のGemfile
Gemfile# 一部抜粋 gem 'rails', github: 'rails/rails', branch: "5-0-stable" gem 'mysql2', '>= 0.3.18', '< 0.5' gem 'puma', '~> 3.0' gem 'rspec-rails', '~> 3.5' gem 'factory_girl_rails'Rubyのバージョン指定はしておらず、Herokuのデフォルトのバージョンになっていた。
今回はRubyとRailsのバージョンを上げたいので以下を設定してみた。Gemfileruby "3.0.0" gem 'rails', '~> 6.1', '>= 6.1.3'これで環境を作り直そうとした結果以下のエラー
Bundler could not find compatible versions for gem "bundler": In Gemfile: bundler-audit was resolved to 0.6.0, which depends on bundler (~> 1.2) license_finder was resolved to 3.0.2, which depends on bundler rails (~> 6.1, >= 6.1.3) was resolved to 6.1.3, which depends on bundler (>= 1.15.0) Current Bundler version: bundler (2.2.3) This Gemfile requires a different version of Bundler. Perhaps you need to update Bundler by running `gem install bundler`? Could not find gem 'bundler (~> 1.2)', which is required by gem 'bundler-audit', in any of the sources.bundler関連の問題でうまく環境が作れないようだった。
どうやらbundlerが1.15.3で作ろうとしているのが問題のようだった。
Gemfile.lockを消した状態でbundlerのバージョンを上げてbundle installした。
bundlerは2.2.11になった。Gemfile.lock# 一部抜粋 + RUBY VERSION + ruby 3.0.0p0 BUNDLED WITH - 1.15.3 + 2.2.11rexml
gem installすると以下のエラー
LoadError - cannot load such file -- rexml/document
こちらの記事を参考にしました。
Gemfilegem 'rexml'factory_girl
DEPRECATION WARNING: The factory_girl gem is deprecated. Please upgrade to factory_bot. See https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md for further instructions. (called from <top (required)> at /usr/src/app/config/application.rb:17) porter-web-dev | /usr/local/bundle/gems/activesupport-6.1.3/lib/active_support/dependencies.rb:332:in `require': cannot load such file -- rexml/document (LoadError)factory_girlは非推奨なのでfactory_botにバージョンアップする。
Gemfile- gem 'factory_girl_rails' + gem 'factory_bot'rspecファイル内にてFactoryGirlをFactoryBotに置換。
更新が用意してくれている置換コマンドで割といい感じに置換できた。mysql2
手元の環境はDockerで作成しているが
mysql-client
がないと言われたのでdefault-mysql-client
に変更Package mysql-client is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is only available from another source
Dockerfile- RUN apt-get install -y mysql-client + RUN apt-get install -y default-mysql-client環境を立ち上げてみたがDBアクセスで以下エラー
Puma caught this error: Error loading the 'mysql2' Active Record adapter. Missing a gem it depends on? can't activate mysql2 (~> 0.5), already activated mysql2-0.4.10. Make sure all dependencies are added to Gemfile. (LoadError)mysql2のバージョンも上げる。
Gemfile- gem 'mysql2', '>= 0.3.18', '< 0.5' + gem 'mysql2', '~> 0.5.3'puma
ローカル環境が立ち上がらない。
こちらの記事を参考にしました。config/initializers/new_framework_defaults.rb- ActiveSupport.halt_callback_chains_on_return_false = false + #ActiveSupport.halt_callback_chains_on_return_false = falseローカル環境を立ち上げると証明書関連のエラー
こちらの記事を参考にpumaのバージョンも上げる。Gemfile- gem 'puma', '~> 3.0' + gem 'puma', '~> 5.2', '>= 5.2.1'Rspec
ローカル環境も立ち上がりある程度動くようになったのでテストを通してみたところ全部失敗した。
Failure/Error: raise WrongScopeError, "`#{name}` is not available from within an example (e.g. an " \ "`it` block) or from constructs that run in the scope of an " \ "example (e.g. `before`, `let`, etc). It is only available " \ "on an example group (e.g. a `describe` or `context` block)." `name` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).こちらの記事を参考にしました。
spec/factories/hoge.rb- FactoryGirl.define do + FactoryBot.define do factory :hoge do - name 'テスト' + name {'テスト'} end endspec/spec_helper.rbconfig.before(:all) do FactoryBot.reload end最終的に
以下のようなGemfileになった。
Gemfile# 一部抜粋 ruby "3.0.0" gem 'rails', '~> 6.1', '>= 6.1.3' gem 'mysql2', '~> 0.5.3' gem 'puma', '~> 5.2', '>= 5.2.1' gem 'rexml' gem 'rspec-rails', '~> 4.0', '>= 4.0.2' gem 'factory_bot'各バージョンの後方互換を調べる
ローカル環境が起動しひと通りアプリケーションが動くことを確認。
RSpecが全て通ることを確認。
この時点で8割方バージョンアップ完了だったが、念の為各バージョンで後方互換切られているところを中心に調べていくことにした。
Ruby2.3から3.0という記事はなかったので1バージョンずつ調べていった。ruby2.4
特に問題なさそう。ruby2.5
後方互換の話は特になし。ruby2.6
範囲オブジェクトに影響あり。
git grep -i range
git grep "\.\."
あたりで範囲オブジェクトを使っている箇所を調べていった。ruby2.7
こちらも範囲オブジェクト関連。ruby3.0.0
1つずつ見てgrepしてみたが影響のありそうなものはそもそも使っていなかったので問題ないと判断した。rails6.1.3
https://qiita.com/ryohashimoto/items/622c3bcfb3336cb9317e
https://railsguides.jp/upgrading_ruby_on_rails.html
cookiesの話が気になるがrails4との比較をしているのでrails5からのアップデートでは問題なしと判断した。Heroku
以上の調査と修正によりバージョンアップ問題なしと判断し本番をHeroku-20 Stackに上げてデプロイ。
しかし本番でエラーが発生した。ActiveRecord::ConnectionNotEstablished: SSL connection error: error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocolこちらの記事と同様の事象のようだった。
なんとか解消したかったが原因不明のため、記事と同様にHeroku-18 Stackにしたら問題なく動いた。まとめ
今回所要時間15時間程度でバージョンアップすることができた。
ここまで早くできたのは以下の要因によるものと思われる。
- 元のバージョンがそこまで低くなかった。
- テストが書いてあった。
- 該当アプリケーションがシンプルな作りで機能がそこまで多くなく、検証すべき項目が少なかった。
せっかくバージョンを上げたので新バージョンで使えるようになった機能とかを使っていきたい。
まだ全然調べられていないがRails6からは標準でバルクインサートできるようになったらしい。
activerecord-import
とかgemを後入れしなくても良くなったのは嬉しい。
時間ができたら触ってみることにする。
- 投稿日:2021-03-08T17:13:40+09:00
Rails いいね機能の非同期通信化(Ajax)
はじめに
こんにちは!閲覧ありがとうございます。
今回は【いいね機能の非同期通信化(Ajax)】を学習した為、
復習の意味も込めてアウトプットとしてまとめていこうと思います。未来の僕が見返した時に、
また、プログラミング初学者が見てくれた時に、
有益な情報であるよう僕なりに書いていきます。※誤字、脱字、知識の誤りなど見つけた方はご教授いただけると幸いです。
目次
0:はじめに
1:開発環境
2:前提条件
3:処理の流れ
4:実装
5:さいごに
開発環境
・ruby: 2.6.3
・rails: 5.2.4.5
・OS: macOS Catalina ver10.15.7
・Cloud9前提条件
下記3つはすでに実装済で話を進めます。
1:User(ユーザー)
2:book(投稿)
3:favorite(いいね)
※本を投稿するWebアプリを例にしている為、bookモデルを作成しています。
(postで作っている方が多いかも)・device導入
※ここではCRUD機能基礎やアソシエーション、いいね機能そのものについての説明は割愛します。
実装の流れ
Ajaxの概要についてはこちらの記事がとても参考になりました。
初心者目線でAjaxの説明
簡単に言い換えるなら、
webブラウザからのリクエストに対し、JavaScriptを使って、
画面を遷移せずに(リロードすることなく)指定されたリクエストを行う。
例
ユーザーがいいねボタンを押す
↓
(ページは変わらずに)
画面のいいねボタンのところだけ変更される(いいねが増える)
非同期通信 処理の流れ
実際の処理の流れは下記のようになっています。
1:ユーザーがいいねをクリックする(送信する)
(Webブラウザ)
↓
2:ルーティングでどのコントローラのどのアクションを呼び出すかを定める
(route.rb)
↓
3:アクションを実行する[いいねの保存や削除]
(favorites.controller.rb)
↓
4:コントローラ名/アクション名.js.erbファイル内の処理を実行する[部分的にページを更新]
(views/favorites/create.js.erb,
views/favorites/destroy.js.erb)
↓
5:レスポンスを返す
(Webブラウザ)
実装
ここからは順を追って実装方法をまとめていきます。
■jQueryの読み込み
下記、該当箇所に追記
Gemfile.gem 'jquery-rails'bundle install 忘れずに!
app/assets/javascripts/application.js//= require jquery //= require rails-ujs
■いいねボタンにAjaxの処理を適用させる
books/index.html.erb<% @books.each do |book| %> <td> <%= link_to book.title, book_path(book.id) %> </td> <td> <%= book.body %> </td> <td> <% if book.favorited_by?(current_user) %> <%= link_to book_favorites_path(book), method: :delete, remote: true do %> #remote: trueを追加 ♥<%= book.favorites.count %>いいね <% end %> <% else %> <%= link_to book_favorites_path(book), method: :post, remote: true do %> #remote: trueを追加 ♡<%= book.favorites.count %>いいね <% end %> <% end %> </td> <% end %>
link_to内にremote; true
を追加しています。
これにより、HTMLリクエストではなく、JavaScriptのリクエストがfavoritesコントローラに送られます。HTMLリクエストの場合
→データベースにいいねを保存or削除し、画面にリダイレクトする(ページを読み込む)
JavaScriptリクエストの場合
→データベースにいいねを保存or削除し、JavaScriptを使っていいね部分のみ更新する(ページ読み込みなし)
■いいね保存、削除のリダイレクト先を消す。
favorites.controller.rbclass FavoritesController < ApplicationController before_action :authenticate_user! def create @book = Book.find(params[:book_id]) favorite = current_user.favorites.new(book_id: @book.id) favorite.save redirect_back(fallback_location: root_path) #ここを削除! end def destroy @book = Book.find(params[:book_id]) favorite = current_user.favorites.find_by(book_id: @book.id) favorite.destroy redirect_back(fallback_location: root_path) #ここを削除! end endリダイレクト先を削除したことにより、
リダイレクト先がない、かつJavaScriptリクエストという状況になり、createアクション実行後は、
create.js.erbファイル
を、
destroyアクション実行後はdestroy.js.erbファイル
を探すようになります。
■更新したい箇所を部分テンプレート化する
/favorites/_favorites.html.erb<% if book.favorited_by?(current_user) %> <%= link_to book_favorites_path(book), method: :delete, remote: true do %> ♥<%= book.favorites.count %>いいね <% end %> <% else %> <%= link_to book_favorites_path(book), method: :post, remote: true do %> ♡<%= book.favorites.count %>いいね <% end %> <% end %>少しややこしいですが、「2:いいねボタンにAjaxの処理を適用させる」のコードから、
更新したい部分だけを切り取って部分テンプレートにしています。(いいねボタンといいね数)■部分テンプレートを読み込みます。
book/index/html.erb<% @books.each do |book| %> <tr> <td> #本のタイトル、本詳細ページ(show)へのリンク <%= link_to book.title, book_path(book.id) %> </td> <td> #本の内容 <%= book.body %> </td> #部分テンプレートにした箇所(部分的に更新したい箇所) <td id="favorite_buttons_<%= book.id %>"> <%= render "favorites/favorite", book: book %> </td> </tr> <% end %>
point1
eachメソッド内でのrender処理なので、
インスタンス変数は存在しません。bookをbookに渡しています。(book: book
)point2
この箇所の更新を指定するために、変更したい箇所にidで名前をつけます。
id="favorite_buttons_<%= book.id %>"
■js.erbファイルの編集
favorites/create.js.erb$('#favorite_buttons_<%= @book.id %>').html("<%= j(render "favorites/favorite", book: @book) %>");favorites/destroy.js.erb$('#favorite_buttons_<%= @book.id %>').html("<%= j(render "favorites/favorite", book: @book) %>");指定したセレクタ(idの部分)のみHTMLをrenderして部分的に更新します。
これで完成です。
さいごに
実装の流れを簡単にまとめるとこのようになります。
1:
link_to文
にremote: true
を追加
2:create,destroyアクションのリダイレクト先を削除
3:部分テンプレートの作成
4:部分テンプレートの読み込み
5:js.erbファイル
にて部分テンプレート箇所のみ更新処理
・個人的な感想
render内で読み込む部分テンプレートにどのような変数を渡すのかを間違えないように意識することが大事だと思いました。
今までクラス名やid名の指定で-(ハイフン)を使っていましたが、_(アンダーバー)と見間違えて、js.erbファイル内でのセレクタの指定がうまくできず、半日無駄にしてしまいました。今後、-(ハイフン)の使用は禁止しようと思います。
以上です。
拙い知識でわかりづらい文章だと思いますが、最後まで閲覧ありがとうございました。
少しでも誰かの役に立っていたら幸いです。この記事について誤っている部分や改善点等あればコメントしていだけると助かります。
- 投稿日:2021-03-08T17:10:25+09:00
Rails newまでまとめてみた
アウトプット兼ねてまとめてみました。
*アドバイス訂正などありましたらご指摘お願いいたします。ステップ1
mkdir xxxでフォルダー作ってそこに移動
ステップ2
gem install rails ↓ rbenv exec gem install bundler
rbenv exec
はrbenvでインストールしたrubyを使ってbindlerを入れるらしい。ステップ3
rails new プロジェクト名 -d mysql作成したプロジェクトに移動
↓
bundle install --path vendor/bundle
二回目以降はbundle install
のみでOKステップ4
bundle exec rails s
- 投稿日:2021-03-08T16:22:36+09:00
環境構築 OSがMojave以前の場合
1. Command Line Toolsの導入
1-1. Command Line Toolsをインストール
ターミナルに入力
$ xcode-select --install「インストール」をクリック。
「同意する」をクリック。2. Homebrewの導入
2-1. Homebrewをインストール
コマンドを順番に1つずつ実行。
$ cd #ホームディレクトリに移動 $ pwd #ホームディレクトリにいるかどうか確認 $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" # コマンドを実行PCのパスワードを入力する。
「Press RETURN to continue or other key to abort」
が表示されたら、エンターキーをおす。2-2. Homebrewがインストールされているか確認
上のコマンドだけ実行する。
バージョン情報が表示されれば無事にインストールされている。$ brew -v Homebrew 2.5.1 # 数値は異なる場合があります2-3. Homebrewをアップデート
$ brew update2-4. Homebrewの権限を変更
$ sudo chown -R `whoami`:admin /usr/local/bin再度パスワードを求められた場合は、先ほどと同じように入力。
3. Rubyをインストール
Webアプリケーションの開発においては専用のRubyをインストールする必要があります。
3-1. rbenv と ruby-buildをインストール
$ brew install rbenv ruby-build3-2. rbenvをどこからも使用できるようにする
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile3-3. bash_profileの変更を反映
$ source ~/.bash_profile3-4. readlineをinstall
ターミナルのirb上で日本語入力を可能にする設定
$ brew install readline3-5. readlineをどこからも使用できるようにする
$ brew link readline --force3-6. rbenvを利用してRubyをインストール
$ RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)" $ rbenv install 2.6.2.6.5と書いてあるのは今回インストールするRubyのバージョンです。
3-7. 利用するRubyのバージョンを指定
$ rbenv global 2.6.5デフォルトでPCに入っていたRubyから、先ほどインストールしたRubyを使用するように切り替えることができました。
3-8. rbenvを読み込んで変更を反映
$ rbenv rehash3-9. Rubyのバージョンを確認
以下のコマンドで最終確認。
$ ruby -v4. MySQLを用意
4-1. MySQLのインストール
$ brew install mysql@5.64-2. MySQLの自動起動設定
MySQLは本来であればPC再起動のたびに起動し直す必要がありますが、それは面倒であるため、自動で起動するようにしておく。
$ mkdir ~/Library/LaunchAgents $ ln -sfv /usr/local/opt/mysql\@5.6/*.plist ~/Library/LaunchAgents $ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.6.plist4-3.mysqlコマンドをどこからでも実行できるようにする
# mysqlのコマンドを実行できるようにする $ echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.bash_profile $ source ~/.bash_profile # mysqlのコマンドが打てるか確認する $ which mysql # 以下のように表示されれば成功 /usr/local/opt/mysql@5.6/bin/mysql4-4. mysqlを起動を確認
# mysqlの状態を確認するコマンドです $ mysql.server status # 以下のように表示されれば成功 SUCCESS! MySQL running5. Railsの導入
5-1. bundlerをインストール
Rubyの拡張機能(gem)を管理するためのbundler(バンドラー)をインストールする。
$ gem install bundler --version='2.1.4'5-2. Railsをインストール
$ gem install rails --version='6.0.0'5-3. rbenvを再読み込み
$ rbenv rehash5-4. Railsが導入できたか確認
% rails -v Rails 6.0.0 #「Rails」のあとに続く数字は変わる可能性があります6. Node.jsの導入
6-1.Node.jsのインストール
$ brew install node@14Node.jsへのパスを設定
$ echo 'export PATH="/usr/local/opt/node@14/bin:$PATH"' >> ~/.bash_profile $ source ~/.bash_profile6-2. Node.jsが導入できたか確認
$ node -v v14.15.3 # 数値は異なる場合があります7. yarnの導入
7-1. yarnをインストール
$ brew install yarn7-2. yarnが導入できたか確認
$ yarn -v
- 投稿日:2021-03-08T12:52:34+09:00
[Rails]Rspecでログインユーザーを作成する方法
はじめに
- 自分が初めてRspec(リクエストスペック)を実装した時につまづいた。同じ人のために、記事録として作成しています。
課題
- 200を期待しているが、302が返る
it "リクエストが成功する" do subject expect(response).to have_http_status(:ok) #=> found(302) #=>ok(200)を期待しているが、 found(302)が返る end原因
- ログインユーザーではない場合、リダイレクトする
before_action
を設定している解決策
- ユーザーをログインさせる
実装
①設定
以下のファイルにコードを追加するrails_helper.rbRSpec.configure do |config| #以下を追加 # リクエストスペックで Devise のテストヘルパーを使用できるようにする config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::IntegrationHelpers, type: :request # 以上を追加 end②ユーザーをログインさせる
1.before do ~ end
でログインユーザーを作成(テストデータはusers.rbで作成)
2.sign_in @user
でユーザーをログインさせるxxx_request_spec.rbRSpec.describe "xxx", type: :request do # ログインさせるユーザーを作成 + before do + @user = create(:user) + end # 略 it "リクエストが成功する" do # 先ほど作成したユーザーをログインさせる + sign_in @user subject expect(response).to have_http_status(:ok) end endbefore
it "リクエストが成功する" do subject expect(response).to have_http_status(:ok) #=> 302 #=>ok(200)を期待しているが、 found(302)が返る endafter
it "リクエストが成功する" do + sign_in @user subject expect(response).to have_http_status(:ok) #=> 200 #=>ログインしている為、before_actionにかからなくなり、ok(200)が返る end今回のまとめ
- テストを実行するとリダイレクトされてしまう(200 => 302)
- 原因は
before_action
で未ログインユーザーはリダイレクトするようになっている為。 →ユーザーをログインさせる必要がある- ユーザーをログインさせるには、
sign_in
を使用できるようにする必要があるsign_in
を使用するには、rails_helper.rb
に設定を記述する必要がある
- 投稿日:2021-03-08T12:33:28+09:00
【Rails】slickを使ってスライド形式で画像を表示しよう!
slickを利用して複数の画像をスライド形式で表示できる機能を実装します。
今回もレシピアプリを例に作成していきます。完成イメージ
※今回は画像投稿機能が実装されている前提で話を進めていきます。
画像投稿機能については前回記事にしておりますのでそちらを参照してください。
slickとは
slickとはjQueryベースの、スライダーを作成するためのプラグインです。
slickの導入
まずはslickの公式サイトにアクセスし、「get it now」を選択します。
するとページ下部に遷移するので、そこに記載されているCDNのコードをコピーします。
コピーしたコードをapplication.html.erbに貼り付けます。
app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title>Recipe</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css"/> #追加 <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> <script type="text/javascript" src="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js"></script> #追加 </head> #以下略jQueryの導入
Bootstrapを導入している方は、すでにjQueryが導入されているので飛ばしてください。
Bootstrap導入についてはこちら
それではjQueryを導入していきましょう。
ターミナルyarn add jquerywebpackの設定をします。
config/webpack/environment.jsconst { environment } = require('@rails/webpacker') //ここから const webpack = require('webpack') environment.plugins.append( 'Provide', new webpack.ProvidePlugin({ $: 'jquery/src/jquery', jQuery: 'jquery/src/jquery' }) ) //ここまでを追加 module.exports = environmentjQueryを読み込ませます。
app/javascript/packs/application.js//中略 require("@rails/ujs").start() //require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("./preview") require('jquery') //追記 //以下略以上でslick、jQueryの導入完了です。
スライドの実装
ビューファイルの編集
まずは、表示させたい画像をビューに表示させます。
app/views/recipes/show.html.erb<div class="text-center"> <div class="card recipe-card "> #画像の表示 <% @recipe.images.each do |image| %> <%= image_tag image, class: "card-img-top show-img" %> <% end %> <div class="card-body"> <div class="recipe-name"> <%= @recipe.title %> </div> <div class="recipe-content"> カテゴリー: <span class="recipe-category"><%= @recipe.category.name %></span> 所要時間: <span class="recipe-time"><%= @recipe.time_required.name %></span> </div> <hr> <p class="card-text"> <div class="recipe-title">作り方</div> <div class="recipe-text d-flex justify-content-start"> <%= safe_join(@recipe.text.split("\n"),tag(:br)) %> </div> </p> </div> </div> </div>このままだと、以下のようにただ画像を3枚縦に並べただけになります。
slick.jsの作成
slickを使用するためのJSファイルを作成します。
touch app/javascript/packs/slick.js作成したslick.jsに以下のコードを貼り付けます。
app/javascript/packs/slick.js$(function() { $('.slider').slick(); });slick.jsを読み込ませます。
app/javascript/packs/application.js//中略 require("@rails/ujs").start() //require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("./preview") require('jquery') require("./slick") //追記 //以下略そして、ビューファイルを以下のようにsliderのdiv要素でimage_tagを囲うよう再編集します。
app/views/recipes/show.html.erb<div class="text-center"> <div class="card recipe-card "> #sliderで囲う <div class="slider"> <% @recipe.images.each do |image| %> <%= image_tag image, class: "card-img-top show-img" %> <% end %> </div> <div class="card-body"> <div class="recipe-name"> <%= @recipe.title %> </div> #以下略slick.jsの編集
このままでは、上下のボタンが不格好なので表示させないようslick.jsを編集します。
ボタンの代わりに自動でスライドを再生させる記述をします。app/javascript/packs/slick.js$(function() { $('.slider').slick({ arrows: false, //ボタン非表示 autoplay: true, //自動再生 autoplaySpeed: 4000, //再生スピード }); });
- 投稿日:2021-03-08T12:25:19+09:00
AWSへデプロイ後にユーザー情報が更新できない現象への対処法
AWSへデプロイ後に、user.saveでユーザー情報が保存できない現象についての対処法
とあるプログラミング学習サイトを利用してRailsを学び、作成したアプリケーションをサーバーへデプロイしたところまでは良かったのですが、その後作成したユーザーのアイコンを変更できない現象に遭遇しました。手探りでの対処で苦戦しましたので、同じことを繰り返さないためにも投稿に残したいと思います。
現象が発生した元のコード
app/controllers/users_controller.rbdef update @user = User.find_by(id: params[:id]) @user.name = params[:name] @user.user_id = params[:user_id] @user.email = params[:email] if params[:image] @user.image_name = "#{@user.id}.jpg" image = params[:image] File.binwrite("public/user_images/#{@user.image_name}", image.read) end if params[:password] != nil @user.password = params[:password] end if @user.save flash[:notice] = "ユーザー情報を編集しました" redirect_to("/users/#{@user.user_id}") else render("users/#{@user.user_id}/edit") end endこのコードでローカルではうまくいっていたのですが、サーバーへデプロイ後はユーザー画像のみ保存されて、MySQLのデータは更新されませんでした。
試したこと
エラーが吐き出されていないか確認しましたが、log/production.logにはエラーらしきものはなく、mysql.logを確認しようとしましたがどこにあるのかわからず、できませんでした。
このためはじめはrails consoleで状況を確認しようとしましたが、RDSのデータベースには接続されていないのか、users = User.allでユーザー情報を取得しようとしても、users.count = 0の状態でした。
そこで直接MySQLへログインして、テーブルのデータをSQLで更新してみたところ、こちらはできましたので、MySQLを使用して更新する方法を取ることにしました。
現象が改善した後のコード
app/controllers/users_controller.rbdef update id = @current_user.id updated = 0 update_name_sql = "update users set name = '#{params[:name]}' where id =#{id};" updated = ActiveRecord::Base.connection.execute(update_name_sql) update_email_sql = "update users set email = '#{params[:email]}' where id =#{id};" updated = ActiveRecord::Base.connection.execute(update_email_sql) if params[:image] update_image_name_sql = "update users set image_name = '#{id}.jpg' where id =#{id};" updated = ActiveRecord::Base.connection.execute(update_image_name_sql) image = params[:image] File.binwrite("public/user_images/#{id}.jpg", image.read) end if params[:password] hashed_password = BCrypt::Password.create(params[:password]) update_password_sql = "update users set encrypted_password = '#{hashed_password}' where id =#{@current_user.id};" ActiveRecord::Base.connection.execute(update_password_sql) end if updated =! nil flash[:notice] = "ユーザー情報を編集しました" redirect_to("/users/#{@user.id}") else flash[:notice] = "データベースへの保存に失敗しました" render("users/edit") end endSQLを直に作成して、ActiveRecord::Base.connection.execute(SQL)でMySQLのデータベースを直接更新する方法へ変えました。
(パスワードに関してもトラブルがありましたので、直接暗号化してから保存するコードに書き換えました)まとめ
プログラミング学習サイトの良いところは、手軽に言語の学習に手をつけられるところですね。しかし現実には実用レベルに達するためにいくつもの壁を越えなければいけないものなのだと実感しています。ローカルでは動いていたけれど、サーバー環境ではうまくいかないことが普通にあるのだとわかりました。これに懲りずにポートフォリオ作りに励もうと思います!
- 投稿日:2021-03-08T10:52:43+09:00
カラムから外部キーを削除する
アプリを作成している中で外部キーを削除する必要が出てきたので今後のためにも記事を残します。
/schema.rb
create_table "behavior_histories", force: :cascade do |t| t.bigint "care_recipitent_id" t.date "behavior_history_date", null: false t.text "action_record", null: false t.time "behavior_time", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.bigint "family_id" t.index ["family_id"], name: "index_behavior_histories_on_family_id" t.index ["care_recipitent_id"], name: "index_behavior_histories_on_care_recipitent_id"この中の外部キーであるcare_recipitent_idを削除します。
migrationファイルの作成
rails g migration remove_foreign_key_to_behavior_histories外部キーの削除
class RemoveForeignKeyToBehaviorHistories < ActiveRecord::Migration[6.0] def change remove_foreign_key :behavior_histories, :care_recipitents remove_reference :behavior_histories, :care_recipitent, index: true end endrails:db:mirateで削除されていることを無事確認。
※remove_foreign_keyとremove_referenceの順序を変えるとうまくいかないため注意してください。
- 投稿日:2021-03-08T09:51:37+09:00
[Rails]Arelは簡単or難関?使い方大全
はじめに
あるプロジェクトの開発中、「Arel」というSQL文的なものが出てきました。
初めて見た僕は、「なにこれSQL文?」と疑問でした。調べてみると、Railsの簡単にSQLを使えるという機能の一つということがわかりました。
そこで、今後も使う可能性がありそうだったので、記事にまとめようと思った次第です。。
備忘録的にまとめてますが、誰かの為になれたらと思います。Arelとは
Arelとは,「Relational Algebra」または「Active Relation」の略で、
その名前の通り、ActiveRecordから派生した、
「関係代数」をRubyのオブジェクトで取り扱うライブラリのことです。公式ドキュメントによると、、
1.複雑なSQLクエリを簡単に生成可能
2.色んなRDBMSに対応可能
3.フレームワーク(特にORM)のフレームワークとして開発されたつまり、Arelを使うことで、DBの互換性やSQL文字列の生成などに時間を取られることなく、
大事な設計やモデリングに注力してO/Rマッピング処理を実装することができるようになっています。基本的な操作方法一覧
arel_table
モデル名.arel_table
とすることで指定したモデルのテーブルを検索する準備が出来る。
オプションとして、カラム名を追加したり、条件を追加して検索できる。Staff.where( Staff.arel_table[:age] )Arel.sql(〇〇〇〇)
Arel.sql(〇〇〇〇)
とすることでSQL文を生成できる。
※〇〇にはSQL文を入れる。Arel.sql('〇〇〇〇')Arel::Nodes
Arel::Nodes#build_quoted
文字列などの値をラップして、
Arel::Nodes::NamedFunction
などの引数に渡せるようにします。Arel::Nodes.build_quoted(" ")Arel::Nodes::NamedFunction
任意のSQL関数の呼び出しを表現するために使う。
第1引数に関数名、第2引数に関数に渡す引数(配列)、第3引数にエイリアス名(任意)を渡す。staffs_table = Staff.arel_table date_format = Arel::Nodes.build_quoted("%Y-%m-%d") Staff.select( Arel::Nodes::NamedFunction.new( "DATE_FORMAT", # 日時を指定のフォーマットに整形してくれる関数 [staffs_table[:created_at], date_format], # created_atを指定したフォーマットに変換 "registration_date" ) )Arel::Nodes::SqlLiteral
生のSQL文字列を生成出来る。
SQLの構文中に必要な文字列をラップするのに使われる。
- utf8_general_ci
- UTF-8文字コードで半角や全角、濁点を区別し、英語の大文字小文字を区別しない指定。
Arel::Nodes::SqlLiteral.new("utf8_general_ci")Arel::Nodes::InfixOperation
「infix operation」は二項演算という意。
2つの値・変数の計算を行うことができます。
第1引数に演算子、第2引数に演算子の左側の値、第3引数に演算子の右側の値が入る。Staff.where( Arel::Nodes::InfixOperation.new( "COLLATE", # 検索条件で照合順序を指定 Staff.arel_table[:name], # 演算子の左側が、staffsテーブルのnameカラム Arel::Nodes::SqlLiteral.new("utf8_general_ci") # 演算子の右側が、utf8_genera_ci ) .matches(Arel::Nodes.build_quoted("さとう%")) # nameが「さとう」から始まるStaffを取得 )Arel::Nodes::OuterJoin
外部結合(OUTER JOIN)する際に使用する。
JOIN句の第2引数にArel::Nodes::OuterJoin
を指定し、
最後にjoin_sources を呼び出すことで結合できる。comments_table = Comment.arel_table staffs_table = Staff.arel_table Comment.joins( comments_table .join(staffs_table, Arel::Nodes::OuterJoin) .on( comments_table[:staff_id].eq(staffs_table[:id]) .and(staffs_table[:is_active].eq(true)) ) .join_sources )※commentsテーブルのstaff_idとStaffテーブルのidが等しい、
かつstaffsテーブルの is_activeがtrueのものという条件。Arel::Nodes::Descending, Arel::Nodes::Ascending
Arel::Nodes::Descending
は ORDER 句の DESC (降順)の部分を担当。
Arel::Nodes::Ascending
は ASC (昇順)です。引数には、ORDER BY の右側の値が入ります。Staff.order( Arel::Nodes::Descending.new( Staff.arel_table[:staff_type]) # staff_typeの降順 ) )Arel::Nodes::Case
SQL の CASE 構文を表現することができます。
- gt
- 左項の値が右項の値より大きいときに真になる。(「>」と同意)
staffs_table = Staff.arel_table new_cond = staffs_table[:created_at].gt(Time.zone.now - 3.days) Staff.order( Arel::Nodes::Ascending.new( Arel::Nodes::Case.new .when(new_cond).then(1) .else(9) ) )Staffが作成されて3日以内であれば 1、そうでなければ 9 という値とする。
3日以内に作成されたStaffを昇順で並び替えている。Arel::Nodes::Grouping
括弧()で囲む。四則計算等で括弧を使いたい場合に使う。
Arel::Nodes::Grouping( object )Arel::Nodes::As
AS による別名定義の文
〇 AS ×
を作る。Staff.select( Staff.arel_table[:id].as('no') )Tips集
使えるTipsをご紹介。
IS NOT NULL
Staff.where( Staff.arel_table[:age].not_eq(20) )AS
Staff.where( Staff.arel_table[:name].as('admin') ) admin = Staff.arel_table.alias('admin') Comment.joins(admin).select(admin[:name])サブクエリ
FROM句へのサブクエリ
new_table = Arel::Table.new(nil) sql = new_table.from( Staff.where(created_at: start_at...end_at).to_sql ) .project(Arel.sql('*')) .to_sqlJOIN句へのサブクエリ
sub_query = Staff.where(created_at: start_at...end_at).to_sql Comment.joins("INNER JOIN (#{sub_query})")SUM
Staff.where(created_at: start_at...end_at) .select(Staff.arel_table[:name].sum().as('admin'))Where
staffs = Staff.arel_table staffs.arel_table.project(Arel.sql('*')).where(staffs[:id].eq(1)).to_sqlorder by
staffs = Staff.arel_table staffs.project(Arel.sql('*')).order(staffs[:id].asc).to_sql集計関数とgroup by
staffs = Staff.arel_table staffs.project(staffs[:id].count, staffs[:name]).group(staffs[:age]).to_sqlCOUNTで利用するDISTINCT
Staff.select(Staff.arel_table[:id].count()) .select(Staff.arel_table[:id].count('distinct'))終わりに
Arelでは色んな使い方があるのだなと調べて驚きました。
使いこなすには実践あるのみ!ですね。。
しかし、使うことに難を示す人たちもいるようで、賛否両論ではありますが、
チーム開発において、誰かが使って自分が理解出来ない。。ってことが起きると開発にブレーキをかけてしまうので、一応理解しているだけ得だと思いました!参考
Arel::Nodes を使って Arel で複雑な SQL文を作っちゃおう
- 投稿日:2021-03-08T09:16:12+09:00
Basic認証の導入
本日は、Basic認証の導入手順を復習のため記事に
残したいと思います。バージョン
・Ruby 2.6.5
・Rails 6.0.0
・macOS Big SurBasic認証とは
Basic認証とは、HTTP通信の規格に備え付けられている、ユーザー認証の仕組みのことです。
簡単に説明すると、作成したアプリケーションにユーザー名とパスワードを記述して入力した人だけが
アプリケーションを使えるよと言うものです。Basic認証導入手順
まずは、ユーザーとパスワードを設定しましょう。
設定には、authenticate_or_request_with_http_basicメソッドを使用します。
Basic認証によるログインの要求は、すべてのコントローラーで行いたいです。app/controllers/application_controller.rb
class ApplicationController < ActionController::Base before_action :basic_auth private def basic_auth authenticate_or_request_with_http_basic do |username, password| username == ユーザー && password == パスワード end end endBasic認証の処理をapplication_controller.rbのprivate以下にメソッドとして定義し、before_actionで呼び出しましょう。
これで、設定できました。Basic認証コードを改良
設定できましたが、このままではGit Hubに更新した際にそのまま載ってしまうので
セキュリティ的に良くないため、環境変数を利用する実装に切り替えます。
basic_authメソッド内で直接記述しているユーザー名とパスワードを、開発環境の環境変数に格納します。これは使用しているMacのOSによってやり方は変わってきます。
今回は、OSがCatalina以降のやり方となります。1.コマンドの実行
% vim ~/.zshrc2.「iキー」を押して、インサートモードに移行します。
ターミナルの左下に「INSERT」と表示されるのを確認。3.zshの内部に、以下の記述を追加しましょう。
export BASIC_AUTH_USER=ユーザー export BASIC_AUTH_PASSWORD=パスワード既にzsh内に記述がある場合は、その記述の下に「追加」します。既存の記述を削除してしまうと、パソコンが正常に動作しなくなる危険性があります。
4.記述を追加したら「escキー」を押して、 「:wq」と入力しましょう。
入力後、「Enterキー」を押して終了します。5.最後に、「sourceコマンド」を実行します。
% source ~/.zshrcこれで、環境変数の設定は完了です。
出てきたいくつかの用語について説明させていただきます。・zsh(ズィーシェル)
「zsh」はログインシェルと呼ばれるもので、プログラムを実行する時に、ユーザーの要求に一番最初に対応する役割を担います。隠しファイルなので、特別な設定なしではFinderなどには表示されていません。
環境変数を記載する場所は、設定ファイルである「.zshrc」の中です。
OSがCatalina以降であれば「zsh」、Mojave以前であれば「bash」が自動で適用されます。・vim(ヴィム)
vim」とは、サーバー上で使用できるテキストエディタです。vimコマンドを用いることで、指定したファイルの編集をターミナルから行うことが可能です。
以下が使用例になります。・source(ソース)コマンド
「sourceコマンド」とは、シェルに記述された内容を実行する役割を担います。zshファイルに記述された内容を実行します。つまり、.zshrcというファイルは隠しファイルで直接記述できないので、vimコマンド
で編集を行い、sourceコマンド実行を行うということですね。
使っているMacのOSによって、「zsh」か「bash」か変わって来るので、設定する前に確認
が必要です。ここまでできたら再度、application_controller.rbファイルを編集しましょう。
application_controller.rb
class ApplicationController < ActionController::Base before_action :basic_auth private def basic_auth authenticate_or_request_with_http_basic do |username, password| username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"] # 環境変数を読み込む記述に変更 end end endこれで、環境変数を使って、Basic認証を行えるユーザー名とパスワードを定義できました。
本番環境での環境変数の設定
続いて、Herokuの本番環境のアプリケーションにも設定を行います。
下記のコマンドを入力します。% heroku config:set BASIC_AUTH_USER=ユーザー % heroku config:set BASIC_AUTH_PASSWORD=パスワード設定できたかは、下記のコマンドで確認できます。
% heroku config === stormy-journey-22625 Config Vars BASIC_AUTH_PASSWORD: パスワード BASIC_AUTH_USER: ユーザー変更したコードをコミットしHerokuにデプロイします。
% git add . % git commit -m "Basic認証を導入" % git push heroku masterこれで、すべて設定は完了です。
使っている環境によってやり方が違う場合もあるので
ご了承ください。
- 投稿日:2021-03-08T01:59:43+09:00
Rails 超簡単管理者権限
全知全能管理者権限をつけたいそこのあなた!
簡単な方法で実装できる管理者権限を記事にしてみましたっ!^^前提
devise を使ってユーザー周りを作成していること
簡単な投稿機能をつけているサイトであること管理者権限を付与する
まずusersテーブルにユーザーが管理者かどうかを判定するためのカラム(adminカラム)を追加します!
管理者ならtrueをそうでなければfalseを返したいので、データ型はboolean型にします!$ rails g migration: AddAdminToUser admin:booleanここでマイグレーションファイルがうまく作成されているかを確認しましょう!
管理者権限は基本的に特定の人にしか付与しないので、作成されたマイグレーションファイルにdefault: false
を追加します。db/〇〇_add_admin_to_user.rbclass AddAdminToUser < ActiveRecord::Migration[6.0] def change add_column :users, :admin, :boolean, default: false #default: falseの追加をお忘れなく!!! end end$ rails db:migrate管理者権限を持たせたいユーザーの初期レコードをseedファイルで定義する
※このサイトではユーザー名を格納するカラムをnameカラムとしています。schemaで自分のものに適したカラム名を加えてください、!db/seeds.rbUser.create!(name: "kazuya", email: "kazuya@mail.com", password: "ochibi", password_confirmation: "ochibi", admin: true)$ rails db:seed※複数の人に管理者権限を付与したい場合のseedファイルの書き方
db/seeds.rbUser.create!( [ { name: "kazuya", email: "kazuya@mail.com", password: "ochibi", password_confirmation: "ochibi", admin: true }, { name: "kubochiro", email: "kubo@mail.com", password: "dodeka", password_confirmation: "dodeka", admin: true } ] )$ rails db:seed※管理者のデータを間違えて入れてもた!って人向け
$ rails cirb(main):001:0> User.allこれで消し去りたい管理者のidを確認する
今回管理者権限を剥奪したいkazuyaのidは3でした!!!!!!irb(main):002:0> User.find(3).destroyirb(main):003:0> exit〜管理者権限を使ってやりたいこと色々〜
管理者権限でよく実装されるものを書き連ねます。
こんな権限が欲しいというのが他にありましたらぜひ依頼くださいまし (^_^)その1: 不適切な投稿を削除する
どうしてもサイトの趣旨に合わない投稿をされてしまうことってありますよね、、、
これは管理者権限を付与されている人だけが、どんな投稿でも削除できるようにするとことで解決できます!
<% if current_user.admin? %>
これが一番のミソです!!ここでadminの値がtrueかどうかを判定しています(つまりログインしているユーザーが管理者かどうか)posts/index.html.erb<% @posts.each do |t| %> #ここから <% if user_signed_in? %> <% if current_user.id == t.user_id %> <%= link_to "編集する", edit_post_path(t.id) %> <%= link_to "削除する", post_path(t.id), method: :delete %> <% elsif current_user.admin? %> <%= link_to "管理者用編集", edit_post_path(t.id) %> <%= link_to "管理者用削除", post_path(t.id), method: :delete %> <% end %> <% end %> #ここまで <% end %>ざっくり解説すると、、この条件分岐では、ユーザーは自分の投稿の編集削除はできるが、他のユーザーの投稿をいじることはできない。しかし管理者(adminがtrue)だと、全ての投稿を編集削除することが可能。また管理者でログインしているとき、その管理者自身の投稿の編集削除は「編集する」「削除する」と表示されるが、その管理者以外の投稿は「管理者用編集」「管理者用削除」と表示される。
、、、こういう風に条件分岐を駆使して理想状態を考えて作るのは楽しいですよ〜〜 (^O^)その2: 不適切管理者だけの秘密のページ❤️
実際にどれくらいの人が自分の作ったサイトに新規登録してくれているのか、気になっちゃいますよね?
ここでは管理者だけが覗けるページ、今回は例として、登録者情報、登録者数をこっそり閲覧するユーザー一覧ページを作成しようと思います!!
他にも良いやり方はあると思いますが、今回は簡単のため条件分岐だけで実装しまふ。。
usersコントローラー
ユーザーの全情報を取得するために・・・
users_controller.rbclass UsersController < ApplicationController def index @users = User.all end end一覧ページへのルート
urlは適宜変えてください m(_ _)m
なるべくマイページ(show)を作ってる人がエラー出にくくなるようにこのurlにしましたroutes.rbget 'users' =>'users#index'一覧ページのビュー
もし管理者権限を付与されていない人がこのページのURLにたどり着いても、中身を表示させないためにif分で制限をかけています。
今回は、登録者情報(メアド、ユーザー名)とアカウントの合計数を表示しています。users/index.html.erb<% if user_signed_in? %> <% if current_user.admin? %> <% sum = 0 %> <% @users.each do |u| %> <p>メアド:<%= u.email %></p> <p>名前:<%= u.name %>さん</p> <% sum += 1 %> <% end %> <p>登録者合計:<%= sum %></p> <% end %> <% end %>秘密のページに飛ぶための秘密のリンク
秘密のページ(ユーザー一覧ページ)に飛ぶための秘密のリンクを任意のビューページに表示します!
今回は例として posts/index.html.erb に表示させています!posts/index.html.erb<% if user_signed_in? %> <% if current_user.admin? %> <%= link_to "秘密のページへ", users_path %> <% end %> <% end %>いかがでしたでしょうか、、???
これ以外にも管理者権限でやりたいことなどあれば依頼してください?♂️
その3その4、、を追記していきます、、!!忘れないで!
管理者権限を付与する際に、seedファイルに初期レコードを定義しているので、デプロイする際には以下のコマンドを忘れないようにしましょう!!
$ heroku run rake db:seed最後に
ここまで読んでくださってありがとうございました (°▽°)
やっていることは簡単ですが、railsの勉強を始めたばかりの人にとっては、seedファイルをいじったり、コンソールをいじったり、条件分岐を駆使したり、、といい勉強になったのではないかなと思います ( ̄∇ ̄)
修正補足依頼、アドバイス等ございましたら、どしどしお願い致しやーーーーーーーーーーーーーーーーーーーーす !!!
バイチャ〜〜?
- 投稿日:2021-03-08T01:17:45+09:00
routes.rbのルーティングを他ファイルに分割した話
はじめに
本稿は、railsのroutes.rbに記述されたルーティングが数百行にも渡り、コードの可読性が欠けてしまっている場合において、自身がどのように見やすさを確保したのかを記載したものになります。
ルーティングを分割する
0. 編集前のconfig/routes.rb
※記載されている内容は例として設定しているだけなので、それぞれのルーティング名に関連性はありません。
config/routes.rbRails.application.routes.draw do namespace :api, format: 'json' do namespace :v1 do namespace :libraies do resources :libraries end namespace :books do resources :books end end namespace :v2 do namespace :libraries do resources :libraries do collection do get :borrowed_books end end resources :librarians do end end end end endこちらのroutes.rbをファイル分割していきます。
1. config配下に"routes"ディレクトリを作成する
2. config/routes.rbを編集
config/routes.rbRails.application.routes.draw do def draw(routes_name) instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb"))) end Rails.application.routes.draw do # ここに"routes_name"を定義します。 end end
- "routes_name"には
1. config配下に"routes"ディレクトリを作成する
で作成したroutesディレクトリ内に作成したファイル名を指定します。※まだconfig/routes配下にファイルを作成していないので、次で作成していきます。
分割先のファイルを用意
今回サンプルで作成したroutes.rbでは
- api v1
- api v2
この粒度で分割していきたいと思います。
そこで
api_v1.rb
とapi_v2.rb
という名前のファイルを作成します。touch config/routes/api_v1.rb
touch config/routes/api_v2.rb
3. routes.rbに作成したファイル名を指定する
config/routes.rbRails.application.routes.draw do def draw(routes_name) instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb"))) end Rails.application.routes.draw do draw :api_v1 draw :api_v2 end end4. api_v1.rbを編集
config/routes/api_v1.rbnamespace :api, format: 'json' do namespace :v1 do namespace :libraies do resources :libraries end namespace :books do resources :books end end end5. api_v2を編集
config/routes/api_v2.rbnamespace :api, format: 'json' do namespace :v2 do namespace :libraries do resources :libraries do collection do get :borrowed_books end end resources :librarians do end end end end終わりに
これでかなり見やすくなりました!
分割するタイミングはプロダクトによって様々だと思いますが、コードの可読性を上げるためにも管理ができる範囲でコードをどんどん外に出していくことは重要だと思います。
- 投稿日:2021-03-08T00:27:28+09:00
【個人開発】ファラオが情報共有し現代社会を生き抜くアプリ作りました
はじめに
こんにちはファラオ
世は情報社会、多くの人々がスマートフォンを持ち歩きTwitterなどで常に情報交換していますね
しかし、世で使われているSNSは現代の言語で書かれており、ファラオが使うのには少し不便なのではないでしょうか
今回はそんな情報収集に苦労しているファラオ達に使っていただけるSNSを開設しましたサービス概要
hieroglitter
ファラオ達がヒエログリフで情報を共有出来るアプリです
普段お使いの言語を入力すると、ヒエログリフに変換されて投稿されます
これで世界中のファラオと情報を共有出来ますね
※現在は日本語とアルファベットのみに対応しています使い方
こちらがトップページです
ファラオのプライバシーを守るためファラオログイン機能によって平民はフィルタリングしています
安心してお使いください
ファラオの皆さんならどこからファラオログイン出来るか一目瞭然ですね?
こちらがアプリのメインページです
ここから好きなように投稿してください
お気に入りの投稿を見つけたら猫ボタンを押すと好きなだけあなたの好意を伝えられます
使用技術について
Ruby on Rails 6.0.3.5
Yahoo ルビ振り API
https://developer.yahoo.co.jp/webapi/jlp/furigana/v1/furigana.html
漢字で投稿された際に内部でひらがなに変換していますgem 'miyabi'
https://rubygems.org/gems/miyabi/versions/0.1.1
ひらがな、カタカナをアルファベットに変換するのに使わせていただいていますgem 'pharaoh_lang'
https://rubygems.org/gems/pharaoh_lang
アルファベットをヒエログリフに変換するメソッドto_hieroglyphを提供するgemを自作しました
変換表はこちらを参考にさせていただきました
https://gist.github.com/souri-t/3bb9475c2710a55a10e71337d84ce2c3自作gemについて
アルファベットを対応するヒエログリフに変換して出力するメソッドを提供する単純なgemです
Unicodeはヒエログリフに対応しているので、A-Zまでそれぞれの音が対応するヒエログリフの文字コードをもとに文字列を変換しています
アルファベット以外はそのままの文字を出力します
※デプロイ後にノリで作成したのでせっかくなのでノリで使いましたおわりに
クソアプリ作ってみたかったので心が満たされました
自分の好きな感じのアプリが作れたかなと思います
就活用に作ってるポートフォリオそっちのけで作ったので、そっちが全然進んでませんが僕は元気です
よかったら遊んでみてくださいね
hieroglitter
ちなみにファラオファラオ言ってたらアプリの名前誰も覚えてくれなくてファラオSNSって呼ばれてます
このアプリはweb1week-202102に参加するために作りました。だら?FlutterとReactとLaravelと風来のシレン様、良い機会を下さりありがとうございました
- 投稿日:2021-03-08T00:18:46+09:00
Railsチュートリアル6.0に沿って、AWS Cloud9上にRailsの開発環境作ってみた
Railsチュートリアル6.0を実施するにあたって、記載があった手順でAWS Cloud9でクラウド統合開発環境を作ってみました。
Cloud上とPC上で説明が混ざっていたので、こちらにまとめてすっきりさせたい意図で本記事を作成しました。(あと、記事の画面が若干古かったため)なお、アカウントは既に作成済みで、使用機材はwindowsです。(クラウドなので、差異はない気がしています)
以下が参照した手順
https://railstutorial.jp/chapters/beginning?version=6.0#sec-development_environment手順1:AWSのCloud9にアクセスする
ルートユーザーでログインし、AWSマネジメントコンソールにアクセス。
上の検索窓から「Cloud9」を入力し、Cloud9に遷移します。
手順2:「Create environment」を押下
Cloud9の画面で「Create environment」を押下する。
手順3:Name environmentを設定
Name environmentの「Name」と「Description」に適当な値を設定します。
自分の場合は
Name:rails-tutorial
Description:rails-tutorial
と値を設定しました。手順4:各種設定
ここでの設定ですが、
Platform:Ubuntu Server 18.04 LTS
を設定して、他はデフォルトのままで「Next step」を押下します。
手順5:Review
設定した内容を確認し、「Create environment」を押下。
補足:インデントのデフォルトを変えておく
Rubyはインデントに2つのスペースを使うの通例らしいので、エディタのインデント設定をデフォルトから2に変更します。
手順としては、Cloud 9の右上歯車マークを押下し、Project Settingを開きます。
その後、CodeEditer(Ace)のsofttabを2に設定すれば完了です。Railsインストール
下記コマンドをCloud 9のターミナルで走らせます。
まず、Rubyドキュメントをインストールしないよう.gemrcファイルを設定します。
$ echo "gem: --no-document" >> ~/.gemrcバージョンを指定してRailsをインストールします。(Railsチュートリアルではv 6.0.3をインストールしているのでそれに倣います)
$ gem install rails -v 6.0.3下記コマンドを入力して、Railsのバージョンが返ってくれば、Railsのインストールは完了です。
$ rails -vYarnインストール
もう一つだけ、javascriptのパッケージマネージャーの「Yarn」をインストールしていきます。
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)もし警告メッセージが出た場合は、メッセージ通り下記を入力すればOKです。
$ yarn install --check-filesこちらもバージョンを確認して、表示されたらOKです。
$ yarn -vこれでRailsチュートリアルで実施されていたクラウド統合開発環境構築はいったん完了です。
Gitやherokuなどセットアップはまた別の機会に。
- 投稿日:2021-03-08T00:18:46+09:00
Railsチュートリアル6.0に沿って、AWSのCloud9上でRailsの開発環境作ってみた
Railsチュートリアル6.0を実施するにあたって、記載があった手順でAWS Cloud9でクラウド統合開発環境を作ってみました。
Cloud上とPC上で説明が混ざっていたので、こちらにまとめてすっきりさせたい意図で本記事を作成しました。(あと、記事の画面が若干古かったため)なお、アカウントは既に作成済みで、使用機材はwindowsです。(クラウドなので、差異はない気がしています)
以下が参照した手順
https://railstutorial.jp/chapters/beginning?version=6.0#sec-development_environment手順1:AWSのCloud9にアクセスする
ルートユーザーでログインし、AWSマネジメントコンソールにアクセス。
上の検索窓から「Cloud9」を入力し、Cloud9に遷移します。
手順2:「Create environment」を押下
Cloud9の画面で「Create environment」を押下する。
手順3:Name environmentを設定
Name environmentの「Name」と「Description」に適当な値を設定します。
自分の場合は
Name:rails-tutorial
Description:rails-tutorial
と値を設定しました。手順4:各種設定
ここでの設定ですが、
Platform:Ubuntu Server 18.04 LTS
を設定して、他はデフォルトのままで「Next step」を押下します。
手順5:Review
設定した内容を確認し、「Create environment」を押下。
補足:インデントのデフォルトを変えておく
Rubyはインデントに2つのスペースを使うの通例らしいので、エディタのインデント設定をデフォルトから2に変更します。
手順としては、Cloud 9の右上歯車マークを押下し、Project Settingを開きます。
その後、CodeEditer(Ace)のsofttabを2に設定すれば完了です。Railsインストール
下記コマンドをCloud 9のターミナルで走らせます。
まず、Rubyドキュメントをインストールしないよう.gemrcファイルを設定します。
$ echo "gem: --no-document" >> ~/.gemrcバージョンを指定してRailsをインストールします。(Railsチュートリアルではv 6.0.3をインストールしているのでそれに倣います)
$ gem install rails -v 6.0.3下記コマンドを入力して、Railsのバージョンが返ってくれば、Railsのインストールは完了です。
$ rails -vYarnインストール
もう一つだけ、javascriptのパッケージマネージャーの「Yarn」をインストールしていきます。
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)もし警告メッセージが出た場合は、メッセージ通り下記を入力すればOKです。
$ yarn install --check-filesこちらもバージョンを確認して、表示されたらOKです。
$ yarn -vこれでRailsチュートリアルで実施されていたクラウド統合開発環境構築はいったん完了です。
Gitやherokuなどセットアップはまた別の機会に。