2010年6月27日日曜日

Windows で SSID 取得

 といっても、ググってこちらをまんま実行してみただけ…。うーむ・・・この時間帯だと取得できるSSIDは、自分のとこの無線LANだけ…。今度、引越しするか無線LANを持ち運んだときに、どのような結果がでるのか追試してみたくなってきた。こういう時車が無いの痛いなぁー。いつになるかわかんねぇけど、嫁さんの実家に無線LAN運んでみよっと…。GPSユニットって、今時なら安いだろうに…、なんでiPadの3G版にしか付いてないんじゃ???質問したら「人は1日に10時間も本を読まない」と同様に「Wifiだけの人は、iPadを持ち運んだりしない」って、ぶちきれられるのか?

iPad ケース

 自分のライフスタイルだと、低反発3Dメッシュケース(青(黒)が、ベストだと思ってチョイスしました。生のまんま使うので十分だけど、持ち運びには気をつけたい…。さすがに自転車のリアケースで運ぶ勇気は無いですが…。

Google map の位置検索

嫁さんの実家からだと、無線LANのSSIDから、現在位置が算出できない。という事は、ストリートカーで、収集したSSIDから位置情報を割り出しているのかもしれない。便利さとプライバシーはトレードオフの関係にあり、今後は、こういう問題に対して考慮する必要がありそうだ。さっき気がついたけど、iPadだと、ストリートビューが見れない。メモリが少ないので、大量の画像を扱うと、重くて使い物にならないのかも…

WZR2-G300NとiPad

いやー、ひでーな。無線LANの接続は出来るけど、しばらくすると切断されて、ネットに繋がらなくなる。不安定なんで、Biosのバージョンを上げてみたけど、改善されない。AOSSは使うな?ってこと?
嫁さんの実家の環境だけど、しょうがないから接続を2つ作成して、繋がらなくなったら、交互に切り替えてます。DHCPの更新では解決しない。g/nは、もうドラフトじゃなくなったはずだ。へたれすぎだろ。

楽天は?

今ごろ、楽天の英語公用語について、憶測してみるテスト。何となく、楽天はアマゾンのポジションを狙っている気がします。完全に同じものを目指しても、勝ち目は無いので、今のスタイルを世界展開して、winnerの地位を固めにいこうってところでしょうか。サムソン電子では、その国の文化を理解するために、専任者を置いていると聞きます。今のスタイルで勝負するなら、そういうデザイン力も必要かもしれません。

2010年6月26日土曜日

iPad つらつらと

 計ったように、「断言」と「決断」を買っていなかったので、理想書店にて購入しました。価格は、もう少し高くても大丈夫だと思います。iPad,iPhoneのアプリを作った事が無いので、カチンとくるかもしれませんが、理想書店のアプリを使って購入してみて、改善してほしいと思ったところをあげてみます。
  • アプリを再起動した時に前回の状態を記憶していてほしい。
  • 決済カードの記録日数180日は控えめすぎでは?
  • アフィリエイト・プログラムが欲しい(ここ、まじ重要だと思う。ネットで商売すんだろ?)
  • DLボタンの他に、全てダウンロードが欲しい
  • ショップの1頁に表示される書籍数を多くしてほしい(今のスペースの半分で十分)


といった感じです。

 規格書や、akiraさんが訳してくれたPDFなんかを読むために、GoodReader を購入しました。今まで、仕事上、どうしても読まないといけないPDFは印刷していたのですが、まんま読めるのは楽ちんです。つか、印刷しないと読む気になれない障壁が取っ払われたのは大きい。

 変わった無料アプリ、
 Molecules : 分子構造を眺めるアプリ、化学は苦手でサッパリわかりませんが、眺めていると楽しいです。スポーツ飲料などの成分表に書かれているアラニンとかを検索ダウンロードして、ホホーッと、意味もわからず唸ってます…なんじゃそりゃ?
 3D Sun : 太陽の活動をニュースでお届けするアプリ。うぉーっ、コロナたん萌してます。(たまに落ちます)

2010年6月25日金曜日

ぼやきまくる

 Rails の polymorphic association を調べていて、rails では クラスを継承できるから…うんぬんの記述を見つけた。冗談でしょ? foo_controller を継承して、bar_controller を作成した事がありますが、production 環境で、既に生成されたクラスを検索する時に bar_controller が foo_controller として表示されてしまうという酷い目にあいました。なんじゃこれ…?全然ダメじゃん…?と、ぼやきながら、継承を避けて DRY の原則を曲げて、泣く泣くコピペ・コーディングしました。
 Activescaffold の subform における更新も変です。昨日は、この問題を追っていましたが、簡単には解決できず、template オーバーライドで
_hoge_form_column.html.erb

<div>
<%= render :active_scaffold => "hoges", :constraints => { :fuga_id => @record.id }
-%>
</div>

解決しました。
そうでもしないと、has_many の関連レコードが、全部削除して、全部挿入されてしまいます。モデルで

:has_many => hoges, :dependent => :destroy

と指定しない場合には、全部削除の代わりに全部リンクが外され(fuga_id に nil がセットされる)、どんどんとレコードが増え続けます。仮に hoge モデルに対して belongs_to を指定しているモデルがあった場合には、どうするつもりなんでしょうか? ticket 353 の事ですが、habtm が前提となって書かれている気がします。enumlator と yield の嵐で、自己完結的に記述されていて、修正は難しいように感じました。ここは、バグの巣窟です。コントローラにて config.columns[:hoges].show_blank_record = false を指定すると、ここでは最後の1レコードが永遠に削除できません。逆に blank_record を表示するようにした場合、hoge の初期値に何かを設定すると、record.unsaved = true が働いてしまい。fuga 更新時には勝手に hoge のレコードが挿入されてしまいます。

2010年6月23日水曜日

Javascript は好きになれないのな

 いやー、今まで、こんなに突っ込んだ事無かったから、勉強になったのな。推測に過ぎないので、これから書く事は間違っているかもしれないです。

 今回は、ダイアログを出すのに jqModal を使ってるんですが、こいつの中身は ajax でバンバン書き換えるから、1ページにつき、1つしか用意してないわけなんです。iframe を使わないで済むのも気に入ってるところです。

 コードを書いていて、ダイアログを表示して、ウィザード・ページのように状態遷移する度に自己書き換えを ajax で行っていました。ところが、javascript の関数だけが更新されない…。???一体何が???

 冷静に考えてみれば、javascript は DOM 要素じゃないんで、例え inner html ごとゴッソリ入れ替えしても、消えないかもしんないわけです。やや思い悩んだ末に、プレスホルダを利用する事にしました。グローバルに変数定義しておいて、

<javascript defer='defer' type='text/javascript'>
$(function() {
$.hoge_holder = function() { ... }
$.fuga_holder = function() { ... }
});
</javascript>

というように、プレスホルダを更新するという訳です。これなら、だらだらとコードが無尽蔵に追加される訳じゃない。

 話は変わって、jQuery のセレクタで

$('input:name=[hoge]')

みたいにしてたんですが、rails で出力された select要素は、input type='select' ではなく select なわけです。これが、input に該当しないので、また、ハマりました。クソがーーーーーと、怒りながら

$('input:name=[hoge],select:name=[hoge]')

対応したわけなんですが、やっぱ、javascript はどうにも好きになれないかも…。ま、javascript が悪いのか、DOM が悪いのか?

 もひとつ、前々から気に入らないと思っている事があります。DOM要素に属性があり、要素の種類により属性が異なってきます。しかし、javascript 的に見れば、どんなプロパティを追加しようが、それは勝手な話。foo.name が属性なのか、プロパティなのか?ここらへんの感覚にアホ臭さを感じてしまいます。

2010年6月21日月曜日

本の自炊を考えてます

 まぁ、一度読んだ本を読む事は、稀です。ただし、技術書を除く。一度読んだ本を自炊(裁断+スキャンによる電子本化)して、次に読む事は無いでしょう。そう思います。しかし、結婚してから捨てた本は山のようにあります。OpenGL APIリファレンスなんて、一度捨てて、会社で買い直したりしてます。毎回、大掃除の度に聞かれるのが、「この本、いるの?」もう、こっちは譲歩して譲歩して本丸の堀まで埋めて本を残しているのに、これを聞かれるとイラッときてブチッと切れそうになります。
 よほどの豪邸に住むか、倉庫を借りてでも置くかしないと、一般の感覚では、本を置くスペースが足りなくなるでしょう。技術書に関して言えば、8割は枯れて使えない内容となりますが、後で見直したくなるものも多いです。もう、この本を見直す事は無いですが、「トランザクション処理(上)」「トランザクション処理(下)」のブタ本・・・割れてセロハンテープで止めまくりだけど、こいつを電子化したら、さぞかし気分もスカッとする事だろう。
 自炊できない本は、見開きページのある本とか、立体絵本「不思議の国のアリス」とか、巨大見開きの「妖怪の森」とか、まぁ、少数な気がします。

 ま、自炊して負けるとか勝つとかじゃなく、家では切実な問題なんです。

 昼に社長と、これからの本屋はどうする?って、話題になりました。この際だから、本屋もアプリを出して、本屋まで足を運んで電子書籍を買った人には、割引サービスをするとか、アミューズメント式でイベントを開催して電子書籍を売るとか、とにかく本屋まで足を運んで購入してくれた人に、何かサービスをするビジネスモデルなんてどう?って盛り上がりました。本屋での決済は、bluetooth とかで決済するんですよ。ここがミソ。価格を下げるだけでなく、マクドナルドみたいにドラクエのミニ・イベントがオマケになったっていいじゃないですか?出版社と共同で、本屋で電子書籍を買った人には、第0章が付いてきたっていいじゃないですか?って…

えこりん村

日曜日には、えこりん村に行ってきました。びっくりドンキーで有名?なアレフの運営する庭園です。まぁ、ガーデニングが似合うと言ったら、日本においては北海道しかないでしょう(ほんとか?)。正直、ガーデニング?はぁ?だったんですが、ここに足を運んでからは、認識が変わりました。なかなか楽しい世界です。すっかり、リピータと化してます…

















2010年6月19日土曜日

iPad を使って感じた事

 そろそろ iPad 単体での使い方に慣れてきたところです。基本的に、まだ家でしか使っていません。感想を…

有料アプリは、よほどの事が無い限り買いません。悩んだ挙句に買った有料アプリは、3つ(セコっ)。

Magic Piano(とりあえず最初にiPadらしそうな安いアプリを…という事でゲットしました), i文庫HD(ちょい高いのでは?とも思いましたが、サーバを用意したりPDFに加工したり、その他の手間や維持費を考えると妥当な線なのかもしれません), MagicWindow(少ない記憶容量でどう見せるか?が勉強になりました)です。Gravitarium は無料の時にゲットしました(気持ちがいいです)。これぐらいなら、すぐ書けそうな気がしている自分が怖いです…実際に書いてみると苦労が絶えなさそう…。

 売切型のアプリで、クラウド・インフラを提供しているものが多いです。これらのアプリは、どうやってサーバ側の維持費を捻出するのでしょうか?とても疑問に思います。

 adMob のアプリも幾つか使ってみました。しかし、自分の性分には合わなくて、割とすぐに削除しています。原因は、アプリを中断してブラウザが開いたり、AppStore が開いたりするからです。adMob は、よほどスマートに組み込むか、アプリの完成度が高くないと、やっていけないと思います。例えば、無料雑誌中の広告記事として組み込み、クリックされても直ぐに飛ばないで、飛んでいいかどうかを聞くぐらいの慎重さが必要でしょう。

 GoogleEarth、iPad に対応したのは良いとして、iPhone 版には実装されていた SSIDを使った位置情報算出ルーチンが無くなってます。Wifiで使えないので、実装し直して下さい。お願いです。

 Bloomberg 凄いです。何故これが無料なのか?このアプリから使用されるトラフィックを解析して、株式市場で一儲けでも企んでいるのだろうか???と、想像が膨らんでしまいます。

 Beatwave(適当にやっても音楽になるので感動です。オプションで音源を売るビジネスモデルのようですが、このビジネスモデルはどうなんでしょ?), Soundrop Free(これも楽しいです。ついつい起動してしまうアプリです), Backgrounds, Sudoku Tablet, ExploreFlickr, neuNotes, 30min.ランチ あたりが気にいってます。一応 Dropbox, SugarSync, Evernote も入れました。Google の音声検索も、なかなかイケテます。そら案内も、素早く天気がチェックできるので重宝してます。

 CD59枚取り込んで力尽きました…まだまだ残ってますorz... YouTube とかも全然見なかったのですが、iPad だと見る機会が増えました。JeffBeck の A day in the life. とか、Steve vai の Tender Surrender とか見て感動しますた…こんなの見てたら廃人になってしまうので、そこそこのところでやめるようにしてます。

 電子書籍の話にいきましょ…

 まぁ、iBook や Kindle のストアの味気ない事といったら…。ここからどうやって本を探せというのか?画一的な表紙が揃っていて玉石混合、多分 S/N 比が既に低い。こういう一般のものは、SNS的なカテゴリで分類した方が幸せになれるかも?と思いました。SNS的な要素も絡めて、リコメンデーションできれば強い。今後は、良い本に出会うために自分が読んできた本を登録して積極的にリコメンデーションできるようなクラウド・サービスが必要とされているようにも感じます。この役割は Google が担うべきでしょう。情報を収集すると共に、同意が得られればOpenIDでショップ側にも情報を提供する仕組みが欲しいです。

 日本の出版社は縦割りですよね?同じ構造のアプリを幾つもインストールさせられて、本を買うために「アカウントを作成して下さい」ときたもんだ。どうすんのコレ?確かに、iBook で売るのは賢いやり方では無いにしても、ユーザ側の立場で考えてサービスできないもんですかね?そして値段が恐ろしく高い。デジタル媒体ですよ???馬鹿にしてんの???
 Zinio で National Geographic を年間購読するのにかかる費用は $16.50、1冊だと $5.99 です。もう、この差がなんとも言えずガックシきます…。

 UIについても思うところがありました。立ち読みでデータ量を少なくしてボカシて見せて一石二鳥と思ってるかもしれませんが、ボケボケの画像見ても、あっそう?じゃーね?というのが正直な感想です。ただし、この流れるようなパラパラ見のUIは結構好きです。実物の本のようにパラパラめくって見せる必要は無いんだなぁ?と感心しました。一枚づつ指でめくる動作ですが、正直疲れます。パネルの決まった場所を持ったままiPadを少し傾けるとページがめくれるとか、2箇所の決まった場所を持ったまま傾けるとパラパラめくりになるとか、もうちょい工夫の余地があるように思います。片手しか使えない人のためにもUIを考えといた方がいいのかもしれません。

 と、まぁ、今のところは、こんな感じです。

2010年6月18日金曜日

activescaffold の subgroup をタブ化してみる(期待しないで)

あんましオススメできないかもしれないけど、一応、力入れて改造したのを、お披露目です。
activescaffold にて、全てのカラムが add_subgroup されている場合には、タブページのレイアウトにするというものです。jquery を使っているので、要注意です。 subgroup 名が 'main' の場合に限り、タブから脱出させて常に表示するよう配慮しました。

application.html.erb にて

<%= stylesheet_link_tag 'jquery-ui-1.8.1.custom', :media => 'all' %>
<%= javascript_include_tag "jquery-1.4.2.min.js" %>
<script type="text/javascript">
jQuery.noConflict();
var j$ = jQuery;
</script>
<%= javascript_include_tag "jquery-ui-1.8.1.custom.min.js" %>



lib/active_scaffold/helpers/id_helper.rb
追加

def tab_form_id(options = {})
options[:action] ||= params[:action]
options[:id] ||= params[:id]
options[:id] ||= params[:parent_id]
clean_id "#{controller_id}-#{options[:action]}-#{options[:id]}-tabform"
end

def tab_form_panel_id(column,options = {})
options[:action] ||= params[:action]
options[:id] ||= params[:id]
options[:id] ||= params[:parent_id]
clean_id "#{controller_id}-#{options[:action]}-#{options[:id]}-#{column.label}-tabform"
end



lib/active_scaffold/helpers/form_column_helper.rb

def override_tab_form_partial?(column)
path, partial_name = partial_pieces(override_tab_form_partial(column))
template_exists?(File.join(path, "_#{partial_name}"), true)
end

# the naming convention for overriding tab form with partials
def override_tab_form_partial(column)
"#{column.label}_tab_form"
end

def tab_form_partial_for_column(column)
if override_tab_form_partial?(column)
override_tab_form_partial(column)
else
"tab_form"
end
end



従来の_form.html.erb が _tab_form.html.erb になります。
frontends/default/views/_form.html.erb

<%
# 全ての column が、add_subgroup されているかどうかで、処理を切り替える
is_tabbed = true
columns.each :for => @record do |column|
if not is_subsection? column
is_tabbed = false;
break;
end
end
-%>
<% if is_tabbed -%>
<% columns.each :for => @record do |column| -%>
<% if column.label == 'main' %>
<div class="tab-form" id="<%= tab_form_panel_id(column) %>">
<% if !override_tab_form_partial?(column) -%>
<%= render :partial => 'tab_form', :locals => { :columns => column } %>
<% end -%>
</div>
<br clear="all" />
<% end -%>
<% end -%>
<script type="text/javascript">
j$(function(){
j$("#<%= tab_form_id %>").tabs();
});
</script>
<div id="<%= tab_form_id %>" class="form" <%= 'style="display: none;"' if columns.collapsed -%>>
<ul>
<% columns.each :for => @record do |column| -%>
<% if column.label != 'main' -%>
<li class="tab-form"><a href="#<%= tab_form_panel_id(column) %>"><%= t column.label %></a></li>
<% end -%>
<% end -%>
</ul>
<% columns.each :for => @record do |column| -%>
<% if column.label != 'main' -%>
<div class="tab-form" id="<%= tab_form_panel_id(column) %>">
<%= render :partial => tab_form_partial_for_column(column), :locals => { :columns => column } %>
</div>
<% end -%>
<% end -%>
</div>
<% else -%>
<%= render :partial => 'tab_form', :locals => { :columns => columns } %>
<% end -%>



frontends/default/views/_tab_form.html.erb

<ol class="form" <%= 'style="display: none;"' if columns.collapsed -%>>
<% columns.each :for => @record do |column| -%>
<% if is_subsection? column -%>
<li class="sub-section">
<h5><%= column.label %> (<%= link_to_visibility_toggle(:default_visible => !column.collapsed) -%>)</h5>
<%= render :partial => 'form', :locals => { :columns => column } %>
</li>
<% elsif is_subform? column and !override_form_field?(column) -%>
<li class="sub-form <%= active_scaffold_config_for(column.association.klass).subform.layout %>-sub-form <%= column.css_class unless column.css_class.nil? %>" id="<%= sub_form_id(:association => column.name) %>">
<%= render :partial => form_partial_for_column(column), :locals => { :column => column } -%>
</li>
<% else -%>
<li class="form-element <%= 'required' if column.required? %> <%= column.css_class unless column.css_class.nil? %>">
<%= render :partial => form_partial_for_column(column), :locals => { :column => column } -%>
</li>
<% end -%>
<% end -%>
</ol>


 ちょい実装してから、時間が経過しているので、ここにポストした改造箇所が足りてるかどうか不安…。あんまし期待しないで…。
 他にも、Java の absolute layout よろしく <table> レイアウトで、各 column のプロパティから、レイアウトを決定するような改造もしてみたのだけれども…。これもどうなのかな…?

FormHelper の汚染に対応(2)

 FormHelperの汚染に対応の続きです。
 前回のやり方だと、デフォルトの動作と切り替えるのが困難なので、activescaffold に新たにオーバーライドするかどうかをチェックするためのヘルパを加えてみました。

lib/active_scaffold/helpers/form_column_helpers.rb です。
まず下準備としてメソッドを追加します。

def override_form_field_will_do?(column)
respond_to?(override_form_field_will_do(column))
end

def override_form_field_will_do(column)
"#{column.name}_form_column_will_do"
end

次に、active_scaffold_input_for を改造します

module FormColumnHelpers
# This method decides which input to use for the given column.
# It does not do any rendering. It only decides which method is responsible for rendering.
def active_scaffold_input_for(column, scope = nil, options = {})
begin
will_do = true
options = active_scaffold_input_options(column, scope, options)
options = javascript_for_update_column(column, scope, options)
# prepare, check if form_field will be override?
if override_form_field_will_do?(column)
will_do = send(override_form_field_will_do(column), @record, options)
end
# first, check if the dev has created an override for this specific field
if will_do and override_form_field?(column)
send(override_form_field(column), @record, options)
# second, check if the dev has specified a valid form_ui for this column
elsif column.form_ui and override_input?(column.form_ui)
send(override_input(column.form_ui), column, options)
# fallback: we get to make the decision
else
....


同様に list_column_helper.rb も改造します

module ListColumnHelpers
def get_column_value(record, column)
begin
will_do = true
if column_override_will_do? column
will_do = send(column_override_will_do(column), record)
end
# check for an override helper
value = if will_do and column_override? column
......


def column_override_will_do(column)
"#{column.name.to_s.gsub('?', '')}_column_will_do"
# parse out any question marks (see issue 227)
end

def column_override_will_do?(column)
respond_to?(column_override_will_do(column))
end




 これで、FormHelper にて



def name_column_will_do
record.is_a? Customer
end

def name_column(record)
"<a href='mailto:" + record[:email_address].to_s + "'>" + record[:name].to_s + "</a>";
end



と、汚染に対する耐性が簡単に実装できました。

2010年6月17日木曜日

boost::spirit v2.3 ちららつきる

ちょいワケありで、簡単な定義を書いて wsdl を生成するコードをこさえました。
しかし、いざ wsdl を生成してみると、php の SoapServer や SoapClient で、動作させるのが辛そうな感じ。Delphi で、wsdl からクライアント用のインタフェイスを生成するのは、うまくいきました。肝心の ruby はどうか?というと、soap4r があり、いけそうにも思うのですが…。サーバとして常時稼働させるのには実績の面で心配がありまして、悩みまくった挙句、結論としては Soap は使わない…という事に…。
ここは、単純に Delphi + TIdHTTP の組み合わせで、リクエストを送って、レスポンスをゴリゴリ処理するという方式を採用する事にしました…。

 うーん…新しい spirit 書きやすいようで難しいです…。qi より lex 使った方が幸せなのかも?

def_grammar.hpp

#include <iostream>
#include <string>
#include <vector>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

namespace client {
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

struct xsd_datatypes_ : qi::symbols<char,std::string> {
xsd_datatypes_() {
add
( "string", "xsd:string" )
( "bool", "xsd:boolean" )
( "float", "xsd:float" )
( "double", "xsd:double" )
( "short", "xsd:short" )
( "ushort", "xsd:unsignedShort")
( "int", "xsd:integer" )
( "uint", "xsd:unsignedInt" )
( "long", "xsd:long" )
( "ulong", "xsd:ulong" )
( "date", "xsd:date" )
("dateTime", "xsd:dateTime" )
( "binary", "xsd:base64Binary" )
;
}
} xsd_datatypes;

struct inout_ : qi::symbols<char,int> {
inout_() {
add
( "in", 0 )
( "out", 1 )
;
}
} inout;

struct wsdl_arg {
int inout_;
std::string type_;
std::string name_;
};
}

BOOST_FUSION_ADAPT_STRUCT(
client::wsdl_arg,
// メンバ変数の並び順ではなく、構文解析の順番に合わせる事が大切
(std::string, type_ )
( int, inout_ )
(std::string, name_ )
)


namespace client {

#define ASCII_NAME (ascii::char_("a-zA-Z") >> *ascii::char_("a-zA-Z0-9"))

template <typename Iterator>
struct wsdl_arg_parser : qi::grammar<Iterator, wsdl_arg(), ascii::space_type> {
wsdl_arg_parser()
: wsdl_arg_parser::base_type(expression)
{
ioval %= inout;
tname %= xsd_datatypes;
sname %= ASCII_NAME;
expression = tname >> '[' >> ioval >> ']' >> sname;
// 間に lexeme[ qi::blank ] とか、やりたいけど、やり方がわかんね(全部エラー)orz
}
qi::rule<Iterator, int(), ascii::space_type> ioval;
qi::rule<Iterator, std::string(), ascii::space_type> sname, tname;
qi::rule<Iterator, wsdl_arg(), ascii::space_type> expression;
};

struct wsdl_func {
std::string name_;
std::vector<wsdl_arg> args_;
};
}

BOOST_FUSION_ADAPT_STRUCT(
client::wsdl_func,
(std::string, name_ )
(std::vector<client::wsdl_arg>, args_ )
)

namespace client {

template <typename Iterator>
struct wsdl_func_parser : qi::grammar<Iterator, wsdl_func(), ascii::space_type> {

#define SETUP_AT( idx ) (at_c<idx>(qi::_val) = qi::_1)
#define PUSH_BACK_AT( idx ) push_back( at_c<idx>(qi::_val), qi::_1 )
#define PUSH_BACK_ARG arg[ PUSH_BACK_AT(1) ]

wsdl_func_parser()
: wsdl_func_parser::base_type(expression, "expression")
{
using phoenix::push_back;
using phoenix::at_c;

using phoenix::construct;
using phoenix::val;

fname %= ASCII_NAME;
// SETUP_AT(0) しなくて入りそうなもんだけど、ここでは明示的に指定しないと
// いけない。client::wsdl_arg_parser は、何故指定しなくても大丈夫なのか?
// どんな魔術を使っているのだろうと…気になる
expression = qi::lit("function") >> fname[ SETUP_AT(0) ] >> '('
>> ( PUSH_BACK_ARG >> *(',' >> PUSH_BACK_ARG) )
// spirit の qi::eol の扱いがよくわからん(>_<)
// と思ったが、ascii::space_type が スペース・タブ・改行なのだろう・・・
>> ')' >> ';'; // >> qi::eol;

// エラーハンドリングを入れてみたけど動作しない・・・(;_;)
expression.name( "expression" );
fname.name( "fname" );

qi::on_error<qi::fail>(
expression
, std::cerr
<< val("Error! Expecting ")
<< qi::_4 // what failed?
<< val(" here: \"")
<< construct<std::string>(qi::_3, qi::_2) // iterators to error-pos, end
<< val("\"")
<< std::endl
);

}
qi::rule<Iterator, std::string(), ascii::space_type> fname;
qi::rule<Iterator, wsdl_func(), ascii::space_type> expression;
wsdl_arg_parser<Iterator> arg;
};

template <typename Iterator>
struct wsdl_def_grammar : qi::grammar<Iterator, std::vector<wsdl_func>(), ascii::space_type> {
wsdl_def_grammar()
: wsdl_def_grammar::base_type( expression )
{
using phoenix::push_back;

expression = +func[ push_back( qi::_val, qi::_1 ) ];
}
qi::rule<Iterator, std::vector<wsdl_func>(), ascii::space_type> expression;
wsdl_func_parser<Iterator> func;
};

}



wsdl_data.inc

//--------------------------------------------------------------
// PART: definitions
// def_header % 'ServiceName'
const char xml_header[] = "<?xml version=\"1.0\"?>\n";
const char def_header[] =
"<definitions \n" \
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" \
" xmlns:http=\"http://schemas.xmlsoap.org/wsdl/http/\"\n" \
" xmlns:mime=\"http://schemas.xmlsoap.org/wsdl/mime/\"\n" \
" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\"\n" \
" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n" \
" xmlns:ns1=\"urn:%1%\"\n" \
" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\"\n" \
" targetNamespace=\"urn:%1%\"\n" \
" xmlns=\"http://schemas.xmlsoap.org/wsdl/\"\n" \
">\n";
// types
// message ...
// porttype ...
// operation1 ...
// binding
// operation2 ...
// service
const char def_footer[] = "</definitions>";

//--------------------------------------------------------------
// PART: types
// types_part % 'ServiceName'
const char types_part[] =
"<types>\n" \
" <xsd:schema targetNamespace=\"urn:%1%\" attributeFormDefault=\"qualified\" elementFormDefault=\"qualified\">\n" \
" <xsd:import namespace=\"http://schemas.xmlsoap.org/soap/encoding/\"/>\n" \
" </xsd:schema>\n" \
"</types>\n";

//--------------------------------------------------------------
// PART: message
// message_header % 'FunctionName' % 'In' | 'Out'
const char message_header[] = "<message name=\"%1%%2%\">\n";
// message_content % 'ArgName' % 'xsd:base64Binary' | 'xsd:string' | 'xsd:int' | 'xsd:unsignedInt' | ...
// ...
const char message_content[] = " <part name=\"%1%\" type=\"%2%\"/>\n";
const char message_footer[] = "</message>\n";

//--------------------------------------------------------------
// PART: portType
// portType_header % 'FunctionName'
const char portType_header[] = "<portType name=\"%1%Soap\">\n";
// operation ...
const char portType_footer[] = "</portType>\n";

//--------------------------------------------------------------
// PART: operation1
// operation_portType % 'FunctionName'
const char operation_portType[] =
"<operation name=\"%1%\">\n" \
" <input message=\"ns1:%1%In\"/>\n" \
" <output message=\"ns1:%1%Out\"/>\n" \
"</operation>\n";

//--------------------------------------------------------------
// PART: binding
// binding_header % 'ServiceName'
const char binding_header[] =
"<binding name=\"%1%Soap\" type=\"ns1:%1%Soap\">\n" \
"<soap:binding transport=\"http://schemas.xmlsoap.org/soap/http\" style=\"rpc\"/>\n";
// operation2 ....
const char binding_footer[] = "</binding>\n";

//--------------------------------------------------------------
// PART: operation1
// operation_binding % 'ServiceName' % 'FunctionName'
const char operation_binding[] =
"<operation name=\"%2%\">\n" \
" <soap:operation soapAction=\"#%2%\" style=\"rpc\"/>\n" \
" <input>\n" \
" <soap:body use=\"encoded\" namespace=\"urn:%1%\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"/>\n" \
" </input>\n" \
" <output>\n" \
" <soap:body use=\"encoded\" namespace=\"urn:%1%\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"/>\n" \
" </output>\n" \
"</operation>\n";

//--------------------------------------------------------------
// PART: service
// service_part % 'ServiceName' % 'ServiceURL'
// ServiceURL sample => 'http://hoge:8080/fuga.php'
const char service_part[] =
"<service name=\"%1%\">\n" \
" <port name=\"%1%Soap\" binding=\"ns1:%1%Soap\">\n" \
" <soap:address location=\"%2%\"/>\n" \
" </port>\n" \
"</service>\n";



def2wsdl.cpp

#include <iostream>
#include <string>
#include <vector>
#include <fstream>

#include <boost/format.hpp>
#include <boost/foreach.hpp>

#include "def_grammar.hpp"
#include "wsdl_data.inc"

void show_usage() {
std::cout << "def2wsdl [def filename] [output filename] [service name] [url]" << std::endl;
std::cout << "======================================" << std::endl;
std::cout << " >def2wsdl mysvcdef.txt mysvc.wsdl mysvc http://localhost/mysvc/mysvc.wsdl" << std::endl;
std::cout << "--------------------------------------" << std::endl;
std::cout << " type list " << std::endl;
std::cout << " string => xsd:string" << std::endl;
std::cout << " bool => xsd:boolean" << std::endl;
std::cout << " float => xsd:float" << std::endl;
std::cout << " double => xsd:double" << std::endl;
std::cout << " short => xsd:short" << std::endl;
std::cout << " ushort => xsd:unsignedShort" << std::endl;
std::cout << " int => xsd:integer" << std::endl;
std::cout << " uint => xsd:unsignedInt" << std::endl;
std::cout << " long => xsd:long" << std::endl;
std::cout << " ulong => xsd:ulong" << std::endl;
std::cout << " date => xsd:date" << std::endl;
std::cout << " dateTime => xsd:dateTime" << std::endl;
std::cout << " binary => xsd:base64Binary" << std::endl;
std::cout << "--------------------------------------" << std::endl;
std::cout << " def file sampe " << std::endl;
std::cout << "--------------------------------------" << std::endl;
std::cout << " function foo ( " << std::endl;
std::cout << " int [in] arg1, " << std::endl;
std::cout << " string [out] arg2 " << std::endl;
std::cout << " );" << std::endl;
std::cout << " function bar ( " << std::endl;
std::cout << " string [out] arg2 " << std::endl;
std::cout << " );" << std::endl;
}

namespace {
int arg_is_in( const client::wsdl_arg& arg ) {
return (arg.inout_ == 0) ? 1 : 0;
}
}

void output_wsdl(
std::ostream& os,
const std::string& svcname,
const std::string& svcurl,
const std::vector<client::wsdl_func>& funcs
) {
os << xml_header;
os << boost::format( def_header ) % svcname;
// PART: type
os << boost::format( types_part ) % svcname;
// PART: message
BOOST_FOREACH( const client::wsdl_func& func, funcs ) {
os << boost::format( message_header ) % func.name_ % "In";
// sort して range かけた方がスマートか?
// ま、arg の指定順序も壊せないので filter が欲しいところなのかも
BOOST_FOREACH( const client::wsdl_arg& arg, func.args_ ) {
if( !arg.inout_ ) {
os << boost::format( message_content ) % arg.name_ % arg.type_;
}
}
os << message_footer;
os << boost::format( message_header ) % func.name_ % "Out";
BOOST_FOREACH( const client::wsdl_arg& arg, func.args_ ) {
if( arg.inout_ ) {
os << boost::format( message_content ) % arg.name_ % arg.type_;
}
}
os << message_footer;
}
// PART: portType
os << boost::format( portType_header ) % svcname;
BOOST_FOREACH( const client::wsdl_func& func, funcs ) {
// PART: operation1
os << boost::format( operation_portType ) % func.name_;
}
os << portType_footer;
// PART: binding
os << boost::format( binding_header ) % svcname;
BOOST_FOREACH( const client::wsdl_func& func, funcs ) {
// PART: operation2
os << boost::format( operation_binding ) % svcname % func.name_;
}
os << binding_footer;
os << boost::format( service_part ) % svcname % svcurl;
os << def_footer;
}


int main( int argc, char* argv[] ) {
if( argc != 5 ) {
show_usage();
return 1;
}

std::ifstream ifs( argv[1], std::ios::in );
if( !ifs ) {
std::cerr << "fail to open: " << argv[1] << std::endl;
return 2;
}

std::string source_code;

ifs.unsetf(std::ios::skipws); // No white space skipping!
std::copy(
std::istream_iterator<char>(ifs),
std::istream_iterator<char>(),
std::back_inserter(source_code)
);

typedef client::wsdl_def_grammar<std::string::const_iterator> wsdl_parser;

wsdl_parser g;
std::vector<client::wsdl_func> fncs;

bool res = phrase_parse(
source_code.begin(), source_code.end(),
g, boost::spirit::ascii::space, fncs
);


if( res ) {
std::cout << "Success to parse!" << std::endl;
std::ofstream ofs( argv[2], std::ios::out | std::ios::trunc );
output_wsdl( ofs, argv[3], argv[4], fncs );
} else {
std::cerr << "Fail to parse: " << argv[1] << std::endl;
}

return 0;
}

2010年6月16日水曜日

c++ コード中の __ ???

boost_1_43_0/boost/spirit/home/qi/numeric/int.hpp を見ていて



namespace boost { namespace spirit { namespace qi
{
using spirit::short_;
using spirit::short__type;
using spirit::int_;
using spirit::int__type;
using spirit::long_;
using spirit::long__type;



なんて、記述を見つけた…。ダブル・アンダースコアって、リーガルだったっけか???
よくわかんなくなってきた…。実質、リーガルと見なした方が良いのか?

久々にspamassassin 日本語パッチ用ルール更新

ここんところ、SPF をまじめに設定しているスパムメールが増えてきたので、久しぶりにルールの更新を行いました。spamassassin 日本語パッチ版用のルール
 

2010年6月15日火曜日

ATLで自動生成されたuriのリンク切れで脱力

Soap のための wsdl 定義をいろいろ弄っていて、ふと気がついた。以下は、ATLが自動生成したWSDLコードの先頭である。

<?xml version="1.0"?>
<!-- ATL Server generated Web Service Description -->
<definitions
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:s0="urn:HogeService"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:atls="http://tempuri.org/vc/atl/server/"
targetNamespace="urn:HogeService"
xmlns="http://schemas.xmlsoap.org/wsdl/"
>

おんやー?xmlns:atls="http://tempuri.org/vc/atl/server/" なんじゃこりゃ?
自分のモジュールでは使ってないし、リンクが切れている…。
xmlns におけるリンク切れって、どうなんでしょ?必要になるまで、評価が先延ばしされるのであれば問題無いのだが…。
一応、http://tempuri.org/ ってのは、生きてるのねん…。なんなんだよ…。

2010年6月11日金曜日

jqModal with ajax and drag

ネタ元:[jQuery] jqDnR and jqModal - draggable area not working when using ajax

ま、ネタというよりも、俺もハマったんですけどネ…。

 jqModal のサイトに、ダイアログをドラッグするには、jqDnR を使わないとダメなように書いてあるんで、みんな、ついつい jqDnR を使って実装しようとするんですわ。ところが、ネタ元にあるように


$('#hoge').jqm({ajax:'/fuga',target:$('#content'),trigger:'#btn'}).jqDrag('.jqDrag');

なんてコードを書いて、jqDrag クラス要素がドラッグ可能になった所で ajax により要素が書き換えられて、ドラッグできない~、なんて事になるんですわ。

 そもそも jQuery には、draggable という plugin があるので、こちらを使った方がストレスもないんだよね?

$('#hoge').jqm({ajax:'/fuga',target:$('#content'),trigger:'#btn'}).draggable({opacity:0.8,handle:'#hoge-title'});

こんなんで十分です。

2010年6月10日木曜日

record_select の google chrome 対応

 規格がどうだ・・・とか、厳密な話をしていても、微妙な違いは起こるものです。自分の開発環境がノートパソコンだからかどうかは、わかりませんが、少なくとも Firefox と Google chrome では、javascript 内における onkeypress イベントで、ハンドリングされないキーが存在します。
 あと、意図せずに、ESC キーを押してしまった場合、フォーカスを当てなおさないと選択リストが表示されません。これらを考慮に入れた上で、record_select.js を修正してみました。


...

/**
* all the behavior to respond to a text field as a search box
*/
_respond_to_text_field: function(text_field) {
// attach the events to start this party
text_field.observe('focus', this.open.bind(this));

// the autosearch event - needs to happen slightly late (keyup is later than keypress)
text_field.observe('keyup', function() {
if (!this.is_open()) return;
this.container.down('.text-input').value = text_field.value;
}.bind(this));

// keyboard navigation, if available
if (this.onkeypress) {
text_field.observe('keypress', this.onkeypress.bind(this));
}
if (this.onkeydown) {
text_field.observe('keydown', this.onkeydown.bind(this));
}
},

_use_iframe_mask: function() {
return this.container.insertAdjacentHTML ? true : false;
}
});

/**
* Adds keyboard navigation to RecordSelect objects
*/
Object.extend(RecordSelect.Abstract.prototype, {
current: null,

/**
* keyboard navigation - where to intercept the keys is up to the concrete class
* some browser does not handle KEY_XXXX. so separate to keydown event
*/
onkeypress: function(ev) {
var elem;
switch (ev.keyCode) {
case Event.KEY_SPACE:
case Event.KEY_RETURN:
if (this.current) this.current.down('a').onclick();
break;
case Event.KEY_RIGHT:
if( !this.is_open() ) this.open();
elem = this.container.down('li.pagination.next');
if (elem) elem.down('a').onclick();
break;
case Event.KEY_LEFT:
if( !this.is_open() ) this.open();
elem = this.container.down('li.pagination.previous');
if (elem) elem.down('a').onclick();
break;
case Event.KEY_ESC:
break;
case Event.KEY_TAB:
return;
default:
if( !this.is_open() ) this.open();
return;
}
Event.stop(ev); // so "enter" doesn't submit the form, among other things(?)
},
/**
* keyboard navigation - where to intercept the keys is up to the concrete class
*/
onkeydown: function(ev) {
var elem;
switch (ev.keyCode) {
var elem;
switch (ev.keyCode) {
case Event.KEY_UP:
if( !this.is_open() ) this.open();
if (this.current && this.current.up('.record-select')) elem = this.current.previous();
if (!elem) elem = this.container.getElementsBySelector('ol li.record').last();
this.highlight(elem);
break;
case Event.KEY_DOWN:
if( !this.is_open() ) this.open();
if (this.current && this.current.up('.record-select')) elem = this.current.next();
if (!elem) elem = this.container.getElementsBySelector('ol li.record').first();
this.highlight(elem);
break;
case Event.KEY_RIGHT:
if( !this.is_open() ) this.open();
elem = this.container.down('li.pagination.next');
if (elem) elem.down('a').onclick();
break;
case Event.KEY_LEFT:
if( !this.is_open() ) this.open();
elem = this.container.down('li.pagination.previous');
if (elem) elem.down('a').onclick();
break;
case Event.KEY_TAB:
case Event.KEY_ESC:
this.close();
break;
default:
return;
}
},

/**
* moves the highlight to a new object
*/
highlight: function(obj) {
if (this.current) this.current.removeClassName('current');
this.current = $(obj);
obj.addClassName('current');
}
});


追記:修正部分が足りてませんでした。追加しました。
2010/06/11 追記:keypress イベントにもリスト再表示のコードを挿入しました。
2010/07/29 追記:tab イベント時の処理がまずかったので、修正。元のコードの方がすっきりしているので、この改造は本末転倒だったかも…

ControllerHelper の汚染に対応

 何が起こっているのか、よくわかんないのだが、helper は、モジュールで作成されており、コントローラからコントローラを利用すると、モジュールによる汚染があるのでは無いか?と推察しています。
 どういう現象が起こるかと言うと、Employee と Customer に name というフィールドが存在している場合に、CustomersHelper 内で activescaffold の Field override を使った場合に、意図せず Employee の name まで挙動が波及してしまう事があります。


def name_column(record)
"<a href='mailto:" + record[:email_address].to_s + "'>" + record[:name].to_s + "</a>";
end


 Oh!No! Employee の name まで波及してるやんけーーーーー!!!

こんな時は、慌てず

def name_column(record)
if record.is_a? Customer
"<a href='mailto:" + record[:email_address].to_s + "'>" + record[:name].to_s + "</a>";
else
if record[:name] != nil
record[:name].to_s
else
''
end
end
end


です。うーん・・・どうなんでしょ・・・これ・・・

2010年6月8日火曜日

vc で、c1001 どうする?

boost で、テンプレートもプリプロセッサも、限界まで駆使されるようになってきた…。以前なら、コンパイルできていたはずなのに、1.43.0 にバージョンアップすると、

"fatal error C1001: 内部コンパイラ エラー" 

なんて、ものが発生して、おら、どうすればいいんだべ…なんて途方に暮れるもの…。

 2ch で聞いても、解決方法は無い…とバッサリ切られて、可哀想。

解決方法は、こんなところに書いてありました。

 これによると、/Zm200 というコンパイルオプションを付ければ良いようです。コンパイルが重たくなるけど、なるほど、エラーは回避できるようです。デフォルトは、/Zm100 なんだそうな。

rails 2.3.8 で The error occurred while evaluating nil.[]

 またまた、いろいろ変更されたおかげで、mongrel に修正が必要なようだ…。

mongrel-1.1.5/lib/mongrel/cgi.rb

def send_cookies(to)
...
else
#to['Set-Cookie'] = options['cookie'].to_s
to['Set-Cookie'] = @head['cookie'].to_s
end
...
end


2010/06/09 追記:このやり方でも、まだダメみたいだ…。
2010/06/09 追記2: @ ぬけてるやん orz...

boost::polygon で invalid operator <

 VCデバッグモードで、ポリゴン演算のところにて、"invalid operator <" というランタイム・エラーが出た。何事だ?と思ったら、最新版ではフィックスされているようだ。
 参考ページ

追記:うーん・・・VC8 でコンパイルしてるからかな・・・エラーになる場合がある…。template の型はあっているように見えるのだが・・・
追記: GIL の OpenCV ラッパーといい、boost::polygon といい、メタプログラミング+コンセプトは、難しすぎ…。何が起こっているのか、コンパイル・タイム・テンプレート・デバッグでもないと、デバッグは至難の業だろ…。

2010年6月5日土曜日

iPadのgeolocationapi

まさかGPSだけにしか対応していないとは、思わなかった…。
やられたZe!
弄ってると、色々、アプリを作って見たい衝動にかられちゃうかも…

Wifiスポットどないせいちゅうねん?

お客様のご利用のメールアドレスでは、本操作は実施いただけません。
怒り爆発中らしい。俺が買ったのは、3Gでは無いモデルだ。PHSもiPhoneも持ってないちゅうねん。
携帯電話は、auですが、どうすんの?携帯電話のメールアドレストは、企業を信用していないので、極限られた人しか教えていないし、使っていない。どうせ、二年で切れるし、こんなサービスいらんわ?
YahooBBもSoftbankもサービスは、糞。
期限内に登録しないと無効になるのも戦略だろ?

追記 au の携帯電話からでも登録可能なようだが、メールアドレスと携帯電話固有番号の収集が目的だろ?御遠慮願ってます。バーカ。

IPadゲットしました

今iPadから入力してます。全然いけます。キーボードもいらないです。さすがに、タグを打つのは辛そうです。コピペも、ちょっと辛いかな?
ちらっと雑誌を見て、画像の圧縮技術が、ここへ来て必要とされている気がしました。HTMLであればmediatypeのscreenレベルが、小中大と欲しくなりますね!
うーん…文章の手直しiPadじゃ、厳しいかな?
カーソル・キーが欲しいです。タッチパッドのカーソル制御、雑で反応してくれない。

2010年6月3日木曜日

html の select 要素でまたはまる

 html のフォーム系要素は、まだまだ糞だ。IE6 の select が、zindex に関係なく一番上にくるというアホな話は、まあいいとして・・・。今回は、select 要素を readonly にできなくてハマった。正確に言えば、readonly="readonly" と指定しても、ぜんぜん readonly じゃないのだ・・・。バンバン選択できるぜセニョリータ・・・アホけ?

いやいや、disabled がありますよ?

 そうは問屋がおろさないのである。フォームをサブミットした時に、disabled された要素は、もちろん無視される。そうすると、どうなるか? rails とかで、更新時に値がリセットされてしまうのである。こいつを避けるために hidden 要素を追加すればいいのである・・・。である。わかるよ。うん。わかる。hidden 要素を追加すればいいんだよね?アホだろ?

 そもそも、input 要素とかにフィールドと連携した id 属性が自動で割り振られるようになっているところへ、hidden 要素を追加しろだー?なんで、こんなまわりくどい事をせにゃならんねん・・・。

 他にも、style で、ime-mode: active, inactive, ... の中に、カナ・モードとかも欲しいところなのだが、ありま千円。これ最初に考えたの MS でしょ?中途半端だ・・・。せっかく firefox も追随してくれてるのに・・・。

追記: 結局のところ、activescaffold を改造するハメに・・・ form_column_helpers.rb

def active_scaffold_input_singular_association(column, html_options)
associated = @record.send(column.association.name)

select_options = options_for_association(column.association)
select_options.unshift([ associated.to_label, associated.id ]) unless associated.nil? or select_options.find {|label, id| id == associated.id}

selected = associated.nil? ? nil : associated.id
method = column.name
#html_options[:name] += '[id]'
options = {:selected => selected, :include_blank => as_(:_select_)}

html_options.update(column.options[:html_options] || {})
html = ""
# to be disabled ... and add hidden tag
if html_options[:readonly]
html_options[:disabled] = true;
end
options.update(column.options)
html << select(:record, method, select_options.uniq, options, html_options)
if html_options[:readonly]
# この段階でないと、options[:name] が確定していない?
html << "<input type='hidden' name='#{html_options[:name]}' value='#{selected}' />"
end
html
end

くどい・・・

2010/06/18 追記: W3Cでは、readonly なんて属性は invalid のようだ…。ちゃんと練られていないって事でいいのかな?