- 投稿日:2021-01-18T23:28:06+09:00
deviseコントローラーで出逢ったsuperについて調べてみた
はじめに
ユーザー管理でdeviseを使っています。コントローラーをいじっていると、メソッドの中に
super
とだけ記述があるメソッドがあったので、疑問に思い、調べてみました。deviseのコントローラー
rails g devise:controllers userでコントローラーを生成しています。
user/registrations_controller.rbclass Users::RegistrationsController < Devise::RegistrationsController (略) def update super end (略) endどうやら、
Devise::RegistrationsController
クラスから継承されているようです。
いくつかのメソッドの中にsuper
が存在します。superとは
ずばり、superは、継承元のメソッドを呼び出すことができるメソッドです。
上記のコントローラーを例にすると、
Devise::RegistrationsController
にもupdateメソッドが記述されており、
このUsers::RegistrationsController
でも同じ処理をすることができるということです。他の例では…
以下は調べている中で、よく見かけた説明です。
class Tennis def ball puts "Tennisball" end end class Sports < Tennis def ball super puts "balls" end end sports = Sports.new sports.ball #=>Tennisball sports = Sports.new sports.ball #=>balls自分で例えとして作ったら、意味不なコードになりました…
要は、継承後に同じ名前でメソッドを作っても、superを使えば、オーバーライドする前の処理を呼び出すことができるということです。
- 投稿日:2021-01-18T23:13:50+09:00
Railsで個人開発してたら、Railsがめっちゃ叩かれて辛みだった話
こんにちは、だむはです。
去年の12月に個人開発している「sister」というサービスをリリースしたのですが、開発中にRails叩きがおこり、辛みだったので、その時のことをかこうと思います。ちなみに、サービスはまだベータ版です。
「sister」ってどんなサービス?
「sister」は一言で言うと、IT業界に特化した女性向けのキャリア/スキルシェアサービスです。MENTAから多大なる影響を受けています。MENTAは「師弟関係」、sisterは上下関係をもたない「姉妹関係」と言う部分でシスターフット大切にしていこう!って感じです。
興味があったらみてってください。
あなたのsisterを探そう環境
sisterの開発はこんな感じです。
・Ruby
・Rails
・Stripe
・AWS S3
・Heroku
ちなみにローカルはDockerです。はい、フロントエンドにモダンな技術も使ってないし、インフラもAWS使ってないです
なぜこの環境なの?
それは、純粋に私一番使える言語がRailsだったからです!
Herokuはデプロイ楽だし、お金の管理がしやすいからです!
理由はこれだけ!開発期間
2020年9月〜12月の約4ヶ月くらいです。
実はもともとポートフォリオとして、少しだけ作って放置していたものをベースに作りました。作業量としてはこんな感じです。
・仕事終わりに3時間くらい週3
・土日4−5時間(やったりやらなかったり)だらだらやっても終わらんと思って、12月に入り、今年中にリリースすると決めてからは、ほぼ毎日夜中まで実装してました。
事件は突然起きる、、、
12月の中旬くらいに突如、Twitterのトレンドに「Rails」があがったのです。
Twitterで定期的に起こる、Railsフルボッコ現象でした。。。不安になる私
実はsister開発中も何回か「Railsはオワコン」とかの記事を見ていたので、私も、フロントエンドにReactで、Firebaseを導入しようとしてみたり、AWSに環境構築しなおそうかなとか考えてみたり、いろいろとTryしてみようとしたのですが、全て諦めました。
諦めた理由は、時間をかければ取り入れられますが、私が技術力不足で新しいことを取り入れようとすると、インプットにかなり時間をとられるので、サービスリリースを最優先しようと判断したからです。
しかし、、、
12月中旬、、、
Twitterのトレンドに「Rails」があがるほどのフルボッコ、、、さすがに、うわーやばいのかなー、、Railsで開発するのやばいのかなーーと不安が再熱してしまいました。
私はどっちかというと、技術にめっちゃ興味がある方ではないのですが、やっぱりエンジニアとして、モダンな技術は使いたいし、sisterをリリースしたときに「どんな技術使ってるんですか?」と聞かれたら、モダンな技術つかってますって言ってみたい。
私は立ち止まって考えた
私は立ち止まって考えてみました。
自分は何がしたいんだ?目的はなんだ?
個人開発は技術力向上が目的なのか?
新しい技術を使うために始めたのか?違うだろ!!
自分が作りたいサービスがあって、それを世にリリースしたいからだろ!!と、本来の目的を思い出しました。
ちゃんと説明すると、
「モダンな技術を身につけることではなく、サービスをリリースすることがいちばんの目的」
ということです。そして私はリリースまで駆け抜けた
12月中旬の出来事だったので、ここで新しい技術を導入することはリリースを遅らせることにつながるのもありましたし、サービスをまずリリースすることが目的だったので、雑念を消して、寄り道せず、リリースまで駆け抜けることができました。
こうして、「sister ベータ版」は無事、12月中にリリースされたのでした。
エンジニアと個人開発
「sister」開発中は、エンジニアとしての自分と個人開発者としての自分が葛藤していました。
もちろん、個人開発者はエンジニアなんですけど、違う部分としては自分が開発しているサービスをどうしたいのかが最優先なのかなって、個人開発しながら思いました。エンジニアとしては、モダンな技術使いたい!流行ってる技術使いたい!って気持ちがあるけど、実際ユーザーが使う時って、裏で何使ってるか全く気にしてないよね?って部分です。
最後に
現在、「sister ベータ版」はリリースして3週間くらいたっていて、80人以上の方にご登録いただいています。
IT業界に特化した女性向けのサービスと言うこともあり、ターゲット層の母数自体が少ないため、最初は20ー30人くらいかな〜と予想していたのですが、予想より反響があったので、嬉しいです。改修がんばろう!ってなります。
ベータ版としてミニマムで早くリリースすることで、ユーザーの反応を早めにみれたのが、本当良かったかなと思ってます。
結論、どんなときも、何を目的をするかがいちばん大事。
そして、個人開発で学べることは多い!最後まで、読んでいただきありがとうございました!
おまけ
「sister ベータ版」もよろしくお願いします!
あなたのsisterを探そう
- 投稿日:2021-01-18T23:13:44+09:00
【Boostrap】なんで動かないの?jQueryちゃん?〜長い長いエラーとの戦い〜
この三日間悩みまくっていたこと
ポートフォリオを制作してエンジニア業界で働き始めた諸先輩方の投稿とgitのソースコードをいくつか拝見させて頂き、フレームワークを使った方が実装が早くなるということを知ったのですが、
自分のアプリケーションもレスポンシブ化したいということと、デザインセンスが皆無という自分のスペックに早めに見切りをつけ、早々にBootstrapを使用すると決めていました。先日バックエンドの実装がだいたい終わったので、さあBootstrapと戯れるぞと思い公式で目に止まったものを表示できるかどうか試してみることに。
良い感じのデザインですね!
これを表示するには・・・ほほう貼り付ければ良いのか。やってみよう。
どうしてこうなる??
公式からサンプルのソースコードを取ってきて貼り付けているだけなのにその通りに表示されない。
サンプルになってないやんけ???おかしいんとちゃうか???
そう思いながらもHTMLはしっかりと表示されていることに気付き、おかしいのはCSSだということがわかる。
よく見るとソースコードの部分に変なところがある。<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Jekyll v4.0.1"> <title>Carousel Template · Bootstrap</title> <link rel="canonical" href="https://getbootstrap.com/docs/4.5/examples/carousel/"> <!-- Bootstrap core CSS --> ⬅️①コメントアウトされているここと <link href="../assets/dist/css/bootstrap.css" rel="stylesheet"> <style> .bd-placeholder-img { font-size: 1.125rem; text-anchor: middle; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } @media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; } } </style> <!-- Custom styles for this template --> ⬅️②コメントアウトのここの部分 <link href="carousel.css" rel="stylesheet"> 〜以下略〜cssファイルを読みこんでいるところの記述がおかしい。
<link href="../assets/dist/css/bootstrap.css" rel="stylesheet">assets/dist/css/bootstrap.cssなんてファイルがアプリケーションにない。
別でダウンロードしたサンプルソースコードのファイルを覗いてみると、bootstrap-4.5.0-dist
├CSS
└ JSこんな構造になっていた。
CSSの中を覗いてみるとbootstrap.cssがある!よしよし
app>assets>styleseetsの中に入れました。
これで解決するかと思いきや何も変わらない。
記述のしかたがRailsのルールになっていないからだと気付き、
<link rel="~~~~
という記述から①<%= stylesheet_link_tag 'application', media: 'all'%>に変更しました。
コメントアウトされていた②のほうも同じ名前のものがあったので同じ場所に配置。
しかしファイルがあってもなくても特に見た目が変わらなかったのですが、
①で記述した'application'の部分の意味は「このアプリケーションの中の」という意味のようでディレクトリを細かく指定しなくてもきちんと表示されました。
これは推測ですが指定する範囲を広げることで②の部分のコードを削除しても問題ないよ、ということなのではないかなと考えました。さて解決したところでページ更新をしてみると、今度はJavaScriptが動かない。
問題部分(ページ最下部)
〜以下略〜 </main> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script>window.jQuery || document.write('<script src="../assets/js/vendor/jquery.slim.min.js"><\/script>')</script><script src="../assets/dist/js/bootstrap.bundle.js"></script></body> </html>同じ原理で<script src=~~~~がおかしい部分だと思いました。
こんな感じにしてみる
〜以下略〜 </main> <%= javascript_include_tag 'https://code.jquery.com/jquery-3.5.1.slim.min.js' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %></body> </html>結果、変わらない。
integrityとcrossoriginを取ってみたりしたけど変化なし。
この二つも調べてみたがあまり関係なさそう。ここで気づけば2時間ぐらいハマる。うーん。その後、turbolinksが邪魔しているのではないかとのことに辿り着き、gemから削除したりしてからCDN版に出ていたソースコードを貼り付ける。
すると表示された!<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>わかったこと
Bootstrap側が求めていることに自分が気付かずにあれでもない、これでもないと時間をかけてしまいました。
解決できたらくだらないことだったなと思うのですがやってる時は必死すぎて、あまり周りが見えていないことが多々あります。
明日からフロント部分を実装していきますが今日感じた変換していくという考え方と、あれこれ試して学んでいくことを忘れずにやっていこうと思いました。間違っている部分、改善点などありましたら教えて頂ければ幸いです。
参考にさせて頂きました
https://yurutsubu.com/rails-5-x-javascript-does-not-work-well-6745.html#i-3
ありがとうございました。
- 投稿日:2021-01-18T21:37:01+09:00
【Rails】raty.jsを使った評価機能を作る
raty.jsを使って、railsアプリに星画像付きの評価機能を実装する方法です。jqueryが必要です。
環境
$ rails -v Rails 5.2.4.4jqueryを導入
Gemfilegem 'jquery-rails'$ bundle installapp/assets/javascripts/application.js//= require jquery
jquery.raty.jsを作成
$ touch app/assets/javascripts/jquery.raty.js作成したファイルに、https://github.com/wbotelhos/raty/blob/master/lib/jquery.raty.js 内の記述をコピペ。その後
application.js
でrequire(jqueryより後に書く)app/assets/javascripts/application.js//= require jquery.raty.js
星の画像を配置
https://github.com/wbotelhos/raty/tree/master/lib/images から
star-on.png
、star-off.png
、star-half.png
をダウンロードして、app/assets/images
以下に配置。評価を保存するカラムを持つデータを作成
$ rails g migration addEvaluationToMovie evaluation:float
db/migrate/202101011111.rbclass AddEvaluationToMovie < ActiveRecord::Migration[5.2] def change add_column :movies, :evaluation, :float end end$ rails db:migrate
評価投稿画面を作成
今回は例として、お気に入りの映画のタイトルを投稿する画面で、評価も一緒に投稿できるようにしてみます。
app/views/movies/new.html.erb<h2>お気に入りの映画を投稿</h2> <%= form_with(model: movie, local: true) do |form| %> <div> <%= form.label :title %> <%= form.text_field :title %> </div> <div id="evaluate_stars"> <label>評価</label> </div> <div class="actions"> <%= form.submit %> </div> <% end %> <script> $('#evaluate_stars').raty({ starOn: "<%= asset_path('star-on.png') %>", starOff: "<%= asset_path('star-off.png') %>", starHalf: "<%= asset_path('star-half.png') %>", scoreName: 'movie[evaluation]' //登録するモデル名とカラム名を記述 }); </script>app/controllers/movies_controller.rbdef movie_params params.require(:movie).permit(:title, :evaluation) # evaluationを追加 end投稿した評価を表示
ここでは例としてindexページに評価を表示します。
app/views/movies/index.html.erb<h1>映画一覧</h1> <table> <thead> <tr> <th>タイトル</th> <th>評価</th> </tr> </thead> <tbody> <% @movies.each do |movie| %> <tr> <td><%= movie.title %></td> <td class="movie-evaluation" data-score="<%= movie.evaluation %>"></td> </tr> <% end %> </tbody> </table> <script> $('.movie-evaluation').raty({ readOnly: true, score: function() { return $(this).attr('data-score'); }, path: '/assets/' }); </script>参考
- 投稿日:2021-01-18T18:59:16+09:00
テーブルにカラムを追加する方法
今回はmigrateファイルの記述をして追加する方法を実行します。
migrateファイルを編集
まず、README.mdを参考にmigrateファイルを編集します。
README.md## items テーブル | Column | Type | Options | | ----------------- | ---------- | ----------------- | | name | string | null: false | | explanation | text | null: false | | category_id | integer | null: false | | state_id | integer | null: false | | delivery_price_id | integer | null: false | | prefectures_id | integer | null: false | | delivery_time_id | integer | null: false | | price | integer | null: false | | user | references | foreign_key: true | ### Association - belongs_to :user - has_one :purchase20210118070956_create_items.rbclass CreateItems < ActiveRecord::Migration[6.0] def change create_table :items do |t| t.string :name, null: false t.text :explanation, null: false t.integer :category_id, null: false t.integer :state_id, null: false t.integer :delivery_price_id, null: false t.integer :prefectures_id, null: false t.integer :delivery_time_id, null: false t.integer :price, null: false t.references :user, foreign_key: true t.timestamps null: false end end endrails db:rollback rails db:migrateの実行
記述が終わったら以下2点の実行
Terminal
% rails db:rollback
% rails db:migrate
最後に
テーブルの確認
これでテーブルの追加が完了です!
- 投稿日:2021-01-18T17:16:27+09:00
RSpec~Taskモデルのバリデーションテスト作成まで
RSpecとは
Rubyにおけるテスティイングフレームワークのことです。
Rubyでは標準で
Minitest
という別のフレームワークがあります。Ruby on RailsチュートリアルでもMinitest
でテスト方法を教わります。
ですが、実際に現場で使われているテスティイングフレームワークはRSpec
が圧倒的に多いようです。本記事では
RSpec
をインストールし、既に定義されているモデルに対してバリデーションテストするまでを記載します。使用技術のバージョン
・Ruby: 2.6.4
・Ruby on Rails: 5.2.3モデルの概要
今回取り扱うモデルは
user
モデルとtask
モデルです。[userモデル]
app/models/user.rbclass User < ApplicationRecord has_many :tasks, dependent: :destroy validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] } validates :email, uniqueness: true, presence: true end[taskモデル]
app/models/task.rbclass Task < ApplicationRecord belongs_to :user validates :title, presence: true, uniqueness: true validates :status, presence: true enum status: { todo: 0, doing: 1, done: 2 } end今回は
task
モデルのバリデーションをテストするため、user
モデルのバリデーションの説明は割愛します。
task
モデルから分かる通り、title
カラムとstatus
カラムにバリデーション(制限)がかけられています。
title
カラム
presence: true
=> 値が必ず格納されていなければならない
uniqueness: true
=> 格納されている値がユニークな存在でなければならない
status
カラム
presence: true
=> 値が必ず格納されていなければならない※
enum
についてですが、列挙型と呼ばれているもので、数値のカラムに対してプログラム上で別名を与えることができます。
status
はinteger
型が格納されていますが、「0がtodoのステータスで、1がdoingのステータスで、、、、」というように、人間にはstatus
の数値が何を表すのか理解しづらいです。そこでDB上では数値で扱うが、プログラマから見たら文字列で扱うというように設定できるenum
を使用するのです。詳しくはこちらを参照してください。導入手順
ここから少し長いですが、1つずつ段階ごとに手順を紹介します。
RSpecとFactoryBotをインストール
RSpec
は上述した通りテスティイングフレームワークです。
FactoryBot
とはRSpec
でテストする時にモデルのレコードを作成しテストで使用することができます。インストール手順は公式のREADMEの手順を参照しています。
RSpecの公式README
FactoryBotの公式README
Gemfile
の:development
グループと:test
グループの両方にrspec-rails
とfactory_bot_rails
を追加します。Gemfilegroup :development, :test do gem 'factory_bot_rails' gem 'rspec-rails', '~> 4.0.2' gem 'byebug', platforms: %i[mri mingw x64_mingw] endいつも通り
bundle install
$ bundle installそれに加えて、
$ rails generate rspec:install
を実行`rails generate rspec:install` Running via Spring preloader in process 31987 create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rbこれでインストールは完了です。
Task model specのファイルを作成
$ rails generate rspec:model task Running via Spring preloader in process 32051 create spec/models/task_spec.rb invoke factory_bot create spec/factories/tasks.rb念のため
task_spec.rb
のテストが実行可能かを確認しましょう。$ bundle exec rspec spec/models/task_spec.rb * Pending: (Failures listed here are expected and do not affect your suite's status) 1) Task add some examples to (or delete) /Users/sakidendaiki/Downloads/RUNTEQ/tasks/option_tasks/sample_app_for_rspec/spec/models/task_spec.rb # Not yet implemented # ./spec/models/task_spec.rb:4 Finished in 0.0053 seconds (files took 1.6 seconds to load) 1 example, 0 failures, 1 pendingtaskモデルとuserモデルのFactoryBotのテストデータを作成する
task
モデルのFactoryBot
をファイルを作成します。
モデルに対してFactoryBot
のファイルを作成する場合、$ rails g factory_bot:model モデル名と記述します。今回はタスクモデルのテストですので、
$ rails g factory_bot:model task
のコマンドを実行してみましょう。$ bin/rails g factory_bot:model task Running via Spring preloader in process 28632 identical spec/factories/tasks.rb次に
task
のFactoryBot
を以下のように記述します。spec/factories/tasks.rbFactoryBot.define do factory :task do sequence(:title, "title_1") content { "content" } status { :todo } deadline { 1.week.from_now } association :user end end上記を定義することで、
FactoryBot.create(:task)
で上で定義されたtask
モデルを呼び出すことができます。
コンソールで試してみましょう。$ FactoryBot.create(:task) (0.1ms) SAVEPOINT active_record_1 User Exists (0.7ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "user_1@example.com"], ["LIMIT", 1]] 略 => #<Task:0x00007f92a4342b08 id: 1, title: "title_1", content: "content", status: "todo", deadline: Mon, 25 Jan 2021 07:24:22 UTC +00:00, created_at: Mon, 18 Jan 2021 07:24:22 UTC +00:00, updated_at: Mon, 18 Jan 2021 07:24:22 UTC +00:00, user_id: 1>
sequence(:title, "title_1")
は一度FactoryBotを呼ばれると、title
カラムに対し"title_1"という文字列を格納します。
二度目に呼ばれる時は文字列は"title_2"となります。つまり、呼び出されるたびに末尾の数字に1を加えます。何故このようなことをするかというと、
title
カラムにはuniqueness: true
のバリデーションがあるためです。
title
カラムに値がかぶるのを防ぐために、呼び出されるたびにtitle
カラムの値を変えています。
sequence
メソッドはこのようにuniqueness: true
がある時に使用されます。
deadline
カラムで定義されている1.week.from_now
は文字通りの意味です。
一週間後の同時刻の時間を出力します。
本当にRailsって理解しやすいですよね。
association :user
は外部キー参照と非常に似ています。
task
モデルはuser
モデルと1:多の関係にあるので、user_id
カラムがあります。
association :user
とすることで、FactoryBot
のファイルで定義されているfactory :user
のテストデータを参照します。
Task
にはuser_id
も必要となることを踏まえてuser
のFactoryBot
も作成する必要があります。bin/rails g factory_bot:model user Running via Spring preloader in process 29285 create spec/factories/users.rb
user
モデルのFactoryBot
を記述spec/factories/users.rbFactoryBot.define do factory :user do sequence(:email) { |n| "user_#{n}@example.com" } password { "password" } password_confirmation { "password" } end end
sequence
メソッドの記載方法がtask
モデルで記載した時と違いますが、内容は同じです。
つまり、FactoryBot
のファクトリである:user
が呼び出されるたびに、n
の値がどんどん増えていくことで、これでテストデータの準備は完了です!
--format documentation を追記(テスト記述前その1)
RSpec
のテストを記述する前に、.rspec
ファイルに--format documentation
を追記しましょう。.rspec--require spec_helper --format documentationこれにより、テストの実行結果をドキュメント形式に変更します。
簡単にいうと、テスト結果が人間に読みやすくなります。config.include FactoryBot::Syntax::Methodsを追記(テスト記述前その2)
テストを記載する際、テストデータの呼び出し方は
FactoryBot.create(:task)
や、FactoryBot.build(:task)
といったように、必ずFactoryBot
と記載しなければいけません。しかし、テストデータを呼び出す際に毎回書くのは冗長的です。
そこでconfig.include FactoryBot::Syntax::Methods
をruby:spec/rails_helper.rb
に記載します。spec/rails_helper.rbconfig.include FactoryBot::Syntax::Methodsこうすることで、テストデータを呼び出す時、
create(:task)
というように、FactoryBot
の記述を省略できます。task_spec.rbにモデルバリデーションに関するテストを作成
いよいよテストを記述します。
spec/models/task_spec.rbrequire 'rails_helper' RSpec.describe Task, type: :model do describe 'validation' do it '全ての属性が適切に格納されていれば有効' do task = build(:task) expect(task).to be_valid expect(task.errors).to be_empty end it 'タイトルがなければ無効' do task_without_title = build(:task, title: "") expect(task_without_title).to be_invalid expect(task_without_title.errors[:title]).to eq ["can't be blank"] end it 'ステータスがなければ無効' do task_without_status = build(:task, status: nil) expect(task_without_status).to be_invalid expect(task_without_status.errors[:status]).to eq ["can't be blank"] end it '同じタイトルが重複していれば無効' do task = create(:task) task_with_duplicated_title = build(:task, title: task.title) expect(task_with_duplicated_title).to be_invalid expect(task_with_duplicated_title.errors[:title]).to eq ["has already been taken"] end it 'タイトルが別名であれば有効' do task = create(:task) task_with_another_title = build(:task, title: 'another_title') expect(task_with_another_title).to be_valid expect(task_with_another_title.errors).to be_empty end end endit '〜〜〜〜〜〜' doの箇所にテストの内容が記載しています。
1つ目のテスト'全ての値が適切に格納されていれば有効'は、テストデータをそのまま呼び出しテストしています。
テストデータはバリデーションを考慮して値が定義されているので有効であるはずです。
expect(task).to be_valid
とexpect(task.errors).to be_empty
は直感的に理解できるのではないでしょうか。
変数task
はbe_valid
(有効)であり、task.errors
はbe_empty
(空値)であることをテストしています。
もしバリデーションに引っかかっていたらtask.errors
には何かしらのエラーメッセージが格納されているはずです。
expect(task_without_title).to be_invalid
とexpect(task_without_title.errors[:title]).to eq ["can't be blank"]
はどうでしょうか。簡単に推測できると思います。テスト結果を確認
RSpec
でテストします。
$ bundle exec rspec
をターミナルに実行するだけです。$ bundle exec rspec Task validation is valid with all attributes is invalid without title is invalid without status is invalid with a duplicate title is valid with another title Finished in 0.14338 seconds (files took 1.39 seconds to load) 5 examples, 0 failuresバリデーションを削除してテストが失敗するか確認
本当にテストが正しいのかを確認するためにあえてテストを失敗させる方法があります。
例えば、2つ目のテスト'タイトルがなければ無効'が成功するのは、task
モデルのtitle
カラムに対してpresence: true
のバリデーションがあるためです。では、presence: true
を削除してみましょう。validates :title, uniqueness: true【テストの実行結果】
Failures: 1) Task validation is invalid without title Failure/Error: expect(task.errors[:title]).to include("can't be blank") expected [] to include "can't be blank" # ./spec/models/task_spec.rb:12:in `block (3 levels) in <top (required)>' Finished in 0.10893 seconds (files took 0.7813 seconds to load) 5 examples, 1 failure Failed examples: rspec ./spec/models/task_spec.rb:9 # Task validation is invalid without title成功していたテストが失敗したことで、自分の書いたテストが正しいテストであったことを証明できるのです。
- 投稿日:2021-01-18T17:03:01+09:00
railsでほぼ静的なページの作り方
引用先
Railsチュートリアル※ rails new appをしている状況で新しくページを追加するところからです
ターミナルでコマンド実行
まずは作業ディレクトリへ移動
cd ~移動後コマンド実行
$ rails generate controller <コントローラ名> <アクション名> <アクション名> ・・・ # 例 $ rails generate controller StaticPages home help上記コマンドを実行すれば
・コントローラの作成
・ルーティングの設定
・ビューファイルの作成
を自動で作成してくれるので非常に便利かつ簡単ですね!実際これだけでページは作れちゃいます。
新たにページを追加したいとなった時
例えば、新たにaboutページを作成したいとなった時に、手動でファイルを作成したりコードを書けば追加もできます。
手順
①ルーティングの設定
②コントローラにアクション追加
③ビューファイルの作成①ルーティングの設定
config/routes.rbRails.application.routes.draw do get 'static_pages/home' get 'static_pages/help' get 'static_pages/about' ←追加 end②コントローラにアクション追加
app/controllers/static_pages_controller.rbclass StaticPagesController < ApplicationController def home end def help end def about ←追加 end ←追加 end③ビューファイルの作成
app/views/static_pagesにabout.html.erbを右クリックで新規作成app/views/static_pages/about.html.erb<h1>About作成</h1> ・・・ ・・・ ・・・これだけでページの追加は完了です。
URLは末にstatic_pages/aboutをつければwebで開きます。
(https://~~~/static_pages/about)
- 投稿日:2021-01-18T16:52:03+09:00
rails学習 rakeタスク・cron・whenever
課題で行ったrakeタスク・cron・wheneverの使い方とその際のコード記述での覚えておきたいポイントを確認する。
rakeタスク
行いたい処理を定義することにとって、アプリケーションを起動せずに定義した処理を実行することができる
cron
「〇時に〇〇のコマンドを実行」というふうに時間によってコマンドを実行させることができる
whenever
cronをrubyの記述方法にして描きやすくしたもの。
schedule.rbにかいた記述をbundle exec whenever --update-crontab
することによってcronに処理が記述されるrakeタスクの書き方
今回rakeタスクに設定する処理は「公開待ちの中で、公開日時が過去になっているものがあれば、ステータスを「公開」に変更されるようにする」である
①まず、公開待ちの中でというところは「記事が公開待ちつまりArticleのstateがwait_publish」になっているものであるため
Article.wait_publish②次に①の中から公開日時が過去になっているものを探さないといけない
公開日時が過去ということはその記事のpublish_atの時間がTime.currentよりも少ないということになる
よって'published_at < ?', Time.current
となる。これをスコープでまとめたものがmodelに書いてある
scope :viewable, -> { published.where('published_at < ?', Time.current) }になる。つまりviewableをつけると公開日が過去になっている範囲から探すことができる。
よって公開待ちで公開日時が現在より小さい記事はArticle.wait_publish.viewableこうあらわすことができる
③stateが公開待ちで公開日時が現在よりも小さい(過去になっている)ものを
公開にする(つまりstateをpublishにする)Article.wait_publish.viewable.publishこうすれば公開になると思うがもう一つ重要なポイントがある。
Article.wait_publish.viewableで取得した記事は一つではないということ。
なので1つ1つにpublishをつけていかなければならない取得した複数の値1つ1つにpublishをつけていく場合
find_eachを使う
find_eachは取得した複数のレコードを繰り返し処理していく
また(&:published!)
とすることで1つ1つにpublish!をつけていくことができる。①②③を合わせて「公開待ちの中で、公開日時が過去になっているものがあれば、ステータスを「公開」に変更されるようにする」というコードは
Article.publish_wait.viewable.find_each(&:publish!)と書くことができる。
これをlib/tasks/article_state.rakeに記述するとrakeタスクが実行できるようになるlib/tasks/article_state.rakenamespace :article_state do desc '公開待ちの記事で公開日時が過去日付の場合、記事のステータスを「公開」に変更する' task change_publish: :environment do Article.publish_wait.viewable.find_each(&:publish!) end endarticle_stateコマンドをターミナルで実行するだけで処理が実行されるようになった
wheneverの書き方
上記で定義したarticle_stateコマンドを1時間に一回起動するようにcronを動かさなければならない、そのためにwheneverを使う(wheneverは簡単に言えばcronの翻訳機みたいなもの)
db/schedule.rb# Use this file to easily define all of your cron jobs. # # It's helpful, but not entirely necessary to understand cron before proceeding. # http://en.wikipedia.org/wiki/Cron require File.expand_path(File.dirname(__FILE__) + '/environment') # Example: rails_env = ENV['RAILS_ENV']|| :development set :environment, rails_env set :output, "#{Rails.root}/log/cron_log" # every 2.hours do # command "/usr/bin/some_great_command" # runner "MyModel.some_method" # rake "some:great:rake:task" # end # every :hour do rake 'article_state:change_publish' end # Learn more: http://github.com/javan/wheneverevery :hour do rake 'article_state:change_publish' endこの部分が1時間に一度article_stateのなかのchange_publishが実行されるコードになる
記述が終わったらbundle exec whenever --update-crontab
でcronに反映させる覚えておきたいポイント
enumの書き方・ポイント
enumに新しい情報をつける場合に記述方法に気をつける
article.rb#変更前 enum state: { draft: 0, published: 1} #NG enum state: { draft: 0, published: 1, publish_wait: 2 } #OK enum state: %i{ draft published publish_wait }OKコード方が綺麗で見やすい。綺麗なコード記述を心がけるようにする
enumの値でデータを取得するときに記述が楽になる
# NG article.state = 'publish_wait' Article.where(state: :publish_wait) # OK Article.publish_waitenumで定義していると
Article.where
でわざわざ探さなくてもArticle.publish_wait
のようにArticleの後にenumの値を入れるとその値に該当したデータを取得することができるようになるその他のポイント
・enumで定義されたものだと一目でわかる
・コードがスッキリするarticle_controller.rbの良い書き方・ポイント
updateアクションでの書き方を確認する
article_controller.rbdef update authorize(@article) #Point.1 if @article.assign_attributes(article_params) #Point.2 @article.assign_publish_state unless @article.draft? #Point.3 @article.save! flash[:notice] = '更新しました' redirect_to edit_admin_article_path(@article.uuid) else render :edit end end解説ポイント
Point.1 authorizeについて
Punditというgemを使うと認可機能である
authorize
が使えるようになる。
arthorize (@article)
とコントローラー内で定義するとそのコントローラーが@articleを使えるかどうかを確認してくれるというもの。今回
autherize(@article)
で@article
が使えると許可されたため、@article
の定義を書かなくても@article
を使えるようになったPoint.2 assign_attributes(article_params)を使う理由
assign_attributes(article_params)メソッドはattributesを変更するためのメソッドである。
一見update(article_params)とほとんど一緒だが一つ異なったポイントがある。それがassign_attributesはDBにデータを保存しない。updateはDBにデータを保存する
である。
もしupdateを使うと以下のようになるupdateの場合
article_controller.rbdef update authorize(@article) if @article.update(article_params) #updateでDBにアクセス @article.assign_publish_state unless @article.draft? @article.save! #saveでさらにDBにアクセス flash[:notice] = '更新しました' redirect_to edit_admin_article_path(@article.uuid) else render :edit end endassign_attributeの場合
article_controller.rbdef update authorize(@article) if @article.assign_attributes(article_params) #DBにアクセスしない @article.assign_publish_state unless @article.draft? @article.save! #saveでDBにアクセス flash[:notice] = '更新しました' redirect_to edit_admin_article_path(@article.uuid) else render :edit end end上記のようにどのみちsaveで一回DBにアクセスしておりupdateは余計にDBにアクセスするので処理が重たくなる原因になる。なので基本的にはupdateメソッドとsaveメソッドが共存することはない。
Point.3 assign_publish_stateについて(if文のなかにif文)
このコントローラーは公開日が現在時刻よりも未来ならば「公開待ち」、公開日が現在時刻または現在時刻よりも過去ならば「公開」にstateを変えるという条件分岐の処理が必要になってくる。
単純に考えれば
article_controller.rbif @article.assign_attributes(article_params) unless @article.draft? @article.state = if @article.published_at <= Time.current :published else :publish_wait end end @article.save! flash[:notice] = '更新しました' redirect_to edit_admin_article_path(@article.uuid) else #省略 endifのなかにunlessやさらにifが入っておりコントローラーがわかりづらく行数も増えていってしまう(Fatcontroller)
なので@article.state = if @article.published_at <= Time.current :published else :publish_wait endの部分を1つのメソッドとして定義し簡略化を図る
article_controller.rbif @article.assign_attributes(article_params) @article.assign_publish_state unless @article.draft? @article.save! flash[:notice] = '更新しました' redirect_to edit_admin_article_path(@article.uuid) else #省略 endapp/model/article.rbdef assign_publish_state self.state = if self.published_at <= Time.current :published else :publish_wait end endrspecのポイント
元のstateから日時によってstateが変わるかどうかのテストがしたい
元のstateが下書きの場合、公開待ちの場合、公開の場合などから条件分岐をやっているとキリがなくなる。なので元のstateはランダムにしておきそこから日時によって変わる条件分岐を記述していった方がコードを書かなくて済むspec/factories/articles.rb# 例 trait :random_state_article # NG state { Random.rand(1..2) } # OK state { Articles.states.values.sample } endランダムにするtraitはRandom.rand(1..2)ではなくArticles.states.values.sampleにする
- 投稿日:2021-01-18T15:26:30+09:00
Docker+Railsにdotenvを導入して環境変数を管理する
実現したいこと
Dockerを使ったRails開発で、公開してはならないAPIキーやパスワードなどを別ファイルでGit管理する方法。
大雑把な順番
- gemを追加
- .envファイルにプロジェクトで使う環境変数を記述
- .gitignoreに.envファイルを含める
- erbテンプレートに従って環境変数を渡す
使用したバージョン
- Rails6
- ruby 2.7.2
手順
gem 'dotenv-rails'を追加
Gemfilegem 'dotenv-rails'コンテナ内にbundle installを行う
bashdocker-compose run コンテナ名 bundle install
docker-compose.ymlを編集
yml内にenv_fileと書くことで環境変数を管理するファイルを読み込む設定ができる
docker-compose.ymlcode:docker-compose.yml web: 省略 env-file: - .env(環境変数を管理したいファイル名を記述)コンテナをビルドする
docker-compose.ymlを編集したので改めてビルドする
docker-compose build.envファイルに設定したい環境変数を書く
.envMAP_API_KEY="発行したAPIキー" PASSWORD="パスワード".envファイルをgitignoreに追加
.gitignore/.env
viewのerbテンプレートで環境変数として渡す
index.html.erb以上省略 <script src="https://maps.googleapis.com/maps/api/js?key=<%=ENV['MAP_API_KEY']%>&callback=initMap" async defer></script>ここで重要なのがerbテンプレートに従って、
<%=ENV['']%>
と書くという事。
これをせずに、ENV['MAP_API_KEY']
と書いて渡しており、上手くviewで表示できず、2日ほど詰まっていた。
- 投稿日:2021-01-18T13:30:24+09:00
params flash sessionの寿命を考えたことがあるか。
sessionとかflashってどれくらいの間データを保持しているんだろう?
railsでウェブサイトを作っている時、定義した変数等は画面を遷移した時に、すぐに消えてしまいます。
@user等のインスタンス変数は一度の画面遷移で死んでしまう経験をしたことが多いと思います。
sessionやflashは長生きすると聞いていたので、寿命を見てみたい!という好奇心から記事を書きました。実験開始
今回は自分のPFに無理やり作ったので少し見にくくなっています。
①コントローラにsession flash paramsの出場選手を定義
とっつきにくいイメージかもしれませんが、案外カジュアルに定義できちゃいます。
コントローラdef top session[:life]="私はsession。生きています" flash[:life]="私はflash。生きています。" params[:life]="私はparams。生きています。" end②ビューページに選手達を表示する記述を追加
ビュー<%= session[:life]%><br> <%= flash[:life]%><br> <%= params[:life]%><br> <%= yield %>③画面遷移を何度かして、どれくらい生きられるかを見る
0遷移目
1遷移目
2遷移目
結言
名前 寿命 params 1遷移キル flash 2遷移キル session キルするまで生きている sessionにユーザーのログイン状態が保存されていれば、
ずっとログイン状態を保っていることにも納得できます。
ログアウトはsessionに保存しているユーザーのログイン状態をdestroyしていたんですね。
- 投稿日:2021-01-18T13:08:34+09:00
必ず役に立つ!Ruby on Railsでの開発で使えるデバッグコード
こ~れなんなん、これなんなん
こんにちは。
開発で最も必要とされる技術ってデバッグ力だと思っています。特にバグを大量生産することが得意な僕にとって、デバッグ力はとても大事。
そこで、普段開発で使ってるデバッグ用のコードをまとめておこうかと思います。
デバッグコード
Ruby on Railsで使える、デバッグで役に立ちそうなコードの一覧を書いていきます。なお、Rubyで使えるコードもあります。
byebug起動時や
rails console
で使用することを想定しています。byebugの使い方よく分からんて人は以下の記事で使えるようにしてください。
https://web-nari.net/2018/03/16/byebug/メソッド一覧
hoge
という変数の参照できるメソッドの中で、メソッド名にmethod_name
が含まれるメソッドの一覧を返します。hoge.methods.select {|name| name.to_s.include?("method_name") }メソッド定義場所
hoge
という変数の参照できるfoo
メソッドの定義されてるクラスを返します。hoge.method(:foo)ソースファイルのパス
hoge
という変数の参照できるfoo
メソッドの定義されてるファイルのパスを返します。hoge.method(:foo).source_locationコールバック検索
Hoge
というクラスに定義されてるsave時のコールバックの一覧を返します。cb.kind.eql?
の引数にafterかbeforeを与えると、それぞれafter_コールバック関数、before_コールバック関数に定義されてるコールバックを返します。Hoge._save_callbacks.select { |cb| cb.kind.eql?(:after) }.map { |cb| cb.filter }その他のコールバック関数
save
だけではなくて以下のコールバック関数がそれぞれ利用可能。=> [:validate, :validation, :initialize, :find, :touch, :save, :create, :update, :destroy, :commit, :rollback, :before_commit, :before_commit_without_transaction_enrollment, :commit_without_transaction_enrollment, :rollback_without_transaction_enrollment]例えば
after_validation
のコールバックの一覧が知りたい場合は、以下のようにすればいい。Hoge._validation_callbacks.select { |cb| cb.kind.eql?(:after) }.map { |cb| cb.filter }クラス化
:string
という名前から該当するクラスに変換します。例の場合だと、String
が返ってきます。該当するクラスがない場合はNameError: uninitialized constant
が発生します。:string.to_s.classify.constantizeクラスの確認
hoge
というオブジェクトのクラスを確認する。hoge.class継承元を検索する
hoge
というオブジェクトの継承クラスやそのさらに祖先も検索できる。hoge.superclass # 親クラスを検索 hoge.ancestors # 祖先を検索インスタンス変数の確認
hoge
というオブジェクトが持ってるインスタンス変数を返します。クラス変数はclass_variables
で参照可能。hoge.instance_variablesまとめ
タイトルの勢いの割に書いてみたらすんごい少なかった。
これからも追加していきます。
- 投稿日:2021-01-18T12:54:05+09:00
ブラウザのセッションとCookieの違いとRailsのsession,cookiesメソッドの違いについて
はじめに
この記事はRails初学者が書いています。
ご容赦ください。また、間違っている点があれば指摘していただければ幸いです。
それぞれの違い
ブラウザのセッション
あるサイトに接続してから切断するまでの一連の流れのこと。
ブラウザのCookie
ユーザー側のブラウザに保存しておく情報のこと。
一時Cookieと永続Cookieがある。
前者はブラウザ終了時に自動で消える。
後者はユーザー側、サーバー側のどちらかで手動で消去するまで残っている。HTTPプロトコルではログインしているという情報を記憶しておくことができない。
そのため、こういった機能で保存しておき、ページ移動の際に毎回Cookieを確認する必要がある。Railsのsessionメソッド
ブラウザの一回のセッションに必要なデータを残しておくためのもの。
具体的には、ブラウザの一時Cookieに暗号化したデータを保存するメソッドRailsチュートリアルでは、ここにログインしているユーザーIDを保存している。
ユーザーIDを保存しておくことでブラウザを閉じるまではログイン後のページを移動することができる。チュートリアルには、「このデータは攻撃者に盗まれたとしてもログインすることはできないから安全である。」と書いてあるが、
ログイン中であるという情報は残っているので、これを盗めばユーザーになりすますことができるのではないか思う。
僕の勉強不足でここはわからない。Railsのcookiesメソッド
ブラウザ終了後にも残しておきたいデータを保存しておくためのもの。
具体的には、ブラウザの永続Cookieに、暗号化したデータを保存するメソッドチュートリアルでは、RememberMeと呼ばれるトークンを保存している。
これを使うと、ユーザー名とパスワードを入力しなくてもログインすることができてしまう。
そのため、扱いには注意する必要がある。参照
- 投稿日:2021-01-18T12:47:24+09:00
0からRuby on Railsの環境構築【Cloud9】(Rubyのバージョン変更からRailsのインストールまで)
Ruby on Railsの環境構築(Cloud9)
本記事はRuby on Railsの環境構築を初心者の方でも迷わずできるように解説した記事です。
Ruby on Railsは、広範囲にわたる開発で使用されています。日本のスタートアップでも採用されていることが多く、食べログやnote、会計ソフトのfreeeなどもRuby on Railsによって開発されています。
そんなRuby on Railsですが、2019年8月にバージョン6.0がリリース、2020年12月にはバージョン6.1がリリースされました。また、Ruby自体も2020年12月にバージョン3.0に大型アップデートされ新しい機能が追加されています。
ここでは、Ruby 3.0とRuby on Rails 6.1.1の環境構築方法を説明していきます。
環境
- Cloud9 (Mac、Windowsどちらも対応可能です)
- Macでの環境構築方法の記事も公開しています。
- Amazon Linux2
- Cloud9の環境を立ち上げる際に、Amazon Linux2を選択している必要があります。
Rubyのバージョンを確認する
以下のURLからコンソールにサインインして、Cloud9を立ち上げましょう。
https://us-east-2.console.aws.amazon.com/console/
Cloud9上のTerminalを立ち上げて、以下のコマンドを実行してRubyのバージョンを確認しましょう。
ruby -v
以下のように数字が表示されます。
ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]ここではRubyの環境が「2.6.3」であることがわかります。
Rubyのバージョンを変更する
それではここからRubyのバージョンを変更していきます。
Rubyのバージョン管理ツールであるrvmを使います。
以下のコマンドでインストール済みのRubyのバージョンを確認します。
rvm list実行結果は以下になります。
rvm list =* ruby-2.6.3 [ x86_64 ] # => - current # =* - current && default # * - defaultまた、以下のコマンドでインストール可能なRubyのバージョンを一覧表示します。
rvm list known実行結果で、自分のインストールしたいバージョンがあることを確認してください。
rvm list known //====略==== # MRI Rubies [ruby-]1.8.6[-p420] [ruby-]1.8.7[-head] # security released on head [ruby-]1.9.1[-p431] [ruby-]1.9.2[-p330] [ruby-]1.9.3[-p551] [ruby-]2.0.0[-p648] [ruby-]2.1[.10] [ruby-]2.2[.10] [ruby-]2.3[.8] [ruby-]2.4[.6] [ruby-]2.5[.5] [ruby-]2.6[.3] ruby-head //====略====ここでインストールしたいバージョンがない場合は、rvmのアップデートをおこなう必要があります。
アップデートをするには、以下のように
get
コマンドを実行します。rvm get latestrvm get latest //====略==== Thanks for installing RVM Please consider donating to our open collective to help us maintain RVM. Donate: https://opencollective.com/rvm/donate RVM reloaded!インストールしたrvmのバージョンを確認しましょう。
rvm -v rvm 1.29.10 (1.29.10) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]再度インストールしたいバージョンがあるかを確認しましょう。
rvm list known //====略==== # MRI Rubies [ruby-]1.8.6[-p420] [ruby-]1.8.7[-head] # security released on head [ruby-]1.9.1[-p431] [ruby-]1.9.2[-p330] [ruby-]1.9.3[-p551] [ruby-]2.0.0[-p648] [ruby-]2.1[.10] [ruby-]2.2[.10] [ruby-]2.3[.8] [ruby-]2.4[.10] [ruby-]2.5[.8] [ruby-]2.6[.6] [ruby-]2.7[.2] [ruby-]3[.0.0] #←インストールしたいバージョン ruby-head //====略====次に、以下のコマンドを実行してRubyのバージョンをインストールします。
<version>
の部分には、インストールするバージョンを指定してください。rvm install <version>
以下のように表示されれば正常にインストールされています。
ここでは
3.0
を指定しています。rvm install 3.0 //===略=== ruby-2.7.2 - #generating global wrappers....... ruby-2.7.2 - #gemset created /home/ec2-user/.rvm/gems/ruby-2.7.0 ruby-2.7.2 - #importing gemsetfile /home/ec2-user/.rvm/gemsets/default.gems evaluated to empty gem list ruby-2.7.2 - #generating default wrappers.......以下のコマンドで使用するバージョンを指定しましょう。
rvm use <version> rvm use 3.0.0最後に以下のコマンドで、先ほど指定したバージョンが表示されることを確認してください。
ruby -v ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]以上でRubyのバージョン変更は終了です。
Bundlerをインストール
Rubyをインストールしたら、次にBundlerをインストールします。
RubyではGemというライブラリを使いパッケージを管理しています。
Gemコマンドで簡単にインストールやアンインストールができますが、複数のGemを使うとGem同士で依存関係が生まれ、バージョン違いによる不具合が出てきます。
そこで、BundlerでGemのそれぞれのバージョンを正確に追跡し管理して、Rubyプロジェクトに一貫した環境を提供します。
それではBundlerをインストールしましょう。以下のコマンドを入力します。
gem install bundler
コマンドを実行するとインストールが始まります。
インストールされたBlundlerのバージョンを確認しましょう。
次のコマンドを入力します。
bundler -v
コマンドの実行後、次のように表示されます。
bundler -v Bundler version 2.1.4
「version 2.1.4」とバージョン情報が表示されました。
これでBlundlerがインストール済みであることを確認できました。
yarnをインストール
次にyarnをインストールします。
yarnはJavaScriptのライブラリの利用に必要なパッケージマネージャです。
yarnと互換性のあるnpmというパッケージマネージャもあります。しかし、Rails6ではWebpackerが標準になったことにより、yarnが必要です。
それでは、yarnのインストールを行いましょう。
次のコマンドを入力します。
npm i -g yarn
yarnが実際にインストールされたかを確認するために、次のコマンドを入力します。
yarn -v
コマンドを実行すると、次のように表示されます。
yarn -v 1.22.10
ここでは「
1.16.0
」と表示されました。バージョン情報が表示されていれば、yarnがインストールされたことが確認できます。Ruby on Railsのバージョンを確認する
ここからはRailsのバージョンを確認していきます。
Cloud9のTerminalで以下のコマンドを実行してRailsのバージョンを確認しましょう。
rails -v
以下のようにデフォルトで指定されるRailsのバージョンが表示されます。
rails -v Rails 5.0.0
rails new
を実行する際にバージョンを指定しないとこのバージョンでアプリケーションが作成されます。次に、インストールされているRailsのバージョンを確認しましょう。
以下のコマンドを実行して下さい。
gem list rails以下のように表示されるので、railsの項目を確認しましょう。
「5.0.0」がインストールされていることがわかるかと思います。gem list rails *** LOCAL GEMS *** rails (5.0.0) rails-dom-testing (2.0.3) rails-html-sanitizer (1.3.0) sprockets-rails (3.2.2)Ruby on Railsのインストール
いよいよ、Railsをインストールします。
Railsのインストール時に「
-v バージョン番号
」とバージョンを指定してインストールできます。今回はバージョン「
6.1.1
」をインストールします。次のコマンドを入力します。
gem install rails -v 6.1.1コマンドを実行すると、インストールを開始します。インストールの完了までに数分かかることがあります。
Railsをインストールしたら、Railsのバージョンを確認するために次のコマンドを入力します。
rails -v
コマンドを実行すると、次の画面が表示されます。
rails -v Rails 6.1.1
「
Rails 6.1.1
」と表示されました。これで無事にRuby on Railsのインストールが完了しました。
※本記事はTechpitの教材を一部修正したものです。
参考
- 投稿日:2021-01-18T11:28:50+09:00
Rails 記事詳細リンクは、ループ内でしか機能しない?
背景
Page#homeで記事一覧(Posts#index)を表示し、そこに記事詳細(Posts#show)をリンクを貼った。そのときに学んだ事の備忘録。
結論
記事詳細リンクはこう貼った。(成功したコード)
<% if @posts.any? %> <% @posts.each do |p| %> #記事詳細のリンク↓ ++<%= link_to post_path(p), class: 'text-body' do %> <div class="card mt-4"> <%= image_tag p.image.url, class: 'img-fluid' %> <div class="card-body"> <h5 class="card-title"><%= p.title %></h5> <p class="card-text"><%= p.tag %></p> </div> </div> <% end %> <% end %> <% end %>
エラーが起きたコード
↓Couldn't find Post without an ID / ActiveRecord::RecordNotFound
<% if @posts.any? %> ++ <%= link_to post_path(post), class: 'text-body' do %> <% @posts.each do |p| %> <div class="card mt-4"> <%= link_to post_path(p), class: 'text-body' do %> <%= image_tag p.image.url, class: 'img-fluid' %> <div class="card-body"> <h5 class="card-title"><%= p.title %></h5> <p class="card-text"><%= p.tag %></p> </div> </div> <% end %> ++ <% end %> <% end %>なぜこれでワークしたのか?
restfulのこのコードは、
post_path(post)繰り返し処理の
<% @posts.each do |post| %>|post|からきている。
↓
繰り返し処理の外で、<%= link_to post_path(post), class: 'text-body' do %>と書くと、(post)の意味がコンピュータにはわからなくなる。
もう一度うまく行ったコード
<% if @posts.any? %> <% @posts.each do |p| %> #記事詳細のリンク↓ ++<%= link_to post_path(p), class: 'text-body' do %> <div class="card mt-4"> <%= image_tag p.image.url, class: 'img-fluid' %> <div class="card-body"> <h5 class="card-title"><%= p.title %></h5> <p class="card-text"><%= p.tag %></p> </div> </div> <% end %> <% end %> <% end %>この記事を参考にしました。https://skillhub.jp/courses/158/lessons/835
さいごに
本当にループ内でしか記事詳細は機能しないのかはわかりませんが(ユーザープロフィールなどでは、ループは記述していなくても、記事詳細のリンクはワークしている。)、とりあえずうまく行っているので、深くは追求しません。
- 投稿日:2021-01-18T09:09:13+09:00
Ruby on Railsのconfig設定
この記事ではRails6.1のconfig設定をメモしています。
config/application.rb
タイムゾーンをローカルに設定する
config.time_zone = 'Tokyo' config.active_record.default_timezone = :localconfig/puma.rb
developmentではlocalhost:3000、productionではunix socketを使用する
if ENV.fetch("RAILS_ENV", "development") == 'production' bind "unix://#{Rails.root}/tmp/sockets/puma.sock" else port ENV.fetch("PORT") { 3000 } endconfig/environments/production.rb
キャッシュにredisを使用する
cache_servers = %w(redis://localhost:6379/1) config.cache_store = :redis_cache_store, { url: cache_servers, connect_timeout: 30, read_timeout: 0.5, write_timeout: 0.5, reconnect_attemps: 1, error_handler: -> (method:, returning:, exception:){ Raven.capture_exception exception, level: 'warning', tags: { method: method, returning: returning } } }セッションにredisを使用する
config.session_store :redis_store, servers: [{ host: 'localhost', port: 6379, db: 0, namespace: 'sessions', }], key: "_session_name_", expire_after: 30.minutesRailsでredisを使用するためGemfileにgemを追加する
Gemfilegem 'redis-rails'config/webpacker.yml
webpackを使用している場合はhtmlの変更も検知できるようにする?
ここの設定は検討が必要extensions: ... - .html ...
- 投稿日:2021-01-18T07:59:08+09:00
不正な config/master.key が原因でRailsの起動に失敗するケースとその対処方法
発生する問題
git cloneでローカルに持ってきたRailsアプリを、
rails s
やrails c
で起動しようとすると以下のようなエラーが発生する。$ rails s => Booting Puma => Rails 6.1.0 application starting in development => Run `bin/rails server --help` for more startup options Exiting /Users/jnito/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.0/lib/active_support/message_encryptor.rb:203:in `rescue in _decrypt': ActiveSupport::MessageEncryptor::InvalidMessage (ActiveSupport::MessageEncryptor::InvalidMessage)エラーの原因
config/master.key
の鍵がconfig/credentials.yml.enc
用の鍵と一致していないため、例外が発生してRailsが起動しなくなった。もう少し詳しく
config/master.key
は通常、.gitignore
に追加してソースコード管理の対象外とするファイルである。そのため、git cloneしてもconfig/master.key
はローカルにはコピーされない。その状態で「
credentials.yml.enc
を編集したい」と思ってEDITOR="vi" bin/rails credentials:edit
のようなコマンドを実行すると、以下のようなメッセージとともに、config/master.key
が新規に作成されてしまう。(加えて、credentials.yml.enc
の編集もできない)$ EDITOR="vi" bin/rails credentials:edit Adding config/master.key to store the encryption key: 6eb4ad(以下略) Save this in a password manager your team can access. If you lose the key, no one, including you, can access anything encrypted with it. create config/master.key Couldn't decrypt config/credentials.yml.enc. Perhaps you passed the wrong key?ただし、新規に作られた
config/master.key
はgit cloneしてきたconfig/credentials.yml.enc
の鍵ではないため、Rails起動時に復号化に失敗して例外(ActiveSupport::MessageEncryptor::InvalidMessage)が発生してしまう。対処方法
状況によって対処方法は異なる。
git cloneする前にcredentials.yml.encが使われていた場合
開発チーム内で鍵の共有方法が決められているはずなので、その運用ルールに従って適切な
config/master.key
を自分のローカルに配置する。credentials.yml.encが今まで使われていなかった場合
自動的に作られた
config/master.key
を削除すればRailsが起動するようになる。今後も
credentials.yml.enc
を使わないのであれば、config/credentials.yml.enc
ごと削除するのも一手。逆に、これから
credentials.yml.enc
を使っていく(=自分がチーム内で最初の利用者になる)場合は、新規にconfig/credentials.yml.enc
を作り直した上でcredentials/master.key
をチーム内で適切に管理・共有する必要がある。
(ただし、どこかから適切なconfig/master.key
を入手できれば新規に作り直す必要はない)# 既存のcredentials.yml.encを削除 $ rm config/credentials.yml.enc # credentials.yml.encとmaster.keyを新規に作成して編集する $ EDITOR="vi" bin/rails credentials:edit Adding config/master.key to store the encryption key: 917c31e316061bcdd909754a56897f61 Save this in a password manager your team can access. If you lose the key, no one, including you, can access anything encrypted with it. create config/master.key File encrypted and saved.動作確認環境
- Rails 6.1.0
- 投稿日:2021-01-18T07:56:43+09:00
SNS認証時のエラーについて
経緯
ウィザード形式のユーザー登録機能実装後、
SNS認証を追加するために
registrations_controller内に必要な記述をした。
(userモデルは記述済み。)controllerclass Users::RegistrationsController < Devise::RegistrationsController #一部省略 def create if params[:sns_auth] == 'true' pass = Devise.friendly_token params[:user][:password] = pass params[:user][:password_confirmation] = pass end super #ここまでがSNS認証実装に追記した記述 @user = User.new(sign_up_params) #ここからウィザード形式のための記述 unless @user.valid? render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password] @profile = @user.build_profile render :new_profile end #一部省略 endエラー内容
新規登録を行ってみたところ下記のようなエラーメッセージが出る。
AbstractController::DoubleRenderError in Users::RegistrationsController#create同一アクション内にrenderもしくはredirectが複数回使われていますよ!という内容。
renderの後にand returnで処理を終わらせているのになぜ?となる。。エラー原因
SNS認証を実装するために追記した記述の最後にある
superによるもの。
(superとは、継承元のクラスにある、superを記述したメソッドと同名のメソッドを呼び起こすことができる。)呼び起こし先は下記である。
(https://github.com/heartcombo/devise/blob/master/app/controllers/devise/registrations_controller.rb) 参照元RegistrationsControllerclass Devise::RegistrationsController < DeviseController # 一部省略 def create build_resource(sign_up_params) resource.save yield resource if block_given? if resource.persisted? if resource.active_for_authentication? set_flash_message! :notice, :signed_up sign_up(resource_name, resource) respond_with resource, location: after_sign_up_path_for(resource) else set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}" expire_data_after_sign_in! respond_with resource, location: after_inactive_sign_up_path_for(resource) end else clean_up_passwords resource set_minimum_password_length respond_with resource end end # 一部省略 endこの中のrespond_withがどうやらエラーの原因みたい。
respond_withは、renderやredirectの役割を持っているため。
よってsuperで呼び起こしたメソッド内でrender or redilectしようとしたけど、その後の記述でまたrenderがあり
どっち?となりエラーが起きたと考えられる。
- 投稿日:2021-01-18T01:44:41+09:00
Rails 検索機能の実装
Rails で検索機能を実装
View に form_tag で検索窓を設置
<div class="search_field"> <p>検索:</p> <%= form_tag(posts_path, :method => 'get') do %> <%= text_field_tag :search, "", {class: "search_field_type"}%> <%= submit_tag 'Search', :name => nil, class: "search_field_btn" %> <% end %> </div>form_tag(posts_path, :method => 'get') の path の部分はクラス名によって任意に変更してください。
Model に検索のためのメソッド search を設定
post.rb
def self.search(search) if search Post.where(["title LIKE ?", "%#{search}%"]) else Post.all end endtitle カラムの中の一部でも検索した文字列が該当すればヒットするように設定。
詳しくは SQL の where 句を調べれば何となく設定している意味わかります。
https://www.wakhok.ac.jp/biblion/1994/DB/subsection2.4.3.5.html
Controller の調整
posts_controller.rb
def index @posts = Post.all.includes(:user).order(id: "DESC").search(params[:search]) endこれで検索にヒットした記事が @posts に入る。
あとは View 側で一覧表示処理をすれば OK です。
- 投稿日:2021-01-18T01:39:57+09:00
【Ruby on Rails】Rails Tutorialを全部終えたので、追加で「ストック機能」を実装してみた
はじめに
Railsで「qiita」のような記事をストックする機能を実装したので、その方法を備忘録としてまとめておきます。
(基本的にはいいね機能と同じやり方)※また、以下に記載する方法は前提として「Rails tutorial」を最後まで終えた程で記載させていただきます。
実装すること
・記事をストックするボタンを作成する。
・記事がどれくらいストックされているか表示する。
・ストックした投稿をストック一覧ページで確認できるようにする。Stockモデルを作成する
ストック機能を実装するにあたって、最初のステップとして
Stockモデル
を作成します。$ rails g model Stockカラムは2つ作成します。
・post_id
カラム
・user_id
カラム
マイグレーションファイルは以下のように修正します。xxxxxxxxxxxxx_create_stocks.rbclass CreateStocks < ActiveRecord::Migration[6.0] def change create_table :stocks do |t| t.references :user, foreign_key: true t.references :post, foreign_key: true t.timestamps end add_index :stocks, [:user_id, :post_id], unique: true end end$ rails db:migrate2つのカラムは、それぞれ外部キーとして追加しておきます。
このテーブルは中間テーブルとして活用し、誰がどの投稿をストックしたかという情報を格納しておきます。
また、user_id
カラムとpost_id
カラムの組み合わせが一意になるようにインデックスを設定しておきます。アソシエーションを設定する
以下のようにアソシエーションを設定します。
stock.rb
にはアソシエーションのついでにバリデーションも追加しておきます。user.rb
user.rbhas_many :stocks, dependent: :destroypost.rb
post.rbhas_many :stocks, dependent: :destroystock.rb
stock.rbbelongs_to :user belongs_to :post validates :user_id, presence: true validates :post_id, presence: truestockコントローラを作成する
次に
stockコントローラ
を作成します。$ rails g controller Stocksコントローラに処理を加える
stocks_controller.rb
stockコントローラ
を作成したら、今回の実装に必要なアクションを定義しておきます。stocks_controller.rbclass StocksController < ApplicationController def create @stock = current_user.stocks.create(post_id: params[:post_id]) redirect_back(fallback_location: root_path) end def destroy @stock = Stock.find_by( post_id: params[:post_id], user_id: current_user.id ) @stock.destroy redirect_back(fallback_location: root_path) end def index @stocks = Stock.where(user_id: current_user.id) end endposts_controller.rb
次に
postコントローラ
のshowアクションの部分に変数を追加します。
post/showページにストックボタンを表示する為です。posts_controller.rbdef show @post = Post.find_by(id: params[:id]) @stock = Stock.find_by( user_id: current_user.id, post_id: @post.id ) endルーティングを設定する
次に必要なルーティングを追加していきます。
routes.rb
routes.rbresources :stocks, only:[:create, :destroy, :index]今回はストックする機能(create)とストックを解除する機能(destroy)の他にストックした記事を表示する為の「index」も追加しておきます。
ストックしているかどうかの確認の処理と加える
ユーザーが記事に対して、すでにいストックをしているのかどうかを確認することができるように
stock?
メソッドを定義しておきます。user.rb
user.rb#ユーザーがすでにストックしているか確認する def stock?(post) self.stocks.exists?(post_id: post.id) endViewを追加&変更する
ストックボタンを実装する為に、まずはパーシャルを3つ作成していきます。
shared/_stock.html.erb
_stock.html.erb<%= form_with(url:stocks_path, local: true) do |f| %> <div><%= hidden_field_tag :post_id, @post.id %></div> <%= f.submit "ストック", class:"btn-sm" %> <% end %>shared/_unstock.html.erb
_unstock.html.erb<%= form_with(url: stock_path(@stock), html: {method: :delete}, local: true) do |f| %> <div><%= hidden_field_tag :post_id, @post.id %></div> <%= f.submit "ストックをやめる", class:"btn-sm" %> <% end %>shared/_stock_form.html.erb
_stock_form.html.erb<% if current_user.stock?(@post) %> <%= render "shared/unstock" %> <% else %> <%= render "shared/stock" %> <% end %> <%= @post.stocks.count %>posts/show
ボタンを追加したいところに以下パーシャルを追加します。
show.html.erb<%= render "shared/stock_form" %>stocks/index.html.erb
indek
ページを新しく追加し、ストックした記事を表示させます。index.html.erb<h1>ストック一覧</h1> <h2>あなたがストックしたポートフォリオの一覧です。</h2> <% @stocks.each do |stock| %> <%= link_to stock.post.name, post_path(stock.post) %> <% end %>完成画
おわり
以上で終了です。
機能の実装方法を記述しているので、デザインは全く気にしていません。
デザインはお好みで付け足してみてください。ソースコードはこちら
ポートフォリオ作成中のものですが。以下にあります。
https://github.com/iittaa/Sharing_Portfolio