ガリレオスコープ開発チームによる 最新Web動向、技術メモ ( Ruby, Rails, node.js ) です @techs.empty?Tumblr (3.0; @techs-empty)https://techs-empty.tumblr.com/ssh-copy-idをmacosxで使えるようにするssh-copy-id というsshの公開鍵をリモートサーバーに登録する ちょっと便利なコマンドがあるんですが、macでbrewでインストールできなかったので、メモです。 下記のふぁいるを、/usr/local/binなどに作成します。 <pre> $ vi /usr/local/bin/ssh-copy-id #=&gt; 下記のファイルを貼付け $ chmod +x /usr/local/bin/ssh-copy-id $ ssh-copy-id -i ~/.ssh/id_rsa.pub user@host #=&gt; ~/.ssh/id_rsa.pub を指定したサーバーに設定 </pre> (ssh-copy-id) <pre> #!/bin/sh # Shell script to install your identity.pub on a remote machine # Takes the remote machine name as an argument. # Obviously, the remote machine must accept password authentication, # or one of the other keys in your ssh-agent, for this to work. ID_FILE="${HOME}/.ssh/id_rsa.pub" if [ "-i" = "$1" ]; then shift # check if we have 2 parameters left, if so the first is the new ID file if [ -n "$2" ]; then if expr "$1" : ".*\.pub" ; then ID_FILE="$1" else ID_FILE="$1.pub" fi shift # and this should leave $1 as the target name fi else if [ x$SSH_AUTH_SOCK != x ] ; then GET_ID="$GET_ID ssh-add -L" fi fi if [ -z "`eval $GET_ID`" ] &amp;&amp; [ -r "${ID_FILE}" ] ; then GET_ID="cat ${ID_FILE}" fi if [ -z "`eval $GET_ID`" ]; then echo "$0: ERROR: No identities found" &gt;&amp;2 exit 1 fi if [ "$#" -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then echo "Usage: $0 [-i [identity_file]] [user@]machine" &gt;&amp;2 exit 1 fi { eval "$GET_ID" ; } | ssh $1 "umask 077; test -d .ssh || mkdir .ssh ; cat &gt;&gt; .ssh/authorized_keys" || exit 1 cat </pre>https://techs-empty.tumblr.com/post/42413981319https://techs-empty.tumblr.com/post/42413981319Wed, 06 Feb 2013 15:05:00 +0900kuruma3Bundleではまった最悪なこと<h2>状況</h2> <p>sporkを走らせようとしたが、なぜかrequire &lsquo;spork'ができずこまった。rspec/railsもロードされていないし。 もう少し調べてみると、どうもGemfileのdevelop &amp; test groupにいれたgemがロードできない。 なんじゃこりゃ〜</p> <h2>原因</h2> <p>サーバ側でbundle installした時に下記のコードで実行した。</p> <pre><code> bundle install --without development test </code></pre> <p>これ自体は問題はない。が、 ここから誰かがサーバ側で作業して、リポジトリにpushした。それを自分がpullした。 そうすると、.bundle/configというファイルが勝手にできていて、下記のようになっている。</p> <pre><code>1 --- 2 BUNDLE_WITHOUT: development:test </code></pre> <p>これによって、developmentとtestのgroup中のgemは無視されるらしい。。。</p> <h2>対処</h2> <p>上記の2行目を消す事は当然で、 予防策として、.hgignoreで必ず.bundleを無視しておく必要があると思う。 vim .hgignore</p> <pre><code>+ ^\.bundle </code></pre> <h2>参照</h2> <p>一応ここらへんに書いてあったが、もっとわかりやすくしておいてくれ〜 <a href="http://gembundler.com/groups.html">http://gembundler.com/groups.html</a></p>https://techs-empty.tumblr.com/post/41363968691https://techs-empty.tumblr.com/post/41363968691Fri, 25 Jan 2013 01:08:00 +0900eijik-blograils2.3.9 -> 2.3.14にアップしたときの注意点<p>いまさらこんなレガシーなのを使っているのは、よろしくないが、 現実は3系にあげられないでいる。 そうすると、似たような問題を検索しても引っかかってこない。</p> <h2>現象</h2> <p>何かしらのオブジェクトに対して、ネストしたパラメータがあると、以前は行けたのに、アップ後はエラーとなる。</p> <ul> <li><p>2.3.9 -&gt; OK</p> <p>>> r = Reservation.new<br/> >> r.attributes = {:start_attributes=&gt;{:id=&gt;&lsquo;1005&rsquo;}}<br/> => {:start_attributes=&gt;{:id=&gt;&ldquo;1005&rdquo;}}<br/> >> r.start (=&gt;オブジェクトが表示される。)</p></li> <li><p>2.3.14 -&gt; error</p> <p>>> r = Reservation.new<br/> >> r.attributes = {:start_attributes=&gt;{:id=&gt;'1004&rsquo;}}<br/> ActiveRecord::RecordNotFound: Couldn&rsquo;t find Place with ID=1004 for Reservation with ID=</p></li> </ul> <h2>問題点</h2> <p>以前のバージョンでは、start_attributesのidからオブジェクトを取得しているようだが、アップ後はエラーを出している。</p> <ul> <li><p>2.3.9 L288</p> <p>elsif attributes['id&rsquo;]<br/> existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id&rsquo;])<br/> assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])<br/> self.send(association_name.to_s+&rsquo;=&rsquo;, existing_record)</p></li> <li><p>2.3.14 L288</p> <p>elsif attributes['id&rsquo;]<br/> raise_nested_attributes_record_not_found(association_name, attributes['id&rsquo;])</p></li> </ul> <p><a href="https://github.com/rails/rails/blob/v2.3.9/activerecord/lib/active_record/nested_attributes.rb">https://github.com/rails/rails/blob/v2.3.9/activerecord/lib/active_record/nested_attributes.rb</a><br/> <a href="https://github.com/rails/rails/blob/v2.3.14/activerecord/lib/active_record/nested_attributes.rb">https://github.com/rails/rails/blob/v2.3.14/activerecord/lib/active_record/nested_attributes.rb</a></p> <h2>対処</h2> <p>おそらく、なんらかの理由があって、変更しているのだろうが、 これを使っている箇所全体を新しい書き方に変更するのはかなりのコストなので、 以前の書き方に戻すようにパッチを当てることにした。</p> <p>config/initializers/active_record.rbとか適当にファイルを作成して</p> <pre><code>module ActiveRecord module NestedAttributes #:nodoc: def assign_nested_attributes_for_one_to_one_association(association_name, attributes) options = nested_attributes_options[association_name] attributes = attributes.with_indifferent_access check_existing_record = (options[:update_only] || !attributes['id'].blank?) if check_existing_record &amp;&amp; (record = send(association_name)) &amp;&amp; (options[:update_only] || record.id.to_s == attributes['id'].to_s) assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) elsif attributes['id'] existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id']) assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) self.send(association_name.to_s+'=', existing_record) elsif !reject_new_record?(association_name, attributes) method = "build_#{association_name}" if respond_to?(method) send(method, attributes.except(*UNASSIGNABLE_KEYS)) else raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?" end end end end end </code></pre>https://techs-empty.tumblr.com/post/27114959705https://techs-empty.tumblr.com/post/27114959705Fri, 13 Jul 2012 18:07:00 +0900rails2activerecord2.3.92.3.14bugバグeijik-blogBasic認証がかかったHTTPサイトのresponse headerをtelnetで見る方法<p>続けて、HTTPのサイトをtelnetで見ることになったので同じようなネタでもう一つ</p> <p>HTTPSと同じように、hogehoge.com:80をにアクセスしようとするとこんな感じです。 まずは、Basic認証がない場合から、</p> <pre><code>$ telnet hogehoge.com 80 &gt; GET /index.html HTTP/1.1 &gt; Host: hogehoge.com &gt; [returnを2回] </code></pre> <p>で、さらに、Basic認証がかかっているばあいは、</p> <pre><code>$ telnet hogehoge.com 80 &gt; GET /index.html HTTP/1.1 &gt; Host: hogehoge.com &gt; Authorization: Basic aG9nZWhvZ2U6aG9nZWhvZ2U= &gt; [returnを2回] </code></pre> <p>と、Authorizationも含めておくってあげればOKです。 Basicの後の文字列は、Basic認証なので、&quot;ID:PW&quot;をbase64化したものを送ればOKです。</p> <p> $ echo -n &lsquo;hogehoge:hogehoge&rsquo; | openssl enc -e -base64 #=&gt; aG9nZWhvZ2U6aG9nZWhvZ2U=</p> <p>上記は、IDとPWがhogehogeの場合です。</p>https://techs-empty.tumblr.com/post/25560932267https://techs-empty.tumblr.com/post/25560932267Thu, 21 Jun 2012 15:15:40 +0900serverkuruma3HTTPSのサイトのレスポンスヘッダをTerminalだけで調べる方法<p>最初は、SafariやらChromeでやっていたのですが、面倒になったのでTerminalでやってみました。 ちょっとググったらあったのでメモしておきます。</p> <p>例として、hogehoge.com の /index.html を調べることにします。</p> <p>まずは、以下のコマンドで、接続します。</p> <pre><code>$ openssl s_client -connect hogehoge.com:443 </code></pre> <p>この際、SSLの情報も見れます。 つづけて、</p> <pre><code>GET /index.html HTTP/1.1 Host: hogehoge.com [さらにreturn] </code></pre> <p>と打ちましょう。returnを2回打たないといけないので注意です。 これで、気軽にレスポンスヘッダとSSLの情報がみれました。</p>https://techs-empty.tumblr.com/post/25550248525https://techs-empty.tumblr.com/post/25550248525Thu, 21 Jun 2012 12:00:00 +0900serverkuruma3iOS6のpassbookのAPIを眺めてみた<h1>passbook</h1> <p>WWDCで個人的に一番気になったのが、passbookでした。 passbookは、iOS6から乗るクーポンや映画チケットなどの管理アプリってところです。 NFCへの布石にもなっていそうな本アプリ。 web serverとどうやって連動するのか調べておこうという趣旨で、ざっとSDKを見てみました。</p> <h2>何となく分かったこと</h2> <ul> <li>API等の技術仕様はpass kitというSDKにまとまっている。</li> <li>クーポンなどのCRUD(発行、更新、削除)はREST-apiでserver側と連動して作成する。</li> <li>発行時は、背景画像やテキストなどをセットにしたディレクトリをzipで固めて配布可能</li> <li>配布内容は、jsonで色々指定できる。</li> <li>セキュリティ担保のため、ファイル内容はSHA1で暗号化する</li> </ul> <h2>所感</h2> <ul> <li>epubなどの配布パッケージ形式に セキュリティ担保用の暗号化仕様と、内容更新や削除などのREST-api対応を含めたような仕様になっている。</li> <li>削除などがきちんと担保できている点は嬉しい</li> <li>内容が随時よって更新できたりするのも面白い(ポイントカード的にも利用可能)</li> </ul> <h2>結論</h2> <ul> <li>いわゆるweb標準に近いので、我々のサービに取り込みが比較的容易に可能</li> <li>規格がある意味一般的なものでかつ、公開されているので、Androidなどに飛び火できる(リーガル面は知りませんが。。)</li> </ul> <p>ということで、盛り上がってくればなかなか面白いサービスなんじゃないかなと。 また、オリジナルのクープン等も発行できそうなので、状況を見てサービスに取り込んでいければなと思いました。</p>https://techs-empty.tumblr.com/post/25349205259https://techs-empty.tumblr.com/post/25349205259Mon, 18 Jun 2012 15:15:23 +0900iphonekuruma3node.jsでhttpのreq.headersから必要な情報を集めるメモ<p>メモ的に書いておきます。</p> <pre><code>console.log req.url console.log new Date() console.log req.headers['x-forwarded-for'] || req.connection.remoteAddress #=&gt; console.log req.headers.referer console.log req.headers["user-agent"] console.log req.headers.cookie </code></pre> <p>以上。</p>https://techs-empty.tumblr.com/post/24595547459https://techs-empty.tumblr.com/post/24595547459Thu, 07 Jun 2012 15:16:31 +0900node.jskuruma3Android4系の標準ブラウザではbackbone.jsのRouterが無限ループする件<h2>起こったバグ</h2> Android4の標準ブラウザにて、構築したサイトを開いたら、なんとリダイレクトの無限ループ。 <h2>設定内容</h2> <p>backbone.jsのrouterを使用して、開かれたURLによって、実行するjsを切り替えていた。</p> <h2>原因</h2> <p>Android2系までは対応していたのに、3系以降対応しなくなったらしい。</p> <p><a href="http://d.hatena.ne.jp/zentoo/touch/20111130/1322667152">http://d.hatena.ne.jp/zentoo/touch/20111130/1322667152</a> </p> <p>ちなみにこのサイト(<a href="http://fmbip.com/">http://fmbip.com/</a>)で対応しているかどうかチェックできる。</p> <p>で、backboneではpushStateに対応していないと、URLのパス部分をfragmentとして、</p> <p>hashタグに変換して、リダイレクトする仕様になっているが、<br/>そのあたりでどうも無限ループに陥っていた。</p> <h2>対処</h2> <p>Backbone.Historyの引数とかnavigateでなんとか設定できないか探してみたが、わからなかったので、外部からstartの関数を書き換えることにした。</p> <p>今回の制約事項として、スマホのみ。なのでIEの行は不要</p> <pre><code> // Start the hash change handling, returning `true` if the current URL matches // an existing route, and `false` otherwise. Backbone.History.prototype.start = function(options) { if (Backbone.History.started) throw new Error("Backbone.history has already been started"); Backbone.History.started = true; // Figure out the initial configuration. Do we need an iframe? // Is pushState desired ... is it available? this.options = _.extend({}, {root: '/'}, this.options, options); this._wantsHashChange = this.options.hashChange !== false; this._wantsPushState = !!this.options.pushState; // 20120603 mod for Android4.0 //this._hasPushState = !!(this.options.pushState &amp;&amp; window.history &amp;&amp; window.history.pushState); this._hasPushState = !!(this.options.pushState &amp;&amp; window.history); var fragment = this.getFragment(); var docMode = document.documentMode; //var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) &amp;&amp; (!docMode || docMode ').hide().appendTo('body')[0].contentWindow; //this.navigate(fragment); //} // Depending on whether we're using pushState or hashes, and whether // 'onhashchange' is supported, determine how we check the URL state. if (this._hasPushState) { $(window).bind('popstate', this.checkUrl); } else if (this._wantsHashChange &amp;&amp; ('onhashchange' in window) &amp;&amp; !oldIE) { $(window).bind('hashchange', this.checkUrl); } else if (this._wantsHashChange) { this._checkUrlInterval = setInterval(this.checkUrl, this.interval); } // Determine if we need to change the base url, for a pushState link // opened by a non-pushState browser. this.fragment = fragment; var loc = window.location; var atRoot = loc.pathname == this.options.root; // If we've started off with a route from a `pushState`-enabled browser, // but we're currently in a browser that doesn't support it... //if (this._wantsHashChange &amp;&amp; this._wantsPushState &amp;&amp; !this._hasPushState &amp;&amp; !atRoot) { //this.fragment = this.getFragment(null, true); //window.location.replace(this.options.root + '#' + this.fragment); //// Return immediately as browser will do redirect to new url //return true; //// Or if we've started out with a hash-based route, but we're currently //// in a browser where it could be `pushState`-based instead... //} else if (this._wantsPushState &amp;&amp; this._hasPushState &amp;&amp; atRoot &amp;&amp; loc.hash) { //this.fragment = this.getHash().replace(routeStripper, ''); //window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment); //} if (!this.options.silent) { return this.loadUrl(); } } </code></pre> <h2>その他</h2> <p>そもそも、pushStateが使えなくても動くような、別なプラグインを入れるべきかもしれない。</p>https://techs-empty.tumblr.com/post/24381063410https://techs-empty.tumblr.com/post/24381063410Mon, 04 Jun 2012 12:15:00 +0900androidbrowserpushStatebackbone.jseijik-blogスマホでアプリっぽいサイト作成<p>スマホでアプリっぽいWebシステムを作ろうとした時の設計について書いておきます。</p> <h2>基本情報</h2> <ul> <li> サーバrails2.3.7 <ul> <li>APIは標準のCRUDにすこし付け足している。</li> <li>基本agent別にviewを切り替えて表示</li> </ul> </li> <li> 対応ブラウザ <ul> <li>Android:brawser(標準)</li> <li>Iphone:mobile safari</li> </ul> </li> </ul> <br/> <h2>要点</h2> <p> </p><ol> <li>JqueryMobile(v1.1.0)を使って、さくっと構築しようとしたが、Androidでまともに動かず断念。。。</li> <li>jQueryのanimationでスライドイン、スライドアウトさせて、アプリっぽい動きをさせたが、結局、Androidでselect問題(=&gt;前の記事参照)があり、断念。。。</li> <li>JQMを使っていた部分はそのままhtml描画して、その後にjsを動かしたが、それだけでカバーできない部分があったので、一部backbone.jsを使用した。</li> </ol> <br/> <h2>Androidのみ対応している部分</h2> <ul> <li>inputのdatetimeの入力補助はIphoneでは標準ので十分だが、Androidは必要なので下記のプラグインを使用。<br/> <a href="http://download.mobiscroll.com/">http://download.mobiscroll.com/</a></li> </ul> <br/> <h2>今後開発する際に確認しておくべき事</h2> <ol> <li>JQMがもっとバージョンアップしたとしても、Androidで確実に動くか始めにチェックすべき</li> <li>Androidで動いているものはIphoneでほぼ動くので、基本Androidで確認していくべき。</li> <li>サーバのAPIを用意してから、backbone.jsで全て作るべき。</li> </ol> <br/> <h2>Opera miniについて</h2> <p> AndroidでシェアがそれなりにあるOpera miniは画面が崩れまくって、 リンクがおかしくなって、対応に相当かかるのでとりあえず対応から外した。  </p> <br/>https://techs-empty.tumblr.com/post/24373609004https://techs-empty.tumblr.com/post/24373609004Mon, 04 Jun 2012 10:24:00 +0900スマホsmartアプリサイトbackbone.jsjquerymobileeijik-blogFluentd: Log Everything in JSON<a href="http://fluentd.org/">Fluentd: Log Everything in JSON</a>: <p>Fluentd is a log collector daemon written in Ruby. Fluentd receives log as JSON streams, buffers them, and storeds or forwards to other systems like MongoDB, or even other instances of Fluentd.</p>https://techs-empty.tumblr.com/post/24187050714https://techs-empty.tumblr.com/post/24187050714Fri, 01 Jun 2012 18:12:35 +0900kuruma3Androidのブラウザにて、selectで値を選択しても表示が更新しない<p>表題のようなバグに遭遇し、結構苦労したのでメモです。</p> <pre><code>&lt;div class="parent"&gt; &lt;select&gt; &lt;option&gt;1&lt;/option&gt; &lt;option&gt;2&lt;/option&gt; &lt;/select&gt; &lt;/div&gt; </code></pre> <p>となっている場合に、jQueryで、</p> <pre><code>$(".parent").hide() $(".parent").show() </code></pre> <p>していると、2回目以降で表示が変更されない場合がありました。 結構謎な現象でしたが、 <a href="http://tachesimazzoca.blogspot.jp/2011/12/android-mobile-safari-select.html">http://tachesimazzoca.blogspot.jp/2011/12/android-mobile-safari-select.html</a> を参考に、</p> <pre><code>$(".parent").hide() #=&gt; $(".parent").attr("style","display:none") $(".parent").show() #=&gt; $(".parent").attr("style","display:block") </code></pre> <p>と変更することで動きました。 なぜこれで動くのかは不明です。</p> <h2>追記 by kosaki</h2> <p>上記の設定をし、animationで画面を表示したのちスライドインで表示していましたが、結局同じ問題が出てきたので、animationを使わずスライドインさせない事で問題は解決。</p> <pre><code>//$(.parent).attr('style', 'display;block; right:400px;').animate('right:0px', 550); =&gt; $(.parent).attr('style','display:block;'); </code></pre>https://techs-empty.tumblr.com/post/23916377144https://techs-empty.tumblr.com/post/23916377144Mon, 28 May 2012 15:24:00 +0900androidkuruma3jpmobileのスマホの文字コード<h1>前提</h1> <ul> <li>ruby 1.8.7</li> <li>rails 2.3.9</li> <li>jpmobile branch 2-3 -&gt; <a href="https://github.com/jpmobile/jpmobile/tree/rails-2-3">https://github.com/jpmobile/jpmobile/tree/rails-2-3</a> 注意:rails2系ではjpmobileのブランチが異なる!</li> </ul> <h1>現象</h1> <ul> <li>safariでエージェントをiphoneに変更して、ページを表示したときにsjis(自動選択)になる。</li> <li>ヘッダー部分の定義はutf8になっている。</li> <li>jqueryのajax通信でputすると、railsサイドのcontrollerで文字化けしていた。</li> </ul> <h1>原因</h1> <ul> <li>スマホの時の通信でもmobile_filterが有効になっていて、utf8の文字を送信したが、sjisとして自動的にutf8へ文字変換され、文字化けを引き起こしていた。</li> </ul> <h1>修正方法</h1> <p>mobile_filterの処理を飛ばすのはSoftbankとVodaphoneのみになっていたので、IphoneとAndroidを追記する。 config/initialize/jpmobile.rb というファイルを新規作成して、下記のコードでオーバーライドする。</p> <blockquote> <h1>-<em>- coding: utf-8 -</em>-</h1> <p># module Jpmobile # =文字コードフィルタモジュール。 module Filter # Shift_JISとUnicodeのフィルタ(NKFを使用) class Sjis &lt; Base # to_internalを適用するべきかどうかを返す。 def apply_incoming?(controller) # Vodafone 3G/Softbank(Shift-JISにすると絵文字で不具合が生じる)以外の # 携帯電話の場合に適用する。 # 20120510 kosaki add Iphone &amp; Android mobile = controller.request.mobile mobile &amp;&amp; !(mobile.instance_of?(Jpmobile::Mobile::Vodafone)||mobile.instance_of?(Jpmobile::Mobile::Softbank)||mobile.instance_of?(Jpmobile::Mobile::Iphone)||mobile.instance_of?(Jpmobile::Mobile::Android)) end end end end</p> </blockquote>https://techs-empty.tumblr.com/post/22767288849https://techs-empty.tumblr.com/post/22767288849Thu, 10 May 2012 14:38:02 +0900jpmobilerails2eijik-blogThe Making of GUI Design Icons Font | Vectortuts<a href="http://vector.tutsplus.com/articles/case-study/the-making-of-gui-design-icons-font/">The Making of GUI Design Icons Font | Vectortuts</a>: <p>fontfaceでアイコンを使っているけれど、さらに一歩進んで、枠と内容に分けて組み合わせるアイディアを実現化しているフォント。 素晴らしい。</p>https://techs-empty.tumblr.com/post/22248254815https://techs-empty.tumblr.com/post/22248254815Wed, 02 May 2012 18:23:33 +0900kuruma3元rubistがうっかり間違えそうな、coffeescriptの比較式<p>rubyと違って、coffeescript(javascript)だと色々false判定される場合があったのでメモしておきます。</p> <pre><code>if val then "TRUE" else "FALSE" </code></pre> <table> <tr><th>val</th><th>result</th></tr> <tr><th>undefined</th><td>FALSE</td></tr> <tr><th>undefined?</th><td>FALSE</td></tr> <tr><th>null</th><td>FALSE</td></tr> <tr><th>null?</th><td>FALSE</td></tr> <tr><th>false</th><td>FALSE</td></tr> <tr><th>false?</th><td>TRUE</td></tr> <tr><th>0</th><td>FALSE</td></tr> <tr><th>0?</th><td>TRUE</td></tr> <tr><th>-1</th><td>TRUE</td></tr> <tr><th>-1?</th><td>TRUE</td></tr> <tr><th>NaN</th><td>FALSE</td></tr> <tr><th>NaN?</th><td>TRUE</td></tr> <tr><th>&ldquo;&rdquo;</th><td>FALSE</td></tr> <tr><th>&ldquo;&rdquo;?</th><td>TRUE</td></tr> </table> <p>以上です。?関係は、 a? が、</p> <pre><code>typeof a !== "undefined" &amp;&amp; a !== null; </code></pre> <p>なので、当然かもしれませんが。</p>https://techs-empty.tumblr.com/post/20879791532https://techs-empty.tumblr.com/post/20879791532Wed, 11 Apr 2012 10:55:05 +0900coffeescriptkuruma3iPad版Safariとmac版Safariのinput type="datetime"系まとめ<p>iPad版のView作ってくれという依頼がきたので、responsiveなcss入れたりして色々やっていました。</p> <p>日付系の動作についてまとまっているところがなかったので、まとめておきます。 基本的には、iPad版のSafariの方がバグっぽいです。</p> <table> <tr> <th>type</th><th> </th><th>iPad Safari(iOS5.0.1)</th><th> </th><th>mac Safafi(5.1.3)</th><th> </th></tr> <tr> <th>datetime</th><th> </th><td>local時間から開始、stepは効かない</td><td> </td><td>UTC時間から開始、stepもちゃんと効く</td><td> </td></tr> <tr> <th>datetime-local</th><th> </th><td>local時間から開始、stepは効かない</td><td> </td><td>local時間から開始、stepもちゃんと効く</td><td> </td></tr> <th>time/date</th><th> </th><td>local時間から開始、stepは効かない</td><td> </td><td>local時間から開始、stepもちゃんと効く</td><td> </td></table> <p>以上です。</p>https://techs-empty.tumblr.com/post/20580402496https://techs-empty.tumblr.com/post/20580402496Fri, 06 Apr 2012 18:10:22 +0900iPadhtml5kuruma3Baakbone.Viewの@optionsに嵌った件<p>嵌ったので、メモおよび共有しておきます。 嵌ったコードは下記です。</p> <pre><code>hoge = Backbone.View.extend initialize:(opts={}) @options = opts </code></pre> <p>一見何も問題なさそうなんですが、Backbone.Viewが内部的に@optionsを処理していて、2回目以降のrenderでdelegateEventsがbindしないということになってしまいました。</p> <p>しかも、よくdocを読むと、</p> <pre><code>constructor / initializenew View([options]) When creating a new View, the options you pass are attached to the view as this.options, for future reference. There are several special options that, if passed, will be attached directly to the view: model, collection, el, id, className, tagName and attributes. If the view defines an initialize function, it will be called when the view is first created. If you'd like to create a view that references an element already in the DOM, pass in the element as an option: new View({el: existingElement}) </code></pre> <p>と、書いており、@optionsは標準で提供されているようですね。</p> <p>つまり、<strong>初期化のときに代入したオプションは、@optionsで無条件で取れる</strong>ということでした。嵌ってしまったorz</p>https://techs-empty.tumblr.com/post/20580278826https://techs-empty.tumblr.com/post/20580278826Fri, 06 Apr 2012 18:02:51 +0900backbone.jskuruma3node.jsの運用にclusterを使うことにした<p>下記の記事は、完璧に誤解してました。 同じ名前で、別のclusterってのがあるようでした。</p> <p>本家に取り込まれている(http://nodejs.org/api/cluster.html)と、(<a href="https://github.com/LearnBoost/cluster">https://github.com/LearnBoost/cluster</a>) は別物でした。</p> <hr> <p>node.jsを運用に載せる際に、webサーバーをどうしようかなと思っていましたが、 遅ればせながら、clusterが本家に取り込まれていることを知りました。</p> <p><a href="http://nodejs.org/api/cluster.html">http://nodejs.org/api/cluster.html</a></p> <p>ということで、今後の継続的な開発は全く心配無し。 内容的にも、ダウンタイムゼロでの再起動など、やりたいことを十分満たしていました。</p> <p><a href="https://github.com/LearnBoost/cluster">https://github.com/LearnBoost/cluster</a></p> <ul> <li>zero-downtime restart</li> <li>hard shutdown support</li> <li>graceful shutdown support</li> <li>resuscitates workers</li> <li>maintains worker count, even if worker was _SIGKILL_ed.</li> <li>workers commit suicide when master dies</li> <li>spawns one worker per cpu (by default)</li> <li>extensible via plugins</li> </ul> <p>十分すぎるので、しばらくこいつでテストしてみようと思います。</p>https://techs-empty.tumblr.com/post/20459291811https://techs-empty.tumblr.com/post/20459291811Wed, 04 Apr 2012 17:58:00 +0900node.jsclusterkuruma3jsでi18nの運用を考える<p>クライアントサイドjsでのi18nと言えば、様々なツールがありますが、 シンプルに自分でも実装できるかなと思い、運用方法も含めて自分たちにあった方法を考えてみました。</p> <h2>方針</h2> <p>基本的には、window.i18nという大きなObjectを作成して、keyにi18nのkey+localeを、valueに翻訳された値を入れておく</p> <p>後は、適当にsyntax sugar用の関数を用意してあげる</p> <h2>Objectの構造</h2> <p>普通の翻訳ファイルでは、翻訳者が異なることを想定し、言語毎にファイルを作成して運用することが多い。 そのため、トップレベルで言語が分かれるObjectになる。</p> <pre><code>i18n = ja: users: name: "名前" en: users: name: "name" </code></pre> <p>社内ではこの形式で弊社でも運用しているが、 各言語毎にファイルを作るため、言語ファイルが大きくなってくると、 各言語毎に翻訳項目の抜け漏れがないか調べにくくなってきてしまっていた。</p> <p>そのため、script等を作成してチェックしていたが、なかなか億劫な作業でもある。</p> <p>そこで、今回は思い切って標準的なトップレベルでの言語切り分けから、 下記のような、最末端での言語切替のObjectで運用してみようと考えた。</p> <pre><code>i18n = users: name: ja: "名前" en: "name" </code></pre> <p>メリットとしては、属性毎に翻訳していくので、自然な構造なので、抜け漏れなどが分かりやすく、システム化もしやすいと判断した。</p> <h2>Editor</h2> <p>次に、このObjectをどのように作って行くか考えた。 最終的には、jsonからjsのObjectにするのだが、jsonを手で書くのが面倒なので、 思い切ってnumbers=csv等で管理するのはどうかなと思いついた。</p> <p>例としてはこんな感じ</p> <table> <tr> <th>key</th> <th>ja</th> <th>en</th> </tr> <tr> <td>users.name</td> <td>名前</td> <td>name</td> </tr> <tr> <td>users.email</td> <td>メールアドレス</td> <td>email</td> </tr> </table> <p>で、このcsvをjsonに変換して、そいつをjsとして読み込んでしまおうという作戦にすることに。</p> <p>csvにすることで、ささっと手で直すこともできるし、 mergeとかが面倒であれば、GoogleSpreadSheet使っても良いし、Editorとしてはなかなか良い感じがする。</p> <h2>csv2js</h2> <p>次に、csvからjsに変換するscriptを書いた。</p> <p>まずは、csvからjsonに変換するコードをささっとruby(1.9.3)で。</p> <p>csv2json</p> <pre><code> 1 #!/usr/bin/env ruby 2 3 require 'csv' 4 require 'json' 5 6 reader = CSV.open("./js/i18n.csv","r") 7 header = reader.take(1)[0] 8 9 i2s = header.each.with_index.inject({}){|h,(attr,i)| h[i.to_s]=attr.strip;h } 10 i18n = {} 11 12 class Hash 13 def nset(keys=[],val) 14 k = keys.shift 15 keys.empty? ? self[k] = val : (self[k]||={}; self[k].nset(keys,val)) 16 end 17 end 18 19 reader.each do |row| 20 keys = [] 21 val = {} 22 row.each.with_index do |c,i| 23 if (attr = i2s[i.to_s]) == "key" 24 keys = c.split(".") 25 else 26 case c 27 when "BLANK"; val[attr] = "" 28 when nil; "" 29 else; val[attr] = c 30 end 31 end 32 end 33 i18n.nset(keys,val) 34 end 35 36 puts i18n.to_json 37 </code></pre> <p>で、こいつを使って、csv2jsをさくっと作成</p> <p>csv2js</p> <pre><code>1 #!/bin/bash 2 3 echo -n "window.i18n=" 4 ./script/csv2json </code></pre> <p><img src="https://64.media.tumblr.com/tumblr_m1jih6rymy1qesezw.png" alt=""/></p> <p>あとは、こいつを所定のファイルに吐き出してあげればOKです。 ここまでで、numbersでcsvを作って、jsのObjectにするまでができました。</p> <h2>syntax sugar 用の関数の作成</h2> <p>で、最後にRailsっぽく、tというメソッドを作りました。 なので、t(&ldquo;users.name&rdquo;)とかで呼べます。</p> <pre><code> 1 $ -&gt; 2 #### t 3 # keyとlocaleを設定して翻訳Objectから値を持ってくる 4 # localeに"obj"が渡されたときは、objを表示する 5 # * 翻訳があるもの #=&gt; 設定した文字列を表示 6 # * 翻訳していないものやそもそも存在していないもの #=&gt; keyの最後の部分を表示 7 window.t =(key,locale)-&gt; 8 try 9 locale ||= app.currentUser().locale 10 if locale=="obj" 11 val = _.nget window.i18n, key 12 else 13 val = _.nget window.i18n, "#{key}.#{locale}" 14 if val? 15 return val 16 else 17 return key.split(".").last() 18 catch e 19 console.log e 20 return key.split(".").last() </code></pre> <p>少し拡張して、下記のような仕様にしています。 t(&ldquo;user.name&rdquo;) #=&gt; &ldquo;名前&rdquo; t(&ldquo;user.name&rdquo;,&ldquo;en&rdquo;) #=&gt; &ldquo;name&rdquo; t(&ldquo;user.name&rdquo;,&ldquo;ja&rdquo;) #=&gt; &ldquo;名前&rdquo; t(&ldquo;user.name&rdquo;,&ldquo;??&rdquo;) #=&gt; &ldquo;name&rdquo;(keyの最後) t(&ldquo;user.nam&rdquo;,&ldquo;ja&rdquo;) #=&gt; &ldquo;nam&rdquo;(keyの最後) t(&ldquo;user.name&rdquo;,&ldquo;obj&rdquo;) #=&gt; {en: &ldquo;name&rdquo;, ja: &ldquo;名前&rdquo;}</p> <p>これで、あとはjsから使えば楽しく使えそうです。 Objectの構造も言語を最後にしているので、</p> <pre><code>t("user.name","obj") #=&gt; {en: "name", ja: "名前"} </code></pre> <p>のようなこともできるようになっています。</p> <h2>まとめ</h2> <p>numbers -&gt; csv -&gt; jsのObject のフローを運用を考えながら作りました。 numbersで見ると翻訳の抜け漏れも分かりやすかったり、ソートできたりと便利な上に、 yamlを翻訳者に書いてもらう必要もなくなりました。</p> <p>jsだとシンプルにこんなツールが作れるので楽しいですね。</p>https://techs-empty.tumblr.com/post/20004409362https://techs-empty.tumblr.com/post/20004409362Tue, 27 Mar 2012 20:01:38 +0900javascripti18nkuruma3gimite/web-socket-js · GitHub<a href="https://github.com/gimite/web-socket-js/">gimite/web-socket-js · GitHub</a>: <p>websocketをIEでも使えるらしいjs</p>https://techs-empty.tumblr.com/post/18894420300https://techs-empty.tumblr.com/post/18894420300Wed, 07 Mar 2012 16:58:49 +0900kuruma3mysqltuner.plを使ってmysqlのパラメータを再度調整<p>前回のポストの値で大丈夫かなと色々調べてみると、mysqltuner.plというものがあることを発見。 早速使ってみました。</p> <h2>利用まで</h2> <p>とりあえずインストールして回してみました。</p> <pre><code>$ wget <a href="https://raw.github.com/rackerhacker/MySQLTuner-perl/master/mysqltuner.pl">https://raw.github.com/rackerhacker/MySQLTuner-perl/master/mysqltuner.pl</a> $ chmod +x mysqltuner.pl $ ./mysqltuner.pl $ ./mysqltuner.pl 省略 -------- Recommendations ----------------------------------------------------- General recommendations: Enable the slow query log to troubleshoot bad queries When making adjustments, make tmp_table_size/max_heap_table_size equal Reduce your SELECT DISTINCT queries without LIMIT clauses Increase table_cache gradually to avoid file descriptor limits Variables to adjust: query_cache_size (&gt;= 8M) tmp_table_size (&gt; 32M) max_heap_table_size (&gt; 16M) thread_cache_size (&gt; 900) table_cache (&gt; 900) innodb_buffer_pool_size (&gt;= 2G) </code></pre> <p>とオススメ値が色々出ました。 これは楽チン</p> <p>ということで、設定を下記に変更しておきました。</p> <pre><code>[mysqld] max_connections=300 query_cache_size=32M tmp_table_size=1024M max_heap_table_size=1024M thread_cache_size=1200 table_cache=1200 innodb_buffer_pool_size=2G </code></pre>https://techs-empty.tumblr.com/post/17701962095https://techs-empty.tumblr.com/post/17701962095Thu, 16 Feb 2012 15:28:40 +0900mysqlkuruma3