reflekt 0.7.2 → 0.9.5

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
  SHA256:
3
- metadata.gz: d92d12a27556a25c258f83cf2983ffc2758a70c5cd359f4e7beafb862e88e68c
4
- data.tar.gz: be5fa824eb0e6cd3bd15e03da504c40a65e471687881a5fbfcebc0d0906399fd
3
+ metadata.gz: f2048b61fdb7acc0deede9ee5bcae644e6d51fa6e2dab2842f748433ce5ba949
4
+ data.tar.gz: dce2b1bd92e2eaf90c16e1cad6c27d9b9fc686001c958124f184a5d11a2ebe8d
5
5
  SHA512:
6
- metadata.gz: 9ffb39bc278932fd985731c7e509767e79c3356da41d7114a57cfe47a774bb97742ae283837859e2ff158ff84121e2b565c2bf25aff1ae0e8345671b9bd8da22
7
- data.tar.gz: 3db51deab75140780e718e3536ab06696a1d0ef5308b1cbfcf2fb8a56dc9144905f22f2f53e918afcd7d1e962416134fc41a07676e17c4a16b935c70aece19fb
6
+ metadata.gz: b4bdebf8083aef0a50806c1970b25f90736dfe68b8373f5a742ef6c7f2e4139e4ca0d273d683d826230069de2039c5fda7428c4ed8147471da00f1e93704f59a
7
+ data.tar.gz: afbe22e05eb10efe8ead84240c4296546e10570c19e73efea912494e55a85538c8fbed2872c42485ea21ba696a101347e73ec5feee37a568636a154dcae8bcf6
@@ -0,0 +1,37 @@
1
+ ################################################################################
2
+ # ACCESSOR
3
+ #
4
+ # Access variables via one object to avoid polluting the caller class.
5
+ #
6
+ # Only 2 variables are not accessed via Accessor:
7
+ # - @reflection_counts on the instance
8
+ # - @@reflekt_skipped_methods on the instance's singleton class
9
+ ################################################################################
10
+
11
+ class Accessor
12
+
13
+ attr_accessor :setup
14
+ attr_accessor :db
15
+ attr_accessor :stack
16
+ attr_accessor :renderer
17
+ attr_accessor :rules
18
+ attr_accessor :path
19
+ attr_accessor :output_path
20
+ attr_accessor :reflect_amount
21
+ attr_accessor :reflection_limit
22
+
23
+ def initialize()
24
+
25
+ @setup = nil
26
+ @db = nil
27
+ @stack = nil
28
+ @renderer = nil
29
+ @rules = nil
30
+ @path = nil
31
+ @output_path = nil
32
+ @reflect_amount = nil
33
+ @reflection_limit = nil
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,33 @@
1
+ require 'Reflection'
2
+
3
+ class Control < Reflection
4
+
5
+ ##
6
+ # Reflect on a method.
7
+ #
8
+ # Creates a shadow execution stack.
9
+ #
10
+ # @param method - The name of the method.
11
+ # @param *args - The method arguments.
12
+ #
13
+ # @return - A reflection hash.
14
+ ##
15
+ def reflect(*args)
16
+
17
+ @inputs = *args
18
+
19
+ # Action method with new arguments.
20
+ begin
21
+ @output = @clone.send(@method, *@inputs)
22
+ # When fail.
23
+ rescue StandardError => message
24
+ @status = FAIL
25
+ @message = message
26
+ # When pass.
27
+ else
28
+ @status = PASS
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,47 @@
1
+ class Execution
2
+
3
+ attr_accessor :object
4
+ attr_accessor :caller_id
5
+ attr_accessor :caller_class
6
+ attr_accessor :parent
7
+ attr_accessor :child
8
+ attr_accessor :control
9
+ attr_accessor :reflections
10
+ attr_accessor :is_reflecting
11
+
12
+ def initialize(object, reflection_count)
13
+
14
+ @object = object
15
+ @caller_id = object.object_id
16
+ @caller_class = object.class
17
+ @parent = nil
18
+ @child = nil
19
+
20
+ @control = []
21
+ @reflections = Array.new(reflection_count)
22
+ @is_reflecting = false
23
+
24
+ end
25
+
26
+ def has_empty_reflections?
27
+ @reflections.include? nil
28
+ end
29
+
30
+ ##
31
+ # Is the Execution currently reflecting methods?
32
+ ##
33
+ def is_reflecting?
34
+ @is_reflecting
35
+ end
36
+
37
+ def has_finished_reflecting?
38
+ if is_reflecting?
39
+ return false
40
+ end
41
+ if has_empty_reflections?
42
+ return false
43
+ end
44
+ return true
45
+ end
46
+
47
+ end
@@ -0,0 +1,163 @@
1
+ class Reflection
2
+
3
+ # Keys.
4
+ TIME = "t"
5
+ INPUT = "i"
6
+ OUTPUT = "o"
7
+ TYPE = "T"
8
+ COUNT = "C"
9
+ VALUE = "V"
10
+ STATUS = "s"
11
+ MESSAGE = "m"
12
+ # Values.
13
+ PASS = "p"
14
+ FAIL = "f"
15
+
16
+ attr_accessor :clone
17
+
18
+ def initialize(execution, method, ruler)
19
+
20
+ @execution = execution
21
+ @method = method
22
+ @ruler = ruler
23
+
24
+ # Clone the execution's object.
25
+ @clone = execution.object.clone
26
+ @clone_id = nil
27
+
28
+ # Result.
29
+ @status = nil
30
+ @time = Time.now.to_i
31
+ @inputs = []
32
+ @output = nil
33
+
34
+ end
35
+
36
+ ##
37
+ # Reflect on a method.
38
+ #
39
+ # Creates a shadow execution stack.
40
+ #
41
+ # @param method - The name of the method.
42
+ # @param *args - The method arguments.
43
+ #
44
+ # @return - A reflection hash.
45
+ ##
46
+ def reflect(*args)
47
+
48
+ # Create deviated arguments.
49
+ args.each do |arg|
50
+ case arg
51
+ when Integer
52
+ @inputs << rand(999)
53
+ else
54
+ @inputs << arg
55
+ end
56
+ end
57
+
58
+ # Action method with new arguments.
59
+ begin
60
+
61
+ # Validate input with controls.
62
+ unless @ruler.nil?
63
+ if @ruler.validate_inputs(@inputs)
64
+ @status = PASS
65
+ else
66
+ @status = FAIL
67
+ end
68
+ end
69
+
70
+ # Run reflection.
71
+ @output = @clone.send(@method, *@inputs)
72
+
73
+ # Validate output with controls.
74
+ unless @ruler.nil?
75
+ if @ruler.validate_output(@output)
76
+ @status = PASS
77
+ else
78
+ @status = FAIL
79
+ end
80
+ return
81
+ end
82
+
83
+ # When fail.
84
+ rescue StandardError => message
85
+ @status = FAIL
86
+ @message = message
87
+
88
+ # When no validation and execution fails.
89
+ else
90
+ @status = PASS
91
+ end
92
+
93
+ end
94
+
95
+ def result()
96
+ # Build reflection.
97
+ reflection = {
98
+ TIME => @time,
99
+ STATUS => @status,
100
+ INPUT => normalize_input(@inputs),
101
+ OUTPUT => normalize_output(@output),
102
+ MESSAGE => @message
103
+ }
104
+ end
105
+
106
+ ##
107
+ # Normalize inputs.
108
+ #
109
+ # @param args - The actual inputs.
110
+ # @return - A generic inputs representation.
111
+ ##
112
+ def normalize_input(args)
113
+ inputs = []
114
+ args.each do |arg|
115
+ input = {
116
+ TYPE => arg.class.to_s,
117
+ VALUE => normalize_value(arg)
118
+ }
119
+ if (arg.class == Array)
120
+ input[COUNT] = arg.count
121
+ end
122
+ inputs << input
123
+ end
124
+ inputs
125
+ end
126
+
127
+ ##
128
+ # Normalize output.
129
+ #
130
+ # @param input - The actual output.
131
+ # @return - A generic output representation.
132
+ ##
133
+ def normalize_output(input)
134
+
135
+ output = {
136
+ TYPE => input.class.to_s,
137
+ VALUE => normalize_value(input)
138
+ }
139
+
140
+ if (input.class == Array || input.class == Hash)
141
+ output[COUNT] = input.count
142
+ elsif (input.class == TrueClass || input.class == FalseClass)
143
+ output[TYPE] = :Boolean
144
+ end
145
+
146
+ return output
147
+
148
+ end
149
+
150
+ def normalize_value(value)
151
+
152
+ unless value.nil?
153
+ value = value.to_s.gsub(/\r?\n/, " ").to_s
154
+ if value.length >= 30
155
+ value = value[0, value.rindex(/\s/,30)].rstrip() + '...'
156
+ end
157
+ end
158
+
159
+ return value
160
+
161
+ end
162
+
163
+ end
@@ -0,0 +1,223 @@
1
+ ################################################################################
2
+ # REFLEKT - By Maedi Prichard.
3
+ #
4
+ # An Execution is created each time a method is called.
5
+ # Many Refections are created per Execution.
6
+ # Each Reflection executes on a ShadowStack on cloned data.
7
+ # Then flow is returned to the original method and normal execution continues.
8
+ #
9
+ # Usage:
10
+ # class ExampleClass
11
+ # prepend Reflekt
12
+ ################################################################################
13
+
14
+ require 'set'
15
+ require 'erb'
16
+ require 'rowdb'
17
+ require 'Accessor'
18
+ require 'Control'
19
+ require 'Execution'
20
+ require 'Reflection'
21
+ require 'Renderer'
22
+ require 'Ruler'
23
+ require 'ShadowStack'
24
+
25
+ module Reflekt
26
+
27
+ def initialize(*args)
28
+
29
+ @reflection_counts = {}
30
+
31
+ # Get instance methods.
32
+ # TODO: Include parent methods like "Array.include?".
33
+ self.class.instance_methods(false).each do |method|
34
+
35
+ # Don't process skipped methods.
36
+ next if self.class.reflekt_skipped?(method)
37
+
38
+ @reflection_counts[method] = 0
39
+
40
+ # When method called in flow.
41
+ self.define_singleton_method(method) do |*args|
42
+
43
+ # Don't reflect when limit reached.
44
+ unless @reflection_counts[method] >= @@reflekt.reflection_limit
45
+
46
+ # Get current execution.
47
+ execution = @@reflekt.stack.peek()
48
+
49
+ # When stack empty or past execution done reflecting.
50
+ if execution.nil? || execution.has_finished_reflecting?
51
+
52
+ # Create execution.
53
+ execution = Execution.new(self, @@reflekt.reflect_amount)
54
+ @@reflekt.stack.push(execution)
55
+
56
+ end
57
+
58
+ # Get ruler.
59
+ # The method's ruler will not exist the first time the db generated.
60
+ if @@reflekt.rules.key? execution.caller_class.to_s.to_sym
61
+ ruler = @@reflekt.rules[execution.caller_class.to_s.to_sym][method.to_s]
62
+ else
63
+ ruler = nil
64
+ end
65
+
66
+ # Reflect.
67
+ # The first method call in the Execution creates a Reflection.
68
+ # Subsequent method calls are shadow executions on cloned objects.
69
+ if execution.has_empty_reflections? && !execution.is_reflecting?
70
+ execution.is_reflecting = true
71
+
72
+ class_name = execution.caller_class.to_s
73
+ method_name = method.to_s
74
+
75
+ # Create control.
76
+ control = Control.new(execution, method, ruler)
77
+ execution.control = control
78
+
79
+ # Execute control.
80
+ control.reflect(*args)
81
+
82
+ # Save control.
83
+ @@reflekt.db.get("#{class_name}.#{method_name}.controls").push(control.result())
84
+
85
+ # Multiple reflections per execution.
86
+ execution.reflections.each_with_index do |value, index|
87
+
88
+ # Create reflection.
89
+ reflection = Reflection.new(execution, method, ruler)
90
+ execution.reflections[index] = reflection
91
+
92
+ # Execute reflection.
93
+ reflection.reflect(*args)
94
+ @reflection_counts[method] = @reflection_counts[method] + 1
95
+
96
+ # Save reflection.
97
+ @@reflekt.db.get("#{class_name}.#{method_name}.reflections").push(reflection.result())
98
+
99
+ end
100
+
101
+ # Save results.
102
+ @@reflekt.db.write()
103
+
104
+ # Render results.
105
+ @@reflekt.renderer.render()
106
+
107
+ execution.is_reflecting = false
108
+ end
109
+
110
+ end
111
+
112
+ # Continue execution / shadow execution.
113
+ super *args
114
+
115
+ end
116
+
117
+ end
118
+
119
+ # Continue initialization.
120
+ super
121
+
122
+ end
123
+
124
+ private
125
+
126
+ def self.prepended(base)
127
+
128
+ # Prepend class methods to the instance's singleton class.
129
+ base.singleton_class.prepend(SingletonClassMethods)
130
+
131
+ # Setup class.
132
+ @@reflekt = Accessor.new()
133
+ @@reflekt.setup ||= reflekt_setup_class
134
+
135
+ end
136
+
137
+ # Setup class.
138
+ def self.reflekt_setup_class()
139
+
140
+ # Receive configuration.
141
+ $ENV ||= {}
142
+ $ENV[:reflekt] ||= $ENV[:reflekt] = {}
143
+
144
+ # Set configuration.
145
+ @@reflekt.path = File.dirname(File.realpath(__FILE__))
146
+
147
+ # Build reflections directory.
148
+ if $ENV[:reflekt][:output_path]
149
+ @@reflekt.output_path = File.join($ENV[:reflekt][:output_path], 'reflections')
150
+ # Build reflections directory in current execution path.
151
+ else
152
+ @@reflekt.output_path = File.join(Dir.pwd, 'reflections')
153
+ end
154
+ # Create reflections directory.
155
+ unless Dir.exist? @@reflekt.output_path
156
+ Dir.mkdir(@@reflekt.output_path)
157
+ end
158
+
159
+ # Create database.
160
+ @@reflekt.db = Rowdb.new(@@reflekt.output_path + '/db.json')
161
+ @@reflekt.db.defaults({ :reflekt => { :api_version => 1 }})
162
+
163
+ # Create shadow execution stack.
164
+ @@reflekt.stack = ShadowStack.new()
165
+
166
+ # Define rules.
167
+ # TODO: Fix Rowdb.get(path) not returning data at path after Rowdb.push()?
168
+ @@reflekt.rules = {}
169
+ db = @@reflekt.db.value()
170
+ db.each do |class_name, class_values|
171
+ @@reflekt.rules[class_name] = {}
172
+ class_values.each do |method_name, method_values|
173
+ next if method_values.nil?
174
+ next unless method_values.class == Hash
175
+ if method_values.key? "controls"
176
+
177
+ ruler = Ruler.new()
178
+ ruler.load(method_values['controls'])
179
+ ruler.train()
180
+
181
+ @@reflekt.rules[class_name][method_name] = ruler
182
+ end
183
+ end
184
+ end
185
+
186
+ # The amount of reflections to create per method call.
187
+ @@reflekt.reflect_amount = 2
188
+
189
+ # Limit the amount of reflections that can be created per instance method.
190
+ # A method called thousands of times doesn't need that many reflections.
191
+ @@reflekt.reflection_limit = 10
192
+
193
+ # Create renderer.
194
+ @@reflekt.renderer = Renderer.new(@@reflekt.path, @@reflekt.output_path)
195
+
196
+ return true
197
+ end
198
+
199
+ module SingletonClassMethods
200
+
201
+ @@reflekt_skipped_methods = Set.new
202
+
203
+ ##
204
+ # Skip a method.
205
+ #
206
+ # @param method - A symbol representing the method name.
207
+ ##
208
+ def reflekt_skip(method)
209
+ @@reflekt_skipped_methods.add(method)
210
+ end
211
+
212
+ def reflekt_skipped?(method)
213
+ return true if @@reflekt_skipped_methods.include?(method)
214
+ false
215
+ end
216
+
217
+ def reflekt_limit(amount)
218
+ @@reflekt.reflection_limit = amount
219
+ end
220
+
221
+ end
222
+
223
+ end