LoginSignup
31
28

More than 1 year has passed since last update.

1ヶ月後「Ruby Association Certified Ruby Programmer Gold version 2.1」に合格する方法

Last updated at Posted at 2019-07-02

結論から言うと
**「合格教本で基礎を押さえて、ひたすら問題を解く」**だけです。

メタプログラミングは一切読まなくても合格できます。

学習時間は業務後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つだけでした。他のも覚えとくべきかなとは思うけど。

ruby.rb
以下のコードについて正しいものを選択しなさい(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です。

Ruby2.1.0 リファレンス: インスタンス変数


クラス変数

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

Ruby2.1.0 リファレンス: 大域脱出

その他脱出構文

構文 動作
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.newlambdaの違い」くらい覚えておけばいいと思います。

文法

# 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)

リファンレス: method_missing


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#freeze


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#dup


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が出力される
  • 5回目以降resumeメソッドでファイバを呼んでも、実行する処理がないため、例外が発生する

Fiberに関してはこれくらい覚えてれば大丈夫でした

【Moduleクラス】

class_evalとinstance_eval

こちらの記事を参考にさせていただきました。

リファレンス: class_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の内容を変更: 変更を有効にしました

リファレンス: Refinements


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

リファレンス: append_features


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 引数に指定するメソッド名はStringSymbolで指定する
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 はありません"

リファレンス: const_missing

【karnelクラス】

pとputsとprint

内部動作に関して。

メソッド 動作
p inspectメソッドが呼び出される
puts to_sメソッドが呼び出される
print 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

リファレンス: eval

exit

  • Rubyプログラムの実行を即座に終了させる
  • 例外SystemExitを発生させる
  • **exit**は例外として補足できる
  • **exit!**は例外として補足できない

リファレンス: karnel#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]

リファレンス: Enumerable#lazy

【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 = {})

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\""

リファレンス: to_jsonメソッド

【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」ということを覚えておくだけで点が取れました。
全て覚えることは僕の記憶力ではちょっと無理でした...というかそこに注力するなら他のところにとも思うし。実際に試験で出た問題を模したものを後述しておきます。

スクリーンショット 2019-06-25 23.04.08.png

模擬問題

socketライブラリについて正しい記述を選択しなさい(2つ)
1. socketライブラリにTCPSocketというクラスは存在する
2. socketライブラリにTCPServerというクラスは存在する
3. socketライブラリにUNIXServerというクラスは存在しない
4. socketライブラリにBasicServerというクラスは存在する

正解 1, 2

実際にこのパターンの問題が出ました。
ITトレメや教本にもSocketの問題が数問あるのでそちらを解いておくだけで大丈夫だと思います。

【Date】

クラス図

スクリーンショット 2019-06-28 19.30.10.png

日付の加減算

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#runThread#wakeupで起こされたスレッドはこの状態になる
sleep 停止中
Thread#stopThread#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受けた人たちの復習ブログで知識を補う」は「合格教本ではわからない内容」かつ「試験で役に立つ内容」がたくさん載っていました。ので、見ておいて損はないと思います。

Ruby Gold 試験範囲を一気に見直す

31
28
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
28