10 thủ thuật Ruby mà bạn chưa từng thấy
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 ) = > [ ...
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 Fixnum5. 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__.