- 投稿日:2020-06-01T23:50:08+09:00
[Rails] ECサイトのDB設計
はじめに
Ruby on Rails(-v 5.2)を使用してECサイトを作成しました。今回はその際のDB設計についてこんな感じでやったよ、っていうのを書いていきます。
参考にしたサイト
若手プログラマー必読!5分で理解できるER図の書き方5ステップ
上記サイトの『システムシナリオを確認する』を参考にしていました。
以下の通り。
ユースケース記述
- ユースケース名:顧客が商品を注文する
- 概要 :ECサイトで商品を注文する
- アクター :顧客
- 事前条件 :ログインしている
- トリガー :カートに商品をいれ、カート画面で”注文手続き”ボタンを押す
- 基本フロー
- 注文画面で送付先、決済方法を入力する
- 注文内容の確認”ボタンを押す
- 注文内容を確認して、”注文確定”ボタンを押すと購入が完了する
- 備考 :”注文内容の確認”を押す際、入力をしていないと”注文内容の確認”ボタンを押せないようにする
エンティティの洗い出し
- 顧客
- 商品
- 注文
- 決済
- 送付先住所
マスタ系かトランザクション系か
- マスタ系
- 顧客
- 商品
- トランザクション系
- 注文
- 決済
- 送付先住所
初期段階でここ位まで考えていましたが、作成に取りかかってからは適宜変更はしていました。
決済方法もクレジットカード(PAY.JPを使用)のみの決済にしています。本題のDB設計
ECサイトでは必須機能であるカート機能のためのカートテーブル及び、注文の詳細テーブルをどうするべきか悩みましたが、以下のような感じで中間テーブルとして落とし込みました。
カートテーブルに関しては、カートとしての役割でしかないのでカラムはPK以外ありません。
ユースケースでは記載していませんでしたが、ログインしていなくてもカート機能は使用したいので、user_id(FK)は持たせていません。users table
Column Type Options nickname string null: false string default: "", null: false password string null: false encrypted_password string default: "", null: false reset_password_token string reset_password_token string admin boolean default: false Association
- has_many :comments, dependent: :destroy
- has_one :card, dependent: :destroy
- has_one :address, dependent: :destroy
- has_many :orders, dependent: :nullify
orders table
Column Type Options user references foreign_key: true card references foreign_key: true product references foreign_key: true quantity integer null: false status integer default: 0, null: false postage integer default: 0, null: false price integer null: false Association
- belongs_to :user
- has_many :products, through: :order_details
- has_many :order_details, dependent: :destroy
- belongs_to :card
- belongs_to :address
cards table
Column Type Options customer_id string null: false card_id string null: false user_id string null: false Association
- belongs_to :user
- has_one :order, dependent: :nullify
addresses table
Column Type Options destination_family_name string null: false destination_first_name string null: false destination_family_name_kana string null: false destination_family_name_kana string null: false postcode integer null: false prefecture_code string null: false address_city string null: false address_street string null: false address_building string phone_number bigint user references foreign_key: true, null: false Association
- belongs_to :user
- has_one :order, dependent: :nullify
brands table
Column Type Options name string Association
has_many :products, dependent: :destroy
sexes table
Column Type Options name string Association
has_many :products, dependent: :destroy
seasons table
Column Type Options name string Association
has_many :products, dependent: :destroy
smell_impressions table
Column Type Options name string Association
has_many :products, dependent: :destroy
smell_types table
Column Type Options name string Association
has_many :products, dependent: :destroy
user_scenes table
Column Type Options name string Association
has_many :products, dependent: :destroy
products table
Column Type Options name string null: false namelap string null: false description text image text price integer stock_quantity brand references foreign_key: true sex references foreign_key: true season references foreign_key: true smell_type references foreign_key: true main_spice references foreign_key: true smell_impression references foreign_key: true use_scene references foreign_key: true Association
- belongs_to :brand
- belongs_to :sex
- belongs_to :season, optional: true
- belongs_to :smell_type
- belongs_to :main_spice
- belongs_to :smell_impression
- belongs_to :use_scene
- has_many :comments, dependent: :destroy
- has_many :order_details
- has_many :orders, through: :order_details
- has_many :line_items, dependent: :destroy
carts table
Column Type Options Association
has_many :line_items, dependent: :destroy
comments table
Column Type Options user references foreign_key: true product references foreign_key: true text text null: false rate float Association
belongs_to :product
belongs_to :userorder_details table
Column Type Options product references foreign_key: true order references foreign_key: true quantity integer null: false Association
- belongs_to :order
- belongs_to :product
line_items table
Column Type Options product references foreign_key: true cart references foreign_key: true quantity integer default: 0, null: false Association
- belongs_to :product
- belongs_to :cart
- 投稿日:2020-06-01T23:12:31+09:00
Ruby の Range で ("b".."aa") が使えなかった件について
背景
事の発端
- Kinx では Ruby と同じように文字列の Range をサポートしたが、"z" の次は "aa" なので、単純に辞書順ではない
<=>
演算子を定義してました。- で、普通にソートすると、当然辞書順にはなりません。(文字列長いほうが大きいと判断されるため)
- で、そういえば Ruby は辞書順ですね、文字列のソート。なんか変だな。
[注] Kinx って何?
最初の動機 ⇒ こちらをご覧ください
リポジトリ ⇒ こちらをご覧ください確認してみた
irb で
"b" <=> "aa"
やってみよう、irb(main):001:0> "b" <=> "aa" => 1 irb(main):002:0>おゃ?!
では Range では?試しに
"a".."c"
を表示すると。irb(main):002:0> a = "a".."c" => "a".."c" irb(main):003:0> a.each {|e| p e } "a" "b" "c" => "a".."c"ちゃんと列挙されます。では疑惑の
"b".."aa"
は?irb(main):004:0> a = "b".."aa" => "b".."aa" irb(main):005:0> a.each {|e| p e } => "b".."aa"なんと、
"b".."aa"
は使えないのか...結論
私の気持ち
やっぱり
"b".."aa"
も 動いてほしい よね。"b"
の次は"c"
で、そのまま"z"
まで進んだ後に"aa"
で終了してほしいなーと思いましたが、他の方はどうなんでしょう。実際に行ったこと
なので、最新のリポジトリ(0.8.1 には入ってません)では以下のように修正しましたよ。
- 文字列の
<=>
演算子は辞書順に戻した。class Range
の<=>
の部分は文字列だけ特別扱いにして"b".."aa"
も使えるようにした。
"b", "c", "d", ..., "z", "aa"
になる。おわりに
ということで、ここ(=文字に対する Range の仕様)は Ruby と仕様が違います。何か問題があればお知らせください。
まあ、こんな特殊な条件ではあまり使わないので、誰も困ってなさそうですが。だから別にどっちでもいいと言えばどっちでもいいかもしれません。細かい話でした。
Ruby 的にはどうなんでしょうね。想定内なのか、想定外なのか。意図してなのか、そうでないのか。まあ定義が明確で、定義に従った動作なので、こういうもの=仕様、ってことで落ち着きそうですが。
ではまた、次回。
- 投稿日:2020-06-01T23:08:21+09:00
JekyllでパーマリンクにFront Matterのカスタム変数を使う方法
「Front Matterのカスタム変数をパーマリンクに使いたい!」ので、使えるようにしてみたという記事です。
背景
Jekyll で生成する記事のURLは、
_config.yml
の設定や Front Matter での直接の指定でカスタマイズできます。以下で軽く説明しますが、詳しくは、公式ガイドの Permalinks | Jekyll を参照ください。日本語版
_config.yml
で記事のURLを設定する方法_config.ymlpermalink: /:categories/:title
_config.yml
で、上記のような指定をすることで、全ポストのURLを一定のルールで決めることができます。しかし、事前に用意された Placeholder(置換用変数) しか使えません。
(組み込みフォーマットなどの説明はここでは割愛します)Front Matter で記事のURLを設定する方法
Front Matter とは、ファイルの最初に3つのダッシュで挟まれたYAMLで、このブロックがあると Jekyllさんはファイルをいい感じに処理してくれます。記事のタイトルやカテゴリーなどを設定するのに使ったことがある方も多いでしょう。
about.md--- permalink: /about/ ---この方法は柔軟に好きなパーマリンクを設定できますが、一定のルールで複数のページのパーマリンクを設定するのには向いていません。
本題 (
_plugins
を使ってパーマリンクをカスタムする方法)しかし、「Front Matterのカスタム変数をパーマリンクに使いたい!」という場合にはどちらも向いていません。そこで、
_plugins
を使ってパーマリンクをカスタムする方法の登場です。
今回はこんな記事たち↓があるとします。デフォルトの変数の他に、author
という変数を使っていますね。この著者猫さんの名前をパーマリンクに含めるのが目標です。2020-06-01-test.md--- layout: post title: "テスト記事だにゃー" date: 2020-06-01 11:14:26 +0900 categories: cat author: toraneko ---
- まず、サイトのrootディレクトリ直下に
_plugins
というディレクトリを作ります。
- 適当な名前の.rbファイルを作ります。ここでは
jekyll_change_permalinks.rb
にしてみました。
- 中身を書きます。
jekyll_change_permalinks.rbmodule Jekyll class PermalinkRewriter < Generator # Generatorクラスを継承してあげます safe true priority :low # プラグインがロードされる順位を決めてあげます def generate(site) # ジェネレータはこのメソッドを使います site.posts.each do |item| @author = item.data['author'] # Front Matter の変数はこれで取得できます unless @author.nil? # nilじゃないかな?チェック item.data['permalink'] = "/:categories/#{@author}/:title/" end end end end end
/cat/toraneko/test/
で、先程の記事が見られるようになりました!まとめ
「Front Matterのカスタム変数をパーマリンクに使いたい!」というときはこんな方法があるよ!という紹介でした(´∀`*)
それぞれに長所と短所があるので、目的にぴったりな方法を見つけると良さそうです?
比較
方法 ルールがある カスタム変数を使える _config.ymlを使う方法 ○ ✗ Front Matterで直指定 ✗ ✗ _pluginsを使う方法 ○ ○ 参考
Your first plugin | Jekyll • Simple, blog-aware, static sites
Generators | Jekyll • Simple, blog-aware, static sites
Permalinks | Jekyll • Simple, blog-aware, static sites
What are the steps to getting this 'custom' permalink scheme in Jekyll?
Search · class PermalinkRewriter < Generator
- 投稿日:2020-06-01T23:00:54+09:00
Railsチュートリアル1日目
- Railsチュートリアルを1.3.1まで進める
- 1.3.2で、
$ rails server
を実行するCould not find gem 'puma (= 4.3.4)' in any of the gem sources listed in your Gemfile.
と表示されるCould not find gem 'puma (= 4.3.4)' in any of the gem sources listed in your Gemfile.
でググる
- この記事にたどり着く(この記録の取り方もこの記事を参考にしている)
$ bundle update
を実行する- あらためて
$ rails server
を実行する- できた!
- 投稿日:2020-06-01T22:56:38+09:00
RubyとPython間のプロセス間通信を行う(POSIX メッセージキュー)
概要
rubyとpythonのdaemon間でデータの受け渡しをredis(Pub/Sub)1を使って考えていたら、プロセス間通信(POSIX IPC)使ったほうが手軽だよと天の声が聞こえたので試してみました。
なお、今回はraspberry pi上で動かすので、電源問題のためにもプロセス数は可能な限り減らす作戦で検討しています。
POSIX メッセージキューとは
POSIXのシステムコール/ライブラリ関数を通じて利用できる、プロセス間通信の一つ。非同期型通信プロトコルで、誤解を恐れずにいえばRabbitMQなどのメッセージングミドルウェアの超簡易版のようなもの。POSIX準拠なので簡素なAPIでお手軽に利用できます。
詳しくは以下をご参照ください。
https://linuxjm.osdn.jp/html/LDP_man-pages/man7/mq_overview.7.html
システム全体
- OS: Raspbian GNU/Linux 10 (buster)
- ruby: ruby 2.5.7p206 (2019-10-01 revision 67816) [armv7l-linux-eabihf]
- 利用ライブラリ: posix-mqueue
- https://github.com/sirupsen/posix-mqueue
- python: Python 2.7.16
- 利用ライブラリ: posix_ipc
- https://pypi.org/project/posix_ipc/
Ruby側の実装(送信側)
どっちが送信/受信でもよいのですが、今回はRubyを送信側としてみました。
利用ライブラリはposix-mqueueを利用させていただきました。
めっちゃシンプル。require 'posix/mqueue' require 'json' def main m = POSIX::Mqueue.new("/whatever", msgsize: 8192, maxmsg: 10) 10.times do |i| m.send({ messsage: "hello python world #{i}" }.to_json) sleep 1 end m.send ({ message: 'end'}.to_json) ensure m.unlink if m end if __FILE__ == $PROGRAM_NAME main endPython側の実装(受信側)
こちらも恐ろしくシンプルです。
利用ライブラリはposix_ipc# -*- coding: utf-8 -*- import time import posix_ipc import json def main () : mq = posix_ipc.MessageQueue("/whatever", posix_ipc.O_CREAT) while True: data = mq.receive() j = json.loads(data[0]) # -> (message, priority)のタプルで戻ってくる print j if j.get('message') == "end": break if __name__ == "__main__" : main()結果
想定通りPython側で出力されました!
$ python main.py {u'messsage': u'hello python world 0'} {u'messsage': u'hello python world 1'} {u'messsage': u'hello python world 2'} {u'messsage': u'hello python world 3'} {u'messsage': u'hello python world 4'} {u'messsage': u'hello python world 5'} {u'messsage': u'hello python world 6'} {u'messsage': u'hello python world 7'} {u'messsage': u'hello python world 8'} {u'messsage': u'hello python world 9'} {u'message': u'end'}簡単なソースですが一応githubにも上げておきました。
https://github.com/y-amadatsu/posix-mq-with-raspberry-pi-os余談
ちなみにPOSIX メッセージを使うとメッセージQueueが
/dev/mqueue
配下に表示されます。マウントしてls
やrm
で操作することも可能です。このあたりはすごくUNIX!って感じcat /dev/mqueue/whatever QSIZE:350 NOTIFY:0 SIGNO:0 NOTIFY_PID:0
- 投稿日:2020-06-01T22:28:46+09:00
メモ
偶数か奇数か
puts "数字入れて" n = gets.to_i if n.even? puts "偶数です" else n.odd? puts "奇数です" endto_i to_s
- to_iメソッドが文字→数値
- to_sメソッドが数値→文字
getsは文字列で保存する。
数値に変換するため→gets.to_ieach_with_index
fruits=[“りんご”, “メロン”, “イチゴ”] fruits.each_with_index do |fruit, i| puts “#{i+1}番目の要素は#{fruit}です。” end 【結果】 1番目の要素はりんごです。 2番目の要素はメロンです。 3番目の要素はイチゴです。
- 投稿日:2020-06-01T22:02:22+09:00
マイグレーション up, downメソッド
マイグレーション up, downメソッド
マイグレーションファイルで定義されるup, downメソッドについて考えていきます
テーブルの定義が書いてあるこんなマイグレーションファイルがあるとする
テーブルを定義
class CreateTasks < ActiveRecord::Migration[6.0] def change create_table :tasks do |t| t.string :name t.text :description t.timestamps end end endこのテーブルの :name属性に
30文字以内という文字制限をつけたい場合にどのようなマイグレーションファイルを作るか、というのが今日のテーマ普通に考えればこれでいい。
limit制約を付ける
class ChangeTasksNameLimit30 < ActiveRecord::Migration[6.0] def change change_column :tasks, :name, :string, limit: 30 end endテーブルには望み通りの制限を課すことができる。
ところが後日、やっぱりlimit: 30はなくそうという話になった。
そこでこんなコマンドを叩いてバージョンを一つ戻そうとすると...マイグレーションをロールバック
rails db:rollback # => not automatically reversible エラーが発生つまり定義されたマイグレーションファイルを自動的に戻すことができないよ
って怒られてしまう。何が起こったの??
Railsはchange_columnのバージョンを戻す処理をバージョンを上げる際のコードから自動生成できないため、こう言ったエラーが起こってします。
つまりRailsが「このマイグレーションファイルを消した後の元の姿がわからん!!」
って言ってエラーを吐き出します。どうするか?
upメソッドとdownメソッドを使う
ruby.rbclass ChangeTasksNameLimit30 < ActiveRecord::Migration[6.0] def up change_column :tasks, :name, :string, limit: 30 end def down change_column :tasks, :name, :string end endupメソッドは rails db:migrateのときの処理
downメソッドは rails db:rollbackのときの処理つまりバージョンを上げるときはupメソッドで処理をしてください。
下げるときはdownメソッドで処理をしてください。と伝えることで、rails db:rollbackをした時にアプリケーションがどんな処理をするべきかを
伝えることができるんですね。こうすることでnot automatically reversible エラーの発生も防ぐことができますし、コードをみた人がどんな処理をしたいかもわかりやすいということですね。めでたしめでたし。
本日はここまでです。
一人前のエンジニアになるまであと88日
- 投稿日:2020-06-01T20:46:56+09:00
Ruby インスタンス変数・ローカル変数 違い
インスタンスメソッド
クラスの中でメソッドを定義すると、そのメソッドはインスタンスメソッドとなります。
インスタンスメソッドは、そのクラスのインスタンスに対して呼び出すことができるメソッドです。
(例)class User def emotion "Happy!" end end user = User.new user.emotion結果
"Happy!"
インスタンス変数
クラス内では、インスタンス変数(同じインスタンス内で共有される変数)を使うことができます。
変数名は@から始まります。
(例)class User def initialize(emotion) @emotion = emotion end def happy "I am #{@emotion}." end end user = User.new('Happy') user.happy結果
"I am Happy."
ローカル変数
メソッドやブロック内で作成される変数のことをいいます。
アルファベットの小文字、アンダースコアで始めます。
ローカル変数は参照する前に必ず=で値を代入して作成する必要があります。(例)
class User def initialize(emotion) @emotion = emotion end def happy shuffled_emotion = @emotion.chars.shuffle.join "I am #{shuffled_emotion}." end end user = User.new('Happy') user.happyこの例の場合、ローカル変数はshuffled_emotionになります。
結果
"I am ayppH."
参考にした文献
- 投稿日:2020-06-01T20:33:16+09:00
CGIプログラムをローカルで動かすときの設定等(自分用)
はじめに
CGIプログラム完全初心者の自分用メモです.
こういう記事を書くのは初めてなので,お見苦しい点あるかと思いますがお許しください.
アドバイスやご指摘がありましたら,コメントくださると幸いです.モチベーション
Rubyで記述されたCGIプログラムを,ローカルサーバ環境にて実行テストしたい.
サーバの決定
サーバには,ApacheのHTTPサーバを使う.
AN-HTTPDを使う方法(参考:http://www.aikis.or.jp/~s-suzuki/cgilabo/localserver/ )
もあるみたいだけど,入手先のURLが死んでる.
一応アーカイブからダウンロードできるよってことらしい
(参考:https://www.nishishi.com/blog/2019/02/an_httpd_waybac.html )けど,
試したけどなんかうまくいかなかった.環境
- Windows 10
- Perl 5.28.1
- Ruby 2.7.1-1
- Apache 2.4.43
ActivePerlのインストール
今回のモチベには関係ないけどCGIプログラムにはPerlが定番っぽい(そりゃそうか)ので入れた.
ActivePerlのサイト(https://www.activestate.com/ )から最新版(5.28.1)をダウンロード
Sign In (ログイン必要/GitHubアカウントでログイン可)
-> Featured Projects & Languages
-> ActivePerl 5.28 の枠中の Windows
-> Windows10 の .msi をクリックしてActivePerl-5.28.1.0000-MSWin32-x64-b462fde1.msi
をダウンロードしてインストール☆インストール中
Setup TypeはTypical
Setup Optionsでは,
「Add Perl to the PATH environment variable」(パス通す)と
「Create Perl file extension association」(plファイル関連付け)にはチェックを入れる.
(参考:https://www.hiskip.com/pg-notes/how-to-install/develop-kit/active-perl526.html )インストール後,C:の下に
Perl64
ってディレクトリが置かれる(C:\Perl64
).Rubyのインストール
RubyInstaller(https://rubyinstaller.org/downloads/ )から
最新版(2.7.1-1)をダウンロード
rubyinstaller-2.7.1-1-x64.exe
をダウンロードしてインストール☆インストール中
Perlの時と同じ理由で,
「Add Ruby exexutables to your PATH」と
「Associate .rb and .rbw files with this Ruby installation」にはチェックを入れる.インストール後,C:の下に
Ruby27-x64
ってディレクトリが置かれる(C:\Ruby27-x64
).ApacheのHTTPサーバのインストール
ApacheのHTTPサーバのダウンロードサイト(http://httpd.apache.org/download.cgi )から
最新版(2.4.43)をダウンロードApache HTTP Server Server 2.4.43 (httpd): 2.4.43 is the latest available version
-> Files for Microsoft Windows
-> Apache Lounge (ダウンロード先の指定,何でもいいと思う)
->httpd-2.4.43-win64-VS16.zip
をダウンロード,展開展開したら,
httpd-2.4.43-win64-VS16
のディレクトリの下にApache24
っていうディレクトリが
できるのでC:の下に置く(C:/Apache24
).Apacheの httpd.conf の設定
Apache24/conf/http.conf
は,Apache(httpd)の設定ファイルである.
これを編集する.
以下,編集点
#CGIの実行場所の設定
ScriptAlias /cgi-bin/ "C:/Apache24/cgi-bin/"
#CGIの実行を許可
<Directory "C:/Apache24/cgi-bin">
AllowOverride All
Options Indexes FollowSymLinks ExecCGI
Require all granted
</Directory>
#拡張子.cgi、.pl .rbが使えるようする
AddHandler cgi-script .cgi .pl .rb
#拡張子.cgi、.pl .rbが使えるようするver2
AddType application/x-httpd-cgi .cgi
AddType application/x-httpd-cgi .pl
AddType application/x-httpd-cgi .rb(参考1:https://phpjavascriptroom.com/?t=php&p=cgi#google_vignette )
(参考2:https://qiita.com/hirotoyoshidome/items/6d103e04dd07e90519d2 )
上2つともスペシャルサンクスAddHandlerとAddTypeの違いとかについてはこちら(https://senooken.jp/post/2019/06/01/ )
Apacheの起動
Apache24/bin/httpd.exe
を実行すると,ApacheのHTTPサーバを起動できる.私は
httpd.exe
を実行できなくて,(http://t12488mac.blogspot.com/2011/06/windowsapache.html )を参照した.
Apache24\bin
のディレクトリでhttpd.exe -t
を叩いたら
「httpd.conf
にダメな記述があって実行できないよ~」って教えてくれた.
直したら直った(あたりまえ)CGIプログラムの置き場・記述
CGIプログラムは
Apache24/cgi-bin
の下に置く.
rubyのCGIプログラムには1行目に以下の記述
#!/Ruby27-x64/bin/ruby
または
#!C:/Ruby27-x64/bin/ruby
perlのCGIプログラムには1行目に以下の記述
#!/perl64/bin/perl
または
#!C:/Ruby27-x64/bin/ruby
ブラウザからアクセス
以下の3つを満たす状態にたどり着けたら,いよいよブラウザで表示できる.
- Apacheの
httpd.conf
を適切に編集済の状態- ApacheのHTTPサーバを起動(
httpd.exe
を実行)した状態Apache24/cgi-bin
下に何らかのCGIプログラム(**.cgi
)を置いた状態ブラウザのURLバーに
http://127.0.0.1/cgi-bin/**.cgi
または
http://localhost/cgi-bin/**.cgi
と入力すると,自分が書いたページが,ブラウザ上に表示される.これを以て,ローカルサーバ環境での動作完了とする.
ディレクトリ配置
本記事で紹介したものに関連するディレクトリ配置を以下に示す.
C:/
├ Perl64
│ ├ bin
│ │ ├ Perl.exe
│ : :
│
├ Ruby27-x64
│ ├ bin
│ │ ├Ruby.exe
│ : :
│
├ Apache24
│ ├ bin
│ │ ├ httpd.exe
│ │ :
│ │
│ ├ conf
│ │ ├ httpd.conf
│ │ :
│ │
│ ├cgi-bin
│ : └**.cgi (自作CGIファイル)
│ :
::おわりに
手探りながらも,Rubyで記述されたCGIプログラムを,ローカルサーバ環境でテストできた.
疲れた.
- 投稿日:2020-06-01T18:52:50+09:00
【Rails5.2+Rspec/TDD】Rspecを導入して簡単にテストする手順(テスト駆動開発)
環境
ruby 2.6.4
Rails 5.2.4.1
rbenv 1.1.2
mysql2 0.5.3
やりたいこと
TDD(テスト駆動開発)をするためにRspecを使っていきます
Rspecを使う手順を簡単に説明している記事がそこまで多くないので自分用にメモしておきます
今回は簡単な導入方法を残しておきます
元記事:【Rails5.2/TDD】Rspecを超簡単に導入する簡単手順(テスト駆動開発)
カンタン導入手順
ステップ1. 新規アプリ作成
$ rails new sample_project --api -Tステップ2. TDD用にGem追加
Gemfilegroup :development, :test do gem 'rspec-rails', '~> 3.5' end group :test do gem 'factory_bot_rails', '~> 4.0' gem 'shoulda-matchers', '~> 3.1' gem 'faker' gem 'database_cleaner' endステップ3. gemインストール
$ bundle installステップ4. Rspecファイル生成
$ rails generate rspec:installステップ5. factoriesディレクトリ作成
$ mkdir spec/factoriesステップ6. rails_helper.rb修正
spec/rails_helper.rbrequire 'database_cleaner' # 追加 Shoulda::Matchers.configure do |config| config.integrate do |with| with.test_framework :rspec with.library :rails end end # 追加 RSpec.configure do |config| # 追加 config.include FactoryBot::Syntax::Methods # 追加 config.before(:suite) do DatabaseCleaner.clean_with(:truncation) DatabaseCleaner.strategy = :transaction end # 追加 config.around(:each) do |example| DatabaseCleaner.cleaning do example.run end end今回は導入のみですが実際のテストで下の記事がとても参考になります
参考文献
- 投稿日:2020-06-01T18:50:55+09:00
【Rails5】[ Turbolinks ] ページ遷移やブラウザバックでJSが動かないときの対処法
実装した機能
開発環境
ruby > 2.6.5 rails > 5.2.4.2実装したJS
$(document).ready(function(){ $("#menu").on("click", function() { $(this).next().slideToggle(); }); });jqueryを使って、なんの変哲もない開閉式ハンバーガーメニューをつけました。
JSのコード自体の良し悪しはおいておいて、動作としては問題ないはずです。状況
初期ロード時には問題なく動作する。
ページ遷移、ブラウザバックのときに挙動がおかしい。
クリックで発火はしているが、開閉をループしたり、不安定。
リロードすると通常動作する。考察と対策
動いてはいるので、おそらく読み込みのタイミングが間違っている?
→ready
onload
ajaxStop
など一通り試してみてもダメ。グーグル先生に相談したら、こんな記述を発見
$(document).on('turbolinks:load', function () { ... });
turbolinks:load
なにこれ見たことない…
どうやらRails独自の記述らしいです。Turbolinksの扱い
こちらの記事を参考にさせていただきました。
turbolinksチートシートTurbolinksとは?
- Ajaxによるページ遷移の高速化のためのライブラリ(Gem)
- ユーザ側から見て、通常のページ遷移と同じように表示される/動作する
- Rails4からデフォルトでインストールされている
つまり、この機能が今回のJSに影響してしまっているようです。
turbolinksをどう扱うか
Gemなので、消してしまえば解消はできますが解決にはならないので
どう扱うべきかリサーチしてみました。主にこのような扱いがあります。
- <a>タグごとにturbolinksを無効にする
- turbolinks自体を無効化(削除)する
- JSの読み込み時にturbolinksを適応しない、タイミングを変える
1.<a>タグごとにturbolinksを無効にする
リンク自体に
{"turbolinks" => false}
を指定すると
そのリンクはturbolinksが無効になります。<%= link_to "HOGE", root_path, data: {"turbolinks" => false} %> <%# => <a data-turbolinks="false" href="/">HOGE</a> %>これを記述すれば、間違いなくturbolinksを外すことができます。
特定のスクリプトのみ制御する場合は良さそうですが、さすがに全部に記述するのは厳しそうですね…2.turbolinks自体を無効化(削除)する
Gemを削除
この一行を削除
Gemfile#gem 'turbolinks', '~> 5'
ターミナル$ bundle updateapplication.jsを編集
app/assets/javascripts/application.js//= require turbolinks #この行を削除
application.html.erbを編集
'data-turbolinks-track': 'reload'
を削除します。app/views/layouts/application.html.erb<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>これで無効化されました。
JSなどを多用するサイトでなければ無効化してしまうのが確実かもしれません。3.JSの読み込みにturbolinksを適応しない
ready
onload
などと同じように、
この記述でturbolinksを適応せずにロードできます。$(document).on('turbolinks:load', function () { ... });他にも、turbolinksを適応するタイミングも変更ができます。
詳しく知りたい方はこちらを参照ください。
その他のライフサイクルイベントをとるスクリプトごとに微調整が効くので、今回はこれが最適解だと思います。
まとめ
果たして、Turbolinksは優れた機能なのか、おせっかい機能なのか…
今の所どちらとも言えません笑
デフォルトでインストールされているということは、きっとあったほうが良いのだろうと思いますが…
もっと効果的な使用法をご存じの方はぜひコメントを下さい!
- 投稿日:2020-06-01T18:49:34+09:00
Ruby+Seleniumでブラウザの自動化ツール作成
大学の履修登録の抽選科目で誰かが外した時に、先着でその授業を取るためにひたすらポチポチする作業を自動化するツールを作りました。
大学ごとに履修登録の仕方は異なると思うので、大枠だけ記しておきます。
使用したのはRuby、SeleniumWebDriver、ブラウザはGoogleChromeです。Seleniumとは
Webブラウザ上でWebアプリケーションを自動で操作(テスト)するツール。
Selenium WebDriverとは
旧来のSelenium RCの問題点を解決し、ブラウザを操作する仕組み。
Selenium RCをSelenium1、Selenium WebDriverをSelenium2とも呼ぶ。環境
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G103 $ ruby -v ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin18]Selenium WebDriverのインストール
$ gem install selenium-webdriverターミナルでSelenium WebDriverのgemを追加。
$ gem listでselenium-webdriverと表示されればOK!
ChromeDriverのインストール
今回はブラウザでGoogleChromeを使用するので、下記リンクからChromeDriverをインストールします。
サンプルコード
エディタでコードを書いていきます。大学によってページ遷移など異なると思うので、簡潔に。
今回はRubyを使用するため、selenium_test.rbなどと名付けておきます。selenium_test.rbrequire 'selenium-webdriver' driver = Selenium::WebDriver.for :chrome driver.manage.timeouts.implicit_wait = 60 # 指定したドライバの要素が見つかるまでの待ち時間を指定 driver.navigate.to "URL" # URLを指定してアクセス driver.find_element(:xpath, '該当のxpathを記載').click # xpathでボタンを指定して押す driver.find_element(:name '指定したいname属性').send_keys('入力したいワード') # name属性の入力フォームを指定して文字を入力する。 # 後はif文やloop文などを使用して、各々の大学の履修登録に合わせて作成してください driver.quit # ドライバを閉じる以下コマンドで実行。
$ ruby selenium_test.rbおかしな点があればご指摘お願いします!!
参考
- 投稿日:2020-06-01T14:45:52+09:00
posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.all.order(created_at: :desc)
enddef show
@post = Post.find_by(id: params[:id])
enddef new
@post = Post.new
enddef create
@post = Post.new(content: params[:content])
if @post.save
# 変数flash[:notice]に、指定されたメッセージを代入してください
flash[:notice]="投稿を作成しました"
redirect_to("/posts/index")
else
render("posts/new")
end
enddef edit
@post = Post.find_by(id: params[:id])
enddef 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
enddef destroy
@post = Post.find_by(id: params[:id])
@post.destroy
# 変数flash[:notice]に、指定されたメッセージを代入してください
flash[:notice] = "投稿を削除しました"
redirect_to("/posts/index")
endend
- 投稿日:2020-06-01T13:37:27+09:00
ポートフォリオ用にRubyで作ったブラックジャックを自分でアップデートしてみた
初めに作ったブラックジャック↓↓
Rubyでブラックジャック作ってみた(minitest使ってみた)アップデートの内容
- 100万円稼いだら勝ち、所持金0円になったら負けというルールを追加。
- play.rb(ゲーム起動クラス)
- ディーラーターンを同じ文で2箇所書いていたため、ディーラーターンメソッドを作り、よりコードを読みやすくした。
- カードを引いたときのドキドキ感を増す為に、waitメソッドを作り、ディーラーがドローする前にEnterを押さないと進めないようにした。
- 指定した金額をベットできるようにした。
card.rb(カードクラス)
- マークがわかりやすくなるように「スペード」「ハート」の、様にカタカナ表記を追加。
deck.rb(デッキクラス)
- プレイクラスを作った為、プレイヤーが毎回リセットされなくなった為、デッキをリセットできるように、リセットメソッドを作成した。
player.rb(プレイヤークラス)
- デッキクラスと同様プレイヤーが毎回初期化されなくなった為、テスト用に使っていたリセットメソッドをゲーム中にも使うようにした。
- 「A」はバーストした際に「1」になるようにスコア計算メソッドに機能を追加。
- ベット金額の計算機能を追加。
judge.rb(ジャッジクラス)
- 勝敗が決まったときのベットの計算機能を追加。
- 所持金でゲームクリアかゲームオーバーかを判定する機能を追加。
test.rb(動作確認テスト)
- ベットの計算機能のテストを追加。
main.rb(ブラックジャックを動かすメインファイルだったが、プレイクラスを作った為、削除)
ルール
所持金が100万以上で勝利。0円になったら敗北
「A」は「1」は都合良い数字に変換。というよりトータルスコアを変換。
ヒットかスタンドのみ
トータルスコアが高い方が勝ち
ブラックジャック勝ちでベット金額3倍もらえる。通常2倍。
play.rb(プレイクラス)
play.rbrequire './card' require './deck' require './judge' require './money' require './player' class Play def initialize @player = Player.new @dealer = Player.new(name: 'ディーラー') play end def message(title, choice1="ヒット", choice2="スタンド") # win or lose or draw or error case title when :win puts 'あなたの勝ちです' when :lose puts 'あなたの負けです' when :draw puts '引き分けです' when :error puts '無効です。もう一度入力してください' when :choice puts "選択してください\s\sPush「 1 」→ #{choice1} 、Push「 2 」→ #{choice2}" end end def dealer_turn # スタンド時の処理(ディーラーのターン) puts 'ディーラーのターンです' wait # プレイヤーがブラックジャックだった場合のディーラーの処理 if @player.blackjack? @dealer.draw @dealer.show if !@dealer.blackjack? message :win @player.money.stock = @player.bet_judge(@@bet, :blackjack) @player.money.show return else message :draw @player.money.stock = @player.bet_judge(@@bet, :draw) @player.money.show return end end # プレイヤーに勝つまでディーラーはヒットし続ける処理 while true @dealer.draw @dealer.show wait if @dealer.burst? puts 'バースト!' message :win @player.money.stock = @player.bet_judge(@@bet, :win) @player.money.show break elsif @dealer.total_score == 21 && @player.total_score == 21 message :draw @player.money.stock = @player.bet_judge(@@bet, :draw) @player.money.show break elsif @player.total_score < @dealer.total_score message :lose @player.money.stock = @player.bet_judge(@@bet, :lose) @player.money.show break end end end def bet_turn @player.money.show puts "いくらベットしますか?" while true bet = gets.to_i if bet < 1 message :error redo elsif bet > @player.money.stock.to_i puts "賭け金が手持ち金額を上回っています。もう一度入力してください。" redo else puts <<~TEXT #{bet} 円ベットしました。 #{'-' * 41} TEXT return bet end end end def play while true # 再プレイのためのループ処理 # 手札リセット @player.reset @dealer.reset # 初ターンの処理 puts "ようこそ、ブラックジャックへ" puts "ルール:手持ち資金が100万円を超えたら勝ちです。0円になったら負けです。" puts "カードを配ります" @player.draw @player.draw @dealer.draw @dealer.show @@bet = bet_turn @player.bet_calc(@@bet) @player.money.show # プレイヤーがブラックジャックじゃないか確認の処理 if @player.blackjack? puts "ブラックジャック!" @player.show else @player.show message :choice end # ヒット、スタンドの処理(プレイヤーのターン) while true # ブラックジャックの場合は強制的にスタンドさせる処理 if @player.blackjack? command = 2 else command = gets.to_i # ヒットかスタンドか選択 end # ヒットの場合の処理 if command == 1 while true @player.draw if @player.burst? # バーストしていないか確認 @player.show puts "バースト!!" message :lose @player.money.stock = @player.bet_judge(@@bet, :lose) @player.money.show break end # ドロー後のトータルスコアの確認 @player.show # 最大値(21)の場合は強制的にスタンドの処理 if @player.total_score == 21 puts 'トータルスコアが最大値に達しました' command = 2 break end # 最大値ではない場合は、再度ヒットするか処理 puts "もう一度引きますか?" message :choice while true command = gets.to_i # 再度、ヒットかスタンドの選択 if command == 1 break elsif command == 2 break else message :error redo end end # ヒットかスタンドの処理 if command == 1 redo elsif command == 2 break end end # バースト時の強制終了処理 if @player.burst? break end elsif command == 2 dealer_turn command = nil break else # h,s以外が入力されたときの処理 message :error redo end if command == 2 dealer_turn break end end # ゲームオーバーか判定 if @player.gameover? puts "所持金が0円になりました。" puts "ゲームオーバーです。" break elsif @player.win? puts "所持金が100万円を超えました。" puts "あなたの勝ちです!!!!!!おめでとう!!!!!!!!!!!!" break end # 再プレイの処理 puts "もう一回遊びますか?" message :choice, "遊ぶ", "遊ばない" while true command = gets.to_i # 再プレイするか選択 if command == 1 command = nil break elsif command == 2 break else message :error redo end end # 再プレイの処理 command == nil ? redo : break end end def gameover end def wait(words='Enterキーを押してください') puts words c = gets end end Play.newcard.rb(カードクラス)
card.rbclass Card attr_reader :mark, :number def initialize(mark, number) @mark = mark.freeze @number = number.freeze end def convert if @number == 'J' || @number == 'Q' || @number == 'K' || @number == 'A' 10 else @number.to_i end end enddeck.rb(デッキクラス)
deck.rbclass Deck attr_accessor :cards @@draw_count = 0 def initialize @cards = [] create @cards = @cards.shuffle end def create nums = ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'] mark = ["❤ ハート\s\s\s\s", "♦︎ ダイヤ\s\s\s\s", "♤ スペード\s\s", "♧ クローバー"] mark.each do |m| nums.each do |n| @cards << Card.new(m, n) end end end def draw cards[@@draw_count] end def draw_count @@draw_count += 1 end # テスト用のカード参照メソッド def show 52.times do |n| puts "[ #{cards[n].mark} #{cards[n].number} ]" end end def reset @cards = @cards.shuffle @@draw_count = 0 end endplayer.rb(プレイヤークラス)
player.rbclass Player include Judge attr_accessor :hands, :total_score, :name, :money @@deck = Deck.new def initialize(name: "プレイヤー") @hands = [] @total_score = 0 @name = name @money = Money.new end def draw self.hands << @@deck.draw self.total_score = self.total_score_calc @@deck.draw_count end def show hands = [] self.hands.each do |hand| hands << "#{hand.mark} #{hand.number}" end puts "#{self.name}の手札です" puts hands puts <<~TEXT #{puts "[ トータル #{@total_score} ]"} ----------------------------------------- TEXT end def a_count a = 0 self.hands.each do |card| if card.number == 'A' a += 1 end end a end def reset self.hands = [] self.total_score = 0 @@deck.reset end def total_score_calc @total_score = 0 # 複数回計算した場合に、加算しないように、毎回トータルスコアをリセットする self.hands.each do |hand| # @numberに「A」があり、トータルスコアが22以上ある場合、「A」の数だけ、トータルスコアから-9する。「A」の数をカウントするメソッドを作る。 @total_score += hand.convert end if self.burst? && self.in_a? @total_score -= (self.a_count * 9) # 「A」の数だけ、トータルスコアから-9する。「A」の数をカウントするメソッドを作る。 end @total_score end def bet_calc(bet) self.money.stock = self.money.stock.to_i - bet end # テスト用メソッド def test puts "テストーーーーーー" end def reset_score self.total_score = 0 end def in_blackjack self.hands << Card.new("♦︎", "J") self.hands << Card.new("♦︎", "A") end end # player = Player.new # enemy = Player.new # player.draw # player.draw # enemy.draw # enemy.draw # player.hands # player.total_score_calc # enemy.total_score_calc # p player.blackjack? # p enemy.blackjack?judge.rb(ジャッジモジュール)
judge.rbmodule Judge def bet_judge(bet, judge) stock = self.money.stock case judge when :win stock += bet * 2 when :lose stock += 0 when :blackjack stock += bet * 3 when :draw stock += bet end end def blackjack? if self.hands[1].number == "A" || self.hands[0].number == "A" if self.hands[0].number == "J" || self.hands[0].number == "Q"|| self.hands[0].number == "K" || \ self.hands[1].number == "J" || self.hands[1].number == "Q"|| self.hands[1].number == "K" self.total_score = "ブラックジャック" true else false end else false end end def burst? self.total_score > 21 ? true : false end def in_a? self.hands.map do |card| if card.number == 'A' return true end end false end def gameover? self.money.stock <= 0 ? true : false end def win? self.money.stock > 1000000 ? true : false end endtest.rb(minitest)
test.rbrequire 'minitest/autorun' require './card' require './deck' require './judge' require './money' require './player' class BlackJackTest < Minitest::Test # テストカードのマークは「 ♦︎ 」で統一 @@test_J = Card.new('♦︎', 'J') @@test_Q = Card.new('♦︎', 'Q') @@test_K = Card.new('♦︎', 'K') @@test_A = Card.new('♦︎', 'A') @@test_5 = Card.new('♦︎', 5) @@test_10 = Card.new('♦︎', 10) @@test_player = Player.new def test_blackjack? blackjack_player = Player.new blackjack_player.hands << @@test_A blackjack_player.hands << @@test_J assert blackjack_player.blackjack? blackjack_player.reset blackjack_player.hands << @@test_J blackjack_player.hands << @@test_A assert blackjack_player.blackjack? blackjack_player.reset blackjack_player.hands << @@test_Q blackjack_player.hands << @@test_A assert blackjack_player.blackjack? blackjack_player.reset blackjack_player.hands << @@test_A blackjack_player.hands << @@test_Q assert blackjack_player.blackjack? blackjack_player.reset blackjack_player.hands << @@test_A blackjack_player.hands << @@test_K assert blackjack_player.blackjack? blackjack_player.reset blackjack_player.hands << @@test_K blackjack_player.hands << @@test_A assert blackjack_player.blackjack? # false パターン # blackjack_player.reset # blackjack_player.hands << @@test_A # blackjack_player.hands << @@test_10 # assert blackjack_player.blackjack? # blackjack_player.reset # blackjack_player.hands << @@test_5 # blackjack_player.hands << @@test_5 # assert blackjack_player.blackjack? # blackjack_player.reset # blackjack_player.hands << @@test_J # blackjack_player.hands << @@test_Q # assert blackjack_player.blackjack? end def test_burst? burst_player = Player.new burst_player.hands << @@test_J burst_player.hands << @@test_J burst_player.hands << @@test_5 burst_player.total_score_calc burst_player.total_score assert burst_player.burst? # false # burst_player.reset # burst_player.hands << @@test_10 # burst_player.total_score # assert burst_player.burst? end def test_total_score_calc total_score_calc_player = Player.new total_score_calc_player.hands << @@test_10 total_score_calc_player.hands << @@test_5 total_score_calc_player.total_score_calc assert_equal 15, total_score_calc_player.total_score total_score_calc_player.reset total_score_calc_player.hands << @@test_A total_score_calc_player.hands << @@test_10 total_score_calc_player.hands << @@test_5 total_score_calc_player.total_score_calc assert_equal 16, total_score_calc_player.total_score total_score_calc_player.reset total_score_calc_player.hands << @@test_A total_score_calc_player.hands << @@test_J assert_equal 20, total_score_calc_player.total_score_calc total_score_calc_player.hands << @@test_5 assert_equal 16, total_score_calc_player.total_score_calc total_score_calc_player.hands << @@test_A assert_equal 17, total_score_calc_player.total_score_calc # false #assert_equal 10, total_score_calc_player.total_score end def test_convert assert_equal 10, @@test_J.convert assert_equal 10, @@test_Q.convert assert_equal 10, @@test_K.convert assert_equal 10, @@test_A.convert assert_equal 5, @@test_5.convert end def test_draw_count deck = Deck.new assert_equal 1, @@test_player.draw assert_equal 2, @@test_player.draw assert_equal 3, @@test_player.draw end def test_a_count a_player = Player.new a_player.hands << @@test_A a_player.hands << @@test_A a_player.hands << @@test_A assert_equal 3, a_player.a_count end def test_bet_calc #assert_equal 19000, @@test_player.bet_calc(1000) end def test_bet_judge p @@test_player.money.stock -= 250 # ベット assert_equal 20250, @@test_player.bet_judge(250, :win) assert_equal 19750, @@test_player.bet_judge(250, :lose) assert_equal 20500, @@test_player.bet_judge(250, :blackjack) assert_equal 20000, @@test_player.bet_judge(250, :draw) end end
- 投稿日:2020-06-01T12:39:46+09:00
さくらのVPSにRubyをインストール
環境等は以下の記事をご参照ください。
さくらのVPSにSSH接続+最低限のセキュリティ対策Gitをインストール
まずは
git clone
を使うためにgitをインストールします。$ sudo yum -y install git $ git --versionrbenvをインストール
Rubyのバージョンを管理するツール、rbenvをインストールします。
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build #'rbenv install'コマンドの実行に必要なプラグイン依存パッケージをインストール
rubyをインストールするための関連するパッケージです。これがないと、rbenv installの際にコケるらしいです。
$ yum -y install bzip2 gcc openssl-devel readline-devel zlib-develrbenvの読み込み設定
rbenvコマンドを使える用にするために、シェル起動時読み込むbash_profileを編集します。
$ vi ~/.bash_profile
~/.bash_profile# 省略 PATH=$PATH:$HOME/bin export PATH export PATH="$HOME/.rbenv/bin:$PATH" # 追記 eval "$(rbenv init -)" # 追記$ source ~/.bash_profile $ rbenv -vRubyをインストール
rbenvの設定が終わったので、いよいよRubyのインストールです。
$ rbenv install 2.4.1 $ rbenv global 2.4.1 $ rbenv rehash $ ruby -vbundlerをインストール
gemの管理をいい感じにするツール
bundler
をインストールしましょう。$ gem install bundlenode.js、yarnをインストール
Rails6を動かすためにはyarnが必須です。
$ yum install -y nodejs npm --enablerepo=epel $ npm install -g yarn参考
- 投稿日:2020-06-01T12:27:01+09:00
Railsサーバが起動しない1。エラー「Ignoring nokogiri-1.10.x because its extensions are not built.」
発生した背景
久々に自分で作成したアプリケーションを起動しようとすると、以下のエラーが出力されて、アプリが起動されません。対応した手順を記録します。
(バージョンが合わないなどの問題もありましたが、こちらは別記事にしようと思います。)
非常に長いエラーのため、慎重に対応していく再現手順/実行した内容
アプリのrootpathで、rails -s」コマンド実行した際に、出力されたエラーです。
エラー1:
[staff@term]rails s Ignoring nokogiri-1.10.1 because its extensions are not built. Try: gem pristine nokogiri --version 1.10.1 Ignoring bcrypt-3.1.12 because its extensions are not built. Try: gem pristine bcrypt --version 3.1.12 Ignoring bindex-0.7.0 because its extensions are not built. Try: gem pristine bindex --version 0.7.0 Ignoring bindex-0.5.0 because its extensions are not built. Try: gem pristine bindex --version 0.5.0 Ignoring binding_of_caller-0.8.0 because its extensions are not built. Try: gem pristine binding_of_caller --version 0.8.0 Ignoring bootsnap-1.4.3 because its extensions are not built. Try: gem pristine bootsnap --version 1.4.3 Ignoring bootsnap-1.4.1 because its extensions are not built. Try: gem pristine bootsnap --version 1.4.1 Ignoring bootsnap-1.4.0 because its extensions are not built. Try: gem pristine bootsnap --version 1.4.0 Ignoring bootsnap-1.3.2 because its extensions are not built. Try: gem pristine bootsnap --version 1.3.2 ------- 一部割愛エラー2:
Traceback (most recent call last): 61: from bin/rails:3:in `<main>' 60: from bin/rails:3:in `load' 59: from /Users/ichikawadaisuke/projects/krown/bin/spring:15:in `<top (required)>' 58: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:70:in `require' 57: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:70:in `require' 56: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/spring-2.0.2/lib/spring/binstub.rb:31:in `<top (required)>' 55: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/spring-2.0.2/lib/spring/binstub.rb:31:in `load' 54: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/spring-2.0.2/bin/spring:49:in `<top (required)>' 53: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/spring-2.0.2/lib/spring/client.rb:30:in `run' 52: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/spring-2.0.2/lib/spring/client/command.rb:7:in `call' 51: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/spring-2.0.2/lib/spring/client/rails.rb:28:in `call' 50: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/spring-2.0.2/lib/spring/client/rails.rb:28:in `load' 49: from /Users/ichikawadaisuke/projects/krown/bin/rails:9:in `<top (required)>' 48: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `require' 47: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:257:in `load_dependency' 46: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `block in require' 45: 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:30:in `require' 44: 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:21:in `require_with_bootsnap_lfi' 43: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/bootsnap-1.4.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register' 42: 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 `block in require_with_bootsnap_lfi' 41: 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' 40: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/railties-5.2.3/lib/rails/commands.rb:18:in `<main>' 39: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/railties-5.2.3/lib/rails/command.rb:46:in `invoke' 38: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/railties-5.2.3/lib/rails/command/base.rb:65:in `perform' 37: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch' 36: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command' 35: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run' 34: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/railties-5.2.3/lib/rails/commands/server/server_command.rb:142:in `perform' 33: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/railties-5.2.3/lib/rails/commands/server/server_command.rb:142:in `tap' 32: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/railties-5.2.3/lib/rails/commands/server/server_command.rb:145:in `block in perform' 31: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `require' 30: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:257:in `load_dependency' 29: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `block in require' 28: 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:30:in `require' 27: 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:21:in `require_with_bootsnap_lfi' 26: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/bootsnap-1.4.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register' 25: 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 `block in require_with_bootsnap_lfi' 24: 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' 23: from /Users/ichikawadaisuke/projects/krown/config/application.rb:7:in `<main>' 22: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-2.0.1/lib/bundler.rb:114:in `require' 21: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-2.0.1/lib/bundler/runtime.rb:65:in `require' 20: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-2.0.1/lib/bundler/runtime.rb:65:in `each' 19: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-2.0.1/lib/bundler/runtime.rb:76:in `block in require' 18: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-2.0.1/lib/bundler/runtime.rb:76:in `each' 17: from /Users/ichikawadaisuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-2.0.1/lib/bundler/runtime.rb:81:in `block (2 levels) in require' 16: 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:30:in `require' 15: 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:21:in `require_with_bootsnap_lfi' 14: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/bootsnap-1.4.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register' 13: 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 `block in require_with_bootsnap_lfi' 12: 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' 11: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/uglifier-4.1.20/lib/uglifier.rb:5:in `<main>' 10: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `require' 9: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:257:in `load_dependency' 8: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `block in require' 7: 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:30:in `require' 6: 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:21:in `require_with_bootsnap_lfi' 5: from /Users/ichikawadaisuke/projects/krown/vendor/ruby/2.5.0/gems/bootsnap-1.4.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register' 4: 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 `block in require_with_bootsnap_lfi' 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)対応方法
エラーの出力量が非常に多いため、環境を再構築する方が早いかもしれないと考えたが、時間を決めて対応してみることに。
- ①記述に従い、「gem pristine nokogiri --version 1.10.1」を実行してみる。
- bundle execを付加して実行してみる。
- bundle install --path vendorの実行
- 結果 -> 同じエラーが出力される。
- ②背景から一度「bundle update」を実行してみる。
- 結果 -> エラー2だけが出力されるようになった
エラー2の対応方法については別記事にて掲載する。
- 投稿日:2020-06-01T12:08:51+09:00
ハッシュについて詳しく!
ハッシュで使用頻度の高いメソッド
keysメソッド
ハッシュのキーを配列として返すメソッド
keyメソッドcunrrencies = { japan: 'yen', us: 'doller', india: 'rupee' } currencies.key #=>[:japan, :us, :india]valuesメソッド
ハッシュの値を配列として返すメソッド
valuesメソッドcurrencies = { japan: 'yen', us: 'doller', india: 'rupee' } currencies.values #=> ["yen", "doller, "rupee"]has_key?メソッド
ハッシュの中に指定されたキーが存在するかどうか確認するメソッド
※エイリアスメソッドとして、key?, include?, member? があります。has_key?currencies = { japan: 'yen', us: 'doller', india: 'rupee' } currencies.has_key?(:japan) #=> true currencies.has_key?(:italy) #=> false**でハッシュを展開
**をハッシュの前に付けるとハッシュリテラル内でほかのハッシュのキーと値を展開できる
**h = { us: 'doller', india: 'rupee' } { japan: 'yen', **h } #=> {:japan=>"yen", :us=>"doller", :india=>"rupee"} #mergeメソッドを使っても同じ効果が得られる { japan: 'yen' }.merge(h) #=> {:japan=>"yen", :us=>"doller", :india=>"rupee"}配列とハッシュ
to_aメソッド
ハッシュをto_aメソッドを使って配列に変換するメソッド
to_acurrencies = { japan: 'yen', us: 'doller', india: 'rupee' } currencies.to_a #=> [[:japan, "yen"], [:us, "doller"], [:india, "rupee"]]to_hメソッド
配列をハッシュに変換することができるメソッド
to_harray = [[:japan, "yen"], [:us, "doller"], [:india, "rupee"]] array.to_h #=> {:japan=>"yen", :us=>"dollar", :india=>"rupee"} #ハッシュとして解析不能な配列に対してto_hメソッドを呼ぶとエラーになる array = [1, 2, 3, 4] array.to_h #=> TypeError: wrong element type Integer at 0 (expected array) #キーが重複したりすると思いがけないエラーになる array = [[:japan, "yen"], [:japan, "円"]] array.to_h #=> {:japan=>"円"}Hash[]に対して渡すことで配列に変換することもできる
Hash[]array = [[:japan, "yen"], [:us, "doller"], [:india, "rupee"]] Hash[array] #=> {:japan=>"yen", :us=>"dollar", :india=>"rupee"}ハッシュの初期値
h = Hash.new('hello') a = h[:foo] #=> "hello" b = h[:bar] #=> "hello" #変数aと変数bは同一オブジェクト a.equal?(b) #=> true #変数aに破壊的な変更をすると、変数bの値も一緒に変わってしまう a.upcase! a #=> "HELLO" b #=> "HELLO"ブロックを使うh = Hash.new{ 'hello' } a = h[:foo] #=> "hello" b = h[:bar] #=> "hello" #変数aと変数bは異なるオブジェクト a.equal?(b) #=> false #変数aに破壊的な変更を適用しても、変数bの値は変わらない a.upcase! a #=> "HELLO" b #=> "hello"
- 投稿日:2020-06-01T11:32:21+09:00
【Rails】マイグレーションコマンドまとめ
開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalinaマイグレーションコマンドの書き方
$ rails g migration ChangeColumnToBooks
は、
$ rails generation migration change_column_to_books
と書くのと同じ。つまり、
generation
はg
と略す事ができ、
AddBodyToBooks
の様に、単語の先頭を大文字にする事で「 _ 」を書く手間が省ける。基本コマンド
1.モデルとテーブルを作成
$ rails g model モデル名 カラム名:型名
ターミナル$ rails g model Book title:stringmigrate/~_create_books.rbclass CreateBooks < ActiveRecord::Migration[5.2] def change create_table :books do |t| t.string :title t.timestamps end end end2.モデルとテーブルを削除
rails d model モデル名
ターミナル$ rails d model Book3.マイグレーションを実行
ターミナル$ rails db:migrate4.マイグレーションの内容を戻す
①1ステップ前に戻る場合
ターミナル$ rails db:rollback①複数ステップ前に戻る場合
ターミナル$ rails db:rollback STEP=5 # 数字は自由に変更可能4.マイグレーションのステータス確認
ターミナルrails db:migrate:status
テーブル関係
1.テーブルのみを削除
$ rails g migration Dropテーブル名
ターミナル$ rails g migration DropBooksmigrate/~_drop_books.rbclass DropBooks < ActiveRecord::Migration[5.2] def change drop_table :books # 追記 end end2.テーブル名を変更
$ rails g migration Rename変更前のテーブル名To変更後のテーブル名
ターミナル$ rails g migration RenameBooksToArticlesmigrate/~_rename_books_to_articles.rbclass RenameBooksToArticles < ActiveRecord::Migration[5.2] def change rename_table :books, :articles # 追記 end endカラム関係
1.カラムを追加
①単体
$ rails g migration Addカラム名Toテーブル名 カラム名:型名
ターミナル$ rails g migration AddBodyToBooks body:textmigrate/~_add_body_to_books.rbclass AddBodyToBooks < ActiveRecord::Migration[5.2] def change add_column :books, :body, :text end end②複数
$ rails g migration AddColumnsToテーブル名 カラム名:型名 カラム名:型名 カラム名:型名
ターミナル$ rails g migration AddColumnsToBooks body:text introduction:text price:integermigrate/~_add_columns_to_books.rbclass AddColumnsToBooks < ActiveRecord::Migration[5.2] def change add_column :books, :body, :text add_column :books, :introduction, :text add_column :books, :price, :integer end end2.カラムを削除
①単体
$ rails g migration Removeカラム名Fromテーブル名 カラム名:型名
ターミナル$ rails g migration RemoveTitleFromBooks title:stringmigrate/~_remove_title_from_books.rbclass RemoveTitleFromBooks < ActiveRecord::Migration[5.2] def change remove_column :books, :title, :string end end②複数
$ rails g migration RemoveColumnsFromテーブル名 カラム名:型名 カラム名:型名 カラム名:型名
ターミナル$ rails g migration RemoveColumnsFromBooks body:text introduction:text price:integermigrate/~_remove_columns_from_books.rbclass RemoveColumnsFromBooks < ActiveRecord::Migration[5.2] def change remove_column :books, :body, :text remove_column :books, :introduction, :text remove_column :books, :price, :integer end end3.カラムのデータ型を変更
$ rails g migration ChangeDataカラム名Toテーブル名 カラム名:型名
ターミナル$ rails g migration ChangeDataTitleToBooksmigrate/~_change_data_title_to_books.rbclass ChangeDataTitleToBooks < ActiveRecord::Migration[5.2] def change change_column :books, :title, :text # 追記 end end4.カラムのオプションを追加
$ rails g migration ChangeOptionカラム名Toテーブル名 カラム名:型名
ターミナル$ rails g migration ChangeOptionTitleToBooksmigrate/~_change_option_title_to_books.rbclass ChangeOptionTitleToBook < ActiveRecord::Migration[5.2] def change change_column :books, :title, :string, null: false # 追記 end end5.データ型一覧
型名 役割 string 短い文字列 text 長い文字列 integer 整数 float 浮動少数 decimal 精度の高い少数 datetime 日時 timestamp タイムスタンプ time 時間 date 日付 binary バイナリ文字列 boolean 真偽値 6.よく使うオプション
オプション名 役割 default 初期値を設定 null 空欄の真偽 limit 長さを制限 unique 一意制約を付与 unique インデックスを付与
- 投稿日:2020-06-01T10:46:25+09:00
【リファクタリング】ルーティングの書き方
概要
ルーティングをスッキリ書く方法です
背景
ルーティングの指定をする際、7つのアクションのうち6つを使用する場合、onlyで書いたらダラダラと長くなってしまって、個人的に自分ダサっっとなったので備忘録として残しますRailsの7つのアクション
まずは、Railsの7つのアクションについておさらい
Action 役割 index 一覧を表示する new 追加する create 追加内容を登録す edit 編集する update 編集内容を更新する destroy 削除する show 個別内容を表示する 書き方
ここでは「tweets」というリソースにルーティングを行います
7つのアクション全てを実装する場合
routes.rbresources :tweets使用するアクションを限定する場合
ここでは「index」「new」「create」を使用することとします
routes.rbresources :tweets, only: [:index, :new, :create]不要なアクションを削除する場合
ここでは「show」以外のアクションを使用することとします
routes.rbresources :tweets, except: [:show]まとめ
ついつい、onlyで記入しがちですが、exceptを使用することでコードがスッキリし読みやすくなります
私もスッキリを心掛けてまいります参考
- 投稿日:2020-06-01T07:53:13+09:00
Railsチュートリアルで遭遇したテスト関連のバグ
Railsチュートリアルを勉強していて原因不明のバグに遭遇したので対処方法を記録しておきます。
環境
ホストマシン:macOS Catalina 10.15.4
関係してそうなGemのバージョン:gem 'rails', '6.0.3' gem 'spring', '2.1.0' gem 'rails-controller-testing', '1.0.4' gem 'minitest', '5.11.3'エラー
発生条件
詳細は不明ですが、いろいろなタイミングで発生しました。rails generateコマンドでControllerを作った後などに頻発。
エラー内容
テストがGreenになるはずの状況でrails test コマンドでエラーが出る。しかもエラーの原因箇所を特定不能。
2000行以上にわたる大量のログ(おそらくGemのパスを示している)が吐かれたのちに以下のエラーメッセージ。[NOTE] You may have encountered a bug in the Ruby interpreter or extension libraries. Bug reports are welcome. For details: https://www.ruby-lang.org/bugreport.html [IMPORTANT] Don't forget to include the Crash Report log file under DiagnosticReports directory in bug reports. Traceback (most recent call last): 5: from -e:1:in `<main>' 4: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 3: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 2: from /Library/Ruby/Gems/2.6.0/gems/minitest-5.11.3/lib/minitest.rb:63:in `block in autorun' 1: from /Library/Ruby/Gems/2.6.0/gems/minitest-5.11.3/lib/minitest.rb:140:in `run' /Library/Ruby/Gems/2.6.0/gems/activesupport-6.0.3/lib/active_support/testing/parallelization.rb:124:in `shutdown': Queue not empty, but all workers have finished. This probably means that a worker crashed and 7 tests were missed. (RuntimeError)冒頭の[NOTE]にあるようにライブラリ関連のバグなのでしょうか。
対処方法
ターミナルに以下のコマンドを打ち込んでspringを再起動する
rm bin/spring pkill -9 -f springこれは推測になりますが追加したファイルの内容が正確に読み込まれていないのが原因かもしれません。このようなバグがあるとTDDの効率は著しく下がってので辛いです。本来はGreenなテストがコード以外の部分が原因でRedになってしまうという厄介なバグでした。
追記
Railsチュートリアル内にも対処方法の記述がありました。記載箇所は第3章。
bin/spring stop # テストが原因不明で動かなくなったら、このコマンドを実行してみるとにかくspringとやらを一回止めることが大事らしい。springが一体何者なのか...誰か教えてください?
- 投稿日:2020-06-01T05:08:52+09:00
Rubyでチンチロゲームを作る 第4回 ゲーム進行の処理の作成
1. はじめに
いよいよゲーム進行にかかわる処理を書いていきます。あらかじめ各メソッドを実装しているので、なにもない状態から書き始めるよりも楽にできるはずです。それではやってみましょう。
2. main.rbの作成
各ファイルと同じフォルダに
main.rb
を作成します。まずは以下のように流れを書いてみましょう。# チンチロゲーム # プレイヤーの作成 # ----決着が着くまでループ # 賭け金の設定(自分) # 賭け金の設定(相手) # サイコロを振る # 役の決定 # 勝敗判定 # 賭け金の移動 # 最終的な勝敗判定 # ----決着が着くまでループちょっとずつ付け足していきます。
transfer_money.rb
はplayer_class.rb
に変更しています。require './player_class' # transfer_money.rbから変更 require './dice_roll' # チンチロゲーム # プレイヤーの作成 player_A = Player.new(money:1000,name:'カイジ') player_B = Player.new(money:1000,name:'班長') puts '---チンチロゲーム---' # ----決着が着くまでループ loop do # 賭け金の設定(自分) puts '掛け金を入力してください' bet_money = gets.chomp player_A.bet_money = bet_money.to_i # 賭け金の設定(相手) bet_money_B = rand(1..4) * 200 player_B.bet_money = player_B.money < bet_money_B ? player_B.money : bet_money_B puts <<~TEXT 名前: #{player_A.name} 所持金:#{player_A.money} ペリカ 賭け金:#{player_A.bet_money} ペリカ - 名前: #{player_B.name} 所持金:#{player_B.money} ペリカ 賭け金:#{player_B.bet_money} ペリカ press enter TEXT teisi = gets # サイコロを振る/役の決定 eye_on_the_dices1 = [rand(1..6),rand(1..6),rand(1..6)] eye_on_the_dices2 = [rand(1..6),rand(1..6),rand(1..6)] player_A.hand = roll_dice(eye_on_the_dices1) player_B.hand = roll_dice(eye_on_the_dices2) # 出た目の確認・役の決定 puts <<~TEXT 名前: #{player_A.name} 出目 #{eye_on_the_dices1} 役: #{player_A.hand} - 名前: #{player_B.name} 出目 #{eye_on_the_dices2} 役: #{player_B.hand} press enter TEXT teisi = gets # 勝敗判定 win_or_lose = player_A.check_win_lose(player_B) # 賭け金の移動 move_money = player_A.transfer_money(player_B,win_or_lose) player_A.money += move_money player_B.money -= move_money puts <<~TEXT 名前: #{player_A.name} 所持金:#{player_A.money} ペリカ - 名前: #{player_B.name} 所持金:#{player_B.money} ペリカ -------------------------- TEXT # 最終的な勝敗判定 if player_A.money <= 0 puts '所持金ゼロ。負けました…' break elsif player_B.money <= 0 puts '勝ちました!' break end # ----決着が着くまでループ endいちおうこれで遊べるようになりました。しかし、まだ改善が必要ですね。
3.賭け金の例外処理
上記のコードでは賭け金の入力に制限がありません。この場合、所持金以上に賭け金を入力することもできますし、負の数や小数点、文字列も受け付けてしまいます。
改善しましょう。# 賭け金の設定(自分) while true puts '掛け金を入力してください (終了するにはexitを入力)' bet_money = gets.chomp if bet_money == "exit" puts '終了' exit end if bet_money =~ /^[0-9]+$/ if bet_money.to_i > player_A.money puts '所持金より多い金額は賭けられません' elsif bet_money.to_i == 0 puts '0は無効です' else player_A.bet_money = bet_money.to_i break end else puts '正の整数で入力してください' end end変更点は三つです。
exit
と入力するとゲームを終了できるように変更=~ /^[0-9]+$/
で文字列や小数点がないことを確認- 所持金より多い場合または0の場合はエラーが発生
これで問題なく動くはずです。
4. ゲームができた!
Rubyだけでゲームがつくれてしまった。ちょっと嬉しい。嬉しかったので勉強会で発表した。広島cluster というイベントで、広島県のITちっくなものならなんでも発表できる。
https://hmcn.connpass.com/event/175209/
5. 改善点がたくさんある!
無事にコードが書けましたが、まだまだ改善するところがあります。エンジニアの方々にレビューを依頼したらたくさんのフィードバックをいただきました。ひとまずgithubのissueに入れておいて、どんどん対応することにしました。
やっていくぞ!
https://github.com/kyokucho1989/ruby-game/issuesあと、こちらのソースコードは自由に使ってください。cloneして改造してもOKです。
https://github.com/kyokucho1989/ruby-game
- 投稿日:2020-06-01T00:31:37+09:00
【Rails5.2】RESTful APIをRailsで簡単に実装する手順(APIモード)
環境
ruby 2.6.4
Rails 5.2.4.1
rbenv 1.1.2
mysql2 0.5.3
やりたいこと
APIモードで簡単なtodoアプリを実装します
RESTfulなAPIをRailsで実装する手順を書いていきます
わかりやすさを追求して書いていきます
元記事:【Rails5.2】RESTful APIをRuby on Railsで実装する簡単手順(APIモード)
カンタン実装手順
ステップ1. APIモードで新規アプリを作成
$ rails new sample_app --api -Tステップ2. TDD(テスト駆動開発)用にGem追加
Gemfilegroup :development, :test do gem 'rspec-rails', '~> 3.5' end group :test do gem 'factory_bot_rails', '~> 4.0' gem 'shoulda-matchers', '~> 3.1' gem 'faker' gem 'database_cleaner' endステップ3. gemインストール
$ bundle installステップ4. Rspecファイル生成
$ rails generate rspec:installステップ5. factories作成
$ mkdir spec/factoriesステップ6. rails_helper.rb修正
spec/rails_helper.rbrequire 'database_cleaner' # 追加 Shoulda::Matchers.configure do |config| config.integrate do |with| with.test_framework :rspec with.library :rails end end # 追加 RSpec.configure do |config| # 追加 config.include FactoryBot::Syntax::Methods # 追加 config.before(:suite) do DatabaseCleaner.clean_with(:truncation) DatabaseCleaner.strategy = :transaction end # 追加 config.around(:each) do |example| DatabaseCleaner.cleaning do example.run end end今回は導入部分だけですがapiモードで開発する際に参考になればと思います
参考文献
- 投稿日:2020-06-01T00:19:15+09:00
Ruby で嵌る AtCoder ABC 169 C 浮動小数点
はじめに
「AtCoderは、世界最高峰の競技プログラミングサイトです。
リアルタイムのオンラインコンテストで競い合うことや、
3,000以上の過去問にいつでもチャレンジすることができます。」
を利用して、プログラミングの勉強をしています。
AtCoder さん、ありがとうございます。今回のお題
AtCoder Beginner Contets C - Multiplication 3
Difficulty: 536今回のテーマ、浮動小数点による誤差
Ruby
WA1
wa1.rba, b = gets.split.map(&:to_f) puts (a * b).floor切り捨てなので
floor
を使用するもWA
。WA2
wa2.rba, b = gets.split.map(&:to_f) puts ((a.to_i * (b * 100)) / 100).floor
100倍
してから100
で割る作戦、失敗。WA3
wa3.rba, b = gets.split.map(&:to_f) puts ((a.to_i * (b * 100)) / 100).to_i神頼みで
floor
をto_i
にしてみる。WA4
wa4.rba, b = gets.chomp.split puts ((a.to_i * (b.to_f * 100).to_i) / 100)標準入力からの受け取りを文字列にする作戦。
AC
ac.rba, b = gets.chomp.split puts ((a.to_i * (b.gsub!('.', '').to_i).to_i) / 100)
to_f
を100倍
するのを諦め、小数点.
の文字を削除する作戦。
$\huge{成功}$何が良くて、何が拙かったよく分かっていないのですが、とにかく疲れました。
まとめ
- ABC 169 C を解いた
- Ruby に詳しくなっていない