< 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)
参考文献
変数と定数 (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(変更不可)であり、同値ならば必ず同一になる。
戻り値
戻り値とは、メソッドを呼び出した時、メソッド処理の結果情報が呼び出し元に渡すデータ(オブジェクト)のこと。
キャッチボールで例えると、自分(呼び出し元)が相手(メソッド)に投げたら(引数)、戻ってくる(戻り値)といったイメージ。
複数のオブジェクトを返したい場合は、配列へ入れて一つにまとめるなどする。
引数
引数とは、メソッド名の横に記載される、呼び出し元からメソッドへ渡すデータ(オブジェクト)のこと。
メソッド名の横に(変数)の形で定義し、そこへ引数が入れられて、メソッド内でその変数を使用して計算を行う。
def number(a) puts 1*a end # => :number number(3) # => 3
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
ハッシュは以下のように表され、キーにはシンボルが使用される。
上記は次のように書き換えることができる。
{ 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> 配列とメソッド
- 配列と要素
- 要素
- 配列のメソッド
配列と要素
配列とはArrayオブジェクトまたは配列オブジェクトと呼ばれ、内部にStringやIntegerなどのオブジェクトを入れることができるオブジェクトのこと。
クラスの継承は以下の通り。
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> 繰り返し処理 〜 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枚足りない!!!!!
参考文献
<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
引用・参考文献
GitHub - javan/whenever: Cron jobs in Ruby
Railsでwheneverを使ってcronを設定する - Qiita
バッチ処理(一括処理)とは - IT用語辞典 e-Words
Ruby on Rails - Gemfileに書かれるrequire => false とはどういう意味でしょうか?|teratail
<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> 演算子式 〜==, !=〜
演算子式 〜==〜
式の値が真である:真(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 Retina ・MySQL 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つのフィクスチャファイルが作成される。
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
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の場合
validとerrorsの順番
valid -> errorsの順番でテストしないとerrorsのerrorの理由が表示されない。
validでfalseになった時に@messageなどが更新される。
バリデーションテスト
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になるというテストの場合、以下のように確認する。
参考・引用文献
factory_bot/GETTING_STARTED.md at master · thoughtbot/factory_bot · GitHub
スタブオブジェクト - Oracle® Solaris 11.3 リンカーとライブラリガイド
【RSpec初級編】FactoryBotを用いてテストコードを効率化する方法について解説|TechTechMedia
テストスイート(てすとすいーと):情報システム用語事典 - ITmedia エンタープライズ
Active Support コア拡張機能 - Railsガイド
いまさらですがActive Supportについて概要を説明します。 | collabit(コラビット)|不動産テック(RealEstateTech)企業
Active Record バリデーション - Railsガイド
<Rspec> .rspecのへの--format documentationの記載について
RSpec の出力をデフォルトの形式から読みやすいドキュメント形式に変更する設定。
これによってテストスイートの実行中にどのスペックがパスしたのかわかりやすくなる。
テストスイートとは?
ソフトウェアの目的や対象ごとに複数のテストケースをまとめたもので、自動化テストにおいてはテストの実行単位となる。 テストスイート(てすとすいーと):情報システム用語事典 - ITmedia エンタープライズ
.rspecファイルに以下を追記
--format documentation
記載してない場合
記載した場合
<参考文献>
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
<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と入力し、該当部分を選択する
これで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.