querly 0.15.1 → 1.3.0

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: 667d28c47f1169482de3e65425249fd1e9ca2593876fe13c8661558b1a588151
4
- data.tar.gz: b0d6025b7fcb82b6933b020d080b28f8ef6e5637b9096fbd9d9e806cf5e1177f
3
+ metadata.gz: c9b153bfa5b7a49deb8a6bbff12517452d2b23498b371a4fcbc154fac2330dd9
4
+ data.tar.gz: 7d08df5b8053cbb9e45d6db6b132e6dcb9171797d5687caf96b3827aed53fae2
5
5
  SHA512:
6
- metadata.gz: cf4b1ce23c783b45e54369cfe5372d4be44bf516db2a9938f898d376706b832d3618d7940ed0f78135a565bd853a8707068390df71a1a8c41fdbc8581fec3e49
7
- data.tar.gz: 66cdc533c9a98c0a9cd9139a1f015225bc1d33ecb44c066c65b46c2ca608c80a37d6b31aec3dd7e1837a7684f7307e93e7acfe0be2c378fd9d994ac7cb213489
6
+ metadata.gz: c2a5371c77896ce523078b014875744f52a248b1e84f04772e0b1ae7eee3cdf876136915c87de876436dc180e9c0b1cbb6ef487a9aaf4dccaf7faad68223546d
7
+ data.tar.gz: 2671df7da016eeb25038a504ee5c7e203e6079ead0adcf2cdfeb25c02c3a3157ee5a05347f9cdd33e25289d12b28de5a38a15fb595bb258e49accf5c4d818b3d
@@ -0,0 +1,15 @@
1
+ name: RuboCop
2
+
3
+ on: pull_request
4
+
5
+ jobs:
6
+ rubocop:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - uses: ruby/setup-ruby@v1
11
+ with:
12
+ ruby-version: "3.0"
13
+ - run: gem install rubocop rubocop-rubycw
14
+ - name: Run RuboCop
15
+ run: rubocop --format github
@@ -0,0 +1,22 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request: {}
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby: ["2.7", "3.0", head]
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler-cache: true
21
+ - run: bin/setup
22
+ - run: bundle exec rake build test
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ require:
2
+ - rubocop-rubycw
3
+
4
+ AllCops:
5
+ DisabledByDefault: true
6
+ Exclude:
7
+ - test/data/**/*.rb
8
+
9
+ Rubycw/Rubycw:
10
+ Enabled: true
data/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.3.0 (2021-07-05)
6
+
7
+ * Require Ruby 2.7 or 3.0 by @yubiquitous ([#88](https://github.com/soutaro/querly/pull/88))
8
+ * Use 3.0 compatible parser by @yubiquitous ([#88](https://github.com/soutaro/querly/pull/88))
9
+
10
+ ## 1.2.0 (2020-12-15)
11
+
12
+ * Relax Thor version requirements by @y-yagi ([#85](https://github.com/soutaro/querly/pull/85))
13
+ * Fix ERB comment preprocessing by @mallowlabs ([#84](https://github.com/soutaro/querly/pull/84))
14
+ * Better error message for Ruby code syntax error by @ybiquitous ([#83](https://github.com/soutaro/querly/pull/83))
15
+
16
+ ## 1.1.0 (2020-05-17)
17
+
18
+ * Fix invalid bytes sequence in UTF-8 error by @mallowlabs [#75](https://github.com/soutaro/querly/pull/75)
19
+ * Detect safe navigation operator as a method call by @pocke [#71](https://github.com/soutaro/querly/pull/71)
20
+
21
+ ## 1.0.0 (2019-7-19)
22
+
23
+ * Add `--config` option for `find` and `console` [#67](https://github.com/soutaro/querly/pull/67)
24
+ * Improve preprocessor performance by processing concurrently [#68](https://github.com/soutaro/querly/pull/68)
25
+
26
+ ## 0.16.0 (2019-04-23)
27
+
28
+ * Support string literal pattern (@pocke) [#64](https://github.com/soutaro/querly/pull/64)
29
+ * Allow underscore method name pattern (@pocke) [#63](https://github.com/soutaro/querly/pull/63)
30
+ * Add erb support (@hanachin) [#61](https://github.com/soutaro/querly/pull/61)
31
+ * Add `exit` command on console (@wata727) [#59](https://github.com/soutaro/querly/pull/59)
32
+
5
33
  ## 0.15.1 (2019-03-12)
6
34
 
7
35
  * Relax parser version requirement
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  ![Querly logo](https://github.com/soutaro/querly/blob/master/logo/Querly%20horizontal.png)
2
+
2
3
  # Querly - Pattern Based Checking Tool for Ruby
3
4
 
4
- [![Build Status](https://travis-ci.org/soutaro/querly.svg?branch=master)](https://travis-ci.org/soutaro/querly)
5
+ ![Ruby](https://github.com/soutaro/querly/workflows/Ruby/badge.svg)
5
6
 
6
7
  Querly is a query language and tool to find out method calls from Ruby programs.
7
8
  Define rules to check your program with patterns to find out *bad* pieces.
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ task :default => :test
11
11
  task :build => :racc
12
12
  task :test => :racc
13
13
 
14
- rule /\.rb/ => ".y" do |t|
14
+ rule %r/\.rb/ => ".y" do |t|
15
15
  sh "racc", "-v", "-o", "#{t.name}", "#{t.source}"
16
16
  end
17
17
 
data/bin/setup CHANGED
@@ -4,5 +4,4 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
-
8
- # Do any other automated setup that you need to do here
7
+ bundle exec rake racc
data/lib/querly.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require 'pathname'
2
2
  require "yaml"
3
3
  require "rainbow"
4
- require "parser/ruby25"
4
+ require "parser/ruby30"
5
5
  require "set"
6
6
  require "open3"
7
7
  require "active_support/inflector"
8
+ require "parallel"
8
9
 
9
10
  require "querly/version"
10
11
  require 'querly/analyzer'
data/lib/querly/cli.rb CHANGED
@@ -12,6 +12,7 @@ module Querly
12
12
  option :root
13
13
  option :format, default: "text", type: :string, enum: %w(text json)
14
14
  option :rule, type: :string
15
+ option :threads, default: Parallel.processor_count, type: :numeric
15
16
  def check(*paths)
16
17
  require 'querly/cli/formatter'
17
18
 
@@ -23,6 +24,8 @@ module Querly
23
24
  end
24
25
  formatter.start
25
26
 
27
+ threads = Integer(options[:threads])
28
+
26
29
  begin
27
30
  unless config_path.file?
28
31
  STDERR.puts <<-Message
@@ -32,20 +35,15 @@ Specify configuration file by --config option.
32
35
  exit 1
33
36
  end
34
37
 
35
- root_option = options[:root]
36
- root_path = root_option ? Pathname(root_option).realpath : config_path.parent.realpath
37
-
38
- config = begin
39
- yaml = YAML.load(config_path.read)
40
- Config.load(yaml, config_path: config_path, root_dir: root_path, stderr: STDERR)
38
+ begin
39
+ config = config(root_option: options[:root])
41
40
  rescue => exn
42
41
  formatter.config_error config_path, exn
43
- exit 1
44
42
  end
45
43
 
46
44
  analyzer = Analyzer.new(config: config, rule: options[:rule])
47
45
 
48
- ScriptEnumerator.new(paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) }, config: config).each do |path, script|
46
+ ScriptEnumerator.new(paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) }, config: config, threads: threads).each do |path, script|
49
47
  case script
50
48
  when Script
51
49
  analyzer.scripts << script
@@ -67,6 +65,8 @@ Specify configuration file by --config option.
67
65
  end
68
66
 
69
67
  desc "console [paths]", "Start console for given paths"
68
+ option :config, default: "querly.yml"
69
+ option :threads, default: Parallel.processor_count, type: :numeric
70
70
  def console(*paths)
71
71
  require 'querly/cli/console'
72
72
  home_path = if (path = ENV["QUERLY_HOME"])
@@ -75,21 +75,32 @@ Specify configuration file by --config option.
75
75
  Pathname(Dir.home) + ".querly"
76
76
  end
77
77
  home_path.mkdir unless home_path.exist?
78
+ config = config_path.file? ? config(root_option: nil) : nil
79
+ threads = Integer(options[:threads])
78
80
 
79
81
  Console.new(
80
82
  paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) },
81
83
  history_path: home_path + "history",
82
- history_size: ENV["QUERLY_HISTORY_SIZE"]&.to_i || 1_000_000
84
+ history_size: ENV["QUERLY_HISTORY_SIZE"]&.to_i || 1_000_000,
85
+ config: config,
86
+ threads: threads
83
87
  ).start
84
88
  end
85
89
 
86
90
  desc "find pattern [paths]", "Find for the pattern in given paths"
91
+ option :config, default: "querly.yml"
92
+ option :threads, default: Parallel.processor_count, type: :numeric
87
93
  def find(pattern, *paths)
88
94
  require 'querly/cli/find'
89
95
 
96
+ config = config_path.file? ? config(root_option: nil) : nil
97
+ threads = Integer(options[:threads])
98
+
90
99
  Find.new(
91
100
  pattern: pattern,
92
101
  paths: paths.empty? ? [Pathname.pwd] : paths.map {|path| Pathname(path) },
102
+ config: config,
103
+ threads: threads
93
104
  ).start
94
105
  end
95
106
 
@@ -125,6 +136,13 @@ Specify configuration file by --config option.
125
136
 
126
137
  private
127
138
 
139
+ def config(root_option:)
140
+ root_path = root_option ? Pathname(root_option).realpath : config_path.parent.realpath
141
+
142
+ yaml = YAML.load(config_path.read)
143
+ Config.load(yaml, config_path: config_path, root_dir: root_path, stderr: STDERR)
144
+ end
145
+
128
146
  def config_path
129
147
  [Pathname(options[:config]),
130
148
  Pathname("querly.yaml")].compact.find(&:file?) || Pathname(options[:config])
@@ -8,13 +8,17 @@ module Querly
8
8
  attr_reader :paths
9
9
  attr_reader :history_path
10
10
  attr_reader :history_size
11
+ attr_reader :config
11
12
  attr_reader :history
13
+ attr_reader :threads
12
14
 
13
- def initialize(paths:, history_path:, history_size:)
15
+ def initialize(paths:, history_path:, history_size:, config: nil, threads:)
14
16
  @paths = paths
15
17
  @history_path = history_path
16
18
  @history_size = history_size
19
+ @config = config
17
20
  @history = []
21
+ @threads = threads
18
22
  end
19
23
 
20
24
  def start
@@ -42,9 +46,9 @@ Querly #{VERSION}, interactive console
42
46
  def analyzer
43
47
  return @analyzer if @analyzer
44
48
 
45
- @analyzer = Analyzer.new(config: nil, rule: nil)
49
+ @analyzer = Analyzer.new(config: config, rule: nil)
46
50
 
47
- ScriptEnumerator.new(paths: paths, config: nil).each do |path, script|
51
+ ScriptEnumerator.new(paths: paths, config: config, threads: threads).each do |path, script|
48
52
  case script
49
53
  when Script
50
54
  @analyzer.scripts << script
@@ -60,7 +64,7 @@ Querly #{VERSION}, interactive console
60
64
  def start_loop
61
65
  while line = Readline.readline("> ", true)
62
66
  case line
63
- when "quit"
67
+ when "quit", "exit"
64
68
  exit
65
69
  when "reload!"
66
70
  STDOUT.print "reloading..."
@@ -7,10 +7,14 @@ module Querly
7
7
 
8
8
  attr_reader :pattern_str
9
9
  attr_reader :paths
10
+ attr_reader :config
11
+ attr_reader :threads
10
12
 
11
- def initialize(pattern:, paths:)
13
+ def initialize(pattern:, paths:, config: nil, threads:)
12
14
  @pattern_str = pattern
13
15
  @paths = paths
16
+ @config = config
17
+ @threads = threads
14
18
  end
15
19
 
16
20
  def start
@@ -36,7 +40,7 @@ module Querly
36
40
  puts "#{count} results"
37
41
  rescue => exn
38
42
  STDOUT.puts Rainbow("Error: #{exn}").red
39
- STDTOU.puts "pattern: #{pattern_str}"
43
+ STDOUT.puts "pattern: #{pattern_str}"
40
44
  STDOUT.puts "Backtrace:"
41
45
  STDOUT.puts format_backtrace(exn.backtrace)
42
46
  end
@@ -48,9 +52,9 @@ module Querly
48
52
  def analyzer
49
53
  return @analyzer if @analyzer
50
54
 
51
- @analyzer = Analyzer.new(config: nil, rule: nil)
55
+ @analyzer = Analyzer.new(config: config, rule: nil)
52
56
 
53
- ScriptEnumerator.new(paths: paths, config: nil).each do |path, script|
57
+ ScriptEnumerator.new(paths: paths, config: config, threads: threads).each do |path, script|
54
58
  case script
55
59
  when Script
56
60
  @analyzer.scripts << script
@@ -46,7 +46,12 @@ module Querly
46
46
 
47
47
  def script_error(path, error)
48
48
  STDERR.puts Rainbow("Failed to load script: #{path}").red
49
- STDERR.puts error.inspect
49
+
50
+ if error.is_a? Parser::SyntaxError
51
+ STDERR.puts error.diagnostic.render
52
+ else
53
+ STDERR.puts error.inspect
54
+ end
50
55
  end
51
56
 
52
57
  def issue_found(script, rule, pair)
@@ -140,7 +140,7 @@ module Querly
140
140
 
141
141
  found = false
142
142
 
143
- node = Parser::Ruby25.parse(example)
143
+ node = Parser::Ruby30.parse(example)
144
144
  NodePair.new(node: node).each_subpair do |pair|
145
145
  if analyzer.test_pair(pair, pattern)
146
146
  found = true
@@ -25,7 +25,7 @@ module Querly
25
25
  yield self
26
26
 
27
27
  children.each do |child|
28
- child.each_subpair &block
28
+ child.each_subpair(&block)
29
29
  end
30
30
  else
31
31
  enum_for :each_subpair
@@ -118,7 +118,7 @@ module Querly
118
118
 
119
119
  when :str
120
120
  return false unless type == :string
121
- test_value(node.children.first)
121
+ test_value(node.children.first.scrub)
122
122
 
123
123
  when :sym
124
124
  return false unless type == :symbol
@@ -147,7 +147,8 @@ module Querly
147
147
 
148
148
  def =~(pair)
149
149
  # Skip send node with block
150
- if pair.node.type == :send && pair.parent
150
+ type = pair.node.type
151
+ if (type == :send || type == :csend) && pair.parent
151
152
  if pair.parent.node.type == :block
152
153
  if pair.parent.node.children.first.equal? pair.node
153
154
  return false
@@ -176,7 +177,7 @@ module Querly
176
177
  node = node.children.first if node&.type == :block
177
178
 
178
179
  case node&.type
179
- when :send
180
+ when :send, :csend
180
181
  return false unless test_name(node)
181
182
  return false unless test_receiver(node.children[0])
182
183
  return false unless test_args(node.children.drop(2), args)
@@ -290,7 +291,8 @@ module Querly
290
291
  if receiver.test_node(node)
291
292
  true
292
293
  else
293
- node&.type == :send && test_node(node.children[0])
294
+ type = node&.type
295
+ (type == :send || type == :csend) && test_node(node.children[0])
294
296
  end
295
297
  end
296
298
  end
@@ -315,7 +317,7 @@ module Querly
315
317
  # We don't want lvar without method call
316
318
  # Skips when the node is not receiver of :send
317
319
  parent_node = pair.parent&.node
318
- if parent_node && parent_node.type == :send && parent_node.children.first.equal?(node)
320
+ if parent_node && (parent_node.type == :send || parent_node.type == :csend) && parent_node.children.first.equal?(node)
319
321
  test_node(node)
320
322
  end
321
323
  else
@@ -325,7 +327,7 @@ module Querly
325
327
 
326
328
  def test_node(node)
327
329
  case node&.type
328
- when :send
330
+ when :send, :csend
329
331
  node.children[1] == name
330
332
  when :lvar
331
333
  node.children.first == name
@@ -72,7 +72,7 @@ keyword: LIDENT | UIDENT
72
72
  constant: UIDENT { result = [val[0]] }
73
73
  | UIDENT COLONCOLON constant { result = [val[0]] + val[2] }
74
74
 
75
- send: LIDENT block { result = val[1] != nil ? Expr::Send.new(receiver: nil, name: val[0], args: Argument::AnySeq.new, block: val[1]) : Expr::Vcall.new(name: val[0]) }
75
+ send: LIDENT block { result = val[1] != nil ? Expr::Send.new(receiver: nil, name: val[0], block: val[1]) : Expr::Vcall.new(name: val[0]) }
76
76
  | UIDENT block { result = Expr::Send.new(receiver: nil, name: val[0], block: val[1]) }
77
77
  | method_name { result = Expr::Send.new(receiver: nil, name: val[0], block: nil) }
78
78
  | method_name_or_ident LPAREN args RPAREN block { result = Expr::Send.new(receiver: nil,
@@ -83,18 +83,15 @@ send: LIDENT block { result = val[1] != nil ? Expr::Send.new(receiver: nil, name
83
83
  name: val[1],
84
84
  args: Argument::AnySeq.new,
85
85
  block: val[2]) }
86
- | receiver method_name_or_ident block { result = Expr::Send.new(receiver: val[0],
87
- name: val[1],
88
- args: Argument::AnySeq.new,
89
- block: val[2]) }
90
- | receiver method_name_or_ident LPAREN args RPAREN block { result = Expr::Send.new(receiver: val[0],
91
- name: val[1],
92
- args: val[3],
93
- block: val[5]) }
94
86
  | receiver method_name_or_ident LPAREN args RPAREN block { result = Expr::Send.new(receiver: val[0],
95
87
  name: val[1],
96
88
  args: val[3],
97
89
  block: val[5]) }
90
+ | receiver UNDERBAR block { result = Expr::Send.new(receiver: val[0], name: /.+/, block: val[2]) }
91
+ | receiver UNDERBAR LPAREN args RPAREN block { result = Expr::Send.new(receiver: val[0],
92
+ name: /.+/,
93
+ args: val[3],
94
+ block: val[5]) }
98
95
 
99
96
  receiver: expr DOT { result = val[0] }
100
97
  | expr DOTDOTDOT { result = Expr::ReceiverContext.new(receiver: val[0]) }
@@ -136,6 +133,8 @@ def next_token
136
133
  [:NIL, false]
137
134
  when input.scan(/:string:/)
138
135
  [:STRING, nil]
136
+ when input.scan(/"([^"]+)"/)
137
+ [:STRING, input[1]]
139
138
  when input.scan(/:dstr:/)
140
139
  [:DSTR, nil]
141
140
  when input.scan(/:int:/)
data/lib/querly/pp/cli.rb CHANGED
@@ -42,7 +42,7 @@ module Querly
42
42
  end
43
43
 
44
44
  def run
45
- available_commands = [:haml]
45
+ available_commands = [:haml, :erb]
46
46
 
47
47
  if available_commands.include?(command)
48
48
  send :"run_#{command}"
@@ -70,6 +70,25 @@ module Querly
70
70
  stdout.print compiler.precompiled
71
71
  end
72
72
  end
73
+
74
+ def run_erb
75
+ require 'better_html'
76
+ require 'better_html/parser'
77
+ load_libs
78
+ source = stdin.read
79
+ source_buffer = Parser::Source::Buffer.new('(erb)')
80
+ source_buffer.source = source
81
+ parser = BetterHtml::Parser.new(source_buffer, template_language: :html)
82
+
83
+ new_source = source.gsub(/./, ' ')
84
+ parser.ast.descendants(:erb).each do |erb_node|
85
+ indicator_node, _, code_node, = *erb_node
86
+ next if indicator_node&.loc&.source == '#'
87
+ new_source[code_node.loc.range] = code_node.loc.source
88
+ new_source[code_node.loc.range.end] = ';'
89
+ end
90
+ stdout.puts new_source
91
+ end
73
92
  end
74
93
  end
75
94
  end
data/lib/querly/script.rb CHANGED
@@ -3,6 +3,16 @@ module Querly
3
3
  attr_reader :path
4
4
  attr_reader :node
5
5
 
6
+ def self.load(path:, source:)
7
+ parser = Parser::Ruby30.new(Builder.new).tap do |parser|
8
+ parser.diagnostics.all_errors_are_fatal = true
9
+ parser.diagnostics.ignore_warnings = true
10
+ end
11
+ buffer = Parser::Source::Buffer.new(path.to_s, 1)
12
+ buffer.source = source
13
+ self.new(path: path, node: parser.parse(buffer))
14
+ end
15
+
6
16
  def initialize(path:, node:)
7
17
  @path = path
8
18
  @node = node
@@ -11,5 +21,15 @@ module Querly
11
21
  def root_pair
12
22
  NodePair.new(node: node)
13
23
  end
24
+
25
+ class Builder < Parser::Builders::Default
26
+ def string_value(token)
27
+ value(token)
28
+ end
29
+
30
+ def emit_lambda
31
+ true
32
+ end
33
+ end
14
34
  end
15
35
  end
@@ -2,23 +2,36 @@ module Querly
2
2
  class ScriptEnumerator
3
3
  attr_reader :paths
4
4
  attr_reader :config
5
+ attr_reader :threads
5
6
 
6
- def initialize(paths:, config:)
7
+ def initialize(paths:, config:, threads:)
7
8
  @paths = paths
8
9
  @config = config
10
+ @threads = threads
9
11
  end
10
12
 
13
+ # Yields `Script` object concurrently, in different threads.
11
14
  def each(&block)
15
+ if block_given?
16
+ Parallel.each(each_path, in_threads: threads) do |path|
17
+ load_script_from_path path, &block
18
+ end
19
+ else
20
+ self.enum_for :each
21
+ end
22
+ end
23
+
24
+ def each_path(&block)
12
25
  if block_given?
13
26
  paths.each do |path|
14
27
  if path.directory?
15
28
  enumerate_files_in_dir(path, &block)
16
29
  else
17
- load_script_from_path path, &block
30
+ yield path
18
31
  end
19
32
  end
20
33
  else
21
- self.enum_for :each
34
+ enum_for :each_path
22
35
  end
23
36
  end
24
37
 
@@ -45,9 +58,7 @@ module Querly
45
58
  path.read
46
59
  end
47
60
 
48
- buffer = Parser::Source::Buffer.new(path.to_s, 1)
49
- buffer.source = source
50
- script = Script.new(path: path, node: parser.parse(buffer))
61
+ script = Script.load(path: path, source: source)
51
62
  rescue StandardError, LoadError, Preprocessor::Error => exn
52
63
  script = exn
53
64
  end
@@ -55,13 +66,6 @@ module Querly
55
66
  yield(path, script)
56
67
  end
57
68
 
58
- def parser
59
- Parser::Ruby25.new(Builder.new).tap do |parser|
60
- parser.diagnostics.all_errors_are_fatal = true
61
- parser.diagnostics.ignore_warnings = true
62
- end
63
- end
64
-
65
69
  def preprocessors
66
70
  config&.preprocessors || {}
67
71
  end
@@ -98,17 +102,7 @@ module Querly
98
102
  preprocessors.key?(path.extname)
99
103
  end
100
104
 
101
- load_script_from_path(path, &block) if should_load_file
102
- end
103
- end
104
-
105
- class Builder < Parser::Builders::Default
106
- def string_value(token)
107
- value(token)
108
- end
109
-
110
- def emit_lambda
111
- true
105
+ yield path if should_load_file
112
106
  end
113
107
  end
114
108
  end
@@ -1,3 +1,3 @@
1
1
  module Querly
2
- VERSION = "0.15.1"
2
+ VERSION = "1.3.0"
3
3
  end
data/manual/patterns.md CHANGED
@@ -71,6 +71,8 @@ bar.foo.baz # foo...bar...baz does not match
71
71
  * `1.23` (float)
72
72
  * `:foobar` (symbol)
73
73
  * `:symbol:` (any symbol literal)
74
+ * `"foobar"` (string)
75
+ * NOTE: It only supports double quotation.
74
76
  * `:string:` (any string literal)
75
77
  * `:dstr:` (any dstr `"hi #{name}"`)
76
78
  * `true`, `false` (true and false)
@@ -145,9 +147,9 @@ end
145
147
 
146
148
  # Interpolation Syntax
147
149
 
148
- If you want to describe a pattern that can not be described with adove syntax, you can use interpolation as follows:
150
+ If you want to describe a pattern that can not be described with above syntax, you can use interpolation as follows:
149
151
 
150
- ```rb
152
+ ```yaml
151
153
  id: find_by_abc_and_def
152
154
  pattern:
153
155
  subject: "'finder(...)"
@@ -164,6 +166,16 @@ It matches with `find_by_email_and_name(...)`.
164
166
  - If value of meta var is a string `foo`, it matches send nodes with exactly same method name
165
167
  - If value of meta var is a regexp `/foo/`, it matches send nodes with method name which `=~` the regexp
166
168
 
169
+ You can also use `as` syntax with `:symbol:` and so on.
170
+
171
+ ```yaml
172
+ id: migration_references
173
+ pattern:
174
+ subject: "t.integer(:symbol: as 'column, ...)"
175
+ where:
176
+ column: '/.+_id/'
177
+ ```
178
+
167
179
  # Difference from Ruby
168
180
 
169
181
  * Method call parenthesis cannot be omitted (if omitted, it means *any arguments*)
data/querly.gemspec CHANGED
@@ -22,14 +22,20 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
+ spec.required_ruby_version = ">= 2.7"
26
+
25
27
  spec.add_development_dependency "bundler", ">= 1.12"
26
- spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rake", "~> 13.0"
27
29
  spec.add_development_dependency "minitest", "~> 5.0"
28
- spec.add_development_dependency "racc", "= 1.4.14"
30
+ spec.add_development_dependency "racc", ">= 1.4.14"
29
31
  spec.add_development_dependency "unification_assertion", "0.0.1"
32
+ spec.add_development_dependency "better_html", "~> 1.0.13"
33
+ spec.add_development_dependency "slim", "~> 4.0.1"
34
+ spec.add_development_dependency "haml", "~> 5.0.4"
30
35
 
31
- spec.add_dependency 'thor', ">= 0.19.0", "< 0.21.0"
32
- spec.add_dependency "parser", ">= 2.5.0"
36
+ spec.add_dependency 'thor', ">= 0.19.0"
37
+ spec.add_dependency "parser", ">= 3.0"
33
38
  spec.add_dependency "rainbow", ">= 2.1"
34
39
  spec.add_dependency "activesupport", ">= 5.0"
40
+ spec.add_dependency "parallel", "~>1.17"
35
41
  end
data/sample.yaml CHANGED
@@ -146,6 +146,8 @@ rules:
146
146
 
147
147
  preprocessor:
148
148
  .slim: slimrb --compile
149
+ .haml: querly-pp haml
150
+ .erb: querly-pp erb
149
151
 
150
152
  import:
151
153
  - load: querly/rules/*.yml
data/template.yml CHANGED
@@ -58,7 +58,9 @@ rules:
58
58
  assert_empty some.count
59
59
 
60
60
  preprocessor:
61
- .slim: slimrb --compile
61
+ # .slim: slimrb --compile # Install `slim` gem for slim support
62
+ # .erb: querly-pp erb # Install `better_erb` gem for erb support
63
+ # .haml: querly-pp haml # Install `haml` gem for haml support
62
64
 
63
65
  check:
64
66
  - path: /
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: querly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-12 00:00:00.000000000 Z
11
+ date: 2021-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.0'
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: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,14 +56,14 @@ dependencies:
56
56
  name: racc
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: 1.4.14
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.4.14
69
69
  - !ruby/object:Gem::Dependency
@@ -80,6 +80,48 @@ dependencies:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.0.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: better_html
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.0.13
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.0.13
97
+ - !ruby/object:Gem::Dependency
98
+ name: slim
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 4.0.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 4.0.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: haml
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 5.0.4
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 5.0.4
83
125
  - !ruby/object:Gem::Dependency
84
126
  name: thor
85
127
  requirement: !ruby/object:Gem::Requirement
@@ -87,9 +129,6 @@ dependencies:
87
129
  - - ">="
88
130
  - !ruby/object:Gem::Version
89
131
  version: 0.19.0
90
- - - "<"
91
- - !ruby/object:Gem::Version
92
- version: 0.21.0
93
132
  type: :runtime
94
133
  prerelease: false
95
134
  version_requirements: !ruby/object:Gem::Requirement
@@ -97,23 +136,20 @@ dependencies:
97
136
  - - ">="
98
137
  - !ruby/object:Gem::Version
99
138
  version: 0.19.0
100
- - - "<"
101
- - !ruby/object:Gem::Version
102
- version: 0.21.0
103
139
  - !ruby/object:Gem::Dependency
104
140
  name: parser
105
141
  requirement: !ruby/object:Gem::Requirement
106
142
  requirements:
107
143
  - - ">="
108
144
  - !ruby/object:Gem::Version
109
- version: 2.5.0
145
+ version: '3.0'
110
146
  type: :runtime
111
147
  prerelease: false
112
148
  version_requirements: !ruby/object:Gem::Requirement
113
149
  requirements:
114
150
  - - ">="
115
151
  - !ruby/object:Gem::Version
116
- version: 2.5.0
152
+ version: '3.0'
117
153
  - !ruby/object:Gem::Dependency
118
154
  name: rainbow
119
155
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +178,20 @@ dependencies:
142
178
  - - ">="
143
179
  - !ruby/object:Gem::Version
144
180
  version: '5.0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: parallel
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '1.17'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '1.17'
145
195
  description: Querly is a query language and tool to find out method calls from Ruby
146
196
  programs. Define rules to check your program with patterns to find out *bad* pieces.
147
197
  Querly finds out matching pieces from your program.
@@ -153,8 +203,10 @@ executables:
153
203
  extensions: []
154
204
  extra_rdoc_files: []
155
205
  files:
206
+ - ".github/workflows/rubocop.yml"
207
+ - ".github/workflows/ruby.yml"
156
208
  - ".gitignore"
157
- - ".travis.yml"
209
+ - ".rubocop.yml"
158
210
  - CHANGELOG.md
159
211
  - Gemfile
160
212
  - LICENSE
@@ -212,15 +264,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
212
264
  requirements:
213
265
  - - ">="
214
266
  - !ruby/object:Gem::Version
215
- version: '0'
267
+ version: '2.7'
216
268
  required_rubygems_version: !ruby/object:Gem::Requirement
217
269
  requirements:
218
270
  - - ">="
219
271
  - !ruby/object:Gem::Version
220
272
  version: '0'
221
273
  requirements: []
222
- rubyforge_project:
223
- rubygems_version: 2.7.6
274
+ rubygems_version: 3.1.2
224
275
  signing_key:
225
276
  specification_version: 4
226
277
  summary: Pattern Based Checking Tool for Ruby
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.4.5
5
- - 2.5.3
6
- - 2.6.0
7
- before_install: gem install bundler
8
- script:
9
- - bundle exec rake test
10
- - bundle exec querly test --config=sample.yaml