Làm thế nào để bạn truyền đối số cho định nghĩa_method?


155

Tôi muốn chuyển (các) đối số cho một phương thức được xác định bằng cách sử dụng notify_method, tôi sẽ làm điều đó như thế nào?

Câu trả lời:


198

Khối mà bạn chuyển đến định nghĩa_method có thể bao gồm một số tham số. Đó là cách phương thức được xác định của bạn chấp nhận đối số. Khi bạn xác định một phương thức, bạn thực sự chỉ cần đặt biệt danh cho khối và giữ một tham chiếu đến nó trong lớp. Các tham số đi kèm với khối. Vì thế:

define_method(:say_hi) { |other| puts "Hi, " + other }

Vâng, đó chỉ là một thứ tuyệt vời không bị cản trở. Làm tốt lắm, Kevin Costner.
Darth Egregious

90

... và nếu bạn muốn các tham số tùy chọn

 class Bar
   define_method(:foo) do |arg=nil|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> nil
 a.foo 1
 # => 1

... nhiều tranh luận như bạn muốn

 class Bar
   define_method(:foo) do |*arg|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> []
 a.foo 1
 # => [1]
 a.foo 1, 2 , 'AAA'
 # => [1, 2, 'AAA']

...sự kết hợp của

 class Bar
   define_method(:foo) do |bubla,*arg|
     p bubla                  
     p arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> wrong number of arguments (0 for 1)
 a.foo 1
 # 1
 # []

 a.foo 1, 2 ,3 ,4
 # 1
 # [2,3,4]

... Tất cả bọn họ

 class Bar
   define_method(:foo) do |variable1, variable2,*arg, &block|  
     p  variable1     
     p  variable2
     p  arg
     p  block.inspect                                                                              
   end   
 end
 a = Bar.new      
 a.foo :one, 'two', :three, 4, 5 do
   'six'
 end

Cập nhật

Ruby 2.0 đã giới thiệu splat đôi **(hai sao) mà ( tôi trích dẫn ) hiện:

Ruby 2.0 đã giới thiệu các đối số từ khóa và ** hoạt động như *, nhưng đối với các đối số từ khóa. Nó trả về một Hash với các cặp khóa / giá trị.

... Và tất nhiên bạn cũng có thể sử dụng nó trong phương thức xác định :)

 class Bar 
   define_method(:foo) do |variable1, variable2,*arg,**options, &block|
     p  variable1
     p  variable2
     p  arg
     p  options
     p  block.inspect
   end 
 end 
 a = Bar.new
 a.foo :one, 'two', :three, 4, 5, ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "two"
# [:three, 4, 5]
# {:ruby=>"is awesome", :foo=>:bar}

Ví dụ thuộc tính được đặt tên:

 class Bar
   define_method(:foo) do |variable1, color: 'blue', **other_options, &block|
     p  variable1
     p  color
     p  other_options
     p  block.inspect
   end
 end
 a = Bar.new
 a.foo :one, color: 'red', ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "red"
# {:ruby=>"is awesome", :foo=>:bar}

Tôi đã cố gắng tạo ví dụ với đối số từ khóa, splat và double splat tất cả trong một:

 define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block|
    # ...

hoặc là

 define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block|
    # ...

... nhưng điều này sẽ không hoạt động, có vẻ như có một hạn chế. Khi bạn nghĩ về nó có ý nghĩa vì toán tử splat là "nắm bắt tất cả các đối số còn lại" và splat kép là "nắm bắt tất cả các đối số từ khóa còn lại", do đó trộn chúng sẽ phá vỡ logic dự kiến. (Tôi không có bất kỳ tài liệu tham khảo nào để chứng minh điểm này doh!)

cập nhật 2018 tháng 8:

Tóm tắt bài viết: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html


Thú vị - đặc biệt là khối thứ 4: nó đã hoạt động vào ngày 1.8.7! Khối thứ nhất không hoạt động trong 1.8.7 và khối thứ hai có lỗi đánh máy (nên a.foo 1thay vì foo 1). Cảm ơn!
Sony Santos

1
cảm ơn đã phản hồi, lỗi đánh máy đã được sửa, ... Trên ruby ​​1.9.3 và 1.9.2, tất cả các ví dụ đều hoạt động và tôi khẳng định rằng trên 1.9.1 cũng vậy (nhưng không thử)
tương đương 8

Tôi đã kết hợp câu trả lời này với câu trả lời được chấp nhận tại stackoverflow.com/questions/4470108/, để tìm cách ghi đè (không ghi đè) một phương thức trong thời gian chạy có các đối số tùy chọn và một khối và vẫn có thể gọi phương thức ban đầu bằng các đối số và khối. À, hồng ngọc. Cụ thể, tôi cần ghi đè Savon :: Client.request trong dev env của tôi cho một lệnh gọi API đến máy chủ lưu trữ mà tôi chỉ có thể truy cập trong sản xuất. Chúc mừng!
pduey

59

Ngoài câu trả lời của Kevin Conner: các đối số khối không hỗ trợ cùng một ngữ nghĩa như các đối số phương thức. Bạn không thể xác định đối số mặc định hoặc đối số khối.

Điều này chỉ được sửa trong Ruby 1.9 với cú pháp "stabby lambda" thay thế mới hỗ trợ ngữ nghĩa đối số phương thức đầy đủ.

Thí dụ:

# Works
def meth(default = :foo, *splat, &block) puts 'Bar'; end

# Doesn't work
define_method :meth { |default = :foo, *splat, &block| puts 'Bar' }

# This works in Ruby 1.9 (modulo typos, I don't actually have it installed)
define_method :meth, ->(default = :foo, *splat, &block) { puts 'Bar' }

3
Trên thực tế, tôi tin rằng các đối số khối trên notify_method có hỗ trợ splat, điều này có thể cung cấp một cách hoàn hảo để xác định các đối số mặc định.
Chinasaur

1
Chinasaur là chính xác về các đối số khối cho phép splats. Tôi đã xác nhận điều này trong cả Ruby 1.8.7 và 1.9.1.
Peter Wagenet

Cảm ơn, tôi đã quên điều này. Đã sửa bây giờ.
Jörg W Mittag

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.