My TIPs for the programing

プログラミング学習記録

letter_openerの設定と起動


 letter_opener_webは、開発環境用で実際にメールを送信して確認できるgemです。
 
 letter_opener_webを使用すると、/letter_openerへアクセスしてメールが届いているか確認することができます。
 
###実際にletter_opener_webをインストール
 letter_opener_webは開発環境でのみ使用するので、groupブロックで囲むことによって、開発環境のみ適用するように定義します。
 
 (gemfile)

```
 省略
group :development do
gem 'letter_opener_web'
end
```

ルーティングの設定
(routes.rb)

```
# 省略
if Rails.env.development?
mount LetterOpenerWeb::Engine, at: "/letter_opener"
end
# 省略
```

開発環境でメール送信の際、letter_opener_webを使用するように設定。
(config/environments/development.rb)

```
config.action_mailer.default_url_options = { host: 'localhost3000' }
config.action_mailer.delivery_method = :letter_opener_web
```

/letter_openerにアクセス。
https://imgur.com/a/oeQNn

scaffoldで簡単お問い合わせ機能

rails db:create rails new ののち。。

$ rails g scaffold Contact name:string email:string content:text

ファイルがいっぱいできる。

Running via Spring preloader in process 17820
      invoke  active_record
      create    db/migrate/20171118042216_create_contacts.rb
      create    app/models/contact.rb
      invoke    test_unit
      create      test/models/contact_test.rb
      create      test/fixtures/contacts.yml
      invoke  resource_route
       route    resources :contacts
      invoke  scaffold_controller
      create    app/controllers/contacts_controller.rb
      invoke    erb
      create      app/views/contacts
      create      app/views/contacts/index.html.erb
      create      app/views/contacts/edit.html.erb
      create      app/views/contacts/show.html.erb
      create      app/views/contacts/new.html.erb
      create      app/views/contacts/_form.html.erb
      invoke    test_unit
      create      test/controllers/contacts_controller_test.rb
      invoke    helper
      create      app/helpers/contacts_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/contacts/index.json.jbuilder
      create      app/views/contacts/show.json.jbuilder
      create      app/views/contacts/_contact.json.jbuilder
      invoke  test_unit
      create    test/system/contacts_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/contacts.coffee
      invoke    scss
      create      app/assets/stylesheets/contacts.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss

マイグレーションはする必要がある。

rails db:migrate

メイラーを作成

rails g mailer ContactMailer

mailers/contact/mailer.rbに追記。 contact_mailメソッドにおいて引数を受け取っている。 インスタンス変数は、view(メール本文)で使用するために定義している。

class ContactMailer < ApplicationMailer
  def contact_mail(contact)
    @contact = contact
    mail to:"自分のメールアドレス",subject:"お問い合わせの確認メール"
  end
end

メイラーのviewを作成 ・これは、送信されるメールの本文です。 (app/views/contact_mailer/contact_mail.html.erb)

<h1>お問い合わせが完了しました。</h1>
<h4>name:<%= contact.name %></h4>

<h4>お問い合わせ内容の確認</h4>
<p>content:<%=@contact.content %></p>

お問い合わせをした時にメイラーが呼び出されるように実装。 ・redirect_toにパスではなくインスタンス変数を渡している。これをrailsで勝手に判断してshowを表示してくれる。 ・ここではjsonは特に使用していない。

  # POST /contacts
  # POST /contacts.json
  def create
    @contact = Contact.new(contact_params)
    respond_to do |format|
      if @contact.save
        ContactMailer.contact_mail(@contact).deliver#追記
        format.html { redirect_to @contact, notice: 'Contact was successfully created.' }
        format.json { render :show, status: :created, location: @contact }
      else
        format.html { render :new }
        format.json { render json: @contact.errors, status: :unprocessable_entity }
      end
    end
  end

(まとめ) 1.contacts_controllerのcreateアクションでお問い合わせをデータベースに保存する処理が流れる。

2.1の処理が流れた時ContactMailer.contact_mail(@contact).deliverのメイラーの処理が走り、app/mailers/contact_mailer.rbのcontact_mailメソッドが呼び出される。

3.引数として渡す@contactをcontact_mail(contact)の記述でcontactという引数名で受け取り、contact_mail内の処理が実行される。メソッド内で@contact = contactとしているのは@contactをView(contact_mail.html.erb)で使用するため。

4.処理が一通り流れ、メールがお問い合わせ者に届き、contact_mail.html.erbで記述されているHTMLが表示される。

インスタンス変数が効く範囲の理解が怪しい。

【テストプログラム3】

完成形

(janken2.rb)

# 判定部分のみをクラスに。
PLAYER_INPUT_TO_INTEGER = {'g'=> 1 ,'c' => 2 ,'p' => 3 }
MACHINE_INTEGER_TO_INPUT = { 0 => "g" ,  1  => "c",  2  => "p"}
MACHINE_LIST_JANKEN = ["g","c","p"]
GCP_TO_JAPANESE = {'g'=>'グー','c' => 'チョキ','p'=>'パー'}

puts "input g or p or c"
print "you:"
player_gcp = gets.chomp
machine_gcp = MACHINE_LIST_JANKEN[rand(3)]

class Judge
  def judgement(player_gcp,machine_gcp)
    # p @player_gcp
    player_123 = PLAYER_INPUT_TO_INTEGER[player_gcp]
    machine_012 = MACHINE_INTEGER_TO_INPUT.invert[machine_gcp]
    result = (player_123 - machine_012 +3)% 3
    case result
    when 0
      return "勝ち"
    when 1
      return "あいこ"
    when 2
      return "負け"
    end
  end
end

judge = Judge.new
puts "あなたは#{GCP_TO_JAPANESE[player_gcp]}、私は#{GCP_TO_JAPANESE[machine_gcp]}、"
unless judge.judgement(player_gcp,machine_gcp)=="あいこ"
  print "あなたの"
end
puts "#{judge.judgement(player_gcp,machine_gcp)}です。"

(test_janken2.rb)

require 'test/unit'
require_relative 'janken2'

class TC_janken < Test::Unit::TestCase
  def setup
    @judge = Judge.new
  end

  # def teardown
  # end

  def test_janken1
    assert_equal("勝ち",@judge.judgement("g","c"))
    assert_equal("あいこ",@judge.judgement("g","g"))
    assert_equal("負け",@judge.judgement("g","p"))
    assert_equal("勝ち",@judge.judgement("c","p"))
    assert_equal("あいこ",@judge.judgement("c","c"))
    assert_equal("負け",@judge.judgement("c","g"))
    assert_equal("勝ち",@judge.judgement("p","g"))
    assert_equal("あいこ",@judge.judgement("p","p"))
    assert_equal("負け",@judge.judgement("p","c"))
  end
end
Finished in 0.00094 seconds.
-------------------------------------------------------------------------------------------------
1 tests, 9 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-------------------------------------------------------------------------------------------------
1063.83 tests/s, 9574.47 assertions/s

エラーfailureなし。

やったこと
  • メソッドの数だけテストが必要になるから、共通機能は1つのメソッドにまとめた。 具体的にはg/c/p から数字に変換するメソッドを判定のクラスに組み込んだ。 その際に、machineとplayerのじゃんけんの出し手の処理処理が異なったっため、一致させた。

  • クラスに変数を渡そうとして試行錯誤していたが、クラス直下にメソッドを作成し、そのメソッドに引数を渡すものとした。

  • 直接testを行う部分の記述は複雑な処理を書かずに人間に見てわかりやすいものとした。

  • クラス内のメソッドで参照するハッシュは定数CONSTとした。

  • 判定部分のクラスと出力は分離した。 クラスの戻り値を受け取って出力するものとした。

  • 謎のend_of_inputエラー。classがcalssになってた。

【テストプログラムを作成2】クラスの変数【ruby】

syntaxエラーが発生。

先輩さん曰く、irbを起動してコピペしてはっつけてみると良いとのこと。

irb(main):001:0> result = (player_123 - machine_012 +3)% 3
SyntaxError: (irb):1: syntax error, unexpected tINTEGER, expecting keyword_do or '{' or '('
result = (player_123 - machine_012 +3)% 3
                                     ^

うーむ、

irb(main):002:0> result = (2 - 2 +3)% 3
=> 0

あー、定義してないからか。 でもさっきはできてたぞ?

あ、テスト用に判定部分をメソッド化したからですね。

    def judge
      result = (player_123 - machine_012 +3)% 3
      print "あなたは#{gcp_to_japanese[player_gcp]}、私は#{gcp_to_japanese[machine_gcp]}、"
      case result
      when 0
        puts "あなたの勝ちです。"
        return "勝ち"
      when 1
        puts "引き分けです。"
        return "あいこ"
      when 2
        puts "あなたの負けです。"
        return "負け"
      end
    end

色々とインスタンス変数にして見ます。

class Janken
    @player_input_to_integer = {'g'=> 1 ,'c' => 2 ,'p' => 3 }
    @machine_integer_to_input = { 0 => "g" ,  1  => "c",  2  => "p"}
    @machine_list_janken = [0,1,2]
    @gcp_to_japanese = {'g'=>'グー','c' => 'チョキ','p'=>'パー'}

    def janken_random
      puts "input g or p or c"
      print "you:"

      player_gcp = gets.chomp
      p @player_input_to_integer
      player_123 = @player_input_to_integer[player_gcp]

      machine_012 = @machine_list_janken[rand(3)]
      machine_gcp = @machine_integer_to_input[machine_012]
      # puts "machine:#{machine_gcp}"
    end

    (略)
end

読み込まれません。

クラスの変数?ああ、インスタンス変数っしょwつーかこれからっしょw

ってくらいの理解しかありませんでした。勉強不足。

先輩さん曰く・・

インスタンス変数をクラス内のメソッドでは使えない。 インスタンス変数をクラス直下に定義すると読み込まれない。従ってinitialize内にて定義する。

使うためにはinitialize メソッド内に記述する。 インスタンス変数はクラスからオブジェクトを生成する際にインスタンスごとに異なる変数を格納したい場合に使う。

リファレンスマニュアルより

初期化されていない インスタンス変数を参照した時の値はnilです。

私がインスタンス変数を使用したハッシュ等は、インスタンスごとに値が変更されることはない。

従って・・・

定数CONST や、 クラス変数@@constを使うべきです。

クラス変数を使用してみました。 f:id:mavol:20171117123749p:plain ほぼクラス変数に。atomが真っ赤

$ ruby janken_exe.rb
input g or p or c
you:g
あなたはグー、私はチョキ、あなたの勝ちです。

一応できたけど、被テストプログラムの構造がテストに向いていなかった。その辺を追記。

雑記

rubyの定数は、定数と言いつつ変更可能。 本当の意味で定数にするにはfreezeメソッドを使用する。

【テストプログラムを作成1】test/unit

作成したじゃんけんプログラムをテストしたい。

エラーに次ぐエラーで勉強になりました

test/unitというgemを使うらしい。これは初めて使うのでインストールされているか確認。

$gem list test

*** LOCAL GEMS ***

minitest (5.8.5)
test-unit (3.1.5)

ありますね。

でもtest/unitとtest-unitは違うし、これでいいのか?

先輩さん曰くtest-unitはrequireする際にはrequire 'test/unit'で問題ないとのこと。

ググっても2種類出て来るので混乱しました。

本題の、test/unit (これだとエラー出ます)
require 'test/unit'
require_relative 'janken'

class TC_janken < Test::Unit::TestCase
  def setup
    @obj = Janken.new
  end

  # def teardown
  # end

  def test_janken
    assert_equal("janken",@obj)
  end
end
  • setup jankenクラスのインスタンスを生成

  • teardown 今回は使っていないが、setupのあとに呼ばれる。

  • test_janken テストメソッド。 テストメソッドはtest_で始める必要がある。

  • assert_equal("janken",@obj) ここで比較している。 文字列とオブジェクトを比較していますから、これだとエラーです。

結局・・・

色々(追記)あってこうなりました。

require 'test/unit'
require_relative 'janken2'

class TC_janken < Test::Unit::TestCase
  def setup
    @judge = Judge.new
  end

  # def teardown
  # end

  def test_janken1
    assert_equal("勝ち",@judge.judgement("g","c"))
    assert_equal("あいこ",@judge.judgement("g","g"))
    assert_equal("負け",@judge.judgement("g","p"))
    assert_equal("勝ち",@judge.judgement("c","p"))
    assert_equal("あいこ",@judge.judgement("c","c"))
    assert_equal("負け",@judge.judgement("c","g"))
    assert_equal("勝ち",@judge.judgement("p","g"))
    assert_equal("あいこ",@judge.judgement("p","p"))
    assert_equal("負け",@judge.judgement("p","c"))
  end
end

【ruby】getsで文字列を取得する場合の改行文字

getsによりコンソールで文字列を取得する場合

文字列入力後にエンターキーで確定する。

 

このエンターキー入力が改行文字としてgetsは取得してしまう。

 

(test.rb)

str = gets
p str

 

$ ruby test.rb

腑に落ちない

"腑に落ちない\n"

 

これを防ぐには、、

 

gets.chomp

 

 

format関数の引数・・%03d"とは

3桁以下の数字を変換する課題(例:4 => 004) にて・・・

puts"数字を入力"
num_i = gets.to_i*2

puts format("%03d", num_i)

 

formatを初めて使った。

 

format(format, *arg) -> String

format 文字列を C 言語の sprintf と同じように解釈し、 引数をフォーマットした文字列を返します。

[PARAM] format:
フォーマット文字列です。
[PARAM] arg:
フォーマットされる引数です。

 

ruby リファレンスマニュアルより)

https://docs.ruby-lang.org/ja/latest/method/Kernel/m/format.html

 

 rubyのformatは、c言語のsprintf関数と同じ働きをするらしい。

 以下、sprintfの書式

%[nth$][フラグ][幅][.精度]指示子

%

前述の%から始まる文字列は、フォーマット文字列と呼ばれる。

 

 

0

フラグには #, +, ' '(スペース), -, 0 の5種類がある。

今回は、数字を右詰にする『0』に該当。

他には、符号付きにする『+』や2進法・4進法等の際にそれを明示する『#』等がある。

 

 

3

0以外の数字で始まる数字列は幅指定になります。

幅は生成文字列の長さを示します。

 

d

末尾のdは指示子と呼ばれ、引数の型を示す。

指示子は大きく分けて以下の通り。

  • 文字列を表す指示子: c, s, p
  • 整数を表す指示子: d, i, u, b, B, o, x, X,
  • 浮動小数点数を表す指示子: f, g, e, E, G

それぞれに細かい意味があるので使う時は調べる必要がある。

ちなみにdは、引数の数値を10進表現の整数として出力。

 

 

エクセルの書式のフォーマットを思い出します。

いつもテンプレートしか見ていませんでしたが・・