strategic 0.9.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +91 -58
- data/lib/strategic.rb +43 -26
- data/lib/strategic/strategy.rb +65 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8ddc7e63dc608ac0a7e97ebb9110431c9a490e7ec07abed6936bb347b6105e8
|
4
|
+
data.tar.gz: 4ca98f38d3933fc5f33ac460dc2767a2769cfbe0a870c9f48bfb427ca2a4c1c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '067224647109cf8198006ddce287755a1ec9997ff67fc8a3099d4f172ba6549a8e9ef4f8bf285d44996aa3a5d6ef4949892dffab5a1d26918d17cdc27cbd6e0b'
|
7
|
+
data.tar.gz: 1046857c6fd46e0615ab559bbb4e84bc8ba70f7d7995bd621e7d182316d603d22e4d99e2fefc1adce3d2682769519e0cc9189bef7ce614d0aca8354271994e05
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.0.0
|
4
|
+
|
5
|
+
- Improve design to better match the authentic Gang of Four Strategy Pattern with `Strategic::Strategy` module, removing the need for inheritance.
|
6
|
+
- `#strategy=`/`#strategy` enable setting/getting strategy on model
|
7
|
+
- `#context` enables getting strategic model instance on strategy just as per the GOF Design Pattern
|
8
|
+
- `default_strategy` class body method to set default strategy
|
9
|
+
- Filter strategies by ones ending with `Strategy` in class name
|
10
|
+
|
3
11
|
## 0.9.1
|
4
12
|
|
5
13
|
- `strategy_name` returns parsed strategy name of current strategy class
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Strategic 0.
|
1
|
+
# Strategic 1.0.0
|
2
2
|
## Painless Strategy Pattern in Ruby and Rails
|
3
3
|
[](http://badge.fury.io/rb/strategic)
|
4
4
|
[](https://github.com/AndyObtiva/strategic/actions/workflows/ruby.yml)
|
@@ -7,9 +7,8 @@
|
|
7
7
|
|
8
8
|
`if`/`case` conditionals can get really hairy in highly sophisticated business domains.
|
9
9
|
Object-oriented inheritance helps remedy the problem, but dumping all
|
10
|
-
logic variations in subclasses can cause a maintenance nightmare.
|
11
|
-
Thankfully, the Strategy Pattern as per the [Gang of Four book](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) solves the problem by externalizing logic to
|
12
|
-
separate classes outside the domain models.
|
10
|
+
logic variations in domain model subclasses can cause a maintenance nightmare.
|
11
|
+
Thankfully, the Strategy Pattern as per the [Gang of Four book](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) solves the problem by externalizing logic via composition to separate classes outside the domain models.
|
13
12
|
|
14
13
|
Still, there are a number of challenges with "repeated implementation" of the Strategy Pattern:
|
15
14
|
- Making domain models aware of newly added strategies without touching their
|
@@ -27,7 +26,9 @@ code (Open/Closed Principle).
|
|
27
26
|
|
28
27
|
`Strategic` enables you to make any existing domain model "strategic",
|
29
28
|
externalizing all logic concerning algorithmic variations into separate strategy
|
30
|
-
classes that are easy to find, maintain and extend while honoring the Open/Closed Principle.
|
29
|
+
classes that are easy to find, maintain and extend while honoring the Open/Closed Principle and avoiding conditionals.
|
30
|
+
|
31
|
+
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/us_strategy.rb`, `tax_calculator/canada_strategy.rb`) while gaining extra [API](#api) methods to grab strategy names to present in a user interface (`.strategy_names`), set a strategy (`#strategy=(strategy_name)`), and/or instantiate `TaxCalculator` directly with a strategy from the get-go (`.new_with_strategy(strategy_name, *initialize_args)`). Finally, you can simply invoke strategy methods on the main strategic model (e.g. `tax_calculator.tax_for(39.78)`).
|
31
32
|
|
32
33
|
### Example
|
33
34
|
|
@@ -40,71 +41,74 @@ alt="Strategic Example" />
|
|
40
41
|
class TaxCalculator
|
41
42
|
include Strategic
|
42
43
|
|
43
|
-
|
44
|
-
amount * 0.09
|
45
|
-
end
|
44
|
+
# strategies may implement a tax_for(amount) method
|
46
45
|
end
|
47
46
|
```
|
48
47
|
|
49
48
|
2. Now, you can add strategies under this directory without having to modify the original class: `tax_calculator`
|
50
49
|
|
51
|
-
3. Add strategy classes under the namespace matching the original class name (`TaxCalculator
|
50
|
+
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 `tax_calculator/us_strategy.rb` representing `TaxCalculator::UsStrategy`) and including the module (`Strategic::Strategy`):
|
51
|
+
|
52
|
+
All strategies get access to their context (strategic model instance), which they can use in their logic.
|
52
53
|
|
53
54
|
```ruby
|
54
|
-
class TaxCalculator::UsStrategy
|
55
|
-
|
56
|
-
|
57
|
-
end
|
55
|
+
class TaxCalculator::UsStrategy
|
56
|
+
include Strategic::Strategy
|
57
|
+
|
58
58
|
def tax_for(amount)
|
59
|
-
amount * state_rate
|
59
|
+
amount * state_rate(context.state)
|
60
60
|
end
|
61
|
-
# ...
|
61
|
+
# ... other strategy methods follow
|
62
62
|
end
|
63
63
|
|
64
|
-
class TaxCalculator::CanadaStrategy
|
65
|
-
|
66
|
-
|
67
|
-
end
|
64
|
+
class TaxCalculator::CanadaStrategy
|
65
|
+
include Strategic::Strategy
|
66
|
+
|
68
67
|
def tax_for(amount)
|
69
|
-
amount * (gst + qst)
|
68
|
+
amount * (gst(context.province) + qst(context.province))
|
70
69
|
end
|
71
|
-
# ...
|
70
|
+
# ... other strategy methods follow
|
72
71
|
end
|
73
72
|
```
|
74
73
|
|
75
|
-
|
74
|
+
(note: if you use strategy inheritance hierarchies, make sure to have strategy base classes end with `StrategyBase` to avoid getting picked up as strategies)
|
76
75
|
|
77
|
-
|
78
|
-
tax_calculator_strategy_class = TaxCalculator.strategy_class_for('us')
|
79
|
-
```
|
80
|
-
|
81
|
-
5. Instantiate the strategy object:
|
76
|
+
4. In client code, set the strategy by underscored string reference minus the word strategy (e.g. UsStrategy becomes simply 'us'):
|
82
77
|
|
83
78
|
```ruby
|
84
|
-
|
79
|
+
tax_calculator = TaxCalculator.new(args)
|
80
|
+
tax_calculator.strategy = 'us'
|
85
81
|
```
|
86
82
|
|
87
|
-
|
83
|
+
4a. Alternatively, instantiate the strategic model with a strategy to begin with:
|
88
84
|
|
89
85
|
```ruby
|
90
|
-
|
86
|
+
tax_calculator = TaxCalculator.new_with_strategy('us', args)
|
91
87
|
```
|
92
88
|
|
93
|
-
|
89
|
+
5. Invoke the strategy implemented method:
|
94
90
|
|
95
91
|
```ruby
|
96
|
-
|
97
|
-
tax = tax_calculator_strategy.tax_for(39.78)
|
92
|
+
tax = tax_calculator.tax_for(39.78)
|
98
93
|
```
|
99
94
|
|
100
95
|
**Default strategy for a strategy name that has no strategy class is the superclass: `TaxCalculator`**
|
101
96
|
|
97
|
+
You may set a default strategy on a strategic model via class method `default_strategy`
|
98
|
+
|
102
99
|
```ruby
|
103
|
-
|
104
|
-
|
105
|
-
|
100
|
+
class TaxCalculator
|
101
|
+
include Strategic
|
102
|
+
|
103
|
+
default_strategy 'canada'
|
104
|
+
end
|
105
|
+
|
106
|
+
tax_calculator = TaxCalculator.new(args)
|
107
|
+
tax = tax_calculator.tax_for(39.78)
|
106
108
|
```
|
107
109
|
|
110
|
+
If no strategy is selected and you try to invoke a method that belongs to strategies, Ruby raises an amended method missing error informing you that no strategy is set to handle the method (in case it was a strategy method).
|
111
|
+
|
108
112
|
## Setup
|
109
113
|
|
110
114
|
### Option 1: Bundler
|
@@ -112,7 +116,7 @@ tax = tax_calculator_strategy.tax_for(100.0) # returns 9.0 from TaxCalculator
|
|
112
116
|
Add the following to bundler's `Gemfile`.
|
113
117
|
|
114
118
|
```ruby
|
115
|
-
gem 'strategic', '~> 0.
|
119
|
+
gem 'strategic', '~> 1.0.0'
|
116
120
|
```
|
117
121
|
|
118
122
|
### Option 2: Manual
|
@@ -120,7 +124,7 @@ gem 'strategic', '~> 0.9.1'
|
|
120
124
|
Or manually install and require library.
|
121
125
|
|
122
126
|
```bash
|
123
|
-
gem install strategic -
|
127
|
+
gem install strategic -v1.0.0
|
124
128
|
```
|
125
129
|
|
126
130
|
```ruby
|
@@ -130,34 +134,63 @@ require 'strategic'
|
|
130
134
|
### Usage
|
131
135
|
|
132
136
|
Steps:
|
133
|
-
1. Have the original class you'd like to strategize include Strategic
|
134
|
-
2. Create a directory matching the class underscored file name minus the '.rb' extension
|
135
|
-
3. Create a strategy class under that directory, which:
|
137
|
+
1. Have the original class you'd like to strategize include `Strategic` (e.g. `def TaxCalculator; include Strategic; end`
|
138
|
+
2. Create a directory matching the class underscored file name minus the '.rb' extension (e.g. `tax_calculator/`)
|
139
|
+
3. Create a strategy class under that directory (e.g. `tax_calculator/us_strategy.rb`), which:
|
136
140
|
- Lives under the original class namespace
|
137
|
-
-
|
141
|
+
- Includes the `Strategic::Strategy` module
|
138
142
|
- Has a class name that ends with `Strategy` suffix (e.g. `NewCustomerStrategy`)
|
139
|
-
4.
|
140
|
-
5. Instantiate strategy with needed constructor parameters
|
143
|
+
4. Set strategy on strategic model using `strategy=` attribute writer method or instantiate with `new_with_strategy` class method, which takes a strategy name string (any case), strategy class, or mirror object (having a class matching strategy name minus the word `Strategy`) (note: you can call `::strategy_names` class method to obtain available strategy names or `::stratgies` to obtain available strategy classes)
|
141
144
|
6. Invoke strategy method needed
|
142
145
|
|
143
|
-
|
146
|
+
## API
|
147
|
+
|
148
|
+
- `StrategicClass::strategy_names`: returns list of strategy names (strings) discovered by convention (nested under a namespace matching the superclass name)
|
149
|
+
- `StrategicClass::strategies`: returns list of strategies discovered by convention (nested under a namespace matching the superclass name)
|
150
|
+
- `StrategicClass::new_with_strategy(string_or_class_or_object, *args, &block)`: instantiates a strategy based on a string/class/object and strategy constructor args
|
151
|
+
- `StrategicClass::strategy_class_for(string_or_class_or_object)`: selects a strategy class based on a string (e.g. 'us' selects UsStrategy) or alternatively a class/object if you have a mirror hierarchy for the strategy hierarchy
|
152
|
+
- `StrategicClass::default_strategy`: (used in model class body) sets default strategy as a strategy name string (e.g. 'us' selects UsStrategy) or alternatively a class/object if you have a mirror hierarchy for the strategy hierarchy
|
153
|
+
- `StrategicClass::strategy_matcher`: (used in model class body) custom matcher for all strategies (e.g. `strategy_matcher {|string| string.start_with?('C') && string.end_with?('o')}`)
|
154
|
+
- `StrategicClass#strategy=`: sets strategy
|
155
|
+
- `StrategicClass#strategy`: returns current strategy
|
156
|
+
- `StrategyClass::strategy_name`: returns parsed strategy name of current strategy class
|
157
|
+
- `StrategyClass::strategy_matcher`: (used in model class body) custom matcher for a specific strategy (e.g. `strategy_matcher {|string| string.start_with?('C') && string.end_with?('o')}`)
|
158
|
+
- `StrategyClass::strategy_exclusion`: (used in model class body) exclusion from custom matcher (e.g. `strategy_exclusion 'Cio'`)
|
159
|
+
- `StrategyClass::strategy_alias`: (used in model class body) alias for strategy in addition to strategy's name derived from class name by convention (e.g. `strategy_alias 'USA'` for `UsStrategy`)
|
160
|
+
- `StrategyClass#context`: returns strategy context (the strategic model instance)
|
161
|
+
|
162
|
+
Example with customizations via class body methods:
|
144
163
|
|
145
|
-
|
146
|
-
|
164
|
+
```ruby
|
165
|
+
class TaxCalculator
|
166
|
+
default_strategy 'us'
|
167
|
+
|
168
|
+
# fuzz matcher
|
169
|
+
strategy_matcher do |string_or_class_or_object|
|
170
|
+
class_name = self.name # current strategy class name being tested for matching
|
171
|
+
strategy_name = class_name.split('::').last.sub(/Strategy$/, '').gsub(/([A-Z])/) {|letter| "_#{letter.downcase}"}[1..-1]
|
172
|
+
strategy_name_length = strategy_name.length
|
173
|
+
possible_keywords = strategy_name_length.times.map {|n| strategy_name.chars.combination(strategy_name_length - n).to_a}.reduce(:+).map(&:join)
|
174
|
+
possible_keywords.include?(string_or_class_or_object)
|
175
|
+
end
|
176
|
+
# ... more code follows
|
177
|
+
end
|
147
178
|
|
148
|
-
|
149
|
-
|
179
|
+
class TaxCalculator::UsStrategy
|
180
|
+
include Strategic::Strategy
|
150
181
|
|
151
|
-
|
182
|
+
strategy_alias 'USA'
|
183
|
+
strategy_exclusion 'U'
|
184
|
+
|
185
|
+
# ... strategy methods follow
|
186
|
+
end
|
152
187
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
- `StrategicClass::strategy_exclusion`: exclusion from custom matcher (e.g. `strategy_exclusion 'Cio'`)
|
160
|
-
- `StrategicClass::strategy_alias`: alias for strategy in addition to strategy's name derived from class name by convention (e.g. `strategy_alias 'USA'` for `UsStrategy`)
|
188
|
+
class TaxCalculator::CanadaStrategy
|
189
|
+
include Strategic::Strategy
|
190
|
+
|
191
|
+
# ... strategy methods follow
|
192
|
+
end
|
193
|
+
```
|
161
194
|
|
162
195
|
## TODO
|
163
196
|
|
data/lib/strategic.rb
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
-
|
21
|
+
|
22
22
|
module Strategic
|
23
23
|
def self.included(klass)
|
24
24
|
klass.extend(ClassMethods)
|
@@ -26,27 +26,19 @@ module Strategic
|
|
26
26
|
end
|
27
27
|
|
28
28
|
module ClassMethods
|
29
|
-
def strategy_alias(alias_string_or_class_or_object)
|
30
|
-
strategy_aliases << alias_string_or_class_or_object
|
31
|
-
end
|
32
|
-
|
33
|
-
def strategy_aliases
|
34
|
-
@strategy_aliases ||= []
|
35
|
-
end
|
36
|
-
|
37
|
-
def strategy_exclusion(exclusion_string_or_class_or_object)
|
38
|
-
strategy_exclusions << exclusion_string_or_class_or_object
|
39
|
-
end
|
40
|
-
|
41
|
-
def strategy_exclusions
|
42
|
-
@strategy_exclusions ||= []
|
43
|
-
end
|
44
|
-
|
45
29
|
def strategy_matcher(&matcher_block)
|
46
|
-
if
|
30
|
+
if matcher_block.nil?
|
31
|
+
@strategy_matcher
|
32
|
+
else
|
47
33
|
@strategy_matcher = matcher_block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_strategy(string_or_class_or_object = nil)
|
38
|
+
if string_or_class_or_object.nil?
|
39
|
+
@default_strategy
|
48
40
|
else
|
49
|
-
@
|
41
|
+
@default_strategy = strategy_class_for(string_or_class_or_object)
|
50
42
|
end
|
51
43
|
end
|
52
44
|
|
@@ -65,7 +57,7 @@ module Strategic
|
|
65
57
|
def strategy_class_for(string_or_class_or_object)
|
66
58
|
strategy_class = strategy_matcher_for_any_strategy? ? strategy_class_with_strategy_matcher(string_or_class_or_object) : strategy_class_without_strategy_matcher(string_or_class_or_object)
|
67
59
|
strategy_class ||= strategies.detect { |strategy| strategy.strategy_aliases.include?(string_or_class_or_object) }
|
68
|
-
strategy_class ||=
|
60
|
+
strategy_class ||= default_strategy
|
69
61
|
end
|
70
62
|
|
71
63
|
def strategy_class_with_strategy_matcher(string_or_class_or_object)
|
@@ -93,26 +85,49 @@ module Strategic
|
|
93
85
|
end
|
94
86
|
end
|
95
87
|
|
96
|
-
def
|
97
|
-
|
88
|
+
def new_with_strategy(string_or_class_or_object, *args, &block)
|
89
|
+
new(*args, &block).tap do |model|
|
90
|
+
model.strategy = string_or_class_or_object
|
91
|
+
end
|
98
92
|
end
|
99
93
|
|
100
94
|
def strategies
|
101
95
|
constants.map do |constant_symbol|
|
102
96
|
const_get(constant_symbol)
|
103
97
|
end.select do |constant|
|
104
|
-
constant.
|
105
|
-
end
|
98
|
+
constant.ancestors.include?(Strategic::Strategy) && constant.name.split('::').last.end_with?('Strategy') && constant.name.split('::').last != 'Strategy' # has to be something like PrefixStrategy
|
99
|
+
end.sort_by(&:strategy_name)
|
106
100
|
end
|
107
101
|
|
108
102
|
def strategy_names
|
109
103
|
strategies.map(&:strategy_name)
|
110
104
|
end
|
111
105
|
|
112
|
-
|
113
|
-
|
106
|
+
end
|
107
|
+
|
108
|
+
def strategy=(string_or_class_or_object)
|
109
|
+
@strategy = self.class.strategy_class_for(string_or_class_or_object)&.new(self)
|
110
|
+
end
|
111
|
+
|
112
|
+
def strategy
|
113
|
+
@strategy
|
114
|
+
end
|
115
|
+
|
116
|
+
def method_missing(method_name, *args, &block)
|
117
|
+
if strategy&.respond_to?(method_name, *args, &block)
|
118
|
+
strategy.send(method_name, *args, &block)
|
119
|
+
else
|
120
|
+
begin
|
121
|
+
super
|
122
|
+
rescue => e
|
123
|
+
raise "No strategy is set to handle the method #{method_name} with args #{args.inspect} and block #{block.inspect} / " + e.message
|
124
|
+
end
|
114
125
|
end
|
115
126
|
end
|
127
|
+
|
128
|
+
def respond_to?(method_name, *args, &block)
|
129
|
+
strategy&.respond_to?(method_name, *args, &block) || super
|
130
|
+
end
|
116
131
|
|
117
132
|
private
|
118
133
|
|
@@ -124,3 +139,5 @@ module Strategic
|
|
124
139
|
text.chars.reduce('') {|output,c| !output.empty? && c.match(/[A-Z]/) ? output + '_' + c : output + c}.downcase
|
125
140
|
end
|
126
141
|
end
|
142
|
+
|
143
|
+
require_relative 'strategic/strategy'
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Copyright (c) 2020-2021 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
module Strategic
|
23
|
+
module Strategy
|
24
|
+
def self.included(klass)
|
25
|
+
klass.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def strategy_alias(alias_string_or_class_or_object)
|
30
|
+
strategy_aliases << alias_string_or_class_or_object
|
31
|
+
end
|
32
|
+
|
33
|
+
def strategy_aliases
|
34
|
+
@strategy_aliases ||= []
|
35
|
+
end
|
36
|
+
|
37
|
+
def strategy_exclusion(exclusion_string_or_class_or_object)
|
38
|
+
strategy_exclusions << exclusion_string_or_class_or_object
|
39
|
+
end
|
40
|
+
|
41
|
+
def strategy_exclusions
|
42
|
+
@strategy_exclusions ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
def strategy_matcher(&matcher_block)
|
46
|
+
if block_given?
|
47
|
+
@strategy_matcher = matcher_block
|
48
|
+
else
|
49
|
+
@strategy_matcher
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def strategy_name
|
54
|
+
Strategic.underscore(name.split(':').last).sub(/_strategy$/, '')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :context
|
59
|
+
|
60
|
+
def initialize(context)
|
61
|
+
@context = context
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strategic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-03-
|
11
|
+
date: 2021-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- LICENSE.txt
|
148
148
|
- README.md
|
149
149
|
- lib/strategic.rb
|
150
|
+
- lib/strategic/strategy.rb
|
150
151
|
homepage: http://github.com/AndyObtiva/strategic
|
151
152
|
licenses:
|
152
153
|
- MIT
|