togls 2.2.1 → 3.0.0.pre.rc.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e07a68d3fc95321858fb09e282d568faa8957588
4
- data.tar.gz: 6af7cd9bd40afb53d6d367b232e690d34e2d675d
3
+ metadata.gz: e15bb80dde590be56e09b63b345d321068356ed7
4
+ data.tar.gz: cf898035f7e06327aca28ba7805440fcfb123a09
5
5
  SHA512:
6
- metadata.gz: 2d66393fb05305227911e1a9fd56e2b651cddd9e7efec9e6980ac37eae888fa16fa2fc6542ffdb5d5fa016be099c1566ff345f6b7e1b470f37a885433cab7de0
7
- data.tar.gz: ef4b6e18ba83c0ab616a853daf405f606ce5b56db09e76a04c1230399e8feaa67ff13dae6b06361275d3d34a45e38280caf2becdd88cd694531e5f370f41507b
6
+ metadata.gz: 059266b27c515744c891db005dab8ac82485670a18749c3a857ee7996a2016287bba98a1ef9063a33495e937e967e3516dac505e51f5fe52f1cc499f17cb6d8d
7
+ data.tar.gz: ebe1d54110e5f27989a0f4fab4d5ebfcfc8f4c427776e5efb5fe40ce3c5e9a7716765a2c4aa75cb65db4c2be4c5f50b78533676e14660318124e88f5e1eb6a3d
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.3.0
1
+ 2.3.1
data/CHANGELOG.md CHANGED
@@ -2,55 +2,126 @@
2
2
 
3
3
  The following are lists of the notable changes included with each release.
4
4
  This is intended to help keep people informed about notable changes between
5
- versions, as well as provide a rough history.
6
-
7
- #### Next Release
8
-
9
- ### v2.2.1
10
-
11
- * Added: in-memory driver `set`, `get`, `all` Marshaling to correct a threading
12
- reference based collision. Fixes issue #35
13
-
14
- ### v2.2.0
15
-
16
- * Changed Togls to be thread-safe
17
- * Added Togls::TestToggleRegistry class for initializing test state
18
- * Added ability to create additional toggle registries
19
-
20
- ### v2.1.1
21
-
22
- * Fixed issue #21, env variable override wasn't falling through to in
5
+ versions, as well as provide a rough history. Each item is prefixed with
6
+ one of the following labels: `Added`, `Changed`, `Deprecated`,
7
+ `Removed`, `Fixed`, `Security`. We also use [Semantic
8
+ Versioning](http://semver.org) to manage the versions of this gem so
9
+ that you can set version constraints properly.
10
+
11
+ #### [Unreleased] - now
12
+
13
+ * `Changed`: The testing interface to allow for contract enforcement in tests
14
+ and allow altering existing feature rules within tests.
15
+ * `Added`: `Togls.rule` method to simplify rule construction from `type_id`
16
+ ([#86](https://github.com/codebreakdown/togls/issues/86))
17
+ * `Changed`: Rule construction to require an abstract rule `type_id`
18
+ ([#86](https://github.com/codebreakdown/togls/issues/86))
19
+ * `Added`: Feature repository response validation
20
+ * `Added`: validation of targets against feature target_type contract
21
+ ([#78](https://github.com/codebreakdown/togls/issues/78))
22
+ * `Added`: setting a default feature target type
23
+ ([#69](https://github.com/codebreakdown/togls/issues/69))
24
+ * `Added`: logging for target type mismatches/erroneous states
25
+ ([#69](https://github.com/codebreakdown/togls/issues/69))
26
+ * `Changed`: in code feature rule association API to require `target_type`
27
+ ([#69](https://github.com/codebreakdown/togls/issues/69))
28
+ * `Added`: rule instance target types and switch type checking over
29
+ ([#67](https://github.com/codebreakdown/togls/issues/67))
30
+ * `Added`: optional target types, and target type checking
31
+ ([#65](https://github.com/codebreakdown/togls/issues/65))
32
+ * `Changed`: rule type repository to store meta data
33
+ ([#62](https://github.com/codebreakdown/togls/issues/62))
34
+ * `Added`: uniqueness check for rule types
35
+ ([#61](https://github.com/codebreakdown/togls/issues/61)
36
+ * `Changed`: rule repository to use rule type repository
37
+ ([#59](https://github.com/codebreakdown/togls/issues/59))
38
+ * `Added`: rule type registration
39
+ ([#59](https://github.com/codebreakdown/togls/issues/59))
40
+ * `Changed`: driver construction to RegistryManager
41
+ ([#56](https://github.com/codebreakdown/togls/issues/56))
42
+ * `Removed`: `TestToggleRegistry`
43
+ ([#56](https://github.com/codebreakdown/togls/issues/56))
44
+ * `Changed`: `ReleaseToggleRegistry` to `ToggleRegistry`
45
+ ([#56](https://github.com/codebreakdown/togls/issues/56))
46
+ * `Removed`: `features` rake task
47
+ ([#48](https://github.com/codebreakdown/togls/issues/48))
48
+ * `Added`: `test_mode` block style method
49
+ ([#44](https://github.com/codebreakdown/togls/issues/44))
50
+ * `Changed`: `Togls.features` to `Togls.release`
51
+ ([#38](https://github.com/codebreakdown/togls/issues/38))
52
+ * `Changed`: Renamed `FeatureToggleRegistry` to `ReleaseToggleRegistry`
53
+ ([#41](https://github.com/codebreakdown/togls/issues/41))
54
+ * `Added`: `FeatureToggleRegistryManager` methods, `enable_test_mode` &
55
+ `disable_test_mode`
56
+ * `Removed`: `features=` setter
57
+ * `Changed`: `FeatureRepository` moved from `Registry` to `RegistryManager`
58
+ ([#40](https://github.com/codebreakdown/togls/issues/40))
59
+ * `Removed`: `FeatureToggleRegistry.create` and `TestToggleRegistry.create`
60
+ * `Changed`: `ReleaseToggleRegistryManager` to `FeatureToggleRegistryManager`
61
+ * `Added`: Base Error class for Togls exceptions
62
+ ([#39](https://github.com/codebreakdown/togls/issues/39))
63
+ * `Added`: Exception for when a feature has already been defined in the feature
64
+ repository
65
+ ([#42](https://github.com/codebreakdown/togls/issues/42))
66
+
67
+ #### [v2.2.1] - 2016-03-24
68
+
69
+ * `Added`: in-memory driver `set`, `get`, `all` Marshaling to correct a threading
70
+ reference based collision.
71
+ ([#35](https://github.com/codebreakdown/togls/issues/35))
72
+
73
+ #### [v2.2.0] - 2016-03-04
74
+
75
+ * `Changed`: Togls to be thread-safe
76
+ * `Added`: `Togls::TestToggleRegistry` class for initializing test state
77
+ * `Added`: ability to create additional toggle registries
78
+
79
+ #### [v2.1.1] - 2015-12-14
80
+
81
+ * `Fixed`: env variable override wasn't falling through to in
23
82
  code defined memory value.
83
+ ([#24](https://github.com/codebreakdown/togls/issues/24))
24
84
 
25
- ### v2.1.0
85
+ #### [v2.1.0] - 2015-11-24
26
86
 
27
- * Fixed #19, exceptions happened on evaluation after setting
87
+ * `Fixed`: exceptions happened on evaluation after setting
28
88
  `Togls.features = nil`
89
+ ([#19](https://github.com/codebreakdown/togls/issues/19))
29
90
 
30
- ### v2.0.0
91
+ #### [v2.0.0] - 2015-11-23
31
92
 
32
- * Add ability to create empty feature toggle set via `Togls.features`
33
- * Add toggle definition expansion with multiple `Togls.features` blocks
34
- * Add set FeatureToggleRegistry instance, `Togls.features = toggle_registry`
35
- * Change `Togls.features` to return FeatureToggleRegistry instance
93
+ * `Added`: ability to create empty feature toggle set via `Togls.features`
94
+ * `Added`: toggle definition expansion with multiple `Togls.features` blocks
95
+ * `Added`: set FeatureToggleRegistry instance, `Togls.features = toggle_registry`
96
+ * `Changed`: `Togls.features` to return FeatureToggleRegistry instance
36
97
  rather than an Array of Toggle objects.
37
98
 
38
- #### v1.1.0
99
+ #### [v1.1.0] - 2015-11-17
39
100
 
40
- * Add `off?` method for asking if a defined feature is off
41
- * Add feature toggle overrides
42
- * Rearchitect to support concept of repositories and datastores allowing
101
+ * `Added`: `off?` method for asking if a defined feature is off
102
+ * `Added`: feature toggle overrides
103
+ * `Changed`: Architecture to support concept of repositories and datastores allowing
43
104
  further growth in the future
44
105
 
45
- #### v1.0.0
106
+ #### [v1.0.0] - 2015-05-19
46
107
 
47
- * Require human readable description to define a feature toggle
48
- * Add rake task that outputs all the feature toggles states (on, off, ? -
108
+ * `Changed`: to require human readable description to define a feature toggle
109
+ * `Added`: rake task that outputs all the feature toggles states (on, off, ? -
49
110
  unknown due to Compex Rule), keys, and human readable descritpions
50
111
 
51
- #### v0.1.0
112
+ #### [v0.1.0] - 2015-05-03
52
113
 
53
- * Add concept of Groups as a provided rule
54
- * Add concept of Rules and Custom Rules, allowing users to have more dynamic
114
+ * `Added`: concept of Groups as a provided rule
115
+ * `Added`: concept of Rules and Custom Rules, allowing users to have more dynamic
55
116
  feature toggles
56
- * Add basic feature toggles able to be switched on/off
117
+ * `Added`: basic feature toggles able to be switched on/off
118
+
119
+ [Unreleased]: https://github.com/codebreakdown/togls/compare/v2.2.1...HEAD
120
+ [v2.2.1]: https://github.com/codebreakdown/togls/compare/v2.2.0...v2.2.1
121
+ [v2.2.0]: https://github.com/codebreakdown/togls/compare/v2.1.1...v2.2.0
122
+ [v2.1.1]: https://github.com/codebreakdown/togls/compare/v2.1.0...v2.1.1
123
+ [v2.1.0]: https://github.com/codebreakdown/togls/compare/v2.0.0...v2.1.0
124
+ [v2.0.0]: https://github.com/codebreakdown/togls/compare/v1.1.0...v2.0.0
125
+ [v1.1.0]: https://github.com/codebreakdown/togls/compare/v1.0.0...v1.1.0
126
+ [v1.0.0]: https://github.com/codebreakdown/togls/compare/v0.1.0...v1.0.0
127
+ [v0.1.0]: https://github.com/codebreakdown/togls/compare/0fa2feb...v0.1.0
data/README.md CHANGED
@@ -50,7 +50,7 @@ The following, `config/initializers/togls_features.rb`, is an example of
50
50
  how one would define some basic feature toggles.
51
51
 
52
52
  ```ruby
53
- Togls.features do
53
+ Togls.release do
54
54
  # Set this feature to always be on
55
55
  feature(:pop_up_login_form, "use pop up login instead of normal login").on
56
56
 
@@ -95,7 +95,7 @@ feature toggle in all caps with `TOGLS_` prefixed to it. For example if
95
95
  we had the following feature toggle defined.
96
96
 
97
97
  ```ruby
98
- Togls.features do
98
+ Togls.release do
99
99
  # Set this feature to always be on
100
100
  feature(:pop_up_login_form, "use pop up login instead of normal login").on
101
101
  end
@@ -131,7 +131,7 @@ based on group membership.
131
131
  alpha_testers = Togls::Rules::Group.new(["user1@email.com",
132
132
  "user2@example.com"])
133
133
 
134
- Togls.features do
134
+ Togls.release do
135
135
  feature(:new_contact_form, "use new contact form").on(alpha_testers)
136
136
  end
137
137
  ```
@@ -183,7 +183,7 @@ in the example above and it would look something like the following:
183
183
  # the group.
184
184
  alpha_testers = Togls::Rules::Group.new([1, 23, 42, 83])
185
185
 
186
- Togls.features do
186
+ Togls.release do
187
187
  feature(:new_contact_form, "use new contact form").on(alpha_testers)
188
188
  end
189
189
  ```
@@ -207,14 +207,17 @@ including details of advanced usage in the `README.md` as to not
207
207
  overwhelm people on first impression. For more details on some of the
208
208
  more advanced features feel free to check out our
209
209
  [Wiki](https://github.com/codebreakdown/togls/wiki). Just a few of the
210
- many things it contains are [Provided Rules
210
+ many things it contains are
211
+ [Testing with
212
+ Toggles](https://github.com/codebreakdown/togls/wiki/Testing-with-Toggles),
213
+ [Provided Rules
211
214
  Reference](https://github.com/codebreakdown/togls/wiki/Provided-Rules-Reference),
212
215
  [Custom
213
216
  Rules](https://github.com/codebreakdown/togls/wiki/Custom-Rules),
214
- [Review Feature
215
- Toggles](https://github.com/codebreakdown/togls/wiki/Review-Feature-Toggles),
216
- [Testing with
217
- Toggles](https://github.com/codebreakdown/togls/wiki/Testing-with-Toggles)
217
+ [Organize Toggle
218
+ Definitions](https://github.com/codebreakdown/togls/wiki/Organize-Toggle-Definitions),
219
+ [Creating Additional Toggle
220
+ Registries](https://github.com/codebreakdown/togls/wiki/Create-Additional-Toggle-Registries),
218
221
  etc.
219
222
 
220
223
  ## Development
@@ -0,0 +1,25 @@
1
+ module Togls
2
+ module DefaultFeatureTargetTypeManager
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def default_feature_target_type(target_type = nil)
9
+ if target_type
10
+ if @default_feature_target_type
11
+ raise Togls::DefaultFeatureTargetTypeAlreadySet, 'the default feature target type has already been set'
12
+ else
13
+ @default_feature_target_type = target_type
14
+ end
15
+ else
16
+ if @default_feature_target_type
17
+ return @default_feature_target_type
18
+ else
19
+ return Togls::TargetTypes::NOT_SET
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/togls/errors.rb CHANGED
@@ -1,6 +1,17 @@
1
1
  module Togls
2
- class NoFeaturesError < StandardError; end
3
- class MissingDriver < StandardError; end
4
- class InvalidDriver < StandardError; end
5
- class NotImplemented < StandardError; end
2
+ class Error < StandardError; end
3
+ class NoFeaturesError < Error; end
4
+ class MissingDriver < Error; end
5
+ class InvalidDriver < Error; end
6
+ class NotImplemented < Error; end
7
+ class FeatureAlreadyDefined < Error; end
8
+ class RuleTypeAlreadyDefined < Error; end
9
+ class RuleFeatureTargetTypeMismatch < Error; end
10
+ class RuleMissingTargetType < Error; end
11
+ class DefaultFeatureTargetTypeAlreadySet < Error; end
12
+ class FeatureMissingTargetType < Error; end
13
+ class EvaluationTargetMissing < Error; end
14
+ class UnexpectedEvaluationTarget < Error; end
15
+ class RepositoryFeatureDataInvalid < Error; end
16
+ class RepositoryRuleDataInvalid < Error; end
6
17
  end
data/lib/togls/feature.rb CHANGED
@@ -6,13 +6,25 @@ module Togls
6
6
  class Feature
7
7
  attr_reader :key, :description
8
8
 
9
- def initialize(key, description)
9
+ def initialize(key, description, target_type)
10
10
  @key = key.to_s
11
11
  @description = description
12
+ @target_type = target_type
13
+ raise Togls::FeatureMissingTargetType, "Feature '#{self.key}' is missing a required target type" if self.missing_target_type?
14
+ end
15
+
16
+ def target_type
17
+ return @target_type unless @target_type.nil?
18
+ return Togls::TargetTypes::NOT_SET
12
19
  end
13
20
 
14
21
  def id
15
22
  @key
16
23
  end
24
+
25
+ def missing_target_type?
26
+ return false if target_type && (target_type != Togls::TargetTypes::NOT_SET)
27
+ return true
28
+ end
17
29
  end
18
30
  end
@@ -22,7 +22,7 @@ module Togls
22
22
  end
23
23
 
24
24
  def extract_feature_data(feature)
25
- { 'key' => feature.key, 'description' => feature.description }
25
+ { 'key' => feature.key, 'description' => feature.description, 'target_type' => feature.target_type.to_s }
26
26
  end
27
27
 
28
28
  def fetch_feature_data(id)
@@ -34,14 +34,40 @@ module Togls
34
34
  feature_data
35
35
  end
36
36
 
37
+ def include?(feature_id)
38
+ result = fetch_feature_data(feature_id)
39
+ result.nil? ? false : true
40
+ end
41
+
37
42
  def get(feature_id)
38
43
  feature_data = fetch_feature_data(feature_id)
44
+ validate_feature_data(feature_data)
39
45
  reconstitute_feature(feature_data)
40
46
  end
41
47
 
42
48
  def reconstitute_feature(feature_data)
43
49
  Togls::Feature.new(feature_data['key'],
44
- feature_data['description'])
50
+ feature_data['description'],
51
+ feature_data['target_type'].to_sym)
52
+ end
53
+
54
+ def validate_feature_data(feature_data)
55
+ if feature_data.nil?
56
+ Togls.logger.debug("None of the feature repository drivers claim to have the feature")
57
+ raise Togls::RepositoryFeatureDataInvalid, "None of the feature repository drivers claim to have the feature"
58
+ end
59
+
60
+ ['key', 'description', 'target_type'].each do |k|
61
+ if !feature_data.has_key? k
62
+ Togls.logger.debug("One of the feature repository drivers returned feature data that is missing the '#{k}'")
63
+ raise Togls::RepositoryFeatureDataInvalid, "One of the feature repository drivers returned feature data that is missing the '#{k}'"
64
+ end
65
+
66
+ if !feature_data[k].is_a?(String)
67
+ Togls.logger.debug("One of the feature repository drivers returned feature data with '#{k}' not being a string")
68
+ raise Togls::RepositoryFeatureDataInvalid, "One of the feature repository drivers returned feature data with '#{k}' not being a string"
69
+ end
70
+ end
45
71
  end
46
72
  end
47
73
  end
@@ -0,0 +1,138 @@
1
+ module Togls
2
+ # Feature Toggle Registry Manager
3
+ #
4
+ # This is the primary DSL interface. It provides a DSL to facilitate housing
5
+ # and managing a toggle registry.
6
+ module FeatureToggleRegistryManager
7
+ def self.included(mod)
8
+ mod.extend(ClassMethods)
9
+ end
10
+
11
+ # Feature Toggle Registry Manager Class Methods
12
+ #
13
+ # The class methods that should be extended onto the module/class when
14
+ # FeatureToggleRegistryManager is included.
15
+ module ClassMethods
16
+ def release(&block)
17
+ release_blocks << block if block
18
+
19
+ release_toggle_registry.expand(&block) if block
20
+ release_toggle_registry
21
+ end
22
+
23
+ def release_blocks
24
+ @release_blocks ||= []
25
+ end
26
+
27
+ def rule_types(&block)
28
+ rule_type_registry.expand(&block) if block
29
+ rule_type_registry
30
+ end
31
+
32
+ def rule_type(type_id)
33
+ rule_type_registry.get(type_id)
34
+ end
35
+
36
+ def rule(type_id, data, target_type: Togls::TargetTypes::NOT_SET)
37
+ rule_type(type_id).new(type_id, data, target_type: target_type)
38
+ end
39
+
40
+ def feature(key)
41
+ Toggler.new(release_toggle_registry.instance_variable_get(:@toggle_repository), release_toggle_registry.get(key))
42
+ end
43
+
44
+ def logger
45
+ @logger ||= Logger.new(STDOUT)
46
+ end
47
+
48
+ def enable_test_mode
49
+ @previous_release_toggle_registry = @release_toggle_registry
50
+ @release_toggle_registry = test_toggle_registry
51
+ end
52
+
53
+ def disable_test_mode
54
+ @release_toggle_registry = @previous_release_toggle_registry
55
+ end
56
+
57
+ def test_mode
58
+ enable_test_mode
59
+ yield
60
+ disable_test_mode
61
+ end
62
+
63
+ private
64
+
65
+ def rule_type_repository
66
+ if @rule_type_repository.nil?
67
+ rule_type_repository_drivers = [RuleTypeRepositoryDrivers::InMemoryDriver.new]
68
+ @rule_type_repository = RuleTypeRepository.new(rule_type_repository_drivers)
69
+ end
70
+ @rule_type_repository
71
+ end
72
+
73
+ def rule_type_registry
74
+ if @rule_type_registry.nil?
75
+ @rule_type_registry = RuleTypeRegistry.new(rule_type_repository)
76
+ @rule_type_registry.register(:boolean, Togls::Rules::Boolean)
77
+ @rule_type_registry.register(:group, Togls::Rules::Group)
78
+ end
79
+ @rule_type_registry
80
+ end
81
+
82
+ def test_toggle_registry
83
+ feature_repository_drivers =
84
+ [Togls::FeatureRepositoryDrivers::InMemoryDriver.new]
85
+ test_feature_repository = Togls::FeatureRepository.new(
86
+ feature_repository_drivers)
87
+
88
+ rule_repository_drivers =
89
+ [Togls::RuleRepositoryDrivers::InMemoryDriver.new]
90
+ rule_repository = Togls::RuleRepository.new(rule_type_registry, rule_repository_drivers)
91
+
92
+ toggle_repository_drivers = [
93
+ Togls::ToggleRepositoryDrivers::InMemoryDriver.new]
94
+
95
+ toggle_repository = Togls::ToggleRepository.new(
96
+ toggle_repository_drivers, test_feature_repository, rule_repository)
97
+
98
+ tr = ToggleRegistry.new(test_feature_repository, toggle_repository)
99
+ release_blocks.each do |p|
100
+ tr.expand(&p)
101
+ end
102
+
103
+ return tr
104
+ end
105
+
106
+ def release_toggle_registry
107
+ if @release_toggle_registry.nil?
108
+ rule_repository_drivers = [
109
+ Togls::RuleRepositoryDrivers::InMemoryDriver.new,
110
+ Togls::RuleRepositoryDrivers::EnvOverrideDriver.new
111
+ ]
112
+
113
+ rule_repository = Togls::RuleRepository.new(rule_type_registry, rule_repository_drivers)
114
+
115
+ toggle_repository_drivers = [
116
+ Togls::ToggleRepositoryDrivers::InMemoryDriver.new,
117
+ Togls::ToggleRepositoryDrivers::EnvOverrideDriver.new
118
+ ]
119
+
120
+ toggle_repository = Togls::ToggleRepository.new(
121
+ toggle_repository_drivers, feature_repository, rule_repository)
122
+
123
+ @release_toggle_registry = ToggleRegistry.new(feature_repository,
124
+ toggle_repository)
125
+ end
126
+ @release_toggle_registry
127
+ end
128
+
129
+ def feature_repository
130
+ if @feature_repository.nil?
131
+ feature_repository_drivers = [Togls::FeatureRepositoryDrivers::InMemoryDriver.new]
132
+ @feature_repository = Togls::FeatureRepository.new(feature_repository_drivers)
133
+ end
134
+ @feature_repository
135
+ end
136
+ end
137
+ end
138
+ end
data/lib/togls/helpers.rb CHANGED
@@ -3,8 +3,8 @@ module Togls
3
3
  #
4
4
  # Collection of helper methods used through the Togls library.
5
5
  module Helpers
6
- def self.sha1(klass, data)
7
- Digest::SHA1.hexdigest("#{klass}:#{data}")
6
+ def self.sha1(*args)
7
+ Digest::SHA1.hexdigest(args.join(':'))
8
8
  end
9
9
  end
10
10
  end
@@ -5,7 +5,7 @@ module Togls
5
5
  # toggle found when requested to be retreived through a Toggle Repository.
6
6
  class NullToggle < Togls::Toggle
7
7
  def initialize
8
- feature = Togls::Feature.new('null', 'the official null feature')
8
+ feature = Togls::Feature.new('null', 'the official null feature', Togls::TargetTypes::EITHER)
9
9
  super(feature)
10
10
  end
11
11
 
@@ -17,4 +17,7 @@ module Togls
17
17
  self
18
18
  end
19
19
  end
20
+
21
+ class ToggleMissingToggle < NullToggle; end
22
+ class RuleFeatureMismatchToggle < NullToggle; end
20
23
  end
data/lib/togls/rule.rb CHANGED
@@ -4,10 +4,25 @@ module Togls
4
4
  # The Rule is an abstract base class that is intended to act as an interface
5
5
  # for other rules to be implemented against.
6
6
  class Rule
7
- attr_reader :data
7
+ attr_reader :data, :type_id
8
8
 
9
- def initialize(data = nil)
9
+ def self.title
10
+ raise Togls::NotImplemented, "Rule type title not implemented"
11
+ end
12
+
13
+ def self.description
14
+ raise Togls::NotImplemented, "Rule type description not implemented"
15
+ end
16
+
17
+ def self.target_type
18
+ Togls::TargetTypes::NOT_SET
19
+ end
20
+
21
+ def initialize(type_id, data = nil, target_type: Togls::TargetTypes::NOT_SET)
22
+ @type_id = type_id
10
23
  @data = data
24
+ @target_type = target_type
25
+ raise Togls::RuleMissingTargetType, "Rule '#{self.id}' of type '#{self.class}' is missing a required target type" if self.missing_target_type?
11
26
  end
12
27
 
13
28
  def run(key, target = nil)
@@ -15,7 +30,18 @@ module Togls
15
30
  end
16
31
 
17
32
  def id
18
- Togls::Helpers.sha1(self.class, @data)
33
+ Togls::Helpers.sha1(@type_id, @data, target_type)
34
+ end
35
+
36
+ def target_type
37
+ return @target_type if @target_type && @target_type != Togls::TargetTypes::NOT_SET
38
+ return self.class.target_type unless self.class.target_type.nil?
39
+ return Togls::TargetTypes::NOT_SET
40
+ end
41
+
42
+ def missing_target_type?
43
+ return false if target_type && (target_type != Togls::TargetTypes::NOT_SET)
44
+ return true
19
45
  end
20
46
  end
21
47
  end
@@ -5,7 +5,7 @@ module Togls
5
5
  # It does these by interfacing with Rule Repository Drivers which are passed
6
6
  # in during construction as an Array.
7
7
  class RuleRepository
8
- def initialize(drivers)
8
+ def initialize(rule_type_registry, drivers)
9
9
  unless drivers.is_a?(Array)
10
10
  raise Togls::InvalidDriver, 'RuleRepository requires a valid driver'
11
11
  end
@@ -13,6 +13,7 @@ module Togls
13
13
  raise Togls::MissingDriver, 'RuleRepository requires a driver'
14
14
  end
15
15
  @drivers = drivers
16
+ @rule_type_registry = rule_type_registry
16
17
  end
17
18
 
18
19
  def store(rule)
@@ -23,7 +24,11 @@ module Togls
23
24
  end
24
25
 
25
26
  def extract_storage_payload(rule)
26
- { 'klass' => rule.class, 'data' => rule.data }
27
+ {
28
+ 'type_id' => @rule_type_registry.get_type_id(rule.class.to_s),
29
+ 'data' => rule.data,
30
+ 'target_type' => rule.target_type.to_s
31
+ }
27
32
  end
28
33
 
29
34
  def fetch_rule_data(id)
@@ -37,11 +42,39 @@ module Togls
37
42
 
38
43
  def get(rule_id)
39
44
  rule_data = fetch_rule_data(rule_id)
45
+ validate_rule_data(rule_data)
40
46
  reconstitute_rule(rule_data)
41
47
  end
42
48
 
49
+ def validate_rule_data(rule_data)
50
+ if rule_data.nil?
51
+ Togls.logger.debug "None of the rule repository drivers claim to have the rule"
52
+ raise Togls::RepositoryRuleDataInvalid, "None of the rule repository drivers claim to have the rule"
53
+ end
54
+
55
+ ['type_id', 'data', 'target_type'].each do |k|
56
+ if !rule_data.has_key? k
57
+ Togls.logger.debug "One of the rule repository drivers returned rule data that is missing the '#{k}'"
58
+ raise Togls::RepositoryRuleDataInvalid, "One of the rule repository drivers returned rule data that is missing the '#{k}'"
59
+ end
60
+ end
61
+
62
+ ['type_id', 'target_type'].each do |k|
63
+ if !rule_data[k].is_a?(String)
64
+ Togls.logger.debug "One of the rule repository drivers returned rule data with '#{k}' not being a string"
65
+ raise Togls::RepositoryRuleDataInvalid, "One of the rule repository drivers returned rule data with '#{k}' not being a string"
66
+ end
67
+ end
68
+ end
69
+
43
70
  def reconstitute_rule(rule_data)
44
- rule_data['klass'].new(rule_data['data'])
71
+ if rule_data.has_key?('target_type')
72
+ @rule_type_registry.get(rule_data['type_id'])\
73
+ .new(rule_data['type_id'].to_sym, rule_data['data'],
74
+ target_type: rule_data['target_type'].to_sym)
75
+ else
76
+ @rule_type_registry.get(rule_data['type_id']).new(rule_data['type_id'].to_sym, rule_data['data'])
77
+ end
45
78
  end
46
79
  end
47
80
  end