togls 2.2.1 → 3.0.0.pre.rc.1

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 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