spellr 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +34 -15
- data/exe/spellr +8 -1
- data/lib/spellr/backports.rb +48 -21
- data/lib/spellr/base_reporter.rb +0 -4
- data/lib/spellr/check.rb +7 -47
- data/lib/spellr/check_dry_run.rb +14 -0
- data/lib/spellr/check_interactive.rb +24 -0
- data/lib/spellr/check_parallel.rb +23 -0
- data/lib/spellr/cli.rb +5 -90
- data/lib/spellr/cli_options.rb +98 -0
- data/lib/spellr/column_location.rb +2 -4
- data/lib/spellr/config.rb +39 -41
- data/lib/spellr/config_loader.rb +3 -29
- data/lib/spellr/config_validator.rb +63 -0
- data/lib/spellr/file_list.rb +7 -24
- data/lib/spellr/interactive.rb +80 -45
- data/lib/spellr/interactive_add.rb +20 -16
- data/lib/spellr/interactive_replacement.rb +52 -29
- data/lib/spellr/key_tuner/naive_bayes.rb +7 -59
- data/lib/spellr/key_tuner/possible_key.rb +5 -24
- data/lib/spellr/key_tuner/stats.rb +2 -0
- data/lib/spellr/language.rb +7 -8
- data/lib/spellr/line_location.rb +2 -7
- data/lib/spellr/line_tokenizer.rb +1 -9
- data/lib/spellr/output.rb +5 -7
- data/lib/spellr/output_stubbed.rb +16 -16
- data/lib/spellr/quiet_reporter.rb +1 -0
- data/lib/spellr/reporter.rb +0 -4
- data/lib/spellr/string_format.rb +8 -15
- data/lib/spellr/token.rb +13 -29
- data/lib/spellr/token_regexps.rb +2 -2
- data/lib/spellr/tokenizer.rb +0 -10
- data/lib/spellr/validations.rb +31 -0
- data/lib/spellr/version.rb +1 -1
- data/lib/spellr/wordlist.rb +9 -17
- data/lib/spellr/wordlist_reporter.rb +0 -4
- data/lib/spellr.rb +12 -0
- data/spellr.gemspec +12 -7
- data/wordlists/ruby.txt +1 -0
- metadata +59 -26
@@ -9,8 +9,13 @@ require 'yaml'
|
|
9
9
|
class NaiveBayes
|
10
10
|
YAML_PATH = File.join(__dir__, 'data.yml')
|
11
11
|
|
12
|
+
attr_reader :feature_set
|
13
|
+
attr_reader :num_classes
|
14
|
+
attr_reader :classes
|
15
|
+
attr_reader :features
|
16
|
+
|
12
17
|
def initialize(path = YAML_PATH)
|
13
|
-
load_from_yaml(path)
|
18
|
+
load_from_yaml(path)
|
14
19
|
@key = {}
|
15
20
|
end
|
16
21
|
|
@@ -21,7 +26,7 @@ class NaiveBayes
|
|
21
26
|
end
|
22
27
|
|
23
28
|
def load_from_yaml(path = YAML_PATH)
|
24
|
-
data = YAML.safe_load(::File.read(path), [Symbol])
|
29
|
+
data = YAML.safe_load(::File.read(path), permitted_classes: [Symbol])
|
25
30
|
|
26
31
|
@feature_set = data[:feature_set]
|
27
32
|
@num_classes = data[:num_classes]
|
@@ -29,63 +34,6 @@ class NaiveBayes
|
|
29
34
|
@features = data[:features]
|
30
35
|
end
|
31
36
|
|
32
|
-
def save_to_yaml(path = YAML_PATH)
|
33
|
-
write_yaml(path,
|
34
|
-
feature_set: feature_set,
|
35
|
-
num_classes: num_classes,
|
36
|
-
classes: classes,
|
37
|
-
features: features)
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def write_yaml(path = YAML_PATH, **hash)
|
43
|
-
require 'yaml'
|
44
|
-
|
45
|
-
File.write(path, hash.to_yaml)
|
46
|
-
end
|
47
|
-
|
48
|
-
def training_data
|
49
|
-
@training_data ||= PossibleKey.keys.each_with_object({}) do |key, data|
|
50
|
-
data[key.classification] ||= []
|
51
|
-
data[key.classification] << key.features
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def num_classes
|
56
|
-
@num_classes ||= training_data&.length
|
57
|
-
end
|
58
|
-
|
59
|
-
def classes
|
60
|
-
@classes ||= training_data&.keys
|
61
|
-
end
|
62
|
-
|
63
|
-
def features
|
64
|
-
@features ||= training_data.first.last.first.keys
|
65
|
-
end
|
66
|
-
|
67
|
-
def feature_set
|
68
|
-
@feature_set ||= classes.each.with_object({}) do |class_name, feature_set|
|
69
|
-
feature_set[class_name] = features.each.with_object({}) do |feature, feature_set_for_class|
|
70
|
-
feature_set_for_class[feature] = feature_stats_for_class(class_name, feature)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def feature_stats_for_class(class_name, feature)
|
76
|
-
values = training_data[class_name].map { |row| row[feature] }
|
77
|
-
|
78
|
-
feature_stats(values)
|
79
|
-
end
|
80
|
-
|
81
|
-
def feature_stats(values)
|
82
|
-
{
|
83
|
-
standard_deviation: Stats.standard_deviation(values),
|
84
|
-
mean: Stats.mean(values),
|
85
|
-
variance: Stats.variance(values)
|
86
|
-
}
|
87
|
-
end
|
88
|
-
|
89
37
|
# given a class, this method determines the probability
|
90
38
|
# of a certain value occurring for a given feature
|
91
39
|
# feature: name of the feature in consideration in the training data
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'pathname'
|
4
3
|
require_relative 'stats'
|
4
|
+
require_relative '../backports'
|
5
5
|
|
6
6
|
class PossibleKey # rubocop:disable Metrics/ClassLength
|
7
7
|
include Stats
|
@@ -15,29 +15,11 @@ class PossibleKey # rubocop:disable Metrics/ClassLength
|
|
15
15
|
B C D F G H J K L M N P Q R S T V W X Y Z
|
16
16
|
}.freeze
|
17
17
|
BASE_64 = VOWELS + CONSONANTS + %i{0 1 2 3 4 5 6 7 8 9 - _ + / =}.freeze
|
18
|
-
|
18
|
+
letter_count_hash = BASE_64.map { |k| [k.to_sym, 0] }.to_h
|
19
|
+
letter_count_hash.default = 0
|
20
|
+
LETTER_COUNT_HASH = letter_count_hash
|
19
21
|
FEATURE_LETTERS = %i{+ - _ / A z Z q Q X x}.freeze
|
20
22
|
|
21
|
-
class << self
|
22
|
-
def keys
|
23
|
-
@keys ||= begin
|
24
|
-
load_from_file('false_positives.txt', false) +
|
25
|
-
load_from_file('keys.txt', true)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def load_from_file(filename, key)
|
32
|
-
Pathname.new(__dir__).join('data', filename).each_line.map! do |line|
|
33
|
-
line = line.chomp
|
34
|
-
next if line.empty?
|
35
|
-
|
36
|
-
PossibleKey.new(line, key)
|
37
|
-
end.compact
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
23
|
attr_reader :string
|
42
24
|
|
43
25
|
def initialize(string, key = nil)
|
@@ -100,8 +82,6 @@ class PossibleKey # rubocop:disable Metrics/ClassLength
|
|
100
82
|
when /^[a-z0-9]+$/ then :lower36
|
101
83
|
when /^[A-Z0-9]+$/ then :upper36
|
102
84
|
when %r{^[A-Za-z0-9\-_+/]+={0,2}$} then :base64
|
103
|
-
else
|
104
|
-
raise "#{string.inspect} is an unrecognised character set"
|
105
85
|
end
|
106
86
|
end
|
107
87
|
|
@@ -111,6 +91,7 @@ class PossibleKey # rubocop:disable Metrics/ClassLength
|
|
111
91
|
when :lower36 then 36
|
112
92
|
when :upper36 then 36
|
113
93
|
when :base64 then 64
|
94
|
+
else 0
|
114
95
|
end
|
115
96
|
end
|
116
97
|
|
data/lib/spellr/language.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'wordlist'
|
4
|
+
require_relative 'file'
|
5
|
+
require 'pathname'
|
6
|
+
require 'fast_ignore'
|
4
7
|
|
5
8
|
module Spellr
|
6
9
|
class Language
|
@@ -30,7 +33,7 @@ module Spellr
|
|
30
33
|
|
31
34
|
def project_wordlist
|
32
35
|
@project_wordlist ||= Spellr::Wordlist.new(
|
33
|
-
|
36
|
+
Spellr.config.pwd.join('.spellr_wordlists', "#{name}.txt"),
|
34
37
|
name: name
|
35
38
|
)
|
36
39
|
end
|
@@ -49,7 +52,9 @@ module Spellr
|
|
49
52
|
def matches_includes?(file)
|
50
53
|
return @hashbangs.empty? if @includes.empty?
|
51
54
|
|
52
|
-
@fast_ignore ||= FastIgnore.new(
|
55
|
+
@fast_ignore ||= FastIgnore.new(
|
56
|
+
include_rules: @includes, gitignore: false, root: Spellr.config.pwd_s
|
57
|
+
)
|
53
58
|
@fast_ignore.allowed?(file.to_s)
|
54
59
|
end
|
55
60
|
|
@@ -67,12 +72,6 @@ module Spellr
|
|
67
72
|
end
|
68
73
|
end
|
69
74
|
|
70
|
-
def load_wordlists(name, paths)
|
71
|
-
wordlists = paths + default_wordlist_paths(name)
|
72
|
-
|
73
|
-
wordlists.map(&Spellr::Wordlist.method(:new))
|
74
|
-
end
|
75
|
-
|
76
75
|
def default_wordlists
|
77
76
|
[
|
78
77
|
gem_wordlist,
|
data/lib/spellr/line_location.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'file'
|
4
|
+
|
3
5
|
module Spellr
|
4
6
|
class LineLocation
|
5
7
|
attr_reader :line_number
|
@@ -24,12 +26,5 @@ module Spellr
|
|
24
26
|
def file
|
25
27
|
@file ||= Spellr::File.wrap(@filename)
|
26
28
|
end
|
27
|
-
|
28
|
-
def advance(line)
|
29
|
-
LineLocation.new(@filename,
|
30
|
-
line_number + 1,
|
31
|
-
char_offset: char_offset + line.length,
|
32
|
-
byte_offset: byte_offset + line.bytesize)
|
33
|
-
end
|
34
29
|
end
|
35
30
|
end
|
@@ -48,19 +48,12 @@ module Spellr
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
# jump to character-aware position
|
52
|
-
# TODO: handle jump backward
|
53
|
-
def charpos=(new_charpos)
|
54
|
-
skip(/.{#{new_charpos - charpos}}/m)
|
55
|
-
end
|
56
|
-
|
57
51
|
private
|
58
52
|
|
59
53
|
def column_location(term)
|
60
54
|
ColumnLocation.new(
|
61
55
|
byte_offset: pos - term.bytesize,
|
62
|
-
char_offset: charpos - term.length
|
63
|
-
**(line.respond_to?(:location) ? { line_location: line.location.line_location } : {})
|
56
|
+
char_offset: charpos - term.length
|
64
57
|
)
|
65
58
|
end
|
66
59
|
|
@@ -97,7 +90,6 @@ module Spellr
|
|
97
90
|
def key?(possible_key)
|
98
91
|
# I've come across some large base64 strings by this point they're definitely base64.
|
99
92
|
return true if possible_key.length > 200
|
100
|
-
return unless possible_key.length >= Spellr.config.key_minimum_length
|
101
93
|
return unless possible_key.match?(min_alpha_re) # or there's no point
|
102
94
|
|
103
95
|
BAYES_KEY_HEURISTIC.key?(possible_key)
|
data/lib/spellr/output.rb
CHANGED
@@ -2,10 +2,8 @@
|
|
2
2
|
|
3
3
|
module Spellr
|
4
4
|
class Output
|
5
|
-
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@exit_code = 0
|
5
|
+
def exit_code
|
6
|
+
@exit_code ||= 0
|
9
7
|
end
|
10
8
|
|
11
9
|
def stdin
|
@@ -52,10 +50,10 @@ module Spellr
|
|
52
50
|
stdout.print(str)
|
53
51
|
end
|
54
52
|
|
55
|
-
def <<(other)
|
53
|
+
def <<(other)
|
56
54
|
self.exit_code = other.exit_code
|
57
|
-
|
58
|
-
|
55
|
+
warn other.stderr.string if other.stderr?
|
56
|
+
puts other.stdout.string if other.stdout?
|
59
57
|
counts.merge!(other.counts) { |_k, a, b| a + b }
|
60
58
|
end
|
61
59
|
end
|
@@ -4,12 +4,6 @@ require_relative 'output'
|
|
4
4
|
|
5
5
|
module Spellr
|
6
6
|
class OutputStubbed < Spellr::Output
|
7
|
-
attr_accessor :exit_code
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@exit_code = 0
|
11
|
-
end
|
12
|
-
|
13
7
|
def stdin
|
14
8
|
@stdin ||= StringIO.new
|
15
9
|
end
|
@@ -23,15 +17,21 @@ module Spellr
|
|
23
17
|
end
|
24
18
|
|
25
19
|
def marshal_dump # rubocop:disable Metrics/MethodLength
|
20
|
+
l_exit_code = @exit_code if defined?(@exit_code)
|
21
|
+
l_counts = @counts if defined?(@counts)
|
22
|
+
l_stdin = @stdin if defined?(@stdin)
|
23
|
+
l_stdout = @stdout if defined?(@stdout)
|
24
|
+
l_stderr = @stderr if defined?(@stderr)
|
25
|
+
|
26
26
|
{
|
27
|
-
exit_code:
|
28
|
-
counts:
|
29
|
-
stdin:
|
30
|
-
stdin_pos:
|
31
|
-
stdout:
|
32
|
-
stdout_pos:
|
33
|
-
stderr:
|
34
|
-
stderr_pos:
|
27
|
+
exit_code: l_exit_code,
|
28
|
+
counts: l_counts,
|
29
|
+
stdin: l_stdin&.string,
|
30
|
+
stdin_pos: l_stdin&.pos,
|
31
|
+
stdout: l_stdout&.string,
|
32
|
+
stdout_pos: l_stdout&.pos,
|
33
|
+
stderr: l_stderr&.string,
|
34
|
+
stderr_pos: l_stderr&.pos
|
35
35
|
}
|
36
36
|
end
|
37
37
|
|
@@ -51,8 +51,8 @@ module Spellr
|
|
51
51
|
@stderr.pos = dumped[:stderr_pos]
|
52
52
|
end
|
53
53
|
|
54
|
-
@exit_code = dumped[:exit_code]
|
55
|
-
@counts = dumped[:counts]
|
54
|
+
@exit_code = dumped[:exit_code] if dumped[:exit_code]
|
55
|
+
@counts = dumped[:counts] if dumped[:counts]
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
data/lib/spellr/reporter.rb
CHANGED
data/lib/spellr/string_format.rb
CHANGED
@@ -8,39 +8,32 @@ module Spellr
|
|
8
8
|
"#{count} #{word}#{'s' if count != 1}"
|
9
9
|
end
|
10
10
|
|
11
|
-
# TODO: make it work without color
|
12
|
-
def color_enabled?
|
13
|
-
true
|
14
|
-
end
|
15
|
-
|
16
11
|
def aqua(text)
|
17
|
-
return text unless Spellr::StringFormat.color_enabled?
|
18
|
-
|
19
12
|
"\e[36m#{text}#{normal}"
|
20
13
|
end
|
21
14
|
|
22
15
|
def normal(text = '')
|
23
|
-
return text unless Spellr::StringFormat.color_enabled?
|
24
|
-
|
25
16
|
"\e[0m#{text}"
|
26
17
|
end
|
27
18
|
|
28
19
|
def bold(text)
|
29
|
-
return text unless Spellr::StringFormat.color_enabled?
|
30
|
-
|
31
20
|
"\e[1;39m#{text}#{normal}"
|
32
21
|
end
|
33
22
|
|
34
|
-
def
|
35
|
-
|
23
|
+
def lighten(text)
|
24
|
+
"\e[2;39m#{text}#{normal}"
|
25
|
+
end
|
36
26
|
|
27
|
+
def red(text)
|
37
28
|
"\e[1;31m#{text}#{normal}"
|
38
29
|
end
|
39
30
|
|
40
31
|
def green(text)
|
41
|
-
return text unless Spellr::StringFormat.color_enabled?
|
42
|
-
|
43
32
|
"\e[1;32m#{text}#{normal}"
|
44
33
|
end
|
34
|
+
|
35
|
+
def key(label)
|
36
|
+
"[#{bold label[0]}]#{label[1..-1]}"
|
37
|
+
end
|
45
38
|
end
|
46
39
|
end
|
data/lib/spellr/token.rb
CHANGED
@@ -17,53 +17,41 @@ module Spellr
|
|
17
17
|
class Token < String
|
18
18
|
attr_reader :location, :line, :replacement
|
19
19
|
|
20
|
-
def self.wrap(value)
|
21
|
-
return value if value.is_a?(Spellr::Token)
|
22
|
-
|
23
|
-
Spellr::Token.new(value || '')
|
24
|
-
end
|
25
|
-
|
26
20
|
def initialize(string, line: string, location: ColumnLocation.new)
|
27
21
|
@location = location
|
28
22
|
@line = line
|
29
23
|
super(string)
|
30
24
|
end
|
31
25
|
|
32
|
-
def strip
|
33
|
-
@strip ||= begin
|
34
|
-
lstripped = lstrip
|
35
|
-
new_column_location = lstripped_column_location(lstripped)
|
36
|
-
Token.new(lstripped.rstrip, line: line, location: new_column_location)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def lstripped_column_location(lstripped)
|
41
|
-
ColumnLocation.new(
|
42
|
-
byte_offset: bytesize - lstripped.bytesize,
|
43
|
-
char_offset: length - lstripped.length,
|
44
|
-
line_location: location.line_location
|
45
|
-
)
|
46
|
-
end
|
47
|
-
|
48
26
|
def line=(new_line)
|
49
27
|
@line = new_line
|
50
28
|
location.line_location = new_line.location.line_location
|
51
29
|
end
|
52
30
|
|
31
|
+
# :nocov:
|
53
32
|
def inspect
|
54
33
|
"#<#{self.class.name} #{to_s.inspect} @#{location}>"
|
55
34
|
end
|
35
|
+
# :nocov:
|
56
36
|
|
57
37
|
def char_range
|
58
|
-
@char_range ||=
|
38
|
+
@char_range ||=
|
39
|
+
location.char_offset...(location.char_offset + length)
|
59
40
|
end
|
60
41
|
|
61
42
|
def byte_range
|
62
|
-
@byte_range ||=
|
43
|
+
@byte_range ||=
|
44
|
+
location.byte_offset...(location.byte_offset + bytesize)
|
63
45
|
end
|
64
46
|
|
65
47
|
def file_char_range
|
66
|
-
@file_char_range ||=
|
48
|
+
@file_char_range ||=
|
49
|
+
location.absolute_char_offset...(location.absolute_char_offset + length)
|
50
|
+
end
|
51
|
+
|
52
|
+
def file_byte_range
|
53
|
+
@file_byte_range ||=
|
54
|
+
location.absolute_byte_offset...(location.absolute_byte_offset + bytesize)
|
67
55
|
end
|
68
56
|
|
69
57
|
def coordinates
|
@@ -78,9 +66,5 @@ module Spellr
|
|
78
66
|
@replacement = replacement
|
79
67
|
location.file.insert(replacement, file_char_range)
|
80
68
|
end
|
81
|
-
|
82
|
-
def file_name
|
83
|
-
location.file_name
|
84
|
-
end
|
85
69
|
end
|
86
70
|
end
|
data/lib/spellr/token_regexps.rb
CHANGED
@@ -9,7 +9,7 @@ module Spellr
|
|
9
9
|
# [Word], [Word]Word [Word]'s [Wordn't]
|
10
10
|
TITLE_CASE_RE = /[[:upper:]][[:lower:]]+(?:['’][[:lower:]]+(?<!['’]s))*/.freeze
|
11
11
|
# [WORD] [WORD]Word [WORDN'T] [WORD]'S [WORD]'s [WORD]s
|
12
|
-
UPPER_CASE_RE = /[[:upper:]]+(?:['’][[:upper:]]+(?<!['’][Ss]))*(?:(?![[:lower:]])|(?=s(?![[:lower:]])))/.freeze # rubocop:disable
|
12
|
+
UPPER_CASE_RE = /[[:upper:]]+(?:['’][[:upper:]]+(?<!['’][Ss]))*(?:(?![[:lower:]])|(?=s(?![[:lower:]])))/.freeze # rubocop:disable Layout/LineLength
|
13
13
|
# [word] [word]'s [wordn't]
|
14
14
|
LOWER_CASE_RE = /[[:lower:]]+(?:['’][[:lower:]]+(?<!['’]s))*/.freeze
|
15
15
|
# for characters in [:alpha:] that aren't in [:lower:] or [:upper:] e.g. Arabic
|
@@ -30,7 +30,7 @@ module Spellr
|
|
30
30
|
REPEATED_SINGLE_LETTERS_RE = /(?:([[:alpha:]])\1+)(?![[:alpha:]])/.freeze # e.g. xxxxxxxx
|
31
31
|
URL_ENCODED_ENTITIES_RE = /%[0-8A-F]{2}/.freeze
|
32
32
|
# There's got to be a better way of writing this
|
33
|
-
SEQUENTIAL_LETTERS_RE = /a(?:b(?:c(?:d(?:e(?:f(?:g(?:h(?:i(?:j(?:k(?:l(?:m(?:n(?:o(?:p(?:q(?:r(?:s(?:t(?:u(?:v(?:w(?:x(?:yz?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?(?![[:alpha:]])/i.freeze # rubocop:disable
|
33
|
+
SEQUENTIAL_LETTERS_RE = /a(?:b(?:c(?:d(?:e(?:f(?:g(?:h(?:i(?:j(?:k(?:l(?:m(?:n(?:o(?:p(?:q(?:r(?:s(?:t(?:u(?:v(?:w(?:x(?:yz?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?(?![[:alpha:]])/i.freeze # rubocop:disable Layout/LineLength
|
34
34
|
|
35
35
|
# I didn't want to do this myself
|
36
36
|
# BUT i need something to heuristically match on, and it's difficult
|
data/lib/spellr/tokenizer.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../spellr'
|
4
3
|
require_relative 'token'
|
5
4
|
require_relative 'column_location'
|
6
5
|
require_relative 'line_location'
|
@@ -78,15 +77,6 @@ module Spellr
|
|
78
77
|
|
79
78
|
attr_reader :line_tokenizer
|
80
79
|
|
81
|
-
def each_line_token
|
82
|
-
line_location = @start_at.line_location
|
83
|
-
|
84
|
-
file.each_line do |line|
|
85
|
-
yield Token.new(line, location: ColumnLocation.new(line_location: line_location))
|
86
|
-
line_location = line_location.advance(line)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
80
|
def prepare_tokenizer_for_line(line)
|
91
81
|
line_tokenizer.string = line
|
92
82
|
line_tokenizer.pos = 0
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spellr
|
4
|
+
module Validations
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def validations
|
11
|
+
@validations ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate(method)
|
15
|
+
validations << method
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
self.class.validations.each do |validation|
|
21
|
+
send(validation)
|
22
|
+
end
|
23
|
+
|
24
|
+
errors.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def errors
|
28
|
+
@errors ||= []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/spellr/version.rb
CHANGED
data/lib/spellr/wordlist.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require_relative '../spellr'
|
5
|
-
require_relative 'token'
|
5
|
+
require_relative 'token' # for spellr_normalize
|
6
6
|
|
7
7
|
module Spellr
|
8
8
|
class Wordlist
|
@@ -12,20 +12,20 @@ module Spellr
|
|
12
12
|
|
13
13
|
def initialize(file, name: file)
|
14
14
|
path = @file = file
|
15
|
-
@path =
|
15
|
+
@path = Spellr.config.pwd.join('.spellr_wordlists').join(path).expand_path
|
16
16
|
@name = name
|
17
17
|
@include = {}
|
18
18
|
end
|
19
19
|
|
20
20
|
def each(&block)
|
21
|
-
|
22
|
-
|
23
|
-
@path.each_line(&block)
|
21
|
+
words.each(&block)
|
24
22
|
end
|
25
23
|
|
24
|
+
# :nocov:
|
26
25
|
def inspect
|
27
26
|
"#<#{self.class.name}:#{@path}>"
|
28
27
|
end
|
28
|
+
# :nocov:
|
29
29
|
|
30
30
|
# significantly faster than default Enumerable#include?
|
31
31
|
# requires terms to have been sorted
|
@@ -60,12 +60,6 @@ module Spellr
|
|
60
60
|
clear_cache
|
61
61
|
end
|
62
62
|
|
63
|
-
def read
|
64
|
-
raise_unless_exists?
|
65
|
-
|
66
|
-
@path.read
|
67
|
-
end
|
68
|
-
|
69
63
|
def exist?
|
70
64
|
return @exist if defined?(@exist)
|
71
65
|
|
@@ -80,6 +74,10 @@ module Spellr
|
|
80
74
|
clear_cache
|
81
75
|
end
|
82
76
|
|
77
|
+
def length
|
78
|
+
to_a.length
|
79
|
+
end
|
80
|
+
|
83
81
|
private
|
84
82
|
|
85
83
|
def insert_sorted(term)
|
@@ -92,11 +90,5 @@ module Spellr
|
|
92
90
|
@include = {}
|
93
91
|
remove_instance_variable(:@exist) if defined?(@exist)
|
94
92
|
end
|
95
|
-
|
96
|
-
def raise_unless_exists?
|
97
|
-
return if exist?
|
98
|
-
|
99
|
-
raise Spellr::Wordlist::NotFound, "Wordlist file #{@file} doesn't exist at #{@path}"
|
100
|
-
end
|
101
93
|
end
|
102
94
|
end
|
data/lib/spellr.rb
CHANGED
@@ -10,6 +10,18 @@ module Spellr
|
|
10
10
|
class NotFound < Spellr::Error; end
|
11
11
|
end
|
12
12
|
|
13
|
+
class Config
|
14
|
+
class NotFound < Spellr::Error; end
|
15
|
+
class Invalid < Spellr::Error; end
|
16
|
+
end
|
17
|
+
|
18
|
+
class InvalidByteSequence < ArgumentError
|
19
|
+
RE = /invalid byte sequence/.freeze
|
20
|
+
def self.===(error)
|
21
|
+
error.is_a?(ArgumentError) && error.message.match?(RE)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
13
25
|
module_function
|
14
26
|
|
15
27
|
def config
|
data/spellr.gemspec
CHANGED
@@ -14,7 +14,9 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.homepage = 'http://github.com/robotdana/spellr'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
17
|
-
spec.
|
17
|
+
spec.required_ruby_version = '>= 2.4'
|
18
|
+
|
19
|
+
spec.files = Dir.glob('{lib,exe,wordlists}/**/{*,.*}') + %w{
|
18
20
|
CHANGELOG.md
|
19
21
|
Gemfile
|
20
22
|
LICENSE.txt
|
@@ -26,15 +28,18 @@ Gem::Specification.new do |spec|
|
|
26
28
|
spec.require_paths = ['lib']
|
27
29
|
|
28
30
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
31
|
+
spec.add_development_dependency 'mime-types', '~> 3.3.1'
|
32
|
+
spec.add_development_dependency 'nokogiri'
|
29
33
|
spec.add_development_dependency 'pry'
|
30
|
-
spec.add_development_dependency 'rake', '
|
34
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
31
35
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
-
spec.add_development_dependency 'rspec-eventually'
|
33
36
|
spec.add_development_dependency 'rubocop'
|
34
37
|
spec.add_development_dependency 'rubocop-rspec'
|
35
|
-
spec.add_development_dependency '
|
36
|
-
spec.add_development_dependency '
|
37
|
-
spec.add_development_dependency '
|
38
|
-
spec.
|
38
|
+
spec.add_development_dependency 'simplecov', '~> 0.18.5'
|
39
|
+
spec.add_development_dependency 'simplecov-console'
|
40
|
+
spec.add_development_dependency 'tty_string', '>= 0.2.1'
|
41
|
+
spec.add_development_dependency 'webmock', '~> 3.8'
|
42
|
+
|
43
|
+
spec.add_dependency 'fast_ignore', '~> 0.6.0'
|
39
44
|
spec.add_dependency 'parallel', '~> 1.0'
|
40
45
|
end
|