cql_ruby 0.0.5 → 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50e03910538e8b57539419210406155ba4a9d0aed14f0852edfd1867e6fbccbe
4
- data.tar.gz: acb0f56f0faefb1f39613f2805b25cbee66e61d0a715138007c021036e05e03a
3
+ metadata.gz: e75a456d8dff7bb1b1affde452bfc7df1e01508a2514ca1729cf996e28c3bff6
4
+ data.tar.gz: 57d5bdbfe2991ba0ed0c529373405885cc4382c8cf2f6cec3c86117b65fc31bf
5
5
  SHA512:
6
- metadata.gz: 71feb8dab0a500e27989d7a96117b2f558b09f1eade1db44503a69ee3cadf6f21bec65d7e921ae219a39835ded528fc62cf91216989a9121006343636098b81b
7
- data.tar.gz: e3fdf1a7ecdc01fb7f5f3174d932a60e4718d2c08534414ec0627666590df1d29985cc113763423a3eb913a0d7994b789d1ad7c8baed645e04fe2b8104ecdb98
6
+ metadata.gz: b56ead4780a2823d1e327c7ab24e5d73923d21096a5b95832cdfbe7bc499a335bdc2ea5d252efaff96e2ba4fd6471b4ca6e12c9c18f90249116bff509217b66d
7
+ data.tar.gz: 1b30aedc86c325e1cc6fdd8813abe2d46f5d76d34f542e0d9f0446b0a7fa42721c4f76dfec376c477287eff46d71311f2b86b20c1dea6c9658cfe8b04207b775
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'parser'
3
4
  require 'cql_ruby'
4
5
 
5
6
  def show_help
@@ -14,14 +15,25 @@ def show_help
14
15
  \tFILTERS
15
16
  \t\tParent node type: type:T(,T)* Example: type:def,send,arg
16
17
  \t\tNesting under: nest:T(=NAME) Example: nest:def=save_user nest:class=UserManager
18
+ \t\tHas child: has:T(=NAME) Example: has:const has:def=valid?
17
19
 
18
20
  \tOPTIONS
21
+ \t\t--include=PATTERN Parses only files whose name matches the pattern.
22
+ \t\t--exclude=PATTERN Parses only files whose name does not match the pattern.
23
+ \t\t-lN (N is integer) Add N surrounding line before and after.
19
24
  \t\t-nc (--no-color) No color on output.
20
25
  \t\t-nf (--no-file) No file names.
21
26
  \t\t-ns (--no-source) No source code.
27
+ \t\t-nr (--no-recursion) Non-recursive search.
28
+ \t\t-v -vv -vvv Debug output levels.
29
+
30
+ \tALLOWED NODE TYPES
31
+ \t\tWhen defining filters only valid AST types can be defined. They are:
32
+ #{Parser::Meta::NODE_TYPES.to_a.join(' ')}
22
33
 
23
34
  \tEXAMPLES
24
- \t\tcql_ruby -ns update_user_info ./ type:send,arg
35
+ \t\tcql_ruby user ./
36
+ \t\tcql_ruby -ns -nr %user_info ./ type:send,arg nest:block nest:class=r/User/i has:str=WARNING
25
37
  HELP
26
38
 
27
39
  exit
@@ -34,6 +46,9 @@ def extract_options
34
46
  show_file: true,
35
47
  show_source: true,
36
48
  recursive_search: true,
49
+ surrounding_lines: 0,
50
+ include_pattern: nil,
51
+ exclude_pattern: nil,
37
52
  }
38
53
 
39
54
  ARGV.delete_if do |arg|
@@ -51,6 +66,12 @@ def extract_options
51
66
  CqlRuby::Config.debug_level = lvl
52
67
  elsif %w[-nr --no-recursive].include?(arg)
53
68
  options[:recursive_search] = false
69
+ elsif arg[0..1] == '-l' && arg[2..].to_i > 0
70
+ options[:surrounding_lines] = arg[2..].to_i
71
+ elsif arg.start_with?('--include=')
72
+ options[:include_pattern] = arg.split('=')[1]
73
+ elsif arg.start_with?('--exclude=')
74
+ options[:exclude_pattern] = arg.split('=')[1]
54
75
  else
55
76
  raise "Unknown arg #{arg}"
56
77
  end
@@ -78,7 +99,6 @@ begin
78
99
  pattern = ARGV.shift
79
100
  CqlRuby.log "Call pattern: <#{pattern}>" if CqlRuby::Config.debug_level_2?
80
101
 
81
- # TODO Make path patterns universal.
82
102
  path = ARGV.shift
83
103
  CqlRuby.log "Call path: <#{path}>" if CqlRuby::Config.debug_level_2?
84
104
 
@@ -92,6 +112,7 @@ begin
92
112
  printer.color_on = options[:show_color]
93
113
  printer.file_on = options[:show_file]
94
114
  printer.source_on = options[:show_source]
115
+ printer.surrounding_lines = options[:surrounding_lines]
95
116
 
96
117
  collector = CqlRuby::CrumbCollector.new(printer)
97
118
  CqlRuby::Executor.new(
@@ -101,8 +122,10 @@ begin
101
122
  path: path,
102
123
  filters: filters,
103
124
  recursive: options[:recursive_search],
125
+ include: options[:include_pattern],
126
+ exclude: options[:exclude_pattern],
104
127
  ).search_all
105
- rescue => e
106
- puts "Error: #{e}"
128
+ rescue
129
+ puts "Error: #{$!}"
107
130
  show_help
108
131
  end
@@ -8,6 +8,7 @@ module CqlRuby
8
8
  attr_writer :color_on
9
9
  attr_writer :file_on
10
10
  attr_writer :source_on
11
+ attr_writer :surrounding_lines
11
12
 
12
13
  def initialize
13
14
  super
@@ -15,14 +16,33 @@ module CqlRuby
15
16
  @color_on = true
16
17
  @file_on = true
17
18
  @source_on = true
19
+ @surrounding_lines = 0
20
+ @counter = 0
18
21
  end
19
22
 
20
23
  #
21
24
  # @param crumb [Cqlruby::Crumb]
22
25
  #
23
26
  def print(crumb)
24
- puts "#{color(94)}#{crumb.file_name}#{decor_reset}:#{color(33)}#{crumb.line_no}#{decor_reset} #{color(93)}#{crumb.type}#{decor_reset}" if @file_on
25
- puts decorate_source_line(crumb) if @source_on
27
+ parts = "##{color(97)}#{@counter}#{decor_reset}"
28
+ parts += " #{color(94)}#{crumb.file_name}#{decor_reset}:#{color(33)}#{crumb.line_no}#{decor_reset} #{color(93)}#{crumb.type}#{decor_reset}" if @file_on
29
+
30
+ if @source_on && @surrounding_lines.positive?
31
+ parts_visible_len = parts.gsub(/\e\[\d+m/, '').size + 1
32
+ indent = ' ' * parts_visible_len
33
+ (-@surrounding_lines).upto(-1).each { |offs| puts "#{indent}#{crumb.surrounding_line(offs)}" }
34
+ end
35
+
36
+ parts += ' ' + decorate_source_line(crumb) if @source_on
37
+
38
+ puts parts
39
+
40
+ if @source_on && @surrounding_lines.positive?
41
+ 1.upto(@surrounding_lines).each { |offs| puts "#{indent}#{crumb.surrounding_line(offs)}" }
42
+ puts '--'
43
+ end
44
+
45
+ @counter += 1
26
46
  end
27
47
 
28
48
  private
@@ -54,7 +74,6 @@ module CqlRuby
54
74
  # @param [Cqlruby::Crumb] crumb
55
75
  # @return [String]
56
76
  def decorate_source_line(crumb)
57
- # TODO add +- line surrounding options
58
77
  source = crumb.source
59
78
  from = crumb.line_col_no
60
79
  to = from + crumb.expression_size
@@ -63,14 +82,14 @@ module CqlRuby
63
82
  subject = source[from..to - 1] || ''
64
83
  suffix = source[to..] || ''
65
84
 
66
- color(90) +
85
+ color(97) +
67
86
  prefix +
68
87
  decor_reset +
69
88
  color(31) +
70
89
  bold +
71
90
  subject +
72
91
  decor_reset +
73
- color(90) +
92
+ color(97) +
74
93
  suffix +
75
94
  decor_reset
76
95
  end
@@ -33,7 +33,9 @@ module CqlRuby
33
33
  pattern:,
34
34
  path:,
35
35
  filters: [],
36
- recursive: true
36
+ recursive: true,
37
+ include: nil,
38
+ exclude: nil
37
39
  )
38
40
  @collector = collector
39
41
  @filter_reader = filter_reader
@@ -41,10 +43,15 @@ module CqlRuby
41
43
  @path = path
42
44
  @filters = filters
43
45
  @recursive = recursive
46
+ @include = include
47
+ @exclude = exclude
44
48
  end
45
49
 
46
50
  def search_all
47
51
  files.flat_map do |file|
52
+ next if !@exclude.nil? && CqlRuby::PatternMatcher.match?(@exclude, file)
53
+ next unless @include.nil? || CqlRuby::PatternMatcher.match?(@include, file)
54
+
48
55
  CqlRuby.log "File check: #{file}" if CqlRuby::Config.debug_level_3?
49
56
  search(file)
50
57
  end
@@ -58,6 +65,9 @@ module CqlRuby
58
65
  walk(ast, [], source_reader)
59
66
 
60
67
  nil
68
+ rescue
69
+ CqlRuby.log "File #{file} cannot be parsed"
70
+ CqlRuby.log "Reason: #{$!}" if CqlRuby::Config.debug_level_1?
61
71
  end
62
72
 
63
73
  def walk(node, ancestors, source_reader)
@@ -110,6 +120,10 @@ CqlRuby::Crumb = Struct.new(:full_name, :ancestors, :source_reader) do
110
120
  source_reader.source_line(line_no)
111
121
  end
112
122
 
123
+ def surrounding_line(offset)
124
+ source_reader.source_line(line_no + offset)
125
+ end
126
+
113
127
  def file_name
114
128
  source_reader.file
115
129
  end
@@ -129,6 +143,8 @@ CqlRuby::SourceReader = Struct.new(:file) do
129
143
  end
130
144
 
131
145
  def source_line(n)
146
+ return nil unless lines.size >= n
147
+
132
148
  lines[n - 1].chop
133
149
  end
134
150
 
@@ -7,6 +7,7 @@ module CqlRuby
7
7
  [
8
8
  pass_type?(filter_reader, ancestors),
9
9
  pass_nesting?(filter_reader, ancestors),
10
+ pass_has?(filter_reader, node, ancestors),
10
11
  ].all?
11
12
  end
12
13
 
@@ -25,7 +26,7 @@ module CqlRuby
25
26
  end
26
27
 
27
28
  #
28
- # @param [Cqlruby::FilterReader] filter_reader
29
+ # @param [CqlRuby::FilterReader] filter_reader
29
30
  # @param [Array<Parser::AST::Node>] ancestors
30
31
  #
31
32
  # @return [Boolean]
@@ -49,6 +50,79 @@ module CqlRuby
49
50
  end
50
51
  end
51
52
  end
53
+
54
+ #
55
+ # @param [CqlRuby::FilterReader] filter_reader
56
+ # @param [Parser::AST::Node] node
57
+ # @param [Array<Parser::AST::Node>] ancestors
58
+ #
59
+ def pass_has?(filter_reader, node, ancestors)
60
+ return true unless filter_reader.restrict_children?
61
+
62
+ filter_reader.has_leaves.all? do |has_rule|
63
+ anchor_node = try_get_class(ancestors) || try_get_module(ancestors) || try_get_def(ancestors)
64
+ next false unless anchor_node
65
+
66
+ has_node_with_name?(anchor_node, has_rule)
67
+ end
68
+ end
69
+
70
+ #
71
+ # @param [Array<Parser::AST::Node>] ancestors
72
+ #
73
+ def try_get_class(ancestors)
74
+ return nil unless ancestors.size >= 2
75
+ return nil unless ancestors[-1].type == :const
76
+ return nil unless ancestors[-2].type == :class
77
+
78
+ ancestors[-2].children[2]
79
+ end
80
+
81
+ #
82
+ # @param [Array<Parser::AST::Node>] ancestors
83
+ #
84
+ def try_get_module(ancestors)
85
+ return nil unless ancestors.size >= 2
86
+ return nil unless ancestors[-1].type == :const
87
+ return nil unless ancestors[-2].type == :module
88
+
89
+ ancestors[-2].children[1]
90
+ end
91
+
92
+ #
93
+ # @param [Array<Parser::AST::Node>] ancestors
94
+ #
95
+ def try_get_def(ancestors)
96
+ return nil unless ancestors.size >= 1
97
+ return nil unless ancestors[-1].type == :def
98
+
99
+ ancestors[-1].children[2]
100
+ end
101
+
102
+ #
103
+ # @param [Parser::AST::Node] anchor_node
104
+ # @param [CqlRuby::NodeSpec]
105
+ #
106
+ def has_node_with_name?(anchor_node, has_rule)
107
+ return false unless anchor_node.is_a?(Parser::AST::Node)
108
+
109
+ fn_children_with_type = ->(node) { node.children.map { |child| [child, node.type] } }
110
+ to_visit = fn_children_with_type.call(anchor_node)
111
+
112
+ until to_visit.empty?
113
+ current_node, current_type = to_visit.shift
114
+
115
+ if current_node.is_a?(Parser::AST::Node)
116
+ to_visit += fn_children_with_type.call(current_node)
117
+ else
118
+ if current_type == has_rule.type.to_sym && CqlRuby::PatternMatcher.match?(has_rule.name, current_node)
119
+ return true
120
+ end
121
+ end
122
+ end
123
+
124
+ false
125
+ end
52
126
  end
53
127
  end
54
128
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # TODO: Have convenience filters for type:_ such as: isclass, ismodule, isdef ...
4
+
3
5
  module CqlRuby
4
- class NestRule < Struct.new(:type, :name)
6
+ class NodeSpec < Struct.new(:type, :name)
7
+ # Make this non duplicated.
5
8
  NAME_ANY = '*'
6
- ALLOWED_TYPE = %w[class module def block].freeze
7
9
 
8
10
  class << self
9
11
  #
@@ -15,8 +17,7 @@ module CqlRuby
15
17
  type, name = raw_value.split('=')
16
18
  name ||= NAME_ANY
17
19
 
18
- raise "Unknown type: #{type}. Allowed: #{ALLOWED_TYPE}" unless ALLOWED_TYPE.include?(type)
19
- raise "Type #{type} cannot have a name." if %w[block].include?(type) && name != NAME_ANY
20
+ raise "Type '#{type}' is not recognized. See 'cql_ruby --help' for allowed types." unless Parser::Meta::NODE_TYPES.member?(type.to_sym)
20
21
 
21
22
  new(type, name)
22
23
  end
@@ -38,16 +39,21 @@ module CqlRuby
38
39
  # example: type:def,send
39
40
  #
40
41
  class FilterReader
41
- # @attribute [Parser::AST::Node] allowed_types
42
+ NESTING_ALLOWED_TYPES = %w[class module def block].freeze
43
+
44
+ # @attribute [Array<Symbol>] allowed_types
42
45
  attr_reader :allowed_types
43
- # @attribute [Array<Cqlruby::NestRule>] nest_under
46
+ # @attribute [Array<CqlRuby::NodeSpec>] nest_under
44
47
  attr_reader :nest_under
48
+ # @attribute [Array<CqlRuby::NodeSpec>] has_leaves
49
+ attr_reader :has_leaves
45
50
 
46
51
  def initialize(raw_filters)
47
52
  super()
48
53
 
49
54
  @allowed_types = []
50
55
  @nest_under = []
56
+ @has_leaves = []
51
57
 
52
58
  parse_raw_filters(raw_filters)
53
59
  end
@@ -60,6 +66,10 @@ module CqlRuby
60
66
  !@nest_under.empty?
61
67
  end
62
68
 
69
+ def restrict_children?
70
+ !@has_leaves.empty?
71
+ end
72
+
63
73
  private
64
74
 
65
75
  # @param [Array<String>] raw_filters
@@ -71,7 +81,13 @@ module CqlRuby
71
81
  if %w[type t].include?(name)
72
82
  @allowed_types += value.split(',').map(&:to_sym)
73
83
  elsif %w[nest n].include?(name)
74
- @nest_under << NestRule.from(value)
84
+ spec = NodeSpec.from(value)
85
+ raise "Unknown type for nesting: '#{spec.type}' from '#{raw_filter}'. Allowed: #{NESTING_ALLOWED_TYPES}" unless NESTING_ALLOWED_TYPES.include?(spec.type)
86
+ raise "Type #{spec.type} cannot have a name." if %w[block].include?(spec.type) && spec.restrict_name?
87
+
88
+ @nest_under << spec
89
+ elsif %w[has h].include?(name)
90
+ @has_leaves << NodeSpec.from(value)
75
91
  end
76
92
  end
77
93
 
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
  module CqlRuby
3
3
  module PatternMatcher
4
+ MATCH_ANY = '*'
5
+
4
6
  def self.match?(pattern, subject)
5
- subject = subject.to_s
6
7
  pattern = pattern.to_s
8
+ return true if pattern == MATCH_ANY
9
+
10
+ subject = subject.to_s
7
11
 
8
12
  if regex?(pattern)
9
13
  regex_match?(pattern, subject)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cql_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - itarato