ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

mruby-cli を使ってコマンドラインツールを作ってみた話

さて、世間では Advent Calendar が盛んになる時期ですね。

弊社でもやってみればいいのではと思ったものの私自体が存在を思い出したのが 11/30 なので、 何も調整せずに勝手に書き出してみて、誰かが続いて書いて 25 日まで続いたらいいなーと思ってはじめてみることにます。 (このブログは、強制だったりノルマがあるわけではなくメンバーが書きたくなったら書くというゆるいルールでやっています。)

本題

もう 1 ヶ月ほど前になりますが、mruby-cli を使って個人で使うツールを作っていました。

今回、なぜ mruby-cli を使用したかというと、よく使うコマンドはワンバイナリでインストールできる形の方が扱うのが簡単と思ったのと、クロスコンパイルで複数の環境のバイナリを普段書き慣れている Ruby で生成できるという点を魅力的に感じたからです。

そこで、普段 CRuby を使っている人が mruby-cli でアプリケーションを開発してみた時に感じた気づきについてまとめてみたいと思います。

mruby は CRuby で使えるメソッド全てが使えるわけではない

書き出して最初に意識を変えないといけないと思ったポイントです。mruby は、CRuby で入っている標準ライブラリのメソッドが全部あるというわけではありません。

String や Array など標準ライブラリにあるメソッドでも、mruby における Rubygems 相当の mgem を使って拡張する必要なものがあります。

mruby で利用するライブラリは mruby/mgem-list から探す

次に困ったことはライブラリはどこから探せばいいかということです。 CRuby であれば rubygems.org で探すことができます。

mruby/mgem-list という有志が作っている mgem の一覧から探すようです。

require する必要がない

mruby-cliRuby で書くのですが、ファイルを分割した際、明示的に require をする必要がありません。

動かすにはコンパイルが必要

すごく当たり前のことなのですが、動かす時にコンパイルする必要があります。 今回、REST API を叩くコマンドライブラリを作成していたのですが、API の使い方やレスポンスの内容などを作成しているアプリで try & error でやろうとすると大変という印象を受けました。

最後に

同じ Ruby の文法といっても、ライブラリの探し方から実行させるための手順が違い戸惑うことが最初のうちは多かったです。しかし、Web サービスのクライアントアプリを書くという範囲では、1 つでも API を叩くパスが出来てしまえば、あとはサクサク進むという印象でした。

ツールを作る手段として、覚えておいて損はないと思うので素振りしてみるといいと思いました。

bundler にサブコマンドを追加する

TL;DR

bundle-#{sub_command_name} というコマンドを作り PATH が通っている箇所に配置をすると bundle sub_command_name で呼ぶことができる。

本文

先日、X 回目の Rails Update 業を行いました。 今回の Rails Update 業は、久しぶりに再開したプロジェクトのため Rails 以外の gem から出されている deprecate warning を消すのに苦労しました。

その際、何のメソッドが呼ばれたことによって deprecate warning が出るのはわかっているので、bundler に入れている gem 群を grep できたらと思いました。

これを実現するには、以下のようなコマンドの組み合わせで実現できます。

$ bundle show --paths | xargs grep keyword

これを bundle grep keyword のように書けたらべんりだなーって思い、bundler のコードを読み実現できないかを調べてみました。

そこで見つけたのが、Bundler::CLI にある以下のコードです。

def self.handle_no_command_error(command, has_namespace = $thor_runner)
  if Bundler.settings[:plugins] && Bundler::Plugin.command?(command)
    return Bundler::Plugin.exec_command(command, ARGV[1..-1])
  end

  return super unless command_path = Bundler.which("bundler-#{command}")

  Kernel.exec(command_path, *ARGV[1..-1])
end

これを見ると、bundler 自体がサブコマンドとして用意していないサブコマンドは、bundler-#{command} というコマンドを探し、存在していたら実行してくれるように読めます。

そこで、bundler-grep という、gem を作ってみました。gem i bundler-grep して、環境変数などをセットすれば、お好きな grep コマンドで bundle show --paths 内を grep できるようになります。

余談

これを作っている最中に、それワンライナーでできるよと bundler に pull request 出してた人 から言われたため、このワンライナーだけではできない機能も入れたりしました。

RubyWorld Conference 2016 に参加しました

こんにちは、 @junk0612 です。

11/3(木・祝) と 11/4(金) に島根県松江市で行われた、RubyWorld Conference 2016 に参加してきました。 今回は総勢9名での参加となりました。

Platinum スポンサー

RubyKaigi 2016 でも スポンサーをさせていただきました が、今回も Platinum スポンサーとしてブース出展をしてきました。

前回のアジャイル阿闍梨餅に引き続き、今回も地元の和菓子ということで 福田屋 さんにお願いして、Ruby に見立てた赤い寒天が特別に乗った紅白のお饅頭をブースにて配布しておりました。(紹介してくださったスーパー公務員の皆様、ありがとうございます!)

ショートプレゼンテーション

協賛企業ショートプレゼンテーションとして @koic が「10年生きる Ruby/Rails アプリケーションプログラマーのエコシステム」というプレゼンをさせていただきました。

www.slideshare.net

コーヒーブレイクの時間でのプレゼンテーションでしたが、来ていただいた方とお饅頭を片手にお話ができ、興味を持っていただいたようで良かったです。

セッションの感想

わたしは今回初めての参加でしたが、「小さな町で子供向けのプログラミング講座をはじめてみて」や「ゼロから稼げるエンジニアになる3つのステップ」など、RubyKaigi では聞くことができなさそうなセッションにも参加できて、新鮮で面白かったです。

また、「Scientific Computing in Ruby」の直後に「Ruby における機械学習のための環境整備の取り組み」という機械学習トピック2本立てのセッションが強く印象に残りました。 Python で scikit-learn を用いて、ランダムフォレストを使った分類などのかんたんな機械学習を学生時代に少しかじったことがあるのですが、Ruby でもそれができるようになるといいなぁ...(そこに貢献することができたらもっとうれしいですね)

さいごに

今回のカンファレンスは非常に充実した時間を過ごすことができました。これも運営に携わっていた皆様や発表者の皆様のおかげと存じます。 また、ブースに立ち寄ってくださった皆様、ショートプレゼンテーションで興味をもってくださった皆様ありがとうございました。またどこかのスポンサーブースで (和菓子を用意して?) お待ちしております!

JavaScript のパッケージマネージャ Yarn を Idobata で使った雑感

こんにちは、hibariya です。npm と互換性のある新しいパッケージマネージャ Yarn が話題ですね。idobata.io でも npm の代わりに Yarn を使うようになり、CI やデプロイが数十秒早くなりました。

今日は Yarn を試すなかで気になったポイントをまとめてみます。なお、これを書いている時点で既に 0.16.00.16.1 がリリースされ、既に解決したものもあります。ぜひ最新版へアップデートしましょう。

yarn run のスペースの扱い

0.15.1 までは、スペースを含めてコマンドの一部として扱われてしまうようで、オプションや引数を渡す使い方ができませんでした。そのため package.json に以下のような scripts を用意して yarn run で実行すると失敗していました。

  "scripts": {
    "build": "ember build",
    "start": "ember server",
    "install-build": "bower install --silent --allow-root && ember build"
  },

yarnpkg/yarn#809 で修正され、0.16.0 のリリースに含まれています。

CircleCI + Docker で yarn install できない

CircleCI 上で動かした Docker コンテナの中で yarn install が失敗することがあります。yarnpkg/yarn#918 と同じ現象と思われます。

ESM では Idobata をオンプレミスで使う 方法を提供していて、その手段のひとつとして Docker 上での動作環境もメンテナンスしています。CI での動作確認は必要なので、いったん、 Docker 上では npm を使うような対策を施しました。

Heroku で Yarn を使う

heroku-buildpack-nodejs の Support for Yarn package manager で対応が進められており、今のところは yarn ブランチで試せます。

heroku buildpacks:set https://github.com/heroku/heroku-buildpack-nodejs#yarn

Engine Yard 環境に Yarn を入れる

idobata.io は Engine Yard 上で動作しており、Engine Yard の環境は Gentoo Linux です。Gentoo 上で Yarn をパッケージ管理できて、ひとまずお手軽な方法を、と検討した結果、ebuild ファイルを作り ローカルの overlay として使うようにしました。

おわりに

現時点の Yarn を使ってみて気になった点を紹介しました。みなさんの開発の手助けになればと思います。

技術面接で出された問題

9月に中途で入社した@wat-aroです.
前職はプログミングと全く関係のない仕事でしたが,プログラムを書く仕事がしたくて退職しました.
退職してからはまず基礎を身につけようとSICPを読み,ほとんどの問題を解き終わったのでFjrodのリモートインターンに参加して勉強していました.
今日は永和システムマネジメントの技術面接で出されたアルゴリズムの問題を紹介しようと思います.

出された問題はアナグラムの判定です.
アナグラムとは文字列の順番を入れかえて,別の文字列になっているものです.
erosrose は文字の順番を入れ替えているだけなのでアナグラムです.
eroslose は文字を入れ替えただけでは一致しないのでアナグラムではありません.
これを判定するコードを書きます.

面接ではRubyで書くのが難しければ疑似コードでもいいし,口頭でアルゴリズムを説明するだけでもいいと言われたので慣れているSchemeで考えることにしました.
さらに文字列は考えにくいのでリストで考えることにしました.

まずは一致する条件を考えます.
二つのリストの要素を順に比べていって,最後に両方のリストが空ならtrueです.
片方が空なのに,もう片方にまだ要素が残っていれば要素の数が違うのでfalseです.
ここまでをとりあえず書いてみます.

(define (anagram lst1 lst2)
  (cond ((and (null? lst1)
              (null? lst2))
         #t)
        ((or (null? lst1)
             (null? lst2))
         #f)
        (else
         ...)))

次にlst1の最初の要素をlst2から取り除く補助関数を作ります.
lst1の最初の要素がlst2に含まれていなければfalseを返してくれると
含まれているかの判定と次の再帰で使うリストの作成が同時にできるのでそのようにします.
もちろん,末尾再帰のほうが嬉しいのでそうします.

(define (remove-item item lst result)
  (cond ((null? lst) #f)
        ((eq? (car lst) item)
         (append (reverse result) (cdr lst)))
        (else
         (remove-item item (cdr lst) (cons (car lst) result)))))

これを最初の関数に組み合わせます.

(define (anagram lst1 lst2)
  (define (remove-item item lst result)
    (cond ((null? lst) #f)
          ((eq? (car lst) item)
           (append (reverse result) (cdr lst)))
          (else
           (remove-item item (cdr lst) (cons (car lst) result)))))
  (cond ((and (null? lst1)
              (null? lst2))
         #t)
        ((or (null? lst1)
             (null? lst2))
         #f)
        (else
         (let ((removed-list (remove-item (car lst1) lst2 '())))
           (if removed-list
               (anagram (cdr lst1) removed-list)
               #f)))))

動かしてみると

gosh> (anagram '(e r o s) '(r o s e))
#t
gosh> (anagram '(e r o s) '(r o s ))
#f
gosh> (anagram '(e r o s) '(l o s e))
#f
gosh> (anagram '(e r o) '(r o s e))
#f

大丈夫そうですね.
実際の面接では後半は口頭で説明しただけでした.
リストをこねくり回すのは楽しいですね.

動きそうだということで答え合わせです. モニタに写されたコードは次のようなものでした.

def anagram(s1, s2)
  s1.chars.sort == s2.chars.sort
end

とても簡単です… 最後に悔しくなりましたが,ホワイトボードに向かってLispを書くのは楽しかったです.

Rubyでprivateなクラスメソッドを定義するには

先日、privateなクラスメソッドを定義しようとして、つまづきました。 なので、僕が実装に失敗したパターンと正しい実装方法を記載したいと思います。

失敗パターン

まず、僕が実装に失敗したパターンです。何事もなく呼び出せてしまっています。

class C
  private

  def self.def1
    p 'def1'
  end
end

C.def1 # "def1"

正しい実装方法

Module#private_class_methodを使う場合

次に、正しく実装した場合です。想定通りdef1がprivateメソッドになっているので、呼び出すことが出来ませんでした。

class C
  def self.def1
    p 'def1'
  end

  private_class_method :def1
end

C.def1 # a.rb:9:in `<main>': private method `def1' called for C:Class (NoMethodError)

class << selfの内部でprivateを指定する場合

また、class << selfの内部でprivateを指定しても、定義することが出来ました。

class C
  class << self
    private

    def def1
      p 'def1'
    end
  end
end

C.def1 # a.rb:11:in `<main>': private method `def1' called for C:Class (NoMethodError)

結論

privateなクラスメソッドを定義するには、Module#private_class_methodを使用する、class << selfの内部でprivateを指定するの2パターンの方法があることが分かりました。

RubyKaigi 2016 に参加していました

どうも、muryoimpl です。9/8 から 9/10 にかけて開催された RubyKaigi 2016 に総勢15名で参加してきました。

Bento Sponsor をやりました

事業部として3日に渡ってブースを出したり、 2日目の Bento Sponsor でお弁当を配ったりもしました。

f:id:muryoImpl:20160920023551j:plain

ブースでは、3 日間で 800 個阿闍梨餅を用意し、皆さんのお越しをお待ちしておりました。用意した阿闍梨餅は、全てお召し上がりいただけました。ブースに立ち寄ってくださった方、質問してくださった方、陳列されていた私をいじってくれた方ありがとうございました!

事業部の名にある「アジャイル」と「あじゃり」をかけたダジャレとわかってもらえる予定が、存外わかってもらえずにダジャレの説明を求められるという3日間を過ごしました……

Bento Sponsor としては、お弁当の配布と、のし紙に弊社のロゴを模したちょっとしたコードをつけてアピールさせていただきました。

アジャイル事業部ホームページのエントリ にコードが掲載されていますので、まだ見ていない方は是非読み解いたり実行してみたりしてください。

2日目 『Learn Programming Essence from Ruby patches』 by Mitsutaka Mimura

弊社の三村が上記のタイトルで発表をしました。 動画やスライドへのリンクは RubyKaigi 2016 Schedule Sep. 9 で観ることができるので、是非観てください。 大学で学習したようなアルゴリズムやどこかで読んだコードというのは思いがけないところで自分の礎になっている、というのはそのとおりですね。改めてコードの多読やアンテナを張っていろんなものを知っておくのは大事だな、と思いました。

セッションについて

今回の RubyKaigi では、今後の Ruby に対する Proposal や、Ruby に加わった変更について興味深く聴きました。

Matzの講演 では、実際のところどうなるかはわかりませんが、速度についての話はなくなって、より静的解析ツール寄りになってきたのかな?という印象をもちました。笹田さんの Guild の提案 も取り込まれると、スレッドを使うときに意識しなければならないことが減ってよさそうですね。

Fixnum と Bignum が Integer に統合された けれども一部を除き Ruby のレイヤーでは影響が少ないこと、upcase/downcaseの挙動が 2.4 で変わる ことについてはお仕事で遭遇しそうな情報なので、特に気になりました。

『Fearlessly Refactoring Legacy Ruby』で出てきた suture という gem がドキュメントがしっかりしていて、使うことになれば試してみたいなと思いました。でもそうならないようにテスト書いてメンテナンスするのが大事ですよね。

今回 mruby についての発表も多かったので、mruby 触ってみる詐欺になっている私はそろそろ mruby-cli を使って何か作ってみるタイミングなのかもしれない……

さいごに

RubyKaigi 2016 の運営に携わっていた皆様、発表者の皆様、興味深い話を聴ける場を提供してくださってありがとうございました。 また、ブースに立ち寄ってくださった皆様、お弁当ののし紙に興味をもってくださった皆様ありがとうございました。またお会いしましょう!