如何設計出漂亮的 Ruby APIs

Python014

如何設計出漂亮的 Ruby APIs,第1张

1.Argument Processing

Ruby 使用了 Symbols 和 Hash 来达到虚拟关键字参数(Pseudo-Keyword Arguments)。这种技巧被广泛应用在 Ruby 的函式库和 Rails 中,增加了阅读性,也很容易使用。

def blah(options)

puts options[:foo]

puts options[:bar]

end

blah(:foo =>"test", :bar =>"test")

Ruby 也可以将参数列当成阵列使用:

def sum(*args)

puts args[0]

puts args[1]

puts args[2]

puts args[3]

end

sum(1,2,3)

如此就可以设计出不固定参数列、十分弹性的 API。类似於 C++ 的 function overloading。在 Rails 中也十分常见这样的 API 设计,例如 link_to 就支援了两种用法:

# USAGE-1 without block

<% link_to 'Posts list', posts_path, :class =>'posts' %>

# USAGE-2 with block

<% link_to posts_path, :class =>'posts' do %>

Posts list

<% end %>

搭配虚拟关键字参数使用的话,可以参考 ActiveSupport#extract_options! 这个小技巧取出 Hash 值。

2. Code Blocks

程式区块(Block)是 Ruby 最重要的特色,除了拿来做迭代(Iteration)之外,也可以包装前後置处理(pre- and Post-processing),一个最基本的例子就是开档了,一般程序式的写法如下:

f = File.open("myfile.txt", 'w')

f.write("Lorem ipsum dolor sit amet")

f.write("Lorem ipsum dolor sit amet")

f.close

使用 Block 之後,我们可以将 f.close 包装起来,不需要明确呼叫。只要程式区块结束,Ruby 就会自动关档。程式一来因为缩排变得有结构,二来也确定档案一定会关闭(不然就语法错误了)

# using block

File.open("myfile.txt", 'w') do |f|

f.write("Lorem ipsum dolor sit amet")

f.write("Lorem ipsum dolor sit amet")

end

另一个程式区块的技法,是用来当做回呼(Dynamic Callbacks)。在 Ruby 中,程式区块也是物件,於是我们可以将程式区块如透过”注册”的方式先储存下来,之後再依照需求找出来执行。例如在 Sinatra 程式中:

get '/posts' do

#.. show something ..

end

post '/posts' do

#.. create something ..

end

我们”注册”了两个回呼:一是当浏览器送出 GET ‘/posts’ 时,会执行 show something 的程式区块,二是 POST ‘/posts’ 时。

3. Module

模组(Module)是 Ruby 用来解决多重继承问题的设计。其中有一招 Dual interface 值得一提:

module Logger

extend self

def log(message)

$stdout.puts "#{message} at #{Time.now}"

end

end

Logger.log("test") # as Logger’s class method

class MyClass

include Logger

end

MyClass.new.log("test") # as MyClass’s instance method

Ruby 的 extend 作用是将模组混入(mix-in)进单件类别(singleton class),於是 log 这个方法除了可以像一般的模组被混入 MyClass 中使用,也可以直接用 Logger.log 呼叫。

要将 Ruby 模组的混入成类别方法(class method),也有一些常见的 pattern 模式,可以将模组设计可以同时混入实例方法(instance method)和类别方法,请参阅投影片范例。这在撰写 Rails plugin 时非常常用。

4. method_missing?

Ruby 的 Missing 方法是当你呼叫一个不存在的方法时,Ruby 仍然有办法处理。它会改呼叫 method_missing 这个方法,并把这个不存在的方法名称传进去当做参数。这个技巧在 Rails 的 ActiveRecord 中拿来使用:

class Person <ActiveRecord::Base

end

p1 = Person.find_by_name("ihower")

p2 = Person.find_by_name_and_email("ihower", "[email protected]")

其中 find_by_name 和 find_by_email 就是这样的方法。不过这个技巧不是万能丹,它的执行效率并不好,所以只适合用在你没办法预先知道方法名称的情况下。不过也不是没有补救之道,如果同样的方法还会继续呼叫到,你可以在 method_missing 之中用 define_method 或 class_eval 动态定义此方法,那麼下次呼叫就不会进来 method_missing,进而获得效能的改善。事实上,ActiveRecord::Base 的 method_missing 就是这麼做的。(感谢 BigCat 留言提醒我有此补救之道)

另一个 Missing 方法的绝妙 API 设计,是拿来构建 XML 文件:

builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)

builder.person do |b|

b.name("Jim")

b.phone("555-1234")

b.address("Taipei, Taiwan")

end

# <person>

# <name>Jim</name>

# <phone>555-1234</phone>

# <address>Taipei, Taiwan</address>

# </person>

搭配了区块功能,就能用 Ruby 语法来写 XML,非常厉害。

5. const_missing

除了 method_missing,Ruby 也有 const_missing。顾名思义就是找不到此常数时,会呼叫一个叫做 const_missing 的方法。现实中的例子有 Rails 的 ActiveSupport::Dependencies,它帮助我们不需要先载入所有类别档案,而是当 Rails 碰到一个还不认识的常数时,它会自动根据惯例,找到该档案载入。

我们也可以利用这个技巧,针对特定的常数规则来处理。例如以下的程式会自动将 U 开头的常数,自动转译成 Unicode 码:

class Module

original_c_m = instance_method(:const_missing)

define_method(:const_missing) do |name|

if name.to_s =~ /^U([0-9a-fA-F]{4})$/

[$1.to_i(16)].pack("U*")

else

original_c_m.bind(self).call(name)

end

end

end

puts U0123 # ģ

puts U9999 # 香

6. Methods chaining

方法串接是一个很常见的 API 设计,透过将方法的回传值设成 self,我们就可以串接起来。例如:

[1,1,2,3,3,4,5].uniq!.reject!{ |i| i%2 == 0 }.reverse

# 5,3,1

7. Core extension

Ruby 的类别是开放的,可以随时打开它新增一点程式或是修改。即使是核心类别如 Fixnum 或是 Object(这是所有类别的父类别) 都一样。例如 Rails 就定义了一些时间方法在 Fixnum 里:

class Fixnum

def hours

self * 3600 # 一小时有多少秒

end

alias hour hours

end

Time.now + 14.hours

Ruby 的物件模型与元编程(Meta-programming)

在 Ruby 中,所有东西都是物件。甚至包括类别(class)本身也是物件。这个类别物件(class object)是一个叫做 Class 的类别所实例出来的物件。而所有的物件(当然也包括类别物件),都有一个 metaclass (又叫做 singleton, eigenclass, ghost class, virtual class 等名字)。定义在 metaclass 里的方法,只有该物件能够使用,也就是 singleton method (单件方法),只有该物件才有的方法。

了解什麼是 metaclass 是 Ruby 元编程的一个重要前提知识。Ruby 元编程最常用的用途,就是因应需求可以动态地定义方法,例如在 Rails ActiveRecord 中常见的 Class Macro 应用。

要能随心所欲动态定义方法的关键重点,就是 variable scope (变数的作用域) 了。例如以下我们透过 class_eval 和 define_method 帮 String 定义了一个 say 方法,注意到整个 variable scope 都是通透的,没有建立新的 scope:

name = "say"

var = "it’s awesome"

String.class_eval do

define_method(name) do

puts var

end

end

"ihower".say # it’s awesome

class_eval 可以让我们改变 method definition 区域(又叫做 current class)。除了本投影片,建议可以阅读 Metaprogramming in Ruby: It’s Allhe Self 和 Three implicit contexts in Ruby 这两篇文章深入了解 self 和 current class。

8. Class Macro (Ruby’s declarative style)

Class Macro 是 Ruby Meta-programming 非常重要的一个应用,例如在 Rails ActiveRecord 中:

class User <ActiveRecord::Base

validates_presence_of :login

validates_length_of :login,:within =>3..40

validates_presence_of :email

belongs_to :group

has_many :posts

end

mac ladybug和ruby woo其实都是很好看的颜色,虽然说ruby woo的名气更大一些,但是却不得不说这款番茄色的mac ladybug也是不得不收的色号

mac ladybug和ruby woo哪个好看

ruby woo是正红色,可以说女生不能没有一款正红色的口红,但是如果你不是浓妆艳抹,只是日常淡妆或者是根本不化妆的话,相信我,很多人在素颜淡妆的情况下根本就驾驭不住正红色,这个时候ladybug就派上用场了,它不是那么清淡的颜色,属于番茄红,有一点点橘调,非常显白的颜色,涂上去感觉气色会好很多,是很日常很实用的颜色,其实这两个颜色都很好看,另外要提醒的是ladybug是滋润款式,所以不持久,但是不容易起皮或者卡唇纹,更好涂抹。大家可以根据需要选择。两者我更喜欢lady bug

下面来看看试色对比

ruby woo是典型的正红色

mac ladybug好看吗

一直都是mac的脑残粉,我认为mac的口红就是平价口红中的佼佼者,可以代替很多大牌口红的色号!这个色号非常漂亮!首先它很润,所以特别好上色!不足的就是上色度不迟久,mac亚光款的上色度倒是蛮持久,但会比较干,这或许就是鱼和熊掌不可兼得吧!但是,还是很满意,因为这个色号完全不挑皮肤,薄涂有点像水红色适合裸妆很少女,厚涂深深的番茄红,像深红色,很大牌,很有范!所以,这款口红不管是平时喜欢裸妆少女系的你,还是喜欢浓妆大牌范的你,都是完美之选!

包装很漂亮,很有设计感,这款口红竟然是礼盒包装,所以不管是自用还是送人,都很适合!

子弹头的设计,可爱乖巧,类似磨砂的材质很大牌!

口红超漂亮,看实物像深红、也像铁锈红

薄涂有点水红的感觉,厚涂就是番茄的颜色!大概是拍摄原因,有点色差

上妆很漂亮,不用涂唇膏也不会觉得干!

优点:颜色漂亮,易上妆!保湿,不拔干!日常补妆,都不需要涂唇膏,直接涂完全不显唇纹

缺点:持久度略差,易沾杯!要经常补色!我觉得这款颜色最好出款亚光的,因为不同的人有不同的需求,有些人喜欢这个颜色,但因为不是哑光而放弃!可能,要想做到让它持久度好,就必须舍弃它的保湿度,和光泽度!总之多一种选择,就完美了!

连写好看又好听的英文名如下:

(Phoebe)菲碧

音标为[ˈfibi]简单、易读,使孩子易记。本身带有“光明的”寓意内涵,在希腊神话中还带有“纯净、明亮”的含义,用作女孩英文名字即好看又好听外还带有正直、光明、单纯的寓意。

(Meroy)玛希

来源于英国,带有“同情、慈悲”的寓意内涵,用作女孩英文名字寓意着善良、懂事,适合女孩子起该英文名,并且从英文名整体查看,易写、易记,满足大家对于好看的查找。

(Myra)麦拉

含义为“宁静之歌”,寓意着女孩子安静,多才多艺,平易近人,有艺术细胞等内涵。并且发音为[ˈmaɪrə]好听、时尚,字母组成好看、易写。温馨提示:大家可以通过起名网“起英文名”进行免费的起英文名参考!

(Janet)珍尼特

在英语中带有“上帝是慈悲的”的寓意内涵,引申为女孩子吉祥、幸福、安康之义。发音为['dʒænit]好听洋气,从英文名字组成部分来看简单、易写,满足大家对于好看的查找。

(Ruby)露比

带有“红宝石”的寓意内涵,象征着稀有,以此延伸吉祥的寓意内涵。发音[ˈrubi]简单、易读,音调朗朗上口、悦耳动听。并且从字母搭配来说,该英文名字母搭配合理,使其英文名整体好看。

(Gill)希尔

带有“少女”的寓意内涵,以此延伸女孩子永远青春靓丽,美丽动人之义,寓意非常美好、吉祥。音标为[ɡɪl]好听、易记、易读,满足大家对于好看又好听的查找。