goodcheck 2.3.2 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f192f5e6c846fa4e75eeec17b1333d47a5c79f3e06a26190af57e89cfe7ccf1
4
- data.tar.gz: f3f0a8e10aad7c78dc1b3dbdc8ae03448401be868d21f10a5227e9007195b5fd
3
+ metadata.gz: 7da3a85a6dd3f95677661f7863fbc9d51da6ec01272c5e023fa2517aebd7db01
4
+ data.tar.gz: 8c5f69286210ead4b2f58cc9d11278afad7c704f9056fb2e29b43dad89859985
5
5
  SHA512:
6
- metadata.gz: a0aee2cbb240465ef3c7ae05383d5cfa1f0827cc132bf79bcb46b5c7a309a2000fc76b79e95ed4bb199fd98d1b0bb5af9e2a6dddb18592a3a06ddc4e6dec6c5a
7
- data.tar.gz: a5039ed9328e05f8f49f3d9c36157771665245f9606fc88a1e49e2f7bb80f19cc5cd1c4d1c4853febc6c0cbb56688b18361873340b6d8558aa93552a181ea674
6
+ metadata.gz: b7394690992cdec71af0ad640998142b77fd07814b9266ce148b18ca65d56f76eadde56d70384e5bc46e3d50911e41ef4350532aeab7f7fc8f85168c4bee46e8
7
+ data.tar.gz: 9acb5eb4387b21293c2e83501cc8478afd2a5c5e3acf49f6db05660cec6c8da28959bcfe398396f3a08312dad14b666c5b5cef199dffd0be0df9aee2071c80bc
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 2.4.0 (2019-07-11)
6
+
7
+ * Add `pattern` command to print regexp of patterns [#63](https://github.com/sider/goodcheck/pull/63)
8
+ * Fix variable pattern without a type [#62](https://github.com/sider/goodcheck/pull/62)
9
+
5
10
  ## 2.3.2 (2019-07-04)
6
11
 
7
12
  * Update README
data/README.md CHANGED
@@ -177,6 +177,26 @@ We have 8 built-in patterns:
177
177
 
178
178
  You can find the exact definitions of the types in the definition of `Goodcheck::Pattern::Token` (`@@TYPES`).
179
179
 
180
+ You can omit the type of a variable binding.
181
+
182
+ ```yaml
183
+ pattern:
184
+ - token: margin-left: ${size}px;
185
+ where:
186
+ size: true
187
+ - token: backgroundColor={${color}}
188
+ where:
189
+ color: true
190
+ ```
191
+
192
+ In this case, the following character will be used to detect the range of binding. In the first example above, the `px` will be used as the marker for the end of `size` binding.
193
+
194
+ If parens are surrounding the variable, Goodcheck tries to match with nested ones in the variable. It expands five levels of nesting of parens. See the example of matches with the second `backgroundColor` pattern:
195
+
196
+ - `backgroundColor={color}` Matches (`color=="color"`)
197
+ - `backgroundColor={{ red: red(), green: green(), blue: green()-1 }}` Matches (`color=="{ red: red(), green: green(), blue: green()-1 }"`)
198
+ - `backgroundColor={ {{{{{{}}}}}} }` Matches (`color==" {{{{{{}}}}}"`)
199
+
180
200
  ### *glob*
181
201
 
182
202
  A *glob* can be a string, or a hash.
@@ -352,13 +372,22 @@ The test contains:
352
372
 
353
373
  Use `test` command when you add new rule to be sure you are writing rules correctly.
354
374
 
355
- Available options is:
375
+ Available options are:
356
376
 
357
377
  * `-c [CONFIG]`, `--config=[CONFIG]` to specify the configuration file.
358
378
  * `-v`, `--verbose` to be verbose.
359
379
  * `--debug` to print all debug messages.
360
380
  * `--force` to ignore downloaded caches
361
381
 
382
+ ### `goodcheck pattern [options] ids...`
383
+
384
+ The `pattern` command prints the regular expressions generated from the patterns.
385
+ The command is for debugging patterns, especially token patterns.
386
+
387
+ Available option is:
388
+
389
+ * `-c [CONFIG]`, `--config=[CONFIG]` to specify the configuration file.
390
+
362
391
  ## Downloaded rules
363
392
 
364
393
  Downloaded rules are cached in `cache` directory in *goodcheck home directory*.
data/cheatsheet.pdf CHANGED
Binary file
data/lib/goodcheck/cli.rb CHANGED
@@ -16,6 +16,7 @@ module Goodcheck
16
16
  init: "Generate a sample configuration file",
17
17
  check: "Run check with a configuration",
18
18
  test: "Test your configuration",
19
+ pattern: "Print regexp for rules",
19
20
  version: "Print version",
20
21
  help: "Show goodcheck version and quit"
21
22
  }
@@ -161,5 +162,17 @@ module Goodcheck
161
162
  end
162
163
  0
163
164
  end
165
+
166
+ def pattern(args)
167
+ config_path = Pathname("goodcheck.yml")
168
+
169
+ OptionParser.new("Usage: goodcheck pattern [options] ids...") do |opts|
170
+ opts.on("-c CONFIG", "--config=CONFIG") do |config|
171
+ config_path = Pathname(config)
172
+ end
173
+ end.parse!(args)
174
+
175
+ Commands::Pattern.new(stdout: stdout, stderr: stderr, path: config_path, ids: Set.new(args), home_path: home_path).run
176
+ end
164
177
  end
165
178
  end
@@ -0,0 +1,41 @@
1
+ module Goodcheck
2
+ module Commands
3
+ class Pattern
4
+ attr_reader :stdout
5
+ attr_reader :stderr
6
+ attr_reader :config_path
7
+ attr_reader :ids
8
+ attr_reader :home_path
9
+
10
+ include ConfigLoading
11
+ include HomePath
12
+
13
+ def initialize(stdout:, stderr:, path:, ids:, home_path:)
14
+ @stdout = stdout
15
+ @stderr = stderr
16
+ @config_path = path
17
+ @ids = ids
18
+ @home_path = home_path
19
+ end
20
+
21
+ def run
22
+ handle_config_errors stderr do
23
+ load_config!(cache_path: cache_dir_path, force_download: true)
24
+
25
+ config.rules.each do |rule|
26
+ if ids.empty? || ids.any? {|pat| pat == rule.id || rule.id.start_with?("#{pat}.") }
27
+ stdout.puts "#{rule.id}:"
28
+ rule.triggers.each do |trigger|
29
+ trigger.patterns.each do |pattern|
30
+ stdout.puts " - #{pattern.regexp.inspect}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ 0
38
+ end
39
+ end
40
+ end
41
+ end
@@ -162,13 +162,48 @@ module Goodcheck
162
162
  /\b(?<#{name}>#{AUTO_EMAIL_RE})/
163
163
  }
164
164
 
165
- def self.regexp_for_type(name:, type:)
166
- ty = type || :word
167
- if @@TYPES.key?(ty)
168
- @@TYPES[ty][name]
165
+ def self.expand(prefix, suffix, depth: 5)
166
+ if depth == 0
167
+ [
168
+ /[^#{suffix}]*/
169
+ ]
170
+ else
171
+ expandeds = expand(prefix, suffix, depth: depth - 1)
172
+ [/[^#{prefix}#{suffix}]*#{prefix}#{expandeds.first}#{suffix}[^#{prefix}#{suffix}]*/] + expandeds
169
173
  end
170
174
  end
171
175
 
176
+ def self.regexp_for_type(name:, type:, scanner:)
177
+ prefix = scanner.pre_match[-1]
178
+ suffix = scanner.check(WORD_RE) || scanner.peek(1)
179
+
180
+ case
181
+ when type == :__
182
+ body = case
183
+ when prefix == "{" && suffix == "}"
184
+ ::Regexp.union(expand(prefix, suffix))
185
+ when prefix == "(" && suffix == ")"
186
+ ::Regexp.union(expand(prefix, suffix))
187
+ when prefix == "[" && suffix == "]"
188
+ ::Regexp.union(expand(prefix, suffix))
189
+ when prefix == "<" && suffix == ">"
190
+ ::Regexp.union(expand(prefix, suffix))
191
+ else
192
+ unless suffix.empty?
193
+ /(?~#{::Regexp.escape(suffix)})/
194
+ else
195
+ /.*/
196
+ end
197
+ end
198
+ /(?<#{name}>#{body})/
199
+
200
+ when @@TYPES.key?(type)
201
+ @@TYPES[type][name]
202
+ end
203
+ end
204
+
205
+ WORD_RE = /\w+|[\p{L}&&\p{^ASCII}]+/
206
+
172
207
  def self.compile_tokens(source, variables, case_sensitive:)
173
208
  tokens = []
174
209
  s = StringScanner.new(source)
@@ -177,15 +212,24 @@ module Goodcheck
177
212
  case
178
213
  when s.scan(/\${(?<name>[a-zA-Z_]\w*)(?::(?<type>#{::Regexp.union(*@@TYPES.keys.map(&:to_s))}))?}/)
179
214
  name = s[:name].to_sym
180
- type = s[:type] && s[:type].to_sym
215
+ type = s[:type] ? s[:type].to_sym : :__
181
216
 
182
217
  if variables.key?(name)
218
+ if !s[:type] && s.pre_match == ""
219
+ Goodcheck.logger.error "Variable binding ${#{name}} at the beginning of pattern would cause an unexpected match"
220
+ end
221
+ if !s[:type] && s.peek(1) == ""
222
+ Goodcheck.logger.error "Variable binding ${#{name}} at the end of pattern would cause an unexpected match"
223
+ end
224
+
225
+ tokens << :nobr
183
226
  variables[name].type = type
184
- regexp = regexp_for_type(name: name, type: type).to_s
227
+ regexp = regexp_for_type(name: name, type: type, scanner: s).to_s
185
228
  if tokens.empty? && (type == :word || type == :identifier)
186
229
  regexp = /\b#{regexp.to_s}/
187
230
  end
188
231
  tokens << regexp.to_s
232
+ tokens << :nobr
189
233
  else
190
234
  tokens << ::Regexp.escape("${")
191
235
  tokens << ::Regexp.escape(name.to_s)
@@ -195,7 +239,7 @@ module Goodcheck
195
239
  tokens << ::Regexp.escape(s.matched)
196
240
  when s.scan(/\s+/)
197
241
  tokens << '\s+'
198
- when s.scan(/\w+|[\p{L}&&\p{^ASCII}]+/)
242
+ when s.scan(WORD_RE)
199
243
  tokens << ::Regexp.escape(s.matched)
200
244
  when s.scan(%r{[!"#%&'=\-^~¥\\|`@*:+;/?.,]+})
201
245
  tokens << ::Regexp.escape(s.matched.rstrip)
@@ -204,18 +248,32 @@ module Goodcheck
204
248
  end
205
249
  end
206
250
 
207
- if tokens.first =~ /\A\p{L}/
251
+ if source[0] =~ /\p{L}/
208
252
  tokens.first.prepend('\b')
209
253
  end
210
254
 
211
- if tokens.last =~ /\p{L}\Z/
255
+ if source[-1] =~ /\p{L}/
212
256
  tokens.last << '\b'
213
257
  end
214
258
 
215
259
  options = ::Regexp::MULTILINE
216
260
  options |= ::Regexp::IGNORECASE unless case_sensitive
217
261
 
218
- ::Regexp.new(tokens.join('\s*').gsub(/\\s\*(\\s\+\\s\*)+/, '\s+'), options)
262
+ buf, skip = tokens[0].is_a?(String) ? [tokens[0], false] : ["", true]
263
+ tokens.drop(1).each do |tok|
264
+ if tok == :nobr
265
+ skip = true
266
+ else
267
+ buf << '\s*' unless skip
268
+ skip = false
269
+ buf << tok
270
+ end
271
+ end
272
+
273
+ ::Regexp.new(buf.
274
+ gsub(/\\s\*(\\s\+\\s\*)+/, '\s+').
275
+ gsub(/#{::Regexp.escape('\s+\s*')}/, '\s+').
276
+ gsub(/#{::Regexp.escape('\s*\s+')}/, '\s+'), options)
219
277
  end
220
278
  end
221
279
  end
@@ -1,3 +1,3 @@
1
1
  module Goodcheck
2
- VERSION = "2.3.2"
2
+ VERSION = "2.4.0"
3
3
  end
data/lib/goodcheck.rb CHANGED
@@ -32,3 +32,4 @@ require "goodcheck/commands/check"
32
32
  require "goodcheck/commands/init"
33
33
  require "goodcheck/commands/test"
34
34
  require "goodcheck/import_loader"
35
+ require "goodcheck/commands/pattern"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goodcheck
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.2
4
+ version: 2.4.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-07-04 00:00:00.000000000 Z
11
+ date: 2019-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -158,6 +158,7 @@ files:
158
158
  - lib/goodcheck/commands/check.rb
159
159
  - lib/goodcheck/commands/config_loading.rb
160
160
  - lib/goodcheck/commands/init.rb
161
+ - lib/goodcheck/commands/pattern.rb
161
162
  - lib/goodcheck/commands/test.rb
162
163
  - lib/goodcheck/config.rb
163
164
  - lib/goodcheck/config_loader.rb