10 thủ thuật Ruby mà bạn chưa từng thấy
Bài đăng này đã không được cập nhật trong 7 năm
1. Deep copy
Khi bạn copy
một object mà bên trong nó có chứa những object khác, ví dụ như là một Array
, thì đơn thuần bạn chỉ copy tham chiếu đến các object đó.
Bạn có thể xem nó ở hành động dưới đây:
food = %w( bread milk orange )
food.map(&:object_id)
=> [69612840, 69612820, 69612800]
[122] pry(main)> food.clone.map(&:object_id)
=> [69612840, 69612820, 69612800]
Sử dụng Marshal
(thường được dùng cho serialization) bạn có thể tạo ra một deep copy
của một object.
def deep_copy(obj)
Marshal.load(Marshal.dump(obj))
end
Và kết quả như sau:
ruby
ObjectSpace.each_object(String).select{|x| x == food.first}.count
=> 54
deep_copy(food).map &:object_id
=> [77564660, 77564420, 77564300]
ObjectSpace.each_object(String).select{|x| x == food.first}.count
=> 56
Bonus: Sự khác biệt giữa clone
và dup
(bản chất thì tương đương nhau nhưng mà clone
có 2 điểm mà dup
không có):
- Sao chép cả singleton class của object được sao chép
- Duy trì trạng thái frozen của object được sao chép.
Ví dụ:
- Singleton methods
a = Object.new
def a.foo; :foo end
p a.foo
=> :foo
b = a.dup
p b.foo
=> undefined method `foo' for #<Object:0x007f8bc395ff00> (NoMethodError)
c = a.clone
c.foo
=> :foo
- Trạng thái Frozen
a = Object.new
a.freeze
p a.frozen?
=> true
b = a.dup
p b.frozen?
=> false
c = a.clone
p c.frozen?
=> true
2. Các cách khác nhau để call một lambda
my_lambda = -> { puts 'Hello' }
my_lambda.call
my_lambda[]
my_lambda.()
my_lambda.===
Cách đầu tiên là cách được hầu hết mọi người sử dụng => bạn cũng chỉ cần nhớ đến cách đó là đủ.
3. Tạo mảng các phần tử có sẵn
Array.new(10) { rand 300 }
Nó sẽ tạo ra một mảng chứa 10 phẩn tử có giá trị random từ 0 đến 299
4. Lambdas quản lý rất nghiêm các tham số truyền vào, nhưng Procs thì không quan tâm đến điều đó
my_lambda = ->(a, b) { a + b }
my_proc = Proc.new { |a, b| a + b }
my_lambda.call(2)
=> ArgumentError: wrong number of arguments (1 for 2)
my_proc.call(2)
=> TypeError: nil can't be coerced into Fixnum
5. Thực thi code trực tiếp mà không cần đến irb hay files
Ruby command có rất nhiều option cho bạn lựa chọn, bạn có thể xem bằng lệnh:
ruby -h
Usage: ruby [switches] [--] [programfile] [arguments]
-0[octal] specify record separator (\0, if no argument)
-a autosplit mode with -n or -p (splits $_ into $F)
-c check syntax only
-Cdirectory cd to directory before executing your script
-d set debugging flags (set $DEBUG to true)
-e 'command' one line of script. Several -e's allowed. Omit [programfile]
-Eex[:in] specify the default external and internal character encodings
-Fpattern split() pattern for autosplit (-a)
-i[extension] edit ARGV files in place (make backup if extension supplied)
-Idirectory specify $LOAD_PATH directory (may be used more than once)
-l enable line ending processing
-n assume 'while gets(); ... end' loop around your script
-p assume loop like -n but print line also like sed
-rlibrary require the library before executing your script
-s enable some switch parsing for switches after script name
-S look for the script using PATH environment variable
-T[level=1] turn on tainting checks
-v print version number, then turn on verbose mode
-w turn warnings on for your script
-W[level=2] set warning level; 0=silence, 1=medium, 2=verbose
-x[directory] strip off text before #!ruby line and perhaps cd to directory
-h show this message, --help for more info
Với flag -e
bạn có thể thực thi được một đoạn mã ngắn gửi vào trong đó:
ruby -e '5.times { puts "Fun with Ruby" }'
6. Sử dụng mini-irb trong một lệnh
Có bao giờ bạn tự hỏi rằng irb
hoạt động như thế nào? Dưới đây là một version vô cùng đơn giản của nó
ruby -n -e 'p eval($_)'
Bạn sẽ không thấy được sự nhắc nhở nào, tuy nhiên hãy gõ gõ các lệnh ruby ở đây (để kết thúc gõ exit, hoặc ctrl+c)
ruby -n -e 'p eval($_)'
puts "a"
a
nil
7. Unfreeze một object (cái này rất nguy hiểm)
Ruby không có bất kì một method nào để unfreeze một object, nhưng sử dụng Fiddle class bạn có thể vào Ruby internals để làm cho nó có thể xảy ra.
require 'fiddle'
str = 'water'.freeze
str.frozen?
=> true
memory_address = str.object_id * 2
Fiddle::Pointer.new(memory_address)[1] &= ~8
str.frozen?
=> false
8. Object với identity đặc biệt
Ruby object có một identitier hoặc số id để bạn có thể truy cập sử dụng phương thức object_id
. Một số object có object_id cố định như : Fixnums, true, false & nil.
false.object_id # 0
=> 0
true.object_id
=> 20
nil.object_id
=> 8
1.object_id
=> 3
2.object_id
=> 5
Fixnum ids sử dụng công thức: (number * 2) + 1
Bonus: Maximum của Fixnum là 1073741823, sau đó bạn sẽ có 1 object Bignum
9. Tránh dùng output lớn trong irb hoặc pry
Nếu bạn đang làm việc với irb
và muốn tránh điền vào màn hình của bạn một contents lớn như là một array hay một string lớn, bạn có thể nối thêm ;
ở cuối dòng code của bạn
Ví dụ
require 'rest-client'
RestClient.get('blackbytes.info');
Hãy thử với trường hợp không có ;
và xem sự khác biệt của nó.
10. Sử dụng caller method để lấy call stack hiện tại
Xem ví dụ sau
def foo
bar
end
def bar
puts caller
end
foo
Và output sẽ như sau
(pry):236:in `foo'
(pry):244:in `<main>'
....
Nếu bạn cần tên phương thức hiện tại, bạn có thể dùng __method__
hoặc __callee__
.
All rights reserved