2008/07/17
Rails 2.1.0 をインストールしてみる
久しぶりに、Railsの環境を更新しようと下記のコマンドこ実行すると、エラーがでました。
やはり、すんなりといきませんねぇ~。
> gem update --system
> gem update rails
Updating installed gems
Updating rails
ERROR: While executing gem ... (Gem::InstallError)
invalid gem format for c:/ruby/lib/ruby/gems/1.8/cache/activesupport-2.1.0.gem
いろいろ調べてみると、activesupportを手動でインストールすることで解決できるようです。
http://rubyforge.org/projects/activesupport/
Download gem to a local file, then
sudo gem install --local activesupport-2.1.0.gem
All fixed.
参考:Riding Rails: Rails 2.1: Time zones, dirty, caching, gem dependencies, caching, etc
ラベル: rails
automatically translated by Google Translate Hack!
2008/04/25
ホスト名とOpenID

Rails で OpenID を試してみようと思って思わぬところで躓いたのでメモです。
ホスト名に使用できる文字は、英数字文字(a-zA-Z0-9)および、ハイフン(-)となっています。
ホスト名についてホスト名(hostname)は,[RFC1034]の3.及び[RFC1123]の2.1 で示される形式をとる。すなわち,"."によって分離されたドメインラベルの列であって,各々のドメインラベルは,英数字文字(alphanum)で開始及び終了し,"-"文字を含んでもよい。完全限定ドメイン名の最も右にあるドメインラベルは,数字で始まってはならない。そのために,IPv4アドレスとは構文的に区別されるドメイン名になる。
Uniform Resource Identifiers (URI): Generic Syntax: Main(日本語)
テストのために自分のOpenIDを取得したのですが、これがまた、上で述べたルールに反したものを取得してしまいました・・。
取得したIDは、「ishikawa_rs.openid.ne.jp」なのですが、アンダーバーが入っています><
このIDをOpenIDのプラグインであるopen_id_authenticationに通すと、「ishikawa_rs.openid.ne.jp is not an OpenID URL」と言われてしまいます。
エラーが発生するところをirbで再現してみると下記のようになります。
irb(main):001:0> require 'uri'
=> true
irb(main):002:0> url = "ishikawa_rs.openid.ne.jp"
=> "ishikawa_rs.openid.ne.jp"
irb(main):003:0> uri = URI.parse(url.to_s.strip)
=> #
irb(main):004:0> uri = URI.parse("http://#{uri}") unless uri.scheme
URI::InvalidURIError: the scheme http does not accept registry part: ishikawa_rs
.openid.ne.jp (or bad hostname?)
from c:/ruby/lib/ruby/1.8/uri/generic.rb:195:in `initialize'
from c:/ruby/lib/ruby/1.8/uri/http.rb:78:in `initialize'
from c:/ruby/lib/ruby/1.8/uri/common.rb:488:in `new'
from c:/ruby/lib/ruby/1.8/uri/common.rb:488:in `parse'
from (irb):4
irb(main):005:0>
bad hostnameです。
そもそも、なぜこのIDになったかというと、「ishikawa.rs」にしようとしたところ、「ドット(.)はだめです。アンダーバー(_)は使えるよ。」と言われたからでした・・。
OpenIDを取得する際、ユーザIDがホスト名になる場合はご注意ください。
※そして、困ったことにopenid.ne.jpではアカウントの削除ができないようです。
automatically translated by Google Translate Hack!
2008/03/28
Rails と AIR で、付箋紙アプリ
Rails の最新は 2.0.2 ですし、AIR は正式版の 1.0 が公開されました。
情報はある程度追ってはいるものの、やはり実際に試してみないとなかなか身につきません。
というわけで、以前から気になっていた、[Think IT] 第1回:付箋紙アプリケーションを作ろう!を参考に、Ruby on RailsとAIRによるデスクトップ付箋紙アプリケーションを作ってみました。
Adobe AIR のインストールはこちらから。
サンプル付箋紙アプリをお試しいただく場合は、こちらから。
ちなみに、このアプリはユーザ管理はしておりません。ので、大変ソーシャルな付箋アプリです(汗
基本的な動作は、[Think IT] 第1回:付箋紙アプリケーションを作ろう!と、Ruby on RailsとAdobe AIRでデスクトップアプリを作る - Pokeal.COMをベースに、なんとなくタスクトレイアイコンも使ってみました。ついでに、常に前面表示も可能です。
Railsに関しては、実質、次の2行のみです。これでRestfulなバックエンドアプリのできあがりです・・。
$ ruby script/generate scaffold sticky body:text x:float y:float width:integer height:integer $ rake db:migrate
なお、Railsアプリは、先日紹介した、Herokuで構築しています。
AIRでは、透過するTextFieldを作るのにちょっと苦労しました。
ポイントは、blendModeをLAYERにしたSpriteでした。
I am not sure if this is quite what you are looking for but here is a solution I found for adjusting alpha on a TextField object.
// create an empty sprite var rect:Sprite = new Sprite(); rect.blendMode = BlendMode.LAYER; addChild(rect); var txtField:TextField = new TextField(); txtField.txtColor = 0x000000; txtField.alpha = .5; txtField.appendText("SOME TEXT"); rect.addChild(txtField);the key is to set the blendMode property on the sprite to LAYER.
以下、参考までにソースコードです。
Menu.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="300" height="200" creationComplete="onCreationComplete();" closing="closing(event);">
<mx:Script>
<![CDATA[
import mx.core.BitmapAsset;
[Embed(source="icons/broken-16x16.png")]
private var icon16:Class;
private var stickies:Array = [];
private function create():void {
setStatus(">> Create new sticky");
var sticky:Sticky = new Sticky();
sticky.setAlwaysInFront(isAlwaysInFront.selected);
sticky.save();
sticky.show();
stickies.push(sticky);
}
private function onCreationComplete():void {
setStatus(">> Initializing...");
// is supports system tray icon
if (NativeApplication.supportsSystemTrayIcon) {
var images:Array = [];
images.push((new icon16() as BitmapAsset).bitmapData);
nativeApplication.icon.bitmaps = images;
var systemTrayIcon:SystemTrayIcon = (nativeApplication.icon as SystemTrayIcon);
systemTrayIcon.tooltip = "Stickynotes";
var nativeMenu:NativeMenu = new NativeMenu();
var menuItemNew:NativeMenuItem = new NativeMenuItem("New Stickynote");
menuItemNew.addEventListener(Event.SELECT, function(e:Event):void {
create();
});
var menuItemReload:NativeMenuItem = new NativeMenuItem("Reload Stickynotes");
menuItemReload.addEventListener(Event.SELECT, function(e:Event):void {
load();
});
var menuItemRestore:NativeMenuItem = new NativeMenuItem("Restore window");
menuItemRestore.addEventListener(Event.SELECT, function(e:Event):void {
restore();
});
var menuItemExit:NativeMenuItem = new NativeMenuItem("Exit");
menuItemExit.addEventListener(Event.SELECT, function(e:Event):void {
exit();
});
nativeMenu.addItem(menuItemNew);
nativeMenu.addItem(menuItemReload);
nativeMenu.addItem(menuItemRestore);
nativeMenu.addItem(menuItemExit);
systemTrayIcon.menu = nativeMenu;
}
load();
}
private function load():void {
setStatus(">> Loading from http://stickynotes.heroku.com ...");
closing(null);
var request:URLRequest = new URLRequest("http://stickynotes.heroku.com/stickies.xml");
request.method = 'GET';
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, function(e:Event):void {
messages.text += e.target.data + "\n";
var xml:XML = new XML(e.target.data);
for each (var element:Object in xml.sticky) {
var sticky:Sticky = new Sticky();
sticky.id = element.id;
sticky.editor.text = element.body;
sticky.window.x = element.x * Capabilities.screenResolutionX;
sticky.window.y = element.y * Capabilities.screenResolutionY;
sticky.window.width = element.width;
sticky.window.height = element.height;
sticky.setAlwaysInFront(isAlwaysInFront.selected);
sticky.updateStatus();
sticky.show();
stickies.push(sticky);
}
clearStatus();
});
loader.load(request);
}
private function setStatus(s:String):void {
this.status = s;
}
private function clearStatus():void {
this.status = "";
}
private function closing(e:Event):void {
for each (var sticky:Sticky in stickies) {
if (sticky) {
sticky.window.close();
}
}
stickies = [];
}
private function saveAll():void {
setStatus(">> Save stickies");
for each(var sticky:Sticky in stickies) {
sticky.save();
}
}
private function onChangeHandle():void {
for each (var sticky:Sticky in stickies) {
sticky.setAlwaysInFront(isAlwaysInFront.selected);
}
}
]]>
</mx:Script>
<mx:Button left="10" top="10" label="New" id="new_btn" click="create();" />
<mx:Button left="60" top="10" label="Reload" id="load_btn" click="load();" />
<mx:Button right="10" top="10" label="Save" id="save_btn" click="saveAll();" />
<mx:CheckBox left="10" top="40" label="always in front" id="isAlwaysInFront" change="onChangeHandle();" />
<mx:TextArea right="10" top="70" left="10" bottom="10" id="messages" />
</mx:WindowedApplication>
Sticky.as
package {
import flash.text.*;
import flash.display.*;
import flash.events.*;
import flash.system.*;
import flash.net.*;
import mx.controls.*;
public class Sticky {
public var window:NativeWindow; // sticky window
public var editor:TextField; // sticky edit area
public var id:Number; // primary key
private var button:SimpleButton = new SimpleButton(); // cloase button
private var resizeHandle:SimpleButton = new SimpleButton(); // resize handle
private var x:Number; // sticky x
private var y:Number; // sticky y
private var height:Number; // sticky height
private var width:Number; // sticky width
private var body:String; // sticky body
private var sprite:Sprite = new Sprite();
private static var RESIZE_HANDLE_SIZE:int = 20;
/* create sticky window */
public function Sticky():void {
var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
initOptions.systemChrome = NativeWindowSystemChrome.NONE;
initOptions.transparent = true;
initOptions.type = NativeWindowType.LIGHTWEIGHT;
window = new NativeWindow(initOptions);
window.alwaysInFront = false;
window.stage.align = StageAlign.TOP_LEFT;
window.stage.scaleMode = StageScaleMode.NO_SCALE;
// layer for transparent editor
sprite.blendMode = BlendMode.LAYER;
window.stage.addChild(sprite);
// for edit area
editor = new TextField();
editor.x = editor.y = 0;
editor.selectable = true;
editor.border = false;
editor.type = TextFieldType.INPUT;
editor.multiline = true;
editor.background = true;
editor.wordWrap = true;
editor.backgroundColor = 0xE6E082;
editor.alpha = 1;
sprite.addChild(editor);
// window position of center
window.x = 0.5 * Capabilities.screenResolutionX - 300 * 0.5
window.y = 0.5 * Capabilities.screenResolutionY - 100 * 0.5
// size for window and edit area
window.width = editor.width = 300;
window.height = editor.height = 100;
// resize for window and edit area
window.stage.addEventListener(Event.RESIZE, function(e:Event):void {
resized();
});
// move and resize
window.stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
var x:Number = e.stageX;
var y:Number = e.stageY;
if (y > window.height - RESIZE_HANDLE_SIZE && x > window.width - RESIZE_HANDLE_SIZE) {
//window.startResize(NativeWindowResize.BOTTOM_RIGHT);
} else {
window.startMove();
}
});
window.stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
if (isChanged()) {
save();
}
});
editor.addEventListener(FocusEvent.FOCUS_OUT, function(e:FocusEvent):void {
if (isChanged()) {
save();
}
});
} // end of function Sticky
// show window
public function show():void {
// create close button
button.x = window.width - 15;
button.y = 5;
button.upState = createBox(0xE6E082, 10, 0.3);
button.overState = createBox(0xE6E082, 10, 1);
button.downState = createBox(0xCCCCCC, 10, 1);
button.hitTestState = button.upState;
button.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
var request:URLRequest = new URLRequest("http://stickynotes.heroku.com/stickies/" + id + ".xml");
request.method = 'DELETE';
var loader:URLLoader = new URLLoader();
loader.load(request);
window.close();
});
window.stage.addChild(button);
// create resize handle
resizeHandle.x = window.width - 15;
resizeHandle.y = window.height - 15;
resizeHandle.upState = createResizeHandle(0xE6E082, 10, 0.3);
resizeHandle.overState = createResizeHandle(0xE6E082, 10, 1);
resizeHandle.downState = createResizeHandle(0xCCCCCC, 10, 1);
resizeHandle.hitTestState = button.upState;
resizeHandle.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
window.startResize(NativeWindowResize.BOTTOM_RIGHT);
});
window.stage.addChild(resizeHandle);
window.visible = true;
}
// call after window resized
public function resized():void {
editor.width = window.width;
editor.height = window.height;
button.x = window.width - 15;
button.y = 5;
resizeHandle.x = window.width - 15;
resizeHandle.y = window.height - 15;
}
// save sticky
public function save():void {
// create URL of sticky
var request:URLRequest
if (!id) {
request = new URLRequest("http://stickynotes.heroku.com/stickies.xml");
request.method = 'POST';
} else {
request = new URLRequest("http://stickynotes.heroku.com/stickies/" + id + ".xml");
request.method = 'PUT';
}
// create paramater for edit
var variables:URLVariables = new URLVariables();
variables['sticky[x]'] = window.x / Capabilities.screenResolutionX;
variables['sticky[y]'] = window.y / Capabilities.screenResolutionY;
variables['sticky[width]'] = window.width;
variables['sticky[height]'] = window.height;
variables['sticky[body]'] = editor.text;
request.data = variables;
// send
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, function(e:Event):void {
var xml:XML = new XML(e.target.data);
if (!id) {
id = xml.id;
}
updateStatus();
});
loader.load(request);
}
public function setAlwaysInFront(value:Boolean):void {
window.alwaysInFront = value;
if (value) {
editor.alpha = 0.85;
} else {
editor.alpha = 1;
}
}
public function updateStatus():void {
// sticky status
x = window.x;
y = window.y;
width = window.width;
height = window.height;
body = editor.text;
}
/* private methods ***********/
// close button
private function createBox(color:uint, radius:Number, vis:Number):Shape {
var xShape:Shape = new Shape();
xShape.graphics.lineStyle(1, 0x000000);
xShape.graphics.beginFill(color);
xShape.graphics.drawRect(0, 0, radius, radius);
xShape.graphics.moveTo(0, radius);
xShape.graphics.lineTo(radius, 0);
xShape.graphics.moveTo(radius, radius);
xShape.graphics.lineTo(0, 0);
xShape.graphics.endFill();
xShape.alpha = vis;
return xShape;
}
private function createResizeHandle(color:uint, radius:Number, vis:Number):Shape {
var xShape:Shape = new Shape();
xShape.graphics.lineStyle(1, 0x000000);
xShape.graphics.beginFill(color);
xShape.graphics.moveTo(0, radius);
xShape.graphics.lineTo(radius, 0);
xShape.graphics.lineTo(radius, radius);
xShape.graphics.lineTo(0, radius);
xShape.graphics.endFill();
xShape.alpha = vis;
return xShape;
}
// is paramater changed
private function isChanged():Boolean {
return (x != window.x || y != window.y ||
width != window.width || height != window.height ||
body != editor.text);
}
} // end of class Sticky
} // end of package
automatically translated by Google Translate Hack!
2008/02/24
ブラウザだけでRuby on Rails - Heroku -
以前、TechCrunchの記事でHerokuを知って、すぐにベータテスタとして登録したのですが、先日アカウント登録のメールが届いたのでさっそく使ってみました。
Herokuとは、ブラウザの中だけで、Railsのアプリケーションを開発できるサービスです。また、作成したアプリは、Amazon EC2上で簡単にホストすることもできるようです。
気になるRailsのバージョンは「2.0.2」になるようです。また、データベースはPostgresなようです。
ほんとうに簡単に開発からデプロイまでできるので、ちょっとしたアプリを作るにはもってこいですね。
それに、後に公開されるプレミアム版では、サブドメインではなく、独自ドメインも使用可能になるようですので、要注目です。
ラベル: rails
automatically translated by Google Translate Hack!
2007/06/26
Rails API を読む
Railsに限らず、Documentは非常に重要で、とても参考になるものです。
いつもはRails Framework Documentを参考にするのですが、他に以下のようなサイトもあるようです。知らなかった・・・w
Ruby on Rails Manual
過去のバージョンのDocumentも揃っています。
Rails API with the AJAX flavor
AjaxでSuggestしてくれるので便利!
gotAPI/HTML - Instant search in HTML and other developer documentation
AJAX and Frameworks(Prototype.js等)や、HTML/CSS/Javascript等、Rails以外のも多数載ってます。こちらもAjaxでSuggest!これはいい!
ラベル: rails
automatically translated by Google Translate Hack!
2007/06/21
Edge Railsとruby-Gettext
先日、新しいRailsプロジェクトを作る機会がありまして、せっかくなのでRESTfulなRailsでいこうと思い、Edge Rails使うことを決意。
ですが、さっそく躓いたので、以下問題と解決です。ご参考までに。
アプリを起動するとエラーが発生し、development.logに以下のエラーが。
DISPATCHER FAILSAFE RESPONSE (has cgi) Wed Jun 20 21:18:28 +0900 2007
Status: 500 Internal Server Error
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
C:/ruby/lib/ruby/1.8/cgi.rb:1165:in `[]'
C:/ruby/lib/ruby/gems/1.8/gems/gettext-1.9.0-mswin32/lib/gettext/locale_cgi.rb:26:in `system'
C:/ruby/lib/ruby/gems/1.8/gems/gettext-1.9.0-mswin32/lib/gettext/locale.rb:88:in `system'
C:/ruby/lib/ruby/gems/1.8/gems/gettext-1.9.0-mswin32/lib/gettext/locale.rb:96:in `default'
C:/ruby/lib/ruby/gems/1.8/gems/gettext-1.9.0-mswin32/lib/gettext/rails.rb:276:in `render_file'
:
:
[gettext-u-en] Problem with gettext 1.9.0 and RESTful rails appを参考に、問題解決(ここまでくるのに、数日の間Google先生に質問攻めでした・・・)
--- /opt/local/lib/ruby/gems/1.8/gems/gettext-1.9.0/lib/gettext/locale_cgi.rb 2007-05-28 16:11:27.000000000 +0300
+++ Desktop/locale_cgi.rb 2007-05-28 16:11:49.000000000 +0300
@@ -23,7 +23,7 @@ module Locale
def system
return @@default_locale unless @@cgi
cgi_ = cgi
- if ret = cgi_["lang"] and ret.size > 0
+ if not cgi_.params.empty? and ret = cgi_["lang"] and ret.size > 0
elsif ret = cgi_.cookies["lang"][0]
elsif lang = cgi_.accept_language and lang.size > 0
num = lang.index(/;|,/)
上記問題が発生したのは、
svn co http://dev.rubyonrails.org/svn/rails/trunk rails
でとってきたEdge Railsでした。
今しがた、再度Edge Railsを取り直したら上記問題は発生せずでした・・・
rake rails:freeze:edge TAG=rel_1-2-3
なんだったんだろw
ラベル: rails
automatically translated by Google Translate Hack!
2007/03/24
logに残されるパスワードをフィルタリングする
Railsアプリを作成していて、動作の確認等で非常に重要なログファイルですが、このログには様々な情報が残されます。
例えばユーザ登録画面等で送信したパラメータもログに残されます。
ユーザ登録の際は、パスワードを入力したりしますので、パスワードがそのまま平文で残されるのはちょいとまずいものです。
Processing UserController#signup (for 192.168.0.25 at 2007-03-24 10:41:08) [POST]
Session ID: 42d8820cd16b8a672b86f737d8d6b4e8
Parameters: {"user"=>{"password_confirmation"=>"testpassword", "lastname"=>"Tarou", "firstname"=>"Test", "login"=>"tester", "password"=>"testpassword", "email"=>"test@test.com"}, "commit"=>"Signup", "action"=>"signup", "controller"=>"admin/user"}
そこで、以下のようにするとログに残されるパスワードをフィルタリングすることができます。
filter_parameter_logging(*filter_words) {|key, value| ...}
class ApplicationController < ActionController::Base
filter_parameter_logging "password"
end
すばらしい!
Processing UserController#signup (for 192.168.0.25 at 2007-03-24 10:43:50) [POST]
Session ID: 42d8820cd16b8a672b86f737d8d6b4e8
Parameters: {"user"=>{"password_confirmation"=>"[FILTERED]", "firstname"=>"Test", "lastname"=>"Tarou", "password"=>"[FILTERED]", "login"=>"tester2", "email"=>"test2@test.com"}, "commit"=>"Signup", "action"=>"signup", "controller"=>"admin/user"}
ラベル: rails
automatically translated by Google Translate Hack!
2007/03/13
J-PHONE/3.0で、postがうまくいかない
携帯向けアプリを構築していて、特定の端末(おそらく)の場合だけ、post送信時、sessionが維持されない現象を確認。
post送信されるパラメータで、「"_session_id"=>"901cbb6b5a6d515a99a06373d20ba2f4?page=1"」というように、セッションIDのなかになぜか他のパラメータが混入する。
これでは当然、「そんなセッションありません」、ということになってsessionが維持できなくなります。
で、あれこれ試行錯誤した結果、どうやら、formのアクションで、自動でURLパラメータとしてsession_idを付与しているところが問題のよう。
formのアクションを直打ちして解決しました。
ちなみに自動でsession_idを付与してくれるのは、ActiveHeartのTransSidのお陰です。
参考:
RubyOnRails を使ってみる 【第 5 回】 ActiveHeart
ラベル: rails
automatically translated by Google Translate Hack!
2007/02/14
file_column and capistrano
Railsで画像ファイルをアップロードする場合、file_columnというプラグインがとても使えます。
このpluginは、画像のアップロード、保存、サムネイル作成といった面倒な処理を劇的に簡略化してくれます。
データベースにファイル名を保存するカラムを追加し、
add_column :entry, :image, :string
モデルで、file_column pluginを指定します。
class Entry < ActiveRecord::Base
file_column :image
end
この場合の保存先は、
public/[model_name]/[attribute_name]/[id]/[file_name].jpg
になります。
サムネイルを作成する場合は、次のようにします。
class Entry < ActiveRecord::Base
file_column :image,
:magick => {
:versions => {
:thumb => "50x50",
:midle => "100x100",
:large => "800x600"
}
}
end
この場合はそれぞれ、
public/[model_name]/[attribute_name]/[id]/[file_name].jpg
public/[model_name]/[attribute_name]/[id]/thumb/[file_name].jpg
public/[model_name]/[attribute_name]/[id]/midle/[file_name].jpg
public/[model_name]/[attribute_name]/[id]/large/[file_name].jpg
に保存されます。
ファイルをアップロードするには以下のhelperを使用します。
<%= file_column_field "entry", "image" %>
アップロードした画像を表示するには、以下のhelperを使用します。
<%= url_for_file_column "entry", "image" %>
サムネイルを表示するには、
<%= url_for_file_column "entry", "image", "thumb" %>
上記のように簡単に、非常に簡単に画像処理を扱えるようになるわけですが、capistranoでデプロイしている場合、画像の保存先がデフォルトのままだと、デプロイするたびに画像ファイルをコピーしなければなりません。
これではあんまりですが、ちゃんと回避策がありました。
file_columnのオプションに、root_pathというのがありまして、デフォルトの保存先を変更できます。
以下のように、capistranoが自動でリンクしてくれるsystemディレクトリに画像を保存するように指定すれば、いちいちコピーしなくてもよさそうです。
class Entry < ActiveRecord::Base
file_column :image,
:magick => {
:versions => {
:thumb => "50x50",
:midle => "100x100",
:large => "800x600"
}
},
:web_root => "system/files/",
:root_path => File.join(RAILS_ROOT, "public", "system", "files")
end
ラベル: rails
automatically translated by Google Translate Hack!
2007/01/31
増加するログファイルへの対処
Railsでは、ログファイルに絶えず情報が追加されていき、ものすごい勢いで肥大化していくわけですが、でかいログファイルは様々な面でよくありません。
ですので、定期的にログファイルのローテーションをするわけですが、主にlogrotateを使用する方法と、Loggerを使用する方法があるようです。
ただし、FastCGIをマルチで使用している場合は、各FastCGIプロセスにてLoggerインスタンスが存在し、同一ログをローテートしてしまうため、Loggerの使用は控えたほうが良いようです。
というわけで、logrotateを使用します。
/path/to/your/app/log/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
copytruncate
create 0666 daemon daemon
}
参考:ラベル: rails
automatically translated by Google Translate Hack!
2007/01/30
ActiveRecodStoreな場合のセッションの掃除
Railsでは、セッションが自動生成されるのですが、自動削除はしてくれません。
ですので、自分で掃除をしなければいけません。
session_cleaner.rb
class SessionCleaner
def self.remove_stale_sessions
CGI::Session::ActiveRecordStore::Session.destroy_all(['updated_on < ?', 1.days.ago])
end
end
上記クラスをlibあたりに保存し、cronで毎日自動実行させます。0 0 * * * ruby /full/path/to/script/runner -e production "SessionCleaner.remove_stale_sessions"
参考:
Removing Stale Rails Sessions
ラベル: rails
automatically translated by Google Translate Hack!
2007/01/23
jpmobile使用時のfunctionalテスト
[03/18追記]
下記は何れも既に対応されております。ご苦労様です。
詳しくはdara日記 [jpmobile]をご覧下さい。
dara日記 - jpmobile - A Rails plugin for Japanese mobile-phones
jpmobileというプラグインを使用すると、携帯向けのサイト構築が非常に楽になります。
jpmobileを使用すると、以下のことができるようになります。
- 携帯電話の判別
- 端末位置情報の取得
- 端末製造番号、契約者番号等の取得
- IPアドレスの検証(キャリアが公開しているIPアドレス帯域からのアクセスか判定)
ものすごく便利なプラグインなのですが、functionalテスト時にはまりました。
NoMethodError: undefined method `mobile?' for #
mobile?そんなメソッドありません。とのことです。で、悩んだ挙句導き出した答えは、
ActionController::TestRequest.class_eval { include Jpmobile::CgiRequestExpansion}
を、テストクラスにて記述します。きっと、テスト用のリクエストにjpmobileの機能をincludeするってことだと思います(w)。
それと、ソフトバンク携帯からの実機確認時に、なぜか携帯端末と判定されないという問題が発生。
こちらは、vendor/plugins/jpmobile/lib/jpmobile/cgi_request_expansion.rb のSoftbank端末判定部分を
when /^Softbank/
から、
when /^SoftBank/
に修正しました。
ラベル: rails
automatically translated by Google Translate Hack!
2007/01/18
DateHelperでid属性を使いたい
RailsにはDateHelperという便利なものがありまして、これを利用すると簡単に日付や時刻のセレクトボックスを作成できます。
ですが、なぜかidや、class等の属性が無視されてしまいます。(Rails 1.1.6にて。最新のものはどうなの?)
javascriptで操作したいときなど、idがないと困ります。
そんなときは、下記のようにするとよいです。(bad hack!)
<%= select_day(Date.today, :prefix => 'search[date][day]" id="day_field', :discard_type => true) %>
参考:
Rails Forum / Ruby on Rails Help and Discussion Forum / How to give an ID to an input when using select helpers?
ラベル: rails
automatically translated by Google Translate Hack!

