結論から言うと
**「合格教本で基礎を押さえて、ひたすら問題を解く」**だけです。
メタプログラミングは一切読まなくても合格できます。
学習時間は業務後2〜3時間(土日はあんまり勉強していない)を1ヶ月間くらい。
Rubyの実行環境を用意して極力コードを実行しながら行うとより効率がよいです。
**【補足】**
この記事は僕が試験対策でやったことと、実際に出た問題を解くための参考、「ここは押さえとくべき」というところを簡単にご紹介して、少しでも参考にして頂ければという気持ちで作成しました。合格教本の基礎問題、模擬問題だけでは試験範囲を押さえきれていません。 **教本には載ってない内容も結構出ます。**
僕が試験を受けて「ここは押さえた方が合格につながる」と思う部分を後述しておりますので、参考になればと思います。おそらく教本だけでは合格できなかっただろうと思います。
**「ITトレメ」「教本の問題集」にはないが試験で出たパターン**を可能な限り(主に時間と共に薄れゆく記憶を元に)抑えた[**問題集的なものを限定共有記事**](https://qiita.com/shunogu/private/6fbccfddee858e8923c3)で公開中ですのでよろしければご参照ください。
まだまだ途中です。割と時間かかっている。現在10問くらい。(2019/07/04時点)理想50問程度(あくまでパターンを抑えるのが目的)。
以下やったことを順番に書いていきます。
【1】 基礎を固める
**【教材】**
[**『Ruby 技術者認定試験合格教本』**](https://www.amazon.co.jp/%EF%BC%BB%E6%94%B9%E8%A8%822%E7%89%88%EF%BC%BDRuby%E6%8A%80%E8%A1%93%E8%80%85%E8%AA%8D%E5%AE%9A%E8%A9%A6%E9%A8%93%E5%90%88%E6%A0%BC%E6%95%99%E6%9C%AC%EF%BC%88Silver-Gold%E5%AF%BE%E5%BF%9C%EF%BC%89Ruby%E5%85%AC%E5%BC%8F%E8%B3%87%E6%A0%BC%E6%95%99%E7%A7%91%E6%9B%B8-%E7%89%A7-%E4%BF%8A%E7%94%B7-ebook/dp/B0756VF9Y3) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320855/e3a31a44-55a2-6656-99e3-106aa4e42dfe.png)**【自分用メモ作成】**
**・** 第2章から第6章の内容をひたすらまとめる各項目ごとにQiitaの限定公開記事を作成。
**・** 教本だけでは理解できない場合は**リファレンス、Qiita他記事**を参照
**・** リファレンスは必ず**試験と同じバージョンのもの(2.1系)**を参照する
**・** [paiza.IO](https://paiza.io/ja/projects/new)、irbで動作を確認する(大事)
**・** paiza.IOだとRubyのバージョンが試験と違うので注意(paizaは2.5.3)
**・** って言っても、2.1系にはArray#sumメソッドが無かったり、「Fixnumクラス/Bignumクラス」がIntegerクラスに統合する前だったりとかバージョンの違いは正直あんまり気にしてなかった。
というか、気にならなかった。けど、するべきだとは思うから一応。
**・** 「**Array#sumメソッドは2.1にはない**」と覚えてただけで1問回答できた。
**・** ちなみにArray#sumは2.4系から。Fixnum、Bignumの統合も2.4系から。
**【所感】**
丸写しするだけでも内容をまとめようと記事作ってたらそのうちわかってくる。ってやってれば、昨日までよく理解できなかったところが、次の日少し理解できたりする。
電車の中でスマホで見る自分用メモとして使える(出退勤の時間無駄にしたくないし)
**こんな記事(一部)を作って片手でいつでもスマホで見れる用にまとめて見てました。**
![スクリーンショット 2019-06-18 19.32.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320855/e98a1708-89a0-5da4-a32b-ed74953f2596.png)【2】 問題を解く
**【解いた問題集】**
**・** [**ITトレメ Ruby技術者認定【Gold】試験**](https://jibun.atmarkit.co.jp/scenter/ittrain/121_today_q.html)**・** 教本 基礎問題30問 / 模擬問題50問
**【所感】**
「解く→間違える→調べる→解く→正解する→もう一度解く→...」を繰り返して9割以上当たり前のように解けるようになれば、公開されてる問題集はこれをやるだけで十分だと今だから思える。1ヶ月後に試験受けるぞ!と意気込んだわけではなく、これを繰り返してたら1ヶ月くらい経って「そろそろいけるのでは?」という根拠ない自信が湧いてきたので、受けてみたら、「9割とまではいかないにしても7割では済まないだろう」という予想通り8割以上解けていた。
おそらく2, 3回見直しをしても試験時間60分かかるかかからないかという感じになると思います。
もう**試験のための勉強**をしなくてよくなったという安堵感。
**【出題範囲】**
[**Ruby Association**](https://www.ruby.or.jp/ja/certification/examination/) Gold出題範囲 ![スクリーンショット 2019-06-18 21.48.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320855/4c3971d5-e98d-cb64-1a91-9320d3ff2052.png) 上から順番に合格教本と合わせながら見ていきます。【実行環境】
コマンドラインオプション
オプション-l
の動作を覚える
Rubyのオプションには無いものを選択する問題は出るので覚える
Rubyで使用されるオプションを全て選択しなさい(複数)
1. -e
2. -w
3. -t
4. -r
5. -b
このパターン出た。
答え 1, 2, 4
組み込み定数
- 環境変数ENVのキー、代入する値共に文字列のみ指定可能
- 数値、シンボルの指定は不可
ENV[:PATH] = "user/..." # => キーがシンボルなので指定不可
ENV['PATH'] = 1234 # => 代入する値が文字列ではないので指定不可
ENV['PATH'] = 'user/local...' # => どちらも文字列なので指定可
【例題】
Objectクラスに標準で定義されている組み込み定数ではないものを選びなさい。
1. NULL
2. ARGF
4. ENV
5. DATA
このパターン出た。これはサービス問題。
答えは1のNULL
requireとload
動作を簡単にまとめました。これだけ覚えておけばいいと思います。
条件 | require | load |
---|---|---|
同じファイルの実行 | 一度だけ実行 | 何度も実行 |
ファイルの拡張子 | 省略できる(自動補完する) | 省略できない(自動補完しない) |
バイナリエクステンション | ロードできる | ロードできない |
特殊変数
変数名 | 参照される値 |
---|---|
$0 | 実行中のファイル名 |
$1 〜 $n | n番目のカッコにマッチする文字列 |
※特殊変数に関しての問題はこの2つだけでした。他のも覚えとくべきかなとは思うけど。
以下のコードについて正しいものを選択しなさい(2つ選択)
/(https://www(\.)(.*)/)/ =~ "https://www.qiita.com/"
1. $0の値は、ruby.rbである
2. $1の値は、https://www.qiita.com/である
3. $2の値は、.qiitaである
4. $3の値は、https://www.qiitaである
答え1, 2
このパターンは出ました。
Rdoc
- 書式を覚えたら点取れます。
意味 | 書式 |
---|---|
見出し | = <h1> == <h2> === <h3> ==== <h4> ===== <h5> ====== <h6>
|
ボールド体(太字) | *text* |
イタリック体 | _text_ |
タイプライター体 | +text+ |
リスト |
* または-
|
番号付きリスト | 数字 + .
|
ラベル付きリスト |
[] で囲む : で区切る |
【変数と定数】
インスタンス変数
class Foo
def foo
@value
end
end
p Foo.new.foo # => nil
初期化されていない インスタンス変数を参照した時の値はnilです。
クラス変数
class Foo
@@value
def bar
@@value # => uninitialized class variable ... (NameError)
end
end
p Foo.new.bar
初期化していないクラス変数を参照しようとすると例外発生。
Ruby2.1.0 リファレンス: クラス変数
定数
定数の探索経路はまず自クラス内を探し、なければ外側のクラスを探すといった感じ。
CONST = "Top"
module Mod
puts CONST # => Top
puts ::CONST # => Top
# モジュール内定数定義
CONST = "Mod"
puts CONST # => Mod まず自クラスから探索
puts ::CONST # => Top
end
class Cls1
puts CONST # => Top
# クラス内定数
CONST = "Cls1"
puts CONST # => Cls1
def func
puts CONST
puts ::CONST
puts Mod::CONST
#メソッド内で定数初期化
# CONST = "func" # => dynamic constant assignment メソッド内で定数定義できない
end
include Mod
puts CONST # => Cls1
puts Mod::CONST # => Mod
end
# メソッドから参照
Cls1.new.func
# => Cls1
# => Top
# => Mod
class Cls2 < Cls1
puts CONST # => Cls1
CONST = "Cls2"
puts CONST # => Cls2
puts Cls1::CONST # => Cls1
puts Mod::CONST # => Mod
end
p Cls1.ancestors # => [Cls1, Mod, Object, Kernel, BasicObject]
p Cls2.ancestors # => [Cls2, Cls1, Mod, Object, Kernel, BasicObject]
# 自クラスから探索開始。範囲演算子「::」を使用し探索開始経路を変更できる
::CONST # => トップレベルのCONSTを探す
Cls1::CONST # => Cls1クラス内のCONSTを探す
Mod::CONST # => Modモジュール内のCONSTを探す
定数の初期化について
CONST = "a"
def foo1; p CONST; end
def foo2; p CONST = "b"; end # => dynamic constant assignment
def foo3; p CONST += "c"; end # => dynamic constant assignment
def foo4; p CONST << "d"; end
foo1 # => "a"
foo2 # 定義の段階でエラー
foo3 # 定義の段階でエラー
foo4 # => "ad" 破壊的なメソッドではないのでいけるらしいです
foo4の評価に関しては2、3日そんなバカなと現実として受け止める事が出来ませんでした。が、時間が経つとそれが当たり前の様に感じていた。この問題も出たので覚えてよかった。
【演算子】
<=>演算子(メソッド)
既存のクラスから生成されたオブジェクト(数値や文字列)には、<=>
演算子が定義されているが、自作したクラスのインスタンスには<=>
演算子が定義されていないので、自作したクラス内で<=>
メソッドを定義しないとsortメソッドなどを使用できない。
class C
attr_reader :num
def initialize(num)
@num = num
end
end
class MyNum < C
def <=>(other)
@num <=> other.num
end
end
var1 = C.new(3)
var2 = C.new(1)
var3 = C.new(2)
p [var1, var2, var3].sort.map{|n| n.num } # => `sort': comparison of C with C failed (ArgumentError)
num1 = MyNum.new(30)
num2 = MyNum.new(10)
num3 = MyNum.new(20)
p [num1, num2, num3].sort.map{|n| n.num } # => [10, 20, 30]
<=>演算子(メソッド)の戻り値
p 1 <=> 2 # => -1
p 2 <=> 1 # => 1
p 1 <=> 1 # => 0
# 左がレシーバ 右が引数
| 条件 | 戻り値 |
|---|---|---|
| レシーバの方が小さい | -1 |
| レシーバの方が大きい | 1 |
| 同等
==がtrueな関係 | 0 |
【ブロック】
制御構文とメソッドのスコープ
for文
for var in [0, 1, 2] do
num = var
end
# 参照できる
p num # => 2
eachメソッド
[1, 2, 3].each do |n|
num = n
end
# 参照できない
p num # => undefined local variable or method `num'
スコープを作るもの作らないものを表にまとめてみた
スコープを作る | スコープを作らない |
---|---|
loop | if |
upto downto |
unless |
times | for |
each | while |
each_with_index | case |
これくらい覚えとけば大丈夫。この辺はSilverでも範囲だった気がする。
メソッドのブロックはスコープを作るので、ブロック内部で初期化したローカル変数はブロック外で直接参照できない。
どういう理由でfor文は参照できて、eachメソッドはできないのかと言われたら、どなたかご存知でしたらご教授頂きたいです...。←こちらに関してはコメントでご回答を頂きました。
Proc
Proc.newでブロックをオブジェクト化する。
そのオブジェクトに対して**call
メソッドによって評価する**ということだけ覚えておけばいい。
block = Proc.new{|x, y| p x + y }
block.call(1, 2) # => 3
こちらの記事(ブロック, Proc.new, lambda)がすごくわかりやすかった。
yield
def method(arg)
yield
p block_given?
end
method(1) { p "Hello" }
# => "Hello"
# => true
Ruby2.1.0 リファレンス: yield
Ruby2.1.0 リファレンス: block_given?
クロージャについて
事前に定義している変数をブロック内で呼ぶ場合。
変数と同じ参照先の値をブロック内で更新するので、ブロック外で同じ変数の値を参照する際、値が更新されている。らしい。
a = 1
def method
yield
end
method { a += 1 }
p a # => 2
【例外処理】
ensure節があるときの評価値
def method
begin
"a"
rescue
"b"
else
"c"
ensure
"d"
end
end
p method # => "c"
最後に評価されたrescure節、else節の値がそのbegin式の評価値になるらしい。
ensure節に関しては実行されるけど、評価値としては無視されるらしい。
教本にそんなこと書いてなかった気がするけど。
こちらの記事を参考にさせて頂きました。
Hatena Glog 記事
【大域脱出】
catch/throw
throw
からcatch
までジャンプする。ということだけ覚えておけばいい。
- タグ付けをして、同じタグの
catch
にジャンプする -
throw
には、引数を渡すことができ、渡した引数をcatch
の戻り値とすることができる。
a, b = catch :exit do
for x in 1..10
for y in 1..10
throw :exit, [x, y] if x + y == 10
end
end
end
puts a, b
# => 1
# => 9
タグ付けしないと動作しませんでした
catchのみ記述して、throwの代わりにその他脱出構文書いても動作はしました
throw含め「このうち正常に動作するコードを選択しなさい(複数)」という問題は出てたと記憶しているので一応。
a, b = catch :exit do
for x in 1..10
for y in 1..10
return [x, y] if x + y == 10
end
end
end
a, b = catch :exit do
for x in 1..10
for y in 1..10
break [x, y] if x + y == 10
end
end
end
a, b = catch :exit do
for x in 1..10
for y in 1..10
next [x, y] if x + y == 10
end
end
end
その他脱出構文
構文 | 動作 |
---|---|
return | メソッドから抜ける |
break | ループから抜ける |
next | 処理を中断し次の処理を実行する |
return
def foo
return p "メソッドを抜ける" if true
p "ここは実行されない"
end
foo # => "メソッドを抜ける"
break
def foo
[1, 2, 3].each do |num|
break
p num
end
p "ここは実行される"
end
foo # => "ここは実行される"
next
def foo
[1, 2, 3].each do |num|
next if num == 2
puts num
end
end
foo
# => 1
# => 3
【引数】
キーワード引数
def qualification(level, rang: "Ruby")
puts "#{rang} #{level}"
end
qualification("Gold") # => Ruby Gold
可変長引数/配列
def func(arg, *args)
p arg
p args
p *args
end
func *[1, 2, 3]
# 実行結果
1
[2, 3]
2
3
可変長引数/ハッシュ
def func(**hash)
p hash
p **hash
end
func key: "val"
# 実行結果
{:key=>"val"}
{:key=>"val"}
可変長引数
- デフォルトの値は指定できない
- 1つのメソッドに対して1つしか指定できない
ブロック引数
def func(arg, &block)
yield
block.call
end
func(1){ p "ブロック引数" }
# 実行結果
{ p "ブロック引数" }
{ p "ブロック引数" }
yield
でもcall
メソッドでも呼べる
こちらの記事にわかりやすくまとめてありました。
【ラムダ式】
試験では、「文法」と「Proc.new
とlambda
の違い」くらい覚えておけばいいと思います。
文法
# lambda記法
# 書式①
lmd = lambda{ |a| p a }
lmd.call("Ruby") # => "Ruby"
# 書式②
lmd = ->(a){ p a }
lmd.call("Ruby") # => "Ruby"
# ①②どちらもProcクラスのインスタンスを生成する。
Proc.newとlambdaの違い
Proc | lambda | |
---|---|---|
引数が一致しない場合の動作 | nilを代入 | ArgumentError発生 |
returnの動作 | 生成元のスコープを脱出 | 呼び出し元に復帰 |
引数が一致しない場合の動作
# Proc
proc = Proc.new{ |x, y| y }
p proc.call(1) # => nil 第2引数yにはnilが代入される
# lambda
lambda = ->(x, y){ y }
p lambda.call(1) # => ArgumentError
Proc.newのreturnとlambdaのreturn
# Procの場合
def proc_test
proc = Proc.new{return "生成元のスコープ(メソッド)から脱出"}
proc.call
2 # ここは実行されない
end
p proc_test # => "生成元のスコープ(メソッド)から脱出"
# lambdaの場合
def lambda_test
lambda = -> {return "呼び出し元へ復帰"}
lambda.call
2 # ここは実行される
end
p lambda_test # => 2
ラムダに関してはこれくらい覚えていれば大丈夫かと思いました。
こちらの記事(ブロック, Proc.new, lambda)がすごくわかりやすかった。
【Objectクラス】
method_missing
呼び出したメソッドが見つからない場合、BasicObject
クラスのmethod_missing
が呼び出され例外NoMethodError
を発生させる
デフォルトの定義
- 第1引数 > 指定されたメソッド名
- 第2引数以降 > 指定された引数を渡す
- 例外
NoMethodError
を発生させる
class C
end
p C.new.no_method
# 実行結果
undefined method `no_method' for #<C:0x00005636a82557f8> (NoMethodError)
Cクラスのオブジェクトにはno_method
メソッドを持っていないため、暗黙的にmethod_missing
メソッドが呼びだされNoMethodError
を発生させている
method_missingの定義内容を変更してみる
class C
def method_missing(name) # 引数nameには「no_method」が入る
"メソッド #{name} は存在しません"
end
end
p C.new.no_method
# 実行結果
"メソッド no_method は存在しません"
親にBasicObjectクラスを持つCクラスには、BasicObjectクラスで定義されたmethod_missingメソッドを知っている。Cクラスでそのmethod_missingの内容を書き換えてみた。
Cクラスのインスタンスには存在しないメソッドを呼ぶとCクラスで再定義したmethod_missingが呼ばれ処理を行う。
親クラスのmethod_missingを呼んでみる
class C
def method_missing(name)
"メソッド #{name} は存在しません"
super
end
end
p C.new.no_method
# 実行結果
undefined method `no_method' for #<C:0x00005636a82557f8> (NoMethodError)
Object#freeze
freezeメソッドとは
- オブジェクトを凍結(変更を禁止)する
- 凍結されたオブジェクトの変更はVer2.1.0ではRumtimeErrorが発生、Ver2.6.0ではFrozenErrorが発生
- 凍結されたオブジェクトを元に戻す方法はない
- 凍結されるのはオブジェクトであり、変数ではない
- 代入などで変数の指すオブジェクトが変化するのはfreezeメソッドでは防げない
- freezeメソッドが防ぐのは破壊的な操作のみ。例えばreplaceメソッドやgsubメソッドで変数の中身を変更しようとした場合
var1 = "".freeze
var1 = "foo" # 代入は防げない
p var1 #=> "foo"
var1.replace("bar") # 凍結後、再代入すると破壊的メソッドで変更可能でした
p var1 # => "bar"
var1.freeze # 再度凍結
var1.replace("bar")# can't modify frozen string (RuntimeError)
Object#dup
- オブジェクトを複製して返す
- オブジェクトの内容、taint情報をコピー
a = "hoge"
# = > "hoge"
a.object_id
# => 47186718116820
b = a.dup
# => "hoge"
b.object_id
# => 47186718116580
Object#clone
- オブジェクトを複製して返す
- オブジェクトの内容、taint情報、凍結状態(freeze)、特異メソッドをコピーする
dupメソッド、cloneメソッドでの複製はシャローコピー(浅いコピー)であり、自分自身の複製しかできない。
a = "hoge"
# = > "hoge"
a.object_id
# => 47168843196940
b = a.clone
# => "hoge"
b.object_id
# => 47168843196760
dupとcloneの違いに関しての問題は出ました。
どちらも変数の参照先まではコピーできない。
メソッド | 概要 |
---|---|
dup |
・ オブジェクトの内容、taint情報をコピー ・ 特異メソッド、凍結状態はコピーしない |
clone | ・ 特異メソッド、凍結状態もコピーする |
shallow copy(浅いコピー)とdeep copy(深いコピー)
概要 | |
---|---|
shallow copy | ・ オブジェクト自身を複製するだけ(オブジェクトの指している配列の要素までは複製しない) |
deep copy |
・ Rubyではdeep copyを行う機能はない ・ Marshalモジュールを用いて近いことは実現可能 ・ だが、Marshalモジュールが扱えないオブジェクトは複製できない |
Object#taint / #untaint / #tainted?
動作 | |
---|---|
tait | ・ オブジェクトに汚染マークを付ける |
untaint | ・ オブジェクトの汚染マークを外す |
tainted? |
・ オブジェクトの汚染状態を確認する ・ 汚染状態ならtrue 汚染状態ではない場合はfalseを返す |
a = "string"
# => "string"
p a.tainted?
# => false
p a.taint
# => "string"
p a.tainted?
# => true
p a.untaint
# => "string"
p a.tainted?
# => false
リファレンス: taint
リファレンス: セキュリティモデル
Fiber
複数のタスクを切り替え、並行処理する機能
Threadとの違い
- Fiberはタスク切り替えのタイミングを開発者がプログラム内で明示的に記述する
- Threadのタスク切換えはOSや仮想マシンが行う
Fiberクラスをインスタンス化して使う
f = Fiber.new do
(1..3).each do |i|
Fiber.yield i
end
nil
end
p f.resume # => 1
p f.resume # => 2
p f.resume # => 3
p f.resume # => nil
p f.resume # FiberError(例外発生)
Fiber.newのブロック内の処理とそれ以外の処理を切り替えながら実行するサンプル。
- ファイバへコンテキストの切り替えは
resume
メソッドを使用する - 対象のファイバ内の処理を終了するか
Fiber.yield
が呼ばれるまで処理を実行する - 一回目の
f.resume
が呼び出されたタイミングで、ファイバ内に処理が移る -
resume
の戻り値はFiber.yield
の引数を返す- なので、最初の1行目には
1
が出力される - 2, 3と順番に出力され
Fiber
を抜け、4回目はnil
が出力される
- なので、最初の1行目には
- 5回目以降
resume
メソッドでファイバを呼んでも、実行する処理がないため、例外が発生する
Fiberに関してはこれくらい覚えてれば大丈夫でした
【Moduleクラス】
class_evalとinstance_eval
こちらの記事を参考にさせていただきました。
Marshal
- Rubyのオブジェクトを文字列化し、ファイルに書き出し、読み戻しをする機能がある。
- 文字列化したデータを「マーシャルデータ」と呼ぶ
- Railsのセッション変数をクッキーやデータベースに登録するときに利用される
- 一部書き出せないクラスがある
- オブジェクト化にはMarshal.dumpメソッドを使用する
- Marshal.dumpメソッドで文字列化できないオブジェクトが指定された場合はTypeErrorが発生する
- 文字列化できないオブジェクト
- 名前のついていないクラスやモジュール。ArgumentErrorが発生
- システムがオブジェクトの状態を保持するよな、IOクラス、Dirクラス、Procクラス、Threadクラスなどのインスタンス
- 特異メソッドを定義したオブジェクト
- 上記オブジェクトを間接的に指定しているクラスのオブジェクト。初期値をブロックで指定したHashくクラスのオブジェクトなど
Rubyオブジェクトを文字列化する
p Marshal.dump({a: 1, b: 2, c: 5})
# 実行結果
"\x04\b{\b:\x06ai\x06:\x06bi\a:\x06ci\n"
2つ目の引数にIOクラスかそのサブクラスのオブジェクトを指定すると、そのオブジェクトに直接書き出す
文字列化をした文字列をファイルに書き出す
file = File.open("/tmp/marshaldata", "w+")
# => #<File:/tmp/marshaldata>
Marshal.dump({a: 1, b: 2, c: 3}, file)
# => #<File:/tmp/marshaldata>
文字列化したマーシャルデータからRubyオブジェクトを復元する
Marshal.load
str = Marshal.dump({a: 1, b: 2, c: 3})
p str # => "\x04\b{\b:\x06ai\x06:\x06bi\a:\x06ci\b"
p Marshal.load(str) # => {:a=>1, :b=>2, :c=>3}
2つ目の引数にIOクラスかそのサブクラスのオブジェクトを指定すると、そのオブジェクトから直接読み戻す(復元)する
file = File.open("/tmp/marshaldata", "w+")
# # => <File:/tmp/marshaldata>
Marshal.dump({a: 1, b: 2, c: 3}, file)
# # => <File:/tmp/marshaldata>
file.rewind
# => 0
p Marshal.load(file)
# => {:a=>1, :b=>2, :c=>3}
Refinements
指定したクラス/モジュールに対してrefine
メソッドを使い変更を加える宣言をし、using
メソッドで呼び出した以降からreifne
メソッドで変更した内容を適用する
usingの有効範囲
- クラス/モジュール定義の外で
using
呼び出した場合は、ファイルの末尾まで - クラス/モジュール定義の中で
using
を呼び出した場合は、そのクラス/モジュールの末尾まで -
using
はメソッドの中で呼び出すことはできない。呼び出した場合RuntimeError
が発生 -
using
を2行書いたとしても1つのメソッドで有効になる再定義は1つだけ。最後に書いたusing
が優先される
refine/usingで制限してみる
class C
def hoge
puts "hogeを定義"
end
end
module M
refine C do
def hoge
puts "hogeの内容を変更: 変更を有効にしました"
end
end
end
c = C.new
c.hoge
using M
c.hoge
# 実行結果
hogeを定義
hogeの内容を変更: 変更を有効にしました
include
- モジュールで定義したメソッドをインスタンスメソッドとして追加する
-
include
したクラスより後に探索される
module M
def foo
puts "foo"
end
end
class C
include M # モジュールMのメソッドをインスタンスメソッドとして追加
end
C.new.foo # => foo
C.foo # => NoMethodError
# 継承チェーン
p C.ancestors # => [C, M, Object, Kernel, BasicObject]
prepend
- モジュールで定義したメソッドをインスタンスメソッドとして追加する
-
prepend
したクラスより先に探索される
module M
def foo
puts "foo"
end
end
class C
prepend M
end
# 継承チェーン
# prependを呼び出したクラスよりも先に定義
p C.ancestors # => [M, C, Object, Kernel, BasicObject]
includeとprependの違い
どちらもモジュールのメソッドをクラスに取り込むが
include | クラスの次に呼び出したモジュールをメソッド探索する |
---|---|
prepend | クラスよりも前に呼び出したモジュールからメソッド探索する |
module M
def method
"1"
end
end
class C1
prepend M
def method
"2"
end
end
class C2
include M
def method
"3"
end
end
p C1.new.method
p C2.new.method
# 実行結果
"1"
"3"
extend
- モジュールで定義したメソッドを特異メソッドとして追加する
module M
def foo
puts "foo"
end
end
class C
extend M # モジュールMのメソッドをクラスメソッドとして追加
end
C.new.foo # => NoMethodError
C.foo # => foo
オブジェクトにMix-in
module M
def f
puts "Hello"
end
end
class C
end
c = C.new
c.extend(M)
c.f
# 実行結果
Hello
継承チェーン
module M
end
class C
extend M
end
p C.ancestors
# 実行結果
[C, Object, Kernel, BasicObject]
Module#ancestors
- Moduleクラスのインスタンスメソッド
- クラス、モジュールのスーパークラスとインクルードしているモジュール を優先順位順に配列に格納して返します。
- レシーバがModuleクラスのインスタンスかclassクラスのインスタンスの場合に有効
module M1
end
module M2
end
class Cls1
include M1
end
class Cls2 < Cls1
def foo
p self.ancestors
end
include M2
end
Cls2.new.foo
# 実行結果
undefined method `ancestors' for #<Cls2:0x0000557a3e210710> (NoMethodError)
append_features
append_featuresを上書きする場合はsuperを書かないとincludeされない
module M
def self.append_features(include_class_name)
puts "append_features"
super # このsuperを書かないとエラー発生
end
def func
p "Hello World"
end
end
class C
include M
end
C.new.func
undef_method
- クラスやモジュールのメソッドを未定義にして、呼び出せなくする。
- 引数にメソッド名をシンボルか文字列で指定する(複数指定可)
- 戻り値はクラスやモジュール自身
class C
def func
puts "Hello"
end
end
c = C.new
c.func # => Hello
# class_evalメソッド
# ブロックをクラス定義やモジュール定義の中のコードであるように実行します。
C.class_eval { undef_method :func }
c.func # => undefined method `func' for #<C:0x000056370fe002a8> (NoMethodError)
親クラスのメソッドをサブクラスで未定義にした場合は、undef_method
を呼び出したクラスとそのサブクラスでメソッドを呼び出せなくなる。
ただし、親クラスのメソッドには影響しない。
class C
def func; puts "Hello"; end
end
class Child < C
undef_method :func
end
class GrandChild < Child
end
C.new.func # => Hello
Child.new.func # => NoMethodError
GrandChild.new.func # => NoMethodError
remove_method
- クラスやモジュールからメソッドを削除する
- 引数にはメソッド名をシンボルか文字列で指定する(複数指定可)
- 戻り値はクラスやモジュール自身
class C
def func; puts "Hello"; end
end
C.class_eval { remove_method :func }
puts C.new.func
# 実行結果
undefined method `func' for #<C:0x000055b9766f5128> (NoMethodError)
- 削除できるのはそのクラス・モジュールで定義されたメソッドだけ
- 親クラスのメソッドは指定できない
- 削除したメソッドと同名のメソッドが親クラスにある場合は、親クラスのメソッドが呼び出される
class C
def f; puts "World"; end
end
class Child < C
def f; puts "Hello"; end
end
child = Child.new
puts child.f
Child.class_eval { remove_method :f }
puts child.f
alias_methodとalias
メソッド名 | 概要 |
---|---|
alias_method | 引数に指定するメソッド名はStringかSymbolで指定する |
alias | 引数のメソッド名は識別子かSymbolで指定する |
# 書式
alias_method 新メソッド名 旧メソッド名
alias 新メソッド名 旧メソッド名
alias_methodの定義例
class String
alias_method :hoge, :reverse
end
p "12345".hoge
# 実行結果
"54321"
aliasの定義例
class String
alias :hoge :reverse
end
p "12345".hoge
# 実行結果
"54321"
const_missing
metheod_missing
の定数版
class Foo
def self.const_missing(name)
"定数 #{name} はありません"
end
end
p ::Foo::C
# 実行結果
"定数 C はありません"
【karnelクラス】
pとputsとprint
内部動作に関して。
メソッド | 動作 |
---|---|
p | inspectメソッドが呼び出される |
puts | to_sメソッドが呼び出される |
to_sメソッドが呼び出される |
【例外】
+
メソッドなどで文字列を連結する場合は、to_str
メソッドが呼び出される
リファンレス: p
リファレンス: puts
リファレンス: print
リファレンス: to_str
class C
def initialize(str)
@str = str
end
def inspect
"inspect"
end
def to_s
"to_s"
end
def to_str
"to_str"
end
end
p C.new "" # => inspect
p "#{ C.new("") }" # => "to_s"
p "" + C.new("") # => "to_str"
puts C.new "" # => to_s
puts "#{ C.new("") }" # => to_s
puts "" + C.new("") # => to_str
print C.new "" # => to_s
print "#{ C.new("") }" # => to_s
print "" + C.new("") # => to_str
eval
そのままですが。
a = nil
eval('a = RUBY_RELEASE_DATE')
p a #=> "2007-03-13"
eval('def fuga;p 777 end')
fuga #=> 777
exit
- Rubyプログラムの実行を即座に終了させる
- 例外SystemExitを発生させる
- **
exit
**は例外として補足できる - **
exit!
**は例外として補足できない
def proc_exit
begin
exit
rescue SystemExit
puts "processing_exit"
ensure
puts "確保する"
end
end
proc_exit
# 実行結果
processing_exit
確保する
exit!
def proc_exit!
begin
exit!
rescue SystemExit
puts "processing_exit"
ensure
puts "確保する"
end
end
proc_exit! # =>処理を強制終了し、rescue節、ensure節の処理は通らない
【Enumerableクラス】
- メソッドの遅延評価版を提供するためのクラス
以下のメソッドが遅延評価を行う |
---|
map/collect |
flat_map/collect_concat |
select/find_all |
reject |
grep, grep_v |
take, take_while |
drop, drop_while |
slice_before, slice_after, slice_when |
chunk, chunk_while |
uniq |
zip (※互換性のため、ブロックを渡さないケースのみlazy) |
Enumerable#lazy
a = [1, 2, 3, 4, 5].lazy.select { |e| e % 2 == 0 }
#<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3, 4, 5]>:select>
b = a.map { |e| e * 2 }
#<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3, 4, 5]>:select>:map>
c = a.take(3)
#<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3, 4, 5]>:select>:take(3)>
p c.to_a # ここで評価される
# => [2, 4]
【Arrayクラス】
Array#push
- 配列の末尾に引数を要素として追加する
- 破壊的
arr = [1, 2]
arr.push(3)
p arr
# 実行結果
[1, 2, 3]
Array#pop
- 末尾の要素を取り出す/取り除く
- 破壊的
arr = [1, 2, 3]
puts arr.pop # 取り出し/取り除き
p arr # 内容を変更された
# 実行結果
3
[1, 2]
Array#shift
- 配列の先頭の要素を取り出す/取り除く返す
- 破壊的
arr = [1, 2]
puts arr.shift
p arr
# 実行結果
1
[2]
Array#unshift
- 引数を配列の先頭に追加する
- 破壊的
arr = [1, 2]
arr.unshift(3)
p arr
# 実行結果
[3, 1, 2]
【Comparableクラス】
- インクルードしたクラスで比較演算子である<=>を元にオブジェクト同士の比較ができるようになる
メソッド | <=>演算子の返り値 |
---|---|
< | 負の整数 |
<= | 負の整数か0 |
== | 0 |
> | 正の整数 |
>= | 正の整数か0 |
between? | --- |
※この辺はリファレンス見たほうがわかりやすかった
自作クラスを定義してみる
class Sample
def initialize(value)
@value = value
end
def value
@value
end
def <=>(other)
other.value <=> self.value
end
end
自作クラスにComparableモジュールをインクルードしてみる
class Sample
include Comparable
def initialize(value)
@value = value
end
def value
@value
end
def <=>(other)
other.value <=> self.value
end
end
a = Sample.new(10)
#<Sample:0x000055c4a9f0cd00 @value=10>
b = Sample.new(5)
#<Sample:0x00005576e2290348 @value=5>
a < b
# => true
a <= b
# => true
a == b
# => false
a > b
# => false
a >= b
# => false
【数値】
RationalとComplexの演算
a = 1 + "(1/2r)".to_r
p a.class # => Rational
a = a + 1.0
p a.class # => Float
a = a + "(1/2r)".to_r
p a.class # => Float
a = a + "(1 + 2i)".to_c
p a.class # => Complex
【正規表現】
正規表現オブジェクト生成
/Ruby Gold/ # => /Ruby Gold/
%r|Ruby Gold| # => /Ruby Gold/
Regexp.new("Ruby Gold") # => /Ruby Gold/
文字列との比較
特殊変数のところでもふれたけど。()
内にマッチした部分がグローバル変数に代入されるらしいです。って教本に書いているけどこれ覚えてたら点取れます。
変数名 | 参照される値 |
---|---|
$0 | 実行中のファイル名 |
$1 〜 $n | n番目のカッコにマッチする文字列 |
%r|(https://www(\.)(.*)/)| =~ "https://www.qiita.com/"
p $1 # => "https://www.qiita.com/"
p $2 # => "."
p $3 # => "qiita.com"
後方参照
「マッチした文字列の前後」と「マッチした文字」を参照する、正規表現の後方参照と呼ぶものらしいです。
%r|Ruby| =~ "u"
p $` # => "R"
p $& # => "u"
p $' # => "b"
正規表現記号
一応載せておきます
本に書いているけど、スマホで片手でっていう趣旨も一応ありますし...
記号 | 意味 |
---|---|
. | 改行以外の文字。 mオプション指定の場合は改行にもマッチする |
\d | 数字 |
\D | 数字以外 |
\w | 英数字とアンダースコア |
\W | 英数字とアンダースコア以外 |
\s | 空白文字(\t, \n, \r, \f) |
\S | 空白以外 |
\A | 先頭の文字(改行無視) |
\z | 末尾の文字(改行無視) |
\Z | 末尾の文字(改行にもマッチ) |
* |
直前の文字の0回以上の繰り返し |
+ | 直前の文字の1回以上の繰り返し |
{m} | 直前の文字のm回の繰り返し |
{m,} | 直前の文字の最低m回の繰り返し |
{m, n} | 直前の文字の最低m回、最高n回の繰り返し |
オプションも
オプション | 意味 |
---|---|
i | 大文字小文字を区別しない |
o | 一度だけ式展開を行う |
x | 空白と改行を無視する #以降をコメントとして無視する |
m | 記号「.」が改行にマッチする(複数行モード) |
【JSON】
書き方
--- # 配列["red", "green", "blue"]
# JSONの表現
["red", "green", "blue"]
--- # ハッシュ{"country" => "japan", "state" => "tokyo"}
# JSONの表現
{"country": "japan", "state": "tokyo"}
JSONの読み込みに使用するメソッド一覧
メソッド | 動作 |
---|---|
load | 文字列からJSONを読み込む |
parse | 文字列からJSONを読み込む |
load
メソッド | 動作 |
---|---|
load |
・ 文字列からJSONを読み込む ・ 文字列以外も指定可能 |
JSON.load(source, proc = nil)
- **
source
**にはJSON形式の文字列を指定する - to_str, to_io, readなどのメソッドを持つオブジェクトであれば文字列以外も指定できる
-
proc
には、要素を読み込むごとに呼び出されるProcオブジェクトを渡すことができる
require 'json'
json = <<-DATA
["Red", "Green", "Blue"]
DATA
p JSON.load(json)
# 実行結果
["Red", "Green", "Blue"]
proc指定
require 'json'
json = <<-DATA
"Red"
"Green"
"Blue"
DATA
p JSON.load(json, lambda{|x| p x})
# 実行結果
["Red", "Green", "Blue"]
文字列からJSONを読み込む
require 'json'
JSON.load(File.open(""data.json))
# 実行結果
["Red", "Green", "Blue"]
メソッド | 動作 |
---|---|
parse |
・ 文字列からJSONを読み込む ・ 文字列以外は指定不可 |
書式
JSON.parse(source, options = {})
- optionsには解釈するときのオプションを指定
- リファレンス: オプションなど
JSONの書き込みに使用するメソッド
メソッド | 動作 |
---|---|
dump | JSONの書き込み |
JSON.dump(obj, io = nil, limit = nil)
- **
obj
**に書き出したい対象のオブジェクト(書き出し元)を指定 - 文字列で結果を得たい場合は第2引数、第3引数を省略する
- ファイルに書き出したい場合は**
io
**にIOオブジェクトを指定する -
limit
以上深くリンクしたオブジェクトをダンプしようとするとArgumentErrorが発生する
require 'json'
array = ["Red", "Green", "blue"]
JSON.dump(array)
# 実行結果
"[\"Red\",\"Green\",\"blue\"]"
ファイルに書き出したい場合
require 'json'
array = ["Red", "Green", "blue"]
File.open("dump.json", "w") do |f|
JSON.dump(array, f)
end
to_jsonメソッド
- 自身を JSON 形式の文字列に変換して返します
- 内部的にはハッシュにデータをセットしてからHash#to_jsonを呼び出しています。
require 'json'
h = {"a" => 1, "b" => 2}
p h.to_json
# => "{\"a\":1,\"b\":2}"
p to_json(h)
# => "\"main\""
【yaml】
require 'yaml'
**書き方**
--- # 配列["red", "green", "blue"]
# yamlの表現
- red
- green
- blue
---# ハッシュ{"country" => "japan", "state" => "tokyo"}
# yamlの表現
country: japan
state: tokyo
読み込みメソッド一覧
メソッド | 動作 |
---|---|
load |
・ YAMLデータが記録された文字列またはIOインスタンスからHashクラスのインスタンスを生成 ・ 文字列またはIOに複数のYAML記録されていても最初のYAMLのみが生成され、残りは無視 |
load_file | 引数に指定したパスのYAMLファイルからオブジェクトを生成。 ・ 文字列またはIOに複数のYAML記録されていても最初のYAMLのみが生成され、残りは無視 |
load_stream | 引数のオブジェクトに記録された複数のYAMLデータを順番にロードし、結果をYAML::Streamのインスタンスで返す |
load_documents | 引数のオブジェクトに記録された複数のYAMLデータを順番にロードし、それぞれをブロック内で処理する |
YAML.load
メソッド | 動作 |
---|---|
load |
・ YAMLデータが記録された文字列またはIOインスタンスからHashクラスのインスタンスを生成 ・ 文字列またはIOに複数のYAML記録されていても最初のYAMLのみが生成され、残りは無視 |
require 'yaml'
yaml_data = <<-DATA
- red
- green
- blue
---
- yellow
- pink
- white
DATA
p YAML.load(yaml_data)
# 結果
["red", "green", "blue"]
YAML.laod_file
メソッド | 動作 |
---|---|
load_file | 引数に指定したパスのYAMLファイルからオブジェクトを生成。 ・ 文字列またはIOに複数のYAML記録されていても最初のYAMLのみが生成され、残りは無視 |
require 'yaml'
YAML.load_file "sample.yml"
# 結果
["red", "green", "blue"]
YAML.load_stream
メソッド | 動作 |
---|---|
load_stream | 引数のオブジェクトに記録された複数のYAMLデータを順番にロードし、結果をYAML::Streamのインスタンスで返す |
require 'yaml'
yaml_stream = YAML.laod_stream(File.open "sample.yml")
p yaml_stream
# 結果
[["red", "green", "blue"], ["yellow", "pink", "white"]]
YAML.load_documents
require 'yaml'
YAML.load_documents(File.open "sample.yml") do |yaml|
p yaml.first
end
# 結果
"red"
"yellow"
書き込みメソッド一覧
メソッド | 動作 |
---|---|
dump |
・ 単一のインスタンスを文字列またはIOインスタンスに出力する ・ 引数を省略するとyaml形式 に変換した文字列を返し、引数を指定すると指定した出力先にyaml形式のデータを書き込む |
dump_stream | 複数のオブジェクトを文字列に出力する |
YAML.dump
メソッド | 動作 |
---|---|
dump |
・ 単一のインスタンスを文字列またはIOインスタンスに出力する ・ 引数を省略するとyaml形式 に変換した文字列を返し、引数を指定すると指定した出力先にyaml形式のデータを書き込む |
require 'yaml'
# ===== 引数を省略した場合 =====
colors = ["red", "green", "yellow"]
YAML.dump colors
# 結果
"---\n- red\n- green\n- yellow\n"
# ===== 引数を指定した場合 =====
File.open("sample.yml", "w+") do |f|
YAML.dump(colors, f)
end
# 結果
---
- red
- green
- blue
YAML.dump_stream
メソッド | 動作 |
---|---|
dump_stream | 複数のオブジェクトを文字列に出力する |
require 'yaml'
# ===== 引数を省略した場合 =====
colors1 = ["red", "green", "yellow"]
colors2 = ["yellow", "pink", "white"]
YAML.dump_stream colors1, colors2
# 結果
"---\n- red\n- green\n- yellow\n---\n- yellow\n- pink\n- white\n"
【Socket】
クラス図
Scoketライブラリに関しては、クラス名にserverと付くものは「TCPS」「UNIX」ということを覚えておくだけで点が取れました。
全て覚えることは僕の記憶力ではちょっと無理でした...というかそこに注力するなら他のところにとも思うし。実際に試験で出た問題を模したものを後述しておきます。
模擬問題
socketライブラリについて正しい記述を選択しなさい(2つ)
1. socketライブラリにTCPSocketというクラスは存在する
2. socketライブラリにTCPServerというクラスは存在する
3. socketライブラリにUNIXServerというクラスは存在しない
4. socketライブラリにBasicServerというクラスは存在する
正解 1, 2
実際にこのパターンの問題が出ました。
ITトレメや教本にもSocketの問題が数問あるのでそちらを解いておくだけで大丈夫だと思います。
【Date】
クラス図
日付の加減算
DateTimeクラス、Timeクラスもついでに。
同クラスインスタンス同士の減算
require 'time'
require 'date'
time = Time.now
day = Date.new(2019)
datetime = DateTime.new
p (time - time).class # => Float
p (day - day).class # => Rational
p (datetime - datetime).class # => Rational
日付の加減算
require "date"
require "time"
# Dateクラス
date = Date.new(2000, 1, 1)
puts date + 1
puts date >> 1
# => 2000-01-02
# => 2000-02-01
# DateTimeクラス
datetime = DateTime.new(2000, 1, 1)
puts datetime + 1
puts datetime >> 1
# => 2000-01-02T00:00:00+00:00
# => 2000-02-01T00:00:00+00:00
# Timeクラス
time = Time.new(2000, 1, 1, 0, 0, 0)
puts time + 1
# Timeだけ秒単位
# => 2000-01-01 00:00:01 +0000
日付の加減算は実際に出ました。2、3問取れた気がする。
<<
、>>
の演算結果は大なり小なりの関係で覚えたら覚えやすかったです。
-
date << 1
ならdateより1小さいので1ヶ月前 -
date >> 1
ならdateより1大きいので1ヶ月後
のように。って教本にも書いてあったようななかったような。小さいかったらなんで1ヶ月前になんねんという疑問もありつつ感覚的に覚えやすかったのでとしか...すみません。
【StringIO】
StringIOに関しては問題を解くだけでほとんど何も学習してません。ので、何も述べることができませんが、下記のような問題パターンは出題されました。というかStringIOの問題は下記のような問題パターンしか見たことありません。ので、StringIOについては下記のような問題パターンに答えれるようにしておくぐらいにして、あとは他に注力すべきだと思いました(試験を受けた所感)。なので問題だけ載せておきます。
stringioライブラリの説明として正しいものを選択しなさい。
文字列をファイルに読み書きすることができる
文字列をファイル入出力専用で扱うことができる
StringIOクラスはIOクラスのサブクラスである
文字列をIOオブジェクトのように扱うことができる
正解 4
【Thread】
覚えること
- Thread作成メソッド
- ライフサイクル
- 例外時の挙動
スレッドを作成する
スレッド作成メソッド
Thread#new
Thread#fork
Thread#start
引数に必ずブロックを渡す必要がある。
引数にブロックを指定しないとThread
は作成できず、ThreadError
を返す
Thread.new { 'Hello, World' }
Thread.fork { 'Good Morning World' }
Thread.start { 'Good Evening World' }
p Thread.list
[#<Thread:0x000055d8bd4c4b68 run>, #<Thread:0x000055d8bd7b04d0@Main.rb:1 dead>, #<Thread:0x000055d8bd7b0368@Main.rb:2 dead>, #<Thread:0x000055d8bd7b0250@Main.rb:3 run>]
スレッドの状態(ライフサイクル)
- スレッドには5つの状態がある
例
<Thread:0x00007fdf4c154860@code11.rb:3 run> 末尾の「run」がそのスレッドの状態を表す
状態 | 意味 |
---|---|
run | 【実行可能または実行中】 生成直後、Thread#run、 Thread#wakeupで起こされたスレッドはこの状態になる |
sleep | 【停止中】 Thread#stopやThread#joinにより一時的に停止されたスレッドはこの状態になる |
aborting | 【終了処理】 ** Thread#kill **などで終了されるスレッドは一時的にこの状態になる |
false | 【正常に終了した】Thread#killで終了したり、正常終了したスレッドの場合**false **が返る |
nil | 例外などで終了した場合は**nil **が返る |
dead | 【終了状態】 ** Thread#kill 等で終了したスレッドはこの状態になる。この状態のスレッドはどこからも参照されていなければGC **によりメモリ上からなくなり、この状態のスレッドは「死んで」いる |
例外時の挙動
マルチスレッドで例外が発生し、その例外がrescueで補足されなかった場合、例外が発生したスレッドだけが警告なしで終了し、他のスレッドは実行を続ける。
例外は発生するが、メッセージは出力せず停止状態になるということ。
-dオプションを付けるとデバッグモードで実行され、いずれかのスレッドで例外が発生した時点でメッセージを出力しインタプリタ全体が中断する
t1 = Thread.start do
raise ThreadError
end
loop {
n = rand(0..3)
puts n
if n == 0
break
end
}
ということを覚えていたら点が取れました。
【メソッドの可視性】
public / protected / privateの振る舞い
可視性 | 振る舞い |
---|---|
public | どのインスタンスからでも実行できる |
protected | 自分自身またはサブクラスのインスタンスから実行できる |
private | レシーバを付けた呼び出しはできない |
public
class C
def call_method
public_1
end
def with_receiver
self.public_1
end
def public_1; "public_1"; end
public
def public_2; "public_2"; end
end
p C.new.public_1 # => "public_1"
p C.new.public_2 # => "public_2"
p C.new.call_method # => "public_1"
p C.new.with_receiver # => "public_1"
protected
class C
def call_protected
protected_method
end
def with_receiver
self.protected_method
end
protected
def protected_method; "protected"; end
end
p C.new.protected_method # => NoMethodError
p C.new.call_protected # => "protected"
p C.new.with_receiver # => "protected"
private
class C
def call_private
private_method
end
def with_receiver
self.private_method # ここでNoMethodError発生
end
private
def private_method; "private"; end
end
p C.new.call_private # => "private"
p C.new.private_method # => NoMethodError
p C.new.with_receiver # => # NoMethodError
可視性の変更
- メソッドの可視性はサブクラスで自由に変更できる
class C
def method_1; end
def method_2; end
private
def method_3; end
end
class Child < C
public :method_3
protected :method_1
private :method_2
end
p C.new.public_methods(false) # => [:method_1, :method_2]
p C.new.protected_methods(false) # => []
p C.new.private_methods(false) # => [:method_3]
p Child.new.public_methods(false) # => [:method_3]
p Child.new.protected_methods(false) # => [:method_1]
p Child.new.private_methods(false) # => [:method_2]
【クラスの継承】
スーパークラスを省略してクラスを定義すると、自動的にObjectクラスを継承する。
つまりCクラスはObjectクラスを継承している。(合格教本p 125から引用)
class C
end
class Child < C
end
p C.ancestors # => [C, Object, Kernel, BasicObject]
p Child.ancestors # => [Child, C, Object, Kernel, BasicObject]
【マーシャリングについて】
マーシャリングとは
- 異なる2つのシステム間で、データを交換できるようにデータを操作する処理。
- 例えば、JavaとC言語のデータ型の違いを吸収するために、データの型を変換するなどの処理のこと
マーシャリングできないもの
- 無名クラス/モジュール
- システムがオブジェクトの状態を持つもの
- Dir, File, IO, Socket等
- 特異メソッドを定義したオブジェクト
マーシャリングできるもの
- ユーザーが作成したオブジェクトなど。
- マーシャリングできないものとして定義しているもの以外のもの
たぶん**「マーシャリングできるのはユーザーが作成したオブジェクトなど」**ということだけ覚えていれば1問解ける。
【参考】
こちらの記事の特に「Ruby Gold受けた人たちの復習ブログで知識を補う」は「合格教本ではわからない内容」かつ「試験で役に立つ内容」がたくさん載っていました。ので、見ておいて損はないと思います。