2011-11-16
社内Scala勉強会その2
そういえば4月に退職してから何をしてたか全然書いてなかったですが、半年ほど自宅警備員を続けた後にフリーランスのエンジニアとして活動を始めています。今はとある会社に常駐しつつ Rails 3.1.1 を使って開発をしています。
ところで2週間ほど前から、ふとしたきっかけで社内のエンジニアの方々と週一ペースの Scala 勉強会を始めることになりました。教材は twitter が公開しているScala Schoolです。僕が先週やった分のまとめ担当になったので、まとめを書いていこうと思います!先週は Scala School - Basics continued を一通りみんなで眺めました。
バックナンバー
初回の勉強会の内容を @satococoa さんにまとめていただきました。
社内Scala勉強会始めました - 223 Software
apply methods
オブジェクトをメソッド的に使えるシンタックスシュガーを提供してくれる。後述するような、 Companion Object をファクトリとして使う時に利用するのが主な使われ方?
scala> class Bar { | def apply() = 0 | } defined class Bar scala> val bar = new Bar bar: Bar = Bar@47711479 scala> bar() res8: Int = 0
Objects
定義したクラスの一つのインスタンスを保持するものらしい。singleton instance のようなもの?
object Timer { var count = 0 def currentCount(): Long = { count += 1 count } }
実行するたびにインクリメントされたcountが返る。
scala> Timer.currentCount() res0: Long = 1 scala> Timer.currentCount() res1: Long = 2 scala> Timer.currentCount() res2: Long = 3
クラスとオブジェクトは同じ名前を持てて、その場合 "Companion Object" と呼ばれる。Companion Object は主に下記のような感じで定義されることが多いらしい。
class Bar(foo: String) object Bar { def apply(foo: String) = new Bar(foo) }
こうすると、Bar() で Barクラスのインスタンスが返る!べんり。
Functions are Objects
関数は trait らしい。例えば引数を一つ取る関数は "Function1" という名前の trait を mixin したインスタンス。"Function1" は apply() を定義していて、オブジェクトを関数っぽく呼べる。
scala> object addOne extends Function1[Int, Int] {
| def apply(m: Int): Int = m + 1
| }
defined module AddOne
scala> addOne(1)
res2: Int = 2
上記のコードは
def addOne(m: Int): Int = m + 1
と等価っていう認識で良いんだろうか。それとも「def キーワード使わなくても同じ事が出来るよ」ってこと?→コップ本読んだけど等価って言う認識でよさそう。
extends Function1[Int, Int] は extends (Int => Int) に省略可能らしいです。でもそもそも[Int, Int]が何なのか説明がないのでよくわかりません…Int を引数にとって Int を返すってことかな。
Packages
package は java っぽい感じ。object を使うと static function っぽく書ける。
package com.twitter.example
object colorHolder {
val BLUE = "Blue"
val RED = "Red"
}
上記のように定義すると、パッケージの外から下記のように呼び出せる。
println("the color is: " + com.twitter.example.colorHolder.BLUE)
Pattern Matching
Ruby で言う case when ですね。どうもcase whenとは違うらしい…!!今度調べます。
times match {
case 1 => "one"
case 2 => "two"
case _ => "some other number"
}
scala はパターンマッチング中に if を使うことで、複雑な条件をスマートに分岐できるっぽい。
times match {
case i if i == 1 => "one"
case i if i == 2 => "two"
case _ => "some other number"
}
パターンマッチング用の変数は自動で "i" になるみたい。上記のコードでは "i" を使っていますが、変数名に特に縛りは無いみたいです。x とか別の変数名にしても普通に動作しました。
上記の例だと if を使うメリットは特にないけど、もっと複雑な条件で恩恵を受けられそうな文法。
Matching on class members
Calculator というクラスのメンバを調べて場合分けするのを普通に書くと下記のようになる。
def calcType(calc: Calculator) = calc match {
case calc.brand == "hp" && calc.model == "20B" => "financial"
case calc.brand == "hp" && calc.model == "48G" => "scientific"
case calc.brand == "hp" && calc.model == "30B" => "business"
case _ => "unknown"
}
後述する case class というものを使うともっとスマートに書けるらしい。
case classes
case class で宣言して、メソッド呼び出しのような形で case class のインスタンスを作る。
scala> case class Calculator(brand: String, model: String)
defined class Calculator
scala> val hp20b = Calculator("hp", "20b")
hp20b: Calculator = Calculator(hp,20b)
case class は自動で同値判定用のメソッドと toString メソッドを持ってる。あと他の普通のクラスと同じようにメソッドを持たせることもできる。
case classes with pattern matching
case class を使ってパターンマッチングをしてみると下記のようになる。確かに見やすくなってる!
val hp20b = Calculator("hp", "20B")
val hp30b = Calculator("hp", "30B")
def calcType(calc: Calculator) = calc match {
case Calculator("hp", "20B") => "financial"
case Calculator("hp", "48G") => "scientific"
case Calculator("hp", "30B") => "business"
case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
}
case の一番下は下記のようにも書ける
case Calculator(_, _) => "Calculator of unknown type"
Calculatorクラスのインスタンス以外もマッチングさせたかったら下記のように書ける
case _ => "Calculator of unknown type"
マッチした値に別の名前を付けて使うことも出来る。下記はマッチした Caluculator インスタンスに c という名前を付けて、戻り値となる文字列中で使ってる。
case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)
Exceptions
例外の書き方はjavaと同じ。
try { remoteCalculatorService.add(1, 2) } catch { case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.") } finally { remoteCalculatorService.close() }
javaと違うのは、式として使うことも出来るということ。下記のような書き方はRubyでも時々使いますね。
val result: Int = try { remoteCalculatorService.add(1, 2) } catch { case e: ServerIsDownException => { log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.") 0 } } finally { remoteCalculatorService.close() }
雑感
Scala Schoolを使って勉強会を2回やってみて、この教材はおそらくScalaに詳しい人が近くに居る想定で作られているんだろうなと感じました。少なくともscalaを知らない初心者が一人でやるものじゃなさそう。勉強会ドリブンじゃなくて一人だったら続かなかっただろうなー。
なんとか最後までやりきって、簡単なwebアプリを作って公開できるくらい Scala をマスターできたらいいなと思います。
2011-10-18
Ruby1.9.3ではKernel#randが少し便利になる
5から9までの整数をランダムで返すような処理を書くとき、1.9.2までは
rand(5) + 5
もしくは
r = Random.new r.rand(5..9)
のようにする必要があります。でも1.9.3だと
rand(5..9)
のようにできるようになっています。少しのことですけど嬉しい改善点ですね。
参考
Be Prepared for Ruby 1.9.3 and 1.9.4: What’s New and What’s Different
2011-10-06
ローカルgemのrdocみるなら gem server より yard server --gems
復習用エントリ。そもそもローカルのgemのrdoc見る機会も少なくなってるというのもあり完全に忘れてた。gem server よりも yard server --gems で出力する rdoc の方が見やすいですね。
参考
2011-10-04
zsh のコマンド pushd と popd について
復習エントリ。zsh のコマンド pushd と popd について。
setopt auto_pushd
として cd -[TAB] で直近の cd 履歴が見れて移動できるのは知ってたけど popd は忘れてた。
cd 系コマンドは、上記エントリの up コマンドを時々思い出したように使っていただけ。popd をもっと積極的に活用して縦横無尽にディレクトリを渡り歩きたいので
alias pd=popd
としてみた。これで pd とすると一つ前のディレクトリに戻れる。今度は忘れないように習慣づけたい!
参考
2011-09-28
sprockets の README 意訳
Rails3.1 から同梱されるようになった sprockets の README の意訳です。
sstephenson/sprockets - GitHub
はじめに
Sprockets は assets ファイルのコンパイルと配信を行うための Ruby のライブラリです。JavaScript と CSS ファイルの依存関係を宣言でき、さらにプリプロセッサを使うことで CoffeeScript, Sass, SCSS, LESS 等の言語を assets の記述に利用出来ます。
インストール
いわゆる普通のやり方でインストール可能です。
gem install sprockets
Gemfileなら
gem 'sprockets', '~> 2.0'
Sprockets Environment
assets ファイルにアクセスして配信するなら、 Sprockets::Environment クラスのインスタンスが必要です。Rails 3.1 以降なら、YourApp::Application.assets でSprockets::Environment のインスタンスにアクセスできます。Rack ベースのアプリなら、config.ru の中でインスタンスを作りましょう。
Sprockets::Environment は assets ファイルの取得と配信、ロードパスの操作、プロセッサの登録用のメソッドを持っています。それはまた URL にマウントすることが出来て静的ファイルを配信する Rack アプリでもあります。
ロードパス
ここで言うロードパスとは、Sprockets が assets ファイルを探すときに使うディレクトリの順序づけられたリストのことです。
最も単純なケースでは、Sprockets のロードパスはアプリの assets ファイルを含む単一のディレクトリから構成されるでしょう。Sprockets の environment がマウントされたら、environment はこのディレクトリの assets をまるで public root にある静的ファイルのように配信します。
ロードパスによって複数ディレクトリ内のソースファイルを管理して、これらのディレクトリを単一の仮想ファイルシステムにすることができます。さらにアプリの外のディレクトリを指定することもできます。つまりこれは、Ruby のライブラリに同梱されている JavaScript, CSS, 画像を簡単にアプリにインポートすることが出来ると言うことを意味します。
ロードパスの操作方法
ロードパスにディレクトリを加えたい場合、 append_path と prepend_path メソッドが使えます。ロードパス内では最初のパスが後のパスよりも優先されます。通常、パスを追加するときにはデフォルトのパスの後に追加して、既存の assets を上書きしたいときのみデフォルトのパスの前に追加するようなやり方が一般的です。
environment = Sprockets::Environment.new environment.append_path 'app/assets/javascripts' environment.append_path 'lib/assets/javascripts' environment.append_path 'vendor/assets/jquery'
assets へのアクセス
environment にロードパスをセットしたら、Rackサーバのように environment をマウントして、HTTP経由で assets をリクエストすることが出来るようになります。また、アプリの中からassetsにアクセスすることも出来ます。
論理パス
Sprockets 中の assets は常に論理パスで参照されます。論理パスとは、ロードパス中に含まれるディレクトリからの相対パスのことを指します。例えば、ロードパスに app/assets/javascripts を含めていたとすると、下記のようになります。
| asset | 論理パス |
| app/assets/javascripts/application.js | application.js |
| app/assets/javascripts/models/project.js | models/project.js |
このような方法でロードパス中の全てのディレクトリがマージされて、論理パス用の仮想的なファイルシステムが作られます。
HTTP越しにassetsを配信する
environment をマウントすると、マウントポイント配下の論理パス越しに全ての assets にアクセスすることが出来ます。例えば、environment を /assets にマウントして /assets/application.js にリクエストを飛ばすと、Sprockets はロードパス中の application.js を探して配信します。
Rails 3.1 以降では、Sprockets の environment は自動で /assets にマウントされます。もし(Rails以外の) Rack アプリで Sprockets を使うなら、手動で environment をマウントする必要があります。config.ru 中で、map メソッドを使うのがよいでしょう。
require 'sprockets'map '/assets' do environment = Sprockets::Environment.new environment.append_path 'app/assets/javascripts' environment.append_path 'app/assets/stylesheets' run environment end map '/' do run YourRackApp end
assets にアクセスするには
find_asset メソッド([]がエイリアス)を使うことで、Sprocket の environment から asset を受け取ることが出来ます。論理パスを引数として渡すと、Sprockets::BundledAsset インスタンスを返します。
environment['application.js'] # => #<Sprockets::BundledAsset ...>
Sprockets::BundledAsset のインスタンスメソッド
- to_s
- asset の内容を返します
- length
- asset のバイト数を返します
- mtime
- asset の最終修正日時を返します
- pathname
- asset のフルパスを返します。
エンジンを使う
asset のソースは SCSS や CoffeeScript 等の他の言語で書くことが出来ます。その場合 Sprockets によって自動で CSS や JavaScript にコンパイルされます。これらの言語用のコンパイラはエンジンと呼ばれます。
使用されるエンジンは asset のファイル名に追加された拡張子で判断されます。例えば、SCSS で書かれた CSS ファイルは例えば layout.css.scss のようになるでしょう。一方 CoffeeScript で書かれた JavaScript ファイルは dialog.js.coffee のようになるでしょう。
Sass と SCSS を使う
Sass は CSS にネストや変数やmixinやセレクタの継承を加えた言語です。もし sass gem が使えるなら、CSS assets を 書くのに Sass が使えます。
Sprockets は Sass と SCSS 両方をサポートしています。Sass を使いたい場合は . css.sass、SCSS を使いたい場合は .css.scss のように拡張子を指定します。
LESS を使う
LESS コンパイラは JavaScript で書かれています。less gem は Ruby で書かれた V8 JavaScript ランタイムである therubyracer に依存しています。LESS を使いたい場合は .css.less のように拡張子を指定します。
CoffeeScript を使う
coffee-script gem が使えるなら、CoffeeScript を使うことが出来ます。CoffeeScript コンパイラは JavaScript で書かれているため、ExecJS がサポートしているランタイムが必要です。CoffeeScript が使いたい場合は .js.coffee のように拡張子を指定します。
EJS と Eco で JavaScript テンプレートを扱う
Sprockets は クライアントサイドでのレンダリングを扱うJavaScript テンプレートをサポートしています。JavaScript テンプレートは特別な拡張子 .jst をもち、JavaScript の関数にコンパイルされます。
JavaScript テンプレートをロードしたとき、グローバルの JST オブジェクトを通して、JavaScript テンプレートの関数にアクセスすることが出来ます。テンプレートの関数は、文字列でテンプレートを描画するためのものです。戻り値となる文字列は DOM に挿入できます。
<!-- templates/hello.jst.ejs --><div>Hello, <span><%= name %></span>!</div> // application.js //= require templates/hello $("#hello").html(JST["templates/hello"]({ name: "Sam" }));
Sprockets は二つの JavaScript テンプレート言語をサポートしています。JavaScript に組み込むための EJS、CoffeeScript に組み込むための Eco です。両方の言語とも<% … %> でテンプレート内に埋め込む事が出来ます。
もし ejs gem が使える状態なら、EJS テンプレートを使うことが出来ます。EJS を使いたい場合は.jst.ejsのように拡張子を指定します。
eco gem が使える状態なら、Eco テンプレートを使うことが出来ます。Eco を使いたい場合は .jst.eco のように拡張子を指定します。eco gem は CoffeeScript コンパイラに依存しているので、前述の CoffeeScript エンジンを使う際の注意点と同じ内容が適用されます。
ERB で Ruby を実行する
Sprockets は ERB エンジンを提供しています。.erb を CSS または JavaScript asset のファイル名の後に付けると、ERBエンジンを有効に出来ます。
注意:Sprockets が複数のエンジンを使う場合、拡張子の右側から順番に適用していきます。ERB を処理してから CoffeeScriptをコンパイルしたい場合は .js.coffee.erb のように拡張子を指定します。
assets は Sprockets::Context インスタンスのコンテキストで評価されます。
このあたりちょっと省略してます
文字列埋め込みのシンタックス
asset 中で Rubyにアクセスしたいけれど <%= %> が使えないときには、.str 拡張子を使うと #{ … } が使えます。
依存関係を解決する
各ソースファイルの頭で特別なコメント風シンタックスを使うことにより、asset ファイルを順序づけて結合した *asset bundles* を作ることが出来ます。
Sprockets は assets ファイル中のディレクティブと呼ばれるコメントを解析し、依存関係のグラフを作成します。もし依存関係のある asset ファイルをリクエストした場合、ファイルの頭で指定されている依存ファイルが include されます。
ディレクティブプロセッサ
Sprockets は CSS と JavaScript ソースファイルでディレクティブプロセッサを走らせます。ディレクティブプロセッサはファイル頭の "=" で始まるコメントを見ます。"=" の後の最初の単語がディレクティブ名になり、続く文字列は引数として取り扱われます。Sprockets はコードが始まったらディレクティブを探すことをやめます。
サポートしているコメントの種類
/* 複数行のコメント形式 (CSS, SCSS, JavaScript) *= require foo */ // 一行コメント形式 (SCSS, JavaScript) //= require foo # 一行コメント形式 (CoffeeScript) #= require foo
Sprockets のディレクティブ
下記のディレクティブが使えます。それぞれパスを引数としてとります。指定できるパスは論理パスまたは相対パスです。相対パスは "./" で始まり、現在のファイルの場所からの位置を指定します。
require ディレクティブ
require path
path に指定されたファイルを挿入します。同じファイルが複数回 require されても有効なのは一回だけです。
include ディレクティブ
include path
require と似た働きをしますが、複数回 include されたときに、複数回内容が挿入される部分が異なります。
require_directory ディレクティブ
require_directory path
ディレクトリ中のファイルをアルファベット順で全部 require します
require_tree ディレクティブ
require_tree path
require_directory と似た働きをしますが、path から再帰的にディレクトリを辿って require していく部分が異なります。
require_self ディレクティブ
require_self
require_self で指定した後の require や include の前に現在のファイルを挿入するように Sprockets に伝えます。
depend_on ディレクティブ
depend_on path
bundle 中に含まない path に依存していることを宣言します。依存しているファイルが変更されたときにキャッシュを expire したいときに便利です。



