DeclarativeInitialization
Boilerplate slows down devs and irritates everyone, plus the added cruft makes it harder to scan for the actual logic in a given file.
This is a small layer to support declarative initialization specifically for simple keyword-based classes.
Usage
Given a standard ruby class like so:
class SomeObject
def initialize(foo:, bar:, baz: "default value")
@foo = foo
@bar = bar
@baz = baz
end
attr_reader :foo, :bar, :baz
endWith this library it can be simplified to:
class SomeObject
include DeclarativeInitialization
initialize_with :foo, :bar, baz: "default value"
endQuick note on naming
The gem name is declarative_initialization because there's already a very outdated gem claiming the initialize_with name.
We've set up an alias, however, so you can do either include DeclarativeInitialization or include InitializeWith.
FWIW in practice at Teamshares we just include DeclarativeInitialization in the base class for all our View Components.
Custom logic
Sometimes the existing initialize method also does other work, for instance setting initial values for additional instance variables that aren't passed in directly.
We support that by passing an optional block to initialize_with -- for instance, in the example above if the original version also set @bang = foo * bar, we could support that by changing the updated version to:
initialize_with :foo, :bar, baz: "default value" do
@bang = @foo * @bar
endEdge cases
-
Accepting a block: this is handled automatically -- if a block was provided to the Foo.new call, it'll be made available as
@block/attr_reader :block -
If a method with same name already exists, we log a warning and do not create the
attr_reader. In that case you'll need to reference the instance variable directly.- Because of this, best practice when referencing variables in the post-initialize block is to use
@foorather than relying on thefooattr_reader
- Because of this, best practice when referencing variables in the post-initialize block is to use
-
Due to ruby syntax limitations, we do not support referencing other fields directly in the declaration:
- Does not work:
initialize_with :user, company: user.employer
- Workaround:
initialize_with :user, company: nil do @company ||= @user.employer end
- Does not work:
-
If using
initialize_withon a subclass where the superclass definesinitialize, we will not automatically callsuper, because if we do we get thisRuntimeError:implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly.
-
If you need to call
superfrom the block passed intoinitialize_with(unusual edge case, subclass requires different arguments than parent):- Does not work (due to
instance_execchanging execution context but not the method lookup chain):initialize_with :foo do super(bar: 123) end
- Workaround possible (but really, probably more understandable to just fall back to manually writing
def initialize):initialize_with :foo do parent_initialize = method(:initialize).super_method parent_initialize.call(bar: 123) end
- Does not work (due to
-
If you find yourself backed into a weird corner, just use a plain ole
def initialize! This library is meant to make the easy cases less work, but there's no requirement that you must use it for every super complex case you run into. :)
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/teamshares/declarative_initialization. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
Code of Conduct
Everyone interacting in the DeclarativeInitialization project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.