rshade 0.1.7 → 0.1.9.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.
@@ -0,0 +1,28 @@
1
+ module RShade
2
+ module Filter
3
+ class Default
4
+ RUBY_VERSION_PATTERN = /ruby-[0-9.]*/
5
+
6
+ def self.create
7
+ new.create
8
+ end
9
+
10
+ def create
11
+ [create_exclude]
12
+ end
13
+
14
+ def create_exclude
15
+ filter = ExcludePathFilter.new
16
+ filter.config do |paths|
17
+ excluded_paths.each do |path|
18
+ paths << path
19
+ end
20
+ end
21
+ end
22
+
23
+ def excluded_paths
24
+ [ENV['GEM_PATH'].split(':'), RUBY_VERSION_PATTERN, /internal/].flatten.compact
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ module RShade
2
+ module Filter
3
+ class ExcludePathFilter < IncludePathFilter
4
+ NAME = :exclude_paths
5
+
6
+ def name
7
+ NAME
8
+ end
9
+
10
+ def priority
11
+ 0
12
+ end
13
+
14
+ def call(event)
15
+ event_path = event.path
16
+ !paths.any? do |path|
17
+ next str?(path, event_path) if path.is_a? String
18
+ next regexp?(path, event_path) if path.is_a? Regexp
19
+ false
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ module RShade
2
+ module Filter
3
+ class FilterBuilder
4
+ def self.build(arr)
5
+ new.traverse(arr)
6
+ end
7
+
8
+ def map
9
+ {
10
+ or: [RShade::Filter::FilterComposition::OR_OP, 2],
11
+ and: [RShade::Filter::FilterComposition::AND_OP, 2],
12
+ unary: [RShade::Filter::FilterComposition::UNARY_OP, 1]
13
+ }
14
+ end
15
+
16
+ def traverse(arr)
17
+ op, arity = map[arr[0]]
18
+ arg1 = arr[1]
19
+ arg2 = nil
20
+ arg2 = arr[2] if arity == 2
21
+ arg1 = traverse(arg1) if arg1.is_a?(Array)
22
+ arg2 = traverse(arg2) if arg2.is_a?(Array)
23
+
24
+ RShade::Filter::FilterComposition.new(op, arg1, arg2)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,57 @@
1
+ module RShade
2
+ module Filter
3
+ class FilterComposition
4
+ include Enumerable
5
+ AND_OP = :and
6
+ OR_OP = :or
7
+ UNARY_OP = :unary
8
+ attr_reader :op, :left, :right, :parent
9
+ attr_accessor :parent
10
+
11
+ # @param [#call, Enumerable] left
12
+ # @param [#call, Enumerable] right
13
+ def initialize(op=UNARY_OP, left=nil, right=nil)
14
+ @op = op
15
+ @left = left
16
+ @right = right
17
+ end
18
+
19
+ def call(event)
20
+ case op
21
+ when UNARY_OP
22
+ return left&.call(event)
23
+ when AND_OP
24
+ return left&.call(event) && right&.call(event)
25
+ when OR_OP
26
+ l = left&.call(event)
27
+ r = right&.call(event)
28
+ # puts "#{left} => #{l} OR #{right} => #{r}"
29
+ return l || r
30
+ else
31
+ raise 'undefined op'
32
+ end
33
+ end
34
+
35
+ def each(&block)
36
+ if left&.respond_to?(:each)
37
+ left&.each(&block)
38
+ else
39
+ yield left
40
+ end
41
+
42
+ if right&.respond_to?(:each)
43
+ right&.each(&block)
44
+ else
45
+ yield right
46
+ end
47
+ end
48
+
49
+ def config_filter(type, &block)
50
+ filter = find do |filter|
51
+ filter.is_a? type
52
+ end
53
+ filter.config(&block) if filter
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ module RShade
2
+ module Filter
3
+ class IncludePathFilter < AbstractFilter
4
+ attr_reader :paths
5
+
6
+ NAME = :include_paths
7
+
8
+ def initialize
9
+ @paths = []
10
+ end
11
+
12
+ def name
13
+ NAME
14
+ end
15
+
16
+ def priority
17
+ 1
18
+ end
19
+
20
+ def call(event)
21
+ event_path = event.path
22
+ paths.any? do |path|
23
+ next str?(path, event_path) if path.is_a? String
24
+ next regexp?(path, event_path) if path.is_a? Regexp
25
+ false
26
+ end
27
+ end
28
+
29
+ def config_call(&block)
30
+ block.call(@paths)
31
+ end
32
+
33
+ private
34
+ def str?(str, event_path)
35
+ event_path.include?(str)
36
+ end
37
+
38
+ def regexp?(regex, event_path)
39
+ regex.match?(event_path)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ module RShade
2
+ module Filter
3
+ class VariableFilter < AbstractFilter
4
+ attr_reader :matchers
5
+ NAME = :variable_filter
6
+
7
+ def initialize
8
+ @matchers = []
9
+ end
10
+
11
+ def name
12
+ :variable_filter
13
+ end
14
+
15
+ def priority
16
+ 2
17
+ end
18
+
19
+ def call(event)
20
+ matchers.each do |match|
21
+ event.vars.each do |name, value|
22
+ return true if match.call(name, value)
23
+ end
24
+ end
25
+ false
26
+ end
27
+
28
+ def config_call(&block)
29
+ matchers << block
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ module RShade
2
+ module Formatter
3
+ class File
4
+ attr_reader :formatter
5
+ FILE_NAME = 'stacktrace.json'.freeze
6
+
7
+ def initialize(args={})
8
+ @formatter = args.fetch(:format, Json)
9
+ end
10
+
11
+ # @param [RShade::EventProcessor] event_store
12
+ def call(event_store)
13
+ data = formatter.call(event_store)
14
+ if formatter == Json
15
+ write_to_file(JSON.pretty_generate(data))
16
+ else
17
+ write_to_file(data.to_s)
18
+ end
19
+ end
20
+
21
+ def write_to_file(data)
22
+ ::File.open(::File.join(RShade::Config.store_dir, FILE_NAME), "w+") do |f|
23
+ f.write data
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ require 'json'
2
+
3
+ module RShade
4
+ module Formatter
5
+ class Html
6
+ attr_reader :formatter
7
+ FILE_NAME = 'stacktrace.html'.freeze
8
+ TEMPLATE = 'html/template.html.erb'
9
+
10
+ def initialize(args={})
11
+ @formatter = args.fetch(:formatter, Json)
12
+ end
13
+
14
+ # @param [RShade::EventProcessor] event_store
15
+ def call(event_store)
16
+ data = formatter.call(event_store)
17
+ erb_template = ERB.new(template)
18
+ content = erb_template.result_with_hash({json: data.to_json})
19
+ write_to_file(content)
20
+ end
21
+
22
+ def write_to_file(data)
23
+ ::File.open(::File.join(RShade::Config.store_dir, FILE_NAME), "w+") do |f|
24
+ f.write data
25
+ end
26
+ end
27
+
28
+ def template
29
+ @template ||=::File.read(::File.join(::RShade::Config.root_dir, TEMPLATE))
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,41 +1,30 @@
1
1
  module RShade
2
2
  module Formatter
3
- class Json < ::RShade::Base
4
- attr_reader :event_store
5
- FILE_NAME = 'stacktrace.json'.freeze
3
+ class Json
4
+ attr_reader :event_processor
6
5
 
7
- def initialize(event_store)
6
+ # @param [RShade::EventProcessor] event_store
7
+ def call(event_store)
8
8
  @event_store = event_store
9
- end
10
-
11
- def call
12
9
  flat
13
10
  end
14
11
 
15
12
  def flat
16
13
  arr = []
17
- event_store.iterate do |node, depth|
18
- arr << item(node)
14
+ event_store.each do |node|
15
+ arr << item(node.event)
19
16
  end
20
- write_to_file(JSON.pretty_generate(arr.sort_by { |item| item[:depth]}))
21
- arr
17
+ arr.sort_by { |item| item[:level]}
22
18
  end
23
19
 
24
20
  def hierarchical
25
21
  hash = {}
26
- event_store.iterate do |node, depth|
22
+ event_store.each do |node|
23
+ depth = node.level
27
24
  ref = hash_iterate(hash, depth)
28
25
  ref[:data] = item(node)
29
26
  end
30
- hash = sort_hash(hash)
31
- write_to_file(JSON.pretty_generate(hash))
32
- hash
33
- end
34
-
35
- def write_to_file(data)
36
- File.open(File.join(RShade.config.store_dir, FILE_NAME), "w+") do |f|
37
- f.write data
38
- end
27
+ sort_hash(hash)
39
28
  end
40
29
 
41
30
  def sort_hash(h)
@@ -58,11 +47,11 @@ module RShade
58
47
 
59
48
  def item(value)
60
49
  {
61
- class: value.klass.to_s,
62
- method_name: value.method_name,
63
- full_path: "#{value.path}:#{value.lineno}",
64
- depth: value.depth,
65
- vars: value.vars
50
+ class: value.klass.to_s,
51
+ method_name: value.method_name,
52
+ full_path: "#{value.path}:#{value.lineno}",
53
+ level: value.depth,
54
+ vars: value.vars
66
55
  }
67
56
  end
68
57
  end
@@ -1,9 +1,9 @@
1
1
  module RShade
2
2
  module Formatter
3
3
  class Stdout < String
4
- def call
5
- buffer = super
6
- puts buffer
4
+ # @param [RShade::EventProcessor] event_store
5
+ def call(event_store)
6
+ puts super(event_store)
7
7
  end
8
8
  end
9
9
  end
@@ -1,29 +1,35 @@
1
1
  module RShade
2
2
  module Formatter
3
- class String < ::RShade::Base
4
- attr_reader :event_store
3
+ class String
5
4
  ROOT_SEP = "---\n"
6
5
 
7
- def initialize(event_store)
8
- @event_store = event_store
6
+ def initialize(opts= {})
9
7
  end
10
8
 
11
- def call
9
+ # @param [RShade::EventProcessor] event_store
10
+ def call(event_store)
12
11
  buffer = StringIO.new
13
- event_store.iterate do |node, depth|
12
+ event_store.each_with_index do |node, idx|
13
+ depth = node.level
14
+ event = node.value
14
15
  if depth == 1
15
16
  buffer << ROOT_SEP
16
17
  next
17
18
  end
18
- puts line(node, depth)
19
+ next unless event
20
+ buffer.write line(idx, event, node.vlevel)
19
21
  end
20
22
  buffer.string
21
23
  end
22
24
 
23
- def line(value, depth)
24
- class_method = "#{value.klass}##{value.method_name}".colorize(:green)
25
- full_path = "#{value.path}:#{value.lineno}".colorize(:blue)
26
- "#{' ' * depth}#{class_method}(#{value.vars}) -> #{full_path}\n"
25
+ def line(line_idx, value, depth)
26
+ vars = value.vars
27
+ returned = ColorizedString["=> |#{value.return_value}|"].colorize(:magenta)
28
+
29
+ class_method = ColorizedString["#{value.klass}##{value.method_name}"].colorize(:green)
30
+ full_path = ColorizedString["#{value.path}:#{value.lineno}"].colorize(:blue)
31
+ line_idx = ColorizedString["[#{line_idx}] "].colorize(:red)
32
+ "#{' ' * depth}#{line_idx}#{class_method}(#{vars}) #{returned} -> #{full_path}\n"
27
33
  end
28
34
  end
29
35
  end
@@ -0,0 +1,47 @@
1
+ module RShade
2
+ module Serializer
3
+ class Traversal
4
+ attr_reader :types
5
+
6
+ def initialize(custom_types={})
7
+ @types = default_types.merge(custom_types)
8
+ end
9
+
10
+ def call(hash)
11
+ new_val = {}
12
+ hash.each do |name, value|
13
+ new_val[name] = traverse(value)
14
+ end
15
+ new_val
16
+ end
17
+
18
+ def default_types
19
+ {
20
+ default: ->(value) { value.to_s },
21
+ Integer => ->(value) { value },
22
+ Float => ->(value) { value },
23
+ Numeric => ->(value) { value },
24
+ String => ->(value) { value },
25
+ Hash => ->(value) do
26
+ hash = {}
27
+ value.each do |k,v|
28
+ hash[k] = traverse(v)
29
+ end
30
+ hash
31
+ end,
32
+ Array => ->(value) do
33
+ value.map { |item| traverse(item) }
34
+ end
35
+ }
36
+ end
37
+
38
+ def traverse(value)
39
+ klass = value
40
+ klass = value.class unless value.is_a?(Class)
41
+ serializer = types[klass]
42
+ serializer = types[:default] unless serializer
43
+ serializer.call(value)
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/rshade/trace.rb CHANGED
@@ -1,54 +1,34 @@
1
1
  module RShade
2
2
  class Trace
3
- attr_accessor :event_store, :formatter, :filter
4
- EVENTS = %i[call return].freeze
5
-
6
- def initialize(options={})
7
- @event_store = EventStore.new
8
- @formatter = options.fetch(:formatter, RShade.config.formatter)
9
- @filter = options.fetch(:filter, RShade.config.filter)
10
- @tp = TracePoint.new(*EVENTS, &method(:process_trace))
11
- @level = 1
12
- @stat = {}
13
- @calls = 0
14
- @returns = 0
3
+ attr_reader :config, :event_store
4
+
5
+ # @param [RShade::Config,RShade::Config::Store] config
6
+ def initialize(config)
7
+ @config = fetch_config(config)
8
+ @event_store = EventTree.new
15
9
  end
16
10
 
17
- def self.reveal(options={}, &block)
18
- new(options).reveal(&block)
11
+ def self.reveal(config=nil, &block)
12
+ new(config).reveal(&block)
19
13
  end
20
14
 
21
- def reveal
22
- return unless block_given?
23
-
24
- @tp.enable
25
- yield
15
+ def reveal(&block)
16
+ processor = EventProcessor.new(event_store, config)
17
+ observer = EventObserver.new(config, processor)
18
+ observable = RShade::TraceObservable.new([observer], config)
19
+ observable.reveal &block
26
20
  self
27
- ensure
28
- @tp.disable
29
21
  end
30
22
 
31
23
  def show
32
- formatter.call(event_store)
24
+ config.formatter.call(event_store)
33
25
  end
34
26
 
35
- def stat
36
- { calls: @calls, returns: @returns }
37
- end
38
-
39
- def process_trace(tp)
40
- if tp.event == :call
41
- @level +=1
42
- hash = EventSerializer.call(tp, @level)
43
- return unless filter.call(hash)
44
- @calls += 1
45
- event_store << Event.new(hash)
46
- end
47
-
48
- if tp.event == :return && @level > 0
49
- @returns += 1
50
- @level -=1
51
- end
27
+ private
28
+ def fetch_config(config)
29
+ config = config || ::RShade::Config.default
30
+ config = config.value if config.is_a?(::RShade::Config)
31
+ config
52
32
  end
53
33
  end
54
34
  end
@@ -0,0 +1,40 @@
1
+ module RShade
2
+ class TraceObservable
3
+ include Observable
4
+ attr_reader :trace_p
5
+ CALL_EVENTS = Set[:call, :c_call, :b_call]
6
+ RETURN_EVENTS = Set[:return, :c_return, :b_return]
7
+
8
+ # @param [Enumerable<#call>, #call] observers
9
+ # @param [::RShade::Config::Store] config
10
+ def initialize(observers, config)
11
+ @trace_p = TracePoint.new(*config.tp_events, &method(:process))
12
+ observers = [observers] unless observers.is_a?(Enumerable)
13
+
14
+ observers.each do |observer|
15
+ add_observer(observer, :call)
16
+ end
17
+ end
18
+
19
+ def reveal
20
+ return unless block_given?
21
+
22
+ trace_p.enable
23
+ yield
24
+ self
25
+ ensure
26
+ trace_p.disable
27
+ end
28
+
29
+ private
30
+ # more info https://rubyapi.org/3.1/o/tracepoint
31
+ # @param [TracePoint] tp
32
+ def process(tp)
33
+ changed
34
+ event = Event.from_trace_point(tp)
35
+ return notify_observers(event, :enter) if CALL_EVENTS.include?(tp.event)
36
+ return notify_observers(event, :leave) if RETURN_EVENTS.include?(tp.event)
37
+ notify_observers(event, :other)
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module RShade
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.9.1"
3
3
  end
data/lib/rshade.rb CHANGED
@@ -1,32 +1,31 @@
1
- require 'colorize'
2
- require 'rshade/configuration'
3
- require 'rshade/base'
4
- require 'rshade/event_serializer'
5
- require 'rshade/filter/app_filter'
1
+ require 'colorized_string'
2
+ require 'erb'
3
+ require 'weakref'
4
+ require 'set'
5
+ require 'observer'
6
+ require 'rshade/config'
7
+ require 'rshade/config/store'
8
+ require 'rshade/serializer/traversal'
9
+ require 'rshade/event_tree'
10
+ require 'rshade/event_processor'
11
+ require 'rshade/event_observer'
12
+ require 'rshade/trace_observable'
13
+ require 'rshade/filter/abstract_filter'
14
+ require 'rshade/filter/filter_builder'
15
+ require 'rshade/filter/filter_composition'
16
+ require 'rshade/filter/include_path_filter'
17
+ require 'rshade/filter/exclude_path_filter'
18
+ require 'rshade/filter/variable_filter'
19
+ require 'rshade/filter/default'
6
20
  require 'rshade/formatter/string'
7
21
  require 'rshade/formatter/json'
22
+ require 'rshade/formatter/file'
23
+ require 'rshade/formatter/html'
8
24
  require 'rshade/formatter/stdout'
9
- require 'rshade/formatter/http'
10
25
  require 'rshade/event'
11
- require 'rshade/event_store'
12
26
  require 'rshade/trace'
13
27
  require 'rshade/rspec/rspec'
14
28
  require 'rshade/version'
15
29
 
16
-
17
30
  module RShade
18
- APP_TRACE = :app_trace
19
- FULL_TRACE = :full_trace
20
-
21
- class << self
22
- attr_writer :config
23
-
24
- def config
25
- @config ||= Configuration.new
26
- end
27
-
28
- def configure
29
- yield configuration
30
- end
31
- end
32
31
  end
data/rshade.gemspec CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
38
38
 
39
39
  spec.add_runtime_dependency 'colorize'
40
40
 
41
- spec.add_development_dependency "bundler", "~> 1.16"
41
+ spec.add_development_dependency "bundler", "~> 2.2.33"
42
42
  spec.add_development_dependency "rake", ">= 12.3.3"
43
43
  spec.add_development_dependency "rspec", "~> 3.0"
44
44
  end