stitcher 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/README.md +107 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/docs/matome.md +57 -0
- data/docs/stitcher.md +208 -0
- data/docs/type.md +212 -0
- data/docs/typed.md +15 -0
- data/example/core_ex.rb +23 -0
- data/example/register.rb +33 -0
- data/example/simple.rb +66 -0
- data/example/type.rb +24 -0
- data/lib/stitcher.rb +40 -0
- data/lib/stitcher/accessor.rb +21 -0
- data/lib/stitcher/concepts.rb +6 -0
- data/lib/stitcher/core.rb +31 -0
- data/lib/stitcher/core_ext.rb +7 -0
- data/lib/stitcher/define_method.rb +52 -0
- data/lib/stitcher/operators.rb +37 -0
- data/lib/stitcher/register.rb +31 -0
- data/lib/stitcher/require.rb +27 -0
- data/lib/stitcher/stitch.rb +22 -0
- data/lib/stitcher/type.rb +107 -0
- data/lib/stitcher/type/core_ext.rb +11 -0
- data/lib/stitcher/variadic_argument.rb +12 -0
- data/lib/stitcher/version.rb +3 -0
- data/stitcher.gemspec +25 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 60b567e6e0cac2f1dacd168e411da63d097eeb68
|
4
|
+
data.tar.gz: 137483e2bf3e741fe861ff1ea2b4c7c0604b9be0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 879f602d5e7456953ac1b0b16d98dd7346f6e2ac4f216702bc88600f7d64d939f7bbe1ad5c1299ccc9ddc951281b756d3605e917d7919e68a480e2c60bc1815a
|
7
|
+
data.tar.gz: 883c44327d337921e864614153f874f92b5c90eba6a5eb01ac542fd1a1bbf776a6056c865b9649b4f1dd99040bc0ede011031ba0dc5ee88f4dc1957e47e7949f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/osyo-manga/gem-stitcher.svg?branch=master)](https://travis-ci.org/osyo-manga/gem-stitcher)
|
2
|
+
|
3
|
+
# Stitcher
|
4
|
+
|
5
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/stitcher`. To experiment with that code, run `bin/console` for an interactive prompt.
|
6
|
+
|
7
|
+
TODO: Delete this and the text above, and describe your gem
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'stitcher'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install stitcher
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require "stitcher"
|
29
|
+
|
30
|
+
# Using stitcher library.
|
31
|
+
using Stitcher
|
32
|
+
|
33
|
+
class X
|
34
|
+
# Define accessor with variable type(Class).
|
35
|
+
stitcher_accessor name: String, age: Integer
|
36
|
+
|
37
|
+
def set name, age
|
38
|
+
self.name = name
|
39
|
+
self.age = age
|
40
|
+
end
|
41
|
+
# Register set method with Argument types(Classes).
|
42
|
+
stitch :set, [String, Integer]
|
43
|
+
|
44
|
+
# Register for next define method.
|
45
|
+
stitcher_require [Hash]
|
46
|
+
def set hash
|
47
|
+
set hash[:name], hash[:age]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Define "set" method with Argument types.
|
51
|
+
# set(Integer, String)
|
52
|
+
stitcher_define_method(:set, age: Integer, name: String){
|
53
|
+
self.name = name
|
54
|
+
self.age = age
|
55
|
+
}
|
56
|
+
|
57
|
+
# Other define method
|
58
|
+
stitch.set(ary: [String, Integer]){
|
59
|
+
set *ary
|
60
|
+
}
|
61
|
+
|
62
|
+
def print
|
63
|
+
p "name:#{name} age:#{age}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Require format with block object.
|
67
|
+
stitcher_require [String] & Stitcher::Concepts.blockable
|
68
|
+
def print fmt
|
69
|
+
printf(fmt, *yield(name, age))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
x = X.new
|
74
|
+
x.name = "homu"
|
75
|
+
x.age = 14
|
76
|
+
# x.age = 14.0 # Error: No match method.
|
77
|
+
|
78
|
+
x.set "mami", 15
|
79
|
+
x.print
|
80
|
+
# => "name:mami age:15"
|
81
|
+
|
82
|
+
x.set({ name: "saya", age: 14 })
|
83
|
+
x.print
|
84
|
+
# => "name:saya age:14"
|
85
|
+
|
86
|
+
x.set 14, "mado"
|
87
|
+
x.print("%s-%s\n"){ |name, age| [name, age] }
|
88
|
+
# => mado-14
|
89
|
+
|
90
|
+
x.set ["homu", 14]
|
91
|
+
x.print("%s-%s\n"){ |name, age| [age, name] }
|
92
|
+
# => 14-homu
|
93
|
+
```
|
94
|
+
|
95
|
+
## Development
|
96
|
+
|
97
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
98
|
+
|
99
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
|
103
|
+
1. Fork it ( https://github.com/osyo-manga/gem-stitcher )
|
104
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
105
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
106
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
107
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "stitcher"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/docs/matome.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Stitcher
|
2
|
+
|
3
|
+
## 目的
|
4
|
+
|
5
|
+
* メソッドの多重定義をする
|
6
|
+
* 引数によって呼び出すメソッドを切り替える
|
7
|
+
* メソッドの引数に対しての制限を設ける
|
8
|
+
* 引数に対する型チェック
|
9
|
+
* 引数に対するコンセプトチェック
|
10
|
+
* モンキーパッチで定義するメソッドを制限したい
|
11
|
+
* 引数が○○○の場合でのみモンキーパッチのメソッドを呼び出す等
|
12
|
+
|
13
|
+
|
14
|
+
## 動作イメージ
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
class X
|
18
|
+
include Stitcher
|
19
|
+
|
20
|
+
def plus a, b
|
21
|
+
p "plus(a.to_i, b.to_i)"
|
22
|
+
a.to_i + b.to_i
|
23
|
+
end
|
24
|
+
# #plus を呼び出すコンセプトを設定する
|
25
|
+
# 引数がそれぞれ #to_i を呼び出せればこのメソッドを呼び出す
|
26
|
+
concept :plus do |a, b|
|
27
|
+
a.respond_to?(:to_i) && b.respond_to?(:to_i)
|
28
|
+
end
|
29
|
+
|
30
|
+
# concept 設定後に別のメソッドを定義し直す事ができる
|
31
|
+
def plus a, b
|
32
|
+
p "plus(a + b)"
|
33
|
+
a + b
|
34
|
+
end
|
35
|
+
|
36
|
+
# 再定義したメソッドに対しても条件を設定できる
|
37
|
+
# こっちは両方共 Numeric だった場合に呼び出す
|
38
|
+
concept :plus do |a, b|
|
39
|
+
a.class <= Numeric && b.class <= Numeric
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
X.new.plus "1", "2"
|
44
|
+
# => 最初に定義したメソッドが呼ばれる
|
45
|
+
X.new.plus 1, 2
|
46
|
+
# => あとで定義したメソッドが呼ばれる
|
47
|
+
|
48
|
+
X.new.plus X, X
|
49
|
+
# => どれにも該当しないのでエラー
|
50
|
+
```
|
51
|
+
|
52
|
+
|
53
|
+
## 考えること
|
54
|
+
|
55
|
+
* 呼び出すことができるメソッドが複数あった場合どうするか
|
56
|
+
* 呼び出すメソッドの優先順位
|
57
|
+
|
data/docs/stitcher.md
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
# Stitcher
|
2
|
+
|
3
|
+
## これは何?
|
4
|
+
|
5
|
+
定義する(定義した)メソッドに対して引数のシグネチャを設定するライブラリです。
|
6
|
+
シグネチャを指定することにより、静的型付け言語のようなメソッドの多重定義を行うことを目的としています、
|
7
|
+
|
8
|
+
|
9
|
+
## 導入
|
10
|
+
|
11
|
+
#### install
|
12
|
+
|
13
|
+
```shell
|
14
|
+
$ gem install stitcher
|
15
|
+
```
|
16
|
+
|
17
|
+
#### require
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require "stitcher"
|
21
|
+
|
22
|
+
using Stitcher
|
23
|
+
```
|
24
|
+
|
25
|
+
## 使い方
|
26
|
+
|
27
|
+
#### シグネチャの設定
|
28
|
+
|
29
|
+
`stitcher` は定義したメソッドか、次に定義するメソッドに対してシグネチャを設定することで使用します。
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require "stitcher"
|
33
|
+
|
34
|
+
# using することで Module クラスに Stitcher の機能が mixin される
|
35
|
+
using Stitcher
|
36
|
+
|
37
|
+
|
38
|
+
class Counter
|
39
|
+
attr_reader :value
|
40
|
+
|
41
|
+
def initialize value
|
42
|
+
@value = value
|
43
|
+
end
|
44
|
+
# 既存のメソッドに対して引数のシグネチャを設定する
|
45
|
+
# #initialize メソッドに対して Fixnum クラスのインスタンスのみ受け取るようにする
|
46
|
+
stitcher_register :initialize, [Fixnum]
|
47
|
+
|
48
|
+
# stitcher_register を使用することで次に定義するメソッドに対しての
|
49
|
+
# シグネチャを設定することもできる
|
50
|
+
# stitcher_register の第二引数と stitcher_require の第一引数は同じである
|
51
|
+
stitcher_require [Fixnum]
|
52
|
+
# add メソッドは Fixnum のインスタンスのみ受け付ける
|
53
|
+
def add value
|
54
|
+
@value += value
|
55
|
+
end
|
56
|
+
|
57
|
+
# また、違うシグネチャを設定することにより複数のメソッドを定義する事ができる
|
58
|
+
stitcher_require [String]
|
59
|
+
def add str
|
60
|
+
@value += str.to_i
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
count = Counter.new 0 # OK
|
65
|
+
# count = Counter.new "" # Error: Fixnum クラスのインスタンスではない
|
66
|
+
|
67
|
+
count.add 10 # OK
|
68
|
+
count.add "12" # OK
|
69
|
+
# count.add 0.42 # Error
|
70
|
+
p count.value
|
71
|
+
# => 22
|
72
|
+
```
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
using Stitcher
|
76
|
+
```
|
77
|
+
|
78
|
+
まず、`using` を使用して、特定の範囲でのみ Stitcher を使用することを定義します。
|
79
|
+
この時に `Module` クラス内に Stitcher の機能が `mixin` され、いくつかのクラス拡張が適用されます。
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
def initialize value
|
83
|
+
@value = value
|
84
|
+
end
|
85
|
+
stitcher_register :initialize, [Fixnum]
|
86
|
+
```
|
87
|
+
|
88
|
+
`.stitcher_register` を使用して既存のメソッドに対してシグネチャを設定します。
|
89
|
+
この時に設定するシグネチャは各引数に対するリストになります。
|
90
|
+
また、各シグネチャと実引数の要素は `#===` を用いて比較が行われ、このメソッドが真を返せばシグネチャにマッチしたと認識されます。
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
stitcher_require [Fixnum]
|
94
|
+
def add value
|
95
|
+
@value += value
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
`.stitcher_require` を使用して、次に定義されるメソッドに対してのシグネチャを設定します。
|
100
|
+
設定するシグネチャは `.stitcher_register` と同じです。
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
stitcher_require [String]
|
104
|
+
def add str
|
105
|
+
@value += str.to_i
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
違うシグネチャを設定することで静的型付け言語のような多重定義を行うことができます。
|
110
|
+
これにより『引数のクラス』によって簡単に処理を分岐することができます。
|
111
|
+
|
112
|
+
#### シグネチャの設定
|
113
|
+
|
114
|
+
各引数のシグネチャは `#===` が定義されてるオブジェクトであればなんでも設定する事ができます。
|
115
|
+
これは Ruby の `case` 文と似ています。
|
116
|
+
例えば、`Class` 以外にも `Proc` や `Regexp` などを渡すことができます。
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
class Http
|
120
|
+
stitcher_require [/^https?/]
|
121
|
+
def post url
|
122
|
+
# ...
|
123
|
+
end
|
124
|
+
|
125
|
+
stitcher_require [ proc { |hash| (Hash === hash && hash.key?(:url)) } ]
|
126
|
+
def post hash
|
127
|
+
post hash[:url]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
http = Http.new
|
132
|
+
http.post "http://docs.ruby-lang.org/" # OK
|
133
|
+
http.post({ url: "http://docs.ruby-lang.org/" }) # OK
|
134
|
+
|
135
|
+
http.post "ftp://docs.ruby-lang.org/" # Error
|
136
|
+
http.post({ uri: "http://docs.ruby-lang.org/" }) # Error
|
137
|
+
```
|
138
|
+
|
139
|
+
このようにしてより厳密な引数に対する要求を設定することができます。
|
140
|
+
また、`Class` や `Proc`、`Regexp` は `&` や `|` で結合することもできます。
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# Hash であることと Proc の条件を満たすことを要求する
|
144
|
+
stitcher_require [Hash & proc { |hash| (hash.key?(:url)) } ]
|
145
|
+
def post hash
|
146
|
+
post hash[:url]
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
#### 多重定義字に呼び出されるメソッドの優先順位
|
151
|
+
|
152
|
+
Stitcehr を利用してメソッドを多重定義した場合に呼び出されるメソッドの優先順位は __下から順に優先順位が高くなります。__
|
153
|
+
例えば、次のような定義の場合、
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
require "stitcher"
|
157
|
+
|
158
|
+
using Stitcher
|
159
|
+
|
160
|
+
class X
|
161
|
+
stitcher_require [Fixnum]
|
162
|
+
def func value
|
163
|
+
"Fixnum"
|
164
|
+
end
|
165
|
+
|
166
|
+
stitcher_require [Numeric]
|
167
|
+
def func value
|
168
|
+
"Numeric"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
あとから定義されている `func(Numeric)` の方が優先順位が高くなり、
|
174
|
+
|
175
|
+
```
|
176
|
+
x = X.new
|
177
|
+
x.func 10 # Numeric === 10 # => true
|
178
|
+
```
|
179
|
+
|
180
|
+
というような呼び出しの場合は、`Numeric#===` がどちらも真となるため、`func(Fixnum)` ではなくて `func(Numeric)` が呼び出されます。
|
181
|
+
これは次のように `Fixnum` にマッチしないようにすることで回避することは可能です。
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
require "stitcher"
|
185
|
+
|
186
|
+
using Stitcher
|
187
|
+
|
188
|
+
class X
|
189
|
+
stitcher_require [Fixnum]
|
190
|
+
def func value
|
191
|
+
"Fixnum"
|
192
|
+
end
|
193
|
+
|
194
|
+
# Numeric であり、Fixnum 以外にマッチする場合のシグネチャ
|
195
|
+
stitcher_require [Numeric & !Fixnum]
|
196
|
+
def func value
|
197
|
+
"Numeric"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
x = X.new
|
202
|
+
p x.func 10
|
203
|
+
# => "Fixnum"
|
204
|
+
p x.func 0.42
|
205
|
+
# => "Numeric"
|
206
|
+
```
|
207
|
+
|
208
|
+
|
data/docs/type.md
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# Ruby の型
|
2
|
+
|
3
|
+
## やりたいこと
|
4
|
+
|
5
|
+
* Ruby で型を定義する
|
6
|
+
* 現状は Class = 型とする
|
7
|
+
* 型チェックを行う仕組み
|
8
|
+
* ジェネリックプログラミングや Variant 型(Haskell の Either みたいなの?)の定義
|
9
|
+
* C++ の template みたいなのもほしい
|
10
|
+
* 型によって処理を切り替える装置
|
11
|
+
|
12
|
+
## 動作イメージ
|
13
|
+
|
14
|
+
#### 型の定義
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# To type
|
18
|
+
1.type # => Fixnum
|
19
|
+
"".type # => String
|
20
|
+
String.type # == String
|
21
|
+
```
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# Variant type
|
25
|
+
(String | Fixnum) # => String or Fixnum
|
26
|
+
(String | Fixnum | Hash) # => String or Fixnum or Hahs
|
27
|
+
```
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# Super class type
|
31
|
+
+Numeric # => Numeric is super class type
|
32
|
+
|
33
|
+
# No type
|
34
|
+
!Numerci # => Not Numeric type
|
35
|
+
```
|
36
|
+
|
37
|
+
##### 型の比較
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
1.type == Fixnum # => true
|
41
|
+
1.type == String # => false
|
42
|
+
1.type <= Numeric # => true
|
43
|
+
1.type == Numeric # => false
|
44
|
+
```
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# Type1 | Type2 => Variant type
|
48
|
+
(String | Fixnum) == 1 # true
|
49
|
+
(String | Fixnum) == "" # true
|
50
|
+
(String | Fixnum) == {} # false
|
51
|
+
```
|
52
|
+
|
53
|
+
|
54
|
+
#### 型変数の定義
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
let :name, String
|
58
|
+
self.name = "" # OK
|
59
|
+
self.name = 42 # Error
|
60
|
+
```
|
61
|
+
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
let :data, String | Numeric # Variant 型
|
65
|
+
self.data = "" # OK
|
66
|
+
self.data = 42 # OK
|
67
|
+
self.data = {} # Error
|
68
|
+
```
|
69
|
+
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
let :bool, True | False # Variant 型
|
73
|
+
self.bool = true # OK
|
74
|
+
self.bool = false # OK
|
75
|
+
self.bool = nil # Error
|
76
|
+
```
|
77
|
+
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
class X
|
81
|
+
let name: String, age: Numeric
|
82
|
+
end
|
83
|
+
x = X.new
|
84
|
+
x.name = "homu" # OK
|
85
|
+
x.age = 13 # OK
|
86
|
+
x.name 13 # Error
|
87
|
+
```
|
88
|
+
|
89
|
+
## 悩み
|
90
|
+
|
91
|
+
型同士の比較をどうするか考える
|
92
|
+
|
93
|
+
1.type # => Fixnum
|
94
|
+
|
95
|
+
1.type == Numeric # => Fixnum == Numeric
|
96
|
+
1.type <= Numeric # => Fixnum <= Numeric
|
97
|
+
1.type === Numeric # => Fixnum === Numeric
|
98
|
+
|
99
|
+
1.type == +Numeric # => Fixnum == Numeric
|
100
|
+
1.type <= +Numeric # => Fixnum <= Numeric
|
101
|
+
1.type === +Numeric # => Fixnum <= Numeric
|
102
|
+
|
103
|
+
1.type == !Numeric # => Fixnum == Numeric
|
104
|
+
1.type != !Numeric # => Fixnum != Numeric
|
105
|
+
1.type === !Numeric # => Fixnum != Numeric
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
class A
|
112
|
+
def == rhs
|
113
|
+
p "A#== #{rhs.class}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class B
|
118
|
+
def == rhs
|
119
|
+
p "B#== #{rhs.class}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class A
|
124
|
+
prepend(Module.new do
|
125
|
+
define_method(:==) do |rhs|
|
126
|
+
rhs.class == B ? rhs == self : super(rhs)
|
127
|
+
end
|
128
|
+
end)
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
class C
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
A.new == B.new
|
137
|
+
A.new == C.new
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
class A
|
142
|
+
def == rhs
|
143
|
+
if rhs.respond_to? :callable_equal
|
144
|
+
rhs.callable_equal(self)
|
145
|
+
else
|
146
|
+
p "A#== #{rhs.class}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class B
|
152
|
+
def callable_equal rhs
|
153
|
+
p "B#== #{rhs.class}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class C
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
A.new == B.new
|
162
|
+
A.new == C.new
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
class A
|
167
|
+
def == rhs
|
168
|
+
p "A#== #{rhs.class}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class B
|
173
|
+
def == rhs
|
174
|
+
p "B#== #{rhs.class}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class A
|
179
|
+
prepend(Module.new do
|
180
|
+
define_method(:==) do |rhs|
|
181
|
+
rhs.class == B ? rhs == self : super(rhs)
|
182
|
+
end
|
183
|
+
end)
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
class C
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
class D
|
192
|
+
def == rhs
|
193
|
+
p "D#== #{rhs.class}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class A
|
198
|
+
prepend(Module.new do
|
199
|
+
define_method(:==) do |rhs|
|
200
|
+
rhs.class == D ? rhs == self : super(rhs)
|
201
|
+
end
|
202
|
+
end)
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
A.new == Object.new
|
207
|
+
A.new == A.new
|
208
|
+
A.new == B.new
|
209
|
+
A.new == C.new
|
210
|
+
A.new == D.new
|
211
|
+
|
212
|
+
|