- Strategy Matcher: custom matcher support for selecting strategies (e.g. implement a fuzz matcher for picking a strategy by full string name or any partial match in case it is typed by a user)
- Strategy Alias: specify an alias for selecting a strategy (e.g. car strategy has
"sedan"as an alias)
- Strategy Exclusion: exclude a strategy from a matcher (e.g. partial match on
'US', but not
To give you some background, the Strategic Ruby gem was mostly born out of work I did last year at Chronogolf by Lightspeed, a golf course management web app built in Ruby on Rails, where we had countless of strategies for customizing models, especially in relation to quotes, pricing, payments, memberships, and golf course tee time reservations.
It is currently used in the DCR Programming Language, implementing language commands (Command Pattern) as a special case of Strategy Pattern, with auto-inference of strategy names from command file names by convention.
Strategic enables you to make any existing domain model "strategic", externalizing all logic concerning algorithmic variations into separate strategy classes that are easy to find, maintain and extend while honoring the Open/Closed Principle and avoiding conditionals.
In summary, if you make a class called
TaxCalculator strategic by including the
Strategic mixin module, now you are able to drop strategies under the
tax_calculator directory sitting next to the class (e.g.
tax_calculator/canada_strategy.rb) while gaining extra API methods to grab strategy names to present in a user interface, grab strategy classes to select, and/or instantiate
TaxCalculator directly with a strategy from the get-go.
1- Include the
Strategic module in the Class to strategize
class TaxCalculator include Strategic # strategies may implement a tax_for(amount) method end
2- Now, you can add strategies under this directory without having to modify the original class:
3- Add strategy classes having names ending with
Strategy by convention (e.g.
UsStrategy) under the namespace matching the original class name (
TaxCalculator:: as in
TaxCalculator::UsStrategy) and including the module (
class TaxCalculator::UsStrategy include Strategic::Strategy def tax_for(amount) amount * state_rate(context.state) end # ... other strategy methods follow end class TaxCalculator::CanadaStrategy include Strategic::Strategy def tax_for(amount) amount * (gst(context.province) + qst(context.province)) end # ... other strategy methods follow end
(note: if you use strategy inheritance hierarchies, make sure to have strategy base classes end with StrategyBase to avoid getting picked up as strategies)
4- In client code, set the strategy by underscored string reference minus the word strategy (e.g. UsStrategy becomes simply 'us'):
tax_calculator = TaxCalculator.new(args) tax_calculator.strategy = 'us'
4a. Alternatively, instantiate the strategic model with a strategy to begin with:
tax_calculator = TaxCalculator.new_with_strategy('us', args)
5- Invoke the strategy implemented method:
tax = tax_calculator.tax_for(39.78)
Default strategy for a strategy name that has no strategy class is nil
You may set a default strategy on a strategic model via class method default_strategy
class TaxCalculator include Strategic default_strategy 'canada' end tax_calculator = TaxCalculator.new(args) tax = tax_calculator.tax_for(39.78)
Learn more at: