0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Meta-2章-オブジェクトモデル

Last updated at Posted at 2017-03-25

オープンクラス

  • 標準クラスにmethod追加できる
require 'test/unit'

def to_alphanumeric(s)
    s.gsub /[^\w\s]/, '' # [a-zA-Z0-9_空白]以外
end

class ToAlphanumericTest < Test::Unit::TestCase
    def test_steps_non_alphanumeric_characters
        assert_equal '3 the Magic Number', to_alphanumeric('#3, the ***Magic, Number*?')
    end
end
require 'test/unit'

class String
    def to_alphanumeric
        gsub /[^\w\s]/, ''
    end
end

class ToAlphanumericTest < Test::Unit::TestCase
    def test_steps_non_alphanumeric_characters
        assert_equal '3 the Magic Number', '#3, the ***Magic, Number*?'.to_alphanumeric
    end
end
  • method検索
[].methods.grep /^re/
# [:repeated_permutation, :repeated_combination, :reject, :reverse_each, :replace, :reverse, :reverse!, :reject!, :reduce, :remove_instance_variable, :respond_to?]

"".methods.grep /^re/
[:replace, :reverse, :reverse!, :remove_instance_variable, :respond_to?]

Objectとinstance変数

  • objectのinstance変数はobjectにある(@vはそれぞれのobjectにある)
  • objectのmethodはclassにある(my_methodはMyClassにある)
  • instance変数は値が代入された時に初めて出現する
class MyClass
	def my_method(v)
		@v = v
	end
end

obj1 = MyClass.new.my_method(5)
obj2 = MyClass.new.my_method(7)
obj3 = MyClass.new
obj3.instance_variables 
# []
obj3.my_method
obj3.instance_variables 
# [:@v]

オブジェクトはinstance変数とclassへのリンクで構成されている

オブジェクトのmethodはclassに住んでいる。classから見ればinstance methodと呼ばれる

classはClassクラスのオブジェクト。クラス名は単なる定数。

ClassはModuleのサブクラス

  • module: methodをまとめたもの
  • class: newしたり、superclassで階層構造を作れる

moduleやclassの名前がディレクトリ、定数がファイルのような感じ

 module MyModule
	 MyConstant = 'out 定数'
	 class MyClass
		 MyConstant = 'innner 定数'
	 end
 end
 
MyModule::MyConstant
# "out 定数"
MyModule::MyClass::MyConstant
# "inner 定数"

require, load method

require: ファイルを一度だけ読み込む
load: 呼び出すたびにファイルを実行する

require './a.rb'

# loadは定数が変数を汚染する、防ぐためにtrueオプション
load('a.rb', true)

classはBasicObjectまでの継承チェーンを持っている

method探索

  • レシーバーのクラスに入り、メソッドを見つけるまで継承チェーンを上る
  • レシーバ: obj.my_methodの場合objがレシーバ
  • 継承チェーン: クラスをsuperclassに向かってたどる
class MyClass
	def my_method
		@v = 1
	end
end

class SubClass < MyClass
end

obj = SubClass.new
obj.my_method
SubClass.ancestors
# SubClass, MyClass, Object, Kernel, BasicObject]

classにmoduleをinclude, prepend

  • include: includeするclassの真上に入る
  • prepend: prependするclassの真下に入る
module M1
	def my_method
		@v = 1
	end
end

class C 
	include M1
end

class C1
	prepend M1
end

class D < C
end

D.ancestors
# [D, C, M1, Object, Kernel, BasicObject]

class E < C1
end

E.ancestors
# [E, M1, C1, Object, Kernel, BasicObject]

moduleがすでにチェーンに含まれていたら2回目は無視する

  1. M3にM1がprepend [M1, M3]
  2. M3にM2がinclude [M1, M3, M2]
  3. M2にM1がincludeだが、M1はすでにチェーンにあるため無視
module M1; end
module M2
	include M1
end
module M3
	prepend M1
	include M2
end

M3.ancestors
# [M1, M3, M2]

Kernel

  • どこからでも呼び出せるprint methodはObject classがKernel moduleをincludesしているから
  • Kernelにmethodを追加すればどこからでも使えるようになる
require 'awesome_print'

local_time = { :city => "Roma", :now => Time.now }
ap local_time, :indent => 2

# {
#  :city => "Roma",
#   :now => 2017-03-29 21:32:45 +0900
# }
# awesome_printがKernelにap methodを追加している

methodを呼び出すときはレシーバがselfになる

  • コードはcurrent object内部(self)で実行される
  • methodを呼び出すときはmethodのレシーバがself
  • その時点から全てのinstance変数はselfのinstance変数になり、レシーバを明示しないmethod呼び出しは全てselfに対する呼び出しになる
  • 最後にmethodのレシーバになったobjectを追いかけると流れがわかりやすい
class MyClass
	def testing_self
		@var = 10
		my_method
		self
		puts @var
	end

	def my_method
		@var = @var + 1
	end
end

obj = MyClass.new
obj.testing_self
# #<MyClass:0x007f83542e7cd0 @var=11>
# testing_selfを呼び出すとレシーバであるobjがselfになる
# そのためinstance変数@varはobjのinstance変数となる
# レシーバを明示していないmy_methodの呼び出しもobjに対するものとなる
# my_method時もselfはobjなので@varはobjのinstance変数のまま
# よって、@varは+1される

トップレベル

  • methodを呼び出してない、または呼び出したmethodが全て戻った時のselfはmain
self # main
self.class # Object

Refinements

  • クラスのコードにパッチを当てて、通常のメソッド探索をオーバーライドする感じ
  • Refinementsはusingを呼び出したところから、ファイルやモジュールの定義が終わるところまでの限られた部分でのみ有効になる
module StringExtensions
	refine String do
		def to_alphanumeric
			gsub(/[^\w\s]/, '')
		end
	end
end

"my *1st* refinement!".to_alphanumeric
# NoMethodError
using StringExtensions
"my *1st* refinement!".to_alphanumeric
# "my 1st refinement!"
module StringExtensions
	refine String do
		def reverse
			"esrever"
		end
	end
end

module StringStuff
	using StringExtensions
	"my *1st* refinement!".reverse
	# "esrever"
end

"my *1st* refinement!".reverse
# "!tnemenifer *ts1* ym"

Documentのprintを実行したい場合

  1. どちらかのprintの名前を変える
  2. includeの順番を変える
module Printable
	def print
		puts 1
	end	
	def prepare_cover
		puts 2
	end
end

module Document
	def print_to_screen
		prepare_cover # 2 Printableで発見
		format_for_screen # 3 Documentで発見
		print # 1
	end	
	def format_for_screen
		puts 3
	end	
	def print
		puts 4
	end
end

class Book
	include Document # Bookの真上に入る
	include Printable # Bookの真上に入る
	puts 5
end

# Book > Printable > Document > Object > Kernel > Basic Object


b = Book.new
b.print_to_screen
# 5 2 3 1

Book.ancestors
# [Book, Printable, Document, Object, Kernel, 
BasicObject]

b.method(:print).owner
# Printable

Ctrl + L

pry画面クリア

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?