- 投稿日:2020-07-09T23:11:14+09:00
デバッグを表示する
何度も忘れるのでメモ。
デバッグを使う事でより開発がスムーズになる。
現在表示されているページのcontroller,view,actionなどが確認できる。
注意点として本番環境ではデバッグ情報は表示するべきではありません。そのため、次のように記述することで開発環境のみデバッグ情報を表示されるよう対策しています。
まずはgem 'byebug'をbundle installする。
その際development、test環境で使用するのでgroup :development, :test do~end内に書き込む。group :development, :test do gem 'sqlite3' gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end$ bundle install下記コードをapp/views/layouts/application.html.erbに書き込む。
<%= debug(params) if Rails.env.development? %><html> <head> <title><%= full_title(yield(:title)) %></title> <%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> <%= render 'layouts/shim' %> </head> <body> <%= render 'layouts/header' %> <div class="container"> <%= yield %> <%= debug(params) if Rails.env.development? %> </div> </body> </html>これで表示、使用可能になります。
- 投稿日:2020-07-09T22:38:31+09:00
railsのbelongs_toに指定できるoptional: true
class Task < ApplicationRecord has_many :user end class User < ApplicationRecord belongs_to :task, optional: true endこのoptional: trueとは何なのか?
これはbelongs_toの外部キーのnilを許可するというもの!
上記のコードであれば、User.task_idが外部キーとなり、値がセットされていない場合はバリデーションで弾かれるが、optional: trueを設定しておくと、外部キーがnilであってもDBに保存できる!
- 投稿日:2020-07-09T22:13:56+09:00
find_or_create_by
find_or_create_by
引数の条件に指定するデータがあった場合はそのデータを返します。無かった場合は新規作成します。
def find_or_create_by(attributes, &block) find_by(attributes) || create(attributes, &block) endメリット
結果が冪等になる。
冪等とは何度行っても得られる結果が等しいこと。すでに指定するデータが存在した場合は新規作成をしないので、
find_or_create_byを繰り返したとしても、得られる結果が等しいです。対してcreateメソッドだった場合は、
繰り返した分だけ新規作成が行われるので冪等ではないと言えます。
- 投稿日:2020-07-09T21:22:14+09:00
RailsとAjaxを使ったいいね機能の非同期通信
転職活動用に個人アプリを開発中です。
今回、RailsとAjaxを使って、いいね機能の非同期化を行いました。AjaxではjQueryを使うため、jQueryを使えるようにしておく事前準備が必要です。それは参考記事を見てください。以下の記述はそれが設定済みのうえでの話です。
実現した機能
・「いいね」ボタンを押すとリロードせずに「いいねを取り消す」に表示が変わる
・「いいね」ボタンを押すとリロードせずにlikesテーブルにデータが1つ追加される
・「いいね」ボタンを押すとリロードせずにいいね数が1つ増える
※その逆もしかりこのコードでうまくいきました
コントローラー(likes_controller.rb)
likes_controller.rbclass LikesController < ApplicationController def create @post = Post.find(params[:post_id]) @like = current_user.likes.build(post_id: params[:post_id]) @like.save @likeCounts = Like.where(post_id: params[:post_id]) end def destroy @post = Post.find(params[:post_id]) @like = Like.find_by(post_id: params[:post_id], user_id: current_user.id) @like.destroy @likeCounts = Like.where(post_id: params[:post_id]) end end個別の投稿ページ(上記の画像のページ)
show.html.haml.like = render partial: "likes/like", locals: {post: @post}部分テンプレート(_like.html.haml)
_like.html.haml- if user_signed_in? - if current_user.already_liked?(post) = button_to 'いいねを取り消す', post_like_path(post_id: post.id, id: post.likes[0].id), method: :delete, remote: true - else = button_to 'いいね', post_likes_path(post.id), method: :post, remote: true .likeCounts いいね数: = post.likes.count更新したい部分のビュー(いいねしたとき)
create.js.erb$('.like').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");更新したい部分のビュー(いいねを取り消すとき)
destroy.js.erb$('.like').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");JavaScriptが動く仕組み
・
link_to
やbutton_to
には:remoteオプション(remote: true
)がある。button_to
にremote: true
を追加することで、js形式のリクエストを送信できるようになる。= button_to 'いいね', post_likes_path(post.id), method: :post, remote: true参考:Railsガイド
非同期通信の流れ
1.「いいね」ボタンを押す
2.下記のリンクでlikes#create
にjs形式でリクエストが飛ばされる= button_to 'いいね', post_likes_path(post.id), method: :post, remote: true3.likesコントローラーのcreateアクションが動き、いいねが保存される。Likeモデルを介してデータベースに追加される(リロードせずに)
4.更新したい部分のページcreate.js.erb
,destroy.js.erb
がレスポンスとして返される。.html
(jQueryのhtmlメソッド)は、.like
(likeクラス)の部分をhtmlの後ろの( )内に置き換える役割をはたす。今回苦労したところ
1.部分テンプレートの理解
create.js.erb$('.like').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");partialオプション:部分テンプレートの呼び出しを行う。今回はlikesフォルダの_like.html.hamlを呼び出したいので
likes/like
となる。localsオプション:部分テンプレート内で{ }内の左辺が変数として使えるようになる。今回でいうと
post
が変数として使えるようになる。右辺の@post
は何かというと、右辺の@post
が左辺のpost
に代入して、それが変数として使えるようになる。右辺の@post
はどこからきているかというと、postsコントローラのshowアクションで定義しているので、そこからきている。posts_controller.rbdef show @post = Post.find(params[:id]) Like.new end2.
_like.html.haml
のbutton_to
のpathの設定
ずっとこのエラーに悩まされていました。ActionView::Template::Error No route matches (中略) missing required keys: [:id])この記事を見つけてようやく下記のように記述して解決できました。
= button_to 'いいねを取り消す', post_like_path(post_id: post.id, id: post.likes[0].id), method: :delete, remote: true解決はしたものの、この部分
id: post.likes[0].id
がまだちゃんと理解できていません。
1つの投稿に複数のいいねがついていたとして、その最初のいいねのidを取得している?今考えてみると、確かに1つの投稿に複数のいいねがある場合、「どのいいねを取り消すの?指定してくれないとわからないよ」と言われても無理ないなと思いました。
だとすると、必ずしもcurrent_userの付けたいいねではなく、他の人の付けたいいねを取り消してしまう?
まだ修正する必要があるかもしれません?
(追記)
Sequel Proで確認したところ、他の人のいいねを取り消してしまうことはなく、ちゃんとcurrent_userのいいねが取り消されていました。
一応いいねを消せることは消せます。
この点が明らかになったらまた追記します。参考記事
Railsで remote: true と js.erbを使って簡単にAjax(非同期通信)を実装しよう!(いいね機能のデモ付)
【Rails×Ajax】いいね機能ハンズオン
Railsでいいね機能を実装。Ajaxを使い非同期対応。で
- 投稿日:2020-07-09T18:44:59+09:00
Enumの設定ミス。なぜかEnum指定の列からデータが取れない…
発生した背景
※備忘録として記録します(ネタ的にはしょぼいです…)
以下のようなモデルにEnum設定したとき、データが取り出せない。table.rbclass ColorManage < ApplicationRecord enum color_type: { single: 0, double: 1, graphic: 2 } (省略)(前提)singleというラジオボタンを押すと、0が入力されるようにする。
color-edit.html.erb<!-- カラータイプ、どの設定にするか。--> <%= color_manage_record.radio_button :color_type, :single ,class:"color-form__button" %> <%= color_manage_record.label :color_type, "single-color", {class: "color-form__tag"} %>問題点> 画像のとおり、color_typeには"ゼロ"で入力されていますが、モデル抽出して、列値を取得しようとしてもnullで返ってくる
環境
項目 内容 OS.Catalina v10.15.4 Ruby v2.5.1 Ruby On Rails v5.2.4.3 MySQL V5.6 対応手順
原因)テーブルの定義がstringだったので、enumを以下のように定義しなおしました。ポートフォリオなので、恥ずかしながら、設計側を変えた次第です。
test.rbenum color_type: { single: "single", double: "double", graphic: "graphic" }これで解決しました。
以上です。
- 投稿日:2020-07-09T18:33:30+09:00
Timeオブジェクトの次の値ってどうやってだすのが正しい?
概要
RubyでTimeオブジェクトを扱ってると境界問題にぶち当たる。
普通にやってるとRangeオブジェクトなりを使ってそこまで問題になることはないが、普通じゃないやり方をしていて躓いた。環境
Ruby: 2.6.2
Rails: 5.1.2そもそもハマった問題
Time.now.end_of_day => 2020-05-15 23:59:59 +0900これの次の値を取りたい。
※ end_of_dayはわかりやすいから使ってるだけであって、とある日の先端、終端が欲しいわけではない。飽くまでもTimeオブジェクトの次の値真っ先に思いついたのがこれ
Time.now.end_of_day + 1 => 2020-05-16 00:00:00 +0900ただ、これは間違い
(Time.now.end_of_day + 1).iso8601(3) => "2020-05-16T00:00:00.999+09:00"
+ 1
は飽くまでも1秒加算であって、次の値に移行するわけではないので使えない。Timeクラスを漁ってるとすごくそれっぽいメソッドを見つけた
https://docs.ruby-lang.org/ja/latest/method/Time/i/succ.htmlTime.now.end_of_day.succ (pry):109: warning: Time#succ is obsolete; use time + 1 => 2020-05-16 00:00:00 +0900しかし、時代遅れだから使うなって怒られる上
+ 1
と同じことしてるだけらしい。だめ。解決方法
結局良さげな方法はわからなかったからゴリ押し
例の問題だと少数9桁までしか見ていない
(Time.now.end_of_day + 1).iso8601(10) => "2020-05-16T00:00:00.9999999990+09:00"つまり、1ナノ秒加算してやればいいわけだ
(Time.now.end_of_day + 1/1000000000.0).iso8601(9) => "2020-05-16T00:00:00.000000000+09:00"できた!
おまけ
Time.now.iso8601(10) => "2020-05-15T19:56:55.4979370000+09:00" Time.new(2020, 5, 16, 16, 59, 59.3).iso8601(9) => "2020-05-16T16:59:59.299999999+09:00" Time.parse("2020-05-16T16:59:59.3+09:00").iso8601(9) => "2020-05-16T16:59:59.300000000+09:00" Time.parse("2020-05-16T16:59:59.35555555555555555555+09:00").iso8601(20) => "2020-05-16T16:59:59.35555555555555555555+09:00"だめだった
結論
- Timeオブジェクト内の小数秒の桁数は無限に持てる
- そのため次の値という概念がそもそも無い
- ただ、Timeオブジェクトの生成の仕方に依存するので、DBから引っ張ってくるならその有効桁数を考慮してやれば無理やり作れなくはない
- そもそも素直にRangeオブジェクトを使うなりして比較すべき
- 投稿日:2020-07-09T18:13:18+09:00
Rails5でECサイトを作る⑥ ~seedデータ投入~
はじめに
架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る⑤の続きです。
この後の実装を続けていく上で、初期データを入れておいた方が何かと都合が良さそうなので、一旦そちらを優先することにしました。複数のseedファイルを管理する
あくまで表示が正しくできるか確認するためのデータなので、最小限作っておけば良いと思います。しかし、Model数が多い分、やはり一つでファイルで全て済まそうとすると記述量が膨大になってしまいます。
そこで、新たにseedsというフォルダを作り、その中に各Model毎の初期データを入れることにしました。(一つのseeds.rbファイルで済ませたい人はこちら)$ cd work/fumizuki/db $ mkdir seeds $ cd seeds $ touch customer.rb $ touch address.rb $ touch genre.rb $ touch product.rb $ touch order.rb $ touch order_item.rbファイル群の読み込み、Adminデータ作成
作成したファイル群をseeds.rbファイルから読み込めるようにします。これでrails db:seedを叩いた時にseedsフォルダ内のデータも認識されます。
ここで注意が必要な点は、親Modelを上に記述することです。ファイルの読み込みは上の行から順番に行われるため、子Modelを先にしてしまうと「Customerデータがありません!」とエラーになります。db/seeds.rbrequire './db/seeds/customer.rb' require './db/seeds/address.rb' require './db/seeds/genre.rb' require './db/seeds/product.rb' require './db/seeds/order.rb' require './db/seeds/order_item.rb' Admin.create!( id: 1, email: 'admin@user', password: 'adminuser', password_confirmation: 'adminuser' )Adminのデータもこちらに書いておきました。管理者のデータは1件あれば十分で、わざわざ別ファイルに分けるほどのコード量ではないので。
データをたくさん作る
Address
db/seeds/.rbAddress.create!( [ { id: 1, customer_id: 1, addressee: '稲継亜矢子', post_code: '1111111', address: '月川県岩青山町四南寺2-15', }, { id: 2, customer_id: 2, addressee: '鈴鹿由美子', post_code: '2222222', address: '細野県城見市世史が丘3-1-7', }, { id: 3, customer_id: 3, addressee: '関沢智恵美', post_code: '3333333', address: '赤田県初山市十越智町6-12', }, { id: 4, customer_id: 4, addressee: '巻譲', post_code: '4444444', address: '根野県羽島市後目台8-12', }, { id: 5, customer_id: 5, addressee: '岡崎弥生', post_code: '5555555', address: '古岡県紫波市刈唯山3-5-2', }, ] )Customer
db/seeds/.rbCustomer.create!( [ { id: 1, email: '1@1', family_name: '長岡', family_name_kana: 'ナガオカ', first_name: '聡美', first_name_kana: 'サトミ', post_code: '1111111', address: '北岡県乙西川市馬城寺2-6-1', tel: '11111111', is_active: true, password: '111111', password_confirmation: '111111', }, { id: 2, email: '2@2', family_name: '野崎', family_name_kana: 'ノザキ', first_name: '健吾', first_name_kana: 'ケンゴ', post_code: '2222222', address: '鳥川県盤上山市升沖ヶ丘1-15', tel: '22222222', is_active: true, password: '222222', password_confirmation: '222222', }, { id: 3, email: '3@3', family_name: '妹尾', family_name_kana: 'セオ', first_name: '千代美', first_name_kana: 'チヨミ', post_code: '3333333', address: '仁田県那珂和町山巻学園3-2', tel: '33333333', is_active: true, password: '333333', password_confirmation: '333333', }, { id: 4, email: '4@4', family_name: '塚越', family_name_kana: 'ツカゴシ', first_name: '貴美', first_name_kana: 'タカミ', post_code: '4444444', address: '笠川県冬田市志林川町5-2-7', tel: '44444444', is_active: true, password: '444444', password_confirmation: '444444', }, { id: 5, email: '5@5', family_name: '鎌田', family_name_kana: 'カマタ', first_name: '幸宏', first_name_kana: 'ユキヒロ', post_code: '5555555', address: '早田県響山市大字威初2-16', tel: '55555555', is_active: true, password: '555555', password_confirmation: '555555', }, ] )Genre
db/seeds/.rbGenre.create!( [ { id: 1, name: '食パン', validity: true }, { id: 2, name: '総菜パン', validity: true }, { id: 3, name: '菓子パン', validity: true }, { id: 4, name: '限定', validity: true }, { id: 5, name: 'その他菓子', validity: true }, ] )Order
db/seeds/.rbOrder.create!( [ { id: 1, customer_id: 1, addressee: '稲継亜矢子', post_code: '1111111', send_to_address: '月川県岩青山町四南寺2-15', how_to_pay: true, order_status: 0, }, { id: 2, customer_id: 2, addressee: '鈴鹿由美子', post_code: '2222222', send_to_address: '細野県城見市世史が丘3-1-7', how_to_pay: true, order_status: 2, }, { id: 3, customer_id: 3, addressee: '関沢智恵美', post_code: '3333333', send_to_address: '赤田県初山市十越智町6-12', how_to_pay: true, order_status: 1, }, { id: 4, customer_id: 4, addressee: '巻譲', post_code: '4444444', send_to_address: '根野県羽島市後目台8-12', how_to_pay: false, order_status: 2, }, { id: 5, customer_id: 5, addressee: '岡崎弥生', post_code: '5555555', send_to_address: '古岡県紫波市刈唯山3-5-2', how_to_pay: true, order_status: 0, }, ] )OrderItem
db/seeds/.rbOrderItem.create!( [ { id: 1, order_id: 1, product_id: 10, quantity: 4, order_price: 270, make_status: 2, }, { id: 2, order_id: 1, product_id: 7, quantity: 6, order_price: 230, make_status: 1, }, { id: 3, order_id: 2, product_id: 2, quantity: 2, order_price: 260, make_status: 0, }, { id: 4, order_id: 3, product_id: 3, quantity: 6, order_price: 180, make_status: 1, }, { id: 5, order_id: 4, product_id: 4, quantity: 8, order_price: 370, make_status: 1, }, { id: 6, order_id: 5, product_id: 5, quantity: 10, order_price: 160, make_status: 2, }, ] )Product
db/seeds/.rbProduct.create!( [ { id: 1, name: 'ブリオッシュ', introduction: 'バターの風味豊かな食パンです。', genre_id: 1, price: 500, status: true, }, { id: 2, name: 'バゲット', introduction: '国産小麦100%のフランスパン。', genre_id: 1, price: 260, status: true, }, { id: 3, name: 'カレーパン', introduction: '中辛のカレーを、甘いパン生地と組み合わせました。', genre_id: 2, price: 180, status: true, }, { id: 4, name: 'ハンバーガー', introduction: '自家製のバンズに具材をたくさん挟み込みました。', genre_id: 2, price: 370, status: true, }, { id: 5, name: 'あんぱん', introduction: 'どこから食べても美味しい斬新なあんぱんです。', genre_id: 3, price: 160, status: true, }, { id: 6, name: 'クリームパン', introduction: '当店こだわりのカスタードをお楽しみください。', genre_id: 3, price: 170, status: true, }, { id: 7, name: 'パン・オ・ショコラ', introduction: '当店の一番人気です♪', genre_id: 3, price: 230, status: true, }, { id: 8, name: 'ラウゲン・ブロートヒェン', introduction: 'ドイツ風のもっちりパン。', genre_id: 4, price: 200, status: true, }, { id: 9, name: 'いちごのお花パン', introduction: 'ふんわりとした生地に苺の風味を加えました。', genre_id: 4, price: 180, status: true, }, { id: 10, name: 'モンブラン', introduction: 'ブリオッシュにマロンクリームを合わせてどうぞ。', genre_id: 5, price: 270, status: true, }, { id: 11, name: 'パンケーキ', introduction: '強力粉からつくるしっとりしたパンケーキです。', genre_id: 5, price: 150, status: true, }, ] )データをまとめて作成
もし見た目にこだわりがなく、「文月太郎1」「文月太郎2」のようなデータで構わない場合は、複数のデータを一気に作ることもできます。
db/seeds.rbAdmin.create!( id: 1, email: 'admin@user', password: 'adminuser', password_confirmation: 'adminuser' ) 50.times do |n| Customer.create!( email: "1@#{n}", family_name: "文月#{n}", family_name_kana: "フミヅキ#{n}", first_name: "太郎#{n}", first_name_kana: "タロウ#{n}", post_code: "1111111", address: "横岡県氷川市絵向寺#{n}丁目", tel: "11111111111", is_active: true, password: "111111", password_confirmation: "111111", ) Address.create!( customer_id: 1, addressee: "文月なな#{n}", post_code: '1111111', address: '三橋県東里見町松林2-15-#{n}' ) Genre.create!( name: "ジャンルその#{n}", validity: true ) Product.create!( name: "自家製パン#{n}", introduction: "#{n}倍美味しくなりました!(当社比)", genre_id: 1, price: 260, status: true, Order.create!( customer_id: 1, addressee: "文月花子#{n}", post_code: "1111111", send_to_address: "遊明県鳥窪町", how_to_pay: true, order_status: 1, ) OrderItem.create!( order_id: 1, product_id: 5, quantity: 10, order_price: 600, make_status: 1, ) end後記
seedデータはいつ入れるのが適切なのか、よく分かりませんがとりあえず入れました。Modelの設定を終えた段階で一通り作ってしまうのが、一番スムーズかも知れません。データが入っていなくてもアプリ自体は作れるので、余計に迷うところです。
アプリ開発をする上で、どのような順序で進めるのが最も効率的なのか判断しきれずに、かなりめちゃくちゃな順番でやっているのではという気がしています。このECサイトを仕上げたら、解説記事たちも順番を入れ替えて、サクサク実装できるチュートリアルにまとめたいと思います。
一番の問題は「完成までこぎつけるかどうか」ですけどね! 次回へ続く!
参考
- 投稿日:2020-07-09T17:41:07+09:00
circleciでrspecを実行すると、Webpacker can't find applicationのエラーが出た
ローカルでは通っていたテストがcircleciで実行すると、Webpacker can't find applicationのエラーが出て、解決できたのでメモとして記録
参考URL
https://qiita.com/Shantti-Y/items/8ddc1aee3b854ee7877c
rspecのエラー全文
Failure/Error: <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> ActionView::Template::Error: Webpacker can't find application in /home/circleci/FANTRA/public/packs-test/manifest.json. Possible causes: 1. You want to set webpacker.yml value of compile to true for your environment unless you are using the `webpack -w` or the webpack-dev-server. 2. webpack has not yet re-run to reflect updates. 3. You have misconfigured Webpacker's config/webpacker.yml file. 4. Your webpack configuration is not creating a manifest. Your manifest contains: { }原因
circleciの環境の中でyarnをインストールもせずに、- run: bundle exec bin/webpackでビルドしようとしていたから、エラーが発生していた(推測)
.circleci/config.yalの中身(エラー発生時)
version: 2.1 orbs: ruby: circleci/ruby@0.1.2 jobs: build: docker: # specify the version you desire here - image: circleci/ruby:2.6.6-stretch-node environment: - RAILS_ENV: 'test' - image: circleci/mysql:8.0 name: "db" command: mysqld --default-authentication-plugin=mysql_native_password environment: - MYSQL_ROOT_PASSWORD: password - image: selenium/standalone-chrome name: "chrome" # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images # documented at https://circleci.com/docs/2.0/circleci-images/ # - image: circleci/postgres:9.4 working_directory: ~/FANTRA steps: - checkout # Download and cache dependencies - restore_cache: keys: - v1-dependencies-{{ checksum "Gemfile.lock" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- - run: bundle install --jobs=4 --retry=3 --path vendor/bundle - save_cache: paths: - ./vendor/bundle key: v1-dependencies-{{ checksum "Gemfile.lock" }} # Database setup - run: bundle exec rake db:create - run: bundle exec rake db:schema:load - run: bundle exec bin/webpack # rubocop。 # - run: # name: Rubocop # command: bundle exec rubocop # rspec # run tests! - run: name: run tests command: | mkdir -p /tmp/test-results TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \ circleci tests split --split-by=timings)" bundle exec rspec \ --format progress \ --format RspecJunitFormatter \ --out /tmp/test-results/rspec.xml \ --format progress \ $TEST_FILES # collect reports - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-results解決方法
先にyarnをインストールしてから、実行してやるとエラーは消えた
+ - run: + name: yarn Install + command: yarn install - run: bundle exec bin/webpack反省
全般的にやけど、理解もせずに使ってしまっているからエラーの解決に苦戦してしまう。
- 投稿日:2020-07-09T16:47:20+09:00
Ruby 二次元配列
二次元配列とは?
2次元配列とは、プログラムで利用される配列において、配列の中に配列が入っている配列のことである。
ソースコード
fruits_price = [["apple", [200, 250, 220]], ["orange", [100, 120, 80]], ["melon", [1200, 1500]]] # 配列Key 合計額算出 fruits_price.each do |fruit| sum = 0 fruit[1].each do |price| sum += price end puts "#{fruit[0]}の合計金額は#{sum}円です" end結果
appleの合計金額は670円です orangeの合計金額は300円です melonの合計金額は2700円です説明
keyにフルーツの名前、valueにそのフルーツの値段が配列で複数
[["apple", [200, 250, 220]]Ruby each文
オブジェクト.each do |変数| 実行する処理1 実行する処理2 endこちらで要素を一つづつ取り出しています。
fruits_price.each do |fruit| sum = 0 fruit[1].each do |price| sum += price endここで、配列の中の価格が入っている配列に対して要素を取り出し、
合計(sum)に格納します。これで完成です。
- 投稿日:2020-07-09T16:04:07+09:00
CKEditorで余計な<ul>タグが出てくるのを防ぐ
ハマったこと
記事のコンテンツをCKEditorを使って管理しているのですが、以下のタグを保存したときに悲劇は起こりました...
<li>ほげほげ</li> <li>ふがふが</li> <li>なむなむ</li>CKEditor君 :「
<ul>
タグなかったから追加しといたわ~ 感謝してや」わい :「いや、テンプレート側に
<ul>
書いてあるんだけど余計なことしないで(# ゚Д゚)」CKEditorの仕様で保存すると
<ul>
タグが消えてしまうらしい。原因
ドキュメントを見てみると
CKEDITOR.dtd.$intermediate
なるものがありました。List of elements that are not to exist standalone that must live under it's parent element.
これだ...
解決策
CKEditorの
config.js
に以下の設定を追加し、CKEditorの<li>
に対する振る舞いを強制的に削除しました。delete CKEDITOR.dtd.$listItem['li']; delete CKEDITOR.dtd.$intermediate['li'];
delete CKEDITOR.dtd.$intermediate['li'];
これだけで、行けそうな気がしたんですがうまくいかなかったので
$listItem
を削除することでうまくいきました。備考
私はCKEditorをGCSの画像をいい感じに扱いたいために使っていて基本的にHTMLタグを直書きなので問題ないのですが、エディタを使っている人は多分この設定を入れるだけだと、リストを挿入するときに
<dt>
や<dd>
になっちゃうだけだと思うので注意が必要です(多分)
- 投稿日:2020-07-09T13:08:30+09:00
(初心者向け)【Rails】時短テク!slimの導入方法と使い方
はじめに
Rubyのテンプレートエンジン
slim
の導入方法と使い方をご説明します
Railsでは、html.erb
というファイルでhtmlを書きますが、<h1></h1>
や<% %>
を毎回記入するのが少し面倒です。
slim
を利用すると、htmlをより簡潔に書けるため、開発にかかる手間を減らすことができます。※ erbとは
embedded Rubyのこと。Viewファイルの中の好きな場所でRubyのコードを実行できるようになります。1. slim 導入方法
- railsのディレクトリにある
gemfile
に、2つのgemを追加します。gem 'slim-rails' # railsでslimを利用するためのgem gem 'html2slim' # html.erbをhtmle.slimに変換するためのgem
- ターミナルで
bundle install
と打ち、gemの追加を反映させます。bundle install
- 以下のコマンドをターミナルに打ち、app/viewsディレクトリにある既存のhtml.erbファイルをhtml.slimに切り替えます。
bundle exec erb2slim app/views app/views
- slimファイルが追加されたことを確認したら、既存のerbファイルは邪魔なので削除します。
bundle exec erb2slim app/views app/views -d※ 念の為、
git commit
を使ってファイルの状態を記録することをオススメします。ファイルを削除して問題が生じても、git commit
前の状態に戻すことができます。git log #ハッシュ値を調べるgit reset --hard ハッシュ値 #出てきたハッシュ値を--hardの後に入力すると、指定したコミットまで戻ります。2. slim 書き方
基本的なslimの使い方を列挙します。
<% %>の書き方
- html
<% if %>
- slim
-
- if<%= %>の書き方
- html
<%= puts hoge %>
- slim
=
= puts hogeテキストの書き方
- html
<p>hoge<p>
- slim
|
p | hoge見出しタグの書き方
- html
<h2>hogehoge</h2>
- slim
<>は必要なし
h2 hogehogeidの書き方
- html
<div id = id></div>
- slim
#
# idクラスの書き方
- html
<div class = hoge></div>
- slim
.
.hogeコメントの書き方
- html
<!-- hoge -->
- slim
/
/hoge
htmlとslimを見比べると、非常にシンプルに書けることが分かります。
参考記事
より詳しい情報について知りたい方は、以下のURLをご参照ください。
- 【爆速で習得】Railsでslimを使う方法から基本文法まで
https://qiita.com/ngron/items/c03e68642c2ab77e7283
- git commitを使って変更内容をコミットする方法【初心者向け】
- 投稿日:2020-07-09T12:48:23+09:00
[Rails]カテゴリを選択するフォームを作りたい
こんにちは。
現在Railsで掲示板をつくっています。掲示板のスレッドをカテゴリで分類して表示したかったので、スレッドをたてる時にカテゴリーを選択するようにしようと思って実装しました。
最近Railsを触り始めたので詳しい人いたらマサカリおねがいします。
バージョンとか
Ruby 2.5
Rails 5.1DB・準備
モデル間の設定も忘れずに...
thread.rbclass Thread < ApplicationRecord belongs_to :category endcategory.rbclass Category < ApplicationRecord has_many :threads endまずはカテゴリを登録する
カテゴリがないと始まらないので、登録しましょう。コンソールからでもいいのですが、今後カテゴリを追加していくと考えて、登録フォームを作ってしまいます。登録したら一覧ページに飛ぶ様にしてあります。
はじめに、routes,controllerを書いていく
- ルーティング
routes.rbRails.application.routes.draw do root 'thread#index' resources :thread #スレッドのルーティングも書いちゃいます resources :categories end
- コントローラ
categories.controller.rbclass CategoriesController < ApplicationController def new @category = Category.new end def create @category = Category.new(category_params) if @category.save redirect_to categories_path, notice: "登録しました" else render :new end end def index @categories = Category.all end private def category_params params.require(:category).permit(:name) end end
- 登録フォーム
new.html.erb<div class="col-sm-12"> <h2 class="text-center">カテゴリの追加</h2> <%= form_with model: @category, local: true do |f| %> <div class="form_input"> <%= f.label :name %> <%= f.text_field :name, class:"form-control" %> </div> <div class="form_action row"> <%= f.submit "登録する", class: "btn col-sm-12 submit_btn" %> </div> <% end %> </div>
- 一覧ページ
これはとりあえず表示できればいいかなと
index.rb<div> <% @categories.each do |category| %> <%= category.name %> <% end %> </div>スレッド投稿フォームをつくる
登録ができたので、あとはスレッドの投稿フォームでカテゴリを選べる様にします。
- コントローラ
threads_controller.rbclass ThreadsController < ApplicationController def new @thread = Thread.new end def create @Thread = Thread.new(board_params) if @thread.save redirect_to thread_path(@thread), notice: "投稿が完了しました" else render :new end end def show @thread = Thread.find(params[:id]) end private def board_params params.require(:thread).permit(:title,:body) end end
- 投稿ページ
collection_select
で選択フォームを作ることができます。使い方としては、こんな感じに使うのですが...
collection_select(オブジェクト名, メソッド名, 要素の配列, value属性の項目, テキストの項目 [, オプション or HTML属性 or イベント属性])
今回の例だと、
Category.all
が「要素の配列」の部分にあたりますね。
もしかしたら、Category.all
って書くよりもコントローラの方で変数に入れちゃってviewで使用。という様な形の方がいいのかもしれません。ここはだれか指摘していただけると助かります。
threads/new.index.erb<div class="col-sm-12"> <h2 class="text-center">スレッド立てる</h2> <%= form_with model: @thread, local: true do |f| %> <div class="form_input"> <%= f.label :title %> <%= f.text_field :title, class: "form-control" %> </div> <div class="form_input"> <%= f.label :body %> <%= f.text_area :body, class: "form-control" %> </div> <div class="form_input"> <%= f.label :category_id %> <%= f.collection_select :category_id, Category.all, :id, :name, :include_blank => "カテゴリを選択してください" %> </div> <div class="form_action row"> <%= f.submit "投稿する", class: "btn col-sm-12 submit_btn" %> </div> <% end %> </div>以下の様な感じで選択できるようになります。
おわりに
最後までみてくれてありがとうございました。
これからたくさん記事書いて、どんどんアウトプットしていこうと思います。
- 投稿日:2020-07-09T10:47:22+09:00
[Rails] VScodeで.erbファイルにHTMLタグをサクサク出せるようにする
- 投稿日:2020-07-09T09:43:15+09:00
circleci ビルドができない、、、
ビルドすることに苦戦しすぎているので、備忘録メモ。まだ完璧に解決できていないが、一歩進んだので投稿
本題の前に一言
circleciのエラー文って不親切やし、めっちゃ難しい。dockerをちゃんと勉強しろってことなのか。
改めて現役エンジニアを尊敬する。参考URL
https://github.com/docker-library/mysql/issues/129#issuecomment-178265632
登場ファイル
- .circleci/cofing.yml
- database.yml
- docker-comopose.yml
今回の抱えている問題
・MySQL、コンテナの立ち上げ失敗←今回で解決できていないが、前進した。
・コンテナの立ち上げに失敗しているからdb:createも失敗
・rubocopを実行できていない(db:createをできていないから?)
・/tmp/test-resultsがあるのに、Not Foundになってしまう←今回で解決できていない。ファイルの中身(エラー発生時)
circleci/cofing.ymlversion: 2.1 orbs: ruby: circleci/ruby@0.1.2 jobs: build: docker: # specify the version you desire here - image: circleci/ruby:2.6.6-stretch-node environment: - RAILS_ENV: 'test' - image: circleci/mysql:8.0 name: "db" command: mysqld --default-authentication-plugin=mysql_native_password environment: - MYSQL_PASSWORD: "password" - MYSQL_ROOT_HOST: '%' # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images # documented at https://circleci.com/docs/2.0/circleci-images/ # - image: circleci/postgres:9.4 working_directory: ~/アプリ名 steps: - checkout # Download and cache dependencies - restore_cache: keys: - v1-dependencies-{{ checksum "Gemfile.lock" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- - run: bundle install --jobs=4 --retry=3 --path vendor/bundle - save_cache: paths: - ./vendor/bundle key: v1-dependencies-{{ checksum "Gemfile.lock" }} # Database setup - run: bundle exec rake db:create - run: bundle exec rake db:schema:load # rubocop。 - run: name: Rubocop command: bundle exec rubocop -a # rspec # run tests! - run: name: run tests command: | mkdir /tmp/test-results TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \ circleci tests split --split-by=timings)" bundle exec rspec \ --format progress \ --format RspecJunitFormatter \ --out /tmp/test-results/rspec.xml \ --format progress \ $TEST_FILES # collect reports - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-resultsdatabase.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <%= ENV.fetch("MYSQL_USERNAME", "root") %> password: <%= ENV.fetch("MYSQL_PASSWORD", "password") %> host: <%= ENV.fetch("MYSQL_HOST", "db") %> development: <<: *default database: アプリ名_development test: <<: *default database: アプリ名_test production: <<: *default database: <%= ENV['DB_DATABASE'] %> adapter: mysql2 encoding: utf8mb4 charset: utf8mb4 collation: utf8mb4_general_ci host: <%= ENV['DB_HOST'] %> username: <%= ENV['DB_USERNAME'] %> password: <%= ENV['DB_PASSWORD'] %>docker-comopose.ymlversion: '3' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: password ports: - '3306:3306' command: --default-authentication-plugin=mysql_native_password volumes: - mysql-data:/var/lib/mysql web: build: . command: bundle exec puma -C config/puma.rb environment: RAILS_ENV: development volumes: - .:/アプリ名 - bundle:/usr/local/bundle - /app/vendor - /app/log - /app/.git ports: - "3000:3000" depends_on: - db tty: true stdin_open: true nginx: build: context: . dockerfile: ./nginx/Dockerfile ports: - '80:80' depends_on: - web chrome: image: selenium/standalone-chrome ports: - "4444:4444" shm_size: "2g" volumes: mysql-data: driver: local bundle: driver: localやったこと
開発でMySQLのコンテナの立ち上げに成功しているから、docker-comose.ymlに書いてあることを.circleci/cofing.ymlでも真似すればいけるんじゃないかと考えて、.circleci/cofing.ymlを以下のように修正
circleci/cofing.ymlversion: 2.1 orbs: ruby: circleci/ruby@0.1.2 jobs: build: docker: # specify the version you desire here - image: circleci/ruby:2.6.6-stretch-node environment: - RAILS_ENV: 'test' - image: circleci/mysql:8.0 name: "db" command: mysqld --default-authentication-plugin=mysql_native_password environment: + - MYSQL_ROOT_PASSWORD: password 追記 - - MYSQL_PASSWORD: "password" 削除 - - MYSQL_ROOT_HOST: '%' 削除 # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images # documented at https://circleci.com/docs/2.0/circleci-images/ # - image: circleci/postgres:9.4 working_directory: ~/アプリ名 steps: - checkout # Download and cache dependencies - restore_cache: keys: - v1-dependencies-{{ checksum "Gemfile.lock" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- - run: bundle install --jobs=4 --retry=3 --path vendor/bundle - save_cache: paths: - ./vendor/bundle key: v1-dependencies-{{ checksum "Gemfile.lock" }} # Database setup - run: bundle exec rake db:create - run: bundle exec rake db:schema:load # rubocop。 - run: name: Rubocop command: bundle exec rubocop -a # rspec # run tests! - run: name: run tests command: | mkdir /tmp/test-results TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \ circleci tests split --split-by=timings)" bundle exec rspec \ --format progress \ --format RspecJunitFormatter \ --out /tmp/test-results/rspec.xml \ --format progress \ $TEST_FILES # collect reports - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-results結果
コンテナの作成のところで「!」マークだったのが、「ー」マークに変わった!!(ただしエラーは解決できていないっぽい。。。)「!」と「ー」の違いは何なんやろ?
それでもrubocopは実行できるようになったので、前進はできた。
メモ
解決進んだら、また投稿。
/tmp/test-resultsのエラーがホンマに分からへん。デフォルト同じなはずやのに。。。
- 投稿日:2020-07-09T09:34:08+09:00
Mysql2::Error::ConnectionErrorを解決します
環境/バージョン
ローカルでやってます
Ruby v2.5.1
Rails v5.2.4
MySQL v8.0.19何が起こった?
railsプロジェクトを作成したあと、`rails s'してみたらこんなエラーに遭遇
Mysql2::Error::ConnectionError (Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)):どうやって解決した?
エラー文からみてわかる通り「あ、MySQL」かとすぐわかります。
ConnectionError
とあるので「繋がっていませんね〜」ってすぐわかります。
database.yml
を調べて見る。default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: socket: /tmp/mysql.sock ---以下省略---ymlファイルのpasswordが抜けてる。
この間、MySQLにpassword設定するの忘れていたからでした。以前までは設定してなかったのでなくても接続できていましたが、設定したのもあって今回はうまく繋がってくれなかったみたいです。
default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password #追記 socket: /tmp/mysql.sockこの画面出るとホッとする。かわいい子供たち
最後まで読んでくれてありがとうございました。
少してもお役に立てれば嬉しいです。
- 投稿日:2020-07-09T01:16:01+09:00
Rails6のAPI使用でCookieを有効にする
概要
RailsでAPIを使用するときに、セッション等でCookieを使いたくなる時があると思います。調べるとけっこう記事が出てくると思いますが、何種類か対応方法があるみたいで、今回はRails6でも上手くいった方法を書きます。
対応
【application_controller.rbの設定】
ActionController::Cookies
を読み込みます。記事はrails-apiでcookieを使うを参考にしました。- CSRFトークンチェックを回避したい場合は
skip_before_action :verify_authenticity_token
で回避してください。protect_from_forgery with: :null_session
だとCookieが使えなくなります。なお、[Rails] CSRFトークンチェックでエラーになるのを回避する方法を参考にしています。application_controller.rbclass ApplicationController < ActionController::Base include ActionController::Cookies skip_before_action :verify_authenticity_token end【application.rb】
- 今回は
config.api_only
設定はtrueを前提とします。falseにするとCookieは使えるようになりますが、API利用前提とは少し外れるので。ActionDispatch::Cookies
とActionDispatch::Session::CookieStore
を使用します。Rails の API モードでセッションを有効にするを参考にしました。私の環境だとActionDispatch::ContentSecurityPolicy::Middleware
を使わなくてもCookie使えましたが、環境によっては使う必要があるのかもです。application.rb# requireの設定は記載省略しています module WebApi class Application < Rails::Application config.load_defaults 6.0 config.api_only = true config.middleware.use ActionDispatch::Cookies config.middleware.use ActionDispatch::Session::CookieStore end end