Google chart api revoke using ruby and extjs
ruby api of google chart: http://gchart.rubyforge.org/gchart/
Extjs Generator for the Chart Server API: http://almaer.com/chartmaker/
ruby api of google chart: http://gchart.rubyforge.org/gchart/
Extjs Generator for the Chart Server API: http://almaer.com/chartmaker/
发表者 俞 伟军 位置在: 12/25/2007 10:11:00 AM 1 评论
收信端
require 'socket'
u1 = UDPSocket.open()
u1.bind("0.0.0.0", 10000)
p u1.recvfrom(65536)
发信端
require 'socket'
u2 = UDPSocket.new()
u2.connect('localhost', 10000)
u2.send('Hello world!' , 0)
接收结果
["Hello world!", ["AF_INET", 32818, "localhost", "127.0.0.1"]]
原文: http://d.hatena.ne.jp/emergent/20071225/1198510691
在Vim中运行:
:help =
提示错误类似:
E433:No tags file
E149:Sorry, no help for =
Press ENTER or type command to continue
在Vim下运行:
:helptags ~/.vim/doc
(或者是 :helptags 安装目录下的/vim7/doc)
安装了新的插件也是一样要运行一下此命令,可单独指定help文件名
发表者 俞 伟军 位置在: 12/24/2007 12:56:00 PM 0 评论
Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
一个表有130个varchar(255)字段,GBK编码,建表报以上错误,原因如下:
130 * 255 * 2 = 66560 > 66535 (GBK 2字节)
表字段长度总和Mysql有限制.如果表是utf-8的话,按3字节计算.
在~/.vimrc(_vimrc)中添加下面几行:
"完全隐藏菜单:
:set guioptions-=m " 可以随时用 :set guioptions+=m 再呼出菜单,下面工具条也类似
"完全隐藏工具栏:
:set guioptions-=T
zf#j creates a fold from the cursor down # lines.
zf/string creates a fold from the cursor to string .
zj moves the cursor to the next fold.
zk moves the cursor to the previous fold.
zo opens a fold at the cursor.
zO opens all folds at the cursor.
zm increases the foldlevel by one.
zM closes all open folds.
zr decreases the foldlevel by one.
zR decreases the foldlevel to zero -- all folds will be open.
zd deletes the fold at the cursor.
zE deletes all folds.
[z move to start of open fold.
]z move to end of open fold.
more usage command of vim folding can get from this page.
发表者 俞 伟军 位置在: 12/20/2007 02:33:00 PM 0 评论
1.从http://code.google.com/p/vim-scripts/wiki/TailBundle上面Subversion导出文件
tail_options.vim
autoload/tail.vim
doc/tail.txt
macros/vim-tail.zsh
plugin/tail.vim
2.放到Gvim/vim对应目录下即可
3.使用方法
:Tail
:STail
:TabTail
4.可参考tail_options.vim设置~/_vimrc(~/.vimrc),也可不设置按默认的使用.
http request referer:
Referer http://www.google.cn/search?hl=zh-CN&q=%E6%B5%B7%E8%AF%8D&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
php $_SERVER['http_referer']:
http://www.google.cn/search?hl=zh-CN&q=%E6%B5%B7%E8%AF%8D&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
javascript referer:
document.referrer
统计文章中出现的单词的数目,可以使用下面的命令:
:%s/\w//gn
如何将一串十进制数字转换为16进制数字,使用VIM完成转换的最简单方法如下:
:%s/\d\+/\=printf("%X", submatch(0))/g
这条命令的原理是,把一串数字,用printf()函数的输出替换掉,printf()函数输出的正是这串数字的16进制形式。
分析如下:
\= 使用表达式的结果进行替换 (:help /\w )
printf 按指定格式输出 (:help printf() )
submatch() 返回:s命令中的指定匹配字符串 (:help submatch() )
g 替换行内所有出现的匹配 (:help :s_flags)
发表者 俞 伟军 位置在: 12/17/2007 05:53:00 PM 0 评论
标签: Replace , Substitute , Vim
validates_uniqueness_of(*attr_names)
Validates whether the value of the specified attributes are unique across the system. Useful for making sure that only one user can be named "davidhh".
class Person < ActiveRecord::Base
validates_uniqueness_of :user_name, :scope => :account_id # 类似account_id, :user_name 组成表中的unique key
end
It can also validate whether the value of the specified attributes are unique based on multiple scope parameters. For example, making sure that a teacher can only be on the schedule once per semester for a particular class.
class TeacherSchedule < ActiveRecord::Base
validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id] # 类似3个字段组成表的unique key
end
发表者 俞 伟军 位置在: 12/14/2007 04:47:00 PM 0 评论
标签: Rails , validates_uniqueness_of
var fragment = document.createDocumentFragment();
var testScript = document.createElement('script');
testScript.setAttribute('type', 'text/javascript');
testScript.setAttribute('src', 'http://www.test.com/test.php');// test.php 中有document.write('<iframe src....></iframe>')
fragment.appendChild(testScript);
document.body.appendChild(fragment);
程序在载入当前页和test.php后就不能再载入iframe里的scr指向的页面,并页面锁死,这个同在页面载完后调Function输出一个document.write一样的错误.
发表者 俞 伟军 位置在: 12/14/2007 03:16:00 PM 0 评论
标签: document , JavaScript , Write
" gvim menu language
set langmenu=zh_CN.GBK
"set langmenu=en_US.GBK
发表者 俞 伟军 位置在: 12/14/2007 11:43:00 AM 0 评论
在用户目录下的~/_vimrc文件里加上此行(只是Windows生效)
au GUIEnter * simalt ~x "maximum the initial window
"自动进行编码匹配
set fileencodings=gb2312,ucs-bom,utf-8,chinese
"加载配色方案
colorscheme murphy
$ wget http://gems.rubyforge.org/gems/faker-0.2.0.gem
--11:33:09-- http://gems.rubyforge.org/gems/faker-0.2.0.gem
=> `faker-0.2.0.gem'
Resolving gems.rubyforge.org... 205.234.109.19
Connecting to gems.rubyforge.org|205.234.109.19|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://rubyforge.iasi.roedu.net/gems/faker-0.2.0.gem [following]
--11:33:10-- http://rubyforge.iasi.roedu.net/gems/faker-0.2.0.gem
=> `faker-0.2.0.gem'
Resolving rubyforge.iasi.roedu.net... 192.129.4.120
Connecting to rubyforge.iasi.roedu.net|192.129.4.120|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 43,520 (42K) [application/octet-stream]
100%[====================================>] 43,520 5.05K/s ETA 00:00
11:33:25 (5.04 KB/s) - `faker-0.2.0.gem' saved [43520/43520]
$ gem install faker
Successfully installed faker, version 0.2.0
Installing ri documentation for faker-0.2.0-...
Installing RDoc documentation for faker-0.2.0-...
$ irb
irb(main):002:0> require 'rubygems'
=> true
irb(main):003:0> require 'faker'
=> true
irb(main):005:0> Faker::Name.name
=> "Lauriane Stanton"
irb(main):006:0>
irb(main):007:0* Faker::Name.name
=> "Sallie Rippin"
irb(main):008:0> Faker::Name.name
=> "Selina Jerde"
irb(main):010:0> Faker::Internet.email
=> "elda.friesen@cassin.co.uk"
irb(main):011:0> Faker::Internet.email
=> "ervin@parkerblock.us"
irb(main):012:0> Faker::Internet.email
=> "jaron.bailey@abbotthintz.co.uk"
irb(main):013:0> Faker::Internet.email
=> "stan@torp.us"
irb(main):014:0> Faker::Internet.free_email
=> "yasmeen@yahoo.com"
irb(main):015:0> Faker::Internet.free_email
=> "ona@hotmail.com"
irb(main):016:0> Faker::Internet.free_email
=> "naomi@hotmail.com"
irb(main):017:0> Faker::Internet.free_email
=> "alisha@hotmail.com"
irb(main):018:0> Faker::Internet.user_name
Undefined method 'assign_variables_from_controller' message can appear when in controller you operate with instance variable @template.
To avoid such message in your controller try to rename instance variable @template in your method to something other, @tmpl for example.
发表者 俞 伟军 位置在: 11/29/2007 10:49:00 AM 0 评论
php安装了不知道多少次,经常会碰到一些安装问题,这次装5.2.5碰到2个错误如下:
configure: error: xml2-config not found. Please check your libxml2 installation
在CentOS5.0中执行以下命令:
$> yum install libxml2-devel
接下来又有新问题了(用rpm包装的mysql server 5.1.22):
Note that the MySQL client library is not bundled anymore!
configure: error: Cannot find MySQL header files under /usr/shared/mysql.
Note that the MySQL client library is not bundled anymore.
ERROR: Could not configure PHP
到mysql官网下载对应的5.1.22 mysql-devel开发包,安装完再装PHP即可。
原文
首先,在rails_project/log/目录下新建sql文件夹,sql文件夹用来保存sql日志
接着,修改rails_project/config/environment.rb代码,需注意代码顺序。
#web访问日志
RAILS_DEFAULT_LOGGER = Logger.new("#{RAILS_ROOT}/log/#{RAILS_ENV}#{Date.today.to_s}.log", "daily")
#rails的初始化
Rails::Initializer.run do |config|
#...
end
#sql日志
ActiveRecord::Base.logger = Logger.new("#{RAILS_ROOT}/log/sql/#{RAILS_ENV}#{Date.today.to_s}.log", "daily")
发表者 俞 伟军 位置在: 11/26/2007 11:41:00 AM 0 评论
phpinfo()里的一段信息如下,PHP加载的配置文件默认放到C:\WINDOWS\php.ini下。
Configure Command cscript /nologo configure.js "--enable-snapshot-build" "--with-gd=shared"
Server API Apache 2.0 Handler
Virtual Directory Support enabled
Configuration File (php.ini) Path C:\WINDOWS
Loaded Configuration File C:\WINDOWS\php.ini
gd
GD Support enabled
GD Version bundled (2.0.34 compatible)
FreeType Support enabled
FreeType Linkage with freetype
FreeType Version 2.1.9
T1Lib Support enabled
GIF Read Support enabled
GIF Create Support enabled
JPG Support enabled
PNG Support enabled
WBMP Support enabled
XBM Support enabled
cmd>ruby script\console
Loading development environment.
>> Time.now.to_s(:db)
=> "2007-11-19 13:21:22"
>> Time.now.to_s(:long)
=> "November 19, 2007 13:21"
>> Time.now.to_s(:stamp)
=> "Mon Nov 19 13:21:38 +0800 2007"
>> Time.now.to_s(:short)
=> "19 Nov 13:21"
>> Time.now.to_s(:rfc822)
=> "Mon, 19 Nov 2007 13:21:54 \326\320\271\372\261\352\327\274\312\261\274\344"
>> puts Time.now.to_s(:rfc822)
Mon, 19 Nov 2007 13:22:11 中国标准时间
发表者 俞 伟军 位置在: 11/19/2007 01:23:00 PM 0 评论
R4R
Defining a top-level method
Suppose you define a method at the top level:
def talk
puts "Hello"
end
def talk
puts "Hello"
end
puts "Trying 'talk' with no receiver..."
talk
puts "Trying 'talk' with an explicit receiver..."
obj = Object.new
obj.talk
发表者 俞 伟军 位置在: 11/16/2007 03:09:00 PM 0 评论
标签: Class , Console , Environment , Kernel , Methods , Module , Ruby , top-level
<iframe src="http://www.baidu.com"/>
因为iframe标签关闭不正确,后面的Javascript和此文本内容在FF/IE上(其他浏览器未测试)都不会被执行,HTML输出流截止于iframe。
<script type="text/javascript">
alert('test');
</script>
发表者 俞 伟军 位置在: 11/15/2007 01:14:00 PM 0 评论
标签: HTML , Iframe , JavaScript , Problem
为apache增加mod_usertrack module,只要动态生成一个so文件在配置文件里包括进来即可,之前有写了个文章关于这个操作过程。这次又用上了。
在虚拟主机的配置里再加上以下配置,其中具体的参数含义可参考此链接:http://httpd.apache.org/docs/1.3/mod/mod_usertrack.html
CookieTracking on
CookieStyle Cookie
CookieExpires "2 weeks"
CookieDomain .foo.bar
CustomLog logs/cookie-track.log "%{cookie}n %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
CookieName FOO_BAR
发表者 俞 伟军 位置在: 11/14/2007 03:59:00 PM 0 评论
标签: Apache2 , Cookie , mod_usertrack , Module , Track
在安装好jdk和jruby后并测试jruby正常工作后,设置了几个命令别名,以防跟原来系统装的ruby命令冲突,ruby下的irb/rails/gem/ruby等命令是包含到系统中的。Jruby要以示区别:
$> vi .bashrc
alias jgem='/usr/local/jruby/bin/gem'
alias jruby='/usr/local/jruby/bin/jruby'
alias jrails='/usr/local/jruby/bin/rails'
alias jirb='/usr/local/jruby/bin/irb'
alias jrake='/usr/local/jruby/bin/rake'
重新登录后,jruby正常工作,但jgem报错,Google无果。只能命令行调jruby安装rails到Jruby环境下,-S cmd run the specified command in JRuby's bin dir。
$> jgem -v
/usr/bin/env: jruby -J-Xmx512M: No such file or directory
$> jruby -J-Xmx384m -S gem install rails --version 1.2.3 --include-dependencies
Bulk updating Gem source index for: http://gems.rubyforge.org
Successfully installed rails-1.2.3
Successfully installed activesupport-1.4.2
Successfully installed activerecord-1.15.3
Successfully installed actionpack-1.13.3
Successfully installed actionmailer-1.3.3
Successfully installed actionwebservice-1.2.3
Installing ri documentation for activesupport-1.4.2...
Installing ri documentation for activerecord-1.15.3...
....
新建Jrails项目如下:
$> jruby -S rails test
却不能用 $> jrails test
报错如下:
/usr/local/jruby/bin/rails: line 9: require: command not found
/usr/local/jruby/bin/rails: line 10: version: command not found
/usr/local/jruby/bin/rails: line 11: syntax error near unexpected token `('
/usr/local/jruby/bin/rails: line 11: `if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then'
哪位仁兄如果看到此问题并有解决方案请跟贴指点,谢谢。
另附mongrel+jruby的windows安装过程:
$> jruby -S gem install mongrel --include-dependencies
$> jruby -S gem install gem_plugin
$> jruby -S gem install cgi_multipart_eof_fix
$> jruby -S gem install mongrel-1.1.1-jruby
ERROR: While executing gem ... (OpenURI::HTTPError)
404 Not Found
# Why can't install gem package from rubyforge? Make sure to invoke the command from the directory where the gem is downloaded.
$> wget http://rubyforge.org/frs/download.php/27884/mongrel-1.1.1-jruby.gem
$> jruby -S gem install mongrel-1.1.1-jruby
$> wget http://rubyforge.org/frs/download.php/20455/mongrel_jcluster-0.0.1.gem
$> jruby -S gem install mongrel_jcluster-0.0.1
# mongrel_cluster does not work with JRuby and mongrel_jcluster does not work on Windows. So Mongrel clusters cannot be configured on a Windows machine.
=================================================================================
$> wget http://rubyforge.org/frs/download.php/21765/hpricot-0.6-jruby.gem
$> jruby -S gem install hpricot-0.6-jruby
$> jruby -S gem install mechanize # not install scrubyt will raise error about openssl
$> jruby -S gem install scrubyt --include-dependencies # need install this gem package for using mechanize
$> wget http://rubyforge.org/frs/download.php/24515/ActiveRecord-JDBC-0.5.gem
$> jruby -S gem install ActiveRecord-JDBC-0.5
$> wget http://rubyforge.org/frs/download.php/23995/jruby-openssl-0.0.4.gem
$> jruby -S gem install jruby-openssl-0.0.4
如果在IRB中调以下代码会出现类here document的作用,而不是直接进行位移操作,虽然结果最后还是对的。
irb(main):020:0> a = 2
irb(main):020:0> c = a <<1
irb(main):021:0" 2
irb(main):022:0" 1
=> 1
irb(main):023:0> c
=> 4
如果a <<1中1之前再加个半角空格,那会正常运算,不知是否属于IRB的Bug,在Ruby脚本中直接运行是正常的。
在开发模式下,Rails的Models里的文件修改后会自动重载入,但是Models里require进来的Ruby lib和Rails lib下的文件有所修改是不会自动重载入的,必须需要重启Rails应该。
发表者 俞 伟军 位置在: 11/08/2007 12:37:00 PM 0 评论
标签: Development , Environment , Library , Models , Problem , Rails
参考http://zh.linuxvirtualserver.org/node/26,环境如下:
linux centos5.0 server: eth0 192.168.0.199(RIP); eth0:1 192.168.1.199(VIP); gateway 192.168.1.1(连外网)
windowsXP: 192.168.0.109(RIP); gateway: 192.168.0.199(网关设置为Linux CentOS5.0机器的IP)
windowsXP: 192.168.0.112(RIP); gateway: 192.168.0.199(网关设置为Linux CentOS5.0机器的IP)
XP机器都有开80端口WEB服务,linux机器上80端口的服务不一定要开。
1. 在CentOS5.0上搭建NAT
$> vi /etc/sysctl.conf
net.ipv4.ip_forword = 1
$> /sbin/sysctl -p
(需要安装iptables的模块,用这个命令检查: $> modprobe ip_tables)
(也能用以下命令开启NAT: $> echo "1" > /proc/sys/net/ipv4/ip_forword)
2. 关闭iptables service,测试NAT是否启用
$> service iptables stop
将其中一台XP机器的网关设置为此CentOS5.0的VIP地址192.168.1.199,IP改为192.168.1.109(如果real server是linux机器,修改了ip和gateway需要重启网络: $> service network restart),正确的话就可以正常访问局域网内其他192.168.1.*网段的机器和外网。
测试NAT通过后:
($> iptables -t nat -A POSTROUTING -j MASQUERADE -s 192.168.1.0/24)可选
$> iptables -t nat -A POSTROUTING -j MASQUERADE -s 192.168.0.0/24
$> service iptables save(先stop再save会覆盖原来iptables规则,如果不想覆盖则在iptable开启状态进行这二个命令)
$> service iptables start
再测试NAT是否正常,能否从XP机器联外网,如果正常那就把IP和gateway改回原来的设置。如果不能访问外网,需要调整一下iptables reject规则,具体可以看鸟哥的NAT设置相关文章。
3. $> yum install ipvsadm
$> chkconfig ipvsadm on
(linuxcommand info: http://www.linuxcommand.org/man_pages/ipvsadm8.html)
$> vi /etc/sysconfig/ipvsadm
ipvsadm -A -t 192.168.1.199:80 -s rr
ipvsadm -a -t 192.168.1.199:80 -r 192.168.0.109:80 -m -w 1
ipvsadm -a -t 192.168.1.199:80 -r 192.168.0.112:80 -m -w 1
# ipvsadm -a -t 192.168.1.199:80 -r 192.168.0.199:80 -m -w 1
$> service ipvsadm start
$> ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.1.199:80 rr
-> 192.168.0.112:80 Masq 1 0 1
-> 192.168.0.109:80 Masq 1 0 0
4. $> yum install heartbeat
$> yum install heartbeat-ldirector
$> chkconfig ldirectord on
$> vi /etc/ha.d/ldirectord.cf
# Global Directives
checktimeout=10
checkinterval=2
# fallback=127.0.0.1:80
autoreload=no
# logfile="/var/log/ldirectord.log"
logfile="local0"
quiescent=yes
# Virtual Server for HTTP
virtual=192.168.1.199:80
fallback=127.0.0.1:80
# real=192.168.0.199:80 masq
real=192.168.0.109:80 masq
real=192.168.1.112:80 masq
service=http
request="heartbeat.html"
receive="Test Page"
scheduler=rr
# persistent=600
protocol=tcp
checktype=negotiate
一、全局作用域和局部作用域
在全局环境里:
var a = 1;
与
a = 1;
的作用是相同的。但是如果是在一个函数体内这二者就不同了,前者是声明了一个函数体内的局部变量,而后者在此函数被运行一次之后就会生成一个全局变量 a 。
一般在声明变量时尽可能的加上var。
二、delete与变量关系:
按JavaScript权威指南书中所言,一个变量一旦被 var 声明之后(未初始化)就有一个默认值'undefined',并delete运算符不能删除这些变量,不然会引发一个错误。不过在Firefox中测试是可以对声明后的变量进行delete,并返回true,在操作之后再引用就会报未定义错误,说明变量正常删除。在IE7里进行delete是的确返回false,无法删除,不过也没有引发错误。
三、JavaScript没有块级作用域
这个不同于C/C++/Java,Javascript的变量只要声明了就会在整个函数体中都有定义,而不管声明的前后位置,会覆盖全局的同名变量。
function test(o) {
var i = 0; // i is defined throughout function
if (typeof o == "object") {
var j = 0; // j is defined everywhere, not just block
for(var k=0; k < 10; k++) { // k is defined everywhere, not just loop
document.write(k);
}
document.write(k); // k is still defined: prints 10
}
document.write(j); // j is defined, but may not be initialized
}
var scope = "global";
function f( ) {
alert(scope); // Displays "undefined", not "global"
var scope = "local"; // Variable initialized here, but defined everywhere
alert(scope); // Displays "local"
}
f( );
发表者 俞 伟军 位置在: 10/26/2007 08:55:00 PM 0 评论
标签: Example , Explain , JavaScript , Scope , Var
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\RecycleBinOnDesktop]
"RegPath"="Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel"
"Text"="在桌面上显示回收站图标"
"Type"="checkbox"
"valueName"="{645FF040-5081-101B-9F08-00AA002F954E}"
"Checkedvalue"=dword:00000000
"Uncheckedvalue"=dword:00000001
"Defaultvalue"=dword:00000001
"HKeyRoot"=dword:80000001
然后打开“资源管理器-工具-选项-查看”,先将“在桌面上显示回收站图标”这项选上“应用”再去掉勾“应用”即可控制桌面显示与不显示垃圾桶回收站。
在这个页面进行数据存储(仅限IE userData持久性)
<html>
<body>
<!-- This stylesheet defines a class named "persistent" -->
<style>.persistent { behavior:url(#default#userData);}</style>
<!-- This <div> element is a member of that class -->
<div id="memory" class="persistent"></div>
<script type="text/javascript">
var memory = document.getElementById("memory"); // Get persistent element
memory.setAttribute("username", 'user_name'); // Set data as attributes
memory.setAttribute("favoriteColor", '#ddd');
var now = (new Date( )).getTime( ); // now, in milliseconds
var expires = now + 10 * 24 * 60 * 60 * 1000; // 10 days from now in ms
memory.expires = (new Date(expires)).toUTCString( ); // convert to a string
memory.save("myPersistentData"); // Save the data
</script>
</body>
</html>
<html>
<body>
<!-- This stylesheet defines a class named "persistent" -->
<style>.persistent { behavior:url(#default#userData);}</style>
<!-- This <div> element is a member of that class -->
<div id="memory" class="persistent"></div>
<script type="text/javascript">
document.write("test result: <br/>");
var memory = document.getElementById("memory"); // Get persistent element
memory.load("myPersistentData"); // Retrieve saved data by name
var user = memory.getAttribute("username"); // Query attributes
var color = memory.getAttribute("favoriteColor");
document.write(user + '; ' + color);
</script>
</body>
</html>
发表者 俞 伟军 位置在: 10/24/2007 05:05:00 PM 0 评论
标签: Cookie , IE , Persistent , userData
<div id="form_div"></div>
<script type="text/javascript">
var urls = 'http://www.google.com/search?q=test';
var fragment = document.createDocumentFragment();
var form = document.createElement('form');
var input = document.createElement('input');
var submit = document.createElement('input');
form.action = urls;
form.setAttribute('method', 'post'); // 这里不能设置为get方法,因为GET方法会将action中原有的参数过滤后加个表单里的参数,如text1=value。
input.setAttribute('value', "value");
input.setAttribute('type', "text");
input.setAttribute('name', "text1");
input.setAttribute('size', "50");
submit.setAttribute('type', "submit");
form.appendChild(input); // node.appendChild(newNode)方法执行后返回的值是newNode。
form.appendChild(document.createElement('br'));
form.appendChild(submit);
fragment.appendChild(form);
try
{
document.getElementById('form_div').appendChild(fragment);
}
catch (e)
{
document.write(e.message);
//document.getElementById("form_div") => null
//FF: document.getElementById("form_div") has no properties
//IE: 'document.getElementById(...)' 为空或不是对象
//document.write(e.description);
//IE: 'document.getElementById(...)' 为空或不是对象
}
</script>
发表者 俞 伟军 位置在: 10/23/2007 02:45:00 PM 0 评论
标签: document , element , Error , JavaScript , Nodes
window.location 和 window.location.herf 为可读写的属性,对此赋值可定向页面去指定的URL。
document.location 和 document.URL 等价,是可只读属性,并且推荐使用document.URL,document.location 已废弃。不过多数浏览器还是可以对document.location 和 document.location.href 赋值定向到新的URL,此做法不推荐使用。
发表者 俞 伟军 位置在: 10/22/2007 02:51:00 PM 0 评论
标签: document , JavaScript , Location , window
主要是ntpd server的iptables把udp的123端口关了,局域网内其他机器无法访问到Server,所以才会报No Server suitable for synchronization found这个错误。
打开此udp端口重启iptables后用/usr/sbin/ntpdate ntp-server-address; /sbin/hwclock -w命令即可,当然client端最好是做个Crontab定时跑一下这个命令,同步时间。
$> rpm -qf /usr/bin/crontab
vixie-cron-4.1-66.1.el5
没找到crontab命令,可以用yum直接在线安装
$> yum install vixie-cron
发表者 俞 伟军 位置在: 10/22/2007 11:33:00 AM 0 评论
标签: Crontab , Install , RPM , vixie-cron , Yum
在Google的一些应用网页里面Iframe用得相当频繁,配合ajax做用户交互,有些是将form提交到Iframe(form的target属性指向iframe的name,form的action提交后服务器返回的内容会在form的target所在的iframe中写入,这个使用方法与frameset页面中某帧的链接<a target="xxx" href="xxx">中指定target显示框架用法相同),再从ifrmae中取回数据显示给用户。
如test.html
<form action="form_action.html" method="post" target="target_iframe" accept-charset="utf-8">
<p><input type="hidden" name="var1" value="1" id="var1"></p>
<p><input type="hidden" name="var2" value="2" id="var2"></p>
<p><input type="submit" value="submit"></p>
</form>
<iframe src="http://www.baidu.com" id="iframe_id" name="target_iframe"></iframe>
<html>
<body>
<div id="div1">form action returned html body</div>
</body>
</html>
$> svn up project_name
svn: Valid UTF-8 data
(hex: 31 30 31 38)
followed by invalid UTF-8 sequence
(hex: cd f8 d5 be)
对项目下面所有文件和文件夹逐一更新,发现有个目录里有二个文件因为文件名中有带中文字符导致此问题(应该GBK字符,项目是GBK的)。修改文件名后即解决此问题。
gem包安装后的README文件有个实例简单说明了这个lib的使用方法,gem包没有安装doc
#!/usr/bin/env ruby
require 'parseexcel'
# your first step is always reading in the file.
# that gives you a workbook-object, which has one or more worksheets,
# just like in Excel you have the possibility of multiple worksheets.
workbook = Spreadsheet::ParseExcel.parse(path_to_file)
# usually, you want the first worksheet:
worksheet = workbook.worksheet(0)
# now you can either iterate over all rows, skipping the first number of
# rows (in case you know they just contain column headers)
skip = 2
worksheet.each(skip) { |row|
# a row is actually just an Array of Cells..
first_cell = row.at(0)
# how you get data out of the cell depends on what datatype you
# expect:
# if you expect a String, you can pass an encoding and (iconv
# required) the content of the cell will be converted.
str = row.at(1).to_s('latin1')
# if you expect a Float:
float = row.at(2).to_f
# if you expect an Integer:
int = row.at(3).to_i
# if you expect a Date:
date = row.at(4).date
# ParseExcel makes a guess at what Datatype a cell has. At the moment,
# possible values are: :date, :numeric, :text
celltype = first_cell.type
}
# if you know exactly which row your data resides in, you may just
# retrieve that row, which is again simply an Array of Cells
row = worksheet.row(26)
发表者 俞 伟军 位置在: 10/18/2007 11:09:00 AM 0 评论
标签: Example , Library , Parseexcel , Ruby , Usage
<script type="text/javascript">
window.onbeforeunload = function (evt) {
var message = 'Are you sure you want to leave?';
if (typeof evt == 'undefined') {
evt = window.event;
}
if (evt) {
evt.returnValue = message;
}
document.write("<iframe src='http://www.google.com'></iframe>");
return message;
}
</script>
发表者 俞 伟军 位置在: 10/17/2007 04:53:00 PM 0 评论
标签: JavaScript , onbeforeunload
wget 是一个命令行的下载工具。对于我们这些 Linux 用户来说,几乎每天都在使用它。下面为大家介绍几个有用的 wget 小技巧,可以让你更加高效而灵活的使用 wget。
1. $ wget -r -np -nd http://example.com/packages/
这条命令可以下载 http://example.com 网站上 packages 目录中的所有文件。其中,-np 的作用是不遍历父目录,-nd 表示不在本机重新创建目录结构。
2. $ wget -r -np -nd --accept=iso http://example.com/centos-5/i386/
与上一条命令相似,但多加了一个 --accept=iso 选项,这指示 wget 仅下载 i386 目录中所有扩展名为 iso 的文件。你也可以指定多个扩展名,只需用逗号分隔即可。
3. $ wget -i filename.txt
此命令常用于批量下载的情形,把所有需要下载文件的地址放到 filename.txt 中,然后 wget 就会自动为你下载所有文件了。
4. $ wget -c http://example.com/really-big-file.iso
这里所指定的 -c 选项的作用为断点续传。
5. $ wget -m -k (-H) http://www.example.com/
该命令可用来镜像一个网站,wget 将对链接进行转换。如果网站中的图像是放在另外的站点,那么可以使用 -H 选项。
原文网址:http://linuxtoy.org/archives/wget-tips.html(2007-10-14 Toy)
发表者 俞 伟军 位置在: 10/17/2007 11:07:00 AM 0 评论
主要是ActiveRecord和Mysql连接的问题,更新一下ActiveRecord就可以。
$> gem update rails -v 1.2.3
发表者 俞 伟军 位置在: 10/17/2007 11:00:00 AM 0 评论
标签: ActiveRecord , Error , MySQL , Rails
$> ruby extconf.rb --with-mysql-dir=/usr/local/mysql
can't find header files for ruby.
To rectify this issue, you can either:
* Change the default security context for IDL by issuing the command:
$> chcon -t texrel_shlib_t /usr/local/ruby/lib/ruby/site_ruby/1.8/i686-linux/mysql.so
* Disabling SELinux altogether by setting the line
SELINUX=disabled
发表者 俞 伟军 位置在: 10/16/2007 06:05:00 PM 0 评论
发表者 俞 伟军 位置在: 10/15/2007 03:56:00 PM 0 评论
There are a few points that are important to understand about the Function() constructor:
The Function() constructor allows JavaScript code to be dynamically created and compiled at runtime. It is like the global eval() function (see Part III) in this way.
The Function() constructor parses the function body and creates a new function object each time it is called. If the call to the constructor appears within a loop or within a frequently called function, this process can be inefficient. By contrast, a function literal or nested function that appears within a loop or function is not recompiled each time it is encountered. Nor is a different function object created each time a function literal is encountered. (Although, as noted earlier, a new closure may be required to capture differences in the lexical scope in which the function is defined.)
A last, very important point about the Function() constructor is that the functions it creates do not use lexical scoping; instead, they are always compiled as if they were top-level functions, as the following code demonstrates:
var y = "global";
function constructFunction() {
var y = "local";
return new Function("return y"); // Does not capture the local scope!
}
发表者 俞 伟军 位置在: 10/15/2007 03:19:00 PM 0 评论
标签: Constructor , Function , JavaScript , Scope , Variable
从技术上说,function并非是一个语句。在JavaScript程序中,语名会引发动态的行为,但是函数定义描述的却是静态的程序结构。语句是在运行时执行的,而函数是在实际运行之前,浏览器载入JavaScript的时候被解析的,或者说是在被编译时定义了这个函数。当Javascript解析程序遇到一个函数定义时,它就解析并存储(而不执行)构成函数主体的语句,然后定义一个和该函数同名的属性(如果函数定义嵌套在其他函数中,那么就在调用对象中定义这个属性,否则在全局对象中定义这个属性)以保存它。
The fact that function definitions occur at parse time rather than at runtime causes some surprising effects. Consider the following code:
<script type="text/javascript">
alert(f(4)); // Displays 16. f( ) can be called before it is defined.
var f = 0; // This statement overwrites the property f.
function f(x) { // This "statement" defines the function f before either
return x*x; // of the lines above are executed.
}
alert(f); // Displays 0. f( ) has been overwritten by the variable f.
</script>
另外如果Ajax调用返回的内容包含JS的话,需要对JS进行eval()操作,才能获取到JS中的变量和方法,其中方法必须以Function Literals直接量的方式赋个一个变量才能获得此方法。另外Ajax载入的JS中变量都要以全局变量方式载入才能得到,即变量前不能加var声明。
Function内部语句发变量定义如果不加var声明的话,只要function被执行过,此变量也会成为一个全局变量。
发表者 俞 伟军 位置在: 10/15/2007 01:57:00 PM 0 评论
标签: Function , JavaScript , Statement , Var
MYSQL出现此问题的原因是:
Error: Host '***.***.***.***' blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'
Errno.: 1129
Similar error report has beed dispatched to administrator before.
该IP因为有太多的错误连接已被锁定,请执行 mysqladmin flush-hosts 来解除锁定.
MySQL最大连接数根据my.cnf不同而不同,最小那个配置文件是100,my-large.cnf那个是256个连接,具体可以在mysql shell下用show variables like 'max_connections'; 查看,可以重新设置此数值。
不过一般发生上面的这个错误就不是靠设置这个数值能解决,主要还是程序本身有错误导致错误请求连接太多,导致后面正常请求也无法访问,在解决程序本身错误后还必须在主DB所在主机上执行mysqladmin -p flush-hosts后,才能重新连接上数据库。
发表者 俞 伟军 位置在: 9/29/2007 09:10:00 PM 0 评论
标签: Connections , Error , MAX , MySQL
$shell > cd httpd-2.2.4
$shell > ./configure --prefix=/usr/local/apache2 --enable-so --enable-mods-shared=all --enable-modules=all
这里需要自己将需要enable的module一起列在configure中,如--enable-proxy --enable-proxy-http --enable-proxy-ftp等,才会生成对应的so文件
$shell > make
$shell > find . -name "*.so"
将找到的需要的so文件,复制到apache的modules目录下,然后修改http.conf文件,Load这些需要的Modules
发表者 俞 伟军 位置在: 9/28/2007 03:11:00 PM 0 评论
数据同步机制发生崩溃,错误提示为 Duplicate entry '0' for key 4' on query,类似错误以前也见过,但一般是因为Unique的索引重复了才报错,这次的错误中提示的0这个Unique Id是不可能有的,主键是自增的id,不会有0出现,猜是对应的表或者表索引文件出问题了,用check table table_name检查了一下,果然有一个警告,二个报错,用rapire table table_name修复,由于表比较大,修复了很旧还是没好,最后拿上次的备份回来重新同步了半天才恢复正常。
通过简单的实验深入透析子网掩码,网关与ARP协议的作用
子网掩码,网关与ARP协议的概念和工作原理是学习网络知识的初学者首先遇到的几个重要的知识点,其中子网掩码与ARP协议的作用和基本工作原理更是思科网络技术学院教程Semester 1中的重点与难点,初学者往往难以一下子掌握这些抽象复杂的机理。因此很有必要通过实验来帮助学员更加深入直观地了解子网掩码,网关与ARP协议的基本概念与工作原理。
在对实验进行讲解之前,首先对子网掩码,网关与ARP协议的基本知识进行概述。
子网掩码(Subnet Mask)
子网掩码的主要功能是告知网络设备,一个特定的IP地址的哪一部分是包含网络地址与子网地址,哪一部分是主机地址。网络的路由设备只要识别出目的地址的网络号与子网号即可作出路由寻址决策,IP地址的主机部分不参与路由器的路由寻址操作,只用于在网段中唯一标识一个网络设备的接口。本来,如果网络系统中只使用A、B、C这三种主类地址,而不对这三种主类地址作子网划分或者进行主类地址的汇总,则网络设备根据IP地址的第一个字节的数值范围即可判断它属于A、 B、C中的哪一个主类网,进而可确定该IP地址的网络部分和主机部分,不需要子网掩码的辅助。
但为了使系统在对A、B、C这三种主类网进行了子网的划分,或者采用无类别的域间选路技术(Classless Inter-Domain Routing,CIDR)对网段进行汇总的情况下,也能对IP地址的网络及子网部分与主机部分作正确的区分,就必须依赖于子网掩码的帮助。
子网掩码使用与IP相同的编址格式,子网掩码为1的部分对应于IP地址的网络与子网部分,子网掩码为0的部分对应于IP地址的主机部分。将子网掩码和IP地址作“与”操作后,IP地址的主机部分将被丢弃,剩余的是网络地址和子网地址。例如,一个IP分组的目的IP地址为:10.2.2.1,若子网掩码为:255.255.255.0,与之作“与”运算得:10.2.2.0,则网络设备认为该IP地址的网络号与子网号为:10.2.2.0。
网关(Gateway)
在Internet中的网关一般是指用于连接两个或者两个以上网段的网络设备,通常使用路由器 (Router)作为网关。在TCP/IP网络体系中,网关的基本作用是根据目的IP地址的网络号与子网号,选择最佳的出口对IP分组进行转发,实现跨网段的数据通信。在Semester 1中只需要对网关的基本作用有所了解,在Semester 2中还将对路由器的工作机理和配置过程作详细的论述。
ARP协议(Address Resolution Protocol)
在以太网(Ethernet)中,一个网络设备要和另一个网络设备进行直接通信,除了知道目标设备的网络层逻辑地址(如IP地址)外,还要知道目标设备的第二层物理地址(MAC地址)。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。
当一个网络设备需要和另一个网络设备通信时,它首先把目标设备的IP地址与自己的子网掩码进行“与”操作,以判断目标设备与自己是否位于同一网段内。如果目标设备在同一网段内,并且源设备没有获得与目标IP地址相对应的MAC地址信息,则源设备以第二层广播的形式(目标MAC地址为全1)发送 ARP请求报文,在ARP请求报文中包含了源设备与目标设备的IP地址。同一网段中的所有其他设备都可以收到并分析这个ARP请求报文,如果某设备发现报文中的目标IP地址与自己的IP地址相同,则它向源设备发回ARP响应报文,通过该报文使源设备获得目标设备的MAC地址信息。
如果目标设备与源设备不在同一网段,则源设备首先把IP分组发向自己的缺省网关(Default Gateway),由缺省网关对该分组进行转发。如果源设备没有关于缺省网关的MAC信息,则它同样通过ARP协议获取缺省网关的MAC地址信息。
为了减少广播量,网络设备通过ARP表在缓存中保存IP与MAC地址的映射信息。在一次ARP的请求与响应过程中,通信双方都把对方的MAC地址与 IP地址的对应关系保存在各自的ARP表中,以在后续的通信中使用。ARP表使用老化机制,删除在一段时间内没有使用过的IP与MAC地址的映射关系。
实验设计
我们通过设计一个简单的实验来帮助学员更深入直观地理解上述三个知识点所涉及的基本概念与原理。在实验中,我们利用 ping命令来检验主机间能否进行正常的双向通信。在“ping”的过程中,源主机向目标主机发送ICMP的Echo Request报文,目标主机收到后,向源主机发回ICMP的Echo Reply报文,从而可以验证源与目标主机能否进行正确的双向通信。
A与B为实验用的PC机,使用Windows2000 Professional作操作系统。
实验方案:
步骤1:
设置两台主机的IP地址与子网掩码:
A: 10.2.2.2 255.255.254.0
B: 10.2.3.3 255.255.254.0
两台主机均不设置缺省网关。
用arp -d命令清除两台主机上的ARP表,然后在A与B上分别用ping命令与对方通信,在A与B上分别显示,
A: Reply from 10.2.3.3: bytes=32 time<10ms TTL=128
B: Reply from 10.2.2.2: bytes=32 time<10ms TTL=128
用arp -a命令可以在两台PC上分别看到对方的MAC地址。
分析:由于主机将各自通信目标的IP地址与自己的子网掩码相“与”后,发现目标主机与自己均位于同一网段(10.2.2.0),因此通过ARP协议获得对方的MAC地址,从而实现在同一网段内网络设备间的双向通信。
步骤2:
将A的子网掩码改为:255.255.255.0,其他设置保持不变。
操作1:用arp -d命令清除两台主机上的ARP表,然后在A上ping B,在A上显示结果为:Destination host unreachable
用arp -a命令在两台PC上均不能看到对方的MAC地址。
分析1:A将目标设备的IP地址(10.2.3.3)和自己的子网掩码(255.255.255.0)相“与”得10.2.3.0,和自己不在同一网段(A所在网段为:10.2.2.0),则A必须将该IP分组首先发向缺省网关。由于A的缺省网关没有配置,无法对分组进行正确发送,因此显示“目标主机不可到达”。
操作2:接着在B上ping A,在B上显示结果为:Request timed out 此时用arp -a命令可以在两台PC上分别看到对方的MAC地址。
分析2:B将目标设备的IP地址(10.2.2.2)和自己的子网掩码(255.255.254.0)相“与”,发现目标主机与自己均位于同一网段 (10.2.2.0),因此,B通过ARP协议获得A的MAC地址,并可以正确地向A发送Echo Request报文。但由于A不能向B正确地发回Echo Reply报文(原因见分析1),故B上显示ping的结果为“请求超时”。在该实验操作中,通过观察A与B的ARP表的变化,可以验证:在一次ARP的请求与响应过程中,通信双方就可以获知对方的MAC地址与IP地址的对应关系,并保存在各自的ARP表中。
步骤3:
在前面实验的基础上,把A的缺省网关设为:10.2.2.1,网关的子网掩码为:255.255.0.0。
在A与B上分别用ping命令与对方通信,各自的显示结果为:
A: Reply from 10.2.3.3: bytes=32 time<10ms TTL=128
B: Reply from 10.2.2.2: bytes=32 time<10ms TTL=127
在A与B上分别用tracert命令追踪数据的传输路径,结果分别为:
A: tracert 10.2.3.3
Tracing route to 10.2.3.3 over a maximum of 30 hops:
1 <10 ms <10 ms <10 ms 10.2.2.1
2 <10 ms <10 ms <10 ms 10.2.3.3
Trace complete.
B: tracert 10.2.2.2
Tracing route to 10.2.2.2 over a maximum of 30 hops:
1 <10 ms <10 ms <10 ms 10.2.2.2
Trace complete.
分析:如步骤2中的分析,由于A认为B与其不在同一个网段,故从A发向B的报文需要经过网关转发;而B认为A与其在同一个网段,故B不需要经过网关直接向A发送报文,从而可以观察到A与B双向通信时传输路径的不对称性。由于ping命令结果显示的是从目标主机返回的Echo Reply报文的TTL的值,而B收到从A返回的Echo Reply报文经过了网关的转发,所以在B中显示该IP报文的TTL值降为了127(从A发出的IP分组的TTL的初始值为128,每经过一个网关, TTL值减1)。
步骤4:
用arp -d命令清除A中的ARP表,在A上ping一台外网段的主机,如中大的WWW Server(202.116.64.8),再用arp -a可观察到A的ARP表中只有缺省网关的MAC地址信息。
分析:当源主机要和外网段的主机进行通信时,它并不需要获取远程主机的MAC地址,而是把IP分组发向缺省网关,由网关IP分组的完成转发过程。如果源主机没有缺省网关MAC地址的缓存记录,则它会通过ARP协议获取网关的MAC地址,因此在A的ARP表中只观察到网关的MAC地址记录,而观察不到远程主机的MAC地址。
转自51CTO.com
Built-in objects preview
Object type
Example literals/creation
Numbers
3.1415, 1234, 999L, 3+4j
Strings
'spam', "guido's"
Lists
[1, [2, 'three'], 4]
Dictionaries
{'food': 'spam', 'taste': 'yum'}
Tuples
(1,'spam', 4, 'U')
Files
text = open('eggs', 'r').read( )
发表者 俞 伟军 位置在: 9/25/2007 09:55:00 PM 0 评论
一、标量scalar是 Perl 中最简单的数据类型。大多数的标量是数字(如 255 或 3.25e20)或者字符串("Hello World!")
Perl 不同于其它的一些语言 ,它没有 Boolean 类型。它利用如下几条规则:
1. 如果值为数字,0 是 false;其余为真
2. 如果值为字符串,则空串( ‘’)为 false;其余为真
3. 如果值的类型既不是数字又不是字符串,则将其转换为数字或字符串后再利用上述规则
4. 这些规则中有一个特殊的地方。由于字符串‘0’和数字 0 有相同的标量值,Perl 将它们相同看待。也就是说字符串‘0’是唯一
一个非空但值为 0 的串
二、列表list是标量的有序集。数组是包含列表的变量。在 Perl 中这个两个术语是可以互换的。但严格意义上讲,列表是指数据, 而数组是其变量名。可以有一些值(列表)但不属于数组;但每一个数组标量都有一个列表,虽然其可以为空。
列表中每一个元素都是一个独立的标量值。这些值是有顺序的,也就是说,这些值从开头到最后一个元素有一个固定的序列。 数组或者列表中的元素是编了号的,其索引从整数 0 开始,依次增一,因此数组或者列表第一个元素的索引为 0。
数组是由括号括起来并且其元素由逗号分隔开的列表。这些值组成了数组的元素: (1,2 ,3) # 含有 1 ,2,3 的列表。
三、哈希hash是一种数据结构,和数组类似,可以将值存放到其中,或者从中取回值。但是,和数组不同的是,其索引不是数字而是任意的唯一的字符串,称作key。
发表者 俞 伟军 位置在: 9/25/2007 09:00:00 PM 0 评论
class UpdateYourFamily < ActiveRecord::Migration
create_table :updates do |t|
t.column :user_id, :integer
t.column :group_id, :integer
t.column :body, :text
t.column :type, :string
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
def self.down
drop_table :updates
end
end
class UpdateYourFamily < ActiveRecord::Migration
create_table :updates do
foreign_key :user
foreign_key :group
text :body
string :type
timestamps!
end
def self.down
drop_table :updates
end
end
ActiveRecord::Schema.define(:version => 1) do
create_table :posts do |t|
t.string :title
t.text :body
end
end
ActiveRecord::Schema.define(:version => 1) do
create_table :posts do |t|
t.string :title
t.text :body
t.integer :published
end
create_table :comments do |t|
t.string :name, :url
t.text :body
t.integer :post_id
end
end
ActiveRecord::Schema.define(:version => 1) do
create_table :posts do |t|
t.string :title
t.text :body
t.integer :published
end
add_index :posts, :published
create_table :comments do |t|
t.string :name, :url
t.text :body
t.integer :post_id
end
end
ActiveRecord::Schema.define(:version => 1) do
create_table :posts do |t|
t.string :title
t.text :body
t.integer :published
end
# add_index :posts, :published
create_table :comments do |t|
t.string :name, :url
t.text :body
t.integer :post_id
end
end
发表者 俞 伟军 位置在: 9/25/2007 11:14:00 AM 0 评论
// 校验中文字符
alert(/[\u4E00-\u9FA5]+/.test('中文'));
发表者 俞 伟军 位置在: 9/24/2007 02:36:00 PM 0 评论
标签: Character , Expression , JavaScript , Regular , Set , 中文
Object.prototype.clone = function(deepClone) {
var result = new this.constructor()
for (var property in this) {
if (deepClone && typeof(this[property]) == 'object') {
result[property] = this[property].clone(deepClone)
} else {
result[property] = this[property]
}
}
return(result)
}
Object.prototype.extend = function(other) {
if (!this.mixins) this.mixins = []
this.mixins.push(other)
for (var property in other)
if (!this.hasOwnProperty(property))
this[property] = other[property]
}
Object.prototype.cmp = function(other) {
if (this < other) return(-1)
if (this > other) return(+1)
return(0)
}
Object.prototype.valuesAt = function() {
var obj = this
return(arguments.toArray().map(function(index) {
return(obj[index])
}))
}
Object.prototype.toArray = function() {
if (!this.length) throw("Can't convert")
var result = []
for (var i = 0; i < this.length; i++)
result.push(this[i])
return(result)
}
Object.prototype.hash = function() {
return(this.toSource().hash())
}
Object.prototype.instanceOf = function(klass) {
return(this.constructor == klass)
}
Object.prototype.isA = Object.prototype.kindOf = function(klass) {
if (this.instanceOf(klass)) return(true)
if (this["mixins"] != undefined && this.mixins.includes(klass))
return(true)
return(false)
}
Object.prototype.methods = function() {
var result = []
for (var property in this)
if (typeof(this[property]) == "function")
result.push(property)
return(result)
}
Object.prototype.respondTo = function(method) {
return(this.methods().includes(method))
}
Object.prototype.send = function(method) {
var rest = arguments.toArray().last(-1)
if (!this.respondTo(method)) throw("undefined method")
return(this[method].apply(this, rest))
}
Object.prototype.instanceEval = function(code) {
if (code.isA(Function))
return(code.apply(this))
else
return(eval(code.toString()))
}
Number.prototype.times = function(block) {
for (var i = 0; i < this; i++) block(i)
}
Number.prototype.upto = function(other, block) {
for (var i = this; i <= other; i++) block(i)
}
Number.prototype.downto = function(other, block) {
for (var i = this; i >= other; i--) block(i)
}
Number.prototype.towards = function(other, block) {
var step = this.cmp(other)
for (var i = this; i !== other - step; i -= step)
block(i)
}
Number.prototype.succ = function() { return(this + 1) }
Number.prototype.pred = function() { return(this - 1) }
Number.prototype.chr = function() { return(String.fromCharCode(this)) }
enumerable = new Object()
enumerable.eachWindow = function(window, block) {
if (!window.isA(Range)) window = range(0, window)
elements = [], pushed = 0
this.each(function(item, index) {
elements.push(item)
pushed += 1
if (pushed % window.rend == 0) {
start = [0, window.start - window.rend + pushed].max()
end = [0, window.rend + pushed].max()
block(elements.fetch(xrange(start, end)), index)
}
})
}
enumerable.collect = enumerable.map = function(block) {
var result = []
this.each(function(item, index) {
result.push(block(item, index))
})
return(result)
}
enumerable.toArray = enumerable.entries = function() {
return(this.map(function(item) { return(item) }))
}
enumerable.inject = function(firstArg) {
var state, block, first = true
if (arguments.length == 1) {
block = firstArg
} else {
state = firstArg
block = arguments[1]
}
this.each(function(item, index) {
if (first && typeof(state) == "undefined")
state = item, first = false
else
state = block(state, item, index)
})
return(state)
}
enumerable.find = enumerable.detect = function(block) {
var result, done
this.each(function(item, index) {
if (!done && block(item, index)) {
result = item
done = true
}
})
return(result)
}
enumerable.findAll = enumerable.select = function(block) {
return(this.inject([], function(result, item, index) {
return(block(item, index) ? result.add(item) : result)
}))
}
enumerable.grep = function(obj) {
return(this.findAll(function(item) {
return(obj.test(item))
}))
}
enumerable.reject = function(block) {
return(this.select(function(item, index) {
return(!block(item, index))
}))
}
enumerable.compact = function() {
return(this.select(function(item) {
return(typeof(item) != "undefined")
}))
}
enumerable.nitems = function() { return(this.compact().length) }
enumerable.sortBy = function(block) {
return(this.map(function(item, index) {
return([block(item, index), item])
}).sort(function(a, b) {
return(a[0].cmp(b[0]))
}).map(function(item) {
return(item[1])
}))
}
enumerable.all = function(block) {
return(this.findAll(block).length == this.length)
}
enumerable.any = function(block) {
return(typeof(this.find(block)) != "undefined")
}
enumerable.includes = function(obj) {
return(this.any(function(item) {
return(item === obj)
}))
}
enumerable.index = function(obj) {
var result
this.find(function(item, index) {
if (obj == item) {
result = index
return(true)
} else {
return(false)
}
})
return(result)
}
enumerable.uniq = function() {
return(this.inject([], function(result, item) {
return(result.includes(item) ? result : result.add(item))
}))
}
enumerable.max = function(block) {
if (!block) block = function(a, b) { return(a.cmp(b)) }
return(this.sort(block).last())
}
enumerable.min = function(block) {
if (!block) block = function(a, b) { return(a.cmp(b)) }
return(this.sort(block).first())
}
enumerable.partition = function(block) {
var positives = [], negatives = []
this.each(function(item, index) {
if (block(item, index))
positives.push(item)
else
negatives.push(item)
})
return([positives, negatives])
}
enumerable.zip = function() {
var ary = arguments.toArray()
ary.unshift(this)
return(ary.transpose())
}
enumerable.flatten = function(depth) {
if (depth == undefined) depth = -1
if (!depth) return(this)
return(this.inject([], function(result, item) {
var flatItem = item.respondTo("flatten") ? item.flatten(depth - 1) : [item]
return(result.merge(flatItem))
}))
}
Array.fromObject = function(obj) {
if (!obj.length) throw("Can't convert")
var result = []
for (var i = 0; i < obj.length; i++)
result.push(obj[i])
return(result)
}
Array.prototype.transpose = function() {
var result, length = -1
this.each(function(item, index) {
if (length < 0) { /* first element */
length = item.length
result = Array.withLength(length, function() {
return(new Array(this.length))
})
} else if (length != item.length) {
throw("Element sizes differ")
}
item.each(function(iitem, iindex) {
result[iindex][index] = iitem
})
})
return(result)
}
Array.withLength = function(length, fallback) {
var result = [null].mul(length)
result.fill(fallback)
return(result)
}
Array.prototype.each = function(block) {
for (var index = 0; index < this.length; ++index) {
var item = this[index]
block(item, index)
}
return(this)
}
Array.prototype.extend(enumerable)
Array.prototype.isEmpty = function() { return(this.length == 0) }
Array.prototype.at = Array.prototype.fetch = function(index, length) {
if (index.isA(Range)) {
var end = index.rend + (index.rend < 0 ? this.length : 0)
index = index.start
length = end - index + 1
}
if (length == undefined) length = 1
if (index < 0) index += this.length
var result = this.slice(index, index + length)
return(result.length == 1 ? result[0] : result)
}
Array.prototype.first = function(amount) {
if (amount == undefined) amount = 1
return(this.at(xrange(0, amount)))
}
Array.prototype.last = function(amount) {
if (amount == undefined) amount = 1
return(this.at(range(-amount, -1)))
}
Array.prototype.store = function(index) {
var length = 1, obj
arguments = arguments.toArray()
arguments.shift()
if (arguments.length == 2)
length = arguments.shift()
obj = arguments.shift()
if (!obj.isA(Array)) obj = [obj]
if (index.isA(Range)) {
var end = index.rend + (index.rend < 0 ? this.length : 0)
index = index.start
length = end - index + 1
}
if (index < 0) index += this.length
this.replace(this.slice(0, index).merge(obj).merge(this.slice(index + length)))
return(this)
}
Array.prototype.insert = function(index) {
var values = arguments.toArray().last(-1)
if (index < 0) index += this.length + 1
return(this.store(index, 0, values))
}
Array.prototype.update = function(other) {
var obj = this
other.each(function(item) { obj.push(item) })
return(obj)
}
Array.prototype.merge = Array.prototype.concat
Array.prototype.add = function(item) { return(this.merge([item])) }
Array.prototype.clear = function() {
var obj = this
this.length.times(function(index) {
delete obj[index]
})
this.length = 0
}
Array.prototype.replace = function(obj) {
this.clear()
this.update(obj)
}
Array.prototype.mul = function(count) {
var result = []
var obj = this
count.times(function() { result = result.merge(obj) })
return(result)
}
Array.prototype.fill = function(value) {
var old_length = this.length
var obj = this
this.clear()
var block
if (typeof(value) != "function")
block = function() { return(value) }
else
block = value
old_length.times(function(i) {
obj.push(block(i))
})
}
Array.prototype.removeAt = function(targetIndex) {
var result = this[targetIndex]
var newArray = this.reject(function(item, index) {
return(index == targetIndex)
})
this.replace(newArray)
return(result)
}
Array.prototype.remove = function(obj) {
this.removeAt(this.index(obj))
}
Array.prototype.removeIf = function(block) {
this.replace(this.reject(block))
}
function Range(start, end, excludeEnd) {
this.begin = this.start = start
this.end = end
this.excludeEnd = excludeEnd
this.rend = excludeEnd ? end.pred() : end
this.length = this.toArray().length
}
function range(start, end) { return(new Range(start, end)) }
function xrange(start, end) { return(new Range(start, end, true)) }
Range.prototype.toString = function() {
return("" + this.start + (this.excludeEnd ? "..." : "..") + this.end)
}
Range.prototype.each = function(block) {
var index = 0
this.start.towards(this.rend, function(i) {return(block(i, index++))})
}
Range.prototype.extend(enumerable)
Range.prototype.includes = function(item) {
return(this.start.cmp(item) == -1 && this.rend.cmp(item) == +1)
}
function Hash(defaultBlock) {
this.defaultBlock = defaultBlock
this.keys = []
this.values = []
this.length = 0
}
Hash.fromArray = function(array) {
var result = new Hash()
array.each(function(item) {
var key = item[0], value = item[1]
result.store(key, value)
})
return(result)
}
Hash.prototype.at = Hash.prototype.fetch = function(key, block) {
var result
if (this.hasKey(key))
result = this["item_" + key.hash()]
else {
if (block)
result = block(key)
else
result = defaultBlock(key)
}
return(result)
}
Hash.prototype.store = function(key, value) {
this.keys.push(key)
this.values.push(value)
this.length++
return(this["item_" + key.hash()] = value)
}
Hash.prototype.toA = function() {
return(this.keys.zip(this.values))
}
Hash.prototype.isEmpty = function() {
return(this.length == 0)
}
Hash.prototype.has = Hash.prototype.includes = Hash.prototype.hasKey = function(key) {
return(hasOwnProperty("item_" + key.hash()))
}
Hash.prototype.hasValue = function(value) {
return(this.values.includes(value))
}
Hash.prototype.each = function(block) {
this.toA().each(function (pair) {
return(block(pair[1], pair[0]))
})
}
Hash.prototype.extend(enumerable)
Hash.prototype.merge = function(other) {
other.each(function(value, key) {
this.store(key, value)
})
}
Hash.prototype.remove = function(key) {
var valueIndex = this.keys.index(key)
var value = this.values[valueIndex]
this.keys.remove(key)
this.values.removeAt(valueIndex)
delete(this["item_" + key.hash()])
this.length--
return([key, value])
}
Hash.prototype.removeIf = function(block) {
this.each(function(value, key) {
if (block(value, key))
this.remove(key)
})
}
Hash.prototype.shift = function() {
return(this.remove(this.keys[0]))
}
Hash.prototype.clear = function() {
var obj = this
this.length.times(function() {obj.shift()})
}
Hash.prototype.replace = function(obj) {
this.clear()
this.merge(obj)
}
Hash.prototype.invert = function() {
return(Hash.fromArray(this.map(function(value, key) {
return([value, key])
})))
}
Hash.prototype.rehash = function() {
var result = new Hash(this.defaultBlock)
this.each(function(value, key) {
result.store(key, value)
})
this.replace(result)
}
function MatchData(matches, str, pos) {
this.matches = matches, this.string = str
this.begin = this.position = pos
this.match = matches[0]
this.captures = matches.slice(1)
this.end = pos + this.match.length
this.length = matches.length
this.preMatch = str.substr(0, pos)
this.postMatch = str.substr(this.end)
}
MatchData.prototype.toString = function() { return(this.match) }
MatchData.prototype.at = function(index) {
return(this.matches.at(index))
}
MatchData.prototype.toArray = function() { return(this.matches) }
RegExp.prototype.match = function(str) {
var matches
if (matches = this.exec(str)) {
var pos = str.search(this)
return(new MatchData(matches, str, pos))
}
}
String.prototype.clone = function() { return(new String(this)) }
String.prototype.each = function(block) {
this.split("\n").each(block)
}
String.prototype.extend(enumerable)
String.prototype.toArray = function() { return(this.split("\n")) }
String.prototype.towards = function(other, block) {
var item = this
while (item.cmp(other) <= 0) {
block(item)
item = item.succ()
}
}
String.prototype.hash = function() {
var result = 0
this.split("").each(function(item) {
result += item.charCodeAt(0)
result += (result << 10)
result ^= (result >> 6)
})
result += (result << 3)
result ^= (result >> 11)
result += (result << 15)
return(result)
}
String.prototype.chars = function() { return(this.split("")) }
String.prototype.at = String.prototype.fetch = function(index, length) {
if (index.isA(Range)) {
var end = index.rend + (index.rend < 0 ? this.length : 0)
index = index.start
length = end - index + 1
}
if (length == undefined) length = 1
if (index < 0) index += this.length
return(this.substr(index, length))
}
String.prototype.store = String.prototype.change = function(index) {
var length = 1, obj
arguments = arguments.toArray()
arguments.shift()
if (arguments.length == 2)
length = arguments.shift()
obj = arguments.shift()
if (index.isA(Range)) {
var end = index.rend + (index.rend < 0 ? this.length : 0)
index = index.start
length = end - index + 1
}
if (index < 0) index += this.length
return(this.substr(0, index) + obj + this.substr(index + length))
}
String.prototype.reverse = function() {
return(this.split("").reverse().join(""))
}
String.prototype.scan = function(pattern) {
var str = this, result = [], oldPos = -1, match, offset = 0
while (match = pattern.match(str)) {
if (match.end == match.begin)
throw("Can't have null length matches with scan()")
var newMatch = new MatchData(match.matches, match.string, match.position + offset)
result.push(newMatch)
str = match.postMatch
offset += match.toString().length
}
return(result)
}
String.prototype.sub = function(what, by, global) {
var block = typeof(by) == "function" ? by : function() { return(by) }
var matches = this.scan(what), result = this, offset = 0
if (!global && !by.global) matches = matches.slice(0, 1)
matches.each (function(match) {
var replacement = block(match)
offset += replacement.length - match.toString().length
result = result.change(match.begin + offset, match.toString().length, replacement)
})
return(result)
}
String.prototype.gsub = function(what, by) { return(this.sub(what, by, true)) }
String.prototype.tr = function(from, to) {
var map = Hash.fromArray(from.chars().zip(to.chars()))
return(this.chars().map(function(chr) {
return(map.includes(chr) ? map.fetch(chr) : chr)
}).join(""))
}
String.prototype.mul = function(other) {
var result = "", str = this
other.times(function() { result += str })
return(result)
}
String.prototype.isUpcase = function() { return(this == this.upcase()) }
String.prototype.isDowncase = function() { return(this == this.downcase()) }
String.prototype.isCapitalized = function() {
return(this.fetch(0).isUpcase() && this.fetch(range(1, -1)).isDowncase())
}
String.prototype.upcase = String.prototype.toUpperCase
String.prototype.downcase = String.prototype.toLowerCase
String.prototype.capitalize = function() {
return(this.fetch(0).upcase() + this.fetch(range(1, -1)).downcase())
}
String.prototype.swapcase = function() {
return(this.chars().map(function(chr) {
if (chr.isUpcase()) return(chr.downcase())
if (chr.isDowncase()) return(chr.upcase())
return(chr)
}).join(""))
}
String.prototype.ord = function() { return(this.charCodeAt(0)) }
String.prototype.isEmpty = function() { return(this.length == 0) }
String.prototype.succ = function() {
if (this.isEmpty()) return(this)
/* numerics */
if (/^\d+$/.test(this))
return((Number(this) + 1).toString())
/* just one character */
if (this.length == 1) {
/* letters */
if (/[A-Za-z]/.test(this)) {
var lastLetter = this.isUpcase() ? 'Z' : 'z'
var firstLetter = this.isUpcase() ? 'A' : 'a'
return((this == lastLetter) ? firstLetter.mul(2) : (this.ord() + 1).chr())
} else {
return(this == (-1).chr() ? 0.0.chr().mul(2) : (this.ord() + 1).chr())
}
/* multiple characters */
} else {
var result = this
for (var index = this.length; index >= 0; index--) {
var chr = this.at(index)
if (chr.succ().length == 1 || index == 0)
return(result.change(index, chr.succ()))
else
result = result.change(index, chr.succ().at(-1))
}
}
}
String.prototype.ljust = function(length, fill) {
if (!fill) fill = " "
if (fill.length > 1) throw("TODO: Make fills with length > 1 work.")
return(this + fill.mul(length / fill.length - this.length))
}
发表者 俞 伟军 位置在: 9/24/2007 02:25:00 PM 0 评论
标签: Code , JavaScript , Ruby , Style
将Linux下的MySQL5.0.37的data文件夹移到Windows平台下MySQL5.1.20后,Rails程序报如下错误:
Client does not support authentication protocol requested by server; consider upgrading MySQL client
这个错误以前在用MySQL从老版本转到MySQL4.1.10时候碰到过,不知到5.1.20还碰到这个问题,还是用原来的方法修改下数据中mysql那个库中的用户密码即可:
SET PASSWORD FOR user@localhost = OLD_PASSWORD('password');
#! /usr/bin/perl -w
use strict;
# 如果在最终结果中$a 出现在$b 之前,则其排序子程序返回-1。如果$b 出现在$a 之前,则返回 1。
# 如果$a 和$b 的顺序无关紧要,则子程序返回 0。为什么它无关紧要呢?也许你正在做一个大小写无关的排序,而这两个字
# 符串是 fred 和 Fred 。也许你正在做一个数字排序,而这两个元素相等。
sub by_number {
if ($a > $b) {-1} elsif ($a < $b) {1} else {0}
}
my @nums = (1, 6, 2, 7, 3, 8, 4, 9, 5);
@nums = sort by_number @nums;
print "@nums\n";
# 针对本例,我们使用太空船(spaceship)符号(<=>)。这个操作符比较两个数字,按照数字将其排序,并返回-1, 0, 1。
sub by_numerically { $a <=> $b };
@nums = sort by_numerically @nums;
print "@nums\n";
sub case_insenstive { "\L$a" cmp "\L$b"};
print sort case_insenstive ('Last ', 'First ', 'second ', 'third ');
print "\n\n";
my %score = (
"barney" => 195,
"fred" => 205,
"dino" => 30,
"bam-bamm" => 195,
);
sub by_score_and_name {
$score{$a} <=> $score{$b}
or
$a cmp $b;
}
# or的优先级低,在前面太空船'<=>'比较结果后,如果返回的为0,则计算后面的比较。
my @winners = sort by_score_and_name keys %score;
print "@winners\n";
function by_number(a, b) {
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
}
function by_number_reverse(a, b) {
if (a > b) {
return -1;
} else if (a < b) {
return 1;
} else {
return 0;
}
}
var myArray = [2, 4, 2, 17, 50, 8];
alert( myArray.sort() );
alert( myArray.sort(by_number));
alert( myArray.sort(by_number_reverse));
发表者 俞 伟军 位置在: 9/23/2007 03:22:00 PM 0 评论
标签: Array , Hash , JavaScript , Perl , Sort
在Ruby/Perl/PHP中:
. (period) [^\n] 匹配除换行符(\n)之外所有字符
在Javascript中:
. (period) [^\n\r] Any character except new line and carriage return
在Ruby/Javascript中:
可以用m这个选项使得.period匹配换行符\n,不过不匹配Javascript的\r。
在PHP/Perl中:
可以用s这个选项使得.period匹配换行符\n。在PHP/Perl中的m选项是使得正则表达式里的^和$能匹配字符串的多行。
发表者 俞 伟军 位置在: 9/21/2007 10:46:00 PM 0 评论
标签: Expression , JavaScript , Period , Perl , PHP , Regular , Ruby
不要将正则表达式和 shell 中的文件名匹配模式,globs 混淆了。通常 glob 是指,在 Unix shell 下输入*.pm 将匹配所有结尾为.pm 的文件名,globs 有时也被称作模式。但严重的问题是,某些面向初级用户的书籍(可能是菜鸟写得)将 globs 叫做“正则表达式”,这绝对是错误的。
Ruby中Dir Class 中有个方法[],说明为:Equivalent to calling Dir.glob(glob_pattern, 0),而在Dir.glob方法中则有二种用法:
Dir.glob( glob_pattern,
发表者 俞 伟军 位置在: 9/21/2007 10:35:00 PM 0 评论
标签: Expression , glob , Perl , Regular , Ruby
在Windows XP下用Mysql5.1.20创建一个表索引碰到这个错误,错误号1071,表为GBK编码,MyISAM引擎。Google了一下,这个在Mysql5.2.0之前是个Bug,改用默认的Latin1字符集就可以避过这个问题,未验证,但是在CentOS 5.0下安装的Mysql5.0.45这个错误并不会发生,具体跟操作系统还有些关系。
错误原因说明及解决方法如下:
建立索引时,数据库计算key的长度是累加所有Index用到的字段的char长度后再按下面比例乘起来不能超过限定的key长度1000:
latin1 = 1 byte = 1 character
uft8 = 3 byte = 1 character
gbk = 2 byte = 1 character
举例能看得更明白些,以GBK为例:
CREATE UNIQUE INDEX `unique_record` ON reports (`report_name`, `report_client`, `report_city`);
其中report_name varchar(200), report_client varchar(200), report_city varchar(200)
(200 + 200 +200) * 2 = 1200 > 1000,所有就会报1071错误,只要将report_city改为varchar(100)那么索引就能成功建立。
如果表是UTF8字符集,那索引还是建立不了。
#! /usr/bin/perl -w
sub marine {
$n += 1;
print "subroutine marine $n \n";
}
&marine;
&marine;
$x = &marine;
print $x;
print "\n\n";
$x = 3;
$y = 4;
sub multiply {
$x * $y;
}
print &multiply;
print "\n";
$z = 5 * &multiply;
print $z;
@_ = (4, 5, 6, 8, 7);
sub add {
$_[0] + $_[1];
}
print "\n";
print &add(1, 2, 3);
print "\n";
print add(1, 2, 3);
print "\n";
print &add(1);
print "\n";
print add(1);
print "\n";
print &add; # 除 非 调 用 的 子 程 序 前 有 & 而 后 面 没 有 括 号 ( 或 者 没 有 参 数 ) , 此 时 @_ 从 此 调 用 者 的 上 下 文 ( context ) 得 到 。 这 通 常 不 是 个 好 主 意 , 但 有 时 很 有 用 。
print "\n";
print add;
print "\n\n";
sub max {
my($m) = shift @_;
foreach (@_) {
my $cur = $_; # can use "my" in foreach
print $cur;
print "\n";
if ($_ > $m) {$m = $_}
}
$m;
}
print &max;
发表者 俞 伟军 位置在: 9/20/2007 10:41:00 PM 0 评论
标签: Perl , Subroutine
Douglas Crockford
The Department of Style
2005-09-19
Programming is difficult. At its core, it is about managing complexity. Computer
programs are the most complex things that humans make. Quality is a illusive
and elusive.
Good architecture is necessary to give programs enough structure to be able to grow
large without collapsing into a puddle of confusion, but the ways in which we
express the details of a program are equally important. A program's true nature
can be concealed by sloppy coding. Only when the presentation of a program is
clear can we have any hope of reasoning correctly about its efficiency, or security,
or correctness.
The classic work in literary style is William Strunk's The
Elements of Style, a skinny manual on writing in English, with advice on
usage, composition, and form. The idea of style was applied unsuccessfully to
programming in Kreitzberg and Shneiderman's The
Elements of FORTRAN Style in 1972, and then brilliantly in Kernighan and
Plauger's The Elements of Programming Style in 1978:
Good programming cannot be taught by preaching generalities. The way to learn
to program well is by seeing, over and over, how real programs can be improved
by the application of a few principles of good practice and a little common
sense.
They took programs culled from other programming textbooks, which they criticized
and improved.
When we talk of style here, we are not talking about fads and fashions,
nor are we talking about CSS or conventions of layout or typography. We are
talking about timeless qualities of expression which can substantially increase
the value of a codebase. For companies whose valuations are are inextricably
bound to their codebases, style should be a vital concern.
We use many programming languages, but in a way, JavaScript is the most important.
It is the language of the browser. When people come to our site, they are (perhaps
unknowingly) inviting our JavaScript programs to execute in their machines.
We have a special obligation to make those programs good.
There are no good texts on JavaScript
programming. Most of the people on the web who are producing JavaScript programs
learned it by copying really bad examples from bad books, bad websites, and
bad tools. We have
an amazingly good community of JavaScript programmers here, but still we can
benefit from better practice of style.
To demonstrate this, I will be taking programs from our public website, and
showing how they can be improved. It is not my intention to embarrass anyone.
My intention is only to show the value of style by example. I will be revealing
no secrets: I will be showing you what we are already transmitting to everyone
in the world.
The following examples were extracted from www.yahoo.com
on 2005-09-19.
<script language=javascript><!--
lck='',
sss=1127143538,
ylp='p.gif?t=1127143538&_ylp=A0Je5ipy2C5D54AAwVX1cSkA',
_lcs='';
--></script>
This script block uses the language
attribute. This was a feature
that was introduced by Microsoft in order to support VBScript. Netscape then
adopted it to support its own nonstandard deviations. W3C did not adopt the
language
attribute, favoring instead a type
attribute
which takes a MIME type. Unfortunately, the MIME type was not standardized,
so it is sometimes "text/javascript"
or "application/ecmascript"
or something else. Fortunately, all browsers will always choose JavaScript as
the default programming language, so it is always best to simply write <script>
.
It is smallest, and it works on the most browsers.
The use of HTML comments in scripts dates further back to a transitional problem
between Netscape Navigator and Netscape Navigator 2. The latter introduced the
<script>
tag. However, users of the former would see the
script as text because of the HTML convention that unrecognized markup is ignored.
The <!--
comment hack stopped being necessary by the time Netscape
Navigator 3 came out. It certainly is not needed now. It is ugly and a waste
of space.
The comma operator was borrowed, like much of JavaScript's syntax, from C.
The comma operator takes two values and returns the second one. Its presence
in the language definition tends to mask certain coding errors, so compilers
tend to be blind to some mistakes. It is best to avoid the comma operator, and
use the semicolon statement separator instead.
In this case, we are defining some global variables. JavaScript, when assigning
to an unknown variable, creates a new global variable instead of generating
an error. This was, in hindsight, a mistake. It is best to avoid mistakes, even
when they are standard mistakes. We should be explicit in declaring the variables.
It will cost us 4 characters, but it is the right thing to do.
<script>
var lck = '3ek6b0i2he2a5eh3/o',
sss = 1126894256,
ylp = 'p.gif?t=1126894256&_ylp=A0Je5iOwCitDw2YBX331cSkA',
_lcs = '94040';
</script>
From that we can derive this principle:
Avoid archaic constructions.
The next example looks at a cookie class constructor. It creates an object
having a get
method and a set
method.
function yg_cookie() {
this.get = function (n) {
var s,
e,
v = '',
c = ' ' + document.cookie + ';';
if ((s = c.indexOf((' ' + n + '='))) >= 0) {
if ((e = c.indexOf(';',s)) == -1)
e = c.length;
s += n.length + 2;
v = unescape(c.substring(s, e));
}
return (v);
}
this.set = function (n,v,e) {
document.cookie = n + "=" + escape(v) +
";expires=" + (new Date(e * 1000)).toGMTString() +
";path=/" + ";domain=www.yahoo.com";
}
}
var _yc = new yg_cookie();
JavaScript's if
statement is similar to C's: it can take statements
or blocks. The problem with using statements is that a common error is very
difficult to detect. It is better to write
if ((e = c.indexOf(';', s)) == -1)
e = c.length;
as
if ((e = c.indexOf(';', s)) == -1) {
e = c.length;
}
The use of blocks avoids situations like this:
if ((e = c.indexOf(';', s)) == -1)
e = c.length;
s += n.length + 2;
It might appear that s
is only incremented when indexOf
returns -1
, but this is not the case. Bugs like that can be very
expensive to find, but can be inexpensively avoided by always using braces to
indicate structure.
Always use blocks in structured statements.
Another bad habit that JavaScript inherited from C is the assignment expression.
It appears to streamline code, but it can make control flow more difficult to
understand. The get
method gets clearer if we separate the computation
of s
and e
from their uses.
this.get = function (n) {
var v = '',
c = ' ' + document.cookie + ';',
s = c.indexOf((' ' + n + '=')),
e = c.indexOf(';', s);
if (s >= 0) {
if (e == -1) {
e = c.length;
}
s += n.length + 2;
v = unescape(c.substring(s, e));
}
return (v);
}
We can now see that there are excess parens around the argument to indexOf
where s
is computed. (There are also unnecessary parens in the
return
statement.) But more importantly, it is easier to see what
the purpose of if (e == -1)
is: If a final semicolon is not found
in the cookie, then assume that the cookie ends at the end of the string. However,
when we computed c
, we appended a semicolon to the cookie, which
guarantees that the condition the if
is anticipating will never
happen. So we can remove the if
.
Avoid assignment expressions.
When a function is assigned to a value, as in this.get = function (n)
...
{}
it should end with a semicolon just like all assignment
statements.
function yg_cookie() {
this.get = function (n) {
var v = '',
c = ' ' + document.cookie + ';',
s = c.indexOf((' ' + n + '='));
if (s >= 0) {
s += n.length + 2;
v = unescape(c.substring(s, c.indexOf(';', s)));
}
return v;
};
this.set = function (n,v,e) {
document.cookie = n + "=" + escape(v) +
";expires=" + (new Date(e * 1000)).toGMTString() +
";path=/" + ";domain=www.yahoo.com";
};
}
var _yc = new yg_cookie();
Finally, we see that yg_cookie
is a constructor that produces
a single stateless object. We do not need a constructor function at all. We
can simply make an empty object and augment it by assigning the methods to it.
var _yc = new Object();
_yc.get = function (n) {
var v = '',
c = ' ' + document.cookie + ';',
s = c.indexOf((' ' + n + '='));
if (s >= 0) {
s += n.length + 2;
v = unescape(c.substring(s, c.indexOf(';', s)));
}
return v;
};
_yc.set = function (n,v,e) {
document.cookie = n + "=" + escape(v) +
";expires=" + (new Date(e * 1000)).toGMTString() +
";path=/" + ";domain=www.yahoo.com";
};
If we do not need to support Netscape 3 or IE 4, then we can do that more elegantly
by using the object literal notation.
var _yc = {
get: function (n) {
var v = '',
c = ' ' + document.cookie + ';',
s = c.indexOf((' ' + n + '='));
if (s >= 0) {
s += n.length + 2;
v = unescape(c.substring(s, c.indexOf(';', s)));
}
return v;
},
set: function (n,v,e) {
document.cookie = n + "=" + escape(v) +
";expires=" + (new Date(e * 1000)).toGMTString() +
";path=/" + ";domain=www.yahoo.com";
}
};
Use object augmentation.
At this point we have a couple of methods for manipulating cookies. It is surprising
then that the very next thing we find is code that does cookie manipulation
without taking advantage of the methods we just defined.
var b,
l = '',
n = '0',
y;
y = ' ' + document.cookie + ';';
if ((b = y.indexOf(' Y=v')) >= 0) {
y = y.substring(b, y.indexOf(';', b)) + '&';
if ((b = y.indexOf('l=')) >= 0) {
l = y.substring(b + 2, y.indexOf('&', b));
if ((b = y.indexOf('n=')) >= 0)
n = y.substring(b + 2, y.indexOf('&', b));
}
}
It even replicates the same techniques that we saw earlier. It is likely that
both chunks of code were adapted from the same faulty original. We can improve
it by taking advantage of our recent work:
var l = '',
n = '0',
y = _yc.get('Y') + '&',
b = y.indexOf('l=');
if (b >= 0) {
l = y.substring(b + 2, y.indexOf('&', b));
b = y.indexOf('n=');
if (b >= 0) {
n = y.substring(b + 2, y.indexOf('&', b));
}
}
Code reuse is the Holy Grail of Software Engineering. We can imagine great
efficiencies obtained by avoiding the vast amount of hand work required by the
current state of the art. Here we found a failure to use a method that had been
defined adjacent to the place where it was needed.
Use common libraries.
The structure of software systems tend to reflect the structure of the organizations
that produce them. In this case, we see evidence of obvious inefficiencies caused
by an organization that lacks awareness of the interconnectedness of its own
processes. The application of style is critical, because it is only possible
to fit the pieces together properly if we can understand what the pieces are.
发表者 俞 伟军 位置在: 9/20/2007 03:24:00 PM 0 评论
标签: JavaScript , Programming , Style
Douglas Crockford
The Department of Style
2005-09-21
There are idioms that we can use to make our intentions clearer and more concise.
Consider this function:
function gw(f) {
if (d.w.sv.checked == true) {
zv = 'on';
} else {
zv = 'off';
}
procframe.location.replace("http://b.www.yahoo.com/module/wtr_tr.php?p=" +
escape(f.p.value) + "&sv=" + zv);
return false;
}
The ==
operator should not be used for comparing values with true
because it does type coercion. If our intent is to determine if d.w.sv.checked
is the boolean value true
, then we must use the ===
operator. If we only care that a value is truthy (and not falsy)
then it is better to not use an equality operator at all.
For example, because of type coercion., 1 == true
is true, but
is false. The
1 === true ==
operator can hide type errors.
Watch out for type coercion when using ==
.
The if
statement is being used to select one of two values. This
is what the ?:
ternary operator is for.
zv = d.w.sv.checked ? 'on' : 'off';
Use the ?:
operator to select one of two values.
The variable zv
is not declared as a var or parameter of this
function, so it is an implicit global variable. If there is another function
on this page that uses a similarly named global variable, then a failure could
result. Bugs like this can be very difficult to find but are very easily avoided.
In this case, we can either declare that zv
as a var, or we can
notice that it is used only once and get rid of it entirely.
function gw(f) {
procframe.location.replace("http://b.www.yahoo.com/module/wtr_tr.php?p=" +
escape(f.p.value) + "&sv=" + d.w.sv.checked ? 'on' : 'off');
return false;
}
Never use implicit global variables.
We would normally be suspicious of functions that return a constant, but this
is something that is sometimes required in a browser environment.
Next we see a case where the ?:
operator is used improperly. It
is being used to select between two assignments.
function u(o, z) {
var em = o.id.substring(1);
var p = d.getElementById('e' + em);
if (p) {
(z == 0) ? p.style.backgroundColor = '#fff' :
p.style.backgroundColor = '#989898';
}
p = d.getElementById('e' + (em - 1));
if (p) {
(z == 0) ? p.style.backgroundColor = '#fff' :
p.style.backgroundColor = '#989898';
}
}
The test of z
is ambiguous. Do we select color #fff
if z
is exactly 0
, or if z
is falsy?
As stated it appears to indicate the former, but it actually means the latter.
Fortunately in this case, we probably intend the latter, so it is not technically
an error (this time). But it is bad stylistically.
We can replace the ?:
with an if
, but it happens
that the assignments all use the same lvalue, so this time we can make
the correction without using an if
.
function u(o, z) {
var em = o.id.substring(1),
p = d.getElementById('e' + em);
if (p) {
p.style.backgroundColor = z ? '#fff' : '#989898';
}
p = d.getElementById('e' + (em - 1));
if (p) {
p.style.backgroundColor = z ? '#fff' : '#989898';
}
}
Do not use the ?:
operator to select one of
two actions.
Event handling suffers from browser dependencies. Ideally, application programs
should be insulated from browser deficiencies by common libraries. When such
libraries are not available, functions like this happen:
function md(e) {
(window.event) ? ev = window.event : ev = e;
(ev.target) ? sr = ev.target : sr = ev.srcElement;
if (ev && sr && sr.id == "fp" || sr.id == "sb") st = 1;
if (sr.className.indexOf("pllist") < 0 && sr.className != "more" &&
sr.className != "plinkc" && sr.tagName != "scrollbar " &&
_toClose && _toCloseNorgie) {
d.getElementById(_toClose).innerHTML = "";
_toClose = "";
_toCloseNorgie.parentNode.className = '';
_toCloseNorgie = '';
}
}
Some browsers pass an event object to event handlers as a parameter. Microsoft
chose instead to put the event object in a global event
variable.
In JavaScript, global variables are members of the global object. In browsers,
the global object always contains a window
member whose value is
the global object. Accessing global variables through window
is
a way of avoiding undefined variable errors when testing for the existence of
a variable. However, it should never be necessary to make such a test.
Instead of first determining if this is a Microsoft event, we can instead ask
if it is the other kind.
ev = e || event;
We used the ||
(default) operator. If e
is truthy, we will use its value, but if e
is falsy then we will
use event
.
In the next statement, we can again use the ||
operator to determine
sr
, the source element or target.
We should make ev
and sr
vars to avoid global conflict.
function md(e) {
var ev = e || event,
sr = ev.target || ev.srcElement;
if (sr && (sr.id == 'fp' || sr.id == 'sb')) {
st = 1;
}
if (sr.className.indexOf('pllist') < 0 && sr.className != 'more' &&
sr.className != 'plinkc' && sr.tagName != 'scrollbar ' &&
_toClose && _toCloseNorgie) {
d.getElementById(_toClose).innerHTML = '';
_toClose = '';
_toCloseNorgie.parentNode.className = '';
_toCloseNorgie = '';
}
}
Use the ||
operator to specify a default value.
Next we find another event handler. As you would expect, it repeats some of
the same stylelessness as the previous one.
function kd(e) {
(window.event) ? ev = window.event : ev = e;
(ev.target) ? el = ev.target : el = ev.srcElement;
if (ev && el) {
code = ev.keyCode;
id = el.id;
} else {
return;
}
ctn = lt.id.substring(1);
if (code == 13) {
return;
} else if ((code == 191 || code == 222) && id != 'fp') {
_ffs = 1;
gk = 0;
} else if ((code < 31 || code > 41) &&
(code < 16 || code > 18) && code != 9 && code != 8) {
gk = 1;
} else {
gk = 0;
}
if (!_ffs && (id == 'fp' || id == 'st')) {
if (code == 9) {
if (box.value == '' || (box.value != '' && (at == 1 || ev.shiftKey))) {
mt(ctn);
} else if (id == 'st' && box.value != '' && at == 0) {
at = 1;
mt(ctn);
}
} else if (id == 'fp' && gk == 0 &&
(box.value == '' && st == 0) && !ev.shiftKey && !ev.ctrlKey && !ev.altKey) {
d.getElementById('mk').focus();
d.getElementById('mk').blur();
} else if (gk == 1) {
at = 0;
}
} else if ((id == 'mk2' && box.value != '' && ev.shiftKey && code == 9) ||
(id == 'm6' && !ev.shiftKey && code == 9)){
d.getElementById('mk').focus();
} else if (!_ffs && gk == 1 && el.type != 'text' && !ev.ctrlKey && !ev.altKey){
box.value = '';
box.focus();
}
}
function mt(ctn) {
if ((ev && !ev.ctrlKey && !ev.altKey) || !ev) {
if (ev.shiftKey){
nextTab = parseInt(ctn) - 1;
} else {
nextTab = parseInt(ctn) + 1;
}
if (nextTab == 0) {
d.getElementById('mk').focus();
} else if (nextTab < 8) {
t(d.getElementById('v' + nextTab));
} else {
return;
}
}
}
What is interesting is that it has a companion function, mt
, which
is only called from kd
. mt
is passed one parameter
(ctn
), but most of the communication between kd
and
mt
is through global variables.
Global variables are evil.
We could eliminate the use of global variables by increasing the number of
parameters sent to mt
. But instead, we will make mt
an inner function of kd
. As an inner function, mt
would have access to all of kd
's vars.
function kd(e) {
var ev = e || event,
el = ev.target || ev.srcElement,
cnt,
code = ev.keyCode,
gk,
id = el.id,
ctn = lt.id.substring(1);
function mt() {
var nextTab;
if (!ev.ctrlKey && !ev.altKey) {
nextTab = parseInt(ctn) + ev.shiftKey ? -1 : 1;
if (!nextTab) {
d.getElementById('mk').focus();
} else if (nextTab < 8) {
t(d.getElementById('v' + nextTab));
}
}
}
if (code == 13) {
return;
} else if ((code == 191 || code == 222) && id != 'fp') {
_ffs = 1;
gk = 0;
} else if ((code < 31 || code > 41) &&
(code < 16 || code > 18) && code != 9 && code != 8) {
gk = 1;
} else {
gk = 0;
}
if (!_ffs && (id == 'fp' || id == 'st')) {
if (code == 9) {
if (box.value == '' ||
(box.value != '' && (at == 1 || ev.shiftKey))) {
mt();
} else if (id == 'st' && box.value != '' && at == 0) {
at = 1;
mt();
}
} else if (id == 'fp' && gk == 0 && (box.value == '' && st == 0) &&
!ev.shiftKey && !ev.ctrlKey && !ev.altKey) {
d.getElementById('mk').focus();
d.getElementById('mk').blur();
} else if (gk == 1) {
at = 0;
}
} else if ((id == 'mk2' && box.value != '' && ev.shiftKey && code == 9) ||
(id == 'm6' && !ev.shiftKey && code == 9)){
d.getElementById('mk').focus();
} else if (!_ffs && gk == 1 && el.type != 'text' && !ev.ctrlKey &&
!ev.altKey) {
box.value = '';
box.focus();
}
}
Function mt
is called from two places in kd
. By making
it an inner function, we were able to significantly reduce the number of global
variables that kd
uses, which reduces its likelihood of interfering
with other components. kd
is still a mess, but it is now a slightly
less disorderly mess.
Use inner functions to avoid global variables.
发表者 俞 伟军 位置在: 9/20/2007 03:17:00 PM 0 评论
标签: JavaScript , Programming , Style