How to cleanly initialize attributes in Ruby with new?

前端 未结 7 442
夕颜
夕颜 2020-12-06 04:17
class Foo
  attr_accessor :name, :age, :email, :gender, :height

  def initalize params
    @name = params[:name]
    @age = params[:age]
    @email = params[:email]         


        
相关标签:
7条回答
  • 2020-12-06 04:45

    If you are receiving a hash as the sole argument, why not just keep that as an instance variable? Whenever you need a value, call it from the hash. You can keep the instance variable name short so that it can be easily called.

    class Foo
      attr_reader :p
      def initalize p
        @p = p
      end
      def foo
        do_something_with(@p[:name])
        ...
      end
    end
    

    If @p[:name] is still too lengthy for you, then you can save a proc as an instance variable, and call the relevant value like @p.(:name).

    class Foo
      attr_reader :p
      def initialize p
        @p = ->x{p[x]}
      end
      def foo
        do_something_with(@p.(:name))
        ...
      end
    end
    

    Or, still an alternative way is to define a method that calls the hash and applies the key.

    class Foo
      def initalize p
        @p = p
      end
      def get key
        @p[key]
      end
      def foo
        do_something_with(get(:name))
        ...
      end
    end
    

    If want to set the values, you can define a setter method, and further check for invalid keys if you want.

    class Foo
      Keys = [:name, :age, :email, :gender, :height]
      def initalize p
        raise "Invalid key in argument" unless (p.keys - Keys).empty?
        @p = p
      end
      def set key, value
        raise "Invalid key" unless Keys.key?(key)
        @p[key] = value
      end
      def get key
        @p[key]
      end
      def foo
        do_something_with(get(:name))
        ...
      end
    end
    
    0 讨论(0)
  • 2020-12-06 04:48
    def initialize(params)
      params.each do |key, value|
        instance_variable_set("@#{key}", value)
      end
    end
    
    0 讨论(0)
  • 2020-12-06 04:53
    Foo = Struct.new(:name, :age, :email, :gender, :height)
    

    This is enough for a fully functioning class. Demo:

    p Foo.class # Class
    
    employee = Foo.new("smith", 29, "smith@foo.com", "m", 1.75) #create an instance
    p employee.class # Foo
    p employee.methods.sort # huge list which includes name, name=, age, age= etc
    
    0 讨论(0)
  • 2020-12-06 05:00

    To capitalize on Joshua Cheek's answer with a bit of generalization

    module Initializable
      def initialize(params = {})
        params.each do |key, value|
          setter = "#{key}="
          send(setter, value) if respond_to?(setter.to_sym, false)
        end
      end
    end
    
    class Foo
      include Initializable
    
      attr_accessor :name, :age, :email, :gender, :height
    end
    
    Foo.new name: 'Josh', age: 456
    => #<Foo:0x007fdeac02ecb0 @name="Josh", @age=456>
    

    NB If the initialization mix-in has been used and we need custom initialization, we'd just call super:

    class Foo
      include Initializable
    
      attr_accessor :name, :age, :email, :gender, :height, :handler
    
      def initialize(*)
        super
    
        self.handler = "#{self.name} #{self.age}"
      end
    end
    
    Foo.new name: 'Josh', age: 45
    => #<Foo:0x007fe94c0446f0 @name="Josh", @age=45, @handler="Josh 45"> 
    
    0 讨论(0)
  • 2020-12-06 05:00

    Why not just explicitly specify an actual list of arguments?

    class Foo
      attr_accessor :name, :age, :email, :gender, :height
    
      def initialize(name, age, email, gender, height)
        @name = name
        @age = age
        @email = email
        @gender = gender
        @height = height
      end
    end
    

    This version may be more lines of code than others, but it makes it easier to leverage built-in language features (e.g. default values for arguments or raising errors if initialize is called with incorrect arity).

    0 讨论(0)
  • 2020-12-06 05:01

    You can just iterate over the keys and invoke the setters. I prefer this, because it will catch if you pass an invalid key.

    class Foo
      attr_accessor :name, :age, :email, :gender, :height
    
      def initialize params = {}
        params.each { |key, value| send "#{key}=", value }
      end
    end
    
    foo = Foo.new name: 'Josh', age: 456
    foo.name  # => "Josh"
    foo.age   # => 456
    foo.email # => nil
    
    0 讨论(0)
提交回复
热议问题