読者です 読者をやめる 読者になる 読者になる

速習プログラミングElixir勉強会を主催してました #quick_elixir

10月末くらいから Elixir の勉強会を主催していて、今日で一通り終わりました。

connpass.com

資料

今日のLT資料は Qiita に上げた。 Elixir のマクロとプロセスの関係

この勉強会を開催したきっかけ

10月にプログラミングElixir を買ったけど、積み本になりそうな予感がしたのでちゃんと読了するために勉強会を開催しました。

あと、社内Slackで聞いたら意外と興味ある人がいたので、勢いのまま勉強会のページを作成した。

f:id:sinsoku:20161115234331p:plain

開催して良かった

毎週大変だったけど、なんとか当初の目的であるプログラミングElixirの読了を達成できた。

読んだ感想などはLTでも話したけど、やっぱり普段使いの Ruby と考え方が違うところは楽しいですね。新しい発見とかがある。

プログラミングElixir は良い本

読んでいて面白いし、分かりやすいので興味をもった人は買ってみたら良いと思いますよ。

プログラミングElixir

プログラミングElixir

Elixir で Sleep Sort を実装する

タイトルの通り、 Sleep Sort を実装してみました。

Elixir の並行プログラミングを生かしているけど、全く実用的じゃないソートですね!

defmodule SleepSort do
  def sort(collection) do
    me = self
    collection
    |> Enum.map(fn (n) ->
        spawn_link fn -> (send me, { :timer.sleep(n * 10) && n }) end
      end)
    |> Enum.map(fn (_pid) ->
        receive do { result } -> result end
      end)
  end
end

range = 1..20
collection = Enum.shuffle(range)
IO.inspect collection
#=> [6, 13, 17, 10, 1, 16, 15, 14, 12, 11, 18, 8, 3, 5, 4, 20, 9, 7, 2, 19]
sorted = SleepSort.sort(collection)
IO.inspect sorted
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
IO.inspect Enum.to_list(range) == sorted
#=> true

factory girl で特定のメソッドを rspec-mock の stub で潰す

備忘録。

外部APIを呼ぶメソッド、処理に時間のかかるメソッドなど、普段のテストでは不要であるメソッドは factory 側で潰すと楽。

FactoryGirl.define do
  factory :user do
    transient do
      stubs [:call_external_api]
    end

    after :build do |user, evaluator|
      evaluator.stubs.each do |m|
        r = RSpec::Mocks::Matchers::Receive.new(m, -> {})
        RSpec::Mocks::AllowanceTarget.new(user).to r
      end
    end

もし User#call_external_api のテストをしたい場合は stubs を空にすれば良い。

user = build :user, stubs: []
user.call_external_api
#=> call_external_api が呼べる。

8月に購入したバルドハート、テイルズオブベルセリアをクリアした

8月末に発売された「BALDR HEART バルドハート (PC 18禁ゲーム)」と「テイルズオブベルセリア(PS3)」を最近やっとクリアできたので、たまにはゲームの感想でもブログに書いてみる。

Qiita と違ってこういう記事書いても非公開にならないので、はてなブログは良い。

BALDR HEART

戯画のバルドシリーズの最新作。今までのシリーズが好きなら、買って後悔しない出来だと思う。

プレイ時間は全ヒロイン、サバイバル Stage 260 クリアまでで50時間くらい。

あらすじ

電脳化が一般化し、仮想世界(ネット)はもう一つの現実となった。 仮想世界に没入(ダイブ)して活動する電子体が怪我を負えば現実の肉体にフィードバックされ、リミッターがかかってない場所での死は脳死(フラットライン)に至ってしまうほどリアルな手触りを得ていた。

とある事情で戦闘能力と職を失った瀧沢蒼は、養父を頼り故郷である『海神』という島に帰ってきたが、傭兵時代の相棒である三納岸ユーリがなぜか島にまでついて来た上に、ついて早々、記憶喪失の少女月詠を助けたことで自宅に居候させることになってしまう。

過疎化が進みろくな仕事も無い島だったが、謎の人物から仕事の依頼が舞い込む。 その内容は、海神唯一の学園である『甲華学園』に編入し、学園生が島で発生しているテロに関わっていないか調査することだった。

学園に潜入しようとした蒼の前にシュミクラムで立ちはだかる生徒会長の天ケ瀬茉緒。

その時、蒼の前に自らを妖精と名乗る少女が現れ、戦闘能力を失った蒼のシュミクラムに同化しはじめた

引用: http://dic.nicovideo.jp/a/baldr%20heart

世界観

前作のバルドスカイから未来の話なので、

  • 電子体幽霊の話
  • AI
  • 無意識の海

など、過去作をやっている人にとっては懐かしい単語が出てくる。

シナリオ

今までのバルドシリーズと同様、ヒロインの攻略順は完全固定で 月詠 → 茉緒 → ユーリ → 凪 の順になる。

シナリオを進めると伏線が少しずつ分かるようになっていて、過去の事件と現在の事件がつながり始める。凪シナリオ後半の世界がカオスな感じになるのはバルドっぽくて良かった。

ラスボス戦

主題歌が流れてラスボス戦に突入する演出が熱い!!

武装のレベルが低いと、だいぶ厳しい。

master 武装でコンボすれば、結構簡単に倒せた。

武装(兵装少女)

今作では武装が擬人化され、武装を使うごとに兵装少女との信頼関係が高まって新しい武装が使えるようになる。 また、装備する武装の組み合わせで兵装少女同士が戦友になって、更に武装が強くなる。

やっぱり武装が強くなってコンボ組み立てるのは楽しい。「バルトハンマー → I・A・I → 彗星脚 → DDミサイル」みたいに、最後は重火器で〆るのが威力高くてよく使ってた。

巨弾のイリヤのグラルパトン「こいつをふっ飛ばして、更地にしてやろうぞ〜」の台詞が好き。

アペンドディスク

アペンドディスク出るらしいので、全兵装の武装を master にして、全兵装少女を戦友にしておかないといけない。

テイルズオブベルセリア

テイルズシリーズの最新作。前作ゼスティリアと同じ世界観で、過去の話。

プレイ時間は隠しダンジョン、真ラスボスを倒すの含めて70時間くらい。

あらすじ

ウェイストランドと呼ばれる世界。ミッドガンド聖導王国の辺境の村にベルベット・クラウという少女が住んでいた。彼女は明るく飾り気のない性格で家族と平和に暮らしていた。 しかし、赤い月が上ったある夜、この村に異変が起きた。この事件がきっかけでベルベットは肉親を失い、左腕が魔の手になってしまう。 それから3年、ベルベットは復讐のため、旅に出る。

引用: https://ja.wikipedia.org/wiki/テイルズオブベルセリア

シナリオ

復讐から始まる旅だけど、話が進むにつれて謎が少しずつ分かっていき、そして世界の危機を救うことに・・・というテイルズらしい話。

前作のような変な矛盾も無いし、良いシナリオだったと思う。ただ、ベルセリアの未来がゼスティリアだと思うと複雑な気持ちになるが・・・。

戦闘システム

戦闘システムはSG(ソウルゲージ)で特技を結構自由に出せるし、慣れるとスイッチブラストでキャラ交換しながらコンボを繋げられるようになって楽しい。

前作のようにカメラワークが酷かったり、戦闘キャラが2人固定みたいな事もないのでストレスなく遊べると思う。

キャラクター

ライフィセット の存在が癒やし。一番可愛い。

おわり

バルドハートの感想に時間かけすぎて、テイルズは短くなってしまった。

Rails で gem を導入するときに最低限やること

普通にみんなやっていることだと思うけど、自分が gem を入れるときに最低限やることをまとめてみた。

何かしらの方法で探してきた gem を「導入するかどうか」の判断をするときにやることです。

やること一覧

  1. 今後もメンテされそうかを調べる
  2. README を 全部 読む
  3. 依存関係を調べる
  4. ソースコードを読む

今後もメンテされそうか?

ほとんどの gem は GitHub で公開されているので、下記の観点を参考に調べる。

  • 最終コミット日時
    • 1年前とかだと安定している or 放置されている
  • contributes の数
    • 多いとメンテされそう
  • star の数
    • 使用者が多く、問題が報告(修正)されそう
  • Releases の数
    • 基本機能は安定してそう
  • Issues, Pull Requests の数、対応状況
    • 問題が放置されていないか
    • 自分が踏みそうなものが挙がっていないか
  • 作者はどんな人か?
    • 有名な gem の作者なら技術力ありそう(= ちゃんと動きそう)

ただ、「機能が少なく、あまり更新されない gem」とか「Issues は多いけど、他に代用もない phantomjs」みたいなのもあるので、上記だけでは判断できないこともある。

README を全部読む

はてなブログや Qiita などの記事は参考になるけど、「gem のアップデートに追従できていない」「インストール手順が中途半端」「一応動くけど、内容が微妙」みたいなのが多いです。 安易に信じると、未来の自分が死にます。

今までの経験上、信頼できるのは「ソースコード、次に README」です。英語が苦手でも、 README は一通り目を通した方が良いです。

依存関係を調べる

GitHubリポジトリにある xxx.gemspec を読んで、依存関係がどのくらいあるかを調べる。

  • 依存関係が多い
    • 他 gem (特に Rails )のアップデートで苦労するかも
  • 依存関係が少ない
    • 他 gem へ移行することがあっても楽そう

ソースコードを読む

全部を読むのは時間かかるので、「バグに遭遇したとき、コードを読めそうか?直せるか?」の観点でざっと目を通します。

  • ファイル数がどのくらいか
    • 少ない場合は fork してメンテ(もしくは再実装)がしやすい
  • メタプロがどのくらい使われているか
    • rails_admin などは導入に覚悟が必要になってくる
  • コードが読みやすいか
    • 複雑なコードは直す気になりにくい
    • デバッグ・修正も大変
  • テストコードがあり、 CI が動いているか
    • コードを直したときに、既存機能が壊れていないか確認できる

おまけ: 絶対にやったらダメ

はてなブログや Qiita の「おすすめ gem 10選」みたいなので公開されている gem を一気に Gemfile に入れるのは絶対ダメ。

問題が起きたときに「どの gem が原因か?」が分からなくなって大変なので、面倒でも1つずつ調べてから導入すべきです。

まとめ

ざっと gem を導入するときに気にしてることを書いてみました。何か参考になれば幸いです。

rake notes で TODO が増え過ぎたら CI で検知できるように拡張する

Rails アプリから機能を gem 化する方法の実例を1つ公開してみる。

sinsoku.hatenablog.com

rake notes を拡張する

rake notesRails アプリ内の "OPTIMIZE|FIXME|TODO" を一覧表示できるが、これを拡張する。

ソースコードの探し方

  1. rails/railsリポジトリをローカルに git clone して、 git grep :notes する
  2. annotations.rake が見つかる
  3. git grep "class SourceAnnotationExtractor" する
  4. source_annotation_extractor.rb が見つかる

パッチを書く

lib/rails_notes_validation/lib/rails_notes_validation.rb

# frozen_string_literal: true
class SourceAnnotationExtractor
  module MaxSizeValidation
    def display(results, options = {})
      super

      annotations = results.values.flatten
      display_annotations(annotations)
      display_failed_if_exceed(annotations)
    end

    private

    def display_annotations(annotations)
      puts 'Result'
      tag_and_size = annotations.group_by(&:tag).map { |tag, arr| [tag, arr.size] }
      tag_and_size.sort_by(&:last).reverse_each do |e|
        tag, size = e
        puts "  * [#{tag}] #{size}"
      end
      puts
      puts "Total"
      puts "  * [#{tag}] #{annotations.size}"
    end

    def display_failed_if_exceed(annotations)
      max_size = ENV['SOURCE_ANNOTATION_MAX_SIZE'].to_i
      exceed_size = annotations.size - max_size
      if exceed_size.positive?
        puts
        puts "Failed: #{exceed_size} notes exceed. [#{annotations.size}/#{max_size}]"
        exit 1
      end
    end
  end
  SourceAnnotationExtractor.prepend(MaxSizeValidation)
end

動かしてみる

$ rake notes
app/models/user.rb
  * [10] [TODO] Fix me

Result
  * [TODO] 1

Total
  * [OPTIMIZE|FIXME|TODO] 1

Failed: 1 notes exceed. [1/0]
$ echo $?
1

これで終了コードが 1 になるので、 CI で実行させれば fail になる。

おわり

gem にするまでもないコードとか、公開前の動作確認とか、そういうので便利ですよ。

Rails アプリから汎用的な機能を gem 化する方法

これから Rails 用の gem を作ってみたい人向けの内容。

gem 化する

実は下記のようなコードを置くだけで RubyGems に登録しなくても、 gem として認識される。

Gemfile

gem 'hello_world', path: 'lib/hello_world'

lib/hello_world/hello_world.gemspec

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)

Gem::Specification.new do |spec|
  spec.name          = 'hello_world'
  spec.version       = '0.0.1'
  spec.summary       = ''
  spec.authors       = ['']
end

lib/hello_world/lib/hello_world.rb

module HelloWorld
  def self.say
    puts 'Hello, World'
  end
end

これは簡単な例だけど、 rails c で読み込まれていることが確認できる。

HelloWorld.say
#=> "Hello, World"

仕事のコードで「これ gem にできるかも?!」みたいなのを gem 化して、ある程度試して安定してきたら公開すると良い。

一般公開する

概要は下記のような感じ。分からんところはググれば参考になる記事がたくさん見つかると思います。

$ bundle gem hello_world 
MIT License enabled in config
Code of conduct enabled in config
      create  hello_world/Gemfile
      create  hello_world/.gitignore
      create  hello_world/lib/hello_world.rb
      create  hello_world/lib/hello_world/version.rb
      create  hello_world/hello_world.gemspec
      create  hello_world/Rakefile
      create  hello_world/README.md
      create  hello_world/bin/console
      create  hello_world/bin/setup
      create  hello_world/.travis.yml
      create  hello_world/.rspec
      create  hello_world/spec/spec_helper.rb
      create  hello_world/spec/hello_world_spec.rb
      create  hello_world/LICENSE.txt
      create  hello_world/CODE_OF_CONDUCT.md
$ cd hello_world/
$ vim hello_world.gemspec # TODO: を直す
$ vim README.md # 使い方を書く
$ bundle exec rake release # RubyGems に公開される