- 投稿日:2020-05-17T23:39:03+09:00
railsでアプリを作る際にdbを指定する方法
railsでアプリを作る際にdbを指定する方法
簡単なことなのですが、よくDB指定を忘れてアプリを作ってしまう私への戒めの備忘録です、、、。
データベースの指定
rails newをする際に、データベースを指定してアプリを作ります。
何も指定しないとデータベースのデフォルトはsqliteになるので、Mysqlなどを使いたい時に行います。例
$ rails new アプリ名 --database=mysql簡略化した書き方でこういうのもあります。
例$ rails new アプリ名 -d mysqlこっちの方が簡単でいいですね、-dはデータベースの意味です。
簡単に備忘録として書かせていただきました。
- 投稿日:2020-05-17T23:03:32+09:00
Day16 変数が使える範囲・スコープについて
変数の受け渡しまとめ
ReviewAppdef post_review # 変数の定義 post = {} puts "ジャンルを入力してください:" post[:genre] = gets.chomp puts "タイトルを入力してください:" post[:title] = gets.chomp puts "感想を入力してください:" post[:review] = gets.chomp line = "---------------------------" # レビューの描画 puts "ジャンル : #{post[:genre]}\n#{line}" puts "タイトル : #{post[:title]}\n#{line}" puts "感想 :\n#{post[:review]}\n#{line}" # 配列オブジェクトに追加 posts << post end↑これだと
post_reviewメソッド
内にposts
が定義されていないのでNG。
原則メソッド内で定義した変数しか使えない。
これを使えるようにするために、引数というものを使う。<引数を使う上でのルール>
1. 「メソッドを定義している部分」と「メソッドを呼び出す部分」両方に書く
2. 「仮引数」と「実引数」の名前は、一致している必要はない引数を使ったメソッドの実行def multi(number) #メソッド定義では、仮引数として、numberとしている。 puts number * number end puts "何か数字を入力してください" value = gets.to_i multi(value) #実際にmultiメソッドを使うときは、さっきのnumberではなく、valueを置いている。
- 投稿日:2020-05-17T22:54:25+09:00
Webアプリケーション(Rails)のコードレビューをする時に気にしている観点
コードをレビューする時にいつも似たようなところを指摘している気がしたので自分が気にしている観点をまとめてみました。
使用言語に依存しない観点もありますが、私は仕事では主にRailsを使っているので、RubyやRailsに特化した観点もあります。とりあえず現時点で思いつくものをざっと書きましたが、今後も気にしている観点を思い出したら追記していきます。
開発もQiita記事も最初から完璧を目指さずに継続してデリバリー(CD)していくのがよいですね。インデントやコーディングルール
インデントやコーディングルールに従っているかなど、コードの静的な部分のチェックは静的解析ツールを使いましょう。
人が目視で確認するのは時間がもったいないし、漏れやブレが発生します。
また細かいところにばかりに気が取られてしまい本来指摘すべきところを見逃してしまいます。静的解析ツールはRubyではRubocopが有名です。
このような開発をサポートする機能の導入は初期開発では後回しにされがちですが、静的解析ツールは途中で入れるのがとても大変です。
後から追加しようとすると、ルールを緩和したり免除するコードがでてきてしまうので初期開発の段階で入れるようにしましょう。
個人的にはきつめに設定しておいて、必要に応じて緩和していくのが良いと思います。静的コードチェックに縛られすぎないか
1つ前に静的コードチェックを使えと書いたばかりで早速矛盾するテーマなのですが、静的コードチェックの指摘を直すためにわかりづらい修正をするくらいなら静的コードチェックの方を緩和したり例外を設定した方が良いです。
よくあるのがメソッドの行数超過です。
メソッドが一定行を超えると分割しろと言われるのですが、処理が1つのまとまりであるとことが妥当な場合はわかりづらくなってしまうだけなので無理に分ける必要はないと思います。HTTPのステータスコードは適切か
Railsだとステータスコードとメソッドやレスポンスの整合性があっていなくても問題なく動くことが多いので、ステータスコードの使い方が適切かを確認するようにしています。
細かい使い分けは人によって解釈が分かれるところだと思いますが、主要なステータスコードについて私の解釈を書きます。2XX
リクエストを正常に処理した時のステータスコードです。
201 Created
POSTやPUTリクエストでリソースを生成した時に使います。
201を使う場合はレスポンスボディに生成したリソースを返却することがよくあります。
もし返却するものがないのであれば、204 No Contentを使った方が良いと思います。204 No Content
DELETEリクエストでリソースを削除した時に使うことが多いですが、POSTやPUTなどでもレスポンスボディがない場合に使います。
これを使う場合はレスポンスボディに値を入れてはいけません。200 OK
上記以外で正常なレスポンスを返す場合は基本的に200 OKを使えば良いと思います。
3XX系
別のページへリダイレクトさせたい時のステータスコードです。
301 Moved Permanently
恒久的に別のページにリダイレクトさせる場合は301を使います。
例えばWebページを移行した場合などに古いページのURLから新しいページへリダイレクトさせる時に使います。
また、SEO対策としても役立ちます。301を指定すると検索エンジンが新しいURLをインデックスしてくれます。303 See Other
私も最近まで意識していなかったのですが、リソースを登録・更新させた後にそのリソースの詳細ページなどにリダイレクトさせる場合は303を使います。
下記のようなパターンです。
POST /resources でリソースを新規登録して、リソース情報を表示するページ(GET /resources/:id)へリダイレクト302 Found
301とは異なり、一時的に別のページにリダイレクトさせる場合は302を使います。
Railsでredirect_to
を使うとデフォルトで302になりますが、リダイレクトさせる目的を考えると302の用途は少なく、301 or 303に振り分ける方が適切なことが多いと思います。4XX系
クライアント起因のエラーが発生した場合のステータスコードです。
404 Not Found
ページが見つからない場合に使われるステータスコードです。
画面に大きく「404」と表示されることも多いので一般の方でも知っている方が多いと思います。人によっては考え方が分かれるところかもしれませんが、私はリソースが見つからないことがクライアントにとって異常なのか正常なのかを考えて404と200を使い分けるようにしています。
イメージしやすいようにいくつか具体例を挙げます。
GET /resources/:id
でリソースが見つからない場合
クライアントが存在しないidを指定しているため404を返却TODOリストを取得する
GET /todos
でTODOが0件の場合
TODOが0件ということは正常動作なので200を返却お店検索で絞り込み条件を指定したら0件になった
クライアントが指定した条件に合致するお店がなかったの404を返却
SEO観点でも0件のお店検索結果ページを検索エンジンにインデックスさせたくないので404を指定することでインデックスさせない。401 Unauthorized
認証が必要なアクションに対して有効な認証情報が渡されなかった場合に使用します。
403 Forbidden
認証はしているが利用権限が不足している場合に使用します。
例えば、あるリソースに対して読み取り専用のユーザーが更新を行おうとした場合などに使用します。ただし、403を使う時に考慮した方が良い点が1つあります。
あるユーザーにとって非公開のリソースにアクセスした場合です。
この場合、システム的に考えるとアクセス権限がないので403を使いたくなりますが、403を返却するとリソースが存在していることがユーザーに伝わってしまいます。
こういう場合は存在自体を知らせない方が良いので、存在しないという意味を込めて404を返却するのが良いでしょう。400 Bad Request
必須パラメーターが指定されていないなど不正なリクエストが来た場合に使用します。
401, 403, 404に当てはまらない場合には大体400を使うことが多いです。410 Gone
期間限定のキャンペーンページで期間が終了してページを削除した後など、恒久的に削除するURLにアクセスされた時に使います。
蛇足ですが、最近はキャンペーン期間が終わっても放置されているページが多くて検索した時のノイズになってしまい邪魔ですね。
期限が過ぎたら削除するというタスクも積んでおき、きちんと実行してほしいものです。5XX系
サーバー起因のエラーが発生した場合のステータスコードです。
500 Internal Server Error
サーバーで予期せぬエラーが発生した場合に使用します。
プログラムで明示的に500エラーを発生させることはあまりないと思うので、明示的に500エラーを返却している箇所があったら適切なエラーを返却するように修正した方が良いと思います。503 Service Unavailable
定期メンテナンスなどにより、一時的に正常にアクセスできずメンテナンスページを表示する場合などに利用します。
クライアントへのエラーメッセージが適切か
エラーが発生した時にクライアントにエラーメッセージを返却することがあると思いますが、そのメッセージが適切かを確認します。
観点はそのエラーメッセージを受け取ってアクションを起こせるかです。
よくある悪い例は複数入力項目がある画面で1つ入力条件を満たしていない時に「入力項目が不正です」のような項目を特定しないメッセージを返すパターンです。
これだとどの項目が間違っているのか分からず、何をすれば良いか分からなくなります。
「xx項目の値は数値で入力してください」のように項目とエラー原因を伝えてあげましょう。ただ、ユーザーに細かく伝えることがNGの場合もあります。
例えばID/パスワードを入力するログイン画面で存在するIDを指定してパスワードが間違っていた場合に「パスワードが間違っています」と返してしまうとIDが存在していることがユーザーに伝わってしまいます。
この実装をしてしまうと悪意のあるユーザーが適当にIDを入力して存在しているIDのリストを作ることができてしまいます。
ログイン画面はIDが存在しない場合もパスワードだけが違う場合も「ログイン情報が不正です」のように何が間違っているか特定できないメッセージを返すのが良いです。apiのレスポンスは意味のある値を返す
最近のWebアプリケーションはフロントとバックエンドを別で作ることが多いと思います。
そのためバックエンドはAPIとして機能を提供することがよくあります。
APIを作る際、列挙型の項目を返却する時は値を見るだけで意味がわかるように返却すべきです。
例えば下記のようなステータスがあるとします。enum status { todo: 1, in_progress: 2, done: 3 }これをフロントに返却する場合は数値(1, 2, 3)ではなく名称('todo'など)を返却すべきです。
status=1
のように数値を返却するとレスポンスを見た時に1ってなんだっけ?となっていしまいますが、status='todo'
のように名称を返却するとパッと見ただけで意味を理解することができます。配列の並び順が一意になるか
配列を返却する場合は配列の並び順が一意になるかをチェックします。
下記ではuser_idで絞り込んだreviews配列を返却していますが、orderを指定していないのでどのような並び順で返却されるのか不定です。
呼び出し元が並び順を気にしないことが確定しているのであれば良いですが、並び順が定まるようにorderを指定しましょう。def my_reviews(user_id) Review.where(user_id: user_id) end下記ではcreated_atの降順を指定しています。これであれば新規作成したものから順番に取得できそうです。
def my_reviews(user_id) Review.where(user_id: user_id).order(created_at: :desc) endただこれでもまだ問題があります。created_atが同じreviewがある場合に順番が定まりません。
並び順を確実に定めるには下記のようにorderに指定しているカラムで一意になるようになっているかを考えると良いです。
今回は一意にするために第2ソートにidの降順を指定しました。def my_reviews(user_id) # 1. created_atの降順に並べる。ただしcreated_atはユニーク制約はないので同日がありえる # 2. 同日だった場合、idの降順に並べる。idはユニークなので同じ値はありえない # => 並び順が一意に定まる Review.where(user_id: user_id).order(created_at: :desc, id: :desc) end他人が読んでスムーズに理解できること
コードレビューをしている時に理解するのに時間がかかる実装や勘違いしてしまう実装を見かけることがあります。
このような実装は今後このコードを見る人全員が同じように感じてコードリーディングの工数を上げてしまい、バグを埋め込む確率も上げてしまうので可能な限りシンプルな実装にした方が良いです。
どうしてもシンプルにできない場合はコメントを入れるなどすると良いと思います。
Railsなどフレームワークを使っていると下記のようにフレームワークの枠から離れた使い方をしていると勘違いさせてしまうことがあります。
勘違いさせるコードは高確率で後々バグを生み出すのでなくしていきましょう。class Review belongs_to :user enum status { open: 0, close: 1, } end class User # × デフォルトのアソシエーションなのに条件を指定して絞り込んでいる has_many :reviews, -> { open } # ○ 条件をつける場合はそれがわかる名前にしましょう has_many :open_reviews, -> { open }, class_name: 'Review' end class Hoge def fuga(user_id) user = User.find user_id # ここだけ見るとuserのreviewsを全部取得していると勘違いする可能性が高い user.reviews end endYAGNI
ご存知の方も多いと思いますが、YAGNIという言葉があります。
詳細はwikipediaを参照してください。
https://ja.wikipedia.org/wiki/YAGNI上記に記載されている通り、まだ使わない機能を想像して実装しておいてもそのまま使える可能性はほとんどありません。
未来を想像して書いているコードを見かけたら要注意です。実装されたコードを削除してもらうのは心苦しいですが削除しておく方が良いことが多いです。
残しておくと後々それが邪魔になったり、リファクタリングの時に使っていないのに直さないといけなかったりと生産性が下がる可能性があります。メソッドや定数のscopeは小さくする
外部から呼び出さないメソッドや定数はprivateにしておきましょう。
publicだとそのメソッドや定数に手を入れる時の影響範囲がソース全体になっていしまいますが、privateだと同Class内だけを気にすれば良いのでリファクタリングなどソース修正の影響範囲調査がかなり楽になります。責務は適切か
各クラスはそれぞれ責務を持っています。その責務が守れているかをチェックします。
例えばアカウントをアクティベートするアクションについて考えてみます。
- アクティベーションするにはtokenが必要
- Accountのstatusとアクティベート日時を更新してtokenを削除する
アクティベーションする時に実行される処理はAccountの責務を持っているAccountモデルが知っているべきことなので、下記の○のような方針で実装されている方が良いです。
class AccountController # × アクティベーションの時にすべきことをcontrollerが把握している def activate account = Account.find_by(token: params[:token]) account.status = :activated account.activated_at = Time.now account.token = nil account.save! end # ○ アクティベーションの時にすべきことをcontrollerは知らず、accountモデルに依頼している def activate account = Account.find_by(token: params[:token]) account.activate! end end class Account def activate! status = :activated activated_at = Time.now token = nil save! end endincludes, eager_load, preload
これらの違いが曖昧な方は、Railsを使っている方は必ず1回は見ていると思われる下記のQiita記事をご覧ください。
https://qiita.com/k0kubun/items/80c5a5494f53bb88dc58includesはpreloadとeager_loadを勝手に使い分けてくれる便利な存在です。
Railsを使い始めたことはincludesを使っておけばいいだろうくらいに思っていたのですが今の考えは逆です。
基本preloadかeager_loadを使うようにしています。
というのも実装する時にjoinすべきかどうかはきちんと把握して実装すべきと考えているからです。
実装によってjoinしたりしなかったりするincludesは制御が難しく予期せぬ挙動をしてしまう可能性があるので使わない方が良いです。無駄なjoinはないか
ActiveRecordはとても便利ですが、RailsエンジニアからSQLを遠い存在にしているように感じます。
自分でSQLを書いていると書かないようなSQLも平気で発行してしまいます。
その中でも特に見かけるのは無駄にjoinしているパターンです。
下記に具体例を示します。
organizatoin_idを指定してその組織に所属するuserを取得するメソッドfind_by_organization
です。class User has_many :user_organization def self.find_by_organization(organization_id) # × organizationsテーブルは結合する必要がない # select * from users inner join user_organizations on users.id = user_organizations.user_id inner join organizations on user_organizations.organization_id = organizations.id where organizations.id = #{organization_id} joins(user_organization: :organization).merge(Organization.where(id: organization_id)) # ○ user_organizationsしか結合していない # select * from users inner join user_organizations on users.id = user_organizations.user_id where user_organizations.organization_id = #{organization_id} joins(:user_organization).merge(UserOrganization.where(organization_id: organization_id)) end end class Organization has_many :user_organization end class UserOrganization belongs_to :user belongs_to :organization end
- 投稿日:2020-05-17T21:17:45+09:00
Rails Docker 環境構築
前提
railsでDockerの環境構築をした時の備忘録。
Docker for macインストール済み本題
rails |--Dockerfile |--Gemfile |--Gemfile.lock |--docker-compose.ymlDockerfile.FROM ruby:2.4.5 RUN apt-get update -qq && apt-get install -y build-essential nodejs RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /appDockerfileをbuildしDocker Image(コンテナ仮想環境の雛形が作成される)
Gemfile.source 'https://rubygems.org' gem 'rails', '5.0.0.1'Gemfile(Installしたいgemを定義)→bundle installコマンド実行→gemがインストールされる→InstallしたgemがGemfile.lockに記載される
docker-compose.ymlversion: '3' services: web: #Railsコンテナの定義 build: . #Dockerfileを元にイメージを作成し使用すること command: bundle exec rails s -p 3000 -b '0.0.0.0' #Rails起動のコマンド volumes: - .:/app ports: - 3000:3000 #<公開するポート番号>:<コンテナ内部の転送先ポート番号> depends_on: - db #Railsが起動する前にMySQLサーバーが先に起動するように設定 tty: true stdin_open: true db: #MySQLサーバーコンテナの定義 image: mysql:5.7 volumes: - db-volume:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: password volumes: db-volume: #この定義によりPC上にdb-volumeという名前でボリューム(データ保持領域)が作成される上記設定が終わったらターミナルにてRailsプロジェクトの作成
terminal.rails$ docker-compose run web rails new . --force --database=mysql
docker-compose run web
でwebサービスのコンテナで後ろのコマンドを実行するコマンド
--force
は、既存ファイルを上書きするオプション
--databace=mysql
はMySQLを使用する設定を入れるオプションbuildが完了したら、Railsで使用するデーターベースファイルを編集
config/database.yml#省略 default: &default adapter: mysql2 encoding: utf8 pool: 5 username: root password: password #編集 host: db #編集 #省略設定が完了したら下記のコマンドをターミナルに入力
terminal.rails$ docker-compose up -d上記は、現在のディレクトリにあるdocker-compose.ymlに基づいてコンテナ(仮想環境)を起動するコマンド
続いて、開発環境用のデータベースが作成されていないため、下記コマンドで作成
terminal.rails$ docker-compose run web bundle exec rake db:create
bundle exec rake
の部分は、Rails環境にインストールされているrakeコマンドを実行している
rake db:create
でRailsで使用するデータベースをMySQLサーバー上に作成してくれるブラウザでlocalhost:3000と入力しRailsの画面が出てきたら完了!
- 投稿日:2020-05-17T21:10:43+09:00
【Ruby on Rails】NoMethodError undefined method `devise_for'のエラー解消
- 投稿日:2020-05-17T20:12:03+09:00
Rspec、TDD(1)
初めまして。4月から医療業界→Webエンジニアに転職しました。Ruby経験は2ヶ月程度です。
入社して1ヶ月半が経過し、細かい修正以外に、新しい機能の開発なども少しずつ行うようになってきており、少しずつ成長を感じています(そう言い聞かせています)。
新しい機能を開発する際に「テスト書いておいてね。」と言われテストコードを見てみると、「書き方が分からん・・・。」という状態になり、優先的にテストの勉強をしています。どの条件でテストをした方が良いのかはなんとなく分かる(気がしている)のですが、Rspec独特の記法への慣れも少なからず必要であると思います。
現在は以下の書籍や記事を参考に学習を進めています。
Everyday Rails - RSpecによるRailsテスト入門
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」
テスト駆動開発備忘録ですが、テストを勉強した過程を少しずつ記事として残して行こうと思います。
そもそも自分の書いたテストを信頼出来ないと始まらないですもんね・・・。
そもそもテストを書くメリットとは?
1.テスト全体にかかるコスト削減
- 自動テストをコツコツテストを書いていくことで、既存の機能が想定通りに動いていることを確認しながら、新機能の開発ができる
- リファクタリングをする際に元の機能を破壊していないか担保することができる
2.環境の変化に強くなる
- ruby、rails、gemのアップデートなどのバージョン変更時に影響を調査することができる
3.テスト自体が使用のドキュメントとなる
- 期待している挙動の詳細を表すものとなる為、他者が仕様を理解する手助けとなる
- 仕様やインターフェイスについてに再考する機会となる
4.コードの粒度を保つ助けとなる
- テストが書きにくいメソッドやアクションはそれだけ多くの役割を持っていることとなり、実装について考え直す機会となる
5.全体的な開発効率の向上
- ずばりこれ。手動テストして、お祈りデプロイするよりもずっと安心、安全に開発が行える
Rspecのセットアップ
- Railsにデフォルトで含まれていない為Gemfileに追記→bundle install
Gemfilegroup :development, :test do gem 'rspec-rails', '~> 3.8.0' end
- テスト用DBの作成
config/database.ymltest: <<: *default database: db/test.sqlite3bin/rails db:create:allを実行
Rspecの設定
- bin/rails g rspec:installを実行 spec格納ディレクトリ、ヘルパーファイル、設定ファイルが作成される
.rspec--format documentation追記すると出力情報が読みやすくなる
- Rspecテスト効率を上げる為にbinstub、springをインストールする
gemfilegroup :development do gem 'spring-commands-rspec' end$bundle exec spring binstub rspecを実行
これで$bin/rspecでテスト実行可能になる最初に基本的な構文の確認
- describe: テストのグループ化宣言、ネスト可能
- it: テストをexampleという単位にまとめる
- expect(A).to eq B: AがBであると期待するテスト
sample_spec.rbRspec.describe '計算' do it '100 + 100は200' do expect(100 + 100).to eq 200 end it '100 * 100は10000' do expect(100 * 100).to eq 10000 end end
- context: 条件別にグループ分け
- before: exampleの実行前に必ず呼ばれる、テスト実行前のデータ準備やアセットなどを行うことが多い
beforeとitはスコープが異なる為、変数を使用する時は注意が必要
let(:A) { B }: BをAとして参照することができる
letは遅延評価の為、呼び出されるまでは使われない
let!: exampleの実行前に定義した値が作られる、遅延評価が原因でテストが落ちている時はこちらが良いかも
subject: テスト対象のオブジェクトをまとめることができる
その場合はis_expectedを使用する
shared_example、it_behaves_like: exampleがDRYでない時に再利用することができる
shared_context、include_context: contextの再利用が可能
とりあえず今回はここまでです。
基礎は理解したのでどんどん書いていこうと思います
- 投稿日:2020-05-17T19:48:31+09:00
【開発ログ⑮】外部リンクやPDFリンクをつけたい
前提について
はじめまして、 プログラミングスクールに通ういりふねと申します。この記事は、スクールの課題である個人アプリの開発の記録を書くことで、自身のアウトプットに利用しています。もし、読んでいただけた方がいましたら、フィードバックをしていただけたら嬉しいです。
開発するのは「有給休暇管理ツール」です。仕様は過去記事をどうぞ。
アプリはデプロイまで行いますが、サービスとして提供するものではありません。あくまでも自学習の一環ですので、ご理解下さい。では本題へどうぞ。
今回の実施内容
今回は厚生労働省のリンクや有給休暇の届出のPDFのリンクを付けたいと思います。おまけ機能的なものですね。実際には以下の手順で実施します。
- 厚生労働省のリンクを実装
- 新しいタブで開くを実装
- google driveの共有設定
- 有給休暇の届出のリンクを実装
厚生労働省のリンクを実装
ページ左下の関連サイトのボタンに設置します。こちらは、link_toにURLを入力するだけなので、とっても簡単です。
branches/_side.html.haml〜中略〜 .links = link_to '#' do 有給休暇届 = link_to "https://www.mhlw.go.jp/shingi/2004/04/s0428-7b2a.html", class: "link" do 関連サイト新しいタブで開くを実装
実際にリンクをクリックすると、現在開いている有給休暇管理ツールのページから厚生労働省のページに遷移します。これでは、いちいち戻るボタンを押す必要があるので、新しいタブにリンク先が表示されるようにします。
branches/_side.html.haml〜中略〜 .links = link_to '#' do 有給休暇届 = link_to "https://www.mhlw.go.jp/shingi/2004/04/s0428-7b2a.html", target: :_blank, class: "link" do 関連サイト追加したのは、「target: :_blank」の部分です。これで、新しいタブで開くようになりました。
google driveの共有設定
次にPDFの文書「年次有給休暇届」のリンクを実装します。有給休暇の申請を紙ベースで管理している会社の方が多いと思いますので、有給休暇管理ツールに見本があれば、すぐにプリントアウトして社員さんに渡してあげることができます。
今回は、google driveの共有設定を利用してリンクを取得します。
まずは、自身のdriveにPDFファイルを保存します。保存はドラッグ&ドロップでOKです。次に共有したいファイルを右クリックして、「共有」を押します。出てきたメニューの下側にリンクを取得があるので、その中の「リンクを知っている全員に変更」をクリック、「リンクをコピー」をクリックします。
参考にさせていただいた記事「Google ドライブのファイルを共有する」有給休暇の届出のリンクを実装
最後に取得したリンクをrailsのlink_toに反映させます。
branches/_side.html.haml〜中略〜 .links = link_to "https://drive.google.com/file/d/1NXmC35ZbmCOAqu7oGyCOc2LqipHDGDJj/view?usp=sharing", target: :_blank, class: "link" do 有給休暇届 = link_to "https://www.mhlw.go.jp/shingi/2004/04/s0428-7b2a.html", target: :_blank, class: "link" do 関連サイトこちらの方法は、手軽にできますが、開いたリンクにアカウント名やメールアドレスが表示されるので、きちんとサービスとして実施する際には、専用のgoogleアカウントを用意するか、別の方法が良いかも知れません。
今日の振り返り
個人発表会が近くなってきました。有休の付与と消化を手入力で行うという意味ではだいぶ形になってきましたが、大切な付与計算などは省いているので、5月19日以降も継続してアプリケーションを作っていこうと思います。
- 投稿日:2020-05-17T19:44:19+09:00
Windows10 「rails s」を起動できない。localhost:3000にアクセスできない
初学者です。初歩的な質問だと思いますが、環境構築のところで躓いてしまったのでご教示頂けたら助かります。
Windows10環境下でRails serverを起動しようとトライしています。コマンドプロンプトから
ruby -vで実行すると、
ruby 2.3.3p222 (2016-11-21 revision 56859) [i386-mingw32]rails -vで実行すると
Rails 5.1.7と返ってきています。アプリケーション名を「book」にする予定だったので、
rails new book
と実行し、C:\Users\81804の中に「book」というフォルダが生成されました。
cmdから「cd book」と実行し、bookディレクトリまで移動し、そこで「rails s」と実行したところ以下のようなエラーが表示されました。
Could not find gem 'turbolinks (~> 5) x86-mingw32' in any of the gem sources listed in your Gemfile.
Runbundle install
to install missing gems.
ここで「bundle install」とコマンドプロンプトで実行してみました。するとしばらく文字列が綴られ、テキスト下部のほうを読んでみると、エラーを起こしていることがわかりました。エラーメッセージはこのような内容でした。An error occurred while installing sqlite3 (1.4.2), and Bundler cannot continue.
Make sure thatgem install sqlite3 -v '1.4.2' --source 'https://rubygems.org/'
succeeds before bundling.In Gemfile:
sqlite3
このような内容でした。エラー文から
gem install sqlite3 -v '1.4.2' --source 'https://rubygems.org/
とコマンドプロンプトで実行すればいいのか?と解釈し、こちらを入力してみるとGem files will remain installed in C:/RailsInstaller/Ruby2.3.3/lib/ruby/gems/2.3.0/gems/sqlite3-1.4.2 for inspection.
Results logged to C:/RailsInstaller/Ruby2.3.3/lib/ruby/gems/2.3.0/extensions/x86-mingw32/2.3.0/sqlite3-1.4.2/gem_make.out
と書かれ、何やら成功しているようにみえるのですが、いざ改めて「rails s」を実行してみるとCould not find gem 'turbolinks (~> 5) x86-mingw32' in any of the gem sources listed in your Gemfile.
Runbundle install
to install missing gems.
また同じエラーが発生する、という状況に陥ってます。
関係ないかもしれませんが、ウイルスバスターは無効にしてあります。どなたかご助言いただけないでしょうか。
使用PC環境は以下の通りです。
・Windows10 Home
・Intel Corei7-8550U CPU 1.80GHz 1.99GHz
・実装メモリ16.0GB
・64ビットオペレーティングシステム,×64ベースプロセッサhttps://teratail.com/questions/176355
こちらのご質問内容に非常に近しかったので早速トライしてみました。有効と思われる回答↓
「sqlite3の1.4.0と言うバージョンはrailsと相性が悪く、使えません。Gemfile内で以下のようにバージョンを指定してください」gem 'sqlite3', '~> 1.3.6'
私には「gemfile」が何なのかわからなかったのですが、新たに生成したディレクトリ内に「Gemfile」という2KBくらいのファイルがあったので、これをVS Codeで開き、
gem 'sqlite3' と書かれていたところを
→gem 'sqlite3', '~> 1.3.6'
に書き換えて上書き保存してみました。そして、もう一度コマンドプロンプトで、こちらのディレクトリまで移動し「Rails s」と実行してみましたが、やはり
Could not find gem 'turbolinks (~> 5) x86-mingw32' in any of the gem sources listed in your Gemfile.
Runbundle install
to install missing gems.
というエラーが返ってきてしまいます...。
- 投稿日:2020-05-17T18:59:09+09:00
AtCoder Beginners SelectionでRuby学習【Some Sums】使えるメソッドを増やす
はじめに
Ruby学習の一環として「競技プログラミング(競プロ)」に挑戦します。
そのための学習の中で学んだことをアウトプットしていきます。
今回は「AtCoder Beginners Selection」の六問目(Some Sums)より。
https://atcoder.jp/contests/abs問題
1以上N以下の整数のうち、
10進法でA以上B以下であるものの総和を求めなさい。制約
1 ≤ N ≤ 10,000
1 ≤ A ≤ B ≤ 36
入力は全て整数である入力は以下の形で与えられる。
N A B # 例 20 2 5出力例 # 上記例の場合 => 8420以下の整数のうち、各桁の和が2以上5以下なのは
2,3,4,5,11,12,13,14,20です。これらの合計である84を出力します。解答①
まずは僕の解答から。
n, a, b = gets.split(" ").map(&:to_i) r = 0 (1..n).each do |i| r += i if i.to_s.split("").map(&:to_i).inject(:+).between?(a, b) end print rこの解答をする中で学んだメソッドを以下にまとめていきます。
splitメソッド
入力を受け取る時によく使ってましたが、
今回のeach文の中での使用で、改めてStringクラスのメソッドだと認識出来ました。
解答では一度String型に変換した上でsplitメソッドを使い、またInteger型に戻すということをしています。#例 print "1234".split("") => ["1", "2", "3", "4"]between?メソッド
self.between?(min, max) #selfがminからmaxの間(min,max含む)であった場合、trueを返す #例 print 6.between?(1, 5) => false最小値と最大値を指定し、selfがその範囲内だった場合はtrueを返すこのメソッドを使って、
各桁の和がa以上b以下の範囲内かを判定しています。解答②
他の方の解答を見てみます。
n, a, b = gets.split.map(&:to_i) puts (1..n).select{|e|(a..b).include?(e.to_s.chars.map(&:to_i).inject(&:+))}.inject(&:+)入力を受け取るところまでは、解答①とほぼ同じですね。その後の流れを見てみると、
① 整数の各桁の和がa以上b以下であるかとinclude?メソッドで判定する
② ①を通過した整数は、selectメソッドで新しい配列を生成し、その中に追加する
③ 条件に合う整数が出そろったところで、最後尾のinjectメソッドで整数の総和を求め、出力するここで使われているメソッドについて簡単にまとめます。
include?メソッド(Rangeクラス)
オブジェクトが範囲内に含まれている時、trueを返します。
#例 print (1..5).include?(4) => trueselectメソッド
条件に一致した要素を取得して、新しい配列として返します。
#例 print [1,2,3,4,5].select { |num| num > 3 } => [4, 5]最後に
以上、AtCoder Beginners Selection【Some Sums】を解く中で学んだメソッドをご紹介しました。
もし間違いなどございましたら、ご指摘いただけると嬉しいです。
- 投稿日:2020-05-17T18:15:24+09:00
描いて理解する Action Cable
はじめに
コロナ騒ぎで時間があるので Rails の Action Cable を触ってみました。
基本的な使い方は Rails ガイドで調べましたが、いまいちピンとこなかったので理解するために図を描きました。特に用語についての章を読んでもそれぞれの用語の関係性がよく分からなかったので、自分なりに整理しています。
用語について
Connection, Consumer
WebSocket の基礎
- Web 上でクライアント、サーバ間の双方向通信を実現するための通信規格
Upgrade: websocket
ヘッダをつけた HTTP リクエストで開始- レスポンスとして HTTP ステータス 101(Switching Protocols) が返ってきたら TCP コネクションを使いまわして WebSocket として通信ができる
- 送信できるデータはテキストまたはバイナリ
- HTTP とは別のプロトコルなので Cookie などは送られない
- 通信が確立したあとはクライアント、サーバどちらからでもメッセージを送ることが可能
Connection
- WebSocket としてのコネクションを表すクラス
- (おそらく)WebSocket として通信が確立した時点でインスタンスが生成される
- 開始時のリクエストはふつうの HTTP なので Cookie も送られる
- なので
connection.rb
ではcookies
が使える- 認証処理はこのクラス内に記述
Consumer
- WebSocket コネクションのクライアント(ブラウザなど)
- HTTP とは異なり、同じブラウザの異なるタブは完全に別のクライアントとして扱われる
Consumer を区別する方法
HTTP とは異なり毎回認証情報が送られたりはしないので、コネクション確立時に「このコネクションは誰のものか」をサーバー側で管理する必要がある。
そのためのしくみとして Action Cable では Connection クラスの
identified_by
を使う。# app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = User.find_by(id: cookies.encrypted[:user_id]) end end endChannel, Subscription
Channel
- 文字通りのチャンネル
- Controller のようなもの
Subscription
- チャネルの購読
- ひとつのコネクション内でいくつでも作成可能
- あるチャネルの購読をしている、という意味ではコンシューマをサブスクライバとも呼ぶ
以下のように
consumer.subscriptions.create
で新たなSubscription
オブジェクトが作成され、consumer.subscriptions
に追加される。chat_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("ChatChannel", { // 略 });Stream
- ブロードキャストを行う場合に使う
- チャネルがサブスクリプションとストリームの紐付けを行う
- あるストリームにブロードキャストした場合、そのストリームに紐づくサブスクリプションにメッセージが送られる
例
たとえば以下のようなチャネルクラスを定義しておくと
ChatChannel
をサブスクライブしたときにchat_001
というストリームに紐付けられる。chat_channel.rbclass ChatChannel < ApplicationCable::Channel def subscribed stream_from "chat_001" end endこの状態で以下のコードが実行されると、サブスクライバに
{"text": "Hello, World!"}
というJSON文字列が送られる。broadcastdata = {text: "Hello, World!"} ActionCable.server.broadcast("chat_001", data)ストリームに対するブロードキャスト自体は、Rails アプリケーションのどこからでも実行可能。
複数のStream
前節の例では固定文字列だったので、どのサブスクリプションも同じストリームに紐付いていました。しかし、チャネルをサブスクライブするときサーバー側にパラメータを渡すことができるので、ユーザーごと、もしくは開いているページごとに異なるストリームに紐付けることが可能です。
まとめ
ストリームとサブスクリプションの関係がよく分からなかったのでそこを中心に図示してみました。この図だとストリームが必須みたいに見えてしまうので、action パラメータによってサーバー側のメソッドを実行する、という観点でも整理して別途記事にしてみようと思います。
- 投稿日:2020-05-17T18:01:19+09:00
正規表現でバリデーション設定した項目を空欄のままでも通過できるようにしたい
はじめに
電話番号やメールアドレス等の入力値のフォーマットチェックに正規表現が使える。
指定した文字列、数列以外の値が入力されたらバリデーションチェックに引っ掛かる仕組みだ。
今回は任意入力とした項目を指定された正規表現、または空欄のままでもバリデーションが通過するようにしたい。
電話番号にバリデーションをかけてみよう
今回は電話番号を例にして、まずは電話番号にバリデーションをかけてみる
validates :phone_number, format: { with: /\A\d{10,11}\z/ }これはハイフンなしの10桁または11桁の半角数字で入力してくださいという意味の正規表現だ。
今回は電話番号は任意入力にしたいのでpresence: trueは書いてないし、このままでいいだろうと思ったが…。
問題発生
指定された正規表現でのバリデーションは突破し、次に入力欄を空白にして進んだらバリデーションに引っかかってしまった!
原因
どうやら正規表現でバリデーションをかけるとその正規表現以外の入力をたとえ空欄(nil)でも受け付けなくなってしまうらしい。
解決法その1
メンターに聞いてみるとカスタムメソッドというのを用いて
「空か、あるいは10桁または11桁の半角数字」といったカスタムメソッドを作るという方法があると言われたが、カスタムメソッドについて調べると非常に面倒臭いし大変そうである。
↓カスタムメソッドの参考記事
https://qiita.com/h1kita/items/772b81a1cc066e67930eなので自分で他の方法がないか考えて試行錯誤した結果、非常に簡単な解決法が見つかった。
解決法その2
以下のようにallow_blank: trueを追加するだけである。
validates :phone_number, format: { with: /\A\d{10,11}\z/ }, allow_blank: trueこれは文字通り空欄のままでもOKという意味。
これで電話番号が未入力でもバリデーションが通るようになりました!
まとめ
入力値を正規表現、または空欄でバリデーションを通過したい場合はallow_blank: trueを追加すれば良い。
- 投稿日:2020-05-17T17:51:54+09:00
Rubyのselfってなんだ?
記事の対象
selfって何?
クラスメソッドって?
classやインスタンスはなんとなくわかるこの記事で解説すること
- selfとは
- オブジェクトとは
- いろいろな場所でのself(クラス内・クラス外・インスタンスメソッド内・クラスメソッド内など)
selfの一般的な説明
selfはオブジェクトそのものである
と言われてもピンとこなかったんですよね。
ただ、腑に落ちた今は確かにそのとおりだなと思います。オブジェクトとは
まずオブジェクトについて理解していきましょう。
Rubyは全てがオブジェクトです。
- 文字列オブジェクト
- 数値オブジェクト
- 配列オブジェクト
- ハッシュオブジェクトなど
現実世界で言えば人やモノ、動物などありとあらゆるものをオブジェクトとして扱います。
オブジェクトとインスタンスの違い
オブジェクトとインスタンスの違いは下記サイトでわかりやすく説明されていたので気になる方は読んでみてください。
一言で言うと
クラスから生成されたオブジェクトのことをインスタンスと呼ぶ
です。クラス・インスタンス・オブジェクトの違いは神の気分になるとわかる
インスタンスの生成
クラス名.new
インスタンスの生成class Sample end p Sample.new #=> #<Sample:0x00007fe79b835240>インスタンスが生成できました。
selfについて
ここまではインスタンスやオブジェクトについてでしたが、ついにselfに片足を突っ込みます。
とりあえず適当にrbファイルを作って実行してみましょう。
もしくはターミナルでirbと打った後selfと打ってみましょう。
(Rubyをインストールしていないと使えません。ruby -vと打ってバージョン情報が出ないようならインストールされていないのでインストールしてください。)sample.rbp self #=> mainmainというものが出てきました。
mainとはなんでしょうか?一言で言うと
mainとはトップレベルに存在するインスタンスのことでObjectクラスのインスタンスです。
難しいですね。しっかり解説していきます。まずはObjectクラスからです。
Objectクラス
Objectクラスとはクラスの実質的頂点(親)にいるクラスです。(実際にはもう一つ上のクラスが存在しますが、ここでは割愛します。)
クラスは階層構造になっていて文字列クラスなどの親クラスがObjectクラスになります。
(classメソッドで自身のクラス、superclassメソッドで親クラスを取得することができます。)p "文字列".class #=> String p String.superclass #=> Objectトップレベル
次にトップレベルですが、これはclass定義やmodule定義の外側のことを言います。
(moduleについては本記事では解説しません。制限が加わったclassのようなものと思っておいてください。)トップレベル# ここから # # # # ここまでがトップレベル class Sample # クラス内 end # ここから # # # # ここまでもトップレベル module SampleModule # モジュール内 end # ここから # # # # ここまでもトップレベルそしてターミナルでirbと打った時にいるのもトップレベルです。
ターミナルでirbと打つと
irb(main):001:0>
となります。
ここがトップレベルであり、そしてirbの後にmainとあることが確認できると思います。またトップレベルでselfのクラスを確認してみると
p self #=> main p self.class #=> Objectトップレベルのself(main)のクラスはObjectだということがわかります。
先ほどの発言に戻ると
mainとはトップレベルに存在するインスタンスのことでObjectクラスのインスタンスです。
でした。
これはトップレベルでのpメソッドの実行結果は
p self #=> main
だったこと。
そしてp self.class #=> Object
だったこと
そしてselfとはオブジェクト自身(インスタンス自身)を指すこと。からもわかります。
トップレベルでいきなりpメソッドなどを実行できるのはmainがObjectクラスのインスタンスだからなんですね!
(ObjectクラスにはKernelモジュールがインクルードされているので実行できるというのが正確な答えだがここでは割愛)クラス内のself
次にクラス内のselfをみていきます。
クラス内のselfclass Sample p self #=> Sample endselfはオブジェクト自身のことでした。そのためクラス内ではそのクラスオブジェクト自身を表しています。
インスタンスメソッド内のself
インスタンスメソッド内のselfはインスタンス自身(オブジェクト自身)を表しています。
インスタンスメソッド内のselfclass Sample def hoge p self end end sample = Sample.new sample.hoge #=> #<Sample:0x00007f8524034f70>ちなみにRubyはメソッドを呼び出す時、暗黙的にselfに対してメソッドを呼び出しています。(勝手にselfに対してメソッドを呼び出す。)
どういうことかというと
class Person def greeting p self hello end def hello p "hello" end end person = Person.new person.greeting #=> #<Person:0x00007f9937848a28> #=> "hello"これと同じものが以下になります。
class Person def greeting p self self.hello end def hello p "hello" end end person = Person.new person.greeting #=> #<Person:0x00007f9937848a28> #=> "hello"どこが違うかお気づきでしょうか?
そうです。greetingメソッド内のhelloの呼び出しの前にself.がついています。
実はこのself.は付けても付けなくても同じ動きになるのです。
メソッドの呼び出しのたびにselfをつけるのは大変なのでこの仕様になっているのだと思います。この時のgreetingメソッド内のselfはPersonクラスのインスタンス自身ですのでそのインスタンスに対してhelloメソッドが呼ばれているんですね。
(greetingメソッド内のself.hello
とトップレベルのperson.hello
は同じ結果になる。)クラスメソッドのself
クラスメソッド内のselfもオブジェクト自身を表しています。
クラスメソッド内のselfclass Person def self.greeting p self end end Person.greeting #=> PersonPersonが返されています。
もちろんこのメソッド定義部分のselfもオブジェクト自身を表しているのですがメソッドの定義場所にあるselfってちょっと不思議な感じですよね。
def self.greeting # ← このself endクラスメソッドとは特異メソッドの一つです。
では特異メソッドとは何か?特異メソッド
特異メソッドはクラスとは関係なく個々のオブジェクト(インスタンス)に対してメソッドを定義します。
morning = "good morning" def morning.greeting p self end morning.greeting #=> "good morning"これは何が起こっているのでしょうか?
まずmorning = "good morning"
でmorningという変数(オブジェクト)に"good morning"という文字列を代入しています。
その後morningオブジェクトに対してgreetingメソッドを定義しています。
greetingメソッド内ではpメソッドでselfを返しています。
この場合のselfは"good morning"という文字列オブジェクトなので
"good mornig"という文字列が出力されます。この形が特異メソッドです。
クラスメソッド定義方法と同じですね。
クラスメソッド定義class Person def self.greeting end end上のコードと見比べるとdef 〇〇.メソッド名のように定義方法が一緒ですね。
そしてこの時のselfはPersonだったのでクラスメソッド定義class Person def Person.greeting end endこのようにも書けます。
最後に
いろいろなところでselfを使ってみました。
selfがオブジェクト自信を表すということが理解できれば幸いです。やはり自分でコードを動かして検証していくと理解が高まりますね。
何かお気づきのことがありましたらコメント欄で教えていただけると助かります。参考
- 投稿日:2020-05-17T17:43:17+09:00
Rubyでブラックジャック作ってみた(minitest使ってみた)
Rubyでブラックジャック作ってみた(minitest使ってみた)
ファイル構成とコードを書いた時に、考えていたこと
card.rb(カードクラス)
- 1枚ごとのカードデータの書き換えができないように(データが壊れないように)、attr_readerにを使った。
deck.rb(デッキクラス)
- インスタンスを生成した段階で、デッキがシャッフルされているようにし、それをテストした。
- each文を使って、デッキの生成をなるべく簡潔に書いた。
- クラス変数を使い、カードをドローしたときに、今、山札の何枚目がドローされているかカウントするようにした。
player.rb(プレイヤークラス)
- プレイヤーとディーラーで共通していて、同じデッキを使う為に、クラス変数にデッキのインスタンスを代入して、デッキを使用した。
judge.rb(ジャッジクラス)
- 判定するためのクラス、わかりやすくするために、あえてプレイヤーと別でモジュールを使用した。
test.rb(動作確認テスト)
- minitestは「プロを目指す人のためのRuby入門」で、書いて以来、使ったことがなかったので、理解を深める為に使ってみた。
main.rb(ブラックジャックを動かすメインファイル)
- わかりやすいようにゲームの全体の流れのみを最初に書いた
- そこから必要なクラスとメソッドを考えて、付け足していった。
card.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' 10 elsif @number == 'A' 1 else @number.to_i end end enddeck.rb (デッキクラス)
deck.rbrequire './card' class 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 = ['❤︎', '♦︎', '♤', '♧'] 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 endplayer.rb (プレイヤークラス)
player.rbrequire './deck' require './judge' class Player include Judge attr_accessor :hands, :total_score, :name @@deck = Deck.new def initialize(name: "プレイヤー") @hands = [] @total_score = 0 @name = name end def draw self.hands << @@deck.draw self.total_score += @@deck.draw.convert @@deck.draw_count end def show puts "#{self.name}の手札です" self.hands.each do |hand| puts "[ #{hand.mark} #{hand.number} ]" end puts "[ トータルスコア #{@total_score} ]" end # テスト用メソッド def test puts "テストーーーーーー" end def total_score_calc self.hands.each do |hand| @total_score += hand.convert end #puts "トータル「#{@total_score}」" end def reset self.hands = [] self.total_score = 0 end def in_blackjack self.hands << Card.new("♦︎", "J") self.hands << Card.new("♦︎", "A") end endjudge.rb(ジャッジクラス)
judge.rbmodule Judge # def game_judge(target) # if self.total_score > target.total_score # puts "あなたのトータルスコア[ #{self.total_score} ]" # puts "ディーラーのトータルスコア[ #{target.total_score} ]" # puts 'あなたの勝ちです' # elsif self.total_score == target.total_score # puts "あなたのトータルスコア[ #{self.total_score} ]" # puts "ディーラーのトータルスコア[ #{target.total_score} ]" # puts '引き分けです' # else # puts "あなたのトータルスコア[ #{self.total_score} ]" # puts "ディーラーのトータルスコア[ #{target.total_score} ]" # puts 'あなたの負けです' # 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? if self.total_score > 21 true else false end end endtest.rb(動作確認テスト)
test.rbrequire 'minitest/autorun' 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 # 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 1, @@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 endmain.rb(ブラックジャックを動かすメインファイル)
main.rbrequire './player' lose_message =<<~TEXT あなたの負けです TEXT win_message =<<~TEXT あなたの勝ちです TEXT game_draw_message =<<~TEXT 引き分けです TEXT error_message = "無効な文字です。もう一度入力してください" class Play attr_accessor :player, :dealer def initialize @player = Player.new @dealer = Player.new(name: "ディーラー") end def test @player.draw @player.draw @player.show end end while true # 再プレイのためのループ処理 # 初ターンの処理 player = Player.new dealer = Player.new(name: "ディーラー") puts "ようこそ、ブラックジャックへ" puts "カードを配ります" player.draw player.draw dealer.draw dealer.show # ブラックジャックテスト # dealer.reset # dealer.in_blackjac # dealer.show # player.reset # player.in_blackjack # player.show # プレイヤーがブラックジャックじゃないか確認の処理 if player.blackjack? puts "ブラックジャック!" player.show else player.show puts h_or_s_message =<<~TEXT 選択してください Push「h」, ヒット Push「s」, スタンド TEXT end # ヒット、スタンドの処理(プレイヤーのターン) while true # ブラックジャックの場合は強制的にスタンドさせる処理 if player.blackjack? command = 's' else command = gets.chomp # ヒットかスタンドか選択 end # ヒットの場合の処理 if command == "h" while true player.draw if player.burst? # バーストしていないか確認 player.show puts "バースト!!" puts lose_message break end # ドロー後のトータルスコアの確認 player.show # 最大値(21)の場合は強制的にスタンドの処理 if player.total_score == 21 puts 'トータルスコアが最大値に達しました' command = 's' break end # 最大値ではない場合は、再度ヒットするか処理 puts "もう一度引きますか?" puts h_or_s_message while true command = gets.chomp # 再度、ヒットかスタンドの選択 if command == 'h' break elsif command == 's' break else puts error_message redo end end # ヒットかスタンドの処理 if command == 'h' redo elsif command == 's' break end end # バースト時の強制終了処理 if player.burst? break end else # h,s以外が入力されたときの処理 puts error_message redo end # スタンド時の処理(ディーラーのターン) if command == 's' # プレイヤーがブラックジャックだった場合のディーラーの処理 if player.blackjack? dealer.draw dealer.show if !dealer.blackjack? puts win_message break else puts game_draw_message break end end # プレイヤーに勝つまでディーラーはヒットし続ける処理 while player.total_score > dealer.total_score dealer.draw dealer.show if dealer.burst? puts 'バースト!' puts win_message break elsif dealer.total_score == 21 && player.total_score == 21 break end end if dealer.burst? # バーストしていた場合処理処理終了 break elsif dealer.total_score == 21 && player.total_score == 21 # お互い最大値に達していないか確認 puts game_draw_message break else puts lose_message break end end end # 再プレイの処理 puts <<~TEXT もう一回遊びますか? 選択してください Push「h」, はい Push「s」, いいえ TEXT while true command = gets.chomp # 再プレイするか選択 if command == "h" command = nil break elsif command == "s" break else puts error_message redo end end # 再プレイの処理 if command == nil redo else break end end学べたこと
「プロを目指す人のためのRuby入門」で
「なぜこんなことをするんだろう」、
「いつこれは使うのだろう」、
と、思ってたことが、使うタイミングがよくわかった。最初はminitestは使わず「p」を使ってテストしていたが、
使った方が作業が早くなるということを理解した。minitestのメソッド名の最初の単語に「test」を入れないと、
テストしてくれないことをすっかり忘れてて、2日くらい悩んだけど笑使ったことないものは積極的に使っていくと理解が深まる。
まだリファクタリングできるところは、たくさんあるんだけど、
作るの飽きそうになったので、一旦、Qiita書きました。
- 投稿日:2020-05-17T17:38:46+09:00
巨大なファイル(100万行とかでも)を1行1行処理するrubyスクリプトのテンプレートを作った
私が巨大ファイルを作るときにテンプレートをコピーして使います。
メリット
- いちいちCSVやファイルの読み書きをするときにコマンドを調べなくていい
- プログレスが出る
- 大抵巨大ファイルを読む場合10分くらいかかるけど、進捗が見えなくて進んでるかわからないため表示
スクリプトテンプレート
以下のスクリプトはCSVを読む場合のスクリプトです
require "csv" $is_debug = true def main(csv) #, output_file) # output_file_writer = CSV.open(output_file, "w") # output_cols = ["hoge"] # output_file_writer.puts(output_cols) FileHandler.csv_foreach(csv) do |row| # 処理 p row # output_row_values = [] # output_file_writer.puts(output_row_values) end # puts "#{output_file}を作成しました" # output_file_writer.close end module FileHandler class << self def csv_foreach(csv) log "#{Time.now}: read start #{csv}" all_line_count = line_count(csv) return_values = CSV.foreach(csv, headers: true).with_index(1).map do |row, row_no| log progress(row_no, all_line_count) if progress_timing?(all_line_count, row_no) yield(row) end log "#{Time.now}: read end #{csv}" return_values end def line_count(file) open(file){|f| while f.gets; end f.lineno } end private def log(message) puts(message) if $is_debug end def progress_timing?(all_line_count, line_no) return false if all_line_count < 100 # NOTE 処理時間によって変更 div_number = 100 percent_unit = all_line_count / div_number line_no % percent_unit == 0 end def progress(current_count, all_count) "#{Time.now}: #{CommonUtilities.percent(current_count, all_count)}% (#{CommonUtilities.number_with_commma(current_count)} / #{CommonUtilities.number_with_commma(all_count)})" end end end module CommonUtilities class << self def percent(num, all_count) (num.fdiv(all_count) * 100).round(2) end def number_with_commma(number) number.to_s.gsub(/(\d)(?=\d{3}+$)/, '\\1,') end end end main(ARGV[0])
- 投稿日:2020-05-17T17:31:21+09:00
【開発ログ⑭】社員を削除したら有休データも削除したい
前提について
はじめまして、 プログラミングスクールに通ういりふねと申します。この記事は、スクールの課題である個人アプリの開発の記録を書くことで、自身のアウトプットに利用しています。もし、読んでいただけた方がいましたら、フィードバックをしていただけたら嬉しいです。
開発するのは「有給休暇管理ツール」です。仕様は過去記事をどうぞ。
アプリはデプロイまで行いますが、サービスとして提供するものではありません。あくまでも自学習の一環ですので、ご理解下さい。では本題へどうぞ。
今回の実施内容
前回までで有休登録の機能が実装できたので、今回は社員と支社の削除機能を実装します。具体的には以下の通りです。
- 社員削除の問題点
- 社員削除の制約の設定
- 支社の削除画面の検討
- ビューで確認
社員削除の問題点
今回のアプリケーションでは、4つのテーブルを用意しています。社員は、employeeテーブルに保存していますが、外部キーとして支社のbranch_idを持っています。社員にとっては親テーブルは支社になるわけです。さらに社員は、有休登録されたデータを複数持ちます。これは、holidayテーブルに保管され、holidayレコードは、外部キーとして社員のemployee_idを持ちます。
親子関係を表すと以下のとおりです。
親:支社のbranchテーブル
子:社員のemployeeテーブル
孫:有休のholidayテーブルここで、親や子(つまり支社や社員)を削除すると、その下のテーブルには外部キーに対応する親レコードがなくなってしまいエラーになってしまいます。
そこで、社員を削除したら、社員の持つ有休データもまとめて消せるように実装していきます。社員削除の制約の設定
実装は非常に簡単で、モデルファイルにdependent制約を記述するだけです。
参考にさせていただいた記事「[Rails] has_manyおよびbelongs_toへのdependent: :destroyの設定について」@after4649様employee.rbclass Employee < ApplicationRecord 〜中略〜 has_many :holidays, dependent: :destroy 〜中略〜 endアソシエーションを記述しているhas_manyの後ろに文章を追記するだけで、コントローラーなどの設定は変更せずにそのまま使用することができました。
ただし、記事にもありますが、belongs_toの方につけると大変なことになるので、注意が必要です。それからこのdependent制約には:destroy以外にも記述方法があるようなので、ご自身にあったものを使用して下さい。支社の削除画面の検討
社員の設定は、これで良いとして支社を削除する際に同じように社員とその有休のデータをまとめて削除してよいか悩みましたが、同じようにdependent制約をかけることにしました。
そのかわり、支店削除は簡単に行えないよいうにステップに分けるようにしました。
まず、削除ボタンがindexに表示されないように「編集/削除」のボタンを設定し、編集画面を表示させます。編集画面内に「支社削除」がありますが、大きめの文字で注意を促します。
ここについては、私の技術がさらに向上したときに別の方法を検討しようと思います。今は、これが精一杯汗ビューで確認
ちょっと回り道をしましたが、ビューで確認します。座布団運びの山田くんは、5日有休を付与されていますが、このたび解雇となります。
社員の削除は表の右端(これも簡単に押せないよう配慮しているつもり)にあります。クリックすると見事に削除されました。
付与及び消化の履歴にも山田くんの履歴は残っていません。無事に成功しました。
ちなみに可愛そうだったので、この後山田くんは再登録してあげることにしました。今日の積み上げ
dependent制約は、実はカリキュラムで触れられていました。まだまだ知識が十分定着していないのかも知れません。今後もどんどんアウトプットをして学んだことを定着させたいです。
- 投稿日:2020-05-17T17:12:55+09:00
YouTube Data APIを使わずにチャンネルの最新ライブ配信IDを取得する方法
バックグランド
最近の個人アプリ開発中に出会った問題です。
YouTubeのライブ配信が日々活発している今、チャンネルIDを持ちながらそのチャンネルの最新ライブ配信の情報を取得したい場合がよくあります。しかし、YouTube Data APIを使って最新ライブ配信情報を取得しようとすると以下二つの問題があります。
- チャンネル情報丸ごと取得する
チャンネル(channels)
リソースからチャンネル最新動画取得しようとしてもライブ配信情報(ライブ配信中または公開予定の情報)が含まれない。(現在2020年5月時点)- チャンネルIDを持って
search
リソースを使うと、どうやら書き込み操作と同様に扱われるので他の情報取得操作リスト(list)
の何十倍のクォータコスト(https://developers.google.com/youtube/v3/getting-started?hl=ja) が消費されます。ライブ配信情報の有無を頻繁に取得・更新しないと意味ないので、
search
リソースを使うと一日使えるクォータコストの割り当てはすぐなくなります。一方、ライブ配信IDであるvideoId
を取得する手段があれば、videoId
を指定して動画(videos)
リソースから低いクォータコストでライブ配信の情報をアクセスできます。
そのため、YouTube Data APIを使わずにチャンネルの最新ライブ配信IDであるvideoId
を取得することが必要とされています。方法(Ruby)
あまり使われていないかもしれませんが、チャンネルにライブ配信情報があれば下記のurlを使ってその最新のライブ配信playerをページに埋め込むことができます。
url = "https://www.youtube.com/embed/live_stream?channel=<チャンネルID>"最新のライブ配信を表示してくれれば話がしやすくなります。次のやることが
そのページを裸にして必要な情報videoId
を取り出すことです。content = Net::HTTP.get_response(URI.parse(url)).entity unless content.match(/watch\?.+/) == nil match = content.match(/watch\?.+/)[0] videoId = match.sub("watch?v=","").sub("\">","") endYouTubeのページやurlには基本的に
watch?v=<videoId>
のような形に書かれているので、取得したcontent
からmatch
とsub
メソッド使えばvideoId
を簡単に入手できます。(そこにちゃんと整備したYouTubeさんに感謝)
videoId
を入手した以上、YouTube Data APIの動画(videos)
リソースを使えば配信状態やら配信時間やらの取得はもう何ても来い!参考(PHPでのやり方)
https://stackoverflow.com/questions/58040154/how-to-get-live-video-id-from-from-youtube-channel-html
- 投稿日:2020-05-17T16:56:56+09:00
AtCoder Beginners SelectionでRuby学習【Coins】短いコードで解答する
はじめに
Ruby学習の一環として「競技プログラミング(競プロ)」に挑戦します。
そのための学習の中で学んだことをアウトプットしていきます。
今回は「AtCoder Beginners Selection」の五問目(Coins)より。
https://atcoder.jp/contests/abs問題
500円がA枚、100円がB枚、50円がC枚持っています。
これらの硬貨の中から何枚かを選び、合計金額をX円にする方法は何通りあるか。
同じ種類の硬貨同士は区別出来ません。制約
0 ≤ A,B,C ≤ 50
A + B + C ≥ 1
50 ≤ X ≤ 20,000
A,B,Cは整数である。
Xは50の倍数である。入力は以下の形で与えられる。
A B C X # 例 2 2 2 100出力例 # 上記例の場合 => 2条件を満たす選び方は以下の2通り。
500円を0枚、100円を1枚、50円を0枚
500円を0枚、100円を0枚、50円を2枚解答
まずは僕が最初に書いたコードです。
a = gets.to_i b = gets.to_i c = gets.to_i x = gets.to_i count = 0 a_array = [] b_array = [] c_array = [] for i in 0..a do a_array.push(500 * i) end for i in 0..b do b_array.push(100 * i) end for i in 0..c do c_array.push(50 * i) end all_array = a_array.product(b_array, c_array) all_array.each do |one_array| if one_array.inject(:+) == x count += 1 end end print count① 硬貨の種類ごとに、0枚からn枚まで、選べる枚数に応じた金額を持つ配列を生成する(for~do~end)。
※100円を3枚持っているなら、[0,2,4,6]の配列が生成されるように。
② 生成された3つの配列から1つずつの要素を取り出し、新たに配列を生成。これを全パターン用意(productメソッド)
③ ②で用意した配列1つ1つに対して、その要素の合計がX(を50で割ったもの)と等しくなるパターンの数を記録する (count)。上記のような流れで解答しました。
この解答をする中で学んだメソッドを以下にまとめていきます。for文
for 変数 in オブジェクト do 実行する処理 endオブジェクトに繰り返す範囲を指定します。
解答では、0から持っている硬貨の枚数まで、0枚、1枚、2枚と順に取り出し、変数に入れるイメージです。
その上でブロック内に指定する処理を繰り返す、といった具合です。
解答では、直前に生成した配列に、枚数に応じた金額を順に入れていっています。pushメソッド
配列オブジェクト.push(要素, …)pushメソッドは、配列の末尾に指定した要素を追加します。
productメソッド
#例 [1,2].product([3,4],[5,6]) => [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]複数の配列が持つ全要素の組み合わせを配列にして返します。
eachメソッド
配列オブジェクト.each do |変数| 実行する処理 end配列から要素をひとつずつ取り出し、変数に代入した上で、
要素の数だけ指定した処理を実行します。injectメソッド
配列に対して、要素の合計を算出して返します。
今回はシンボル(:+)を用いた記法を使っています。#例 array = [1, 2, 3] result = array.inject(:+) print result => 6改めて調べていて知りましたが、sumメソッドを使うともっと簡単に書けるようです。
#例 array = [1, 2, 3] result = array.sum print result => 6短いコードで解答する
ここからは今回の解答を短くしていきます。
縦に並んだ複数の整数入力を受け取る
A, B, C, x = 4.times.map{gets.to_i}timesメソッド
指定の回数処理を繰り返す場合に使います。
#例 3.times { puts "Hello, World!" } =>Hello, World! =>Hello, World! =>Hello, World!配列を生成せず、入力された整数を直接判定に使う
解答では配列を生成して、全てのパターンの金額を追加する、ということを行っていましたが、
これによって解答がえらく長いことになってしまっていました。
入力した整数をそのままeach文を使って判定に使用することでコードの長さを抑えられました。(0..A).each{|a| (0..B).each{|b| (0..C).each{|c| x == (500*a + 100*b + 50*c) ? count +=1 : count += 0 } } }完成
A, B, C, x = 4.times.map{gets.to_i} count = 0 (0..A).each{|a| (0..B).each{|b| (0..C).each{|c| x == (500*a + 100*b + 50*c) ? count +=1 : count += 0 } } } print count空白を入れて32行あったコードが9行になりました。
最後に
以上、AtCoder Beginners SelectionでRuby学習【Coins】から学んだメソッド・記法をご紹介しました。
今回のように短いコードで解答する練習は常にやっていきたいです。
他の方の解答を見てみると1行で解答をされている方もいました。
そこまでいくには道のりは長いですが、その分やりがいもありますね!もし間違いなどございましたら、ご指摘いただけると嬉しいです。
- 投稿日:2020-05-17T16:54:33+09:00
【開発ログ⑬】formに0は入れさせない!!
前提について
はじめまして、 プログラミングスクールに通ういりふねと申します。この記事は、スクールの課題である個人アプリの開発の記録を書くことで、自身のアウトプットに利用しています。もし、読んでいただけた方がいましたら、フィードバックをしていただけたら嬉しいです。
開発するのは「有給休暇管理ツール」です。仕様は過去記事をどうぞ。
アプリはデプロイまで行いますが、サービスとして提供するものではありません。あくまでも自学習の一環ですので、ご理解下さい。では本題へどうぞ。
今回のアプリのフォームについて
記事にはしていませんが、有休の付与や消化を登録するフォームとそれらを表示するログを作成しました。以下は写真です。
こちらは、フォームです。たい平師匠がよくわからない理由で休もうとしていますが、今回は見逃します。
次に有休の付与や消化の履歴を確認するビューです。社員の消化理由を確認したいとか、有休登録を行ったけど間違って登録していないか確認したいなど、入力する人にとって必要であろうと思い設置しています。有休登録の削除もできます。
この消化の履歴は、右下に「付与か」「消化か」「付与と消化」が表示されるようにメソッドを設定していますが、ここの条件が今回のテーマに関連します。以下はメソッド部分です。
holiday.rbclass Holiday < ApplicationRecord 〜中略〜 def add_or_delete if add_day.nil? "#{delete_day}日消化" elsif delete_day.nil? "#{add_day}日付与" else "#{add_day}日付与と#{delete_day}日消化" end end endadd_day(付与日数)やdelete_day(消化日数)がnil(空)かどうかを条件に設定していますが、そこで問題になるのが、「0」という存在です。
0もデータでnilとは違う
例えば、有休登録時に付与を0、消化を1と登録すると、履歴には「0日付与と1日消化」と表示されてしまって、非常に不格好になりました。これを避けるためにモデルファイルに定義しているメソッドの条件を「nilまたは0ではない」と変更してもよいのですが、この後、実装する機能にいちいちこの条件を設定するのは面倒。ということでフォーム入力時点で「0」が登録されないようにしたいのです。
今回の実施内容
毎度のことながら前置きが長くなりましたが、ここから真の本題です。付与日や消化日のカラムには、nilか1以上の数字しか入らないようにいくつか改良します。具体的には以下のことを行いました。全部必要だったかどうかは、わかりません。
- バリデーション「exclusion」
- フォームの入力制限
- リセットボタンの設置
バリデーション
0を保存させないと思って真っ先に検索したのが、バリデーションでした。調べてみると「exclusion」という、特定の文字を保存させないバリデーションがありました。
参考にさせていただいた記事【Rails】完全解説!Railsのバリデーションの使い方をマスターしよう!
記事を元に付与日と消化日に「0」という文字が入らないようにバリデーションをかけてみました。
holiday.rbclass Holiday < ApplicationRecord validates :reason, presence: true validates :add_day, exclusion: { in: [0]} validates :delete_day, exclusion: { in: [0]} 〜中略〜 end0と入れて試してみましたが、どうやら保存されていないようです。念のため、10日付与なども行いましたが、こちらはちゃんと保存できました。
フォームの入力制限
しかし、保存されずにindexに戻ってきてしまうため、利用者の方にとっては、なぜ保存されなかったのかが分かりにくくなってしまいます。例えば、付与日を2、消化日を0と打ったが、送信前に間違えに気づき、付与日を0、消化日を2に修正して送信ボタンを押したのに保存されていないなどです。
そこで、フォームそのものにも1以下の数字は入れられないように制限をかけていきます。holidays/new.html.haml〜中略〜 .field .field-label = f.label "消化日数" .field-input = f.number_field :delete_day, max: "50", min: "1" .field .field-label = f.label "付与日数" .field-input = f.number_field :add_day, max: "50", min: "1" 〜以下省略〜number_fieldに最大50、最小1を追記しました。これによりフォームの上下のボタンでは1~50までしか選択できないようになりました。万が一、手入力で、0を入れて送信しようとしたときには、このフォームの制限によりエラー文が出てくるようになります。
リセットボタンの設置
これで、0を保存されず、かつフォーム上でも0が送信できないように設定できましたが、念のためフォームのリセットボタンも設置します。わざわざ手入力で0を入れる方は少ないと思ったので、何かの入力ミスで0になってしまったけれど、フォームについている上下ボタンでは、変更できない!!という事態を防ぐためです。
holidays/new.html.haml〜中略〜 .btns = f.button "リセット", type: :reset, class: 'btn' = f.button "登録", type: :submit, class: 'btn'最初、「f.reset...」とsubmitと同じ要領で記入していたのですが、うまく行かなかったので、再び調べて、下の記事で解決しました。
参考にさせていただいた記事「Railsでリセットボタンの実装方法」ビューの確認
最終的なビューのスクリーンショットを取り忘れていたので、CSSが付与された後の状態になりますが、以下のように完成しました。
今日の積み上げ
今まで、派手な機能にばかり目を向けていたので、こういった機能も大切であると学びました。あからさまなエラーが出ない分、開発者として細やかな気配りが求められるのではないかと思います。
一方で、こういったバリデーションは、必要であれば、どんどん付けられるのかも知れません。必要十分な量にとどめておかないと作業量が増えすぎるとも感じました。
- 投稿日:2020-05-17T16:51:08+09:00
容量がいっぱいなときに使うduコマンドが使いにくいから、rubyでラッピングしてみた
結論
duコマンドをrubyでラッピングしたスクリプトを作りました。
expand.rb
という名前を作って、下のスクリプトを貼って、以下のように実行します。# ruby expand.rb (調べたいパス) $ ruby expand_du.rb /var/lib/ 7GB /var/lib/ 3GB /var/lib/jenkins 2GB /var/lib/docker 1GB /var/lib/mysql ... 4KB /var/lib/mysql-files du_-k_--max-depth_1_var_lib__20200516231438.logに保存しましたスクリプト(github)
# 使い方 # ruby expand_du.rb パス # 例:ruby expand_du.rb ~ # # 効果 # - パスに対してduコマンドを実行 # - 実行結果を標準出力とファイルに吐き出す # # 引数 # - パス: duコマンドを行う対象パス require 'rbconfig' def main(target_path) return puts "ArgumentError: パスを引数に入れてください" if target_path.nil? max_depth_option_str = if os == :macosx "-d" else "--max-depth" end exec_command = "du -k #{max_depth_option_str} 1 #{target_path}" du_result_str = `#{exec_command}` return if du_result_str.empty? output_disksizes(du_result_str, exec_command) end def output_disksizes(du_result_str, exec_command) disk_usages = du_result_str .split("\n") .map{|du_result| DiskUsage.new(du_result)} .sort{|x, y| x.size <=> y.size}.reverse output_filename = "#{exec_command.gsub(/( |\/){1,}/, "_")}_#{Time.new.strftime("%Y%m%d%H%M%S")}.log" output_file = File.open(output_filename, "w") disk_usages.each do |disk_usage| puts disk_usage.to_s output_file.puts(disk_usage.to_s) end output_file.close puts "#{output_filename}に保存しました" end class DiskUsage attr_reader :size, :path def initialize(du_result_line) du_result_params = du_result_line.split(" ").map(&:strip) @size = du_result_params[0].to_i @humanreadable_size, @humanreadable_unit = calc_humanreadable_size @path = du_result_params[1] end def to_s # NOTE とりあえず5桁を指定。必要になったら増やす "#{sprintf("%5d" % @humanreadable_size)}#{@humanreadable_unit} #{@path}" end def humanreadable_size_with_unit "#{@humanreadable_size}#{@humanreadable_unit}" end private def calc_humanreadable_size return [@size, "KB"] if mb_size < 1 return [mb_size, "MB"] if gb_size < 1 return [gb_size, "GB"] if tb_size < 1 [tb_size, "TB"] end def kb_size @size end def mb_size kb_size.fdiv(1024).round(1) end def gb_size mb_size.fdiv(1024).round(1) end def tb_size gb_size.fdiv(1024).round(1) end end def os case RbConfig::CONFIG['host_os'] when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ :windows when /darwin|mac os/ :macosx when /linux/ :linux when /solaris|bsd/ :unix else :unknown end end経緯
レンタルサーバやMacの容量がいっぱいになったときに、どのフォルダが原因なのか知りたくなります。
そこでMacならストレージ使用状況を見たり、レンタルサーバでもdfコマンドとかで全体のどれくらいを使っているかはわかります。しかし全体がわかるだけでどのフォルダが原因かわかりません。
そこでターミナルを使える人が使えるのはduコマンドです(lsコマンドも候補にあがるんですが、lsコマンドは直下のファイルの大きさはわかるんですが、フォルダがどれだけ大きいかはわかりません)。
しかしこのduコマンドなんですが、UNIXコマンドであるせいか、ちょっといまいちなんですよね。
ソートがいまいち
読みやすくするのに -h オプションをつけて、sortコマンドを使ってサイズを降順で出すんですが、そうすると5KBが4GBよりも上に来るんですよね。数字だけ見ているので仕方ないんですが。
記録するのに2回打つのが面倒くさい
duを使うと標準出力に結果を出してくれるけれど、出したあと結局結果を記録したくなります。最初からファイル出力すればいいんですが、ファイル出力してからcatするのが面倒なんですよね。工夫してコマンド打てばファイルにも標準出力にも出してくれるんですが、duコマンド必要なときって大抵急いでいるので、楽にやりたかったんです。
スクリプトの特徴
↑の問題を解決したスクリプトが最初に載せたスクリプトです。
特徴は次のようになります。
- 単位を考えた上でソートしてくれる
- 標準出力にもファイルにも出してくれる(&どのフォルダを調べたかわかるファイル名で出力してくれる)
完全に自分用に作ったんですが、同じことに困っている方がいらっしゃったら使ってみてください。
- 投稿日:2020-05-17T16:42:32+09:00
Ruby on rails を初心者向けに解説①
はじめに
今回はRuby on railsの解説記事です。
初心者向けに、わかりやすく解説していきます。
頑張っていきましょう。
今回は、記事を書く上で
たかし君
に協力してもらいます。Ruby on rails とは
Ruby on rails とは、Webフレームワークの一種です。
最初に、そもそもフレームワークとは一体何なのかについて解説します
フレームワークとは
フレームとは、簡単に言えば
骨組み
のことです。フレームワークの意味をwikipedia先生に聞いてみましょう。
プログラミングにおいて、一般的な機能をもつ共通コードをユーザーが選択的に上書きしたり特化させたりすることで、ある特定の機能をもたせようとする抽象概念のことである。
相変わらず堅い言葉なので、噛み砕いて解説します。
フレームワークとは、
様々な機能を提供してくれる骨組み
のことであると考えて問題ないと思います。今度は、フレームワークの具体例をみて考えていきましょう。
Bootstrap
何かWebサイトを作成しようとしたときに、コツコツHTMLとCSSを書いていくのは大変ですよね。
当然、何とか楽をしたいと考えます。どうすれば楽にできるでしょうか。
そうです、誰かが作ってくれたものをパクれば楽ができるのです。
しかし、Webサイトをそのままパクれば怒られてしまうし、作れるバリエーションも限られてしまいます。
ここでたかし君は考えました。
たかし君「そういえば、Webサイトに使われる部品って限られてるよな。ヘッダー部分とかボタン部分とかを個別に楽にかける
枠組み
があれば楽に書けるんじゃね?」こうして生まれたのが
Bootstrap
です。BootstrapはCSSのフレームワークで、Webサイトの各々の部品ごとの枠組みを提供してくれています。
Webフレームワークとは
ここまでの説明でなんとなく分かりましたね。
Webフレームワークとは、
Webアプリを作るための枠組み
のことです。もう一度たかし君に聞いてみましょう。
たかし君「Webアプリ作るのめっちゃだるいな。でも、Webアプリで使う機能って以外に限られてるよな。ユーザーが打ち込んだURLに応じてデータベースを操作したり、HTMLファイルを作成して送ったり、ログイン機能が必要だったりするだけじゃん。この
枠組み
を誰かが作ってくれて、それをパクれば楽に書けるんじゃね?」このようにして生まれたのが、Webフレームワークだったり、Ruby on railsだったりするわけです。
それでは、実際にRuby on railsでアプリを作ってみましょう。
骨組みの作成 (rails new)
最初にローカルのパソコンにRubyとRuby on railsをインストールしてください。
環境構築については、色々な人が記事を書いているので割愛します。
環境構築はうまく行かないことがほとんどですが、できるまで粘り強く調べ続けてください。応援しています。
最初に、railsアプリを作りたいディレクトリに移動して、以下のコマンドを打ちましょう。
rails new qiita_project
new
の後ろは何でも好きな名前にしてください。以下のようファイルが作成されます。
これで、Webアプリを作る際の
枠組み
を作成することができました。サーバーの起動(rails server)
作成した
qiita_project
に移動してください。cd qiita_projectこれで作成した
qiita_project
に移動することができました。次はサーバーを起動してみましょう。次のコマンドです。
rails server次に、chromeなどのブラウザのURL欄に以下のURLを入力してください。
http://localhost:3000/ここまでで、サーバーを起動することができました。
コントローラーの作成 (rails generate controller コントローラー名)
以下のコードでコントローラーを作成することができます。
コントローラーが何を行うかは後に解説します。
rails generate controller home上のコードを実行すると、app>>controllers配下に
home_controller.rb
という名前のファイルが作成されます。home_controllerの中身を見てみましょう。
home_controller.rbclass HomeController < ApplicationController endこのようにして、コントローラーを作成することができました。
それでは、コントローラーの解説行います。
コントローラーとは
コントローラーを理解するためには、まず以下の図を理解する必要があります。railが動作する仕組みについてです。
順番に見ていきましょう。
①ユーザーがサーバーにリクエストを送る
まず、左のユーザーがサーバーにリクエストを送ります。右の様々な動作は、全てサーバー側の話です。
リクエスト送る
とは、サーバーに何かを要求することです。例えば、URLを入力すると、「このURLに対応するファイルを送ってこい!」と要求(getリクエスト)することになりますし、IDとパスワードを入れてログインしようとすれば、「送ったデータを使って何かしらの処理をしろ!」と要求(postリクエスト)することになります。②ルーティングによりどのコントローラーを使うのかを決定
ルーティングにより、ユーザーから受け取ったURLを処理します。
例えば、ユーザーから
home/top
というURLを受け取ったとします。そうすると、ルーティングは
home#top
というルーティングに変換します。これは、「homeコントローラーのtopアクションを利用します」という意味です。アクションというのは、コントローラーごとに設定されている「何かを行うコード」だと考えてください。
今回は、簡単のためにユーザーからは「このURLに対応するファイルを送ってこい!」と要求(getリクエスト)されたと考えます。
ユーザーから"home/top"というURLが送られてきて、それをhomeコントローラーのtopアクションを実行するという命令にルーティングしましょう。
configディレクトリ配下のroutes.rbファイルに以下のように追記してください。
routes.rbRails.application.routes.draw do get "home/top" => "home#top" endコントローラーによりviewファイルを探索
home/top
というURLがユーザーから送られてきて、ルーティングによりhome#top
に変換されたとします。この場合、homeコントローラーのtopアクションを行うことになります。
以下のように、homeコントローラーにtopアクションを追加しましょう。
home_controller.rbclass HomeController < ApplicationController def top end endこのようにコードを書くと、homeコントローラーは
home/top.html.erb
ファイルを探して、それをユーザーに返してくれます。viewファイルの作成
app >> views 配下のhomeディレクトリにtop.html.erbファイルを追加してください。
top.html.erbファイルを以下のように編集してください。
top.html.erb<h1>Hello World</h1>このようにして、viewファイルを作成することができました。
実際にレスポンスを見てみましょう。
ユーザーがURLを打ち込んだ場合
再び、以下のコードでサーバーを起動しましょう。
rails serverwebブラウザで以下のURLを打ち込んでください。
http://localhost:3000/home/topそうすると、以下の画面が送られてきます。
何が起きているのかを、もう一度ざっくり解説します。
http://localhost:3000/home/topというURLが送られると、それがルーティングにより`home#top`という形に変換されます。
それにより、homeコントローラーのtopアクションが行われます。
そうすると、app >> viewsディレクトリ配下のhomeディレクトリの中のtop.html.erbファイルがrailsにより発見され、それがユーザーに送られます。
終わりに
長くなってきたので、今回はここまでになります。
また暇なときに次回の記事を書きますので、読んで頂けると嬉しいです。
お疲れさまでした。
- 投稿日:2020-05-17T15:02:31+09:00
rails server起動時に「Could not find a JavaScript runtime.」とエラーが出る場合の対処法について
windowsでは出来ていたサーバーの立ち上げが買ったばかりのMacでは出来ず・・・。
色々調べた結果なんとか出来たのでまとめました。エラー内容
Progateの「Ruby on Railsの環境構築をしてみよう!(macOS)」の「5. ローカルでRailsサーバを立てる」を進め、rails sコマンドを実行したところ以下のエラーが発生。
% rails s (略) Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)ブラウザでlocalhost:3000と入力してもエラーで表示されず。
どうやらJavaScript runtimeとやらが存在しないためエラーが出ている模様。runtimeが分からず検索すると以下の記事を発見。https://wa3.i-3-i.info/word13464.html
記事によると、
ランタイム(英:runtime)とは
プログラムとかを動かすとき(実行時)のこと。
あるいは
プログラムとかを動かすときに必要な部品のこと
とのこと。つまり、
Ruby自体にはJavaScriptを実行する機能が存在せず、実行するためにはruntimeというものが必要
ということらしい。解決まで
とりあえずネットでCould not find a JavaScript runtime.と検索したところRuby on Railsチュートリアル の記事を発見。
https://railstutorial.jp/chapters/beginning?version=4.0#sec-rails_server
記事によると、
(JavaScriptランタイムがインストールされていないというエラーが表示された場合は、GitHubのexecjsページにあるインストール可能なランタイムを確認してください。Node.jsが特にお勧めです)。
とのこと。早速エラー文中にあるGitHubのサイトへアクセス。
https://github.com/rails/execjs
Node.jsをクリックし、公式サイトからダウンロード後インストール。
その後もう一度コマンドでrails sを実行するとサーバーが立った。
- 投稿日:2020-05-17T14:48:53+09:00
ドリル(自作)
経緯
某プログラミングスクールのドリルはわかってなかったことだらけで勉強になるのですが,「問題は」出すけど,「解釈,解答の背景」は自分で調べろ的なスタンスなので公開記事で記述形式でまとめていきます。
ちなみに前回の記事のようにスクールの問題は限定記事にまとめて復習しています。ちなみに下のを解く場合,自分が示した答えは最適解とは限りませんが,参考程度にして自分で調べていただければと。
railsにおけるコールバックってなんなの?
*
*
*
*
*
*
*
*
*
*
*
*A
・オブジェクトはrailsのアクションで生成されたり削除されたりする。この過程をオブジェクトライフサイクルという。このサイクルのなかでオブジェクトが変化する前後で行われる処理がコールバック。
・乱暴に噛み砕くとはデータベースから抽出する時,登録する時に行われる処理。身近なものだとバリデーションとかもそう。データベースに登録する前になにか処理を行うなら,before_createとか,モデルにメソッド同様に記述。シンボルってなんなの?
*
*
*
*
*
*
*
*
*
*
*
**
A
見た目は文字列だけど処理では数値として扱われるよ。シンボルに対応する数値は1対1だからたくさん定義をしてもメモリを圧迫しないよ
selfってなんなの?
*
*
*
*
*
*
*
*
*
*A
・モデルを通過するオブジェクトそのものだよ
・モデルの中はインスタンスとか定義してないから@postをコントローラーで定義しても認識してくれない。だからselfがその代わりになってくれる。p self.nameってやったらTaroが出力される感じ。selfはわざわざ定義する必要なし。CSRFという攻撃の意味とrails上での対策
*
*
*
*
*
*
*
*
*
*
*
*A
クッキーを不正に利用して他人のアカウントで不正操作(投稿削除)を行うこと。(噛み砕き)
対策ー protect_from_forgery with: :exception
これによってフォームからの投稿ごとに不正に利用されていないかトークンを発行して照合している。countの使い方(引数の有無でどう変わるか)
*
*
*
*
*
*
*
*
*
*
*A
レシーバの要素数を返します。
引数を指定しない場合は、レシーバの要素数を返します。
引数を一つ指定した場合は、レシーバの要素のうち引数に一致するものの個数をカウント
ブロックを指定した場合は、ブロックを評価して真になった要素の個数をカウント
- 投稿日:2020-05-17T14:40:52+09:00
Rubyの基礎~わかりにくいところ復習~
オブジェクト
オブジェクトとは、Rubyで扱うデータのことを指します。
文字列オブジェクトでは文字を扱います。
文字をダブルクォーテションまたはシングルクォーテーションで囲むことによって文字列になります。つまりそれを文字列オブジェクトと言います。
文字列オブジェクトの他にも、時刻オブジェクトや、日付オブジェクト、配列オブジェクト、数値オブジェクトなどもあります。
オブジェクト名 扱うもの 文字列オブジェクト 文字 配列オブジェクト 複数のデータ 数値オブジェクト 数値 時刻オブジェクト 時間 日付オブジェクト 日付 メソッド
メソッドとは、プログラミングにおける何らかの処理をする命令群のことです。
例えば、ターミナルに文字列オブジェクトを出力したい時にはputs
メソッドを使います。オブジェクトを別のオブジェクトに形を変えるメソッドもあります。
例えばlengthメソッドです。このメソッドは文字列メソッドに使えるメソッドで文字列メソッドを自身の文字数の値を数値メソッドに変換します。
to_sメソッドは数値メソッドを文字列メソッドに変換してくれます。返り値
返り値とは、オブジェクトや、メソッドが処理されたあとの最終的な値のことです。
Rubyのオブジェクト自体、メソッドを利用した式には必ず返り値があります。つまりlengthメソッドを使ったときは、文字列の文字数の値の数値を返り値として返します。
to_sメソッドは数値オブジェクトを、文字列オブジェクトに変換して、それを返り値として返します。
to_iメソッドは文字列オブジェクトを数値オブジェクトに変換してくれます。
- オブジェクト単体の返り値はそのオブジェクト自体を返します。
- 計算式はその式の答えが返り値として返ります。
- メソッドを利用したときはその処理の実行結果が返り値として返ります。
- しかしputsメソッドは返り値として
nil
を返します。空という意味です。出力と返り値は別です。- 代入式は格納した値(代入した値)自体が返り値です。
getsメソッドはユーザーが入力した値の文字列オブジェクトを返り値として返します。
getsメソッドは、末尾に改行がついて返り値として返してしまいます。バックスラッシュ記法の\n
がついた状態です。
それを取り除くにはchomp
メソッドを使います。このメソッドを使うと新たに\n
を取り除いた状態の文字列オブジェクトを返してくれます。比較演算子
== は左右の式や値が等しい時にtrueを返り値として返します。等しくないときはfalseを返します。
それとは逆に
!= では左右の値が等しくない時にtrueを返し、等しい時にfalseを返します。変数
変数とはオブジェクトの入れ物のようなもので、その入れ物に名前がついています。
変数にオブジェクトを格納することができるのです。
変数は最代入が可能で値を更新することができます。
それに対して、定数は最代入が原則として禁止で、かつ全て大文字で書くのが慣習です。
つまり、固定した値を格納したい時に定数を使います。ハッシュ
ハッシュは変数の一つで、複数のデータを持つことができるオブジェクトです。
値をキーで管理しています。このペアで保存する形式のことをキーバリューストアと言います。
ハッシュオブジェクトのキーには文字列オブジェクトとシンボルオブジェクトが使えます。
実行速度がシンボルオブジェクトの方が速いので、シンボルオブジェクトが推奨されます。
シンボルオブジェクトは名前を識別するためのラベルのようなものでほぼほぼ文字列オブジェクトと同義で使えます。index.rbhash = {} ##空のハッシュを生成 hash[:name] = "taro" ##値の代入 hash[:age] = 20 ##値の代入 puts hash[:name] ##値の取得自分で定義するメソッド
putsのような処理を自分で定義してメソッド化することができます。
同じコードを何度も書かなくて済みますし、コードの可読性も上がります。また。修正や管理もしやすくなります。定義は defの隣に任意のメソッド名を書いてendまでの間に実行した処理を記述します。
index.rbdef my_name puts "私の名前は太郎です。" end注意しなければならないのはメソッドの定義部分はそのメソッドが呼びだれるまで読み込まれません。
ですのでメソッド名が呼び出される前は定義部分はスルーされます。index.rbdef my_name ##次にここが読まれる。それまではスルー。 puts "私の名前は太郎です。" end my_name ##メソッドの呼び出し 最初にここが読まれるwhile文
同じ処理を繰り返すことができる文法です。
index.rbwhile 条件式 do #処理 end条件式の部分がtrueであるかぎり処理を何回も繰り返します。
つまり
index.rbwhile true do #処理 endだと無限ループでプログラムを終了させなくできます。
処理の中にexitメソッドを使えば特定の時に無限ループを止めることができます。
配列
配列とは配列オブジェクトのことです。ハッシュがキーでオブジェクトを管理していたのに対して、配列は順番で管理します。配列にはハッシュも入れることができます。
順番の管理は0から始まるので、1つ目に入れた要素は0で管理されます。
配列オブジェクトに要素を追加するときは<<
メソッドを使います。
配列の数を数えたいときはlengthメソッドを使います。変数のスコープ
変数にはその変数が使える範囲というものが決まっています。
通常はメソッド内では。そのメソッド内で定義された変数しか使用することができません。
メソッド外で定義された変数を使うには引数を使います。引数
メソッドを呼び出す部分に書く引数を本引数と言います。
また、メソッドを定義している部分に書く引数を仮引数と言います。
本引数と呼び出したい変数は同じ名前である必要がありますが、本引数と仮引数は同じでなくて構いません。
仮引数で設定した名前でメソッド内で変数を使えます。index.rbdef age_sister(age) puts "my sister is #{age} years old." return age ##putsメソッドは返り値としてnilを返してしまうのでreturn文で変数の値を返り値として返している end sister = 25 ##age_sisterメソッドのスコープ外に変数sisterが定義されている。 age_sister_and_me(sister) ##age_sisterメソッドを呼び出す部分で本引数に変数sisterを取っている。
return
文はそこで返り値が確定するのでそこで処理が終了する。
定義されたメソッドの返り値は最後に記述された式の値が返ります。index.rbdef animal_name(name) puts "この動物は#{name}です。" return "僕は#{name}!" end puts animal_name("犬")上記のような場合、animal_nameメソッドでreturn文が使われているので返り値として"僕は犬!"が返ります。
putsメソッドで 僕は犬! が出力されています。
メソッドの呼び出し側で定義された("犬”)はメソッド内で変数として使うことができます。今回で言うと変数nameとして使えるようになっています。引数は関数を呼び出す際にどんなことを出力させるか決めるために利用される。
メソッドは引数を渡すと何かを返してくれる。
メソッドが引数を受け取るときその名前は何でも良い。
メソッドの最後で、メソッドの呼び出し元に値を返すときはreturn
を使います。eachメソッド
eachメソッドとは繰り返しのためのメソッドです。
配列オブジェクトにeachメソッドを使うと、その配列の要素の数だけ繰り返し処理が行われます。
- 投稿日:2020-05-17T13:39:20+09:00
【開発ログ⑫】有休未消化のアラートを実装
前提について
はじめまして、 プログラミングスクールに通ういりふねと申します。この記事は、スクールの課題である個人アプリの開発の記録を書くことで、自身のアウトプットに利用しています。もし、読んでいただけた方がいましたら、フィードバックをしていただけたら嬉しいです。
開発するのは「有給休暇管理ツール」です。仕様は過去記事をどうぞ。
アプリはデプロイまで行いますが、サービスとして提供するものではありません。あくまでも自学習の一環ですので、ご理解下さい。では本題へどうぞ。
今回実施する内容
前回までで、有休に関する簡単な計算までが終了しましたので、今回は有給休暇の未消化アラートを実装したいと思っています。以下の手順で実施します。今回は軽め。
- 未消化アラートとは?
- 未消化アラートの実装
- ビューに表示
未消化アラートとは?
働き方改革法案により、2019年4月1日より有給休暇を消化させることが、義務化されました。条件は有休を10日以上持つ社員に対して、毎年5日以上は時期を指定して計画的に消化させるというものです。毎年の期間については、就業規則により変わると考えられますが、今回は「直近付与日から次回付与日までの間」に5日未満しか消化していない社員については、有給休暇管理表のアラートカラムに「未消化」を表示させることにします。
未消化アラートの実装
今回も、モデルファイルに未消化アラートを表示させるメソッドを記述しておきます。
employee.rbclass Employee < ApplicationRecord 〜中略〜 def delete_days_alert a = total_delete_day b = total_add_day if b >= 10 && a < 5 "未消化" else "-" end end endIF文の前のメソッド、total_delete_dayとtotal_add_dayは、前回の記事で定義済みです。これは、直近の付与日から現在までの日数を計算するメソッドになります。
そして、IF文の条件は、b(付与日数の合計)が10以上、かつ、a(消化日数の合計)が5未満のとき、ということになります。有休の付与日数が10日未満の方やすでに今年5日以上消化している方は対象外となります。
ビューに表示
branches/index.html.haml.main =render 'branches/mainheader' 〜中略〜 %th{id: "short", class: "bgc"} 5日消化 〜中略〜 - @employees.each do |employee| %tr 〜中略〜 %th{id: "short", class: "bgc"} = employee.delete_days_alert 〜中略〜結果は、以下のとおりです。
未消化アラートは、目立ったほうが良いと思ったので、5日消化カラムには、class名bgcを追加し、黄色の背景色を設定しました。今日の積み上げ
本来であれば、「未消化」と表示されている方だけに黄色を表示させたかったのですが、私の能力では調べても実装できませんでした。一応、hamlでclassを定義する際にIF文で条件を定義し、条件に合う時、classを追加するという方法があるようです。まだまだ未熟であると感じましたが、できる限りで頑張りたいと思います。
参考にさせていただいた記事
「【Haml】条件(if)に応じてClassを変更させる方法」@nishina555様
「Hamlで条件に合わせてclassを追加する方法」@day-1様
「Hamlで条件に合う時だけ特定のクラスを加えたい」@aprikip様
- 投稿日:2020-05-17T13:39:20+09:00
RubyのテストRspecでコードカバレッジを理解する
テストケースを書いていると、どこまでカバレッジをカバーするって話がでてきます。
コードカバレッジってそういえばどんなのだっけ?って思ったので、自分の備忘録としてまとめようと思います。
コードカバレッジとは?
ソフトウェアテストにおいて、テスト対象となるプログラムコード(内部ロジック)全
体の中で、テストが行われた部分が占める割合(網羅率)です。
参照代表的なカバレッジ
C0:命令網羅
それぞれの命令文が少なくとも1回は実行
C1:分岐網羅
それぞれの判定条件に置ける真偽が少なとも1回は実行
C2:条件網羅
それぞれの条件文に置ける真偽が少なくとも1回は実行
MCC:複合条件網羅
それぞれの条件に置ける真偽の組み合わせが全て実行
実際Rspecでカバレッジを意識してテストを書く
テストをするRubyコード
sample.rbmodule CodeCoverage class << self # main # @params [interger] x # @params [interger] y # @return [String] xとyの値によってことなる文字列 # # 引数は、intergerのみを想定 def main(x, y) result = '' if x >= y || x % 2 == 0 result << '処理1' else result << '処理2' end if x*y > 10 result << '処理3' else result << '処理4' end return result end end endテストコード
rspecで書いてます。
RSpec.describe CodeCoverage do shared_examples '処理1と処理3通る' do it '処理1と処理3の文言が返却される' do expect(subject).to eq '処理1処理3' end end shared_examples '処理1と処理4通る' do it '処理1と処理4の文言が返却される' do expect(subject).to eq '処理1処理4' end end shared_examples '処理2と処理3通る' do it '処理2と処理3の文言が返却される' do expect(subject).to eq '処理2処理3' end end shared_examples '処理2と処理4通る' do it '処理2と処理4の文言が返却される' do expect(subject).to eq '処理2処理4' end end describe 'main' do subject { described_class.send(:main, x, y) } # C0:命令網羅 # 命令が少なくても1回だけ実行するように書く context 'C0の場合' do context '処理1と処理3を通る' do let(:x) { 11 } let(:y) { 1 } it_behaves_like '処理1と処理3通る' end context '処理2と処理4を通る' do let(:x) { 1 } let(:y) { 9 } it_behaves_like '処理2と処理4通る' end end # C1:分岐網羅 # それぞれの判定条件に置ける真偽が少なとも1回は実行 context 'C1の場合' do context '処理1と処理3を通る' do let(:x) { 11 } let(:y) { 1 } it_behaves_like '処理1と処理3通る' end context '処理1と処理4を通る' do let(:x) { 3 } let(:y) { 2 } it_behaves_like '処理1と処理4通る' end context '処理2と処理3を通る' do let(:x) { 3 } let(:y) { 5 } it_behaves_like '処理2と処理3通る' end context '処理2と処理4を通る' do let(:x) { 1 } let(:y) { 4 } it_behaves_like '処理2と処理4通る' end end # C2:条件網羅 # それぞれの条件文に置ける真偽が少なくとも1回は実行 context 'C2の場合' do context 'x>=yかつx*y>10' do context '処理1と処理3を通る' do let(:x) { 11 } let(:y) { 1 } it_behaves_like '処理1と処理3通る' end end context 'x>=yかつx*y<10' do context '処理1と処理4を通る' do let(:x) { 3 } let(:y) { 2 } it_behaves_like '処理1と処理4通る' end end context 'x<yかつxが偶数かつx*y>10' do context '処理1と処理3を通る' do let(:x) { 4 } let(:y) { 5 } it_behaves_like '処理1と処理3通る' end end context 'x<yかつxが偶数かつx*y<10' do context '処理1と処理4を通る' do let(:x) { 2 } let(:y) { 3 } it_behaves_like '処理1と処理4通る' end end context 'x<yかつxが奇数かつx*y>10' do context '処理2と処理3を通る' do let(:x) { 3 } let(:y) { 5 } it_behaves_like '処理2と処理3通る' end end context 'x<yかつxが奇数かつx*y<10' do context '処理2と処理4を通る' do let(:x) { 1 } let(:y) { 4 } it_behaves_like '処理2と処理4通る' end end end end end endこうやって書くとパッとわかりやすいですね。
でも実務だと、C1までしか書かないです。
- 投稿日:2020-05-17T12:49:48+09:00
【開発ログ⑪】有休付与日から現在までの消化日数の合計を計算〜残日数まで
前提について
はじめまして、 プログラミングスクールに通ういりふねと申します。この記事は、スクールの課題である個人アプリの開発の記録を書くことで、自身のアウトプットに利用しています。もし、読んでいただけた方がいましたら、フィードバックをしていただけたら嬉しいです。
開発するのは「有給休暇管理ツール」です。仕様は過去記事をどうぞ。
アプリはデプロイまで行いますが、サービスとして提供するものではありません。あくまでも自学習の一環ですので、ご理解下さい。では本題へどうぞ。
今回の実施内容
前回までで、従業員の入社日をもとに付与日(入社日から6ヶ月後の有休が付与される日)と勤続年数まで計算できました。今日は、消化日数の合計を計算させます。具体的には以下の手順で考えます。
- 消化日数の合計とは?
- 今回扱う消化日数の合計は?
- 合計する対象範囲を検索
- 検索結果を合計させる
- 合計した結果をビューに表示
- ローカルブラウザで確認できればOK
消化日数の合計とは?
まず毎年もらえる有給休暇は、付与されてから2年間有効となります。消化する場合は、古いものから順に消化されるのもポイントです。
例えば、2018年4月1日に入社した方が、有休のうち3日間を2回に分けて消化した場合、以下のような流れになります。
日付 有休の増減 残日数 2018/4/1 入社時は増減なし 0日 2018/10/1 有休10日付与 10日 2019/1/1 有休3日消化★ 7日 2019/10/1 有休11日付与 18日 2020/1/1 有休3日消化★ 15日 2020/5/17 有休の残日数を確認 15日 2020/10/1 有休4日は未使用のまま消滅、有休12日付与 23日 つまり、現在2020/5/17日時点の有休消化日の合計は、以下の2つをレコードをテーブルから検索し、合計すればよいということになります。
- 直近付与日から現在までの合計(2019/10/01~2020/5/17)
- 2つ前の付与日から直近付与日までの合計(2018/10/1~2019/10/1)
ただし、この計算式で行く場合は、別途2020/10/01に未消化のまま消滅する有休も計算させる必要があります。
今回扱う消化日数の合計は?
今回、最終的にアプリケーションでで実装したい日数計算は、以下の通りになります。
① 過去2年間の付与日数の合計
② 直近付与日から現在までの消化日数合計
③ 2つ前の付与日から直近付与日までの消化日数合計
④ ①から②と③を引いた残日数長々書きましたが、私自身の備忘録として書かせていただきました。よって、今後考え方が変わるかも知れません。あくまで記事作成時点のいりふねの脳内の出来事とお考え下さい。
で、今回実装したのは、「②直近付与日から現在までの消化日数合計」なので、これの紹介を以下より行います。合計する対象範囲を検索
前回までは、社員のレコードのみで表を作成していましたが、今回から有給休暇のレコードも計算に入ってきます。社員はemployeeテーブル、有給休暇はholidayテーブルにそれぞれ格納されているので、コントローラーから編集します。
employees_controller.rbclass EmployeesController < ApplicationController def index @employees = Employee.where(branch_id: params[:branch_id]).includes(:holidays) end 〜中略〜 end後半にincludesメソッドでemployee_idをもつholidayのレコードも一緒に取得しています。includesメソッドはカリキュラムで「N+1問題」を学んだときに登場しました。その際は、1対多関係のテーブルのうち、外部キーを持つ多のレコードを取得する際に、主キーを持つレコードを先読みするために使用すると学びました。今回は主キーをもつレコードに紐付く外部キーのレコードを取得しているので、順番が逆になっていますが、無事使えました。
参考にした記事「Railsで1対多のテーブルデータを取得し表示させる」@bitarx様
次にモデルファイルで、「直近付与日から現在まで」検索を行わせるメソッドを定義します。
models/employee.rbclass Employee < ApplicationRecord 〜中略〜 def range_to_add_or_delete grant_day = hire_date >> 6 month = grant_day.month day = grant_day.mday year = Date.today.year grant_date_this_year = Time.local(year, month, day) if grant_date_this_year > Date.today last_year = year - 1 grant_date_this_year = Time.local(last_year, month, day) else grant_date_this_year end holidays.where(created_at: grant_date_this_year..Date.tomorrow) endまず、冒頭のgrant_dayは、法定付与日(入社日から6ヵ月後)を計算しています。これに「.month」や「.mday」のメソッドを使用して、月と日の数字を取り出して変数に格納しています。年については、「Date.today」で今日の日付を用意し、それに「.year」メソッドを実行することで年の数字を取り出しています。最後に「Time.local」で3つの数字を合体させ、一旦直近付与日(grant_date_this_year)を完成させます。
参考にさせていただいた記事「[Ruby入門] 14. 日付と時刻を扱う(全パターン網羅)」@prgseek様
続けて、IF文の箇所ですが、条件に応じて年を加工して、最終的な直近付与日を確定させています。条件は、先程作った直近付与日(grant_date_this_year)が現在の日付より大きいかというものです。これを行う理由は、法定付与日が12月の方などは、直近法定付与日の結果が2020年12月1日と未来の日付になってしまうからです。そこで、一旦完成させた直近付与日が、現在の日付よりも大きくなっている場合は、年からマイナス1を実行して、「last_year」という新しい変数で、直近付与日を作り直しています。else以降は、必要かどうかが、現段階で判断できません。すみません。
途中結果のビューですが、消化日数のカラムに各社員の直近付与日が表示されました。4月入社の昇太師匠も未来の日付にならずに済んでいます。良かった〜!!
コードの解説に戻ります。
最後の1文で完成した直近付与日から現在までの範囲のcreated_atを検索し、必要なレコードを検索させます。参考にさせていただいた記事「ActiveRecordで日付・時刻の範囲検索をシンプルに書く方法」@hachi8833様
検索結果を合計させる
指定の範囲で検索できたので、これを元に合計を出していきます。モデルファイルに合計を計算させるメソッドを別で書きます。
なお、今回は開発途中ということで付与日の合計日数も同じ方式で計算することにしました。前述した通り付与日の合計の算出方法は別にありますが、付与日の合計には他に実装しなければならないメソッドがあるので、今回はビュー確認用で一旦同じにしておきます。models/employee.rbclass Employee < ApplicationRecord 〜中略〜 def total_delete_day total_delete_day = range_to_add_or_delete.sum(:delete_day) end def total_add_day total_add_day = range_to_add_or_delete.sum(:add_day) end def calculate_remaining_days a = total_delete_day b = total_add_day b - a end end「.sum」メソッドの引数は、合計を出したいカラム名になります。
参考にさせていただいた記事「【Rails】カラムの合計値を求める!」@tomokichi_ruby様ついでにcalculate_remaining_daysを定義し、残日数を計算させています。本来であれば、付与日の合計と消化日の合計を引いた結果が、マイナスであればエラーが出るように条件分岐させるべきですが、同じくビュー確認用でシンプルに引き算だけさせます。
合計した結果をビューに表示
branches/index.html.haml.main =render 'branches/mainheader' .main__body 社員データの編集は「名前」をクリック、削除は右端です。 %table %tr ~中略~ %th{id: "short"} 付与日数 %th{id: "short"} 消化日数 %th{id: "short"} 残日数 - @employees.each do |employee| %tr 〜中略〜 %th{id: "short"} = employee.total_add_day %th{id: "short"} = employee.total_delete_day %th{id: "short"} = employee.calculate_remaining_days 〜中略〜先程、定義したメソッドを「index.html.haml」に反映させました。ビューの結果は以下のとおりです。無事に計算させることができました。
今日の積み上げ
だいぶ、Qiitaの記事投稿に慣れてきました。内容の割に文章が多いと感じますが、自分が調べたことや躓いたこと、同じ初学者の助けになればと思えば、解説がバカ丁寧になるものなのかな?と思うようにしています。とはいえ、参考にさせていただいた記事のように簡潔に書いて初学者の参考になる記事も多いので、簡潔さを意識したいです。以上。
- 投稿日:2020-05-17T11:45:34+09:00
【超かんたん】Rubyの環境構築(mac)
環境構築のやり方
全て、progateのサイトにわかりやすく載っています。
初めての環境構築でしたが、20分ほどでできました。
【macの人】
https://prog-8.com/docs/ruby-env【windowsの人】
https://prog-8.com/docs/ruby-env-win補足
記事の最後のほうに、
rbenv global 2.6.5というコマンドを入力することで、今回インストールしたバージョンのRubyを使用するように設定を変更し、
ruby -vとコマンド入力をすることで2.6.5 と表示されていればOKと書いてあります。
しかし、rbenv globalコマンド実行後にターミナルの再起動を行ってからruby -vとコマンド入力しないと、
2.6.5と表示されない(古いバージョンが表示される)ことがあるので、そこだけ注意しましょう。また、記事ではRubyのバージョン2.6.5をインストールするようなコマンドが書かれています(2020/5/17現在)が、
他のバージョンをインストールしたいときには、バージョンの部分だけ置き換えてコマンド入力すればOKです。
- 投稿日:2020-05-17T11:45:34+09:00
【超かんたん】Rubyの環境構築
環境構築のやり方
全て、progateのサイトにわかりやすく載っています。
初めての環境構築でしたが、20分ほどでできました。
【macの人】
https://prog-8.com/docs/ruby-env【windowsの人】
https://prog-8.com/docs/ruby-env-win補足
記事の最後のほうに、
rbenv global 2.6.5というコマンドを入力することで、今回インストールしたバージョンのRubyを使用するように設定を変更し、
ruby -vとコマンド入力をすることで2.6.5 と表示されていればOKと書いてあります。
しかし、rbenv globalコマンド実行後にターミナルの再起動を行ってからruby -vとコマンド入力しないと、
2.6.5と表示されない(古いバージョンが表示される)ことがあるので、そこだけ注意しましょう。また、記事ではRubyのバージョン2.6.5をインストールするようなコマンドが書かれています(2020/5/17現在)が、
他のバージョンをインストールしたいときには、バージョンの部分だけ置き換えてコマンド入力すればOKです。
- 投稿日:2020-05-17T11:03:04+09:00
【Rails基礎】画面ロード時のエラーについて(NameError, LoadError)
2回目の投稿になります。よろしくお願いします。
今回は個人アプリを作る中で起きた画面ロード時に起きたエラーについての記事を書きたいと思います。環境
Ruby: 2.5.1
RubyOnRais: 5.0.7エラー内容
エラー内容は以下の二つです。
① LoadError in EventsController#choise_artist
Unable to autoload constant Set_list, expected ~ FesLive-app/app/models/set_list.rb to define it.② Name Error in EventsController#choise_artist
uninitialized constant EventsController:Setlist解決方法
①の解決方法
まずひとつ目のエラーですが、autoloadできないと言われています。
まず、Railsのautoloadとは「命名規則に則ったファイルを自動でrequireしてくれる機能」のことです。
つまり、下の記述の @set_lists = Set_list.all は命名規則に従っていないということになります。
実はrails の命名規則ではクラス名に対してアンダーバー(_)を使うことはできません。
そのため、Set_listクラスをautoloadできませんよ〜と言われているのでした。class EventsController < ApplicationController def choise_artist @set_lists = Set_list.all @event = Event.find(params[:id]) end endなので、「@set_lists = Setlist.all」とすれば①のエラーに関しては突破できました。
②の解決方法
クラス名の表記の仕方を変えautoloadはできるようになりましたが、今度はNameErrorのエラーがでました。
uninitialized ⇒ 初期化されていない ⇒ クラスが使える状態が整っていないということなので、
クラス名の指定の仕方に問題がありそうだということが分かります。
実際にSetlistモデルを見に行くと、クラス名が「SetList」とLが大文字になっていました。
なので、クラス名がまちがってますよ〜と言われているのでした。class SetList < ApplicationRecord belongs_to :event end この「@set_lists = SetList.all」とすればこのエラーは突破できました。以上、ここまで読んでくださりありがとうございました。
分かりにくい箇所やアドバイス等ありましたら、コメントくださると幸いです。では!【参考サイト】
https://qiita.com/hirokisoccer/items/4ba62a56b18eb834a8ee
https://wa3.i-3-i.info/word16120.html
- 投稿日:2020-05-17T10:56:00+09:00
ログインしていない時は遷移先のアクションを制約する方法
背景
今回は、ログインしていないユーザーが指定したページ以外遷移できないように制約をかける方法を紹介していきます。
やりたいこと
ログインしていないユーザーは、index,showページのみ遷移できて、newページやeditページに遷移しようとすると強制的にindexページに遷移されるように処理します。
使い方
コントローラー内で繰り返し使用されるコードは、private以下でメソッド化します。
以下の処理を行うことで、ユーザーがログインしていない状態でindex,showページ以外に遷移しようとすると、強制的にindexページに遷移されるようになります。controller.rbclass PracticeController < ApplicationController before_action :move_to_index, except: [:index, :show] ~省略~ private def move_to_index redirect_to action: :index unless user_signed_in? end
以上