プリキュアのRuby実装「rubicure」

# rubicureとは？
プリキュアのRuby実装です :heart: 

* https://rubygems.org/gems/rubicure
* https://github.com/sue445/rubicure

![maho_girls.gif](https://qiita-image-store.s3.amazonaws.com/0/8059/cd685e0b-9065-0d49-7693-e5e375271470.gif)

「sue445 = Rubyでプリキュアの人」と良くも悪くも印象づけた代表作です。

# 機能紹介
## プリキュアの番組タイトルを出力
`Precure` に続けてタイトルを入力すると番組が取得できます。直感的！

```ruby
> Precure.maho_girls.title
#=> "魔法つかいプリキュア！"
```

suffixに `_precure` がついていても反応します

```ruby
> Precure.maho_girls_precure.title
#=> "魔法つかいプリキュア！"
```

`#started_date` で放送開始年月がとれます

```ruby
> Precure.maho_girls.started_date
#=> Sun, 07 Feb 2016
```

## 番組に登場するプリキュアを取得
その番組に登場するプリキュアを取得したい場合には `#girls` でその番組に登場するプリキュアが取得できます [^cure_mofurun]

[^cure_mofurun]: TODO: キュアモフルン取得

```ruby
> Precure.maho_girls.girls.count
#=> 3

> Precure.maho_girls.girls.map(&:precure_name)
#=> ["キュアミラクル", "キュアマジカル", "キュアフェリーチェ"]
```

## 全てのプリキュアを取得
映画でおなじみの `Precure.all_stars` で取得できます

```ruby
> Precure.all_stars.count
#=> 44

> Precure.all_stars.map(&:precure_name)
#=> ["キュアブラック", "キュアホワイト", "シャイニールミナス", "キュアブルーム", "キュアイーグレット", "キュアドリーム", "キュアルージュ", "キュアレモネード", "キュアミント", "キュアアクア", "ミルキィローズ", "キュアピーチ", "キュアベリー", "キュアパイン", "キュアパッション", "キュアブロッサム", "キュアマリン", "キュアサンシャイン", "キュアムーンライト", "キュアメロディ", "キュアリズム", "キュアビート", "キュアミューズ", "キュアハッピー", "キュアサニー", "キュアピース", "キュアマーチ", "キュアビューティ", "キュアハート", "キュアダイヤモンド", "キュアロゼッタ", "キュアソード", "キュアエース", "キュアラブリー", "キュアプリンセス", "キュアハニー", "キュアフォーチュン", "キュアフローラ", "キュアマーメイド", "キュアトゥインクル", "キュアスカーレット", "キュアミラクル", "キュアマジカル", "キュアフェリーチェ"]
```

`#all_stars` に日付を渡すとその時点での人数が取得できます

```ruby
> Precure.all_stars(Date.parse("2016-01-01")).count
=> 41
```

`#all_stars` に映画を表すものを渡すとその時点での人数が取得できます。便利！

```ruby
> Precure.all_stars(:dx).count
#=> 14

> Precure.all_stars(:dx2).count
#=> 17

Precure.all_stars(:dx3).count
#=> 21

> Precure.all_stars(:new_stage).count
#=> 29

> Precure.all_stars(:new_stage2).count
#=> 32

> Precure.all_stars(:new_stage3).count
#=> 37

> Precure.all_stars(:spring_carnival).count
#=> 40

> Precure.all_stars(:sing_together_miracle_magic).count
#=> 44
```

[^stmm]

[^stmm]: みんなで歌う奇跡の魔法の英名は「STMM製作委員会」から推測したものなので本当にsing together miracle magicかどうかは自信がないです

実際に使えるのは https://github.com/sue445/rubicure/blob/v0.4.9/config/movies.yml を見てください

特例として、キュアエコーは映画限定のプリキュアなので `Precure.all_stars` に対応した映画 [^echo_movie] を渡した時のみ取得できます。

[^echo_movie]: `:new_stage`, `:new_stage3`, `:sing_together_miracle_magic`

```ruby
> Precure.all_stars(:new_stage).include?(Cure.echo)
#=> true
```

## 個別にプリキュアを取得
`Cure` の後にプリキュアの名前を入力すると対応したプリキュアが取得できます

```ruby
> Cure.miracle.precure_name
#=> "キュアミラクル"

> Cure.magical.precure_name
#=> "キュアマジカル"

> Cure.felice.precure_name
#=> "キュアフェリーチェ"
```

「キュア」で始まらないプリキュア（シャイニールミナスとミルキィローズ）は下記で取得できます

```ruby
> Shiny.luminous.precure_name
#=> "シャイニールミナス"

> Milky.rose.precure_name
#=> "ミルキィローズ"
```

### 人間の時の名前を取得

```ruby
> Cure.miracle.human_name
#=> "朝日奈みらい"
```

### プリキュアに変身した後の名前を取得

```ruby
> Cure.miracle.precure_name
#=> "キュアミラクル"
```

### キャストの名前を取得

```ruby
> Cure.miracle.cast_name
#=> "高橋李依"
```

### プリキュアの色を取得

```ruby
> Cure.miracle.color
#=> "pink"
```

存在するプリキュアの色を全部取得したい時には `Rubicure::Girl.colors` が使えます

```ruby
> Rubicure::Girl.colors
#=> [:black, :blue, :green, :pink, :purple, :red, :white, :yellow]
```

特定の色のみ調べるワンライナー

```ruby
> Precure.all_stars.select(&:pink?).map(&:precure_name)
#=> ["キュアブルーム", "キュアドリーム", "キュアピーチ", "キュアブロッサム", "キュアメロディ", "キュアハッピー", "キュアハート", "キュアラブリー", "キュアフローラ", "キュアミラクル"]
```

色ごとの人数を集計して多い順に取得するワンライナー

```ruby
> Precure.all_stars.group_by{ |girl| girl.color }.map{ |color, girls| [color, girls.count] }.sort_by{ |color, girl_count| girl_count }.reverse
#=> [["pink", 10], ["yellow", 9], ["blue", 9], ["red", 5], ["purple", 4], ["white", 3], ["green", 3], ["black", 1]]
```

### 初登場した日付を取得

```ruby
> Cure.miracle.created_date
#=> Sun, 07 Feb 2016
```

`created_date` は厳密には正式加入した日付です。これは `Precure.all_stars` で特定の日付や映画の時点でプリキュアオールスターズとして存在するかチェックするのに `created_date` を使っているためです。（ハピネスチャージプリキュア！で序盤から登場しているキュアフォーチュンの `created_date` が2014/6/29（22話）なのになっているのはそのため）

### 誕生日を取得
```ruby
> Cure.miracle.birthday
#=> "6/12"
```

設定によっては誕生日が存在しないキャラもいますが、`#has_birthday?` や `#have_birthday?` でチェックできます。

```ruby
> Cure.miracle.has_birthday?
#=> true

> Cure.felice.has_birthday?
#=> false
```

`#birthday?` に日付を渡すと誕生日かどうか取得できます

```ruby
# 引数省略時は Date.today
> Cure.miracle.birthday?
#=> false

> Cure.miracle.birthday?(Date.parse("2016-06-12"))
#=> true
```

現時点で誕生日の存在しているキャラを取得

```ruby
> Precure.all_stars.select(&:have_birthday?).map(&:human_name)
#=> ["美墨なぎさ", "雪城ほのか", "九条ひかり", "日向咲", "美翔舞", "相田マナ", "菱川六花", "四葉ありす", "剣崎真琴", "春野はるか", "海藤みなみ", "天ノ川きらら", "紅城トワ", "朝日奈みらい", "十六夜リコ"]

> Precure.all_stars.select(&:have_birthday?).map(&:human_name).count
#=> 15
```

ちなみにrubicureで採用している誕生日はあくまでも **公式** 設定のみです。

「映画Yes!プリキュア5GoGo! お菓子の国のハッピーバースディ♪」（映画公開日11/8）で夢原のぞみの誕生日を祝うシーンがあるので11/8が夢原のぞみの誕生日という説もありますが、公式設定というには微妙なのでrubicureでは採用していません。（rubicureでは採用しないってだけなので他の人が11/8にのぞみの誕生日を祝うことは否定していません。）

### 変身
プリキュアといえば変身！

rubicureでは変身を忠実に再現しています

初期状態では `#name` は人間の時の名前が返ります。

```ruby
> yayoi = Cure.peace
> yayoi.name
#=> "黄瀬やよい"
```

`#transform!` で名乗りとともに変身します。

```ruby
> yayoi.transform!

(レディ？)
プリキュア・スマイルチャージ！
(ゴー！ゴー！レッツ・ゴー！ピース！！)
ピカピカピカリンジャンケンポン！ キュアピース！
5つの光が導く未来！
輝け！スマイルプリキュア！
```

変身は自分が変化する破壊的なメソッドなのでRubyの慣習に則ってメソッド名に `!` がついています。

変身後はプリキュアの名前が返ります。

```ruby
> yayoi.name
#=> "キュアピース"
```

変身後は `#attack!` で必殺技をうてます。

```ruby
> yayoi.attack!

プリキュア！ピースサンダー！！
```

必殺技は敵を破壊する破壊的なメソッドなのでこれもRubyの慣習に則ってメソッド名に `!` がついています。

2段階目以降の変身形態があれば順次変身します。（必殺技もそれに応じて変わります。）

```ruby
> yayoi.transform!
> yayoi.name
#=> "プリンセスピース"

> yayoi.attack!

開け、ロイヤルクロック！
(みんなの力を1つにするクル！)
届け、希望の光！
はばたけ！光り輝く未来へ！
プリキュア！ロイヤルレインボーバースト！

> yayoi.transform!
> yayoi.name
#=> "ウルトラピース"

> yayoi.attack!

(みんなの力を1つにするクル！)
プリキュア！ミラクルレインボーバースト！
輝けー！！
スマイルプリキュア！！
```

`humanize!` で元の人間形態に戻ります。

```ruby
> yayoi.humanize!
> yayoi.name
#=> "黄瀬やよい"
```

`#transform!` はシリーズ共通で使えるメソッドですが、シリーズ固有の変身の名乗りも全部対応しています

```ruby
> Cure.black.dual_aurora_wave!
> Cure.luminous.luminous_shining_stream!
> Cure.bloom.dual_spilitual_power!
> Cure.dream.metamorphose!
> Milky.rose.sky_rose_translate!
> Cure.peach.change_precure_beatup!
> Cure.blossom.open_my_heart!
> Cure.melody.lets_play_precure_modulation!
> Cure.happy.smile_charge!
> Cure.heart.love_link!
> Cure.ace.dressup!
> Cure.lovely.kururin_mirror_change!
> Cure.fortune.kirarin_star_symphony!
> Cure.flora.princess_engage!
```

### 魔法つかいプリキュア！の変身
魔法つかいプリキュア！はリンクルストーンによって変身先が変わります。

ダイヤスタイルに変身

```ruby
> mirai = Cure.miracle
> mirai.name
# => "朝日奈みらい"

> mirai.cure_up_rapapa! :diamond
キュアップ・ラパパ！　ダイヤ！
ミラクル・マジカル・ジュエリーレ！
ふたりの奇跡！キュアミラクル！
魔法つかいプリキュア！！

> mirai.name
#=> "キュアミラクル（ダイヤスタイル）"

> mirai.attack!
リンクルステッキ！
ダイヤ！永遠の輝きを私達の手に！
フルフルリンクル！
プリキュア！ダイヤモンドエターナル！
```

ルビースタイルに変身

```ruby
> mirai.cure_up_rapapa! :ruby
キュアップ・ラパパ！　ルビー！
ミラクル・マジカル・ジュエリーレ！
ふたりの奇跡！キュアミラクル！
魔法つかいプリキュア！！

> mirai.attack!
リンクルステッキ！
ルビー！紅の情熱よ私達の手に！
フルフルリンクル！
プリキュア・ルビー・パッショナーレ！
```

サファイアスタイルに変身

```ruby
> mirai.cure_up_rapapa! :sapphire
キュアップ・ラパパ！　サファイア！
ミラクル・マジカル・ジュエリーレ！
ふたりの奇跡！キュアミラクル！
魔法つかいプリキュア！！

> mirai.attack!
リンクルステッキ！
サファイア！青き知性よ私達の手に！
フルフルリンクル！
プリキュア・サファイア・スマーティッシュ！
```

トパーズスタイルに変身

```ruby
> mirai.cure_up_rapapa! :topaz
キュアップ・ラパパ！　トパーズ！
ミラクル・マジカル・ジュエリーレ！
ふたりの奇跡！キュアミラクル！
魔法つかいプリキュア！！

> mirai.attack!
リンクルステッキ！
トパーズ！金色の希望よ私達の手に！
フルフルリンクル！
プリキュア・トパーズ・エスペランサ！
```

はーちゃんこと花海ことははエメラルドで変身します。

```ruby
> ha_chan = Cure.felice
> ha_chan = Cure.felice.name
#=> "花海ことは"

> ha_chan.cure_up_rapapa! :emerald
キュアップ・ラパパ！エメラルド！
フェリーチェ・ファンファン・フラワーレ！
あまねく生命に祝福を！キュアフェリーチェ！

> ha_chan.name
#=> "キュアフェリーチェ"

> ha_chan.attack!
フラワーエコーワンド！
エメラルド！
キュアー・アップ！
プリキュア！エメラルド・リンカネーション！
```

## 現在放映中のプリキュアの番組を取得
`Precure.now` や `Precure.current` で取得できます

```ruby
> Precure.now.title
#=> "魔法つかいプリキュア！"

> Precure.current.title
#=> "魔法つかいプリキュア！"
```

## プリキュアの番組が放映中かどうか取得する
`#on_air?` を使うと、引数に渡した日付で放映されているかチェックできます

```ruby
> Date.today
#=> Thu, 1 Dec 2016

> Precure.maho_girls.on_air?(Date.today)
#=> true

> Precure.go_princess.on_air?(Date.today)
#=> false
```

`Date` や `Time` 以外にも日付っぽい文字列を渡してもいけます

```ruby
> Precure.maho_girls.on_air?("2015-12-01")
#=> false

> Precure.go_princess.on_air?("2015-12-01")
#=> true
```

## ピカリンジャンケン
キュアピースはピカリンジャンケンができます。

```ruby
> Cure.peace.pikarin_janken
ピカピカピカリン
ジャンケンポン！
（パー）
```

## eachを独自定時
`Precure` （実体は `Rubicure::Core`）はシリーズ順に取得できるように `each` を実装しています。

```ruby
> Precure.each{ |series| puts series.title }
ふたりはプリキュア
ふたりはプリキュア Max Heart
ふたりはプリキュア Splash☆Star
Yes！ プリキュア5
Yes！ プリキュア5 Go Go！
フレッシュプリキュア！
ハートキャッチプリキュア！
スイートプリキュア♪
スマイルプリキュア！
ドキドキ！プリキュア
ハピネスチャージプリキュア！
Go!プリンセスプリキュア
魔法つかいプリキュア！
```

 `Enumerable`[^Enumerable] を `include` しているため `map` などのメソッドにも反応します。 

[^Enumerable]: https://docs.ruby-lang.org/ja/latest/class/Enumerable.html

```ruby
> Precure.map(&:title)
#=> ["ふたりはプリキュア", "ふたりはプリキュア Max Heart", "ふたりはプリキュア Splash☆Star", "Yes！ プリキュア5", "Yes！ プリキュア5 Go Go！", "フレッシュプリキュア！", "ハートキャッチプリキュア！", "スイートプリキュア♪", "スマイルプリキュア！", "ドキドキ！プリキュア", "ハピネスチャージプリキュア！", "Go!プリンセスプリキュア", "魔法つかいプリキュア！"]
```

### `!` 演算子
特定のキャラは否定の `!` 演算子を実装しているので、使う度に正義と悪が入れ替わります

```ruby
> passion = Cure.passion
> passion.name
#=> "東せつな"

> !passion

> passion.name
#=> "イース"
```

```ruby
> beat = Cure.beat

> beat.name
#=> "黒川エレン"

> !beat

> beat.name
#=> "セイレーン"
```

```ruby
> scarlet = Cure.scarlet
> scarlet.name
#=> "紅城トワ"

> !scarlet

> scarlet.name
#=> "トワイライト"
```

ちなみにこの機能はRubyKaigi 2014で `!` 演算子をオーバーライドできるというのを聞いて思いつきました。

![image](https://qiita-image-store.s3.amazonaws.com/0/8059/ec95e0c1-3d74-e519-3400-dfde9f42e64d.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/8059/f34611e4-df57-6449-b220-fa02a6c1b36f.png)

## ワンライナー用の `ubicure`
`ruby` コマンドや `irb` コマンドで特定のgemを `require` した状態で起動する時は `-r<gem名>` のようになります。

rubicureの場合 `ruby -rrubicure` ですが、長いので代わりに `ubicure` を `require` できます。

```shell-session
$ ruby -rubicure -e'puts Shiny.luminous.transform_message'
```

# 改善点
## did_you_mean対応
全ての機能を `method_missing` で作りきってしまったため、typoしてもdid_you_meanで教えてくれないのが難点。

フルスタックキュアエンジニアなら全プリキュアの名前くらい知っているものなので不要だとは思うのですが、typoした時にdid_you_meanで教えてくれるのに慣れきってしまったので入れたい欲はあります。

# その他関連プロダクト
## rubicure API

Rubyがインストールされていない環境でもrubicureが使えるようにAPIサーバも提供しています。

* https://rubicure.herokuapp.com/
* ソースコード： https://github.com/sue445/rubicure_api

```shell-session
$ curl -s https://rubicure.herokuapp.com/series/go_princess.json | jq .
{
  "series_name": "go_princess",
  "title": "Go!プリンセスプリキュア",
  "started_date": "2015-02-01",
  "girls": [
    "cure_flora",
    "cure_mermaid",
    "cure_twinkle",
    "cure_scarlet"
  ]
}

$ curl -s https://rubicure.herokuapp.com/girls/cure_twinkle.json | jq .
{
  "girl_name": "cure_twinkle",
  "human_name": "天ノ川きらら",
  "precure_name": "キュアトゥインクル",
  "cast_name": "山村響",
  "color": "yellow",
  "created_date": "2015-02-22",
  "transform_message": "プリキュア！プリンセスエンゲージ！\nきらめく星のプリンセス！キュアトゥインクル！\n強く、やさしく、美しく！\nGo!プリンセスプリキュア！\n冷たい檻に閉ざされた夢、返していただきますわ！\nお覚悟はよろしくて？",
  "extra_names": null,
  "attack_messages": [
    "エクスチェンジ！モードエレガント！\nキラキラ、星よ！プリキュア・トゥインクル・ハミング！\n(ドリーミング)\nごきげんよう\n"
  ],
  "transform_calls": [
    "princess_engage"
  ]
}
```

[Tachikoma.io](http://tachikoma.io/) を使ってrubicureがアップデートされたら `bundle update` するPRを送るようにしているので雑に最新版に保ち続けています。

## faker-precure
テストデータでプリキュアを使うためのライブラリです

https://github.com/sue445/faker-precure

使い方

```ruby
require "faker/precure"

> Faker::Precure.human_name
#=> "黄瀬やよい"

> Faker::Precure.precure_name
#=> "キュアアクア"

> Faker::Precure.cast_name
#=> "豊口めぐみ"

> Faker::Precure.title
#=> "Yes！ プリキュア5"

> Faker::Precure.transform_message
#=> "レッツプレイ！プリキュアモジュレーション！！\n爪弾くは女神の調べ！ キュアミューズ！\n届け4人の組曲！スイートプリキュア！"

> Faker::Precure.user_name
#=> "cure_mint"
```

[factory_girl](https://github.com/thoughtbot/factory_girl) を使っている場合こういうfactoryを作ればいい感じにランダムなテストデータを作れます。

```ruby
FactoryGirl.define do
  factory :user do
    name { Faker::Precure.human_name }
  end
end
```

一応注意点としてはプリキュアは44人しかいない [^precure_count]ので、一意制約をつけているカラムに対して使わないでください。 **44分の1の確率でテストが落ちます**

[^precure_count]: 2016年12月時点

## cure-bots
https://github.com/sue445/cure-bots

プリキュアの誕生日の0:00にツイートするだけのボットです。

![image](https://qiita-image-store.s3.amazonaws.com/0/8059/fa71d460-5ae5-cb51-7749-c849c52f532e.png)


https://twitter.com/sue445/status/797091587029405698

# 他言語での実装について
[プリキュアの各言語での実装まとめ - Qiita](http://qiita.com/sue445/items/b41a4f5bdca46f1736c3) 参照

# 最後に
駆け足になりましたがrubicureとその周辺ツールの紹介をさせてもらいました。

レッツプリキュア！