20200724のRubyに関する記事は19件です。

Python でクラス作成してダックタイピングしてみた

人間クラスと継承、車のクラスで判定

ダックテストというものがあるらしい。Wikipedia様の情報によりますと
"If it walks like a duck and quacks like a duck, it must be a duck"
「もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない」
という考え方のようです。ここから来たのがダックタイピング。

ダックタイピングは例えばRubyだとこんな感じとなります。

Ruby でのダックタイピング

sampleRuby.rb
# テスト
def test(foo)
   puts foo.sound
end

# アヒルの鳴き声
 class Duck
   def sound
     'quack'
   end
 end
 # 猫の鳴き声
 class Cat
   def sound
     'myaa'
   end
 end
 # 実行
 test(Duck.new)
 test(Cat.new)

出力は

# 出力結果
quack
myaa

要するに、クラスが別であっても同じ名前のメソッドを使用することができ、異なるオブジェクトで同じ操作を切り替えて使うことができるというもののようです。

例えば、人間クラスを若者と大人が継承して、それを車クラスで判定して、ドライバーの資格があるかどうか判定をするとします。
例えば以下のようなサンプルコードはどうでしょう。Pythonです。

Python でのダックタイピング

samplePython.py
#
# 人間のクラス
#  2020.07.24 ProOJI
#
class Person(object):
    """年齢メソッド"""
    def __init__(self, age=1):
        self.age = age
    """ドライバー資格メソッド"""
    def drive(self):
        if self.age >= 18:
            print('You can drive!')
            print("Because You're {} years old.".format(self.age))
        else:
            raise Exception('Sorry...you cannot drive.')
# 若者のクラス
class Young(Person):
    def __init__(self, age=12):
        if age < 18:
            super().__init__(age)
        else:
            raise ValueError
# 大人のクラス
class Adult(Person):
    def __init__(self, age=18):
        if age >= 18:
            super().__init__(age)
        else:
            raise ValueError
#
# 自動車クラス
#  ドライバー資格の判定
class Car(object):
    def __init__(self, model=None):
        self.model = model
    def ride(self, person):
        person.drive()

# 若者_インスタンスの生成(13歳)
young = Young(13)
# 大人_インスタンスの生成(46歳)
adult = Adult(46)
# 自動車_インスタンスの生成_1
car = Car()
# 若者の判定 13歳
car.ride(young)
# 出力結果
# Exception: Sorry...you cannot drive.
# 自動車_インスタンスの生成_2
car = Car()
# 若者の判定 46歳
car.ride(adult)
# 出力結果
# You can drive!
# Because You're 46 years old.

まとめ

プログラミングにはポリモーフィズムという概念があり、日本語では多態性・多様性など言われます。これはオブジェクト指向の概念の一つです。
クラスの型が別であっても同じ名前のメソッドがあればそれを使用することができ、異なるオブジェクトで同じ操作を切り替えて使うことができます。
このようなコードをダックタイピング(duck typing)と言うことがわかりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseのビューファイルに自動でbootstrapのスタイルを適用する

結論

$ rails g devise:views:bootstrap_templates -f

これで全てのdeviseのビューファイルに、自動でbootstrapのスタイルが適用される。
アカウント.png
ログイン.png
パスワード.png

もうちょっと整える

deviseのビューファイルにデフォで記述されているコードを

<div class="container">
  <div class="row">
    <div class="mx-auto" style="width: 580px;">

    </div>
  </div><!-- End of row -->
</div><!-- End of container -->

これで囲ってあげると、
アカウント登録(中央).png
ログイン(中央).png
パスワード(中央).png

いい感じ!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyでドローポーカーを作ってみる~実装編5(ゲーム)【完結】~

概要

rubyでドローポーカーを作ってみる~準備編~

rubyでドローポーカーを作ってみる~test-unit準備編~

rubyでドローポーカーを作ってみる~実装編1(カード)~

rubyでドローポーカーを作ってみる~実装編2(役)~

rubyでドローポーカーを作ってみる~実装編3(プレイヤー)~

rubyでドローポーカーを作ってみる~実装編4(山札)~
に続いて。

ソース: https://github.com/rytkmt/ruby_poker

ゲームの実装

やってきました。
整理することが多そうなので若干びびってますが、一度すべて洗い出してみたら全体像がつかめて案外・・ってなるかもしれないですね。

では、整理していきましょう。

  • 山札を作成
  • 指定人数分、プレイヤーを作成
    • 山札から5枚ずつカードを配る
  • 各プレイヤーを順番に変更するカードの確認
    • 手札を画面に表示する
      • 全カード
      • 現在の手札での役
    • 変更する場合、枚数分山札からカードを渡し、不要カードを回収し山札の不要カードとして追加する
    • 変更後の手札を画面に表示する
      • 全カード
      • 現在の手札での役
  • 全プレイヤーの役を判定する
    • 勝者を表示

ゲームの開始

まずゲームは開始するとともに山札を初期化します

ruby_poker/game.rb
module RubyPoker
  class Game
    def initialize
      @deck = Deck.new
    end
  end
end

ゲームの実行

startって付けようとしていましたが、gameを行うのはplayのほうが適切だと思ったのでplayでいきます
このとき、new.playって毎回するのもあほらしいので、.playメソッドを用意してしまいます

ruby_poker/game.rb
    def self.play
      new.send(:play)
    end

  private

    def play
      puts "<ゲーム開始>"

      puts "<ゲーム終了>"
    end

プレイヤーの名称を保持

毎回プレイヤー番号を渡し続けるのもしんどいので、プレイヤーの名称を保持しておくように機能強化します。

ruby_poker/player.rb
 module RubyPoker
   class Player
     include Comparable
-    attr_reader :hand
+    attr_reader :name, :hand

-    def initialize(cards:)
+    def initialize(name:, cards:)

プレイヤー人数の決定

プレイヤーを初期化するにあたり、人数を入力してもらい、カードを5枚ずつ配ります
人数の入力は間違うこともあるため、間違っている場合は再度入力を促すようにします

ruby_poker/game.rb
    def init_players(count:)
      @players = count.times.map { |i| Player.new(name: "プレイヤー#{i + 1}", cards: @deck.draw(count: 5)) }
    end

    def input_player_count
      puts "プレイヤーの参加人数を入力してください(1~7)"
      player_count = gets.to_i
      return player_count if (1..7).include?(player_count)
      puts "入力が間違っています"
      input_player_count
    end

プレイヤーの手札表示

プレイヤーの手札を表示するために、カード、役などそれぞれ表示用の文字列を生成します。
このとき、コンソール出力はinspectメソッドを使用するのが一般的なのでそちらで実装します。

プレイヤーは各カードと役を表示します。
なので、カード、役にもそれぞれinspectを定義し、それを合わせて出力するのをプレイヤーの出力で行うようにします。

また、カードの表示がわりと見にくいので、色を付けて表示するようにしてみます。

色表示用のgemインストール

Gemrfile
+gem "rainbow"
$ bundle install
ruby_poker.rb
+require "rainbow"

これでputs Rainbow("hoge").redとかすることでカラー表示できるようになります

カードのinspect

  • スートの表示用に記号へ変換
  • 数字の記号変換

- スートに応じてカラーを切替

をそれぞれ行います

ruby_poker/card.rb
    def inspect
      suit_str =
        case @suit
        when :spade; "♠"
        when :heart; "♥"
        when :diamond; "♦"
        when :club; "♣"
        end

      number_str =
        case @number
        when 1; "A"
        when 13; "K"
        when 12; "Q"
        when 11; "J"
        else; @number
        end

      color =
        case @suit
        when :spade; :blue
        when :heart; :red
        when :diamond; :yellow
        when :club; :green
        end

      Rainbow("[#{suit_str} #{number_str}]").send(color)
    end

役のinspect

ruby_poker/hand.rb
    def inspect
      hand_type_name =
        case @hand_type
        when :royal_straight_flush; "ロイヤルストレートフラッシュ"
        when :straight_flush; "ストレートフラッシュ"
        when :four_of_a_kind; "フォーカード"
        when :full_house; "フルハウス"
        when :flush; "フラッシュ"
        when :straight; "ストレート"
        when :three_of_a_kind; "スリーカード"
        when :two_pair; "ツーペア"
        when :one_pair; "ワンペア"
        when :high_card; "?"
        end

      "役: #{hand_type_name}"
    end

プレイヤーのinspect

最後にプレイヤーです

カード交換の際に番号入力をしてもらうため、各カードに番号をつけて表示します。

ruby_poker/player.rb
    def inspect
      cards_str = @cards.map.with_index(1) do |card, i|
        "#{i}. #{card.inspect}"
      end.join("   ")

      cards_str + "\n" + @hand.inspect + "\n"
    end

これで、p playerをすることで こんな感じになります(色はここでは映らないですが・・)

1. [♥ 5]   2. [♦ 8]   3. [♣ 4]   4. [♠ 10]   5. [♠ 4]
役: ワンペア

プレイヤーのターン処理

  1. 各プレイヤーの現在の状況を表示
  2. カード交換を確認
    • 入力が間違っていたら再度確認する
  3. 交換後の手札を表示
ruby_poker/game.rb
    def turn(player:)
      puts "\n#{player.name} のターンです"
      p player

      puts "カードの交換ができます"
      indexes = input_change_indexes
      return if indexes.empty?

      new_cards = @deck.draw(count: indexes.size)
      trushed = player.change(indexes: indexes, new_cards: new_cards)
      @deck.trush(cards: trushed)
      p player
    end

    def input_change_indexes
      puts "捨てるカードの番号を半角スペース区切りで入力してください(なければそのままEnter)"

      indexes = gets.chomp.split.map(&:to_i)
      return indexes.map { |i| i - 1 }.sort if indexes.all? { |i| (1..5).include?(i) }
      input_change_indexes
    end

プレイヤーの勝敗判定

ruby_poker/game.rb
    def judge
      puts "----- 結果 -----"
      @players.each do |player|
        puts "#{player.name} の手札"
        p player
        puts ""
      end

      winner = @players.max
      puts Rainbow("\n#{winner.name} の勝ち ??").underline
    end

実行メソッドにまとめる

ruby_poker/game.rb
    def play
      puts "<ゲーム開始>"

      init_players(count: input_player_count)
      @players.each { |player| turn(player: player) }

      judge

      puts "<ゲーム終了>"
    end

ゲームの実行を簡単にする

RubyPoker::Game.play となりますが、 RubyPoker.palyがいい。。

ruby_poker.rb
  def self.play
    RubyPoker::Game.play
  end

完成

bundle exec console を実行することでゲームを楽しむことができます。

image.png

感想

Qiitaで見た1つの記事をきっかけに「やってみよう」と思い今回の記事を書きながら実装しました。

実際に実装していく流れなどをみなさんがどういう風に行っているのか?というところは、いざ仕事で開発をしていると各個人で頭の中で考えて全て整ったプログラムを確認することばかりのため、
あまり実装のプロセスを覗く機会がない印象があります。
今回の記事が少しでも参考になればうれしいです。

普段仕事ではrailsのプロジェクトなので、railsやgemを触ることはありますが、
単一のrubyプロジェクトを作ることは無かったためGemfile、Rakefileなど改めてこういう感じで進めるんだ~と自分のなかでもふわっとしていたところが理解できました。
普段使わないgemを触るのも楽しかったです。

この記事をもとに同じように刺激をうけ、なにか作ってみよう と思う方がいればうれしいです。
また、流れを公開したことにより、もしここをこういう風にするとより良くなる などのアドバイスがあればぜひ教えてください。お待ちしております。

実際のソースはこちらにあります。 https://github.com/rytkmt/ruby_poker

実装に関しての感想

今回、ポーカーに限った実装でしたが、
カードを共通として、プレイヤーと山札の処理をゲームの種別に応じてモジュールを切り替えることによっていろんなゲームを載せる なども出来そうかなーと思いました。

また、役判定を完全に切り分けることで、ローカルルールで役を追加する(奇数・偶数でのストレートとかあっても面白そう)などもできるかなと思います

今回jokerは考慮せず作ったため、それを追加するのは少し大変だなーとは思いますが、やってみるのも楽しいかもしれないですね。

以上、かなり記事数も多くなりましたが、長々と見ていただいた方、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]変数の中身を表示する

概要

Rubyのputsメソッドを使用したときに、用意した変数の中身を表示する方法を記します。

環境

Ruby 2.6.5

内容

変数の中身を表示する場合は以下のように記述します。

number = 10
puts number
puts "#{number}"
puts '#{number}'

実行結果

10
10
#{number}

`※ポイントは、シングルクォーテーションで囲った場合はそのまま出力されてしまうところです。`

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bundle install時のエラー

概要

Railsアプリケーションでgemをbundle installした際に発生したエラーと解消方法について記します。

環境

Ruby 2.6.5
Ruby on Rails 6.0.0

内容

エラー文

An error occurred while installing pg (1.2.3), and Bundler cannot
continue.
Make sure that `gem install pg -v '1.2.3' --source 'https://rubygems.org/'`
succeeds before bundling.

Could not find gem 'pg (>= 0.18, < 2.0)' in any of the gem sources listed in your Gemfile.

といったエラーです。

解消方法

下記コマンドで解消しました。

brew install postgres

postgresSQLで引っかかっていたみたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]エラーメッセージの日本語化

概要

Railsアプリケーションのエラーメッセージを日本語化する手順を記します。
入植画面で未入力や不備があった場合に出力する下記の様なメッセージです。

bcb5b66aaa2336c94683fe2b94ccc49d.png

環境

Ruby 2.6.
Ruby on Rails 6.0.0

手順

①config/application.rbに記述

module Pictweet
 class Application < Rails::Application
   # Initialize configuration defaults for originally generated Rails version.
   config.load_defaults 6.0


   # 日本語の言語設定
   config.i18n.default_locale = :ja
  # 省略
 end
end

②gemfileに記述

gem rails-i18n'

bundle install実行

※ここまででカラム名以外は日本語になっています。

③config/locales/devise.en.ymlに以下URLのコードをコピペする
https://github.com/tigrish/devise-i18n/blob/master/rails/locales/ja.yml

※ここまででdeviseであらかじめ作成されるカラム(emai、password、password確認用)は日本語になっています。

④config/localesにja.yml作成

ja:
 activerecord:
   attributes:
     user:
       nickname: ニックネーム
     tweet:
       text: テキスト
       image: 画像

この記述で、Nickname、Text、Imageが日本語に翻訳されます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】環境変数設定-dotenv-rails-

環境変数とは

環境変数とは、Githubにアップロードすることができない情報を入れておく箱のようなものです。
これにより、アプリケーションに直接パスワードなどを記述しなくとも、
OSからデータを渡す事で、情報漏洩の心配なく、アプリを稼働させる事ができます。

設定方法

gemのインストール
Gemfile
gem 'dotenv-rails'
$ bundle install
.envファイル作成

Gemfileが置いてあるルートディレクトリに
.envファイルを作成

環境変数作成
KEY='*******' #使用したいキーやパスワードを
SECRET_KEY='*******'

呼び出す時は

ENV['KEY']

とすれば呼び出せます。

Git管理下から除外
.gitignore
/.env

これを忘れると情報が漏洩してしまうので忘れずに必ず設定してください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]Formオブジェクト使用時のエラーメッセージ日本語化

概要

RailsフリマアプリでFormオブジェクトを実装した部分のみ、日本語化がうまく行かなかったので、解消方法を記します。

前提

ja.ymlファイルの作成までは完了している。
Formオブジェクト以外のエラーメッセージについては日本語化ができている。

環境

  • ruby 2/6/5
  • Ruby on Rails 6.0.0

内容

ja.ymlに日本語入力の記述をしても日本語にならない

config/locales/ja.yml

ja:
    activerecord:
        attributes:
            user:
                nickname: ニックネーム

            item:
                text: テキスト
                image: 画像
                name: 商品名

            card_address:
                postal_code: 郵便番号
                city: 市区町村
                address: 番地
                phone_number: 電話番号

以下の内容に修正し解消しました。

ja:
    activerecord:
        attributes:
            user:
                nickname: ニックネーム


            item:
                text: テキスト
                image: 画像
                name: 商品名

    activemodel:
        attributes:
            card_address:
                postal_code: 郵便番号
                city: 市区町村
                address: 番地
                phone_number: 電話番号

原因

formオブジェクトの記述を行なったforms/card_addressファイルはActiveModelを継承しているファイルだった為、activerecord:(2行目)では反映されなかったみたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyでドローポーカーを作ってみる~実装編4(山札)~

概要

rubyでドローポーカーを作ってみる~準備編~

rubyでドローポーカーを作ってみる~test-unit準備編~

rubyでドローポーカーを作ってみる~実装編1(カード)~

rubyでドローポーカーを作ってみる~実装編2(役)~

rubyでドローポーカーを作ってみる~実装編3(プレイヤー)~
に続いて。

ソース: https://github.com/rytkmt/ruby_poker

山札の実装

はい、では今回も同じくまずは要件の整理からいきます。

  • はじめは13 x 4のカードを全て使用して山札を作成する
    • カードの並び順はばらばら
  • 手札交換を行って不要になったカードは回収する
  • 山札から任意の枚数をドローする
    • ドローする枚数が足りなくなった場合、手札交換にて不要になったカードをまぜてシャッフルしてそこから引く

という感じですかね。

プレイヤーの手札交換の修正

ここで気づいてしまいましたが、前回のプレイヤーの実装にて手札交換でただ手札のカードから削除するだけと実装してしまっていたため
捨てるカードを回収するように修正します・・

ruby_poker/player.rb
    def change(indexes:, new_cards:)
       raise(ArgumentError) unless indexes.size == new_cards.size
       raise(ArgumentError) unless indexes.all? { |i| (0..4).include?(i) }
-      indexes.sort.reverse_each { |i| @cards.delete_at(i) }
+      trushed = indexes.sort.reverse.each_with_object([]) { |i, trushed| trushed << @cards.delete_at(i) }
       @cards += new_cards
       @hand = Hand.new(cards: @cards)
+      trushed
     end

山札の作成

ruby_poker/deck.rb
module RubyPoker
  class Deck
    def initialize
      @cards = init_cards
      @trushed = []
    end
  private

    def init_cards
      RubyPoker::SUITS.each_with_object([]) do |suit, cards|
        cards.concat(
          RubyPoker::NUMBERS.map { |number| Card.new(suit: suit, number: number) }
        )
      end.shuffle
    end
  end
end

始め、each_with_object内で+=を使って実装したのですが、よくよく考えるとこれは+で結合したものを=で変数代入してるので同一の変数に結合した別オブジェクトを格納しているだけで、破壊的な変更を行っているわけではないのでうまくいかなかったです。

破壊的にcardsを変更するためにconcatを使います。

手札交換によるカードの回収

プレイヤーの#changeにて返ってきた不要カードを回収して保持しておくだけなので簡単です

ruby_poker/deck.rb
    def trush(cards:)
      @trushed += cards
    end

カードのドロー

  • 山札から任意の枚数をドローする
    • ドローする枚数が足りなくなった場合、手札交換にて不要になったカードをまぜてシャッフルしてそこから引く
ruby/ruby_poker/deck.rb
    def draw(count:)
      merge_trushed if @cards.size < count
      raise(ArgumentError, "No cards.") if @cards.size < count
      @cards.shift(count)
    end

  private

    def merge_trushed
      @cards += @trushed
      @cards.shuffle!
      @trushed = []
    end

足りなくなるってプレイ人数の設定がイケてないだけな気がするので、そこは起こらない前提でエラーを吐くようにしました。

終わりに

山札もシンプルでしたね。
これで次こそゲーム進行回りの実装に入れるかな?と思います。

完成も近づいてきました。4連休で終わらせてしまいたいですし最後までやってしまいます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【cygwin】Redmineをインストール

参考ページ一覧

留意点

  • 現在(2020-07-24)、bundleがバージョンアップを見据えた対応している。
  • この関係もあり、古い情報は rubygem, bundle に注意

ダウンロードファイル一覧

  • redmine-4.1.1.tar.gz
  • rubygems-3.1.4.tgz

cygwinパッケージ一覧

  • rubygemインストールで必要
    • ruby, ruby-devel
  • bundle installに必要
    • mysql-devel, libsqlite3-devel
  • nokogiriのインストールに必要
    • libxml2-devel, libxslt-devel

各バージョン

name version installation
ruby 2.6.4p104 cygwin
rubygem 3.1.4 rubygems-3.1.4.tgz
rails 6.0.3.2 gem
redmine 4.1.1 redmine-4.1.1.tar.gz

インストール手順

rubygems

$ tar xvzf rubygems-3.1.4.tgz
$ cd rubygems-3.1.4
$ ruby setup.rb
  • /usr/bin/gem, /usr/bin/bundle がインストールされます。

redmine

redmine動作環境に必要なパッケージの情報として、 GemFileredmine-4.1.1.tgz に含まれています。

以下はHOMEから始まります

$ tar xvzf src/redmine-4.1.1.tar.gz
$ cd redmine-4.1.1/conf
$ cp configuration.yml.example configuration.yml
$ cp database.yml.example database.yml
$ vi configuration.yml
$ vi database.yml
  • configuration.ymlの設定箇所
    • scm_subversion_command 設定例通りに設定
    • scm_cvs_command 設定例通りに設定
    • scm_cvs_path_regexp 一つあるcvs repositoryのフルパス
    • scm_stderr_log_file 設定例通りに設定
  • database.ymlの設定箇所
    • mysql2 を無効にして sqlite3 を有効にした。
      • 利用者1名のため

以降に継続

$ bundle config without 'development test' --local
$ vi .bundle/config
  • --local をつけ忘れると ~/.bundle/config に保存される

以降に継続

$ bundle lock --add-platform x86-mingw32 x64-mingw32 x86-mswin32
  • 結果として、 "GemFile.lock"が作成される。

以降に継続

$ bundle install
$ bundle update
$ bundle config build.nokogiri --use-system-libraries --local
$ bundle install
$ bundle update
$ bundle exec rake generate_secret_token
$ RAILS_ENV=production bundle exec rake db:migrate
$ RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data

remine立ち上げ

bundle exec rails server webrick -e production

localhost:3000にアクセス

課題

  • ImageMagickのインストール

参考

rubygemインストールの様子(要点のみ)

$ ruby setup.rb
  Successfully built RubyGem
  Name: bundler
  Version: 2.1.4
  File: bundler-2.1.4.gem
Bundler 2.1.4 installed
RubyGems 3.1.4 installed
Regenerating binstubs
Parsing documentation for rubygems-3.1.4
Installing ri documentation for rubygems-3.1.4

…

------------------------------------------------------------------------------

RubyGems installed the following executables:
        /usr/bin/gem
        /usr/bin/bundle

Ruby Interactive (ri) documentation was installed. ri is kind of like man
pages for Ruby libraries. You may access it like this:
  ri Classname
  ri Classname.class_method
  ri Classname#instance_method
If you do not wish to install this documentation in the future, use the
--no-document flag, or set it as the default in your ~/.gemrc file. See
'gem help env' for details.

bundle実行時の警告

参考にしたページに倣って bundle install --without development test を実行すると以下のメッセージが黄色で表示される。

$ bundle install --without development test
[DEPRECATED] The `--without` flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer do in future versions. Instead please use `bundle config set without 'development test'`, and stop using this flag
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x64-mingw32, x86-mswin32. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x64-mingw32 x86-mswin32`.
The dependency ffi (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x64-mingw32, x86-mswin32. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x64-mingw32 x86-mswin32`.

rubygemインストールの様子(詳細)

$ ruby setup.rb
  Successfully built RubyGem
  Name: bundler
  Version: 2.1.4
  File: bundler-2.1.4.gem
Bundler 2.1.4 installed
RubyGems 3.1.4 installed
Regenerating binstubs
Parsing documentation for rubygems-3.1.4
Installing ri documentation for rubygems-3.1.4

=== 3.1.4 / 2020-06-03

Minor enhancements:

* Deprecate rubyforge_project attribute only during build
  time. Pull request #3609 by Josef Šimánek.
* Update links. Pull request #3610 by Josef Šimánek.
* Run CI at 3.1 branch head as well. Pull request #3677 by Josef Šimánek.
* Remove failing ubuntu-rvm CI flow. Pull request #3611 by
  Josef Šimánek.

=== 3.1.3 / 2020-05-05

Minor enhancements:

* Resolver: require NameTuple before use. Pull request #3171 by Olle
  Jonsson.
* Use absolute paths with autoload. Pull request #3100 by David Rodríguez.
* Avoid changing $SOURCE_DATE_EPOCH. Pull request #3088 by Ellen Marie
  Dash.
* Use Bundler 2.1.4. Pull request #3072 by Hiroshi SHIBATA.
* Add tests to check if Gem.ruby_version works with ruby git master.
  Pull request #3049 by Yusuke Endoh.

Bug fixes:

* Fix platform comparison check in #contains_requirable_file?. Pull
  request #3495 by Benoit Daloze.
* Improve gzip errors logging. Pull request #3485 by David Rodríguez.
* Fix incorrect `gem uninstall --all` message. Pull request #3483 by David
  Rodríguez.
* Fix incorrect bundler version being required. Pull request #3458 by
  David Rodríguez.
* Fix gem install from a gemdeps file with complex dependencies.
  Pull request #3054 by Luis Sagastume.

=== 3.1.2 / 2019-12-20

Minor enhancements:

* Restore non prompting `gem update --system` behavior. Pull request #3040
  by David Rodríguez.
* Show only release notes for new code installed. Pull request #3041 by
  David Rodríguez.
* Inform about installed `bundle` executable after `gem update --system`.
  Pull request #3042 by David Rodríguez.
* Use Bundler 2.1.2. Pull request #3043 by SHIBATA Hiroshi.

Bug fixes:

* Require `uri` in source.rb. Pull request #3034 by mihaibuzgau.
* Fix `gem update --system --force`. Pull request #3035 by David
  Rodríguez.
* Move `require uri` to source_list. Pull request #3038 by mihaibuzgau.

=== 3.1.1 / 2019-12-16

Bug fixes:

* Vendor Bundler 2.1.0 again. The version of Bundler with
  RubyGems 3.1.0 was Bundler 2.1.0.pre.3. Pull request #3029 by
  SHIBATA Hiroshi.


------------------------------------------------------------------------------

RubyGems installed the following executables:
        /usr/bin/gem
        /usr/bin/bundle

Ruby Interactive (ri) documentation was installed. ri is kind of like man
pages for Ruby libraries. You may access it like this:
  ri Classname
  ri Classname.class_method
  ri Classname#instance_method
If you do not wish to install this documentation in the future, use the
--no-document flag, or set it as the default in your ~/.gemrc file. See
'gem help env' for details.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【cygwin】Redmineを動かす

留意点

  • 現在(2020-07-24)、bundleがバージョンアップを見据えた対応している。
    • この関係もあり、古い情報は rubygem, bundle に注意
  • 個人で利用することを前提に、sqlite3を利用する。

ダウンロードファイル一覧

  • redmine-4.1.1.tar.gz
  • rubygems-3.1.4.tgz

cygwinパッケージ一覧

  • rubygemインストールで必要
    • ruby, ruby-devel
  • bundle installに必要
    • mysql-devel, libsqlite3-devel
  • nokogiriのインストールに必要
    • libxml2-devel, libxslt-devel

各バージョン

name version installation
ruby 2.6.4p104 cygwin
rubygem 3.1.4 rubygems-3.1.4.tgz
rails 6.0.3.2 gem
redmine 4.1.1 redmine-4.1.1.tar.gz

インストール手順

rubygems

$ tar xvzf rubygems-3.1.4.tgz
$ cd rubygems-3.1.4
$ ruby setup.rb
  • /usr/bin/gem, /usr/bin/bundle がインストールされます。

redmine

redmine動作環境に必要なパッケージの情報として、 GemFileredmine-4.1.1.tgz に含まれています。

以下はHOMEから始まります

$ tar xvzf src/redmine-4.1.1.tar.gz
$ cd redmine-4.1.1/conf
$ cp configuration.yml.example configuration.yml
$ cp database.yml.example database.yml
$ vi configuration.yml
$ vi database.yml
  • configuration.ymlの設定箇所
    • scm_subversion_command 設定例通りに設定
    • scm_cvs_command 設定例通りに設定
    • scm_cvs_path_regexp 一つあるcvs repositoryのフルパス
    • scm_stderr_log_file 設定例通りに設定
  • database.ymlの設定箇所
    • mysql2 を無効にして sqlite3 を有効にした。
      • 利用者1名のため

以降に継続

$ bundle config without 'development test' --local
$ vi .bundle/config
  • --local をつけ忘れると ~/.bundle/config に保存される

以降に継続

$ bundle lock --add-platform x86-mingw32 x64-mingw32 x86-mswin32
  • 結果として、 "GemFile.lock"が作成される。

以降に継続

$ bundle install
$ bundle update
$ bundle config build.nokogiri --use-system-libraries --local
$ bundle install
$ bundle update
$ bundle exec rake generate_secret_token
$ RAILS_ENV=production bundle exec rake db:migrate
$ RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data

remine立ち上げ

bundle exec rails server webrick -e production

localhost:3000にアクセス

課題

  • ImageMagickのインストール

参考

rubygemインストールの様子(要点のみ)

$ ruby setup.rb
  Successfully built RubyGem
  Name: bundler
  Version: 2.1.4
  File: bundler-2.1.4.gem
Bundler 2.1.4 installed
RubyGems 3.1.4 installed
Regenerating binstubs
Parsing documentation for rubygems-3.1.4
Installing ri documentation for rubygems-3.1.4

…

------------------------------------------------------------------------------

RubyGems installed the following executables:
        /usr/bin/gem
        /usr/bin/bundle

Ruby Interactive (ri) documentation was installed. ri is kind of like man
pages for Ruby libraries. You may access it like this:
  ri Classname
  ri Classname.class_method
  ri Classname#instance_method
If you do not wish to install this documentation in the future, use the
--no-document flag, or set it as the default in your ~/.gemrc file. See
'gem help env' for details.

bundle実行時の警告

参考にしたページに倣って bundle install --without development test を実行すると以下のメッセージが黄色で表示される。

$ bundle install --without development test
[DEPRECATED] The `--without` flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer do in future versions. Instead please use `bundle config set without 'development test'`, and stop using this flag
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x64-mingw32, x86-mswin32. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x64-mingw32 x86-mswin32`.
The dependency ffi (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x64-mingw32, x86-mswin32. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x64-mingw32 x86-mswin32`.

rubygemインストールの様子(詳細)

$ ruby setup.rb
  Successfully built RubyGem
  Name: bundler
  Version: 2.1.4
  File: bundler-2.1.4.gem
Bundler 2.1.4 installed
RubyGems 3.1.4 installed
Regenerating binstubs
Parsing documentation for rubygems-3.1.4
Installing ri documentation for rubygems-3.1.4

=== 3.1.4 / 2020-06-03

Minor enhancements:

* Deprecate rubyforge_project attribute only during build
  time. Pull request #3609 by Josef Šimánek.
* Update links. Pull request #3610 by Josef Šimánek.
* Run CI at 3.1 branch head as well. Pull request #3677 by Josef Šimánek.
* Remove failing ubuntu-rvm CI flow. Pull request #3611 by
  Josef Šimánek.

=== 3.1.3 / 2020-05-05

Minor enhancements:

* Resolver: require NameTuple before use. Pull request #3171 by Olle
  Jonsson.
* Use absolute paths with autoload. Pull request #3100 by David Rodríguez.
* Avoid changing $SOURCE_DATE_EPOCH. Pull request #3088 by Ellen Marie
  Dash.
* Use Bundler 2.1.4. Pull request #3072 by Hiroshi SHIBATA.
* Add tests to check if Gem.ruby_version works with ruby git master.
  Pull request #3049 by Yusuke Endoh.

Bug fixes:

* Fix platform comparison check in #contains_requirable_file?. Pull
  request #3495 by Benoit Daloze.
* Improve gzip errors logging. Pull request #3485 by David Rodríguez.
* Fix incorrect `gem uninstall --all` message. Pull request #3483 by David
  Rodríguez.
* Fix incorrect bundler version being required. Pull request #3458 by
  David Rodríguez.
* Fix gem install from a gemdeps file with complex dependencies.
  Pull request #3054 by Luis Sagastume.

=== 3.1.2 / 2019-12-20

Minor enhancements:

* Restore non prompting `gem update --system` behavior. Pull request #3040
  by David Rodríguez.
* Show only release notes for new code installed. Pull request #3041 by
  David Rodríguez.
* Inform about installed `bundle` executable after `gem update --system`.
  Pull request #3042 by David Rodríguez.
* Use Bundler 2.1.2. Pull request #3043 by SHIBATA Hiroshi.

Bug fixes:

* Require `uri` in source.rb. Pull request #3034 by mihaibuzgau.
* Fix `gem update --system --force`. Pull request #3035 by David
  Rodríguez.
* Move `require uri` to source_list. Pull request #3038 by mihaibuzgau.

=== 3.1.1 / 2019-12-16

Bug fixes:

* Vendor Bundler 2.1.0 again. The version of Bundler with
  RubyGems 3.1.0 was Bundler 2.1.0.pre.3. Pull request #3029 by
  SHIBATA Hiroshi.


------------------------------------------------------------------------------

RubyGems installed the following executables:
        /usr/bin/gem
        /usr/bin/bundle

Ruby Interactive (ri) documentation was installed. ri is kind of like man
pages for Ruby libraries. You may access it like this:
  ri Classname
  ri Classname.class_method
  ri Classname#instance_method
If you do not wish to install this documentation in the future, use the
--no-document flag, or set it as the default in your ~/.gemrc file. See
'gem help env' for details.

リンク一覧

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveStorageをクラウドストレージ(GCS,S3, etc)で使ってた時に処理が遅くなる問題

概要

Heroku上で動かしていたRailsアプリの処理が遅かった時の調査メモです。
結論として、画像保存に使用していたGCPのクラウドストレージの部分がボトルネックとなっていました。

環境

  • Ruby 2.6.3p62
  • Rails 6.0.2.2

背景

Herokuの無料プランでRailsを使った趣味のWEBサイトを構築運用しています。
当初から処理はあまり早くないものの、SQLに関してもRedisでキャッシュを入れたりしていたので、まぁ無料ならこんなものかと思っていました。

しかしながら、一部のページのみ他のページより早い事に気がつき、何が原因かを調べることとしました。

調査

調査方法

一般的な方法ですが、下記のコマンドでログを出力させて処理時間を調べる事にしました。

heroku logs --tail

デフォルトでRailsはDEBUGログで処理時間を色々出してくれるので、まずはそこを確認する事にしました。

ログの調査結果

当初はキャッシュしていない部分のSQLが遅いのかと思っていましたが、
SQLに関してはキャッシュも効いて遅いクエリも見つかりませんでした。

しかしながら、代わりに下記のような処理時間に100ms以上かかっているログが何箇所が見受けられました。

 GCS Storage (169.9ms) Checked if file exists at key: variants/xxxx

該当のサイトは画像をGCSにアップロードしており、その部分が関連している事が推測できました。
また、variantといったキーワードからActiveStorageの該当機能が影響していることもわかります。

Variant

このvariantに関しては下記の参考資料などを見ていただければと思いますが簡単に言えば、アップロードした画像をそのまま使わずにリサイズ等を行う機能です。
参考

この機能を使えばわざわざ自分でリサイズ処理などを実装する必要がなく、更に初回アクセス時にリサイズした画像を保存して裏で再利用してくれてかなり便利な機能です。
ですが、今回はきの機能が何か悪影響を与えているようです。

ググってみる

これ以上の調査は難しいと判断し、とりあえず似たような症状が他でも起きているのかググってみました。

結果として、AWSのS3などで似たような症状と思われるものが何点か見つかりました。

https://stackoverflow.com/questions/49415911/activestorage-checking-if-file-exists-is-slow
https://github.com/rails/rails/issues/32548
https://github.com/rails/rails/pull/37901

拙い英語力で斜め読みした所、下記のように判断しました。
(間違っていたらすみません。)

  • 変換し保存した画像の存在チェックを行なっており、そこで時間がかかっている
  • 既に対応する修正を行なっており、Railsバージョン(6.1)に含まれる

解決方法

上記を見て、最新版にすれば解決するかと思いましたが、残念ながら6.1はまだリリースされていませんでした・・・。

結論として、varianによる圧縮処理をやめて元の画像を使用する事としました。
元の画像に関して、そもそもアップロード前に手動で画像ツール等でリサイズなどの補正をしているため問題にはならないだろうという判断です。

結果として元の100ms以上かかっていたログは消えて、下記のような1ミリ秒の処理に変更され、体感的に速度が早くなったように感じます。

GCS Storage (1.5ms) Generated URL for file at key: xxxx 

まとめ

という事で、クラウドストレージ(GCS)でActiveStorageで加工した画像を使用する際に処理時間がかかっている事が原因でした。
根本的な対策はせずに機能を使用しないという、微妙な形で対応する事としました。

いずれリリースされるRails6.1では対策がされているようなので、アップデート後にはまた機能を使用したいと思います。

S3やGCSなどクラウドストレージをお手軽に使用できるActive Storageですが、裏で色々と隠蔽してくれているおかげで、逆に見えない箇所で性能に影響を与える事がわかりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NGSシミュレーションの古典的ソフトwgsimで遊ぶ 〜謎の生物プー〜

はじめに

謎の生物 プー それは、太古から存在するかもしれない 謎の生物です。

image.png
プーの想像図

wgsimとはなんじゃらほい?

wgsimとはバイオインフォ界の巨星 Heng Liさんが作ったNGSのシミュレーターです。
2011年のソフトですが問題なく動きます。

https://github.com/lh3/wgsim

インストール

Biocondaを導入している場合は

conda install wgsim

〜 謎の生物プー 〜

image.png

謎の生物プーは、まったくの気まぐれから生み出されました。
神は生物を創造します。(進化など言語道断です。)
あなたは神なので、ちょっとしたウイルスと同じぐらいの配列、5000塩基の生物を創造しましょう。この生物の名前を「プー」と名付けました。

プーのリファレンス配列は、ランダムで生成しています。(筆者は絶滅危惧種のRuby派なのですが、ほかのプログラミング言語でも同じことができるでしょう。)

genome = Array.new(5000){ %w[A C T G].sample }
puts '>poo chr1'
genome.each_slice(80) do |l|
  puts l.join('')
end

このスクリプトを実行すると、こんな感じで配列が出力されると思います。

これが謎の生物プーのリファレンスゲノムだッ!!!

>poo chr1
AAAGCGCTGGATCATGCTTTAAGGCTTAGTTTTATGGCCCTAAACACGGAGACTGAAGCCGCTTGAGTCCGGTTTCAGAG
AGATTTGGTTTCACCCACAACTGCCAGCATATAATGGACCGTCGGCCGTACACGTTATGAGCCTGGCAGGGGTTACTACG
CGCCACGGGCTAGAGACCTAGTGGCGAAACGTGAAGGGCAATTATGGAATCCCGTACGCGCAATAAATGTCTACGCATTC
CCGGGGGGTGTTGTAGTCCCACGGCCCCGGACCTGCATTGGTCCAAATTCTCTTCGGTGCATATTAGCTTCATCGGAACC
GGCCTTTCTAATTCAGAACTGTTCGGTCATACACTATCTTCCAACACGCCGTGCACGAGTTGCACGGAGATCTAGTTGTT
ACTCGACGCGCGTACGACCGAGGCGGGGCTTAACACGCGAGTAGAAGCCCTGGGTTATCCACCACTTAATTTTGTAAAAA
GCGCTTTCAGTACTACACGGCTTGACCGTAGCTCTGTCGGACTTGTGACCCCTTCCGGCGAACATTAATTGTACCCCTTC
TTCTGACGGATTGGTTAGTAGTCCAAGCAGTCTTCACCCCAGGAGCACTTCTTCTTGCCGCGAGGCCATTCAGTCCAGTT
CGTGATGGCGGGGGTACCGATGCAAAAGCTCGTCTTTCTTGAGGCATCCACAGCCAGAGGCGGGGTATTTACGAGATTAA
GTTCTATCATTGCACTAGACAAAGCAAGTACAGCTCGGCAAAAAGAAACTCGTGGGCGCGTCTTGACCTGCAGCTAATAC
TGACTAGCATCCGTAGATATTACCGATCGTGCCGCACAAGGGAAATCTCGAGGCTGACAACGTCTTTGCCAAGGCAGTGG
CCGTGTCTTCTCGCCAGGGGTAAAGGTGCCGCTGGACTAGCGCTAACTTAGTGTCGTAATATCTCCATATGGACGATTTC
CCCGATCTTACCGGCTAGCCGCTGGTTGTATGCCGCGCCCAGGCCACCGAGTCTGACATGTTCCAAGGGCCTCATGTGAA
AGCGATGTGCTGATCCAGCATGGCCTTCAGCCTCACAAGACTGAGAGTTGGTGTAGAAACCCGGTTTGTTGGTAGTATGA
TGCTTAAAAATCGGTTTAATTTAGCTTGGTTAGATTTGAACGTGAAGAGCCTATGTCCAAAGACCAACTAACCTACTCAG
ACTAAGATTTACCGATTTCTCGTTTTGACTGAGATACGTAGTTAGCTATACGGGGCGTCGCCATCCTCTACCCCTTGAAT
ATGATTCGTAAGTGATGCCATTCTCGTGCTCGTGCGCTATGAATAGCAATGTCACCACAATAGTGCCCGGTGGCCGACAA
TCCGAGACCATGTCCGTCCTCATTCATGCGTAGTTAGTAGTGTATCGACTCACCTGGAGATGACCTCTATGTCCTGCTGC
GGTACGGCACGGCAGGATCTTTCTAGAAACATCGGCGATGCCCCAGAGGTTCTCTAAACTGTGTCATCTTTTTCTGCGGT
GCGTGGCTGGTCAAACCATTCAAATATATGAGAGTCATGCGCTAGCTCCCGGTAAAACCCCACAATAACGGGATTTGGGG
TTGAAAATGCGAGGCAGCGCACATGGATCAGGCCATTTAGGGTGTGATTGGTATGTTCCGAAAAAGCTGATGAGAACTAT
GCCCCGTTTACTTTGCCAGTAGAAAGAGTAGGAAGTAGTGTAACGCGGGCACCGGAAAACGACCGACACGATACTTTCCC
TTGTGCTGGAATTGTTTGATATATTAGGACGAGAATAGTTCTCTCCATTCCGCCGGCGTATAGACCAGGCCATGCCGTTC
TATTTCCAAGTTCCTTTGAAGGATCGGTGTTATTTCGGATGTCGTCGAGTAGTCGTGGGGCGTTAGGGTCTAAGCGACCA
GTTGTAGCTAATCGGGTGTCCTCCACTAACTGCTATTAGCATAGTACACCATGCTGCTTCAACTCATTTGGGATTTGCAC
TACTCTAGGCCAGAGATACAGATGCAAGCCGACTTAGCTGCACTAGCCAAGATTGAGCCCTAGGGCGTAGCGACCCTGAG
AATCCTGGCTAGCCCCCCTAGCCCCGGAAACTGCCCGTGTTGCTCTCGAAGCAGTTAGTAATCATCGTAATTTTGTCAAG
ATGGCCGTAGAGTGCAACCTGCAAGGCTGCAGGGTGTGTTCGGCATATTAATGCGGACAGTGCAGTGGACAAAGAGTCTC
GGAAGCGACGTGTATACAGTAGTGAGTAAATTGTTAAGCTTACTATCGCAGGAACTTCTGATCTGCCCGACCGTCGAGAT
CGAGTCTAGTAACAAGATTGCAGTCCTCAAGACTAGAAGTACCTAACTCCCTCAAATGCGAGTCCCAACAACTCATCCTC
ACCTCTGGAATCTCACCTCTAAGCATGACAGGCTAGGACCATTTAACATAGTTACAGTGACCTGATACGTAACGCCTCGA
AAGGAAAGGCGCGCCTAGGCAGCAATTGCAACGAACTCGGACGTCAGTACTCAACCAACCGAGTTATTTCAGCGTCGCGA
ATCTAATGCATCTTCCTGCTTAAGACGTCGTACTCCGGTGTCACCCTTCGACTAACTTAAAATCGCGCATTGGCAAGCAT
ACCTCTAATACCGGATCGCTCCCCTCAGGTTTACTTGATGAGCGCTGAACCCAACCATAATTTCGGCAAGGTAATCGCTT
ATCGTTCCACCCAGGCGTGAATAATTCATGCCCAATGCAGGTGACCGTATCTCAGGGAGATATAAGTGAAAGTTATGTGG
AAGGTATTGTAGTACATTTCAGGGTTCATCTAAGCTTCAGGACCACAGATGCAGAAGACGGGTTAAAGGACATGTCGGCA
CGAAGACATATCTTGTGGATCCCTATTCAGTGCCCGTGACTCAACAGATAGACTCATTTTCTCCTTTGGCGTGTCCGCAG
GTAAGATAACTTCAGTGTCCGAGCGGCGGAGTGGGTGTTCACGTCGTCGAGTGTCCATTTCGATTGGGGATGTTGGCTCG
CATACCACACGACCCTTTAGTGGACGGTCTAGTTTGTATACTCCCCGATTGGACTTTACCCGCGAAGCTAGCACTGTAAA
TATTGCCCCGGAGTGGCGGGATACGTTGGATATGCCTATACTGGACCGCAGCCTATTACATCAGATCGGCTTACCGCCAA
TCTTAGTGCTCAGCTACCCAGAGCTGTATCCATGGACATAGGCGCTATGACAAGCAGTACTGTAAAGCAGGGACAGCTTT
ACTATGTCGACAGCCGGCGTACGTCGTTGGGCTACCGCCGGCCTAGTTCCTAGTCAGTTCCGAGGCATAATCTACAAGTC
TAAGTAGGGCGCCATGATGTCACGAGACTTATAGGAGTCCACCGGCAAGTGCGTTGACGTAAATACATATGTGTATGCAG
CACTTTTGACAGGCCCGTAGATTTTGCACAAACAGCCGGGTGTGGTTCAAAAAGGGTGTTGCGGTCAGCCGCAGCAGCGC
AATTGTAACCAATCTACCTCCCGTTTGCCGACCGCTCGCGCAGATTCATGTAGGAACCGCTAGTTCCCCGTCCGCTACAT
TTCAATGGGCTTTTGCATGTCCTATCGCGATACCATCTCTGGTGGCGATGATATTTTATCAGGTTGCATTGGCCCAGTCC
ATCTTCGCGGCCGATTGGGCACGCCATTAGTTGCTCTCTGCGCAATCGCAGCTATACTAATGGTATGGTTGGGGAAATCA
TTGGAGTGCATTATTCCGCCCTCTTAACCTGTTGTGAACGGAACCGCAGTACGGTAGATTGCAAAGTCCAAGGTTTCTGG
ATTGTCTCTGAAAATGAAAGTTCTGCTCTGTCAAGTGATAAACGGAACGTCCTAGGAGGCGCCTACCGCGTTTAGAGAAG
TAACCCACGATATCTCGAACAAAAAATCAAAAGCGGTGTAGGTACAAAACGTATCGCTTACATCCCGTGAAGCTTAGTAG
GACGCACCCTTCATCTCTCAGGGGGTCGCGTACACGATGTCCGTCTTTTATCGTGCCACGTGTACAAGTAGGGGTGCAGA
AATAACTGTACTAATGCAACGTTTCAGTTCTTCTGATGAACATCTCACACGGTTGTCCCCTCTAATATTAGGATTATCGG
CCTAGACGTGTTATGCACGCCGACCCCTTTCCCTAAGTGCTTCCGCAATGAAGTCGCGTATTTGTAACACACATTGGGTA
TGAATATAACTGTCCCTCCTGGAATAAGGAGTGCGCTTATCCTTTCGGACGTCAGAGCCCAGGGAGGAGACGCGTCAGAT
GCCATAGCCTATCGGGATCCGTCGTGCTAGTAGAACCAAAGTAACGGTGCTGTTAAATCGTTCCCTCAGCAGTCCGGGGT
TGACAGGTACAGTGTTTCCAACTACTAGAGGATCTCGCTGCTATAGAACGATACCACTGCGGGGGACACCGCCACTTTTC
GAGCTGTGGGGCCAGAAGTGCTGTCTGGATAATTCGGTAGGCATCAGCGGACAGCGGAGCTCGTAAGTGCCATGCACTTG
TAGCCCATTACGATGAGATCGGTTCCGCCAACTCCGTCAGCACAGGTATCCACGAGGAGTCACCGTCCGGCATGACGCAG
GACGGGGGCGGTGGAAATAATCGAGGCGGATACTCTTCATTTGTAGGCGCCAGCTACAGCGGTCCGGCGGGTCAATACCG
CATCCGCAGACATAATTGACCTCACTCAGCTCGCACGAACCTCCCTACTCTTTTCTACTCGAGGGTGCCTGATCAGAGAA
CGGAAGACGCTATAGGCAAGGCGGGCACGCCTGGAATACAGTCCCTTTCCGAATATTCACACCCGCTACCTATGTCGTAG
CGTTGCCGGGCCCGTTATCCAGACCGAACCCGTTGTCTGTTATAACATATTGAGTAATGGGCAGACAAACCCGTGTAGAG
TCCTCTCGGTTCCACATATCGAAGAGGGCGATCCGTACGA

ながい。

poo.fa として保存します。

wgsimを動かす 〜プーの全ゲノムシークエンス〜

 さて、謎の生物プーは ヒツジ用の歯磨き粉から発見されました。ところでヒツジってハミガキするんでしょうかね?さあ、私は詳しいことはわかりません。ただ、プーは若いヒツジがこっそり使用していた歯磨き粉から発見されたと報告されてますので、それを信じることにしましょう。学名は、顕微鏡でみた様子がぷるぷるしていることから、プル・プルプルスと言うそうです。(今適当につけた)

 さてプーを発見した人はその後人生に山あり谷ありプーに対する興味を失って、その後50年間プーは放置していました。しかし天と地の気のめぐり、赤道の気温の上昇、町内会長のおののき、ほのかな春のおとづれ、会長と社長の和解など、たまたまいろいろな偶然の成り行きがあって、プーが全ゲノムシークエンスされる時がやってきました。さて、あなたは神なのでゲノムシークエンスの結果をwgsimで求めます。

wgsimの実行に先立って、オプションをみてみます。

wgsim --help
Options: -e FLOAT      base error rate [0.020]
         -d INT        outer distance between the two ends [500]
         -s INT        standard deviation [50]
         -N INT        number of read pairs [1000000]
         -1 INT        length of the first read [70]
         -2 INT        length of the second read [70]
         -r FLOAT      rate of mutations [0.0010]
         -R FLOAT      fraction of indels [0.15]
         -X FLOAT      probability an indel is extended [0.30]
         -S INT        seed for random generator [0, use the current time]
         -A FLOAT      discard if the fraction of ambiguous bases higher than FLOAT [0.05]
         -h            haplotype mode

ところどころ私にはよくわからないオプションがありますが(-s が何の標準偏差なのか、-A とか -h がどう動くのかとか)、それは後で勉強することにしましょう。(しない)

ここではリード数を1000本にします。

wgsim -N 1000 poo.fa poo_1.fq poo_2.fq

実行すると突然変異が表示されます。poo_1.fqpoo_2.fq が生成されたと思います。簡単ですね。

poo 977 A   W   +
poo 1210    T   W   +
poo 1655    G   K   +
poo 1874    T   K   +
poo 2043    C   S   +
poo 2378    G   T   -

生成されたfastaファイルの中味を見てみましょう。

head -n 12 poo_1.fq
@poo_418_960_2:0:0_1:0:0_0/1
TCGTCGAGTAGTCGTGGGGCGTTAGGGTCTAAGCGACCAGTTGTAGCTAATCGGGTGTCCTCCACTAACT
+
2222222222222222222222222222222222222222222222222222222222222222222222
@poo_624_1182_3:0:0_1:0:0_1/1
CTTAGGACATAGGCTCTTCACGTTCAAATCTAACCAAGCTAAATTAAACCGATTTTTAAGCATCATACTA
+
2222222222222222222222222222222222222222222222222222222222222222222222
@poo_2608_3166_3:0:0_0:0:0_2/1
GTCCAGTATAGGCATATCCAACGTATCCCGCCACTCCGGGGCAATATTTACAGTGCTAGCTTCGCGGGTA
+
2222222222222222222222222222222222222222222222222222222222222222222222

まあ、普通リードの後半の方がクオリティスコアが落ちてきたりすると思いますが、そうはなっていませんね。
Fastqcでsequence qualityを見て確認してみましょう。

image.png

クオリティが一定(しかも低い)ことがわかります。

一般的には下図のようにリードの後方にいくにつれて、クオリティが下がっていくはずです。(画像は Fastqcの公式ホームページから https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)

まあそこのへんは昔のソフトなので仕方がないということで。

BWAを使ってマッピング

さて、全ゲノムシークエンスなので、bwaでマッピングします。なになに。minimap だと? ここは10年前の世界観で、wgsimで遊ぶ記事なんだぞ、ロングリードなんて存在しないんだ!!おっと取り乱してしまいました。神は取り乱さないように気をつけなければなりません。

まずはともあれindexの作成します。

bwa index poo.fa

プーのゲノムサイズは小さいので一瞬で実行が完了すると思います。
それではbwaをかけてみましょう。(bwa も Heng Li氏の作ったツールです。まさに神といって良いでしょう。)

bwa mem poo.fa poo_1.fq poo_2.fq > poo_wgs.sam

さて、SAM → BAM、BAM → sorted BAM、インデックスまでつけてやりましょう。

samtools view -bS poo_wgs.sam > poo_wgs.bam
samtools sort     poo_wgs.bam poo_wgs.sorted
samtools index    poo_wgs.sorted.bam 

IGVでリードの様子を見てみよう

IGVで、poo.fa をリファレンスゲノムに設定し、poo_was.sorted.bam を開きましょう。

うおぉぉぉ!できたよ!できたよ兄さん!(うるさいな)

igv_snapshot.png

さてIGVには、実は便利なスプリットビューの機能がついています。これを利用して、突然変異の場所を観察してみましょう。

image.png

うおぉぉぉ!兄さん(略)

ちゃんと突然変異が検出されていますね。

おわりに

さて、謎の生物 プー に関する記事を気まぐれで書いてみましたが、思いのほか書いていて楽しいのと、勉強にもなることが判明しました。プーシリーズ、また気が向いたら書くかも。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

redirect_toとrenderの違い

はじめに

私はredirect_toとrenderの違いを理解していなかったために、redirect_backを使用しエラーメッセージを表示をすることができず、悩みました。もし、悩んでいる方がいれば少しでも助けになれればと思います。

redirect_to

一回指定したアクションを実行して、そのアクションに対応したビューを表示させる
ちなみにredirect_backはredirect_toと同じ動き方をし、ユーザを直前のページに戻すことができます。

render

アクションを実行せずにビューファイルを表示する

2つの動き方

・redirect_to   : controller → URL → route → controller → view
・render     : controller → view

使い分け

・render: ログインや入力形式に失敗した場合など = ただエラーを表示させるだけ
・redirect_to: データ更新/削除が必要な場合    = controllerの処理が必要

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyでドローポーカーを作ってみる~実装編3(プレイヤー)~

概要

rubyでドローポーカーを作ってみる~準備編~

rubyでドローポーカーを作ってみる~test-unit準備編~

rubyでドローポーカーを作ってみる~実装編1(カード)~

rubyでドローポーカーを作ってみる~実装編2(役)~

に続いて。

ソース: https://github.com/rytkmt/ruby_poker

今回はカード、役を扱うプレイヤーの実装に入っていきたいと思います。

プレイヤーの実装

今回もまずは要件を整理してみます。

  • 始め5枚のカードをもらう
    • 役を判定する
  • 任意の枚数、任意のカードを交換する
    • 再度、役を判定する
  • プレイヤーの勝敗を判定する
    • 実質、プレイヤーの持つ役を判定する

ファイルの作成

ruby_poker/player.rb
module RubyPoker
  class Player
    def initialize(cards:)
      @cards = cards
      @hand = Hand.new(cards: cards)
    end
  end
end

もらったカードで役を判定するまではこれで出来ました。

ruby_poker.rb
require "ruby_poker/version"
require "active_support/all"
-require "ruby_poker/card"
-require "ruby_poker/hand"
+Dir[__dir__ + "/ruby_poker/*.rb"].each { |p| require p }

module RubyPoker

requireも忘れずに行いますが、1ファイルずつ追加していくのもあほらしく感じたので全て読み込むように変更しました。

カードの交換

続いて、任意のカードの交換を実装します。

  • いらないカードを捨てる
  • 新しいカードをもらう
  • 新しいカードにて、再度役を判定する

カードの交換は要素番号をもらい削除するようにしようと思います。
こんとき、要素番号で削除するため小さいものから削除してしまうと要素がそのぶんずれておかしなことになってしまいます。
なので、大きい番号から削除するようにしないといけないので注意して実装します。
検証)

x = %i[a b c d e]
# a & c & d    =>   [b, e]
indexes = [0, 2, 3]

indexes.each { |i| x.delete_at(i); p x }
# => [:b, :c, :d, :e]
# => [:b, :c, :e]
# => [:b, :c, :e]

x = %i[a b c d e]
indexes.sort.reverse_each { |i| x.delete_at(i); p x }
# => [:a, :b, :c, :e]
# => [:a, :b, :e]
# => [:b, :e]

カードは山札からもらう必要があるため、捨てるカードの枚数だけ新しいカードをもらえばプレイヤーの責務の範囲は小さくなるかと思いました。
なので、そのように実装してみましょう(あとから変える可能性もありますが、、)k

ruby_poker/player.rb
    def change(indexes:, new_cards:)
      raise(ArgumentError) unless indexes.size == new_cards.size
      raise(ArgumentError) unless indexes.all? { |i| (0..4).include?(i) }

      indexes.sort.reverse_each { |i| @cards.delete_at(i) }
      @cards += new_cards
      @hand = Hand.new(cards: @cards)
    end

プレイヤーの勝敗判定

今回もComparableを使用します。
結局は役にて判定するので委譲するようにします。

delegateはactive_supportにてModuledelegateメソッドが拡張で用意されています。
ClassModuleのサブクラスなので同じくdelegateが使えます。

ruby_poker/player.rb
    delegate :<=>, to: :@hand

↑ダメでした。。
<=>のotherが、そのままdelegateだとhandに渡されてしまうため、自分のhandとotherのplayerを比較してしまうことになってしまいました。

ruby_poker/player.rb
    attr_reader :hand
    def <=>(other)
      @hand <=> other.hand
    end

おとなしく普通に実装しました。。

これで完了。楽ですねー

終わりに

今回は、勝敗などに関わる機能面での処理のみ実装しました。
ここから、ゲーム進行のことを考えるとコンソール出力回りを実装する必要もでますが、それは次の進行回りを検討するタイミングで追加するようにしていきたいと思います。

続き


rubyでドローポーカーを作ってみる~実装編4(山札)~

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Rails レコード検索、無ければ作成 find_or_create_by メソッド

find_or_create_by メソッドとは?

find_or_create_by メソッドとは、findメソッドとcreateメソッドが合わさったメソッドであり、
引数に渡した条件に該当するレコードをテーブルから検索し
該当するレコードがあれば取得、無ければ作成してくれるメソッド。

使い方

モデル.find_or_create_by(検索条件)

例:usersテーブルの中からnameが"suzuki"のレコードを取得、無ければ作成する場合

User.find_or_create_by(name: "suzuki")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】vendor/bundleにgemがインストールされたはずなのに、requireでエラー

経緯

スクレイピングについて少し学習しようと思い、
Rubyを使って20分でスクレイピングの大元を作るを参考に実装。
「Gemはvendor/bundle配下にインストールするようにします。」とあるので、
指示通りbundle installするときに --path vendor/bundle のオプションを追加。

エラー発生

記事の通りのcodeをmain.rbに書き込み、main.rbを実行すると、

training $ ruby main.rb 
Traceback (most recent call last):
    2: from main.rb:2:in `<main>'
    1: from /Users/yusaku/.rbenv/versions/2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/Users/yusaku/.rbenv/versions/2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- nokogiri (LoadError)

'require': cannot load such file -- nokogiri (LoadError) と書かれているが、
nokogiriはvender/bundle 配下にbundle install済みである。

training $ ls vendor/bundle/ruby/2.6.0/gems/
byebug-11.1.3       method_source-1.0.0 nokogiri-1.10.10    pry-byebug-3.9.0
coderay-1.1.3       mini_portile2-2.4.0 pry-0.13.1

解決

エラーの内容をよくみて見ると、
/Users/yusaku/.rbenv/ 配下を探しに行っているように見える。
それでハッとしたのだが、bundle execをしていなかった。

training $ bundle exec ruby main.rb

これでエラーも発生せず、main.rbが実行できた(require "nokogiri"もできた)。
bundle execをすることによって、同一ディレクトリ配下でgemを探してくれたように見える。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「RSpecが動かない!」原因はspringだったので調べてみた

あるアプリケーションでRSpecを導入し、いざテストコードを実行したらエラーが出てしまった。
何度見直しても設定や記述に問題はないはずなのに、なぜ動かないのか。。

原因は「spring」というgemでした。
下記のコマンドをアプリのディレクトリで入力し、再度テストコードを実行すると正常に作動しました。良かった。

mac@ myApp % spring stop
Spring stopped. ←この表記が出ればOK

ちなみにこのspringはrailsコマンドを入力すると自動で再始動しますのでご安心を。

このspringというのは何者?

簡単に言えば、railsのキャッシュみたいなものです。
rails newした際に、Gemfileにデフォルトで導入されています。

今回の場合だと、RSpecの導入をする前のキャッシュがたまたま残っており、
その状態でテストコードを実行すると「RSpecなんて使えないよ」っていうエラーが出てしまったようですね。

ちなみにspringの状態を確認するためにはこんな感じで確認できます。

mac@ myApp % spring status
Spring is running:

97404 spring server | myApp | started 10 secs ago   
97405 spring app    | myApp | started 10 secs ago | development mode   


spring stoprails cexitspring statusの順番で入力するとこのようになります。

mac@ myApp % spring stop
Spring stopped. 

mac@ myApp % rails c
Running via Spring preloader in process 97418
Loading development environment (Rails 6.0.3.2)
irb(main):001:0> exit

mac@ myApp % spring status
Spring is running:

97404 spring server | myApp | started 10 secs ago   
97405 spring app    | myApp | started 10 secs ago | development mode



ちなみに下記のコマンドではPC内で稼働してるspringを全て確認できます。

mac@ ps aux | grep spring
mac        97139 101.0  0.5  4370124  43216   ??  Rs    2:24AM   0:00.73 spring app    | myApp3 | started 0 secs ago | development mode      
mac        97106   0.2  0.3  4352516  24104 s002  S     2:24AM   0:00.42 spring server | myApp3 | started 39 secs ago   
mac        96929   0.0  1.2  4462288 100216   ??  Ss    2:20AM   0:03.23 spring app    | myApp2 | started 4 mins ago | development mode      
mac        77394   0.0  0.1  4352516   8816   ??  S     05PM     0:01.03 spring server | myApp2 | started 56 hours ago   
mac         4764   0.0  0.0  4486444    472   ??  Ss    5 720    1:18.61 spring app    | myApp1 | started 439 hours ago | development mode      
mac         4760   0.0  0.0  4351492    432   ??  S     5 720    0:01.58 spring server | myApp1 | started 439 hours ago   
mac        97142   0.0  0.0  4276476    696 s002  S+    2:24AM   0:00.00 grep spring

killコマンドでもstopできるのかな、と思い実行してみましたがすぐ復活して止めることはできませんでした。
やはり該当のディレクトリに移動し、素直にspring stopした方が良さそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyでドローポーカーを作ってみる~実装編2(役)~

概要

rubyでドローポーカーを作ってみる~準備編~

rubyでドローポーカーを作ってみる~test-unit準備編~

rubyでドローポーカーを作ってみる~実装編1(カード)~

に続いて。

ソース: https://github.com/rytkmt/ruby_poker

今回は役について実装を行っていきます。

役の実装

改めて要件を整理してみます。
- 役は5枚のカードをもとに判定を行う
- プレイヤーは役をもとに勝敗を比較する
- 役の比較をするために「役」クラスを作りComparableを使えば比較ができそう
- 役の中でも、同じ役の際に比較するカードを保持する(1枚)
- 役は英語で「 hand 」 ここ大事。

ということでいざ実装。

ファイル作成

まずは普通にファイル作成

ruby_poker/hand.rb
module RubyPoker
  class Hand
    def intialize(cards:)
    end
  end
end
ruby_poker.rb
require "ruby_poker/hand"

役判定

「役」のクラスを生成しているということは、そのタイミングで判定まで行うべきですよね。。

なので、生成時に判定メソッドを使用し種別と同役時の比較用カードをセットするようにする

生成

ruby_poker/hand.rb
    attr_reader :hand_type, :comparison_card

    def intialize(cards:)
      raise(ArgumentError) if cards.size != 5
      @hand_type, @comparison_card = judge(cards: cards)
    end

  private

    def judge(cards:)
    end

役の判定にcardsを使うだけで保持する必要がないためインスタンス変数にはしないようにする。

役判定をどのように実装するか・・・

役は多いので1つ1つメソッドを用意して・・とやるのもなんか切り分けが出来ていない感が残る。。
なので、判定moduleにして切り出すことにしましょう。

そして、判定は強い順番に行っていき初めに一致したものをセットすればよいため、
まずはカードの時と同様に、定義を行います。

タイプの定義

英語名はこちらを参考に・・笑
https://en.wikipedia.org/wiki/List_of_poker_hands

ruby_poker.rb
  HAND_TYPES = %i[
    royal_straight_flush
    straight_flush
    four_of_a_kind
    full_house
    flush
    straight
    three_of_a_kind
    two_pair
    one_pair
    high_card
  ].reverse.freeze

動的にモジュールのクラスを取得したいため、スネークケースからキャメルケースに変換を行ってconst_getで取得したい。。
なので、そこの文言変換のためにactive_supportのgemを使用しましょう
(普段rails触ってるとどうしてもこの辺りのかゆいところに手が届くのが便利で使いたくなります)

Gemfile
gem "activesupport"
$ bundle install

これでインストール完了

ruby_poker.rb
require "active_support/all"

読込も忘れず行う

モジュールに判定を移譲

ruby_poker/hand.rb
Dir[__dir__ + "/hand/hand_type_judgers/*.rb"].each { |p| require p }

module RubyPoker
  class Hand
ruby_poker/hand.rb
  private

    def judge(cards:)
      matched_cards = nil
      matched_type = RubyPoker::HAND_TYPES.find do |type|
        judger = RubyPoker::Hand::HandTypeJudgers.const_get(type.to_s.classify) # <= ここのためにactive_support
        matched_cards = judger.judge(cards: cards)
      end

      [matched_type, matched_cards.max]
    end

handの内部処理を切り分ける形で、あくまでhandの責務内だと思ったので、handのインナーモジュールとして実装するようにします
Judgerの.judgeは一致した場合、同役判定の対象となるカードを必ず返すようにし、一致しなかったらnilを返すようにする

役判定モジュール実装

ロイヤルストレートフラッシュ
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/royal_straight_flush.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module RoyalStraightFlush
        def self.judge(cards:)
          return nil unless cards.map(&:suit).uniq.size == 1
          return nil unless cards.sort.map(&:number) == [10, 11, 12, 13, 1]
          cards
        end
      end
    end
  end
end
ストレートフラッシュ
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/straight_flush.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module StraightFlush
        def self.judge(cards:)
          return nil unless cards.map(&:suit).uniq.size == 1
          min_number_level = cards.min.number_level
          expected = [*min_number_level..min_number_level + 4]
          return nil unless cards.sort.map(&:number_level) == expected
          cards
        end
      end
    end
  end
end
フォーカード
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/four_of_a_kind.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module FourOfAKind
        def self.judge(cards:)
          cards.group_by(&:number).detect { |k, v| v.size == 4 }&.second
        end
      end
    end
  end
end
フルハウス
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/full_house.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module FullHouse
        def self.judge(cards:)
          grouped_number_cards = cards.group_by(&:number)
          three_card_number, three_cards = grouped_number_cards.detect { |k, v| v.size == 3 }
          return nil unless three_card_number
          grouped_number_cards.delete(three_card_number)
          return nil unless grouped_number_cards.detect { |k, v| v.size == 2 }
          three_cards
        end
      end
    end
  end
end
フラッシュ
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/flush.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module Flush
        def self.judge(cards:)
          return nil unless cards.map(&:suit).uniq.size == 1
          cards
        end
      end
    end
  end
end
ストレート
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/straight.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module Straight
        def self.judge(cards:)
          min_number_level = cards.min.number_level
          expected = [*min_number_level..min_number_level + 4]
          return nil unless cards.sort.map(&:number_level) == expected
          cards
        end
      end
    end
  end
end
スリーカード
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/three_of_a_kind.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module ThreeOfAKind
        def self.judge(cards:)
          cards.group_by(&:number).detect { |k, v| v.size == 3 }&.second
        end
      end
    end
  end
end
ツーペア
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/two_pair.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module TwoPair
        def self.judge(cards:)
          pairs = cards.group_by(&:number_level).select { |k, v| v.size == 2 }
          return nil unless pairs.size == 2
          pairs.max.second
        end
      end
    end
  end
end
ワンペア
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/one_pair.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module OnePair
        def self.judge(cards:)
          cards.group_by(&:number_level).detect { |k, v| v.size == 2 }&.second
        end
      end
    end
  end
end
ハイカード(豚)
ruby_poker/lib/ruby_poker/hand/hand_type_judgers/high_card.rb
module RubyPoker
  class Hand
    module HandTypeJudgers
      module HighCard
        def self.judge(cards:)
          [cards.max]
        end
      end
    end
  end
end

テストの実装も含めて少し大変でしたが実装完了。これで役判定と、同役時の判定用カードの返却までしっかりと行うことができるようになりました。

Comparable用の判定追加

ruby_poker/hand.rb
+    include Comparable
ruby_poker/hand.rb
    def hand_level
      RubyPoker::HAND_TYPES.reverse.index(@hand_type)
    end

    def <=>(other)
      hand_comparison = hand_level <=> other.hand_level
      hand_comparison.zero? ? @comparison_card <=> other.comparison_card : hand_comparison
    end

これで役の勝敗を>などで比較することができるようになりました

終わりに

今回の内容にて、カード、役 などゲームに大切な処理は一通り終わったかと思います。
次回は、これらを使うプレイヤーやゲーム進行回りを作っていきたいと思います。

続き


rubyでドローポーカーを作ってみる~実装編3(プレイヤー)~

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む