2008-09-18 63 views

Trả lời

168

Khối bạn vượt qua để define_method có thể bao gồm một số thông số. Đó là cách phương thức được định nghĩa của bạn chấp nhận các đối số. Khi bạn xác định một phương thức, bạn thực sự chỉ cần đặt tên 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ì vậy:

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

Đó chỉ là một điều tuyệt đối không được pha trộn. Làm tốt lắm, Kevin Costner. – Fuser97381

58

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

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

Ví 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 define_method làm hỗ trợ splat, mà có thể cung cấp một cách vòng-a-bout để xác định đối số mặc định quá. – Chinasaur

+1

Chinasaur là chính xác về các đối số khối cho phép các biểu tượng. Tôi đã xác nhận điều này trong cả Ruby 1.8.7 và 1.9.1. –

+0

Cảm ơn, tôi đã quên mất điều này. Đã sửa lỗi. –

74

... và nếu bạn muốn thông 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 

... như nhiều lập 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'] 

... 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ả trong số 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

của Ruby 2.0 giới thiệu đôi splat ** (hai ngôi sao) mà (I quote) thực hiện: đối số từ khóa

của Ruby 2.0 giới thiệu và ** hoạt động như *, nhưng đối với đố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ó thể sử dụng nó trong xác định phương pháp quá :)

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} 

Đặt tên thuộc tính ví dụ:

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 ra ví dụ với lập luận 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

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ó giới hạn. Khi bạn nghĩ về nó có ý nghĩa như 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ỳ tham chiếu nào để chứng minh điểm này!)

+0

Thú vị - đặc biệt khối thứ tư: nó đã hoạt động trên 1.8.7! Khối đầu tiên không hoạt động trong 1.8.7 và khối thứ hai có lỗi đánh máy (phải là 'a.foo 1' thay vì' foo 1'). Cảm ơn! –

+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ụ hoạt động và tôi tích cực là trên 1.9.1 quá (nhưng không thử) – equivalent8

+0

Tôi kết hợp câu trả lời này với chấp nhận câu trả lời tại http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i để tìm cách ghi đè (không ghi đè lên) một phương thức trong thời gian chạy có các tham số tùy chọn và một khối và vẫn có thể gọi phương thức gốc với các tham số và khối. Ah, ruby. Cụ thể, tôi cần ghi đè Savon :: Client.request trong env dev của tôi cho một cuộc gọi API đến một máy chủ mà tôi có thể truy cập chỉ trong sản xuất. Chúc mừng! – pduey

Các vấn đề liên quan