heckler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 605da338e5d75dfc3615fd7255390559228de9223cea8c4696a7781f64b7d33f
4
+ data.tar.gz: 067dfae307730437b0d5bae6a90e06f9033ab0f479469234d11da2dde64bff92
5
+ SHA512:
6
+ metadata.gz: 0e84e49df096985d552c49bd91d9bae548904174d743c5ca6288884e9494ce4be5e5a4601f900b967334500b376e113c719888fbaa91ace5a02ef405b955d8bc
7
+ data.tar.gz: 3b1f705402cb5e522ee4f47d8652947ac1cb56b6e1d2ff539e2fd9d616b15e55a497f173295acbc917d58464fa79e9ab37e35fb03426b8871fad9cb4facfa12e
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-01-16
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Stanislav (Stas) Katkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Heckler
2
+
3
+ **Heckler** is a tool to identify wording or spelling mistakes in ruby codebase: filenames, class names, method names, property names and more. Spelling correction is powered by [GNU Aspell](https://en.wikipedia.org/wiki/GNU_Aspell) and originally started as a ruby port of [Peck](https://github.com/peckphp/peck).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bundle add heckler
9
+ ```
10
+
11
+ If bundler is not being used to manage dependencies, install the gem by executing:
12
+
13
+ ```bash
14
+ gem install heckler
15
+ ```
16
+
17
+ ## Dependencies
18
+ Heckler requires Aspell to be present on a system to function.
19
+
20
+ ### MacOS
21
+ `brew install aspell`
22
+
23
+ ### Linux
24
+ - Debian/Ubuntu: `sudo apt-get install aspell aspell-en`
25
+ - Fedora: `sudo dnf install aspell aspell-en`
26
+ - Arch Linux: `sudo pacman -S aspell aspell-en`
27
+ - openSUSE: `sudo zypper install aspell aspell-en`
28
+ - Alpine: `sudo apk add aspell aspell-en`
29
+
30
+ ## Usage
31
+
32
+ Start off by creating a configuration file, by running:
33
+ `heckler init`
34
+
35
+ Run spelling check with `heckler`
36
+
37
+
38
+ ## Contributing
39
+
40
+ Bug reports and pull requests are welcome on GitHub at https://github.com/skatkov/heckler.
41
+
42
+ ## License
43
+
44
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "standard/rake"
9
+
10
+ task default: %i[test standard]
11
+
12
+ desc "Build and install gem locally"
13
+ task :local_install do
14
+ system "gem build heckler.gemspec"
15
+ system "gem install ./heckler-#{Heckler::VERSION}.gem"
16
+ end
data/exe/heckler ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "heckler"
4
+
5
+ puts "test"
6
+ Heckler::CLI.start(ARGV)
data/heckler.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "preset": "base",
3
+ "ignore": {
4
+ "words": [],
5
+ "paths": [
6
+ "tmp/",
7
+ "log/"
8
+ ]
9
+ }
10
+ }
@@ -0,0 +1,47 @@
1
+ require "open3"
2
+ require_relative "config"
3
+
4
+ Misspelling = Data.define(:word, :suggestions)
5
+
6
+ module Heckler
7
+ class Aspell
8
+ @process = nil
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+
14
+ def check(text)
15
+ misspellings = get_misspellings(text)
16
+ misspellings.reject { |misspelling| @config.word_ignored?(misspelling.word) }
17
+ end
18
+
19
+ private
20
+
21
+ def get_misspellings(text)
22
+ misspellings = run(text)
23
+ misspellings
24
+ end
25
+
26
+ def take_suggestions(suggestions)
27
+ suggestions = suggestions.select { |suggestion| suggestion.match(/[^a-zA-Z]/).nil? }
28
+ suggestions.uniq
29
+ end
30
+
31
+ def run(text)
32
+ stdin, stdout, stderr, wait_thr = Open3.popen3("aspell", "--encoding", "utf-8", "-a", "--ignore-case", "--lang=en_US", "--sug-mode=ultra")
33
+ stdin.puts text
34
+ stdin.close
35
+ output = stdout.read
36
+ stdout.close
37
+ stderr.close
38
+
39
+ output.lines.select { |line| line.start_with?("&") }.map do |line|
40
+ word_metadata, suggestions = line.strip.split(":")
41
+ word = word_metadata.split(" ")[1]
42
+ suggestions = suggestions.strip.split(", ")
43
+ Misspelling.new(word, take_suggestions(suggestions))
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,91 @@
1
+ require_relative "../aspell"
2
+
3
+ require "find"
4
+
5
+ # The Checker::FileSystem class provides functionality to check spelling in file and directory names within a given directory structure.
6
+ #
7
+ # Usage:
8
+ # checker = Checker::FileSystem.new
9
+ # issues = checker.check("/path/to/directory")
10
+ #
11
+ # The check method:
12
+ # - Recursively traverses the given directory
13
+ # - Skips hidden files/directories (starting with .)
14
+ # - Skips .git directories
15
+ # - Checks spelling of each file/directory name
16
+ # - Returns an array of Issue objects containing any misspellings found
17
+ #
18
+ # Each Issue object contains:
19
+ # - The misspelling that was found
20
+ # - The full path to the file/directory
21
+ # - The line number (always 0 for filenames)
22
+ module Heckler
23
+ class Checker
24
+ class FileSystem
25
+ def initialize(config, spellchecker)
26
+ @config = config
27
+ @spellchecker = spellchecker
28
+ end
29
+
30
+ def check(directory)
31
+ files_or_directories = []
32
+ Find.find(directory) do |path|
33
+ next if File.basename(path).start_with?(".")
34
+ next if File.fnmatch("*/.git/*", path)
35
+
36
+ files_or_directories << path
37
+ end
38
+
39
+
40
+ files_or_directories.sort_by! { |path| File.realpath(path) }
41
+
42
+ puts files_or_directories
43
+
44
+ issues = []
45
+
46
+ files_or_directories.each do |file_or_directory|
47
+ name = SpellcheckFormatter.format(File.basename(file_or_directory, ".*"))
48
+ new_issues = @spellchecker.check(name).map do |misspelling|
49
+ Issue.new(misspelling, File.realpath(file_or_directory), 0)
50
+ end
51
+
52
+ issues.concat(new_issues)
53
+ end
54
+
55
+ issues
56
+ end
57
+ end
58
+ end
59
+
60
+ class SpellcheckFormatter
61
+ def self.format(input)
62
+ # Remove leading underscores
63
+ input = input.gsub(/^_+/, "")
64
+
65
+ # Replace underscores and dashes with spaces
66
+ input = input.tr("_-", " ")
67
+
68
+ # Insert spaces between lowercase and uppercase letters (camelCase or PascalCase)
69
+ input = input.gsub(/([a-z])([A-Z])/, '\1 \2')
70
+
71
+ # Split sequences of uppercase letters, ensuring the last uppercase letter starts a new word
72
+ input = input.gsub(/([A-Z]+)([A-Z][a-z])/, '\1 \2')
73
+
74
+ # Replace multiple spaces with a single space
75
+ input = input.gsub(/\s+/, " ")
76
+
77
+ # Convert the final result to lowercase
78
+ input.downcase
79
+ end
80
+ end
81
+
82
+ class Issue
83
+ attr_reader :misspelling, :file, :line
84
+
85
+ def initialize(misspelling, file, line)
86
+ @misspelling = misspelling
87
+ @file = file
88
+ @line = line
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,50 @@
1
+ require 'optparse'
2
+
3
+ require_relative "config"
4
+ require_relative "aspell"
5
+ require_relative "preset"
6
+ require_relative "checker/file_system"
7
+
8
+
9
+ module Heckler
10
+ class Runner
11
+ attr_reader :checkers
12
+
13
+ def initialize(checkers)
14
+ @checkers = checkers
15
+ end
16
+
17
+ def run(directory)
18
+ issues = []
19
+
20
+ puts directory
21
+
22
+ @checkers.each do |checker|
23
+ issues.concat(checker.check(directory))
24
+ end
25
+
26
+ issues
27
+ end
28
+ end
29
+
30
+ class CLI
31
+ def self.start(args)
32
+ command = args.shift
33
+
34
+ case command
35
+ when 'init'
36
+ Config.init
37
+ when nil
38
+ config = Config.instance
39
+ aspell = Aspell.new(config)
40
+
41
+ Runner.new([
42
+ Heckler::Checker::FileSystem.new(config, aspell)
43
+ ]).run(Dir.pwd)
44
+ else
45
+ puts "Unknown command: #{command}"
46
+ puts "Available commands: init, or no command for default behavior"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,86 @@
1
+ require "json"
2
+ require "singleton"
3
+ require_relative "preset"
4
+
5
+ module Heckler
6
+ class Config
7
+ include Singleton
8
+
9
+ JSON_CONFIGURATION_NAME = "heckler.json".freeze
10
+
11
+ attr_accessor :whitelisted_words, :whitelisted_paths, :preset
12
+
13
+ def initialize
14
+ @whitelisted_words = []
15
+ @whitelisted_paths = []
16
+ @preset = nil
17
+ load_config
18
+ end
19
+
20
+ def self.flush
21
+ instance.whitelisted_words = []
22
+ instance.whitelisted_paths = []
23
+ instance.preset = nil
24
+ @resolve_config_file_path_using = nil
25
+ end
26
+
27
+ def self.exists?
28
+ File.exist?(Dir.pwd + "/" + JSON_CONFIGURATION_NAME)
29
+ end
30
+
31
+ def self.init
32
+ file_path = Dir.pwd + "/" + JSON_CONFIGURATION_NAME
33
+
34
+ return false if File.exist?(file_path)
35
+
36
+ configuration = {
37
+ preset: "base",
38
+ ignore: {
39
+ words: [],
40
+ paths: ["tmp/", "log/"]
41
+ }
42
+ }
43
+
44
+ File.write(file_path, JSON.pretty_generate(configuration))
45
+ true
46
+ end
47
+
48
+ def ignore_words(words)
49
+ @whitelisted_words.concat(words.map(&:downcase)).uniq!
50
+ persist
51
+ end
52
+
53
+ def word_ignored?(word)
54
+ @whitelisted_words.include?(word.downcase) ||
55
+ Preset.whitelisted_words(@preset).include?(word.downcase)
56
+ end
57
+
58
+ private
59
+
60
+ def load_config
61
+ base_path = Dir.pwd
62
+ file_path = base_path + "/" + (self.class.instance_variable_get(:@resolve_config_file_path_using)&.call || JSON_CONFIGURATION_NAME)
63
+
64
+ contents = File.exist?(file_path) ? File.read(file_path) : "{}"
65
+ json_as_array = JSON.parse(contents, symbolize_names: true) rescue {}
66
+
67
+ @whitelisted_words = (json_as_array.dig(:ignore, :words) || []).map(&:downcase)
68
+ @whitelisted_paths = json_as_array.dig(:ignore, :paths) || []
69
+ @preset = json_as_array[:preset]
70
+ end
71
+
72
+ def persist
73
+ file_path = Dir.pwd + "/" + JSON_CONFIGURATION_NAME
74
+
75
+ configuration = {
76
+ preset: @preset,
77
+ ignore: {
78
+ words: @whitelisted_words,
79
+ paths: @whitelisted_paths
80
+ }
81
+ }.compact
82
+
83
+ File.write(file_path, JSON.pretty_generate(configuration))
84
+ end
85
+ end
86
+ end
File without changes
@@ -0,0 +1,20 @@
1
+ module Heckler
2
+ class Preset
3
+ PRESET_STUBS_DIRECTORY = File.join(__dir__, "/presets")
4
+
5
+ def self.whitelisted_words(preset)
6
+ return [] if preset.nil? || !stub_exists?(preset)
7
+
8
+ get_words_from_stub("base") + get_words_from_stub(preset)
9
+ end
10
+
11
+ def self.get_words_from_stub(preset)
12
+ path = File.join(PRESET_STUBS_DIRECTORY, "#{preset}.stub")
13
+ File.read(path).lines.map(&:strip).reject(&:empty?)
14
+ end
15
+
16
+ def self.stub_exists?(preset)
17
+ File.exist?(File.join(PRESET_STUBS_DIRECTORY, "#{preset}.stub"))
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,98 @@
1
+ backfill
2
+ cancelled
3
+ propable
4
+ dns
5
+ verifier
6
+ enum
7
+ backend
8
+ formatter
9
+ init
10
+ enums
11
+ env
12
+ misconfigured
13
+ webhook
14
+ auth
15
+ webhooks
16
+ middleware
17
+ json
18
+ fakeable
19
+ fillable
20
+ verifiers
21
+ slugifier
22
+ txt
23
+ namespace
24
+ truncatable
25
+ unformatted
26
+ finishable
27
+ gitlab
28
+ gitfake
29
+ uri
30
+ authenticatable
31
+ hmac
32
+ callables
33
+ validator
34
+ hostname
35
+ ssl
36
+ backoff
37
+ bool
38
+ schemas
39
+ pre
40
+ accessors
41
+ unsuspend
42
+ frontend
43
+ repo
44
+ src
45
+ jwt
46
+ nano
47
+ slugify
48
+ config
49
+ ecr
50
+ cloudflare
51
+ bitbucket
52
+ serverless
53
+ postgres
54
+ dev
55
+ ui
56
+ vite
57
+ tls
58
+ acl
59
+ http
60
+ parsable
61
+ confirmable
62
+ changelog
63
+ livewire
64
+ autocomplete
65
+ followable
66
+ recaptcha
67
+ stringable
68
+ viewables
69
+ resizer
70
+ params
71
+ unfollow
72
+ unfollows
73
+ datetime
74
+ otp
75
+ lifecycle
76
+ unbookmark
77
+ pinnable
78
+ mailables
79
+ sharable
80
+ blocklist
81
+ conf
82
+ timezones
83
+ autocompletion
84
+ arrayable
85
+ pinkary
86
+ sponsorship
87
+ sponsorships
88
+ iframe
89
+ tokenize
90
+ pwa
91
+ stylesheets
92
+ rubygems
93
+ gemfile
94
+ rakefile
95
+ cli
96
+ exe
97
+ readme
98
+ sig
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Heckler
4
+ VERSION = "0.0.1"
5
+ end
data/lib/heckler.rb ADDED
@@ -0,0 +1,11 @@
1
+ ##
2
+ # Heckler is a ruby port of Peck tool
3
+ # @see https://github.com/peckphp/peck
4
+ ##
5
+
6
+ require_relative "heckler/version"
7
+ require_relative "heckler/cli"
8
+
9
+ module Heckler
10
+
11
+ end
data/sig/heckler.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Heckler
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heckler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Stanislav (Stas) Katkov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-01-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: "**Heckler** is a tool to identify wording or spelling mistakes in ruby
14
+ codebase: filenames, class names, method names, property names and more. Spelling
15
+ correction is powered by **GNU Aspell**."
16
+ email:
17
+ - github@skatkov.com
18
+ executables:
19
+ - heckler
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - ".standard.yml"
24
+ - CHANGELOG.md
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - exe/heckler
29
+ - heckler.json
30
+ - lib/heckler.rb
31
+ - lib/heckler/aspell.rb
32
+ - lib/heckler/checker/file_system.rb
33
+ - lib/heckler/cli.rb
34
+ - lib/heckler/config.rb
35
+ - lib/heckler/personn.rb
36
+ - lib/heckler/preset.rb
37
+ - lib/heckler/presets/base.stub
38
+ - lib/heckler/version.rb
39
+ - sig/heckler.rbs
40
+ homepage: https://github.com/skatkov/heckler
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ homepage_uri: https://github.com/skatkov/heckler
45
+ source_code_uri: https://github.com/skatkov/heckler
46
+ changelog_uri: https://github.com/skatkov/heckler/CHANGELOG.md
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 3.1.0
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.5.22
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: tool to identify wording or spelling mistakes in ruby codebase
66
+ test_files: []