フリーランスになって経験したトラブル
IT業界は新しいように見えても実際には汎用機の時代など含めてもう数十年の歴史があるわけですから、それなりに古株な人たちもたくさんいます。また他業種から流れてくる人も大勢いるので、決して特殊なタイプの人間の集まりではありません。したがって、どこの業界にもいるゴロツキや詐欺師、ヤクザや社畜、セールスガイはこの業界にもちゃんといるのです。
もっとも、貴方がよっぽどのエンタープライズ系の仕事をしているのでなければ、そんなにすごい大物な人に出会うことはありません。筆者のような零細の技術者であれば、巡り会うのはせいぜいケチな小悪党くらいのものです。こんなのに手こずるとは余っ程ダメ人間なんだなと笑われそうですが、これから同じような目に遭うかもしれない人たちのために、フリーランスになって出会った困った人たちについてまとめておこうと思います。ちなみに筆者は自分でコンテンツを立ち上げてそれで食べているわけではなく、受託案件で生活をまかなっているので、企業や企業の担当者との話に限定されます。
正直、恥をさらすようなものですが、それでも何かの教訓になればいいと思うので書いていきます。それにしても、トラブルのほとんどが「受注/発注処理があいまいである」ことが原因だったのがなんともいえません。
(1)受発注処理がきちんとしていない隙を狙う
営業と開発を同時にこなすのはなかなか大変なので、どちらかがおろそかになりがちです。通常、受託案件は見積りから営業の交渉があり、金額が決まって案件の発注、受注という流れになりますが、例えば知人やクライアントの紹介などで担当者同士で話がまとまり、正式な手続きなしで仕事が始まることもあります。そんな場合でも、見積りを出すこと無しに仕事が始まることは経験上一度もありませんでしたが、逆に受注と発注の処理は省略されるか後回しになることが結構ありました。
しかし、残念なことに、この手のケースは大抵後で問題が起きました。
例えば、ある開発案件では、新規案件のために200万の見積りを提出しました。B2Bのサイト構築で、内容はオーソドックスなウェブサイトなのですが、短期間での開発が求められるためRuby on Railsでの構築を提案したところ、特に問題ないとのご回答を頂き、何度か打ち合わせた後、スケジュールや素材が届いたので初期開発まで完了しました。正式な発注処理はなかったのですが、前に同じクライアントの別案件を請け負っていたこともあり、その流れで他とコンペになってもいなかったので、その辺りの処理は相手方にお任せ状態でした。開発は特に滞りなく進み、期日にも間に合ってめでたしめでたし、となるはずでした。
ところが、その後案件は二転三転します。サイト規模から事業計画まで、クライアント側で次々と変更が発生し、サイト自体がオープンしません。途中大きな変更が入り、結局、4ヶ月後になってやっと検収が完了したのですが、ほぼ同内容のものを二度構築するような目に遭いました。それでも、終わってしまえばまあよしとしましょう。残るは請求などの事務処理だけです。200万の案件ですから、以前会社に所属していた頃はなんでもないような金額ですが、フリーランスにとっては大金です。さらに、来年早々に出産を控える家庭にとってはありがたい話です。トータルで二ヶ月くらいは働いたので、報酬としては悪くありません。
しかし、この段になって急に呼び出しをくらいました。一次受けと筆者の間に入っていた業者が金額の調整がしたいというのです。なるほど、実はこの案件、最後になって要件の追加があり、その分については後日開発となっていました。しかし追加分については、当初の見積りの金額でカバーしても問題ない規模だったため、特に追加請求はしないことにしました。そのため、総額が支払われなくても追加分を除いた分でいったん清算する方が、追加分の検収を待ってまた支払いが延び延びになってしまうより余っ程ありがたいくらいだったのです。ところが、待ち合わせ場所で聞いたのは意外な話ばかりでした。開口一番、この案件の見積りは幾らだったのかと質問されました。見積りを提出したのはもう何ヶ月も前のことです。しかし出したことには変わりはないのですから、それを忘れたというのは幾らなんでも失礼です。何かあるぞと、今は手元にないので確認したいと返答したところ、その場で実際の稼働状況について聞き取り調査をされました。おおまかなところを答えたのですが、先方の担当者は渋い顔をしています。そして、おもむろに紙を取り出して計算を始めると、顔を上げて「これじゃ厳しいですね」と言い出しました。「あなたの日当が3万円として、総額にすると約70万円、でもこれだと予算を越えちゃうんだよね」「予算?予算って幾らなんですか?」「30万円」
驚きました。まず、なぜこの人がこちらの日当を勝手に決めて計算しているのかがわかりません。業界の相場というものがあるのかもしれませんが、それはそれ、こちらの見積りは見積りです。だいたいバイトじゃあるまいし、そんな日当で仕事をするつもりなど毛頭ありません。それから、なぜ今頃予算の話になるのでしょう。それに納品してから金額を調整するというのはいったいどういうことでしょうか。もう、わけがわかりません。業務の中にはサーバ構築など技術料をプログラム開発とは別にしたい項目も含まれるので、単純に日雇いの計算などされるいわれはありません。そもそも見積りと全く関係のない、しかも恐ろしいほど低い金額で話を進めようとする意図が理解できません。
「ちょっと待ってください、最初にこちらから提出した見積りがありますよね?その金額は200万円です。それでOKというお話だったので作業を進めたんですよ。そこに交渉の余地なんかありません。そもそもこの金額は案件の費用の総額の何パーセント分なんですか?」
「それは確認していない」
「だいたい、この金額の案件なら、最初から受注なんかしていませんよ。もちろん、信用していたからといって受発注をきちんとしなかったこちらにも非があります。なので、まずあなたのおっしゃる金額というのは、お客様が開発は何パーセント納品/検収済みで、それに対して総額の何パーセントをお支払いするとお考えなのか確認してください」
「確認しましょう」
「それもわからないということなんですね。これが総額という可能性もあるということですか?」
「確認しましょう」
「(絶望して)今後の開発については、正式な受発注手続き無しには一切進めません。それはご理解ください」
こんな不毛なやり取りだけで打ち合わせは終了しました。この規模の案件ですから、それなりの準備や他のクライアント様とのスケジュールの調整も必要になります。年間で受注する仕事の量もこの見積りをベースにしているわけですから、それが大きく計画と異なることになってしまいます。言い方は悪いですが、これは立派な詐欺です。ソースコードの引き上げも含めて現在いろいろ検討しているところです。
というわけで教訓。大人は信用なんかで仕事を進めてはいけません。営業職には基本中の基本でしょうが、開発の人は忘れがちなので書いておきます。受注、発注は双方でハンコを押した書類が揃って初めて完了です。それまでは開発に着手してはいけません。
(2)業務内容が全て記載されていないのにつけ込む
見積りが複雑になり、案件が当初の規模からころころと変わって何度も見積りを出し直すことになった経験はあるでしょうか。ここに問題が発生する隙が生じることがあります。何度目かのやり取りを経て「では、最後にAndroidとiPhoneのアプリ開発、両方の見積りをください」そんな依頼で提出した二通の見積りで開発が開始された案件で、開発終了後に請求を送ったところ、いや、片方の分しか発注していないと突き返されたことがありました。そういうつもりじゃなかった、とは担当者の弁ですが、意図がどうこうという問題ではありません。しかし、確かに見積書には「スマートフォン向けアプリ開発」と書かれているので、完全に間違っているとはいえませんでした。それはそれで、こちらのミスではあります。片方分の支払いは滞り無く、また担当者に謝罪はされましたが、それでもこちらとしては大損しただけなので非常にがっかりしました。
要件が完全に定まっていない場合、変更要求にどこまで応じられるかの案分も大事です。営業の人は仕事を取ることに必死ですから無茶な要件をねじ込むことも珍しくはありません。携帯サイトを開発していたつもりが、いつの間にかPCに対応し、スマートフォンに対応し、挙げ句の果てには店舗据え置き機器からFelicaで認証する機能まで実装せざるを得なくなった案件がありましたが、この手の話はザラにあります。いくつかの機能は別途費用を請求できましたが、他は営業に押し切られました。別の案件では管理画面の機能がいくつか後になって追加されてしまい、別途請求しようとしても「いや、みんな頑張ってるんですから」と妙な説教をされる始末。
さらに難しいのが、社会人は他人に親切をするのは決して正しいことではないという問題があります。何かトラブルを起こした人がいたとして、親切心から対応してあげるとします。例えば要件定義が下手で必要な機能が含まれていなかった場合、仕方がないので追加費用無しで開発してあげると、その人もきっと喜んでくれるでしょう。ところが、社会人というのは親切を親切で返していたら上司に怒られてしまいますので、こちらのミスで何か実装が抜けていたりなんかした場合には、途端に手のひらを返して責めてくるのが普通です。社畜たるもの「上司>>>>>>>>>>客>>>越えられない壁>>>>下請け」という序列を骨の髄にまでインプットしているのが当たり前なのですから。また、そもそもトラブルを起こす人というのは、それなりの理由(無能、怠惰、口に出すのもはばかられるほどの悲しい理由など)があるのですから、この次もまた何かやらかす可能性は高いはずです。今回はたまたまそれがこちらが親切にすれば収束するものであったとしても、この次はそうはいかないかもしれません。そのとき、この人が言い訳として問題をあなたのせいにしてしまうことはあり得ないでしょうか。下請け業者のせいにすることは社会人にとってアリを踏みつぶすくらいの良心の呵責しか感じられない行為だということを理解しておく必要があります。
(3)損失を弁済させようとする
企業間の取引であれば、基本契約やNDAなどは事務型の基本的な作業ですが、フリーランスのプログラマの場合、その手の作業はクライアント側で用意してもらうことも多いかと思います。または、そもそもそんなやり取りもなく開発して納品するケースもあるでしょう。
しかし、基本契約を結ばずに業務を請け負うと、トラブルが発生した場合に面倒なことになります。例えば、よくある契約にはソフトウェアのトラブルが原因で発生した損失については受注金額を補償の上限とする、みたいな条項が含まれています。これがないとどこまで責任を負うべきか不明確になり、裁判になると個人はどうしても弱いので負けることになります。また裁判にはならなくても、下請け業者というのは弱い立場なので無理な要求を飲まされることになってしまいます。クライアントとの間に別の業者が挟まっていたりすると余計にそうなることが多いでしょう。
というわけで、フリーランスの開発者には、ガチガチな契約か、損をしても知らんぷりする余裕のどちらかが求められます。
Popularity: 10% [?]
【Titanium Advent Calendar 2011:18日目】Titanium MobileでAndroid
この記事は、@astronaughtsさん企画の「Titanium Advent Calendar 2011」向けに書いています。
12月1日~25日まで毎日誰かがTitanium Mobileについての記事を書いていくというイベントです。
最近、よくいろんな方から「Androidの案件を受注してしまったけど、Titaniumで大丈夫だろうか」と相談を受けるので、そんな人たちを少しでも慰めるためにそれをテーマに書いてみたいと思います。
さて。AndroidでTitanium Mobileを使う場合の鉄則がいくつかあります。面倒くさがりな方のために先にまとめておきます。
【鉄則1】iOSを基準に仕様を作ってはいけません
【鉄則2】試験用にIS03とタブレットは必ず用意しましょう
【鉄則3】anyDensityでごまかせると思ったら大間違いです
それから、開発する上で外してはいけないポイントがいくつかあります。
【ポイント1】シングルコンテクストにしないとまずいことが起きる
【ポイント2】Javaは友達、怖くないよ
では、ざっとみていきましょう。
【鉄則1】iOSを基準に仕様を作ってはいけません
クロスプラットフォームの開発を楽にするのがウリのTitanium Mobileですが、iOSでは動作してもAndroidでは実装されていない機能がたくさんあるので、iOSのKitchenSinkを見ただけで仕様を決めると後で痛い目に遭います。考えてみれば当たり前の話で、これだけ大きく異なるiOSとAndroidの違いを抽象化しようとしても、どうしても漏れるものがあるのは火を見るより明らかです。漏れのある抽象化そのものですから。
最初に作ったAndroid用アプリ「AKB48大島優子 ボクの彼女」に取りかかったときには、リモートのムービーファイルを再生する機能がAndroidには実装されていないことを知りませんでした。Intentを起動して端末にインストールされた動画プレーヤーを起動して解決する方が断然楽なのでやり方はないか探しましたが、結局いったんChromeを起動させて、そこから動画プレーヤーを起動させる方法しかありません。でも、それだとムービーの再生が終わってバックボタンを押すと真っ白なブラウザの画面に戻ることになってしまいます。そんなのを作ってお金をもらってたら、詐欺ですよね。
そこで、リリースの際はTitanium Mobileのソースコードに手を入れて、直接動画プレーヤが起動するように変更して対応していたのですが、その後モジュールが書けることがわかったので、今ではMIMEタイプを渡すと適当なIntentが起動するこちらのモジュールを使って回避しています。とっても簡単なものなので、PDFビューワなどにも応用できると思います。
いずれにせよ、Android案件ではこんな苦労はしょっちゅうです。また、特に困るのがUI設計の問題です。例えばiOSでは上の画像みたいにナビゲーションバーが表示されて、その左側には何もしなくても戻るボタンがあり、また必要に応じて画面下部にタブを表示する、みたいな画面設計になるのが一般的なのですが、Androidだとナビゲーションバーというものは存在せず、タブも画面上部に並んでしまうので、どう転んでもiOSと同じものにはなりません。Android向けのアプリは、バックボタンやメニューボタンで遷移することを考慮する必要があります。なんといってもiOSと比較するとタッチセンサーの感度は微妙だし、小さな画面で正確にボタンをタッチするのは面倒くさいので、バックボタンを押しまくるユーザも多いのです。しかし、世の中というのは不思議なもので、何をどう勘違いしたのか、それでもUIを全てiOSと同じになるようにしろと要求する人が後を絶ちません。しかもその数は開発者の予想を遥かに越えてたくさんいることが最近の調査で判明しています。その手のプロジェクトは、iOSが用意してくれるボタンなどの機能をAndroid用にわざわざ画像でパーツを全部作って対応しようとするなど、無駄なコストがたくさんかかってしまっているので遠くからでもすぐにわかります。テストも倍以上になり、プログラムは複雑になり、それでいてiOSの偽物のような外観の、全く使用感の異なるパチモノが出来るだけなのですから、どうやっても結局誰も得をしません。WindowsとMacでもそうですが、UIを複数のOSで共通化しようというのはほとんどが無駄な努力だと思います。たいていのユーザはどちらかでしかそのプログラムを使いません。Microsoft Officeならいざ知らず、あなたのアプリをiOSとAndroidの両方で使うというユーザなんて、あなたが雇ったテスター以外は誰もいないでしょう。だから、もしあなたがiOSとAndroidで共通のUIのアプリを作ろうと頑張っているのなら、まあ多少はいいですが、そろそろいい加減に目を覚ましてください。
【鉄則2】試験用にIS03とタブレットは必ず用意しましょう
Androidマーケットにアクセスした端末に表示されるアプリは、その端末の画面サイズに適したものだけです。Titanium Mobileではデフォルトでたしかこんな感じで指定されていたかと思います。
<supports-screens
android:smallScreens="false"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="false"
/>
smallScreensに非対応にするとIDEOSみたいな端末ではそのアプリは利用できないことになります。シェアからすると別に構わないと思いますが、特殊な用途がある場合などは注意する必要があります。また、一般的な端末にも要注意なものがあります。携帯サイトの開発を経験された方なら誰もが開発者泣かせの癖のある端末をいくつか苦々しい思い出と共に心の中にしまってあるかと思いますが、スマートフォンでもその手のやつは根絶されたわけでは決してありません。代表的なのはauが只同然で配っていたためユーザ数の多いIS03です。こいつはなかなか癖のある端末で、例えばソフトキーの高さを除いた画面サイズを返してくれないのでレイアウトの調整に苦労します。またSnapdragon第一世代らしいハードウェアスペックの低さは想像以上です。
⇨ココ大事!
それから、Ti.UI.createAlertDialog()を使うとき、どうしてもダイアログが出ないことがあります。そんなときは、ダイアログの前にTi.UI.createWindow()してそのウィンドウにダイアログをaddしてみましょう。IS03のときだけに起きる現象です。内緒にしてぼくだけの競争優位にしようと思っていたのですが、口が軽いので白状します。
また、タブレット機の存在も非常に厄介です。画面を縦または横固定で使うアプリを作成すると、作る側のコストはとても低く抑えることができるのですが、普通のスマートフォンならそれだけで使い勝手に悪影響が生じることは滅多にありません。縦固定で使いにくいアプリなら大抵は横でも使いにくいからです。しかし、タブレットは重量があるので寝転がって使ったりする際に手で支えるのも大変なため、縦でも横でも好きなように変更できるのが望ましいです。ご存知の通り、これは開発者にとって地獄への道まっしぐらなので、せめて試験用端末だけでも用意しておきましょう。
【鉄則3】anyDensityでごまかせると思ったら大間違いです
iOSとAndroidの一番大きな違い(二番目は飲み屋でそこら辺の人に自慢できるかどうかの違いです)は複数の画面サイズに対応しなければいけないことです。以前はAndroid SDKのサイトに対応する画面サイズの一覧があったのですが、デバイスが増え続ける昨今、いつの間にかそんな資料は見かけなくなってしまいました。
一応、Android側でもanyDensityという仕組みを使ってこれに対応しようとしています。
「anyDensity = false というのは、そいういう対処をしていないので、コード側で設定しているピクセル値を解像度に応じて調整してください」ということです。 この場合、コード側で setWidth(10) とした場合と XMLで android:layout_width=”10dip”とした場合の大きさは端末の解像度によらず同じになります。
Titanium Mobileの場合、anyDensityはデフォルトでfalseです。つまりdipで指定したのと同じことになります。ところが、そもそも端末の縦横サイズがまちまちなので、複数のディスプレイで同じ画面構成にしようとすると大変なことになります。
この辺、資料を読んでもいまいちよくわからないんですよね。たぶん、書いてる人が悪いんだと思います。
例えば上の画像みたいなアプリで、赤く塗ったところをタップするとアクションが実行される、という機能を実現したい場合、その部分に透明なViewを置いてタッチイベントを検知する、みたいな作り方でなんとかなります。しかし、その範囲を指定するのに、端末の縦横サイズがバラバラではうまく指定することができません。
そこで、例えばこんな対応方法を考えてやってみたところ、結構うまくいきました。まず、背景となるviewにはbackgroundImageとして横幅が端末の横サイズに拡大/縮小されることを前提に、上下に余分な部分を残しておきます。次に、各パーツを以下の手順に従って配置していきます:
・画面中央の位置を取得する
・画面の横幅を10または100分割して1つの単位とする(例・1 andro_scale)
・各パーツのY座標は中央から何andro_scale上/下かで決める
・各パーツのX座標は左/右端から何andro_scale右/左かで決める
・各パーツのwidthやheightも何andro_scaleかで指定する
このやり方だと、どんなサイズの端末でも同じ距離の指定方法で動作しました。もちろん画面最上部や最下部に張りついたパーツなどは素直にtop = 0やbottom = 0とすればいいでしょう。
1 andro_scaleをどれくらいにするかで勝負が決まるところでもあります。あんまり大きいとタブレットで画面が大きく(解像度が低く)なった途端にズレが目立ったりします。
そういう意味では鉄則2の試験端末の用意は必須ですね。
【ポイント1】シングルコンテクストにしないとまずいことが起きる
最近ではAppcelerator社から出ているアプリのソースでも使われていることが話題になっていた、シングルコンテクストで記述する方法ですが、Androidだとおそらくこれをやらないととてもじゃありませんがうまくいかないと思います。例えば
・HTTPClientからPOSTリクエストが送信されない
・コールバック関数がたまにしか実行されない
といった症状にお悩みの方は、シングルコンテクストで記述しないと改善されません。その際、記述が冗長にならないようにいろいろと気をつける必要があるのですが、個人的にはsyrupという名前で公開されたこの手法がとても参考になりました。そして全部のアプリをCoffeeScriptで書き直しました。残念なことに、syrupという名前のCoffeeScript的Lispというのもgithubにあるので、名前については再考の余地がありますが、とても分かりやすいのでオススメです。これに、例えばデータベースの扱いやIO関連のユーティリティを用意しておけば便利です。いつも用意しているライブラリでは、例えばデータベースの扱いは下のようなモデルを用意するだけでテーブルがなければ作成、初期データの投入(あれば何もしない)まで勝手にやってくれます。
syrup.namespase 'MYAPP.Model', (exports)->
Model = syrup.use 'MYAPP.Model.Model'
class User extends Model
init:()->
this.property "id", "integer"
this.property "name", "text"
this.property "email", "text"
this.init_data.push {id:1, name:'ヒューバート・セルビー・ジュニア', email:'last_exit@to.brooklyn.org'}
this.init_data.push {id:2, name:'ダグラス・アダムズ', email:'adams_d@42.org'}
super
exports.User = new User('users')
簡単でしょ?これでusersテーブルが(まだ未作成の場合は)作成され、2行のデータがINSERTされます。
CoffeeScriptで作るノウハウがもっと溜まってきたらもっと公開して還元していこうと思います。
【ポイント2】Javaは友達、怖くないよ
ここまで頑張ったら普通はどんな残酷な悪魔でも許してくれそうなものですが、Androidの開発ではそうは問屋がビジネスしてくれません。実はこれまでJavaで何かを作ったことは一度も無い(一応、教養として一通りいじったことはある)のですが、できればこのまま一生避けて通りたいと思っていました。でも、Ti.UI.createToolbar()を指定しただけでこんなものを見せつけられるようでは余裕こいてばかりもいられません。
パンがないならお菓子を食べるのが真の王妃というものです。じゃなかった、ないならないで作ってしまうのが開発者というものです。そんなとき、ヒントになるのが、Titanium MobileのAndroid用モジュールのソースコードです。実はAndroidのカメラなどもモジュールとして作られているので、そこで使われているテクニックは自作モジュールでも利用できます。Intentを起動してコールバックを指定するとか、Intentを起動するだけとか、ごく普通に関数を実行するとかのサンプルはこちらをどうぞ。
そろそろ疲れてきたので仕事に戻りますが、他にもAndroidという二番手ならではのbkはたくさんあります。みなさんも、見つけた悪しき知恵は出来るだけウェブで公開して、同じ苦労を分かち合いましょう。そうすれば、プラットフォームとしてのTitanium Mobileの発展に貢献することができます。このプラットフォームが成長して機能が改善され、また良い人材が引き込まれてくるようになれば、この先もっといいことが待っているはずです。たぶん、きっと。
Popularity: 6% [?]
私は如何にして禁煙するのを止めてタバコを愛するようになったか
意味もなく事実と反対のタイトルにしました。
8月にタバコをやめました。たまに理由は?と聞かれますが、それは自転車を盗まれたことから始まります。年初に自宅に駐輪していた自転車を盗まれました。うちは玄関に人が近づくとライトが点灯する仕組みになっていて、自転車は玄関のドアの前にありました。上の子のところに友達が遊びに来ていて、送り出したときにはまだあったのですが、その20分後にタバコを吸いに外に出たところ、もう自転車はありませんでした。
そして、手にしたタバコを見つめ、いつものようにアスファルトに押し付けて火を消すと、最後の一本を吸い終えた私の中で何かが死に、同時に何かが生まれたような気がしたのでした。
とかいうことは全然なくて、普通に困ってひとしきり泣き言をほざいてから、盗難届を出してこの件は処理しました。しかし、これは人生の大きな転換点の前触れだったのです。
しばらく不便なまま生活していましたが、かみさんの実家に行ったとき、義父母に自転車を盗まれた話をしたら、義父が「じゃあこれをあげよう」と使っていなかった原チャリをくれました。古い型ですが、手入れがよかったのでちゃんと動きます。実は、長いこと生きてきて、これまでヤンキーっぽいからという理由で原チャリに乗ったことがほとんどありませんでした。たぶん、ほんのちょっとの距離を一回くらい、これも誤解の末に仕方なく乗っただけだったはずです。だいいち普通自動車運転免許だって30歳で取得したくらいですから、乗り物全般にほとんど興味がなく、二輪車なんてろくでもない嗜好品としか思えませんでした。でも、なんとなく話の流れで、これからはこの原チャリがぼくの通勤とかの足になってしまったのです。かみさんと近くのホームセンターに出かけてとりあえずヘルメットを買い、運転の仕方についてあれやこれやと教わりましたが、ここ埼玉から練馬の自宅まで、車でも小一時間かかります。でも原チャリを積むことも出来ず、ほぼ人生初の原チャリなのに、大通りを延々と走る羽目になってしまいました。
田舎道を原チャリで走るとたくさんの自殺願望の昆虫が目の中に次々と飛び込んできます。途中でゴーグルを買いました。大きな車に追い抜かれるときは死が具体的な姿で迫ってくる気がしました。アスファルトの路面がまるで1970年代のマンハッタンのように穴だらけなのを知りました。完全にビビりながらの走行でしたが、大通り沿いのコンビニで休憩していると、なんだか自分がいっぱしのライダーになったような気がしてきます。タバコをふかして、気分はまるでハーレーに乗るマルボロマンです。なんだか楽しいじゃないか。時速30キロ台でも、風を切って走っていることには変わりなく、片手をひねれば、もうどこにでも行けるんだという感覚は、魔法のような強さで人を引きつけます。
中年の危機は、男性にとっては、2つのかたちで現れるものなのだそうです。すなわち、若い女に走るか、バイクに乗りたがるか。
いつしか、通勤にもほぼ原チャリを使うようになりました。新宿、渋谷くらいなら原チャリで余裕ですが、五反田あたりまで行くと自分でも相当にいかれてると思います。山手通りの左端を原チャリでのろのろ走っていると、なんだかとても自由な感じがします。電車での通勤だと、ふと目に留まった場所で降りてみるなんてふざけたことが出来る余裕はないのですが、原チャリだったら真っすぐ帰るのも寄り道するのも気の向くまま、どのコースを通ってもいいわけです。たったこれだけのことが、ずいぶんと開放的な気分を演出してくれるとは知りませんでした。それに、原チャリって満タンまで給油しても400円くらいしかしないので、電車賃より安く済みます。交通費をもらえる立場にないので、それも好感触です。もちろん、読書ができるとか天気に左右されないといった電車ならではの特典はたくさんあるのですが、この気分だけは電車の運転手にだって味わえないものなのです。
そして、数ヶ月が経って、あることに気付きました。なんだよ、この俺様より気分良さそうに走る乗り物にのってる奴らがいるじゃねえか。そうです。原チャリは制限時速が30キロ、普段自動車に乗っているとあまりの遅さにすぐに追い抜くような邪魔くさい存在です。でも、渋滞のときは車の脇をすいすいすり抜けて小気味よく走る素敵な乗り物でもあります。しかし、この2つの特性を同時に昇華させて利点に変えたとんでもねえ奴らがいるではありませんか。そう、普通の、いわゆるバイクってやつです。むかつきました。あいつら制限時速も車と一緒だし、二段階右折を強要されることもなく、それでいて渋滞の車の間をすいすい走り抜けていやがる。
悔しくて眠れませんでした。寝ましたけど。それからは、まるで中学生のようにバイクに乗りたい、なんとかして乗りたい、免許買えないかなあ、とか、そんなことばかり考えるようになってしまいました。でも、ぼくが勝手に自営業になったばかりで、うちにそんな余裕はありません。ますます悔しさが募ります。このままでは手当たり次第にバイカーを襲う妖怪に化けてしまいそうです。「バイクがほしいんだけど」ある日、思い切ってかみさんに相談してみました。「お金どうすんのよ」「どうしようもない」確かに、どうしようもありません。ない袖でスイングできないのですが、スイングしなけりゃ意味はないのです。ああ。すると、かみさんがいいことを思いつきました。入る金に変化がないのであれば、使う分をどうにかすればいいのです。
以前、何かの予防接種に上の子を連れて行ったとき、診療所の部屋から部屋へとせわしなく動き回りながら、オリックスのなんとかというピッチャーと飲みに行ったとか、そういうとりとめの無い話を滔々としゃべっていた近所の内科医のオフィスで、チャンピックスを処方されたのが8月の終わり頃でした。考える間もなく始めたので、家にはまだ3カートンもタバコが残っています。
始めてみると、期待していたような禁断症状もなく、二週目から完全に禁煙し、3回目の診察で呼気一酸化炭素が普通の人と同じになりました。最後は薬(チャンピックス)を飲むのもさぼりがちになり、今は「吸うことは出来るし、仕方がなければ別に吸ってもいいけど、別に吸わなくてもいい」という気分のまま、全く吸わないで生活しています。でも、今さら禁煙席に座るのって恥ずかしくて、ときどき喫煙室に座ったまま一服もせずにいることがあるとか、ある程度の時間を過ごすのであればスターバックスは喫煙できないから入らないという習慣が抜けなかったりします。他人の喫煙は全く気になりません。いいにおいだなと思うくらいです。今さら受動喫煙で文句をいえるような立場でもないし、喫煙はいい気分になることだと知っているので反対もしません。むしろ喘息でもないのにタバコを吸ったことがないなんて、お受験エリートみたいな歪んだ子供時代を過ごしたんじゃないかと疑わしく思うくらいです。
さて、肝心のバイクですが、子供の頃にそういうのに全く憧れなかったので、どんなバイクがいいのかさっぱりわかりません。レースに出る人でもないのにゴテゴテくっつけてるのは嫌いだな、とか、その程度の判断しかできません。おまけに免許も取っていません。なので、結果的にはまだタバコをやめただけになっています。調べていて驚いたのが、大型免許って今はすぐに取得することが可能なんですね。といっても教習所でそういうのを受け付けているところは少ないですが。とにかく、まあ、その、こんなことを延々と書いているのはちょっとむしゃくしゃすることがあったからなので、そろそろやめますが、最後に、タバコやめると太るよ。これほんと。
Popularity: 3% [?]
jsshell
Firefoxを使っていたときはGreasemonkeyという便利なものがあって、これで役に立たないログイン時間制限のあるシステムなんかを使いやすく改造していたのだが、Chromeで似たようなのはないかと思ったらjsshellというのがあったので、これでWikipediaの迷惑な画像を見えないようにしてみた。
消したいのはこれ。
jsshellをインストールしたら、普通にjQueryでやりたいようにやってみよう。例えば
$('#centralNotice').css('display', 'none');
うまくいったら、保存して毎回自動的に実行されるようにしよう。
js.custom.edit('保存するスクリプトの名前');
これをタイプすると保存用のダイアログが表示されるので、その中にさっきうまくいったスクリプトを書いて、Greasemonkeyみたいに実行したいURLを指定して保存する。
Popularity: 2% [?]
Wikipediaの誘惑
よかった、同じこと考えてる人がいたらしい。
Wikipediaの寄付を募るバナーの人物画像がタイトルのすぐ上にくるのでギョッとして思わずスクリーンショットを撮ってしまった人は少なくないはずだ。
…仕事してきます。
Popularity: 1% [?]
はてな、レベル高いな
リファラ辿ってたら、はてなブックマークに辿り着いた。
ええと、「試しに使ってみる」と「今すぐインストールする」しか選択肢がないんですけど、これを使いたくない場合はどうすればいいの?
どうしようもないですね。まあ、他の操作は許さん!という潔さはいいと思います。
Popularity: 2% [?]
Titaniumでリジェクト・iCloud関連
Titanium Mobileで作ったアプリがリジェクトされたので記念に。
今回リジェクトされた理由は、iCloud対応でデータの格納場所が厳密に定められ、チェック内容が変更になったからです。これからのアプリは生成されたデータを格納する際には以下のルールに従う必要があります。
(1) アプリが再生成できないファイルは/Documentsに保存
例えば、メモ帳アプリでユーザが書いたメモなんかはこちらに保存しないといけません。iCloudで自動的にバックアップされます。
(2) 再生成可能なファイルは/Library/Cachesに
データベースのキャッシュも含む、とありますが、ダウンロードされたファイルなどもここに置くのが正しいようです。
(3) 一時的なファイルはちゃんと/tmpに
アプリ終了時に消すのをお忘れなく。
(4) バックアップされては困るデータは”do not back up”
オフラインでの利用などで使いたい、iCloudやiTunesにバックアップされては困るファイルにはこの属性を追加しましょう。
さて、Titanium Mobileですが、例えばsqliteを使った場合、データファイルは/Library/Application Support/database以下に保存されてしまいます。残念ながら、Appleの新しい規制ではこれはリジェクトの理由になります。
Ti.Database.install('Resouces以下に置いたdbファイル', 'my_app');
このようにinstallを使って設置したデータファイルは/Library/Application Support/database/my_app.sqlとして保存されます。
これをAppleの規制に適応されるためには、例えば
Ti.Database.install('Resouces以下に置いたdbファイル', '../../Caches/my_app');
とすることで、/Library/Caches/my_app.sqlとして保存することができるようになります。
また、ファイルを保存する際にTi.Filesystem.applicationDataDirectoryを使っていた場合は
var root_dir = Ti.Filesystem.applicationDataDirectory + '../Library/Caches/';
このように変更すると/Library/Cachesを指定することができます。
既存のアプリをアップデートする場合は、Ti.Database.installを上のようにしてあげて、アプリの起動時に古いデータファイルを新しい場所に移動してあげるとかしないといけませんね。途中、スペースを含むパスがありますのでこんな風に指定します。
var db_file = 'my_app.sql';
var old_file = Ti.Filesystem.applicationDataDirectory + '../Library/Application%20Support/database/' + db_file;
var new_file = Ti.Filesystem.applicationDataDirectory + '../Library/Caches/' + db_file;
var old = Ti.Filesystem.getFile(old_file);
if(old.exists()){
old.move(new_file);
}
Popularity: 6% [?]
Titanium Mobileでアプリがリジェクト?!
Appleのアプリ検証ツールのバグでTitanium Mobileで作成されたアプリがリジェクトされるようです。回避方法が発表されていたのでざっくり訳しておきました。
再度追記:Appleが審査用ツールを更新してこの問題はなくなったそうです。もう大丈夫。
追記:なんと、この方法ではうまくいかなかったとのこと。
Update: 2:42pm 11/13/11
Appleの審査でのスキャンを回避する修正がCIサーバ上の全てのリリースブランチでご利用頂けるようになりました。こちらのビルドでアプリを再度申請して頂ければ問題を解消することが出来るはずです。ただし、現在も私たちは今後アプリが「プライベートAPIの利用」と判定されることがないよう、申請済みのアプリでこの修正をテストしています。Update 6:11pm 11/12/11:
下記の方法では問題は解消されないことが判明しました。現在、Appleの審査で「プライベートAPI」の利用と誤判定されるのを回避できるかどうか、別の方法を内部テスト中です。ご不便をおかけしておりますが、この問題の解決を最優先として取り組んでおります。元の投稿:
登録したアプリがプライベートなAPIを利用しているというAppleからのフィードバックについてコミュニティから何件か報告がありましたが、まだ「layout」であるとしか確認されていません。TitaniumのアプリケーションはプライベートなAPIを利用してはいないので、私たちはこれはAppleのアプリ解析ツールに何か変更があり、それが間違ってTitaniumのアプリのプロパティを検知しているのではないかと考えています。Appleの解析ロジックにおけるこの不具合の回避方法ですが、私たちは実装ロジックから「layout」というキーワードの使用を避ける新しいビルドを用意しました。現在もこの修正が正しいものかどうかAppleのApp Storeで検証しているところです。この特別版ビルドのインストールして利用するには、下記の手順に従ってください。
今回のエラーはAppleの処理ロジックが原因でしたが、皆様がApp Storeへの登録作業を問題なく出来るようにするのが私たちの希望です。本件についてご質問、フィードバックなどございましたら、直接私(kwhinnery at appcelerator dot com)までご連絡頂ければ喜んでお手伝い致します。
というわけで、インストール方法はこちらの手順に従ってください。
Popularity: 3% [?]
CropImage
Android用モジュール「CropImage」を公開したまま忘れていました。
iOSのカメラには、撮影後やギャラリーから画像ファイルを選択した後から一部分だけを切り取る機能が用意されています。Ti.Media.showCameraのプロパティにallowEditingを追加するだけでこのAPIを呼び出すことができるのですが、Androidでは正常に動作しません。Android自体はこの機能をサポートしているのですが、どういうわけかOSのバージョンが上がるにつれ引っ込められてしまったようなのです。
とはいえ、今のところ普通に呼び出すことは出来るので、iOSと違ってソースが公開されており、非公開のAPI呼び出しについて制限のないAndroidなら使ってしまおう、というのがこのモジュールです。
注意事項としては、シングルコンテクストでしか動作しません。というのも、Androidのカメラはそうしないとマルチスレッドの扱いがまずいのでfireEventが正しく呼び出されず、撮影後に画像の切り取りを実行しようとしてもそもそもイベントが発火しないから、うんともすんとも動作しないことがあるのです。公開当初から更新してないREADMEには「app.jsで使わないと動かないよ」とか書いてありますね。気が向いたら直します。
使い方は以下の通りです。
function crop_image(e){
var cropImage = require('org.selfkleptomaniac.ti.cropimage');
cropImage.cropper({
success: function(elem){
var imageView = Ti.UI.createImageView({image:elem.media);
win.add(imageView);
},
error:function(e){alert('failed');},
cancel:function(e){alert('canceled');},
width:width,
height:height,
image:e.media
});
}
Ti.Media.showCamera({
success:function(e){
crop_image(e);
},
error:function(){alert('error');},
cancel:function(){alert('cancel');}
});
Popularity: 1% [?]
VMware FusionでBootcampは使うな
今までずっとVMWare FusionでBootcamp領域のWindowsを起動していたのだけれど、正直なところ遅すぎてあまり使い物にならなかった。そもそも、Windows用クライアントしか用意されていないYAMAHAのルータ経由のVPNを利用するなど特殊な用途でVMwareを起動して使っていたのだけれど、ただでさえ面倒くさいその手の作業が本当にストレスになるほどVMware FusionのWindowsは遅かった。
で、HDDが激安な昨今、MacbookProをSSDにするか悩んだ末に、メインマシンなので容量重視と1TBのHDDにしたのをきっかけに、ふと複数のWindowsの試験環境を作成してみたのだが、イメージから起動するWindowsの速さに驚いた!ぜんぜんストレスなく使える!Bootcamp領域のWindowsの遅さでよく調教されてしまったせいかもしれないが、それでもいいじゃないか!
というわけで、Bootcampに入れたWindowsの遅さで白髪が増えたという方は、一度普通にイメージでVMを作成してみるといいと思いますよ。
Popularity: 1% [?]

















