Ruby Style

# The Ruby Style Guide

> Hey jude, don't make it bad.

> Take a sad song and make it better.

> Remember to let her into your heart,

> Then you can start to make it better.

* [github Ruby style guide](https://github.com/styleguide/ruby)

* [community-driven Ruby style guide](https://github.com/bbatsov/ruby-style-guide)

* [Ruby on Rails 3 Style Guide](https://github.com/bbatsov/rails-style-guide)

* [浠g爜澶у叏](http://book.douban.com/subject/1477390/)

* [浠g爜鏁存磥涔嬮亾](http://book.douban.com/subject/4199741/)

* [閲嶆瀯](http://book.douban.com/subject/1229923/)

## 鎺掔増

* 浣跨敤`UTF-8`缂栫爜

```VimL

set encoding=utf-8

set fileencodings=utf-8,gb2312,gb18030,gbk,ucs-bom,cp936,latin1 " 濡傛灉浣犺鎵撳紑鐨勬枃浠剁紪鐮佷笉鍦ㄦ鍒楋紝閭e氨娣诲姞杩涘幓

set termencoding=utf-8

```

* 浣跨敤绛夊瀛椾綋

```VimL

set guifont=Bitstream\ Vera\ Sans\ Mono\ 12

```

* 浣跨敤涓や釜绌烘牸浣滀负缂╄繘

```Ruby

# good

def some_method

do_something

end

# bad - four spaces

def some_method

do_something

end

```

* 涓嶈鍦ㄧ缉杩涗腑鐣欎笅鍒惰〃绗�(Tab)銆傝缃紪杈戝櫒锛岃嚜鍔ㄥ皢鍒惰〃绗﹀睍寮�涓轰袱涓┖鏍�

```VimL

" 绀轰緥1锛氬叏灞�璁剧疆

set autoindent

set expandtab

set tabstop=2 shiftwidth=2 softtabstop=2

```

```VimL

" 绀轰緥2锛氭瘡璇█鍗曠嫭璁剧疆

autocmd FileType ruby,eruby,yaml set ai ts=2 sw=2 sts=2 et

```

* 姝g‘鎷煎啓鑻辨枃鍗曡瘝

```Ruby

# bad

def has_joind?(talbe_name)

return false unless scoped.respond_to?(:joins_values)

scoped.joins_values.any?{|joins| joins =~ /inner\s+join\s+#{talbe_name}/i }

end

# good

def has_joined?(table_name)

return false unless scoped.respond_to?(:joins_values)

scoped.joins_values.any? { |joins| joins =~ /inner\s+join\s+#{table_name}/i }

end

```

* 灏介噺灏嗚鐨勯暱搴︽帶鍒跺湪80涓瓧绗︿互涓�

```Ruby

# bad

def some_method

a_long_long_long_long_long_long_long_method_call if condition1 && condition2 && condition3

a_long_long_long_long_long_long_long_operation1 && a_long_long_long_long_long_long_long_operation2 && a_long_long_long_long_long_long_long_operation3

end

# good, 浣嗘洿濂界殑鍋氭硶鏄�冭檻閲嶅懡鍚嶆柟娉曟垨灏嗙浉搴旈�昏緫鎶藉彇鎴愬瓙鏂规硶

def some_method

if condition1 && condition2 && condition3

a_long_long_long_long_long_long_long_method_call

end

a_long_long_long_long_long_long_long_operation1 &&

a_long_long_long_long_long_long_long_operation2 &&

a_long_long_long_long_long_long_long_operation3

end

```

* 涓嶈鍦ㄨ灏剧暀涓嬬┖鐧藉瓧绗�

```VimL

set list

set listchars=tab:,.,trail:.,extends:#,nbsp:. " Highlight problematic whitespace

```

* 鍦ㄤ互涓嬩綅缃姞涓婄┖鏍硷細鎿嶄綔绗﹀墠鍚�, 閫楀彿銆佸啋鍙枫�佸垎鍙峰悗锛�'{'鍓嶅悗锛� '}'鍓�

```Ruby

sum = 1 + 2

result = 10 / (1 + 2)

a, b = 1, 2

1 > 2 ? some_operation : other_operation

[1, 2, 3].each { |e| puts e }

hash = { key1: 'luo', key2: 'xin' }

def find(name, options = {})

result

end

```

鍞竴鐨勪緥澶栨槸鎸囨暟鎿嶄綔绗�

```Ruby

# bad

e = M * c ** 2

# good

e = M * c**2

```

* 浠ヤ笅浣嶇疆娌℃湁绌烘牸锛�'(', '['鍚庡拰']', ')'鍓�

```Ruby

some(arg).other

[1, 2, 3].length

```

* 淇濊瘉`when`鍜宍case`鐨勭缉杩涘眰娆$浉鍚�

```Ruby

case

when song.name == 'Misty'

puts 'Not again!'

when song.duration > 120

puts 'Too long!'

when Time.now.hour > 21

puts "It's too late"

else

song.play

end

kind = case year

when 1850..1889 then 'Blues'

when 1890..1909 then 'Ragtime'

when 1910..1929 then 'New Orleans Jazz'

when 1930..1939 then 'Swing'

when 1940..1950 then 'Bebop'

else 'Jazz'

end

```

* 浣跨敤绌鸿鍒嗛殧涓嶅悓鏂规硶鍙婂悓涓�鏂规硶鐨勪笉鍚岄�昏緫娈�

```Ruby

def some_method

data = initialize(options)

first_method_call_with(data)

second_method_call_with(data)

third_method_call_with(data)

data.result

end

def other_method

result

end

```

* 濡傛灉浣跨敤琛ㄨ揪寮忚祴鍊硷紝琛ㄨ揪寮忕殑缂╄繘瑕佷繚鎸佷竴鑷�

```Ruby

# bad

result = if index > 10

do_something

elsif index > 5

do_other_thing

else

puts 'I can not bear it.'

end

# good

result = if index > 10

do_something

elsif index > 5

do_other_thing

else

puts 'I can not bear it.'

end

```

* 澶氳璧嬪�兼椂锛屼负淇濇寔缇庤锛屽敖閲忓皢绛夊彿瀵归綈

```Ruby

# ok

var1 = 1

var12 = 12

var12345 = 12345

var123 = 123

# good

var1 = 1

var12 = 12

var12345 = 12345

var123 = 123

```

* 浣跨敤鍝堝笇璧嬪�兼椂锛屽鏋滃搱甯岄暱搴﹁繃闀匡紝搴旇鐢ㄥ琛岃〃绀猴紝涓斿敖閲忎繚鎸佸榻�

```Ruby

# very bad

result = {:key1=>:value1,:key2=>:value2}

# bad

result = { :this_is_the_first_key => :this_is_the_first_value, :this_is_another_key => :this_is_another_value, :this_is_the_last_key => :this_is_the_last_value }

# good for ruby 1.8

result = { :key1 => :value1, :key2 => :value2 }

result = { :this_is_the_first_key => :this_is_the_first_value,

:this_is_another_key => :this_is_another_value,

:this_is_the_last_key => :this_is_the_last_value }

# good for ruby 1.9

result = { key1: :value1, key2: :value2 }

result = { this_is_the_first_key: :this_is_the_first_value,

this_is_another_key: :this_is_another_value,

this_is_the_last_key: :this_is_the_last_value }

```

## 璇硶

* 瀹氫箟鏂规硶鏃讹紝濡傛灉甯﹀弬瑕佸姞涓婃嫭鍙凤紝鍚﹀垯涓嶈鍔犳嫭鍙�

```Ruby

def some_method

# body omitted

end

def some_method_with_arguments(arg1, arg2)

# body omitted

end

```

* 浣跨敤杩唬鍣紝鑰屼笉瑕佷娇鐢╢or璇彞

```Ruby

# bad

for elem in arr do

puts elem

end

# good

arr.each { |elem| puts elem }

```

* 涓嶈鍦ㄥ琛岀殑if/unless璇彞涓娇鐢╰hen

```Ruby

# bad

if some_condition then

# body omitted

end

# good

if some_condition

# body omitted

end

```

* 涓嶈浣跨敤鍐楅暱鐨勪笁鍏冭〃杈惧紡(?:)銆備絾鍦ㄩ�昏緫杈冪畝鍗曪紝鑳界敤涓�琛岃〃杈剧殑鎯呭喌涓嬶紝浣跨敤涓夊厓琛ㄨ揪寮忚�屼笉鏄痠f/then/else/end璇彞

```Ruby

# bad

result = if some_condition then something else something_else end

# good

result = some_condition ? something : something_else

```

* 涓夊厓琛ㄨ揪寮忎腑涓嶈鍑虹幇閫昏緫鍒嗘敮銆傚湪杩欑鎯呭喌涓嬪簲璇ヤ娇鐢╥f/else璇彞

```Ruby

# bad

some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else

# good

if some_condition

nested_condition ? nested_something : nested_something_else

else

something_else

end

```

* 涓嶈浣跨敤and鍜宱r. 鎬绘槸浣跨敤&&鍜寍|

```Ruby

# bad. 娉ㄦ剰鎿嶄綔绗︾殑浼樺厛绾�

# 绛変环浜�(result = find_by_name(name)) or find_by_code(code) or fail 'wow!'

result = find_by_name(name) or find_by_code(code) or fail 'wow!'

# good

result = find_by_name(name) || find_by_code(code) || fail('wow!')

```

* 涓嶈璺ㄨ浣跨敤涓夊厓琛ㄨ揪寮�, 浣跨敤if/unless璇彞

* 鍦ㄨ鍙ラ潪甯哥畝鐭殑鎯呭喌涓嬩娇鐢ㄥ崟琛屽悗缃甶f/unless璇彞

```Ruby

# bad

if some_condition

do_something

end

# good

do_something if some_condition

```

* 涓嶈浣跨敤unless/else璇彞锛屽皢瀹冩敼鍐欎负if/else璇彞

```Ruby

# bad

unless success?

puts 'failure'

else

puts 'success'

end

# good

if success?

puts 'success'

else

puts 'failure'

end

```

* 涓嶈瀵筰f/unless/while璇彞鐨勪腑鐨勫崟涓潯浠朵娇鐢ㄦ嫭鍙�

```Ruby

# bad

if (x > 10)

# body omitted

end

# good

if x > 10

# body omitted

end

# good, 涓嶇敤鎷彿璇箟浼氭敼鍙樸�傚嵆浣跨敤鎷彿涔熶笉瑕佺敤or

if (x = something) && y

# body omitted

end

```

* 鍗曡block浣跨敤鑺辨嫭鍙穥...}. 澶氳block浣跨敤do...end. 涓嶈浣跨敤do...end杩涜閾惧紡璋冪敤

```Ruby

names = %w(Bozhidar Steve Sarah)

# good

names.each { |name| puts name }

# bad

names.each do |name|

puts name

end

# good

names.select { |name| name.start_with?('S') }.map { |name| name.upcase }

# bad

names.select do |name|

name.start_with?("S")

end.map { |name| name.upcase }

# ok, 浣嗘洿浣崇殑鏂瑰紡鏄妸澶嶆潅閫昏緫鍖呰鎴愭柟娉曡皟鐢�

names.select { |name| name.start_with?('S') && name.end_with?('e') }.

map { |name| name.upcase }

```

* 涓嶈鍦ㄤ笉蹇呰鐨勬儏鍐典笅浣跨敤return

```Ruby

# bad

def some_method(some_arr)

return some_arr.size

end

# good

def some_method(some_arr)

some_arr.size

end

```

* 鏂规硶瀹氫箟鏃讹紝鍦ㄥ畾涔夋湁榛樿鍊肩殑鍙傛暟鏃讹紝绛夊彿鐨勫墠鍚庤鍔犱笂绌烘牸

```Ruby

# bad

def some_method(arg1=:default, arg2=nil, arg3=[])

# do something...

end

# good

def some_method(arg1 = :default, arg2 = nil, arg3 = [])

# do something...

end

```

* 鍙互浣跨敤璧嬪�艰鍙�(=)鐨勮繑鍥炲�硷紝浣嗚娉ㄦ剰浼樺厛绾э紝闇�瑕佹椂浠ユ嫭鍙风幆缁�

```Ruby

# ok

if (v = array.grep(/foo/))

# do something...

end

# good

if v = array.grep(/foo/)

# do something...

end

# also good - 鐢ㄦ嫭鍙蜂繚璇佹搷浣滅浼樺厛绾ф纭�

if (v = self.next_value) == "hello"

# do something...

end

```

* 鍙互浣跨敤||=缁欏彉閲忚祴鍒濆�硷紝浣嗕笉瑕佷娇鐢▅|=缁欏竷灏旂被鍨嬪彉閲忚祴鍒濆�硷紙鍥犱负鍘熷鍊煎彲鑳戒负false锛�

```Ruby

# set name to Bozhidar, only if it's nil or false

name ||= 'Bozhidar'

# bad - would set enabled to true even if it was false

enabled ||= true

# good

enabled = true if enabled.nil?

```

* 鍦ㄧ敤瀹炰緥鍙橀噺瀹炵幇memoize妯″紡鐨勬柟娉曚腑锛屼娇鐢╠efined?鑰屼笉鏄瘄|=

```Ruby

# bad, 褰揹o_something鐨勮繑鍥炲�间负nil鎴杅alse鏃讹紝娌℃湁璧峰埌memoize鐨勬晥鏋�

def some_method

@result ||= do_something

end

# good

def some_method

defined?(@result) ? @result : @result = do_something

end

```

* 涓嶈鍦ㄦ柟娉曞悕鍜屾嫭鍙蜂腑闂村姞绌烘牸

```Ruby

# bad

f (3 + 2) + 1

# bad

def fun (a)

a * 2

end

# good

f(3 + 2) + 1

# good

def fun(a)

a * 2

end

```

* 鐢ㄤ笅鍒掔嚎鍙栦唬鏈娇鐢ㄧ殑block鍙傛暟

```Ruby

# bad

result = hash.map { |k, v| v + 1 }

# good

result = hash.map { |_, v| v + 1 }

```

## 鍛藉悕瑙勫垯

* 鏂规硶鍜屽彉閲忎娇鐢╯nake_case

```Ruby

# bad

def someMethod

myName = 'luoxin'

end

# good

def some_method

my_name = 'luoxin'

end

```

* 绫诲拰妯″潡鐨勫畾涔変娇鐢–amelCase. 娉ㄦ剰淇濇寔棣栧瓧姣嶇缉鍐欒瘝鐨勫師鏈夊舰寮忥紝濡侶TTP, RFC, XML绛�

```Ruby

# bad

module HttpServer

# ...

end

# good

module HTTPServer

# ...

end

```

* 甯搁噺鐨勫畾涔変娇鐢⊿CREAMING_SNAKE_CASE

```Ruby

# bad

class Week

Days_In_A_Week = 7

end

# good

class Week

DAYS_IN_A_WEEK = 7

end

```

* 閰嶇疆鐩稿叧淇℃伅涓嶈浣跨敤甯搁噺

```Ruby

# bad

class JavaService

JAVA_SERVICE_URL = '192.168.8.81'

end

# good

class JavaService

cattr_accessor :url, instance_write: false

self.url = '192.168.8.81'

end

```

* 閫昏緫鍒ゆ柇鐨勬柟娉�(杩斿洖鍊间负甯冨皵鍨�)搴旇浣跨敤褰㈠璇嶆垨鍔ㄨ瘝瀹屾垚鏃舵�侊紝骞朵互闂彿缁撳熬銆備笉瑕佷娇鐢╥s_鍓嶇紑

```Ruby

# bad

def empty

something.count > 0

end

# bad

def is_empty?

something.count > 0

end

# good

def empty?

something.count > 0

end

```

* 浣跨敤鍏锋湁鎻忚堪鎬х殑鏂规硶鍚嶅拰鍙橀噺鍚�

```Ruby

# bad

def gen_ymdhms

# ...

end

# good

def generate_timestamp

# ...

end

# bad

all = deals.map { |x| x.id }

# good

deal_ids = deals.map { |deal| deal.id }

```

## 闆嗗悎

* 褰撳0鏄庡叏鐢卞瓧绗︿覆缁勬垚鐨勬暟缁勬椂锛屼娇鐢�%w()

```Ruby

# bad

STATES = ['draft', 'open', 'closed']

# good

STATES = %w(draft open closed)

```

* 濡傛灉闆嗗悎涓殑鍏冪礌鐨嗕负鍞竴锛屼娇鐢⊿et鑰屼笉鏄疉rray

```Ruby

s = Set.new

s << 1 << 2 << 3 << 2 << 1 #

```

* 灏介噺浣跨敤symbol鑰屼笉鏄痵tring浣滀负鍝堝笇鐨勯敭

```Ruby

# bad

hash = { 'one' => 1, 'two' => 2, 'three' => 3 }

# good for ruby 1.8

hash = { :one => 1, :two => 2, :three => 3 }

# good for ruby 1.9

hash = { one: 1, two: 2, three: 3 }

```

## 瀛楃涓�

* 浣跨敤瀛楃涓叉彃鍏�(interpolation)锛屼笉瑕佷娇鐢ㄥ瓧绗︿覆鍔犳硶

```Ruby

# bad

email_with_name = user.name + ' <' + user.email + '>'

# good

email_with_name = "#{user.name} <#{user.email}>"

```

* 褰撲笉闇�瑕佸瓧绗︿覆鎻掑叆鎴栬�呭鐞哱t, \n绛夌壒娈婂瓧绗︽椂锛屼娇鐢ㄥ崟寮曞彿

```Ruby

# bad

require "active_support"

name = "Bozhidar"

# good

require 'active_support'

name = 'Bozhidar'

```

* 鏋勯�犲瓧绗︿覆鏃朵笉瑕佷娇鐢ㄥ瓧绗︿覆鍔犳硶, 瑕佷娇鐢ㄥ瓧绗︿覆杩炴帴鎿嶄綔绗�

```Ruby

# good and also fast

html = ''

html << 'Page title'

paragraphs.each { |paragraph| html << "#{paragraph}" }

```

## 鐗规畩鏋勯�犵

* 浣跨敤%w鏋勯�犲瓧绗︿覆鏁扮粍

```Ruby

STATES = %w(draft open closed)

```

* 浣跨敤%()鏋勯�犲崟琛岀殑銆佸寘鍚弻寮曞彿鍜屾彃鍏ョ殑瀛楃涓�

```Ruby

# bad (no interpolation needed)

%(Some text)

# should be 'Some text'

# bad (no double-quotes)

%(This is #{quality} style)

# should be "This is #{quality} style"

# bad (multiple lines)

%(\n#{exclamation}\n)

# should be a heredoc

# bad (no double-quotes)

%(#{name})

# should be "#{name}"

# good (requires interpolation, has double quotes, single line)

%(#{name})

```

* 瀵瑰琛岀殑瀛楃涓蹭娇鐢╤eredoc

```Ruby

# bad (multiple lines)

%(\n#{exclamation}\n)

# should be a heredoc

# good

<<-HTML

#{exclamation}

HTML

```

* 鍦ㄦ鍒欒〃杈惧紡涓寘鍚涓�'/'鏃朵娇鐢�%r

```Ruby

# bad

%r(\s+)

# still bad

%r(^/(.*)$)

# should be /^\/(.*)$/

# good

%r(^/blog/2011/(.*)$)

```

* 涓嶈浣跨敤%q, %Q, %x, %s, and %W. 浣跨敤%鏋勯�犵鏃讹紝棣栭��()浣滀负鍒嗗壊绗�

```Ruby

# bad

name = %q(I'm luoxin)

# should be "I'm luoxin" or 'I\'m luoxin'

# bad

div = %Q()

# should be %()

# bad

div = %Q[div id="#@div_id" name="#@div_name">]

# should be %()

# good

js = %[alert("Hi, #@user_name");]

```

## 鍏朵粬

* 浠g爜涓祵鍏QL璇彞鏃讹紝SQL鍏抽敭瀛楀ぇ鍐欙紝鍏朵粬瀛楃灏忓啓

```Ruby

# bad

User.find_by_sql('select id, name from users where id < 5')

# good

User.find_by_sql('SELECT id, name FROM users WHERE id < 5')

```

* 濡傛灉浠g爜涓祵鍏ョ殑SQL璇彞杩囬暱锛屼娇鐢ㄦ崲琛屾潵鍒掑垎閫昏緫

```Ruby

# bad, too long

ActiveRecord::Base.connection.execute('INSERT INTO hui800.deals VALUES (id, code, title, start_time, end_time) SELECT id, code, name, start_date, end_date FROM coupons.deal')

# good

ActiveRecord::Base.connection.execute <<-SQL

INSERT INTO hui800.deals

VALUES (id, code, title, start_time, end_time)

SELECT id, code, name, start_date, end_date

FROM coupons.deal

SQL

```

* 涓嶈鍑虹幇N+1鏌ヨ

```Ruby

# bad, N+1

users = User.where('id < 100')

users.each { |u| puts u.school }

# good

users = User.where('id < 100').includes(:school)

users.each { |u| puts u.school }

```

* 鏂规硶鍙傛暟鐨勪釜鏁板敖閲忎笉瓒呰繃涓や釜锛屾渶澶氫笉瓒呰繃涓変釜(*args闄ゅ)銆傚鏋滄湁澶氶�夐」锛屼竴鑸�氳繃鏈�鏈殑鍝堝笇鍙傛暟鏉ヤ紶閫�

```Ruby

# bad

def my_cache(key, city_id = nil, page = nil, per_page = nil, order_by = nil)

end

# good

def my_cache(key, options = {})

end

```

* 鍦≧uby 1.9涓娇鐢ㄦ柊鐨勫搱甯岃娉�

```Ruby

# bad for Ruby 1.9

hash = { :a => 1, :b => 2, :c => 3 }

# good for Ruby 1.9

hash = { a: 1, b: 2, c: 3 }

```

* 鍦≧uby 1.9涓娇鐢ㄦ柊鐨刲ambda璇硶

```Ruby

# bad for Ruby 1.9

my_lambda = lambda { |x| puts x }

my_lambda.call(3)

# good for Ruby 1.9

my_lambda = ->(x) { puts x }

my_lambda.(3)

```

* 鍦ㄤ笉鏈熸湜淇敼鐨勫璞′笂璋冪敤freeze鏂规硶

```Ruby

# bad

configuration = { database: 'tuan800', adapter: 'mysql2' }

# good

configuration = { database: 'tuan800', adapter: 'mysql2' }.freeze

```

* 鐔熺粌浣跨敤涓�绉嶇紪杈戝櫒銆傛帹鑽愪娇鐢╒im鎴朎macs

## 鍘熷垯

1. Consistency(涓�鑷存��)

2. KISS(绠�娲佹��)

3. Common sense

4. Follow your heart

> 鎬濇兂銆佽瑷�銆佹枃瀛楋紝鏄竴浣撶殑锛屽亣濡傚康璧锋潵涔辩碂绯燂紝鎰忔�濅篃涓�

> 浼氬ソ鈥斺�旇繖鏄渶绠�鍗曠殑鐪熺悊锛屼絾鍋囧娌℃湁鍓嶈緢鏉ュ憡璇夋垜锛屾垜鎬庝箞

> 浼氱煡閬撳晩銆傛湁鏃舵垜涔熷啓鐐逛笉璐熻矗浠荤殑绮楃硻鏂囧瓧锛屼互鍚庨噸璇绘椂锛�

> 鎯劎寰楁棤鍦拌嚜瀹癸紝鐪熸兂鑷繁鑴变簡瑁ゅ瓙璇烽亾涔惧厛鐢熸墦鎴戜袱妫嶃�傚瓱

> 瀛愭浘璇达紝鏃犺�讳箣鑰伙紝鏃犺�荤煟銆傜幇鍦ㄦ垜鍦ㄦ枃瀛︿笂鏄釜鏈夊粔鑰荤殑浜猴紝

> 閮芥槸澶氫簭浜嗚繖浜涘厛鐢熺殑鏁欒銆傚鎴戞潵璇达紝浠栦滑鐨勪綔鍝佹槸姣旈灜瀛�

> 杩樻湁鍔涢噺鐨勯灜绛栥��

> 鎴戜竴鐩存兂鎵胯鎴戠殑鏂囧甯堟壙鏄繖鏍蜂竴鏉¢矞涓轰汉鐭ョ殑绾跨储銆傝繖鏄�

> 缁欐垜鑴镐笂璐撮噾銆傛垜鏈�缁堝啓鍑轰簡杩欎簺锛屼笉鏄洜涓烘垜鐨勪功宸茬粡鍐欏緱

> 濂戒簡锛岃�屾槸鍥犱负锛屼笉鎶婅繖涓瀵嗚鍑烘潵锛屽鐜板湪鐨勫勾杞讳汉鏄笉

> 鍏亾鐨勩�傛病鏈変汉鍛婅瘔浠栦滑杩欎簺锛屽彧鎸夊悕澹版潵鐞嗚В鏂囧锛屽氨浼氫笉

> 鐭ラ亾浠�涔堟槸鍧忥紝浠�涔堟槸濂姐��

> --鐜嬪皬娉€�婃垜鐨勫笀鎵裤��

你可能感兴趣的:(Ruby Style)