Initialize a Ruby class from an arbitrary hash, but only keys with matching accessors

后端 未结 6 600
遇见更好的自我
遇见更好的自我 2021-01-01 04:00

Is there a simple way to list the accessors/readers that have been set in a Ruby Class?

class Test
  attr_reader :one, :two

  def initialize
    # Do someth         


        
6条回答
  •  一个人的身影
    2021-01-01 04:25

    This is what I use (I call this idiom hash-init).

     def initialize(object_attribute_hash = {})
      object_attribute_hash.map { |(k, v)| send("#{k}=", v) }
     end
    

    If you are on Ruby 1.9 you can do it even cleaner (send allows private methods):

     def initialize(object_attribute_hash = {})
      object_attribute_hash.map { |(k, v)| public_send("#{k}=", v) }
     end
    

    This will raise a NoMethodError if you try to assign to foo and method "foo=" does not exist. If you want to do it clean (assign attrs for which writers exist) you should do a check

     def initialize(object_attribute_hash = {})
      object_attribute_hash.map do |(k, v)| 
        writer_m = "#{k}="
        send(writer_m, v) if respond_to?(writer_m) }
      end
     end
    

    however this might lead to situations where you feed your object wrong keys (say from a form) and instead of failing loudly it will just swallow them - painful debugging ahead. So in my book a NoMethodError is a better option (it signifies a contract violation).

    If you just want a list of all writers (there is no way to do that for readers) you do

     some_object.methods.grep(/\w=$/)
    

    which is "get an array of method names and grep it for entries which end with a single equals sign after a word character".

    If you do

      eval("@#{opt} = \"#{val}\"")
    

    and val comes from a web form - congratulations, you just equipped your app with a wide-open exploit.

提交回复
热议问题