Avatar

@techs.empty?

@techs-empty / techs-empty.tumblr.com

ガリレオスコープ開発チームによる 最新Web動向、技術メモ ( Ruby, Rails, node.js ) です

ssh-copy-idをmacosxで使えるようにする

ssh-copy-id というsshの公開鍵をリモートサーバーに登録する ちょっと便利なコマンドがあるんですが、macでbrewでインストールできなかったので、メモです。 下記のふぁいるを、/usr/local/binなどに作成します。

$ vi /usr/local/bin/ssh-copy-id #=> 下記のファイルを貼付け $ chmod +x /usr/local/bin/ssh-copy-id $ ssh-copy-id -i ~/.ssh/id_rsa.pub user@host #=> ~/.ssh/id_rsa.pub を指定したサーバーに設定

(ssh-copy-id)

#!/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`" ] && [ -r "${ID_FILE}" ] ; then GET_ID="cat ${ID_FILE}" fi if [ -z "`eval $GET_ID`" ]; then echo "$0: ERROR: No identities found" >&2 exit 1 fi if [ "$#" -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then echo "Usage: $0 [-i [identity_file]] [user@]machine" >&2 exit 1 fi { eval "$GET_ID" ; } | ssh $1 "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" || exit 1 cat

Bundleではまった最悪なこと

## 状況 sporkを走らせようとしたが、なぜかrequire 'spork'ができずこまった。rspec/railsもロードされていないし。 もう少し調べてみると、どうもGemfileのdevelop & test groupにいれたgemがロードできない。 なんじゃこりゃ〜 ## 原因 サーバ側でbundle installした時に下記のコードで実行した。 bundle install --without development test これ自体は問題はない。が、 ここから誰かがサーバ側で作業して、リポジトリにpushした。それを自分がpullした。 そうすると、.bundle/configというファイルが勝手にできていて、下記のようになっている。 1 --- 2 BUNDLE_WITHOUT: development:test これによって、developmentとtestのgroup中のgemは無視されるらしい。。。 ## 対処 上記の2行目を消す事は当然で、 予防策として、.hgignoreで必ず.bundleを無視しておく必要があると思う。 vim .hgignore + ^\.bundle ## 参照 ## 一応ここらへんに書いてあったが、もっとわかりやすくしておいてくれ〜 http://gembundler.com/groups.html

rails2.3.9 -> 2.3.14にアップしたときの注意点

いまさらこんなレガシーなのを使っているのは、よろしくないが、 現実は3系にあげられないでいる。 そうすると、似たような問題を検索しても引っかかってこない。 ## 現象 何かしらのオブジェクトに対して、ネストしたパラメータがあると、以前は行けたのに、アップ後はエラーとなる。 * 2.3.9 -> OK >> r = Reservation.new >> r.attributes = {:start_attributes=>{:id=>'1005'}} => {:start_attributes=>{:id=>"1005"}} >> r.start (=>オブジェクトが表示される。) * 2.3.14 -> error >> r = Reservation.new >> r.attributes = {:start_attributes=>{:id=>'1004'}} ActiveRecord::RecordNotFound: Couldn't find Place with ID=1004 for Reservation with ID= ## 問題点 以前のバージョンでは、start_attributesのidからオブジェクトを取得しているようだが、アップ後はエラーを出している。 * 2.3.9 L288 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) * 2.3.14 L288 elsif attributes['id'] raise_nested_attributes_record_not_found(association_name, attributes['id']) https://github.com/rails/rails/blob/v2.3.9/activerecord/lib/active_record/nested_attributes.rb https://github.com/rails/rails/blob/v2.3.14/activerecord/lib/active_record/nested_attributes.rb ## 対処 おそらく、なんらかの理由があって、変更しているのだろうが、 これを使っている箇所全体を新しい書き方に変更するのはかなりのコストなので、 以前の書き方に戻すようにパッチを当てることにした。 config/initializers/active_record.rbとか適当にファイルを作成して 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 && (record = send(association_name)) && (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

Basic認証がかかったHTTPサイトのresponse headerをtelnetで見る方法

続けて、HTTPのサイトをtelnetで見ることになったので同じようなネタでもう一つ

HTTPSと同じように、hogehoge.com:80をにアクセスしようとするとこんな感じです。 まずは、Basic認証がない場合から、

$ telnet hogehoge.com 80 > GET /index.html HTTP/1.1 > Host: hogehoge.com > [returnを2回]

で、さらに、Basic認証がかかっているばあいは、

$ telnet hogehoge.com 80 > GET /index.html HTTP/1.1 > Host: hogehoge.com > Authorization: Basic aG9nZWhvZ2U6aG9nZWhvZ2U= > [returnを2回]

と、Authorizationも含めておくってあげればOKです。 Basicの後の文字列は、Basic認証なので、"ID:PW"をbase64化したものを送ればOKです。

 $ echo -n 'hogehoge:hogehoge' | openssl enc -e -base64 #=> aG9nZWhvZ2U6aG9nZWhvZ2U=

上記は、IDとPWがhogehogeの場合です。

HTTPSのサイトのレスポンスヘッダをTerminalだけで調べる方法

最初は、SafariやらChromeでやっていたのですが、面倒になったのでTerminalでやってみました。 ちょっとググったらあったのでメモしておきます。

例として、hogehoge.com の /index.html を調べることにします。

まずは、以下のコマンドで、接続します。

$ openssl s_client -connect hogehoge.com:443

この際、SSLの情報も見れます。 つづけて、

GET /index.html HTTP/1.1 Host: hogehoge.com [さらにreturn]

と打ちましょう。returnを2回打たないといけないので注意です。 これで、気軽にレスポンスヘッダとSSLの情報がみれました。

iOS6のpassbookのAPIを眺めてみた

passbook

WWDCで個人的に一番気になったのが、passbookでした。 passbookは、iOS6から乗るクーポンや映画チケットなどの管理アプリってところです。 NFCへの布石にもなっていそうな本アプリ。 web serverとどうやって連動するのか調べておこうという趣旨で、ざっとSDKを見てみました。

何となく分かったこと

  • API等の技術仕様はpass kitというSDKにまとまっている。
  • クーポンなどのCRUD(発行、更新、削除)はREST-apiでserver側と連動して作成する。
  • 発行時は、背景画像やテキストなどをセットにしたディレクトリをzipで固めて配布可能
  • 配布内容は、jsonで色々指定できる。
  • セキュリティ担保のため、ファイル内容はSHA1で暗号化する

所感

  • epubなどの配布パッケージ形式に セキュリティ担保用の暗号化仕様と、内容更新や削除などのREST-api対応を含めたような仕様になっている。
  • 削除などがきちんと担保できている点は嬉しい
  • 内容が随時よって更新できたりするのも面白い(ポイントカード的にも利用可能)

結論

  • いわゆるweb標準に近いので、我々のサービに取り込みが比較的容易に可能
  • 規格がある意味一般的なものでかつ、公開されているので、Androidなどに飛び火できる(リーガル面は知りませんが。。)

ということで、盛り上がってくればなかなか面白いサービスなんじゃないかなと。 また、オリジナルのクープン等も発行できそうなので、状況を見てサービスに取り込んでいければなと思いました。

node.jsでhttpのreq.headersから必要な情報を集めるメモ

メモ的に書いておきます。

console.log req.url console.log new Date() console.log req.headers['x-forwarded-for'] || req.connection.remoteAddress #=> console.log req.headers.referer console.log req.headers["user-agent"] console.log req.headers.cookie

以上。

Android4系の標準ブラウザではbackbone.jsのRouterが無限ループする件

起こったバグ

Android4の標準ブラウザにて、構築したサイトを開いたら、なんとリダイレクトの無限ループ。

設定内容

backbone.jsのrouterを使用して、開かれたURLによって、実行するjsを切り替えていた。

原因

Android2系までは対応していたのに、3系以降対応しなくなったらしい。

http://d.hatena.ne.jp/zentoo/touch/20111130/1322667152

ちなみにこのサイト(http://fmbip.com/)で対応しているかどうかチェックできる。

で、backboneではpushStateに対応していないと、URLのパス部分をfragmentとして、

hashタグに変換して、リダイレクトする仕様になっているが、 そのあたりでどうも無限ループに陥っていた。

対処

Backbone.Historyの引数とかnavigateでなんとか設定できないか探してみたが、わからなかったので、外部からstartの関数を書き換えることにした。

今回の制約事項として、スマホのみ。なのでIEの行は不要

// 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 && window.history && window.history.pushState); this._hasPushState = !!(this.options.pushState && window.history); var fragment = this.getFragment(); var docMode = document.documentMode; //var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); //if (oldIE) { //this.iframe = $('

').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 && ('onhashchange' in window) && !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 && this._wantsPushState && !this._hasPushState && !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 && this._hasPushState && atRoot && 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(); } }

その他

そもそも、pushStateが使えなくても動くような、別なプラグインを入れるべきかもしれない。

スマホでアプリっぽいサイト作成

スマホでアプリっぽいWebシステムを作ろうとした時の設計について書いておきます。

基本情報

  • サーバrails2.3.7
  • APIは標準のCRUDにすこし付け足している。
  • 基本agent別にviewを切り替えて表示
  • 対応ブラウザ
  • Android:brawser(標準)
  • Iphone:mobile safari

要点

  1. JqueryMobile(v1.1.0)を使って、さくっと構築しようとしたが、Androidでまともに動かず断念。。。
  2. jQueryのanimationでスライドイン、スライドアウトさせて、アプリっぽい動きをさせたが、結局、Androidでselect問題(=>前の記事参照)があり、断念。。。
  3. JQMを使っていた部分はそのままhtml描画して、その後にjsを動かしたが、それだけでカバーできない部分があったので、一部backbone.jsを使用した。

Androidのみ対応している部分

  • inputのdatetimeの入力補助はIphoneでは標準ので十分だが、Androidは必要なので下記のプラグインを使用。 http://download.mobiscroll.com/

今後開発する際に確認しておくべき事

  1. JQMがもっとバージョンアップしたとしても、Androidで確実に動くか始めにチェックすべき
  2. Androidで動いているものはIphoneでほぼ動くので、基本Androidで確認していくべき。
  3. サーバのAPIを用意してから、backbone.jsで全て作るべき。

Opera miniについて

AndroidでシェアがそれなりにあるOpera miniは画面が崩れまくって、 リンクがおかしくなって、対応に相当かかるのでとりあえず対応から外した。 

Androidのブラウザにて、selectで値を選択しても表示が更新しない

表題のようなバグに遭遇し、結構苦労したのでメモです。

<div class="parent"> <select> <option>1</option> <option>2</option> </select> </div>

となっている場合に、jQueryで、

$(".parent").hide() $(".parent").show()

していると、2回目以降で表示が変更されない場合がありました。 結構謎な現象でしたが、 http://tachesimazzoca.blogspot.jp/2011/12/android-mobile-safari-select.html を参考に、

$(".parent").hide() #=> $(".parent").attr("style","display:none") $(".parent").show() #=> $(".parent").attr("style","display:block")

と変更することで動きました。 なぜこれで動くのかは不明です。

追記 by kosaki

上記の設定をし、animationで画面を表示したのちスライドインで表示していましたが、結局同じ問題が出てきたので、animationを使わずスライドインさせない事で問題は解決。

//$(.parent).attr('style', 'display;block; right:400px;').animate('right:0px', 550); => $(.parent).attr('style','display:block;');

jpmobileのスマホの文字コード

# 前提 * ruby 1.8.7 * rails 2.3.9 * jpmobile branch 2-3 -> https://github.com/jpmobile/jpmobile/tree/rails-2-3 注意:rails2系ではjpmobileのブランチが異なる! # 現象 * safariでエージェントをiphoneに変更して、ページを表示したときにsjis(自動選択)になる。 * ヘッダー部分の定義はutf8になっている。 * jqueryのajax通信でputすると、railsサイドのcontrollerで文字化けしていた。 # 原因 * スマホの時の通信でもmobile_filterが有効になっていて、utf8の文字を送信したが、sjisとして自動的にutf8へ文字変換され、文字化けを引き起こしていた。 # 修正方法 mobile_filterの処理を飛ばすのはSoftbankとVodaphoneのみになっていたので、IphoneとAndroidを追記する。 config/initialize/jpmobile.rb というファイルを新規作成して、下記のコードでオーバーライドする。 ># -*- coding: utf-8 -*- ># >module Jpmobile > # =文字コードフィルタモジュール。 > module Filter > # Shift_JISとUnicodeのフィルタ(NKFを使用) > class Sjis < Base > # to_internalを適用するべきかどうかを返す。 > def apply_incoming?(controller) > # Vodafone 3G/Softbank(Shift-JISにすると絵文字で不具合が生じる)以外の > # 携帯電話の場合に適用する。 > # 20120510 kosaki add Iphone & Android > mobile = controller.request.mobile > mobile && !(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

元rubistがうっかり間違えそうな、coffeescriptの比較式

rubyと違って、coffeescript(javascript)だと色々false判定される場合があったのでメモしておきます。

if val then "TRUE" else "FALSE"

valresult undefinedFALSE undefined?FALSE nullFALSE null?FALSE falseFALSE false?TRUE 0FALSE 0?TRUE -1TRUE -1?TRUE NaNFALSE NaN?TRUE ""FALSE ""?TRUE

以上です。?関係は、 a? が、

typeof a !== "undefined" && a !== null;

なので、当然かもしれませんが。

iPad版Safariとmac版Safariのinput type="datetime"系まとめ

iPad版のView作ってくれという依頼がきたので、responsiveなcss入れたりして色々やっていました。

日付系の動作についてまとまっているところがなかったので、まとめておきます。 基本的には、iPad版のSafariの方がバグっぽいです。

type iPad Safari(iOS5.0.1) mac Safafi(5.1.3) datetime local時間から開始、stepは効かない UTC時間から開始、stepもちゃんと効く datetime-local local時間から開始、stepは効かない local時間から開始、stepもちゃんと効く time/date local時間から開始、stepは効かない local時間から開始、stepもちゃんと効く

以上です。

Baakbone.Viewの@optionsに嵌った件

嵌ったので、メモおよび共有しておきます。 嵌ったコードは下記です。

hoge = Backbone.View.extend initialize:(opts={}) @options = opts

一見何も問題なさそうなんですが、Backbone.Viewが内部的に@optionsを処理していて、2回目以降のrenderでdelegateEventsがbindしないということになってしまいました。

しかも、よくdocを読むと、

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})

と、書いており、@optionsは標準で提供されているようですね。

つまり、初期化のときに代入したオプションは、@optionsで無条件で取れるということでした。嵌ってしまったorz

node.jsの運用にclusterを使うことにした

下記の記事は、完璧に誤解してました。 同じ名前で、別のclusterってのがあるようでした。 本家に取り込まれている(http://nodejs.org/api/cluster.html)と、(https://github.com/LearnBoost/cluster) は別物でした。 ---------------------- node.jsを運用に載せる際に、webサーバーをどうしようかなと思っていましたが、 遅ればせながら、clusterが本家に取り込まれていることを知りました。 http://nodejs.org/api/cluster.html ということで、今後の継続的な開発は全く心配無し。 内容的にも、ダウンタイムゼロでの再起動など、やりたいことを十分満たしていました。 https://github.com/LearnBoost/cluster * zero-downtime restart * hard shutdown support * graceful shutdown support * resuscitates workers * maintains worker count, even if worker was _SIGKILL_ed. * workers commit suicide when master dies * spawns one worker per cpu (by default) * extensible via plugins 十分すぎるので、しばらくこいつでテストしてみようと思います。

Source: nodejs.org

Sponsored

You are using an unsupported browser and things might not work as intended. Please make sure you're using the latest version of Chrome, Firefox, Safari, or Edge.