spellr 0.6.0 → 0.7.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 +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
|