- 投稿日:2020-02-24T23:04:06+09:00
【Rails】掲示板アプリでTopicとPostを紐づける方法(アソシエーション)
はじめに
現在、ポートフォリオとして掲示板アプリを作成しており、題目にある「TopicとPostを紐づける」部分でハマった(時間かかった)ので備忘録として。
ちなみに、初投稿なので暖かい目で見守ってやってください。笑早速ですが、紐づけるために必要な要素として以下の2点を中心に記載します。
- model側の修正
- controller側の修正
やりたいこと
各modelを関連付けて、「@post.topic.title」のような形で値を取得したい
環境
macOS:10.14.6
Ruby:2.5.7
Rails:5.2.4.1model側の修正(アソシエーション)
今回登場するmodelは、以下の3つ。
・Topic
・User(deviseを使ってます)
・PostTopicがスレッド、Postがスレッドに対するレスで、
Topic(1)---(多)Post
User(1)---(多)Post/Topic
というような位置付けになります。今回の場合は既に各modelを作成(generate)済みだったので、アソシエーション用のマイグレーションファイルを作って、マイグレートしていく形で進めます。
マイグレーションファイルを作成する
$ rails g migration AddUserToTopic $ rails g migration AddTopicToPostマイグレーションファイルを編集する
文法は、「add_reference table名、reference名」が基本形になります。
indexはオプションなのでお好みで。
ちなみに超ざっくりですが、indexは読み込み/取得速度を上げてくれるものです
(但し、書き込み速度は遅くなるので注意が必要)20200222110925_add_user_to_topic.rbclass AddUserToTopic < ActiveRecord::Migration[5.2] def change add_reference :topics, :user, index: true #追記箇所 end end20200223032448_add_topic_to_post.rbclass AddTopicToPost < ActiveRecord::Migration[5.2] def change add_reference :posts,:topic #追記箇所 end endマイグレートする
$ rails db:migrate上記コマンド実行後、以下のようなmigratedが出力されればOK
== 20200223032448 AddTopicToPost: migrating =================================== -- add_reference(:posts, :topic) -> 0.0796s == 20200223032448 AddTopicToPost: migrated (0.0797s) ==========================モデルを編集する
topic.rbclass Topic < ApplicationRecord validates :title, presence: true has_many :posts #追記 belongs_to :user #追記 endpost.rbclass Post < ApplicationRecord belongs_to :topic #追記 belongs_to :user #追記 enduser.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable validates :name,:email,:encrypted_password, presence: true has_many :topics, through: :posts #追記 endこれでmodel側はOKです!
controller側の修正
各modelの関連付けできたので以下のような形で、関連先の値を拾ってこれるようになりました。細かく言うとパラメーターの中から明示的に値を取得するイメージです。
params[:topic_id]という風な感じで。topics_controller.rbclass PostsController < ApplicationController def new @topic_id = params[:topic_id] @post = Post.new end def create @post = Post.new @topic_id = params[:topic_id] #paramsからtopic_idを取得し、インスタンス変数@topic_idに代入 @post.topic_id = @topic_id #上記を@post.topic_idに代入 @post.body = params[:post][:body] @topic = Topic.find(@topic_id) @post.user_id = current_user.id if @post.save redirect_to topic_path(@topic_id), notice: '投稿しました' else render 'posts/new', alert: '投稿できませんでした' end end endちょっと汚いですね、、、汗
取得したい値は「paramsから取得してインスタンスに代入」と書く形です。
なので、取得したい値に応じて編集してください。上記はあくまで参考として。viewの修正(おまけ)
以下はトピック一覧画面(topic#index)ですが、postの数やpostの投稿時間を取得して表示することができます。
index.html.erb<div class="container mt-5 ml-5"> <div class="row"> <% @topics.each do |topic| %> <div class="table tabel-hover"> <%= link_to topic do %> <div class="list-group" style="max-width: 500px;"> <div class="list-group-item list-group-item-action"> <%= topic.title %> (<%= topic.posts.count %>) #post数を計算 <br> <small class="text-muted text-right"> 最終投稿日時:<%= topic.posts.last.created_at %> #最新のpost投稿時間を取得する </small> </div> <% end %> </div> <% end %> </div> </div> </div>こんな表示になります↓
非常に便利ですね。
参考文献
- 投稿日:2020-02-24T22:34:39+09:00
数式を含むMarkdownファイルをRe:VIEWにする
はじめに
数式を含む文書を書くことが多いのですが、いつもMarkdownで書いています。Markdownはいろいろなことができませんが、逆に「いろいろなこと」をしないように書くようになるので、慣れるとLaTeXより楽だったりします。とりあえず私は
- 大きなブロック(章等)が表現できる
- 箇条書きなどがある
- リンクを張れる
- 数式を入れられる
- 画像を入れられる
だけができれば良く、それ以上のことは要求しません。
VSCodeで書くと、数式も含めてプレビューできるので便利です。そのままGitHubに上げても数式は見えませんが、適当なテンプレートを使ってPandocで変換してやればGitHub Pagesで数式が見えるようになり、ついでにレスポンシブ対応にもできて便利です。例えば一週間でなれる!スパコンプログラマはそうやって作っています。
このようにMarkdownは気軽に書けていいのですが、方言が極めて多く、特に数式の扱いが統一されていないのが困りもので、変換時に様々なトラブルを引き起こします。
というわけで今回、こうやって書いた「数式を含むMarkdown」をRe:VIEW StarterでPDF化する際に困ったことをまとめておきます。以下、Re:VIEW Starterを使う関係で、Re:VIEWのバージョンは2.5を想定します。
方針
マークダウンファイル
*.md
をmd2reviewで*.re
に変換します。ただし、数式が変換できないので、事前に変換スクリプトをかませておきます。また、数式中にアンダースコアがあると変換がおかしくなるので、それもエスケープしておき、後で元に戻す必要があります。数式
Markdownに数式を入れる方法はいくつか流儀がありますが、ここではインライン数式は
$
、ブロック数式は$$
で囲むことにします。VSCodeではプレビューにKaTeXを使う関係で、aligned
まわりとか、微妙にQiitaと違ったりします。アンダースコアのエスケープ
まず困るのが、アンダースコアが文中に二回出現すると、強調構文と解釈されてしまうことです。md2reviewは変換にRedcarpetを使っていますが、Redcarpetは数式に対応しておらず、数式中に出現するアンダースコアをMarkdownの強調と解釈してしまいます。こんなコードを書いてみましょう。
test.rbrequire 'redcarpet' require 'redcarpet/render/review' render = Redcarpet::Render::ReVIEW.new() mk = Redcarpet::Markdown.new(render) puts mk.render(ARGV[0])これに
$t_1$
を食わせてもそのままスルーされます。$ ruby test.rb '$t_1$' $t_1$しかし、
From $t_1$ to $t_2$
みたいなのを食わせるとこうなります。$ ruby test.rb 'From $t_1$ to $t_2$' From $t@<b>{1$ to $t}2$途中にある二つのアンダースコアに挟まれた部分を強調だと認識されてしまったのです。これは、ブロック内でも同じで、インライン、ブロックともに数式中のアンダースコアをエスケープしてやる必要があります。なんでも良いですが、なんかRe:VIEWっぽく
@<underscore>
にしておいて、後で戻すことにしましょう。インライン数式中の中カッコ
Re:VIEWでは、インライン数式は`@<m>{}'で表現できます。しかし、この中で、そのままでは中カッコが使えません。なので、
$T_i^n$みたいなのは
@<m>{T_i^n}にすればよいのですが、
$T_i^{n+1}$をそのまま
@<m>{T_i^{n+1}}にするとエラーとなります。
}
を\{
とエスケープしてやるなど、解決策はいくつかあると思いますが、一番簡単なのはフェンス記法を使うことです。フェンスには
$
か|
が使えますが、$
だと変換が面倒になるので$|$を使うことにします。これを使うと、$T_i^{n+1}$は
@<m>|T_i^{n+1}|と書けます。もちろん、数式中に
|
を使っている場合は真面目にエスケープする必要があります。スクリプト
というわけで、
*.md
をmd2review
に食わせる前と後に手抜き変換スクリプトをかませることにします。前処理はこんな感じでしょうか。
pre.rbdef escape_underscore(str) str.gsub('_','@<underscore>') end def escape_inline_math(str) while str =~ /\$(.*?)\$/ math = escape_underscore($1) str = $` + "@<m>|" + math + "|" + $' end str end def in_math while line=gets if line=~/\$\$/ puts "//}" return else puts escape_underscore(line) end end end while line=gets if line=~/\$\$/ puts "//texequation{" in_math else puts escape_inline_math(line) end endインライン、ブロック中の数式のアンダースコアをエスケープするのと、それぞれRe:VIEW形式に変換しています
ポスト処理は、エスケープしていたアンダースコアを戻すだけです。
post.rbwhile line=gets puts line.gsub('@<underscore>','_') endこれだけならsedでもいい感じがしますが、将来なんか面倒な処理が増えた時のためにRubyで書いておきます。
後は、Markdownから参照する画像をRe:VIEWから参照できるディレクトリ(
images
以下)に適当にコピーすれば、変換が可能です。まとめ
数式を含むMarkdownファイルを、
md2reviwe
を使ってRe:VIEWファイルに変換してみました。上記の方法で変換して作ったPDFをここに置いておきます。まだ変換ミスなどで「??」になってたり、そこかしこに変なところはありますが、パッと見にはそれっぽくなってることがわかるかと思います。もっと苦労するかと思いましたが、もともと自分の書いてたMarkdownであまり凝ったことをしていなかったせいか、上記の修正だけでそれなりの変換ができました。ただし、印刷用にちゃんとしたものにするためには、変換後にそれなりに手で修正をいれる必要はありそうです。今回、Re:VIEW Starterを使いましたが、簡単なステップできれいなPDFができて便利でした。また、変換トラブルでエラーが起きた時、Re:VIEW Starter拡張の範囲コメント(
#@+++
と#@---
)で二分探索ができたのがデバッグに役に立ちました。「手軽に書けて、ウェブで簡単に見られるようにして、かつきれいなPDFを生成したい」、文書書きさんならいつも思うことだと思います。「これが最善」という方法はありませんが、とりあえずMarkdown+数式で書いて、PandocでHTMLにして、md2reviewでRe:VIEW経由でPDFにする、という方法を紹介してみました。他にもいろんな方法はあると思いますが、本稿が誰かの参考になれば幸いです。
参考
- 投稿日:2020-02-24T22:20:17+09:00
ERROR: While executing gem ... (Errno::EACCES)が出た時の解決法
実現したいこと
railsでWEBアプリケーションを作るための環境構築。
エラー内容
Rubyの拡張機能(gem)を管理するためのbundlerをインストールした時にでたエラー
ERROR: While executing gem ... (Errno::EACCES)
bundlerとは
gem一種
railsでアプリケーションを開発する際には複数のgemを使用します。gem同士の互換性を保ちながらバージョン等を管理してくれる仕組みのこと。複数人、複数環境で開発を行う際に各環境で扱うパッケージの種類やバージョンを合わせてくれて非常に便利である。解決方法
参考記事
今回このエラーの解決までに参考にした記事です。
https://qiita.com/nachiguro1003/items/4b564b92eca3ba35744a修正内容
上記参考記事にも記載されていますが、今回のエラー内容は、gemをインストールする時に不必要
sudo
をつけて実行してしまったため、ファイルへ書き込みする権限を失ってしまったためです。
ですので解決策としてはこの権限を修正します。
ターミナルで以下の通り実行します。
sudo chown -R (ユーザ名):staff /Users/(ユーザ名)/.rbenv
これで権限が戻りますので、再度'gem install bundler'することで無事解決できました。
- 投稿日:2020-02-24T22:11:40+09:00
読書ログ『メタプログラミング Ruby 第2版』2章
2章 月曜日: オブジェクトモデル
オブジェクトモデル
全ての 言語要素(クラス、モジュール、インスタンス変数 etc...)が共存しているシステムのこと。
以下の2つの質問に答える場所
- このメソッドはどのクラスに所属するものなのか
- このモジュールをincludeしたら何が起きるのか
2.1 オープンクラス
オープンクラス
- いつでも既存のクラスを再オープンして、そのばで修正できる技法のこと
- (既存クラスには、String,Arrayなど標準クラスも含む)
classキーワード
クラス宣言。主な仕事はクラスのコンテキストに連れて行くこと。Rubyではクラスを定義するコードと、その他のコードに違いはない。
モンキーパッチ
オープンクラスを否定的に言う、蔑称。
何も考えずにクラスにコードを追加することで、既存のメソッドを上書きし、意図しない挙動を生み出すこと。
Refinementsを使うことで、安全にモンキーパッチを利用できるようになる。
2.2 オブジェクトモデルの内部
インスタンス変数
オブジェクトに存在する
値が代入されたときに初めて出現する
- Rubyのオブジェクトのクラスと、インスタンス変数には何のつながりもない!
メソッドを呼び出さなければ、インスタンス変数は存在しない。
class MyClass def my_method @v = 1 end end obj = MyClass.new obj.instance_variables => [] obj.my_method => 1 obj.instance_variables => [:@v]メソッド
- メソッドはオブジェクトではなく、クラスに存在する
- オブジェクトにはメソッドはなく、インスタンス変数とクラスへの参照があるだけ
クラスとは
- クラスはClassクラスのオブジェクト
- オブジェクトのメソッド → そのクラスのインスタンスメソッド
- クラスのメソッド → Classクラスのインスタンスメソッド
- 全てのクラスは、モジュールである
- クラスとは、インスタンスの生成と継承のための3つのメソッドをもつモジュール
- クラス名は、定数
定数
- 大文字で始まる参照は、クラス名やモジュール名を含めて、全て定数
- プログラムにある全ての定数は、ファイルシステムのようにツリー状に配置
- モジュール: ディレクトリ / 定数: ファイル
- 定数ツリーの奥にいるときは、ルートを示すコロン2つで書き始めれば、外部の定数を絶対パスで指定できる
Y = 'ルートレベルの定数' module M Y = 'Mにある定数' Y #=> "Mにある定数" ::Y #=> "ルートレベルの定数" endオブジェクトとクラスのまとめ
オブジェクト
- インスタンス変数の集まりにクラスへのリンクがついたもの
インスタンスメソッド
- オブジェクトではなく、オブジェクトのクラスに住む
クラス
- オブジェクトにインスタンスメソッドの一覧とスーパークラスのリンクがついたもの
クラス名
クラスへのアクセスを行うための参照
2.4 メソッドを呼び出すときに何が起きているの?
- メソッドを探す。メソッド探索
- メソッドを実行する。selfが必要
メソッド探索
- Rubyがレシーバのクラスに入り、メソッドを見つけるまで、継承チェーンを上ること。
「右へ一歩、それから上へ」
- レシーバのクラスに向かって右へ一歩進み、メソッドが見つかるまで継承チェーンを上へ進む。
レシーバ
- 呼び出すメソッドが属するオブジェクト
継承チェーン
- スーパークラスを、BasicObjectまで続けたとき、通ったクラスの道筋のこと
モジュールとメソッド探索
- モジュールをクラスにインクルードすると、Rubyはモジュールを継承チェーンに挿入する
- include: インクルードしたクラスの上にモジュールが挿入される
- Prepende: 下にモジュールが挿入される
Kernal
- Objectクラスが、Kernelモジュールをincludeしているので、全てのオブジェクトの継承チェーンに、Kernelモジュールが挿入される
メソッドの実行
selfキーワード
- メソッドを呼び出すとき、メソッドのレシーバがselfになる
- レシーバを明示しないメソッド呼び出しは、全てselfに対する呼び出しになる。
- 他のオブジェクトを明示してメソッドを呼び出すと、今度はそのオブジェクトがselfになる
- 最後にメソッドのレシーバとなったオブジェクトが、selfとなる
トップレベル
- メソッドを呼び出さないときselfになるのは、Objectクラスのインスタンスの
main
self #=> main self.class #=> Objectクラス定義とself
- クラスやモジュールの定義の内側(メソッドの外側)では、selfの役割はクラスやモジュールそのもの
class MyClass self #=> MyClass endRefinements
- Refinementsが有効になる場所
- refineブロック
- usingを呼び出した場所からモジュールの終わりまで(モジュール定義にいる場所 / ファイルの終わりまで(トップレベルにいる場合)
- クラスをリファインすると言うのは、元のコードにパッチを貼り付けるようなもの
- リファインされた側のクラスよりも優先される
- インクルードやプリペンドしたものより優先される
感想
これまで、オブジェクトがメソッド・変数を持つと、漠然とまとめて理解していたので、
インスタンス変数は、オブジェクトに存在する
/メソッドはオブジェクトではなく、クラスに存在する
と整理できたのがかなり、すっきりきたーーーー(ような気がする。)
- 投稿日:2020-02-24T21:15:48+09:00
Ruby on Rails 学習メモ.1
Ruby on Rails学習メモ.1
以下、初学者の独学によるメモ(チラシの裏)。
Progateでの学習もある程度進んできたため、アウトプットをすることにした。
学習中のため、誤った解釈もあるかもしれない。Railsでのサイト作成は、
view(ブラウザ)→
ルーティング(アクションを呼び出す)→
コントローラー(URLに対応したHTMLを送信)
(→view…)
の編集の連続である。これらの基本をよく理解すること。
また、何をやろうとしているのか(目的を)理解しないまま、コードを覚える事を深追いしながら無理矢理学習を進めようとすると単なる写経になってしまうので、まず目的を理解する事に努めること(理解できない場合は何度も読む)。(先の学習内容まで進めた後に復習すると理解できる場合もあるので、まず進める方が良い場合もある。)1.新規投稿
HTML(view)の設定
<div class="form-body"> <%= form_tag("/posts/create") do %> <textarea name="content"></textarea> <input type="submit" value="投稿"> <% end %> </div>
<%= form_tag("/posts/create") do %>
のform_tagが設定されていることにより、
<input type="submit" value="投稿">
、つまり投稿ボタンを押すと、submitされたデータを「/posts/create」のURLに送信するようになっている。
<textarea name="content"></textarea>
入力したデータにname属性を指定している。
これにより、コントローラーのアクション内(変数:params)で入力したデータを受け取れるようになる。ルーティングの設定
get "posts/create" => "posts#create"URL「/posts/create」から情報を受け取り、
「posts」コントローラーの「create」アクションを呼び出す。コントローラーの設定
def create @post = Post.new(content: params[:content]) @post.save redirect_to("/posts/index") endルーティングからcreateアクションが呼び出され、以下の処理が実行される。
@post = Post.new(content: params[:content])
@post.save
変数@postに投稿された内容を代入(受け取るために変数paramsを使用している)し、データベースに保存する。
redirect_to("/posts/index")
URL:/posts/indexに転送(ページ移行)する。※
@user = User.new(name: params[:name], email: params[:email])
のように、配列として複数のデータを受け取ることもできる。2.投稿詳細画面の表示
HTML
<% @posts.each do |post| %> <div class="posts-index-item"> <div class="post-right"> <%= link_to(post.name, "/posts/#{post.id}") %> </div> </div> <% end %>idを取得・送信するように設定。
ルーティング
get "posts/index" => "posts#index" get "posts/:id" => "posts#show"showアクションのルーティングはindexアクションより下に書かないといけないことに注意する。
コントローラー
def show @posts = Post.find_by(id: params[:id]) end3.投稿成功・失敗の表示
クラス定義:投稿の制限(Validate:検証の意)
class Post < ApplicationRecord validates :content, {presence: true} validates :content, {length: {maximum: 140}} endValidateはクラス内(models/post/rb)で定義するので、postクラス配下すべてで適用される。
上記は空の投稿と140字以上の投稿にfalseを返すようになっている。コントローラー(アクション)
def create @post = Post.new(content: params[:content]) if @post.save redirect_to("/posts/index") flash[:notice] = "投稿を作成しました" else render("posts/new") endif @post.saveがtrueの場合、「/posts/index」に飛び、
「flash[:notice] = "投稿を作成しました"」を実行する。falseの場合、「posts/new」に飛ぶ
HTML
flashはいろいろな箇所で共通で使っていくのでapplication.html.erbに設定する。
投稿成功の場合:<% if flash[:notice] %> <div class="flash"> <%= flash[:notice] %> </div> <% end %>投稿失敗の場合
投稿失敗の場合posts/newに飛ぶので、application.htmlではなくnew.htmlを編集する。(2行目から6行目までが該当。)<%= form_tag("/posts/create") do %> <% @post.errors.full_messages.each do |message| %> <div class="form-error"> <%= message %> </div> <% end %> <textarea name="content"><%= @post.content %></textarea> <input type="submit" value="投稿"> <% end %>saveメソッドを呼び出した際にバリデーションに失敗すると、Railsでは自動的に@post.errors.full_messagesの中にエラーメッセージが配列で入るようになっている。
each文を用いることで、配列の中のメッセージ全てを表示させる。
また、<textarea>
内に<%= @post.content %>
を設定しているため、入力した(投稿に失敗した)文章が出力される。
(newアクションではこの時点では変数postが定義されていないため、@post = Post.newを定義する必要があることに注意。←newアクション作成時に作っておく?)4.投稿一覧ページの作成
先にターミナルで「rails g model Post content:text」(Post:モデル名、content:カラム名、text:データ型)を実行し、モデルとマイグレーションファイル(データベース)を作成しておく。
コントローラー(アクション)
def index @posts = Post.all endHTML
<% @posts.each do |post| %> <div class="posts-index-item"> <%= post.content %> </div> <% end %>each文で変数@posts内の内容を繰り返し(全て)表示させる。
5.投稿の編集
HTML(edit.htmlを作成する必要あり)
<div class="main posts-new"> <div class="container"> <h1 class="form-heading">編集する</h1> <%= form_tag("/posts/#{@post.id}/update") do %> <div class="form"> <div class="form-body"> <% @post.errors.full_messages.each do |message| %> <div class="form-error"> <%= message %> </div> <% end %> <textarea name="content"><%= @post.content %></textarea> <input type="submit" value="保存"> </div> <% end %> </div> </div>4行目:idを取得し「/posts/id/update」のURLを送信する。
7行目:アクション(コントローラー)内のifでエラーが出てrenderメソッドを介してページに戻ってきた場合、エラーメッセージを表示させるようになっている。ルーティング
post "posts/:id/update" => "posts#update"コントローラー(アクション)
def update @post = Post.find_by(id: params[:id]) @post.content = params[:content] if @post.save flash[:notice] = "投稿を編集しました" redirect_to("/posts/index") else render("posts/edit") end end
@post = Post.find_by(id: params[:id])
:送信されてきたURLとidを参照し、変数@postに代入する。
@post.content = params[:content]
:送信されてきた内容をparamsで受け取り、変数@postのcontentに代入する。
if @post.save
が成立したら「投稿を編集しました」のメッセージとともに、「/posts/index"」に戻る。
成立しない(エラーの)場合、renderで「posts/edit」に戻る。
renderで戻らないと「/posts/index」を介し投稿内容の変数が更新されてしまう(編集内容が最初の投稿に戻り、編集したかった投稿がフォームに維持されない。)6.投稿の削除
HTML(show.html)
<div class="post-menus"> <%= link_to("編集", "/posts/#{@post.id}/edit") %> <%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %> </div>(投稿詳細画面のshow.htmlで完結するので、新しくhtml(view)を追加する必要はない。)
link_toメソッドの第三引数に{method:"post"}を設定しないと、次のルーティングでpostではなくgetを探してしまい、エラーとなってしまう。ルーティング
post "posts/:id/destroy" => "posts#destroy"コントローラー(アクション)
def destroy @post = Post.find_by(id: params[:id]) @post.destroy flash[:notice] = "投稿を削除しました" redirect_to("/posts/index") end編集の際と同様、HTMLより送信されたidを変数に代入し、destroyアクションで削除する。
(実際のポートフォリオでは「削除しますか?」の質問を出せるようにする?)
- 投稿日:2020-02-24T19:59:00+09:00
deviseのユーザー登録失敗後にビューがずれる。
登録画面ビュー
通常の登録画面でapp/view/devise/registrations/new.html.hamlにCSSを当てたもの
これにフォームに空などの無効な値を入れると以下のようになる。
登録失敗後のビュー
//登録失敗時のnickname部分のHTML <span class="nickname"> <div class="field_with_errors"><label for="user_nickname">ニックネーム</label></div> <div class="field_with_errors"><input type="text" value="" name="user[nickname]" id="user_nickname"></div> </span>"field_with_errors"クラスが自動生成されてしまっていることが原因
registrations.scss.field_with_errors { display: contents; }上記のCSSを追記して解決
- 投稿日:2020-02-24T19:34:42+09:00
Ruby on rails でTodoアプリを作る
今回はRuby on railsを使ってTodoアプリを作っていきます!!!
完成品が↑
railsの学習のために作ります!
1.プロジェクトを作ろう!
コンソールを起動して、
$ rails new #app名これでプロジェクトを作ります!
次にcdでプロジェクトに移動しましょう一旦作成したファイルの方にいって
gemfile
というものがあると思うのでそれを編集します!
ここをいじってBootstrap等を導入するワケですね!# gem 'bcrypt', '~> 3.1.7' gem 'bootstrap', '~> 4.1.1' #この1行だけ追加 # Use ActiveStorage variantこのように記載すればOKです!
ではコンソールに戻りましょう
※ここまででsqliteについてエラーが発生した人がいる場合
ridk exec pacman -S mingw-w64-x86_64-sqlite3
こちらのコマンドを一度うってくださいそして全て完了したらコンソールにて
bundle installこちらのコマンドをうちます
( bundlerというgemを使って、Gemfileに従ってgemをインストールするためのコマンドです!)ここまで完了したら
rails sでサーバーを起動してみましょう!
こういうのが出たら成功です!
成功を確認できたら、ブラウザを開いて
localhost:3000
を入力してください!
このような画面が出てきたら無事、railsアプリの作成準備が整いました!!
(サーバーを終了させたい場合はCtrl+cでyを入力して終了できます)2.データベースを構築しよう!
RailsはModelを使ってデータベースを操作します
id、created_at、updated_atの3つのカラムがデフォルトで作成されます。
今回はstring型のcontentというカラムを追加したいと思います。
Model名は頭文字が大文字で、単数形でなくてはなりません。
ではコンソールに戻って$ rails g model Task content:string $ rails db:migrateを実行してください!
これで(migurate)データベースにテーブルが作成されます。
このようにTaskというテーブルを作成すると
tasksというように複数形のテーブルができます以上でデータベースの構築は終わりました!
3.Controllerを使ってみよう!
controllerはMVCを構成するコンポーネントの1つです。
MVCとは
Model:データベースを取り扱う
View:画面表示を取り扱う
Controller:ModelとViewと連携する
といったようなものです。では今回はタスクの一覧を表示するアクションcontrollerを作っていきましょう!
$ rails generate controller Tasks indexこれでタスク一覧を表示するトップページのtasks_controllerを作ります!
作成されたControllerに、登録されている全てのタスクを取得する処理を記述します。/app/controllers/tasks_controller.rbdef index @tasks = Task.all endTask.allでtasksテーブルの全てのレコードを取得します!
4.ルーティングを設定しよう!
URLに対するリクエストを受けた時にcontrollerのどのアクションを実行するか、を設定します。
resources :{Controller名}
で複数のルーティングが一気に設定できるようになります!
これでlocalhostに入った際にindexアクションが動くようになっています/config/routes.rbRails.application.routes.draw do root 'tasks#index' resources :tasks end設定されているルーティングは
rails routes
で確認しよう!5.Viewを作ろう!
Controller内のアクション(タスク一覧表示する)が実行されると、対応するViewファイルが呼び出されます!
今回の場合は対応するViewファイルはindex.html.erbです!/app/views/tasks/index.html.erb<div class="mx-auto" style="width: 200px;"> <h1>Task list</h1> </div> <h2><%= link_to 'Creat new Todo', new_task_path ,class: 'btn btn-info'%></h2> <table class="table"> <thead> <tr> <th>タスク</th> <th>編集</th> <th>削除</th> </tr> </thead> <tbody> <% @tasks.each do |task| %> <tr> <td><%= task.title %></td> </tr> <% end %> </tbody> </table>Bootstrapを使ってテーブルとかを綺麗に表示するようにしてるよ
<% @tasks.each do |task| %>
にて格納されているタスクを表示するようにしています!https://hackerthemes.com/bootstrap-cheatsheet/#input-group-append
↑もっとBootstrap使いたい方向けにチートシート紹介しときます↑6.タスクを追加するための準備!
タスク追加画面を表示するためのnewアクションを作ります!
/app/controllers/tasks_controller.rbdef new @task = Task.new endこれを追記します!
リクエストがあった時にフォームを表示するために@task = Task.newでTaskのオブジェクトを作成しています!次に、タスク追加画面のviewファイルを作成します。
/app/views/tasks/new.html.erb<%= form_for(@task) do |f| %> <div><%= f.text_field :title %></div> <div><input type="submit" value="追加" class="btn btn-success"></div> <% end %> <%= link_to '戻る', tasks_path %今回はform_forを使っていますがこの説明は長くなるので下記のリンクを参考にしてください!
form_forを使って「フォーム作成→データをテーブルに保存」を実装しています。
https://qiita.com/jumpyoshim/items/ee5af466ef79595671747.タスク追加処理を作っていこう!
/tasksにPOSTリクエストがあった時、タスクの追加処理を行うaddアクションを作成します。
/app/controllers/tasks_controller.rbdef add @task = Task.add(task_params) redirect_to tasks_path end private def params params.require(:task).permit(:title) endPOSTで送信された物をデータベースに登録し、その後
redirect_to tasks_path
を使ってタスク一覧画面に遷移するようにしています!またpraivateの部分ですが、Taskモデルのtitleを渡された場合のみ追加処理が実行されるようになっています。
以上で追加処理の部分は完成です!
8.タスクを編集する機能を作ろう!
/tasks/{id}/editにGETリクエストがあった時、タスク編集を表示するeditアクションと
編集した後のデータベースを更新するアクションを一緒に作っときましょう!/app/controllers/tasks_controllers.rbdef edit @task = Task.find(params[:id]) end def update @task = Task.find(params[:id]) @task.update(task_params) redirect_to tasks_url end-edit-
リクエスト部分の数値をparams[:id]で取り出しています。-update-
タスク編集画面から送信された内容を受け取り、データベースを更新します!
find
で主キーに対応するレコードを取り出します。続いて、タスク編集画面のviewファイルを作成します。
/app/views/tasks/edit.html.erb<h1>タスク編集</h1> <%= form_for(@task) do |f| %> <div><%= f.label :title %></div> <div><%= f.text_field :title %></div> <div><input type="submit" value="編集" class="btn btn-success"></div> <% end %> <%= link_to '戻る', tasks_path %>これで編集機能は終了です!
9.タスクの削除機能を作ろう!
/tasks/{id}にDELETEリクエストがあった時
idに対応するレコードをデータベースから削除するdestroyアクションを追加します。/app/controllers/tasks_controller.rbdef destroy @task = Task.find(params[:id]) @task.destroy redirect_to tasks_url endではこれまで編集機能、削除機能を作ってきたのでそれをタスク一覧画面に追加しよう!
/app/views/tasks/index.html.erb<tr> <td><%= task.title %></td> <td><%= link_to '編集', edit_task_path(task) ,class: 'btn btn-success' %></td> <td><%= link_to '削除', task_path(task), method: :delete, class: 'btn btn-danger' %></td> </tr>classはBootstrapを使っていい感じのボタンにするため設置
destroyアクションを実行するには、DELETEメソッドのリクエストを送らないとダメなので引数にmethod: deleteを指定しています!
以上で投稿、編集、削除の機能は完成です!!!
10.空の投稿や文字数制限の機能をつけよう!
空の投稿やあまりに多い文字数は色々邪魔だと思うのでこちらを規制しましょう!!
/app/models/task.rbvalidates :title, presence: true, length: { maximum: 50 }上記のように何か入力内容に規制をしたいとなった場合
validates
を使って titleに入力される内容に規制をかけます
presence
で空白禁止にして、length:{maximum:30}
で30文字までしか入力できないようにしました!/app/views/tasks/new.html.erb<h1>Task add</h1> <% if @task.errors.any? %> <div class="alert alert-warning"> <ul> <% @task.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>を追加して、空白や文字数超えた時にはエラーを表示するように追記しましょう!
これの詳しい説明は
https://qiita.com/ryuuuuuuuuuu/items/1a1e53d062bff774d88a
を参考に!11.最後に
今回todoアプリを作って、railsでアプリを作る流れは掴めたかと思います。
けど実際にもっと複雑なWebアプリを作るってなるとまだまだ途方にくれそうなのでまずはこういう物を作ったりしてみて
もっと基礎等が掴めてきたら応用に活かせたらいいなと思いました!
https://github.com/t4k3p0n/todo-rails
↑githubです
- 投稿日:2020-02-24T18:58:47+09:00
Ruby on railsで、プロジェクトにファイルが生成されない時の対処法
- 投稿日:2020-02-24T18:11:14+09:00
Ruby/GTK3 - Button
gem install gtk3
Button
"Click me" button was clicked
"Open" button was clicked
Closing applicationrequire 'gtk3' class ButtonWindow < Gtk::Window def initialize super set_title 'Button Demo' set_border_width 10 hbox = Gtk::Box.new(:horizontal, spacing = 6) add(hbox) button = Gtk::Button.new(label: 'Click Me') button.signal_connect('clicked') { on_click_me_clicked } hbox.pack_start(button) button = Gtk::Button.new(mnemonic: '_Open') button.signal_connect('clicked') { on_open_clicked } hbox.pack_start(button) button = Gtk::Button.new(mnemonic: '_Close') button.signal_connect('clicked') { on_close_clicked } hbox.pack_start(button) end def on_click_me_clicked puts '"Click me" button was clicked' end def on_open_clicked puts '"Open" button was clicked' end def on_close_clicked puts 'Closing application' Gtk.main_quit end end win = ButtonWindow.new win.signal_connect('destroy') { Gtk.main_quit } win.show_all Gtk.main
- 投稿日:2020-02-24T17:52:09+09:00
ruby-dnnとディープラーニングで建物の画像を生成(Pix2pix)
はじめに
ruby-dnnで、Pix2pixを使って、建物の画像変換をやってみます。
コード全文はhttps://github.com/unagiootoro/facade-pix2pixにあります。Pix2pix
Pix2pixを知らない人向けに大雑把に解説します。
Pix2pixは、主に画像変換に使われるディープラーニングのモデルです。
Pix2pixの最大の特徴は、くっきりとした画像を生成できるというところにあります。
通常の画像変換では、生成した画像が本物の画像にどれぐらい近いかをピクセル値で判断しますが、Pix2pixでは、生成した画像がどれくらい本物の画像に近いかを判断する基準を自動で学習してくれます。ピクセル値の差異は、くっきりした画像より、ぼやけた画像のほうが少なくなります。そのため、ピクセル値に依存しない学習を行うことで、くっきりとした画像が生成できるようになります。1使用ライブラリ/バージョンなど
Ruby ... v2.6.5
ruby-dnn ... v1.1.3
Numo::NArray ... v0.9.1.5
Numo::Linalg ... v0.1.4
rubyzip ... v2.2.0ソースコード
ruby-dnnでPix2pixを学習させるコードの大雑把な解説です。
とりあえず動かしてみたい方は、「学習結果の確認」まで飛ばしてください。データセットの読み込み
facade_dataset.rb
ruby-dnnのdownloaderを使用して、Facadeデータセットをダウンロードします。
やってる処理は以下の通りです。
1. Facadeデータセットをダウンロードして解凍する。
2. ダウンロードしたデータを読み込み、64 * 64のサイズに縮小する。require "zip" require "dnn/image" module DNN module Facade FACADE_URL = "http://cmp.felk.cvut.cz/~tylecr1/facade/" BASE_DIR_NAME = "CMP_facade_DB_base" EXTENDED_DIR_NAME = "CMP_facade_DB_extended" BASE_PATH = "#{DOWNLOADS_PATH}/downloads/#{BASE_DIR_NAME}" EXTENDED_PATH = "#{DOWNLOADS_PATH}/downloads/#{EXTENDED_DIR_NAME}" def self.downloads unless Dir.exist?(BASE_PATH) zip_file_name = "#{BASE_DIR_NAME}.zip" Downloader.download("#{FACADE_URL}/#{zip_file_name}") zip_decompression(BASE_PATH) File.unlink("#{DOWNLOADS_PATH}/downloads/#{zip_file_name}") end unless Dir.exist?(EXTENDED_PATH) zip_file_name = "#{EXTENDED_DIR_NAME}.zip" Downloader.download("#{FACADE_URL}/#{zip_file_name}") zip_decompression(EXTENDED_PATH) File.unlink("#{DOWNLOADS_PATH}/downloads/#{zip_file_name}") end end def self.zip_decompression(zip_path) Zip::File.open("#{zip_path}.zip") do |zip| zip.each do |entry| zip.extract(entry, "#{zip_path}/#{entry.name}") { true } end end end def self.load_images(dir_path) downloads in_imgs = [] out_imgs = [] Dir["#{dir_path}/*.png"].each do |fpath| img = DNN::Image.read(fpath) img = DNN::Image.resize(img, 64, 64) in_imgs << img end Dir["#{dir_path}/*.jpg"].each do |fpath| img = DNN::Image.read(fpath) img = DNN::Image.resize(img, 64, 64) out_imgs << img end [in_imgs, out_imgs] end def self.load_train load_images("#{BASE_PATH}/base") end def self.load_test load_images("#{EXTENDED_PATH}/extended") end end endtrain.rb より抜粋
ダウンロードしたデータは、load_datasetで読み込めるようにします。
このとき、画像データを-1 ~ 1の範囲に正規化します。def load_dataset x, y = DNN::Facade.load_train x = Numo::SFloat.cast(x) y = Numo::SFloat.cast(y) x = (x / 127.5) - 1 y = (y / 127.5) - 1 [x, y] endモデル定義
dcgan.rb
画像を入力として受け取るDCGANモデルを定義します。各クラスの役割は、以下の通りです。
Generator: 入力画像から変換先の画像を生成します。
Discriminator: 入力画像とGeneratorが生成した画像のセットを受け取り、それらの組み合わせが本物か生成された画像かを判断できるように学習させるためのモデルです。
DCGAN: Discriminatorを騙せるような画像を生成できるようにGeneratorを学習させるためのモデルです。
長いので折りたたんでいます
include DNN::Models include DNN::Layers class Generator < Model def initialize(input_shape) super() @input_shape = input_shape @l1 = Conv2D.new(32, 4, padding: true) @l2 = Conv2D.new(32, 4, strides: 2, padding: true) @l3 = Conv2D.new(64, 4, padding: true) @l4 = Conv2D.new(64, 4, strides: 2, padding: true) @l5 = Conv2D.new(128, 4, padding: true) @l6 = Conv2DTranspose.new(64, 4, strides: 2, padding: true) @l7 = Conv2D.new(64, 4, padding: true) @l8 = Conv2DTranspose.new(32, 4, strides: 2, padding: true) @l9 = Conv2D.new(32, 4, padding: true) @l10 = Conv2D.new(32, 4, padding: true) @l11 = Conv2D.new(3, 4, padding: true) @bn1 = BatchNormalization.new @bn2 = BatchNormalization.new @bn3 = BatchNormalization.new @bn4 = BatchNormalization.new @bn5 = BatchNormalization.new @bn6 = BatchNormalization.new @bn7 = BatchNormalization.new @bn8 = BatchNormalization.new @bn9 = BatchNormalization.new end def forward(x) input = InputLayer.new(@input_shape).(x) x = @l1.(input) x = @bn1.(x) h1 = ReLU.(x) x = @l2.(h1) x = @bn2.(x) x = ReLU.(x) x = @l3.(x) x = @bn3.(x) h2 = ReLU.(x) x = @l4.(x) x = @bn4.(x) x = ReLU.(x) x = @l5.(x) x = @bn5.(x) x = ReLU.(x) x = @l6.(x) x = @bn6.(x) x = ReLU.(x) x = @l7.(x) x = @bn7.(x) x = ReLU.(x) x = Concatenate.(x, h2, axis: 3) x = @l8.(x) x = @bn8.(x) x = ReLU.(x) x = @l9.(x) x = @bn9.(x) x = ReLU.(x) x = Concatenate.(x, h1, axis: 3) x = @l10.(x) x = ReLU.(x) x = @l11.(x) x = Tanh.(x) x end end class Discriminator < Model def initialize(gen_input_shape, gen_output_shape) super() @gen_input_shape = gen_input_shape @gen_output_shape = gen_output_shape @l1_1 = Conv2D.new(32, 4, padding: true) @l1_2 = Conv2D.new(32, 4, padding: true) @l2 = Conv2D.new(32, 4, strides: 2, padding: true) @l3 = Conv2D.new(32, 4, padding: true) @l4 = Conv2D.new(64, 4, strides: 2, padding: true) @l5 = Conv2D.new(64, 4, padding: true) @l6 = Dense.new(1024) @l7 = Dense.new(1) @bn1 = BatchNormalization.new @bn2 = BatchNormalization.new @bn3 = BatchNormalization.new @bn4 = BatchNormalization.new @bn5 = BatchNormalization.new @bn6 = BatchNormalization.new end def forward(inputs) input, images = *inputs x = InputLayer.new(@gen_input_shape).(input) x = @l1_1.(x) x = @bn1.(x) x1 = LeakyReLU.(x, 0.2) x = InputLayer.new(@gen_output_shape).(images) x = @l1_2.(x) x = @bn2.(x) x2 = LeakyReLU.(x, 0.2) x = Concatenate.(x1, x2) x = @l2.(x) x = @bn3.(x) x = LeakyReLU.(x, 0.2) x = @l3.(x) x = @bn4.(x) x = LeakyReLU.(x, 0.2) x = @l4.(x) x = @bn5.(x) x = LeakyReLU.(x, 0.2) x = @l5.(x) x = @bn6.(x) x = LeakyReLU.(x, 0.2) x = Flatten.(x) x = @l6.(x) x = LeakyReLU.(x, 0.2) x = @l7.(x) x end def enable_training trainable_layers.each do |layer| layer.trainable = true end end def disable_training trainable_layers.each do |layer| layer.trainable = false end end end class DCGAN < Model attr_reader :gen attr_reader :dis def initialize(gen, dis) super() @gen = gen @dis = dis end def forward(input) images = @gen.(input) @dis.disable_training out = @dis.([input, images]) [images, out] end endモデルの作成
train.rb より抜粋
Generator、Discriminator、DCGANのそれぞれのモデルを作成します。
最適化には、Discriminator、DCGANともにAdamを使用しています。
損失関数は、DiscriminatorにはSigmoidCrossEntropyを適用し、DCGANには、出力画像に対する損失関数としてMeanSquaredErrorを、画像の真偽判定に対する損失関数としてSigmoidCrossEntropyを10:1の割合で設定しています。gen = Generator.new([64, 64, 3]) dis = Discriminator.new([64, 64, 3], [64, 64, 3]) dcgan = DCGAN.new(gen, dis) dis.setup(Adam.new(alpha: 0.00001, beta1: 0.1), SigmoidCrossEntropy.new) dcgan.setup(Adam.new(alpha: 0.0002, beta1: 0.5), [MeanAbsoluteError.new, SigmoidCrossEntropy.new], loss_weights: [10, 1])モデルの学習
train.rb より抜粋
学習部分のコードになります。
1. load_datasetでFacadeデータセットを読み込む。
2. gen.predict(x_in)で、画像を生成する。
3. dis.train_on_batch([x_in, x_out], y_real)とdis.train_on_batch([x_in, images], y_fake)で、生成した画像と本物の画像を判断できるようにDiscriminatorが学習させる。
4. dcgan.train_on_batch(x_in, [x_out, y_real])で、Discriminatorを騙せるようにGeneratorを学習させる。x_in, x_out = load_dataset iter1 = DNN::Iterator.new(x_in, x_out) iter2 = DNN::Iterator.new(x_in, x_out) num_batchs = x_in.shape[0] / batch_size (initial_epoch..epochs).each do |epoch| num_batchs.times do |index| x_in, x_out = iter1.next_batch(batch_size) images = gen.predict(x_in) y_real = Numo::SFloat.ones(batch_size, 1) y_fake = Numo::SFloat.zeros(batch_size, 1) dis.enable_training dis_loss = dis.train_on_batch([x_in, x_out], y_real) dis_loss += dis.train_on_batch([x_in, images], y_fake) x_in, x_out = iter2.next_batch(batch_size) dcgan_loss = dcgan.train_on_batch(x_in, [x_out, y_real]) puts "epoch: #{epoch}, index: #{index}, dis_loss: #{dis_loss}, dcgan_loss: #{dcgan_loss}" end iter1.reset iter2.reset dcgan.save("trained/dcgan_model_epoch#{epoch}.marshal") if epoch % 50 == 0 end学習結果の確認
さあ、学習の準備が整ったので早速学習開始...と行きたいところですが、ruby-dnnはまだGPUに
対応していないので、学習しようとすると1日以上かかります。そのため、今回はすぐに試せるよう、
学習済みの重みを用意しました。
■学習済みの重みを使用する場合。$ ruby imgen.rb■一から学習する場合。(1日以上かかります)
$ ruby train.rb $ ruby make_data.rb $ ruby imgen.rb実行結果
inputが入力画像、outputが出力画像、realが本物の画像です。
モデルが小さいので細かいところは再現できていないですが、雰囲気を掴むのには成功してそうですね。
おわりに
いかがだったでしょうか。ディープラーニングというと、どうしてもPythonのイメージが強いですが、Rubyでも意外とできるってことが伝わってくれれば嬉しく思います。
実際には、Pix2pixでは、ピクセル値による平均絶対誤差とDiscriminatorによる画像の真偽判定の両方を損失関数として使用しています。 ↩
- 投稿日:2020-02-24T17:19:49+09:00
MacにDocker+Rails6+MySQLで開発環境構築
公式ドキュメント(https://docs.docker.com/compose/rails/) を以下のコンポーネントでやってみた。
- ruby2.6.5
- Rails6系
- MySQL5.7前提
MacにDocker Desktopがインストールされていること。
適宜、プロジェクト用のディレクトリを用意する。Dockerfile
DockerfileFROM ruby:2.6.5 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]Gemfile
source 'https://rubygems.org' gem 'rails', '~>6'Gilefile.lock
touch Gemfile.lock
entrypoint.sh
entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"docker-compose.yml
docker-compose.ymlversion: '3' services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: root ports: - "3306:3306" web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/myapp ports: - "3000:3000" depends_on: - dbRailsプロジェクトの作成
# 薄いRailsプロジェクト docker-compose run web rails new . --force --database=mysql -B -M -C --skip-coffee --skip-turbolinksbuild
docker-compose builddatabase.ymlの修正
config/database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # docker-compose.yml の MYSQL_ROOT_PASSWORD host: dbコンテナ再起動
docker-compose down docker-compose up -dWebpacker
docker-compose run web bundle exec rails webpacker:installDB作成
docker-compose run web bundle exec rails db:create
- 投稿日:2020-02-24T16:56:24+09:00
devise token authの出力をserializerで整形する
はじめに
この記事ではdevise token authでのjsonの出力を
active_model_serializer
gemを使って整形する手順を解説するわ。実際の開発中に少し詰まったことを元にしているからゴールとして"ユーザーのログイン成功時に返されるjsonをserializerで整形する"を設定するわ。他のアクション後に返されるjsonにも応用できるように心掛けるから安心しなさい。
devise関連の導入まで含めちゃうと長くなるからそこは割愛するわ、ごめんなさいね。準備するわよの巻
まずはgemのインストールから始めるわ。
Gemfile.gem 'active_model_serializers'bundle installこれでactive_model_serializersのインストールが完了したわ。
このgem自体の説明は他の記事に譲るとしてまずは設定をするわ。
設定用のconfig/initializers/ams.rb
ファイルを手動で追加しなさい。config/initializers/ams.rbActiveModel::Serializer.config.adapter = :json_apiこの部分では出力するjsonのフォーマットを指定しているわ。オプションの詳しい説明についてはこちらの記事に詳しいわ。
Railsのactive_model_serializerについて学ぶ100DaysOfCodeチャレンジ10日目(Day10:#100DaysOfCode)
ここでは
json_api
を指定したけど、あなたの好きなように設定しなさい。さて、次はいよいよserializerのファイルを作っていくわよ。
rails generate serializer User最後の
User
の部分は整形したいモデル名を指定することね。このコマンドでapp/serializers/user_serializer.rb
ファイルが作成されたわ、早速いじいじしちゃいましょう。app/serializers/user_serializer.rbclass UserSerializer < ActiveModel::Serializer attributes :id, :name, :nickname, :image, :confirmed_at, :updated_at endここではjsonで返すモデルのカラムを指定しているわ。他にも色々設定できるけど適宜自分で調べなさい、頑張ることね。
本題に入るわよの巻
ここまでで準備は完了よ、configに追加した設定を読み込ませるためにこのあたりで一旦サーバを再起動しておくことね、ん?もうやった? 良い子ね、クマちゃんのビスケットをあげるわ。
さて、まず見て欲しいものがあるの、serializerで整形をしない場合のログイン成功時に返されるjson(ログインに成功したユーザーの情報)よ。
続いて同じjsonをserializerで整形した場合よ。
データが構造化されているのと設定したカラムのみが出力されていることが分かるわね。
ちなみに下はapp/controllers/api/v1/user_controller.rbclass Api::V1::UsersController < ApplicationController def show @user = User.find(params[:id]) render json: @user, serializer: UserSerializer end endこんな感じのコードよ。
見ての通り、deviseの方のjsonも整形しないとアプリケーション全体として返すjsonのフォーマットに統一性が取れないことが分かるわ。これはフロント側としても混乱するしメンテナンス性も大きく損なうわ。
でも、自分で一から書くアクションなら上のコードみたいに簡単に実装できるんだけどdeviseは内部で勝手にやってくれるから分かりづらいわよね。
そこで今回は
1. コントローラを継承する
2. そこで各アクションごとのレンダリングメソッドをオーバーライドしてjsonをserializerで整形するという手順でdeviseで返されるjsonの整形をするわ。
コントローラの継承をするわよ編
今回はログイン時のjsonを整形したいということで、ログイン時に呼ばれるアクションは
devise_token_auth/sessions#create
よ。ということで
devise_token_auth/sessions
コントローラを継承するわ。app/controllers/api/v1/auth/sessions_controller.rbclass Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController end新しく作ったコントローラが参照されるようにルーティングも変更よ。
私の場合はconfig/routes.rbRails.application.routes.draw do namespace :api do namespace :v1 do resources :users, only: [:show] mount_devise_token_auth_for 'User', at: 'auth', controllers: { registrations: 'api/v1/auth/registrations', # ここの部分ね sessions: 'api/v1/auth/sessions' } end end endさて、準備が整ったところでさっきの手順の2の太字になっている各アクションごとのレンダリングメソッドの説明をするわ。出力を制御する、と聞いて最初に対象のアクションをそのままオーバーライドすることを考えた子も多いと思うの。でも、出力だけを変えたいのにいちいち大元のアクションメソッドをオーバーライドするのは面倒じゃない?deviseちゃんでは特定のアクションで成功、失敗した時にjsonをレンダリングするメソッドが個別で用意されてるの。要は必要に応じてそこをいじればいいってわけね。
このメソッドちゃんたちの存在は公式ドキュメントに載ってるの。この記事で日本語訳してくださってる方がいるので貼っておくわ。
【翻訳】devise-auth-token公式ドキュメント表を見れば分かる通り、今回私が整形したいのはログインの成功時のjsonよ。つまりレンダリングメソッドで言うと
render_create_success
ね。早速オーバーライドしちゃいましょう。app/api/v1/auth/session_controller.rbclass Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController def render_create_success render json: @resource, serializer: UserSerializer end endログインが成功した時のリソース、つまりuserの情報は@resourceで取得できるわ。これをserializerで整形すると...
この通り!jsonが構造化されてカラムも設定したものしか返されていないわ!
参考文献
情報が比較的少ない中で有益な記事の存在に助けられました。心よりの感謝を申し上げます。
- 投稿日:2020-02-24T16:56:24+09:00
devise token authのjsonをserializerで整形するわよの巻
はじめに
この記事ではdevise token authでのjsonの出力を
active_model_serializer
gemを使って整形する手順を解説するわ。実際の開発中に少し詰まったことを元にしているからゴールとして"ユーザーのログイン成功時に返されるjsonをserializerで整形する"を設定するわ。他のアクション後に返されるjsonにも応用できるように心掛けるから安心しなさい。
devise関連の導入まで含めちゃうと長くなるからそこは割愛するわ、ごめんなさいね。準備するわよの巻
まずはgemのインストールから始めるわ。
Gemfile.gem 'active_model_serializers'bundle installこれでactive_model_serializersのインストールが完了したわ。
このgem自体の説明は他の記事に譲るとしてまずは設定をするわ。
設定用のconfig/initializers/ams.rb
ファイルを手動で追加しなさい。config/initializers/ams.rbActiveModel::Serializer.config.adapter = :json_apiこの部分では出力するjsonのフォーマットを指定しているわ。オプションの詳しい説明についてはこちらの記事に詳しいわ。
Railsのactive_model_serializerについて学ぶ100DaysOfCodeチャレンジ10日目(Day10:#100DaysOfCode)
ここでは
json_api
を指定したけど、あなたの好きなように設定しなさい。さて、次はいよいよserializerのファイルを作っていくわよ。
rails generate serializer User最後の
User
の部分は整形したいモデル名を指定することね。このコマンドでapp/serializers/user_serializer.rb
ファイルが作成されたわ、早速いじいじしちゃいましょう。app/serializers/user_serializer.rbclass UserSerializer < ActiveModel::Serializer attributes :id, :name, :nickname, :image, :confirmed_at, :updated_at endここではjsonで返すモデルのカラムを指定しているわ。他にも色々設定できるけど適宜自分で調べなさい、頑張ることね。
本題に入るわよの巻
ここまでで準備は完了よ、configに追加した設定を読み込ませるためにこのあたりで一旦サーバを再起動しておくことね、ん?もうやった? 良い子ね、クマちゃんのビスケットをあげるわ。
さて、まず見て欲しいものがあるの、serializerで整形をしない場合のログイン成功時に返されるjson(ログインに成功したユーザーの情報)よ。
続いて同じjsonをserializerで整形した場合よ。
データが構造化されているのと設定したカラムのみが出力されていることが分かるわね。
ちなみに下はapp/controllers/api/v1/user_controller.rbclass Api::V1::UsersController < ApplicationController def show @user = User.find(params[:id]) render json: @user, serializer: UserSerializer end endこんな感じのコードよ。
見ての通り、deviseの方のjsonも整形しないとアプリケーション全体として返すjsonのフォーマットに統一性が取れないことが分かるわ。これはフロント側としても混乱するしメンテナンス性も大きく損なうわ。
でも、自分で一から書くアクションなら上のコードみたいに簡単に実装できるんだけどdeviseは内部で勝手にやってくれるから分かりづらいわよね。
そこで今回は
1. コントローラを継承する
2. そこで各アクションごとのレンダリングメソッドをオーバーライドしてjsonをserializerで整形するという手順でdeviseで返されるjsonの整形をするわ。
コントローラの継承をするわよ編
今回はログイン時のjsonを整形したいということで、ログイン時に呼ばれるアクションは
devise_token_auth/sessions#create
よ。ということで
devise_token_auth/sessions
コントローラを継承するわ。app/controllers/api/v1/auth/sessions_controller.rbclass Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController end新しく作ったコントローラが参照されるようにルーティングも変更よ。
私の場合はconfig/routes.rbRails.application.routes.draw do namespace :api do namespace :v1 do resources :users, only: [:show] mount_devise_token_auth_for 'User', at: 'auth', controllers: { registrations: 'api/v1/auth/registrations', # ここの部分ね sessions: 'api/v1/auth/sessions' } end end endさて、準備が整ったところでさっきの手順の2の太字になっている各アクションごとのレンダリングメソッドの説明をするわ。出力を制御する、と聞いて最初に対象のアクションをそのままオーバーライドすることを考えた子も多いと思うの。でも、出力だけを変えたいのにいちいち大元のアクションメソッドをオーバーライドするのは面倒じゃない?deviseちゃんでは特定のアクションで成功、失敗した時にjsonをレンダリングするメソッドが個別で用意されてるの。要は必要に応じてそこをいじればいいってわけね。
このメソッドちゃんたちの存在は公式ドキュメントに載ってるの。この記事で日本語訳してくださってる方がいるので貼っておくわ。
【翻訳】devise-auth-token公式ドキュメント表を見れば分かる通り、今回私が整形したいのはログインの成功時のjsonよ。つまりレンダリングメソッドで言うと
render_create_success
ね。早速オーバーライドしちゃいましょう。app/api/v1/auth/session_controller.rbclass Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController def render_create_success render json: @resource, serializer: UserSerializer end endログインが成功した時のリソース、つまりuserの情報は@resourceで取得できるわ。これをserializerで整形すると...
この通り!jsonが構造化されてカラムも設定したものしか返されていないわ!
参考文献
情報が比較的少ない中で有益な記事の存在に助けられました。心よりの感謝を申し上げます。
- 投稿日:2020-02-24T16:39:23+09:00
[font-awsomeのエラー]File to import not found or unreadable: font-awesome-sprockets.のエラーについて
1.エラーの様子
デスクトップ(裏側で製作していたファイル)では動いていたデータをホームディレクトリ(本番のファイル)に移した時上記のようなエラーが出ました。
2.エラーの原因
1.一般的な原因
・Gemfileのなかに
gem 'font-awesome-sass'
が抜けている・app/stylesheets/application.scssの中に
@import "font-awesome-sprockets"
と@import "font-awesome";
が抜けている2.見落としがち(特殊)な原因
・
@import "font-awesome-sprockets"
とするところを@import "font-awesome-compass";
としている。またはその逆。・別フォルダで作成したファイルを移すときに、
rails s
をしたままgemを追記し、bundle install
している3.解決方法
1.Gemfileのなかに
gem 'font-awesome-sass'
が抜けているGemfileに
gem 'font-awesome-sass'
を追記して下記コマンドを打ちましょう$ bundle install2.app/stylesheets/application.scssの中に
@import "font-awesome-sprockets"
と@import "font-awesome";
が抜けているapp/stylesheets/application.scssの中に下記2行を追記しましょう
@import "font-awesome-sprockets" @import "font-awesome";3.
@import "font-awesome-sprockets"
とするところを@import "font-awesome-compass";
としている。githubに記載されている公式の使用方法(readmeの部分)を読んで、どちらが自分の使用すべきコマンドか確認してみましょう。公式は下記urlです。
https://github.com/FortAwesome/font-awesome-sass
4.別フォルダで作成したファイルを移すときに、
rails s
をしたままgemを追記し、bundle install
している・Gemfilはサーバーを起動したままだと反映されないため、railsを再起動しましょう。コマンドは
controll+c
でrailsサーバーを落とし、再度rails s
をしましょう・上記でうまくいかない場合、他のもともとあったgemと干渉しあっている可能性があるためGemfile.lock(gemの実行コードが自動生成されたファイル)を全て削除し
bundle install
をしましょう。その際もサーバーを起動中の方はcontroll+c
でrailsサーバーを落とし、再度rails s
をしましょう
- 投稿日:2020-02-24T16:19:42+09:00
[Bootstrap]ドロップダウンボタンのアイコンを変更&「ドロップダウンがボタンに対して右下に出ちゃう!」と「選択肢の下に変な余白が!?」を解消
概要
表題の通りですが、長いので下記にまとめます。
1.ドロップダウンボタンのアイコンを変更する。
2.ドロップダウンがボタンに対して右下に出ちゃう!」を解消。
3.「選択肢の下に変な余白が!?」を解消。すべて簡単ですが、2.3に少しハマったのでメモを兼ねて共有させていただきます。
環境
Ruby:2.6.3
Rails:5.1.6
bootstrap:4.4.1
FontAwesome方法
1.ドロップダウンボタンのアイコンを変更する。
-button.btn.dropdown-toggleの中にFontawesome(iタグ)を通常通り配置。
-デフォルトのボタンを消す(.dropdown-toggle::after { display: none; })。2.ドロップダウンがボタンに対して右下に出ちゃう!」を解消。
div.dropdown-menuにdropdown-menu-rightクラスを追加。
→position:absolute関連のleftプロパティの値をいじっていたが、これだけでよかった。
要は、ドロップダウンメニューの起点がボタンに対して左上(デフォルト)になっているのを右上に変更する。3.「選択肢の下に変な余白が!?」を解消。
div.dropdown-item(下記link_toメソッド(HTMLではaタグ))の後にいくつかの半角スペース
→これが選択肢の内容として認識されていた為、選択肢の下の「変な余白」として反映されていた。home.html.erb<div class="dropdown"> <button class="btn dropdown-toggle" type="button" id="dropdownMenuButton", data-toggle="dropdown"> <i class="fas fa-chevron-down"></i> </button> <div class="dropdown-menu dropdown-menu-right"> <%= link_to "削除する", dreampost, class: 'dropdown-item', method: :delete, data: { confirm: "削除してよろしいですか?" } %> </div> </div>custom.scss.dropdown-toggle::after { display: none; }補足
ドロップダウンボタンのアイコンは、親要素にdisplay:flexとjustify-content:space-betweenで右端寄せしています。
ご指摘などございましたら、ぜひよろしくお願いいたします。
- 投稿日:2020-02-24T16:00:47+09:00
Ruby/GTK3 - AboutDialog
gem install gtk3
AboutDialog
require 'gtk3' unless Gtk::Version.or_later?(3, 4, 2) puts "This sample requires GTK+ 3.4.2 or later: #{Gtk::Version::STRING}" exit end a = Gtk::AboutDialog.new a.artists = ['Artist 1 <no1@foo.bar.com>', 'Artist 2 <no2@foo.bar.com>'] a.authors = ['Author 1 <no1@foo.bar.com>', 'Author 2 <no2@foo.bar.com>'] a.comments = 'This is a sample script for Gtk::AboutDialog' a.copyright = 'Copyright (C) 2020 Ruby-GNOME Project' a.documenters = ['Documenter 1 <no1@foo.bar.com>', 'Documenter 2 <no2@foo.bar.com>'] a.license = 'This program is licenced under the same licence as Ruby-GNOME.' a.logo = GdkPixbuf::Pixbuf.new(file: File.join(__dir__, 'gnome-logo-icon.png')) a.program_name = 'Gtk::AboutDialog sample' a.translator_credits = "Translator 1\nTranslator 2\n" a.version = '1.0.0' a.website = 'https://ruby-gnome2.osdn.jp' a.website_label = 'Ruby-GNOME Project Website' a.signal_connect('activate-link') do |_widget, uri| p _widget.class p uri end p a.run#!/usr/bin/env ruby # frozen_string_literal: true # aboutdialog2.rb - Ruby/GTK sample script. # # Copyright (c) 2005-2020 Ruby-GNOME Project Team # This program is licenced under the same licence as Ruby-GNOME. require 'gtk3' unless Gtk::Version.or_later?(3, 4, 2) puts "This sample requires GTK+ 3.4.2 or later: #{Gtk::Version::STRING}" exit end about_dialog = Gtk::AboutDialog.show( nil, 'artists' => ['Artist 1 <no1@foo.bar.com>', 'Artist 2 <no2@foo.bar.com>'], 'authors' => ['Author 1 <no1@foo.bar.com>', 'Author 2 <no2@foo.bar.com>'], 'comments' => 'This is a sample script for Gtk::AboutDialog', 'copyright' => 'Copyright (C) 2005-2020 Ruby-GNOME Project', 'documenters' => ['Documenter 1 <no1@foo.bar.com>', 'Documenter 2 <no2@foo.bar.com>'], 'license' => 'This program is licenced under the same licence as Ruby-GNOME.', 'logo_icon_name' => 'gtk-home', 'program_name' => 'Gtk::AboutDialog sample', 'translator_credits' => "Translator 1 <no1@foo.bar.com>\nTranslator 2 <no2@foo.bar.com>\n", 'version' => '1.0.0', 'website' => 'https://ruby-gnome2.osdn.jp', 'website_label' => 'Ruby-GNOME Project Website' ) about_dialog.signal_connect('delete_event') do Gtk.main_quit end Gtk.main
- 投稿日:2020-02-24T13:06:34+09:00
【Rails男子必見】エラー系女子との会話が弾む3つのテクニック【モテたい】
はじめに
添野です。TECH::EXPERTというプログラミングスクールに通っています。
7日目で基礎本試験を突破しました。今日は
エラー系女子にモテたい男子必見
Railsのエラー問題で苦戦中の方向けの記事です。モテる男子の会話テクニック
モテる男子がどんな会話をしているのか、渋谷のエラー系女子にインタビューしてみました。
✅ちゃんと話(エラー文)を聞いてくれて、私のことをしっかり考えてくれる(20歳 OL)
✅私が怒って(エラーを出して)も何の話をしてるのか考えてくれて、先回りで行動してくれる(17 歳JK)
✅前に話したこと(エラー)を覚えていて、何度も同じことを言わなくていいと気が楽ですね(26歳 看護師)話を聞いてくれる男子と一緒にいたいと感じるようですね。
それでは、インタビューで出た女子の意見について詳しく見ていきましょう。1. ちゃんと話を聞く「エラー文を見る」
皆さんは彼女とケンカになって、一方的に自分の意見を言ってしまった経験はありませんか?
モテる男子になるにはまず、エラー系女子の話をちゃんと聞いてあげましょう。
例えば、次のエラー文を見てください。
実はエラー文には「直すべきファイルが何か」「どう直せばいいか」が書かれているときがあります。
彼女は「あんたのここがダメだって言ってんでしょ!こうしてよ!」ってわざわざ教えてくれてます。
「showビューの6行目"user_signed_in"を"user_signed_in ? "に直せ」と書いてあるので、
これを直すだけで彼女の怒りは収まり、あなたはモテる男に生まれ変わります。とにかく、最初は彼女の言っていることをしっかり聞きましょう。
2. 何の話か考える「対象のファイルを特定する」
上記のように直すべき点を指摘してくれる場合は楽ですが、世の中親切な女の子ばかりではありません。
それでも、好きな子には振り向いて欲しいですよね。そんなアナタにとっておきの方法です。次のエラー文を見てください。
これは、pictweetアプリでログアウトボタンをクリックしたら発生しました。
これだけでは何のエラーかさっぱりですが、モテる男子はここで「あの話かな?」と先読みをします。
そして、先読みをするには「直前にあなたがとった行動」を思い出してください。
例えば、上記のエラーでは直前に「ログアウトボタン」をクリックしました。
ということは「ログアウトボタンに関係する何か」で彼女が怒っているのだと先読みできます。ログアウトボタンはapplicationビューに書かれているので、ファイルの中を見てみると、
「, method: :delete」という記述が抜けていることに気付きます。これが彼女の怒りの原因です。before <%= link_to "ログアウト", destroy_user_session_path> after <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
ここで、「記述抜けなんて、そんなの気づけなくね?」と思うかもです。
そんな時は作りたいアプリの完成形を横に並べておきましょう。補助輪を付けるんです。「そんなのダサいし勉強にならない」と思うかもしれません。ですが、プライドを捨ててください。
この方が早く成長できます。モテる男は、無駄なプライドは捨てています。
モノマネ・パクりから始めましょう。自分磨きは、その後です。こうして、モテる男は常に女の子の不満を先読みし、自分の行動を変えていきます。
3. 前に話したことを覚えている「エラーリストを作る」
これも特に多かった意見です。何度も同じことを言わせる男子はモテません。
一度指摘されたり、先読みで特定した彼女の不満をリスト化して、常にエラーに備えましょう。インタビューでは、「エラー系女子を怒らせたRails男子の行動Top14」も調査してきました。
ランキング形式で発表していきます。モテたい男子の皆さん、要チェックですよ!!第14位「検索すると全ての投稿が表示される」
「検索用パラメータ受け渡し時のキー名の記載ミス」
対象ファイル:indexビューbefore <%= form.text_field :keywords, placeholder: "投稿 検索", class: "search-input" %> after <%= form.text_field :keyword, placeholder: "投稿 検索", class: "search-input" %>第13位「ArgumentError」
「引数指定の記載漏れ」
対象ファイル:tweetモデルbefore def self.search after def self.search(search)第12位「ActionControl ler::ParameterMissing」
「form_withを使用しているのにformの記述が抜けている」
対象ファイル:showビューbefore <%= form_with(model: [@tweet,@comment], local: true) do |form| %> <textarea cols="30" name="text" placeholder="" rows="2"></textarea> <input type="submit" value="SEND"> <% end %> after <%= form_with(model: [@tweet,@comment], local: true) do |form| %> <%= form.text_area :text, placeholder: "text" , rows: "2" %> <%= form.submit "SEND" %> <% end %>第11位「リファクタリング問題」
「省略できる記述が省略せずに書かれている」
対象ファイル:indexビューbefore <% @tweets.each do |tweet| %> <%= render partial: "tweet", locals: { tweet: tweet } %> <% end %> after <%= render @tweets %>「彼氏のメールが長すぎて何を言いたいか分からない」
第10位「Undefined method」
パターン1「ログイン判定記述ミス」
対象ファイル:applicationビューbefore <% if true %> after <% if user_signed_in? %>パターン2「deviseメソッドの記述位置ミス」
対象ファイル:ルーティングファイルbefore resources :tweets devise_for :users after devise_for :users resources :tweets第9位「AcctiveRecord::RecordNotFound」
パターン1「ログアウトボタン記述ミス」
対象ファイル:applicationビューbefore <%= link_to "ログアウト", destroy_user_session_path %> after <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>パターン2「namespaceメソッドとresourcesメソッドの記述順ミス」
対象ファイル:ルーティングファイルbefore resources :tweets do resources :comments, only: :create end namespace :tweets do resources :searches, only: :index end after namespace :tweets do resources :searches, only: :index end resources :tweets do resources :comments, only: :create end第8位「NotNullViolation」
「deviseパラメータに指定しているキー名の記述ミス」
対象ファイル:applicationコントローラーbefore devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) after devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname]「イタリアンを食べに行こうって言ってデートにきたら寿司屋に連れてかれた」
第7位「空のツイートを保存できてしまう」
「バリデーションの記述がない」
対象ファイル:tweetモデルbefore after validates :text, :image, presence: true第6位「ターミナルでNo template found」
「ページ切替のリダイレクトアクション記載漏れ」
対象ファイル:commentsコントローラーbefore after redirect_to tweet_path(@comment.tweet)第5位「テキストや画像、コメントが表示されない」
「ストロングパラメーターの記述ミス」
対象ファイル:tweetsコントローラーbefore { user_id: current_user.id } after params.permit(:image, :text).merge(user_id: current_user.id)「LINEで素直な感謝の気持ちを送ったのに、未読スルー」
第4位「NoMethodError」
パターン1「renderメソッドで誤字」
対象ファイル:newビューbefore <%= render partial: "form", locals: { form: @form } %> after <%= render partial: "form", locals: { form: form } %>「デートの集合場所の住所をLINEで送ってきたのに、間違ってる」
パターン2「commentのルーティングがネストされてない」
対象ファイル:ルーティングファイルbefore resources :tweets resources :comments, only: :create after resources :tweets do resources :comments, only: :create end「デートの集合場所を、私が入ってないLINEグループにLINEしたことに気づいてない」
パターン3「コントローラーの誤字」
対象ファイル:tweetsコントローラーbefore def index @post = Post.includes(:user) end after def index @posts = Post.includes(:user) endパターン4「パス指定順のミス」
対象ファイル:showビューbefore <%= form_with(model: [@comment, @tweet], local: true) do |form| %> after <%= form_with(model: [@tweet, @comment], local: true) do |form| %>第3位「ツイート保存不可(表示されない)」
「ストロングパラメーターのrequireメソッド記述抜け」
対象ファイル:tweetsコントローラーbefore params.permit(:image, :text).merge(user_id: current_user.id) after params.require(:tweet).permit(:image, :text).merge(user_id: current_user.id)第2位「ActionView::MissingTemplate」
パターン1「ビューファイルがない/配置が間違ってる」
対象ファイル:_tweetビュー(部分テンプレート)before 部分テンプレート名が"tweet.html.erb" after 部分テンプレート名を"_tweet.html.erb"に修正「東京タワーを展望できるホテルというタレコミでデートにきたのに、東京タワーが見えない部屋を予約していた」
パターン2「renderメソッドの記述ミス」
対象ファイル:showビューbefore <%= render partial: "/post", locals: { post: post } %> after <%= render partial: "posts/post", locals: { post: post } %>第1位「Rounting Error」
対象ファイル:ルーティングファイル
パターン1 resourcesメソッドの誤字または抜け
indx→indexに修正、:createが抜けているので追加before resources :tweets, only: [:indx, :show, :new, :destroy, :edit, :update] do after resources :tweets, only: [:index, :show, :new, :create, :destroy, :edit, :update] doパターン2 rootメソッドの誤字
before root to: 'post#index' after root to: 'posts#index'「デート当日になって、デート先を決めてない/デート先を忘れた/道を間違えた」
まとめ
いかがだったでしょうか。
最後にもう一度、モテる男子の特徴をおさらいしましょう。✅ちゃんと話を聞く「エラー文を見る」
✅何の話か考える「対象のファイルを特定する」
✅前に話したことを覚えている「エラーリストを作る」大まかに理解できたら、上記でなぜモテるのかを考えて、自分を磨いていきましょう。
以上、Rails男子がエラー系女子にモテる方法でした。おすすめ記事
Rails消化のコツ
Rails用語集 基礎
Railsは"5つの属性"を意識しろ