rshade 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,15 +1,15 @@
1
1
  module RShade
2
2
  module Formatter
3
- class File < ::RShade::Base
4
- attr_reader :formatter, :event_store
3
+ class File
4
+ attr_reader :formatter
5
5
  FILE_NAME = 'stacktrace.json'.freeze
6
6
 
7
- def initialize(event_store, args={})
8
- @event_store = event_store
7
+ def initialize(args={})
9
8
  @formatter = args.fetch(:format, Json)
10
9
  end
11
10
 
12
- def call
11
+ # @param [RShade::EventProcessor] event_store
12
+ def call(event_store)
13
13
  data = formatter.call(event_store)
14
14
  if formatter == Json
15
15
  write_to_file(JSON.pretty_generate(data))
@@ -19,7 +19,7 @@ module RShade
19
19
  end
20
20
 
21
21
  def write_to_file(data)
22
- ::File.open(::File.join(RShade.config.store_dir, FILE_NAME), "w+") do |f|
22
+ ::File.open(::File.join(RShade::Config.store_dir, FILE_NAME), "w+") do |f|
23
23
  f.write data
24
24
  end
25
25
  end
@@ -1,16 +1,18 @@
1
+ require 'json'
2
+
1
3
  module RShade
2
4
  module Formatter
3
- class Html < ::RShade::Base
4
- attr_reader :formatter, :event_store
5
+ class Html
6
+ attr_reader :formatter
5
7
  FILE_NAME = 'stacktrace.html'.freeze
6
8
  TEMPLATE = 'html/template.html.erb'
7
9
 
8
- def initialize(event_store, args={})
9
- @event_store = event_store
10
+ def initialize(args={})
10
11
  @formatter = args.fetch(:formatter, Json)
11
12
  end
12
13
 
13
- def call
14
+ # @param [RShade::EventProcessor] event_store
15
+ def call(event_store)
14
16
  data = formatter.call(event_store)
15
17
  erb_template = ERB.new(template)
16
18
  content = erb_template.result_with_hash({json: data.to_json})
@@ -18,13 +20,13 @@ module RShade
18
20
  end
19
21
 
20
22
  def write_to_file(data)
21
- ::File.open(::File.join(RShade.config.store_dir, FILE_NAME), "w+") do |f|
23
+ ::File.open(::File.join(RShade::Config.store_dir, FILE_NAME), "w+") do |f|
22
24
  f.write data
23
25
  end
24
26
  end
25
27
 
26
28
  def template
27
- @template ||=::File.read(::File.join(::RShade.config.root_dir, TEMPLATE))
29
+ @template ||=::File.read(::File.join(::RShade::Config.root_dir, TEMPLATE))
28
30
  end
29
31
  end
30
32
  end
@@ -1,27 +1,26 @@
1
1
  module RShade
2
2
  module Formatter
3
- class Json < ::RShade::Base
4
- attr_reader :event_store
3
+ class Json
4
+ attr_reader :event_processor
5
5
 
6
- def initialize(event_store)
6
+ # @param [RShade::EventProcessor] event_store
7
+ def call(event_store)
7
8
  @event_store = event_store
8
- end
9
-
10
- def call
11
9
  flat
12
10
  end
13
11
 
14
12
  def flat
15
13
  arr = []
16
- event_store.iterate do |node, depth|
17
- arr << item(node)
14
+ event_store.each do |node|
15
+ arr << item(node.event)
18
16
  end
19
- arr.sort_by { |item| item[:depth]}
17
+ arr.sort_by { |item| item[:level]}
20
18
  end
21
19
 
22
20
  def hierarchical
23
21
  hash = {}
24
- event_store.iterate do |node, depth|
22
+ event_store.each do |node|
23
+ depth = node.level
25
24
  ref = hash_iterate(hash, depth)
26
25
  ref[:data] = item(node)
27
26
  end
@@ -48,11 +47,11 @@ module RShade
48
47
 
49
48
  def item(value)
50
49
  {
51
- class: value.klass.to_s,
52
- method_name: value.method_name,
53
- full_path: "#{value.path}:#{value.lineno}",
54
- depth: value.depth,
55
- 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
56
55
  }
57
56
  end
58
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
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)
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.8"
2
+ VERSION = "0.1.9"
3
3
  end
data/lib/rshade.rb CHANGED
@@ -1,34 +1,32 @@
1
- require 'colorize'
1
+ require 'colorized_string'
2
2
  require 'erb'
3
- require 'rshade/configuration'
4
- require 'rshade/base'
5
- require 'rshade/event_serializer'
6
- require 'rshade/filter/app_filter'
3
+ require 'weakref'
4
+ require 'set'
5
+ require 'observer'
6
+ require 'rshade/config'
7
+ require 'rshade/config/store'
8
+ require 'rshade/event_tree'
9
+ require 'rshade/event_processor'
10
+ require 'rshade/binding_serializer'
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'
7
20
  require 'rshade/formatter/string'
8
21
  require 'rshade/formatter/json'
9
22
  require 'rshade/formatter/file'
10
23
  require 'rshade/formatter/html'
11
24
  require 'rshade/formatter/stdout'
12
25
  require 'rshade/event'
13
- require 'rshade/event_store'
14
26
  require 'rshade/trace'
15
27
  require 'rshade/rspec/rspec'
16
28
  require 'rshade/version'
17
29
 
18
30
 
19
31
  module RShade
20
- APP_TRACE = :app_trace
21
- FULL_TRACE = :full_trace
22
-
23
- class << self
24
- attr_writer :config
25
-
26
- def config
27
- @config ||= Configuration.new
28
- end
29
-
30
- def configure
31
- yield configuration
32
- end
33
- end
34
32
  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
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rshade
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Lopatin
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-25 00:00:00.000000000 Z
11
+ date: 2022-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.16'
33
+ version: 2.2.33
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.16'
40
+ version: 2.2.33
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -85,12 +85,21 @@ files:
85
85
  - bin/setup
86
86
  - html/template.html.erb
87
87
  - lib/rshade.rb
88
- - lib/rshade/base.rb
89
- - lib/rshade/configuration.rb
88
+ - lib/rshade/binding_serializer.rb
89
+ - lib/rshade/config.rb
90
+ - lib/rshade/config/store.rb
91
+ - lib/rshade/core_extensions/object/reveal.rb
90
92
  - lib/rshade/event.rb
91
- - lib/rshade/event_serializer.rb
92
- - lib/rshade/event_store.rb
93
- - lib/rshade/filter/app_filter.rb
93
+ - lib/rshade/event_observer.rb
94
+ - lib/rshade/event_processor.rb
95
+ - lib/rshade/event_tree.rb
96
+ - lib/rshade/filter/abstract_filter.rb
97
+ - lib/rshade/filter/default.rb
98
+ - lib/rshade/filter/exclude_path_filter.rb
99
+ - lib/rshade/filter/filter_builder.rb
100
+ - lib/rshade/filter/filter_composition.rb
101
+ - lib/rshade/filter/include_path_filter.rb
102
+ - lib/rshade/filter/variable_filter.rb
94
103
  - lib/rshade/formatter/file.rb
95
104
  - lib/rshade/formatter/html.rb
96
105
  - lib/rshade/formatter/json.rb
@@ -98,8 +107,8 @@ files:
98
107
  - lib/rshade/formatter/string.rb
99
108
  - lib/rshade/rails/rails.rb
100
109
  - lib/rshade/rspec/rspec.rb
101
- - lib/rshade/storage.rb
102
110
  - lib/rshade/trace.rb
111
+ - lib/rshade/trace_observable.rb
103
112
  - lib/rshade/version.rb
104
113
  - rshade.gemspec
105
114
  - shade.jpg
@@ -112,7 +121,7 @@ metadata:
112
121
  homepage_uri: https://github.com/gingray/rshade
113
122
  source_code_uri: https://github.com/gingray/rshade
114
123
  changelog_uri: https://github.com/gingray/rshade/CHANGELOG.md
115
- post_install_message:
124
+ post_install_message:
116
125
  rdoc_options: []
117
126
  require_paths:
118
127
  - lib
@@ -128,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
137
  version: '0'
129
138
  requirements: []
130
139
  rubygems_version: 3.0.8
131
- signing_key:
140
+ signing_key:
132
141
  specification_version: 4
133
142
  summary: https://github.com/gingray/rshade
134
143
  test_files: []
data/lib/rshade/base.rb DELETED
@@ -1,13 +0,0 @@
1
- module RShade
2
- class Base
3
- def initialize(*args); end
4
-
5
- def self.call(*args)
6
- new(*args).call
7
- end
8
-
9
- def call
10
- raise NotImplementedError
11
- end
12
- end
13
- end
@@ -1,43 +0,0 @@
1
- module RShade
2
- class Configuration
3
- RUBY_VERSION_PATTERN = /ruby-[0-9.]*/
4
-
5
- attr_accessor :track_gems, :filter, :formatter
6
-
7
- def initialize
8
- @track_gems = Set.new
9
- end
10
-
11
- def excluded_paths
12
- @excluded_paths ||= begin
13
- paths = [ENV['GEM_PATH'].split(':'), parse_ruby_version].flatten.compact
14
- paths.reject do |v|
15
- track_gems.any? { |gem_name| v.include? gem_name }
16
- end
17
- end
18
- end
19
-
20
- def parse_ruby_version
21
- val = RUBY_VERSION_PATTERN.match(ENV['GEM_PATH'])
22
- return nil unless val
23
-
24
- val[0]
25
- end
26
-
27
- def filter
28
- @filter ||= ::RShade::Filter::AppFilter
29
- end
30
-
31
- def formatter
32
- @formatter ||= ::RShade::Formatter::Stdout
33
- end
34
-
35
- def store_dir
36
- File.expand_path('../../tmp', __dir__)
37
- end
38
-
39
- def root_dir
40
- @root_dir ||= File.expand_path('../../', __dir__)
41
- end
42
- end
43
- end