前些天去參加了Google的開發日活動,由于之后直接就去郊區所以一直沒有時間寫blog來談談感想。首先是google的OpenSocial,進入大廳簽到之后轉角過去就是個大網站的展臺,每個柱子的一面是一個網站,都是OpenSocial的合作網站,有聚友,天際,天涯等。有幸跟聚友的CTO譚曉生先生和天際網的CTO郭應壽先生聊了聊OpenSocial使用中的益處和遇到的問題,感覺OpenSocial目前還是不太成熟,對比FB來說還有一定的差距,比如沒有照片處理等等問題。而且我始終有一種感覺,OpenSocial是一個google的標準,但是各大網站在使用OpenSocial的時候幾乎都會加上自己的擴展,這樣下去,最終OpenSocial統一平臺的目標很難實現,因為兩個網站的Gidget幾乎不能通用,這些問題OpenSocial的合作網站跟Google也在討論中。另外得到的消息是,OpenSocial可能也會考慮出一套XXml,規范化OpenSocial的使用,并借此加強安全性。
稍后的OpenSocial專題topic中了解到了更多有關OpenSocial的信息,主要是Shindig了, 前一天還在跟白衣提議用ss做一個類似Shindig的東西,主要是做一個OpenSocial的實現,當時就想這玩意估計有了,但是沒想到是host到apache了,在apache的孵化器中大家可以自己找找。會議的時候逮住了一個google的人問了一堆問題,過后才知道原來是google中國sns的負責人。
最大的收獲實在是太意外了,在中午休息的時候,走過過道到樓下吃飯,突然發現一個熟悉的身影坐在角落里一個人在玩電腦,我暈是Andy Rubin,Android的老大。居然就坐在那里,而且旁邊的人沒有人在意他,要知道聽android topic的人可不少呀,居然每一個人認出他來的。我叫了朋友確認一下,準備上前打招呼。剛上去打了個招呼,握了個手,又來了一個人,他一下認出Andy,并且跟Andy簡短的交談了一下。我提議先照個相,照相后簡短聊下那個人就走了。過后Andy說那個人就是Google中國負責android的老大邸爍博士,我狂暈呀兩位老大都讓我給碰上了。稍候跟Andy聊了一下Android的發展,隨后他掏出一個android的原型機,就是google IO大會上的那種機器。這里要說明的是,不管在模擬器上android的表現如何,在真機上你的體驗絕對絕對是不一樣的,太COOL了。尤其是第二屏的widget效果,還有那個street view效果,真機拿在手上體驗就是不一樣呀~~~~ 隨后又了解到了一些android目前的發展情況,這里可以告訴大家的是,android是絕對值得期待的~!!!
posted @
2008-06-15 13:03 差沙 閱讀(2799) |
評論 (4) |
編輯 收藏
經過兩天的努力,終于在Android上面跑起來MSN了。現在實現了基本的聊天功能,打算陸續的把其他功能完善,萬事開頭難,相信以后的工作會很順利。
Gtalk的功能由于有官方支持,早就弄了跟這個差不多的版本。
另外,這個軟件目前是以插件的形式開發,多一個協議就是多一個apk,實現一些intent即可。官方的IM包里面有很多支持,可惜文檔沒有提及。

Android中文論壇原帖
posted @
2008-03-21 01:59 差沙 閱讀(4216) |
評論 (2) |
編輯 收藏
今天看到Air1.0和Ext2.0.2的發布消息,很是興奮,要知道Ext的每個版本升級都是很實惠的。
Ext2.0.2的這次升級主要是針對Air的支持,看來他們的關系是越來越近了,我也借機小試了一下Air。
簡單說說Air的開發過程:
1.先要下載jre 和 airSDK
2.jre就不說了,airSDK也是簡單的解壓一下便可。
可以看看SDK下面的bin目錄,一共倆文件,一個adl一個adt,adl是用來測試的,adt使用編輯發布的(ADT怎么讓我想起來android了)
3.創建一個普通的目錄作為項目目錄。OK,可以寫頁面了。就是寫html js flash什么的,都可。
4.編寫一個xml文件,名字不限。內容嘛~~~~
看看這個文件,airSDK\templates\descriptor-template.xml 里面有詳細的解釋,拿過來改改即可,給一個例子:
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.0.M6">
<id>examples.html.HelloWorld</id>
<version>0.1</version>
<filename>HelloWorld</filename>
<initialWindow>
<content>HelloWorld.html</content>
<visible>true</visible>
<width>400</width>
<height>200</height>
</initialWindow>
</application>
5.運行airSDK\bin\adl.exe (那個創建的xml文件名),運行即可。
PS:我一般用eclipse來編輯,用外部命令的工具來運行adl,很方便。
簡單的說了一下Air的開發方法后,我們來簡單的看一下Ext都對Air提供了什么支持。
1.Ext.air.FileProvider
Ext提供狀態保存的功能,在對AIr的支持中Ext實現了一個Provider,這個Provider把狀態保存在本地目錄的一個文件里。使用方法如下:
// Initialize the state provider
Ext.state.Manager.setProvider(new Ext.air.FileProvider({
file: 'ext.state',
// if first time running
defaultState : {
mainWindow : {
width:400,
height:200,
x:10,
y:10
}
}
}));
制定Provider為Ext.air.FileProvider即可,這個操作一般在onReady里面執行。當然需要你先創建一個ext.state文本文件,在程序根目錄下即可。這樣ext的應用運行時,窗口等大小的調整,位置之類的調整都能保存下來了。很方便。
這里包裝的Air代碼主要是:
air.File.applicationStorageDirectory.resolvePath
air.FileStream
等
2.Ext.air.NativeWindow
Ext可以調用Air實現Native的窗口。使用方法如下:
var win = new Ext.air.NativeWindow({
id: 'mainWindow',
instance: window.nativeWindow,
minimizeToTray: true,
trayIcon: 'ext-air/resources/icons/extlogo16.png',
trayTip: 'Simple Tasks',
trayMenu : [{
text: 'Open Simple Tasks',
handler: function(){
win.activate();
}
}, '-', {
text: 'Exit',
handler: function(){
air.NativeApplication.nativeApplication.exit();
}
}]
});
我們可以看到,其中最吸引人的是系統托盤的功能,可以方便的指定系統托盤的各種屬性。
這里包裝的主要是:
window.runtime.flash.html.HTMLLoader.createRootWindow
air.Screen.mainScreen.visibleBounds
3.Ext.sql.Connection
這個是重頭戲,對db的支持,我們知道Air的DB是采用sqlite3來實現的。Ext封裝了Air的db操作,簡化統一了一些,而且提供了Store的支持,非常強大:
讓我們簡單看一下用法:
首先取得connection
var conn = Ext.sql.Connection.getInstance();
而后打開db
conn.open('ext.db');//這里的文件如果不存在會自動創建
創建Table
conn.createTable({
name: 'user',
key: 'userId',
fields: [
{name: 'userId', type:'string'},
{name: 'name', type:'string'},
{name: 'pwd', type:'string'},
{name: 'email', type:'string'}
]
});
取得一個Table
var userDao = conn.getTable('user','userId');
查詢
var users = userDao.select();//這里可以寫條件式
添加
var newuser = {
userId : 'sshw',
name : 'sshwsfc',
pwd : 'test',
email : 'sshwsfc@gmail.com'
}
userDao.insert(newuser);
感覺使用上還是蠻不錯的,大家可以一點點試試看。
值得一提的是,在源碼中發現Connection這里設計成了工廠的模式,Connection是一個偽接口,實現除了Air的還有GoogleGear的,會根據所處環境選擇,灰常的好。
功能還在研究中,有心得了一點點給大家分享,
PS:打算搞一個iphone的信息管理軟件練練手。反正都是sqlite的
posted @
2008-02-26 13:49 差沙 閱讀(4810) |
評論 (4) |
編輯 收藏
在朋友和同事的極力推薦下最近開始看上了python,其實主要是還是因為python是2007年度語言,怎么的也要與時俱進呀.最近一路看來有些心得,希望能與大家分享,小弟其實也只接觸不到一周的python,有說錯的地方還望大家指出改正.
不打算從py的語法基礎說起了,直接說說對django的心得:
接觸django首先需要了解可能就是他那個model,建立一個model就什么都有了,這對于搞java得人員來說還是挺有吸引力的(當然貌似對于動態語言這都是小兒科),那么讓我們先看一個model的例子:
偷懶了,直接拿django-admin里面的User出來了
class User(models.Model):
username = models.CharField(_('username'), maxlength=30, unique=True, validator_list=[validators.isAlphaNumeric]))
first_name = models.CharField(_('first name'), maxlength=30, blank=True)
last_name = models.CharField(_('last name'), maxlength=30, blank=True)
email = models.EmailField(_('e-mail address'), blank=True)
password = models.CharField(_('password'), maxlength=128))
class Meta:
ordering = ('username',)
每個屬性就是一個庫表的字段,定義起來非常簡單明了,models里面提供了很多種類的Field類似上面的EmailField。不同的Field有不同的設置,可以看相應的原來來了解相關的設置.
在model class內部還有一個class Meta,這個Class的屬性制定了這個表的一些存取策略,例如這里的ordering。MetaClass里面的屬性可以用model的_meta屬性取得。OK,那么這樣一個model怎么就能實現對數據庫表的靈活操作了呢。讓我們來看看吧。
首先先分析一下/django/django/db/models/base.py這個文件,其中包含了models.Model這類的定義:
看看class定義的第一行吧,第一行就夠我琢磨一陣子的了:
class Model(object):
__metaclass__ = ModelBase
Model采用了new style class定義,關于這個內容大家可以放狗看一下,第一行是一個__metaclass__屬性的定義,該屬性的值是ModelBase,這是一個類。__metaclass__的意思是,指定一個class,這個class的實例就是本class,相信您已經暈了。那么就拿這個Model的例子來說明一下,如果沒有__metaclass__這個屬性,產生一個實例就是正常的流程,有了這個屬性流程會有改變:
首先調用BaseModel.__new__(cls, name, bases, attrs)這個方法,回返回的值是一個class類型,然后用這個class來創建實例。其實BaseModel就是Model的元類,來制定Model這個類的最終樣子。關于元類的更多信息請看這里
那么我們的目光一下轉移到BaseModel這個類上,我有種直覺,Meta這個class最后可以用_meta來取就是在這里做的手腳,看一下BaseModel的定義吧,有點長:
class ModelBase(type):
"Metaclass for all models"
def __new__(cls, name, bases, attrs):
# If this isn't a subclass of Model, don't do anything special.
if name == 'Model' or not filter(lambda b: issubclass(b, Model), bases): #1
return super(ModelBase, cls).__new__(cls, name, bases, attrs)
# Create the class.
new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')}) #2
new_class.add_to_class('_meta', Options(attrs.pop('Meta', None))) #3
new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}))
# Build complete list of parents #4
for base in bases:
# TODO: Checking for the presence of '_meta' is hackish.
if '_meta' in dir(base):
new_class._meta.parents.append(base)
new_class._meta.parents.extend(base._meta.parents)
model_module = sys.modules[new_class.__module__]
if getattr(new_class._meta, 'app_label', None) is None:
# Figure out the app_label by looking one level up.
# For 'django.contrib.sites.models', this would be 'sites'.
new_class._meta.app_label = model_module.__name__.split('.')[-2] #5
# Bail out early if we have already created this class.
m = get_model(new_class._meta.app_label, name, False) #6
if m is not None:
return m
# Add all attributes to the class.
for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj) #7
# Add Fields inherited from parents
for parent in new_class._meta.parents:
for field in parent._meta.fields:
# Only add parent fields if they aren't defined for this class.
try:
new_class._meta.get_field(field.name)
except FieldDoesNotExist:
field.contribute_to_class(new_class, field.name) #8
new_class._prepare()
register_models(new_class._meta.app_label, new_class) #9
# Because of the way imports happen (recursively), we may or may not be
# the first class for this model to register with the framework. There
# should only be one class for each model, so we must always return the
# registered version.
return get_model(new_class._meta.app_label, name, False) #10
簡單分析一下這個代碼:
1. 檢查class是否為Model的子類,不是的話,不做任何處理,直接傳給父類處理,也就相當于正常的處理了class,注意super在多重繼承的時候應該嚴格使用
2. 用type來創建類,創建的就是正常的ModelClass
3. 這句很重要,add_to_class是Model里面的class方法,這個方法其實就是傳入name和value,給Model添加class屬性.看到了,原來神奇的_meta就是這么來的. 提到add_to_class方法,簡單看一下它的代碼:
def add_to_class(cls, name, value):
if name == 'Admin':
assert type(value) == types.ClassType, "%r attribute of %s model must be a class, not a %s object" % (name, cls.__name__, type(value))
value = AdminOptions(**dict([(k, v) for k, v in value.__dict__.items() if not k.startswith('_')]))
if hasattr(value, 'contribute_to_class'):
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
add_to_class = classmethod(add_to_class)
最后一句是制定這個方法是class方法,特點就是方法的第一個參數是本class,其實classmethod就是一個裝飾器,在2。4之后可以使用@來簡寫。這里不得不提的是他對Admin的特殊處理,雖然AdminOption不是在admin模塊里面的,但是這么做還是跟一個Admin的東東綁定起來了,在java的世界解耦是一件大事,看到下面還有對'contribute_to_class'這個方法的特殊處理,django為啥不弄的解耦點呢。而且同樣是包裝成Option,一個是在BaseModel里面弄(那個Meta的包裝),一個在add_to_class方法里面弄,實在有點不優雅,可能還沒了解太多,不知道他的深度用意吧。
4. Meta的集成,Option的這個類提供繼承方法
5. 取得applabel,就是把model的名字分割取到數第二個,我很喜歡-2這樣的設定
6. get_model方法取得緩存里面的東西。
7. 把所有的class attr拿出來搞一遍,一般的屬性就setattr弄回去了,要是這個屬性有contribute_to_class這個callable屬性,那就執行之(Admin的處理完全也可以這樣,其實我們常用的objects就是用這個方法弄的)
8. 每個Field調用自己的contribute_to_class方法來進行特殊的處理
9. 進入緩存,,暫且叫緩存吧,里面的東西大家看看很簡單 文件在 /django/django/db/models/loading.py 里面還是有很多內容的
10.看注釋說的很清楚了,我們一定要在緩存里面拿model。
其中需要指出的是,new_class._prepare() 這個方法,簡單列出片段:
def _prepare(cls):
# Creates some methods once self._meta has been populated.
opts = cls._meta
opts._prepare(cls)
....
dispatcher.send(signal=signals.class_prepared, sender=cls)
中間省略了一些代碼,不是我沒看懂的就是沒意思的,關鍵要看這個dispatcher呀。這里是監聽者模式,dispatcher.send(signal=signals.class_prepared, sender=cls)放松了一個包含特定信號的事件,讓監聽的人可以做相應的處理。這樣的信號還有很多種,我們可以很簡單的截獲信號,處理相應的內容。也許您還記得,在我們創建db的時候,會提示創建一個超級用戶,其實就是用這個方法來弄的。
那我為什么要把這個單獨拿出來說呢,因為監聽這個事件的人不是別人,是"objects"!!!在文件/django/django/db/models/manager.py的開頭就有這樣的代碼:
def ensure_default_manager(sender):
cls = sender
if not hasattr(cls, '_default_manager'):
# Create the default manager, if needed.
try:
cls._meta.get_field('objects')
raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % cls.__name__
except FieldDoesNotExist:
pass
cls.add_to_class('objects', Manager())
dispatcher.connect(ensure_default_manager, signal=signals.class_prepared)
定義了一個callable,然后監聽signals.class_prepared信號,呵呵,連上了吧,只要Class準備好了,就調用這個方法。簡單判斷有沒有_default_manager'屬性和meta里面的objects后,開始插入objects。cls.add_to_class('objects', Manager()) 很熟悉吧,前面講過不多說了。
PS:寫到這里不由得感嘆,為啥同樣是往Class里面加入東東,要搞這么多的花樣呢,我以前也寫過一個Rails配置加載過程的分析文檔,雖然一個簡單的加載配置就把所有動態語言玩個遍,但是這也太不規范了吧,可能不這么玩就不算“動態”語言了吧,哈哈。
終于Model的Class造好了,相信大家以后造自己的Class也能玩出更多的花樣。那么可以開始下一步了。我到老家后再寫吧,打算講講神奇的objects,也就是Manager,其實就是玩QuerySet。。那里有很多的__xxx__方法,很好很強大
posted @
2008-02-02 17:32 差沙 閱讀(3240) |
評論 (1) |
編輯 收藏
Android是google為手機開發的操作系統, 基于Linux2.6內核.
2007年11月12日(北美時間), Android的APP SDK公布, 開始了google手機的開發大賽. 我們致力于Android的開發文檔翻譯, 開發技術交流等工作, 希望為Android平臺在中國的推廣貢獻自己的力量.
Andorid的SDK是完全針對java設計的, 甚至提供eclipse的插件, 作為一個javaer不研究實在可惜.
現在中文文檔已經有了初步進展, 大家翻譯的熱情都很高漲. 論壇里的討論也十分熱烈, 歡迎您的訪問.
我們的網站,
http://www.androidcn.net
中文文檔計劃:
http://www.androidcn.net/wiki
PS: 管理員您好, 不知道這個符不符合首頁發布的規則, 要是不妥我會撤下, 我們都想為Android中文化貢獻力量
posted @
2007-11-15 13:41 差沙 閱讀(2338) |
評論 (0) |
編輯 收藏
摘要: 相信大家看了Ext2.0后, 印象最深的應該是Ext的組件模式,很好的規范了組件設計,用Manager的統一管理,也是很好的方式.下面簡單分析一下Ext的組件結構.
閱讀全文
posted @
2007-10-20 20:01 差沙 閱讀(7723) |
評論 (25) |
編輯 收藏
ProcessInstance里面有一個findToken(String tokenPath)方法。這里面寫的是tokenPath。 tokenPath跟文件系統的規則差不多。類似 /fork1/fork2 這樣的。
那么,怎么到fork2下的一個分支token ?
token自己也有findToken這個方法,不同的是ProcessInstance的是以rootToken為起點,token自己的是以自己為起點。那么取到token2下面的分支
token2.findToken("token")
如果有返回token,沒有返回null
至于token的名字,我剛才查源碼才發現,只有在Fork節點才會給token賦予名字。。。
那么token2.findToken("token") 這里"token"那里來
命名的規則,如下。。
String tokenName = null;

if ( transitionName != null )
{

if ( ! parent.hasChild( transitionName ) )
{
tokenName = transitionName;

} else
{
int i = 2;
tokenName = transitionName + Integer.toString( i );

while ( parent.hasChild( tokenName ) )
{
i++;
tokenName = transitionName + Integer.toString( i );
}
}

} else
{ // no transition name
int size = ( parent.getChildren()!=null ? parent.getChildren().size()+1 : 1 );
tokenName = Integer.toString(size);
}
return tokenName;
這里"token"那里來?這個token名字,你自己可以根據命名規則得到 。看到這里應該很明了了
token的命名規則;首先根據transitionName的名字命名,這個transitionName的名字是你自己的寫的你不可能不知道。 如果transitionName的名字重名了,加自然數區分 當然,如果transitionName沒有名字,用分支的個數+1作為名字。
應該比較清楚了。
PS:這個是qq聊天記錄的整理版,所以說話比較詭異。為的是記錄下來避免忘記,也感謝群里的朋友。發現互相解決問題是提高水平的一個捷徑。。
posted @
2007-04-24 16:04 差沙 閱讀(2381) |
評論 (1) |
編輯 收藏
說ruby是怪異的語法有點不妥當,動態語言里面不乏這樣的語法出現。但是看了一些源碼后發現,使用ruby的用法真的各有不同,就像大家以前說的,ruby每個人寫出來的可能都不同。
現來說Rails里面如何加載config的吧。
在java里面config絕對是一個resource文件,然后通過讀取配置的工具加入進來,在分析處理。
在ROR里面完全不是這么回事。
1.首先大家要了解的是,在我們啟動 ruby script/server 的時候,rails做了一系列的處理,最后他執行了environment.rb
ruby 代碼
Rails::Initializer.run?do?
|
config
|
????
??
#
?這里能插入我們自己的配置。????
??
#
?config.?之類????
end???
這里的config其實是Initializer內部的一個變量,掌控全局的配置信息,我們可以使用這個config來配置我們想要的。Rails::Initializer.run的源碼是這樣的,yield再一次顯示了他的威力,讓我們在配置文件中得以配置config。然后實例化了一個initializer?之后,把config作為參數傳入了。
ruby 代碼
def
?self.run(command?
=
?:process,?configuration?
=
?Configuration.new)????
??
yield
?configuration?
if
?block_given?????
??initializer?
=
?new?configuration????
??initializer.send(command)????
??initializer????
end???
我們接著往下走,可以看到initializer?做了一系列的初始化工作,包括load_path的設定,路由的初始化,和activerecord的初始化。我們關心的還是配置如何起作用,那么來看看environments目錄下面的配置文件是如何導入的吧。
ruby 代碼
def
?load_environment????
??silence_warnings?do???
????config?
=
?configuration????
????constants?
=
?self.
class
.constants????
????eval(IO.read(configuration.environment_path),?binding)????
????(self.
class
.constants?
-
?constants).each?do?
|
const
|
????
??????Object.const_set(const,?self.
class
.const_get(const))????
????end???
??end???
end???
IO.read(configuration.environment_path) ,,這里就不使用什么回調不回調了,而是干脆IO拿出來eval一把,這里也是吃了一驚,這樣也可以呀~~~~~~~然后,我們可以看看,他處理常量的方法,把自己配置文件中的常量全部放入Object里面,起到全局常量的目的。
最絕的還是initialize_framework_settings,使用了有一個ruby的技巧。
ruby 代碼
def
?initialize_framework_settings????
??configuration.frameworks.each?do?
|
framework
|
????
????base_class?
=
?framework.to_s.camelize.constantize.const_get(
"
Base
"
)????
???
????configuration.send(framework).each?do?
|
setting,?value
|
????
??????base_class.send(
"
#{setting}=
"
,?value)????
????end???
??end???
end???
configuration.frameworks里面存放的是rails個個組件的名字,比方說active_record之類。然后把這個名字大寫轉換,然后用constantize取得ActiveRecord這個Module(注意,這些東西都在activesupport里面呢,activesupport/lib/active_support/core_ext/string/inflections.rb )。然后用const_get取得這個模塊的Base類,也就是ActiveRecord::Base這個類了(下面都叫做Base類),所有的Rails的組件都是這個命名規則改天我們自己想要做一個Rails的組件加進來,也可以這樣(但是要稍微修改一個源碼)。
然后,我們吧config里面的內容給Base類。configuration.send(framework)是調用一個組件名稱的方法,比方說active_record,就是去的config里面的active_record屬性(這是最基本的),通過后面的do我們可以看到config返回的是一個hash,然后把hash中每一個key作為變量,value為傳入值,傳入Base類。。。這里大家應該沒什么問題了,看看我們的config文件是怎么寫的吧。
ruby 代碼
#
?Settings?specified?here?will?take?precedence?over?those?in?config/environment.rb????
???
#
?In?the?development?environment?your?application's?code?is?reloaded?on????
#
?every?request.??This?slows?down?response?time?but?is?perfect?for?development????
#
?since?you?don't?have?to?restart?the?webserver?when?you?make?code?changes.????
config.cache_classes?
=
?false???
???
#
?Log?error?messages?when?you?accidentally?call?methods?on?nil.????
config.whiny_nils?
=
?true???
???
#
?Enable?the?breakpoint?server?that?script/breakpointer?connects?to????
config.breakpoint_server?
=
?true???
???
#
?Show?full?error?reports?and?disable?caching????
config.action_controller.consider_all_requests_local?
=
?true???
config.action_controller.perform_caching?????????????
=
?false???
config.action_view.cache_template_extensions?????????
=
?false???
config.action_view.debug_rjs?????????????????????????
=
?true???
???
#
?Don't?care?if?the?mailer?can't?send????
config.action_mailer.raise_delivery_errors?
=
?false???
哦,看著很暈吧,config就是我們的配置對象,按照我們上面的說法,config.action_view之類framework的變量應該是一個hash才對呀,如果是hash的話,不應該用這樣的方式傳入,可能會用 config.action_view = {:debug_rjs => true}來傳入。
OK.我們來看這個變量到底是什么樣的hash。
ruby 代碼
def
?initialize????
??
.????
??
.????
??
for
?framework?
in
?default_frameworks????
????self.send(
"
#{framework}=
"
,?OrderedOptions.new)????
??end???
end???
在初始化這些變量的時候,Rails給他賦值為OrderedOptions.new。這個特殊的類型可能就是關鍵。
ruby 代碼
class
?OrderedOptions?
<
?OrderedHash?
#
:nodoc:????
??
def
?[]
=
(key,?value)????
????super(key.to_sym,?value)????
??end???
??????
??
def
?[](key)????
????super(key.to_sym)????
??end???
???
??
def
?method_missing(name,?
*
args)????
????
if
?name.to_s?
=~
?
/
(.
*
)
=
$
/
????
??????self[$
1
.to_sym]?
=
?args.first????
????
else
???
??????self[name]????
????end???
??end???
end???
看到其中的玄妙了么,method_missing~~~!! 如果調用一個**=的方法 ,就像當用傳入一個HASH的值,key就是方法的名字。
也就是:config.action_view.debug_rjs??=?true?相當于config.action_view[:debug_rjs] = true
OK ,大體上描述了一下,可以看到簡單的一個Rails初始化已經給我們展示了幾乎全部ruby的靚麗之處,這能說明,這個亮點肯定是貫穿rails的基本,在以后的深入研究中我們就能看到了。
posted @
2006-11-21 12:10 差沙 閱讀(3751) |
評論 (2) |
編輯 收藏
為了記錄自己看Rails源碼的過程,全程記錄無廢話。
我們看看script/server都干了什么
require?File.dirname(__FILE__)?
+
?
'
/../config/boot
'
require?
'
commands/server
'
引用了boot.rb這個文件。看來這個文件是rails啟動的入口,來看看怎么回事吧。
unless?defined?(RAILS_ROOT)
??root_path?=?File.join(File.dirname(__FILE__),?'..')

??unless?RUBY_PLATFORM?=~?/mswin32/
????require?'pathname'
????root_path?=?Pathname.new(root_path).cleanpath(true).to_s
??end

??RAILS_ROOT?=?root_path
end這一部分定義了RAILS_ROOT這個系統的全局變量,指定了項目的根目錄,大家可以在以后華麗的使用了。
下一部分是找到rails,粗略看一下。
??if?File.directory?("#{RAILS_ROOT}/vendor/rails")
????require?"#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
??else
????require?'rubygems'這里能看到,他先跑到
vendor/rails去找rails了,這就是我們為什么能在插件里用rails是原因。如果沒有那么gems的干活。
接下來是初始化一下load_path,沒什么看的了。boot.rb就這樣的吧。
回到script/server的第二行,包含了'commands/server'這個文件,這個文件是什么?Rails的源碼里面找吧。我們在Rails的源碼里面找到這個文件。
require?'active_support'
require?'fileutils'

begin
??require_library_or_gem?'fcgi'
rescue?Exception
??#?FCGI?not?available
end

server?=?case?ARGV.first
??when?"lighttpd"
????ARGV.shift
??when?"webrick"
????ARGV.shift
??else
????if?RUBY_PLATFORM?!~?/mswin/?&&?!silence_stderr?{?`lighttpd?-version`?}.blank??&&?defined?(FCGI)
??????"lighttpd"
????else
??????"webrick"
????end
end

if?server?==?"webrick"
??puts?"=>?Booting?WEBrick
"
else
??puts?"=>?Booting?lighttpd?(use?'script/server?webrick'?to?force?WEBrick)"
end

FileUtils.mkdir_p(%w(?tmp/sessions?tmp/cache?tmp/sockets?))
require?"commands/servers/#{server}"

沒想到ActiveRecord居然是在這里引用的,這個ActiveRecord里面擴展了很對Ruby的既有類型,所以我們看源碼的時候如果發現有不熟悉的方法,就來這里找找,當然,看Rails的API是最好的選擇。
從參數一目了然,我們可以傳入server的名字,
lighttpd和webrick,根據不同的server選擇不同的server文件來讀取。我們還是看看webrick的吧。
require?'webrick'
require?'optparse'

OPTIONS?=?{
??:port????????????=>?3000,
??:ip??????????????=>?"0.0.0.0",
??:environment?????=>?(ENV['RAILS_ENV']?||?"development").dup,
??:server_root?????=>?File.expand_path(RAILS_ROOT?+?"/public/"),
??:server_type?????=>?WEBrick::SimpleServer,
??:charset?????????=>?"UTF-8",
??:mime_types??????=>?WEBrick::HTTPUtils::DefaultMimeTypes
}

ARGV.options?do?|opts|
??script_name?=?File.basename($0)
??opts.banner?=?"Usage:?ruby?#{script_name}?[options]"

??opts.separator?""

??opts.on("-p",?"--port=port",?Integer,
??????????"Runs?Rails?on?the?specified?port.",
??????????"Default:?3000")?{?|v|?OPTIONS[:port]?=?v?}
??opts.on("-b",?"--binding=ip",?String,
??????????"Binds?Rails?to?the?specified?ip.",
??????????"Default:?0.0.0.0")?{?|v|?OPTIONS[:ip]?=?v?}
??opts.on("-e",?"--environment=name",?String,
??????????"Specifies?the?environment?to?run?this?server?under?(test/development/production).",
??????????"Default:?development")?{?|v|?OPTIONS[:environment]?=?v?}
??opts.on("-m",?"--mime-types=filename",?String,
??????????????????"Specifies?an?Apache?style?mime.types?configuration?file?to?be?used?for?mime?types",
??????????????????"Default:?none")?{?|mime_types_file|?OPTIONS[:mime_types]?=?WEBrick::HTTPUtils::load_mime_types(mime_types_file)?}

??opts.on("-d",?"--daemon",
??????????"Make?Rails?run?as?a?Daemon?(only?works?if?fork?is?available?--?meaning?on?*nix)."
??????????)?{?OPTIONS[:server_type]?=?WEBrick::Daemon?}

??opts.on("-c",?"--charset=charset",?String,
??????????"Set?default?charset?for?output.",
??????????"Default:?UTF-8")?{?|v|?OPTIONS[:charset]?=?v?}

??opts.separator?""

??opts.on("-h",?"--help",
??????????"Show?this?help?message.")?{?puts?opts;?exit?}

??opts.parse!
end

ENV["RAILS_ENV"]?=?OPTIONS[:environment]
RAILS_ENV.replace(OPTIONS[:environment])?if?defined?(RAILS_ENV)

require?RAILS_ROOT?+?"/config/environment"
require?'webrick_server'

OPTIONS['working_directory']?=?File.expand_path(RAILS_ROOT)

puts?"=>?Rails?application?started?on?http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}"
puts?"=>?Ctrl-C?to?shutdown?server;?call?with?--help?for?options"?if?OPTIONS[:server_type]?==?WEBrick::SimpleServer
DispatchServlet.dispatch(OPTIONS)

本來不想把大段的代碼貼上來,但是這里面的內容可能大家都比較關心,涉及到server的啟動參數。

??:port????????????
=>?端口,
??:ip??????????????=>?server ip,
??:environment?????=>運行環境,
??:server_root?????=>?web訪問的目錄,(很多人問這個怎么改)
??:server_type?????=>?WEBrick::SimpleServer,
??:charset?????????=>?"UTF-8", 編碼
??:mime_types??????=>?WEBrick::HTTPUtils::DefaultMimeTypes
后面的require?引入了兩個文件,一個是'webrick_server',別看,就是他。另外一個是config/environment,這個文件是系統的全局配置文件,很重要,我們還是下次看看這個文件去吧。里面還真挺有意思。
(不知道blogjava讓不讓寫ROR的文章,看到有人寫了所以放在首頁了,如果不妥,我會盡快刪除)
posted @
2006-11-20 23:43 差沙 閱讀(4572) |
評論 (0) |
編輯 收藏
這兩天找點時間看了看jbpm,其設計思想相當不錯,功能強勁,而且幾乎實現了工作流模型的所有要求。可擴展的思想也是貫穿了整個jbpm的設計思路,這都是能看得出來的優勢。但是這也僅僅是限于表面的一兩眼,如果真的研究起其代碼來,你會驚訝的發現Jbpm的代碼漏洞百出,其設計思想完全沒有發揮出來,應該說,Jbpm的代碼真是“秀逗”了。下面來舉幾個例子吧。另外,我還沒有看svn上的最新代碼,所以可能有的問題jbpm自己已經修復,那就謝天謝地了。下面的代碼基于jbpm3.1
秀逗1。無中生有看到最核心的JpdlXmlReader代碼真實欲哭無淚,如果好好精簡精簡,至少能踢掉1/3的代碼。而其中甚至有些無中生有的代碼:
if?(?(actorId!=null)
???????????||?(pooledActors!=null)?

?????????)?
{
????????assignmentDelegation.setProcessDefinition(processDefinition);
????????assignmentDelegation.setClassName("org.jbpm.taskmgmt.assignment.ActorAssignmentHandler");
????????String?configuration?=?"";我們看到,如果符合之前的條件,就用代理類來代理
org.jbpm.taskmgmt.assignment.ActorAssignmentHandler這個類,但是找了好半天也沒有找到這個類。。。。 難道是讓用戶在自己的項目中用這個類么?還是為了兼容原來的程序,,就算是其中一種,但是,可但是,這個條件根本就不可能滿足,也就是說這是段廢話,而且還無中生有的出來個ActorAssignmentHandler。。
秀逗2。畫蛇添足
應該是我的基本功不都扎實,實在是高不明白下面的代碼在干什么。。。

??public?void?setActorId(String?actorId)?
{
????DefaultAuthenticationService?authenticationService?=?(DefaultAuthenticationService)?services.getAuthenticationService();
????DefaultAuthenticationService?defaultAuthenticationService?=?(DefaultAuthenticationService)?authenticationService;
????defaultAuthenticationService.setActorId(actorId);
??}先強制轉換成DefaultAuthenticationService,然后再強制轉換成DefaultAuthenticationService。。。。
而且這里這么設計基本上就把DefaultAuthenticationService實現的AuthenticationService接口晾在那里了,根本就是應該用AuthenticationService這個接口來說話才對。jbpm的service設計的擴展性很強,可自己配制。但如果這么用service的話,再怎么擴展也沒用。
秀逗3。莫“名”其妙Jbpm中變量的名字真的莫名其妙,很多明明是Map的類型他叫xxList,而不是Map的類型,他卻叫xxMap。這個地方我相信應該是能體現出程序員編寫程序的嚴謹性的地方,而Jbpm作的還不夠好。
秀逗4。固若金湯Jbpm的擴展性貫穿始終,但是在最重要的泳道的擴展上卻小家子氣起來。看看泳道類代理的擴展代碼。

if?(expression!=null)
{
????????assignmentDelegation.setProcessDefinition(processDefinition);
????????assignmentDelegation.setClassName("org.jbpm.identity.assignment.ExpressionAssignmentHandler");
????????assignmentDelegation.setConfiguration("<expression>"+expression+"</expression>");
??????
????}寫的很明確,如果泳道使用表達式來表示的那么就用代理類來代理處理表達式。。我本想,太好了,寫我自己的表達式,然后代理交給Acegi來根據表達式分配ActorId,但是,可是,但可是。他的代理類居然是寫死的,寫得就是自己的java.identity包里面的東西,不是說java.identity設計的不好,但是一個綜合系統的用戶角色管理系統是不可能跟著你的jbpm走的。強行要加入的java.identity的設計有點保護自我的意思,真的固若金湯,讓我結合acegi的想法又是難上加難。(不過還是有辦法的。大家自己找找看)。
秀逗5。口徑不一口徑不一就是指兩個程序部分的結合不一致。這種例子很多,我舉一個程序和xsd的沖突的例子。
Instantiator是jbpm代理里面一個比較不錯的概念。代理功能之一是生成代理的類的實例,而Instantiator則是負責生成實例的機制,這個Instantiator設計的不錯,可以在配制文件中的config-type屬性來擴展。看程序。
??????//?find?the?instantiator
??????instantiator?=?(Instantiator)?instantiatorCache.get(configType);

??????if?(instantiator?==?null)?
{
????????//?load?the?instantiator?class
????????Class?instantiatorClass?=?classLoader.loadClass(configType);
????????//?instantiate?the?instantiator?with?the?default?constructor
????????instantiator?=?(Instantiator)?instantiatorClass.newInstance();
????????instantiatorCache.put(configType,?instantiator);
??????}這里的設計很人性化,可以根據configType來用自己的構造器,但是xsd卻不這么想。
??????<xs:attribute?name="config-type"?default="field">
????????<xs:simpleType>
??????????<xs:restriction?base="xs:string">
????????????<xs:enumeration?value="field"/>
????????????<xs:enumeration?value="bean"/>
????????????<xs:enumeration?value="constructor"/>
????????????<xs:enumeration?value="configuration-property"/>
??????????</xs:restriction>
????????</xs:simpleType>
??????</xs:attribute>可以看到它限制了4種類型,別說使用自己的構造器了,就連他自己的XmlInstantiator都不再考慮范圍之內,真是大義滅親呀。。
構造器來這里的作用很大,我寫了自己的spring構造器,構造的時候使用beanFactory來構造,這樣就算是存在數據庫里面的class也能當作spring的bean來處理。但是如果用xsd的話就會導致交驗錯誤,所以索性把xsd去掉了,還好一切正常,就是感覺別扭點。
秀逗N。。。 能夠看得出來Jbpm需要提高的地方還很多。但是這些問題應該是一些開發人員的小疏忽,相信在以后的版本中可以改進。不管再怎么秀逗,Jbpm在工作流中仍然保有著強勁的地位,對BPM模型的實現也作的最為全面。而jbpm的par熱部署和IDE也是整個系統中的兩大亮點,這些優點都是不可不提的,所以我仍舊支持Jbpm,希望他能更加迅速的發展壯大起來。。。。
PS:文中錯誤之處還望大家指出,我希望有些“秀逗”是我自己秀逗了。
posted @
2006-08-24 11:07 差沙 閱讀(5746) |
評論 (7) |
編輯 收藏
Acegi好早就實現了ACL(好像是0.5),但是使用起來確實有點麻煩,所以用的不是太廣泛。這里簡單的說明一下使用方法,希望有更多的朋友來試試。
首先要理解Acegi里面Voter的概念,ACL正是在一個Voter上擴展起來的。現來看一下AclVoter的配置。
????<bean?id="aclBeanReadVoter"?class="org.acegisecurity.vote.BasicAclEntryVoter">
????????<property?name="processConfigAttribute">
????????????<value>ACL_READ</value>
????????</property>
????????<property?name="processDomainObjectClass">
????????????<value>org.springside.modules.security.acl.domain.AclDomainAware</value>
????????</property>
????????<property?name="aclManager">
????????????<ref?local="aclManager"/>
????????</property>
????????<property?name="requirePermission">
????????????<list>
????????????????<ref?local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
????????????????<ref?local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
????????????</list>
????????</property>
????</bean>- ACL_READ指的是這個Voter對哪些SecurityConfig起作用,我們可以把ACL_READ配置在想要攔截的Method上。比方說我們要攔截readOrder這個方法,以實現ACL控制,可以這樣配置。
orderManager.readOrder=ACL_READ - processDomainObjectClass指出哪些DomainObject是要進行ACL校驗的。
- aclManager是一個比較重要的概念,主要負責在權限列表中根據用戶和DomainObject取得acl列表。
- requirePermission指出要進行這個操作必須具備的acl權限,比方說read操作就必須有ADMINISTRATION或READ兩個權限。
其實整個過程看下來比較清晰,下面來看一下AclManager如何配置。
????<!--?=========?ACCESS?CONTROL?LIST?LOOKUP?MANAGER?DEFINITIONS?=========?-->

????<bean?id="aclManager"?class="org.acegisecurity.acl.AclProviderManager">
????????<property?name="providers">
????????????<list>
????????????????<ref?local="basicAclProvider"/>
????????????</list>
????????</property>
????</bean>

????<bean?id="basicAclProvider"?class="org.acegisecurity.acl.basic.BasicAclProvider">
????????<property?name="basicAclDao">
????????????<ref?local="basicAclExtendedDao"/>
????????</property>
????</bean>

????<bean?id="basicAclExtendedDao"?class="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl">
????????<property?name="dataSource">
????????????<ref?bean="dataSource"/>
????????</property>
????</bean>很明顯ACLManager繼承了Acegi的一貫風格,Provider可以提供多種取得ACL訪問列表的途徑,默認的是用
basicAclProvider在數據庫中取得。既然提到了數據庫,那我們就來看一下Acegi默認提供的ACL在數據庫里的保存表結構:
CREATE?TABLE?acl_object_identity?(
id?IDENTITY?NOT?NULL,
object_identity?VARCHAR_IGNORECASE(250)?NOT?NULL,
parent_object?INTEGER,
acl_class?VARCHAR_IGNORECASE(250)?NOT?NULL,
CONSTRAINT?unique_object_identity?UNIQUE(object_identity),
FOREIGN?KEY?(parent_object)?REFERENCES?acl_object_identity(id)
);
CREATE?TABLE?acl_permission?(
id?IDENTITY?NOT?NULL,
acl_object_identity?INTEGER?NOT?NULL,
recipient?VARCHAR_IGNORECASE(100)?NOT?NULL,
mask?INTEGER?NOT?NULL,
CONSTRAINT?unique_recipient?UNIQUE(acl_object_identity,?recipient),
FOREIGN?KEY?(acl_object_identity)?REFERENCES?acl_object_identity(id)
);- acl_object_identity表存放了所有受保護的domainObject的信息。其中object_identity字段保存了domainObject的class和id,默認的保存格式是:domainClass:domainObjectId。
- acl_permission?就是ACL權限列表了,recipient?是用戶或角色信息,mask表示了這個用戶或角色對這個domainObject的訪問權限。注意這些信息的保存格式都是可以根據自己的需要改變的。
這樣讀取和刪除的時候Acegi就能很好的完成攔截工作,但是讀取一個List的時候,如何才能把該用戶不能操作的domainObject剔除掉呢?這就需要afterInvocationManager來完成這個工作。下面來看下配置:
????<!--?==============?"AFTER?INTERCEPTION"?AUTHORIZATION?DEFINITIONS?===========?-->

????<bean?id="afterInvocationManager"?class="org.acegisecurity.afterinvocation.AfterInvocationProviderManager">
????????<property?name="providers">
????????????<list>
????????????????<ref?local="afterAclCollectionRead"/>
????????????</list>
????????</property>
????</bean>
????<!--?Processes?AFTER_ACL_COLLECTION_READ?configuration?settings?-->
????<bean?id="afterAclCollectionRead"?class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider">
????????<property?name="aclManager">
????????????<ref?local="aclManager"/>
????????</property>
????????<property?name="requirePermission">
????????????<list>
????????????????<ref?local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
????????????????<ref?local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
????????????</list>
????????</property>
????</bean> afterAclCollectionRead會在攔截的方法執行結束的時候執行。主要的作用就是在返回的List中挨個檢查domainObject的操作權限,然后根據requirePermission來剔除不符合的domainObject。
posted @
2006-06-17 00:20 差沙 閱讀(2276) |
評論 (4) |
編輯 收藏
acegi1.0發布,其實有點出乎意料,因為我一向認為acegi的代碼已經相當穩定了,但是acegi力求精益求精,從新版還是能看到不少實用的改動和升級。這里簡單分析一下。
[SEC-183] - Avoid unnecessary HttpSession creation when using Anonymous and Remember-Me authentication
以前如果使用HttpSessionContextIntegrationFilter的話,不管你是否需要創建session,他都會給你創建。這在一些Base驗證的時候是多余的。現在加上了forceEagerSessionCreation,在創建session的時候做了控制。
[SEC-29] - Save POST request parameters before redirect
在前幾個版本出現這個問題,如果實現了登陸自動跳轉,acegi僅僅是簡單記錄了URL,沒有深入的紀錄信息。新版本中acegi不僅僅是保持POST中的數據不會丟失,request里面的東西幾乎全都序列化保存下來了,實現可以看看SavedRequest。
[SEC-40] - HibernateDao.scroll() performance
[SEC-92] - Hibernate ACL implementation
這個比較激動的改進在1.0的源碼中沒有找到,看alex的意思好像是僅僅提供各演示,目的是為了生成數據腳本方便點。(其實這個還真的沒法做成特別通用的,畢竟每個人的ACL實現都有可能不同)
[SEC-147] - BasicAclEntryAfterInvocationProvider should support processDomainObjectClass
對List進行ACL交驗的時候,會把第一個元素取出,看看是否AssignableFrom這個processDomainObjectClass ,算是做一下安全檢查吧。
[SEC-172] - Allow SimpleAclEntry to take 'null' as recipient constructor argument
其實應該是不允許recipient 為空。
[SEC-187] - inHttp & inHttps not fully utilized in AuthenticationProcessingFilterEntryPoint
[SEC-191] - AclTag class should use the BeanFactoryUtils.beanNamesForTypeIncludingAncestors method to search for the AclManager
AclTag在尋找AclManager 時候會更加靈活了,得益于spring的強大。
<明天繼續吧。。。。>
[SEC-194] - RememberMeServices should be available when using BasicAuth logins
[SEC-195] - Create Acegi-backed CAS3 AuthenticationHandler
[SEC-196] - Update web site and documentation to reference JA-SIG CAS
[SEC-203] - Allow setting the AuthenticationManager onto the ConcurrentSessionController for inverted dependency
[SEC-204] - Better detection of malformed text in FilterInvocationDefinitionSourceEditor
[SEC-205] - Allow multiple URLs in DefaultInitialDirContextFactory
[SEC-206] - TokenBasedRememberMeServices using context root when setting cookie paths (inc code)
[SEC-207] - Implement countermeasures against session attacks
[SEC-209] - Make AbstractProcessingFilter.eventPublisher field protected
[SEC-217] - Improve Siteminder Filter
[SEC-220] - Allow ExceptionTranslationFilter to not catch exceptions
[SEC-221] - AbstractProcessingFilter.onPreAuthentication exceptions should be caught
[SEC-224] - Make Authentication.getPrincipal() for CAS return the UserDetails
[SEC-229] - Allow redirects to external URLs in AbstractProcessingFilter
[SEC-231] - Add another DefaultLdapAuthoritiesPopulator.getGroupMembershipRoles
[SEC-234] - Allow WebAuthenticationDetails pluggable implementations
[SEC-236] - JbossAcegiLoginModule to use ApplicationContext interface
[SEC-238] - Add AuthenticationException to AbstractProcessingFilter.onUnsuccessfulAuthentication method signature
[SEC-242] - Logger in AbstractProcessingFilter
[SEC-244] - Column names instead of indexes for org.acegisecurity.userdetails.jdbc.JdbcDaoImpl
[SEC-246] - Enable late-binding of UserDetailsService on DaoAuthenticationProvider
[SEC-247] - Allow to specify resources that shouldn't be filtered in FilterChainProxy
[SEC-251] - DefaultLdapAuthoritiesPopulator: Add filter argument {1} for username as in Tomcat JNDIRealm
[SEC-255] - Reorder AuthenticationProcessingFilter to create HttpSession before delegating to AuthenticationDetailsSource
[SEC-257] - ExceptionTranslationFilter to use strategy interface for AccessDeniedException handling
[SEC-259] - AccessDecisionVoter: typo in JavaDoc
[SEC-260] - AbstractAccessDecisionManager and loggers
[SEC-262] - AbstractAccessDecisionManager needs standard handling ifAllAbstainDecisions
[SEC-264] - Introduction of LdapUserDetails and changes to LdapAuthenticator and LdapAuthoritiesPopulator interfaces
[SEC-276] - Restructure reference guide
posted @
2006-06-01 23:05 差沙 閱讀(559) |
評論 (0) |
編輯 收藏