rspecのdescribeとitを自作する / ついでにGrapeのexposeのソースコードを読む

# rspecのdescribeとitを自作してみる

## 読書コード対象者

Rubyのコード少しでも読める方なら最後まで読了できるかと思います。

- Rubyの引数について詳しくなります。
- GrapeというAPIを簡単に作れるOSSのソースコードを少しだけ一緒に読んで行きたいと思うので毎日使っているコードが親近感でます

最終的には、下記の文法が何しているかわかります。

```ruby
describe "RspecでこちらのコードがRuby内部でどういう文法で動いているか" do

  puts "分かるようになります"

end

class Status < Grape::Entity
    # またGrapeのこのあたりのコードにも詳しくなります
    expose :style
    expose :html, documentation: { type: String, desc: 'html' } do |f|
        f.body
    end
end
```

またこの `do end` や `{ }` みたいな`ブロック`と呼ばれるものへの理解が少し広がるかと思います。

## 背景

こんにちは僕です。この話は、Vue.jsからAPIを叩こう思い、Rails側にGrapeによるAPIを作っていたことから始まります。そのときに `Grapeエラーでるよ〜〜あああああ〜〜` って思いソースコードまで追ってみたのでその時の話を順番にしていきます。（ ドキュメント見てから実装開始しろってツッコミはなしで ）

## Grapeのコードを追ってみる

### Entityのサンプル

[Grape::EntityのGitHub](https://github.com/ruby-grape/grape-entity)

```ruby
class Status < Grape::Entity
    expose :style
    expose :html, documentation: { type: String, desc: 'html' } do |f|
        f.body
    end
end
```

と書いておくと

```ruby
present s, with: Status
```

みたいな感じでJSONの返却をぬるっとやってくれるのがGrapeです。

今回はこの `expose` の引数に何を渡せばいいのか全くわからなかったので、そのあたりのコードを追っていきます。

### Rubyの引数の振り返り

```ruby

def x1(text)
    puts text
end

def x2 text
    puts text
end

x1("it calls x1") # it calls x1
x2 "it calls x2"  # it calls x2

```

おもむろにTerminalでirbと打つとRubyが対話モードで使えるので（MacUser）上のコードは適当に自分で流し込んで試してください。！！

```shell-session
$ irb
$ irb(main):001:0>
```

<img width="634" alt="スクリーンショット 2018-06-21 0.43.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/112929/38b58d98-5c18-7ef2-42dd-4871256bba58.png">

こんな感じでRubyは `()` が省略できたりします。

参考: [Ruby のコーディングスタイル](http://i.loveruby.net/ja/ruby/codingstyle.html)

### Grapeの `expose` の使い方をもう一度見る

前回はRubyの()の省略について学んだので次のコードも理解できるはずです。

```ruby
expose :user_name
```

つまり `expose` はメソッドで、引数に `:user_name` というシンボルを渡している、という風に見えて来ましたね。（間違ってたら偉い人が教えてくれるのでコメント欄参照！！）

ここまでで、ちょっとRubyに詳しくなった気がします。

なので次は `expose` というメソッドを見に行きます。

### Grapeの `expose` のソースコードを見る

ソースコードはこちら [lib/grape_entity/entity.rb](https://github.com/ruby-grape/grape-entity/blob/master/lib/grape_entity/entity.rb)

```ruby
def self.expose(*args, &block)
    options = merge_options(args.last.is_a?(Hash) ? args.pop : {})

    if args.size > 1
        raise ArgumentError, 'You may not use the :as option on multi-attribute exposures.' if options[:as]
        raise ArgumentError, 'You may not use the :expose_nil on multi-attribute exposures.' if options.key?(:expose_nil)
        raise ArgumentError, 'You may not use block-setting on multi-attribute exposures.' if block_given?
    end

    raise ArgumentError, 'You may not use block-setting when also using format_with' if block_given? && options[:format_with].respond_to?(:call)

    if block_given?
        if block.parameters.any?
            options[:proc] = block
        else
            options[:nesting] = true
        end
    end

    @documentation = nil
    @nesting_stack ||= []
    args.each { |attribute| build_exposure_for_attribute(attribute, @nesting_stack, options, block) }
end
```

20行くらいなのでみんなすぐ理解できますよね（ちなみに私理解できなかったのでこの記事書きながら理解しています）

### Grapeの `expose` のソースコードを適当に理解する

```ruby
def self.expose(*args, &block)
    options = "引数の *args からオプションをいい感じに整形する"

    if args.size > 1
        "問題あったらエラーだす"
    end

    raise ArgumentError, '問題ないよね…？'

    if block_given?
        "引数として &block が渡されてるとごにょごにょ"
    end

    @documentation = nil
    @nesting_stack ||= []
    args.each { |attribute| "いい感じにゴニョゴニョしてデータ作り出す" }
end
```

正直なんかよくわからないけど `def self.expose(*args, &block)` これの `*args` は `引数はたくさん渡せるよ！`で、 最後の `&block` は `引数の最後にブロック渡せるぜ！！！！` って意味だと思ったらいいと思います。（強引） 

※ブロックとは `do end` とか `{}` のことで、ブロックが引数として渡されたのを判定するのは Rubyの `block_given?` というメソッドが使えます。

### Grapeの `expose` の使い方をもう一度見る（3度目）

なので、こちらももう読めるようになったはずです。。

```ruby
expose :html, documentation: { type: String, desc: 'html' } do |f|
    f.body
end
```

exposeというメソッドの引数は

1. `:html`
2. `documentation: { type: String, desc: 'html' })`
3. `do |f| f.body; end`

の３つで、最後の3つ目はブロック要素として認識されています。


```ruby
def self.expose(*args, &block)
    options = merge_options(args.last.is_a?(Hash) ? args.pop : {})
end
```

そして、もう一度exposeメソッドを見ると、1と2は(*args)に入って、3のブロックは(&block)に入っていくことがわかります。

`args.last.is_a?(Hash)` は `*args の最後がハッシュなら` という意味ですが、おそらくですが、この `*args` の最後には `documentation` のドキュメントをいれないとエラー起きるような気がします（今回はそこは追わない）

### ブロックが渡されたか判断する (&block)

Rubyのメソッドに`block_given?`で渡されたかチェックできます。

```ruby
def self.expose(*args, &block)
    if block_given?
        "引数として &block が渡されてるとごにょごにょ"
    end
end
```

なのでexposeには２つの書き方があります。

```ruby
class Status < Grape::Entity
    expose :style
    expose :html, documentation: { type: String, desc: 'html' } do |f|
        f.body
    end
end
```

`do; end;`をつける書き方、何もつけない書き方、どちらも文法的に正しくなるように `expose` は設計されています。

普段なんとなく使っているコードをしっかり追ってみましたが、個人的にはRubyをちゃんと知らなくなんとなく使っているのはエンジニアとして成長するためにもだめだなあと思いました。

[block_given? のドキュメント](https://docs.ruby-lang.org/ja/latest/method/Kernel/m/block_given=3f.html)


## Rspecのコードは追わないが作ってみる

### Rspec序文

ここまでで `expose` のコードを読んでrubyの使い方への理解が深まったと思います。

次は本題ですが Rspecの `describe` を自分で書いてみようと思います。

```ruby
describe "xxxをテストする" do
  puts "これで合ってるかな…"
end
```

ここまで読んだ読者はもうわかると思いますが、`describe` というメソッドは引数は `（text, block）` テキストとブロックだと判断がつきます。

### Rspec作成編

```ruby
# encoding: utf-8

def describe(msg, &block)
    if block_given?
        block.call
    else
        puts msg
    end
end

puts "~~~~~~~~~~~~~~~~~~~~~~~~~~"

describe "こちらテストですよ！！！！！！"


puts "~~~~~~~~~~~~~~~~~~~~~~~~~~"

describe "こちらテストです" do
  puts "ブロック渡したらどうなるかテスト"
end

# 返ってくる値
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~
# こちらテストですよ！！！！！！
# ~~~~~~~~~~~~~~~~~~~~~~~~~~
# ブロック渡したらどうなるかテスト

```

今回作成した `describe` はブロックを渡したかどうかで処理を分岐させてみました。

1. ブロックを渡した場合
    -  `block_given?` が `true` になる
    - `block.call`を実行させる

2.  ブロックを渡さない場合
    -  ただ第一引数で渡したmsgをputsするだけになります。

[Paiza.ioでの動作サンプル](https://paiza.io/projects/e/KB5pPxfpANaqrggZbqLTLQ)

ここまで自分で作れるようになったら、Rubyの引数マスターだと思います

### block.call

渡したブロックを実行させられます。

```ruby
block.call
```

### Rspecのソースコードについて

たぶんレポジトリは[こちら](https://github.com/rspec/rspec-rails/)ですが `describe` とかどこで実装されているか正直読んでないです。分かる方は記事を書いてくれると楽しんで読みたいと思うのでコメント欄でお願いします〜〜

## 最後に

自分が普段使っているソースコードを、気になったところだけでもいいので読んで見る。これだけでもOSS使ってるっぽいし（形から入る人）、ドキュメントにしっかりかかれていないこともなんとなく分かるのがすごくいいですね。（今回は `expose` の最後の引数はdocumentじゃね？って考えれたこととか）

Ruby詳しいですとは口がさけても言えないので詳しい方PRとかコメント欄でご指導いただけると幸いです。最後まで読んでいただきありがとうございました〜！！