rshade 0.1.8 → 0.1.9

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 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