Ruby Goldへの道 day4
以下のサイトでruby gold取得に向けて毎日一回Goldチャレンジを行い間違えた問題を簡単にまとめる。 rex.libertyfish.co.jp
4日目 得点 70点/100点中
惜しい。順調に得点を伸ばしてる。
では、今日も間違えた問題を簡単にまとめる。
- includeはModuleのインスタンスメソッドをMix-inするメソッド
module M def class_m # インスタンスメソッド "class_m" end end class C include M end p C.methods.include? :class_m #=> false # Cの特異メソッドを表示 p C.new.methods.include? :class_m #=> true
- selfはnewされたクラスのオブジェクトになる
class C def initialize p self.class # selfはnewされたオブジェクトになる end end class C2 < C end C2.new #=> C2
- モジュールを読み込むには
using モジュール
が必要
class C def m1 200 end end module R refine C do def m1 100 end end end c = C.new puts c.m1 #=>200 # Rモジュールは読み込めていない # 読み込むにはusingRが必要 using R c2 = C.new puts c2.m1 #=> 100
- loadは外部ライブラリを読み込む...
モジュールの使用は include か using
module M def foo super puts "M#foo" end end class C2 def foo puts "C2#foo" end end class C < C2 def foo super puts "C#foo" end using M # or include M # ※using と include は挙動が違う # load M # loadは外部ライブラリを読み込む end C.new.foo
- method_missingはチェーンをたどった末にメソッドが見つからなかったら呼ばれる
module M def method_missing(id, *args) puts "M#method_missing" end end class A include M def method_missing(id, *args) puts "A#method_missing" end end class B < A def method_missing(id, *args) puts "B#method_missing" end # このmethod_missingメソッドをコメントアウトすると # Aクラスのmethod_missingが呼ばれる end obj = B.new obj.dummy_method #=> B#method_missing # 継承チェーンをたどった末に見つからなかったらオブジェクト(Bクラス)のmethod_missingが呼ばれる # method_missingも継承チェーンをたどるのでBに定義しなかったらAのmethod_missingが呼ばれる
- Objectクラスに*メソッドは定義されていない
p [1,2,3,4].map(&self.method(:*)) #=> undefined method `*' for class `#<Class:#<Object:0x00007ff4e18b9d58>>' (NameError) # オブジェクトクラスに*メソッドが定義されていないためエラー
- 定数は静的に探索が行われる
module A B = 42 def f 21 end end A.module_eval(<<-CODE) def self.f p B end CODE B = 15 A.f #=> 42
p "Matz is my tEacher"[/[J-P]\w+[^ ]/] #=> "Matz"
- クラスメソッドの呼び出しは
self.[メソッド]
class C class << C # 特異クラス def hoge # クラスメソッド 'Hi' end end def hoge # selfをつけるとクラスメソッドになる 'Goodbye' end end p C.hoge #=> "Hi" # クラスメソッドを呼び出す p C.new.hoge #=> "Goodbye"
- Module.nestingはネストの状態を表示する
浅いところから表示していく
module SuperMod module BaseMod p Module.nesting end end #=> [SuperMod::BaseMod, SuperMod] # ネストの状態を浅いところから全て表示する
- raiseの例外クラスを省略した場合は、RuntimeErrorを発生させる
begin raise "Err!" # raise 発生させたい例外クラス, "エラーメッセージ" rescue => e puts e.class #=> RuntimeError # 例外クラスの記述が省略されているので RuntimeError が発生 end
- superと呼び出した場合は、現在のメソッドと同じ引数が引き継がれる
super() と引数がないことを明示的に示す必要がある
class S def initialize puts "S#initialize" end end class C < S def initialize(*args) super # super()と記述するとエラーはなくなる puts "C#initialize" end end C.new(1,2,3,4,5) #=> wrong number of arguments (given 5, expected 0) (ArgumentError)
- モジュールにクラスメソッドを定義するには3つ方法がある
# モジュールにクラスメソッドを定義する方法 # ① module M extend self def a 100 end end p M.a #=> 100 # ② module M def a 100 end module_function :a end p M.a #=> 100 # ③ module M class << self def a 100 end end end p M.a #=> 100
- freezeは破壊的な変更を禁止する
どこにfreezeがあるかを確認する必要がある
array = ["a", "b", "c"].map(&:freeze) #=> 配列の要素に対して破壊的な変更を禁止する array = array.freeze array.each do |chr| chr.upcase! end p array #=> can't modify frozen String: "a" (FrozenError)
- Stringクラスはクラスメソッドnewでインスタンスを生成する
p Class.method_defined? :new #=> true p String.method_defined? :new #=> false p Class.singleton_class.method_defined? :new #=> true p String.singleton_class.method_defined? :new #=> true
Ruby Goldへの道 day3
以下のサイトでruby gold取得に向けて毎日一回Goldチャレンジを行い間違えた問題を簡単にまとめる。 rex.libertyfish.co.jp
3日目 得点 54点/100点中
半分超えた〜少しずつメソッドを覚えてきた と言うより問題の内容を覚えてきた(良くない。。)
- takeはEnumerable オブジェクトの先頭から n 要素を配列として返す
p (1..10).lazy.map{|num| num * 2 }.take(3).inject(0, &:+) # [1*2, 2*2, 3*2]を返す #=> 12
- 値を取り出すには、Enumerator::Lazy#forceまたはEnumerator::Lazy#firstを呼び出す必要がある
p (1..100).each.lazy.chunk(&:even?).first(5) #=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]] p (1..100).each.lazy.chunk(&:even?).take(5).force #=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]]
- extendは引数に指定したモジュールのメソッドを特異メソッドとして追加する
module M def class_m "class_m" end end class C extend M # Mモジュールのclass_mメソッドを特異メソッドとして追加 end p C.methods.include? :class_m #=> true
- Refinementは有効化したスコープのみに影響を与えることが出来る
オープンクラスについての理解が必要そう...
class C def m1 400 end end module M refine C do def m1 100 end end end class C using M # スコープ外のため無効 end puts C.new.m1 #=> 400
- initializeの可視性はprivateに設定されている
class C private def initialize end end p C.new.public_methods.include? :initialize #=> false p C.new.private_methods.include? :initialize #=> true
- usingはメソッドの中で呼び出すことは出来まず、呼び出した場合はRuntimeErrorが発生する
class C end module M refine C do def m1 100 end end end class C def m1 400 end def self.using_m using M # メソッドの中で呼び出すことができない end end C.using_m puts C.new.m1 #=> Module#using is not permitted in methods (RuntimeError)
- loadはrequire同様外部ライブラリを呼び出す
モジュールは呼び出せない
loadはrequire loadはrequire loadはreq...
module M def foo super puts "M#foo" end end class C2 def foo puts "C2#foo" end end class C < C2 def foo super puts "C#foo" end load M # モジュールは呼び出せない end C.new.foo #=> no implicit conversion of Module into String (TypeError)
- 定数の定義はメモリ上にあるテーブルに管理される
別々に書いてもテーブル上の値を参照する
module M CONST = "Hello, world" end module M def self.say CONST end end p M::say #=> "Hello, world"
- instance_evalの引数に文字列を指定するとネストの状態はモジュールMの特異クラスになる
module M CONST = "Hello, world" end M.instance_eval(<<-CODE) def say CONST end CODE p M::say #=> uninitialized constant #<Class:M>::CONST (NameError) # CONSTはモジュールMにのみあるので、例外が発生する
- module_evalの引数に文字列を指定するとネストの状態はモジュールMになる
module M CONST = "Hello, world" end M.module_eval(<<-CODE) # モジュールMになる def self.say CONST end CODE p M::say #=> "Hello, world"
- Refinementで再定義したメソッドの探索はprependより優先して行われる
class C def m1 200 end end module R refine C do # 優先して探索が行われる def m1 300 end end end using R class C def m1 100 end end puts C.new.m1 #=> 300
class Base # 参照 CONST = "Hello, world" end class C < Base # 参照 end module P CONST = "Good, night" end class Base prepend P end module M class C CONST = "Good, evening" end end module M class ::C # トップレベルの探索 def greet CONST end end end p C.new.greet #=> "Hello, world"
- クラス変数はレキシカルに決定される
class C @@val = 10 end module B @@val = 30 end module M include B @@val = 20 class << C p @@val #=> 20 end end
- includeはモジュールのメソッドをインスタンスメソッドとして追加する
メソッド探索順はselfの後に追加される
module M def foo super puts "M#foo" end end class C2 def foo puts "C2#foo" end end class C < C2 def foo super puts "C#foo" end include M # モジュールMのfooメソッドを追加する self.fooの後に呼ばれる end C.new.foo #=> C2#foo #=> M#foo #=> C#foo
- const_defined?メソッドは第2引数に継承関係を探索するか指定出来る
デフォルトではtrue
mod = Module.new mod.module_eval do EVAL_CONST = 100 end puts "EVAL_CONST is defined? #{mod.const_defined?(:EVAL_CONST)}" #=> EVAL_CONST is defined? true puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST)}" #=> EVAL_CONST is defined? true # 第2引数にfalseを指定すると継承関係まで探索しない puts mod.const_defined? :EVAL_CONST, false # false
- ブロック引数は他の引数より後ろに記述する
def bar(&block) block.yield end bar do puts "hello, world" end def bar(&block) block.call end bar do puts "hello, world" end def bar(n, &block) block.call end bar(5) { puts "hello, world" } # エラー def bar(&block, n) block.call end bar(5) { puts "hello, world" }
-__END__
をファイルとして扱い、このファイルへのアクセスはDATAを用いる
while not DATA.eof? print DATA.read 1 end __END__ 1 2 3 4 #=> 1 #=> 2 #=> 3 #=> 4
- superを実行した場合にもRefinementが影響する
class C end module M refine C do def m1(value) super value - 100 #=> ② end end end class C def m1(value) value - 100 #=> ③ end end using M class K < C def m1(value) super value - 100 #=> ① end end puts K.new.m1 400 #=> 100
- レキシカルスコープにある定数を探索する
module K CONST = "Good, night" class P end end module K::P::M class C CONST = "Good, evening" end end module M class C CONST = "Hello, world" end end class K::P class M::C # K::P::M::CのCONSTを参照する p CONST #=> "Good, evening" end end
- チェーンを行う場合はEnumeratorオブジェクトを作成する必要がある
作成に必要なメソッドenum_for
とto_enum
class Array def succ_each(step = 1) return to_enum(:succ_each) unless block_given? each do |int| yield int + step end end end p [98, 99, 100].succ_each(2).map {|succ_chr| succ_chr.chr} [101, 102, 103].succ_each(5) do |succ_chr| p succ_chr.chr end
- オブジェクトの破壊的な変更は禁止するが配列の要素の破壊的な変更は禁止しない
array = ["a", "b", "c"].freeze array.each do |chr| chr.upcase! end p array #=> ["A", "B", "C"]
- Kernel#Array、Kernel#Hash、Kernel#StringはKernelのモジュール関数として定義されている
Ruby Goldへの道 day2
以下のサイトでruby gold取得に向けて毎日一回Goldチャレンジを行い間違えた問題を簡単にまとめる。 rex.libertyfish.co.jp
2日目 得点 46点/100点中
- クラスインスタンス変数は特異メソッドからアクセスできる
class C @val = 3 # クラスインスタンス変数 attr_accessor :val # 特異クラスのクラスインスタンス変数 class << self @val = 10 end def initialize @val *= 2 if val end end c = C.new c.val += 10 p c.val #=> undefined method `+' for nil:NilClass (NoMethodError)
-tと-fはオプションがない 昨日も同じとこ間違えた〜 forceとtailはないで覚えておこう。
複数回includeされた場合は、後に宣言された方からメソッド探索される
module M1 end module M2 end class C include M1 include M2 end p C.ancestors #=> [C, M2, M1, Object, Kernel, BasicObject]
- クラスCの特異クラスにあるCONSTもコンテキストが異なる
class Object CONST = "100" end class C CONST = "010" class << self CONST = "001" end end p C::CONST #=> "010"
- FixnumとRationalの演算はRationalになる
- 1/2rはRationalのインスタンスが作成される
rが未定義でエラーかと思った。
val = 1 + 1/2r puts val.class #=> Rational
- class << self; endで定義されたメソッドは、特異クラスのインスタンスメソッドになる
module M def method_missing(id, *args) puts "M#method_missing" end end class A include M def method_missing(id, *args) puts "A#method_missing" end end class B < A class << self # 特異クラス def method_missing(id, *args) puts "B.method_missing" end end end B.new.dummy_method #=> A#method_missing
- const_getは、selfに定義された定数を探索する
class Human NAME = "Unknown" def self.name const_get(:NAME) # Fukuzawaクラスのインスタンス end end class Fukuzawa < Human NAME = "Yukichi" end puts Fukuzawa.name #=> Yukichi
- キーワード引数へHashオブジェクトを渡すことができる
def foo(arg1:100, arg2:200) puts arg1 puts arg2 end option = {arg2: 900} foo arg1: 200, **option #=> 200 #=> 900 #foo(arg1: 200, arg2: 900)になる
- キーワード引数は省略できない これも昨日やったような、、、
def foo(arg:) puts arg end foo 100 #=> wrong number of arguments # 下記のように書き換える def foo(arg:) puts arg end foo arg: 100 # <= arg: は省略できない
- Enumerator::Yielderを評価するには、<<を呼び出す
enum_char = Enumerator.new do |yielder| "apple".each_char do |chr| yielder << chr # Enumerator::Yielderの評価 end end array = enum_char.map do |chr| chr.ord end p array #=> [97, 112, 112, 108, 101]
- succ ≒ next selfの次の文字列を返す
class Object CONST = "1" def const_succ CONST.succ! # 1,2,... end end class Child1 const_succ class << self const_succ end end class Child2 const_succ def initialize const_succ end end Child1.new Child2.new p Object::CONST #=> "5"
- Module.nestingはネストの状態を表示する
モジュールがネストされた場合全ての状態を表示する
module SuperMod end module SuperMod::BaseMod p Module.nesting #=> [SuperMod::BaseMod] end
- レキシカルスコープに定数がない場合は、スーパークラスの探索を行う
class Ca CONST = "001" end class Cb CONST = "010" end class Cc CONST = "011" end class Cd CONST = "100" end module M1 class C0 < Ca class C1 < Cc class C2 < Cd p CONST #=> "100" # C2のスーパークラスCdを探索 class C2 < Cb end end end end end
- ブロック変数に*argsを渡すことができる
def hoge(*args, &block) block.call(args) end hoge(1,2,3,4) do |*args| # [[1, 2, 3, 4]]が渡される p args.length < 0 ? "hello" : args #=> [[1, 2, 3, 4]] end
- sort!は破壊的メソッド
大きい順に並び替える
class Company attr_reader :id attr_accessor :name def initialize id, name @id = id @name = name end def to_s "#{id}:#{name}" end def <=> other other.id <=> self.id end end companies = [] companies << Company.new(2, 'Liberyfish') companies << Company.new(3, 'Freefish') companies << Company.new(1, 'Freedomfish') companies.sort! # 大きい順に並び替える companies.each do |e| puts e end # 3:Freefish # 2:Liberyfish # 1:Freedomfish
- freezeはオブジェクトの破壊的な変更を禁止する
array = ["a", "b", "c"].freeze array = array.map!{|content| content.succ} p array #=> can't modify frozen Array: ["a", "b", "c"] (FrozenError)
- method_missingは、継承チェーンを辿った末にメソッドが見つからなかった場合に呼び出される
class Class def method_missing(id, *args) puts "Class#method_missing" end end class A def method_missing(id, *args) puts "A#method_missing" end end class B < A def method_missing(id, *args) puts "B#method_missing" end end p B.class # Class ??? B.dummy_method #=> Class#method_missing # Classクラスのインスタンスメソッドが呼ばれる
- raiseの例外クラスを省略した場合は、RuntimeErrorを発生させる
RuntimeErrorはstanderdErrorのサブクラス
begin raise "Err!" rescue => e puts e.class #=> RuntimeError end
- ブロックにあるローカル変数valはトップレベルにあるものと同じ
val = 100 def method(val) yield(15 + val) end _proc = Proc.new{|arg| val + arg } p method(val, &_proc) #=> 215
- const_defined? 指定された名前の定数が定義されていたら真を返す
mod = Module.new mod.module_eval do EVAL_CONST = 100 end puts "EVAL_CONST is defined? #{mod.const_defined?(:EVAL_CONST, false)}" #=> EVAL_CONST is defined? false puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST, false)}" #=> EVAL_CONST is defined? true
- Fiberは軽量スレッドを提供する
f = Fiber.new do Fiber.yield 15 5 end f.resume f.resume # f.resume 三回以上呼び出すとエラーになる
- requireはライブラリのロード、loadは設定ファイルの読み込みに用いる。
# lib.rb module Lib $num += 1 end ## # 実行ファイル $num = 0 1..10.times do |n| require './lib.rb' end puts $num #=> 1
require 'json' json = <<JSON { "price":100, "order_code":200, "order_date":"2018/09/20", "tax":0.8 } JSON using_parse = JSON.parse json p using_parse #=> {"price"=>100, "order_code"=>200, "order_date"=>"2018/09/20", "tax"=>0.8} using_load = JSON.load json p using_parse #=> {"price"=>100, "order_code"=>200, "order_date"=>"2018/09/20", "tax"=>0.8}
- 破壊的な変更できない また出た
CONST_LIST_A = ['001', '002', '003'] begin CONST_LIST_A.map{|id| id << 'hoge'} # 値変更する rescue end CONST_LIST_B = ['001', '002', '003'].freeze begin CONST_LIST_B.map{|id| id << 'hoge'} # 値変更する rescue end CONST_LIST_C = ['001', '002', '003'].freeze begin CONST_LIST_C.map!{|id| id << 'hoge'} # 値変更しない(破壊的メソッド) rescue end CONST_LIST_D = ['001', '002', '003'].freeze begin CONST_LIST_D.push('add') # 値変更しない rescue end p CONST_LIST_A #=> ["001hoge", "002hoge", "003hoge"] p CONST_LIST_B #=> ["001hoge", "002hoge", "003hoge"] p CONST_LIST_C #=> ["001", "002", "003"] p CONST_LIST_D #=> ["001", "002", "003"]
Ruby Goldへの道 day1
以下のサイトでruby gold取得に向けて毎日一回Goldチャレンジを行い間違えた問題を簡単にまとめる。 rex.libertyfish.co.jp
簡単な自己紹介
1日目 得点 32点/100点中
50問中16問正解。逆に言うと34問間違い。。。
間違えた問題について復習する
- selfはnewされたオブジェクトのクラス
class Fuga def initialize p self.class end end class Hoge < Fuga end Hoge.new #=> Hoge
- 特異クラス内で宣言されたメソッドは特異メソッド
class Hoge class << Hoge def fuga '特異メソッドだよん' end end def fuga 'インスタンスメソッドだよん' end end p Hoge.new.fuga #=> "インスタンスメソッドだよん" p Hoge.fuga #=> "特異メソッドだよん"
-t, -fオプションはない
オープンクラスによる影響をローカルにとどめる為にRefinementがある
RefinementはModule#refineで呼び出すことができModule#usingで定義したRefinementを有効化できる
class Hoge def fuga 200 end end module R refine Hoge do # Refinementを呼び出す def fuga 100 end end end puts Hoge.new.fuga #=> 200 using R # Refinementを有効か puts Hoge.new.fuga #=> 100
- Procはcallの際に引数の数を省略され不足の引数にはnilが代入される
local = 0 p1 = Proc.new { |arg1, arg2| arg1, arg2 = arg1.to_i, arg2.to_i local += [arg1, arg2].max } p1.call("1", "2") #=> 2 p1.call("7", "5") #=> 2 + 7 = 9 p1.call("9") #=> 9 + 9 = 18 p local # => 18
- 定数はレキシカルに決定される
レキシカル...静的??
module M1 class C1 CONST = "class_C1" end class C2 < C1 CONST = "class_C2" module M2 CONST = "module_M2" class Ca CONST = "class_Ca" end class Cb < Ca p CONST #=> "module_M2" end end end end
- ブロック引数は仮引数の中で最後に記述する
def hoge(&block, *args) block.call(*args) end hoge(1,2,3,4) do |*args| p args.length > 0 ? "hello" : args end
- initializeはpublicなどでアクセス修飾子をつけたとしても、privateから変わることはない。
class Hoge public def initialize end end p Hoge.new.private_methods.include? :initialize #=> true
- loadはrequire同様に外部ライブラリを読み込む
外部ライブラリではなくモジュールを読み込むとエラーになる
module M def foo super puts "M#foo" end end class C2 def foo puts "C2#foo" end end class C < C2 def foo super puts "C#foo" end load M end C.new.foo #=> no implicit conversion of Module into String (TypeError)
- method_missingは継承チェーンをたどった末にメソッド が見つからなかったら呼び出される
module M def method_missing(id, *args) puts "M#method_missing" end end class A include M def method_missing(id, *args) puts "A#method_missing" end end class B < A def method_missing(id, *args) puts "B#method_missing" end end obj = B.new obj.dummy_method #=> B#method_missing
- クラス/モジュールに定義されているクラス変数 name の値を返す
class S @@val = 0 def initialize @@val += 1 end end class C < S class << C @@val += 1 end def initialize @@val += 1 super end end C.new C.new S.new S.new p C.class_variable_get(:@@val) #=> 7
- キーワード引数は省略することができない
def foo(arg:) puts arg end foo 100 #=> wrong number of arguments (given 1, expected 0; required keyword: arg) (ArgumentError)
- クラス変数が更新されるタイミング
クラスメソッドが定義された
C.newが呼び出された
superからCのinitializeを呼び出された
S.newが呼び出された
class S @@val = 0 def initialize @@val += 1 end end class C < S class << C @@val += 1 end end C.new C.new S.new S.new p C.class_variable_get(:@@val) #=> 5
- Dateクラス同士の減算はRationalになる
require 'date' d = Date.today - Date.new(2015,10,1) p d.class #=> Rational
- Procオブジェクトをメソッドで実行するにはブロックに変換する必要がある
val = 100 def method(val) yield(15 + val) end _proc = Proc.new{|arg| val + arg } p method(val, &_proc.to_proc) #=> 215 # &_proc.to_procと&_procはブロックに変換できる
- ブロック変数に可変長引数はそのまま受け取る
def hoge(*args, &block) block.call(args) end hoge(1,2,3,4) do |*args| p args.length < 0 ? "hello" : args #=> [[1, 2, 3, 4]] end
- each_charとto_enum(:each_char)は同じ結果が返ってくる
enum = "apple".to_enum(:each_char) # enum = "apple".each_char p enum.next #=> "a" p enum.next #=> "p" p enum.next #=> "p" p enum.next #=> "l" p enum.next #=> "e"
- 特異クラス定義の中でクラス変数を定義してもレキシカルに決定される
class C @@val = 10 end module B @@val = 30 end module M include B @@val = 20 class << C p @@val #=> 20 end end
- :fishはSymbolクラスのオブジェクト
begin print "liberty" + :fish.to_s rescue TypeError print "TypeError." rescue print "Error." else print "Else." ensure print "Ensure." end #=> libertyfishElse.Ensure.
- andは左辺が真であれば、右辺の結果を返します。左辺が偽であれば、左辺の結果を返す
優先順位が低いのでputs v2が評価される
p v1 = 1 / 2 == 0 #=> true p v2 = !!v1 or raise RuntimeError #=> true puts v2 and false #=> true
__method__
はメソッドの中で呼び出すとそのメソッドに名になる
def awesome_method __method__ end p awesome_method #=> :awesome_method
- 引数名に&を付与することでブロック引数になる
def bar(&block) block.yield end bar do puts "hello, world" #=> hello, world end
- Module.nestingはネストの状態を表示する
module SuperMod module BaseMod p Module.nesting #=> [SuperMod::BaseMod, SuperMod] end end
- superを実行した場合にもRefinementが影響する
class C end module M refine C do def m1(value) super value - 100 # 300 - 100 end end end class C def m1(value) value - 100 # 200 - 100 end end using M class K < C def m1(value) super value - 100 # 400 - 100 # Refinementが有効なのでsuperはモジュールMにあるm1を参照する end end puts K.new.m1 400 #=> 100
- String#scanはマッチした部分文字列を配列で返す
p "Matz is my tEacher".scan(/[is|my]/).length #=> 4
- attr_accessorはattr_readerとattr_writerを同時に定義したもの
class C attr_accessor :v # ここ # 同じ def v=(other) @v = other end def v @v end # 同じ attr_reader :v attr_writer :v end c = C.new c.v = 100 p c.v class C end
- 定数の探索順位はクラス内 -> スーパークラス -> クラス探索順に行われる
class Human NAME = "Unknown" def name NAME end end class Noguchi < Human NAME = "Hideyo" end puts Noguchi.new.name #=> Unknown
- between?で値を比較するためには、Comparableをincludeする必要がある
# falsetrueになるように class Company include Comparable # XXXX attr_reader :id attr_accessor :name def initialize id, name @id = id @name = name end def to_s "#{id}:#{name}" end def <=> other # YYYY self.id <=> other.id end end c1 = Company.new(3, 'Liberyfish') c2 = Company.new(2, 'Freefish') c3 = Company.new(1, 'Freedomfish') print c1.between?(c2, c3) print c2.between?(c3, c1)
class Array def succ_each(step = 1) return enum_for(:succ_each, step) unless block_given? each do |int| yield int + step end end end p [98, 99, 100].succ_each(2).map {|succ_chr| succ_chr.chr} # ["d", "e", "f"] [101, 102, 103].succ_each(5) do |succ_chr| p succ_chr.chr # "j" # "k" # "l" end
- freezeはオブジェクトの破壊的な変更を禁止する
配列の破壊的な変更を禁止するが、配列の要素の破壊的な変更は禁止しない
array = ["a", "b", "c"].freeze array.each do |chr| chr.upcase! end p array #=> ["A", "B", "C"]
require 'json' json = <<JSON { "price":100, "order_code":200, "order_date":"2018/09/20", "tax":0.8 } JSON hash = JSON.load json p hash #=> { "price"=>100, "order_code"=>200, "order_date"=>"2018/09/20", "tax"=>0.8 }
- Stringクラスはクラスメソッドnewでインスタンスを生成する
p Class.method_defined? :new #=> true p String.method_defined? :new #=> false p Class.singleton_class.method_defined? :new #=> true p String.singleton_class.method_defined? :new #=> true
- 定数の特徴
- 代入を行うと警告が発生するが、値は変更される
- 中身を直接変更した場合は値が変わる。ただし、警告は発生しない
CONST_LIST_A = ['001', '002', '003'] begin CONST_LIST_A.map{|id| id << 'hoge'} rescue end CONST_LIST_B = ['001', '002', '003'].freeze begin CONST_LIST_B.map{|id| id << 'hoge'} rescue end CONST_LIST_C = ['001', '002', '003'].freeze begin CONST_LIST_C.map!{|id| id << 'hoge'} rescue end CONST_LIST_D = ['001', '002', '003'].freeze begin CONST_LIST_D.push('add') rescue end p CONST_LIST_A #=> ["001hoge", "002hoge", "003hoge"] p CONST_LIST_B #=> ["001hoge", "002hoge", "003hoge"] p CONST_LIST_C #=> ["001", "002", "003"] p CONST_LIST_D #=> ["001", "002", "003"]
Rails ルーティング Tips
- resources
- resource
- collection
- member
- namespace
ルーティングファイルの配置場所 config/routes.rb
Rails.application.routes.draw do # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html ## 以下に記述していく end
resources
いくつかの書き方が存在する
## 1. 基本のアクション(index, new, create, show, edit, update, destroy)のルーティングが生成される resources :posts ## 2. index, new, create, showのルーティングが生成される。 resources :posts, only: %i[index new create show] ## 3. destroy以外のアクションが生成される。 resources :posts, except: %i[destroy]
実務では2の書き方をよく見る。 必要なルーティングのみ生成する。
resourcesとresourceの違い
まず、生成されるルーティングの違いについてみていく
Rails.application.routes.draw do # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html resources :posts resource :post end
- 生成されるルーティングを確認する方法
- ターミナルで rails routesコマンドを実行
% rails routes Prefix Verb URI Pattern Controller#Action ######## resources :posts で生成されたルーティング ############ posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format) posts#edit post GET /posts/:id(.:format) posts#show PATCH /posts/:id(.:format) posts#update PUT /posts/:id(.:format) posts#update DELETE /posts/:id(.:format) posts#destroy ######## resource :post で生成されたルーティング ############ GET /post/new(.:format) posts#new GET /post/edit(.:format) posts#edit GET /post(.:format) posts#show PATCH /post(.:format) posts#update PUT /post(.:format) posts#update DELETE /post(.:format) posts#destroy POST /post(.:format) posts#create
違いをいくつかまとめる
- resources :posts
- :idが存在する
- resource :post
- URI Patternの始めが/postで始まる
- Controller#Actionにposts#indexがない
両者を使い分ける方法としては
特定のレコードを取得する際にidが必要かどうかで判断するといい
少し具体例を出して説明する
例えば
ユーザーの基本的な情報(名前、メールアドレス)を保存するUserモデル
が存在し
さらに詳しい情報(身長、体重、生年月日)を保存するProfileモデル
が存在したとする
- UserとProfileはhas_oneの関係になる(1対1)
- Profileモデルは必ずUserのidに紐づく(Profileモデルがuser_idを持つ)
ある特定のユーザーのデータを取得する際はidが必要
(idがないとレコードを特定できない)
しかしProfileのレコードは必ずユーザーに紐づくので特定のユーザーの情報さえ取得できればそれに紐づくProfileのレコードは特定できる
したがってProfileは特定のレコードを取得する際にidは不要
生成するルーティングとしてはUserはresources, Profileはresourceになる
Rails.application.routes.draw do # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html resources :users do resource :profile, only: %i[show edit update] end end
% rails routes Prefix Verb URI Pattern Controller#Action new_user_profile GET /users/:user_id/profile/new(.:format) profiles#new edit_user_profile GET /users/:user_id/profile/edit(.:format) profiles#edit user_profile GET /users/:user_id/profile(.:format) profiles#show PATCH /users/:user_id/profile(.:format) profiles#update PUT /users/:user_id/profile(.:format) profiles#update POST /users/:user_id/profile(.:format) profiles#create users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy
profileのdestroyアクションはおそらくUserのdestroyメソッドを使用した時にprofileも同時に削除されるような実装をすると思うので外した。
collection
## 1.一つだけルーティングを生成する場合 get 'search', on: :collection ## 2.複数ルーティングを生成する場合 collection do get 'search' get 'download_file' : end
さっきのユーザーのルーティングに新たに検索機能(searchアクション)を追加する場合
resources :users do resource :profile, only: %i[new create show edit update] collection do get 'search' end end
生成されるルーティング
% rails routes Prefix Verb URI Pattern Controller#Action ## 省略 search_users GET /users/search(.:format) users#search ## 省略
collectionとmemberの違い
生成されるルーティングの違い
resources :users do resource :profile, only: %i[new create show edit update] get 'search', on: :collection get 'search', on: :member end
% rails routes Prefix Verb URI Pattern Controller#Action ## 省略 search_users GET /users/search(.:format) users#search search_user GET /users/:id/search(.:format) users#search ## 省略
- memberの方はidがつく
Ruby on Rails [単一モデル] CRUD
複数回に渡って単体モデルのCRUDから多対多のモデルのCRUDまでを記事にしてまとめます。
アプリが完成する頃には下記のような機能が実装できます。
- ユーザーはログインできる
- ユーザーは投稿できる
- ユーザーは他のユーザーの投稿にいいねができる
またER図は以下のようになっています。
前提条件としてrailsの環境構築はできているものとします。(rails s コマンドでlocalhost:3000にアクセスでき下記画像のように表示される)
下記環境で進めていきます。
% ruby -v ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19] % rails -v Rails 6.0.3.2 % postgres --version postgres (PostgreSQL) 9.6.18
では早速まいりましょう。
まず誰でも投稿できるようにPostモデルを作成します。
% rails g model post
rails gはrails generateの略で以下のように使用します。
rails g model モデル名(単数) 例) rails g model post rails g controller コントローラー名(複数) 例) rails g controller pos