Skip to content

Instantly share code, notes, and snippets.

@isubas
Last active July 21, 2020 21:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isubas/92095e2474bcc8d18424ea690834fa12 to your computer and use it in GitHub Desktop.
Save isubas/92095e2474bcc8d18424ea690834fa12 to your computer and use it in GitHub Desktop.
Easy Initializer for Ruby Class
module EasyInitializer
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
def initialize_with(*attributes, **config)
named_params = _prepare_named_params config[:named_params]
getter = attributes + named_params.keys
# define accessors
instance_eval do
attr_reader(*getter)
attr_writer(*getter) if config.fetch(:writable, true)
config.fetch(:private_new_method, true) ? private_class_method(:new) : public_class_method(:new)
end
define_method :initialize do |*args|
params = attributes.dup
unless args.size >= params.size
raise ArgumentError, "Wrong number of arguments (given #{args.size}, expected #{params.size})"
end
params.size.times { instance_variable_set("@#{params.shift}", args.shift) }
options = args&.first || {}
named_params.each do |key, default|
instance_variable_set("@#{key}", options[key] || (default unless options.key?(key)))
end
__send__(config[:after]) if config.key?(:after)
end
end
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
def callable(enable: true, instance_method: :call)
return unless enable
define_singleton_method :call do |*args|
new(*args).public_send(instance_method)
end
end
private
def _prepare_named_params(named_params)
Array(named_params).each_with_object({}) do |item, hash|
hash.merge!(item.is_a?(Hash) ? item : { item => nil })
end
end
end
class Base
extend EasyInitializer
callable
end
class Foo < Base
initialize_with(
:foo, :bar,
named_params: [:params, test: 'default'],
after: :valid!,
writable: false,
private_new_method: false
)
def call
{
foo: foo,
bar: bar,
params: params,
test: test
}
end
private
def valid!
true
end
end
Foo.call('foo-value', 'bar-value', params: 'params-value')
# => {:foo=>"foo-value", :bar=>"bar-value", :params=>"params-value", :test=>"default"}
Foo.call('foo-value', 'bar-value', params: 'params-value', test: 'test-value')
# => {:foo=>"foo-value", :bar=>"bar-value", :params=>"params-value", :test=>"test-value"}
Foo.call('foo', params: 'params')
# => {:foo=>"foo", :bar=>{:params=>"params"}, :params=>nil, :test=>"default"}
Foo.call('foo')
# => ArgumentError: Wrong number of arguments (given 1, expected 2)
class Bar < Base
initialize_with :foo, named_params: [test: :test]
callable instance_method: :run
def run
{
foo: foo,
test: test
}
end
end
Bar.call('foo')
# => {:foo=>"foo", :test=>:test}
Bar.call('foo', test: 'value')
# => {:foo=>"foo", :test=>"value"}
Bar.new
# => NoMethodError: private method `new' called for Bar:Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment