ラベル ruby の投稿を表示しています。 すべての投稿を表示
ラベル ruby の投稿を表示しています。 すべての投稿を表示

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日間チャレンジ」の話を紹介したかったのと、自分自身の見解や、これからコミュニティーやってくる人への話をしたいと思いました。










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









■最後に


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

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

2012年5月28日月曜日

ruby/rails勉強会54thで、ライトニングトークスさせて頂きました!

 2012/05/26に、Ruby関西様主催の「ruby/rails勉強会54th」で、ライトニングトークスをさせて頂きました。

スライドはこちら

発表の是非は皆様のご判断を仰ぐとして、今後の為に思ったことを記載しておきます。

■練習・リハーサルは重要


  • 前日に3時間程度練習しました。少ない!と言われるかもしれませんが、必要以上に緊張せずにはすみました。
  • リハーサルにて時間が長すぎる事が分かったので、結果9枚(1/4程度)スライドを捨てました。中心的なメッセージ以外のくだりは、思い切って外しました。

■前回某所でのLTでは、練習では5分丁度だったのに、実際には  3.5分程度になってしまった。


  • 練習をしてくるのは当然だが、使える機能は利用すべきと思った。
  • マルチモニタ機能で、プレゼン画面と発表者用をわける。
  • ちら見程度でも、時間の把握と次のスライドがわかるとよい。次のスライドが分かれば、先に話を進めてからスライドを切り替えるという、進め方を確実にする事が可能。
  • もっと時間をかけて練習すれば、不要なのは理解しているのですが現実との兼ね合いで妥協。
  • 音楽における”暗譜”と似てますね。

■反省点


  • 5分という制約の中で、この題材は適切だったのか少し反省。
  • 話題を詰め込みすぎて、喋りが慌ただしかったと思う。
  • リモコン?が無いので、画面切り替え時にPCを操作しなければいけなかった。結果目線を落とす必要があり、また体を自由に使って表現が難しかった。
  • スライドは自分のしゃべりを支える物と考えているのですが、いざslideshareにあげてみると、よく分からんだろうなと思いました。
  • 「プレゼンテーションzen」の影響うけてますよね?と感想を頂いた。全くその通りで指摘頂き嬉しい半面、さらに精進して影響を消化したいなと思いました。


2011年8月20日土曜日

javaの32bitと64bitの使い分けにはご注意を

最近では64bit OS環境が広く利用されている事と思います。
一般的に64bit環境での32bitコマンドの実行は問題が無いと思いますが、java関係で二つ程問題が発生したので記録しておきます。

■Tomcatが起動出来なくなった。

64bit => 32bit javaに変更した所、Tomcatが起動しなくなりました。調べてみるとTomcat本体の問題では無かったのですが、jsvcを利用して起動している為、問題が発生しているようです(TOMCAT_HOME/bin/startup.sh等では問題なく起動しました)。

一部推測混じりですが

・jsvcを利用するにはコンパイルが必要
・コンパイル時には、configureを実行
・configureのログをよく見てみると、linux自体を"x86_64"と判定
・よってjavaは64bit版を想定してコンパイルしてしまう(推測)

という理由で上手く行かなくなったのではと思われます。

結論:jsvcを利用する場合は、32/64bitをOSとあわせる必要があります。

■rjb(ruby java bridge)が動かなくなった。

こちらも64bit => 32bit javaに変更した所、動かなくなりました。ruby側からJava VMを作成できなくなるようです。

但し上記問題を解決しても、javaのマイナーバージョン違いで急に動かなくなる場合を確認しています(jdk6u22 => jdk6u23)。ログを見た限りではGC発生タイミングでseg faultしてしまうようです。

リリースノートを見ている限りでは、hotspot vmのバージョンが上がったから?等推測してますが根本原因は判明しませんでした。

結論:rjbを利用する場合は、32/64bitをOSとあわせる必要があります。またマイナーバージョン含め実績のあるjava/rubyのバージョン組み合わせ・設定を大事にする必要があります。

2011年8月13日土曜日

ruby1.9時代にrcovは使ってはいけない。simplecovを使おう!

■概要

rubyにおけるテスト網羅率の定番ツールといえばrcovですが、どうもテストの通っている箇所の色付けがおかしいのと網羅率に誤差があると感じてました。

よくよくgithubのrcovのページを見てみると、

NOTE: This fork does not work on Ruby 1.9.x. For coverage on Ruby 1.9 look at SimpleCov. Even if you get results on 1.9 they will probably be inaccurate. Ruby 1.9 has call detection built in for faster, more accurate results.

なんて書いてあります。という訳でSimpleCovを試してみました。

なおこちらで簡単にセットアップできるシェルスクリプト置いてます!

■説明

まずはいつものGemfileに

gem 'simplecov', :require => false

と記載して

$ bundle install

次にspec/spec_helper.rbに下記コードを追加しましょう。
この時大事なのは実行の早い段階(railsが読み込まれる前!)で下記を実行する必要がある事です。

require 'simplecov'
SimpleCov.start 'rails'



$ rake spec

すると、coverageディレクトリ以下にhtmlファイルが出力されます(画像はsimplecovのページより)


このままでも良いのですが、rcovのフォーマットと異なるので、CIに統合するには不便です。そんな時はsimplecov_rcovを利用すると上手く行きます。

group :test do
  gem 'simplecov', :require => false
  gem 'simplecov-rcov', :require => false
end

$ bundle install

spec/spec_helper.rbには、SimpleCovの出力フォーマットを変更するコードを書きます。

require 'simplecov'
require 'simplecov-rcov'
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter

※くれぐれも早い段階で読み込んで下さい!

すると見慣れたあのHTMLがcoverage/rcov以下に出力されます。
coverage/rcovをCI(というかjenkins)のrcovレポートの場所として指定してあげると良好な結果が得られます!

ちなみにsimplecov-rcovのページで下記の様な記載があり、網羅率測定有無を切り替える方法があります。
if( ENV['COVERAGE'] == 'on' )
  require 'simplecov'
  require 'simplecov-rcov'
  SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
  ...
end

$ COVERAGE=on rake spec
また複数の出力フォーマットで出力する例もありますので是非見てみて下さい。

2011年6月21日火曜日

俺gem java_binをバージョンアップした

■概要

拙作gem、java_bin を約1年ぶりに更新しました!
















ダウンロードは http://rubygems.org/gems/java_bin からどうぞ!
...て何者か良く分からないので少し説明します。

■java_binとは

全文検索サーバの有名なプロダクトの一つに、Apache Solr があります。内部ではLuceneライブラリ(ツイッター内部でも利用されている)を利用しており、一説では white house のサイトでも利用されているようです。

アプリケーション側からSolrへの通信は、httpプロトコルで行いますが、その際のレスポンスフォーマットを、複数の中から選択する事ができます(wtパラメータ)。

通常rubyから利用する場合は、wt=ruby もしくは wt=jsonを利用する事になりますが、wt=javabin を利用する事により、バイナリフォーマットを指定する事ができます。しかしrubyでは解釈する事が出来ない為、C言語拡張を利用しparserを作成しました。







バイナリフォーマットを利用する事により、下記の点が期待できます。

・parseコストの削減
・通信量の削減
・型に応じたインスタンス化

実際parseコストに関しては、3~6倍程度の高速化が実現できています。

■今回何が新しくなったのか

0.4.0では下記の点で新しくなっています。

・solr 3.1以降のjavabin format2に対応
・RailsInstallerでのコンパイルサポート

0.3.5を一年近くプロダクションで利用してきましたが、無問題でした!
rubyからsolrを利用する際に、高速化したい!というニッチなお悩みをお持ちの方は是非 ^o^

2011年6月16日木曜日

gemやmavenで気をつけるべき事

gem(ruby)や、maven(java)を利用し始めると、

「何て便利なんだ!」

と感じると思います。実際非常に便利です。

しかし何年にも渡って運用するようなシステムの場合、「ハード故障に伴うリプレース」や「インフラ移行」が発生する場合があります。
その際に絶対忘れてはいけないのは、

「ライブラリ提供側が、いつまでも場所を提供してくれると考えてはいけない」

という事です。

実際gemで起こった事があるのは、

・githubが提供場所では無くなった(gemcutterへ移行?)
・gems.rubyonrails.orgが提供場所では無くなった(gemcutterへ移行?)
・sqlserverのアダプターの特定のバージョン(1.0.0.9250)が取得できなくなった。
  現在は http://rubygems.org/gems/activerecord-sqlserver-adapter/ ですが欲しいバージョンがありません。

と、かなり慌ててしまう状況です。

# さらに性質が悪いのがmaven。
# 私が出くわしたのは「リポジトリはあるがライブラリの該当バージョンが提供されなく無くなった」状況。
# なんと「*.jarの中身をテキストエディタで見ると、503のHTML」。
# しかもエラーを出力しないという難しい状況になります(どのファイルが無くなったのか分からない)

よってgemの場合cacheディレクトリ以下のgemファイルはちゃんととっておきましょう!」というのが教訓になります。もちろんアプリ側ではversionを絶対指定しておく」というのも大事ですね。

もちろんrailsの場合、vendor配下にコミットしたら良いのでは?という意見もあるかもしれませんが、度々デプロイする場合、純粋にサイズが大きすぎて遅い場合があります。
コンテキストに合わせて最良の方法を考えましょう。

2010年12月28日火曜日

rails2.3.5のマルチパート解析ではまった

概要
rails2.3.5、正確にはrack1.0.1のマルチパート解析処理に問題があり、データの欠落する可能性があります。

問題
「チェックボックスがおかしな状態になる」という問い合わせがあり調査開始しました。ほどなく"ごくまれ"にチェックボックスの入力状態が正しく反映されないケースがある事に気付きました。

切り分けの為、問題箇所を確認した所
  • パケットダンパーでデータを確認すると、socket通信上は想定値が確認できた。
  • 例外メール上の記録から、railsでのparamsには、おかしな値が入っている。
  • 上記メールから、rackより引き継いだ段階でも、おかしな値が入っている。
という事が判明しました。これで犯人は、apache か passenger か rack に絞られました。

そこで、Rack::Utils::Multipart.parse_multipartメソッドにログを仕込んで確認した所
  • content_lengthは想定どおりの値が確認できた。
  • io上入ってきた内容も想定どおり確認できた。
  • 処理ループが想定よりもかなり早い段階で抜けている。但し例外は発生していない。
と言う事がわかりました。
上手くいかない場合は、変数の動きをみていると、buf変数に一度読み込まれるだけで、後続のデータが処理されていません。

解決
buf変数に読み込んだデータを逐次処理していきますが、バウンダリ+EOL(\r\n)で丁度処理が終了した場合、buf.empty?が真となり、bufに読み込まれていないデータがあっても処理終了になっているようです。よって上記をrack1.1.xで修正されているように

break if (buf.empty? && $1 != EOL) || content_length == -1

とする必要があるようです($1 != EOLを追加)。
しかしrack1.1とrails2.3.5は相性が悪いみたいなので、モンキーパッチで対処する必要がありますね。

下記のような内容をconfig/initializers/rack_patch.rbとかで配置して下さい。

module Rack
  module Utils
    module Multipart
      def self.parse_multipart(env)
        unless env['CONTENT_TYPE'] =~
            %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
          nil
        else
          boundary = "--#{$1}"

          params = {}
          buf = ""
          content_length = env['CONTENT_LENGTH'].to_i
          input = env['rack.input']
          input.rewind

          boundary_size = Utils.bytesize(boundary) + EOL.size
          bufsize = 16384

          content_length -= boundary_size

          read_buffer = ''

          status = input.read(boundary_size, read_buffer)
          raise EOFError, "bad content body"  unless status == boundary + EOL

          rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n

          loop {
            head = nil
            body = ''
            filename = content_type = name = nil

            until head && buf =~ rx
              if !head && i = buf.index(EOL+EOL)
                head = buf.slice!(0, i+2) # First \r\n
                buf.slice!(0, 2)          # Second \r\n

                filename = head[/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/ni, 1]
                content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
                name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]

                if content_type || filename
                  body = Tempfile.new("RackMultipart")
                  body.binmode  if body.respond_to?(:binmode)
                end

                next
              end

              # Save the read body part.
              if head && (boundary_size+4 < buf.size)
                body << buf.slice!(0, buf.size - (boundary_size+4))
              end

              c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
              raise EOFError, "bad content body"  if c.nil? || c.empty?
              buf << c
              content_length -= c.size
            end

            # Save the rest.
            if i = buf.index(rx)
              body << buf.slice!(0, i)
              buf.slice!(0, boundary_size+2)

              content_length = -1  if $1 == "--"
            end

            if filename == ""
              # filename is blank which means no file has been selected
              data = nil
            elsif filename
              body.rewind

              # Take the basename of the upload's original filename.
              # This handles the full Windows paths given by Internet Explorer
              # (and perhaps other broken user agents) without affecting
              # those which give the lone filename.
              filename =~ /^(?:.*[:\\\/])?(.*)/m
              filename = $1

              data = {:filename => filename, :type => content_type,
                      :name => name, :tempfile => body, :head => head}
            elsif !filename && content_type
              body.rewind
              
              # Generic multipart cases, not coming from a form
              data = {:type => content_type,
                      :name => name, :tempfile => body, :head => head}
            else
              data = body
            end

            Utils.normalize_params(params, name, data) unless data.nil?

            # break  if buf.empty? || content_length == -1
            break  if (buf.empty? && $1 != EOL) || content_length == -1
          }

          input.rewind

          params
        end
      end
    end
  end
end