simple_english 0.2.0 → 0.4.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +61 -39
  3. data/docs/RULES.md +1 -1
  4. data/lib/simple_english/cli.rb +53 -15
  5. data/lib/simple_english/client/daemon.rb +225 -0
  6. data/lib/simple_english/client/language_tool.rb +95 -0
  7. data/lib/simple_english/daemon/engine.rb +40 -0
  8. data/lib/simple_english/{http.rb → daemon/http.rb} +10 -4
  9. data/lib/simple_english/{server.rb → daemon/server.rb} +113 -7
  10. data/lib/simple_english/{extractor.rb → lint/extractor.rb} +6 -1
  11. data/lib/simple_english/lint/finding.rb +9 -0
  12. data/lib/simple_english/lint/lint_plan.rb +22 -0
  13. data/lib/simple_english/{markdown.rb → lint/markdown.rb} +28 -10
  14. data/lib/simple_english/lint/plain_text.rb +30 -0
  15. data/lib/simple_english/{result.rb → lint/result.rb} +1 -1
  16. data/lib/simple_english/{config.rb → setup/config.rb} +3 -2
  17. data/lib/simple_english/setup/fingerprint.rb +38 -0
  18. data/lib/simple_english/{install.rb → setup/install.rb} +19 -9
  19. data/lib/simple_english/setup/languagetool.rb +226 -0
  20. data/lib/simple_english/version.rb +1 -1
  21. data/lib/simple_english.rb +25 -43
  22. data/rules/simple-english.xml +5 -1
  23. metadata +42 -36
  24. data/lib/simple_english/client.rb +0 -165
  25. data/lib/simple_english/engine.rb +0 -31
  26. data/lib/simple_english/finding.rb +0 -8
  27. data/lib/simple_english/languagetool.rb +0 -143
  28. data/lib/simple_english/plain_text.rb +0 -18
  29. /data/lib/simple_english/{annotated_text.rb → lint/annotated_text.rb} +0 -0
  30. /data/lib/simple_english/{counts.rb → lint/counts.rb} +0 -0
  31. /data/lib/simple_english/{paragraph.rb → lint/paragraph.rb} +0 -0
  32. /data/lib/simple_english/{segment.rb → lint/segment.rb} +0 -0
  33. /data/lib/simple_english/{span.rb → lint/span.rb} +0 -0
  34. /data/lib/simple_english/{suppressions.rb → lint/suppressions.rb} +0 -0
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The complete lint engine, daemon-side. Never call SimpleEnglish.lint_text
4
- # here: that call probes the daemon and recurses into the server.
5
-
6
- require "json"
7
-
8
- module SimpleEnglish
9
- module Engine
10
- module_function
11
-
12
- def lint(text, base_url:, language: nil)
13
- if language
14
- spans = SimpleEnglish::Extractor.comment_spans(text, language)
15
- spans.empty? ? [] :
16
- SimpleEnglish::Client.check(
17
- SimpleEnglish::AnnotatedText.build(text, spans), base_url: base_url
18
- )
19
- else
20
- stripped = SimpleEnglish::Markdown.strip(text)
21
- (SimpleEnglish::Counts.check(stripped) +
22
- SimpleEnglish::Client.check(stripped, base_url: base_url))
23
- .sort_by { |finding| [finding.line, finding.rule] }
24
- end
25
- end
26
-
27
- def lint_json(text, base_url:, language: nil)
28
- JSON.generate(lint(text, base_url: base_url, language: language).map(&:to_h))
29
- end
30
- end
31
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # One lint result. The line and column are 1-based. The column is
4
- # nil when the rule has no position within the line.
5
-
6
- module SimpleEnglish
7
- Finding = Struct.new(:line, :column, :rule, :message)
8
- end
@@ -1,143 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The pinned LanguageTool distro: its version, its rules file, and the
4
- # downloader. Everything that resolves "where it is on this machine and
5
- # which java runs it" lives in Install. The engine runs as the daemon's
6
- # inner HTTP server (lib/simple_english/server.rb).
7
-
8
- module SimpleEnglish
9
- module LanguageTool
10
- RULES_FILE = File.expand_path("../../rules/simple-english.xml", __dir__)
11
- # The only place a LanguageTool version number appears. `se
12
- # setup` downloads this version, and every jar path derives from it. No
13
- # env var can point at another one.
14
- LT_VERSION = "6.6"
15
- CACHE_DIR_ENV = "SE_CACHE_DIR"
16
- JAVA_ENV = "SE_JAVA"
17
- # macOS ships a /usr/bin/java stub that reports no runtime. Homebrew
18
- # installs the real one here, outside PATH.
19
- HOMEBREW_JAVA = "/opt/homebrew/opt/openjdk/bin/java"
20
- TIMEOUT_SECONDS = 300
21
-
22
- module_function
23
-
24
- # Download and unpack the pinned LanguageTool into DIR. Idempotent:
25
- # returns the install directory, or nil after warning why. Pure Ruby
26
- # throughout: the download uses Net::HTTP and the unpack uses rubyzip,
27
- # so setup needs no curl or unzip on the machine.
28
- def install(dir)
29
- require "fileutils"
30
- dest = File.join(dir, "LanguageTool-#{LT_VERSION}")
31
- if File.exist?(File.join(dest, "languagetool-commandline.jar"))
32
- remove_stale_versions(dir, dest)
33
- return dest
34
- end
35
- zip = File.join(dir, "LanguageTool-#{LT_VERSION}.zip")
36
- FileUtils.mkdir_p(dir)
37
- return nil unless download(download_url, zip)
38
- return nil unless extract(zip, dir)
39
- File.delete(zip)
40
- remove_stale_versions(dir, dest)
41
- dest
42
- end
43
-
44
- def download_url
45
- "https://languagetool.org/download/LanguageTool-#{LT_VERSION}.zip"
46
- end
47
-
48
- # Streams URL to TO and follows redirects. Warns and returns false
49
- # on any transport or HTTP failure, so callers never see a stack
50
- # trace.
51
- def download(url, to, redirects: 3)
52
- require "net/http"
53
- uri = URI(url)
54
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
55
- open_timeout: 10, read_timeout: 60) do |http|
56
- http.request(Net::HTTP::Get.new(uri)) do |response|
57
- case response
58
- when Net::HTTPRedirection
59
- return false if redirects.zero?
60
- return download(response["location"], to, redirects: redirects - 1)
61
- when Net::HTTPSuccess
62
- File.binwrite(to, "")
63
- File.open(to, "wb") do |file|
64
- response.read_body { |chunk| file.write(chunk) }
65
- end
66
- return true
67
- else
68
- warn "error: download failed: HTTP #{response.code} from #{url}"
69
- return false
70
- end
71
- end
72
- end
73
- rescue SystemCallError, SocketError, Timeout::Error,
74
- OpenSSL::SSL::SSLError, Net::ProtocolError => e
75
- warn "error: download failed: #{e.class}: #{e.message}"
76
- false
77
- end
78
-
79
- # Unpacks ZIP into DIR and refuses entries that try to escape it.
80
- # Warns and returns false on any failure.
81
- def extract(zip, dir)
82
- require "zip"
83
- Zip::File.open(zip) do |archive|
84
- archive.each do |entry|
85
- unless safe_entry_target(dir, entry.name)
86
- warn "error: zip entry escapes the install dir: #{entry.name}"
87
- return false
88
- end
89
- entry.extract(entry.name, destination_directory: dir,
90
- create_parent_directories: true)
91
- end
92
- end
93
- true
94
- rescue SystemCallError, Zip::Error => e
95
- warn "error: unpack failed: #{e.class}: #{e.message}"
96
- false
97
- end
98
-
99
- # The path to write zip entry NAME into, or nil when NAME escapes
100
- # DIR. A download is untrusted input: this is the zip-slip
101
- # guard.
102
- def safe_entry_target(dir, name)
103
- target = File.expand_path(File.join(dir, name))
104
- base = File.expand_path(dir) + File::SEPARATOR
105
- target.start_with?(base) ? target : nil
106
- end
107
-
108
- # One gem version pins one LanguageTool version. Older dirs are
109
- # hundreds of MB each, so delete them on every setup run and keep
110
- # only the current version (and any zips: they are re-deleted or
111
- # re-extracted by their own flow).
112
- def remove_stale_versions(dir, keep)
113
- require "fileutils"
114
- Dir.glob(File.join(dir, "LanguageTool-*")).each do |entry|
115
- FileUtils.rm_rf(entry) if File.directory?(entry) && entry != keep
116
- end
117
- end
118
-
119
- # Proves the installation works end to end: java runs the
120
- # commandline jar and the custom rules load. The command exits
121
- # nonzero on a broken java, a corrupt jar, or invalid rules XML,
122
- # so setup can report a real verdict instead of "files exist".
123
- # Returns a boolean, never raises or warns: the caller owns the
124
- # message.
125
- def smoke(install)
126
- rule = rule_ids.first
127
- return false if rule.nil?
128
- require "tmpdir"
129
- Dir.mktmpdir do |dir|
130
- text = File.join(dir, "smoke.txt")
131
- File.write(text, "Check this sentence.\n")
132
- system(install.java!, "-jar", install.commandline_jar,
133
- "--language", "en", "--rulefile", RULES_FILE,
134
- "--enable", rule, "--enabledonly", text,
135
- out: File::NULL, err: File::NULL) ? true : false
136
- end
137
- end
138
-
139
- def rule_ids
140
- File.read(RULES_FILE).scan(/<rule(?:group)? id="(\w+)"/).flatten
141
- end
142
- end
143
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SimpleEnglish
4
- module Client
5
- # The plain-text payload for /v2/check, duck-compatible with
6
- # AnnotatedText::Result: #lt_params and #locate.
7
- PlainText = Struct.new(:text) do
8
- def lt_params
9
- {"text" => text}
10
- end
11
-
12
- def locate(utf16_offset)
13
- [Client.offset_to_line(text, utf16_offset),
14
- Client.offset_to_column(text, utf16_offset)]
15
- end
16
- end
17
- end
18
- end
File without changes
File without changes
File without changes