Last active
July 21, 2020 21:36
-
-
Save isubas/92095e2474bcc8d18424ea690834fa12 to your computer and use it in GitHub Desktop.
Easy Initializer for Ruby Class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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