ï»??xml version="1.0" encoding="utf-8" standalone="yes"?>国产高清一区视频,久久狠狠久久,国产一区二区三区天码http://www.aygfsteel.com/raimundox/知天之所为,知ähä¹‹æ‰€ä¸ø™€…ï¼Œè‡³çŸ£ã€‚çŸ¥å¤©ä¹‹æ‰€ä¸ø™€…,天而生也;知ähä¹‹æ‰€ä¸ø™€…,以其知之所知以å…Õd…¶çŸ¥ä¹‹æ‰€ä¸çŸ¥åQŒç»ˆå…¶å¤©òq´è€Œä¸ä¸­é“夭者:是知之盛也ã€?/description>zh-cnSat, 07 Jun 2025 23:13:23 GMTSat, 07 Jun 2025 23:13:23 GMT60Add primitive Progression Enhancement support of Railshttp://www.aygfsteel.com/raimundox/archive/2009/03/18/260475.htmlRaimundoxRaimundoxWed, 18 Mar 2009 06:38:00 GMThttp://www.aygfsteel.com/raimundox/archive/2009/03/18/260475.htmlhttp://www.aygfsteel.com/raimundox/comments/260475.htmlhttp://www.aygfsteel.com/raimundox/archive/2009/03/18/260475.html#Feedback0http://www.aygfsteel.com/raimundox/comments/commentRss/260475.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/260475.htmlCurrently the concept of Progressive Enhancement is getting hotter and hotter. It emphasizes accessibility, semantic markup, and the importance of separating the complex rich interaction logic into well modularized javascript files. It's really a usefully way of thinking about how to modularize and manage web presentation components. But the Rails framework doesn't have good support for PE, so we have to define our own convention and helpers to make our life easier.

Usually, I'd like to organize js files in the Rails convention, which means we'll have something like this:

app
  |
  - views
      |
      - admin
          |
          _  new.html.erb
          -  index.html.erb
public
  |
  - javascripts
       |
       - admin
           |
           - new.js
           - index.js

And new.js looks similar to:
$(document).ready(function() {
  enhanceInteractionOnElements();
  
});

function helper_methods() {
  
}


Then, add the follow method to ApplicationHelper module:

def page_javascript_include_tag
  file 
= "#{params[:controller]}/#{params[:action]}.js"
  File.exist
?("#{RAILS_ROOT}/public/javascripts/#{file}"? javascript_include_tag(file) : ""
end

this method will look for js file for a particular page. And in you layout file, add one line in the head sectin:
<%= page_javascript_include_tag %>

That's it. Whenever you request an action of a particular controller, it will find and include the PE js files automatically. Now we've very very primitive support of PE in Rails framework now.

 


 


]]>
A very brief introduction to Aurumhttp://www.aygfsteel.com/raimundox/archive/2007/09/05/143029.htmlRaimundoxRaimundoxWed, 05 Sep 2007 15:21:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/09/05/143029.htmlhttp://www.aygfsteel.com/raimundox/comments/143029.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/09/05/143029.html#Feedback0http://www.aygfsteel.com/raimundox/comments/commentRss/143029.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/143029.html Aurum is a Ruby-based LALR(n) parser generator that you can use to develop your own domain specified languages, scripting languages and programming languages.Although it's just yet another parser generator, Aurum is slightly different from other widely used parser generators:

  1. One of major targets of Aurum is to simplify external DSL development, espectually Ruby external DSL.
  2. Aurum uses incremental LALR(n) algorithm instead of the common used LALR(1)/Full LALR(n) algorithm. That means:
    •   Allowing the user to express grammars in a more intuitive mannar.
    •   Making it easier to handle complicated grammars. For exmaple, COBOL(LALR(2 or 3)), simplified nature language(LALR(3+)) and etc.
    •   Closer to Generalized LR in language recognizing but much more faster.
    •   Smaller parsing table comparing to Full LALR/LR(n) algorithm.
  3. Aurum supports grammar reuse, and itslef'll be shipped with some pre-defined common structures. One of the pain points of external DSL is that you have to re-define lots of common structures, such as if statements, block structure and etc. With Aurum, you could simply reuse them.
  4. Aurum uses a Ruby interal DSL as meta-language, and provides a generic lexer/parser as well. You could test your grammar by the comprehensive testing libraries Ruby has(you could even develop your lexer/parser in the TDD fashion).
  5. As the name suggested, Aurum, the Latin word for Gold, is partially inspired by the GOLD Parsing System. The grammar you created with Aurum could be completely independent of any implementation language,even Ruby.(not implemented yet :) )

Ok, let's start from the 'Hello World in Compiler Construction' —�Expression Evaluation

 1 require 'aurum'
 2 
 3 class ExpressionGrammar < Aurum::Grammar
 4   tokens do
 5     ignore string(' ').one_or_more     # <= a
 6     _number range(?0?9).one_or_more  # <= b
 7   end
 8 
 9   precedences do  # <= c
10     left '*''/'
11     left '+''-'
12   end
13 
14   productions do # <= d
15     expression expression, '+', expression {expression.value = expression1.value + expression2.value} # <= e
16     expression expression, '-', expression {expression.value = expression1.value - expression2.value}
17     expression expression, '*', expression {expression.value = expression1.value * expression2.value}
18     expression expression, '/', expression {expression.value = expression1.value / expression2.value}
19     expression '(', expression, ')'        do expression.value = expression1.value end # <= f
20     expression _number                     {expression.value = _number.value.to_i}
21     expression '+', _number                {expression.value = _number.value.to_i}
22     expression '-', _number                {expression.value = -_number.value.to_i}
23   end
24 end

If you has any experience with other compiler compiler/parser generator, you probably could understand what happens above quite easily. Instead of explaining things like token, character class, and production, I'd like to emphasise some Aurum conventions:
  1. At point a, we use 'ignore' directive to declare the ignored pattern, such as whitespaces etc.'string' is one of the helper methods(others are enum, range and concat), which is used to define lexical patterns. It will create a pattern matching the given string exactly.
  2. At point b, we declare a lexical token named '_number'. In Aurum, lexical tokens, or terminals from syntax perspective, always start with '_'. The expression '_token_name pattern' is equivalent to 'match pattern, :recognized => :_toke_name'. The 'match' directive is a common way to associate lexical action with leixcal pattern.
  3. At point c, we declare operator precedences of the Expression grammar.The eariler the operators definied, the higher precedence they will have.
  4. At point d, we declare syntax rules of Expression grammar. According to Aurum naming convention, all terminals should start with '_' while all nontermainls start with lower case alphabet character. String literals will be interpreted as reserve words, and added to lexer automatically.
  5. At point e, we define a semantic action to the Addition rule. In semantic action, you could access to the objects in value stack via the name of corresponding symbols.If there are more than one symbol with the same name, you could differentiate them by the order they appered in the production.
  6. At point f, we use do..end instead of {..}. Using Ruby internal DSL as meta-langauge is a double-side sword, you have to bear its flaws while enjoying the remaining parts. There is no perfect world, isn't it?

Now, let's find out how we could use this expression grammar. You could use the helper method as below(it will recalcuate lexical table and parsing table for every call, could be quite slow):

1 puts ExpressionGrammar.parse_expression('1+1').value

or use the lexical table and parsing table to create your own lexer & parser:
 
1   lexer = Aurum::Engine::Lexer.new(ExpressionGrammar.lexical_table, '1+1')
2   parser = Aurum::Engine::Parser.new(ExpressionGrammar.parsing_table(:expression))
3   puts parser.parse(lexer).value


At the end of this post, I'd like to give another grammar example coming from Martin Fowler's HelloParserGenerator series:

 1 require 'aurum'
 2 
 3 Item = Struct.new(:name)
 4 
 5 class Catalog < Aurum::Grammar
 6   tokens do
 7     ignore enum(" \r\n").one_or_more
 8     _item range(?a,?z).one_or_more
 9   end
10 
11   productions do
12     configuration configuration, item {configuration.value = configuration1.value.merge({item.value.name => item.value})}
13     configuration _                   {configuration.value = {}}
14     item 'item', _item                {item.value = Item.new(_item.value)}
15   end
16 end
17 
18 config = Catalog.parse_configuration(<<EndOfDSL).value
19   item camera
20   item laser
21 EndOfDSL
22 
23 puts config['camera'].name


P.S.:The post is based on the developing version of Aurum(0.2.0). You could get it from the svn repository.
P.S.P.S.: There is a more complicated example in the examples directory, a simple Smalltalk interpreter. Have fun:)


]]>
A very brief introduction to Aurumhttp://www.aygfsteel.com/raimundox/archive/2007/09/05/143028.htmlRaimundoxRaimundoxWed, 05 Sep 2007 15:12:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/09/05/143028.htmlhttp://www.aygfsteel.com/raimundox/comments/143028.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/09/05/143028.html#Feedback0http://www.aygfsteel.com/raimundox/comments/commentRss/143028.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/143028.html
Aurum是一个用Ruby实现的LALR(n) parser generatoråQˆæ˜¯çš„,又是一个parser generatoråQ‰ï¼Œä¸è¿‡å®ƒå’Œå…¶ä»–一些广泛应用的parser generator相比略有不同的:

1.Aurum的主要目标之一åQŒæ˜¯½Ž€åŒ–external DSL的开发(ž®¤å…¶æ˜¯ruby external DSLåQ‰ã€?br /> 2.Aurum采用增量LALR(n)½Ž—法åQŒè€Œä¸æ˜¯é€šå¸¸çš„LALR(1)。这意味着åQ?br />   a.不必ç”׃ºŽLALR(1)能力的限åˆÓž¼Œè€Œæ”¹å†™è¯­æ³•,很多在LALR(1)中冲½Hçš„语法在LALR(n)中可以比较自然地表达ã€?br />   b.ç”׃ºŽè¯†åˆ«èƒ½åŠ›çš„å¢žå¼ºï¼Œå¯ä»¥å¤„ç†ä¸€äº›æ¯”è¾ƒå¤æ‚çš„è¯­æ³•åQŒæ¯”如COBOL(LALR(2)或LALR(3))åQŒæ¯”如一些简化的自然语言(LALR(3+))ã€?br />   c.处理能力接近Generalized LRåQŒå´å¿«å¾ˆå¤?br />   d.比è“vFull LALR/LR(n)åQŒå¢žé‡ç®—法生成的语法表更ž®ã€?br /> 3.å‡ÞZºŽ½Ž€åŒ–external DSL实现的考虑åQŒAurum支持语法重用ã€?br /> 4.Aurum采用Ruby internal DSLä½œäØ“è¯­æ³•å£°æ˜Žçš„å…ƒè¯­è¨€åQŒå¯ä»¥åˆ©ç”¨Ruby丰富的测试框æžÓž¼Œæœ‰æ•ˆåœ°å¯¹¾~–译åQè§£é‡Šï¼åˆ†æžå™¨è¿›è¡Œæµ‹è¯•ã€?br /> 5.正如名字所暗示的,AurumåQˆGold的化学名¿UŽÍ¼‰çš„一部分灉|„Ÿæ¥è‡ªGOLD parsing systemåQŒå®ƒ?y¨­u)®†æ”¯æŒç‹¬ç«‹äºŽòq›_°å’Œè¯­­a€çš„编译器开发ã€?br />
好,闲话ž®‘说åQŒçœ‹ä¸€ä¸ªä¾‹å­ï¼Œ¾~–译原理中的Hello World —â€?表达式求å€û|¼š
 1 require 'aurum'
 2 
 3 class ExpressionGrammar < Aurum::Grammar
 4   tokens do
 5     ignore string(' ').one_or_more     # <= a
 6     _number range(?0?9).one_or_more  # <= b
 7   end
 8 
 9   precedences do  # <= c
10     left '*''/'
11     left '+''-'
12   end
13 
14   productions do # <= d
15     expression expression, '+', expression {expression.value = expression1.value + expression2.value} # <= e
16     expression expression, '-', expression {expression.value = expression1.value - expression2.value}
17     expression expression, '*', expression {expression.value = expression1.value * expression2.value}
18     expression expression, '/', expression {expression.value = expression1.value / expression2.value}
19     expression '(', expression, ')'        do expression.value = expression1.value end # <= f
20     expression _number                     {expression.value = _number.value.to_i}
21     expression '+', _number                {expression.value = _number.value.to_i}
22     expression '-', _number                {expression.value = -_number.value.to_i}
23   end
24 end


å¦‚æžœè¯æ€½å¯¹ä¹‹å‰æœ‰ç”¨è¿‡compiler compiler或者parser generator的话åQŒåº”该能看个七七八八吧。我大概解释一下:
 a.˜q™é‡Œå®šä¹‰äº†æ–‡æ³•空白,也就是被lexer忽略的部分,在通常的语­a€ä¸­ï¼Œæ˜¯ç©ºæ ¼å›žè½¦æ¢è¡Œä¹‹¾cȝš„字符åQ›string是用于定义lexical patternçš„helperæ–ÒŽ(gu¨©)³•åQˆå‡ºäº†string之外åQŒè¿˜æœ‰range, enumå’ŒconcatåQ‰ï¼›ignore是一个预定义的说明指令,表示若文本匹配给定模式则该文本会被lexer自动忽略åQŒå…¶æ ¼å¼ä¸ºï¼š
    ignore pattern {//lexical action}
 b.此处为lexical token声明åQŒæ‰€æœ‰lexical token必须以_å¼€å¤ß_¼Œå…¶æ ¼å¼äØ“åQ?br />     _token_name pattern {//lexical action}
   ˜q™é‡Œå…¶å®žæ˜¯ä¸€ä¸ªç®€ç•¥å†™æ³•,½{‰ä­h(hu¨¢n)äº?br />     match pattern, :recognize => åQš_token_name
 c.æ­¤å¤„ä¸ø™¿½Ž—符优先¾U§å£°æ˜Žï¼Œæ”¯æŒå·?右结合运½Ž—符åQˆæ— ¾l“合属性运½Ž—符开发中åQ‰ï¼›æ¯ä¸€è¡Œä¸­æ‰€æœ‰è¿½Ž—符å…ähœ‰ç›¸åŒä¼˜å…ˆ¾U§ï¼›æ¯”它下一行的˜qç®—½W¦é«˜ä¸€ä¸ªä¼˜å…ˆçñ”。比如在˜q™ä¸ªä¾‹å­ä¸­ï¼Œ'*'å’?/'å…ähœ‰ç›¸åŒä¼˜å…ˆ¾U§ï¼Œä½†æ˜¯æ¯?+'å’?-'的优先çñ”别高ã€?br />  d.æ­¤å¤„ä¸ø™¯­æ³•规则声明,所使用的symbol主要有三¿Uï¼Œnonterminal(ž®å†™å­—母开å¤?åQŒterminal(其实ž®±æ˜¯lexical tokenåQŒä»¥_å¼€å¤?å’Œliteral(字符串常é‡?åQŒå…¶ä¸­æ‰€æœ‰literaléƒ½ä¼šè¢«è‡ªåŠ¨å£°æ˜ŽäØ“ä¿ç•™å­—ã€?br />  e.此处定义了一条文法规则(加法åQ‰ï¼Œä»¥åŠå¯¹åº”çš„semantic action。在semantic action中可以直接通过symbol的名字来获取值栈中的对象。如遇到同名symbolåQŒåˆ™æŒ‰ç…§å‡ºçް™åºåº˜q›è¡Œ¾~–号卛_¯ã€?br />  f.其实˜q™ä¸ªæ²¡å•¥åQŒåªä¸è¿‡ç”׃ºŽæˆ‘们使用的是Ruby DSLåQŒæ‰€ä»¥æœ‰æ—¶å€™ä¸èƒ½éƒ½ç”¨{}åQŒéœ€è¦do endåQŒè¿™ž®±æ˜¯ä¸€ä¸ªä¾‹å­ã€?br />
最后测试一下实际中如何使用定义好的语法åQˆä‹É用helper methodåQŒæ³¨æ„ç”±äºŽåˆ†æžè¡¨æ²¡æœ‰¾~“å­˜åQŒæ¯‹Æ¡éƒ½ä¼šé‡½Ž—语法表åQŒä»…仅适用于debug mode。)
  puts ExpressionGrammar.parse_expression('1+1').value
或者通过分析表自己构造lexer和parser
  lexer = Aurum::Engine::Lexer.new(ExpressionGrammar.lexical_table, '1+1')
  parser = Aurum::Engine::Parser.new(ExpressionGrammar.parsing_table(:expression))
  puts parser.parse(lexer).value

最后最后,¾l™å¦å¤–一个例子,ž®±æ˜¯Martin Fowler Blog上的HelloParserGenerator¾pÕdˆ—中所用的语法åQ?br />
 1 require 'aurum'
 2 
 3 Item = Struct.new(:name)
 4 
 5 class Catalog < Aurum::Grammar
 6   tokens do
 7     ignore enum(" \r\n").one_or_more
 8     _item range(?a,?z).one_or_more
 9   end
10 
11   productions do
12     configuration configuration, item {configuration.value = configuration1.value.merge({item.value.name => item.value})}
13     configuration _                   {configuration.value = {}}
14     item 'item', _item                {item.value = Item.new(_item.value)}
15   end
16 end
17 
18 config = Catalog.parse_configuration(<<EndOfDSL).value
19   item camera
20   item laser
21 EndOfDSL
22 
23 puts config['camera'].name


P.S.:本文是根据Aurum0.2.0写成的,你可以从rubyforgeçš„svn上得到它ã€?br /> P.S.P.S.: 在exmaples目录里有一个更复杂一些的例子åQŒæ˜¯ä¸€ä¸ªç®€å•çš„Smalltalk解释器ã€?br />



]]>
Erlang Ring Benchmarkhttp://www.aygfsteel.com/raimundox/archive/2007/08/01/133863.htmlRaimundoxRaimundoxWed, 01 Aug 2007 12:34:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/08/01/133863.htmlhttp://www.aygfsteel.com/raimundox/comments/133863.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/08/01/133863.html#Feedback4http://www.aygfsteel.com/raimundox/comments/commentRss/133863.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/133863.html
 1 -module(ring_benchmark). 
 2 -export([start/2]).
 3 
 4 start(N, M) -> 
 5   Pid = create_process(self(), N - 1, M),
 6   time(fun() -> Pid ! start, loop(Pid, M) end).
 7 
 8 time(Fun) -> 
 9   statistics(wall_clock),
10   Fun(),
11   {_,Time} = statistics(wall_clock),
12   io:format("Run : ~w s ~n", [Time/1000]).  
13 
14 create_process(Pid, 0, _) -> Pid;
15 create_process(Pid, N, M) -> create_process(spawn(fun() -> loop(Pid, M) end),  N - 1, M).
16 
17 loop(_, 0-> void;
18 loop(Next, M) -> 
19   receive
20     Message -> Next ! Message,
21     loop(Next, M - 1)
22   end.
23 
24 

有意思是它还有一个第二问åQŒè®©ä½ ç”¨å¦å¤–一¿Uç†Ÿæ‚(zh¨¨n)‰çš„语言实现同样的功能,发送同样多的消息,也把旉™—´è®°å½•下来åQŒç„¶åŽå†™ä¸€½‹‡blog来publishä½ çš„¾l“果。其实,大家心知肚明åQŒè¿™¿Ulightweight process啊,message passing concurrency啊都是Erlang的强™å¹ï¼Œè€Œä¸”实测¾l“果也着实颇为恐怖,一般也ž®±æ²¡é‚£é—²å¿ƒæ‹¿åˆ«çš„东西来陪衬一把了åQˆArmstrong同学自己实现了一个Java versionåQŒæ•ˆçއ大¾U¦èƒ½å·®åˆ°ç™‘Ö€å§åQ‰ã€‚不˜q‡è¿˜çœŸæœ‰é‚£å†™ä¸ä¿¡é‚ªçš„老大åQ?a >用stackless python实现了同æ ïLš„ring benchmarkåQŒå‘现比erlang˜q˜å¿«...后来修改代码åŽÀLމio操作åQŒErlang倒是比stackless python快些åQŒä½†ä¹Ÿåªæ˜¯ä¸€äº›è€Œå·²ã€?br>





]]>
Feng Shui for Standard ML Programmershttp://www.aygfsteel.com/raimundox/archive/2007/07/10/129436.htmlRaimundoxRaimundoxTue, 10 Jul 2007 13:53:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/07/10/129436.htmlhttp://www.aygfsteel.com/raimundox/comments/129436.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/07/10/129436.html#Feedback4http://www.aygfsteel.com/raimundox/comments/commentRss/129436.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/129436.htmlhttp://www.dcs.ed.ac.uk/home/stg/fengshui.ps.gz

今天早上打开Google Readerž®Þqœ‹è§è¿™ä¹ˆä¸€½‹‡ï¼Œå†…容倒也¾|¢äº†åQŒä¸˜q‡æ˜¯bad smell的另一个名字而已åQŒç¡¬è¦æ‰¯ä¸Šåˆ†æ°´ä¹Ÿåªèƒ½½Ž—是勉勉强强。不˜q‡éƒé—ïLš„æ˜¯ï¼Œç«Ÿç„¶æ˜¯ä¸ª‹z‹äh的手½W”,国学不昌实不能不令我辈心忧啊ã€?br>
p.s. 预计未来6个月口头¼›…:"你这写当心坏了项目的风水"


]]>
Domain Oriented Web Testing with Selenium & Ruby (Posted@InfoQ China)http://www.aygfsteel.com/raimundox/archive/2007/06/13/123772.htmlRaimundoxRaimundoxWed, 13 Jun 2007 01:44:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/06/13/123772.htmlhttp://www.aygfsteel.com/raimundox/comments/123772.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/06/13/123772.html#Feedback1http://www.aygfsteel.com/raimundox/comments/commentRss/123772.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/123772.html
应用Selenium˜q›è¡ŒWeb‹¹‹è¯•往往会存在几个bad smellåQ?br>1.大量使用name, id, xpath½{‰é¡µé¢å…ƒç´ ã€‚无论是功能修改、UI重构˜q˜æ˜¯äº¤äº’性改˜q›éƒ½ä¼šåª„响到˜q™äº›å…ƒç´ åQŒè¿™ä½¿å¾—Selenium‹¹‹è¯•变得非常脆弱ã€?br>2.˜q‡äºŽ¾l†èŠ‚çš„é¡µé¢æ“ä½œä¸å®ÒŽ(gu¨©)˜“ä½“çŽ°å‡ø™¡Œä¸ºçš„æ„å›¾åQŒä¸€ŒD‰|—¶é—´ä¹‹åŽå°±å¾ˆéš¾çœŸæ­£æŠŠæ¡‹¹‹è¯•原有的目的了åQŒè¿™ä½¿å¾—Selenium‹¹‹è¯•变得难于¾l´æŠ¤ã€?br>3.对具体数据取值的存在依赖åQŒå½“个别数据不再合法的时候,‹¹‹è¯•ž®×ƒ¼šå¤ÞpÓ|åQŒä½†˜q™æ ·çš„å¤±è´¥åÆˆä¸èƒ½æ ‡è¯†åŠŸèƒ½çš„ç¼ºå¤±ï¼Œ˜q™ä‹Éå¾—Selenium‹¹‹è¯•变得脆弱且难以维护ã€?br>
而这几点直接衍生的结果就是不断地æ·ÕdŠ æ–°çš„‹¹‹è¯•åQŒè€Œæžž®‘地去重构、利用原有测试。其实这åˆîC¹Ÿæ˜¯æ­£å¸¸ï¼Œå•å…ƒ‹¹‹è¯•‹¹‹è¯•写多了,也有会有˜q™æ ·çš„问题。不˜q‡æ¯”较要命的是,Selenium的执行速度比较慢(相对单元‹¹‹è¯•åQ‰ï¼Œéšç€‹¹‹è¯•逐渐的增多,˜qè¡Œæ—‰™—´ä¼šé€æ¸å¢žåŠ åˆîC¸å¯å¿å—çš„½E‹åº¦ã€‚一¾l„意图不明难以维护的Selenium‹¹‹è¯•åQŒå¯ä»¥å¾ˆè½ÀL¾åœ°åœ¨æ¯æ¬¡build的时候杀æŽ?0分钟甚至2个小时的旉™—´åQŒåœ¨ä¸‹å°±æœ‰èб2个小时坐在电脑前面等å¾?50个Selenium‹¹‹è¯•˜qè¡Œé€šè¿‡çš„æ?zh¨¨n)²æƒ¨ç»åŽ†ã€‚å› æ­¤åˆç†æœ‰æ•ˆåœ°è§„åˆ’Selenium‹¹‹è¯•ž®±æ˜¾å¾—格外的˜q«åˆ‡å’Œé‡è¦äº†ã€‚而目前比较行之有效的办法åQŒå¾€å¤§äº†è¯ß_¼Œå¯ä»¥å«domain based web testingåQŒå…·ä½“来è®ÔŒ¼Œž®±æ˜¯Page Object Patternã€?br>
Page Object Pattern里有四个基本概念åQšDriver, Page, Navigatorå’ŒShortcut。Driver是测试真正的实现机制åQŒæ¯”如SeleniumåQŒæ¯”如WatiråQŒæ¯”如HttpUnit。它们懂得如何去真正执行一个webè¡ŒäØ“åQŒé€šå¸¸åŒ…含像clickåQŒselectåQŒtype˜q™æ ·çš„表½Cºå…·ä½“行为的æ–ÒŽ(gu¨©)³•åQ›Page是对一个具体页面的ž®è£…åQŒå®ƒä»¬äº†è§£é¡µé¢çš„¾l“æž„åQŒçŸ¥é“诸如idåQ?nameåQ?classåQŒxpath˜q™ç±»å®žçް¾l†èŠ‚åQŒåƈ描述用户可以在其上进行何¿Uæ“ä½œï¼›Navigator则代表了URLåQŒè¡¨½CÞZ¸€äº›ä¸¾lé¡µé¢æ“ä½œçš„直接跌™{åQ›æœ€åŽShortcutž®±æ˜¯helperæ–ÒŽ(gu¨©)³•了,需要看具体的需要了。下面来看一个超¾U§ç®€å•的例子——测试登录页面ã€?br>
1. Page Object

假设我们使用一个单独的Login Page˜q›è¡Œç™Õd½•åQŒé‚£ä¹ˆæˆ‘们可能会ž®†ç™»å½•的操作ž®è£…在一个名为LoginPageçš„page object里:

 1 class LoginPage
 2   def initialize driver
 3     @driver = driver
 4   end
 5  
 6   def login_as user
 7     @driver.type 'id=', user[:name]
 8     @driver.type 'xpath=', user[:password]
 9     @driver.click 'name='
10     @driver.wait_for_page_to_load
11   end
12 end


login_as是一个具有业务含义的™åµé¢è¡Œäؓ。在login_asæ–ÒŽ(gu¨©)³•中,page object负责通过依靠idåQŒxpathåQŒname½{‰ä¿¡æ¯å®Œæˆç™»å½•操作。在‹¹‹è¯•中,我们可以˜q™æ ·æ¥ä‹É用这个page objectåQ?br>
1 page = LoginPage.new $selenium
2 page.login_as :name => 'xxx', :password => 'xxx'
3 

不过既然用了rubyåQŒæ€»è¦ç”¨ä¸€äº›ruby sugar吧,我们定义一个onæ–ÒŽ(gu¨©)³•来表è¾ùN¡µé¢æ“ä½œçš„环境åQ?br>
1 def on page_type, &block
2   page = page_type.new $selenium
3   page.instance_eval &block if block_given?
4 end

之后我们ž®±å¯ä»¥ä‹É用page object的类名常量和block描述在某个特定页面上操作了:

1 on LoginPage do
2   login_as :name => 'xxx', :password => 'xxx'
3 end
4 

é™¤äº†è¡ŒäØ“æ–ÒŽ(gu¨©)³•之外åQŒæˆ‘们还需要在page object上定义一些获取页面信息的æ–ÒŽ(gu¨©)³•åQŒæ¯”如获取登录页面的‹Æ¢è¿Žè¯çš„æ–ÒŽ(gu¨©)³•åQ?br>
def welcome_message
  @driver.get_text 
'xpath='
end


˜q™æ ·‹¹‹è¯•也可表达得更生动一些:

1 on LoginPage do
2   assert_equal 'Welcome!', welcome_message
3   login_as :name => 'xxx', :password => 'xxx'
4 end

当你把所有的™åµé¢éƒ½ç”¨Page Objectž®è£…了之后,ž®±æœ‰æ•ˆåœ°åˆ†ç¦»äº†æµ‹è¯•å’Œ™åµé¢¾l“构的耦合。在‹¹‹è¯•中,只需使用诸如login_as, add_product_to_cart˜q™æ ·çš„业务行为,而不必依靠像idåQŒname˜q™äº›å…·ä½“且易变的™åµé¢å…ƒç´ äº†ã€‚当˜q™äº›™åµé¢å…ƒç´ å‘生变化æ—Óž¼Œåªéœ€ä¿®æ”¹ç›¸åº”çš„page objectž®±å¯ä»¥äº†åQŒè€ŒåŽŸæœ‰æµ‹è¯•åŸºæœ¬ä¸éœ€è¦å¤ªå¤§æˆ–å¤ªå¤šçš„æ”¹åŠ¨ã€?br>
2. Assertation

åªæœ‰è¡ŒäØ“˜q˜å¤Ÿä¸æˆ‹¹‹è¯•åQŒæˆ‘们还要判断行为结果,òq¶è¿›è¡Œä¸€äº›æ–­­a€ã€‚简单回™å¾ä¸€ä¸‹ä¸Šé¢çš„例子åQŒä¼šå‘现˜q˜æœ‰ä¸€äº›å¾ˆé‡è¦çš„问题没有解冻I¼šæˆ‘怎么判断ç™Õd½•成功了呢åQŸæˆ‘如何才能知道真的是处在登录页面了呢?如果我调用下面的代码会怎样呢?

1 $selenium.open url_of_any_page_but_not_login
2 on LoginPage {}

因此我们˜q˜éœ€è¦å‘page object增加一些断­a€æ€§æ–¹æ³•。至ž®‘,每个™åµé¢éƒ½åº”该有一个方法用于判断是否真正地辑ֈ°äº†è¿™ä¸ªé¡µé¢ï¼Œå¦‚果不处在这个页面中的话åQŒå°±ä¸èƒ½˜q›è¡Œä»ÖM½•的业务行为。下面修改LoginPage使之包含˜q™æ ·ä¸€ä¸ªæ–¹æ³•:

1 LoginPage.class_eval do
2   include Test::Unit::Asseration
3   def visible?
4     @driver.is_text_present(&& @driver.get_location == 
5   end
6 end


在visible?æ–ÒŽ(gu¨©)³•中,我们通过对一些特定的™åµé¢å…ƒç´ åQˆæ¯”如URL地址åQŒç‰¹å®šçš„UI¾l“构或元素)˜q›è¡Œåˆ¤æ–­åQŒä»Žè€Œå¯ä»¥å¾—之是否真正地处在某个™åµé¢ä¸Šã€‚而我们目前表达测试的基本¾l“构是由onæ–ÒŽ(gu¨©)³•来完成,我们也就™åºç†æˆç« åœ°åœ¨onæ–ÒŽ(gu¨©)³•中增加一个断­a€åQŒæ¥åˆ¤æ–­æ˜¯å¦çœŸçš„处在某个™åµé¢ä¸Šï¼Œå¦‚果不处在这个页面则不进行ä“Q何的业务操作åQ?br>
1 def on page_type, &block
2   page = page_type.new $selenium
3   assert page.visible?"not on #{page_type}"
4   page.instance_eval &block if block_given?
5   page
6 end
7 

˜q™ä¸ªæ–ÒŽ(gu¨©)³•¼œžç§˜åœ°è¿”回了page对象åQŒè¿™é‡Œæ˜¯ä¸€ä¸ªæ¯”较tricky的技巧。实际上åQŒæˆ‘们只惛_ˆ©ç”¨page != nil˜q™ä¸ªäº‹å®žæ¥æ–­­a€™åµé¢çš„æµè½¬ï¼Œæ¯”如åQŒä¸‹é¢çš„代码描述ç™Õd½•成功的页面流转过½E‹ï¼š

on LoginPage do
  assert_equal 
'Welcome!', welcome_message
  login_as :name 
=> 'xxx', :password => 'xxx'
end
assert on WelcomeRegisteredUserPage

除了˜q™ä¸ªåŸºæœ¬æ–­è¨€ä¹‹å¤–åQŒæˆ‘们还可以定义一些业务相关的断言åQŒæ¯”如在购物车页面里åQŒæˆ‘们可以定义一个判断购物èžR是否为空的断­a€åQ?br>
1 def cart_empty?
2   @driver.get_text('xpath='== 'Shopping Cart(0)'
3 end

需要注意的是,虽然我们在page object里引入了Test::Unit::Asseration模块åQŒä½†æ˜¯åƈ没有在断­a€æ–ÒŽ(gu¨©)³•里ä‹É用ä“Q何assert*æ–ÒŽ(gu¨©)³•。这是因为,概念上来讲page objectòq¶ä¸æ˜¯æµ‹è¯•。ä‹É之包含一些真正的断言åQŒä¸€åˆ™æ¦‚忉|؜乱,二则å®ÒŽ(gu¨©)˜“使page object变成针对某些场景的test helperåQŒä¸åˆ©äºŽä»¥åŽ‹¹‹è¯•的维护,因此我们往往們֐‘于将断言æ–ÒŽ(gu¨©)³•实现ä¸ÞZ¸€ä¸ªæ™®é€šçš„˜q”回å€égØ“boolean的方法ã€?br>
3. Test Data

‹¹‹è¯•意图的体çŽîC¸ä»…ä»…æ˜¯åœ¨è¡ŒäØ“çš„æ˜qîC¸ŠåQŒåŒæ ¯‚¿˜æœ‰æµ‹è¯•数据,比如如下两段代码åQ?br>
 1 on LoginPage do
 2   login_as :name => 'userA', :password => 'password'
 3 end
 4 assert on WelcomeRegisteredUserPage
 5 
 6 registered_user = {:name => 'userA', :password => 'password'}
 7 on LoginPage do
 8   login_as registered_user
 9 end
10 assert on WelcomeRegisteredUserPage


‹¹‹è¯•的是同一个东西,但是昄¡„¶½W¬äºŒä¸ªæµ‹è¯•更好的体现了测试意图:使用一个已注册的用æˆïL™»å½•,应该˜q›å…¥‹Æ¢è¿Ž™åµé¢ã€‚我们看˜q™ä¸ª‹¹‹è¯•的时候,往往不会兛_¿ƒç”¨æˆ·åå•Šå¯†ç å•Šå…·ä½“是什么,我们兛_¿ƒå®ƒä»¬è¡¨è¾¾äº†æ€Žæ ·çš„æµ‹è¯•案例。我们可以通过DataFixture来实现这一点:

 1 module DataFixture
 2   USER_A = {:name => 'userA', :password => 'password'}
 3   USER_B = {:name => 'userB', :password => 'password'}
 4 
 5   def get_user identifier
 6     case identifier
 7     when :registered then return USER_A
 8     when :not_registered then return USER_B
 9     end
10   end
11 end


在这里,我们ž®†æµ‹è¯•案例和具体数据做了一个对应:userAæ˜¯æ³¨å†Œè¿‡çš„ç”¨æˆøP¼Œè€ŒuserB是没注册的用戗÷€‚当有一天,我们需要将ç™Õd½•用户名改为邮½ŽÞqš„æ—¶å€™ï¼Œåªéœ€è¦ä¿®æ”¹DataFixture模块ž®±å¯ä»¥äº†åQŒè€Œä¸å¿…修改相应的‹¹‹è¯•åQ?br>
1 include DataFixtureDat
2 
3 user = get_user :registered
4 on LoginPage do
5   login_as user
6 end
7 assert on WelcomeRegisteredUserPage

当然åQŒåœ¨æ›´å¤æ‚çš„‹¹‹è¯•中,DataFixture同样可以使用真实的数据库或是Rails Fixture来完成这æ ïLš„对应åQŒä½†æ˜¯æ€ÖM½“的目的就是ä‹É‹¹‹è¯•和测试数据有效性的耦合分离åQ?br>
1 def get_user identifier
2   case identifier
3   when :registered then return User.find '.' 
4   end
5 end


4.Navigator

与界面元素类ä¼û|¼ŒURL也是一¾cÀL˜“变且难以表达意图的元素,因此我们可以使用Navigator使之与测试解耦。具体做法和Test Dataç›æ€¼¼åQŒè¿™é‡Œå°±ä¸èµ˜˜qîCº†åQŒä¸‹é¢æ˜¯ä¸€ä¸ªä¾‹å­ï¼š

1 navigate_to detail_page_for @product
2 on ProductDetailPage do
3   .
4 end

5. Shortcut

前面我们已经有了一个很好的基础åQŒå°†Selenium‹¹‹è¯•与各¿Uè„†å¼×ƒ¸”意图不明的元素分¼›Õd¼€äº†ï¼Œé‚£ä¹ˆæœ€åŽshortcut不过是在蛋糕上面最漂亮的奶油çŞ了——定义具有漂亮语法的helperåQ?br>
1 def should_login_successfully user
2   on LoginPage do
3     assert_equal 'Welcome!', welcome_message
4     login_as user
5   end
6   assert on WelcomeRegisteredUserPage
7 end

然后是另外一个magicæ–ÒŽ(gu¨©)³•åQ?br>
1 def given identifer
2   words = identifier.to_s.split '_'
3   eval "get_#{words.last} :#{words[0..-2].join '_'}"
4 end

ä¹‹å‰çš„æµ‹è¯•å°±å¯ä»¥è¢«æ”¹å†™äØ“åQ?br>
def test_should_xxxx
  should_login_successfully given :registered_user
end


˜q™æ˜¯ä¸€¿Uç»“论性的shortcut描述åQŒæˆ‘们还可以有更behaviour的写法:

 1 def login_on page_type
 2   on page_type do
 3     assert_equal 'Welcome!', welcome_message
 4     login_as @user
 5   end
 6 end
 7 
 8 def login_successfully
 9   on WelcomeRegisteredUserPage
10 end
11 
12 def given identifer
13   words = identifier.to_s.split '_'
14   eval "@#{words.last} = get_#{words.last} :#{words[0..-2].join '_'}"
15 end


最后,‹¹‹è¯•ž®×ƒ¼šå˜æˆ¾cÖM¼¼éªŒæ”¶æ¡äšg的样子:

1 def test_should_xxx
2   given :registered_user
3   login_on LoginPage
4   assert login_successfully
5 end

æ€ÖM¹‹shortcut是一个无兛_¥½åï¼Œåªå…³ä¹Žæƒ³è±¡åŠ›çš„ä¸œè¥¿ï¼Œž®½æƒ…挥洒Ruby DSLå?D

¾l“论

Selenium是一个让人又爱又恨的东西åQŒé”™è¯¯åœ°ä½¿ç”¨Selenium会给整个敏捷团队的开发节奏带来灾难性的影响。不˜q‡å€¼å¾—庆幸的是正确åœîC‹É用Selenium的原则也是相当的½Ž€å•:

1.通过ž®†è„†å¼±æ˜“变的™åµé¢å…ƒç´ å’Œæµ‹è¯•分¼›Õd¼€åQŒä‹É得页面的变化不会å¯ÒŽ(gu¨©)µ‹è¯•äñ”生太大的影响ã€?br>2.明确指定‹¹‹è¯•数据的意图,不在‹¹‹è¯•用ä‹É用ä“Q何具体的数据ã€?br>3.ž®½ä¸€åˆ‡å¯èƒ½ï¼Œæ˜Žç¡®åœ°è¡¨è¾‘Ö‡º‹¹‹è¯•的意图,使测试易于理解ã€?br>
当然åQŒé™¤äº†éµå¾ªè¿™å‡ ä¸ªåŸºæœ¬åŽŸåˆ™ä¹‹å¤–åQŒä‹É用page object或其他domain based web testing技术是个不错的选择。它们将会帮助你更容易地控制Selenium‹¹‹è¯•的规模,更好地åã^衡覆盖率和执行效率,从而更加有效地交付高质量的Web™å¹ç›®ã€?br>
鸣谢

此文中涉及的都是我最˜q‘三周以来对Selenium‹¹‹è¯•˜q›è¡Œé‡æž„时所采用的真实技术。感谢Nick Drew帮助我清晰地划分了Driver, Page, Nagivatorå’ŒShortcut的层‹Æ¡å…³¾p»ï¼Œå®ƒä»¬æž„成我整个实è·ëŠš„基石åQ›æ„Ÿè°¢Chris LeishmanåQŒåœ¨å’Œä»–pairing programming的过½E‹ä¸­åQŒä»–帮助我锤ç‚égº†Ruby DSLåQ›è¿˜æœ‰Mark Ryallå’ŒAbhiåQŒæ˜¯ä»–们½W¬ä¸€‹Æ¡åœ¨™å¹ç›®ä¸­å¼•入了Test Data FixtureåQŒä‹É得所有äh的工作都变得½Ž€å•è“v来ã€?br>


]]>
The Keyword 'end' Drives Me Crazyhttp://www.aygfsteel.com/raimundox/archive/2007/05/11/116864.htmlRaimundoxRaimundoxFri, 11 May 2007 12:32:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/05/11/116864.htmlhttp://www.aygfsteel.com/raimundox/comments/116864.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/05/11/116864.html#Feedback1http://www.aygfsteel.com/raimundox/comments/commentRss/116864.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/116864.html
1 while @productions.inject(false) {|c, p| c |= !nullable?(p.nonterminal) && p.symbols.all? {|s| nullable? s} && @nullables << p.nonterminal}

注意1不是行号...˜q™å¥ç”¨çš„statement modifier, 1是我能想到的最ž®ruby语句äº?..

p.s.
我现在已¾læ¢å¤åˆ°OO保护模式äº?..刚才˜q½æ±‚短小˜q‡äº†å¤´çš„同时åQŒå‘çŽîCº†ruby bulid-in object的一个陷é˜?..
a = Array.new 5, []
[[],[],[],[],[]]
a[0] << 1
[[1],[1],[1],[1],[1]]

想不到华丽的Array直接假设传进åŽÈš„都是值对象了åQŒå¥½æ­ÒŽ(gu¨©)‚¨ä¹Ÿè°ƒä¸ªdupå•?..




]]>
I'm Smalltalk, Which Programming Language are You?http://www.aygfsteel.com/raimundox/archive/2007/05/02/115053.htmlRaimundoxRaimundoxWed, 02 May 2007 09:48:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/05/02/115053.htmlhttp://www.aygfsteel.com/raimundox/comments/115053.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/05/02/115053.html#Feedback3http://www.aygfsteel.com/raimundox/comments/commentRss/115053.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/115053.html
Which Programming Language are You?

p.s. ˜q™ä¸ªå¯èƒ½ä¸å‡†...å› äØ“æŽé»˜åŒå­¦ç«Ÿç„¶æ˜¯Lisp...怎么可能...


]]>
X-Fileshttp://www.aygfsteel.com/raimundox/archive/2007/03/30/107563.htmlRaimundoxRaimundoxFri, 30 Mar 2007 14:52:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/03/30/107563.htmlhttp://www.aygfsteel.com/raimundox/comments/107563.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/03/30/107563.html#Feedback3http://www.aygfsteel.com/raimundox/comments/commentRss/107563.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/107563.html
1. IEC61970 Metadata: Electricity Power Trading System

当时刚上班,team里有一个Doamin知识很厉害的清华的博士,毕业的论文就是电力市场,而清华又是国家引入IEC61970的五家之一。所以他很超前的把这两个东西¾l“åˆåœ¨ä¸€èµøP¼Œåšæˆäº†ä¸€ä¸ªç³»¾lŸã€‚说实话åQŒåˆšäº†è§£IEC61970的时候,我是相当的震撼的åQŒæœ‰èµ¶ä¸Šé‚£æ—¶å€™MDA风气刚è“våQŒIEC61970又是同时MOF(Meta Object Facility)å’ŒRDF basedåQŒåŽä¸½å¾—不行。一下子我就变成了一个MDA guyåQŒä¸€ä¸ªmetadata guy...以至于,在BJUG最初的2òq´é‡ŒåQŒMDA/MOF/MetadataæˆäØ“äº†ä¸»æ—‹å¾‹...

2. IEC61970 & CWM(Common Warehouse Metamodel) & Office Plugin : Data Warehouse Integration System

˜q™æ˜¯˜q„今为止åQŒæˆ‘最不愿意回忆的一个项ç›?..因䨓Office Plugin...动辄蓝屏的遭遇让我心有余æ‚(zh¨¨n)?..˜q™æ˜¯ä¸€ä¸ªbackend是J2EEåQŒfrontendæ˜?Netçš„office插äšg¾pȝ»ŸåQŒä¸»è¦æ˜¯æŠ¥è¡¨...两边都ä‹É用CWMä½œäØ“æ•°æ®¾lŸä¸€çš„åÅžå¼?..基本上做åˆîC¸€åŠæˆ‘的意志就崩溃äº?..

3. DB Migration/Refactoring : Jyxpearl

˜q™ä¸ª™å¹ç›®...是李默同学的¿Uæˆ¿æœ€çˆ±ï¼Œä»Žå¤§å­¦ä¸€ç›´åšäº†å¾ˆä¹…,改版无数‹Æ?..当时没有˜q™ä¹ˆ‹¹è¡Œçš„好词,什么DB Migration啊,DB Refactoring啊,那时候我们统¿U°å¯¼æ•°æ®...我导了好多会...基本上线一回导一å›?..时至今日...李默同学æ€ÀL˜¯ä¸æ— å¾—意的说åQšä½ çœ‹ï¼Œä½ DB Migration的能力就是我培养çš?..

4. JMI(Java Metadata Interface) & Eclipse RCP : Multi/Rich Client ERP Product

˜q™ä¸ªteam其实挺华丽的åQŒè€æ ¾çš„äñ”品经理,李默是开发经理,­‘…çñ”资深行业专家(人家实际做过生äñ”¿U‘é•¿åQŒMRPIIåQŒERP都是人家玩剩下的)老齐做需求,俺是ArchitectåQŒè¿˜æœ‰åŠ¨ç‰©å›­é‡Œçš„çŒªSenior DevåQŒæˆ‘认识äºÞZ¸­¾ŸŽå·¥èƒ½åŠ›½W¬ä¸€äº¤äº’设计能力½W¬ä¸€çš„ç±³¾c›_§åšUI和交互。由于当时看了netbeanså’Œsun的官方JMI实现得太玩具。我们决定从自己的JMI实现开始,¾pȝ»Ÿ¾l“构要求多客æˆïL«¯åQŒwebåQŒrcp都要...所以是­‘…è½»http协议的b/såQŒc/s。结构还是不错的åQŒè¿‡½E‹æŽé»˜å’Œæˆ‘当然是敏捷了。似乎一起都­‘…çñ”完美的时候,ž®±æ˜¯è¦åèœçš„æ—¶å€?..企业事业部解散了...

5. Java Communication & Eclipse RCP : IC Card Reader

上面那个™å¹ç›®è§£æ•£ä¹‹åŽåQŒæˆ‘跟李默赋闲在å®Óž¼Œæœ‰ä¸å¿å¿ƒæ‰“扰政府åQŒè‡ªè°‹ç”Ÿè·¯æ‰¾çš„项ç›?..˜q™ä¸ª™å¹ç›®è¦ç”¨IC卡读卡器åQŒäؓ了锻炼我们的Eclipse RCP能力åQŒæˆ‘们决定用eclipse rcp来做。于是问题就出来äº?..IC卡怎么办?google一把发现天无绝äºÞZ¹‹è·?..Java有一个Communication包,可以˜qžæŽ¥serial port...不过当时tricky的是...我的本子没有串口åQŒæˆ‘们买了一个串口到usbçš„è{换器...发现æ ÒŽ(gu¨©)œ¬ä¸èƒ½ç”?..于是只好跑到李默家用他华丽的台式机(˜q™åŽ®å½“å¹´èª“è¨€æ—¦æ—¦çš„è¯´åQŒlaptop太慢åQŒä¸€å®šè¦ç”¨å°å¼æœºåQŒä¸œå€Ÿè¥¿å€Ÿæžäº†ä¸ª2G RAM SATAåQÀL³¨æ„ï¼Œ˜q™æ˜¯ä¼ç¬”åQ½çš„æœºå™¨åQ‰ã€‚我当时ž®Þp§‰å¾—,Java的这个东西基本就是充数的åQŒè²Œä¼¼å®Œå…¨æ²¡æœ‰äh用过åQŒæ–‡æ¡£å•¥çš„都特少...只能自己摸烦。在¾låŽ†äº†æ— æ•°æ¬¡å¤ÞpÓ|之后åQŒç»ˆäºŽæˆåŠŸäº†ã€‚åœ¨showcase那天的上午,我最后实验了è¯Õd¡ä»€ä¹ˆçš„åQŒéƒ½æ²¡é—®é¢˜ã€‚兴高采烈的把jar拷到优盘上,刚插到usb口上...只见一道闪ç”?..机器黑了...据李默后来分析是ä¸ÀL¿çƒ§äº†...我说没事åQŒæ‹¿ä¸Šç¡¬ç›˜ï¼ŒåœŸä¸€ç‚¹ä¹Ÿä¸åª„响showcase。李默说...˜q™ä¸ª...SATAè€?..˜q˜ä¸‹¹è¡Œå‘?..我绿...此后很长旉™—´åQŒæˆ‘都怀疑是我跟李默同学范冲åQŒè¶…¾U§é¡¹ç›®æ€æ‰?..

6. RDF, Semantic Web, SparQL : Ontology-Relationship DB Mapping

˜q™æ˜¯åœ¨ä¸€å®¶å…¬å¸åšäº§å“åQŒå½“时我元数æ?MDA领域颇有¿U¯ç¯...跟这家公司做得类ä¼û|¼Œž®Þp¿‡æ¥è´Ÿè´£ç ”发本体到关系数据库的映射...兼带在D2RQ的基¼‹€ä¸Šå®žçŽîC¸€ä¸ªSparQL查询语言。怎么æ ?..听上åŽÕd¾ˆåŽä¸½å?..到现在我都认为,˜q™ä¸ª™å¹ç›®æ˜¯æˆ‘最有潜力的牛皮åQŒä¸å®šé‚£å¤©web x.0了,我也老了åQŒæˆ‘ž®±å¯ä»¥æ‹‰ç€ž®æœ‹å‹çš„æ‰‹åŽ»å¹ç‰›bäº?05òq´æˆ‘ž®±åšsemantic web,O/R mapping知道不?Ontology啊,你们啊,sometime too simple"...不过估计˜q™ä¸€å¤©è¿˜æ—©å¾—很呢

7. Agile Domain Specified Language : Goodhope

˜q™ä¸ªä¹Ÿæ˜¯æŽé»˜åŒå­¦æœ‰ä†¾çš„项ç›?..话里的敏捷DSL实践...不过说实话,也有点X...

]]>
Agile 101: CoC & Why Agile is Hardhttp://www.aygfsteel.com/raimundox/archive/2007/03/30/107375.htmlRaimundoxRaimundoxThu, 29 Mar 2007 22:53:00 GMThttp://www.aygfsteel.com/raimundox/archive/2007/03/30/107375.htmlhttp://www.aygfsteel.com/raimundox/comments/107375.htmlhttp://www.aygfsteel.com/raimundox/archive/2007/03/30/107375.html#Feedback4http://www.aygfsteel.com/raimundox/comments/commentRss/107375.htmlhttp://www.aygfsteel.com/raimundox/services/trackbacks/107375.html要我è¯ß_¼Œ˜q™äº›äºø™¦ä¹ˆæ˜¯ä¸çœŸçš„了解敏捷开发,没有认识到敏捷开发的革命性,只是用外在的形式来把它和其他æ–ÒŽ(gu¨©)³•˜q›è¡Œäº†æ¯”较。有又或者是实施敏捷æ–ÒŽ(gu¨©)³•的时候不å½Õdº•åQŒæ‰€ä»¥å››å¤„碰壁以至于搞è“v了修正主义。最可怕的ž®±æ˜¯æŸäº›å¤§å…¬å¸ï¼Œçœ‹æ•æïL«äº†ï¼Œæ€ÀLœ‰åŒ…装一下,到底˜q˜æ˜¯è¦å–产品。敏捯‚ÊY件开发就是一个革命性的æ–ÒŽ(gu¨©)³•åQŒåªä¸è¿‡å®ƒè¦é¢ è¦†çš„不仅仅是低质量的èÊY件开发方式,更重要的是,它要颠覆软äšg生äñ”企业和èÊY件的使用企业之间的生产关¾p»ï¼!˜q™ä¸€ç‚¹åœ¨æ•æ·å®£è¨€é‡Œå†™å¾—再明白不过äº?br />
Customer collaboration over Contract negotiation

敏捷软äšg开发,ž®±æ˜¯è¦ä»¥ä¸€¿Uæ›´åˆç†çš„共赢的合作关系åQŒä»£æ›¿ä»¥å‰ç•¸å½¢çš„é‡‡è´­å¼çš„åˆçº¦å…³ç³»ã€‚äØ“ä»€ä¹ˆåˆ¾U¦å…³¾pÕd°±æ˜¯ç•¸å½¢çš„åQŸæˆ‘们来看看合约双方的处境ã€?br />
首先软äšg团队斚w¢æ‰¿æ‹…了过多的风险åQšä¸šåŠ¡å˜åŒ–ï¼Œæ”¹ä»£ç ï¼åQå•†ä¸šæŠ‰æ‹©è{换,改代码!åQå‡­å•¥ä½ ç”²æ–¹çš„缘故非要我承担额外的成本?你说我冤不冤åQŸå†¤åQä½†æ˜¯äh家甲方也冤!åQäh家花了大把的银子åQŒæ‹¿åˆîC¸€å †ä¸èƒ½ç”¨çš„èÊYä»Óž¼ˆä½ è¦æ˜¯ç¡¬ä»¶äh家还能è{手卖炚w’±åQ‰ï¼Œž®±åƒä½ è¦ç æ ‘别äh¾l™ä½ æŠŠé“²å­ï¼Œä½ è¦¿Uæ ‘人家¾l™äº†ä½ æŠŠé”¯ã€‚搁你,你也不愿意。且不说博弈åQŒå°±½Ž—双斚wƒ½æœ‰å¿ƒæŠŠäº‹æƒ…做好,按合同来åQŒç”²æ–¹ä¸òqÔŒ¼›ä¸æŒ‰åˆåŒæ¥ï¼Œä¹™æ–¹ä¸å¹²åQŒæœ€åŽå˜æˆâ€œæœ‰å¿ƒæ€è´¼æ— åŠ›å›žå¤©â€ï¼Œå¤§å®¶ä¸€èµäh‰¯æ‰¯çš®½{‰äºŒæœŸç®—了。lose-loseåQŒæ²¡æœ‰èµ¢å®¶ã€?br />
那么合作的关¾pÀL˜¯ä»€ä¹ˆå‘¢åQŸåˆä½œçš„关系ž®±å¥½æ¯”你去subwayä¹îC¸‰æ˜Žæ²»åQŒé¢åŒ…你自己选,要什么肉你来挑,蔬菜åQŒcheeseåQŒé…±æ±ä½ ä¹Ÿè‡ªå·Þqœ‹ç€åŠžã€‚æŠ€æœ¯æˆ‘æ¥ï¼Œå£å‘³ä½ é€‰ã€‚æŠ€æœ¯å¤±è´¥æˆ‘è´Ÿè´£åQŒå£å‘³ä¸åˆé€‚你负责。你做你的强™åÒŽ(gu¨©)ˆ‘来我的强™å¹ï¼Œæœ€¾lˆå¤§å®‰™«˜é«˜å…´å…´å˜»å˜Õd“ˆå“ˆä¸åµä¸é—¹ï¼Œä½œå‡ºä¸€™å¿å¯å£åˆ˜¡ã€‚这是时候,生äñ”关系变了åQŒæˆ‘不是你的冷冰冰的供应商,你也不是我邪恶的客户åQŒæˆ‘们是拴在一根ç‘Ñ子上的蚂蚱。成功是我们的,å¤ÞpÓ|也是我们的。荣è¾×ƒ¸Žå…±ï¼Œæºæ‰‹òq¶è‚©ã€‚听着有点耳熟åQŸæ²¡é”™ï¼ŒSaaS。敏捷宣­a€æ—©å°±è¯´äº†åQŒCoC啊。从供应商变成服务商åQŒä»ŽæœåŠ¡å•†å˜æˆæˆ˜ç•¥åˆä½œä¼™ä¼ß_¼Œ˜q™æ˜¯åœ¨ç»™è½¯äšg企业指出路,新的生äñ”关系已经ž®½åœ¨å…¶ä¸­äº†ã€?br />
如果看不清敏æïLš„˜q™ä¸ªæ ÒŽ(gu¨©)œ¬é©å‘½ç‚¹ï¼Œä»¥äØ“˜q˜æ˜¯å¼€å‘方法的ž®æ‰“ž®é—¹åQŒé‚£ä¹ˆæ•æäh ¹æœ¬å®žæ–½ä¸æˆã€‚这话一般我不敢说的åQŒç¨‹åºå‘˜è‡ªå‘实施敏捷åQŒåªåœ¨ä¸€¿Uæƒ…况下可能成功åQšå¤§ä¼ä¸šçš„IT部门。再赶上个强力的IT领导åQŒè‡ªå®¶äh嘛,有什么不好谈的。一来二去,ž®±æˆåŠŸäº†åQˆçœ‹çœ‹C3åQŒè¯´ç™½äº†ä¸å°±æ˜¯IT部门和业务部门?åQ‰ä½†æ˜¯ï¼Œå¦‚果是做™å¹ç›®çš„公司,你营销手段不改变,敏捷ž®×ƒ¸å¯èƒ½æˆåŠŸã€‚ä½ çš„å®¢æˆ¯‚·Ÿä½ ä¸æ˜¯åˆä½œå…³¾p»ï¼Œä½ é€šè¿‡æ•æ·å¢žåŠ è´¨é‡åQˆç¬¦åˆæ€§è´¨é‡ï¼‰çš„工作就不会被äh可,那么ž®×ƒ¸èƒ½æˆä¸ºæŠ•资,只能是成本。当成本增加åˆîC¸å¯æ‰¿æ‹…的时候,敏捷ž®×ƒ¸äº†äº†ä¹‹äº†ã€‚äØ“ä»€ä¹ˆå¥½å¤šäh说老板没有响应åQŸæ—§çš„生产关¾pÖM¸‹æ•æ·æ ÒŽ(gu¨©)œ¬ž®±æ˜¯è´Ÿæ‹…ã€?br />
说道˜q™é‡ŒåQŒè¯´ä¸€ä¸‹ä»¥æ•æ·é—Õdçš„ThoughtWorks。其实很多äh都以为ThougtWorks只有æ–ÒŽ(gu¨©)³•论咨询,没错我们是有æ–ÒŽ(gu¨©)³•论咨询,但是也有业务模式咨询åQŒå®¢æˆ·ä¸šåŠ¡æ¨¡å¼ä¸æ”¹å˜åQŒä»–æ€Žä¹ˆèƒ½å½»åº•æ•æøP¼Ÿ˜q™ç‚¹å¤§å®¶ä¸å¯ä¸æŸ¥å•Šã€?img src ="http://www.aygfsteel.com/raimundox/aggbug/107375.html" width = "1" height = "1" />

]]>
Ö÷Õ¾Ö©Öë³ØÄ£°å£º ÕżҸÛÊÐ| Í­ÁºÏØ| ÑïÖÝÊÐ| »Ȱ| Ìì×£| ËçÄþÏØ| ʯ¼ÒׯÊÐ| À´·ïÏØ| Î÷»ªÏØ| ¸»Ô´ÏØ| кÍÏØ| ÎÚÇ¡ÏØ| ´óͬÊÐ| ÐÂÀÖÊÐ| ÉîÔóÏØ| Íû¶¼ÏØ| Æ½Ò£ÏØ| кÓÏØ| ÐìË®ÏØ| ÐÂÐËÏØ| ÖñÉ½ÏØ| ÄϲýÊÐ| ÁÙâ¢ÏØ| º£·áÏØ| ºéºþÊÐ| µ÷±øÉ½ÊÐ| ¿Æ¼¼| ·âÇðÏØ| ºÍË¶ÏØ| ÁÙº£ÊÐ| Ëþ³ÇÊÐ| ËÕÄáÌØ×óÆì| ¶ÁÊé| À¼Î÷ÏØ| ÀóÆÖÏØ| ÎäÐûÏØ| ¤Î÷ÏØ| ÎâÆìÏØ| Æ«¹ØÏØ| ÂÀÁºÊÐ| ÎÚÀ¼²ì²¼ÊÐ|