ホーム ブログ ページ 51

rspec で、referer や user_agent 関連の機能もテストしよう

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

お恥ずかしながら、TDDを行わずにRailsで開発を行い続けてきたred です。最近、自分でも spec を書く用になりました。かなり捗ります。まだの方、お早めに。

自分にとって、spec を覚えるにあたっての最大の難関は、参考になる情報をなかなか見つけられなかった、ということでした。

結局、自分は、プロジェクトメンバの書いたものをコピペ改変することにより覚えました。

もし、独学の方、まわりにspec を書いている方のいない Rails/Ruby プログラマの方、まずはこのブログの記述もしくは下の私のコードを写経してみて、そこから、RSpec on Rails の使い方を調べるのが、導入によろしいかと思います。

=====

これ以下の内容は、下記環境にて動作確認をしています。

  • ruby 1.8.7
  • rails 2.1.0
  • rspec 1.3.0
  • rspec-rails 1.3.2

=====

直近のプロジェクトでは、ユーザエージェントやリファラを見て、それによって LPO を行うナビゲーションを実装していました。

つまりは、下記のような機能を実装しました。勿論、本物はもっと高機能です。

#{RAILS_ROOT}/app/controllers/test_controller.rb

class TestController < ApplicationController

  # リファラより、どの検索エンジンから流入したかを特定
  def referer
    referer = request.env['HTTP_REFERER']
    if referer.blank?
      @engine = "no referer"
    elsif referer.split("?")[0].include? "google"
      @engine = "google"
    elsif referer.split("?")[0].include? "yahoo"
      @engine = "yahoo"
    else
      @engine = "not determined"
    end
    render :text => @engine
  end
 

  # ユーザエージェントより、どのブラウザよりアクセスしているかを特定
  def ua
    ua = request.env['HTTP_USER_AGENT']
    if ua.include? "MSIE"
      @browser = "ie"
    elsif ua.include? "Firefox"
      @browser = "firefox"
    elsif ua.include? "Chrome"
      @browser = "chrome"
    elsif ua.include? "Opera"
      @browser = "opera"
    elsif ua.include? "safari"
      @browser = "safari"
    else
      @browser = "others"
    end
    render :text => @browser
  end

end

上記のようなアプリがうまく動いているか確認する際、通常のブラウザで見ることが不可能な場合は、Live HTTP Headers や User Agent Switcher のようなツールを使い、ユーザエージェントやリファラを詐称してチェックするのがセオリーかと思います。

spec で上記テストを自動化したい場合、下記のように記述すれば実行することができます。

#{RAILS_ROOT}/spec/controllers/test_controller_spec.rb

require 'spec_helper'

describe TestController do
  
  describe "referer" do
    it "google 検索からの流入かどうか判別するテスト" do
      request.env["HTTP_REFERER"] = "http://www.google.co.jp/search?q=test"
      get "referer"
      response.should be_success
      assigns[:engine].should == "google"
    end
    
    it "yahoo 検索からの流入かどうか判別するテスト" do
      request.env["HTTP_REFERER"] = "http://search.yahoo.co.jp/search?p=test"
      get "referer"
      response.should be_success
      assigns[:engine].should == "yahoo"
    end
  end
  
  describe "ua" do
    it "firefox を使っているかどうか判別するテスト" do
      request.env["HTTP_USER_AGENT"] = "Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.0.17) Gecko/2009122116 Firefox/3.0.17 GTB6 (.NET CLR 3.5.30729)"
      get "ua"
      response.should be_success
      assigns[:browser].should == "firefox"
    end
    
    it "IE を使っているかどうか判別するテスト" do
      request.env["HTTP_USER_AGENT"] = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"
      get "ua"
      response.should be_success
      assigns[:browser].should == "ie"
    end
  end
end

上記のように、get で controller/method にアクセスする前に、request.env の各項目の値を上書きすれば、該当のテストを作成することができます。

dev_machine:rails_root red$ spec spec/controllers/test_controller_spec.rb 
....

Finished in 0.065284 seconds

4 examples, 0 failures
dev_machine:rails_root red$ 

describe や it は配列の each の中での実行も出来るので、テストしたいユーザエージェントやリファラとその結果をDB化しておくことにより、いちいちツールを用いて切り替えることなく、一気にhttp_header 関連のテストが行えるようになりました。

参考:http://stackoverflow.com/questions/542856/how-do-i-set-http-referer-when-testing-in-rails

【FlashLite】機種依存の不具合や仕様について7つの事例。

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

FlashLiteにはFlashLite固有の機種依存による不具合や仕様が数多く存在します。

今回は実際に自分が実際に直面した中から7つの事例をピックアップしてご紹介します。

(1) SH機種でFlashの再生が停止する

【現象】

・SoftBankのSH機種の場合は、画面が真っ白で表示(画像取得失敗と同様の表示)され、閲覧不可となる。
・DocomoのSH機種の場合は、「画像に誤りがあり、正しく動作しません」というメッセージが表示され、閲覧不可となる。

【原因】

無限ループが発生しているため。大半の携帯端末はFlash内で無限ループが発生した際に無視するが、一部SH機種はそのまま実行後、実行回数制限によりFlashの再生を停止する。

【解決方法】

無限ループが発生する場合のほとんどはコーディングミスである可能性が高いため、細かくtraceしながらデバッグを行うしかありません。
※次項にて意図しない無限ループが発生する特殊な事例についてご紹介しています。

(2) 一部端末で無限ループが発生する

【現象】

一部端末で無限ループが発生する

【原因】

「for(i=0; i < NaN; i++){}」となった場合に無限ループが発生するため。

【解決方法】

for文のlimit指定が「NaN」になっていないかどうかtraceをしながらデバッグするしかありません。 例えば、「int(“”)」等で「NaN」になっている可能性もあります。

(3) System.capabilities.hasSharedObjectsの判定結果が誤っている

【現象】

Panasonic製/TOSHIBA製のSoftbank端末で「System.capabilities.hasSharedObjects」を実行すると、Shared Object が利用できないにもかかわらず「true」が返される。
この結果を受けて、SharedObject.addListener を実行している場合はそのまま処理が停止する恐れがあるため注意が必要です。

【解決方法】

System.capabilities.hasSharedObjects と同等の判定を他の方法で行うことはほぼ不可能。

【代替案】

System.capabilities.hasSharedObjects の結果が「true」の場合に、SharedObject.addListenerを実行している場合、
一定時間Listenerが走らない場合は強制的にremoveListenerを実行後、次処理へ移行させる等の対応が必要です。
※設定時間が短すぎると、正常にデータをロード中の端末において読み込み完了前に次処理に強制移行されてしまうので注意が必要です。

(4) _rotationを指定すると、_xscale, _yscaleの値が勝手に変わってしまう

【現象】

_rotationにFloat型の数値を指定すると 対象MCの_xscale, _yscaleが伸縮されてしまう(_rotation後に_xscale, _yscaleを再定義する必要有り)

【発生例】

アナログ時計の針を動かす処理等

【原因】

45°毎の _rotation 指定しか保証されていないため(?)

【解決方法】

_rotation後に_xscale, _yscaleを再設定することで処理は増えてしまいますが、見た目の崩れを防ぐことができます。

(5) auの一部端末にてPOSTができない

【現象】

auの一部端末でPOST通信ができない

【発生例】

・swfから大量の入力テキストをPOSTでサーバーに送りたい場合等
・HTML内のPOSTが指定された検索フォームから検索した結果としてswfのページを表示させたい場合等

【解決方法】

なし

【代替案】

swfの前後の通信はGET形式で統一します

(6) デバイスフォントが勝手に縁取りされる

【現象】

古いSHARP製Softbank端末において、デバイスフォントの色を白に設定すると端末側で勝手に白で縁取りされてしまう
※参考:”【web creators】今“ 知っておくべき”ケータイサイト制作事情/第五回”:http://www.mdn.co.jp/di/articles/2145/?page=2

【解決方法】

なし
白デバイスフォントは使用しない、不具合が生じる機種は非対応機種とする等の対策が必要です。

(7) Docomoの文字コード

【現象】

Docomo端末からswf経由でマルチバイト文字が含まれるパラメータを送信した場合、端末によって文字コードがShift_JISのものとUTF-8のものがある

【発生例】

(例)「あ」をGETパラメータに付与した場合

(1) F905i

 swf内にてescape処理無しswf内にてパラメータ全体をescape処理swf内にてパラメータの名前, 値を個別にescape処理
パラメータの文字コードShift_JISパラメータとして認識されないUTF-8

(2) SH01-B

 swf内にてescape処理無しswf内にてパラメータ全体をescape処理swf内にてパラメータの名前, 値を個別にescape処理
パラメータの文字コードUTF-8パラメータとして認識されないUTF-8

通常、携帯HTMLサイトを構築する際にDocomo端末の場合はShift_JISのパラメータを受け取る前提で実装されることが多くありますが、 FlashLiteの場合、Docomo端末で表示されたswfからパラメータが送られた場合に、上記のようにパラメータの文字コードにばらつきがでてきます。

【解決方法】

暫定的に判別ロジックを組んではいますが、あまりスマートな方法ではないので公開は控えておきます…。

アリペイの導入について

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

最近、アリペイを試験ECサイトへ導入しています。導入しながら、まとめて少しずつメモします。ご参考まで

PS:
エンドユーザ⇒例、ECサイトで注文者
パートナーシステム⇒例、ECサイト及び運用者

第一回

——————2011-03-11(金)晴れ—————————————-
1.概要
アリペイ外部向けAPIには2種類あり、
一つは外部からリクエストを受け取るAPIであり、受取APIと言い、
もうひとつはアリペイから外部に通知するAPIであり、通知APIと言う。

受取API、通知APIの例はこの後詳しく説明するのでここで省略

2.交互パタン
(1)要求/応答パタン
もっとも使われているパタンで、パートナーシステムからリクエストを送信して、アリペイシステムにて処理を行って、処理結果を同期にパートナーシステムに戻すこと。

画面上から見れば呼出と遷移に分かれて、
呼出:パートナーシステムがアリペイの関連APIを呼び出すこと
遷移:パートナーシステムからアリペイの支払い画面に遷移して、エンドユーザはアリペイ支払システムにて支払いを行うこと。

アリペイ支払処理完了後、またパートナーシステムに戻らせたい場合、パートナーシステムからアリペイAPIを呼び出す時、パラメータの一つとして、return_url(戻り先)を渡す必要があります。


・処理フローとしては、

・呼出先URL:
https://www.alipay.com/cooperate/gateway.do


(2)通知モード
エンドユーザがパートナーシステムからアリペイシステムに遷移して、アリペイシステムにて今回の操作を完了させる。(パートナーシステムに戻らない)
アリペイシステムはパートナーが関心しているイベント、ステータスなどをパートナーシステムに通知する。
アリペイシステムの処理結果を非同期に通知されたい場合、パートナーシステムからアリペイAPIを呼び出す時、パラメータの一つとして、notify_url(通知先)の指定が必要です。

例、エンドユーザがパートナーシステムにて注文して、アリペイシステムにて支払う。支払いステータスが変わる時、アリペイシステムから
最新の支払いステータスや関連情報をパートナーシステムに通知する。

・処理フローとしては

・1.アリペイシステムからパートナーシステム(通知URL:nofity_url)に通知する。
・2.通知到着後、パートナーシステムは通知ID(nofity_id)で通知の有効性をアリペイシステムに確認する。
・3.アリペイシステムは通知の有効性を判断して、判断の結果をパートナーシステムに戻す。(True/False)
・4.パートナーシステムは通知により処理を行い、処理結果をアリペイシステムに戻す。
・5.パートナーシステムから戻された処理結果によりアリペイシステムは処理を行う。

アリペイシステムはHTTP/HTTPSプロトコルのpostメソッドで、データをパートーナシステムに送信する。
通知URLはアリペイとの契約に静的に指定できる。通知URL(notify_url)が指定されたら、すべての通知を指定されたURLに通知する。
アリペイ通知失敗またはパートーナシステムから処理成功の応答がない場合、アリペイシステムは定期的(1分間、3分間、5分間、10分間…)にデータを再送するが、すべての通知がパートーナシステムに届けることを保証しない。
重複送信の可能性がある、かつ、必ず業務上に前後順位で通知するわけではないので、パートーナシステムには重複送信無視及びだたしくソートする処理が必要だ。通知を受け取って処理する前に本システムの処理ステータスなどで処理されたかどうか判断する。本システムの処理ステータスをチェックする時、データロックかタイムスタンプを見るのをお勧め。

2-2.通知検証
システムの安全性から考えると、パートーナシステムはアリペイの通知を受けると、通知の有効性を検証する必要がある。アリペイAPIが正しく利用されているのを保証するために、パートーナシステムが1分間以内(現状1分間、今後調整可能性もある)の通知のみ検証できる。
検証用URL:
HTTPSの場合
https://www.alipay.com/cooperate/gateway.do

検証リクエスト例
https://www.alipay.com/cooperate/gateway.do?service=notify_verify&partner=1234567890¬ify_id=abcdefghijklmnopqrst

HTTPの場合
http://notify.alipay.com/trade/notify_query.do
検証リクエスト例
http://notify.alipay.com/trade/notify_query.do?partner=1234567890¬ify_id=abcdefghijklmnopqrst

通知検証APIの出力パラメータ
出力内容    説明
Invalid        パラメータ無効
True        検証成功
False        検証失敗

——————————————————————————

まだまだありますが、今回はここまでです。☆.。*・゜゜‥*。.☆.。*・゜゜‥*。. ☆.。*・゜゜‥*

MacPorts で /opt/local/bin/perl が無くなってしまった時の対処方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは、たろちゃんです。MacPortsで/opt/local/bin/perlが無くなってしまってImageMagickがインストールできないという現象が起きたので対処してみました。

MacPortsは便利ですが、たまに依存関係などで問題が起きるケースがあります。

本日MacPortsをアップデートしたところ、ImageMagickがインストールできないというエラーが発生しました。

行ったコマンドは以下の通りです。

$ sudo port selfupdate
$ sudo port upgrade outdated

たったこれだけでエラーになりました。ログを確認すると”/opt/local/bin/perl”が存在しないため、ImageMagickのインストールに失敗しているようです。

Perlの変更点を確認したところ、“Changeset 76485”にて以下の修正があったのが原因のようでした。

change perl5.* ports to do +mangle_names by default
change perl5 to link perl5.x pieces to their non-mangled names
change perl5 to default to perl5.12

この修正点でperl5.12がデフォルトになるのですが、perl5.12側のアップデートの歳にperl5を消してしまうように動いてしまうため、/opt/local/bin/perlが消えてしまったようです。

なので、以下のようにしてperl5を再インストールしました。

$ sudo port install perl5

あとは先ほど失敗したupgradeをやり直してあげます。

$ sudo port upgrade outdated

これで無事ImageMagickがインストールされました。

なお、perl5はデフォルトでperl5.12を使うようになりますが、これはperl5のvariantsで設定された値がデフォルトで5.12ということになります。

Wisp:~ btm$ port variants perl5
perl5 has the variants:
   perl5_10: use perl 5.10 instead of perl 5.8 or perl 5.12
     * conflicts with perl5_12 perl5_8
[+]perl5_12: use perl 5.12 instead of perl 5.8 or perl 5.10
     * conflicts with perl5_10 perl5_8
   perl5_8: use perl 5.8 instead of perl 5.10 or perl 5.12
     * conflicts with perl5_10 perl5_12

perl5.8やperl5.10を使いたい場合は、variantsに指定してperl5をインストールすれば良いでしょう。

jQuery animation 〜動きのあるWebサイト作り〜

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

rick No21です。
今回はjQueryでできる動きのあるサイトの作り方の一部をご紹介。

表示されている場所がおかしいかもしれませんが、表示されていますか?
ちなみにchromeでしか動作確認していません。
firefoxだとみれませんでしたw


では解説に入ります。

jQueryの読み込み

まずは、jQueryを読み込まないと始まりません。
< script type=”text/javascript” src=”http://search.kbmj.com/javascripts/jquery-1.4.2.min.js” >< /script >これによりjQueryを読み込みます。
自分で試すときは
http://jquery.com/
ここから落としてきてください。
本来は内に記述します。

表示場所

表示場所の指定
< div id=”box” title=”sample” > < /div>
< div id=”linkText” class=”lt” > < /div>

jQuery

< script language=”JavaScript” >< !–
URL = [1,2,3,4] //長いので省略
jQuery(function(){
  for (var i=1; i <= URL.length; i++) {
    jQuery(“#box”).append(‘< a href=”‘ + URL[(i – 1)] + ‘” >< div id=”box’+ i + ‘” class=”box” title=”sample’ + i + ‘” >< /div >’);
    move(“box” + i, i);
  };

  jQuery(“.box”).css({
    ”width”:”40px”,
    ”height”:”40px”,
    ”position”:”absolute”,
     “top”:”400px”,
    ”background-color”:”#FFCC66″,
    ”cursor”:”pointer”
  });

  jQuery(“.lt”).css({
    ”width”:”270px”,
    ”position”:”absolute”,
    ”font”:”bold 10pt monospace”,
    ”top”:”350px”,
    ”letter-spacing”:”2px”,
    ”margin”:”0px”,
    ”padding”:”5px”,
    ”border-top”:”dotted 2px #FFCC66″,
    ”border-bottom”:”dotted 2px #FFCC66″,
    ”display”:”none”
  });

  function move(id, count){
    jQuery(‘#’ + id).animate({“left”: (count * 100) + 200 + ‘px’});
  };

  jQuery(“.box”).hover(
   function(){
     jQuery(this).css({“background-color”:”#669933″});
     var tx = jQuery(this).attr(“title”)
     jQuery(“#linkText”).text(“->” + tx);
     jQuery(“#linkText”).show();
   },

   function(){
     jQuery(this).css({“background-color”:”#FFCC66″});
     jQuery(“#linkText”).text(“”);
     jQuery(“#linkText”).hide();
   }
  );
});
//– >< /script >

#boxに対しての動作

for (var i=1; i <= URL.length; i++) {
    jQuery(“#box”).append(‘< a href=”‘ + URL[(i – 1)] + ‘” >< div id=”box’+ i + ‘” class=”box” title=”sample’ + i + ‘” >< /div >’);
    move(“box” + i, i);
  };
内にURLの数だけ

タグを追加します。
  function move(id, count){
    jQuery(‘#’ + id).animate({“left”: (count * 100) + 200 + ‘px’});
  };
タグ追加後moveに引数でid名と現在地(何ループ目?)を引数に渡してmoveの処理を行います。
moveの処理は、その名の通りboxを動かします。
animateによって動きのある動作をcssで指定できます。
どのboxをで第1引数のidをどれだけ移動させるかに第2引数のcountを使用しています。

boxと#linkTextに対しての動作

  jQuery(“.box”).hover(
   function(){
     jQuery(this).css({“background-color”:”#669933″});
     var tx = jQuery(this).attr(“title”)
     jQuery(“#linkText”).text(“->” + tx);
     jQuery(“#linkText”).show();
   },
   
   function(){
     jQuery(this).css({“background-color”:”#FFCC66″});
     jQuery(“#linkText”).text(“”);
     jQuery(“#linkText”).hide();
   }
  );
hoverは、各boxにマウスが重なったとき、外れたときの動作です。
まずhoverの第1引数に記述されているのが、マウスが重なったときの処理です。
cssで背景を変更、タグのtitleを取り出して#linkTextに記述して表示しています。
第2引数は、マウスが外れたときの動作です。
cssで背景を元に戻して#linkTextの内容を空にして非表示にしています。

CSS
CSSの記述内容は割愛させていただきますが、本来は外部参照の方が綺麗ですね。

jQueryを使用するとこのような動きのあるサイトが簡単に作成可能です。<>

ブラウザ関連でひっかかった諸々のこと(主にjavascript)

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

「結局、お客様にとってサービスの品質って、画面がどれだけ洗練されているか、なんだよね」

企画や営業担当の方からよく聞く言葉です。全くもってその通り。

でもエンジニアとしては、データの保存と加工、セキュリティを最初に気にしてしまいがち。だから「こそ」、画面のしっかいりしているサービスは「いいサービス」と言えるのでしょう。

というわけで、自分が携わってきた画面(ブラウザ)系のびっくりしたことを紹介します。

まず前提として、自分は普段このようなスタイルでコーディングを行っています。

  • 基本的にサーバサイドのアプリを作ることがメイン(Rails 以前はPHP)
  • よく使うブラウザは FireFox。LiveHttpHeaders と FireBugs に惹かれてそのまま
  • 開発機は MacOS。テスト用に Linux。windowsテスト機は共有

cookie制限の罠

自分が初めて本格的なjavascriptを書いたのは今から4年前。新卒で入社して1ヶ月が経ったGW前でした。作ったアプリは、画面上のガジェットを「開閉」できるようにする、という今ではよく有るもの。mixi では、ガジェットの右上をクリックして調整します。

open_close

今ではmixiも上記のガジェットはリアルタイムで行いましたが、当時はクリックするたびにリクエストが飛んでいるのを見て、

  「じゃぁ自分は遷移せずに変更/保存できるようにしまーす」

ととんでもないことを宣言した新入社員時代。しかも javascript は素人同然。「○日までに実現の目処が経たなかったら、遷移で実装してくださいね」と繰り返すリーダーを尻目に設計/実装してみたものFireFoxでは動くが IE6で動かない。

んで、その原因は何故かというと、自分はその実装で各ガジェットの状態をcookie で保存していたのですが、1ガジェットにつき1つのcookieを発行していました。それが、制限にひっかかっていました。

http://www.studyinghttp.net/cookies#Restriction

で、これを共有したときに「これだからIEは。。。」と言いそうになったのですが、

実はこれ、IEの方が本来のcookieの仕様(rfc)に忠実で、FFの方が拡大解釈をしている模様。そのため、設計(仮実装)のときにうまくいっていたのが、そろそろ出来そうかな、となった時点で大幅変更を加えるはめになりました(各ガジェットの状態を構造化し、jsonにして1つのcookieに保存)。

FFの「ゆるさ」というのも、後々問題になるというお話。

IE6のselect tag問題

IE7が出来て(普及して)一番うれしかったのが、この問題が解消されたこと。

これは、各タブを重ねたときに、IE6(以前)の場合はselectタグが1番上にきてしまう、というもの。このページの一番下を参照して下さい

で、iframe を使うという対応策は既に知られているのですが、 とはいっても複数ブラウザで競合チェックしたりというのはやっぱりめんどくさいです。

IE での position : fixed が効かない問題

こちらも、比較的有名なIEの不具合?仕様?でしょうか。こちらができると固定したサイドバー等が簡単に実装できるのですが(javascript で実現したものはわざとらしくて)、これはどうしようもないです。

fixed.jsというライブラリがありますが、それでもテストの心配はあるわけで、 その分の調査やデバッグの期間というのは長く取る必要がある、と経験上感じています。

上記のセレクトタグと併せて、javascriptとCSSでリッチな画面を作ろうとした場合に大問題となる場所ですので、その点、調整にはかなりの時間を確保しておく必要がある所です。

 json.stringify と prototype の喧嘩

2つ続けてIE6という古いブラウザの話をしましたが、今後は最近のブラウザの話。

前回の記事、 何か変なjson が吐かれるために苦労していましたが、そちら、やっと原因がつかめました。

現在担当しているプロジェクトではjavascript のライブラリはjQuery を主に使っているのですが、以前からの資産もありprototypeで実装されているものもあります。そのprototype のライブラリが json.stringify を汚染していた模様。

http://d.hatena.ne.jp/Gimite/20091129/1259495440

よって、今回は、json化は他のライブラリを使わず、その部分だけprototypeを使うように実装しました。

    var json = Object.toJSON(obj);

 ブラウザというよりもprototype.jsの問題ですが、高機能というのもアレなものだということで。

ajax 通信における post(本番) と get(開発時) の使い分け with firebugs

 rfc には含まれていないのですが、IE系では get で指定できるURLの長さは2083文字という制限があります。たしかにそれくらいの長さになるドメインやディレクトリはとても考えづらく、それくらい長いパラメータをつけるならpostにしろよ、というのは極々当たり前の常識的な判断ですので、特に疑問や憤りを覚えず他のブラウザの場合でも素直に従うべきでしょう。

ですが、ajax で非同期にページに一部だけ更新する場合、その通信は「開発期間の間は」getで行った方が特なことがあります。

Firefox で ajax 通信を行った場合、「接続」タブを開いた状態ではそのページでリクエストされた全てのコンテンツのリクエスト状況が表示されます。勿論、ajax でリクエストされたページも表示されます。

ajax で呼び出す URL(上記だと「ajax_body」)は、大抵の場合、Ruby(on Rails)等のサーバサイドの言語で動的に生成されています。勿論、開発中でバグや未実装があることも多いでしょう。そのリクエストが失敗した(500系のステータスコードが返ってきた)場合、上記のようにpostだと追跡が難しいです。

そのため、開発期間中だけでも get で ajax通信をするようにします。

このようにすると、上記の「ajax_body」にリクエストパラメータが付属してつくため、そのパーツとなるページだけ、別途ブラウザで表示し、デバッグを進めることができます。

最近のajax は、jQuery.ajax 等を用いて実装されるケースが多いと思われます。getとpostの切り替えに大幅な仕様変更を伴いませんので、ちょっとした参考にして下さいませ。勿論、リリース時より前、例えば負荷検証やUIテストの前には、post に戻しておく必要があります。

<script type=”???”>の怪奇

最後に、ちょっと脱力してしまったお話しを。

<script type=” text/javascript” language=”javascript”>〜</script> で囲まれたjavascript は、firefox, safari, chrome では実行されますが、IE系, Opera では実行されません。

つまり、

type の中が不正、つまり「(半角スペース)text/javascript」等、想定されていないものが入った場合、ブラウザ毎に挙動が異なります!!

お気をつけ下さい。

Ruby on RailsでfacebookのOAuth認証を実装する

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回は、Ruby on RailsでfacebookのOAuth認証を実装する手順をご紹介したいと思います。

OAuth認証といえば何だか小難しい印象がありますが、RailsにおけるOAuth認証の実装は驚くほど簡単です。

今回は例として、facebookのOAuth認証を実装する手順を5つのステップに分けてご紹介します。

※以下の手順はfacebookアカウントを登録済みのものとします。

facebookの設定(facebookにアプリケーションを登録する)

STEP1:本人認証を行う

携帯電話のメールアドレス、または、クレジットカードにて本人認証を行います。

※以下、携帯電話のメールアドレスで認証を行う手順を記載します

(1) http://www.facebook.com/docs/guides/web#!/developers/createapp.php

(2) 「携帯認証」のリンクを押下(登録画面がポップアップで表示されます)

(3) 携帯電話のメールアドレスを入力する

(4) 携帯電話に届いた確認用コードを入力する

(5) 本人認証完了

※2011/03/02現在、完了メッセージと共に再び②の入力フォームが表示されますが、①のウィンドウに戻り、画面を更新すると次のステップへ進むことができます。

STEP2:アプリケーションを登録する

(1) アプリケーション名を入力し、利用規約に同意した上で「アプリケーションを作成」を押下します。

(2) 画像認証を行います。

(3) アプリケーションの情報を入力します。

(※WEB Site > Site URL に「http://localhost:3000/」を記載するだけでOKです)

(4) アプリケーション登録完了

※マイアプリ画面内に表示されている「アプリID」「アプリの秘訣(※facebook側の誤訳な気がしますが、以下これで通します)」がfacebookアプリケーション開発時に必要となります。

Railsアプリの作成

STEP3:必要なRails環境

<実行環境>

今回のサンプルは以下の環境で作成しました。必要なgemが入っていない場合はインストールしてください

ruby:1.8.7
rails:2.3.10(sudo gem install rails -v 2.3.10)
oauth:0.4.4(sudo gem install oauth -v 0.4.4)
facebook_oauth:0.2.2(sudo gem install facebook_oauth -v 0.2.2)

STEP4:Railsアプリの作成

(1) 新規Railsプロジェクトを作成します

rails facebook

(2) RAILS_ROOTに移動し、OAuth認証用のControllerを作成します

script/generate controller Users index callback

(3) 各ファイルを下記のとおりに編集します

app/controllers/users_controller.rb

class UsersController < ApplicationController

  CALLBACK_URL    = "http://localhost:3000/users/callback"
  CONSUMER_KEY    = "アプリID"
  CONSUMER_SECRET = "アプリの秘訣"

  def index
  end

  def oauth
    client = FacebookOAuth::Client.new(
      :application_id     => CONSUMER_KEY,
      :application_secret => CONSUMER_SECRET,
      :callback           => CALLBACK_URL
    )
    redirect_to client.authorize_url
  end

  def callback
    @client = FacebookOAuth::Client.new(
      :application_id     => CONSUMER_KEY,
      :application_secret => CONSUMER_SECRET,
      :callback           => CALLBACK_URL
    )
    @client.authorize(:code => params[:code])
  end
  
end

app/views/users/index.html.erb

<%= link_to 'OAuth認証', '/users/oauth' %>

app/views/users/callback.html.erb

<%= @client.me.info %>

STEP5:Railsアプリを動かす

(1) サーバーの起動

script/server

(2) ブラウザで「http://localhost:3000/users」にアクセス

(3) 「OAuth認証」リンクを押下

(4) 未認証の場合、facebookサイトにて認証画面が表示されるので、「許可」を押下します

(5) CALLBACK先(/users/callback)にリダイレクトされます

(6) 「/usrs/callback」ページにてユーザー情報が表示されれば認証成功です。

さいごに。

以上となりますが、いかがでしょうか。意外とハードルは高くないという印象を受けていただけたでしょうか。

ちなみに、TwitterのOAuth認証実装については当サイト内の記事『rubyでOAuthを使ってみる』が参考になります。

pg 0.9.0 の Windows版バイナリをMac OS XでCross compileする

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは、たろちゃんです。先日同僚のWindows環境にpgが必要になったのですがWindows側でコンパイラがなかったのでMac OS Xで作成してみました。

今回はMacPortsの環境を使います。

まず、Windowsのソフトウェアをクロスコンパイルできるようにするためにmingw32の環境をMacPortsで取得します。

$ sudo port install i386-mingw32-gcc

次に必要なgemをインストールします。

$ sudo gem install rdoc
$ sudo gem install rake-compiler

次にクロスコンパイルされたrubyを構築します(調べた当時ではruby-1.8.6 p398がビルドされました)。

$ rake-compiler cross-ruby

次にpg-0.9.0のgemを取得して展開します。

$ gem fetch -v 0.9.0 pg
$ gem unpack pg-0.9.0.gem

最後にrake crossを使ってpgをビルドします。

$ cd pg-0.9.0
$ rake cross native gem POSTGRESQL_VERSION=8.4.6

あとはrake crossを実行したところのpkg以下にできる pg-0.9.0-x86-mingw32.gem をWindows環境で手動インストールします。

今回の注意点としては、クロスコンパイル環境として ruby 1.8.7 を利用したところクロスコンパイルができない状態があり、調べてみると細かいパッチレベルでできるできないがあったりするようです。現段階では1.8系でクロスコンパイルをするには1.8.6の方が安定しているのではないかという印象がありました。

【PHP】画像アップロードとリサイズの仕方(フォームの生成~リサイズ処理の方法)

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

【PHP】画像アップロードとリサイズの仕方(フォームの生成~リサイズ処理の方法)

 フォーム生成からアップロードされた画像を保存する。

プロジェクト内で画像のアップロードからリサイズ処理を見ることがあったので、

勉強がてら処理内容を追ってみました。

  • 1.フォームの生成
  • 2.アップロードされたファイル情報取得
  • 3.画像の保存方法

1. フォームの生成

 <form action="アクション先" method="post" enctype="multipart/form-data">
   <input type="file" name="upload_file" />
   <input type="submit" value="アップロード" />
</form>

※ formタグのmethodをpostに、enctype=”multipart/form-data”を指定する必要があります。

2. アップロードされたファイル情報取得

アップロードされたファイルの情報は $_FILES に格納されます。

$_FILES は $_POST や $_GET と同様、PHPが自動的に値をセットしてくれる連想配列です。

また、アップロードしたファイルは一時的にサーバー側に保存され、PHPプログラムの実行が終了すると削除されるそうです。

$upload_file = $_FILES["upload_file"];
array(5) {
  ["name"]=>
  string(10) "Desert.jpg"
  ["type"]=>
  string(10) "image/jpeg"
  ["tmp_name"]=>
  string(14) "/tmp/phph8PFva"
  ["error"]=>
  int(0)
  ["size"]=>
  int(845941)
}
  • name →アップロードファイル名
  • type →MIMEタイプ
  • tmp_name →アップロードされたファイルが一時的に保存されたファイルパス
  • error →エラーコード(正常にアップロードされた場合、0になります)
  • size →アップロードされたファイルサイズ

となっています。

3. 画像の保存方法

アップロードされた画像は一時的に、tmp_name値で指定された箇所に保存されています。

move_uploaded_file()を用いて、別ディレクトリに移動させます。

move_uploaded_file( $upload_file["tmp_name"], ‘/img/upload/');

とここまでの手順で、アップロードされた画像を保存することが可能です。

4. 画像をリサイズするサンプルコード

下記の1.~6.までの手順をサンプルに落としてみました。

1.画像情報を取得する

2.画像をコピー

3.空画像を作成

4.コピー画像を指定サイズで作成

5.コピー画像を保存

6.画像データ破棄

$orig_file 元画像

$resize_weight リサイズ幅

$resize_height リサイズ高さ

function sample_resize($orig_file, $resize_weight, $resize_height)
{
	// GDライブラリがインストールされているか
	if (!extension_loaded('gd')) {
	    // エラー処理
	    return false;	
	}

	// 画像情報取得
	$result = getimagesize($orig_file);
	list($orig_width, $orig_height, $image_type) = $result;
	
	// 画像をコピー
	switch ($image_type) {
	    // 1 IMAGETYPE_GIF
	    // 2 IMAGETYPE_JPEG
	    // 3 IMAGETYPE_PNG
            case 1: $im = imagecreatefromgif($orig_file); break;
            case 2: $im = imagecreatefromjpeg($orig_file);  break;
            case 3: $im = imagecreatefrompng($orig_file); break;
            default: //エラー処理 
          return false;
        }	

	// コピー先となる空の画像作成
	$new_image = imagecreatetruecolor($resize_width, $resize_height);
        if (!$new_file) {
            // エラー処理 
	    // 不要な画像リソースを保持するメモリを解放する
            imagedestroy($im);
            return false;
        }

 	// GIF、PNGの場合、透過処理の対応を行う
	if [1]$image_type == 1) OR ($image_type==3 {
            imagealphablending($new_image, false);
            imagesavealpha($new_image, true);
            $transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127);
            imagefilledrectangle($new_image, 0, 0, $resize_width, $resize_height, $transparent);
        }

	// コピー画像を指定サイズで作成
 	if (!imagecopyresampled($new_image, $im, 0, 0, 0, 0, $resize_width, $resize_height, $orig_width, $orig_height)) {
            // エラー処理
	  // 不要な画像リソースを保持するメモリを解放する
            imagedestroy($im);
            imagedestroy($new_image);
            return false;
        }

        // コピー画像を保存
	// $new_image : 画像データ
	// $new_fname : 保存先と画像名
        // クオリティ

        switch ($image_type) {
	    // 1 IMAGETYPE_GIF
	    // 2 IMAGETYPE_JPEG
	    // 3 IMAGETYPE_PNG
            case 1: $result = imagegif($new_image, $new_fname, $quality); break;
            case 2: $result = imagejpeg($new_image, $new_fname, $quality); break;
            case 3: $result = imagepng($new_image, $new_fname, $quality); break;
            default: //エラー処理 
          return false;
        }

        if (!$result) {
            // エラー処理 
	    // 不要な画像リソースを保持するメモリを解放する
            imagedestroy($im);
            imagedestroy($new_image);
            return false;
        }

	// 不要になった画像データ削除
	imagedestroy($im);
        imagedestroy($new_image);

}

下記記事を書くにあたって参考にしたサイトになります、

透過処理についての解説

http://www.phppro.jp/qa/1269

References

References
1 $image_type == 1) OR ($image_type==3

checkinstallをつかってrpm化して楽になる

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

なにかをソースから自力でインストールするときに、パッケージ管理しておいたほうが

管理が楽ですので、checkinstallで楽にrpm化して楽しましょう

どういうことをするかというと、

makeまでしてinstallの代わりにcheckinstallしてrpm化して入れる感じです

まずはcheckinstallのインストール、依存してるソフトを入れます。

# yum install gettext rpm-build

次にcheckinstall本体を入れます。

# cd /usr/local/src
# wget http://asic-linux.com.mx/~izto/checkinstall/files/source/checkinstall-1.6.2.tar.gz
# tar zxf checkinstall-1.6.2.tar.gz
# cd checkinstall-1.6.2
# make
# make install

次にcheckinstall自体をrpm化します

# checkinstall 
# rpm -i /usr/src/redhat/RPMS/x86_64/checkinstall-1.6.2-1.x86_64.rpm
# rpm -qi checkinstall => OK^^v

インストールされてます。

これにて準備完了です。試しにRubyをインストールしてみます。

# cd /usr/local/src
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p302.tar.gz
# tar -xzf ruby-1.8.7-p302.tar.gz
# cd ruby-1.8.7-p302
# ./configure
# make
# checkinstall -R --fstrans=no  --provides="ruby"
# rpm -ivh /usr/src/redhat/RPMS/x86_64/ruby-1.8.7-p302-1.x86_64.rpm
# rpm -qi ruby => OK^^v

入りました。

ruby-opensslも同じステップでインストールしてみます。

# cd /usr/local/src/ruby-1.8.7-p302/ext/openssl
# ruby extconf.rb
# make
# checkinstall --exclude=/selinux -R
# rpm -ivh  /usr/src/redhat/RPMS/x86_64/openssl-20101102-1.x86_64.rpm
# rpm -qi openssl => OK^^v

入りました。

パッケージ管理されてるとホっとします

Solaris 10メモ

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

普段Mac OS XやLinuxを使っているとついついわすれてしまうSolarisのコマンド。自分たちでつけてたメモを書いてみる事にしました。

 パッケージの中身を知るには

# pkgchk -vn CSWvim
/opt/csw/bin/ex
/opt/csw/bin/rview
/opt/csw/bin/rvim
/opt/csw/bin/view
/opt/csw/bin/vim
/opt/csw/bin/vimdiff
/opt/csw/bin/vimtutor
/opt/csw/bin/xxd
/opt/csw/share/doc/vim
/opt/csw/share/doc/vim/license

 ファイルがどのパッケージか知るには

# pkgchk -lp /opt/csw/bin/glocate
Pathname: /opt/csw/bin/glocate
Type: regular file
Expected mode: 0755
Expected owner: root
Expected group: bin
Expected file size (bytes): 216836
Expected sum(1) of contents: 14151
Expected last modification: Jun 08 11:44:20 2009
Referenced by the following packages:
        CSWfindutils   
Current status: installed

 patchがどこまであたっているのか知りたい

# patchadd -p | grep パッチ番号

 sudoがないよ?

pfexecを使いましょう。

 共有ライブラリのパスを設定する

参考 http://solaris.roguelife.org/usr/ccs/bin/ld.xhtml

crleコマンドを使う。

# crle -c /var/ld/ld.config -l /lib:/usr/lib:/usr/local/lib:/opt/csw/lib

設定情報を確認するには

# crle

Configuration file [version 4]: /var/ld/ld.config  
  Default Library Path (ELF):   /usr/lib:/usr/local/lib:/opt/csw/lib:/opt/sfw/lib
  Trusted Directories (ELF):    /lib/secure:/usr/lib/secure  (system default)

Command line:
  crle -c /var/ld/ld.config -l /usr/lib:/usr/local/lib:/opt/csw/lib:/opt/sfw/lib

設定を更新(追記)するには

# crle -c /var/ld/ld.config -u -l /opt/sfw/lib
# crle

Configuration file [version 4]: /var/ld/ld.config  
  Default Library Path (ELF):   /usr/lib:/usr/local/lib:/opt/csw/lib
  Trusted Directories (ELF):    /lib/secure:/usr/lib/secure  (system default)

Command line:
  crle -c /var/ld/ld.config -l /usr/lib:/usr/local/lib:/opt/csw/lib:/opt/sfw/lib

:joinと:includeの区別について

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

:joinと:includeの区別について、いままであまり分からかなったので、実行してみて、メモします。


:joinと:includeの区別について、いままであまり分からかなったので、実行してみて、メモします。

(1):includeは最初に関連モデルを全て取得し、その後、キャッシュからロードされる

products,product_detailsテーブルがhas_oneの関係です。
現在、A,Bデータベースの上記2テーブルのデータを比較して、差分分をAからBへコピーする機能です。

set_db(a)
a_prodcut = Product.find(10)

set_db(b)
b_prodcut = Product.find(10)


※set_db(x)はDB指向を指定するメソッドの仮名です。

a_prodcut.product_detailのcommentカラムの内容を取得したい場合、
a_prodcut.product_detail.commentですと
「select * from product_details where product_id = 10」sqlが発行されています。
この場合、DBがbに指向されているので、取得したのはa_prodcutのproduct_detailではなく、
b_prodcutが紐付いているproduct_detailです。
joinでやって、同じく、sqlが発行されています。

includeでやってみます。
set_db(a)
a_prodcut = Product.find(:first,:conditions => [“products.id = 10”], :include => [:item_content])

set_db(b)
b_prodcut = Product.find(:first,:conditions => [“products.id = 10”], :include => [:item_content])


a_prodcut.product_detail.commentしたら正しい内容が取得できます。
:includeは、最初に関連モデルを全て取得しているため、a_prodcut.product_detailがキャッシュからロードされます。

(2):includeと:selectは併用できない
:includeは、最初に関連モデルを全て取得しているため、:selectの指定が無効となります。
:joinの場合、:selectが使えます。

追記:

巨大なテーブルの場合、:includeはすべてキャッシュにロードするので、パフォーマンスが落ちるかもしれないとの話もあったようです。
>:includeは、最初に関連モデルを全て取得しているため
結局、↑この特性を理解したうえで、状況に合わせて使えってことですね

2011-10月

久しぶりに追加します。:joinsは、関連モデルを検索条件に追加する場合に使うべし。includeを使うと、:includeは、最初に関連モデルを全て取得しているため、キャッシュからロードされるということで期待の結果がヒットできない可能性が十分あります。

json の憂鬱

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

現在ではxmlよりもyml よりも使われているであろう構造化文書の形式json。自分がド新人だったときに、「json? なんて不吉な名前の変数なんだ」と思ったのも遠い昔のことです。

json を始めとする構造化文書の便利さは、適切なライブラリを使えば 言語内の変数(hash, array 等)⇔一般的なテキスト、の変換が 容易に行えることにあります。

データを RDB に保存するとき、変数の構造が変化したとしても、 カラムを増やしたり join を行ったりせず、その項目での検索を ある程度あきらめるのであれば、容易かつ高速に、多様な形式の データを保存できます

class Samples < ActiveRecord::Migration
  def self.up
    create_table :samples do |t|
    t.string “name”
    t.text “description”
    t.text “parms_json” #ここに、詳しいデータを json 化して保存する
  end
  def self.down
    drop_table :samples
  end

で、ここの「params_json」の値ですが、上の値に関しては、 サーバサイド(Ruby on Rails)で生成した場合と クライアントサイド(Ajax)で生成した場合と、 同じ形式で保存されることが期待されるのですが、 こちら、ちょっと困った「クセ」がそれぞれありました。 そのクセの内容と、対策を共有します。

0) まずはスタート

下記のような変数を(ruby の記法)

hash = {
    :a => “b”, :c => [ [“あ”, “い”, “う”], [“え”, “お”, “か”] ]
}

を、

{ “a”:”b”, “c”:[ [“あ”,”い”,”う”], [“え”,”お”,”か”] ] }



という json にしたい。

1) Ruby on Rails の場合

何も考えずに hash.to_json した場合、

>> hash.to_json => “{\”a\”:\”b\”,\”c\”:[[\”\\u3042\”,\”\\u3044\”,\”\\u3046\”],[\”\\u3048\”,\”\\u304a\”,\”\\u304b\”]]}”

のように、 unicode_escape されて出力されます。 こちら、escape されて欲しくない場合、下記のような関数を作成し 出力を得ます。

    def json_unescape(str)
      str.gsub(/\\([\\\/]|u[0-9a-fA-F]{4})/) do
        ustr = $1
        if ustr.starts_with?(‘u’)
          [ustr[1..-1].to_i(16)].pack(“U”)
        elsif ustr == ‘\\’
          ‘\\\\’
        else
          ustr
        end
      end
    end

出力は

>> json_unescape(hash.to_json) => “{\”a\”:\”b\”,\”c\”:[[\”あ\”,\”い\”,\”う\”],[\”え\”,\”お\”,\”か\”]]}”

2) jquery-json の場合

jquery および jquery-json を読み込んだ環境下にて、

alert(jQuery.toJSON({a:”b”, c:[[“あ”, “い”, “う”], [“え”, “お”, “か”]]}));

を実行したら、

{“a”:”b”,”c”:”[[\”あ\”, \”い\”, \”う\”], [\”え\”, \”お\”, \”か\”]]”}

のように、配列が内部でエスケープされた状態で出力されます。 上記のエスケープを解除したい場合、下記のような処理になります。

function no_escape_to_json(obj) {
  return jQuery.toJSON(obj).replace(/\\\”/g, “\””).replace(/\”\[/g, “\[“).replace(/\]\”/g, “\]”);
}

1), 2) の処理とも、アドホックな対応のため、あまり推奨されないやり方になります。が、もしお困りの方がいればご参考いただきたいな、と。 また、もっと根本的なスマートなやり方がある場合、お教えいただけますと幸いです。

PHPのソースインストール

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

CentOSにPHP5.2系を入れる為に、utterramblingsリポジトリを使用してyumで楽にインストールしてましたが、ある時から不安定でインストール出来ないという状況が何度か続いた為、最近はソースインストールするようにしています。そのメモです。
私の要件はyumの場合と同じものを使えるようにする事。
ちなみに以前はこんな感じで色々入れてました。
# yum –enablerepo=utterramblings -y install php php-gd php-ldap php-mbstring php-pear php-mcrypt php-xml php-devel php-apc php-soap php-mysql
# pecl install fileinfo
※後は/etc/php.ini、/etc/php.d/apc.ini, fileinfo.iniの設定で完了。

●PHPのインストール

XAMPPに入っているPHP5.2系の最後が1.7.1のPHP5.2.9なので、個人的にはこのバージョンを使用しています。
# wget http://museum.php.net/php5/php-5.2.9.tar.gz
# tar zxvf php-5.2.9.tar.gz
# cd php-5.2.9
# ./configure –with-apxs2 –with-bz2 –enable-calendar –with-curl –enable-exif –enable-ftp –with-gd –with-gettext –with-gmp –with-ldap –enable-mbstring –with-mcrypt –with-mysql –with-mysqli –with-openssl –with-pdo-mysql –with-pspell –enable-shmop –enable-soap –enable-sockets –enable-sysvmsg –enable-wddx –enable-xml –with-xsl –with-zlib –with-config-file-path=/etc –with-config-file-scan-dir=/etc/php.d
後はひたすら、エラーメッセージを見て必要なものをインストール⇔configureを繰り返します。

configure: error: no acceptable cc found in $PATH
# yum -y install gcc
./configure: line 6699: apxs: command not found
# yum -y install httpd-devel
configure: error: xml2-config not found. Please check your libxml2 installation.
# yum -y install libxml2-devel
configure: error: Cannot find OpenSSL’s
# yum -y install openssl-devel
configure: error: Please reinstall the libcurl distribution –
easy.h should be in /include/curl/
# yum -y install curl-devel
configure: error: libpng.(a|so) not found.
# yum -y install gd-devel
configure: error: Unable to locate gmp.h
# yum -y install gmp-devel
configure: error: mcrypt.h not found. Please reinstall libmcrypt.
# yum -y install libmcrypt-devel
Note that the MySQL client library is not bundled anymore!
# yum -y install mysql-devel
configure: error: Cannot find pspell
# yum -y install pspell-devel
configure: error: xslt-config not found. Please reinstall the libxslt >= 1.1.0 distribution
# yum -y install libxslt-devel
Thank you for using PHP.これでconfigureが完了。

# make
# make install
# cp -a php.ini-recommended /etc/php.ini
# mkdir /etc/php.d

●apcのインストール

# pecl install apc
エラーメッセージを見て必要なものをインストール、もう一度実行。
Cannot find autoconf. Please check your autoconf installation and the $PHP_AUTOCONF
environment variable is set correctly and then rerun this script.
ERROR: `phpize’ failed
# yum -y install autoconf
# pecl install apc

●PHPの設定変更

# vi /etc/httpd/conf.d/php.conf

LoadModule php5_module modules/libphp5.so

LoadModule php5_module modules/libphp5-zts.so

AddHandler php5-script .php
AddType text/html .php

DirectoryIndex index.php

php_value date.timezone Asia/Tokyo
SetEnv TZ Asia/Tokyo

デフォルトではモジュールのパスが違うので変更しておく。
# vi /etc/php.ini
extension_dir = “./”↓変更
extension_dir = “/usr/lib/php/modules”
# mkdir /usr/lib/php
# ln -s /usr/local/lib/php/extensions/no-debug-non-zts-20060613 /usr/lib/php/modules
後はそれぞれのモジュールが読み込まれるように設定する。
# vi /etc/php.d/apc.ini
extension=apc.so
# vi fileinfo.ini
extension=fileinfo.so

●Apacheの再起動

# apachectl configtest
Syntax OK
# /etc/rc.d/init.d/httpd restart

cakephpで管理機能を実装する

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回は、cakephpで開発すると意外と悩む、管理ユーザによる管理機能を実装する際の認証ロジックに関するソリューションを紹介します。

こんにちは。エンジニアのサンドリバー砂川です。

2010年も早いもので後3日。弊社も本日29日で、今年の最終営業日となります。

はじめに

はじめに、この記事は以下のような仕様のサイトを作ることを想定しています。サイトの一般会員(users)と、管理ユーザ(admin_users)の独立した二種類のアカウントを使用する一般的な認証機能を持つ(マイページ等)マスタデータ等を管理する管理画面を持ち、管理ユーザでログインしなければ閲覧出来ないものとする

一般的な認証機能 AuthComponent

cakephpには標準の認証機能として、AuthComponentというコンポーネントが存在します。

AuthComponentで普通の認証処理を実装するとこんな感じになります。
app/app_controller.php


class AppController extends Controller {
  var $components = array('Auth');
  var $uses = array('User');
  var $helpers = array('html', 'Form');

  function beforeFilter() {
    parent::beforeFilter();
    $this->layout = 'base';
    $this->setAuth();
  }

  function setAuth() {
    # 実際には、AuthComponentの初期値はusersテーブルを使用するので設定は不要。
    $this->Auth->userModel = 'User'; # 認証に使用するアカウントテーブルとしてusersテーブルを使用
    $this->Auth->fields['username'] = 'username'; # IDとしてusernameカラムを使用
    $this->Auth->loginRedirect = '/mypage'; # ログイン成功時にリダイレクトするurl
  }
}

app_controllerでAuthComponentを設定すると、全てのcontroller処理の前に認証チェックが行われ、beforeFilterに$this->Auth->allow(‘アクション名’);と設定しない限り、ログインしていない場合は/users/loginにリダイレクトされるようになります。

AuthComponentでは、デフォルトではusersテーブルをアカウントテーブルとして使用しますが、適宜指定することも可能です。今回の実装では、一般機能ではusers、管理機能ではadmin_usersを使用するように切り替えることで実装します

管理画面実装

さて、管理画面の実装ですが、cakephpには管理機能を実装する手段としてroutingプレフィックス機能が用意されています。簡単に説明すると通常、cakephpのroutingロジックは以下。


controller = books, action = admin_add
↓
/books/admin_add

しかし、routingプレフィックスにadminを指定すると一部urlが変化します。


controller = books, action = admin_add
↓
/books/admin/add

正直この機能は使い辛く、特に一般ユーザと管理ユーザで独立した認証処理を行いたい今回のケースでは使い物になりません。なのでcontrollerごと分離する方式で実装します。

まず、controllerの共通処理を記述するapp_controller.phpを継承、一般サイト領域の共通処理を記述するbase_controller.phpと、管理機能領域の共通処理を記述するadmin_controller.phpを作成します。
app/app_controller.php


class AppController extends Controller {
  var $components = array('Auth');
  var $uses = array('User');
  var $helpers = array('html', 'Form');

  # 全体共通処理のみ記述
  function beforeFilter() {
    parent::beforeFilter();
    $this->layout = 'base';
  }
}

app/controller/base_controller.php


class BaseController extends AppController {
  var $extra_components = array();
  var $extra_uses = array();
  var $extra_helpers = array();

  function __construct() {
    parent::__construct();
    $this->merge_extra();
  }

  function beforeFilter() {
    parent::beforeFilter();
    $this->layout = 'base';
    $this->setAdminAuth();
  }

  # 一般領域用にusersテーブルをアカウントテーブルとして使用するよう設定
  function setAuth() {
    $this->Auth->userModel = 'User';
    $this->Auth->fields['username'] = 'username';
    $this->Auth->loginRedirect = '/mypage';
  }

  function merge_extra() {
    $this->components = array_merge($this->components, $this->extra_components);
    $this->uses = array_merge($this->uses, $this->extra_uses);
    $this->helpers = array_merge($this->helpers, $this->extra_helpers);
  }
}

app/controller/admin_controller.php


class AdminController extends AppController {
  var $extra_components = array();
  var $extra_uses = array();
  var $extra_helpers = array();

  function __construct() {
    parent::__construct();
    $this->merge_extra();
  }

  function beforeFilter() {
    parent::beforeFilter();
    $this->layout = 'admin_base';
    $this->setAdminAuth();
  }

  # 管理画面領域用に、admin_usersをアカウントテーブルに使用するよう設定
  function setAdminAuth() {
    $this->Auth->userModel = 'AdminUser';
    $this->Auth->fields['username'] = 'mail_address';
    $this->Auth->loginRedirect = '/admin_top';
  }

  function merge_extra() {
    $this->components = array_merge($this->components, $this->extra_components);
    $this->uses = array_merge($this->uses, $this->extra_uses);
    $this->helpers = array_merge($this->helpers, $this->extra_helpers);
  }
}

一般ユーザが使用するcontrollerはbase_controllerを、管理ユーザが使用するcontrollerはadmin_controllerをそれぞれ継承して使用します。

また、一般機能領域の共通処理はbase_controller、管理機能領域の共通処理はadmin_controller、全てのcontrollerの共通処理はapp_controllerに、それぞれ記述することにします。
例:一般controller


App::import('Controller', 'Base');
class MypageController extends BaseController {
  var $Name = 'Mypage';
  var $extra_uses = array('Book');
  var $extra_components  = array('RequestHandler');
  var $extra_helpers = array('bk');

  function index() {
  }
}

例:管理controller


App::import('Controller', 'Admin');
class BookController extends AdminController {
  var $Name = 'Books';
  var $extra_uses = array('Book');
  var $extra_components  = array('RequestHandler');
  var $extra_helpers = array('bk');

  function index() {
  }
}

こうすることで一般機能領域では一般ユーザ、管理機能領域では管理ユーザでの認証を適用することができます。

注意点

この方式の注意点として、通常$usesや$componentsのような各controllerで追加している設定は、代わりに$extra_usesのように追加しなければならない点が挙げられます。これはcakephp内部実装上の理由で以下のような問題が発生するためです。


App
$uses = array('A);
↓
Appを継承したAppFoo
$uses = array('B');
↓
最終的な$uses
$uses = array('A', 'B'); # $usesや$componentsはマージされて適用される

App
$uses = array('A);
↓
Appを継承したBase
$uses = array('B');
↓
Baseを継承したBaseBar
$uses = array('C');
↓
最終的な$uses
$uses = array('B', 'C); # Appで指定したAがマージされない。

この問題に対しては、$extra_usesで指定した配列を一旦マージした後、$usesに設定することで対処しています。


App
$uses = array('A);
↓
Appを継承したBase
$uses = array('B');
↓
Baseを継承したBaseBar
$extra_uses = array('C');
↓
Baseの$usesとBaseBarの$extra_usesをマージ
$uses = array('B', 'C');
↓
最終的な$uses
$uses = array('A', 'B', 'C);

以上です。全ての問題点を綺麗に解決出来ているわけではありませんが、管理機能を実装する際に参考になる点もあるかと思います。

jQueryを使った外部のhtmlの読み込みTips

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

jQueryを使ってヘッダ、フッタなど共通部分のhtmlを本体のページに読み込むカンタンTipsです

共通で使いたいhtmlと、読み込む側のhtmlを用意する。

読み込みたい共通部分にはheadタグとかbodyタグとかは不要です。
ほんとに部分的に抜き出したhtmlだけを用意します。

例えばヘッダ、フッタを共通にする場合は3つのファイルができることになります。

index.html 本体
header.html ヘッダー
footer.html フッター

jQueryを使うので、htmlの文字コードはUTF-8にしておきます。

jQueryをダウンロードする

http://jquery.com/

特に理由がなければ「Production」を使用します。

htmlでjQueryを呼び出し、共通htmlを読み込む

まずjQueryを呼び出します。ふつーに。

<script type=”text/javascript” src=”./javascripts/jquery-1.4.3.min.js”></script>

 次に、共通htmlを組み込みたい部分にidをもった空divを挿入します。

<div id=”header”></div>

こんなカンジ。

headタグ内で、idと読み込むhtmlファイルとを紐づけます。

<script type=”text/javascript”>
  $(function(){
    $(“#header”).load(“./header.html”);
    $(“#footer”).load(“./footer.html”);
  })
</script>

これでOK。

完成したhtmlはこんなカンジ

<html>
<head>

<script type=”text/javascript” src=”./javascripts/jquery-1.4.3.min.js”></script>

 <script type=”text/javascript”>
  $(function(){
    $(“#header”).load(“./header.html”);
    $(“#footer”).load(“./footer.html”);
  })
</script>

</head>

<body>
<div id=”header”></div>
<div>


</div>

<div id=”footer”></div>
</body>

</html>

ちょっとした注意点

共通htmlを読み込んだとき、階層構造は当然読み込む側のhtmlに準じることになります。
imgタグやaタグでURLを相対パス指定にすると、
階層がズレた場合デッドリンクになってしまいます。
パスはすべて絶対パスで記述するか、
相対パスを使用するならデッドリンクにならないよう階層構造に注意が必要です。

Androidアプリをリリースしました。「Live Search Twitter」

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Android2.1 SDK から追加された機能LiveWallpaperを利用したアプリ

Live Search Twitter をリリースしました。

 Live Search Twitter

この度、Androidマーケットに無料アプリを投稿しました。

このアプリは携帯端末の壁紙にTwitterの発言を表示します。

Tweetの内容は事前に登録したハッシュタグをランダムで検索を行います。

設定画面で検索するハッシュタグを追加することができます。

ぜひ使ってみてください。

http://jp.androlib.com/android.application.com-kbmj-live-qEEmz.aspx

Railsプログラマが知っておくべき97のこと

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは、SHIMADAです。

最近、オライリー社の新刊「プログラマが知るべき97のこと」がTwitterで話題に上っているようですね。

ここでは、Railsプログラマが知るべきことを挙げてみました。

残念ながら97も思いつかなかったので、25個です。看板に偽りありですね。ごめんなさい!

 コントローラ

  • 行数よりもインデントの深さに気をつける
  • if-then-elseをネストしたくなったらロジックが間違っている
  • アクションでないメソッドはprotected/privateにする
  • 行数を減らす目的でbeforeフィルタを書かない
  • グローバル変数、インスタンス変数にアクセスするポイントを集中させる
  • グローバル変数(paramsとか)に代入しない
  • ダイナミックファイダを使えないfindをみつけたらモデルのメソッド/named_scopeにする

 ビュー

  • eachとif以外のロジックを書かない
  • 複雑になるところはヘルパにまとめる
  • 分からないことはモデルに聞く
  • 分割するためだけにパーシャルしない
  • 代入とか副作用の起こることをしない
  • eachの中でSQLが走らないかチェックする

 モデル

  • DBのテーブルを持たないクラスはAR::Baseを継承しない(笑
  • テーブルを持たないクラスのうち、多態を使う一連のグループはサブディレクトリを作ってモジュールにまとめる
  • モジュールとディレクトリ名の命名規約はRailsに合わせる
  • モデルが大きくなってきたら機能群をモジュールに切り出してincludeする
  • 共有しないモジュールはクラス名::モジュール名にしてクラスと同じ名前のディレクトリに入れる
  • モジュール名は-ableを気取ってみる
  • モジュールに切り出すときはクラス内にモジュールを作ってテストが通るように
  • 定数はselectable_attrを使う
  • 理由を説明できないfind_by_sqlは使わない
  • conditionsをnamed_scopeにできないか検討する

 その他一般論

  • 引数に代入とかしない
  • あるメソッドの中で同じオブジェクトへのメソッド呼び出しが多い場合、メソッド自体を相手に移すことを検討する

 さいごに

いかがでしょうか。

皆さんもそれぞれ「自分はこうすべきだと思う」「これは知っておくべき」という経験則を持っているかと思います。

こういうものを集めて洗練していくと「ベストプラクティス」と呼ばれるものができあがります。

また、これらを体系的に

  • 名前
  • 背景となる状況
  • 課題点
  • 解決策

としてまとめていくと、パタンランゲージができあがります。

みんなで知恵を出しあって、いきいきとしたシステム開発を進めて行きましょう。

……おっと、さっそくCuriさんからビューについてひとことあるようです。

  • 「SEOやEFOのことは頭に入れておけ」

「エントリーフォーム最適化」なんて言葉があるんですね。勉強になりました!

MacにEclipseをインストールする。

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

以外と誰も書いていなかったので、MacにEclipseをインストールする手順を説明します。まずは環境から。

環境

OS : Mac OS X 10.6.5

Eclipseのダウンロード

ここからEclipseをダウンロードしてきます。
種類は使う言語などに合わせてお好みで選び、Mac OS X 32 Bit or Mac OS X 64 Bitのリンクを押下。適当にミラーページを選びダウンロードする。ちなみに、2010年12月現在のEclipse最新阪は、 Helios (3.6)です。

Eclipseのインストール

ダウンロードしてきたEclipseを解凍。解凍して出来たeclipseディレクトリをアプリケーション配下などお好みの場所へ。これでおしまい。

Eclipseの日本語化

解凍した[eclipseディレクトリ]内にある[Eclipse.app]を押下でEclipseが起動しますが、このままだと英語のままです。読めません。というわけで、日本語化します。
方法は色々ありますが今回は、Pleiades というプラグインを使います。
まずは、ここより Pleiades をダウンロード。 Eclipseはすでに、ダウンロードしてあるので、[Pleiades 本体ダウンロード]の項目より、「安定版 1.3.2」をダウンロード。
その後解凍し、できた[pleiadesディレクトリ]内の[plugins]および、[features]の中身を先ほどインストールした、[eclipseディレクトリ]内の、[plugins]、[features]ディレクトリにコピーしてあげます。

コピー後、[eclipseディレクトリ]内の[Eclipse.app]を右クリック、「パッケージの内容を表示」を押下。
Contents → MacOS 内にある、[eclipse.ini] をテキストエディタで開き、最後に下記1文を追加します。
-javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
設定後、Eclipseを起動させるのですが、普通に起動しても、古いプラグイン情報が使われてしますため、clean 起動で起動します。

ターミナルを開き、下記コマンドを実行。/Applications/eclipse/eclipse -clean
これで、日本語化されたEclipseが使えます。

おまけ

いれるとちょっと便利なEclipseのプラグインを紹介します。
プロパティエディタ
htmlエディタ
基本的に[plugins]、[features]ディレクトリに内容をコピーすれば使用できます。

Slony-Iでのフェイルオーバー手順

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Slony-Iで同期をとっている環境での、マスタDBとスレーブDBのフェイルオーバー手順です。

フェイルオーバー前

 マスタDB:db1

 スレーブDB:db2

postgres@db1$ postgres stop
 →障害発生の想定
postgres@db1$ slon_kill
 →db1,db2それぞれでSlonyデーモンを起動している場合なので注意
postgres@db2$ slonik_failover 1 2 | slonik
 →これでdb2がマスタになり更新の操作が行えるようになる。
  フェイルオーバーはマスタが死んでても可能
 →アプリの参照DBを元スレーブのdb2に変更
postgres@db1,2$ vi /usr/local/etc/slon_tools.conf
 ----------
 $MASTERNODE = 2; <==1を2に変えておく
 ----------
postgres@db1$ postgres start
 →障害復旧の想定

db1 をdb2からスキーマダンプなどしてDB作成(createlang忘れずに)

postgres@db2$ slonik_drop_node 1 | slonik
postgres@db2$ slonik_store_node 1 | slonik
postgres@db2$ slonik_subscribe_set set1 1 | slonik
postgres@db1$ slon_start
 →同期開始。冗長性回復

EclipseでGitを使用してバージョン管理を行う

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

EclipseでGitを使用することができるEGitの導入について説明します。なお、EclipseはEclipse Helios (3.6)を使用しており言語ファイルにて日本語化をしています。

ヘルプ>新規ソフトウェアのインストール>Heliosリポジトリ > egit
上記でEclipse Gitというプラグインがみつかるはずです(現在ver0.9.1)

チェックボックスにチェックをいれたらインストールしてEclipseをリスタートします。

ウィンドウ>ビューの表示>その他 > Git > GitRepositories
を選択するとGItリポジトリのウィンドウが下方に表示されます。

中には何も表示されていませんが右クリックでリポジトリの設定を追加することができます。
ここでGitのリポジトリを指定してプロジェクトをインポートすれば完了です。

Egitでの作業はプロジェクトを右クリックしたときにでる「チーム」という項目から各種操作をすることができます。

最近人気な記事