reflekt 1.0.9 → 1.0.10

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.
data/lib/reflekt.rb CHANGED
@@ -3,14 +3,6 @@
3
3
  #
4
4
  # @author Maedi Prichard
5
5
  #
6
- # @flow
7
- # 1. Reflekt is prepended to a class and setup.
8
- # 2. When a class insantiates so does Reflekt.
9
- # 3. An Action is created on method call.
10
- # 4. Many Refections are created per Action.
11
- # 5. Each Reflection executes on cloned data.
12
- # 6. Flow is returned to the original method.
13
- #
14
6
  # @usage
15
7
  # class ExampleClass
16
8
  # prepend Reflekt
@@ -19,6 +11,8 @@
19
11
  require 'set'
20
12
  require 'erb'
21
13
  require 'rowdb'
14
+ require 'lit_cli'
15
+
22
16
  require_relative 'accessor'
23
17
  require_relative 'action'
24
18
  require_relative 'action_stack'
@@ -27,193 +21,238 @@ require_relative 'control'
27
21
  require_relative 'experiment'
28
22
  require_relative 'renderer'
29
23
  require_relative 'rule_set_aggregator'
30
- # Require all rules.
24
+
25
+ # Require all rules in rules directory.
31
26
  Dir[File.join(__dir__, 'rules', '*.rb')].each { |file| require_relative file }
32
27
 
33
28
  module Reflekt
29
+ include LitCLI
34
30
 
31
+ ##
32
+ # Setup Reflekt per class.
33
+ # Override methods on class instantiation.
34
+ #
35
+ # @scope self [Object] Refers to the class that Reflekt is prepended to.
36
+ ##
35
37
  def initialize(*args)
38
+ if @@reflekt.config.enabled
39
+ @reflekt_initialized = false
36
40
 
37
- # TODO: Store counts on @@reflekt and key by instance ID.
38
- @reflekt_counts = {}
41
+ 🔥"Initialize", :info, :setup, self.class
39
42
 
40
- # Get child and parent instance methods.
41
- parent_instance_methods = self.class.superclass.instance_methods(false)
42
- child_instance_methods = self.class.instance_methods(false)
43
- instance_methods = parent_instance_methods + child_instance_methods
44
-
45
- # TODO: Include core methods like "Array.include?".
46
- instance_methods.each do |method|
43
+ # Override methods.
44
+ Reflekt.get_methods(self).each do |method|
45
+ Reflekt.setup_count(self, method)
46
+ Reflekt.override_method(self, method)
47
+ end
47
48
 
48
- @reflekt_counts[method] = 0
49
+ @reflekt_initialized = true
50
+ end
49
51
 
50
- # When method called in flow.
51
- self.define_singleton_method(method) do |*args|
52
+ # Continue initialization.
53
+ super
54
+ end
52
55
 
53
- # When Reflekt enabled and control reflection has executed without error.
54
- if @@reflekt.config.enabled && !@@reflekt.error
56
+ ##
57
+ # Get child and parent instance methods.
58
+ #
59
+ # TODO: Include methods from all ancestors.
60
+ # TODO: Include core methods like "Array.include?".
61
+ ##
62
+ def self.get_methods(klass)
63
+ child_instance_methods = klass.class.instance_methods(false)
64
+ parent_instance_methods = klass.class.superclass.instance_methods(false)
55
65
 
56
- # Get current action.
57
- action = @@reflekt.stack.peek()
66
+ return child_instance_methods + parent_instance_methods
67
+ end
58
68
 
59
- # Don't reflect when reflect limit reached or method skipped.
60
- unless (@reflekt_counts[method] >= @@reflekt.config.reflect_limit) || self.class.reflekt_skipped?(method)
69
+ ##
70
+ # Override a method.
71
+ #
72
+ # @param klass [Dynamic] The class to override.
73
+ # @param method [Method] The method to override.
74
+ ##
75
+ def self.override_method(klass, method)
76
+ klass.define_singleton_method(method) do |*args|
61
77
 
62
- # When stack empty or past action done reflecting.
63
- if action.nil? || action.has_finished_reflecting?
78
+ # When method called in flow.
79
+ if @reflekt_initialized
80
+
81
+ ##
82
+ # Reflect-Execute loop.
83
+ #
84
+ # Reflect each method before finally executing it.
85
+ #
86
+ # @loop
87
+ # 1. The first method call creates an action
88
+ # 2. The action creates reflections and calls the method again
89
+ # 3. Subsequent method calls execute these reflections
90
+ # 4. Each reflection executes on cloned data
91
+ # 5. The original method call completes execution
92
+ #
93
+ # @see https://reflekt.dev/docs/reflect-execute-loop
94
+ ##
95
+
96
+ unless @@reflekt.error
64
97
 
65
- # Create action.
66
- action = Action.new(self, method, @@reflekt.config.reflect_amount, @@reflekt.stack)
98
+ action = @@reflekt.stack.peek()
67
99
 
68
- @@reflekt.stack.push(action)
100
+ # New action when old action done reflecting.
101
+ if action.nil? || action.has_finished_loop?
102
+ 🔥"^ Create action for #{method}()", :info, :action, klass.class
103
+ action = Action.new(klass, method, @@reflekt.config, @@reflekt.db, @@reflekt.stack, @@reflekt.aggregator)
104
+ @@reflekt.stack.push(action)
105
+ end
69
106
 
70
- end
107
+ ##
108
+ # REFLECT
109
+ ##
71
110
 
72
- ##
73
- # Reflect the action.
74
- #
75
- # The first method call in the action creates a reflection.
76
- # Then method calls are shadow actions which return to the reflection.
77
- ##
78
- if action.has_empty_experiments? && !action.is_reflecting?
111
+ unless action.is_reflecting? && klass.class.reflekt_skipped?(method) || Reflekt.count(klass, method) >= @@reflekt.config.reflect_limit
112
+ unless action.is_actioned?
113
+ action.is_actioned = true
79
114
  action.is_reflecting = true
80
115
 
81
- # Create control.
82
- control = Control.new(action, 0, @@reflekt.aggregator)
83
- action.control = control
84
-
85
- # Execute control.
86
- control.reflect(*args)
87
-
88
- # Stop reflecting when control fails to execute.
89
- if control.status == :error
90
- @@reflekt.error = true
91
- # Continue reflecting when control executes succesfully.
92
- else
93
-
94
- # Save control as a reflection.
95
- @@reflekt.db.get("reflections").push(control.serialize())
96
-
97
- # Multiple experiments per action.
98
- action.experiments.each_with_index do |value, index|
99
-
100
- # Create experiment.
101
- experiment = Experiment.new(action, index + 1, @@reflekt.aggregator)
102
- action.experiments[index] = experiment
103
-
104
- # Execute experiment.
105
- experiment.reflect(*args)
106
- @reflekt_counts[method] = @reflekt_counts[method] + 1
107
-
108
- # Save experiment.
109
- @@reflekt.db.get("reflections").push(experiment.serialize())
110
-
111
- end
112
-
113
- # Save control.
114
- @@reflekt.db.get("controls").push(control.serialize())
115
-
116
- # Save results.
117
- @@reflekt.db.write()
118
-
119
- # Render results.
120
- @@reflekt.renderer.render()
121
-
116
+ action.reflect(*args)
117
+ if action.control.status == :error
118
+ @@reflekt.error = action.control.message
122
119
  end
123
120
 
121
+ # Render results.
122
+ @@reflekt.renderer.render()
123
+
124
124
  action.is_reflecting = false
125
125
  end
126
-
126
+ else
127
+ 🔥"> Skip reflection of #{method}()", :skip, :reflect, klass.class
127
128
  end
128
129
 
129
- # Don't execute skipped methods when reflecting.
130
- unless action.is_reflecting? && self.class.reflekt_skipped?(method)
130
+ ##
131
+ # EXECUTE
132
+ ##
131
133
 
132
- # Continue action / shadow action.
134
+ unless action.is_reflecting? && klass.class.reflekt_skipped?(method)
135
+ 🔥"> Execute #{method}()", :info, :execute, klass.class
133
136
  super *args
134
-
135
137
  end
136
138
 
137
- # When Reflekt disabled or control reflection failed.
139
+ # Finish execution if control encounters unrecoverable error.
138
140
  else
139
-
140
- # Continue action.
141
+ 🔥"Reflection error, finishing original execution...", :error, :reflect, klass.class
141
142
  super *args
142
-
143
143
  end
144
144
 
145
+ # When method called in constructor.
146
+ else
147
+ p "Reflection unsupported in constructor for #{method}()", :info, :setup, klass.class
148
+ super *args
145
149
  end
146
-
147
150
  end
151
+ end
148
152
 
149
- # Continue initialization.
150
- super
153
+ def self.setup_count(klass, method)
154
+ caller_id = klass.object_id
155
+ @@reflekt.counts[caller_id] = {} unless @@reflekt.counts.has_key? caller_id
156
+ @@reflekt.counts[caller_id][method] = 0 unless @@reflekt.counts[caller_id].has_key? method
157
+ end
158
+
159
+ def self.count(klass, method)
160
+ count = @@reflekt.counts.dig(klass.object_id, method) || 0
161
+ count
162
+ end
151
163
 
164
+ def self.increase_count(klass, method)
165
+ caller_id = klass.object_id
166
+ @@reflekt.counts[caller_id][method] = @@reflekt.counts[caller_id][method] + 1
152
167
  end
153
168
 
154
169
  ##
155
- # Provide Config instance to block.
170
+ # Configure Config singleton.
156
171
  ##
157
172
  def self.configure
173
+ reflekt_setup_class()
174
+
158
175
  yield(@@reflekt.config)
159
176
  end
160
177
 
161
178
  private
162
179
 
163
180
  def self.prepended(base)
164
-
165
181
  # Prepend class methods to the instance's singleton class.
166
182
  base.singleton_class.prepend(SingletonClassMethods)
167
183
 
168
- # Setup class.
169
- @@reflekt = Accessor.new()
170
- @@reflekt.setup ||= reflekt_setup_class
171
-
184
+ reflekt_setup_class()
172
185
  end
173
186
 
187
+ ##
174
188
  # Setup class.
189
+ #
190
+ # @paths
191
+ # - package_path [String] Absolute path to the library itself.
192
+ # - project_path [String] Absolute path to the project root.
193
+ # - output_path [String] Absolute path to the reflections directory.
194
+ ##
175
195
  def self.reflekt_setup_class()
176
196
 
177
- # Receive configuration.
178
- @@reflekt.config = Config.new()
179
-
180
- # Set configuration.
181
- @@reflekt.path = File.dirname(File.realpath(__FILE__))
197
+ # Only setup once.
198
+ return if defined? @@reflekt
182
199
 
183
- # Get reflections directory path from config or current action path.
184
- if @@reflekt.config.output_path
185
- @@reflekt.output_path = File.join(@@reflekt.config.output_path, @@reflekt.config.output_directory)
186
- else
187
- @@reflekt.output_path = File.join(Dir.pwd, @@reflekt.config.output_directory)
188
- end
200
+ @@reflekt = Accessor.new()
201
+ @@reflekt.config = Config.new()
202
+ @@reflekt.stack = ActionStack.new()
189
203
 
190
- # Create reflections directory.
204
+ # Setup paths.
205
+ @@reflekt.package_path = File.dirname(File.realpath(__FILE__))
206
+ @@reflekt.project_path = @@reflekt.config.project_path
207
+ @@reflekt.output_path = File.join(@@reflekt.project_path, @@reflekt.config.output_directory)
191
208
  unless Dir.exist? @@reflekt.output_path
192
209
  Dir.mkdir(@@reflekt.output_path)
193
210
  end
194
211
 
195
- # Create database.
212
+ # Setup database.
196
213
  @@reflekt.db = Rowdb.new(@@reflekt.output_path + '/db.js')
197
214
  @@reflekt.db.defaults({ :reflekt => { :api_version => 1 }})
198
- # @TODO Fix Rowdb.get(path) not returning values at path after Rowdb.push()
215
+ # TODO: Fix Rowdb.get(path) not returning values at path after Rowdb.push()
199
216
  db = @@reflekt.db.value()
200
217
 
201
- # Create shadow stack.
202
- @@reflekt.stack = ActionStack.new()
203
-
204
- # Create aggregated rule sets.
218
+ # Train aggregated rule sets.
205
219
  @@reflekt.aggregator = RuleSetAggregator.new(@@reflekt.config.meta_map)
206
220
  @@reflekt.aggregator.train(db[:controls])
207
221
 
208
- # Create renderer.
209
- @@reflekt.renderer = Renderer.new(@@reflekt.path, @@reflekt.output_path)
222
+ # Setup renderer.
223
+ @@reflekt.renderer = Renderer.new(@@reflekt.package_path, @@reflekt.output_path)
224
+
225
+ LitCLI.configure do |config|
226
+ config.statuses = {
227
+ :info => { icon: "ℹ", color: :blue, styles: [:upcase] },
228
+ :pass => { icon: "✔", color: :green, styles: [:upcase] },
229
+ :save => { icon: "✔", color: :green, styles: [:upcase] },
230
+ :skip => { icon: "⨯", color: :yellow, styles: [:upcase] },
231
+ :warn => { icon: "⚠", color: :yellow, styles: [:upcase] },
232
+ :fail => { icon: "⨯", color: :red, styles: [:upcase] },
233
+ :error => { icon: "!", color: :red, styles: [:upcase] },
234
+ :debug => { icon: "?", color: :purple, styles: [:upcase] },
235
+ }
236
+ config.types = {
237
+ :setup => { styles: [:dim, :bold, :capitalize] },
238
+ :event => { color: :yellow, styles: [:bold, :capitalize] },
239
+ :reflect => { color: :yellow, styles: [:bold, :capitalize] },
240
+ :result => { color: :yellow, styles: [:bold, :capitalize] },
241
+ :action => { color: :red, styles: [:bold, :capitalize] },
242
+ :control => { color: :blue, styles: [:bold, :capitalize] },
243
+ :experiment => { color: :green, styles: [:bold, :capitalize] },
244
+ :execute => { color: :purple, styles: [:bold, :capitalize] },
245
+ :meta => { color: :blue, styles: [:bold, :capitalize] },
246
+ }
247
+ end
210
248
 
211
249
  return true
212
-
213
250
  end
214
251
 
252
+ ##
253
+ # Publicly accessible class methods in the class that Reflekt is prepended to.
254
+ ##
215
255
  module SingletonClassMethods
216
-
217
256
  @@reflekt_skipped_methods = Set.new()
218
257
 
219
258
  ##
@@ -233,11 +272,6 @@ module Reflekt
233
272
  return true if @@reflekt_skipped_methods.include?(method)
234
273
  false
235
274
  end
236
-
237
- #def reflekt_limit(amount)
238
- # @@reflekt.reflect_limit = amount
239
- #end
240
-
241
275
  end
242
276
 
243
277
  end
data/lib/renderer.rb CHANGED
@@ -1,41 +1,38 @@
1
1
  module Reflekt
2
- class Renderer
2
+ class Renderer
3
3
 
4
- def initialize(path, output_path)
5
-
6
- @path = path
7
- @output_path = output_path
8
-
9
- end
10
-
11
- ##
12
- # Place files in output path.
13
- ##
14
- def render()
4
+ def initialize(package_path, output_path)
5
+ @package_path = package_path
6
+ @output_path = output_path
7
+ end
15
8
 
16
- filenames = [
17
- "bundle.js",
18
- "index.html",
19
- "package-lock.json",
20
- "package.json",
21
- "README.md",
22
- "server.js"
23
- ]
9
+ ##
10
+ # Place files in output path.
11
+ ##
12
+ def render()
13
+
14
+ filenames = [
15
+ "bundle.js",
16
+ "index.html",
17
+ "package-lock.json",
18
+ "package.json",
19
+ "README.md",
20
+ "server.js"
21
+ ]
22
+
23
+ filenames.each do |filename|
24
+ file = File.read(File.join(@package_path, "web", filename))
25
+ File.open(File.join(@output_path, filename), 'w+') do |f|
26
+ f.write file
27
+ end
28
+ end
24
29
 
25
- filenames.each do |filename|
26
- file = File.read(File.join(@path, "web", filename))
27
- File.open(File.join(@output_path, filename), 'w+') do |f|
30
+ file = File.read(File.join(@package_path, "web", "gitignore.txt"))
31
+ File.open(File.join(@output_path, ".gitignore"), 'w+') do |f|
28
32
  f.write file
29
33
  end
30
- end
31
34
 
32
- file = File.read(File.join(@path, "web", "gitignore.txt"))
33
- File.open(File.join(@output_path, ".gitignore"), 'w+') do |f|
34
- f.write file
35
35
  end
36
36
 
37
37
  end
38
-
39
-
40
- end
41
38
  end