2012-05-05 30 views
142

Tôi đang sử dụng Ruby on Rails 3.2.2 và tôi muốn biết nếu sau đây là một "đúng"/"đúng"/"chắc chắn" cách để ghi đè lên một phương pháp setter cho một thuộc tính lớp học của tôi.Cách đúng để ghi đè phương thức setter trong Ruby on Rails là gì?

attr_accessible :attribute_name 

def attribute_name=(value) 
    ... # Some custom operation. 

    self[:attribute_name] = value 
end 

Mã trên dường như hoạt động như mong đợi. Tuy nhiên, Tôi muốn biết nếu, bằng cách sử dụng mã trên, trong tương lai tôi sẽ có vấn đề hoặc, ít nhất, những vấn đề "tôi nên mong đợi"/"có thể xảy ra" với Ruby on Rails. Nếu đó không phải là cách đúng để ghi đè lên một phương thức setter, đúng cách là gì?


Note: Nếu tôi sử dụng mã

attr_accessible :attribute_name 

def attribute_name=(value) 
    ... # Some custom operation. 

    self.attribute_name = value 
end 

tôi nhận được lỗi sau:

SystemStackError (stack level too deep): 
    actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70 
+4

Tôi thích thuật ngữ áp dụng "đúng đắn"/"đúng"/"chắc chắn". Khi bạn cho nó 3 cách nó thực sự đảm bảo không có sự hiểu sai. Làm tốt lắm! – Jay

+5

@Jay - "Ý nghĩa tinh vi"; -) – Backo

+1

Chỉ cần được rõ ràng, "mức độ ngăn xếp quá sâu" là đề cập đến thực tế là một cuộc gọi đệ quy của nó ... gọi của chính nó. – Nippysaurus

Trả lời

237

============= ================================================== ============ Cập nhật: ngày 19 tháng 7 năm 2017

Bây giờ Rails documentation cũng là gợi ý để sử dụng super như thế này:

class Model < ActiveRecord::Base 

    def attribute_name=(value) 
    # custom actions 
    ### 
    super(value) 
    end 

end 

========================== =================================================

gốc trả lời

Nếu bạn muốn ghi đè các phương pháp setter cho các cột của một bảng trong khi truy cập thông qua các mô hình, này là cách để làm điều đó.

class Model < ActiveRecord::Base 
    attr_accessible :attribute_name 

    def attribute_name=(value) 
    # custom actions 
    ### 
    write_attribute(:attribute_name, value) 
    # this is same as self[:attribute_name] = value 
    end 

end 

Xem Overriding default accessors trong tài liệu Rails.

Vì vậy, phương pháp đầu tiên của bạn là cách đúng để ghi đè các bộ định vị cột trong Mô hình của Ruby on Rails. Các accessors này đã được Rails cung cấp để truy cập các cột của bảng dưới dạng các thuộc tính của mô hình. Đây là những gì chúng ta gọi là ánh xạ ORM ActiveRecord.

Cũng xin lưu ý rằng attr_accessible ở đầu mô hình không liên quan gì đến người truy cập. Nó có một functionlity hoàn toàn khác nhau (xem this question)

Nhưng trong tinh khiết Ruby, nếu bạn đã xác định các phụ kiện cho một lớp và muốn ghi đè lên các setter, bạn phải tận dụng biến Ví dụ như thế này:

class Person 
    attr_accessor :name 
end 

class NewPerson < Person 
    def name=(value) 
    # do something 
    @name = value 
    end 
end 

Điều này sẽ dễ hiểu hơn khi bạn biết những gì attr_accessor thực hiện. Mã attr_accessor :name tương đương với hai phương pháp (phương thức getter và setter)

def name # getter 
    @name 
end 

def name=(value) # setter 
    @name = value 
end 

Ngoài ra phương pháp thứ hai của bạn không thành công vì nó sẽ gây ra một vòng lặp vô hạn như bạn đang gọi cùng một phương pháp attribute_name= bên trong phương thức đó.

+8

Đối với Rails 4 chỉ cần bỏ qua 'attr_accessible' vì nó không còn ở đó nữa, và nó sẽ hoạt động – zigomir

+10

Tại sao không gọi' super'? –

+1

Tôi có ấn tượng rằng vì người truy cập và người viết được tạo động, 'super' có thể không hoạt động. Nhưng, có vẻ như nó không phải là trường hợp. Tôi chỉ kiểm tra nó và nó hoạt động cho tôi. Ngoài ra, [câu hỏi] này (http://stackoverflow.com/questions/373731/override-activerecord-attribute-methods) yêu cầu cùng một – rubyprince

3

Tôi đã phát hiện ra rằng (ít nhất là cho các bộ sưu tập mối quan hệ ActiveRecord) mẫu sau hoạt động:

has_many :specialties 

def specialty_ids=(values) 
    super values.uniq.first(3) 
end 

(này lấy 3 mục không trùng lặp đầu tiên trong mảng thông qua.)

+0

cảm ơn bạn rất nhiều;) – JustBasti

38

Sử dụng super keyword:

def attribute_name=(value) 
    super(value.some_custom_encode) 
end 

Ngược lại, để ghi đè lên người đọc:

def attribute_name 
    super.some_custom_decode 
end 
+1

Câu trả lời hay hơn IMO được chấp nhận vì nó giữ cho cuộc gọi phương thức bị giới hạn ở cùng một tên. Điều này bảo tồn hành vi ghi đè được thừa kế thành attribute_name = –

+0

Ghi đè phương thức getter đã trở nên nguy hiểm trong Rails 4.2 do thay đổi này: https://github.com/rails/rails/commit/787e22bb491bd8c36db1e9734261c4ce02c5c5fd Trình trợ giúp biểu mẫu trước đó sẽ gọi giá trị không được định dạng của trường và không gọi getter tùy chỉnh của bạn. Bây giờ họ gọi phương pháp của bạn, và do đó sẽ tạo ra kết quả khó hiểu trong các biểu mẫu của bạn tùy thuộc vào cách bạn ghi đè giá trị. –

15

Trong ray 4

let nói rằng bạn có tuổi thuộc tính trong bảng của bạn

def age=(dob) 
    now = Time.now.utc.to_date 
    age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1) 
    super(age) #must add this otherwise you need to add this thing and place the value which you want to save. 
    end 

Lưu ý: Đối với những người mới trong đường ray 4 bạn không cần phải xác định attr_accessible trong mô hình. Thay vào đó, bạn phải liệt kê các thuộc tính của mình ở cấp bộ điều khiển bằng cách sử dụng phương thức cho phép.

0

Sử dụng attr_writer ghi đè lên setter attr_writer: ATTRIBUTE_NAME

def attribute_name=(value) 
    # manipulate value 
    # then send result to the default setter 
    super(result) 
    end 
Các vấn đề liên quan