Giới thiệu

Đây là một bài hướng dẫn nhỏ, chỉ cần không quá 20 phút để hoàn thành. Giả thiết là bạn đã cài sẵn Ruby. (Nếu bạn chưa cài đặt, tải về và cài đặt tại www.Ruby-lang.org nhé)

Học lập trình web bằng Ruby on Rails

Tương tác với Ruby

Ruby có một chương trình hiển thị kết quả của bất kỳ câu lệnh Ruby nào bạn gõ vào. Thử sức với Ruby code theo cách tương tác như thế này là một cách tuyệt vời để khám phá nó.
Mở IRB (hiểu là Tương tác với Ruby).

Nếu bạn sử dụng Mac OS X, mở Terminal và gõ irb rồi enter.
Nếu bạn sử dụng Linux, mở một shell và gõ irb rồi enter.
Nếu bạn sử dụng Windows, mở fxri từ phần Ruby trên Start Menu.

irb(main):001:0>

Rồi, nó đã mở. Giờ ta phải làm gì?
Gõ: “Xin chào thế giới

irb(main):001:0> "Xin chào thế giới"
=> "Xin chào thế giới"

Ruby phục vụ bạn!

Điều gì đã xảy ra vậy? Chúng ta vừa viết chương trình “Xin chào thế giới” ngắn nhất thế giới ư? Không phải vậy. Dòng thứ 2 chỉ là cách mà IRB nói cho ta biết kết quả của phép toán cuối cùng nó nhận được. Nếu muốn in ra “Xin chào thế giới” ta cần viết nhiều hơn:

irb(main):002:0> puts "Xin chào thế giới"
Xin chào thế giới
=> nil

puts là cú pháp cơ bản để in ra trong Ruby. Nhưng sau đó => nil là gì? Nó là kết quả của phép toán. puts luôn trả về nil – giá trị rỗng.

Máy tính miễn phí của bạn đây

Rõ ràng, chúng ta có thể sử dụng IRB như một chiếc máy tính cơ bản:

irb(main):003:0> 3+2
=> 5

Ba cộng hai. Quá dễ. Thế còn ba nhân hai thì sao? Bạn có thể gõ trực tiếp vì nó rất ngắn, tuy nhiên bạn có thể sẽ sửa lại những gì mình vừa nhập vào. Bấm nút mũi tên lên trên bàn phím và nó sẽ hiển thị dòng 3+2. Nếu được, bạn có thể sử dụng phím mũi tên trái để di chuyển đến dấu + và thay nó bằng dấu *.

irb(main):004:0> 3*2
=> 6

Tiếp theo, hãy thử tính 3 bình phương:

irb(main):005:0> 3**2
=> 9

Trong Ruby, ** nghĩa là “bình phương”. Nhưng nếu bạn muốn tìm căn bậc hai thì sao?

irb(main):006:0> Math.sqrt(9)
=> 3.0

Bạn thấy gì ở kết quả? Nếu bạn nghĩ, đó là “căn bậc hai của 9” thì bạn đã đúng. Nhưng hãy chú ý vào những thứ khác. Đầu tiên: Math là gì?

Gộp nhóm mã module theo chủ đề

Math là một module của toán học. Các module phụ vụ hai vai trò trong Ruby. Ở đây là một vai trò: nhóm các phương thức giống nhau lại với nhau dưới một cái tên tương tự. Math còn có các phương thức như sin()tan().

Tiếp theo là dấu chấm. Dấu chấm có tác dụng gì? Dấu chấm dùng để xác định nơi nhận của một câu lệnh. Câu lệnh là gì? Trong trường hợp này thì nó là sqrt(9), nghĩa là gọi phương thức sqrt, viết tắt của “căn bậc hai (square root)” với tham số là 9.

Kết quả của việc gọi phương thức này là 3.0. Chú ý rằng nó không chỉ là 3 bởi vì phần lớn căn bậc 2 của một số không phải là một số nguyên, do đó phương thức luôn trả về một số thực.

Nếu muốn lưu lại một vài kết quả của phép toán này, hãy gán kết quả cho một biến.

irb(main):007:0> a = 3 ** 2
=> 9
irb(main):008:0> b = 4 ** 2
=> 16
irb(main):009:0> Math.sqrt(a+b)
=> 5.0

Tuyệt vời như một chiếc máy tính, chúng ta đang vượt xa thông điệp Xin chào thế giới truyền thống mà ban đầu bài hướng dẫn giả sử…

Nếu muốn nói “Xin chào” rất nhiều mà không muốn mỏi tay, ta cần định nghĩa một phương thức!

irb(main):010:0> def h
irb(main):011:1> puts "Xin chào Thế giới!"
irb(main):012:1> end
=> nil

Đoạn mã def h bắt đầu định nghĩa phương thức. Nó báo với Ruby rằng “Chúng tôi đang định nghĩa một phương thức tên là h”. Dòng tiếp theo là phần thân của phương thức, tương tự với dòng chúng ta đã thấy trước đây: puts “Xin chào Thế giới”. Cuối cùng, dòng cuối cùng end thông báo với Ruby rằng ta đã định nghĩa xong phương thức. Ruby trả lại =>; nil để xác nhận.

Tóm tắt, vòng đời của một phương thức

Bây giờ ta sẽ chạy phương thức vừa rồi vài lần:

irb(main):013:0> h
Xin chào Thế giới!
=> nil
irb(main):014:0> h()
Xin chào Thế giới!
=> nil

Tốt rồi, rất đơn giản. Gọi một phương thức trong Ruby rất dễ dàng, chỉ cần gõ tên của nó vào. Nếu phương thức không cần tham số, bạn có thể thêm cặp ngoặc đơn trống nếu thích vì chúng cũng không cần thiết.

Nếu bạn muốn nói xin chào tới một người mà không phải là cả thế giới, hãy định nghĩa lại h để thêm tên là tham số truyền vào.

irb(main):015:0> def h(tên)
irb(main):016:1>   puts "Xin chào #{tên}!"
irb(main):017:1> end
=> nil
irb(main):018:0> h("Matz")
Xin chào Matz!
=> nil

Nó đã hoạt động… nhưng hãy dành vài giây để xem có gì ở đây.

Giữ các đoạn trong xâu

Đoạn #{tên} nghĩa là gì? Đó là cách mà Ruby chèn thứ gì đó vao trong một xâu. Đoạn ở giữa cặp ngoặc nhọn được chuyển thành một xâu (nếu nó chưa phải) sau đó chèn vào xâu bên ngoài tại chỗ mà đoạn đó được viết. Bạn có thể sử dụng cái này để chắc chắn rằng tên của ai đó đã được viết hoa:

irb(main):019:0> def h(tên = "Thế giới")
irb(main):020:1> puts "Xin chào #{tên.capitalize}!"
irb(main):021:1> end
=> nil
irb(main):022:0> h "chris"
Xin chào Chris!
=> nil
irb(main):023:0> h
Xin chào Thế giới!
=> nil

Ta thấy có hai cách gọi khác nhau ở đây. Một là gọi phương thức không cần ngoặc đơn một lần nữa. Nếu đó là mục đích của bạn thì ngoặc đơn chỉ là tùy chọn. Cách còn lại là lấy tham số mặc định là “Thế giới”. Điều này nghĩa là “Nếu không cung cấp tên thì ta sử dụng tên mặc định là ‘Thế giới’”

Phát triển thành ChàoMừng

Phải làm gì nếu chúng ta muốn có những ChàoMừng thực sự ở xung quanh, nhớ tên bạn, chào đón bạn và luôn kính trọng bạn. Bạn sẽ phải dùng đối tượng! Chúng ta sẽ tạo một lớp ChàoMừng.

irb(main):024:0> class ChàoMừng
irb(main):025:1>   def initialize(tên = "Thế giới")
irb(main):026:2>     @tên = tên
irb(main):027:2>   end
irb(main):028:1>   def xin_chào
irb(main):029:2>     puts "Xin chào #{@tên}!"
irb(main):030:2>   end
irb(main):031:1>   def tạm_biệt
irb(main):032:2>     puts "Tạm biệt #{@tên}, hẹn gặp lại."
irb(main):033:2>   end
irb(main):034:1> end
=> nil

Từ khóa mới ở đây là class nghĩa là lớp. Nó định nghĩa một lớp mới tên là ChàoMừng và một vài phương thức thuộc lớp. Thông báo tới @tên, đây là một biến đối tượng và nó có thể sử dụng trong tất cả các phương thức của lớp, như trong đối tượng được sử dụng với xin_chàotạm_biệt.

Vậy, làm thế nào để chúng ta thiết lập lớp Chào Mừng này
Bây giờ, chúng ta hãy tạo một đối tượng ChàoMừng và sử dụng nó:

irb(main):035:0> g = ChàoMừng.new("Pat")
=> #
irb(main):036:0> g.xin_chào
Xin chào Pat!
=> nil
irb(main):037:0> g.tạm_biệt
Tạm biệt Pat, hẹn gặp lại.
=> nil

Khi đối tượng g được tạo, nó nhớ rằng cái tên là Pat. Vậy nếu chúng ta muốn lấy một cái tên trực tiếp thì sao?

irb(main):038:0> g.@tên
SyntaxError: compile error
(irb):52: syntax error
        from (irb):52

Ồ, không thể làm được.

Bên trong của đối tượng

Các biến đối tượng thường ẩn trong trong đối tượng. Chúng không ẩn hoàn toàn, bạn sẽ thấy chúng mỗi khi khởi tạo một đối tượng và có nhiều cách để gọi chúng, nhưng Ruby sử dụng phương pháp tiếp cận hướng đối tượng tốt trong việc giữ dữ liệu ẩn đi phần nào.

Vậy những phương thức nào tồn tại cho đối tượng ChàoMừng?

irb(main):039:0> ChàoMừng.instance_methods
=> ["method", "send", "object_id", "singleton_methods",
    "__send__", "equal?", "taint", "frozen?",
    "instance_variable_get", "kind_of?", "to_a",
    "instance_eval", "type", "protected_methods", "extend",
    "eql?", "display", "instance_variable_set", "hash",
    "is_a?", "to_s", "class", "tainted?", "private_methods",
    "untaint", "xin_chào", "id", "inspect", "==", "===",
    "clone", "public_methods", "respond_to?", "freeze",
    "tạm_biệt", "__id__", "=~", "methods", "nil?", "dup",
    "instance_variables", "instance_of?"]

Quá tuyệt! Có rất nhiều method hay phương thức. Chúng ta chỉ định nghĩa 2 phương thức, vậy cái gì đây? Đó là tất cả các phương thức cho đối tượng Người chào đón, một danh sách hoàn chỉnh bao gồm cả những phương thức được kế thừa. Nếu chúng ta muốn một danh sách chỉ có các phương thức định nghĩa cho ChàoMừng, chúng ta có thể loại bỏ các phương thức được kế thừa bằng cách thêm tham số false, nghĩa là chúng ta không muốn bất kỳ phương thức được kế thừa nào.

irb(main):040:0> ChàoMừng.instance_methods(false)
=> ["xin_chào", "tạm_biệt"]

Nó hiểu được xin_chào to_s (to string – chuyển sang chuỗi kí tự, một phương thức mặc định cho mọi đối tượng), nhưng không biết tên.

Thay thế các lớp – Không bao giờ là quá muộn

Nếu bạn muốn có thể xem hoặc thay đổi tên? Ruby cung cấp một cách dễ dàng để truy xuất dữ liệu tới các biến của một đối tượng.

irb(main):041:0> g.respond_to?("tên")
=> false
irb(main):042:0> g.respond_to?("xin_chào")
=> true
irb(main):043:0> g.respond_to?("to_s")
=> true

Trong Ruby, bạn có thể mở một lớp ở phía trên và sửa nó. Sự thay đổi sẽ xuất hiện trong các đối tượng mới mà bạn tạo và cả những đối tượng đã tồn tại của lớp này. Vậy thì chúng ta hãy tạo một đối tượng mới và thử với thuộc tính @tên của nó.

irb(main):047:0> g = ChàoMừng.new("Andy")
=> #
irb(main):048:0> g.respond_to?("tên")
=> true
irb(main):049:0> g.respond_to?("tên=")
=> true
irb(main):050:0> g.xin_chào
Xin chào Andy!
=> nil
irb(main):051:0> g.tên="Betty"
=> "Betty"
irb(main):052:0> g
=> #
irb(main):053:0> g.tên
=> "Betty"
irb(main):054:0> g.xin_chào
Xin chào Betty!
=> nil

Sử dụng attr_accessor định nghĩa 2 phương thức mới cho chúng ta: tên để lấy giá trị và tên= để gán giá trị.

MegaChàoMừng chào mừng tất cả mọi thứ

Trình chào mừng này không thú vị gì nếu nó chỉ chào được một người trong cùng một thời điểm. Sẽ thế nào nếu chúng ta có thể vài kiểu MegaChàoMừng có thể đồng thời chào Thế giới, chào một người nào đó hay một danh sách cá nhân?

Chúng ta sẽ viết trong một tập tin thay vì gõ trực tiếp trên IRB.

Để thoát IRB, gõ “quit”, “exit” hoặc tổ hợp phím Control-D.

#!/usr/bin/env ruby

class MegaChàoMừng
  attr_accessor :danh_sách_tên

  # Create the object
  def initialize(danh_sách_tên = "Thế giới")
    @danh_sách_tên = danh_sách_tên
  end

  # Nói xin chào tới mọi người
  def xin_chào
    if @danh_sách_tên.nil?
      puts "..."
    elsif @danh_sách_tên.respond_to?("each")
      # @danh_sách_tên là danh sách tên, lặp đi lặp lại!
      @danh_sách_tên.each do |name|
        puts "Xin chào #{name}!"
      end
    else
      puts "Xin chào #{@danh_sách_tên}!"
    end
  end

  # Tạm biệt mọi người
  def tạm_biệt
    if @danh_sách_tên.nil?
      puts "..."
    elsif @danh_sách_tên.respond_to?("join")
      # Gộp các thành phần của danh sách bằng dấu phẩy
      puts "Tạm biệt #{@danh_sách_tên.join(", ")}.  Hẹn gặp lại!"
    else
      puts "Tạm biệt #{@danh_sách_tên}.  Hẹn gặp lại!"
    end
  end

end


if __FILE__ == $0
  mg = MegaChàoMừng.new
  mg.xin_chào
  mg.tạm_biệt

  # Đổi tên thành "Zeke"
  mg.danh_sách_tên = "Zeke"
  mg.xin_chào
  mg.tạm_biệt

  # Đổi tên thành danh sách tên
  mg.danh_sách_tên = ["Albert", "Brenda", "Charles",
    "Dave", "Engelbert"]
  mg.xin_chào
  mg.tạm_biệt

  # Đổi thành rỗng
  mg.danh_sách_tên = nil
  mg.xin_chào
  mg.tạm_biệt
end

Lưu tập tin này với tên “ri20min.rb”, và chạy nó “ruby ri20min.rb”. Màn hình sẽ hiện như này:

Xin chào Thế giới!
Tạm biệt Thế giới.  Hẹn gặp lại!
Xin chào Zeke!
Tạm biệt Zeke.  Hẹn gặp lại!
Xin chào Albert!
Xin chào Brenda!
Xin chào Charles!
Xin chào Dave!
Xin chào Engelbert!
Tạm biệt Albert, Brenda, Charles, Dave, Engelbert.  Come
back soon!
...
...

Có rất nhiều thứ mới trong ví dụ cuối cùng này và chúng ta có thể có cái nhìn sâu hơn vào chương tình mới của chúng ta, chú ý các dòng ban đầu, bắt đầu bởi dấu thăng (#). Trong Ruby, bất cứ thứ gì trong một dòng phía sau dấu thăng là một bình luận và trình biên dịch sẽ bỏ qua. Dòng đầu tiên của tập tin là trường hợp đặc biệt, hệ điều hành nhân Unix sẽ cho shell biết cách chạy tập tin. Mục đích của các bình luận là để làm rõ hơn.

Phương thức xin_chao đã trở nên phức tạp hơn một chút:

# Say hi to everybody
def xin_chao
  if @names.nil?
    puts "..."
  elsif @names.respond_to?("each")
    # @names là danh sách tên, lặp đi lặp lại!
    @names.each do |name|
      puts "Xin chào #{name}!"
    end
  else
    puts "Xin chào #{@names}!"
  end
end

Chương trình sẽ dựa vào biến ví dụ @names để đưa ra quyết định. Nếu nó rỗng thì chỉ in ra ba dấu chấm. Không có lý nào lại đi chào không ai cả, phải vậy chứ?

Chu kỳ và vòng lặp—a.k.a. lặp đi lặp lại

Nếu đối tượng @names phản hồi each (mỗi), nó nghĩa là bạn có thể lặp đi lặp lại, vì thế nó lặp đi lặp lại và mỗi lần chào một người. Cuối cùng, nếu @names là thứ gì đó, tự động chuyển nó thành xâu và thực hiện chào như mặc định.

Chúng ta hãy nhìn sâu hơn vào phép lặp:

@names.each do |name|
  puts "Xin chào #{name}!"
end

each là phương thức cho phép một khối lệnh chạy mỗi lần gặp một thành phần trong danh sách, và đoạn ở giữa doend là khối lệnh. Một khối lệnh giống như một hàm bất định hay lambda Biến nằm giữa 2 dấu gạch dọc là tham số cho khối lệnh này.

Điều xảy ra ở đây là với tất cả các mục trong một danh sách, name ràng buộc với các phần tử của danh sách, và cú pháp puts “Xin chào #{name}!” sẽ chạy với cái tên đó.

Phần lớn các ngôn ngữ lập trình khác làm việc với một danh sách bằng vòng lặp for, ví dụ trong C sẽ như này:

for (i=0; i<number_of_elements; i++)
{
  do_something_with(element[i]);
}

Nó vẫn hoạt động tuy nhiên không được hay cho lắm. Bạn cần một biến đếm i, xác định được độ dài của danh sách, và cần phải giải thích làm thế nào để đi hết danh sách. Cách của Ruby hay hơn nhiều, tất cả chi tiết quản lý được ẩn trong phương thức each, tất cả những gì bạn cần làm là nói với vó cần phải làm gì với các thành phần. Trong đó, phương thức eachvề cơ bản sẽ gọi yield “Albert”, sau đó yeld “Brenda” rồi yield “Charles”, vv.

Khối lệnh, ánh sáng lấp lánh trên các cạnh của Ruby

Sức mạnh thực sự của khối lệnh là khi xử lý các đối tượng phức tạp hơn danh sách. Ngoài việc xử lý các chi tiết nội dung đơn giản trong phương thức, bạn còn có thể xử lý thiết lập, teardown và lỗi – tất cả ẩn đi khỏi sự chú ý của người dùng.

# Tạm biệt mọi người
def tam_biet
  if @names.nil?
    puts "..."
  elsif @names.respond_to?("join")
    # Gộp các thành phần của danh sách bằng dấu phẩy
    puts "Tạm biệt #{@names.join(", ")}.  Hẹn gặp lại!"
  else
    puts "Tạm biệt #{@names}.  Hẹn gặp lại!"
  end
end

Phương thức tam_bietkhông sử dụng each, thay vào đó nó kiểm tra xem nếu @namescó đáp ứng phương thức join không, nếu có thì sử dụng nó. Mặt khác nó chỉ in ra các biến dưới dạng xâu. Phương thức này không quan tâm tới kiểu của biến, chỉ dựa vào phương thức mà nó hỗ trợ được gọi là “Duck Typing”, giống như “nếu nó đi giống con vịt và quạc quạc giống con vịt…”. Lợi ích của điều này là không cần thiết phải hạn chế kiểu biến được hỗ trợ. Nếu ai đó đến với một kiểu danh sách lớp mới, miễn là nó thực thi phương thức join với cùng một ngữ nghĩa như các danh sách khác, mọi thứ sẽ hoạt động theo đúng kế hoạch.

Bắt đầu kịch bản

Vậy, đó là lớp MegaGreeter, phần còn lại của tập tin chỉ để gọi phương thức trong lớp đó. Có một mẹọ cuối cùng để chú ý, đó là dòng

if __FILE__ == $0

__FILE__ là biến kỳ diệu, nó chứa tên của tập tin hiện tại. $0 là tên của tập tin sử dụng để bắt đầu chương trình. Kiểm tra này nói rằng “Nếu đây là tập tin chính được sử dụng…” nghĩa là nó cho phép một tập tin có thể sử dụng như một thư viện và không phải thực thi mã bên trong, nhưng nếu nó là một tập tin có thể thực thi thì mã sẽ được thực thi.

Xem xét tự giới thiệu

Vì vậy, đó là nó sử dụng cho tour tham quan nhanh về Ruby. Có rất nhiều điều cần khám phá, các cấu trúc điều khiển khác nhau mà Ruby cung cấp; cách sử dụng của các khối lệnh và yield; các module như mixins; và còn nhiều hơn nữa. Tôi hy vọng những thú vị ban đầu này của Ruby sẽ khiến bạn muốn học thêm nữa.