2012-04-19 33 views
29

Tôi đang chơi với Ruby on Rails và tôi đang cố tạo phương thức với các tham số tùy chọn. Rõ ràng có nhiều cách để làm điều đó. Tôi cố gắng đặt tên các tham số tùy chọn là băm, và không xác định chúng. Đầu ra là khác nhau. Hãy xem:Phương thức Ruby và tham số tùy chọn

# This functions works fine! 
def my_info(name, options = {}) 
    age = options[:age] || 27 
    weight = options[:weight] || 160 
    city = options[:city] || "New York" 
    puts "My name is #{name}, my age is #{age}, my weight is #{weight} and I live in {city}" 
end 


my_info "Bill" 
-> My name is Bill, my age is 27, my weight is 160 and I live in New York 
-> OK! 

my_info "Bill", age: 28 
-> My name is Bill, my age is 28, my weight is 160 and I live in New York 
-> OK! 

my_info "Bill", weight: 200 
-> My name is Bill, my age is 27, my weight is 200 and I live in New York 
-> OK! 

my_info "Bill", city: "Scottsdale" 
-> My name is Bill, my age is 27, my weight is 160 and I live in Scottsdale 
-> OK! 

my_info "Bill", age: 99, weight: 300, city: "Sao Paulo" 
-> My name is Bill, my age is 99, my weight is 300 and I live in Sao Paulo 
-> OK! 

**************************** 

# This functions doesn't work when I don't pass all the parameters 
def my_info2(name, options = {age: 27, weight: 160, city: "New York"}) 
    age = options[:age] 
    weight = options[:weight] 
    city = options[:city] 
    puts "My name is #{name}, my age is #{age}, my weight is #{weight} and I live in #{city}" 
end 

my_info2 "Bill" 
-> My name is Bill, my age is 27, my weight is 160 and I live in New York 
-> OK! 

my_info2 "Bill", age: 28 
-> My name is Bill, my age is 28, my weight is and I live in 
-> NOT OK! Where is my weight and the city?? 

my_info2 "Bill", weight: 200 
-> My name is Bill, my age is , my weight is 200 and I live in 
-> NOT OK! Where is my age and the city?? 

my_info2 "Bill", city: "Scottsdale" 
-> My name is Bill, my age is , my weight is and I live in Scottsdale 
-> NOT OK! Where is my age and my weight? 

my_info2 "Bill", age: 99, weight: 300, city: "Sao Paulo" 
-> My name is Bill, my age is 99, my weight is 300 and I live in Sao Paulo 
-> OK! 

Cách tiếp cận thứ hai cho tham số tùy chọn là gì? Phương pháp thứ hai chỉ hoạt động nếu tôi không vượt qua bất kỳ tham số tùy chọn nào hoặc nếu tôi vượt qua tất cả.

Tôi đang thiếu gì?

Trả lời

52

Cách đối số tùy chọn làm việc trong ruby ​​là bạn chỉ định một dấu bằng, và nếu không có đối số được truyền sau đó những gì bạn chỉ định được sử dụng. Vì vậy, nếu không có đối số thứ hai nào được chuyển trong ví dụ thứ hai, thì

{age: 27, weight: 160, city: "New York"} 

được sử dụng. Nếu bạn sử dụng cú pháp băm sau đối số đầu tiên, thì chính xác mã hóa được chuyển.

Điều tốt nhất bạn có thể làm là

def my_info2(name, options = {}) 
    options = {age: 27, weight: 160, city: "New York"}.merge(options) 
... 
+0

Đây là giải pháp cho vấn đề của tôi. Sau khi đọc lời giải thích của @ texasbruce về việc ghi đè giá trị băm ban đầu, có nghĩa là hợp nhất hàm băm "mặc định" với giá trị băm. Cảm ơn! –

+3

vì 'tùy chọn' sẽ được gán lại, bạn cũng có thể cập nhật tại chỗ:' options.reverse_update (tuổi: 27, trọng lượng: 160, thành phố: "New York") ' – tokland

2

Đối với các giá trị mặc định trong băm của bạn, bạn nên sử dụng này

def some_method(required_1, required_2, options={}) 
    defaults = { 
    :option_1 => "option 1 default", 
    :option_2 => "option 2 default", 
    :option_3 => "option 3 default", 
    :option_4 => "option 4 default" 
    } 
    options = defaults.merge(options) 

    # Do something awesome! 
end 
+2

Nếu bạn sử dụng merge() thay vì hợp nhất!() Theo cách bạn hiển thị ở đây, nó tạo ra và vứt bỏ băm mới thay vì sửa đổi băm tùy chọn hiện tại. – Winfield

9

Nếu bạn muốn để mặc định các giá trị trong các tùy chọn băm của bạn, bạn muốn kết hợp các giá trị mặc định trong chức năng của bạn. Nếu bạn đặt giá trị mặc định trong tham số mặc định riêng của mình, nó sẽ kết thúc bằng văn bản:

def my_info(name, options = {}) 
    options.reverse_merge!(age: 27, weight: 160, city: "New York") 

    ... 
end 
+0

Điều này không phải lúc nào cũng ghi đè các giá trị được truyền với mặc định? – Christian

+0

Có, phải được reverse_merge hoặc thay đổi thứ tự hợp nhất. – Winfield

+0

Giải pháp nhanh chóng và hiệu quả. Câu trả lời hay nhất cho tôi. +1 – konsolebox

4

Trong phương pháp thứ hai, khi bạn nói,

my_info2 "Bill", age: 28

Nó sẽ vượt qua {tuổi: 28}, và toàn bộ băm mặc định ban đầu {tuổi : 27, trọng lượng: 160, thành phố: "New York"} sẽ bị ghi đè. Đó là lý do tại sao nó không hiển thị đúng.

+0

Đây là giải thích tốt nhất. Cảm ơn bạn. –

0

Có phương thức method_missing trên mô hình ActiveRecord mà bạn có thể ghi đè để lớp của bạn tự động trả lời cuộc gọi trực tiếp. Dưới đây là một số đẹp blog post trên đó.

1

Để trả lời câu hỏi "tại sao?": Theo cách bạn đang gọi điện thoại chức năng của bạn,

my_info "Bill", age: 99, weight: 300, city: "Sao Paulo" 

được thực sự làm báo

my_info "Bill", {:age => 99, :weight => 300, :city => "Sao Paulo"} 

bạn đang đi qua hai tham số, "Bill" và một đối tượng băm, sẽ làm cho giá trị băm mặc định mà bạn đã cung cấp trong my_info2 sẽ bị bỏ qua hoàn toàn.

Bạn nên sử dụng phương pháp tiếp cận giá trị mặc định mà người trả lời khác đã đề cập.

1

#fetch là bạn của bạn!

class Example 
    attr_reader :age 
    def my_info(name, options = {}) 
    @age = options.fetch(:age, 27) 
    self 
    end 
end 

person = Example.new.my_info("Fred") 
puts person.age #27 
14

Vấn đề là giá trị mặc định của options là toàn bộ Hash trong phiên bản thứ hai bạn được đăng. Vì vậy, giá trị mặc định, toàn bộ Hash, bị ghi đè. Đó là lý do tại sao không chuyển được gì, bởi vì điều này kích hoạt giá trị mặc định là Hash và nhập tất cả chúng cũng hoạt động, bởi vì nó ghi đè giá trị mặc định bằng Hash của các khóa giống nhau.

Tôi khuyên bạn nên sử dụng một số Array để chụp tất cả các đối tượng bổ sung ở cuối cuộc gọi phương thức của bạn.

def my_info(name, *args) 
    options = args.extract_options! 
    age = options[:age] || 27 
end 

Tôi đã học được mẹo này từ việc đọc qua nguồn cho Rails. Tuy nhiên, lưu ý rằng điều này chỉ hoạt động nếu bạn bao gồm ActiveSupport. Hoặc, nếu bạn không muốn chi phí của toàn bộ đá quý ActiveSupport, chỉ cần sử dụng hai phương thức được thêm vào HashArray để thực hiện điều này.

ray/activesupport/lib/active_support/core_ext/mảng/ extract_options.rb

Vì vậy, khi bạn gọi phương pháp của bạn, hãy gọi nó giống như bạn sẽ bất kỳ phương pháp helper Rails khác với các tùy chọn bổ sung.

my_info "Ned Stark", "Winter is coming", :city => "Winterfell" 
+1

Đây là một mô hình tuyệt vời, cảm ơn bạn. Khi bạn kết hợp điều này với các phạm vi chuỗi bị lỗi một cách duyên dáng khi nil thông qua '' 'scoped''' bạn có thể thực hiện một số truy vấn mạnh mẽ. – Kelseydh

0

Tôi không thấy điều gì sai khi sử dụng toán tử hoặc toán tử để đặt mặc định. Dưới đây là một ví dụ thực tế đời sống (lưu ý, sử dụng image_tag phương pháp đường ray):

file:

def gravatar_for(user, options = {}) 
    height = options[:height] || 90 
    width = options[:width] || 90 
    alt = options[:alt] || user.name + "'s gravatar" 

    gravatar_address = 'http://1.gravatar.com/avatar/' 

    clean_email = user.email.strip.downcase 
    hash = Digest::MD5.hexdigest(clean_email) 

    image_tag gravatar_address + hash, height: height, width: width, alt: alt 
end 

console:

2.0.0-p247 :049 > gravatar_for(user) 
=> "<img alt=\"jim&#39;s gravatar\" height=\"90\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"90\" />" 
2.0.0-p247 :049 > gravatar_for(user, height: 123456, width: 654321) 
=> "<img alt=\"jim&#39;s gravatar\" height=\"123456\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"654321\" />" 
2.0.0-p247 :049 > gravatar_for(user, height: 123456, width: 654321, alt: %[dogs, cats, mice]) 
=> "<img alt=\"dogs cats mice\" height=\"123456\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"654321\" />" 

Nó cảm thấy tương tự như sử dụng phương pháp khởi tạo khi gọi một lớp.

0

Tại sao không chỉ sử dụng nil?

def method(required_arg, option1 = nill, option2 = nil) 
    ... 
end 
+0

một băm làm cho một mặc định tốt hơn nếu bạn có kế hoạch truy cập nó như một băm. option1 [: undefined_key] trả về nil nếu nó là một băm rỗng, nhưng làm tăng một ngoại lệ nếu nil. – jay

1

Bạn cũng có thể xác định chữ ký phương pháp với đối số từ khóa (mới kể từ, Ruby 2,0, vì câu hỏi này là cũ):

def my_info2(name, age: 27, weight: 160, city: "New York", **rest_of_options) 
    p [name, age, weight, city, rest_of_options] 
end 

my_info2('Joe Lightweight', weight: 120, age: 24, favorite_food: 'crackers') 

Điều này cho phép như sau:

  • Tùy chọn thông số (:weight:age)
  • Giá trị mặc định
  • trật tự Arbitrary các thông số
  • giá trị phụ thu được trong một hash sử dụng splat kép (:favorite_food thu được trong rest_of_options)
Các vấn đề liên quan