that_language-client 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ede343e2d2d2065c8eec215ad7434eed345c0fce
4
- data.tar.gz: 725f13a0d292bcc889ee3bddf8951f47385f64bb
2
+ SHA256:
3
+ metadata.gz: a35e778f0e91bb68c5768bf80e96b4e5b01bb2ccc61742f1d17600beace868ef
4
+ data.tar.gz: 906cc116a23a46c34a7293d4f1731c8e7b8225b0a1da431587724b3ea05942b2
5
5
  SHA512:
6
- metadata.gz: 49442abaaeae4c90371c03ffd294cb7fe8ea75306328d25714bceb56474c79eb3918e4aa6de71967719ef8e3e28e72b56ccc26867ba8dce359b672b6cbed3548
7
- data.tar.gz: b0cac92492926a67f690af4479716852de3dd0fb4517b18f4996b27074d6be86c3fbd5da9359b481f9365752d95d04c346245c1aa00ef1ab24036a535a6c0c3b
6
+ metadata.gz: 155c8e6dcc568f0617cc26d88e72cbfd19beb5f8a80e6f6e6543870d5fdc3e63009b47961194a76cdb92c4ad86b70f6f3a0a0b205f55e4691304bd25dacbabea
7
+ data.tar.gz: fb696ca48f2d3413027ecf3490fbea2476f26842ab8538a4f26ccb7a47c9891544ca836f46f721766145d3a7c39938bb07eed775e249cf0c4f6949d20086bd5f
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ rspec:
12
+ name: rspec (ruby ${{ matrix.ruby }})
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ ruby: ["3.1", "3.2", "3.3", "3.4", "4.0"]
18
+ steps:
19
+ - uses: actions/checkout@v7
20
+ - uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby }}
23
+ bundler-cache: true
24
+ - run: bundle exec rspec
25
+
26
+ rubocop:
27
+ name: rubocop
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v7
31
+ - uses: ruby/setup-ruby@v1
32
+ with:
33
+ ruby-version: "4.0"
34
+ bundler-cache: true
35
+ - run: bundle exec rubocop
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ # TargetRubyVersion is deliberately not set here. RuboCop derives it from
5
+ # required_ruby_version in the gemspec (currently ">= 3.1"), which takes
6
+ # precedence over .ruby-version, so the oldest supported Ruby is stated in
7
+ # exactly one place. It is the *minimum* supported version, not the version
8
+ # used for development -- raising it means dropping support for users.
9
+ NewCops: enable
10
+ SuggestExtensions: false
11
+ Exclude:
12
+ - "bin/**/*"
13
+ - "vendor/**/*"
14
+ - "tmp/**/*"
15
+
16
+ # Specs read better without a documentation comment on every describe block.
17
+ Style/Documentation:
18
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,208 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2026-07-20 03:15:13 UTC using RuboCop version 1.88.2.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 6
10
+ # Configuration parameters: EnforcedStyle, AllowedGems.
11
+ # SupportedStyles: Gemfile, gems.rb, gemspec
12
+ Gemspec/DevelopmentDependencies:
13
+ Exclude:
14
+ - 'that_language-client.gemspec'
15
+
16
+ # Offense count: 1
17
+ # This cop supports safe autocorrection (--autocorrect).
18
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
19
+ # SupportedStyles: with_first_element, with_fixed_indentation
20
+ Layout/ArrayAlignment:
21
+ Exclude:
22
+ - 'spec/that_language/client/details_query_spec.rb'
23
+
24
+ # Offense count: 2
25
+ # This cop supports safe autocorrection (--autocorrect).
26
+ Layout/ClosingHeredocIndentation:
27
+ Exclude:
28
+ - 'spec/support/that_language_client_spec_helper.rb'
29
+
30
+ # Offense count: 1
31
+ # This cop supports safe autocorrection (--autocorrect).
32
+ # Configuration parameters: IndentationWidth.
33
+ Layout/ClosingParenthesisIndentation:
34
+ Exclude:
35
+ - 'spec/that_language/client/details_query_spec.rb'
36
+
37
+ # Offense count: 1
38
+ # This cop supports safe autocorrection (--autocorrect).
39
+ Layout/EmptyLineAfterMagicComment:
40
+ Exclude:
41
+ - 'that_language-client.gemspec'
42
+
43
+ # Offense count: 1
44
+ # This cop supports safe autocorrection (--autocorrect).
45
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
46
+ # SupportedStyles: special_inside_parentheses, consistent, align_brackets
47
+ Layout/FirstArrayElementIndentation:
48
+ Exclude:
49
+ - 'spec/that_language/client/details_query_spec.rb'
50
+
51
+ # Offense count: 4
52
+ # This cop supports safe autocorrection (--autocorrect).
53
+ # Configuration parameters: IndentationWidth.
54
+ # SupportedStyles: special_inside_parentheses, consistent, align_braces
55
+ Layout/FirstHashElementIndentation:
56
+ EnforcedStyle: consistent
57
+
58
+ # Offense count: 3
59
+ # This cop supports safe autocorrection (--autocorrect).
60
+ # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
61
+ # SupportedHashRocketStyles: key, separator, table
62
+ # SupportedColonStyles: key, separator, table
63
+ # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
64
+ Layout/HashAlignment:
65
+ Exclude:
66
+ - 'that_language-client.gemspec'
67
+
68
+ # Offense count: 2
69
+ # This cop supports safe autocorrection (--autocorrect).
70
+ Layout/HeredocIndentation:
71
+ Exclude:
72
+ - 'spec/support/that_language_client_spec_helper.rb'
73
+
74
+ # Offense count: 1
75
+ # This cop supports safe autocorrection (--autocorrect).
76
+ # Configuration parameters: EnforcedStyle.
77
+ # SupportedStyles: symmetrical, new_line, same_line
78
+ Layout/MultilineHashBraceLayout:
79
+ Exclude:
80
+ - 'spec/that_language/client/details_query_spec.rb'
81
+
82
+ # Offense count: 1
83
+ # This cop supports safe autocorrection (--autocorrect).
84
+ # Configuration parameters: EnforcedStyle.
85
+ # SupportedStyles: symmetrical, new_line, same_line
86
+ Layout/MultilineMethodCallBraceLayout:
87
+ Exclude:
88
+ - 'spec/that_language/client/details_query_spec.rb'
89
+
90
+ # Offense count: 1
91
+ # This cop supports safe autocorrection (--autocorrect).
92
+ # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
93
+ # SupportedStyles: space, no_space, compact
94
+ # SupportedStylesForEmptyBraces: space, no_space
95
+ Layout/SpaceInsideHashLiteralBraces:
96
+ Exclude:
97
+ - 'spec/that_language/client/details_query_spec.rb'
98
+
99
+ # Offense count: 1
100
+ # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
101
+ Metrics/AbcSize:
102
+ Max: 20
103
+
104
+ # Offense count: 4
105
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
106
+ # AllowedMethods: refine
107
+ Metrics/BlockLength:
108
+ Max: 60
109
+
110
+ # Offense count: 1
111
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
112
+ Metrics/MethodLength:
113
+ Max: 37
114
+
115
+ # Offense count: 1
116
+ # This cop supports safe autocorrection (--autocorrect).
117
+ # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
118
+ # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
119
+ # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
120
+ # FunctionalMethods: let, let!, subject, watch
121
+ # AllowedMethods: lambda, proc, it
122
+ Style/BlockDelimiters:
123
+ Exclude:
124
+ - 'spec/that_language/client/available_query_spec.rb'
125
+
126
+ # Offense count: 1
127
+ # This cop supports safe autocorrection (--autocorrect).
128
+ Style/Encoding:
129
+ Exclude:
130
+ - 'that_language-client.gemspec'
131
+
132
+ # Offense count: 2
133
+ # This cop supports safe autocorrection (--autocorrect).
134
+ Style/ExpandPathArguments:
135
+ Exclude:
136
+ - 'spec/spec_helper.rb'
137
+ - 'that_language-client.gemspec'
138
+
139
+ # Offense count: 26
140
+ # This cop supports unsafe autocorrection (--autocorrect-all).
141
+ # Configuration parameters: EnforcedStyle.
142
+ # SupportedStyles: always, always_true, never
143
+ Style/FrozenStringLiteralComment:
144
+ Enabled: false
145
+
146
+ # Offense count: 1
147
+ # This cop supports safe autocorrection (--autocorrect).
148
+ # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
149
+ # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
150
+ # SupportedShorthandSyntax: always, never, either, consistent, either_consistent
151
+ Style/HashSyntax:
152
+ Exclude:
153
+ - 'Rakefile'
154
+
155
+ # Offense count: 1
156
+ # This cop supports unsafe autocorrection (--autocorrect-all).
157
+ # Configuration parameters: EnforcedStyle, Recursive.
158
+ # SupportedStyles: literals, strict
159
+ Style/MutableConstant:
160
+ Exclude:
161
+ - 'lib/that_language/client/version.rb'
162
+
163
+ # Offense count: 2
164
+ Style/OpenStructUse:
165
+ Exclude:
166
+ - 'lib/that_language/client/details_query.rb'
167
+ - 'spec/that_language/client/details_query_spec.rb'
168
+
169
+ # Offense count: 2
170
+ # This cop supports safe autocorrection (--autocorrect).
171
+ # Configuration parameters: PreferredDelimiters.
172
+ Style/PercentLiteralDelimiters:
173
+ Exclude:
174
+ - 'that_language-client.gemspec'
175
+
176
+ # Offense count: 2
177
+ # This cop supports safe autocorrection (--autocorrect).
178
+ Style/RedundantPercentQ:
179
+ Exclude:
180
+ - 'that_language-client.gemspec'
181
+
182
+ # Offense count: 1
183
+ # This cop supports safe autocorrection (--autocorrect).
184
+ Style/RedundantSelf:
185
+ Exclude:
186
+ - 'lib/that_language.rb'
187
+
188
+ # Offense count: 1
189
+ # This cop supports safe autocorrection (--autocorrect).
190
+ # Configuration parameters: EnforcedStyle.
191
+ # SupportedStyles: only_raise, only_fail, semantic
192
+ Style/SignalException:
193
+ Exclude:
194
+ - 'lib/that_language/client/query.rb'
195
+
196
+ # Offense count: 168
197
+ # This cop supports safe autocorrection (--autocorrect).
198
+ # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
199
+ # SupportedStyles: single_quotes, double_quotes
200
+ Style/StringLiterals:
201
+ Enabled: false
202
+
203
+ # Offense count: 1
204
+ # This cop supports safe autocorrection (--autocorrect).
205
+ # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
206
+ # URISchemes: http, https
207
+ Layout/LineLength:
208
+ Max: 185
data/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ ## 0.2.0 [☰](https://github.com/Deradon/that_language-client/compare/v0.1.2...v0.2.0) (2026-07-20)
2
+
3
+ Revival release. 0.1.2 dates from 2016 and does not install on Ruby 3 or later.
4
+
5
+ ### Breaking changes
6
+
7
+ * **Return types now match the core gem.** This gem claims to be a drop-in
8
+ replacement for `that_language`, and it was not: language *names* are symbols
9
+ and language *codes* are strings in the core gem, but this gem returned
10
+ strings throughout, and `available_languages` came back unsorted. Seven of
11
+ fourteen public methods disagreed, and had since 2016. Corrected at the JSON
12
+ boundary; `to_h` still exposes the raw wire format. Code that compares
13
+ against strings will need updating — which is precisely the incompatibility
14
+ that made this gem not a drop-in replacement in the first place.
15
+ * `required_ruby_version` is now `>= 3.1`, so the gem no longer installs on
16
+ Ruby 2.x.
17
+ * The default host is now `localhost:4567`. The previous default pointed at
18
+ `that-language-api.ruby-on-rails.guru`, which no longer resolves. A default
19
+ you can start yourself cannot rot; point it elsewhere with
20
+ `ThatLanguage::Client.configure { |c| c.host = "..." }`.
21
+
22
+ ### Bug fixes
23
+
24
+ * `ostruct` is declared as a runtime dependency. It stopped being a default gem
25
+ in Ruby 4.0, where `DetailsQuery` requiring it is a bare `LoadError`.
26
+
27
+ ### Enhancements
28
+
29
+ * Gemspec: dropped the `bundler ~> 1.10` development dependency that made
30
+ `bundle install` unsolvable, and modernised the remaining pins.
31
+ * The specs no longer encode the implementation's own assumptions — they assert
32
+ the core gem's contract, which is why stubs alone never caught the drift
33
+ above.
34
+ * CI moved from Travis to GitHub Actions, covering Ruby 3.1 through 4.0.
35
+
36
+ ## 0.1.2 [☰](https://github.com/Deradon/that_language-client/compare/v0.1.1...v0.1.2) (2016-04-12)
37
+
38
+ ## 0.1.1 [☰](https://github.com/Deradon/that_language-client/compare/v0.1.0...v0.1.1) (2015-12-07)
39
+
40
+ ## 0.1.0 (2015-12-07)
41
+
42
+ * Initial release
data/CLAUDE.md ADDED
@@ -0,0 +1,82 @@
1
+ # CLAUDE.md — that_language-client
2
+
3
+ HTTP client for `that_language-service`, and a drop-in replacement for the
4
+ `that_language` gem: same `ThatLanguage` module, same methods, same return
5
+ types, but each call is an HTTP request instead of a local wordlist lookup.
6
+
7
+ Developed on Ruby 4.0.6 (`.tool-versions`); supports 3.1 and up.
8
+
9
+ ## Commands
10
+
11
+ ```sh
12
+ bundle install
13
+ bundle exec rspec # 56 examples, 0 failures
14
+ bundle exec rubocop
15
+ ```
16
+
17
+ The specs stub HTTP with webmock and never touch the network.
18
+
19
+ ## Hard constraints
20
+
21
+ **The public API is a contract with two other repositories.** `that_language`
22
+ is the reference implementation, `that_language-service` exposes the same
23
+ surface as eight HTTP endpoints, and this gem mirrors it method for method.
24
+ Nothing enforces the parity. Adding a method to the core gem means adding a
25
+ query class here.
26
+
27
+ **Language names are symbols; language codes are strings.** That is the core
28
+ gem's contract, and it is what `Query#language_name` exists to preserve across
29
+ the JSON boundary. The one exception is `available`, which is keyed by symbol.
30
+ `to_h` is deliberately outside this — it returns the raw wire format.
31
+
32
+ This drifted once, undetected from 2016 until 2026-07-20: every language name
33
+ came back as a string and `available_languages` was unsorted. **The specs did
34
+ not catch it because they were written from the implementation** — the webmock
35
+ stubs asserted whatever the code already did, so they were green against a
36
+ client that did not match the gem it replaces.
37
+
38
+ If you change anything on the JSON boundary, verify against a real service
39
+ rather than the stubs:
40
+
41
+ ```sh
42
+ docker run -p 4567:4567 that_language-service # from that_language-docker
43
+ ```
44
+
45
+ Then call every public method and compare the *types* against the core gem. The
46
+ two gems both define `ThatLanguage` and cannot be loaded into one process, so
47
+ this is two processes producing comparable output and a diff.
48
+
49
+ ## Conventions
50
+
51
+ - Two-space indent, double-quoted strings in `require`s.
52
+ - **Do not mass-autocorrect RuboCop.** `.rubocop_todo.yml` grandfathers the
53
+ pre-existing offences by design. If a cop fires on new code, fix that code; if
54
+ the todo needs regenerating, use `rubocop --auto-gen-config`.
55
+ - **Explain a shared constraint once, in the shared place.** If the same comment
56
+ would appear on several call sites, that is a signal to hoist the thing being
57
+ explained — a helper method, a shared example — and put the explanation there.
58
+ Repeating an explanation verbatim is noise: it costs a reader attention at
59
+ every occurrence and it goes stale in more than one place at once.
60
+ `Query#language_name` and `spec/support/private_readers.rb` are both this
61
+ pattern.
62
+
63
+ ## Gotchas
64
+
65
+ - **`ostruct` is a declared runtime dependency.** It stopped being a default gem
66
+ in Ruby 4.0 and `DetailsQuery` requires it. Removing it means a bare
67
+ `LoadError` on 4.x. (There is a `TODO` about replacing OpenStruct with a real
68
+ result class; that would retire the dependency.)
69
+ - **`rspec-its` 2.0 resolves the subject with `public_send`**, so `its(:foo)`
70
+ cannot reach a private reader. Use the shared examples in
71
+ `spec/support/private_readers.rb` rather than widening the API to suit a
72
+ matcher.
73
+ - **The default host is `localhost:4567`** — what the service container listens
74
+ on. It used to be a hosted domain, which went offline and left the gem inert
75
+ out of the box. Prefer a default the reader can start themselves over one that
76
+ depends on somebody else's DNS.
77
+ - `Config#host` accepts a bare host, a `host:port`, or a full base URL. Without a
78
+ scheme, `http` is assumed.
79
+
80
+ ## Context
81
+
82
+ This is a public GitHub repository — do not push without asking.
data/README.md CHANGED
@@ -1,144 +1,131 @@
1
+ [![CI](https://github.com/Deradon/that_language-client/actions/workflows/ci.yml/badge.svg)](https://github.com/Deradon/that_language-client/actions/workflows/ci.yml)
2
+
1
3
  # ThatLanguage::Client
2
4
 
3
- ## Installation
5
+ A drop-in replacement for the [`that_language`][core] gem that answers each call
6
+ over HTTP against a [`that_language-service`][service] instance, instead of
7
+ loading wordlists locally.
8
+
9
+ Same `ThatLanguage` module, same methods, same return types — but no 16 MB of
10
+ wordlists in your process and no cold-start cost.
4
11
 
5
- Add this line to your application's Gemfile:
12
+ [core]: https://github.com/Deradon/that_language
13
+ [service]: https://github.com/Deradon/that_language-service
14
+
15
+ ## Installation
6
16
 
7
17
  ```ruby
8
18
  gem 'that_language-client'
9
19
  ```
10
20
 
11
- And then execute:
12
-
13
- $ bundle
14
-
15
- Or install it yourself as:
16
-
17
- $ gem install that_language-client
18
-
19
- ## Examples
21
+ Note that the published 0.1.2 predates this revision. Until a new version is
22
+ released, point at the repository:
20
23
 
21
24
  ```ruby
22
- text = "This is just a random english sentence."
23
-
24
- ## == Language
25
-
26
- ThatLanguage.language(text)
27
- # => "English"
28
-
29
-
30
- ## == LanguageCode
31
-
32
- ThatLanguage.language_code(text)
33
- # => "en"
34
-
35
-
36
- ## == Detect
37
-
38
- detect = ThatLanguage.detect(text)
39
- # => #<ThatLanguage::Client::DetectQuery:...
25
+ gem 'that_language-client', git: 'https://github.com/Deradon/that_language-client.git'
26
+ ```
40
27
 
41
- detect.language
42
- # => "English"
28
+ ## You need a service to talk to
43
29
 
44
- detect.language_code
45
- # => "en"
30
+ This gem is a client and does nothing on its own. The default host is
31
+ `localhost:4567`, which is what the [service container][service] listens on:
46
32
 
47
- detect.confidence
48
- # => 0.5631093821386951
33
+ ```sh
34
+ docker run -p 4567:4567 that_language-service
35
+ ```
49
36
 
50
- detect.to_h
51
- # => {"language"=>"English", "language_code"=>"en", "confidence"=>0.5631093821386951}
37
+ Point it somewhere else with:
52
38
 
39
+ ```ruby
40
+ ThatLanguage::Client.configure do |config|
41
+ config.host = "https://api.example.com"
42
+ end
43
+ ```
53
44
 
54
- ## == Details
45
+ The host may be a bare `example.com`, a `host:port`, or a full base URL. Without
46
+ a scheme, `http` is assumed.
55
47
 
56
- details = ThatLanguage.details(text)
57
- # => #<ThatLanguage::Client::DetailsQuery:...
48
+ > Earlier versions defaulted to a hosted domain. That host went offline, which
49
+ > left the gem inert out of the box with nothing to show for it but a connection
50
+ > error. The default is now something you can start yourself.
58
51
 
59
- winner = details.winner
60
- # => OpenStruct
52
+ ## Examples
61
53
 
62
- winner.language
63
- # => "English"
54
+ ```ruby
55
+ text = "This is just a random english sentence."
64
56
 
65
- winner.language_code
66
- # => "en"
57
+ ThatLanguage.language(text)
58
+ # => :English
67
59
 
68
- winner.confidence
69
- # => 0.5631093821386951
60
+ ThatLanguage.language_code(text)
61
+ # => "en"
70
62
 
71
- winner.value
72
- # => 3.9417656749708656
63
+ detect = ThatLanguage.detect(text)
64
+ detect.language # => :English
65
+ detect.language_code # => "en"
66
+ detect.confidence # => 0.5631093821386951
67
+ detect.to_h # => {"language"=>"English", ...} # raw wire format
73
68
 
74
- winner.hit_ratio
75
- # => 0.8571428571428571
69
+ details = ThatLanguage.details(text)
70
+ winner = details.winner
71
+ winner.language # => :English
72
+ winner.language_code # => "en"
73
+ winner.confidence # => 0.5631093821386951
74
+ winner.value # => 3.9417656749708656
75
+ winner.hit_ratio # => 0.8571428571428571
76
+ winner.hit_count # => 6
77
+ winner.words_count # => 7
78
+ details.results # => [#<OpenStruct ...>, ...]
76
79
 
77
- winner.hit_count
78
- # => 6
80
+ ThatLanguage.available
81
+ # => { ak: :Akan, am: :Amharic, ... }
79
82
 
80
- winner.words_count
81
- # => 7
83
+ ThatLanguage.available_languages
84
+ # => [:Akan, :Amharic, ...]
82
85
 
83
- results = details.results
84
- # => [#<OpenStruct, #<OpenStruct, ...]
86
+ ThatLanguage.available_language_codes
87
+ # => [:ak, :am, ...]
85
88
 
86
- details.to_h
87
- # =>
88
- # {
89
- # "results" => [
90
- # {
91
- # "language"=>"English",
92
- # "language_code"=>"en",
93
- # "confidence"=>0.5631093821386951,
94
- # "value"=>3.9417656749708656,
95
- # "hit_ratio"=>0.8571428571428571,
96
- # "hit_count"=>6,
97
- # "words_count"=>7
98
- # },
99
- # {
100
- # "language"=>"Sundanese",
101
- # "language_code"=>"su",
102
- # "confidence"=>0.26101716738575337,
103
- # "value"=>1.8271201717002734,
104
- # "hit_ratio"=>0.7142857142857143,
105
- # "hit_count"=>5,
106
- # "words_count"=>7
107
- # },
108
- # # ...
109
- # ]
110
- # }
89
+ ThatLanguage.api_version # the *remote service's* version
90
+ # => "0.1.2"
111
91
  ```
112
92
 
113
- ## Configure
93
+ ### Symbols and strings
114
94
 
115
- Configure the host to use:
95
+ Following the core gem: **language names are symbols, language codes are
96
+ strings.** `available` is keyed by symbol with symbol values, and both
97
+ `available_languages` and `available_language_codes` come back sorted.
116
98
 
117
- ```ruby
118
- ThatLanguage::Client.configure do |config|
119
- config.host = "my-custom-that-language-api.com"
120
- end
121
- ```
99
+ JSON has no symbols, so this gem converts at the boundary. Before this was
100
+ fixed, every one of those returned a string and `available_languages` was
101
+ unsorted which meant the gem was not actually a drop-in replacement. `to_h`
102
+ is the exception and still gives you the raw wire format.
122
103
 
123
104
  ## Development
124
105
 
125
- After checking out the repo, run `bin/setup` to install dependencies.
126
- Then, run `rake rspec` to run the tests.
127
- You can also run `bin/console` for an interactive prompt that will allow you to experiment.
106
+ ```sh
107
+ bundle install
108
+ bundle exec rspec # 56 examples
109
+ bundle exec rubocop
110
+ ```
128
111
 
129
- To install this gem onto your local machine, run `bundle exec rake install`.
130
- To release a new version, update the version number in `version.rb`, and then run
131
- `bundle exec rake release`, which will create a git tag for the version,
132
- push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
112
+ Tested on Ruby 3.2, 3.3 and 4.0. The specs stub HTTP with `webmock` and never
113
+ touch the network.
133
114
 
134
- ## Contributing
115
+ To check parity against a real service rather than stubs, start the container
116
+ and exercise the gem against it — that is how the symbol/string drift above was
117
+ found, and stubs alone would not have caught it, because the stubs encoded the
118
+ same wrong assumption the code did.
135
119
 
136
- Bug reports and pull requests are welcome on GitHub at https://github.com/Deradon/that_language-client.
137
- This project is intended to be a safe, welcoming space for collaboration,
138
- and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
120
+ ## Contributing
139
121
 
122
+ Bug reports and pull requests are welcome on GitHub at
123
+ https://github.com/Deradon/that_language-client. This project is intended to be
124
+ a safe, welcoming space for collaboration, and contributors are expected to
125
+ adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
126
+ conduct.
140
127
 
141
128
  ## License
142
129
 
143
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
144
-
130
+ The gem is available as open source under the terms of the
131
+ [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,25 @@
1
+ require "that_language/client/query"
2
+
3
+ module ThatLanguage
4
+ class Client
5
+ class AvailableQuery < Query
6
+ # FIXME: do not pass text to super
7
+ def initialize(client: Client.current)
8
+ super(text: "", client: client)
9
+ end
10
+
11
+ # The exception to the rule in Query#language_name: this hash is keyed by
12
+ # symbol in the core gem (`{ak: :Akan}`), so the *code* is a symbol here
13
+ # even though language_code(text) returns a string.
14
+ def available
15
+ @available ||= json["available"].to_h { |code, name| [code.to_sym, language_name(name)] }
16
+ end
17
+
18
+ private
19
+
20
+ def endpoint
21
+ "/available"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,10 +1,24 @@
1
1
  module ThatLanguage
2
2
  class Client
3
3
  class Config
4
- attr_accessor :host
4
+ attr_writer :host
5
5
 
6
+ # The default points at a service on this machine, which is what the
7
+ # container image listens on:
8
+ #
9
+ # docker run -p 4567:4567 that_language-service
10
+ #
11
+ # It used to be a hosted domain. That host went offline and the gem became
12
+ # inert out of the box, with nothing to show for it but a connection
13
+ # error -- so the default is now something the reader can start
14
+ # themselves, and it cannot rot, because it is not somebody else's DNS
15
+ # record.
16
+ #
17
+ # Point it at a real deployment with:
18
+ #
19
+ # ThatLanguage::Client.configure { |c| c.host = "https://api.example.com" }
6
20
  def host
7
- @host ||= "that-language-api.ruby-on-rails.guru"
21
+ @host ||= "localhost:4567"
8
22
  end
9
23
  end
10
24
  end
@@ -10,7 +10,9 @@ module ThatLanguage
10
10
  end
11
11
 
12
12
  def results
13
- @results ||= json["results"].map { |result| OpenStruct.new(result) }
13
+ @results ||= json["results"].map do |result|
14
+ OpenStruct.new(result.merge("language" => language_name(result["language"])))
15
+ end
14
16
  end
15
17
 
16
18
  private
@@ -4,7 +4,7 @@ module ThatLanguage
4
4
  class Client
5
5
  class DetectQuery < Query
6
6
  def language
7
- json["language"]
7
+ language_name(json["language"])
8
8
  end
9
9
 
10
10
  def language_code
@@ -4,7 +4,7 @@ module ThatLanguage
4
4
  class Client
5
5
  class LanguageQuery < Query
6
6
  def language
7
- json["language"]
7
+ language_name(json["language"])
8
8
  end
9
9
 
10
10
  private
@@ -19,6 +19,15 @@ module ThatLanguage
19
19
 
20
20
  attr_reader :client
21
21
 
22
+ # The core gem returns language *names* as symbols and language *codes*
23
+ # as strings. JSON has neither, so every name crossing this boundary is
24
+ # converted back -- without it this gem is not the drop-in replacement it
25
+ # claims to be. Every subclass that surfaces a language name goes through
26
+ # here, so the rule is stated once.
27
+ def language_name(value)
28
+ value&.to_sym
29
+ end
30
+
22
31
  def json
23
32
  @json ||= JSON.parse(response.body)
24
33
  end
@@ -28,7 +37,7 @@ module ThatLanguage
28
37
  end
29
38
 
30
39
  def url
31
- "http://#{client.host}#{endpoint}"
40
+ "#{client.base_url}#{endpoint}"
32
41
  end
33
42
 
34
43
  def endpoint
@@ -1,5 +1,5 @@
1
1
  module ThatLanguage
2
2
  class Client
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -0,0 +1,22 @@
1
+ require "that_language/client/query"
2
+
3
+ module ThatLanguage
4
+ class Client
5
+ class VersionQuery < Query
6
+ # FIXME: do not pass text to super
7
+ def initialize(client: Client.current)
8
+ super(text: "", client: client)
9
+ end
10
+
11
+ def version
12
+ json["version"]
13
+ end
14
+
15
+ private
16
+
17
+ def endpoint
18
+ "/version"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,10 +1,12 @@
1
1
  require "that_language"
2
2
  require "that_language/client/config"
3
3
 
4
- require "that_language/client/language_query"
5
- require "that_language/client/language_code_query"
6
- require "that_language/client/detect_query"
4
+ require "that_language/client/available_query"
7
5
  require "that_language/client/details_query"
6
+ require "that_language/client/detect_query"
7
+ require "that_language/client/language_code_query"
8
+ require "that_language/client/language_query"
9
+ require "that_language/client/version_query"
8
10
 
9
11
  require "that_language/client/version"
10
12
 
@@ -16,6 +18,16 @@ module ThatLanguage
16
18
  @host = host
17
19
  end
18
20
 
21
+ # `host` may be a bare "example.com", a "host:port", or a full base URL
22
+ # carrying a scheme. Anything without a scheme is assumed to be http, which
23
+ # is what this client always did; https is now expressible, and any real
24
+ # deployment will need it.
25
+ def base_url
26
+ return host.sub(%r{/+\z}, "") if host.include?("://")
27
+
28
+ "http://#{host}"
29
+ end
30
+
19
31
  def self.current
20
32
  @current ||= Client.new(host: config.host)
21
33
  end
data/lib/that_language.rb CHANGED
@@ -17,6 +17,25 @@ module ThatLanguage
17
17
  ThatLanguage::Client::DetailsQuery.new(text: text)
18
18
  end
19
19
 
20
+ def self.available
21
+ ThatLanguage::Client::AvailableQuery.new.available
22
+ end
23
+
24
+ # Sorted, to match the core gem. The service happens to return codes in
25
+ # order, so only the names were visibly unsorted -- but relying on the
26
+ # server's ordering is exactly how the two implementations drift.
27
+ def self.available_languages
28
+ available.values.sort
29
+ end
30
+
31
+ def self.available_language_codes
32
+ available.keys.sort
33
+ end
34
+
35
+ def self.api_version
36
+ ThatLanguage::Client::VersionQuery.new.version
37
+ end
38
+
20
39
  def self.monkeypatch(klass)
21
40
  klass.class_eval do
22
41
  define_method(:language_code) { ThatLanguage.language_code(self.to_s) }
@@ -10,27 +10,33 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["me@patrick-helm.de"]
11
11
 
12
12
  spec.summary = %q{API client for ThatLanguage.}
13
- # spec.description = %q{Detect language for given text.}
13
+ spec.description = %q{Drop-in replacement for the that_language gem that answers each call over HTTP against a that_language-service instance, instead of loading wordlists locally.}
14
14
  spec.homepage = "https://github.com/Deradon/that_language-client"
15
15
  spec.license = "MIT"
16
16
 
17
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
- # delete this section to allow pushing this gem to any host.
19
- # if spec.respond_to?(:metadata)
20
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
- # else
22
- # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
- # end
17
+ spec.required_ruby_version = ">= 3.1"
18
+
19
+ spec.metadata = {
20
+ "homepage_uri" => spec.homepage,
21
+ "source_code_uri" => spec.homepage,
22
+ "bug_tracker_uri" => "#{spec.homepage}/issues",
23
+ "rubygems_mfa_required" => "true"
24
+ }
24
25
 
25
26
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
27
  spec.bindir = "exe"
27
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
29
  spec.require_paths = ["lib"]
29
30
 
30
- spec.add_development_dependency "bundler", "~> 1.10"
31
+ # ostruct stopped being a default gem in Ruby 4.0, so it must be declared
32
+ # explicitly. DetailsQuery wraps each result in an OpenStruct; without this
33
+ # the gem fails to load on 4.x with a bare LoadError.
34
+ spec.add_dependency "ostruct", "~> 0.6"
35
+
31
36
  spec.add_development_dependency "pry"
32
- spec.add_development_dependency "rake", "~> 10.0"
33
- spec.add_development_dependency "rspec"
34
- spec.add_development_dependency "rspec-its"
35
- spec.add_development_dependency "webmock"
37
+ spec.add_development_dependency "rake", "~> 13.0"
38
+ spec.add_development_dependency "rspec", "~> 3.13"
39
+ spec.add_development_dependency "rspec-its", "~> 2.0"
40
+ spec.add_development_dependency "rubocop", "~> 1.88"
41
+ spec.add_development_dependency "webmock", "~> 3.25"
36
42
  end
metadata CHANGED
@@ -1,29 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: that_language-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Helm
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2015-12-07 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: bundler
13
+ name: ostruct
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '1.10'
20
- type: :development
18
+ version: '0.6'
19
+ type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '1.10'
25
+ version: '0.6'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: pry
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -44,66 +43,86 @@ dependencies:
44
43
  requirements:
45
44
  - - "~>"
46
45
  - !ruby/object:Gem::Version
47
- version: '10.0'
46
+ version: '13.0'
48
47
  type: :development
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - "~>"
53
52
  - !ruby/object:Gem::Version
54
- version: '10.0'
53
+ version: '13.0'
55
54
  - !ruby/object:Gem::Dependency
56
55
  name: rspec
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
- - - ">="
58
+ - - "~>"
60
59
  - !ruby/object:Gem::Version
61
- version: '0'
60
+ version: '3.13'
62
61
  type: :development
63
62
  prerelease: false
64
63
  version_requirements: !ruby/object:Gem::Requirement
65
64
  requirements:
66
- - - ">="
65
+ - - "~>"
67
66
  - !ruby/object:Gem::Version
68
- version: '0'
67
+ version: '3.13'
69
68
  - !ruby/object:Gem::Dependency
70
69
  name: rspec-its
71
70
  requirement: !ruby/object:Gem::Requirement
72
71
  requirements:
73
- - - ">="
72
+ - - "~>"
74
73
  - !ruby/object:Gem::Version
75
- version: '0'
74
+ version: '2.0'
76
75
  type: :development
77
76
  prerelease: false
78
77
  version_requirements: !ruby/object:Gem::Requirement
79
78
  requirements:
80
- - - ">="
79
+ - - "~>"
81
80
  - !ruby/object:Gem::Version
82
- version: '0'
81
+ version: '2.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.88'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.88'
83
96
  - !ruby/object:Gem::Dependency
84
97
  name: webmock
85
98
  requirement: !ruby/object:Gem::Requirement
86
99
  requirements:
87
- - - ">="
100
+ - - "~>"
88
101
  - !ruby/object:Gem::Version
89
- version: '0'
102
+ version: '3.25'
90
103
  type: :development
91
104
  prerelease: false
92
105
  version_requirements: !ruby/object:Gem::Requirement
93
106
  requirements:
94
- - - ">="
107
+ - - "~>"
95
108
  - !ruby/object:Gem::Version
96
- version: '0'
97
- description:
109
+ version: '3.25'
110
+ description: Drop-in replacement for the that_language gem that answers each call
111
+ over HTTP against a that_language-service instance, instead of loading wordlists
112
+ locally.
98
113
  email:
99
114
  - me@patrick-helm.de
100
115
  executables: []
101
116
  extensions: []
102
117
  extra_rdoc_files: []
103
118
  files:
119
+ - ".github/workflows/ci.yml"
104
120
  - ".gitignore"
105
121
  - ".rspec"
106
- - ".travis.yml"
122
+ - ".rubocop.yml"
123
+ - ".rubocop_todo.yml"
124
+ - CHANGELOG.md
125
+ - CLAUDE.md
107
126
  - CODE_OF_CONDUCT.md
108
127
  - Gemfile
109
128
  - LICENSE.txt
@@ -113,6 +132,7 @@ files:
113
132
  - bin/setup
114
133
  - lib/that_language.rb
115
134
  - lib/that_language/client.rb
135
+ - lib/that_language/client/available_query.rb
116
136
  - lib/that_language/client/config.rb
117
137
  - lib/that_language/client/details_query.rb
118
138
  - lib/that_language/client/detect_query.rb
@@ -120,12 +140,16 @@ files:
120
140
  - lib/that_language/client/language_query.rb
121
141
  - lib/that_language/client/query.rb
122
142
  - lib/that_language/client/version.rb
143
+ - lib/that_language/client/version_query.rb
123
144
  - that_language-client.gemspec
124
145
  homepage: https://github.com/Deradon/that_language-client
125
146
  licenses:
126
147
  - MIT
127
- metadata: {}
128
- post_install_message:
148
+ metadata:
149
+ homepage_uri: https://github.com/Deradon/that_language-client
150
+ source_code_uri: https://github.com/Deradon/that_language-client
151
+ bug_tracker_uri: https://github.com/Deradon/that_language-client/issues
152
+ rubygems_mfa_required: 'true'
129
153
  rdoc_options: []
130
154
  require_paths:
131
155
  - lib
@@ -133,16 +157,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
157
  requirements:
134
158
  - - ">="
135
159
  - !ruby/object:Gem::Version
136
- version: '0'
160
+ version: '3.1'
137
161
  required_rubygems_version: !ruby/object:Gem::Requirement
138
162
  requirements:
139
163
  - - ">="
140
164
  - !ruby/object:Gem::Version
141
165
  version: '0'
142
166
  requirements: []
143
- rubyforge_project:
144
- rubygems_version: 2.4.6
145
- signing_key:
167
+ rubygems_version: 4.0.16
146
168
  specification_version: 4
147
169
  summary: API client for ThatLanguage.
148
170
  test_files: []
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.2
4
- before_install: gem install bundler -v 1.10.4