STUDY MEMO

学習のメモ書き

< homebrew > brew searchでPermission deniedエラーが発生した時の対処法

対象のコマンド

brew search mysql@5.7


発生したエラー

Error: Permission denied @ rb_sysopen - /private/tmp/github_api_headers20210107-10504-6f9519


対処法 permission deniedということは許可されてない=権限がないということかな?と思い、private/tempの権限を変更

sudo chown -R 'username' /private/tmp/


結果

brew search mysql@5.7
==> Formulae
mysql@5.7
# 解決!


別で発生していた以下のエラーも消えました。

brew postinstall mysql@5.7
==> Postinstalling mysql@5.7
Warning: The post-install step did not complete successfully
You can try again using `brew postinstall mysql@5.7`


参考URL:https://teratail.com/questions/204129

homebrew-core/mysql.rb at 2f56c335ea14c3b9032bc4cc7fd3846e540886d2 · Homebrew/homebrew-core · GitHub

<Ruby> クラス

クラスとメソッドの関係性(メソッド編でも記載)

クラスやメソッド、オブジェクトは以下のように定義されることが多い。

クラス:設計図
オブジェクト:実体
メソッド:振る舞い

イメージとしては
クラス(設計図)内にメソッド(振る舞い)があって、

オブジェクト(実体)はクラス(設計図)を元に作られたもの


または、別の例えだと、クラスを生物の種族と捉えるというのもよくある。
クラス(猫)は、なく、寝るなどの基本的な行動(振る舞い)があり、
または猫はそれぞれ個体がある。


クラス

クラスとは、オブジェクトの所属を表すもの。
クラスに属するオブジェクトは、そのクラスのインスタンスであるとも表現されるが、そのクラスに属しているなどの意味を強調したいときに使われる。 オブジェクトは所属するクラスのメソッドを使用することができる。
どのクラスに属するかはclassメソッドで調べることができる。

"abc".class
=> String
1.class
=> Integer
[1,2,3].class
=> Array
{id: 1}.class 
=> Hash


クラスを作る

クラスを作るには、以下のような形式で記載する。

class クラス名 
  ~
end

class ~ endの中にメソッドなどを記載していく。
クラス名は先頭が大文字で、その後ろは小文字になるのが慣習。
また、2単語語以上を組み合わせる際はキャメルケースで記載する。

命名規則の表現とは?
sample imageという語句があった時、
キャメルケースではsampleImageと表現され、
スネークケースではsample_imageと表現される。
そのほかケバブケース(sample-image)もある。

クラスを定義するとクラス名は定数として設定される。


オブジェクトの作り方

自作のクラスのオブジェクトはクラスメソッドnewを使用する。
その他StringやArrayなどのクラスのオブジェクトは、以下のように作成できる。

class Sample
  def dish
    puts "お皿"
  end
end

Sample.new
# => #<Sample:0x00007fd89a06f138>

# ArrayやStringなどのオブジェクト
a = "string"  #  => "string"
b = [1,2,3]  #  => [1, 2, 3]
String.new  #  => ""
Array.new  #  => []

ちなみにIntegerのように、newメソッドが存在しないクラスもある。

Integer.new  #  =>  NoMethodError (undefined method `new' for Integer:Class)


classメソッド

classメソッドを使用するとオブジェクトのクラスを調べることができる。

# irb
Sample.new.class  #  => Sample
Sample.new.class.class  #  => Class

実行結果からSample.newはsampleクラスのオブジェクトを指し、そのオブジェクトのクラスはSample、
SampleクラスのクラスはClassだということがわかる。
また以下の結果から、クラスもオブジェクトの一種とわかる。

Sample.class  #  => Class


クラスにメソッドを定義

クラス内にメソッドを書くことをメソッドを定義する、という。
メソッドを定義することで、そのクラスに属する全てのオブジェクトを呼び出せる。
冒頭で猫を使って解説したものでいうならば、
Catクラスに属するミケたちは「食べる」などの振る舞いを使える、ということ。

class Cat
  def eat
    "もぐもぐ"
  end
end

# 変数mikeに生成したCatクラスのオブジェクトを代入
mike = Cat.new  #  => #<Cat:0x00007fd89684df98>
# Catクラスに定義されたeatを呼び出し
p mike.eat  #  => "もぐもぐ"


レシーバ

メソッドを呼び出すオブジェクトのことをレシーバという。 Cat.newのCat、mike.eatのmikeがレシーバ部分にあたる。


methodsメソッド

methodsメソッドは、レシーバであるオブジェクトが呼び出せるメソッドを表示するメソッド。

class Cat
  def eat
    "もぐもぐ"
  end
  def purr
    "ぐるぐる.."
  end
end

mike = Cat.new

mike.methods
=> [:purr, :eat,   #<-定義したメソッド
:instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :instance_variable_set, :protected_methods, :instance_variables, :private_methods, :method, :public_method, :public_send, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :to_s, :display, :class, :nil?, :hash, :dup, :singleton_class, :clone, :then, :itself, :yield_self, :untaint, :taint, :tainted?, :trust, :untrust, :untrusted?, :singleton_methods, :frozen?, :methods, :public_methods, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__]

# sortメソッドを使ってabc順に並べ替え
mike.methods.sort
=> [:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone, :define_singleton_method, :display, :dup, :eat, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :methods, :nil?, :object_id, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :purr, :remove_instance_variable, :respond_to?, :send, :singleton_class, :singleton_method, :singleton_methods, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :untaint, :untrust, :untrusted?, :yield_self]


引数のあるメソッドの定義

クラス内のメソッドにも引数でオブジェクトを渡すことができる。

class Cat
  def eat(food)
    "#{food}をもぐもぐ"
  end
  def purr(sound)
    "#{sound}"
  end
end

mike = Cat.new
p mike.eat('')  #  =>  "魚をもぐもぐ"
p mike.purr('にゃー')  #  =>  "にゃー"


クラス内で同じクラスのメソッドの呼び出し

同じクラス内のメソッドの呼び出しは、メソッド名を書くことで実行できる。
メソッドのレシーバは省略すると実行中のメソッド(eat)のレシーバ(mike)が呼び出される。

class Cat
  def eat(food)
    "#{food}" + eat_sound
  end  
  def eat_sound
    "もぐもぐ"
  end
end

mike = Cat.new
p mike.eat('')  #  =>  "魚をもぐもぐ"


self

selfは擬似変数で、現在のメソッドの実行主体 (オブジェクトまたはクラス)が入る。 以下の例ではeat_soundメソッドのレシーバを調べるため、eatメソッド内でselfを使用している。

class Cat
  def eat(food)
    p self
    "#{food}" + eat_sound
  end  
  def eat_sound
    "もぐもぐ"
  end
end


mike = Cat.new  

p mike
#  =>  #<Cat:0x00007fa9450a9628> -> mikeオブジェクト識別番号

p mike.eat('') 
#  =>  #<Cat:0x00007fa9450a9628>  -> eat_soundのレシーバのオブジェクト識別番号
#  =>  "魚をもぐもぐ"

0x~は識別番号で、0x以下は実行するたびに変更される。
この番号が同じであれば同じオブジェクトであるので、同じレシーバといえる。
そのため、eatメソッドで記載しているeat_soundの呼び出しをselfを省略せず、self.eat_soundともかくことができる。
例の処理の流れを書くと以下の通りになる。


インスタンス変数

@変数と表現される変数で、設定されたオブジェクトで使用することができ、そのクラスまたはサブクラスのメソッドを参照することができる。

class Cat
  def eat(food)
    p "#{food}を食べている"
      food = food
  end  
  def refill
    p "#{food}をまだ食べたそうにしている"
  end
end

mike = Cat.new
mike.eat('')  #  =>  "魚を食べている"
mike.refill  #  =>  NameError (undefined local variable or method `food' for #<Cat:0x00007fa941968858>)

「foodは定義されていない変数または変数である」といったエラーが出ている。
これはローカル変数のスコープの領域を超えたのでfoodという変数のデータが消失したのが原因。
そのためオブジェクトで保持できるインスタンス変数にして改めて実行すると、

class Cat
  def eat(food)
    p "#{food}を食べている"
      @food = food
  end  
  def refill
    p "#{@food}をまだ食べたそうにしている"
  end
end

mike = Cat.new
mike.eat('')
mike.refill
"魚を食べている"
"魚をまだ食べたそうにしている"

流れは以下のような流れになる。

newメソッドで作られたオブジェクトがインスタンス変数の持ち主になるため、この時はmikeオブジェクトだけが@food = '魚'の情報を保持している。

mike = Cat.new
tama = Cat.new
mike.eat('')  #  =>  "魚を食べている"
tama.eat('ちゅ〜る')  #  =>  "ちゅ〜るを食べている"
mike.refill  #  =>  "魚をまだ食べたそうにしている"
tama.refill  #  =>  "ちゅ〜るをまだ食べたそうにしている"


インスタンス変数を取得(getter)

オブジェクトの外でインスタンス変数を取得するには、取得用のメソッドを作成する必要がある。
また、取得するためのメソッドはゲッターと表現される。
ゲッターなしで呼び出そうとする以下のように構文エラーが出る。

class Cat
  def eat(food)
    p "#{food}を食べている"
      @food = food
  end  
end

mike = Cat.new
mike.eat('')  #  =>  "魚を食べている"
p mike.@food
#  =>  SyntaxError (sample.rb:10: syntax error, unexpected tIVAR)
p mike.@food

ゲッターであるメソッドを追加すると以下のようになる。
ゲッターで@foodの情報をfoodメソッドで展開できるように設定。

class Cat
  def eat(food)
    p "#{food}を食べている"
      @food = food
  end
  def food  #<- ゲッター
    @food
  end
end

mike = Cat.new
mike.eat('')  #  =>  "魚を食べている"
p mike.food  #  =>  "魚"

このゲッターをattr_readerというメソッドで1行で記載することもできるが、また別の投稿で記載する。


インスタンス変数へ代入(setter)

インスタンス変数へ代入するためのメソッドは=()を使用する。 代入するためのメソッドはセッターと表現される。

class Cat
  def food=(name)
    @food = name
  end
  def food
    @food
  end
end

mike = Cat.new
mike.food = ""
puts mike.food  #  =>  魚
mike.food = "ちゅ〜る"
puts mike.food  #  =>  ちゅ〜る

()=メソッドはインスタンス変数から@をとり、末尾に=をつけたものを使用する慣習がある。


オブジェクトのもつインスタンス変数を表示

instance_variablesメソッドを使用すると、オブジェクトのインスタンス変数名をシンボルの配列として返す。

class Cat
  def eat(food)
    p "#{food}を食べている"
      @food = food
  end
  def food
    @food
  end
end

mike = Cat.new
mike.food = ""
p mike.instance_variables  #  =>  [:@food]

mike.foodで@foodに@food = "魚"が定義されるので、mikeオブジェクトには@foodが定義されるようになる。 そのためp mike.instance_variablesで[:@food]が返される。


オブジェクトの初期化

initializeメソッドはオブジェクト初期化メソッドであり、Objectクラスのprivateメソッドに分類される。
このメソッドはnewインスタンスで作成されたオブジェクトの初期化のため使用される。
newメソッドから自動で呼び出される。

class Cat
  def initialize
    puts "initializeメソッド"
  end
end

Cat.new  #  =>  initializeメソッド

インスタンス変数の初期値を設定

initializeメソッドを使用することで、インスタンス変数の初期値を設定することができる。

class Cat
  def initialize
    @color = "ぶち"
  end
  def color
    @color
  end
end

mike = Cat.new
puts mike.color  #  =>  ぶち


引数を渡す場合
class Cat
  def initialize(color)
    @color = color
  end
  def color
    @color
  end
end

mike = Cat.new("しろ")
puts mike.color  #  =>  しろ


インスタンスメソッドとクラスメソッド

インスタンスメソッド:インスタンス(オブジェクト)をレシーバにするメソッド(mike.colorなど)
クラスメソッド:クラスをレシーバにするメソッド(Cat.newなど)


クラスメソッドの定義
selfを使った方法

メソッド名の前にself.という記載を入れるとクラスメソッドに定義される。
これは前述の通りselfが擬似変数であり、実行主体の情報が入っているため。
クラス名.クラスメソッドという形でも定義できるが、クラス名が変更になった場合保守性が低くなるためあまり使用しない。

class Cat
  def self.eat_sound
    "もぐもぐ"
  end
end

puts Cat.eat_sound  #  =>  もぐもぐ


<<

複数のクラスメソッドをまとめて書くときに有効。

class Cat
  class << self
    def eat_sound
      "もぐもぐ"
    end
  end
end

puts Cat.eat_sound  #  =>  もぐもぐ


#記法と.記法
インスタンスメソッドは「クラス名#メソッド名」、
クラスメソッドは「クラス名.メソッド名」または「クラス名::メソッド名」
を使って表される。


クラス内のクラスメソッドの呼び出し

インスタンスメソッド同様メソッド名だけでも呼べるが、省略されているクラス名またはselfでも呼べる。

class Cat
  def self.eat_sound
    "もぐもぐ"
  end
  def self.eat_sound_continue
    eat_sound + "ぱくぱく"  # 省略形
    # self.eat_sound + "ぱくぱく"
    # Cat.eat_sound + "ぱくぱく"
  end
end

puts Cat.eat_sound_continue  #  =>  もぐもぐぱくぱく

インスタンスメソッドはクラスメソッドを呼ぶことできるが、逆はできない。
なぜならクラスからオブジェクトのレシーバを判別することはできないため。


継承について

子クラスで、子クラス < 親クラスのような記載をすると継承が定義される。
継承が定義された子クラス(サブクラス)では、親クラス(スーパークラス)のメソッドが使用できる。

class Animal
  def name
    @name
  end
  def name=(text)
    @name = text
  end
end

class Cat < Animal
  def size
    @size
  end
  def size=(text)
    @size = text
  end
end

mike = Cat.new
mike.name = "みけ"
mike.size = "小さい"

puts "#{mike.name}#{mike.size}"

ここではnameメソッドのない子クラスのCatが、親クラスAnimalのnameメソッドを使っている。


継承関係を確認する

ancestorsメソッドは親クラスとincludeしているモジュールを順番に配列に格納して返す。

p Cat.ancestors
#  =>  [Cat, Animal, Object, Kernel, BasicObject]


オーバーライド

親クラスと子クラスで同じ名前のメソッドがある時、子クラスのメソッドが呼ばれる。 これをオーバーライドという。

class Animal
  def name
    @name
  end
  def name=(text)
    @name = text
  end
  def medical_chart
    @name
  end
end

class Cat < Animal
  def size
    @size
  end
  def size=(text)
    @size = text
  end
  def medical_chart
    "名前:#{@name} 大きさ:#{@size}"
  end
end

mike = Cat.new
mike.name = "ミケ"
mike.size = "ちいさい"
puts mike.medical_chart  #  =>  名前:ミケ 大きさ:ちいさい


親クラスメソッドの使用
class Animal
  def name
    @name
  end
  def name=(text)
    @name = text
  end
  def medical_chart
    puts ""
  end
end

class Cat < Animal
  def size
    @size
  end
  def size=(text)
    @size = text
  end
  def medical_chart
    super
    puts "名前:#{@name}  大きさ:#{@size}"
  end
end

mike = Cat.new
mike.name = "ミケ"
mike.size = "ちいさい"
mike.medical_chart

# 結果
猫
名前:ミケ  大きさ:ちいさい

上記の例では、superで親クラスのメソッドが呼ばれ、その次に子クラスへ処理が戻ってきている。
また、#{}の中にもsuperは入れることができる。


メソッドの制限

privateを設定すると、private以下のメソッドについて、クラス外での呼び出しについて変数でしか呼び出せなくなるようにできる。

class Cat
  def eat
    food
  end

  private

  def food
    ""
  end
end

mike = Cat.new
p mike.eat  #  =>  "魚"
p mike.food
#  =>  NoMethodError (private method `food' called for #<Cat:0x00007fa47d046158>)

privateの他にpublic(アクセス制限なし)やprotected(アクセス制限あり)もある。
一度privateの記載をするとそれ以降はprivate設定になるので、もしその下にpublicなメソッドを書きたい場合、privateと同じようにpublicを入力するとそれ以降publicな状態のメソッドになる。

privateの書き方はもう一つあり、以下のようにも記載できる。

class Cat
  def eat
    food
  end

  private def food
    ""
  end
end

mike = Cat.new
p mike.eat  #  =>  "魚"
p mike.food
#  =>  NoMethodError (private method `food' called for #<Cat:0x00007fa47d131158>)


クラスメソッドの制限

private_class_methodをdefの前に記載するとクラスメソッドを制限できる。

class Cat
  private_class_method def self.eat
    puts "もぐもぐ"
  end
end

Cat.eat
#  =>  NoMethodError (private method `eat' called for Cat:Class)

class << selfの場合はprivateが使用できる。

class Cat
  class << self
    private
    def eat_sound
      "もぐもぐ"
    end
  end
end

puts Cat.eat_sound
#  =>  NoMethodError (private method `eat_sound' called for Cat:Class)


参考文献

命名規則「キャメルケース」「スネークケース」「ケバブケース」についてまとめてみました | オウンドメディア | 大阪市天王寺区ホームページ制作 | 合同会社デザインサプライ-DesignSupply. LLC-

変数と定数 (Ruby 3.0.0 リファレンスマニュアル)

Rubyの「attr_accessor」ってなんぞや、という人へ | とむじそブログ

Module#private (Ruby 3.0.0 リファレンスマニュアル)

ゼロからわかる Ruby 超入門 (かんたんIT基礎講座) | 五十嵐 邦明, 松岡 浩平 |本 | 通販 | Amazon

<Ruby> メソッド

メソッドとクラス

クラスやメソッド、オブジェクトは以下のように定義されることが多い。

クラス:設計図
オブジェクト:実体
メソッド:振る舞い

イメージとしては
クラス(設計図)内にメソッド(振る舞い)があって、

オブジェクト(実体)はクラス(設計図)を元に作られたもの


または、別の例えだと、クラスを生物の種族と捉えるというのもよくある。
クラス(猫)は、なく、寝るなどの基本的な行動(振る舞い)があり、
または猫はそれぞれ個体がある。


メソッド

メソッドは前述した通り、クラスに定義されたオブジェクトの振る舞いのことを指す。
rubyの組み込みライブラリに設定されているメソッドはリファレンスマニュアルに記載されているが、自分でも設定することができる。
メソッドの解説中ではとりあえず、クラスは横に置いておいて、メソッドの話のみ言及する。
メソッドの定義では、def ... endの内側に欲しい振る舞いが返り値として返される処理を記載する。

def number
  puts 1*2
end
#  => :number

number # => 2

定義式にシンボルが返ってくる?
リファレンスマニュアルのメソッド項目の部分で、メソッド定義式はメソッド名をシンボルにしたオブジェクトを返す、と記載されている。

シンボルって?
Rubyの内側では、数字で管理すると処理速度が早くなるため、メソッド名や変数名、定数名などさまざまな名称を「整数」で管理している。 その整数を表現したものをシンボルと呼ばれ、対応する文字列と1 : 1で対応するラベルのようなもの。 immutable(変更不可)であり、同値ならば必ず同一になる。


戻り値

戻り値とは、メソッドを呼び出した時、メソッド処理の結果情報が呼び出し元に渡すデータ(オブジェクト)のこと。
キャッチボールで例えると、自分(呼び出し元)が相手(メソッド)に投げたら(引数)、戻ってくる(戻り値)といったイメージ。 Image from Gyazo 複数のオブジェクトを返したい場合は、配列へ入れて一つにまとめるなどする。


引数

引数とは、メソッド名の横に記載される、呼び出し元からメソッドへ渡すデータ(オブジェクト)のこと。
メソッド名の横に(変数)の形で定義し、そこへ引数が入れられて、メソッド内でその変数を使用して計算を行う。

def number(a)
  puts 1*a
end
#  => :number

number(3)
#  => 3

Image from Gyazo 2つ以上の引数を持つメソッドは、引数の後に,(カンマ)を入れて変数を記載する。
特に指定がない場合は、引数の記載された順番(今回だとa=2, b=5)に代入される。

def number(a,b)
  p a*b
end
#  => :number

number(2,5)
#  =>  10

引数の数が異なるとエラーになる。

number(2)

#  =>  ArgumentError (wrong number of arguments (given 1, expected 2))


※引数の()の省略は可能だが、曖昧だと判断されたらエラーになるので適宜設定を考えること。


メソッドを途中で終了する

returnをメソッド内に入れると、それ以下の処理が実行されず終了し、return前の処理結果が戻り値として返される。

def like(catlike)
  print "犬いいよね。"
  return
  print "でも猫の方が好き。"
end

like  #  =>  犬いいよね

条件式を入れると以下のようになる。
今回は犬派か猫派かの会話をしている。

def like(catlike)
  print "犬いいよね。"
  unless catlike
    return
  end
  print "でも猫の方が好き。"
end

#猫好き
like(true)
#  =>  犬いいよね。でも猫の方が好き。

#猫好きじゃない
like(false)
#  =>  犬いいよね。


引数のデフォルト値

引数のデフォルト値は引数に=で値を入れることで設定できる。
何も指定がなければデフォルト値、引数があれば任意の引数が渡される。

def clothe(item = "シャツ")
  print "#{item}"
end

clothe  #  =>  シャツ
clothe("スカート")  #  =>  スカート


キーワード引数

通常引数は順番通り渡されるが、キーワード引数を設定すると任意の引数を渡すことができる。

def clothe(item, size)
  print "#{item}#{size}サイズ"
end

clothe("スカート", "M")  #  =>  スカートのMサイズ
clothe("M","スカート")  #  =>  Mのスカートサイズ

これをキーワードをもとに引数の渡す先を指定する。
キーワード引数の指定はメソッド名(引数名:)で指定でき、メソッドの呼び出し元は(引数名: 引数)を記載することで渡せるようになる。

def clothe(item:, size:)
  print "#{item}#{size}サイズ"
end

clothe(item: "スカート", size: "M")  #  =>  スカートのMサイズ

# デフォルトの設定
def clothe(item: "スカート", size:)
  print "#{item}#{size}サイズ"
end

clothe(size: "M")  #  =>  スカートのMサイズ

キーワード引数について
ruby3.0から仕様変更があるので、使用する際は確認すること。


ローカル変数とスコープ

ローカル変数のスコープとは、そのローカル変数の適応できる適応範囲のことを指す。 定義された行から、その変数が定義されたブロック、メソッド定義、またはクラス/モジュール定義の終わりまでがスコープになる。


参考文献

NEWS for Ruby 2.7.0 (Ruby 3.0.0 リファレンスマニュアル)

ゼロからわかる Ruby 超入門 (かんたんIT基礎講座) | 五十嵐 邦明, 松岡 浩平 |本 | 通販 | Amazon

<Ruby> ハッシュ

ハッシュ

複数のオブジェクトをまとめる箱のようなもので、ハッシュオブジェクトともいう。
公式docでは、「任意の種類のオブジェクト(キー)から任意の種類のオブジェクト(値)への関連づけを行うことができる」と記載されており、以下のように継承されている。
BasicObject > Kernel > Object > Enumerable > Hash
ハッシュは以下のように表され、キーにはシンボルが使用される。

Image from Gyazo

上記は次のように書き換えることができる。

{ name: "山田", address: "東京" }

※値には配列を入れることもできる。


シンボル

オブジェクトの一種で、ハッシュのキーではラベルのように使われている。
またシンボルと文字列はハッシュクラスのto_sメソッドとto_symメソッドでそれぞれ変換ができる。

p "名前".to_sym  #  =>  :名前
p :name.to_s  #  =>  "name"


変数への代入と値の取得

ハッシュは変数に代入できる。
変数に代入すると変数[キー]で値を取得することができる。

user = { name: "山田花子", address: "東京" }
p user  #  =>  {:name=>"山田花子", :address=>"東京"}
p user[:name]  #  =>  "山田花子"

存在しないキーを選択するとnilが返る。
defaultメソッドを使用することで、存在しないキーを選択した時に返る値が設定できる。

user = { name: "山田花子" }
p user[:address]  #  =>  nil
user.default = 0
p user[:address]  #  =>  0


キーの一意性について

ハッシュはすでに存在しているキーを使用することはできない。 もし存在しているキーを=メソッドで追加しようすると代入されてしまう。


キーと値の組の追加

を使用した=インスタンスメソッドを使用して追加する。
※他にもstoreメソッドが同義で存在する

self[key] = value -> object

といったように定義されているため、

=の左辺:自分自身(以下の例だと変数user)との内部に追加したい組のキー =の右辺:入力したい値
とするとhashオブジェクトが返る、ということがわかる。 ちなみに
=でインスタンスメソッドなので、下の
user[:address] = "東京"
で返るのは"東京"ということになる。

user = { name: "山田花子" }
p user  #  =>  {:name=>"山田花子"}
user[:address] = "東京"
p user  #  =>  {:name=>"山田花子", :address=>"東京"}


ハッシュを結合

mergeメソッドはハッシュ同士を結合し、hashオブジェクトを返す。

merge(*others) -> Hash

リファレンスマニュアルには「selfとothersのハッシュの内容を順番にマージ(統合)した結果を返す」と記載されている。

user_name = { name: "山田花子" }
user_address = { address: "東京" }

p user_name  #  =>  {:name=>"山田花子"}
p user_address  #  =>  {:address=>"東京"}
user = user_name.merge(user_address)
p user  #  =>  {:name=>"山田花子", :address=>"東京"}  


キーと値の組を削除

deleteメソッドは指定したキーと値の組み合わせを削除する。

user = { name: "山田花子", address: "東京" }
p user  #  =>  {:name=>"山田花子", :address=>"東京"}
user.delete(:address)
p user  #  =>  {:name=>"山田花子"}


ハッシュの要素の繰り返し処理

配列同様eachメソッドを使用する。
ハッシュの要素が入る変数はキーは左、値は右に入るようになっている。
変数名は特に決まっていないが、key、valueがわかりやすいので基本的にはこれが使用されている。

user = { name: "山田花子", address: "東京" }

# do ~ end形式の場合
user.each do |key, value|
  p "#{key}#{value}"
end

# {}形式の場合
user.each { |key, value| p "#{key}#{value}" }

# 結果
"nameは山田花子"
"addressは東京"


キーだけを取り出す

each_keyメソッドを使用する。

user = { name: "山田花子", address: "東京" }

# do ~ end形式
user.each_key do |key|
  p "#{key}"
end

# {}形式
user.each_key { |key| p "#{key}" }

# 結果
"name"
"address"


値だけを取り出す

each_valueメソッドを使用する。

user = { name: "山田花子", address: "東京" }

# do ~ end形式
user.each_value do |value|
  p "#{value}"
end

# {}形式
user.each_value { |value| p "#{value}" }

# 結果
"山田花子"
"東京"


mapについて

hashもEnumerableクラスのメソッドを継承しているのでmapが使用できる。
が、戻り値が配列になるのでto_hメソッドでハッシュに変換する必要がある。
そしてリファレンスマニュアルを確認してわかるようにhashクラスではmap!は継承されていないので使えない。

salary = { rookie: 20, mid_level: 25, veteran: 30 }

# valueの配列が出力される
p salary.map { |key, value| value*2 }  #  =>  [40, 50, 60]

# Arrayクラスの[]特異メソッドでkeyとvalueを一つの配列に入れているため、配列 in 配列になっている
p salary.map { |key, value| [key, value * 2] }  #  =>  [[:rookie, 40], [:mid_level, 50], [:veteran, 60]]

# 配列で返ってきているのでto_hでハッシュに変換
p salary.map { |key, value| [key, value * 2] }.to_h  #  =>  {:rookie=>40, :mid_level=>50, :veteran=>60}

特異メソッド[]は、引数を要素として持つ配列を生成する。

参考文献、引用文献

class Hash (Ruby 3.0.0 リファレンスマニュアル)

Rubyのmap, map!メソッドの使い方 | UX MILK

【Rails入門】mapメソッドを完全攻略!配列操作の基礎を学ぼう | 侍エンジニアブログ

<Ruby> 配列とメソッド

配列と要素

配列とはArrayオブジェクトまたは配列オブジェクトと呼ばれ、内部にStringやIntegerなどのオブジェクトを入れることができるオブジェクトのこと。
Image from Gyazo クラスの継承は以下の通り。

BasicObject > Kernel > Object > Enumerable > Array
fruits = ['りんご', 'みかん', 'さくらんぼ']
p fruits  #  => ["りんご", "みかん", "さくらんぼ"]

配列を代入する変数は複数形にする慣習がある。


要素

配列の中に入れられているオブジェクトは要素と呼ばれる。
要素の取り出しはfirst、lastのメソッドを使う場合と要素を指定して取り出す場合がある。
要素は先頭が0から始まっていて、0, 1, 2...と続く。

fruits = ['りんご', 'みかん', 'さくらんぼ']
p fruits.first  #  => "りんご"
p fruits.last  #  => "さくらんぼ"
p fruits[0]  #  => "りんご"
p fruits[1]  #  => "みかん"
p fruits[2]  #  => "さくらんぼ"

要素指定はマイナスを使うこともできる。
この場合は後ろから-1,-2,...と続く。

fruits = ['りんご', 'みかん', 'さくらんぼ']
p fruits[-1]  #  => "さくらんぼ"  ( = p fruits[2] )
p fruits[-2]   #  => "みかん" ( = p fruits[1] )
p fruits[-3]  #  => "りんご" ( = p fruits[0] )

オブジェクトの入っていない要素を指定した場合、nilが返る。

fruits = ['りんご', 'みかん', 'さくらんぼ']
p fruits[-4]  #  => nil
p fruits[3]  #  => nil


配列のメソッド

要素の追加

Arrayクラスのインスタンスメソッドである以下が使用される。


push, appendメソッド

指定されたオブジェクトを順番に配列の末尾に追加され、引数は複数指定できる。

fruits = ['りんご', 'みかん' ]
p fruits  #  =>  ["りんご", "みかん"]
fruits.push('さくらんぼ')
p fruits  #  =>  ["りんご", "みかん", "さくらんぼ"]
fruits.append('ぶどう')
p fruits  #  =>  ["りんご", "みかん", "さくらんぼ", "ぶどう"]


<<メソッド

指定された要素を自身の末尾に破壊的(破壊的メソッドについては後述)に追加。
引数は一つしか指定できない。

fruits = ['りんご', 'みかん' ]
p fruits
fruits << 'かぼす'
p fruits


破壊的メソッドのため、自身であるselfを返すので、連続してメソッドを記述することができる。

fruits = ['りんご', 'みかん' ]
p fruits  #  =>  ["りんご", "みかん"]
p fruits << 'トマト' << 'しじみ'  #  =>  ["りんご", "みかん", "トマト", "しじみ"]


unshiftメソッド、prependメソッド

指定されたオブジェクトを引数の後ろから配列の先頭に挿入し、引数は複数指定できる。

fruits = ['りんご', 'みかん' ]
p fruits  #  =>  ["りんご", "みかん"]
fruits.unshift('いちご')
p fruits  #  =>  ["いちご", "りんご", "みかん"]
fruits.prepend('パイナップル')
p fruits  #  =>  ["パイナップル", "いちご", "りんご", "みかん"]


要素の削除

pop

最後の要素を削除し、返す値は取り除いた要素。

fruits = ['りんご', 'みかん', 'ぶどう' ]

p fruits  #  =>  ["りんご", "みかん", "ぶどう"]
p fruits.pop  #  =>  "ぶどう"
p fruits  #  =>  ["りんご", "みかん"]


shift

先頭の要素を削除し、返す値は取り除いた要素。

fruits = ['りんご', 'みかん', 'ぶどう' ]

p fruits  #  =>  ["りんご", "みかん", "ぶどう"]
p fruits.shift  #  =>  "りんご"
p fruits  #  =>  ["みかん", "ぶどう"]

配列の結合

+メソッド

それぞれオブジェクトを繋げた配列を生成する。

fruits = ['りんご', 'みかん', 'ぶどう' ]
vegtables = ['玉ねぎ', 'ほうれん草']
p fruits + vegtables  #  =>  ["りんご", "みかん", "ぶどう", "玉ねぎ", "ほうれん草"]


配列の引き算

-メソッド

元の配列から引く側の配列との重複する要素を削除する。

fruits = ['りんご', 'みかん', 'ぶどう', 'りんご', 'みかん', 'ぶどう']
apples = ['りんご']
p fruits - apples  #  =>  ["みかん", "ぶどう", "みかん", "ぶどう"]


配列の繰り返し処理

each

配列の要素を全て表示することができる。
|(パイプ)に挟まれたものは変数で、この変数に配列の各要素が代入され、繰り返し実行される。
慣例的に単数形が入る。

fruits = ['りんご', 'みかん', 'ぶどう']

# do ... endを使用する場合
fruits.each do |f|
  p f
end

# 一行で記載した場合
fruits.each { |f| p f }

# 結果
"りんご"
"みかん"
"ぶどう"


繰り返しを途中で終了する break

breakと条件式を組み合わせることで繰り返しを途中で終了することができる。

fruits = ['りんご', 'みかん', 'ぶどう']

fruits.each do |f|
  break if f == 'みかん' 
  p f
end

# 結果
"りんご"


次の要素の繰り返しへ進む next

nextと条件式を組み合わせることで指定した繰り返しの処理を終了して次の繰り返しに進めることができる。

fruits = ['りんご', 'みかん', 'ぶどう']

fruits.each do |f|
  next if f == 'みかん' 
  p f
end

# 結果
"りんご"
"ぶどう"


範囲を指定する range

rangeは範囲オブジェクトであり、以下のような書き方ができる。

25..35  # 25以上35以下
25...36  # 25以上36未満

Range.new(25, 35)
=> 25..35

これをeachに使用すると、以下のようになる。

(1..3).each do |a|
  p a
end

# 結果
1
2
3


素数を数える

size,lengthメソッド

配列の長さをinteger型で返し、空のときは 0 を返す。

numbers = [1,2,3,4,5]
p numbers.size  #  =>  5
p numbers.length  #  =>  5

zero = []
p zero.size  #  =>  0
p zero.length  #  =>  0


要素の合計

sumメソッド

要素の合計を返す。
sizeと組み合わせると平均を出すことができる。

numbers = [1,2,3,4,5]
p numbers.sum  #  =>  15
p numbers.sum / numbers.size  #  =>  3

# 少数の場合はto_fを使用してメソッドチェーンで計算
floats = [1, 1, 2]  #  =>  sizeは4, sumは3なので平均は少数になる
p floats.sum / floats.size  #  =>  1
p floats.sum.to_f / floats.size.to_f  #  =>  1.3333333333333333

sumとsizeメソッドの戻り値はInteger型のため、Integer型のインスタンスメソッドであるto_fが使用できるため、上記のメソッドチェーンができる。


重複を削除

uniqメソッドとuniq!メソッド

配列の中での重複を削除した新しい配列を返す。
破壊的メソッドのuniq!は、配列を新しくつくらず、自身の配列を作り直す。
そのため使い分けとしては元の配列の情報を残したいかどうかで分ける。

fruits = ['いちご', 'ぶどう', 'バナナ', 'いちご']

p fruits.uniq  #  =>  ["いちご", "ぶどう", "バナナ"]
p fruits  #  =>  ["いちご", "ぶどう", "バナナ", "いちご"]
p fruits.uniq!  #  =>  ["いちご", "ぶどう", "バナナ"]
p fruits  #  =>  ["いちご", "ぶどう", "バナナ"]

# オブジェクトIDを調べる
p fruits.uniq.object_id  #  =>  70247881844820  
p fruits.object_id  #  =>  70247881844860  object_idが違う = 参照先が異なる

p fruits.uniq!.object_id  #  =>  70247881844860
p fruits.object_id  #  =>  70247881844860  object_idが同じ = 参照先が同じ

uniqメソッドはeachメソッド同様ブロックを渡すことができる。

numbers = ['1', 1, 3, 2, '3']

p numbers.uniq { |n| n.to_i }  #  =>  ["1", 3, 2]

上の例では配列の要素を手前から順に取り出してInteger型に変換していて、それを最後uniqで重複削除している。
重複している場合、最初に記載された要素が残る。
<流れ>
{ |n| ... } : numbersの要素を順に変数nに代入

n.to_i -> 変数nに入った要素をInteger型に変換

ブロックの処理のuniqに返し、numbers.uniqで重複した要素を削除


ランダムに要素を取り出す

sampleメソッド

要素を1個ランダムに返し、引数で指定すると配列内に存在する要素数を越えない範囲で取り出すことができる。

fruits = ['みかん','バナナ','いちご','りんご']

p fruits.sample  #  =>  "りんご"
p fruits.sample  #  =>  "みかん"
p fruits.sample(3)  #  =>  ["いちご", "りんご", "みかん"]


要素をランダムにシャッフルする

shuffleソッド

要素をランダムにシャッフルして,その結果返す。

fruits = ['みかん','バナナ','いちご','りんご']
p fruits.shuffle  #  =>  ["みかん", "バナナ", "りんご", "いちご"]
p fruits.shuffle  #  =>  ["いちご", "みかん", "りんご", "バナナ"]


要素をソートする

sort, sort!メソッド

配列の要素をソートした配列を返す。
破壊的メソッドsort!は前述のように自分自身の配列を作り直したものを返す。
数字の場合は昇順(小さい順)に、文字列の場合はa, b, c...順になる。

ソートとは?
データを一定の基準で並べかえることをソートという。

numbers = [4,3,6,2,5,1]

p numbers.sort  #  =>  [1, 2, 3, 4, 5, 6]
p numbers  #  =>  [4, 3, 6, 2, 5, 1]
p numbers.sort!  #  =>  [1, 2, 3, 4, 5, 6]
p numbers  #  =>  [1, 2, 3, 4, 5, 6]

sortの戻り値は配列のため、arrayクラスのメソッドが使用できる。
reverseメソッドをメソッドチェーンとして使用すると降順にすることができる。

numbers = [4,3,6,2,5,1]

p numbers.sort  #  =>  [1, 2, 3, 4, 5, 6]
p numbers.sort.reverse  #  =>  [6, 5, 4, 3, 2, 1]


要素の連結

joinメソッド

要素に、引数の文字列を間に挟んで連結した文字列を返す。
間に挟むので、最後の要素の後ろに引数の文字列は入らない。

fruits = ['りんご', 'みかん', 'ぶどう' ]
p fruits.join('と、')  #  =>  "りんごと、みかんと、ぶどう"


文字列を分割して配列にする

splitメソッド

Stringクラスのインスタンスメソッド。
文字列を引数に指定した文字列で分割する。

dish_count = '1まい、2まい、3まい'
p dish_count.split('')  #  =>  ["1まい", "2まい", "3まい"]


配列の各要素を変換

map, collectメソッド

各要素に対してブロック処理をして、その結果でできた要素による新しい配列を返す。
よく似たメソッドのeachがあるが、以下のような違いがある。

eachメソッド→各要素に対して処理を行い、その結果が目的であること
mapメソッド→各要素に対して処理を行い、その結果の配列の取得が目的であること

# do~endを使った場合
result = [1000, 5000, 10000].map do |x|
  "#{x}円札"
end
p result

# {}を使った場合
result = [1000, 5000, 10000].map{|x| "#{x}円札" }
p result

↓結果
["1000円札", "5000円札", "10000円札"]


各要素に対して特定のメソッドを呼び出すだけの場合は、 1. メソッドをシンボル(:を先頭につける)にする
2. その前に&をつける
これだけで要素を代入する変数の記載を省略することができる。

# 要素を代入する変数を記載する場合
result = ["123", "456", "789"].map{|n| n.reverse }
p result

# &とメソッドシンボルを使用する場合
result = ["123", "456", "789"].map(&:reverse)
p result

# 結果
["321", "654", "987"]


参考文献

ゼロからわかる Ruby 超入門 (かんたんIT基礎講座) | 五十嵐 邦明, 松岡 浩平 |本 | 通販 | Amazon

class Array (Ruby 3.0.0 リファレンスマニュアル)

class Range (Ruby 3.0.0 リファレンスマニュアル)

制御構造 (Ruby 3.0.0 リファレンスマニュアル)

Integer#to_f (Ruby 3.0.0 リファレンスマニュアル)

最速Rubyリファレンスマニュアル検索! | るりまサーチ

<Ruby> 繰り返し処理 〜 timesメソッド, while〜

Rubyの繰り返し処理

繰り返し処理についてはいくつかあるが代表的なものを今回は記載する。


繰り返しメソッドtimes

クラスは以下のように継承されており、親クラスはIntegerクラスである。

BasicObject > Kernel > Object > Comparable > Numeric > Integer > times

決まった回数を繰り返すことができる処理。 以下のように書くことができる。

2.times do
  p '金だ'
end
# 一行でもかける -> 2.times do p '金だ' end

2.times { p '金だ' }

# 結果
"金だ"
"金だ"


制御構造の繰り返しwhile

whileの条件式がtrueの時、中の式を繰り返し実行する。そのため+=などの自己代入などが入る。

dish = 0
while dish < 9
  dish += 1
  print "お皿が#{dish}枚..."
end
print "....1枚足りない!!!!!"

#結果
お皿が1枚...お皿が2枚...お皿が3枚...お皿が4枚...お皿が5枚...お皿が6枚...お皿が7枚...お皿が8枚...お皿が9枚...1枚足りない!!!!!


参考文献

Integer#times (Ruby 3.0.0 リファレンスマニュアル)

制御構造 (Ruby 3.0.0 リファレンスマニュアル)

演算子式 (Ruby 3.0.0 リファレンスマニュアル)

<Ruby on Rails> Wheneverについて

wheneverとは?

wheneverの公式では以下のように記載されている。

Whenever is a Ruby gem that provides a clear syntax for writing and deploying cron jobs.

Wheneverは、cronを書いたりデプロイするためのクリアな構文を提供するRuby gem、とのこと。
wheneverでバッチを回すという表現がされる。

デプロイとは?
主にネットワークを通じて提供されるWebアプリケーションなどにおいて、システムを利用可能な状態にすること。

cronとは?
常駐プログラム(デーモン)の一種で、利用者の設定したスケジュールに従って指定されたプログラムを定期的に起動してくれるもの。

バッチ処理とは?
複数のプログラムからなる作業において、あらかじめ一連の手順を登録しておき、まとめて連続的に実行する方式。
または、一定期間や一定量ごとにデータをまとめて一括して処理する方式。

使い方

インストール

Gemfileに以下のように記載し、bundle installする。

gem 'whenever', require: false

schedule.rbファイルを作成

以下のコマンドを実行することでconfig/schedule.rbが作成される。

$ bundle exec wheneverize .

schedule.rbファイルの編集

例えば、1時間ごとに実行したい場合はeveryの後に:hourと記載する。
他にも:day、:month、:year、:reboot(再起動)などのショートカットがある。
do ~ endのなかに、実行したいrakeタスクファイルを記載する(runnerやcommandなどの他の記載方法もある)。

# Rails.rootを使用するために必要。config/environment.rbを取り込む。
# 記載しないとNameError: uninitialized constant #<Class:#<Whenever::JobList:...>>::Railsエラーが出る。
require File.expand_path(File.dirname(__FILE__) + '/environment')

# cronを実行する環境変数。
# ENV['RAILS_ENV'] = nilの時:developmentを代入。
rails_env = ENV['RAILS_ENV'] || :development

# cronを実行する環境変数をセット
set :environment, rails_env

# cronのログの吐き出し場所
set :output, "#{Rails.root}/log/cron.log"

# 1時間ごとに指定のrakeタスクを実行
every :hour do
  rake "rakeタスクのnameplace:task名"
end

File.expand_path(...)は、/Users/ユーザー名/workspace/726_noguchime_runteq_learning_advancedを指し、File.dirname(FILE)はカレントディレクトリを指す。

runnerとは?
コマンドラインツール。非対話的にRailsの文脈でRubyのコードを実行することができる。

何故requireするの?
gemfileにrequire :falseと設定しているため、個別に指定が必要となる。 参考文献では、上記のように設定するとRailsアプリ本体ではそのgemは呼ばれないため、スクリプトの方では手動でrequireする、とある。 Railsアプリのデータを触るバッチ(一連の処理)スクリプト(裏側で行われる処理)で、バッチスクリプト(裏側の一連の処理)でしか使わないgemの場合設定するようだ。

schedule.rbを確認

scheduldule.rbファイルをcron構文に変換したものを表示する(crontabファイルを読み書きしない)とき、以下のコマンドを使用。

$ bundle exec whenever

crontabファイルに書き込む

$ bundle exec whenever --update-crontab

消去するときは以下のコマンドを使用

$ bundle exec whenever --clear-crontab

引用・参考文献

デプロイとはとは何?Weblio辞書

GitHub - javan/whenever: Cron jobs in Ruby

cronとは - IT用語辞典 e-Words

Rails のコマンドラインツール - Railsガイド

Railsでwheneverを使ってcronを設定する - Qiita

gemのwheneverを導入してみた - まっしろけっけ

バッチ処理(一括処理)とは - IT用語辞典 e-Words

Ruby on Rails - Gemfileに書かれるrequire => false とはどういう意味でしょうか?|teratail

railsを起動するとgemはどのように読み込まれてんのか - 雑日記

https://wa3.i-3-i.info/word11220.html

<Ruby> 条件分岐 〜 if, unless, case 〜

rubyの条件分岐はif、unless、caseの3種類。
使い分けとしては少ない分岐(2つ程度)であればifまたはunless、多い分岐であればcaseを使用する。

if文と後置if文

条件式の結果がfalseまたはnilの場合:nilを返す
条件式の結果がfalseまたはnil以外(true)の場合:ブロック内の処理の結果を返す

後置if(if修飾子)文の場合はif以下の文がfalseまたはnilの場合は出力されず、trueの場合ifの前に記載した処理が実行される。 右辺の条件がtrueの場合、左辺の式を評価してその結果を返し、条件がfalseの場合nil を返す。

age = 25

# if文
if age >= 20
  puts "お酒が買えます。"
end

# 後置if文
puts "お酒が買えます。" if age >= 20

↓どちらも以下の結果が出力される。

お酒が買えます。

<補足> ifの条件部分について

rubyのtrueについて
Rubyは falseとnil が偽として扱われる。
偽でない値(false でも nil でもない値) は全て真(true)として扱われる。

条件式の結果がfalseまたはnil以外の場合、条件式がtrueとして結果を返すため、両方とも以下のような結果になる。

# 条件式に数字
p 'りんご' if 1  # "りんご"  => true

# 条件式に文字列
p 'りんご' if 1  # "りんご"  => true

unless文(後置unless文)

unlessはifと反対で、条件式が偽の時にunless以下の処理を実行する。
unlessは式にelsifは指定できない。

後置unless(unless修飾子)は、
右辺の条件がfalseの場合:左辺の式を評価してその結果を返す
右辺の条件がtrueになった場合:nil を返す

age = 18

# if文を使った場合
if age != 20
  puts "20歳ではありません"
end

# unlessを使った場合
unless age == 20
  puts "20歳ではありません"
end

# 後置unlessを使った場合
puts "20歳ではありません" unless age == 20

↓どちらも以下の結果が出力される。

20歳ではありません

case

caseに定義された情報をwhenでの情報と一致するかを判定し、一致した部分の処理結果を返す。
前述した通り、多くの分岐があるときに使用されることが多い。

animal = 'うさぎ'
case animal
when 'いぬ'
  p 'やっぱ犬だよね!'
when 'ねこ'
  p 'わかる!猫だよね!'
else
  p '犬猫意外なのね!なるほど!'
end 

# 結果
"犬猫意外なのね!なるほど!"
=> true

<補足>caseに変数を入れない

caseのあとの変数を入れない時もある。
thenは一行にまとめるために使用しているが、一行にまとめないのであれば、thenをなくして前述のように改行すればOK。詳しくは公式documentで。

drink = 150
have_money = 200
case 
when drink < have_money then p 'お釣りあります'
when drink == have_money then p 'ちょうどです'
else p '足りません'
end

# 結果
"お釣りあります"
=> true

参考文献

https://docs.ruby-lang.org/ja/latest/method/Object/c/TRUE.html

制御構造 (Ruby 3.0.0 リファレンスマニュアル)

<Ruby> 演算子式 〜==, !=〜

演算子式 〜==〜

式の値が真である:真(true)
式の値が偽である:偽(false)

irb(main):005:0> a = 1
=> 1
irb(main):006:0> b = 1
=> 1
irb(main):007:0> a == b
=> true
irb(main):008:0> a == a + b
=> false

演算子式 〜!=〜

式の値が真である:偽(false)
式の値が偽である:真(true)

irb(main):009:0> a = 1
=> 1
irb(main):010:0> b = 1
=> 1
irb(main):011:0> a != b
=> false 
# 1と1は等しくない => false
irb(main):012:0> a != a + b
=> true
# 1と2は等しくない => true

<Ruby on Rails エラー編> bundle installが失敗した時の対処 〜libv8, mysql2, therubyracer〜

前提

Mac RetinaMySQL Ver 14.14 Distrib 5.7.32 ・Redis 3.2.9 ・Node.js 12.14.0 ・Yarn 1.22.10

# Gemfile
source 'https://rubygems.org'
ruby '2.6.5'

gem 'rails', '5.2.3'
gem 'sdoc', '~> 0.4.0', group: :doc
gem 'therubyracer'

# Assets
gem 'bootstrap-sass'
gem 'font-awesome-rails'
gem 'jquery-rails'
gem 'sass-rails'
gem 'uglifier'
gem 'webpacker'

# UI/UX
gem 'rails-i18n', '~> 5.0.0'
# gem 'turbolinks'
gem 'jbuilder'
gem 'meta-tags'
gem 'slim-rails'

# Authentication
gem 'pundit'
gem 'sorcery'

# Configuration
gem 'config'
gem 'dotenv-rails', require: 'dotenv/rails-now'

# Database
gem 'mysql2'
gem 'redis-rails'

# Seeds
gem 'seed-fu'

# Pagination
gem 'bootstrap4-kaminari-views'
gem 'kaminari'
gem 'kaminari-i18n'

# Breadcrumbs
gem 'gretel'

# Form
gem 'cocoon'
gem 'simple_form'

# Soft delete
gem 'paranoia'
gem 'paranoia_uniqueness_validator'

# Model
gem 'active_hash'
gem 'enum_help'

# Validation
gem 'validate_url'
gem 'validates_email_format_of'

# Decorator
gem 'active_decorator'

# Storage
gem 'aws-sdk-s3', require: false
gem 'mini_magick'

# Application server
gem 'puma'

# Background Job
gem 'resque'
gem 'whenever', require: false

# Debugger
group :development do
  gem 'listen'
  gem 'web-console'
end

group :development, :test do
  gem 'sqlite3'

  # Email
  gem 'letter_opener_web'

  # CLI
  gem 'spring'
  gem 'spring-commands-rspec'

  # Test
  gem 'factory_bot_rails'
  gem 'rspec-rails'
  gem 'simplecov', require: false

  # Code analyze
  gem 'brakeman', require: false
  gem 'bullet'
  gem 'coffeelint'
  gem 'rails_best_practices'
  gem 'reek'
  gem 'rspec_junit_formatter'
  gem 'rubocop'
  gem 'rubocop-checkstyle_formatter'
  gem 'rubocop-rails'
  gem 'scss_lint', require: false
  gem 'slim_lint'

  # Debugger
  gem 'better_errors'
  gem 'binding_of_caller'
  gem 'byebug'
  gem 'pry'
  gem 'pry-byebug'
  gem 'pry-doc'
  gem 'pry-rails'

  # Print debug
  gem 'awesome_print'
  gem 'tapp'

  # Table/Schema
  gem 'annotate', github: 'ctran/annotate_models', branch: 'develop'
  gem 'migration_comments'

  # Deploy
  gem 'capistrano', '3.9.0'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano-rails-console'
  gem 'capistrano-rbenv'
  gem 'capistrano-resque', require: false
  gem 'capistrano3-puma'
end

group :test do
  gem 'capybara'
  gem 'faker'
  gem 'fuubar'
  gem 'shoulda-matchers'
  gem 'timecop'
  gem 'webdrivers'
end

libv8エラー

$ bundle install
...省略...
Fetching libv8 3.16.14.19
Installing libv8 3.16.14.19 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

current directory:
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/ext/libv8
/Users/ユーザー名/.rbenv/versions/2.6.5/bin/ruby -I /Users/ユーザー名/.rbenv/versions/2.6.5/lib/ruby/2.6.0 -r
./siteconf20210127-3572-1g6465c.rb extconf.rb
creating Makefile
Applying
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/patches/disable-building-tests.patch
Applying
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/patches/disable-werror-on-osx.patch
Applying
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/patches/disable-xcode-debugging.patch
Applying
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/patches/do-not-imply-vfp3-and-armv7.patch
Applying
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/patches/do-not-use-MAP_NORESERVE-on-freebsd.patch
Applying
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/patches/do-not-use-vfp2.patch
Applying
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/patches/fPIC-for-static.patch
Compiling v8 for x64
Using python 2.7.16
Using compiler: c++ (clang version 12.0.0)
Unable to find a compiler officially supported by v8.
It is recommended to use GCC v4.4 or higher
Beginning compilation. This will take some time.
Building v8 with env CXX=c++ LINK=c++  /usr/bin/make x64.release ARFLAGS.target=crs werror=no
GYP_GENERATORS=make \
        build/gyp/gyp --generator-output="out" build/all.gyp \
                      -Ibuild/standalone.gypi --depth=. \
                      -Dv8_target_arch=x64 \
-S.x64  -Dv8_enable_backtrace=1 -Dv8_can_use_vfp2_instructions=true -Darm_fpu=vfpv2
-Dv8_can_use_vfp3_instructions=true -Darm_fpu=vfpv3 -Dwerror=''
CXX(target)
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/vendor/v8/out/x64.release/obj.target/preparser_lib/src/allocation.o
clang: warning: include path for libstdc++ headers not found; pass '-stdlib=libc++' on the command line to use the libc++
standard library instead [-Wstdlibcxx-not-found]
In file included from ../src/allocation.cc:33:
../src/utils.h:33:10: fatal error: 'climits' file not found
#include <climits>
         ^~~~~~~~~
1 error generated.
make[1]: ***
[/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/vendor/v8/out/x64.release/obj.target/preparser_lib/src/allocation.o]
Error 1
make: *** [x64.release] Error 2
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/ext/libv8/location.rb:36:in
`block in verify_installation!': libv8 did not install properly, expected binary v8 archive
'/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/vendor/v8/out/x64.release/obj.target/tools/gyp/libv8_base.a'to
exist, but it was not found (Libv8::Location::Vendor::ArchiveNotFound)
from
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/ext/libv8/location.rb:35:in
`each'
from
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/ext/libv8/location.rb:35:in
`verify_installation!'
from
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/ext/libv8/location.rb:26:in
`install!'
        from extconf.rb:7:in `<main>'

extconf failed, exit code 1

Gem files will remain installed in
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19 for
inspection.
Results logged to
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/extensions/x86_64-darwin-20/2.6.0/libv8-3.16.14.19/gem_make.out

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

In Gemfile:
  therubyracer was resolved to 0.12.3, which depends on
    libv8

対処法

$ brew install v8-315
$ bundle config --local build.libv8 --with-system-v8
You are replacing the current local value of build.libv8, which is currently nil

今回使用しているコマンドの

$ bundle config --local <name> <value>

は、ローカルのアプリケーションに対して設定を行う。この設定は、app/.bundle/configに格納される。

ビルドオプション(build.)はbundle configを使用してbundlerへフラグを指定する。
特定のGemのインストールを行う度にそのフラグをgemインストーラーに渡す。

$ bundle config build.libv8 --with-system-v8

このコマンドの実行後に、bundlerがGemのインストールを必要とすると、この指定に沿ってフラグが渡される。

参考文献

macOS Mojaveで古いlibv8とtherubyracerが入らない時の対処法 - Qiita

bundle config | Bundler日本語ドキュメント | Ruby STUDIO

mysql2エラー

$ bundle
...省略...
Fetching mysql2 0.5.3
Installing mysql2 0.5.3 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/mysql2-0.5.3/ext/mysql2
/Users/ユーザー名/.rbenv/versions/2.6.5/bin/ruby -I /Users/ユーザー名/.rbenv/versions/2.6.5/lib/ruby/2.6.0 -r ./siteconf20210127-35425-nrmtfm.rb extconf.rb
checking for rb_absint_size()... yes
checking for rb_absint_singlebit_p()... yes
checking for rb_wait_for_single_fd()... yes
-----
Using mysql_config at /usr/local/opt/mysql@5.7/bin/mysql_config
-----
checking for mysql.h... yes
checking for errmsg.h... yes
checking for SSL_MODE_DISABLED in mysql.h... yes
checking for SSL_MODE_PREFERRED in mysql.h... yes
checking for SSL_MODE_REQUIRED in mysql.h... yes
checking for SSL_MODE_VERIFY_CA in mysql.h... yes
checking for SSL_MODE_VERIFY_IDENTITY in mysql.h... yes
checking for MYSQL.net.vio in mysql.h... yes
checking for MYSQL.net.pvio in mysql.h... no
checking for MYSQL_ENABLE_CLEARTEXT_PLUGIN in mysql.h... yes
checking for SERVER_QUERY_NO_GOOD_INDEX_USED in mysql.h... yes
checking for SERVER_QUERY_NO_INDEX_USED in mysql.h... yes
checking for SERVER_QUERY_WAS_SLOW in mysql.h... yes
checking for MYSQL_OPTION_MULTI_STATEMENTS_ON in mysql.h... yes
checking for MYSQL_OPTION_MULTI_STATEMENTS_OFF in mysql.h... yes
checking for my_bool in mysql.h... yes
-----
Don't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load
-----
-----
Setting libpath to /usr/local/opt/mysql@5.7/lib
-----
creating Makefile

current directory: /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/mysql2-0.5.3/ext/mysql2
make "DESTDIR=" clean

current directory: /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/mysql2-0.5.3/ext/mysql2
make "DESTDIR="
compiling client.c
compiling infile.c
compiling mysql2_ext.c
compiling result.c
compiling statement.c
linking shared-object mysql2/mysql2.bundle
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2

Gem files will remain installed in /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/mysql2-0.5.3 for inspection.
Results logged to
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/extensions/x86_64-darwin-20/2.6.0/mysql2-0.5.3/gem_make.out

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

In Gemfile:
  mysql2

対処法

$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"

参考文献

mysql2がbundle installエラーになるときの解決方法 - Qiita bundle config | Bundler日本語ドキュメント | Ruby STUDIO

therubyracerエラー

$ bundle
...省略...
Fetching therubyracer 0.12.3
Installing therubyracer 0.12.3 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/therubyracer-0.12.3/ext/v8
/Users/ユーザー名/.rbenv/versions/2.6.5/bin/ruby -I /Users/ユーザー名/.rbenv/versions/2.6.5/lib/ruby/2.6.0 -r ./siteconf20210127-36002-1qz94cy.rb extconf.rb
checking for -lpthread... yes
checking for -lobjc... yes
checking for v8.h... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/Users/ユーザー名/.rbenv/versions/2.6.5/bin/$(RUBY_BASE_NAME)
        --with-pthreadlib
        --without-pthreadlib
        --with-objclib
        --without-objclib
        --enable-debug
        --disable-debug
        --with-v8-dir
        --without-v8-dir
        --with-v8-include
        --without-v8-include=${v8-dir}/include
        --with-v8-lib
        --without-v8-lib=${v8-dir}/lib
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/ext/libv8/location.rb:50:in `configure': By using
--with-system-v8, you have chosen to use the version  (Libv8::Location::System::NotFoundError)
of V8 found on your system and *not* the one that is bundled with 
the libv8 rubygem. 

However, your system version of v8 could not be located. 

Please make sure your system version of v8 that is compatible 
with 3.16.14.19 installed. You may need to use the 
--with-v8-dir option if it is installed in a non-standard location
        from /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/libv8-3.16.14.19/lib/libv8.rb:7:in `configure_makefile'
        from extconf.rb:32:in `<main>'

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/extensions/x86_64-darwin-20/2.6.0/therubyracer-0.12.3/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/gems/therubyracer-0.12.3 for inspection.
Results logged to
/Users/ユーザー名/workspace/runteq_learning_advanced/vendor/bundle/ruby/2.6.0/extensions/x86_64-darwin-20/2.6.0/therubyracer-0.12.3/gem_make.out

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

In Gemfile:
  therubyracer

対処法

$ bundle config --local build.therubyracer --with-v8-dir=/usr/local/opt/v8@3.15
You are replacing the current local value of build.therubyracer, which is currently nil

参考文献

Rails の bundle install 時に出会った therubyracer のエラーと、その解決方法 - Qiita

最終的な.bundle/configの中身

BUNDLE_PATH: "vendor/bundle"
BUNDLE_BUILD__LIBV8: "--with-system-v8"
BUNDLE_BUILD__MYSQL2: "--with-ldflags=-L/usr/local/opt/openssl/lib"
BUNDLE_BUILD__THERUBYRACER: "--with-v8-dir=/usr/local/opt/v8@3.15"

<Rspec> バリデーションテストについてのメモ

前提

Gemfile

group :development, :test do
...
gem 'rspec-rails', '~> 4.0.2'
gem 'factory_bot_rails'
end

schema.rb

create_table "tasks", force: :cascade do |t|
    t.string "title"
    t.text "content"
    t.integer "status"
    t.datetime "deadline"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "user_id"
    t.index ["user_id"], name: "index_tasks_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email", null: false
    t.string "crypted_password"
    t.string "salt"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
  end

app/models/user.rb

class User < ApplicationRecord
  authenticates_with_sorcery!

  has_many :tasks, dependent: :destroy

  validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] }
  validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
  validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] }

  validates :email, uniqueness: true, presence: true

  def my_object?(object)
    object.user_id == id
  end
end

app/models/task.rb

class Task < ApplicationRecord
  belongs_to :user
  validates :title, presence: true, uniqueness: true
  validates :status, presence: true
  enum status: { todo: 0, doing: 1, done: 2 }
end


FactoryBot

Factorybotとは、サンプルデータ(ダミーのインスタンス)を簡単に作成することができるテストツール。
公式では以下のように定義されている。

factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.

=> factory_botは、わかりやすい定義構文を持ったフィクチャの代替で、複数のビルドストラテジ(インスタンスの保存や非保存、属性のハッシュ、スタブオブジェクト)をサポートし、継承しているファクトリを含む、同クラス(user、admin_userなど)の複数のファクトリをサポートする。みたいな感じ。

フィクスチャとは?
サンプルデータを言い換えたもの。
事前に定義したデータをテスト実行直前testDBに導入できる。
YAMLで記述され、特定のDBに依存しない。
1つのモデルにつき、1つのフィクスチャファイルが作成される。
ファクトリとは?
データを作成することを簡単にする仕組み。
テストデータ構築用のブロック。
factory_botではサンプルデータのテンプレートファイルを指す。

spec/factories/users.rb

FactoryBot.define do
  factory :user do
    sequence(:email) { |n| "person#{n}@example.com" }
    password { "password" }
    password_confirmation { "password" }
  end
end

sequenceは一意の属性について定義すると、末尾の文字列または数値がa,b,c...1,2,3...のように順番に振られるようになる。
userのemailの場合最後の文字列ではないので繰り上がっていく場所を指定している。
ちなみに上記のように記載しないと以下のようになる。  

# spec/factories/users.rb
FactoryBot.define do
  factory :user do
    sequence(:email, "person1@example.com") 
    password {"password"}
    password_confirmation {"password"}
  end
end

Image from Gyazo

spec/factories/tasks.rb

FactoryBot.define do
  factory :task do
    sequence(:title, "title1")
    content { "content" }
    status { :todo }
    deadline { 1.week.from_now }
    association :user
  end
end

association :userでuserと関連付けできる。 この記載をしないとuser_idが採番できず、buildの段階では問題ないが、createなどDBに保存しようとするときエラーが出る。
↓buildの場合
Image from Gyazo Image from Gyazo

↓createの場合 Image from Gyazo

validとerrorsの順番

valid -> errorsの順番でテストしないとerrorsのerrorの理由が表示されない。
validでfalseになった時に@messageなどが更新される。 Image from Gyazo


バリデーションテスト

spec/models/task_spec.rb

require 'rails_helper'

RSpec.describe Task, type: :model do
  # バリデーションについてテストするグループ
  describe 'validation' do
    # 全ての属性が有効
    it 'is valid with all attributes' do
      task = build(:task)
      # taskが有効か
      expect(task).to be_valid
      # task.errorsでmessagesやdetailsが空欄になっているか
      expect(task.errors).to be_empty
    end

    # タイトルがなければ無効である
    it 'is invalid without title' do
      task_without_title = build(:task, title: "")
      expect(task_without_title).to be_invalid
      expect(task_without_title.errors[:title]).to eq ["can't be blank"]
    end

    # statusがなければ無効である
    it 'is invalid without status' do
      task_without_status = build(:task, status: nil)
      expect(task_without_status).to be_invalid
      expect(task_without_status.errors[:status]).to eq ["can't be blank"]
    end

    # titleが重複していたら無効である
    it 'is invalid with a duplicate title' do
      task = create(:task)
      task_with_duplicated_title = build(:task, title: task.title)
      expect(task_with_duplicated_title).to be_invalid
      expect(task_with_duplicated_title.errors[:title]).to eq ["has already been taken"]
    end

    # 他のtitleであれば有効(記載してもしなくてもいい)
    it 'is valid with another title' do
      task = create(:task)
      task_with_another_title = build(:task, title: 'another_title')
      expect(task_with_another_title).to be_valid
      expect(task_with_another_title.errors).to be_empty
    end
  end
end

rails consoleでexpectの内容を確認したい時
例えばtitleが重複していたらinvalidになるというテストの場合、以下のように確認する。
Image from Gyazo

参考・引用文献

factory_bot/GETTING_STARTED.md at master · thoughtbot/factory_bot · GitHub

Rails テスティングガイド - Railsガイド

https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4%E5%AF%A7%E5%AD%90/dp/4839962227/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=rails+5&qid=1610940943&sr=8-1

スタブオブジェクト - Oracle® Solaris 11.3 リンカーとライブラリガイド

【RSpec初級編】FactoryBotを用いてテストコードを効率化する方法について解説|TechTechMedia

テストスイート(てすとすいーと):情報システム用語事典 - ITmedia エンタープライズ

Active Support コア拡張機能 - Railsガイド

いまさらですがActive Supportについて概要を説明します。 | collabit(コラビット)|不動産テック(RealEstateTech)企業

Active Record バリデーション - Railsガイド

Rails RSpecの準備とテストコード基礎 - Qiita

Active Record バリデーション - Railsガイド

<Rspec> .rspecのへの--format documentationの記載について

RSpec の出力をデフォルトの形式から読みやすいドキュメント形式に変更する設定。
これによってテストスイートの実行中にどのスペックがパスしたのかわかりやすくなる。

テストスイートとは?
ソフトウェアの目的や対象ごとに複数のテストケースをまとめたもので、自動化テストにおいてはテストの実行単位となる。 テストスイート(てすとすいーと):情報システム用語事典 - ITmedia エンタープライズ

.rspecファイルに以下を追記

--format documentation

記載してない場合

Image from Gyazo

記載した場合

Image from Gyazo

<参考文献>
Everyday Rails… Aaron Sumner 著 et al. [Leanpub PDF/iPad/Kindle]

<Ruby on Rails> Githubにgemをチェックインしない方法

.gitignoreへの設定

githubにgemfileとgemfile.lockをインストールしていることから、gem自体のインストールは不要。(push時とても重くなるなどの弊害がある)
そのため、gitの管理下から外しgithubへチェックインしないように、.gitignoreファイルに以下の記載をする。

チェックインとは:
変更を加えたファイルをバージョン管理システムリポジトリに反映する操作のこと。
チェックインとは何? Weblio辞書

# Gemfile
# Ignore installed gem file.
/vendor/bundle

gemの保管先の指定

gemをインストールして保存する先を指定することで、gemのバージョンをプロジェクトごとに管理できる。

$ bundle install --path vendor/bundle

--pathオプションとは?
bundlerによってgemがインストールされる場所を指定するオプション。
この後に呼び出されるbundle installは、もともと--pathに渡されたディレクトリにgemをインストールする。
bundler実行時にはその場所からgemが実行される。
これをつけないとデフォルトのシステムへ保存される。
デフォルトの保存先は今回rbenvを使用しているので、以下のコマンドでみることができる。

$ gem env home

--path vendor/bundleをつけ忘れた場合

すでにチェックインしたファイルをgitの管理下から外し、チェックインから外すようにするには、ファイルの追跡を解除し、それを無視するように設定する必要がある。
今回はvendorのbundleディレクトリまとめて削除するため、-rオプションを使用する。
git rm の-rオプションは、「recursive」の頭文字を取ったもので、ターゲットディレクトリとそのディレクトリの全てのコンテンツが削除されるようになる。

$ git rm -r cached vendor/bundle

参考文献

bunle install するときの --path vendor/bundle オプション - haayaaa’s diary

【Ruby入門】今更聞けない!Ruby bundlerとは | 侍エンジニアブログ

【Rails】結局bundlerって何?bundlerの仕組みを図解形 | Pikawaka - ピカ1わかりやすいプログラミング用語サイト

GitHub - rbenv/rbenv: Groom your app’s Ruby environment

http://ruby.studio-kingdom.com/bundler/bundle_install/

git rm | Atlassian Git Tutorial

<VSC> ターミナルから開く設定 for Mac

1. vscodeを開く

2. Command Paletteを⇧⌘Pで開く

Command Paletteとは?
you have access to all of the functionality of VS Code, including keyboard shortcuts for the most common operations.
→一般的な操作のショートカットを含むvscodeの機能にアクセスすることができる
Visual Studio Code User Interface

3. shellと入力し、該当部分を選択する

Image from Gyazo

これでterminalでcode + 開きたいファイル名またはディレクトリパスを入力すれば開くことができる。

rbenvとは

公式の導入部分で以下のように説明されている。

Use rbenv to pick a Ruby version for your application and guarantee that your development environment matches production. Put rbenv to work with Bundler for painless Ruby upgrades and bulletproof deployments.

rbenvを使うことによって、アプリケーションのバージョン選択や本番環境に開発環境がマッチすることを保証し、rbenvとbundlerを連携させることでrubyのアプグレードの面倒をなくし、開発環境を実現する。

また、以下のような記述から、rubyのバージョン管理のためのツールとわかる。

rbenv is concerned solely with switching Ruby versions.

github.com