ヨージとプログラミング

プログラミング勉強の記録

【Ruby】ある文字列が、リストの中に一致するものがあるか?のやり方が分かって最高

paizaやっててシリーズ

Pythonの"in"を目指して

もともとPythonを勉強してたこともあり、条件式'in'をついついRubyでも使ってエラーを出してしまう。

word = "greate"
list = ["happy", "planet", "greate"]

# Python
print(word in list)
   => True

# Ruby
puts w in list
   => SyntaxError

Pythonのこの書き方は非常に直感的で好きである。そしてRubyにはない(多分)
そこで、Rubyで同様の表現をするにはどうするのが良いかと、色々考えていて、良いのを見つけました。

word = "greate"
list = ["happy", "planet", "greate"]

puts list.include?(word)
   => true

これです! 一般化すると<配列/リスト>.include?(<調査文字列>)
配列を前側に持ってきているのがみそです。実は今までは似た方法で

word = "greate"
list = ["happy", "planet", "greate"]

puts list.any? { |e| e == w }
   => true

みたいな冗長な方法でやってました。include?メソッドは知ってましたが、アクセプターに配列をもってこれないと思いこんでいた。

【Rails】AWS ECS(EC2)へのseedの流し込み方

王道かはわかりませんが、とりあえずできたので

前提: Mac PC, EC2 on ECS, Rails5, ruby 2.6, EC2 key-pair取得済み, ECS起動済み

① EC2へssh接続 ターミナルです。

$ ssh -i key-pair.pem ec2-user@<パブリックip>

② 起動コンテナ確認

$ docker ps
=>
   CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS        
   <コンテナID>        <image>   "bundle exec puma -C…"   33 seconds ago      Up 32 seconds

コンテナIDをここで確認しておく

③ seed流し込み

$ docker exec -it <コンテナID> bundle exec /bin/sh -c 'rails db:seed'

以上です。
CircleCIでのbuild時にDockerfileでseedできないかなぁ

【Ruby】1.hourなどでNoMethodError (undefined method `hours'になる場合の対応

Paizaをやっててシリーズ

'1.hour'はTimeクラスじゃなかった(衝撃)

Paizaで時刻を扱え問題に出会いました。
そうなると、Timeクラスの計算をすることがあります。
例えば、現在の時間から10分後の時間を表示するとします。↓

now = Time.now
   # => 2020-01-31 00:58:34 +0900

future = now + 10.minites
   # NoMethodError (undefined method `minites' for 10:Integer)

はい、エラーが出ましたね。minitesなど知らんと言われました。 あれ?メソッドがインポートされてないのかな?名著、チェリー本ではTimeクラスは組み込みモジュールと習ったが… っというか、Time.nowはしっかり動いてるしTimeクラスの問題じゃないっぽい

'1.hour'はActiveSupport::Durationクラス

stackoverflow.com

こちら様の記事に答えがありました。
結論から申し上げますと、次のようにすると動きました。

require 'active_support/time'   # 追加

now = Time.now
   # => 2020-01-31 01:08:14 +0900
future = now + 10.minites
   # => 2020-01-31 01:18:14 +0900

どうも私は勘違いしていたらしく、10.minutesとか1.hourはTimeクラスではありませんでした。(反省)

irb(main):> 1.hour.class
=> ActiveSupport::Duration

【Ruby】配列の統合(flatten)と分割(each_slice)

paizaやるときに調べたシリーズ

行列(marix)の操作の時に使用した

数学ではおなじみ行列
以下のような3行3列の行列があったとする
1 2 3
4 5 6
7 8 9
私の浅知恵では、これをプログラムで扱うとすると、配列で入れ子にするのがいいんじゃないかと思うわけです。

# こんな感じ
matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]
# 2行目の2列目の要素を取り出したい時は
matrix_22 = matrix[1][1]

詳しい文脈は割愛しまして、配列の入れ子が邪魔な時もありまして、一旦、1つの配列にしてしまいたいと思いました。それについてはflattenメソッドがあることは知っていましたが、一度flattenしてしまってもとに戻せるの?ってなりましたが、どうやら行けそうです。

flatten <==> each_slice

使い方

matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]
# flatten
matrix.flatten
   # => [1, 2, 3, 4, 5, 6, 7, 8, 9]
p matrix
   # =>  [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] flattenは非破壊メソッド
matrix.flatten!
p matrix
   # => [1, 2, 3, 4, 5, 6, 7, 8, 9] "!"をつけて破壊的に

matrix.each_slice(3)
   # => #<Enumerator: [1, 2, 3, 4, 5, 6, 7, 8, 9]:each_slice(3)>
   # イミュータブルなものが帰ってくるので必ずto_aする
matrix.each_slice(3).to_a
   # => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
p matrix
   # => [1, 2, 3, 4, 5, 6, 7, 8, 9]  each_sliceは非破壊

each_sliceは非破壊メソッドですが、each_slice! とか、each_slice(3)!とかeach_slice(3).to_a!とかは総じてエラーになったので破壊的にはできないのかなぁ

【Ruby】配列から要素を抜き出すpop, shift

Paizaをやってて調べたことシリーズ

popはケツから、shiftはアタマから

配列に入っている要素を使いたい場合、インデックスを指定して利用することが多いと思います(例:array[1]とか)
しかし、その場合だと要素を参照しただけで、元の配列自体から要素を抜き出した(取り去った)わけではありません。そして、そういう処理をしたい時はたまに訪れます。

array = [1, 2, 3, 4]
number = array[2]

p number   # 3
p array    # [1, 2, 3, 4] そのまま

pythonでは最後尾の要素を取り出すのにpopがありました、rubyにもあるかなと思い探すと、あ、り、ま、し、た。

# popの使い方
array = [1, 2, 3, 4]
pop_number = array.pop

p pop_number  # 4
p array       # [1, 2, 3]

popの逆バージョンも見つけました。こっちは知りませんでした

# shiftの使い方
array = [1, 2, 3, 4]
pop_number = array.shift

p pop_number  # 1
p array       # [2, 3, 4]

抜き出した値を利用しない場合は他の選択肢もあり

##非破壊的メソッド「Array#reject」
array = [1,2,3]
p arry.reject {|item| item == 2} #=> [1, 3]
p arry #=> [1, 2, 3]

##破壊的メソッド「Array#delete_if」
array = [1,2,3]
p arry.delete_if {|item| item == 2} #=> [1, 3]
p arry #=> [1, 3]

【Paiza】Rubyでは文字列のeachはeach_charでまわす等

D問題を解く過程で調べたこと

文字列を1文字ずつeachする時はeach_char

word = "abcdef"
#正しい
word.each_char { |w| puts w }
# 間違い
word.each { |w| puts w }  

文字列に特定の文字が含まれているかの判定に、'in'が使えない(多分)

これはpythonとの比較でのこと
"abcdef"という文字列に、母音が含まれているか判別したい時 pythonでは"in"が使えた(多分)

word = "abcdef"
boin = ['a', 'i', 'u', 'e', 'o'] 

for w in word:
  if w in boin:
    print w

どうやらrubyではそのようなものは無いようだ(多分) しかたないので、any?とinclude?で代用

word.each_char do |w|
  unless boin.any? { |b| w.include?(b) }
    puts w
  end
end

【Rails】RSpec(Capybara)で使うvisitはit句の中でしか使えない?(調査中)

調べ中ですが忘れないようにとりあえずメモ

system specでよく使うvisit

system specを書く時、僕なんかはログイン状態のテストをするときは

visit login_path

みたいに書いて、ログインページに移動するんですが。何気なく使っていると以下のようなエラーが出ました。

> docker-compose run web rspec

An error occurred while loading ./spec/system/records_spec.rb.
Failure/Error: visit login_path

NameError:
  undefined local variable or method `login_path' for RSpec::ExampleGroups::Nested::Nested::Nested:Class

0 examples, 0 failures, 1 error occurred outside of examples

login_pathみたいなものは定義されてないよということですが、要は、visit句が機能してないということ。
ちなみにコードはこうです。(分かりやすくしています)

describe 'レコード', type: :system do
  context 'ログインしている時' do
    let(:user) { FactoryBot.create(:user) } # user作成
    visit login_path # ログインページへ
    fill_in "email-form", with: user.eamil

    it 'hogehoge' do       
      expect(page).to have_css('#record')
    end
  end
end

そこで試しに、visit句を含めたフィクスチャをit句の中へ

describe 'レコード', type: :system do
  context 'ログインしている時' do
    it 'hogehoge' do       
      let(:user) { FactoryBot.create(:user) } # user作成
      visit login_path # ログインページへ
      fill_in "email-form", with: user.eamil

      expect(page).to have_css('#record')
    end
  end
end

すると、エラーなくRSpecが動きました。

visit ○○_pathはit句の中でしか使えないのか?

軽くしか調べてないのでなんとも言えませんが 少なくともcontext直下では使えないっぽいですね。

(追記)
とりあえずこんな感じで運用中

describe 'レコード', type: :system do
  context 'ログインしている時' do
    let(:user) { FactoryBot.create(:user) } # user作成

    before do
      visit login_path # ログインページへ
      fill_in "email-form", with: user.eamil
    end

    it 'hogehoge' do       
      expect(page).to have_css('#record')
    end
  end
end

流石にフィクスチャをit句の中に入れるのは抵抗があるので、beforeブロックにしました。ポイントはlet句はbeforeの外に出しています。letはbeforeの中で使えないので