トップ 追記

Route 477

過去の日記


2014-07-08

[ruby] Rails使いが静的サイト作るならmiddlemanがいいよ〜という話をした

最近middlemanを少し触っていたので、社内勉強会でその話をした。

middlemanとは

Ruby製の静的サイトジェネレータ。erbとかmarkdownとかsassとかを書くと、HTMLやCSSにコンパイルしてくれるやつ。
(競合はJekyllとかNanocとかだけどあまり詳しくないので省略)

RubyKaigi 2013のサイトでも使われていたりした。https://github.com/ruby-no-kai/rubykaigi2013

Railsとの親和性

普段Railsのビューを書いてると素のHTMLを書くのが辛くなるけど、middlemanならRailsの便利なあれこれがそのまま使えるので、Railsのビューを書くような感じで静的サイトが作れる。

ディレクトリ構成

middlemanのディレクトリ構成は分かりやすい。
まずsource/以下に.erb、.md、.cssなどを置く。
で、middleman buildコマンドを叩くと、source/以下がまるごとbuild/というディレクトリにコピーされる感じ。その際に.mdから.htmlへの変換や、レイアウトの適用が行われる。
build/以下が完成したサイト一式になるので、あとはrsync等でコピーしてやれば良いというわけ。

チュートリアル

オフィシャルな日本語ドキュメントがあるので、それを参照する。
http://middlemanapp.com/jp/basics/getting-started/

おおむね以下のような感じ。

$ gem install middleman
$ middleman init my_new_website
$ bundle exec middleman server
$ open http://localhost:4567/
$ bundle exec middleman build

プラグインとか

  • middleman-deploy
    • gh-pagesにデプロイするときに便利
  • middleman-blog
    • ブログっぽいものを生成するための公式拡張
    • 動的な部分はerbになってるのでカスタマイズしやすい
    • コメント欄は、TwitterとかDisqusにアウトソース
  • middleman-syntax
    • ソースコードの構文ハイライトを行う
  • font-awesome-middleman

CSSについて

一応プロジェクトテンプレートみたいな仕組みはあるんだけど、これはCSSフレームワークに何を選ぶかとかそういう話で、入れるだけで見た目が格好良くなるやつ(いわゆるテーマ機能)ではない。
http://directory.middlemanapp.com/#/templates/all
ので、Bootstrapが入るテンプレートを使うか、あるいはフリーのCSS素材サイト (http://templated.co/ とか) から一式ダウンロードしてlayout.erbに貼るのが良いかも。

今回はKubeというCSSフレームワークを使った。JSを使っていないシンプルなやつ。


2014-06-20

[haskell][scala] ScalaのEitherとHaskellのEitherはちょっと違うらしい

このへん見てたら、ScalaのEitherはHaskellのとちょっと違って、より柔軟なことができる(代わりに記述が少しだけ増える)らしいという話があって面白かった。
(※自分用のメモなので分かりにくいかもしれないです。)

お題

def index(id:String) = Action {
  Cache.get(id) match {
    case Some(id2) => {
      Cache.get(id2) match {
        case Some(result) => Ok(result)
        case None => NotFound
      }
    }
    case None => NotFound
  }
}

これをどうやって綺麗に書くか、という話。

でHaskellならEither aがモナドだからEitherとdoでこう書けるんだけどという話が出た

getCache :: String -> Maybe String
nanika id = do
  id2 <- getCacheOr id NotFound
  id3 <- getCacheOr id2 BadRequest
  result <- getCacheOr id3 NotFound
  return result
  where
    getCacheOr id nothing = fromMaybe (Left nothing) $ Right <$> getCache id

で、ScalaのEitherではできないのかというと、実は.rightというメソッドがあるんですね。

RightProjection

HaskellのEitherは「Leftはエラー情報を持ち、Rightは成功時のデータを持つ」という使い方を想定してるので、do記法を使った場合は「Rightなら計算を続け、Leftならそこで中止する」という定義になっている。
一方ScalaのEitherは、LeftもRightも等価なものとして扱えるようになっているみたい。そのため生のEitherはforに使えない。

その代わりRightProjection, LeftProjectionをとるとforで使えるようになる。

a :: Either[String, Int]という変数があったとき、a.rightとすると何が返ってくるか。最初はOption[Int]が返るのかな?と思ったのだけど、RightProjectionのインスタンスが返るらしい。これはもとのEitherをラップしたものだけど、flatMapが定義してあって、「Rightなら計算を続け、Leftならそこで中止する」という定義になっている。で、同様にa.leftとやるとLeftProjectionが返って、こっちのflatMapは「Leftなら計算を続け、Rightならそこで中止する」という定義になっていると。

ちなみに

scalazではEither自体もflatMapできるらしい。
http://togetter.com/li/287993?page=5
http://d.hatena.ne.jp/cooldaemon/20111017/1318862426


2014-06-18

[types] Structual Subtyping(構造的部分型)とRow Polymorphism(列多相)ってどう違うの

Well, subtyping and type inference just don't mix. Doing type inference with row polymorphic records is much easier.

結果的には同じだけど、型推論と構造的部分型を組み合わせるのは大変なので、単に自作言語にrecordを入れたいだけなら列多相を使った方が楽だよ、という感じか。


2014-06-17

[misc] 除湿機買った

除湿機を買った。http://d.hatena.ne.jp/maname/20110531 とかを見て前から欲しかったのだった。
湿度が下がるとだいぶ不快指数が下がって、今のところとても満足している。あと本当に目に見えて水が溜まるので楽しい。

Panasonic 除湿機・除湿乾燥機 デシカント方式除湿乾燥機 ホワイト エコナビ F-YZK60-W

方式について

除湿機には「コンプレッサー式」と「デシカント式(ゼオライト式)」があって、一般にコンプレッサー式の方が夏向きだと言われるんだけど、デシカント式の方が小さくて良いという話もあって、実際7kgくらいだと気軽にあちこち移動できて良い感じだった。今は出かけている間にタイマーかけて稼動させる使い方が主なので、室温が上がることに関しては特に気にしてない。暑い日はエアコン付けるし。


2014-06-16

[ruby][opal] Opalのバグを調査した

QiitaにOpalのTipsをいろいろ投稿したりした。
http://qiita.com/tags/opal
OpalはRubyistのためのaltjsで、Rubyっぽいコードを書くとJavaScriptに変換される。今後重要になるRuby処理系だと思う。

ところでOpalにはまだいろいろバグがあるので、そのへんに注意しつつコードを書く必要がある。
今日はmap(&:foo)がうまく動かないので、原因を調べてみた。

コード例

[[1,2],[3,4]].map(&:last)
#=> CRubyだと[2, 4]を返す
#=> OpalだとNoMethodError: undefined method `last' for 1

デモはこちら

調査1: to_proc

&:foo:foo.to_procの省略記法なので、Symbol#to_procの実装を調べてみる必要がありそう。
と思ったがopal/corelib/symbol.rbがないなぁ…ああそうか、OpalにはSymbolがなくて、:foo"foo"と同じなんだった。

opal/corelib/string.rbを見ると、to_procは以下のような定義になっている。

  def to_proc
    proc do |recv, *args|
      recv.send(self, *args)
    end
  end

つまり[[1,2],[3,4]].map(&:last)は、

[[1,2],[3,4]].map{|recv, *args|
  recv.send("last", *args)
}

というコードと等価なわけだ。

ところがこれは正しくない。ここではrecvに[1,2]が入ることを期待しているわけだけど、CRubyでeach{|a, *b|を試すと、以下のように分割されてしまう(Opalでも同様)。

irb(main):004:0> [[1,2],[3,4]].each{|a, *b|
irb(main):005:1*   p a: a, b: b
irb(main):006:1> }
{:a=>1, :b=>[2]}
{:a=>3, :b=>[4]}

調査2: CRubyでは?

to_procはCRubyではどのように実装されているのだろうか。githubのruby/rubyをcloneしたやつが手元にあるので、to_procの定義を探してみる。string.cにあるようだ
その上のsym_callというのがイテレータらしい。

static VALUE
sym_call(VALUE args, VALUE sym, int argc, VALUE *argv, VALUE passed_proc)
{
    VALUE obj;

    if (argc < 1) {
    rb_raise(rb_eArgError, "no receiver given");
    }
    obj = argv[0];
    return rb_funcall_with_block(obj, (ID)sym, argc - 1, argv + 1, passed_proc);
}

移植

とりあえずpassed_procを無視すると、Opalへの移植はこうなるかな。

  def to_proc
    proc do |*args|
      if args.length < 1
        raise ArgumentError, "no receiver given"
      end
      obj = args.shift
      obj.send(self, *args)
    end
  end

うん、動いてそう。

passed_procは、block.callにブロックを渡した場合に使われるんだろうな。こんな感じでいいのかな。

  def to_proc
    proc do |*args, &block|
      if args.length < 1
        raise ArgumentError, "no receiver given"
      end
      obj = args.shift
      obj.send(self, *args, &block)
    end
  end

実際の例はこう。そういえばどっかのバージョン(Ruby 1.9か2.0か)から、ブロックがブロックを取れるようになったんだっけ。

foo = proc{|a, &b|
  b.call(a)
}

foo.call(1){|x| puts "I got #{x}" }

調査3: Rubinius

そういえばRubiniusのto_procはRubyで書かれてそうだな。

https://github.com/rubinius/rubinius/blob/d17c1294e09ab9c70495a1278450c09e38ed01c6/kernel/common/symbol.rb#L108-L120

おお、ほぼ上と同じコードになってる。
コメントで、instance_evalのためにselfに名前を付けてると書いてある。たしかにそうか。

報告

ということで、https://github.com/opal/opal/pull/551 に報告しておいた。

Opalのテストをどこに追加すれば良いかわからなかったんだけど、 spec/filters/ 以下に、「このRubySpecはまだ通らないからスキップする」という一覧があって、通るようになったやつをそこから削除すれば良かったようだ。


過去の日記