Ruby初心者の不思議な経験



私は他の言語経験があります。最近Rubyを勉強して、「初めてのRuby」の本を読んでいると、不思議な疑問が出てきました。勉強しつつ分かった内容をまとめます：


## 1. Rubyは関数型言語ですか？

Javascriptなどの関数は言語のファーストクラスであり、呼び出すことだけではなく、値として引き渡すこと、オブジェクトとしてその関数の属性を引用することも出来ます。Rubyはそうではないでしょうか？

自分の答えは：

* そうです。
* 関数の道具は`ブロック`、`lambda`、`proc`、`Object#method`など幾つかあります。

ただし、Rubyの関数を呼び出す際、括弧を省略出来るルールがあります。また、関数の括弧を省略しても、関数の値が取れません。`method(:name)`/`obj.method(:name)`などを利用すれば、関数の値が取れます。

```ruby
print("abc") # print "abc" and return nil
print "abc"  # print "abc" and return nil 
print() # print "" and return nil 

print   # print "" and return nil 
        # 括弧がなくてもprint()と同じで、print関数の値を取れません

method(:print) # => #<Method: Object(Kernel)#print>
```

ちなみに、関数の括弧を省略することはよく覚えなければなりませんね。私は下記の魔術をよくわかりませんでした：

```ruby
{a:1, b:2}.map.with_index do |(key,value), index| "#{index} : #{key} => #{value}" end
# => ["0 : a => 1", "1 : b => 2"]
```

実は`map`関数の括弧が省略されています。そして`map`の戻り値はEnumeratorのインスタンスであり、`with_index`はEnumeratorのメソッドです。

```ruby
enumerator = {a:1, b:2}.map()
# => #<Enumerator: {:a=>1, :b=>2}:map>

enumerator.method(:with_index)
#=> #<Method: Enumerator#with_index>
    # with_indexはEnumeratorのメソッドです
enumerator.with_index() {|(key,value), index| 
  "#{index} : #{key} => #{value}"
}
#=> ["0 : a => 1", "1 : b => 2"]
```

## 2. blockは関数の最後の引数か？
違いますよ。ブロックは関数の引数ではなく、引数リストの後の特別なしっぽです。
[マニュアル≫ブロック付きメソッド呼び出し](http://docs.ruby-lang.org/ja/1.9.3/doc/spec=2fcall.html#block)によると：

```
文法:
method(arg1, arg2, ...)  do [`|' 式 ... `|'] 式 ... end
method(arg1, arg2, ...) `{' [`|' 式 ... `|'] 式 ... `}'
method(arg1, arg2, ..., `&' proc_object)
```

特に`&proc_object`の形は少し変なものです：

```ruby
["A","B","C"].map{|a| a.downcase}
#=> ["a", "b", "c"]
["A","B","C"].map(&:downcase)
#=> ["a", "b", "c"]

# &:downcaseは式の形ですけど、実は評価出来ないものです。
&:downcase
#    SyntaxError: (irb):21: syntax error, unexpected &
#    &:downcase
#     ^
#      from /usr/local/bin/irb:11:in `<main>'
```
`&:downcase`は`式`ではない、`値`を評価出来ない、ただの`記号`みたいものです。
上記と似ている`&proc`の形も、ただの`記号`みたいです：

```ruby
proc = Proc.new { |a| a.downcase }
["A","B","C"].map &proc
#=> ["a", "b", "c"]

&proc
#    SyntaxError: (irb):31: syntax error, unexpected &
#    &proc
#     ^
#    	from /usr/local/bin/irb:11:in `<main>'
#    
```
上記のコードも`ブロック`は関数の引数ではなく、引数の値に評価出来ない、`ただの記号`である証拠です。

## 3. なぜFile.openの呼び方は、C言語と似ている伝統的なスタイルとブロックスタイルと両方で出来るのか？

```ruby
file_contents = ''
f = File.open('.vimrc', 'r')
file_contents = f.read
f.close
```

```ruby
file_contents = File.open('.vimrc', 'r') {|f| f.read }
```

File.openは不思議な関数ですね:

* どうして２種類のスタイル両方で出来るのか？
* ブロックスタイルだとどうして自動的に`f.close`を呼び出してリソースリークを避けられるのか？

答えは３つのポイントです： `block_given?` 、`yield`、`ensure`


```ruby
def my_fopen(name, mode)
  f = File.open(name, mode)
  return f unless block_given?

  begin
    yield f
  ensure
    p "ensure : f.close"
    f.close
  end
end

# style 1:
file_contents = ''
f = my_fopen('.vimrc', 'r')
file_contents = f.read
f.close
p file_contents

# style 2:
file_contents = my_fopen('.vimrc', 'r') {|f| f.read }
p file_contents
```

## 4. Sinatraの`helpers`ブロックの中で`super`を呼び出せることが不思議です！

```ruby
# Sinatra helpers例
helpers do
  def find_template(views, name, engine, &block)
    ....
    super(folder, name, engine, &block)
  end
end
```

ここの秘密は`class_eval`です。簡単な例を作ってみます：

```ruby
class MyBase
  def foo()
    p 'MyBase::foo'
  end
end

class MyTest < MyBase
  def helpers(&proc)
    self.class.class_eval(&proc) 
        #selfはMyTestのインスタンスです
        #self.classはMyTestです
  end
end

my_test = MyTest.new
my_test.helpers {
  def foo()
    p "my_test.foo"
    super
  end
}

my_test.foo
# output :
# "my_test.foo"
# "MyBase::foo"

```

# ５．なぜSinatraのget/postブロックの中でSinatra::Requestのparams属性にアクセス出来るのか？
```ruby
require 'sinatra'
get '/hello/:name' do
  "Hello #{params[:name]}!" 
    # ここのparamsは不思議なものです
    # なぜブロックの中にSinatra::Requestのparams属性がアクセス出来ます
end
```
ここのポイントは`instance_eval`です。簡単に一番小さいRackフレームワークを作ってみます。そのフレームワークはSinatraのようなget/post DSLを提供します：

```ruby
# app を実装.

# get/post ブロックの中でRack::Requestの属性にアクセス出来ます

get do
  [200, {}, ["hello world // Get : params : " + params.to_s + "\n"]]
end

post do
  [200, {}, ["hello world // POST : body : " + body.read + "\n"]]
end
```
下記は全ての実装コードです：

```ruby
# library を実装

require 'rack'
class MyRackApp
  @@process_map = {}
  def self.register(method, proc)
    @@process_map[method] = proc
  end

  def call(env)
    request = Rack::Request.new(env)
    proc = @@process_map[request.request_method]
    if proc.nil?
      [500, {}, ["未実装"]]
    else
      request.instance_eval(&proc)
    end
  end
end

def get(&proc)
  MyRackApp.register('GET', proc)
end
def post(&proc)
  MyRackApp.register('POST', proc)
end

# app を実装

get do
  [200, {}, ["hello world // Get : params : " + params.to_s + "\n"]]
end

post do
  [200, {}, ["hello world // POST : body : " + body.read + "\n"]]
end

# library を実装
rack_app = MyRackApp.new
Rack::Handler::WEBrick.run rack_app, :Port=>3000
```