表参道.rb #27 ~ RubyKaigiのおさらい ~ で発表してきた

表参道勢でもないし、RubyKaigi にも参加していませんが、タダ飯に惹かれていろいろと思うところがあり参加&発表してきました。
発表内容は以下のような感じ。


内容はやきとりいさんが RubyKaigi で話されていた Pattern Matching in Ruby の話…ではなくて、そのライブラリの実装にあった disable_refinements.rb の解説という超マニアックな話を…。
まあ要約すると refinements まわりの話なんですけども。

いや、本当は参加するだけで発表とかする気はなかったんですけど、disable_refinements.rb があまりにも衝撃的すぎたのと、LT の募集が空いてたようなので勢いで。
そもそもスライドを作り始めたのは今朝で、更に今までまともにこういう勉強会のスライドをつくったことがなかったのでまず開発環境から整えるという…。
あと参加するかどうかを直前まで悩んでいたので参加を決めたのも夕方頃だったり

ちなみにスライドは Rabbit を使ってつくりました。
Rabbit、確かに手軽にスライドをつくることが出来てめっちゃ便利なんですが、レイアウトとかを凝り始めると細かい設定が出来なくていろいろとつらみが…。
まあテーマを自作すれば調整は出来そうなんですが、今回は時間がなくて…。
やっぱりスライドは余裕をもってつくらないとだめですねー(棒。

あと発表に関するレスポンスがなかったので感触が全くわからない…実際はこんなもんなんだろうけど…。

他人の発表を聞いて

本当はメモを取りながら聞こうと思っていたんだけど聞くのに集中していてほとんどメモがry。
どの発表も面白かったんですが、特に『Module Builder Pattern』に関する発表が気になりましたかね。
実際に Module Builder Pattenr で書かれたコードを見せてもらったのですが、もっと(メタプロ的な意味で)いい感じに出来そうな気がしたのでもっとコードを見てみたかったです。
動的に Module.new してメソッド定義して mixin するなんて Ruby だとふつーだよね…?
まあなんかこの辺りの技法?みたいなのは常日頃からやっているようなものだったので、それが『Pattern』としての定石があるのかどうかは気になるところ。
Ruby のスキルの振り方が完全に間違ってる気がする…。

所感

とにかく初発表ということで精神が死にまくってました。
あと当然参加者の方で顔見知りなんていなかったので勇気を出して数分間話をしていた(聞いていた)以外はほとんどぼっちでした。
一人で食う飯は美味いなあ。
某氏が来てくれなかったら完全に心が死んでた
とりあえず、1回経験したことでハードルはだいぶ下がったと思うので、(表参道.rb に限らず)参加できる機会があれば参加してみたい。
今度はいろんな人に話しかけてみたいと思いましたまる(小並感。 あ、あとこれは不満とか文句とかそういうんじゃ全然ないんだけど、電源がないのはかなり不安だった…。
代々受け継がれている MBA の電池持ちが結構怪しいのでこういうケースで弱いのがつらい…。

あ、あと書き忘れていましたが勉強会自体はすごく楽しかったですよ!
少しとはいえ、Rubyについていろいろと話すことも出来ましたし、他の方の考えとかも聞けたので貴重な体験だったと思います。
ご飯も美味しかった。
運営の方々ありがとうございました。

browser-sync を使ってお手軽にライブリロードする

今までライブリロードといえば gulp を使っていたんですが、 『動作確認用のサンプルコードとかでいちいち gulpfile.js を用意するのめんどいなー』 と思い、もっとお手軽にライブリロード出来ないか探していたんですが Browsersync というのがよさそうでした。

Browsersync のインストール

Browsersync は npm からインストールします。

# いちいちローカルに保存したくないのでグローバルにインストール
$ npm install browser-sync -g

これで browser-sync コマンドが使えるようになります。

Browsersync の使い方

$ browser-sync start --server --files "index.html"

これにより開発用のサーバが立ち上がり、ファイルの変更があったら自動的にブラウザがリロードされます。
監視するファイルは --files で指定することができ、

  • --files *.html

  • --files "*.html, *.css, *.js"

のように設定することも出来ます。

所感

とにかくローカルに余計なファイルを置く必要がないのでめっちゃ便利そう。

参照

Vue.js + UIkit でアイコンが2つ表示される場合の対処方法

Vue.js + UIkit でアイコンを表示させようとしたら2つ表示される事があったので対処方法を覚書。

再現する環境

  • Vue : 2.4.4
  • UIkit : 3.0.0 beta 30
  • jQuery : 3.2.1

再現コード

<!DOCTYPE html>
<html>
<head>
  <title>Welcome to Vue</title>
  <!-- UIkit CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.30/css/uikit.min.css" />

  <!-- jQuery is required -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

  <!-- UIkit JS -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.30/js/uikit.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.30/js/uikit-icons.min.js"></script>
  <!-- Vue.js -->
  <script src="https://unpkg.com/vue@2.4.4/dist/vue.js"></script>
</head>
<body>
  <!-- id="app" 内のアイコンが2回レンダリングされる -->
  <div id="app">
    <span uk-icon="icon: user"></span>
    <span uk-icon="icon: check"></span>
    <span uk-icon="icon: heart"></span>
  </div>
  <span uk-icon="icon: user"></span>
  <span uk-icon="icon: check"></span>
  <span uk-icon="icon: heart"></span>
  <script>
    new Vue({
      el: '#app',
    })
  </script>
</body>
</html>

https://jsfiddle.net/zp66kbrb/

こんな感じで Vue.js のコンポーネント内で uk-icon を使おうとするとアイコンが2つ表示されます。

対処方法

以下のようにして uk-icon の設定を v-bind で行うことで回避できます。

<span uk-icon="icon: user"></span>

<span :uk-icon="'icon: user'"></span>

修正コード

<!DOCTYPE html>
<html>
<head>
  <title>Welcome to Vue</title>
  <!-- UIkit CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.30/css/uikit.min.css" />

  <!-- jQuery is required -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

  <!-- UIkit JS -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.30/js/uikit.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.30/js/uikit-icons.min.js"></script>
  <!-- Vue.js -->
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
  <!-- id="app" 内のアイコンが2回レンダリングされる -->
  <div id="app">
    <span :uk-icon="'icon: user'"></span>
    <span :uk-icon="'icon: check'"></span>
    <span :uk-icon="'icon: heart'"></span>
  </div>
  <span uk-icon="icon: user"></span>
  <span uk-icon="icon: check"></span>
  <span uk-icon="icon: heart"></span>
  <script>
    new Vue({
      el: '#app',
    })
  </script>
</body>
</html>

https://jsfiddle.net/fwxbmgsh/

参照

Ruby で xlsx を編集する

Ruby で既存のエクセルファイルを編集したかったので試してみた。
Ruby でエクセルファイルを扱う手段(gem)はいくつかあるが今回は、

  • .xlsx ファイルの読み込み
  • .xlsx ファイルの書き込み
  • セルの結合

を行いたかったので RubyXL を使うことにした。

余談

最初は spreadsheet を使おうと思って試してみたが、これは .xls.xlsx ではない)しか読み書きが出来ないのでボツに。
次に .xlsx が読み込める roo を試してみたが、これも .xlsx の読み込みはできるけど、書き込みが出来ないのでやっぱりボツに。
また axlsx も使い勝手とかはよさそうだったが、これはファイルの読み込み自体が行えずに(ry。
このように Rubyge でエクセルを扱う gem はいくつか存在するが、全体的にかゆいところに手が届かない。
と、いうような問題を解決するためにエクセルを扱う gem の機能を共有した rrxcell というものが存在しており、これであれば .xlsx の読み書きを行うことが出来る。
しかし、残念ながら rrxcell では『セルの結合』を行うことが出来なかったのであえなく撃沈した。
そういうわけで、今回は全ての条件を満たす RubyXL を使うことにした。

インストール

$ gem install rubyXL

使い方

require "rubyXL"

src  = "test.xlsx"
dest = "output.xlsx"

# エクセルファイルの読み込み
book = RubyXL::Parser.parse(src)

# シートを取得
sheet = book[0]


# シートのセルを結合する

# 行3, 列 0 のセル位置
cell1 = [3, 0]
# 行3, 列 1 のセル位置
cell2 = [3, 1]

# cell1 と cell2 を結合する
sheet.merge_cells *cell1, *cell2
# 以下と等価
# sheet.merge_cells 3, 0, 3, 1

# ファイルの保存
book.write(dest)

所感

とりあえず、やりたかったことは出来たので満足。

参照

Electron で jQuery 使う場合の注意点

Electron で UIkit を使っていたらハマったので覚書。
問題点としては、

  • Electron で jQuery を使おうとするとエラーが出た
  • 上記の問題を解決した結果、gulp のライブリロードが動作しなくなった

という2つの不具合が発生した。
先に書いておくと手元では下記の対応で問題なく動作したが JavaScript にそこまで詳しくはないのでこれが最善かどうかは不明です。

動作環境

  • Electron v1.7.5
  • jQuery v3.2.1
  • UIkit v3.0.0-beta.30

Electron で UIkit3(jQuery) を使う場合の問題点

Electron で UIkit3 とそれに依存している jQuery を使おうとすると

Uncaught TypeError: Cannot use 'in' operator to search for 'default' in undefined

というようなエラーがでる。
これは Electron と jQuery が競合してるからぽいらしい?(このあたりはよくわかってない…。
とりあえず、間接的でも Electron で jQuery を使う場合は以下のような設定を追加することで対処することが出来る。

BrowserWindow の生成オプションを追加する

BrowserWindow を作る際に nodeIntegrationfalse に設定することで解決することが出来る。

const {BrowserWindow} = require('electron')
let win = new BrowserWindow({
  webPreferences: {
    // この設定を追加する
    nodeIntegration: false
  }
})
win.show()

HTML 側で jQuery を読み込む前に対処する

HTML 側で jQuery を読み込む前に以下のようなコードを挿入することで解決する事が出来る。

<head>
<script>
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
</script>
<script type="text/javascript" src="jquery.js"></script>
</head>

上記のどちらか一方で解決することができるので対処しやすい方を選択するとよいだろう。

gulp を使ったライブリロードを動作させる

上記の設定で Electron と jQuery が競合する問題は解決した。
しかし、今度は gulp を使ったライブリロードが動作しなくなってしまったので対処法を書いておく。
gulp を使ったライブリロードを行う場合、HTML 側で

<script>
require('electron-connect').client.create()
</script>

というようなスクリプトを追加する必要がある。
しかし、先ほど対処した方法では require(や exportmodule)が削除されてしまい、上記のスクリプトがエラーになってしまう。

スクリプトの定義順を変える

本来は import などを使うべきなのだろうが、手元だと上手く動作しなかったので定義順を変えることで対処した。

<head>
<script>
require('electron-connect').client.create()
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
</script>
<script type="text/javascript" src="jquery.js"></script>
</head>

これで動作するはずである。
ちなみに nodeIntegration を追加する方法だと対処できないので HTML 側で対処を行う必要がある。

参考

今更聞けない! Ruby の継承と mixin の概念を継承リストから学ぶ

Ruby を学ぶ上で継承と mixin の概念を理解することはとても重要である。
しかし、このあたりの仕組みを学ぼうとすると、includeprepend、特異クラスや特異メソッドなどという様々な機能を理解する必要がありとても複雑である。
そこで本記事は『継承リスト』という観点から継承を mixin を学んでみたいと思う。
また、この記事は Ruby 2.4 時点での仕様になる。

継承と mixin

まず、はじめに Ruby での継承と mixin の仕方をみてみよう。
Ruby では継承で既存のクラスを、mixin でモジュールをそれぞれクラスに取り込むことが出来る

継承

class Super
    def super_method
        :super_method
    end
end

# Super クラスの機能(メソッド)を Sub クラスで継承(取り込む)する
class Sub < Super
end

# Super クラスのメソッドが利用できる
p Sub.new.super_method
# => :super_method

mixin

module Mod
    def mod_method
        :mod_method
    end
end

class X
    # Mod モジュールの機能(メソッド)を X クラスで取り込む
    include Mod
end

# Mod モジュールのメソッドが利用できる
p X.new.mod_method
# => :mod_method

他にも #extend#prepend といったメソッドでモジュールを mixin することが出来る。
継承も mixin も『外部の機能をクラスに取り込む』という意味では共通だが、

  • 1つのクラスしか継承する事が出来ない(継承してるクラスを継承するのは可)
  • 複数のモジュールを mixin することが出来る

という点で少し異なる。
また、継承と mixin は同時に利用することも可能である。

class Super
    def super_method
        :super_method
    end
end

module Mod
    def mod_method
        :mod_method
    end
end

class X < Super
    include Mod
end

x = X.new
p x.super_method
# => :super_method

p x.mod_method
# => :mod_method

Module#ancestors

ここまで読んで『継承と mixin が複数あってややこしい…』と思うかもしれない。
そこでまずは継承関係を『継承リスト』という形で視覚化してみてみよう。
継承リストは Module#ancestors メソッドで取得する事が出来る。
Module#ancestors はクラスオブジェクトをレシーバとして呼び出すことができ『クラス、モジュールのスーパークラスとインクルードしているモジュール を優先順位順に配列に格納して返す』というメソッドである。

see:https://docs.ruby-lang.org/ja/latest/method/Module/i/ancestors.html

説明を聞いてもよくわからないと思うので実際に試してみよう。
例えば、Integer であれば

p Integer.ancestors
# => [Integer, Numeric, Comparable, Object, Kernel, BasicObject]

となり、String であれば

p String.ancestors
# => [String, Comparable, Object, Kernel, BasicObject]

となる。
これは単純に

BasicObject
↑
Kernel
↑
Object
↑
Comparable
↑
Numeric
↑
Integer

というような『継承関係にある』と考えれば理解しやすいだろう(厳密に『継承』と言ってしまうと正しくはないが…。
また継承リストには『レシーバのクラス』も含まれている。
ちなみにユーザが定義したクラスは暗黙的に Object を継承することになる。

class X
end

p X.ancestors
# => [X, Object, Kernel, BasicObject]

更に継承を行った場合は Object と定義したクラスの間に継承したクラスが挿入される。

class Super
end

class Sub < Super
end

p Sub.ancestors
# => [Sub, Super, Object, Kernel, BasicObject]

このように Module#ancestors を使うことでそのクラスの『継承リスト』を確認する事が出来る。

メソッドを呼び出す優先順位

ここで少し継承と mixin から離れて『メソッドの探査』について考えてみよう。
と、言っても実はそんなに難しくない。メソッドの探査は
『継承リストの先頭から走査して最初に定義されているクラスのメソッドを実行する』
というような形になる。

class Super
    def mami
        "mami"
    end

    def homu
        "homu"
    end
end

class Sub < Super
    # Super クラスのメソッドを上書きする
    def homu
        "homuhomu"
    end
end

# Super よりも Sub のほうが優先順位が高い
p Sub.ancestors
# => [Sub, Super, Object, Kernel, BasicObject]

sub = Sub.new

# Sub では定義されていないので次の Super のメソッドが呼び出される
p sub.mami
# => "mami"

# Sub で定義されているので Sub のメソッドが呼び出される
p sub.homu
# => "homuhomu"

# Sub でも Super でも定義されていないので、Kernel で定義されているメソッドを呼び出す
p sub.class
# => Sub

ここで重要なのが『継承リストがそのままメソッド探査の優先順位になる』という点である。
逆にいえば『メソッド探査は継承リストに依存してる』ともいえる。
このあたりは重要なので覚えておくとよい。
余談だが、Method#owner でメソッドが定義されているクラス/モジュールを確認する事が出来る。

sub = Sub.new
p sub.method(:mami).owner
# => Super

p sub.method(:homu).owner
# => Sub

p sub.method(:class).owner
# => Kernel

mixin したクラスの #acestors をみてみる

これまでの流れで、

  • 継承リストは Module#ancestors で取得する事が出来る
  • 継承リストには自身も含まれている
  • メソッド探査の優先順位は継承リストに依存する
  • 継承を行うと継承したクラスが継承リストに挿入される

と、いうことがわかったと思う。
では、#include でモジュールを mixin をするとどうなるか。
実際に試してみよう。

module Mod
end

class X
    include Mod
end

# Mod がいる!!
p X.ancestors
# => [X, Mod, Object, Kernel, BasicObject]

あれ、 mixin しても継承リストに追加されてる!?
そう、mixin も実は単に継承リストに追加されるだけなのだ。
ちなみに先程から継承リストに存在している Kernel も実はクラスではなくて『Object に mixin されているモジュール』なのである。

p Kernel.class
# => Module

継承と mixin を両方使ってみると

では、継承と mixin の両方を行うとどうなるのか試してみよう。

class Super
end

module Mod
end

class X < Super
    include Mod
end

p X.ancestors
# => [X, Mod, Super, Object, Kernel, BasicObject]

上のコードの実行結果を見てもらうと Mod モジュールとと Super クラスが継承リストに追加されていることがわかる。
また、継承リストから見てわかる通り、継承と mixin を行った場合は mixin のほうが前に挿入されていることがわかる。
これにより『mixin を行ったモジュールのメソッドを優先して』呼び出されることになる。

class Super
    def homu
        "Super#homu"
    end
end

module Mod
    def homu
        "Mod#homu"
    end
end

class X < Super
    include Mod
end

p X.new.homu
# = > "Mod#homu"

ちなみに複数のモジュールを mixin した場合は『あとから mixin したモジュール』の優先順位が高くなる。

module Mod1
end

module Mod2
end

class X
    include Mod1
    include Mod2
end

p X.ancestors
# = > [X, Mod2, Mod1, Object, Kernel, BasicObject]

このように継承も mixin も『継承リストに挿入されているだけ』に過ぎないのである。

prepend する

ところで Ruby 2.0 で Module#prepend というメソッドが追加された。
これは #include と同様にモジュールを mixin する機能であるが、『優先順位が mixin されたクラスよりも高い』という特性がある。

module Mod
    def homu
        "Mod#homu > #{super()}"
    end
end

class X
    prepend Mod

    def homu
        "X#homu"
    end
end

# X#homu ではなくて Mod#homu が呼ばれる
p X.new.homu
# "Mod#homu > X#homu"

このように #prepend したモジュールのメソッドが自身のクラスのメソッドよりも優先して呼ばれる。
では、これも継承リストを見てみよう。

module Include
end

module Prepend
end

class X
    include Include
    prepend Prepend
end

p X.ancestors
# => [Prepend, X, Include, Object, Kernel, BasicObject]

#include したモジュールと比較すると #prepend したモジュールが『自身のクラスよりも前に』継承リストに挿入されている事がわかる。
このようにして継承リストを確認することで『自身よりも #prepend したモジュールが優先して呼ばれる理由』も納得すると思う。
こうしてみると難しそうな #prepend も単純に『継承リストに挿入する位置が違うだけ』なのがわかると思う。
まとめると

  • #include はクラスよりも前に継承リストに挿入される
  • #prepend はクラスよりも後に継承リストに挿入される

という形になる。

singleton_class.ancestors をみている

ここまで来たら特異メソッドについても考えみよう。
Ruby ではクラスだけではなくて『インスタンスに対しても』メソッドを定義することができる。
また、そのメソッドはクラスで定義されているメソッドよりも優先して呼ばれる。

class X
    def homu
        "X#homu"
    end
end

x = X.new

def x.homu
    "x.homu"
end

p x.homu
# => "x.homu"

このように『インスタンスに対して定義されるメソッド』のことを『特異メソッド』と呼ぶ。
また、特異メソッドは『特異クラス』という特殊なクラスに定義される。
この特異クラスは『各オブジェクトが必ず1つ持っている』クラスになる。

class X
end

# X クラスの特異クラス
p X.singleton_class
# => #<Class:X>

x = X.new

# x インスタンスの特異クラス
p x.singleton_class
# => #<Class:#<X:0x00000001275e10>>

ちょっと複雑になってきたので特異メソッドと特異クラスはこのあたりにしてメソッド探査の話をしよう。
メソッド探査が継承リストに依存しているのはこれまで話したとおりである。
と、いうことで特異クラスの継承リストを確認してみよう。

class X
end

x = X.new

p x.singleton_class
# => #<Class:#<X:0x00000001828790>>
p x.singleton_class.ancestors
# => [#<Class:#<X:0x00000001828790>>, X, Object, Kernel, BasicObject]

上のコードを見てもらうと『特異クラスが自身の継承リストに追加されている』事がわかる。
なのでメソッド探査に依存する継承リストというのは正確にいえば『自身の特異クラスの継承リスト』ということになる。
これにより『クラスで定義されたメソッド』よりも『特異クラスで定義されたメソッド=特異メソッド』が優先されるのである。

#extend してみる

最後に #extend について説明しておく。
これも #include#prepend と同様にモジュールを mixin する機能であるが『特異クラスに mixin する』という点が他のメソッドと異なる。

module Mod
    def homu
        "Mod#homu"
    end
end

class X
end

x = X.new

# Mod のメソッドが x オブジェクトに定義される
x.extend Mod

p x.homu
# => "Mod#homu"

# 特異クラスの継承リストに extend したモジュールが追加されている
p x.singleton_class.ancestors
# => [#<Class:#<X:0x000000017a51b0>>, Mod, X, Object, Kernel, BasicObject]

このように #extend は『特異クラスの継承リスト』に追加される。
逆にいえば

x.extend Mod

x.singleton_class.include Mod

と同等ともいえるだろう。

おまけ:特異メソッドよりも優先するメソッドを定義する

おまけ。
特異クラスに対して #prepend することで特異メソッドよりも優先されるメソッドを定義することが出来る。

module Mod
    def homu
        "Mod#homu"
    end
end

class X
end

x = X.new
def x.homu
    "x.homu"
end
p x.homu
# => "x.homu"

p x.singleton_class.prepend Mod
p x.homu
# => "Mod#homu"

# 特異クラスよりも前に挿入されている事がわかる
p x.singleton_class.ancestors
# => [Mod, #<Class:#<X:0x00000001094dd0>>, X, Object, Kernel, BasicObject]

滅多にないと思うが一番優先したいメソッドを定義したい場合はこういう手段が考えられる。

まとめ

  • メソッド呼び出しの優先順位は継承リストに依存する
  • 継承リストは Module#ancestors で確認できる
  • 継承も mixin も継承リストにクラス/モジュールを挿入する機能に過ぎない
  • #include はクラスよりも前に継承リストに挿入される
  • #prepend はクラスよりも後に継承リストに挿入される
  • 特異メソッドは特異クラスに定義される
  • 特異クラスは継承リストの一番手間に挿入されるのでメソッド探査の優先順位が一番高い
  • メソッドの探査はレシーバの『特異クラスの』継承リストに依存する
  • #extend は特異クラスの継承リストに挿入される

このように継承や mixin は継承リストが中心となって機能していることがみてとれる。
メソッド探査のまとめは以下のようになる。

スーパークラス
↑
include したモジュール
↑
自身のクラス
↑
prepend したモジュール
↑
インスタンスが extend したモジュール
↑
インスタンスの特異クラス(特異メソッド)

Ruby では外部で定義されているメソッドを取り込む機能として継承や includeprepend など手段が分散されており複雑なので理解するのが難しい。
しかし、継承リスト(#ancestors)を参照することでなんとなく仕組みがわかったのではないだろうか。
もし、意図しないメソッドが呼び出される問題が発生した場合は継承リストを確認してみるとよいだろう。
ちなみに特異クラスでも #include#prepend を行うことができるので、継承リストがどうなるのか気になるのであれば手元で試してみるとよいだろう。

参照

LLVM 5.0.0 がリリース

LLVM 5.0.0 がリリースされました。
こちらからダウンロードすることが出来ます。
リリースノートは以下を参照してください。