2013年9月5日木曜日

関西Ruby会議05でのプレゼンの製作について

■概要


先日、関西Ruby会議05が開催されました。大変面白く為になる一日でした。実行委員 / スタッフの皆様、スピーカーの皆様お疲れ様です!

さて幸運にも、そのイベントで招待講演をさせて頂きました。公での40分・100人超という、私にとっては未体験ゾーンでのプレゼンです。

当日のプレゼンの出来は、自分ではわかりませんが、それなりに工夫をして作ったのは間違いないので、吐き出せるものは吐き出しておこうと思い、このエントリを書きはじめました。

少し長いですがお付き合い頂ければと思います。

ちなみに当日の発表資料はこちらです。合わせて御覧頂けたら幸いです。









■きっかけ


2013/05/12 12:15。その時私は生駒山麓公園という所で、子供とアスレチックをしておりました。



すると突然、実行委員の @agilekawabata さんからTwitterのDMが届き
「関西Ruby会議(08/31)にて講演どうですかー?」
ときたので
「お、イイですね!させて頂きます。」
と軽い気持ちで答えてしまいました。

その時、まさか招待講演という重責とはつゆ知らずでしたが、受けてしまったので何とかしようと考えました。
正気ですか?」と叫びたくはなりましたが。

■書籍


以前よりRuby会議やその他のカンファレンスでの素晴らしい発表を見て、プレゼンをするという事に興味を持った事もあり、社内では過去三年ぐらいプレゼン勉強会を主催しています。元々社内で「プレゼンテーションZENが面白い」といった所、賛同してくれる人がおり勉強会に発展しました。

読まれた方も多いと思いますが、上記の「プレゼンテーションZEN」という書籍が
(出来ているかどうかは別にして)私のプレゼンスタイルに与えた影響はかなり大きいです。



後もう一冊非常にオススメなのが、ナンシー・デュアルテの「ザ・プレゼンテーション」です。根底にある物は同じように感じますが、また異なった視点でプレゼンを語っており、本当に為になる一冊です。













いずれの本も「スライドの作り方/Power Point(keynote)の使い方」といったテクニックより、
  • 何故プレゼンをするのか?
  • 誰の為のプレゼンであるか?
  • 論理 vs パッション
  • ストーリーで語る事の重要さ
  • どのようにすれば印象に残るか? ...etc
といった部分が多く語られており、本当に素晴らしい書籍だと思います。未読の方は是非読んで欲しいな~と思います。

■製作


5月には講演が決まったのですが、その後色々ありまして、話のプロットを考え始めたのは、8月の上旬に早めの夏休みをとり家族で旅行した後です。

そこから通勤や休みの日に構成を作って行きました。この段階では全て頭の中で考え、ある程度まとまってからiphoneのメモにテキストを作成しました。

上記の書籍でも語られていますが、最初の段階からデジタルな世界で考えるのは、あまり良くないように感じます(雑音が多いですから!)。ゆっくりネットの無い状態で、心の声と向き合う時間を持つ事が大切だな~と今回痛感しました。

...

その後また色々あって、実際にスライドを作成し始めたのは、2013/08/23の夜からです。何と本番の一週間前です!かなりヒヤヒヤしました。

過去の経験から100枚前後のスライドが必要になるので汽車犬のように作成しました。









ただこれも過去の経験から、私のスタイルと(高くない)スライドの精度なら、話が決まっていればスライド製作はあまり時間がかからないというのもありました。
土日と断続的に作業を続け、日曜の夜にはα版が完成しました。

そこから昼休み、夜等にシャドープレゼンを繰り返し、しゃべりとスライドを馴染ましていきました。
この練習の過程で、120枚ぐらい作ったスライドから優先度の高いトピックを残し、最終的には93枚
のスライドになりました。

このシャドープレゼンの過程では、

  • トピックの取捨選択
  • スライドを俯瞰してのトピックの順番の決定
  • しゃべりと馴染まないスライドの、分割 / 統合 / 破棄の実施
を行ないα版が、β版ぐらいにはなりました。

発表直前まで、スライドは微調整しました。特に小さいフォントを大きくし、見やすくというのを心がけました。

■今回取り入れて良かった事


今回社内のような、良く知っている人向けのプレゼンでは無いので、レビューしてもらう事が重要だと思いました(ex. 面白くない冗談、内輪受けのネタに走っていないかとか)。

まずβ版の段階で、妻(非IT)にスライドを見てもらいながら話そうと思っている事を説明しました。
これは"かなり"効果がありました。

意味不明な写真や、気持ち悪い色使い、良く分からないトピック等、沢山の指摘をしてもらいました。また「以前聞いた話では、タイムゾーンの話と英語の話が印象的だった」というアドバイスをもらいコンテンツに含める事にしました。










また社内のプレゼン勉強会のメンバーには、実際のプレゼンをみてもらいました。
ここでも本当に重要な指摘をしてもらいました。意味不明な写真や語彙について、沢山のアドバイスをもらいました。こちらもスライド/構成/喋りの精度を上げていくのに効果がありました。

森田くん、寺島くん、酒井さん、本当にありがとう。

反省点としては、このレビューをもっと早く受けていれば、もっと改善できた所です。
スケジュール管理がなっていない自分に反省です。

■練習


前述のようなバタバタしたスケジュールでしたが、プレゼンの練習は、通しで何度も行ないました。
概ね5日間で、10回ぐらい通しはしたと思います。また最初の~10分を上手く乗り越えれば、後は
何とかなると思い、冒頭は少し重点的に練習しました。

私は、喋りに対して苦手意識があるので(特にアドリブ)、練習する事により、その不安を払拭する事ができると感じます。またとっさの問題発生時にも、練習により対処方法の平均点を向上できるとも思います。

滑舌を少しでも改善したり、言い難い言葉を他の言葉に置き換えたり、間を考えたり ...etc
スライド製作より、練習という過程の方がよっぽど大事だな~と今は感じます。

また練習すると「次のスライドの内容を少し話してからページ切り替え」が出来るようになります。
これは結構良いテクニックだと思います。

# もっと早く準備します。反省。

ちなみに本番のプレゼン時間は発表者用ツールによると37.5分でした。だいたい練習通りでの終了となりました。

■構成


プレゼン全体の構成は、もっと上手くできるようになりたいです。今回40分という尺を頂いたので、トピックのバランスや順番については、もっと工夫ができたはずと悔やんでいます。

ただ要所要所で考えた事もあるので書いておきます。

・冒頭の話方

プレゼンは、最初の10~20秒で聴衆の印象が決定するというのを聞いたことがありますが、残念ながら私は、最初から笑いを取るのは上手くありません。

だから危険な賭けをするよりは、普通の話題で初めて、少しずつ空気を温める作戦にしたいと考えました。

恐らく私のことは知らない人がほとんどなので、冒頭では、まず皆にとって共通の話題をチョイスし
(ruby言語/Ruby Kaigi)少しでも一体感を作りたいと考えました。また実行委員/スタッフの方々への謝意を伝えたかったので、これも話題としてチョイスし、拍手という体を動かす行為をしてもらう事により、少しでも空気をほぐそうと考えました。

・自己紹介










今回は自己紹介を冒頭に持ってくる構成はやめようと思いました。
40分間あるので、そこそこゆったりと時間が使える事もありますし、最初から「オレがオレが」という
構成にしてしまうと「誰の為のプレゼンか」を自分自身見失ってしまいそうな気がしたからです。

自己紹介に家族の写真を使うのは好きです(自分の中でのお約束にしてます)。人物像・状況を言葉で伝えるよりも、簡単に伝えられると考えています。

・Railsコントリビュータになるまでの話

ここは今でも構成上どうだったのか悩んでいる部分です。
というのも冒頭~Railsコントリビュータになるまでの尺が、何度練習しても10分強になるからです。
人によっては、ここまでで飽きてしまうのではと恐れました。

最初は15分かかる内容だったので少しスライドを捨てました。少し長いとも思いましたが、ストーリー仕立てという事もあり(編集しにくいので)そのままにしておきました。

・思い出のコミットの話

今回Ruby会議という事もあり、聴衆の層が分かりそうな気もしますが、中々分かりかねる部分もありました。

そん中で、私の活動の成功/失敗やコミュニティーという部分が、外から見た時に分かりにくいと思うのと、少しはテクい話しをしないと納得頂けない方もいるのではと思いましたw










ちなみに練習時の指摘を元に「rubyは知っているが、rails/githubについて名前は知っているが深くは知らない」という人に合わせ、語彙の変更をしました(うっかりプルリクとか言った気がしますが...)

・まとめ

好きなプレゼンなので、是非「マッド・カッツの30日間チャレンジ」の話を紹介したかったのと、自分自身の見解や、これからコミュニティーやってくる人への話をしたいと思いました。










少し説教臭い語り口だったと思うので「どうかな?」とも思いましたが、これぐらいの方が選んだ話の終わりとしては適当かなとも思い話の結末といたしました。









■最後に


自分なりには、持てる力を注ぐ事ができた講演ではありました。
次機会があれば、反省点を踏まえ、もっと上手くなりたいな~と感じました。

皆さん聞いて/読んで頂きありがとうございました。

2013年8月18日日曜日

ActiveRecord::Core.generated_feature_methods が何をしているか?

今日参加させて頂いたKobe Rubyist Meetup 1stで話題になった、
ActiveRecord::Core.generated_feature_methods の意味が良く分からない問題を追跡してみました。

■疑問


@sutetotanuki さんより下記のコードの意味が分からないとの質問がありました。
以前から私も良く分からないコードだな~と思っていたのですが、理由を良く知りませんでした。
 97       def generated_feature_methods
 98         @generated_feature_methods ||= begin
 99           mod = const_set(:GeneratedFeatureMethods, Module.new)
100           include mod
101           mod
102         end
103       end
moduleをその場で生成しincludeしているだけ。何の役に立つのか?

■調査した結果


generated_feature_methods を呼び出しているのは、少し上の initialize_generated_modules メソッド とassociations/builder系 と nested_attributes.rb です。

まず initialize_generated_modules メソッドは

activerecord/lib/active_record/core.rb より
 91       def initialize_generated_modules
 92         super
 93
 94         generated_feature_methods
 95       end
となっており、generated_feature_methods を呼び出す前に、さらに上位のメソッドを呼び出しており、 その中で@generated_attribute_methodsが生成されています。

activerecord/lib/active_record/attribute_methods.rb より
 62       def initialize_generated_modules # :nodoc:
 63         @generated_attribute_methods = Module.new { extend Mutex_m }
 64         @attribute_methods_generated = false
 65         include @generated_attribute_methods
 66       end
これにより generated_attribute_methods が返すModuleは、generated_feature_methodsが返すModuleより継承ツリー上、必ず上位になります。

一方でassociations/builder系 と nested_attributes.rbでは、アクセサを生成する為のmoduleとして利用されている事がわかります。

activerecord/lib/active_record/associations/builder/association.rb より
 85     def define_accessors(model)
 86       mixin = model.generated_feature_methods
 87       define_readers(mixin)
 88       define_writers(mixin)
 89     end
これにより属性用のメソッドは、アソシエーション用のメソッドより継承ツリーでは上位で生成される。言い換えるとアソシエーション用のメソッドが優先して呼び出されるようにしたいという意図が見えると思います。

実際、関係がありそうなコミット
を見てみると、そのようなコミットコメントが書かれています。
Instead of generating association methods directly in the model
class, they are generated in an anonymous module which
is then included in the model class. There is one such module
for each association. The only subtlety is that the
generated_attributes_methods module (from ActiveModel) must
be forced to be included before association methods are created
so that attribute methods will not shadow association methods.
 テストケースには、さらによく分かるコードが残っています。
333   def test_association_methods_override_attribute_methods_of_same_name
334     assert_equal(developers(:david), computers(:workstation).developer)
335     # this next line will fail if the attribute methods module is generated lazily
336     # after the association methods module is generated
337     assert_equal(developers(:david), computers(:workstation).developer)
338     assert_equal(developers(:david).id, computers(:workstation)[:developer])
339   end
なるほど!深い理由がある事が分かりましたw

2012年12月17日月曜日

Rails Girls Kyotoに参加させて頂きました。


@satomicchyさんに誘って頂き、2012/12/14日15日とRails Girls Kyotoにコーチとして参加させて頂きました。
とても良いイベントだったので、スポンサー、オーガナイザー、コーチ、スタッフの皆様、そして何より参加頂いた皆様に感謝感謝です。

当日の雰囲気はこちらから。信じられないぜ、これがITのイベントだなんて!
↓懇親会にてケーキを撮る風景
■コーチ業について
14日のインストールデーは、windowsチームを担当しました。
RailsInstallerを利用すれば、意外な程簡単にインストール作業は終了しました。

railsが少し古いのでアップデートしたい心を押し留めたのですが、良く考えたらmacとはバージョンが違うはずなので、アップデートしておいても良かったかもしれないなと思いました。

gem install rails --no-ri --no-rdoc
とコマンドラインで実行すればアップデートできますよ!とここに書いてみる。

15日は、参加者2~3人につき1人ぐらいという事もあり、かなり個別に対応できたのは○だと思いました。
また非常に向上心が高い方ばっかりだったので、教える方も熱が入りましたw.

web/ブラウザとかの説明は、個人的にはもう少し準備しておくべきだと感じました。かなりノープランに話をしてしまいました。

tryrubyはrubyをインタラクティブに勉強できる英語サイトなのですが、思いの他、皆さんどんどん進んで
いくのがかなり予想外でした。ちょいちょい脱線して周辺の話を説明できたのは○でした。
プログラム初心者のとある方に「putsの戻り値は?」と言う質問を受けたのが、かなり衝撃的な出来事でした。ちゃんと理解しようとする姿勢は、良いエンジニアに必要だと思うので(エンジニアの方では無いのですが)。

ローカルで作ったアプリを、PaaS(herokuかSqale)にデプロイし、インターネットに公開できると皆さん嬉し
そうにしていたのが印象的でした(^ ^)

■ライトニングトークスについて

15日のお昼にライトニングトークスさせて頂きました。機材の都合でトップバッター。緊張するがな。。
当日の資料はこちら

今回は「プログラム初心者がかなり来る」との事前情報を得ていたので、
言葉やテーマについて、事前に考えたつもりだったんですが、前日のイントールデーの雰囲気を見て、
これはまずいと感じました。
冒頭に、とあるネタを入れていた(かつキーとなるメッセージに繋がる)のですが、
どう考えてもウケるはずがない!と判断しました(妻からのアドバイスのおかげでもありますが)。

という訳で前日~当日に"武器"を変えたので、資料にタイトルが無かったりします。聴衆を意識して内容を変えるという心意気は、自分的には良かったのですが、その分しゃべりの練度は下がっちゃいました(^_^;)

ナンシーデュアルテのこの本を読んでから、「プレゼンテーションの主役は聴衆である」という事を意識しているのですが、中々実践できていない事を反省をしている所です。

2012年11月29日木曜日

sqliteを利用した日本語での全文検索実験


■概要

ひょんな事から、sqliteでの全文検索について調べました。
すると全文検索用に、fts3/4指定ができる事を発見したのですが、予想通り"スペース"での、単語区切りしかできないようです。
日本語を処理したかったので、自分でn-gramしたデータを登録してテストしてみました。
確かに高速に検索できます。

※ MeCabを利用したtokenizerとかはandroidでの利用を考え考察していません。

■テストデータ

郵便番号データを下記からダウンロードしました。
http://www.post.japanpost.jp/zipcode/dl/kogaki.html

全国版データを利用しました。
ちなみにcentos6.xではlhaコマンドを入手できなかったので、http://d.hatena.ne.jp/pcmaster/20100211/p3 を参考にインストールしました。

$ lha e ken_all.lzh
$ ruby gensql.rb ken_all.csv > ken_all.sql # 下記スクリプトで分割済みのフィールドを準備

■実験

$ sqlite3 a.sqlite
> create virtual table zip_codes using fts3
(
  code text
,  address text
,  search_field text
);

> begin;
> .read ken_all.sql
> commit;

> .timer ON;
> select code, address from zip_codes where address like ('%東成区%');
CPU Time: user 0.272959 sys 0.024997

> select count(*) from zip_codes where address like ('%東成区%');
CPU Time: user 0.262960 sys 0.032995

> select code, address from zip_codes where search_field match '東成 成区';
CPU Time: user 0.001000 sys 0.000000

> select count(*) from zip_codes where search_field match '東成 成区';
CPU Time: user 0.001000 sys 0.000000

なんと数値上は300倍近く高速化されましたw!
ちなみに検索文字列もn-gram分割する必要があります。

■(参考)データ加工用スクリプト

require 'csv'

# なんちゃって2-gram。英数字は特別扱いするとか正規化するとか工夫してください。
# javaで組めば幸せになるはず(?)
def gen_2_gram(org)
  result = []
  (org.length - 1).times { |i| result << org[i, 2] }
  result.join(" ")
end

# *.csvファイルを処理する。
ARGV.each do |file|
  CSV.foreach(file, "r:sjis") do |row|
    zip     = row[0]
    address = (row[6] + row[7] + row[8]).encode("utf-8")
    puts "insert into zip_codes (code, address, search_field) values (#{zip}, '#{address}', '#{gen_2_gram(address)}');"
  end
end

※"以下に掲載がない場合"とかは実験目的なので特に処理していません。

2012年9月30日日曜日

(初心者向け)SELECT文を考える時には最終アウトプットを強く意識する。


SELECT文を考える時、何となくjoinやgroup byとか使ってませんか?もしくはメインとなるテーブルを意識していますか?

SELECT文は非常に便利なので気づかないのですが裏で実行されている事は(非常に簡単に言うと)

 ・縦*横の二次元の表を付きあわせ、新たな縦*横の二次元の表を作成

しているだけなのです。


その際に、一行の粒度が変わる事に注意が必要です。多重度が大きい側にjoinすると、自ずと一行の粒度が変わってしまう(行が増えてしまう)為、初心者のうちは自分が何をしているのか分からなくなります。

例えば次のER図があり、


最終的に次のような表が欲しい場合、どういうSQLを書きますか?


私は概ね次のステップでSELECT文を考えます。

・最終アウトプットの一行の粒度を確認する。
  この場合注文明細の行数が最終アウトプットの行数と同じ

・その粒度にあったテーブルをメインの表にする。
  from 注文明細を起点に考える

・そこに枝葉のテーブルを加える。その時に行の粒度を変えない(膨らまさない)。
  from 注文明細
    inner join 注文 on 注文.注文番号 = 注文明細.注文番号
    inner join 顧客 on 顧客.顧客NO = 注文.顧客NO
    inner join 商品 on 商品.商品NO = 注文明細.商品NO

from 注文からはじめる人も多いと思いますが、注文明細をjoinした時に行が膨れるため、考えを単純化する目的で、こんな簡単な場合でも徹底して明細側をメインテーブルにします。

あまりにも意図せず膨らましてしまい、group by/distinctでお茶を濁そうとする人が多そうなので記事にしてみました。もちろん集計が入ってくるとこの限りでは無いのですが、「最終アウトプットの一行の粒度」を意識する事は非常に重要です。


2012年9月29日土曜日

Amazon EC2でOracle-XEを動かす時の注意点

先日諸般の事情で、Amazon EC2にOracle11gXEをインストールして利用する必要があったのですが、一つ困った事があったのでメモ。

■問題
インストールも簡単にでき重宝していたのですが、EC2インスタンスをStop -> Startすると外部から接続できなくなりました。

FWとかSecurity Groupとか確認したのですが問題を発見できず。
サーバにログインしておもむろにプロセスを確認すると、何と ”リスナー” が起動していないのです!

■原因&対処方法
EC2インスタンスをStop -> StartするとIPおよびホスト名が変わります。ところがOracle11gXEをインストールすると、listener.ora内にインストール時点のホスト名が埋まってしまいます。よって再起動後は前のホスト名に対してリスナーを起動しようとする為、上手くいかなります。

ホスト名を変更し、/etc/init.d/oracle-xe stop / startすると、めでたく起動できました!

■追記
↑な事をtwitterでぼやいていたら、Rails contributor仲間のyahondaさんより、「Host名を空にする」という方法を教えて頂きました。これだとインスタンス起動毎に変更しなくてよいので楽ちんです。

2012年6月24日日曜日

ActiveRecord自体のテストケースをOracleで動かす

■概要

ActiveRecord自体のテストケースは通常sqlite3/mysql/postgresqlで実行しますが、oracleで実行したくなり調べましたが少し手こずりました。手順を残しておきます。

■oracle-xeインストール準備

・swap領域が足りないので追加
# dd if=/dev/zero of=/swap.extended bs=1M count=1024
# mkswap /swap.extended
# swapon /swap.extended

# cat /proc/swaps # 確認
Filename                        Type            Size    Used    Priority
/dev/vda2                               partition       2096472 19024   -1
/swap.extended                          file            1048568 0       -2

# vim /etc/fstab # 再起動時のおまじない
...
/swap.extended          swap                    swap    defaults        0 0

■oracle関係インストール

・ここからoracle11g xeをダウンロード
  http://www.oracle.com/technetwork/database/enterprise-edition/overview/index.html

・oracle-xeインストール
# unzip oracle-xe-11.2.0-1.0.x86_64.rpm.zip
# cd Disk1
# rpm -ivh oracle-xe-11.2.0-1.0.x86_64.rpm
# /etc/init.d/oracle-xe configure
入力は、9080(8080は他の用途で利用している...) => 1521 => パスワード => y
# /etc/init.d/oracle-xe status
・oracle clientインストール
  http://www.oracle.com/technetwork/jp/topics/index-099943-ja.html
# rpm -ivh oracle-instantclient11.2-basic-11.2.0.2.0.x86_64.rpm
# rpm -ivh oracle-instantclient11.2-devel-11.2.0.2.0.x86_64.rpm
# rpm -ivh oracle-instantclient11.2-sqlplus-11.2.0.2.0.x86_64.rpm
■ユーザ設定
・環境変数を設定
$ vim ~/.bash_profile
export LD_LIBRARY_PATH=/usr/lib/oracle/11.2/client64/lib/:$LD_LIBRARY_PATH
export PATH=/usr/lib/oracle/11.2/client64/bin/:$PATH
export NLS_LANG=JAPANESE_JAPAN.AL32UTF8
$ source ~/.bash_profile
・テスト用ユーザ作成
$ sqlplus system/パスワード@localhost:1521/XE
SQL> create user arunit identified by arunit default tablespace USERS temporary tablespace TEMP;
SQL> create user arunit2 identified by arunit2 default tablespace USERS temporary tablespace TEMP;
SQL> grant connect,resource to arunit;
SQL> grant create session to arunit;
SQL> grant create synonym to arunit;
SQL> grant connect,resource to arunit2;
SQL> grant create session to arunit2;
SQL> grant create synonym to arunit2;

■gem準備

$ gem install ruby-oci8
$ git clone git://github.com/rsim/oracle-enhanced.git
$ git checkout -b rails4 origin/rails4
$ cd /home/kennyj/rails/activerecord
$ ORACLE_ENHANCED_PATH=/home/kennyj/oracle-enhanced bundle update

■テスト実行

$ ARUNIT_DB_NAME=localhost:1521/XE ARCONN=oracle \
  ORACLE_ENHANCED_PATH=/home/kennyj/oracle-enhanced \
  ruby -Itest:lib test/cases/schema_dumper_test.rb