最近參加實習了,公司的技術棧中需要用到 Ruby 以及 Rails 框架,所以算是開始了踩坑之旅吧..
Ruby 簡介
網上的簡介要搜都能搜到,具體涉及的包括歷史啦之類這里不再贅述,提幾個關鍵詞吧:
- 1993 年由日本的松本行弘創建
- 純粹面相對象編程/ 腳本語言/ 解釋型/ 動態類型
對於准備邁入 Ruby 的 Java 程序員來說,有幾個地方需要特別的去了解一下。
- 純粹面相對象
其實經過論證,Java 同 Ruby 一樣都是純粹的面相對象的語言,這也就意味着包含所有的數字等在內都是對象,注意所有的都是。 - 腳本語言
這意味着你寫的程序不用編譯就能運行,甚至實時生效。 - 解釋型
同 Java 一樣,Ruby 有自己的虛擬機,運行需要一定的環境,也就是 Ruby 解釋器,它會負責把 Ruby 翻譯成及其能夠執行的代碼。 - 動態類型
Ruby 中的數據更像是一種符號,在使用的時候不檢查類型,而是在運行時動態的檢查。
為什么是 Ruby ?
- 原因很簡單:高效/ 靈活/ 優雅/ 簡單
如果你再稍微花一些心思搜索一下 Ruby on Rails 這個 Web 開發框架,並且打開一些詳細說明了體驗之后的文章或者是多年經驗開發者的分享,你可能會對它產生一些興趣,這一Part就留着之后介紹了,這也是為以后學習 RoR 框架做准備的。
總之我們要明確我們目的:知道最基本的語法,理解其中的一些關鍵概念即可。
Ruby 初體驗
Mac OX 中有默認的 Ruby 環境,我們可以來一個最短的 "Hello World" 程序,首先在控制台中輸入 irb
命令,然后輸入 puts "Hello World!"
命令:
irb
irb(main):001:0> puts "Hello World!"
Hello World!
=> nil
你就能看到緊跟着你的輸入會有一個 Hello World!
的輸出以及一個 nil
(對應 Java 中的 null
)的返回。
再來一個更加復雜的例子,我們這一次來創建一個數組然后循環輸出它:
irb(main):002:0> properties = ['name','age','sex']
=> ["name", "age", "sex"]
irb(main):003:0> properties
=> ["name", "age", "sex"]
irb(main):005:0> properties.each {|property| puts "This is #{property}."}
This is name.
This is age.
This is sex.
=> ["name", "age", "sex"]
不知道感覺如何?至少我們可以直觀的感受到:
- 不用生命變量,直接
=
就好 - 每條 Ruby 代碼都會返回某個值
從 Java 到 Ruby
Java 非常成熟,並且通過 Spring 的加持得到了許多企業的青睞,但是不知道大家有沒有感受到一點:它或許有些啰嗦...(我亂說的啊,我也不知道,別問我啊..)從 Java 到 Ruby 據說可以預見性的將代碼的規模量大大縮小,因此也能使用更少的時間來輸出產品原型。
相似點
Ruby 與 Java 有一些相似的地方...
- 垃圾回收器幫你管理內存。
- 強類型對象。
- 有 public、 private 和 protected 方法。
- 擁有嵌入式文檔工具(Ruby 的工具叫 rdoc)。rdoc 生成的文檔與 javadoc 非常相似。
不同點
Ruby 與 Java 不同的地方...
- 你不需要編譯你的代碼。你只需要直接運行它。
- 有幾個不同的流行的第三方GUI工具包。Ruby 用戶可以嘗試 WxRuby、 FXRuby、 Ruby-GNOME2、 Qt 或 Ruby 內置的 Tk。
- 定義像類這樣的東西時,可以使用
end
關鍵字,而不使用花括號包裹代碼塊。 - 使用
require
代替import
。 - 所有成員變量為私有。在外部,使用方法獲取所有你需要的一切。
- 方法調用的括號通常是可選的,經常被省略。
- 一切皆對象,包括像 2 和 3.14159 這樣的數字。
- 沒有靜態類型檢查。
- 變量名只是標簽。它們沒有相應的類型。
- 沒有類型聲明。按需分配變量名,及時可用(如:
a = [1,2,3]
而不是int[] a = {1,2,3};
)。 - 沒有顯式轉換。只需要調用方法。代碼運行之前,單元測試應該告訴你出現異常。
- 使用
foo = Foo.new("hi")
創建新對象,而非Foo foo = new Foo("hi")
。 - 構造器總是命名為“initialize” 而不是類名稱。
- 作為接口的替代,你將獲得“混入(mixins)”。
- 相比 XML,傾向於使用 YAML。
nil
替代null
。- Ruby 對
==
和equals()
的處理方式與 Java 不一樣。測試相等性使用==
(Java 中是equals()
)。測試是否為同一對象使用equals?()
(Java 中是==
)。
以上的相同與不同來自:https://www.ruby-lang.org/zh_cn/documentation/ruby-from-other-languages/to-ruby-from-java/
延伸閱讀:https://gquintana.github.io/2017/01/08/From-Java-to-Ruby.html
Ruby 基礎
在大致了解了 Ruby 一些基礎信息之后,我們開始 Ruby 基礎語法的學習,雖然面對一門新的語言,語法啊特性啊之類的了解很有必要,但還是想在了解之前看一看 Ruby 的一些代碼規范,好讓自己能快速了解 Ruby 的基礎上還能養成一個良好的編碼習慣。
學習之前必備 - 代碼規范
或許有些語句還不能理解,沒關系,有一個基礎印象就好。
-
一般來講,Ruby 中的變量名和方法名使用下划線命名法(小寫字母 +
_
),類名和模塊名使用 Java 類似的駝峰命名法 -
每個縮進級別使用兩個 space(又名軟 tabs),不要使用硬 tabs
# bad - four spaces
def some_method
do_something
end
# good
def some_method
do_something
end
- 不用使用
;
來分割語句和表達式。以此推論 - 一行使用一個表達式
# bad
puts 'foobar'; # superfluous semicolon
puts 'foo'; puts 'bar' # two expression on the same line
# good
puts 'foobar'
puts 'foo'
puts 'bar'
puts 'foo', 'bar' # this applies to puts in particular
- 避免單行方法。即便還是會受到一些人的歡迎,這里還是會有一些古怪的語法用起來很容易犯錯. 無論如何 - 應該一行不超過一個單行方法.
# bad
def too_much; something; something_else; end
# okish - notice that the first ; is required
def no_braces_method; body end
# okish - notice that the second ; is optional
def no_braces_method; body; end
# okish - valid syntax, but no ; make it kind of hard to read
def some_method() body end
# good
def some_method
body
end
空方法是這個規則的例外。
# good
def no_op; end
- 當賦值一個條件表達式的結果給一個變量時,保持分支的縮排在同一層。
# bad - pretty convoluted
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
result = if some_cond
calc_something
else
calc_something_else
end
# good - it's apparent what's going on
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
result = if some_cond
calc_something
else
calc_something_else
end
# good (and a bit more width efficient)
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
result =
if some_cond
calc_something
else
calc_something_else
end
- 在方法定義之間使用空行並且一個方法根據邏輯段來隔開。
def some_method
data = initialize(options)
data.manipulate!
data.result
end
def some_methods
result
end
- 不要使用區塊注釋。它們不能由空白引導(=begin 必須頂頭開始),並且不如普通注釋容易辨認。
# bad
== begin
comment line
another comment line
== end
# good
# comment line
# another comment line
- 使用括號將def的參數括起來。當方法不接收任何參數的時候忽略括號。
# bad
def some_method()
# body omitted
end
# good
def some_method
# body omitted
end
# bad
def some_method_with_arguments arg1, arg2
# body omitted
end
# good
def some_method_with_arguments(arg1, arg2)
# body omitted
end
- 從來不要使用
for
, 除非你知道使用它的准確原因。大多數時候迭代器都可以用來替for
。for
是由一組each
實現的 (因此你正間接添加了一級),但是有一個小道道 -for
並不包含一個新的 scope (不像each
)並且在它的塊中定義的變量在外面也是可以訪問的。(這里有些像 Java 中不用 for-each 語句類似,感興趣的也可以去搜一搜)
arr = [1, 2, 3]
# bad
for elem in arr do
puts elem
end
# note that elem is accessible outside of the for loop
elem #=> 3
# good
arr.each { |elem| puts elem }
# elem is not accessible outside each's block
elem #=> NameError: undefined local variable or method `elem'
- 利用
if
andcase
是表達式這樣的事實它們返回一個結果。
# bad
if condition
result = x
else
result = y
end
# good
result =
if condition
x
else
y
end
- 布爾表達式使用
&&/||
,and/or
用於控制流程。(經驗Rule:如果你必須使用額外的括號(表達邏輯),那么你正在使用錯誤的的操作符。)
# boolean expression
if some_condition && some_other_condition
do_something
end
# control flow
document.save? or document.save!
- 永遠不要使用
unless
和else
組合。將它們改寫成肯定條件。
# bad
unless success?
puts 'failure'
else
puts 'success'
end
# good
if success?
puts 'success'
else
puts 'failure'
end
- 不用使用括號包含
if/unless/while
的條件。
# bad
if (x > 10)
# body omitted
end
# good
if x > 10
# body omitted
end
- 傾向使用
module
,而不是只有類方法的class
。類別應該只在創建實例是合理的時候使用。
# bad
class SomeClass
def self.some_method
# body omitted
end
def self.some_other_method
end
end
# good
module SomeClass
module_function
def some_method
# body omitted
end
def some_other_method
end
end
- 當你希望將模塊的實例方法變成
class
方法時,偏愛使用module_function
勝過extend self
。
module Utilities
extend self
def parse_something(string)
# do stuff here
end
def other_utility_method(number, string)
# do some more stuff
end
end
# good
module Utilities
module_function
def parse_something(string)
# do stuff here
end
def other_utility_method(number, string)
# do some more stuff
end
end
- 總是為你自己的類提供
to_s
方法, 用來表現這個類(實例)對象包含的對象.
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def to_s
"#@first_name #@last_name"
end
end
- 考慮使用
Struct.new
, 它可以定義一些瑣碎的accessors
,constructor
(構造函數) 和comparison
(比較) 操作。
# good
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
end
# better
class Person < Struct.new(:first_name, :last_name)
end
- 考慮使用
Struct.new
,它替你定義了那些瑣碎的存取器(accessors),構造器(constructor)以及比較操作符(comparison operators)。
# good
class Person
attr_accessor :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
end
# better
Person = Struct.new(:first_name, :last_name) do
end
-
不要去
extend
一個Struct.new
- 它已經是一個新的class
。擴展它會產生一個多余的class
層級 並且可能會產生怪異的錯誤如果文件被加載多次。 -
鴨子類型(duck-typing)優於繼承。
# bad
class Animal
# abstract method
def speak
end
end
# extend superclass
class Duck < Animal
def speak
puts 'Quack! Quack'
end
end
# extend superclass
class Dog < Animal
def speak
puts 'Bau! Bau!'
end
end
# good
class Duck
def speak
puts 'Quack! Quack'
end
end
class Dog
def speak
puts 'Bau! Bau!'
end
end
-
當訪問一個數組的第一個或者最后一個元素,傾向使用 first 或 last 而不是 [0] 或 [-1]。
-
優先使用
字符串插值
來代替字符串串聯
。
# bad
email_with_name = user.name + ' <' + user.email + '>'
# good
email_with_name = "#{user.name} <#{user.email}>"
# good
email_with_name = format('%s <%s>', user.name, user.email)
-
在對象插值的時候不要使用
Object#to_s
,它將會被自動調用。 -
操作較大的字符串時, 避免使用
String#+
做為替代使用String#<<
。就地級聯字符串塊總是比String#+
更快,它創建了多個字符串對象。
# good and also fast
html = ''
html << '<h1>Page title</h1>'
paragraphs.each do |paragraph|
html << "<p>#{paragraph}</p>"
end
-
RuboCop
RuboCop 是一個基於本指南的 Ruby 代碼風格檢查工具。 RuboCop 涵蓋了本指南相當大的部分,支持 MRI 1.9 和 MRI 2.0,而且與 Emacs 整合良好。
基於下面鏈接簡化了大部分,因為有一些編碼風格能從 Java 自然的切換過來,而且我們也可以使用相應的工具來檢查我們的代碼,更多可以看這里:https://ruby-china.org/wiki/coding-style
Ruby 語言基礎
其實通過上面的語法規范,我們或多或少能感知到一些 Ruby 的語法,有一些細節的地方可以自行去菜鳥教程or其他網站能看到,下面我們就着一些重要的東西快速的學習一遍吧..
數據類型
Ruby 中有以下幾種不同的數據類型:
-
數字/ 字符串/ 符號/ 哈希/ 數組/ 布爾
比較在意的是 Ruby 並沒有 Java 中的枚舉類型,可能是出於安全方面的考慮吧..(我也不知道...) -
符號就像字符串。一個符號之前是冒號(
:
)。例如:
:abcd
它們不包含空格。 含有多個單詞的符號用(_
)寫成。 字符串和符號之間的一個區別是,如果文本是一個數據,那么它是一個字符串,但如果它是一個代碼,它是一個符號。
符號是唯一的標識符,表示靜態值,而字符串表示更改的值。
示例:
irb(main):011:0> "string".object_id
=> 26922000
irb(main):012:0> "string".object_id
=> 29115220
irb(main):013:0> :symbol.object_id
=> 788188
irb(main):014:0> :symbol.object_id
=> 788188
- 哈希將其值分配給其鍵。 它們可以用鍵關聯指定值。鍵的值由
=>
符號分配。 鍵/值對之間用逗號分隔,所有對都用大括號括起來。 例如:
{"key1" => "value1", "key2" => "Chemistry", "key3" => "Maths"}
示例:
data = {"key1" => "Physics", "key2" => "Chemistry", "key3" => "Maths"}
puts data["key1"]
puts data["key2"]
puts data["key3"]
執行上述代碼,得到以下結果:
Physics
Chemistry
Maths
變量
Ruby 有四種類型的變量,變量的命名方式決定了變量的種類:
-
局部變量
以英文小寫字母或者_
開頭,作用域等同於 Java 局部變量。 -
全局變量
以$
開頭,作用域等同於 Java 全局變量。只要全局變量的名稱相同,不管變量在程序的哪個部分使用,程序都認為是它們是同一個變量。未初始化的全局變量的值會被初始化為:nil
。建議不要使用全局變量,因為它們使程序變得秘密和復雜。
示例:
$global_var = "GLOBAL"
class One
def display
puts "Global variable in One is #$global_var"
end
end
class Two
def display
puts "Global variable in Two is #$global_var"
end
end
oneobj = One.new
oneobj.display
twoobj = Two.new
twoobj.display
執行代碼輸出結果如下:
Total number of states written: 4
Total number of states written: 4
Total number of states written: 4
Total number of states written: 4
- 實例變量
以@
開頭,在同一個實例中,程序可以超越方法定義,任意引用、修改實例變量。它屬於類的一個實例,可以從方法中的類的任何實例訪問。 它們只能訪問一個特定的類的實例。它們不需要初始化,未初始化的實例變量的值是:nil
。
示例:
class States
def initialize(name)
@states_name=name
end
def display()
puts "States name #@states_name"
end
end
# Create Objects
first=States.new("Hainan")
second=States.new("GuangDong")
third=States.new("Beijing")
fourth=States.new("ShangDong")
# Call Methods
first.display()
second.display()
third.display()
fourth.display()
執行代碼輸出結果如下:
States name GuangDong
States name Beijing
States name ShangDong
- 類變量
以@@
開頭,作用域為該類的所有實例。需要在使用前進行初始化,由類的所有后代共享,未初始化的變量將導致錯誤。
示例:
class States
@@no_of_states=0
def initialize(name)
@states_name=name
@@no_of_states += 1
end
def display()
puts "State name #@state_name"
end
def total_no_of_states()
puts "Total number of states written: #@@no_of_states"
end
end
# Create Objects
first=States.new("Assam")
second=States.new("Meghalaya")
third=States.new("Maharashtra")
fourth=States.new("Pondicherry")
# Call Methods
first.total_no_of_states()
second.total_no_of_states()
third.total_no_of_states()
fourth.total_no_of_states()
執行上面代碼輸出結果如下:
Total number of states written: 4
Total number of states written: 4
Total number of states written: 4
Total number of states written: 4
類和對象
Object
類是所有 Ruby 對象的默認根。 Ruby 對象繼承自 BasicObject
(它是Ruby中所有類的父類)類,允許創建替代對象層次結構。
首先與 Java 很不同的是創建對象:
Object newObject = new Object(); // Java 中新建對象
對比 Ruby:
objectName = className.new
每個 Ruby 類都是 Class
類的一個實例。通常對於類名,使用駝峰命名規則,類的名稱始終以大寫字母開頭。定義類是用 end
關鍵字完成的。
語法
class ClassName
codes...
end
我們使用上面學習過的語法規范來創建一個 Person
類(寫上 to_s
方法):
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def to_s
"#@first_name #@last_name"
end
end
注意這里的 attr_reader
對應在 Java 中相當於為 first_name
和 last_name
定義了 getter
,在 Ruby 中,從對象外部不能直接訪問實例變量或對實例變量賦值,需要通過方法來訪問對象的內部,如果像 Java 那樣一遍一遍為每一個變量寫 getter/setter
就有些冗雜了,Ruby 為我們提供了一些方便的存取器。
定義 | 意義 |
---|---|
attr_reader :name | 只讀(定義 name 方法) |
attr_writer :name | 只寫(定義 name= 方法) |
attr_accessor :name | 讀寫(定義以上兩個方法) |
另外一點上面有一個非常有趣的規范是使用
Struct.new
來簡化代碼,我覺得很酷也想把它應用在上述 Person 類的創建中,但是發現失敗了(不能在其中定義其他功能性的代碼),所以可能結論是:這樣的簡化只適用於一些實體類保存數據的類吧。
方法
Ruby 方法使用 def
關鍵字開始,最后還需要使用 end
關鍵字來表示方法定義結束。
語法:
def methodName
code...
end
示例:
def method_defining(a1 = "Ruby", a2 = "Python")
puts "The programming language is #{a1}"
puts "The programming language is #{a2}"
end
method_defining "C", "C++"
method_defining
運行上面的代碼執行結果如下:
The programming language is C
The programming language is C++
The programming language is Ruby
The programming language is Python
方法返回值:
在初探 Ruby 的時候我們就感受到,貌似每一條指令都會返回一個返回值,方法也是這樣,在 Ruby 中每個方法都有一個返回值,這個返回的值將是最后一個語句的值。例如:
def my_method
i = 100
j = 10
k = 1
end
上面代碼中,最后方法的返回值是 1
。
Ruby return 語句
Ruby 中的 return
語句用於從 Ruby 方法中返回一個或多個值
示例:
def method
i = 100
j = 200
k = 300
return i, j, k
end
var = method
puts var
上面代碼結果如下:
100
200
300
可變參數:
假設聲明一個方法需要兩個參數,每當調用這個方法時,需要傳遞兩個參數。但是,Ruby允許您聲明使用可變數量參數的方法。 讓我們來看一下這個示例:
def sample (*test)
puts "The number of parameters is #{test.length}"
for i in 0...test.length
puts "The parameters are #{test[i]}"
end
end
sample "Maxsu", "6", "F"
sample "Mac", "38", "M", "MCA"
執行上面代碼,得到如下結果:
The number of parameters is 3
The parameters are Maxsu
The parameters are 6
The parameters are F
The number of parameters is 4
The parameters are Mac
The parameters are 38
The parameters are M
The parameters are MCA
類方法:
當方法在類定義之外定義時,默認情況下該方法被標記為 private
。 另一方面,默認情況下,類定義中定義的方法被標記為 public
。模塊的默認可見性和 private
標記可以通過模塊的 public
或 private
更改。
Ruby 給出一種不用實例化一個類就可以訪問一個方法。下面來看看看如何聲明和訪問類方法 -
class Accounts
def reading_charge
end
def Accounts.return_date
end
end
訪問類方法 -
Accounts.return_date
模板
Ruby 模塊是方法和常量的集合。暫時你可簡單的理解為一個不能實例化的類,這樣做的好處是一來可以提供一個命名空間避免名字沖突,另一個是實現了 mixin
的功能。
不知道您有沒有發現,Ruby 沒有提供多重繼承的功能,但 Ruby 的模板幾乎消除了多重繼承的需要,提供了一種名為 mixin
的裝置。
示例:
module A
def a1
end
def a2
end
end
module B
def b1
end
def b2
end
end
class Sample
include A
include B
def s1
end
end
samp=Sample.new
samp.a1
samp.a2
samp.b1
samp.b2
samp.s1
塊
Ruby 代碼在其他編程語言中被稱為閉包。它由一組代碼組成,它們始終用大括號括起來,或者在 do..end
之間書寫。大括號語法總是具有比 do..end
語法更高的優先級。也就是說大括號優先級高,do..end
優先級低。
語法:
block_name{
statement1
statement2
..........
}
yield語句:
def test
puts "在 test 方法內"
yield
puts "你又回到了 test 方法內"
yield
end
test {puts "你在塊內"}
上面代碼運行結果如下:
在 test 方法內
你在塊內
你又回到了 test 方法內
你在塊內
塊和方法:
def test
yield
end
test{ puts "Hello world"}
本實例是實現塊的最簡單的方式。您使用 yield 語句調用 test 塊。
但是如果方法的最后一個參數前帶有 &
,那么您可以向該方法傳遞一個塊,且這個塊可被賦給最后一個參數。如果 *
和 &
同時出現在參數列表中,&
應放在后面。
def test(&block)
block.call
end
test { puts "Hello World!"}
上述代碼運行結果如下:
Hello World!
這一部分建議再去看一下慕課網上的教程,看關於第三章的內容即可:Ruby語言快速入門:https://www.imooc.com/learn/765
總結
經過以上簡單的學習,我們對 Ruby 有了一個大致的了解,算是簡單入了個門(有一些簡單的例如循環啊,判斷啊,運算符之類的簡單我就沒有寫了),更多的東西需要自己平時的編碼中去總結學習(肯定有一些坑需要自己去填的)。
參考文章:
1.Ruby 教程 - 菜鳥驛站 - http://www.runoob.com/ruby/ruby-intro.html
2.Ruby 教程 - 易百教程 - https://www.yiibai.com/ruby
3.20分鍾體驗 Ruby - https://www.ruby-lang.org/zh_cn/documentation/quickstart/
4.笨方法學 Ruby - https://lrthw.github.io/intro/
5.Ruby 快速入門 - https://wdxtub.com/2016/03/30/ruby-first-step/
按照慣例黏一個尾巴:
歡迎轉載,轉載請注明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關注公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693