- 投稿日:2020-06-02T23:50:15+09:00
[Rails]ECサイトのカート機能
はじめに
Ruby on RailsでECサイトを作成しました。
その際、カート機能の実装でかなり時間を費やしたので、やり方を記載してみようと思います。前提条件
カート機能を持たせるためには、DBにテーブルが必要になってきます。
他テーブルとの関連付けなどについては以下の記事にて記載しております。
[Rails] ECサイトのDB設計カート機能の根幹の部分
ここでは、セッションを作成します。
セッションについては、以下の記事がわかりやすいです。
【Rails】Sessionの使い方について要は、データを保持してくれる仕組みですね。
で、このカートのセッション作成は以下のようにコーディングしました。# application_controller.rb class ApplicationController < ActionController::Base private #セッションの作成 def current_cart # セッションから取得したcart_idを元にCartテーブルからCart情報を取得 current_cart = Cart.find_by(id: session[:cart_id]) # Cart情報が存在しない場合、@current_cartを作成 current_cart = Cart.create unless current_cart # 取得したCart情報よりIDを取得し、セッションに設定 session[:cart_id] = current_cart.id # Cart情報を返却 current_cart end endここで定義した"current_cart"を他のcontrollerでも使用していきます。
カートに商品を入れていく
ここからは先ほど作成したメソッドをcarts_controllerで使用していきます。
class CartsController < ApplicationController before_action :set_line_item, only: [:add_item, :destroy] before_action :set_user before_action :set_cart def show @line_items = @cart.line_items end def add_item @line_item = @cart.line_items.build(product_id: params[:product_id]) if @line_item.blank? @line_item.quantity += params[:quantity].to_i if @line_item.save redirect_to current_cart else redirect_to controller: "products", action: "show" end end def destroy @cart.destroy redirect_to current_cart end private def set_user @user = current_user end def set_line_item @line_item = current_cart.line_items.find_by(product_id: params[:product_id]) end def set_cart @cart = current_cart end endadd_itemアクションですが、商品詳細ページに"カートに入れる"ボタンを設置しており、POSTのHTTPメソッドでカートに商品の追加をしています。
@line_item = @cart.line_items.build(product_id: params[:product_id]) if @line_item.blank?上記ですが、buildメソッドを使用しています。
buildはnewのエイリアスですが、慣例として関連付けされたモデルのオブジェクトを生成する際には、buildを使い、それ以外でnewを用いるという使い分けがされているらしいです。今回はcartが親、line_itemが子の関係なので、line_itemモデルのオブジェクトを生成したいためにbuildを使用したということです。(間違ってたらごめんなさい)
基本的にここまででカートとして機能しているかと思います。
因みに
solidusというgemを使用すれば、秒でECサイトを作成できます。
参考記事↓
Spreeの後継ECシステム、Solidusのインストールメモ試しに使ってみましたが、カスタマイズがだるそうです。
目的によっては使ってみるのもアリかもですね。
- 投稿日:2020-06-02T23:42:01+09:00
Rubyの『クラス』関係の問題を集めてみて解いてみた。
Rubyのクラスが分からない
チェリー本を読んでいざアウトプットしていこうと思ったものの、自分でプログラムを作るとなると中々解けずに難しい。。。
そんな僕と同じ悩みを抱えている方のために基礎問題を集めてみました。
基礎問題と言えど正直、曖昧な知識のままこの問題を解いていくと分からないことがあります。
出来れば答えを見ずに進めていくのが良いと思いますが、考えてもどうしても分からない場合は記事の下の方に回答を書いておきますので参考にして下さい。
そして、回答を読んでも分からないと言うことが僕自身ありましたので、分からなかった点まで記載していきます。
問題(3問)
①クラス変数とクラスメソッドについて
sample.rbclass Car def self.run @@count += 1 end def count @@count end end car1 = Car.new car1.run car2 = Car.new car2.run car1.run puts Car.countこちらの記事の問題を参照しました。
●<問題>
上のプログラムは実行するとエラーが出ます。
ある箇所を修正して期待通りの出力ができる様にソースを変更してください。(期待出力)
・インスタンスメソッドrun
を呼ぶとクラスの共有カウンタが1ずつ増加する。
・クラスメソッドcount
を呼ぶと現在のカウンタの値が返される。3と表示されればオッケーです。
●<ヒント>
以下の記事が、この問題を解く上で非常に参考になりました。
https://qiita.com/mogulla3/items/cd4d6e188c34c6819709②特異メソッド
この記事を引用させていただきました。
food.rbclass Food def eat puts "I like." end end natto = Food.new() wasabi = Food.new() karaage = Food.new() natto.eat #=>I like. wasabi.eat #=>I don't like. karaage.eat #=>I love.●<問題>
コメントアウト通りの出力になる様にコードを変えてください。③Musicクラスを継承したRapクラスを定義する
こちらの記事を参照しました。
●<問題>
Rapクラスへ記述するコードを下記の出力結果から予測してください。sample.rbclass Music def mc puts "This is #{@genre} of #{self.class.to_s}" end def initialize(genre) @genre = genre end end Rap.new("mc-battle").mc(出力例)
This is mc-battle of Rap Yo, mic check 1, 2.ー※※※※※※※※※※※解答※※※※※※※※※※※ー
①クラス変数とクラスメソッドについて
クラス変数とクラスメソッドの使い方を理解していないと解けないですね。
●<クラス変数>
- クラスとそのインスタンスがスコープになる
- 定数と似ているがクラス変数は何度でも値を変更できる点で異なる
- クラスメソッド、インスタンスメソッド、クラス定義式内でアクセス可能
以下の記事が参考で、この問題を解く上で非常に参考になりました。
https://qiita.com/mogulla3/items/cd4d6e188c34c6819709まず、見るべき点は2、6行目です。
sample.rbclass Car def self.run #2行目 @@count += 1 end def count #6行目 @@count end end car1 = Car.new car1.run car2 = Car.new car2.run car1.run puts Car.count気づくことはなかったでしょうか?
最後の行を見てください。
puts Car.count
これは、Carクラスcountメソッドをそび出しているみたいですが、おかしくありませんか?
クラスをそのまま呼び出す際は、クラスメソッド出なければいけません。
修正すると、
sample.rbclass Car def self.run #2行目 @@count += 1 end def self.count #6行目 @@count end end #-中略- puts Car.count #最後の行そして、2行目もおかしいですね。
こちらはインスタンスメソッドにしなければいけません。
sample.rbclass Car def run #2行目/def self.runから変更 @@count += 1 end end car1 = Car.new car1.runなぜなら、classのそとでは
car1
とインスタンスを作成しているからそのインスタンスを呼び出すためのメソッドとして、インスタンスメソッドにしなければいけません。しかしこれで終わりではありません。
クラス変数
@@count
が定義されていません。sample.rbclass Car @@count = 0 #クラス変数の定義 def run #クラスメソッド @@count += 1 #@@countはクラス変数 end def self.count #インスタンスメソッド @@count end end car1 = Car.new car1.run car2 = Car.new car2.run car1.run puts Car.count # => $ ruby sample.rb # => 3これでオッケーです。
②特異メソッド
そもそも、特異メソッドとは?
1つのインスタンス固有のメソッドのことを指します。特異メソッドは、
def オブジェクト名.メソッド名
で定義できます。
https://qiita.com/k-penguin-sato/items/d637dced7af32e4ec7c0よって、
food.rbclass Food def eat puts "I like." end end natto = Food.new() wasabi = Food.new() karaage = Food.new() #--追加--↓ def wasabi.eat puts "I don't like." end def karaage.eat puts "I love." end #--追加--↑ natto.eat wasabi.eat karaage.eat同じクラスのインスタンスであるオブジェクトに、同じ名前の動作が異なるメソッドを定義します。
これが特異メソッドで、これはクラスに対してではなくクラスからできたインスタンスに対してのメソッドになります。
よってそれぞれのメソッドからの出力ができます。
(結果)
I like. I don't like. I love.③Musicクラスを継承したRapクラスを定義する
sample.rbclass Music def mc puts "This is #{@genre} of #{self.class.to_s}" end def initialize(genre) @genre = genre end end Rap.new("mc-battle").mc・
self.class.to_s
:Rap.new
の様に作られたインスタンス自体のクラス名を文字列に変換する。では、
Rapクラス
と、を作っていきます。sample.rb#--中略-- class Rap < Music end出力文字を見てください。
Yo, mic check 1, 2.
Musicクラスでは定義されていない出力ですので、Rapクラスで出力していきます。
最後の行の
Rap.new("mc-battle").mc
を見てみると、メソッドはmc
が使われていますので、記載していきます。sample.rb#--中略-- class Rap < Music def mc end end
Musicクラス
を継承しているので必要な部分を記載するとsample.rb#--中略-- class Rap < Music def mc puts "Yo, mic check 1, 2." end endしかしこれで終わりではないです。
まだ、現時点ではMusicクラスの呼び出しができていません。
どうすればいいでしょうか?
「super」メソッドを実行します。
superメソッドは、スーパークラスの中でその呼び出されたメソッドと同じメソッド名を持つメソッドを探して実行します。
それでは、書き換えると
sample.rbclass Music def mc puts "This is #{@genre} of #{self.class.to_s}" end def initialize(genre) @genre = genre end end class Rap < Music def mc super #追加コード puts "Yo, mic check 1, 2." end end Rap.new("mc-battle").mc完成です。
- 投稿日:2020-06-02T23:19:44+09:00
【Rails・MySQL】Mac環境構築
MacのRails・MySQL環境構築について
目次
- ログインシェルの設定
- Command Line Toolsのインストール
- Homebrewのインストール・設定
- Rubyのインストール・設定
- MySQLのインストール・設定
- Railsのインストール・設定
1.ログインシェルの設定
※ OSがCatalina以降の場合、zshを利用するため、設定の確認・変更を行います。
ターミナル
# zshをデフォルトに設定 $ chsh -s /bin/zsh # ログインシェルを表示 $ echo $SHELL # 以下のように表示されれば成功 /bin/zsh
echo $SHELL
コマンドで、/bin/zsh
と表示されない場合は、
一度ターミナルを閉じて、再度開いた上でもう一度echo $SHELL
コマンドを打ってください。2.Command Line Toolsのインストール
Command Line ToolsはWebアプリケーション開発に必要なソフトウェアをダウンロードするために必要な機能となります。(コマンドでプログラムを操作するもの)
Command Line Toolsのインストール
以下のコマンドを入力します。
ターミナル
$ xcode-select --installコマンド入力後にモーダルが表示されるので以下の順に操作します。
①「インストール」をクリック
②「同意する」をクリック
③Command Line Toolsのダウンロードが始まるので、完了まで待機
④ダウンロードが完了したら、「完了」をクリック以上でCommand Line Toolsのインストールは完了となります。
3.Homebrewのインストール・設定
3-1. Homebrewのインストール
Homebrewというソフトウェア管理ツールをインストールします。
以下のコマンドを入力します。ターミナル
$ cd # ホームディレクトリに移動 $ pwd # ホームディレクトリにいるかどうか確認 $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" # コマンドを実行※ 処理に時間がかかるので、ゆっくり待ちましょう。
途中でエンターキーの入力を求められるので入力をしましょう。ターミナル
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" # コマンドを実行 ... ... ... Pres RETURN to continue or any other key to abortまた、途中でパスワードの入力を求められますので入力をしましよう。
(パスワードを入力しても文字は表示されませんが、間違いなく入力はされています)ターミナル
Pres RETURN to continue or any other key to abort ... ... ... Password:ダウンロードが完了し、再びコマンドを入力できるようになれば成功です。
2-2. Homebrewがインストールされているか確認
以下のコマンドを入力します。
ターミナル
$ brew -v Homebrew 2.2.12 # このバージョンはインストールのタイミングで異なります3-3. Homebrewをアップデート
Homebrewを最新の状態にアップデートします。
以下のコマンドを入力します。ターミナル
$ brew update3-4. Homebrewの権限を変更
Homebrewの権限を変更します。
以下のコマンドを入力します。ターミナル
$ sudo chown -R `whoami`:admin /usr/local/bin4.Rubyのインストール・設定
4-1. rbenvとruby-buildをインストール
rbenvとruby-buildを、Homebrewを用いてインストールします。
以下のコマンドを入力します。ターミナル
$ brew install rbenv ruby-build4-2. rbenvをどこからも使用できるように設定
PCにおけるどこのディレクトリからも使用できるようにするため、
以下のコマンドを入力します。ターミナル
$ echo 'eval "$(rbenv init -)"' >> ~/.zshrczshrcというのは、設定ファイルの名称です。
(CatalinaかMojave以前の方で、設定ファイルの場所が異なるのでご注意してください)4-3. zshrcの変更を反映
設定ファイルであるzshrcを修正したので、以下のコマンドでzshrcを再読み込みし、変更を反映させます。
以下のコマンドを入力します。ターミナル
$ source ~/.zshrc4-4. readlineをinstall
ターミナルのirb上で日本語入力を可能にする設定を行うためにインストールします。
以下のコマンドを入力します。ターミナル
$ brew install readlineこちらについてはコマンドを実行すると、すでにインストール済みと表示されることがあります。
(特に問題はありませんので、そのまま次に進みましょう。)4-5. readlineをどこからも使用できるように設定
readlineをどこからでも使用できるようにするため、
以下のコマンドを入力します。ターミナル
$ brew link readline --force4-6. rbenvを利用してRubyをインストール
Webアプリケーション開発用のRubyをインストールします。(Rubyのversionは任意のversionを設定して下さい)
以下のコマンドを入力します。ターミナル
$ RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)" $ rbenv install 2.7.1※ 処理に時間がかかるので、ゆっくり待ちましょう。(約10分くらい)
4-7. 利用するRubyのバージョンを設定
インストールしたRuby 2.7.1を使用するために、
以下のコマンドを入力します。ターミナル
$ rbenv global 2.7.1これまで使用していた、デフォルトでPCに入っていたRubyから、先ほどインストールしたRubyを使用するように切り替えることができました。
4-8. rbenvを読み込んで変更を反映
Rubyのバージョンを切り替えたので、変更を反映させます。
以下のコマンドを入力します。ターミナル
$ rbenv rehash4-9. Rubyのバージョンを確認
バージョンを切り替えることができたか、確認をします。
以下のコマンドを入力します。ターミナル
$ ruby -vRubyのバージョンが、先ほどインストールした2.7.1であることが表示されれば完了です。
5. MySQLのインストール・設定
5-1. MySQLのインストール
MySQLをインストールします。(MySQLのversionは任意のversionを設定して下さい)
以下のコマンドを入力します。ターミナル
$ brew install mysql@5.7※ 処理に時間がかかるので、ゆっくり待ちましょう。
5-2. MySQLの自動起動設定
MySQLは本来であればPC再起動のたびに起動し直す必要がありますが、それでは面倒であるため、自動に起動するように設定します。
以下のコマンドを入力します。ターミナル
$ mkdir ~/Library/LaunchAgents $ ln -sfv /usr/local/opt/mysql\@5.7/*.plist ~/Library/LaunchAgents $ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.7.plist5-3. mysqlコマンドをどこからでも実行できるように設定
rbenvやreadlineの時と同様に、どこからでもMySQLを操作するためのコマンドmysqlを実行できるように設定します。
以下のコマンドを入力します。ターミナル
# mysqlのコマンドを実行できるようにする $ echo 'export PATH="/usr/local/opt/mysql@5.7/bin:$PATH"' >> ~/.zshrc $ source ~/.zshrc # mysqlのコマンドが打てるか確認する $ which mysql # 以下のように表示されれば成功 /usr/local/opt/mysql@5.7/bin/mysql6. Railsのインストール・設定
6-1. bundlerをインストール
Rubyの拡張機能(gem)を管理するためのbundler(バンドラー)をインストールします。
以下のコマンドを入力します。ターミナル
$ gem install bundler6-2. Railsをインストール
Railsをインストールします。(Railsのversionは任意のversionを設定して下さい)
以下のコマンドを入力します。ターミナル
$ gem install rails --version='6.0.3'※ 処理に時間がかかるので、ゆっくり待ちましょう。
6-3. rbenvを再読み込み
一通り開発に必要なものはインストールできたので、以下のコマンドでrbenvを読み込んで、変更を反映させましょう。
以下のコマンドを入力します。ターミナル
$ rbenv rehash6-4. Railsが導入できたか確認
以下のコマンドを実行して、Rails 6.0.3が表示されれば問題なくインストールが完了しています。
ターミナル
$ rails -v以上で、Macでの【Rails・MySQL】の環境構築は完了です。
- 投稿日:2020-06-02T23:18:34+09:00
AtCoder Beginner Contest 169 A,B,Cまでをrubyで
4回目。前回C問題まで解けたので、今回もできるだろう、と舐めてかかったら、計算機工学の基礎のところでつまづく。
A問題
abc169a.rbab = gets.chomp.split(" ").map!{|item| item.to_i} puts ab[0] * ab[1]書くだけ。
B問題
abc169b.rbn = gets.chomp.to_i a = gets.chomp.split(" ").map!{|item| item.to_i} num = 1 ans = 1 th = 10**18 a.sort!{|a,b| (-1)*(a <=> b)} if a[n - 1] == 0 puts 0 exit end for num in 1..n do ans = ans * a[num-1] num = num + 1 if ans > th puts "-1" exit end end puts ans整数の掛け算なのだが、順に、
1. 0が一つでもあったら0
2. 10^18を超えたら即-1を返して終了
3. 要素全部の積を返す
で正解。cだと2のチェックを丁寧にやる必要があるが、rubyなら整数型で必要に応じて多倍長整数が使われるので気にしなくて良い。
2のチェックに早く引っかかるように逆順にソートして、末尾の要素が0かどうかを先にチェック、と書いたが、a.include?(0)か a.minで済む。
破壊的にソートする!をつけ忘れて通せず。C問題
abc169c.rbab = gets.chomp.split(" ") a=ab[0].to_i b=((ab[1].to_s).delete(".")).to_i ans = a*b/100 puts ansbを100倍してto_sで整数にして計算、100で割れば良いや、とやったところで、2進表現で循環小数になるパターンを踏んでwa。
bをintegerとして扱い、小数点を除いた後にto_sが簡単。
これも通せず。
精進します。
- 投稿日:2020-06-02T22:05:46+09:00
スコープについてのメモ
def 〜 endの処理に対して、
処理外から渡したい数値が存在する場合、
発生する。最初に渡したい変数は何かを宣言、
受け取る変数に、その渡したい変数を追記するだけ。[スコープ発生例]
def show_review line = "---------------------------" puts "ジャンル : #{genre}\n#{line}\n" puts "タイトル : #{title}\n#{line}\n" puts "感想 :\n#{impression}\n#{line}\n" end genre = "aaa" title = "qqq" impression = "ddd" review.show_review①「review.show_review」の中の「genre」「title」「impression」に
値を渡す
②スコープでエラーが出る
③「review.show_review」に(genre,title,impression)を加える
④合わせて、渡される側も(genre,title,impression)を加える
⑤渡せる!!<正しい書き方>
def show_review (genre,title,impression)←②受け取る側も追加 line = "---------------------------" puts "ジャンル : #{genre}\n#{line}\n" puts "タイトル : #{title}\n#{line}\n" puts "感想 :\n#{impression}\n#{line}\n" end genre = "aaa" title = "qqq" impression = "ddd" review.show_review(genre,title,impression) ←①渡したい項目を追加
- 投稿日:2020-06-02T21:56:35+09:00
【Rails】gemのgoogle-cloud-visionでv1.0が出たので対応してみた
以前に書いた記事でコードを書き換える必要があったので対応してみました。
gem
google-cloud-vision
をv0.x系からv1.0.0にアップデートしたら要注意です。基本的に
Migrating to google-cloud-vision 1.0
https://github.com/googleapis/google-cloud-ruby/blob/master/google-cloud-vision/MIGRATING.mdに書いてあるとおりなのですが
#safe_search_detection
についてはこちらのほうがわかりやすかったです。require "google/cloud/vision" image_annotator = Google::Cloud::Vision::ImageAnnotator.newを
require "google/cloud/vision/v1" image_annotator = Google::Cloud::Vision::V1::ImageAnnotator::Client.newに書き換えました。
Google Cloud Visionについては以前にも破壊的なバージョンアップがあったような...
- 投稿日:2020-06-02T21:17:23+09:00
Rails Tutorial 第6版 学習まとめ 第2章
概要
この記事は私の知識をより確実なものにするためにRailsチュートリアル解説記事を書くことで理解を深め
勉強の一環としています。稀にとんでもない内容や間違えた内容が書いてあるかもしれませんので
ご了承ください。
できればそれとなく教えてくれますと幸いです・・・この章でやること
toy_appの作成を通してscaffoldの強力な機能を知る
RESTアーキテクチャとは何か大まかに把握する。Toy_appの生成
①hello_appと同じようにRailsのバージョンを指定して生成する
$ cd ~/environment $ rails _6.0.3_ new toy_app $ cd toy_app/②Gemfileも新しくなったのでToyアプリ用に書き換える
Gemfilesource 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem 'rails', '6.0.3' gem 'puma', '4.3.4' gem 'sass-rails', '5.1.0' gem 'webpacker', '4.0.7' gem 'turbolinks', '5.2.0' gem 'jbuilder', '2.9.1' gem 'bootsnap', '1.4.5', require: false group :development, :test do gem 'sqlite3', '1.4.1' gem 'byebug', '11.0.1', platforms: [:mri, :mingw, :x64_mingw] end group :development do gem 'web-console', '4.0.1' gem 'listen', '3.1.5' gem 'spring', '2.1.0' gem 'spring-watcher-listen', '2.0.1' end group :test do gem 'capybara', '3.28.0' gem 'selenium-webdriver', '3.142.4' gem 'webdrivers', '4.1.2' end group :production do gem 'pg', '1.1.4' end # Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]③そして以前と同じく
bundle install
を実行、You have requested: listen = 3.1.5 The bundle currently has listen locked at 3.2.1. Try running `bundle update listen` If you are updating multiple gems in your Gemfile at once, try passing them all to `bundle update`このようなエラーが出たらGemfile.lockで引っかかっているのでGemfile.lockを一度削除するか
bundle update
を実行してから
bundle install
する。④最後にhello_appと同じくGitのバージョン管理下に置く。
$ git init $ git add -A $ git commit -m "Initialize repository"ローカルのリポジトリはできたので、リモートのリポジトリをToyアプリ用に新規作成して
プッシュする。(リモートリポジトリ作成は第1章と同じ方法でGithubのサイト上で行う)
リポジトリは依然と同じくプライベートで。$ git remote add origin https://github.com/<あなたのGitHubアカウント名>/toy_app.git $ git push -u origin master⑤とりあえずデプロイもしてみる。
今の状態だと中身がないのでとりあえずhello_appと同じように
hello,worldを出力するように追記しておくapplication_controller.rbclass ApplicationController < ActionController::Base def hello render html: "hello, world!" end endroutes.rbRails.application.routes.draw do root 'application#hello' endつづいてこの変更をコミット&Github/Herokuにプッシュ
$ git commit -am "Add hello" $ heroku create $ git push && git push heroku masterUserモデルを作成する
ユーザーのモデル設計
Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#sec-modeling_demo_usersid,name,emailの属性がそれぞれテーブルのカラムに相当する。
マイクロポストのモデル設計
マイクロポストはTwitterのツイートと同じような140文字程度の短い投稿が前提となる。
投稿のid、投稿のテキスト内容を格納するtext型のcontent、誰の投稿かを記録するためのuser_idも追加すると
Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#sec-modeling_demo_micropostsこのようなデータモデルになる。
Usersリソース
さっそくUsersリソースからRailsをscaffoldジェネレータで生成する。
$ rails generate scaffold User name:string email:string書き方はrails generate scaffold リソース名 カラム名:データ型 ~ となる。
id属性は必ず1つのモデルに1つ主キーとして付加されるので書く必要はないscaffoldでUserモデルを作成したがこのままでは使えない。
ジェネレータで作成したデータベースはマイグレート(migrate)する必要がある。データベースをマイグレートする↓
$ rails db:migrateこれでローカルWebサーバーを実行できるようになったので、Cloud9を使っている場合は
第1章と同じくdevelopment.rbを編集してCloud9への接続を許可しておく。development.rbRails.application.configure do . . . config.hosts.clear endあとは別タブでターミナルを起動し、
rails server
を実行。
これでローカルサーバーが立ち上がり、hello,world!が表示される。ユーザーページを探検する
先ほどの一連の流れでUsersリソースをscaffoldで生成したことで自動でユーザー管理用のページが多数追加されている。
ページのURLと内容は以下の通り。
URL アクション 用途 /users index すべてのユーザーを一覧するページ /users/1 show id=1のユーザーを表示するページ /users/new new 新規ユーザーを作成するページ /users/1/edit edit id=1のユーザーを編集するページ Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#sec-a_user_tourユーザーページの探検はRailsチュートリアルを参照。
演習
- F12キーなどでブラウザのデベロッパーツールを起動し、該当の箇所を調べると、リロード時に内容が削除されている。
- emailを入力しないで登録しても登録できてしまう。これはまずいので3章から作るアプリではこのような問題の対策もしていく。
- 間違えたメールアドレスを入力されても登録できてしまう。2と同じでこれも対策することになる。
- Are you sure?と警告メッセージが表示されるようになっている。こういったユーザーの使いやすいように工夫された機能も実装していく。
MVCの挙動
/usersのindexページを開くと内部でどういった処理をしているのか、MVCで図解するとこのようになる。
Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#sec-mvc_in_actionブラウザからのリクエスト→ルーターが該当するアクションに割り当て→必要なデータをUserモデルに問い合わせる
→データベースからモデルがデータを取り出す→モデルがデータをコントローラに渡す
→インスタンス変数に受け取ったデータを代入してビューに渡す→ビューはHTMLを生成し、コントローラに渡す
→コントローラがブラウザに受け取ったHTMLを返す
という流れ。Toyアプリのとりあえずの形ができたのでルートURLをToyアプリのインデックスページに変更してみる。
routes.rbRails.application.routes.draw do resources :users # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html root "users#index" end今回も書き方は同じで "コントローラ名#アクション名"で記述する
ここでscaffoldで生成されたusers_controllerをのぞいてみる
users_controller.rbclass UsersController < ApplicationController before_action :set_user, only: [:show, :edit, :update, :destroy] # GET /users # GET /users.json def index @users = User.all end # GET /users/1 # GET /users/1.json def show end # GET /users/new def new @user = User.new end # GET /users/1/edit def edit endこのような記述がされている。
1行目のUsersController < ApplicationControllerという記述は
Rubyの文法で継承を意味する。ここでは
ApplicationControllerを継承したUserControllerクラスという意味になる。以下の表はRESTに対応するアクションの一覧で、
URLが一部重複するものがあるがこれらはHTTPリクエストの種類が別なため別なルートとカウントされる。
HTTPリクエスト URL アクション 用途 GET /users index すべてのユーザーを一覧するページ GET /users/1 show id=1のユーザーを表示するページ GET /users/new new 新規ユーザーを作成するページ POST /users create ユーザーを作成するアクション GET /users/1/edit edit id=1のユーザーを編集するページ PATCH /users/1 update id=1のユーザーを更新するアクション DELETE /users/1 destroy id=1のユーザーを削除するアクション Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#table-demo_RESTful_usersusers_controllerのindexアクションをみてみると
def index @users = User.all endこのような記述になっている。
これは@users変数にUserデータベースのすべてのデータを取り出して代入する処理を行っている。Userモデル自体は何も記述がなく非常にシンプルだが
Userクラスが継承している"ActiveRecordクラス"がDB操作に関する非常に多くの機能を持っているため
今回の場合はUser.allでデータベースからUserのデータを取り出すことができた。この@users変数はindexアクションに対応するindexビューで使われている。
該当の場所をみてみる。index.html.erb<% @users.each do |user| %> <tr> <td><%= user.name %></td> <td><%= user.email %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %>この記述の
<% @users.each do |user| %>
の部分だが
この段階で簡潔に説明すると、@users変数に入っているデータ(レコード)を一つずつ引っ張り出して
userに代入し、endで囲まれた部分をそのレコードの数分繰り返す処理。
ユーザーが3人(A、B、C)といたら
Aのデータをuserに入れてhtmlを生成
Bのデータをuserに入れてhtmlを生成
Cのデータをuserに入れてhtmlを生成
という繰り返し処理を行っている。
現時点では理解しなくてもOK演習
1.
①ブラウザからリクエストがルーターに送られる
②ルーターがリクエストに対応するコントローラのeditアクションに割り当てる。
③editアクションが必要とするデータをモデルにリクエストする。
④モデルがリクエストされたデータをデータベースから読み出し、コントローラに返す
⑤editアクションが対応するビューedit.html.erbを呼ぶ(この時ビューには読み出したデータを渡す。)
⑥ビューがHTMLを生成し、コントローラに返す。
⑦コントローラがブラウザに渡されたHTMLを返す。2.
users_controllerのprivateメソッドに記述されている。users_controller.rbclass UsersController < ApplicationController before_action :set_user, only: [:show, :edit, :update, :destroy] private # Use callbacks to share common setup or constraints between actions. def set_user @user = User.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def user_params params.require(:user).permit(:name, :email) end endbefore_actionという記述でeditアクションを実行する前にset_userメソッドを実行するよう指定されている。
ser_userアクションでは@user変数にUserデータベースのリクエストされたIDのテーブルを読み出すという文が書いてある。3.edit.html.erb
Usersリソースの欠点について
前回の演習でも挙げたがユーザー名やメールアドレスがめちゃくちゃでも登録できてしまう。
またログイン機構がないので誰でもユーザーを削除したり編集したりできてしまう。権限もくそもない。
そしてアプリの動作を確認するテストがほぼない。上記の内容が正しいか。アプリの機能が正しく動作するかなどの
テストが全くかかれていない。→不都合があっても見つからないUIがダサい、レイアウトもめちゃくちゃこんなレイアウトのWebアプリはだれも使わない(断言)
理解が難しい。これに尽きる
Railsチュートリアルを1週読破した自分でさえも完全にコードを理解するのは難しい。
コードは基本簡潔にわかりやすく書かないとNGマイクロポストを作成する
MicropostsもUsersと同様scaffoldで生成してみる。
$ rails g scaffold Micropost content:text user_id:integer Running via Spring preloader in process 3794 invoke active_record create db/migrate/20200601141608_create_microposts.rb create app/models/micropost.rb invoke test_unit create test/models/micropost_test.rb create test/fixtures/microposts.yml invoke resource_route route resources :microposts invoke scaffold_controller create app/controllers/microposts_controller.rb invoke erb create app/views/microposts create app/views/microposts/index.html.erb create app/views/microposts/edit.html.erb create app/views/microposts/show.html.erb create app/views/microposts/new.html.erb create app/views/microposts/_form.html.erb invoke test_unit create test/controllers/microposts_controller_test.rb create test/system/microposts_test.rb invoke helper create app/helpers/microposts_helper.rb invoke test_unit invoke jbuilder create app/views/microposts/index.json.jbuilder create app/views/microposts/show.json.jbuilder create app/views/microposts/_micropost.json.jbuilder invoke assets invoke scss create app/assets/stylesheets/microposts.scss invoke scss identical app/assets/stylesheets/scaffolds.scssUsersの生成時と同じようにまずはデータベースをマイグレートして使えるようにする。
$ rails db:migrate == 20200601141608 CreateMicroposts: migrating ================================= -- create_table(:microposts) -> 0.0041s == 20200601141608 CreateMicroposts: migrated (0.0048s) ========================MicropostsもUsersの時と変わらず、ルーターが更新され、resourcesでRESTアーキテクチャに対応した
各ルーティングが割り当てられる。
HTTPリクエスト URL アクション 用途 GET /microposts index すべてのマイクロポストを表示するページ GET /microposts/1 show id=1のマイクロポストを表示するページ GET /microposts/new new マイクロポストを新規作成するページ POST /microposts create マイクロポストを新規作成するアクション GET /microposts/1/edit edit id=1のマイクロポストを編集するページ PATCH /microposts/1 update id=1のマイクロポストを更新するアクション DELETE /microposts/1 destroy id1のマイクロポストを削除する Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#code-demo_microposts_resourcescaffoldで生成されるコードはクラス名が自分でつけた名前に代わるだけで他は変わらない。
つまり同じ構造である。MicropostsもUsersと同じように/micropostsでインデックスページにアクセスでき、New micropostで投稿ができる。
演習
- Usersの時と同じく、中身が消える。
- これもUsersの時と同じで、現状対策されていないため空のユーザーで空の投稿ができてしまう。
- 文字数を制限するコードが生成されていないため、投稿できてしまう。(これも欠点の一つ)
- indexページからdestroyリンクを踏むことで削除できる。これも確認メッセージが表示されてから削除される。
マイクロポストをマイクロなポストに
前回の演習の3番目の項目で文字数制限がないため、長い文章でも投稿できてしまいマイクロでない問題が発覚した。
この問題を修正していく。データベースの内容に制限(バリデーション)を加えることでこの問題を修正する。
具体的なコードは下のとおりmicropost.rbclass Micropost < ApplicationRecord validates :content, length:{maximum: 140} endデータベースを扱うモデルにバリデーションを書くことでcontentを140字に制限する。
このコードは
contentのlengthのmaximumを140に制限するという、非常に単純なコード。
投稿の文字数の最大を140文字とも言い換えられる。バリデーションを追加することでRailsがエラーを吐くようになった。
実際にMicropostを140文字以上で投稿してみた画面がこちら。
超優秀。
演習
- 上の画面のようにエラーを吐くようになり、indexページに戻っても投稿は追加されない。(データベースに登録されない)
- error_explanationクラスの中にulリストでエラーの内容が格納されている。エラーが増えればliタグのコンテンツがエラーとして追加される。
ユーザーとマイクロポストの関連付け
TwitterやQiitaなどでもそうだが
”一人のユーザーは複数の投稿を持っている”
具体的な言い方に換えると
ユーザIDが1の投稿は複数存在しうる。
こういった1対多のデータの関係性をRailsは超簡単に実装できる。
そのコードがこちらuser.rbclass User < ApplicationRecord has_many :microposts endmicropost.rbclass Micropost < ApplicationRecord belongs_to :user validates :content, length:{maximum: 140} endUserはmicropostを複数持っている。
micropostsは一つのユーザーに属する。
この関係性をこのコードで実装している。このコードを書くことでMicropostsのuser_idカラムの値からユーザーを表示することができる。
実際にやってみよう。まずはRailsの機能を使えるコンソール
rails console
を起動する。
rails console内ではデータベースのデータやコードを自由に試せるので非常に便利。
まずはrails console内でユーザーを作成し、そのユーザーのIDをもったMicropostも作成する。user1 = User.first (0.4ms) SELECT sqlite_version(*) User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "takemo", email: "take.webengineer@gmail.com", created_at: "2020-05-31 14:00:23", updated_at: "2020-05-31 14:00:23"> 2.6.3 :002 > user1.micropostsuser1にUserデータベースの一番最初に登録されているデータを代入
ローカルサーバーで以前ユーザーをお試しで作ったのでそのデータが代入されている。ここで先ほどの関連付けが役立つシーンがこちら
> user1.microposts Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]] => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 1, content: "taskemodsjfa jl", user_id: 1, created_at: "2020-06-01 14:24:23", updated_at: "2020-06-01 14:24:23">, #<Micropost id: 3, content: "Ruby(ルビー)は、まつもとゆきひろ(通称: Matz)により開発されたオブジェクト指向スクリプト...", user_id: 1, created_at: "2020-06-01 14:27:03", updated_at: "2020-06-01 14:27:03">, #<Micropost id: 5, content: "異なるデータモデル同士の関連付けは、Railsの強力な機能です。ここでは、1人のユーザーに対し複数の...", user_id: 1, created_at: "2020-06-01 14:41:51", updated_at: "2020-06-01 14:41:51">]> 2.6.3 :003 >関連付けによってUserには複数のMicropostが結びついていることになったので、user1.micropostsとすると
user1に結びついたMicropostをすべて取り出すことができる。(ユーザーが何を投稿したか調べられる。)逆方向の使い方を試してみる(投稿から誰が投降したのかを調べる。)
> micropost = user1.microposts.first Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."id" ASC LIMIT ? [["user_id", 1], ["LIMIT", 1]] => #<Micropost id: 1, content: "taskemodsjfa jl", user_id: 1, created_at: "2020-06-01 14:24:23", updated_at: "2020-06-01 14:24:23">micropost変数にuser1(user_id=1)の最初のmicropostを代入した。
つまりmicropostからuser_idが1のユーザーを取り出せればいい。> micropost.user => #<User id: 1, name: "takemo", email: "take.webengineer@gmail.com", created_at: "2020-05-31 14:00:23", updated_at: "2020-05-31 14:00:23"> 2.6.3 :004 >
micropost.user
とするだけでmicropostが属するユーザーを取り出すことができた。
1対多の関連性をhas_manyとbelongs_toを使って実装するだけでこのような便利な使い方ができる。演習
- @userのmicropostsの中でfirstのmicropostのcontentを表示すればいいのでそのままほかの行からコピペして書き換えるとこうなる。
show.html.erb<p id="notice"><%= notice %></p> <p> <strong>Name:</strong> <%= @user.name %> </p> <p> <strong>Email:</strong> <%= @user.email %> </p> <p> <strong>Micropost_first</strong> <%= @user.microposts.first.content %> </p> <%= link_to 'Edit', edit_user_path(@user) %> | <%= link_to 'Back', users_path %>2.コードを追加したら実際に空投稿してみると
バリデーションに引っかかってエラーを吐いた。
3.FILL_INにはバリデーションを適用したいカラムを指定する。(nameとemail)
user.rbclass User < ApplicationRecord has_many :microposts validates :name, presence: true validates :email, presence: true endバリデーションを追加したのでこちらも空登録するとエラーを吐くようになった。
Railsの継承について
ここは何となくの理解でもとりあえずOK
RailsのモデルはUser < ApplicationRecord
というRuby特有の文法で継承されている。
モデルの継承の構造は図の通り
Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#fig-demo_model_inheritanceUserやMicropostといったRailsのモデルはすべてApplicationRecordクラスを継承している。
そしてApplicationRecordはActiveRecord::Baseという基本クラスを継承している。
このActiveRecordがデータベースを扱う機能を提供している。
User.firstのfirstメソッドなどもこのActiveRecordを継承していることにより使える。コントローラーも似たような構造でできている。
Railsチュートリアル第6版より
https://railstutorial.jp/chapters/toy_app?version=6.0#fig-demo_controller_inheritance各コントローラーはApplicationControllerを継承しておりApplicationControllerは
ActionController::Baseという基本クラスを継承していることにより
様々なコントローラーで等しくコントローラーの基本機能を使うことができる。
またApplicationControllerを継承しているのでApplicationControllerで定義したルールは
継承先の各コントローラーで適用され、定義したメソッドなども同じように使用できる。演習
1.1行目
application_controller.rbclass ApplicationController < ActionController::Base def hello render html:"hello,world!" end end2.モデルが格納されているmodelsフォルダのapplication_record.rbの1行目
application_record.rbclass ApplicationRecord < ActiveRecord::Base self.abstract_class = true end最後に
この章で作ったToyアプリをHerokuにデプロイする
$ git add -A $ git commit -m "Finish toy app" $ git push $ git push herokuこのままだとHeroku上でエラーが発生して本番環境でサイトが表示されない。
heroku logsでHeroku上のエラーログを追ってみる。
するとこのような行がある。2020-06-01T15:52:35.330114+00:00 app[web.1]: [3a72d85c-e21f-41d2-92ce-40c241660d8f] ActionView::Template::Error (PG::UndefinedTable: ERROR: relation "users" does not existPGとはPostgreSQLのことである。
意味はPGでusersテーブルが存在しないと怒られている。
本番環境のデータベースと開発環境のデータベースは違うため
マイグレーションを本番環境でも行わなければならない。
つまりマイグレーションをしていないから怒られている。
heroku run rails db:migrate
で本番環境のデータベースをマイグレートする。
これでしっかり動作するはず。演習
1.ここで先ほどの演習2.3.3.1をやっていると本番環境のデータベースにはマイクロポストが一つも登録されておらず
エラーを吐いてしまうため、演習で追加したコード部は削除して、コミット、プッシュしなおしておく。
うまくいけばこのようにユーザーを作成できる。
2.本番環境でも同じくマイクロポストを作成できることが確認できた。
- 投稿日:2020-06-02T20:47:01+09:00
プログラム基礎知識を勉強する際の考え方
自分はこんな感じでのイメージ学んだので、メモ書きです。
- 作りたいプログラムの仕様を考える
- プログラムをトリガーやイベントごとに分割して日本語で書き出す
- コードを書く&説明コメントをつけて理解出来ない部分を無くす
- 小さいコードのプログラムを沢山書く
- 少しづつ大きいプログラムを書いてみる
プログラムを書く際は、自分でコメント説明文を入れて
合っているかを確認してもらう!!
→自分で説明できるか?
なぜ、そのコードを入れたのかを意識する<仕様>
そのプログラムができる事を具体的に箇条書き
・
・
・<プログラムでどんな処理をするか日本語で説明>
・登場する変数は?・
・
・実際にコードを書く!
※その際に、自分でコメントを入れて全て
説明できるかを確認していく
- 投稿日:2020-06-02T20:36:52+09:00
【Error】編集後に画面遷移しない事象の解消法
概要
タスクの編集画面を実装し、確認のため編集を行い、ボタンを押下したのですが画面が変わらず。。。。
結果としては、「redirect_to」していなかったということでしたが、エラー解決するまでの経緯を備忘録として残します!
確認方法
【前提】
編集画面で登録ボタンを押した後は、ボタンは押下できない状況にあった。
画面遷移もされない。
①MySQLに編集データが保存されているかを確認
→
変更されている②エラー画面が出ていないのであれば、ターミナルにエラー情報が記入されているのではないかを確認
terminalNo template found for TasksController#update, rendering head :no_content Completed 204 No Content in 139ms (ActiveRecord: 3.4ms)あった
No templateって記載されてた!!③コントローラーを確認
→redirect_toが記載なし修正箇所
修正前
tasks_controller.rbdef update @task.update(task_params) if @task.valid? @task.save else flash.now[:alert] = 'タスク名を入力してください' render :index end end修正後
tasks_controller.rbdef update @task.update(task_params) if @task.valid? @task.save redirect_to group_tasks_path(@group), notice: 'タスクが変更されました' else flash.now[:alert] = 'タスク名を入力してください' render :index end end皆さま、お気をつけください
参考
エラー204に関するURLです。
https://developer.mozilla.org/ja/docs/Web/HTTP/Status/204
- 投稿日:2020-06-02T20:35:23+09:00
form_with scope
form_withのscopeについて
こんなフォームがあったけどscopeってどんな意味だってなった
ruby.rb<%= form_with scope: :session, url: sessions_path do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %>とりあえず検証ツールを見てみる
なるほどこんな働きなわけね。<form action="/sessions" method="post" data-remote="true"> <input type="text" name="session[name]"> </form>つまりscopeオプションはどんなはたらき?
spopeオブジェクトに私た値がname値のプレフィックス(接頭語)になっている
name = "session[name]"
という形でパラメータが送信されている。ってこと
どうやって受け取る?
パラメータのプレフィックスにsessionがついただけなので難しく考えることはないです
controller.rbdef create #スコープを用いて送信された値をsession[:user_id]に代入 session[:user_name] = session_params[:name] endこんな働きをしているってことですね。大したことはなかった。
本日はここまで。
form_withひとつとっても色々ありますね。一人前のエンジニアになるまであと87日
- 投稿日:2020-06-02T18:45:15+09:00
HerokuにBugsnagを導入
Heroku × Railsです。
アドオンを追加→APIキーを取得
ダッシュボードから操作する方法と、コマンドラインから操作する方法があります。
ダッシュボードから操作する場合
- Herokuのダッシュボードにアクセス
- Resourcesタブを開く
- Add-onsの下の検索窓に'Bugsnag'と入力
- Provisionをクリック
- Settingsタブを開く
- Reveal Config Varsをクリックし、BugsnagのAPIキーを確認する
コマンドラインから操作
Heroku CLIがインストールされている状態で以下のコマンドを叩く
$ heroku addons:create bugsnag $ heroku config:get BUGSNAG_API_KEY 70d9b0852a968b1d0d0e329b5507f287 #APIキーアプリケーション側の設定
Gemfilegem 'bugsnag'$ bundle install $ rails generate bugsnag 70d9b0852a968b1d0d0e329b5507f287 # APIキー
config/initializers/bugsnag.rb
が生成される。デフォルトではAPIキーがベタ書きされているので、環境変数にしまう。dotenv
というgemを使って以下のように記述しました。config/initializers/bugsnag.rbBugsnag.configure do |config| config.api_key = ENV['BUGSNAG_API_KEY'] #修正 endproduction環境でのみ動くように修正
config/initializers/bugsnag.rbBugsnag.configure do |config| config.api_key = ENV['BUGSNAG_API_KEY'] config.notify_release_stages = ['production'] #追加 endその他
raise
等を使って自分で発生させている例外を捕捉したい場合、Bugsnag.notify(exception)
を使う。begin raise 'Something went wrong!' rescue => exception Bugsnag.notify(exception) end参考
- 投稿日:2020-06-02T18:34:31+09:00
初心者向けRailsでブログを作ってみるチュートリアルその1
これはなに?
初心者向けRailsでブログを作ってみるチュートリアルその0 の続きです。
今回はブログサービスを作ります。
では、ブログサービスの機能を分解して考えてみましょう
実際のブログサービスにはいくつか機能がありますが、今回はブログサービスと呼ぶために必要なミニマムの機能を開発しましょう。機能一覧
訪問者用機能
- 記事閲覧機能
- コメント機能
管理機能
- 管理者ログイン機能
- 記事投稿機能
この中で、次のステップから最初に最低限ブログらしいものに必要な「記事閲覧機能」を作っていこうと思います。
サイトマップ
ワイヤーフレーム
今回は最初に作る記事閲覧機能に含まれる2画面についてのワイヤーフレームを draw.io で作ってみました。
今回は、全体の機能がコンパクトなためワイヤーフレームと次回以降でやるモデリングにとどめて細かい仕様設計などはしません(実際開発者が1-3名程度のスタートアップや新規事業だともっと大雑把なワイヤーフレームしかない状態で進めてもまあまあうまくいったりします)記事一覧画面
記事詳細画面
完成版にはコメント機能をつけますが、最初の時点では省略します
チュートリアルその1まとめ
今回は、開発に移る前の設計の初期段階として何を作るのか、というところを大まかに定義しました。
次回はプロジェクトのセットアップを行います。
- 投稿日:2020-06-02T18:32:12+09:00
道のりは長い…( ´Д`)y━・~~ MySQLが起動しない事象への対応
Railsを起動するため、Gem、bundleの再設定を行いました。ようやくアプリケーションを起動することができたのですが、アプリケーションにアクセスするとエラー出力され、アプリが動きません。
非常に道のりが長いですが、1つ1つ対応を進めていきます。エラー内容:
* Listening on tcp://localhost:3000 Use Ctrl-C to stop Started GET "/" for ::1 at 2020-06-02 11:31:07 +0900 Mysql2::Error::ConnectionError (Can't connect to local MySQL server through socket '/tmp/mysql.sock' (38)): (省略)①そもそもサービスが起動していなかった。(mysql.server statusで確認)
②ログを確認すると、プロセスを起動する際にファイルのオープンに失敗している
(ichikawadaisukenoMacBook-Air.local.err )200602 11:51:16 mysqld_safe mysqld from pid file /usr/local/var/mysql/ichikawadaisukenoMacBook-Air.local.pid ended③そういえば、先日Mysqlが稼働しなかったため、homebrewコマンドでインストールしなおした経緯がある。/usr/local/Cellar配下をみると、v5.6系とv8.0が存在していた。
競合している可能性もあるので、先日追加したv8.0系はアンインストールした。④さらにリンクも再設定すれば復旧するかもしれないと考え、以下を試した。
CMD> brew unlink mysql@5.6 Unlinking /usr/local/Cellar/mysql@5.6/5.6.42... 0 symlinks removed CMD> brew link --force mysql56 Linking /usr/local/Cellar/mysql@5.6/5.6.42... 99 symlinks created⑤この後、以下のファイルで直接5.6系のサービスを起動しようとするも
/usr/local/Cellar/mysql@5.6/5.6.42/support-files/mysql.server startERROR! The server quit without updating PID file (/usr/local/var/mysql/ichikawadaisukenoMacBook-Air.local.pid).⑥出来るだけ、mysqlの再インストールは選択したくなかったのですが、時間を消耗してしまうので、以下のコマンドからまず環境をクリーンにしました。
※ファイルが存在している場合はバックアップを取得する brew uninstall mysql56 $ sudo rm -rf /usr/local/mysql $ sudo rm -rf /Library/StartupItems/MYSQL $ sudo rm -rf /Library/PreferencePanes/MySQL.prefPane $ sudo rm -rf /Library/Receipts/mysql-.pkg $ sudo rm -rf /usr/local/Cellar/mysql* $ sudo rm -rf /usr/local/bin/mysql* $ sudo rm -rf /usr/local/var/mysql* ※ここにはファイルが存在したため、bk取得 $ sudo rm -rf /usr/local/etc/my.cnf $ sudo rm -rf /usr/local/share/mysql* $ sudo rm -rf /usr/local/opt/mysql*⑦最後にインストールを実行し、希望のサービスが稼働していることを確認しました。
[\W staff@term]brew install mysql56 Updating Homebrew... ==> Downloading https://homebrew.bintray.com/bottles/mysql%405.6-5.6.47.catalina.bottle.tar.gz Already downloaded: /Users/ichikawadaisuke/Library/Caches/Homebrew/downloads/c270819d76ed326059143e1b4c6c6c0ab672e4259c328c140ec71d917babc348--mysql@5.6-5.6.47.catalina.bottle.tar.gz ==> Pouring mysql@5.6-5.6.47.catalina.bottle.tar.gz ==> /usr/local/Cellar/mysql@5.6/5.6.47/bin/mysql_install_db --verbose --user=ichikawadaisuke --basedir=/usr/local/Ce ==> Caveats A "/etc/my.cnf" from another install may interfere with a Homebrew-built server starting up correctly. MySQL is configured to only allow connections from localhost by default To connect: mysql -uroot mysql@5.6 is keg-only, which means it was not symlinked into /usr/local, because this is an alternate version of another formula. If you need to have mysql@5.6 first in your PATH run: echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc For compilers to find mysql@5.6 you may need to set: export LDFLAGS="-L/usr/local/opt/mysql@5.6/lib" export CPPFLAGS="-I/usr/local/opt/mysql@5.6/include" To restart mysql@5.6 after an upgrade: brew services restart mysql@5.6 Or, if you don't want/need a background service you can just run: /usr/local/opt/mysql@5.6/bin/mysql.server start ==> Summary ? /usr/local/Cellar/mysql@5.6/5.6.47: 344 files, 155.2MB [\W staff@term]/usr/local/opt/mysql@5.6/bin/mysql.server status SUCCESS! MySQL running (37799)次はまたRailsが動かなくなりました…?
- 投稿日:2020-06-02T17:48:20+09:00
macOSをバージョンアップしたらrails s出来なくなった
問題
① macOS Catalina のバージョンを10.15.4にアップデートする
② 現在制作途中のアプリケーションのディレクトリにてrails sする
③ 以下のようなエラーが出る
Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
解決
https://nodejs.org/ja/download/current/
上記のサイトにて、最新版のNode.jsをインストールするか
ターミナルで、homebrewを用いてインストールを行う
% brew install nodejs
すると、無事rails sでローカルサーバーを起動できた。
- 投稿日:2020-06-02T17:46:32+09:00
Rspecでwebmockを使う
概要
外部APIなどにアクセスする機能をテストする際に、APIレスポンスのMockを定義できるgem。
Gemfileにwebmockを追加
group :test do gem 'webmock' end$ bundle install --path vendor/bundleテスト対象のmodel
model/smaple.rbrequire 'net/https' require 'uri' class Sample include ActiveModel::Model def get_request(api_url, params) uri = URI(api_url) uri.query = params request(uri) end endrspecを作る
前述のget_requestメソッドを実行すると、WebMockで定義したstubの内容が返る。
require 'rails_helper' require 'webmock/rspec' RSpec.describe Abyss, type: :model do describe 'facet', type: :facet do before do WebMock.enable! WebMock.stub_request(:any, "www.example.com").to_return( body: "This is a mock", status: 200, headers: { 'Content-Length' => 7 } ) end subject { Sample.new } it 'debug' do res = subject.send(:get_request, 'http://www.example.com', '') puts res.body # This is a mock end end end参考
- 投稿日:2020-06-02T17:07:16+09:00
capistranoで構築した環境でassetsの中身を1から作り直したい
なぜそんなことをしたくなったのか
capistranoのassets:precompileが走っている時にEC2がメモリ不足に陥り、インスタンスを再起動させた結果、deployが中途半端なところで終わってしまいました。
もし中途半端なコンパイル結果をもとにWebサーバーが動いて実行時エラーなんて起きたときには目も当てられないので、なんとかまっさらな状態に戻そうとしてあれこれしました。バージョン情報
rails (5.2.4.2) capistrano (3.11.0) sprockets (3.7.2) sprockets-rails (3.2.1)前提条件
current_pathはこのパスとする
- /var/www/app_name/current
shared_pathはこのパスとする
- /var/www/app_name/shared
先に結論
結論から言うと、やるべきことはこれです。
- /var/www/app_name/shared/public/assets の中身を消す
- /var/www/app_name/shared/tmp/cache/assets の中身を消す
- いつもどおりデプロイ
注意点があります。
capistranoにはdeploy:clobber_assets
のようなassetsをいかにも消しそうなコマンドが標準で用意されていますが、
これを使わずちゃんと手で消すということです。理由は後述します。説明
capistranoのディレクトリ構成
まず、capistranoによってデプロイされたサーバーの
/var/www/app_name
の中は、下記のような構造になっています。├── current (releases内の最新のディレクトリにシンボリックリンクが貼られてる) ├── releases │ ├── 20200601101105 │ ├── 20200601102714 │ └── 20200601105159 ├── repo ├── revisions.log └── sharedこのcurrentの下、つまり、releases/20200601105159の下には、あなたのプロジェクトのリポジトリにあるような構成になっているでしょう。
そこにはapp/があり、config/があり、public/があるはずです。このpublic/の下にはビルドされた静的ファイルが詰め込まれたassets/が入っている……ように見せかけて、
/var/www/app_name/current/public/assets
は/var/www/app_name/shared/assets
へのシンボリックリンクになっています。そして
/var/www/app_name/current/tmp/cache
は同様に/var/www/app_name/shared/tmp/cache
へのシンボリックリンクになっています。assets:precompileのキャッシュの仕組み
デフォルトのSprocketsは、development環境とproduction環境でtmp/cache/assetsにアセットをキャッシュします。
こう書かれている通り、tmp/cache/assetsにコンパイル結果のキャッシュがあるので、これを消します。
capistranoだと/var/www/app_name/shared/tmp/cache/assets
がこれにあたるので消します。Sprocketsのデプロイ結果のキャッシュの仕組み
assets:precompileのキャッシュとは別に、Sprocketsはデプロイ結果のファイルも取っておいてあります。
Sprocketsのコードにこんなのが書かれています。module Sprockets module Exporters # Writes a an asset file to disk class FileExporter < Exporters::Base def skip?(logger) if ::File.exist?(target) logger.debug "Skipping #{ target }, already exists" true else logger.info "Writing #{ target }" false end end def call write(target) do |file| file.write(asset.source) end end end end enddeploy:assets:precompileしているときに、標準出力でログがずらっと出てきているのを目にしていませんか? アレを実現しているのがおそらくは上のコードです。
logger.info "Writing
のところを見てください。こんな感じ:
01 I, [2020-06-01T08:56:09.691790 #10361] INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/develop/application-232387090aed00e6b038… 01 I, [2020-06-01T08:56:09.692557 #10361] INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/develop/application-232387090aed00e6b038… 01 I, [2020-06-01T08:56:17.418572 #10361] INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0002/application-d1bedc9937772b59211a… 01 I, [2020-06-01T08:56:17.418856 #10361] INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0002/application-d1bedc9937772b59211a… 01 I, [2020-06-01T08:56:21.414096 #10361] INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0003/application-5a60e0a26af1e40a9188… 01 I, [2020-06-01T08:56:21.414368 #10361] INFO -- : Writing /var/www/app_name/releases/20200601085319/public/assets/lb-0003/application-5a60e0a26af1e40a9188…この
skip?
の判定に引っかかった場合。
つまり、この例だと/var/www/app_name/releases/20200601085319/public/assets/develop/application-232387090aed00e6b038…
のdigest込みのファイルが既に存在していた場合、
Sprocketsはファイルを書き出しません。なので、capistranoだと
/var/www/app_name/shared/assets
に同じファイルがあった場合、このskip?でスキップされてしまうということです。
消しておく必要がありますね。おや? 便利なコマンドがありそうだが……?
capistranoは
cap -T
で全てのコマンドを表示してくれます。
assets関係だとこんなコマンドが見つかります。cap deploy:cleanup_assets # Cleanup expired assets cap deploy:clobber_assets # Clobber assets標準で
deploy:clobber_assets
というコマンドが用意されているではありませんか!
clobberとはぶん殴るという意味です。強制的に新しいassetsにしてくれそうだ! こりゃいいや! と思って使うと、実は思ったような挙動をしてくれません。$ bundle exec cap review deploy:clobber_assets 00:00 deploy:clobber_assets 01 bundle exec rake assets:clobber 01 I, [2020-06-01T11:22:46.062204 #6756] INFO -- : Removed /var/www/app_name/releases/20200601094959/public/assets 01 Removed webpack output path directory /var/www/app_name/releases/20200601094959/public/packsいかにもassetsをディレクトリごと消してくれていそうな雰囲気ですが、よく見るとRemoveしているのはただのシンボリックリンクで、肝心の中身は消えていません。マジか。
そして、そもそもコンパイル時のキャッシュであるtmp/cacheの方は消してくれていません。このことについてはissueも上がっているようです。
なので、「結論」に書いた通り、手動でコマンドを実行して消すか、自分でコマンドを定義してやるのが良さそうです。
- 投稿日:2020-06-02T15:36:30+09:00
「多段階選抜」の問題をRubyで解いてみた
問題
参考 (Clojure 版)
先に Clojure でも解いています。
https://qiita.com/kts_h/items/64bb74fdcef642198bc5Ruby 版
すでにRubyによる解答が多数出されているのですが、面白くなって自分でも解いてみたので、貼っておきます。
solve
の中のeach_char
ブロックを綺麗に書きたかったので、クラスにしてインスタンス変数の中身を次々と変えていくようにしてみました。class MultiStageSelection def self.solve(cmds) new.solve(cmds) end def initialize @enum = 1.step end def drop_nth(n) enum = @enum @enum = Enumerator.new do |y| n -= 1 loop do n.times { y << enum.next } enum.next end end self end def drop_before(test) enum = @enum @enum = Enumerator.new do |y| a, b = enum.next, enum.next loop do y << a unless send(test, b) a, b = b, enum.next end end self end def drop_after(test) enum = @enum @enum = Enumerator.new do |y| a, b = enum.next, enum.next y << a loop do y << b unless send(test, a) a, b = b, enum.next end end self end def drop_100 enum = @enum @enum = Enumerator.new do |y| 100.times { enum.next } loop { y << enum.next } end self end def solve(cmds) cmds.each_char do |cmd| case cmd when "s" then drop_before(:square?) when "S" then drop_after(:square?) when "c" then drop_before(:cube?) when "C" then drop_after(:cube?) when "h" then drop_100 else drop_nth(cmd.to_i) end end puts @enum.take(10).join(",") end private def square?(n) Math.sqrt(n).truncate**2 == n end def cube?(n) Math.cbrt(n).truncate**3 == n end end MultiStageSelection.solve("ss6cc24S") MultiStageSelection.solve("4scC3hh982Cc5s")
- 投稿日:2020-06-02T14:06:41+09:00
いいね機能の追加について
非同期通信でのいいね機能
しばらくJavaに浮気をしていて久しぶりに個人アプリ(Ruby)に触れたら結構記憶が飛んでたので思い出すためにいいね機能を搭載してみました。
いろいろな記事を見ながら実装したので自分用に纏めるメモです。
userとarticleを紐付けてボタンを押すといいねができるようになるまで。注意点...すでにアプリとしての形はある程度できている状態からの説明です
①モデル、テーブルの追加
rails g model favorite をしてマイグレーションファイルを修正
マイグレーションファイルclass CreateFavorites < ActiveRecord::Migration[5.2] def change create_table :favorites do |t| t.references :user, null: false, foreign_key: true t.references :article, null: false, foreign_key: true t.timestamps end end endその後rails db:migrate
モデルにアソシエーションを追記
user.rbhas_many :favorites, dependent: :destroy has_many :favorite_articles, through: :favorites, source: :articlearticle.rbhas_many :favorites, dependent: :destroy has_many :favorite_users, through: :favorites, source: :userfavorite.rbbelongs_to :user belongs_to :article validates :user_id, presence: true validates :article_id, presence: true validates_uniqueness_of :article_id, scope: :user_id②コントローラの追加(ついでにルーティング)
rails g controller favorites でfavoriteコントローラを追加する
そして以下のように追記favorites_contoroller.rbbefore_action :set_article, only: [:create, :destroy] def create @favorite = Favorite.create(user_id: current_user.id, article_id: @article.id) end def destroy @favorite = Favorite.find_by(user_id: current_user.id, article_id: @article.id) @favorite.destroy end private def set_article @article = Article.find(params[:article_id]) endルーティングも編集
routes.rbresources :articles do resources :favorites , only: [:create, :destroy] #追記 end③ビューの編集
いいねをするボタンを部分テンプレートを用いて追加
view.content = render "layouts/favorite", article: @article_favorite.html.haml.content__favorite{id: "like-#{@article.id}"} - if Favorite.find_by(user_id: current_user.id, article_id: article.id) .btn1 = link_to article_favorite_path(article.id, current_user.id), method: :delete, class: "like-delete", remote: true do = icon('fa', 'star') お気に入り追加済み = article.favorites.length - else .btn2 = link_to article_favorites_path(article.id), method: :post, class: "like-create", remote: true do = icon('fa', 'star') お気に入り = article.favorites.lengthremote:trueをつけるとリンクを踏んだ時ajaxが起動する。
④ボタンを非同期通信にする
create.js.haml と destroy.js.hamlファイル作成し追記
views/favorites/create.js.haml$("#like-#{@article.id}").html("#{j(render partial: 'layouts/favorite', locals: { article: @article })}");views/favorites/destroy.js.haml$("#like-#{@article.id}").html("#{j(render partial: 'layouts/favorite', locals: { article: @article })}");ボタンにつけたidを参照してボタンが押されるとファイルを再読み込みするようにしてあるっぽい。
あとはボタンの見た目とかを綺麗に整えたら完成。
参考にした記事
https://qiita.com/yummy888/items/2b7708a498861e5ba733
https://qiita.com/nojinoji/items/2c66499848d882c31ffa
- 投稿日:2020-06-02T12:06:33+09:00
【Rails】Google Mapの表示方法
目標
前提
下記実装済み。
・Googleアカウントを作成済み。
・Slim導入
Google Cloud Platform
に登録1.下記リンクにアクセス
2.「使ってみる」をクリック
3.プロジェクト名(適当で良い)を入力し、「作成」をクリック
4.「請求先アカウントの作成」をクリック
5.利用規約にチェックを入れ、「続行」をクリック
6.請求情報を入力し、「無料トライアルを開始」をクリック
APIキー
を取得1.「APIの概要に移動」をクリック
2.「APIとサービスを有効化」をクリック
3.「Maps JavaScript API」をクリック
4.「有効にする」をクリック
5.「認証情報」をクリック
6.「APIとサービスの認証情報」をクリック
7.「認証情報を作成」をクリック
8.「APIキー」をクリック
9.一旦「閉じる」をクリック
10. 「APIキーの名前」をクリック
11.認証情報の設定をする
①アプリケーションの制限
なし
を選択する。②APIの制限
キーを制限
を選択し、プルダウンメニューからMaps JavaScript API
を選択する。③
Maps JavaScript API
が選択されている事を確認して、保存
をクリック
12.赤枠で囲われているマークをクリックし、APIキーをコピー
実装
1.APIキーを環境変数化
①「gem 'dotenv-rails'」を導入
Gemfilegem 'dotenv-rails'ターミナル& bundle
②アプリケーション直下に「.env」ファイルを作成
※アプリケーションのディレクトリに移動してから下記コマンドを実行ターミナル$ touch .env③
.env
ファイルを編集.envGOOGLE_MAP_API = 'コピーしたAPIキー' # 追記④
.gitignore
ファイルを編集.gitignore/.env # 追記2.ビューを編集
~html.slim/ マップを表示 #map style='height: 500px; width: 500px;' / APIを読み込み - google_api = "https://maps.googleapis.com/maps/api/js?key=#{ ENV['GOOGLE_MAP_API'] }&callback=initMap".html_safe script{ async src=google_api } javascript: let map; function initMap() { geocoder = new google.maps.Geocoder() // マップを作成 map = new google.maps.Map(document.getElementById('map'), { // マップの中心に表示する場所の緯度経度を指定 center: { lat: 40.7828, lng:-73.9653 }, zoom: 12, }); // マーカーを立てる場所の緯度経度を指定 marker = new google.maps.Marker({ position: { lat: 40.7828, lng:-73.9653 }, map: map }); }
- 投稿日:2020-06-02T12:06:23+09:00
[Rails]初学者がform_withの流れを整理・理解するための記事
はじめに
Rails学習初期に、form_withの流れが理解できなかったのでまとめます。
Rails5.2.4です。先にまとめ
「早くまとめろよ!」という方もおられるかと思いますので、先に流れをまとめます。
簡単に解説
UserモデルとPostモデルがある状況です。
①newアクションで、ビューページ(new.html.erb)を表示
newアクションを呼び出すリクエストが出されたら、まずはpostの新規投稿(new)ページが表示される
※イメージ
②newアクションで生成している
@post
インスタンスをビューに渡すpost_controller.rbclass PostsController < ApplicationController ~~ def new @post = Post.new #ここで生成されている@postが、new.html.erbに渡される end ~~④createアクションを呼び出す
上記の画像のように、投稿する
ボタン(form_withのsubmit)をクリックすると、form_withがcreateアクションを呼び出してくれます。※form_withヘルパーメソッドは、渡されたインスタンス(この場合は
@post
)のプロパティを見て、createに飛ばすかupdateに飛ばすかを判断してくれる便利な子です。form_withが考えてること- @post が空 → 初めて作るんだな!じゃあcreate! - @post が空じゃない → 変更したいんだな!じゃあupdate!⑤post_paramsメソッドを呼び出す
post_controller.rbclass PostsController < ApplicationController ~~ def create #createアクションが呼び出され、Post.create(post_params)が実行される→ post_paramsが呼び出される Post.create(post_params) redirect_to root_path end private def post_params params.require(:post).permit(:title, :content).merge(user_id: current_user.id) end ~~⑥paramsで受け取った値のうち、permitで受け取る値を指定(許可)
paramsで受け取る値の状態[1] pry(#<PostsController>)> params => <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"oLGgLMTcqSPi7cbK9j1wFhH5rqiwXYO7GyRoZcZFZe6Y5VdbHrWCOyeo37kgW/bsl+eANQrz7p/lAzZMnAS8Gg==", "post"=>{"title"=>"今日学んだこと", "content"=>"form_withは賢い子!"}, "commit"=>"投稿する", "exept"=>:index, "controller"=>"posts", "action"=>"create"} permitted: false>見にくいので本来ない位置で改行していますが、通常は1行で返ってきます。
"post"=>{"title"=>"今日学んだこと", "content"=>"form_withは賢い子!"},
がpost_params
で扱う値です。
permit
で受け取る値を許可しておかないと、意図しない値がテーブルに保存される可能性があるので、指定がほぼ必須です。⑦post_paramsの値でPostをcreateする(成功すればテーブルに保存される)
post_paramsメソッドで返された値で、Post.createすることになるので、
実質下記と同じ状態です。def create Post.create(title: "今日学んだこと", content: "form_withは賢い子!", user_id: 1) redirect_to root_path endこの流れを確認した上で、もう一度冒頭の画像を見ると理解が深まると思います!
おわりに
form_tag、form_forとの違いはこちらの記事にまとめています。
[Rails]hamlでのform_with/form_for/form_tagの書き方間違い等ありましたらご指摘いただけると幸いです。
- 投稿日:2020-06-02T11:52:08+09:00
【Rails】rails newしてPostgreSQLでデータベース作成まで
はじめに
今年はWebにも少しづつ挑戦しようということで、参考文献の一番多そうなRailsをイジイジしています。取り敢えずローカルでの環境構築を終えてRails newして色々と試しているところです。
このあたりで一度頭の中を整理しつつテンプレ化しておきたいのと、間違いの指摘など頂けたら嬉しいなあと思って記事を書いています。
あとはRails newがうまくいかない人の参考になれば嬉しいです。環境
terminalWindows10 ruby 2.6.6 Rails 6.0.3.1 psql (PostgreSQL) 12.3
環境構築に関しては書きませんので必要に応じてお調べください。
データベースはPostgreSQLに変更します。
それとgemについては基本的にローカルに入れてます。どちらでもいいみたいなんですが、なんとなくそうしています。アプリケーションのディレクトリ内にGemfileを作成する
ディレクトリは適当に作ってください。
terminalC:\Users\user\sample_app> bundle initGemfileに追加するgemを記述する
作成したGemfileをエディタで開き、以下を追加する。
Gemfilegem 'rails'//コメントアウト(#)を外すだけ gem 'pg'//PostgreSQLに変更する場合gemのバージョンを指定したほうがいいのか、よく分からなかったので一先ず指定しないでおく。
bundle installする
terminalbundle install --path vendor/bundle
--path vendor/bundleはgemをローカルにインストールする場合に指定する。こうすることでvendor/bundle内にgemがインストールされるらしい。
--path vendor/bundleは最初の一回目だけ指定すれば、あとは省略できるみたい。rails newする
terminalbundle exec rails new . -d postgresql --skip-turbolinks --skip-test
bundle execはRailsをローカルにインストールした場合につける。以降全てのrailsコマンドにつけます。
-d postgresql --skip-turbolinks --skip-testはオプションでこれら以外にも色々あるみたいなので必要に応じてお調べてください。
いまいち分からないものが多いので取り敢えずみんな書いているものだけ書く。SQL Shellでデータベースを作成する
SQLShell(psql)create role APPLICATION_NAME with createdb login password 'PASSWORD'; select * from pg_user;APPLICATION_NAMEにはユーザーネーム(アプリ名)、PASSWORDには任意のパスワードをいれます。
select * from pg_user;でロールが作成されているか確認します。database.ymlを更新する
configフォルダ内のdatabase.ymlに以下を記述します。
database.yml・ ・ default: &default adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: APPLICATION_NAME//追加(データベース作成時に決めたユーザーネーム) password: PASSWORD//追加(データベース作成時に決めたパスワード) host: localhost//追加 ・ ・database.ymlを保存したらデータベースも更新します。
terminalbundle exec rails db:migrate:reset
サーバーを起動する
terminalbundle exec rails s
ブラウザでhttp://localhost:3000/
にアクセスして画像が表示されれば成功。
- 投稿日:2020-06-02T11:09:20+09:00
複数の正規表現にmatchするかの判定
こんな正規表現のバリデーションメソッドがあった時、
def customer_number_valid_format? return true if a.blank? return true if b.blank? binding.pry regex = /\A(\d{5})-?(\d{5})-?(\d{1})-?(\d{2})\z/, /\A(\d{4})-?(\d{3})-?(\d{3})\z/ # p regex => [/\A(\d{5})-?(\d{5})-?(\d{1})-?(\d{2})\z/, /\A(\d{4})-?(\d{3})-?(\d{3})\z/] return true if regex.blank? unless customer_number.match?(regex) ? self.errors.add(:customer_number, :invalid_and_confirm, target: 'お客様番号') end endunless customer_number.match?(regex)ここで
customer_number
が12345-12345-1-12
か1234-123-123
の時にtrue
を返して欲しいが上記コードだと[27] pry(#<Order>)> customer_number.match?(regex) TypeError: wrong argument type Array (expected Regexp)となる。
match?
はStringクラスのメソッドだからだね。
https://docs.ruby-lang.org/ja/latest/method/String/i/match=3f.html解決
配列に入ってるどっちかの正規表現にあってるか判定するには
Regexp.union
というものを使うらしい。customer_number.match?(Regexp.union(regex))[25] pry(#<Order>)> Regexp.union(regex) => /(?-mix:\A(\d{5})-?(\d{5})-?(\d{1})-?(\d{2})\z)|(?-mix:\A(\d{4})-?(\d{3})-?(\d{3})\z)/ # 配列ではなくパイプで繋がれ他文字列になる [26] pry(#<Order>)> low_voltage_customer_number.match?(Regexp.union(regex)) => true
- 投稿日:2020-06-02T10:50:58+09:00
Railsサーバが起動しない2。エラー「autodetect': Could not find a JavaScript runtime. 」
発生した背景
久々にアプリケーションを起動しようとしたところ、アプリが起動しない。
※こちらは「サーバが起動しないこと」の記事を連載しています。以下のエラーが発生しました:
Traceback (most recent call last): 61: from bin/rails:3:in `<main>' 60: from bin/rails:3:in `load' ------- 一部割愛 3: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/bootsnap-1.4.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require' 2: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/execjs-2.7.0/lib/execjs.rb:4:in `<main>' 1: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/execjs-2.7.0/lib/execjs.rb:5:in `<module:ExecJS>' /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)対応方法
- ①ローカルで「bundle install --path vendor」を実施してみる
Fetching libv8 3.16.14.19 Installing libv8 3.16.14.19 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. In file included from ../src/allocation.cc:33: ../src/utils.h:33:10: fatal error: 'climits' file not found #include <climits> ^~~~~~~~~ 1 error generated. (省略)上記のエラーが発生する。
- ②エラー文言の指示に従い、以下のコマンドを実行する
CMD> gem install libv8 -v '3.16.14.19' --source 'https://rubygems.org/'①と同じエラーメッセージが出力され、コマンドに失敗する。
- ③現在、mac-osは「Catalina v10.15.4」を使用している。調べたところ下位互換性がないとの情報があったため、以下コマンドを実行し、互換性をとる。
CMD> brew install v8-315brewコマンドを実行し、インストールしたので、ローカルアプリケーションの設定を変更します。
CMD>bundle config --local build.therubyracer --with-v8-dir=$(brew --prefix v8-315)最後にbudleインストールをローカルで実行しました。
問題が解決し、Railsサーバが起動出来るようになりました。
- 投稿日:2020-06-02T10:50:58+09:00
エラー行が多い…(-_-)zzz Railsサーバが起動しない2。「autodetect': Could not find a JavaScript runtime. 」
発生した背景
久々にアプリケーションを起動しようとしたところ、アプリが起動しない。
※こちらは「サーバが起動しないこと」の記事を連載しています。以下のエラーが発生しました:
Traceback (most recent call last): 61: from bin/rails:3:in `<main>' 60: from bin/rails:3:in `load' ------- 一部割愛 3: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/bootsnap-1.4.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require' 2: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/execjs-2.7.0/lib/execjs.rb:4:in `<main>' 1: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/execjs-2.7.0/lib/execjs.rb:5:in `<module:ExecJS>' /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)対応方法
- ①ローカルで「bundle install --path vendor」を実施してみる
Fetching libv8 3.16.14.19 Installing libv8 3.16.14.19 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. In file included from ../src/allocation.cc:33: ../src/utils.h:33:10: fatal error: 'climits' file not found #include <climits> ^~~~~~~~~ 1 error generated. (省略)上記のエラーが発生する。
- ②エラー文言の指示に従い、以下のコマンドを実行する
CMD> gem install libv8 -v '3.16.14.19' --source 'https://rubygems.org/'①と同じエラーメッセージが出力され、コマンドに失敗する。
- ③現在、mac-osは「Catalina v10.15.4」を使用している。調べたところ下位互換性がないとの情報があったため、以下コマンドを実行し、互換性をとる。
CMD> brew install v8-315brewコマンドを実行し、インストールしたので、ローカルアプリケーションの設定を変更します。
CMD>bundle config --local build.therubyracer --with-v8-dir=$(brew --prefix v8-315)最後にbudleインストールをローカルで実行しました。
問題が解決し、Railsサーバが起動出来るようになりました。
- 投稿日:2020-06-02T05:15:08+09:00
SinatraでHTTP Live Streaming
動画をダイレクトにサーバから視聴する方法は他にもストリーミング系の仕組みがあるらしいのですが、Sinatraがストリーミングにうまく対応できてないような感じなので、ガチガチの配信ではなく「なんちゃってストリーミング動画配信」をする為のメモです。Appleが考案していてiOSとも親和性が高い技術なので採用しました。
環境
- Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-99-generic x86_64)
- Sinatra 2.0.5 (bundlerで起動する前提です)
- ffmpeg N-97994-g83fa39e
UbuntuとSinatraは既に環境に配置されているとします。
また、インストール作業用フォルダとffmpegのバイナリを格納するフォルダをhomeに作成します。
- ffmpeg_sources
- ffmpeg_build
- bin
ffmpegのインストール
ffmpegをaptでインストールするとlibfdk-aacが含まれていませんので、自分でカスタマイズインストールしなければなりません。libfdk-aacはHTTP Live Streamingを行うために必要な動画を細切れに分割してプレイリストファイルを作るのに必要です。
ffmpegで利用するコーデック群のライブラリをインストールする為に事前に必要なツール群のインストール
consolesudo apt-get update -qq && sudo apt-get -y install \ autoconf \ build-essential \ cmake \ git-core \ libass-dev \ libfreetype6-dev \ libgnutls28-dev \ libsdl2-dev \ libtool \ libva-dev \ libvdpau-dev \ libvorbis-dev \ libxcb1-dev \ libxcb-shm0-dev \ libxcb-xfixes0-dev \ pkg-config \ texinfo \ wget \ yasm \ zlib1g-dev
ffmpegで利用するコーデック群のライブラリのインストール
consolesudo apt-get install nasm sudo apt-get install libx264-dev sudo apt-get install libx265-dev libnuma-dev sudo apt-get install libvpx-dev sudo apt-get install libfdk-aac-dev sudo apt-get install libmp3lame-dev sudo apt-get install libopus-dev
ffmpegのビルド
consolecd ~/ffmpeg_sources && \ wget -O ffmpeg-snapshot.tar.bz2 https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2 && \ tar xjvf ffmpeg-snapshot.tar.bz2 && \ cd ffmpeg && \ env PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure \ --prefix="$HOME/ffmpeg_build" \ --pkg-config-flags="--static" \ --extra-cflags="-I$HOME/ffmpeg_build/include" \ --extra-ldflags="-L$HOME/ffmpeg_build/lib" \ --extra-libs="-lpthread -lm" \ --bindir="$HOME/bin" \ --enable-gpl \ --enable-libass \ --enable-libfdk-aac \ --enable-libfreetype \ --enable-libmp3lame \ --enable-libopus \ --enable-libvorbis \ --enable-libvpx \ --enable-libx264 \ --enable-libx265 \ --enable-nonfree && \ env PATH="$HOME/bin:$PATH" make && \ make install※gnutlsとlibaomはなんかエラーが出たので(今回は必要ないので)除外
テスト
今回は人気アニメ「ミンクの刃」をストリーミング配信で観てみましょう。
mp4形式に変換
consolesudo ffmpeg -i 'ミンクの刃 01.mkv' -vcodec copy 'ミンクの刃 01.mp4'
mp4形式からストリーミング配信用のHLS形式へ変換
consolesudo /home/ユーザ名/bin/ffmpeg -i 'ミンクの刃 01.mp4' -codec copy -vbsf h264_mp4toannexb -map 0 -f segment -segment_format mpegts -segment_time 5 -segment_list 'ミンクの刃 01'/playlist.m3u8 'ミンクの刃 01'/a%03d.ts
配信用サイトの作成
test.rbrequire 'sinatra' require 'uri' set :environment, :production get '/' do erb(:test); end get '/:file_name' do |file_name| sendFilePath = "disk/live/minknoyaiba/#{file_name}"; if file_name.match(/.+m3u8$/) then send_file(sendFilePath, :filename => file_name, :type => "application/vnd.apple.mpegurl"); else send_file(sendFilePath, :filename => file_name, :type => "video/mp2t"); end end/source/onlineフォルダ内にtest.rbがあり、同フォルダ内にdiskフォルダが存在する環境です。minknoyaibaフォルダ内にはHLS用のプレイリスト(.m3u8)や分割動画ファイル(.ts)が入っていると思ってください。
先ほど「ミンクの刃 01」フォルダにHLSのファイル一式を変換したのに何故「minknoyaiba」フォルダに入れるのか?それはSinatraのsend_fileが日本語フォルダをうまく認識してくれないからです。
また外部から接続する為にset :environment, :production
しています。test.erb<!DOCTYPE html> <html> <head> <title>HTTP Live Streaming Example</title> </head> <body> <video src="/playlist.m3u8" width="400" height="300" controls></video> </body> </html>配信用サイトの起動
consolebundle exec ruby -I /source/class -C/source/online test.rb
この時点で以下にアクセスするとサイトで動画が見れます。
http://テスト用サーバのIP:4567/
次にiOS側で動画を観てみましょう。iOSアプリの作成
MinkTubeBase.h// global require #import <AVKit/AVKit.h> #import <AVFoundation/AVFoundation.h> // local require @interface MagiTubeBase : AVPlayerViewController { }AVKitとAVFoundationをプロジェクトに追加しておいてください。
MinkTubeBase.m@implementation MagiTubeBase -(void)viewDidLoad { [super viewDidLoad]; __strong NSURL *url = [NSURL URLWithString:@"http://テスト用サーバのIP:4567/playlist.m3u8"]; __strong AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url]; __strong AVPlayer *player = [AVPlayer playerWithPlayerItem:item]; self.player = player; } @endおしまい
ミンクの刃面白いですよね!
最後に溶鉱炉の中に沈むミンクが最高にカッコイイ!?
- 投稿日:2020-06-02T02:42:47+09:00
AWS Cloud9でRVMのアップデートをする
Cloud9の環境でプリインストールされている「RVM」ですが、Rubyの最新の安定版をインストールする際にRVMをアップデートしないといけなかったのでメモします。
RVMとは
RVMはRuby Version Managerの略です。
Rubyのバージョンを管理するのに使います。
例えばインストール、アンインストール、バージョンの切り替えなどを実行できます。公式サイト
RVM: Ruby Version Manager - RVM Ruby Version Manager - DocumentationRVMのサイトからキーをインストールしておく
セキュリティを確保するため、RVMでは署名をチェックしています。キーをインストールしていない場合はしておきます。
RVM: Ruby Version Manager -
からコマンドをコピーします。トリプルクリックすると便利です。
Cloud9のコンソールで、sudo(root権限で実行)の後にコマンドを貼り付けて実行します。
これでキーをインストールできました。
RVMのバージョンを確認する
今1.29.8ですが、これを1.29.10にしたいんです!
RVMのバージョンを更新する
アップデート(アップグレード)はgetコマンドです。
rvm get <version>
1.29.10に更新したい場合は以下のようになります。
rvm get 1.29.10
確認のため、RVMのバージョンを表示します。
rvm -v
これでアップデートできました。(おまけ)RVMのヘルプを表示する
コマンドを調べたいときに使います。
- 投稿日:2020-06-02T00:43:43+09:00
Ruby+LambdaでAWS SDKを使おうとしたら `sam local` がめちゃくちゃ遅くなったんだけど
起こった現象
LambdaをRuby実装で使っていました。AWS SAM CLIを利用して開発を行っていました。開発自体は何事もなくすすんでいたのですが、機能追加のためにAWS SDKを使うように変更したところ
sam local
がすごく遅くなったのでした。こんな簡単なサンプルアプリでここまで遅くなるのはおかしいでしょ…と調べてみました。サンプルプロジェクトはこちら
※deployして試したあとはstack及びcloudwatchのロググループを忘れずに削除してください!さっさと結論だけ
Lambdaのランタイムには、各環境に合わせてAWS SDKがインストールされているのでLambdaのデプロイパッケージには含めなくてもよいです。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html
特にRubyであれば Gemfileへの「gem 'aws-sdk', '~> 3'」のような記載は不要 です!
普段利用するGemをGemfileに書く癖がついているとすごく気持ち悪いですが、グローバルなgemにインストールされたgemはそのままrequireできますので問題ないです。Gemfileに「gem 'aws-sdk', '~> 3'」を書いた場合(修正前)と、削除(修正後)して比較する
Gemfileに書くかどうかでpackageのサイズが大きく変わります(AWS SDKだけで23M近くある)ので、その差が大きくでる結果となりました。
ちなみに修正前だと
sam local
はかろうじて動きましたのでその比較だけ。デプロイ後はLambdaのメモリ及びタイムアウト時間をかなり伸ばさないとうまく動きませんでした。修正前
Init Duration: 54544.65 ms
とかなり時間がかかります。$ sam local invoke HelloWorldFunction --event events/event.json --env-vars env.json Invoking app.lambda_handler (ruby2.5) Fetching lambci/lambda:ruby2.5 Docker container image...... Mounting /home/ubuntu/environment/test/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container START RequestId: 22ddc38d-745a-1d37-0ce9-324760f75ec4 Version: $LATEST END RequestId: 22ddc38d-745a-1d37-0ce9-324760f75ec4 REPORT RequestId: 22ddc38d-745a-1d37-0ce9-324760f75ec4 Init Duration: 54544.65 ms Duration: 801.49 ms Billed Duration: 900 ms Memory Size: 128 MBMax Memory Used: 60 MB {"statusCode":200,"body":"{\"message\":\"t2.micro\"}"}修正後
Init Duration: 2266.99 ms
と改善!$ sam local invoke HelloWorldFunction --event events/event.json --env-vars env.json Invoking app.lambda_handler (ruby2.5) Fetching lambci/lambda:ruby2.5 Docker container image...... Mounting /home/ubuntu/environment/test/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container START RequestId: a81dffd9-915d-13db-3ed7-4fe0e3b63464 Version: $LATEST END RequestId: a81dffd9-915d-13db-3ed7-4fe0e3b63464 REPORT RequestId: a81dffd9-915d-13db-3ed7-4fe0e3b63464 Init Duration: 2266.99 ms Duration: 557.91 ms Billed Duration: 600 ms Memory Size: 128 MBMax Memory Used: 58 MB {"statusCode":200,"body":"{\"message\":\"t2.micro\"}"}最後に
Lambdaを使うならサイズやパフォーマンスには注意を払わないと余計な費用が掛かってしまいますね。処理時間もパッケージサイズも軽く。Lambdaの分割や、AWS Lambda Layersなども使って対応していきたいと思います。