strategize 0.0.2 → 0.0.3

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: 87efd0ffdecd640f1522ccbd34bb1ea15c733d99
4
- data.tar.gz: 417c5296430434f94ed464c2606f8dfa45130f60
3
+ metadata.gz: 42524de68454e2ad718b1849668425b46440fbac
4
+ data.tar.gz: 0191dd00cce17dbc790d18f14646ffa56a43dada
5
5
  SHA512:
6
- metadata.gz: f1f3b54f43c019a1a4e1810bcff8d1d4ad049dfa2ff5fc4c599973eafebef4b0a5843b2d2f83de67026db2ccdebc6943d11aa54b4e0ac73e73615dcd3a631f21
7
- data.tar.gz: ea04d4dc1a8ad2e00cbf7c9fc76c7620932b99c364863b1f41f3784ac3c1f55c43f8da16d9d10b282a006b0f8b16b96f339e8cf70da538a93295f0818d2c34b9
6
+ metadata.gz: 6cf723d3e305c0f90fdce8cb878f51bd688477101f20bf82daa3b340e95de8c2146274a592860b6106492bfb1a9fcf9f3586ec27f856645a5d2135aa348e009d
7
+ data.tar.gz: ac6bd23200464784123842b3c84c7986f90cdfc96fa3b7e597a6ee17e50be42e4cb3b41fa37de51befb9acf2db48e88f3b3de2f46138441127e90fca2d6f1fb8
data/lib/strategize.rb CHANGED
@@ -1,7 +1,20 @@
1
1
  module Strategize
2
- autoload :Rule, 'strategize/rule'
3
- autoload :RuleGroup, 'strategize/rule_group'
4
- autoload :Policy, 'strategize/policy'
5
- autoload :Operation, 'strategize/operation'
6
- autoload :PolicyGroup, 'strategize/policy_group'
2
+ # Rules
3
+ autoload :Rule, 'strategize/rules/rule'
4
+ autoload :RuleGroup, 'strategize/rules/rule_group'
5
+ autoload :RuleResult, 'strategize/rules/rule_result'
6
+ autoload :RuleResultCollection, 'strategize/rules/rule_result_collection'
7
+
8
+ # Policies
9
+ autoload :Policy, 'strategize/policies/policy'
10
+ autoload :PolicyGroup, 'strategize/policies/policy_group'
11
+ autoload :PolicyResult, 'strategize/policies/policy_result'
12
+ autoload :PolicyResultCollection, 'strategize/policies/policy_result_collection'
13
+
14
+ # Operations
15
+ autoload :Operation, 'strategize/operations/operation'
16
+ autoload :OperationGroup, 'strategize/operations/operation_group'
17
+
18
+ # Exceptions
19
+ autoload :NotPolicyError, 'strategize/exceptions'
7
20
  end
@@ -0,0 +1,5 @@
1
+ module Strategize
2
+ # Raised when a class does not include
3
+ # the Stategize::Policy module.
4
+ NotPolicyError = Class.new(RuntimeError)
5
+ end
@@ -0,0 +1,33 @@
1
+ module Strategize
2
+ # Operations let you define a block of code that you would like to
3
+ # execute, and by calling process with a subject you can tell the
4
+ # operation what object you would like to execute the code against.
5
+ #
6
+ # Operations are similar to Rules, but the block of code that you
7
+ # pass in the constuctor does not have to be a predicate function.
8
+ class Operation
9
+ # @attr_reader name a descriptive name for the operation
10
+ attr_reader :name
11
+
12
+ # @attr_reader function A Proc or Lambda describing the code to be executed
13
+ attr_reader :function
14
+
15
+ # Create a new instance of Operation
16
+ #
17
+ # @param name [Symbol] a descriptive name for the operation
18
+ # @param function [Proc] the code to be executed
19
+ # @return [Operation]
20
+ def initialize(name, function)
21
+ @name = name
22
+ @function = function
23
+ end
24
+
25
+ # Execute the function against a specific object
26
+ #
27
+ # @param subject [Object] will become self when executing the function
28
+ # @return [void]
29
+ def process(subject)
30
+ subject.instance_exec(&@function)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ module Strategize
2
+ class OperationGroup
3
+ # @attr_reader operations [Array<Operation>]
4
+ attr_reader :operations
5
+
6
+ # Create a new instance of OperationGroup
7
+ #
8
+ # @param operations [Array<Operation>] the operations to be executed
9
+ def initialize(operations=[])
10
+ @operations = operations
11
+ end
12
+
13
+ # Add Operation to group
14
+ #
15
+ # @param name [Symbol] a descriptive name for the operation
16
+ # @param operation [Proc] a proc or lambda describing the code to be executed
17
+ def add(name, function)
18
+ operation = Operation.new(name, function)
19
+ @operations.push(operation)
20
+ end
21
+
22
+ # Execute the code definined in each operation against a subject.
23
+ #
24
+ # @param subject [Object] a single object which acts as the subject for each operation
25
+ # @param subject [Hash] a hash of operation names and subjects
26
+ def run(subjects)
27
+ the_subjects = subjects
28
+
29
+ if !subjects.is_a?(Hash)
30
+ the_subjects = build_subjects_hash(subjects)
31
+ end
32
+
33
+ @operations.each do |operation|
34
+ subject = the_subjects[operation.name]
35
+ operation.process(subject) unless subject.nil?
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def build_subjects_hash(subjects)
42
+ { default: subjects }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,47 @@
1
+ module Strategize
2
+ # The policy module allows you to define rules and an operation on
3
+ # any class that it is included on.
4
+ module Policy
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ base.send(:attr_reader, :object)
8
+ end
9
+
10
+ # These methods will be placed on the class that includes
11
+ # the Policy module.
12
+ module ClassMethods
13
+ # Get all the rules defined on this policy
14
+ #
15
+ # @return [RuleGroup]
16
+ def rules
17
+ @rules ||= RuleGroup.new
18
+ end
19
+
20
+ # Get all the operations defined on this policy.
21
+ #
22
+ # @return [OperationGroup]
23
+ def operations
24
+ @operations ||= OperationGroup.new
25
+ end
26
+
27
+ # Define a rule for a policy
28
+ #
29
+ # @param name [Symbol] descriptive name for the rule
30
+ # @param predicate [Proc] code to execute
31
+ # @return [void]
32
+ def rule(name, predicate)
33
+ rules.add(name, predicate)
34
+ end
35
+
36
+ # Define a block of code to run when you call the process
37
+ # method
38
+ #
39
+ # @param name [Symbol] the name of the context for the operation
40
+ # @param block [Proc] the code block to execute
41
+ # @return [void]
42
+ def operation(name = :default, &block)
43
+ operations.add(name, block)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,43 @@
1
+ module Strategize
2
+ # The PolicyGroup allows you to evaluate a subject against
3
+ # multiple policies.
4
+ class PolicyGroup
5
+ attr_reader :policies
6
+
7
+ # Create a new PolicyGroup
8
+ def initialize
9
+ @policies = []
10
+ end
11
+
12
+ # Add policy to the group
13
+ #
14
+ # @param policy [Strategize::Policy] a class that includes the Policy module
15
+ # @return [void]
16
+ def add(policy)
17
+ fail Strategize::NotPolicyError unless policy?(policy)
18
+ @policies.push(policy)
19
+ end
20
+
21
+ # Evaluate each policy against a given subject.
22
+ #
23
+ # @param subject [Object]
24
+ # @return [PolicyResultCollection]
25
+ def evaluate(subject)
26
+ results = @policies.map do |policy|
27
+ result_collection = policy.rules.evaluate(subject)
28
+ PolicyResult.new(policy, result_collection)
29
+ end
30
+ PolicyResultCollection.new(results, subject)
31
+ end
32
+
33
+ private
34
+
35
+ # Check if a policy includes the Policy module
36
+ #
37
+ # @private
38
+ # @param policy [Class] the class to check
39
+ def policy?(policy)
40
+ policy.included_modules.include?(Policy)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ module Strategize
2
+ # A PolicyResult is returned when you evaluate
3
+ # a PolicyGroup and allows you to view the
4
+ # RuleResultCollection and whether the policy
5
+ # was valid (all rules returned true)
6
+ class PolicyResult
7
+ attr_reader :policy, :rule_group, :subject
8
+
9
+ # Create a new PolicyEvaluation
10
+ #
11
+ # @param policy [Policy]
12
+ # @param rule_result_collection [RuleResultCollection]
13
+ # @return [PolicyResult]
14
+ def initialize(policy, rule_result_collection)
15
+ @policy = policy
16
+ @rule_result_collection = rule_result_collection
17
+ end
18
+
19
+ # Check if all the rules for the policy evaluated to true
20
+ #
21
+ # @return [Boolean]
22
+ def valid?
23
+ @rule_result_collection.all_valid?
24
+ end
25
+
26
+ # Get the results for rules on the policy
27
+ #
28
+ # @return [RuleResultCollection]
29
+ def rules
30
+ @rule_result_collection
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Strategize
2
+ # A wrapper for an array of PolicyResult instances.
3
+ class PolicyResultCollection
4
+ # @return [Array<PolicyResult>] the policy results for each policy
5
+ attr_reader :items
6
+
7
+ # @return [Object] the subject the policy was executed against
8
+ attr_reader :subject
9
+
10
+ # Create a new PolicyResultCollection
11
+ #
12
+ # @param policy_results [Array<PolicyResult]
13
+ # @param subject [Object] the subject the policies were executed against
14
+ def initialize(policy_results, subject)
15
+ @items = policy_results
16
+ @subject = subject
17
+ end
18
+
19
+ # Get an array of the PolicyResults that are valid, or return true
20
+ #
21
+ # @return [Array<PolicyResult>]
22
+ def passed
23
+ @items.select(&:valid)
24
+ end
25
+
26
+ # Get an array of the PolicyResults that are invalid, or return false
27
+ #
28
+ # @return [Array<PolicyResult>]
29
+ def failed
30
+ @items.select { |rule| !rule.valid? }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ module Strategize
2
+ # The Rule class represents a predicate function that
3
+ # can be run against a specific subject.
4
+ #
5
+ # Define a new rule
6
+ # Rule.new :rule_name, -> { code to execute }
7
+ #
8
+ # Evaluate a rule
9
+ # rule = Rule.new :rule_name, -> { true }
10
+ # rule.evaluate(nil) #=> true
11
+ class Rule
12
+ attr_reader :name, :predicate
13
+
14
+ # Create a new rule
15
+ #
16
+ # @param name [Symbol] a descriptive name for the rule
17
+ # @param predicate [Proc] code block to be executed
18
+ def initialize(name, predicate)
19
+ @name = name
20
+ @predicate = predicate
21
+ end
22
+
23
+ # Execute the predicate against the subject
24
+ #
25
+ # @param subject [Object]
26
+ # @return [Boolean]
27
+ def evaluate(subject)
28
+ subject.instance_exec(&@predicate)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ module Strategize
2
+ # The RuleGroup allows you evaluate multiple rules against one subject.
3
+ class RuleGroup
4
+ attr_reader :rules
5
+
6
+ # Create new Rule Group
7
+ #
8
+ # @param rules [Array<Rule>] rules to be evaluated
9
+ def initialize(rules = [])
10
+ @rules = rules
11
+ end
12
+
13
+ # Add [Rule] to be evaluated
14
+ #
15
+ # @param name [Symbol] a descriptive name for the rule
16
+ # @param predicate [Lambda] a function that returns true or false
17
+ def add(name, predicate)
18
+ @rules.push(Rule.new(name, predicate))
19
+ end
20
+
21
+ # Evaluate all rules in group and return a collection
22
+ # of the results.
23
+ #
24
+ # @param subject [Object] object to evaluate rules against
25
+ # @return [RuleResultCollection] evaluation results
26
+ def evaluate(subject)
27
+ results = @rules.map do |rule|
28
+ RuleResult.new(rule, rule.evaluate(subject))
29
+ end
30
+ RuleResultCollection.new(results, subject)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module Strategize
2
+ # A RuleResult represents the evaluation result, which gives
3
+ # you access to the Rule and whether it was valid or not.
4
+ class RuleResult
5
+ attr_reader :rule, :valid
6
+
7
+ # Create a new RuleResult which provides details on
8
+ # the evaluation of a rule.
9
+ #
10
+ # @param rule [Rule] the evaluated rule
11
+ # @param valid [Boolean] the result of the executed rule
12
+ # @return [RuleResult]
13
+ def initialize(rule, valid)
14
+ @rule = rule
15
+ @valid = valid
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module Strategize
2
+ # Represents a collection of RuleResults, which lets you see
3
+ # what rules passed, failed, etc.
4
+ class RuleResultCollection
5
+ attr_reader :items, :subject
6
+
7
+ # Create a new RuleResultCollection
8
+ #
9
+ # @param rule_results [Array<RuleResult>]
10
+ # @param subject [Object]
11
+ # @return [RuleResultCollection]
12
+ def initialize(rule_results, subject)
13
+ @items = rule_results
14
+ @subject = subject
15
+ end
16
+
17
+ # Iterate over each [RuleResult] in the items Array.
18
+ #
19
+ # @param block [Proc]
20
+ def each(&block)
21
+ @items.each { |item| block.call(item) }
22
+ end
23
+
24
+ # Returns if all rules evaluated to true
25
+ #
26
+ # @return [Boolean]
27
+ def all_valid?
28
+ @items.all?(&:valid)
29
+ end
30
+
31
+ # Returns all rules that evaluated to true
32
+ #
33
+ # @return [Array<RuleResult>]
34
+ def passed
35
+ @items.select(&:valid)
36
+ end
37
+
38
+ # Returns all rules that evaluated to false
39
+ #
40
+ # @return [Array<RuleResult>]
41
+ def failed
42
+ @items.select { |rule| !rule.valid }
43
+ end
44
+ end
45
+ end
data/test/test_helper.rb CHANGED
@@ -1,3 +1,10 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/test/'
4
+ add_group 'Rules', 'lib/strategize/rules'
5
+ add_group 'Policies', 'lib/strategize/policies'
6
+ end
7
+
1
8
  require 'strategize'
2
9
 
3
10
  require 'minitest/autorun'
@@ -6,6 +13,4 @@ require 'minitest/reporters'
6
13
  test_root = File.dirname(__FILE__)
7
14
  Dir["#{test_root}/support/*.rb"]. each { |file| require file }
8
15
 
9
-
10
-
11
16
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new(color: true)
metadata CHANGED
@@ -1,27 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strategize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - jdmorlan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-31 00:00:00.000000000 Z
11
+ date: 2016-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest-reporters
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: simplecov
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
18
32
  - !ruby/object:Gem::Version
19
33
  version: '0'
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
- - - ">="
38
+ - - "~>"
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  description: Strategize
@@ -31,16 +45,19 @@ extensions: []
31
45
  extra_rdoc_files: []
32
46
  files:
33
47
  - lib/strategize.rb
34
- - lib/strategize/operation.rb
35
- - lib/strategize/policy.rb
36
- - lib/strategize/policy_group.rb
37
- - lib/strategize/rule.rb
38
- - lib/strategize/rule_group.rb
39
- - test/policy_group_test.rb
40
- - test/policy_test.rb
41
- - test/rule_test.rb
48
+ - lib/strategize/exceptions.rb
49
+ - lib/strategize/operations/operation.rb
50
+ - lib/strategize/operations/operation_group.rb
51
+ - lib/strategize/policies/policy.rb
52
+ - lib/strategize/policies/policy_group.rb
53
+ - lib/strategize/policies/policy_result.rb
54
+ - lib/strategize/policies/policy_result_collection.rb
55
+ - lib/strategize/rules/rule.rb
56
+ - lib/strategize/rules/rule_group.rb
57
+ - lib/strategize/rules/rule_result.rb
58
+ - lib/strategize/rules/rule_result_collection.rb
42
59
  - test/test_helper.rb
43
- homepage:
60
+ homepage: https://github.com/jdmorlan/strategize
44
61
  licenses:
45
62
  - MIT
46
63
  metadata: {}
@@ -65,8 +82,5 @@ signing_key:
65
82
  specification_version: 4
66
83
  summary: Validate rules and perform operations
67
84
  test_files:
68
- - test/policy_group_test.rb
69
- - test/policy_test.rb
70
- - test/rule_test.rb
71
85
  - test/test_helper.rb
72
86
  has_rdoc:
@@ -1,18 +0,0 @@
1
- module Strategize
2
- module Operation
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- base.send(:attr_reader, :object)
6
- end
7
-
8
- module ClassMethods
9
- def operation(&block)
10
- @operation = block
11
- end
12
-
13
- def process(subject)
14
- subject.instance_exec &@operation
15
- end
16
- end
17
- end
18
- end
@@ -1,22 +0,0 @@
1
- module Strategize
2
- module Policy
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- base.send(:attr_reader, :object)
6
- end
7
-
8
- module ClassMethods
9
- def rules
10
- @rules ||= RuleGroup.new
11
- end
12
-
13
- def rule(predicate)
14
- rules.add(predicate)
15
- end
16
-
17
- def evaluate(subject)
18
- rules.evaluate(subject)
19
- end
20
- end
21
- end
22
- end
@@ -1,28 +0,0 @@
1
- module Strategize
2
- class PolicyGroup
3
- NotPolicyError = Class.new(RuntimeError)
4
-
5
- def initialize
6
- @policies = []
7
- end
8
-
9
- def add(policy)
10
- raise NotPolicyError unless is_policy(policy)
11
- @policies.push(policy)
12
- end
13
-
14
- def select(subject)
15
- @policies.select { |p| p.evaluate(subject) }
16
- end
17
-
18
- def find(subject)
19
- self.select(subject).first
20
- end
21
-
22
- private
23
-
24
- def is_policy(policy)
25
- policy.included_modules.include?(Policy)
26
- end
27
- end
28
- end
@@ -1,11 +0,0 @@
1
- module Strategize
2
- class Rule
3
- def initialize(predicate)
4
- @predicate = predicate
5
- end
6
-
7
- def evaluate(subject)
8
- subject.instance_exec &@predicate
9
- end
10
- end
11
- end
@@ -1,16 +0,0 @@
1
- module Strategize
2
- class RuleGroup
3
- def initialize
4
- @rules = []
5
- end
6
-
7
- def add(predicate)
8
- predicate = Rule.new(predicate) if predicate.is_a?(Proc)
9
- @rules.push(predicate)
10
- end
11
-
12
- def evaluate(subject)
13
- @rules.all? { |r| r.evaluate(subject) }
14
- end
15
- end
16
- end
@@ -1,21 +0,0 @@
1
- require 'test_helper'
2
-
3
- class PolicyGroupTest < Minitest::Test
4
- def setup
5
- @group = Strategize::PolicyGroup.new
6
- @group.add(CanDrinkPolicy)
7
- @group.add(CanSmokePolicy)
8
- @group.add(CanVotePolicy)
9
- end
10
-
11
- def test_collection_operations_work
12
- subject = OpenStruct.new(age: 19)
13
- data = OpenStruct.new(permissions: {})
14
-
15
- policies = @group.select(subject)
16
- policies.each { |p| p.process(data) }
17
-
18
- assert_equal true, data.permissions[:can_vote]
19
- assert_equal true, data.permissions[:can_smoke]
20
- end
21
- end
data/test/policy_test.rb DELETED
@@ -1,13 +0,0 @@
1
- require 'test_helper'
2
-
3
- class PolicyTest < Minitest::Test
4
- def test_can_drink_is_valid
5
- subject = OpenStruct.new(age: 22)
6
- CanDrinkPolicy.evaluate(subject)
7
- end
8
-
9
- def test_cannot_drink
10
- subject = OpenStruct.new(age: 20)
11
- CanDrinkPolicy.evaluate(subject)
12
- end
13
- end
data/test/rule_test.rb DELETED
@@ -1,18 +0,0 @@
1
- require 'test_helper'
2
-
3
- class RuleTest < Minitest::Test
4
- def test_can_create_rule
5
- rule = Strategize::Rule.new(-> { true })
6
- assert rule.is_a?(Strategize::Rule)
7
- end
8
-
9
- def test_rule_is_valid
10
- rule = Strategize::Rule.new(-> { true })
11
- assert rule.evaluate(nil)
12
- end
13
-
14
- def test_rule_is_not_valid
15
- rule = Strategize::Rule.new(-> { false })
16
- refute rule.evaluate(nil)
17
- end
18
- end