ruby入门看的是经典的《ruby编程语言》,可是那描述的是v1.9的老版本啊!下面聊一下ruby2.x.x的新特性,x是0-n都有可能啊。
1.关键字参数(Keyword arguments)
在1.9的时候为了模拟这个功能,我们需要传递散列:
2.1.3 :044 > def foo(n,others)
2.1.3 :045?> puts n
2.1.3 :046?> puts others[:name]
2.1.3 :047?> puts others[:age]
2.1.3 :048?> end
=> :foo
2.1.3 :049 > foo(11,name:"ks",age:11)
11
ks
11
但是如果要有默认值怎么办?新的关键字参数特性正好满足这些功能:
2.1.3 :050 > def foo(n,name:"noname",age:11)
2.1.3 :051?> puts [n,name,age]
2.1.3 :052?> end
=> :foo
2.1.3 :053 > foo 100
100
noname
11
对于不带默认值的参数如果在调用时省略会抛出异常的:
2.1.3 :054 > def foo(n,name:"noname",age:)
2.1.3 :055?> puts [n,name,age]
2.1.3 :056?> end
=> :foo
2.1.3 :057 > foo 11
ArgumentError: missing keyword: age
from (irb):57
from /Users/apple/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'
2.字符串的#freeze优化
ruby中的字符串总是易变的,就算是内容相同的字符串,这在大量调用时会产生内存的浪费。不过在使用freeze后,会在冻结字符串表中查找字符串,导致内容相同的字符串会重复利用:
2.1.3 :058 > def rept
2.1.3 :059?> "hello"
2.1.3 :060?> end
=> :rept
2.1.3 :061 > 3.times {puts rept.object_id}
70098312821560
70098312821460
70098312821400
=> 3
2.1.3 :062 > def rept;"hello".freeze end
=> :rept
2.1.3 :063 > 3.times {puts rept.object_id}
70098312835340
70098312835340
70098312835340
=> 3
3.def返回方法的名字作为标示符
以前的def定义最后返回的是nil,现在是返回方法名字:
2.1.3 :064 > x=def foo; end
=> :foo
2.1.3 :065 > x
=> :foo
顺面把原来的示例代码贴出来解释下:
module
Around
def
around(method)
prepend(
Module
.
new
do
define_method
(method)
do
|*args, &block|
send(:
"before_#{method}"
)
if
respond_to?(:
"before_#{method}"
,
true
)
result =
super
(*args, &block)
send(:
"after_#{method}"
)
if
respond_to?(:
"after_#{method}"
,
true
)
result
end
end
)
method
end
end
class
Example
extend Around
around
def
call
puts
"call"
end
def
before_call
puts
"before"
end
def
after_call
puts
"after"
end
end
Example.
new
.call
这里用到了Module的prepend方法,和include和extend类似,prepend也是将模块插入到类或其他模块中,它扩展的是实例方法(这点和include更像),不过插入的位置不同:它会插入到被插入类的“前面”而不是“后面”,即是说,如果A中有方法foo,它被prepend到有同名方法的类B中,则A#foo会先调用。如果是include到类B中,则B#foo会先调用。
4 有理数和复数字面量
我们已经有了整数(1) 和浮点数(1.0) 字面量, 现在我们也有有理数(1r)和复数(1i)字面量了。
他们配合Ruby的类型转换机制可以轻松搞定数学操作,好比,一个数学上的有理数1/3可以在Ruby中写作1/3r。3i会生成复数0+3i。这意味着复数可以写成标准的标准的数学符号, 2+3i 生成复数2+3i!
5 数组枚举的#to_h方法
现在数组和任何包含枚举的类都有#to_h方法了,so可以这样写代码:
2.1.3 :067 > [[1,2],[3,4]].to_h
=> {1=>2, 3=>4}
2.1.3 :068 > require "set"
=> true
2.1.3 :069 > Set[[1,2],[3,4]].to_h
=> {1=>2, 3=>4}
6 细粒度方法缓存
Ruby2.1之前使用一个全局方法缓存,当代码中任何一个地方定义一个方法,模块引入,模块对象拓展时,这一全局方法缓存都会失效。这使得一些类--如OpenStruct -- 以及一些技术 -- 如exception
tagging -- 出于性能原因而不可用。
现在不会有这个问题了, Ruby 2.1 使用基于类层次的方法缓存, 只有讨论中的类和任意子类才会失效缓存。
一个已经加入到 RubyVM 类的方法会返回一些方法缓存状态的调试信息。这个直接看示例代码吧:
class Foo end RubyVM.stat #=> {:global_method_state=>133, :global_constant_state=>820, :class_serial=>5689} # setting constant increments :global_constant_state Foo::Bar = "bar" RubyVM.stat(:global_constant_state) #=> 821 # defining instance method increments :class_serial class Foo def foo end end RubyVM.stat(:class_serial) #=> 5690 # defining global method increments :global_method_state def foo end RubyVM.stat(:global_method_state) #=> 134
7 Refinements
refine和using配合使用,它们可以在一个局部的作用域中扩展一个模块或类而不污染全局空间,比如:
2.1.3 :091 > module A
2.1.3 :092?> refine String do
2.1.3 :093 > def sh2
2.1.3 :094?> self*2
2.1.3 :095?> end
2.1.3 :096?> end
2.1.3 :097?> end
=> #<refinement:String@A>
2.1.3 :098 > module B
2.1.3 :099?> using A
2.1.3 :100?> "a".sh2
2.1.3 :101?> end
=> "aa"
2.1.3 :102 > "a".sh2
NoMethodError: undefined method `sh2' for "a":String
from (irb):102
from /Users/apple/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'
2.1.3 :103 > using A
=> main
2.1.3 :104 > "a".sh2
NoMethodError: undefined method `sh2' for "a":String
from (irb):104
from /Users/apple/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'
8 String#scrub
字符串增加scrub方法用来剔除无效的字符,这个可以参考下ri的结果:
= .scrub
(from ruby site)
=== Implementation from String
------------------------------------------------------------------------------
str.scrub -> new_str
str.scrub(repl) -> new_str
str.scrub{|bytes|} -> new_str
------------------------------------------------------------------------------
If the string is invalid byte sequence then replace invalid bytes with given
replacement character, else returns self. If block is given, replace invalid
bytes with returned value of the block.
"abc\u3042\x81".scrub #=> "abc\u3042\uFFFD"
"abc\u3042\x81".scrub("*") #=> "abc\u3042*"
"abc\u3042\xE3\x80".scrub{|bytes| '<'+bytes.unpack('H*')[0]+'>' } #=> "abc\u3042<e380>"
9 $SAFE级别4被移除
设置$SAFE = 4目的是将Ruby放入一个“沙箱”模型并且允许执行不受信任的代码。 然而这并不是十分有效, 因为这需要代码分散到整个Ruby中,并且这几乎从来没有被用到,所以就被移除了。
2.1.3 :114 > $SAFE=4
ArgumentError: $SAFE=4 is obsolete
from (irb):114
from /Users/apple/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'
10 Process.clock_gettime
可以通过以上方法来访问系统的clock_gettime函数,从而获取不同种类的时间值,比如一般系统都支持以下3个:
Process::CLOCK_REALTIME将返回一个unix时间戳。这和Time.now.to_f返回值相同,但是因为它跳过创建时间实例,所以会更快一点。
Process::CLOCK_MONOTONIC用途是访问一个单调时钟,这个时钟总是向前移动,无论系统时钟如何调整。这对关键时序或者基准测试是完美的。
Process::CLOCK_PROCESS_CPUTIME_ID对基准测试是有用的,它的工作方式和单调时钟相似,总是向前移动,只有和另一个cpu time做参考时才有意义,但是它只有在CPU工作的情况下,时间才向前移动。
可以检查Process常量查询是否支持其他时钟,比如我的Mac OS 上有:
2.1.3 :118 > Process.constants
=> [:WNOHANG, :WUNTRACED, :Status, :PRIO_PROCESS, :PRIO_PGRP, :PRIO_USER, :RLIM_SAVED_MAX, :RLIM_INFINITY, :RLIM_SAVED_CUR, :RLIMIT_AS, :RLIMIT_CORE, :RLIMIT_CPU, :RLIMIT_DATA, :RLIMIT_FSIZE, :RLIMIT_MEMLOCK, :RLIMIT_NOFILE, :RLIMIT_NPROC, :RLIMIT_RSS, :RLIMIT_STACK,
:CLOCK_REALTIME, :CLOCK_MONOTONIC, :CLOCK_PROCESS_CPUTIME_ID, :Tms, :UID, :GID, :Sys]
使用很简单:
2.1.3 :121 > Process.clock_gettime(Process::CLOCK_REALTIME)
=> 1417521526.4812741
2.1.3 :122 > start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
=> 30244.170570458
2.1.3 :123 > sleep 1
=> 1
2.1.3 :124 > Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
=> 14.592389030996856
2.1.3 :125 > start = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
=> 0.600038
2.1.3 :126 > sleep 1
=> 1
2.1.3 :127 > Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID) - start
=> 0.01080200000000009
11 升级RubyGems
Ruby自带的RubyGems版本升级到2.2。基础的Gemfile支持已添加对Gemfile.lock的支持,并向着合入所有Bundler功能到RubyGems而努力。
gem install的--file(或者-g)选项不再要求依赖文件的文件名,它将自动检测Gemfile。如果文件不存在,gem install将产生Gemfile.lock,如果文件存在将遵循特定的版本。
1 2 3 4 5 6 7 8 |
|
12 进程标题
添加新的方法Process.setproctitle用于设置进程的标题,不用再设置$0。还增加了相关的方法Process.argv0用于查看$0的初始值,如果$0被设置的话。
2.1.3 :129 > $0
=> "irb"
2.1.3 :130 > $0= "irb_love"
=> "irb_love"
2.1.3 :131 > $0
=> "irb_love"
2.1.3 :132 > Process.argv0
=> "/Users/apple/.rvm/rubies/ruby-2.1.3/bin/irb"
apple@kissAir: ruby_src$ps
PID TTY TIME CMD
1497 ttys000 0:00.11 -bash
501 ttys001 0:00.38 -bash
595 ttys002 0:00.18 -bash
1251 ttys002 0:00.62 irb
633 ttys003 0:00.17 -bash
775 ttys004 0:00.17 -bash
794 ttys005 0:00.16 -bash
856 ttys006 0:00.17 -bash
apple@kissAir: ruby_src$ps
PID TTY TIME CMD
1497 ttys000 0:00.11 -bash
501 ttys001 0:00.38 -bash
595 ttys002 0:00.18 -bash
1251 ttys002 0:00.63 irb_love
633 ttys003 0:00.17 -bash
775 ttys004 0:00.17 -bash
794 ttys005 0:00.16 -bash
856 ttys006 0:00.17 -bash
13 修复了eval作用域解析错误
当eval, instance_eval 或者 module_eval 解析的字符串中含有不带参数的private,protected,public或module_function时,方法的可见作用域变成了它调用处的作用域,如下面这个例子 foo 将会是private的。
1 2 3 4 5 6 7 |
|
这种情况已经在2.1中得到了修正。所以,在这个例子中,foo应该是public的.
14 #untrusted?现在是#tainted?的别名
之前,Ruby有两套方法来标识/检查对象是否是untrusted,第一套是#tainted?,#taint和#untaint,另一套是#untrusted?,#untrust, 和#trust。这些方法行为都一样,但是分别设置了标识,所以一个对象可能是 untrusted,但不是tainted。
这些方法已经被统一成了对单个的标识设值或取值。#tainted?等是推荐的用法,而#untrusted?等会产生警告。
1 2 3 |
|
产生的警告:
1 |
|
不过在2.1.3上测试并没有发现警告
15 Lambda 中的return总是从Lambda返回
Lambdas 不同于内部使用了return的Lambda并从lambda返回的Procs/blocks,它不是封闭方法. 但是有一个例外,如果传给某个方法的lambda带有&并且被yield调用. 这一例外目前已经被移除了。
2.1.3 :155 > def call_lambda
2.1.3 :156?> yield
2.1.3 :157?> end
=> :call_lambda
2.1.3 :158 > def foo
2.1.3 :159?> call_lambda(&lambda {return "from lambda"})
2.1.3 :160?> "from method"
2.1.3 :161?> end
=> :foo
2.1.3 :162 > foo
=> "from method"
上面的例子在Ruby 2.0.0 之前的版本会返回"from lambda"。
16 获取网络接口地址
目前可以通过Socket.getifaddrs获取系统的网络接口详细信息。将返回Socket::Ifaddr对象数组。
2.1.3 :177 > info = Socket.getifaddrs.find do |ifaddr|
2.1.3 :178 > (ifaddr.flags & Socket::IFF_BROADCAST).nonzero? &&
2.1.3 :179 > ifaddr.addr.afamily == Socket::AF_INET
2.1.3 :180?> end
=> #<Socket::Ifaddr en0 UP,BROADCAST,RUNNING,NOTRAILERS,MULTICAST,0x800 192.168.0.3 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=192.168.0.255>
2.1.3 :181 > info.addr.ip_address
=> "192.168.0.3"
17 Queue,SizedQueue和ConditionVariable性能提升
Queue,SizedQueue和ConditionVariable已经在C语言中加速实现。
18 Set
Set 中加入了#intersect? 和 #disjoint? 方法。当接收者和参数两者之间至少有一个共同值的时候#intersect? 会返回true,反之,返回false。#disjoint? 则恰恰相反,如果集合之间没有相同的值,返回true,反之,返回false。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Set的另一个主要的变化是,调用一个 set 的 #to_set 会返回它自身,而不是它的拷贝。
1 2 3 4 5 |
|
19 #include 和 #prepend 现在是public的了
Module 和 Class 中的 #include 和 #prepend 方法现在是public的了
2.1.3 :202 > module M
2.1.3 :203?> def sh
2.1.3 :204?> self*2
2.1.3 :205?> end
2.1.3 :206?> end
=> :sh
2.1.3 :207 > String.include M
=> String
2.1.3 :208 > "a".sh
=> "aa"
2.1.3 :209 > Numeric.include M
=> Numeric
2.1.3 :210 > 1.sh
=> 2
20 Module/Class #singleton_class?
Module 和 Class 中引入了一个#singleton_class? 方法,用来返回接收器是否是一个单类。这个不用解释吧
1 2 3 4 5 6 |
|
21 Method#original_name
Method 和UnboundMethod 中添加了一个#original_name方法,来返回非别名。
1 2 3 4 5 6 7 8 9 10 11 |
|
22 整数/大数 #bit_length
调用integer的#bit_length方法会返回一个代表该数二进制数的位数的数字。
2.1.3 :218 > 15.bit_length
=> 4
2.1.3 :219 > 12**12.bit_length
=> 20736
2.1.3 :220 > 12**120.bit_length
=> 35831808
2.1.3 :221 > 12**1200.bit_length
=> 743008370688
23 pack/unpack 本机字节存储次序 long long
Array#pack和String#unpack中添加了使用Q_/Q!和_/q!指令来处理本机字节存储次序的能力.
1 2 3 4 5 6 7 8 |
|
24 tempfile.create
Tempfile 现在有了一个类似与new的create方法,不同的是,他并不是返回一个当对象被回收后使用finaliser来清理文件的Tempfile实例,而是得到一个块内的普通文件对象,并在块结束时清理该文件。
2.1.3 :225 > require 'tempfile'
=> true
2.1.3 :226 > Tempfile.create("foo") do |f|
2.1.3 :227 > puts f.path
2.1.3 :228?> end
/var/folders/z2/n3vz292s0z7f995w0_bphm780000gn/T/foo20141202-1251-1hghnr0
=> nil
2.1.3 :229 > File.exist? "/var/folders/z2/n3vz292s0z7f995w0_bphm780000gn/T/foo20141202-1251-1hghnr0
2.1.3 :230"> "
=> false
25 curses 库被移除了
curses 已经被从标准库中移除了,现在需要单独下载gem了。
参考地址:http://www.oschina.net/translate/ruby-2-1-in-detail
原文地址:http://globaldev.co.uk/2014/05/ruby-2-1-in-detail/