해시 인수를 사용한 DRY Ruby 초기화
특히 구성을위한 DSL 또는 최종 사용자가 노출 될 다른 API 비트를 작성할 때 생성자에 해시 인수를 사용하는 경우가 많습니다. 내가 끝내는 것은 다음과 같은 것입니다.
class Example
PROPERTIES = [:name, :age]
PROPERTIES.each { |p| attr_reader p }
def initialize(args)
PROPERTIES.each do |p|
self.instance_variable_set "@#{p}", args[p] if not args[p].nil?
end
end
end
이것을 달성하는 더 이상 관용적 인 방법이 없습니까? 버리는 상수와 기호에서 문자열로의 변환은 특히 심각해 보입니다.
상수는 필요하지 않지만 기호-문자열을 제거 할 수 있다고 생각하지 않습니다.
class Example
attr_reader :name, :age
def initialize args
args.each do |k,v|
instance_variable_set("@#{k}", v) unless v.nil?
end
end
end
#=> nil
e1 = Example.new :name => 'foo', :age => 33
#=> #<Example:0x3f9a1c @name="foo", @age=33>
e2 = Example.new :name => 'bar'
#=> #<Example:0x3eb15c @name="bar">
e1.name
#=> "foo"
e1.age
#=> 33
e2.name
#=> "bar"
e2.age
#=> nil
BTW, Struct
클래스 생성기 클래스를 살펴볼 수 있습니다 (아직 보지 않은 경우) , 수행하는 것과 다소 비슷하지만 해시 유형 초기화는 없습니다 (하지만 적절한 생성기를 만드는 것은 어렵지 않을 것 같습니다) 수업).
HasProperties
hurikhan의 아이디어를 구현하려고 시도한 결과 다음과 같습니다.
module HasProperties
attr_accessor :props
def has_properties *args
@props = args
instance_eval { attr_reader *args }
end
def self.included base
base.extend self
end
def initialize(args)
args.each {|k,v|
instance_variable_set "@#{k}", v if self.class.props.member?(k)
} if args.is_a? Hash
end
end
class Example
include HasProperties
has_properties :foo, :bar
# you'll have to call super if you want custom constructor
def initialize args
super
puts 'init example'
end
end
e = Example.new :foo => 'asd', :bar => 23
p e.foo
#=> "asd"
p e.bar
#=> 23
메타 프로그래밍에 능숙하지 않기 때문에 누구나 자유롭게 구현을 변경할 수 있도록 답변 커뮤니티 위키를 만들었습니다.
Struct.hash_initialized
Marc-Andre의 답변을 확장 Struct
하면 해시 초기화 클래스를 만드는 일반적인 기반 방법이 있습니다.
class Struct
def self.hash_initialized *params
klass = Class.new(self.new(*params))
klass.class_eval do
define_method(:initialize) do |h|
super(*h.values_at(*params))
end
end
klass
end
end
# create class and give it a list of properties
MyClass = Struct.hash_initialized :name, :age
# initialize an instance with a hash
m = MyClass.new :name => 'asd', :age => 32
p m
#=>#<struct MyClass name="asd", age=32>
Struct
제공자 라이선스 계약은 이러한 클래스를 구축 할 수 있습니다. 이니셜 라이저는 해시가 아닌 하나씩 인수를 취하지 만 변환하기 쉽습니다.
class Example < Struct.new(:name, :age)
def initialize(h)
super(*h.values_at(:name, :age))
end
end
좀 더 일반적인 상태로 유지하려면 values_at(*self.class.members)
대신 전화 할 수 있습니다 .
루비에는 이런 일을하는 데 유용한 몇 가지가 있습니다. OpenStruct 클래스는 초기화 메소드에 전달 된 값을 클래스의 속성으로 사용할 수 있도록합니다.
require 'ostruct'
class InheritanceExample < OpenStruct
end
example1 = InheritanceExample.new(:some => 'thing', :foo => 'bar')
puts example1.some # => thing
puts example1.foo # => bar
문서는 여기에 있습니다 : http://www.ruby-doc.org/stdlib-1.9.3/libdoc/ostruct/rdoc/OpenStruct.html
OpenStruct에서 상속하지 않으려면 (또는 이미 다른 것을 상속하고 있기 때문에 상속 할 수없는 경우) 어떻게해야합니까? Forwardable을 사용하여 모든 메서드 호출을 OpenStruct 인스턴스에 위임 할 수 있습니다.
require 'forwardable'
require 'ostruct'
class DelegationExample
extend Forwardable
def initialize(options = {})
@options = OpenStruct.new(options)
self.class.instance_eval do
def_delegators :@options, *options.keys
end
end
end
example2 = DelegationExample.new(:some => 'thing', :foo => 'bar')
puts example2.some # => thing
puts example2.foo # => bar
Forwardable 용 문서는 여기에 있습니다 : http://www.ruby-doc.org/stdlib-1.9.3/libdoc/forwardable/rdoc/Forwardable.html
해시에이 포함 ActiveSupport::CoreExtensions::Hash::Slice
되어 있으면 매우 좋은 솔루션이 있습니다.
class Example
PROPERTIES = [:name, :age]
attr_reader *PROPERTIES #<-- use the star expansion operator here
def initialize(args)
args.slice(PROPERTIES).each {|k,v| #<-- slice comes from ActiveSupport
instance_variable_set "@#{k}", v
} if args.is_a? Hash
end
end
이를 포함 할 수 있고 속성을 설정하고 적절한 초기화를 수행하기 위해 "has_properties"메서드를 정의하는 일반 모듈로 추상화합니다 (이는 테스트되지 않았으며 의사 코드로 간주).
module HasProperties
def self.has_properties *args
class_eval { attr_reader *args }
end
def self.included base
base.extend InstanceMethods
end
module InstanceMethods
def initialize(args)
args.slice(PROPERTIES).each {|k,v|
instance_variable_set "@#{k}", v
} if args.is_a? Hash
end
end
end
My solution is similar to Marc-André Lafortune. The difference is that each value is deleted from the input hash as it is used to assign a member variable. Then the Struct-derived class can perform further processing on whatever may be left in the Hash. For instance, the JobRequest below retains any "extra" arguments from the Hash in an options field.
module Message
def init_from_params(params)
members.each {|m| self[m] ||= params.delete(m)}
end
end
class JobRequest < Struct.new(:url, :file, :id, :command, :created_at, :options)
include Message
# Initialize from a Hash of symbols to values.
def initialize(params)
init_from_params(params)
self.created_at ||= Time.now
self.options = params
end
end
Please take a look at my gem, Valuable:
class PhoneNumber < Valuable
has_value :description
has_value :number
end
class Person < Valuable
has_value :name
has_value :favorite_color, :default => 'red'
has_value :age, :klass => :integer
has_collection :phone_numbers, :klass => PhoneNumber
end
jackson = Person.new(name: 'Michael Jackson', age: '50', phone_numbers: [{description: 'home', number: '800-867-5309'}, {description: 'cell', number: '123-456-7890'})
> jackson.name
=> "Michael Jackson"
> jackson.age
=> 50
> jackson.favorite_color
=> "red"
>> jackson.phone_numbers.first
=> #<PhoneNumber:0x1d5a0 @attributes={:description=>"home", :number=>"800-867-5309"}>
I use it for everything from search classes (EmployeeSearch, TimeEntrySearch) to reporting ( EmployeesWhoDidNotClockOutReport, ExecutiveSummaryReport) to presenters to API endpoints. If you add some ActiveModel bits you can easily hook these classes up to forms for gathering criteria. I hope you find it useful.
ReferenceURL : https://stackoverflow.com/questions/2680523/dry-ruby-initialization-with-hash-argument
'programing' 카테고리의 다른 글
wkhtmltopdf를 호출하여 HTML에서 PDF 생성 (0) | 2021.01.17 |
---|---|
쉘에서 .bashrc에 정의 된 함수를 어떻게 호출합니까? (0) | 2021.01.17 |
맞춤 주문 설명 (0) | 2021.01.17 |
Django-AttributeError 'User'객체에는 'backend'속성이 없습니다 (하지만… (0) | 2021.01.17 |
SDK 도구, 버전 12에서 Android 에뮬레이터 시작 (0) | 2021.01.17 |