Optioning
An easy way to retrieve, store, filter, transform and deprecate options passed
to a method. Where options are the keys in our beloved Hash as last parameter
in a method call.
Status
Installation
Add this line to your application's Gemfile:
gem 'optioning'
And then execute:
$ bundle
Or install it yourself as:
$ gem install optioning
Contact
- API Doc: http://rdoc.info/gems/optioning
- Bugs, issues and feature requests: https://github.com/ricardovaleriano/optioning/issues
- Support: http://stackoverflow.com/questions/tagged/optioning-ruby
Usage
An "end to end" example
Given a Hashing module, with a .hasherize method like this:
module Hashing
def hasherize(*)
optioning = Optioning.new
optioning.deprecate :to_hash, :to, "v2.0.0"
optioning.recognize :persist
optioning.process caller
puts "*" * 80
puts optioning.on :to
puts optioning.on :persist
end
end
And a class Client extending the Hashing and invoking the .hasherize
method like in this example:
require "./hashing"
class Client
extend Hashing
hasherize :some_ivar, to_hash: ->(){}, store: "NO!", persist: "I will persist!"
end
The following output will be generated:
NOTE: option `:to_hash` is deprecated; use `:to` instead. It will be removed on or after version v2.0.0.
Called from examples/client_maroto.rb:5:in `<class:Client>'.
NOTE: unrecognized option `:store` used.
You should use only the following: `:to`, `:persist`
Called from examples/client_maroto.rb:5:in `<class:Client>'.
********************************************************************************
#<Proc:0x007fd6f42749a8@examples/client_maroto.rb:5 (lambda)>
I will persist!
The warning messages will be written in the $stderr. Done! :smiley:
A more step by step example
Given the following File class:
class File
def initialize(path, commit = nil, content = nil)
@path, @commit, @content = path, commit, content
end
end
And a module called Hashing, which defines a method .serialize that allow
you configure which ivars should be used to convert instances of File into a
Hash like this:
{
path: @path,
commit: @commit,
content: @content
}
And I can configure a transformation in the values of path, commit and
content when transforming it into a Hash like so:
require 'hashing'
class File
extend Hashing
hasherize :path, :commit, to_hash: ->(value) { value.downcase }
end
As the implementor of this module and the .hasherize method, I want to be able
to use an instance of Optioning, so I can store and retrieve the ivars and
the options passed to be used along those ivars:
module Hashing
def hasherize(*)
@options = Optioning.new
# ...
end
end
Now in the Optioning instance, I can call the following (among others)
methods:
@options.raw
# => [:path, :commit, {to_hash: #<Proc:0x007fa4120bd318@(irb):42 (lambda)>}]
@options.values
# => [:path, :commit]
@options.on :to_hash
# => #<Proc:0x007fa4120bd318@(irb):42 (lambda)>
Deprecating options
Now, following our example, if you need to deprecat the :to_hash option in
favor of the new :to option, you could do:
def hasherize(*)
@options = Optioning.new
@options.deprecate :to_hash, :to
# ...
end
This will replace the deprecated option :to_hash for the new one named :to
so you can do the following invocation to recover the value passed to the
deprecated option:
@options.on :to
# => #<Proc:0x007fa4120bd318@(irb):42 (lambda)>
Deprecation warnings
You can alert your user about those deprecations using the #deprecated_warn
method:
def hasherize(*)
@options = Optioning.new
@options.deprecate :to_hash, :to
@options.deprecation_warn
# ...
end
You can inform the date when the deprecation will not be available anymore. These date will be part of the deprecation message:
@options.deprecate :to_hash, :to, 2015, 05
@options.deprecation_warn
# => NOTE: option `:to_hash` is deprecated use `:to` instead. It will be
# removed on or after 2015-05-01."
Or if you prefer, you can specify a version of your software that pretend to remove the deprecated thing:
@options.deprecate :to_hash, :to, "v2.0.0"
@options.deprecation_warn
# => NOTE: option `:to_hash` is deprecated use `:to` instead. It will be
# removed on or after version v2.0.0"
And finally, you can add information about where te deprecated option was used
by passing the caller to the deprecation_warn method.
Caller info
Sometimes you will want to show information about where the call with the
deprecated option took place. If this is the case, you can pass the caller info
when instantiating the Optioning:
def hasherize(*)
@options = Optioning.new
@options.deprecate :to_hash, :to
@options.deprecated_warn caller
# ...
end
Calling a deprecated option
If you call a deprecated option, the return will be nil, and the deprecation
warning will be exhibited.
- [ ] maybe we should allow a deprecation strategy? To choose between warning or exception when a deprecated options is called?
Ignoring unrecongnized options
If you need, you could fitler the options to mantain just the recognized ones
available. To configure the options that matters to your program, use the method
#recognize. And to warn the user in case a unrecognized option is used, call
the #unrecognized_warn method:
def hasherize(*)
@options = Optioning.new
@options.recognize :from
@options.unrecognized_warn
# ...
end
Now, if a user pass an option different than the :from one, a warning will
inform that the option is not recognized and will be ignored.
Do I Need to register deprecated options as recognized?
Fortunately no. You just need to register your deprecations as usual:
def hasherize(*)
@options = Optioning.new
@options.recognize :from
@options.deprecate :to_hash, :to
@options.deprecated_warn
@options.unrecognized_warn
# ...
end
The #deprecate method already knows what to do (that is register the option
:to_hash as recognized. To sum up, in this last example, the options :from
and :to are already recongnized by the Optioning instance.
#process
The #process method will replace all deprecations, warn about them and warn
about unrecognized options all at once, so you can use it like this:
def hasherize(*)
@options = Optioning.new
@options.recognize :from
@options.deprecate :to_hash, :to
@options.process
# ...
end
If you want the deprecation warning messages with the information about where
the deprecated options were passed, you can pass the caller info to the
process method:
@options.process caller
Fluent interface
And finally, just for a matter of taste, #deprecate, #recognize and
#process returns the Optioning instance itself, so you can write the last
example like this (if you want)
def hasherize(*)
@options = Optioning.new().recognize(:from)
@options.deprecate(:to_hash, :to).process
# ...
end
Contributing
- Fork it ( http://github.com/ricardovaleriano/optioning/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request

