reflekt 0.7.2 → 0.9.0

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: e4d3c0c32672fd8813482a3f546032904d3d6f65e592fd4435eb7ada2bb1b201
4
+ data.tar.gz: d48f2b100986c4408db5cb055f4ebf1e6a50d99fa99dc6d98e8af8e5d8bcd66e
5
5
  SHA512:
6
- metadata.gz: 9ffb39bc278932fd985731c7e509767e79c3356da41d7114a57cfe47a774bb97742ae283837859e2ff158ff84121e2b565c2bf25aff1ae0e8345671b9bd8da22
7
- data.tar.gz: 3db51deab75140780e718e3536ab06696a1d0ef5308b1cbfcf2fb8a56dc9144905f22f2f53e918afcd7d1e962416134fc41a07676e17c4a16b935c70aece19fb
6
+ metadata.gz: 1422110bc34f7b88f8b4ba71359c408d38f3ebf72235735e51864fd0bd16c32fd5abc20e5cfa8a516478932800590f80f7f1b7b4d0dac3c0ebd18a3d8dfffd54
7
+ data.tar.gz: 4019f5868ca20d1aebfa9e4ddb2c5136782aa52185588cb44558f4725f51ea59f90bc75896e8bfac383151d7346bb534d98bc00a11a569c350d0c460fe3b31ab
@@ -0,0 +1,45 @@
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 :reflections
9
+ attr_accessor :is_reflecting
10
+
11
+ def initialize(object, reflection_count)
12
+
13
+ @object = object
14
+ @caller_id = object.object_id
15
+ @caller_class = object.class
16
+ @parent = nil
17
+ @child = nil
18
+
19
+ @reflections = Array.new(reflection_count)
20
+ @is_reflecting = false
21
+
22
+ end
23
+
24
+ def has_empty_reflections?
25
+ @reflections.include? nil
26
+ end
27
+
28
+ ##
29
+ # Is the Execution currently reflecting methods?
30
+ ##
31
+ def is_reflecting?
32
+ @is_reflecting
33
+ end
34
+
35
+ def has_finished_reflecting?
36
+ if is_reflecting?
37
+ return false
38
+ end
39
+ if has_empty_reflections?
40
+ return false
41
+ end
42
+ return true
43
+ end
44
+
45
+ end
@@ -0,0 +1,145 @@
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, is_control)
19
+
20
+ @execution = execution
21
+ @method = method
22
+ @is_control = is_control
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
+ @input = []
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
+ # Reflect on real world arguments.
49
+ if @is_control
50
+ @input = *args
51
+ # Reflect on deviated arguments.
52
+ else
53
+ args.each do |arg|
54
+ case arg
55
+ when Integer
56
+ @input << rand(9999)
57
+ else
58
+ @input << arg
59
+ end
60
+ end
61
+ end
62
+
63
+ # Action method with new arguments.
64
+ begin
65
+ @output = @clone.send(@method, *@input)
66
+ # When fail.
67
+ rescue StandardError => message
68
+ @status = MESSAGE
69
+ @message = message
70
+ # When pass.
71
+ else
72
+ @status = PASS
73
+ end
74
+
75
+ end
76
+
77
+ def result()
78
+ # Build reflection.
79
+ reflection = {
80
+ TIME => @time,
81
+ STATUS => @status,
82
+ INPUT => normalize_input(@input),
83
+ OUTPUT => normalize_output(@output),
84
+ MESSAGE => @message
85
+ }
86
+ end
87
+
88
+ ##
89
+ # Normalize inputs.
90
+ #
91
+ # @param args - The actual inputs.
92
+ # @return - A generic inputs representation.
93
+ ##
94
+ def normalize_input(args)
95
+ inputs = []
96
+ args.each do |arg|
97
+ input = {
98
+ TYPE => arg.class.to_s,
99
+ VALUE => normalize_value(arg)
100
+ }
101
+ if (arg.class == Array)
102
+ input[COUNT] = arg.count
103
+ end
104
+ inputs << input
105
+ end
106
+ inputs
107
+ end
108
+
109
+ ##
110
+ # Normalize output.
111
+ #
112
+ # @param input - The actual output.
113
+ # @return - A generic output representation.
114
+ ##
115
+ def normalize_output(input)
116
+
117
+ output = {
118
+ TYPE => input.class.to_s,
119
+ VALUE => normalize_value(input)
120
+ }
121
+
122
+ if (input.class == Array || input.class == Hash)
123
+ output[COUNT] = input.count
124
+ elsif (input.class == TrueClass || input.class == FalseClass)
125
+ output[TYPE] = :Boolean
126
+ end
127
+
128
+ return output
129
+
130
+ end
131
+
132
+ def normalize_value(value)
133
+
134
+ unless value.nil?
135
+ value = value.to_s.gsub(/\r?\n/, " ").to_s
136
+ if value.length >= 30
137
+ value = value[0, value.rindex(/\s/,30)].rstrip() + '...'
138
+ end
139
+ end
140
+
141
+ return value
142
+
143
+ end
144
+
145
+ end
@@ -0,0 +1,56 @@
1
+ ################################################################################
2
+ # SHADOW STACK
3
+ #
4
+ # Track the executions in a call stack.
5
+ ################################################################################
6
+
7
+ class ShadowStack
8
+
9
+ def initialize()
10
+ @bottom = nil
11
+ @top = nil
12
+ end
13
+
14
+ def peek()
15
+ @top
16
+ end
17
+
18
+ def base()
19
+ @bottom
20
+ end
21
+
22
+ ##
23
+ # Push Execution.
24
+ #
25
+ # @param object - The object being executed.
26
+ # @param args - The arguments being executed.
27
+ #
28
+ # @return Execution - The new execution.
29
+ ##
30
+ def push(execution)
31
+
32
+ # Reference previous execution.
33
+ if @bottom.nil?
34
+ @bottom = execution
35
+ else
36
+ execution.child = @top
37
+ @top.parent = execution
38
+ end
39
+
40
+ # Place new execution at the top of the stack.
41
+ @top = execution
42
+
43
+ end
44
+
45
+ def display
46
+ display_execution_tree(@bottom)
47
+ end
48
+
49
+ def display_execution_tree(execution)
50
+ p execution
51
+ unless execution.parent == nil
52
+ display_execution_tree(execution.parent)
53
+ end
54
+ end
55
+
56
+ end
@@ -1,11 +1,19 @@
1
1
  require 'set'
2
2
  require 'erb'
3
3
  require 'rowdb'
4
+ require 'Execution'
5
+ require 'Reflection'
6
+ require 'ShadowStack'
4
7
 
5
8
  ################################################################################
6
9
  # REFLEKT
7
10
  #
8
- # Usage. Prepend to the class like so:
11
+ # An Execution is created each time a method is called.
12
+ # Multiple Refections are created per Execution.
13
+ # These Reflections execute on a ShadowStack on cloned objects.
14
+ # Then flow is returned to the original method and normal execution continues.
15
+ #
16
+ # Usage:
9
17
  #
10
18
  # class ExampleClass
11
19
  # prepend Reflekt
@@ -13,186 +21,105 @@ require 'rowdb'
13
21
 
14
22
  module Reflekt
15
23
 
16
- # Reflection keys.
17
- REFLEKT_TIME = "t"
18
- REFLEKT_INPUT = "i"
19
- REFLEKT_OUTPUT = "o"
20
- REFLEKT_TYPE = "T"
21
- REFLEKT_COUNT = "C"
22
- REFLEKT_VALUE = "V"
23
- REFLEKT_STATUS = "s"
24
- REFLEKT_MESSAGE = "m"
25
- # Reflection values.
26
- REFLEKT_PASS = "p"
27
- REFLEKT_FAIL = "f"
28
-
29
- @@reflekt_clone_count = 5
24
+ # The amount of reflections to create per method call.
25
+ @@reflekt_reflect_amount = 2
30
26
 
31
- def initialize(*args)
27
+ # Limit the amount of reflections that can be created per instance method.
28
+ # A method called thousands of times doesn't need that many reflections.
29
+ @@reflection_limit = 10
32
30
 
33
- @reflekt_forked = false
34
- @reflekt_clones = []
31
+ def initialize(*args)
35
32
 
36
- # Limit the amount of clones that can be created per instance.
37
- # A method called thousands of times doesn't need that many reflections.
38
- @reflekt_limit = 5
39
- @reflekt_count = 0
33
+ @reflection_counts = {}
40
34
 
41
- # Override methods.
35
+ # Get instance methods.
36
+ # TODO: Include parent methods like "Array.include?".
42
37
  self.class.instance_methods(false).each do |method|
43
- self.define_singleton_method(method) do |*args|
44
38
 
45
- # When method called in flow.
46
- if @reflekt_forked
39
+ # Don't process skipped methods.
40
+ next if self.class.reflekt_skipped?(method)
47
41
 
48
- if @reflekt_count < @reflekt_limit
49
- unless self.class.deflekted?(method)
42
+ @reflection_counts[method] = 0
50
43
 
51
- # Reflekt on method.
52
- @reflekt_clones.each do |clone|
53
- reflekt_action(clone, method, *args)
54
- end
44
+ # When method called in flow.
45
+ self.define_singleton_method(method) do |*args|
55
46
 
56
- # Save results.
57
- @@reflekt_db.write()
47
+ # Don't reflect when limit reached.
48
+ unless @reflection_counts[method] >= @@reflection_limit
58
49
 
59
- reflekt_render()
50
+ # Get current execution.
51
+ execution = @@reflekt_stack.peek()
60
52
 
61
- end
62
- @reflekt_count = @reflekt_count + 1
63
- end
53
+ # When stack empty or past execution done reflecting.
54
+ if execution.nil? || execution.has_finished_reflecting?
64
55
 
65
- end
56
+ # Create execution.
57
+ execution = Execution.new(self, @@reflekt_reflect_amount)
58
+ @@reflekt_stack.push(execution)
66
59
 
67
- # Continue method flow.
68
- super *args
69
- end
60
+ end
70
61
 
71
- end
62
+ # Reflect.
63
+ # The first method call in the Execution creates a Reflection.
64
+ # Subsequent method calls are shadow executions on cloned objects.
65
+ if execution.has_empty_reflections? && !execution.is_reflecting?
66
+ execution.is_reflecting = true
72
67
 
73
- # Continue contructor flow.
74
- super
68
+ # Multiple reflections per execution.
69
+ execution.reflections.each_with_index do |value, index|
75
70
 
76
- # Create forks.
77
- reflekt_fork()
71
+ # Flag first reflection is a control.
72
+ is_control = false
73
+ is_control = true if index == 0
78
74
 
79
- end
75
+ # Create reflection.
76
+ reflection = Reflection.new(execution, method, is_control)
77
+ execution.reflections[index] = reflection
80
78
 
81
- def reflekt_fork()
79
+ # Execute reflection.
80
+ reflection.reflect(*args)
82
81
 
83
- @@reflekt_clone_count.times do |clone|
84
- @reflekt_clones << self.clone
85
- end
82
+ # Add result.
83
+ class_name = execution.caller_class.to_s
84
+ method_name = method.to_s
85
+ @@reflekt_db.get("#{class_name}.#{method_name}").push(reflection.result())
86
86
 
87
- @reflekt_forked = true
87
+ end
88
88
 
89
- end
89
+ # Save results.
90
+ @@reflekt_db.write()
91
+
92
+ # Render results.
93
+ reflekt_render()
90
94
 
91
- def reflekt_action(clone, method, *args)
95
+ execution.is_reflecting = false
96
+ end
92
97
 
93
- class_name = clone.class.to_s
94
- method_name = method.to_s
98
+ end
95
99
 
96
- # TODO: Create control fork. Get good value. Check against it.
100
+ @reflection_counts[method] = @reflection_counts[method] + 1
97
101
 
98
- # Create new arguments that are deviations on inputted type.
99
- input = []
102
+ # Continue execution / shadow execution.
103
+ super *args
100
104
 
101
- args.each do |arg|
102
- case arg
103
- when Integer
104
- input << rand(9999)
105
- else
106
- input << arg
107
105
  end
108
- end
109
106
 
110
- # Action method with new arguments.
111
- begin
112
- output = clone.send(method, *input)
113
-
114
- # Build reflection.
115
- reflection = {
116
- REFLEKT_TIME => Time.now.to_i,
117
- REFLEKT_INPUT => reflekt_normalize_input(input),
118
- REFLEKT_OUTPUT => reflekt_normalize_output(output)
119
- }
120
-
121
- # When fail.
122
- rescue StandardError => message
123
- reflection[REFLEKT_STATUS] = REFLEKT_MESSAGE
124
- reflection[REFLEKT_MESSAGE] = message
125
- # When pass.
126
- else
127
- reflection[REFLEKT_STATUS] = REFLEKT_PASS
128
107
  end
129
108
 
130
- # Save reflection.
131
- @@reflekt_db.get("#{class_name}.#{method_name}").push(reflection)
132
-
133
- end
109
+ # Continue initialization.
110
+ super
134
111
 
135
- ##
136
- # Normalize inputs.
137
- #
138
- # @param args - The actual inputs.
139
- # @return - A generic inputs representation.
140
- ##
141
- def reflekt_normalize_input(args)
142
- inputs = []
143
- args.each do |arg|
144
- input = {
145
- REFLEKT_TYPE => arg.class.to_s,
146
- REFLEKT_VALUE => reflekt_normalize_value(arg)
147
- }
148
- if (arg.class == Array)
149
- input[REFLEKT_COUNT] = arg.count
150
- end
151
- inputs << input
152
- end
153
- inputs
154
112
  end
155
113
 
156
114
  ##
157
- # Normalize output.
158
- #
159
- # @param output - The actual output.
160
- # @return - A generic output representation.
115
+ # Render results.
161
116
  ##
162
- def reflekt_normalize_output(output)
163
-
164
- o = {
165
- REFLEKT_TYPE => output.class.to_s,
166
- REFLEKT_VALUE => reflekt_normalize_value(output)
167
- }
168
-
169
- if (output.class == Array || output.class == Hash)
170
- o[REFLEKT_COUNT] = output.count
171
- elsif (output.class == TrueClass || output.class == FalseClass)
172
- o[REFLEKT_TYPE] = :Boolean
173
- end
174
-
175
- return o
176
-
177
- end
178
-
179
- def reflekt_normalize_value(value)
180
-
181
- unless value.nil?
182
- value = value.to_s.gsub(/\r?\n/, " ").to_s
183
- if value.length >= 30
184
- value = value[0, value.rindex(/\s/,30)].rstrip() + '...'
185
- end
186
- end
187
-
188
- return value
189
-
190
- end
191
-
192
117
  def reflekt_render()
193
118
 
194
- # Render results.
119
+ # Get JSON.
195
120
  @@reflekt_json = File.read("#{@@reflekt_output_path}/db.json")
121
+
122
+ # Save HTML.
196
123
  template = File.read("#{@@reflekt_path}/web/template.html.erb")
197
124
  rendered = ERB.new(template).result(binding)
198
125
  File.open("#{@@reflekt_output_path}/index.html", 'w+') do |f|
@@ -225,20 +152,24 @@ module Reflekt
225
152
  # Setup class.
226
153
  def self.reflekt_setup_class()
227
154
 
228
- # Receive configuration from host application.
155
+ # Receive configuration.
229
156
  $ENV ||= {}
230
157
  $ENV[:reflekt] ||= $ENV[:reflekt] = {}
231
158
 
159
+ # Set configuration.
232
160
  @@reflekt_path = File.dirname(File.realpath(__FILE__))
233
161
 
234
- # Create "reflections" directory in configured path.
162
+ # Create reflection tree.
163
+ @@reflekt_stack = ShadowStack.new()
164
+
165
+ # Build reflections directory.
235
166
  if $ENV[:reflekt][:output_path]
236
167
  @@reflekt_output_path = File.join($ENV[:reflekt][:output_path], 'reflections')
237
- # Create "reflections" directory in current execution path.
168
+ # Build reflections directory in current execution path.
238
169
  else
239
170
  @@reflekt_output_path = File.join(Dir.pwd, 'reflections')
240
171
  end
241
-
172
+ # Create reflections directory.
242
173
  unless Dir.exist? @@reflekt_output_path
243
174
  Dir.mkdir(@@reflekt_output_path)
244
175
  end
@@ -252,7 +183,7 @@ module Reflekt
252
183
 
253
184
  module SingletonClassMethods
254
185
 
255
- @@deflekted_methods = Set.new
186
+ @@reflekt_skipped_methods = Set.new
256
187
 
257
188
  ##
258
189
  # Skip a method.
@@ -260,16 +191,16 @@ module Reflekt
260
191
  # @param method - A symbol representing the method name.
261
192
  ##
262
193
  def reflekt_skip(method)
263
- @@deflekted_methods.add(method)
194
+ @@reflekt_skipped_methods.add(method)
264
195
  end
265
196
 
266
- def deflekted?(method)
267
- return true if @@deflekted_methods.include?(method)
197
+ def reflekt_skipped?(method)
198
+ return true if @@reflekt_skipped_methods.include?(method)
268
199
  false
269
200
  end
270
201
 
271
202
  def reflekt_limit(amount)
272
- @reflekt_limit = amount
203
+ @@reflection_limit = amount
273
204
  end
274
205
 
275
206
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reflekt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maedi Prichard
@@ -30,11 +30,14 @@ executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - lib/Execution.rb
34
+ - lib/Reflection.rb
35
+ - lib/ShadowStack.rb
33
36
  - lib/reflekt.rb
34
37
  - lib/web/script.js
35
38
  - lib/web/style.css
36
39
  - lib/web/template.html.erb
37
- homepage: https://github.com/maedi/reflekt
40
+ homepage: https://github.com/refIekt/reflekt
38
41
  licenses:
39
42
  - MPL-2.0
40
43
  metadata: {}