minting 1.3.0 → 1.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.
@@ -0,0 +1,335 @@
1
+ # `minting` — Recommendations Roadmap
2
+
3
+ Living document of improvement recommendations for the `minting` gem,
4
+ produced from a full read of `AGENTS.md`, `README.md`, `CHANGELOG.md`,
5
+ and the entire `lib/` + `test/` tree (v1.3.0 baseline).
6
+
7
+ **Conventions**
8
+
9
+ - P0 / P1 / P2 / P3 = priority. P0 = small, high-value, ship now.
10
+ - ✅ = done (committed on `master` or an in-flight branch).
11
+ - 🟡 = partially done.
12
+ - Each item is sized to be a single PR unless marked *[epic]*.
13
+ - File references are relative to repo root.
14
+
15
+ ---
16
+
17
+ ## TL;DR
18
+
19
+ The gem is in **excellent shape**: ~1,400 LoC, 100% line coverage,
20
+ RuboCop + Reek clean, performance bench suite, immutable `Rational`
21
+ internals, 117 built-in currencies. The recommendations below are
22
+ **polish, hardening, and roadmap** — not "fix the broken thing".
23
+
24
+ Two of the highest-leverage moves are already in flight:
25
+
26
+ - ✅ **README-as-spec** — `test_readme_usage` now asserts the
27
+ README's `to_s` / `to_json` / `to_hash` / `split` / `allocate`
28
+ examples instead of just exercising them silently.
29
+ - ✅ **`Money.from_fractional`** — symmetric inverse of `#fractional`
30
+ with full test + README + CHANGELOG coverage.
31
+
32
+ The remaining work is mostly documentation discipline, API ergonomics,
33
+ and the two roadmap items already advertised in the README (I18n +
34
+ exchange rates).
35
+
36
+ ---
37
+
38
+ ## 🔴 P0 — Quick wins, ship now
39
+
40
+ ### P0-1 ✅ Tighten README-as-spec
41
+
42
+ **Status:** Merged in commit `20359dc`.
43
+
44
+ - Turned 14 commented `#=>` examples in `test/minting_test.rb` into
45
+ real `assert_equal` calls.
46
+ - Fixed typo: `conversiob` → `conversion`.
47
+ - Corrected the README's `to_json` example to match the actual output
48
+ (the real string has spaces around the `:`).
49
+ - Softened the "2× faster" headline to point at the Performance
50
+ section where the measured ratios live.
51
+ - Added a one-line `rubocop:disable` on `test_readme_usage` (the spec
52
+ has to stay monolithic to serve as a spec).
53
+
54
+ **Why it mattered:** the test was passing while the README silently
55
+ drifted. Now any example change in the README either stays correct or
56
+ fails CI.
57
+
58
+ ### P0-2 🟡 Auto-require `bigdecimal` and clean up `to_d`
59
+
60
+ **Status:** Partial — the dependency was added in commit `33b2c01`
61
+ (`s.add_dependency 'bigdecimal', '>= 4.0'` in `minting.gemspec`).
62
+ The runtime guard in `lib/minting/money/conversion.rb` (lines 10–14)
63
+ is now redundant:
64
+
65
+ ```ruby
66
+ def to_d
67
+ raise NoMethodError, 'decimal gem required' unless defined?(BigDecimal)
68
+ amount.to_d 0
69
+ end
70
+ ```
71
+
72
+ **Follow-up:** delete the `raise NoMethodError` line. It's dead code
73
+ once `bigdecimal` is a hard dependency. One-line PR.
74
+
75
+ ### P0-3 ✅ Fix CHANGELOG typos
76
+
77
+ **Status:** Done in commit `20359dc` — "is no private" →
78
+ "is now private" in the v1.3.0 entry. Watch for new typos as
79
+ items are added under `[Unreleased]`.
80
+
81
+ ### P0-4 Add a CI gate on the marketing claims
82
+
83
+ The README's "**2× faster**" / "**10×+ for formatting**" headline and
84
+ the table at the bottom (lines 213–247) are generated from a single
85
+ Qwen run on 2026-05-30 and aren't reproducible from CI.
86
+
87
+ **Concrete action:** in CI, run
88
+
89
+ ```sh
90
+ BENCH=true rake bench:regression
91
+ ```
92
+
93
+ and fail the build if any benchmark regresses by more than (e.g.)
94
+ 20% vs the stored baseline in `pkg/bench_baseline.json`. The baseline
95
+ file is optional but easy to maintain.
96
+
97
+ This makes the table *defensible* rather than aspirational.
98
+
99
+ ---
100
+
101
+ ## 🟠 P1 — Correctness, clarity, small ergonomics
102
+
103
+ ### P1-1 ✅ `Money.from_fractional(integer, currency)`
104
+
105
+ **Status:** Merged on branch `feature/money-from-fractional` (commit
106
+ `150a2d0`).
107
+
108
+ - Inverse of `#fractional`. Uses `Rational(n, fractional_multiplier)`
109
+ so subunit-0 currencies work without a special case.
110
+ - Integer-only contract — rejects `Float` / `String` / `Rational` with
111
+ `ArgumentError` to preserve the exactness guarantee.
112
+ - Accepts `String`, `Symbol`, or `Currency` for the currency arg,
113
+ matching `Mint.money` / `Money.create`.
114
+ - 4 new tests + 3 new README-spec asserts; 100% coverage maintained.
115
+
116
+ **To do** Add benchmark tests
117
+
118
+ ### P1-2 Add `Money#clamp(min, max)`, `Money#min(other)`, `Money#max(other)`
119
+
120
+ `Comparable` is already mixed in (`lib/minting/money/comparable.rb`),
121
+ so `min` and `max` work *implicitly* through the `<=>` machinery —
122
+ but only when both operands are the same currency. A first-class
123
+ `#clamp` would be a one-liner and very useful in domain code
124
+ (pricing floors/ceilings, exchange-rate bands, etc.).
125
+
126
+ ```ruby
127
+ # Sketch
128
+ def clamp(min, max)
129
+ raise ArgumentError, 'min/max must share currency' unless
130
+ same_currency?(min) && same_currency?(max)
131
+ return min if self < min
132
+ return max if self > max
133
+ self
134
+ end
135
+ ```
136
+
137
+ Add tests for: in-range, below-min, above-max, equal-to-bound,
138
+ non-matching-currency error.
139
+
140
+ ### P1-3 Improve `to_s` format-hash handling
141
+
142
+ `lib/minting/money/formatting.rb` accepts `format:` as either a
143
+ `String` or a `Hash` with `:positive` / `:negative` / `:zero` keys.
144
+ The current code:
145
+
146
+ - Falls through to `'%<symbol>s%<amount>f'` if `:positive` is missing
147
+ and the input is non-zero non-negative (line 58).
148
+ - Has no test coverage for the hash form at all.
149
+
150
+ **Action:** add explicit tests for:
151
+
152
+ - `{ positive: '...', negative: '...', zero: '...' }`
153
+ - `{ positive: '...' }` alone (default for neg/zero)
154
+ - Mixing hash and `decimal:` / `thousand:` kwargs
155
+
156
+ ### P1-4 Document and harden registry thread-safety
157
+
158
+ `lib/minting/mint/registry.rb` memoizes `@currencies`,
159
+ `@currency_symbols`, and `@zero` with `||=`. In a multi-threaded
160
+ server (Rails, Sidekiq, Puma) the first call to `Mint.money` from
161
+ two threads can:
162
+
163
+ - Both call `YAML.load_file` and `register` the same currencies.
164
+ - Both call `@currency_symbols ||= ...` and produce a non-frozen copy
165
+ that diverges.
166
+
167
+ **Action:** either
168
+
169
+ 1. Wrap the lazy initializers in `Mutex#synchronize` (3 places), or
170
+ 2. Switch to `Concurrent::Map` (no new dep), or
171
+ 3. Eager-load in a Railtie/loaded hook and treat the registry as
172
+ read-only after boot.
173
+
174
+ Option 3 is the most pragmatic. Document the contract.
175
+
176
+ ### P1-5 Make `Mint.currencies` immutable
177
+
178
+ `currencies` returns the live `@currencies` hash. A caller can
179
+ `currencies.delete('USD')` and break invariants. Either:
180
+
181
+ - Return a frozen copy (`@currencies.dup.freeze`), or
182
+ - Document loudly that the returned hash must be treated read-only.
183
+
184
+ ### P1-6 Improve `coercion.rb` — add `**` and a clean `==`
185
+
186
+ `lib/minting/money/coercion.rb` defines `CoercedNumber` (used by
187
+ `Money#coerce`) with `+`, `-`, `*`, `/`, `<=>`, but no `**` or `==`.
188
+
189
+ ```ruby
190
+ 2 ** 0.dollars #=> 1 (works)
191
+ 2 ** 5.dollars #=> NoMethodError (silent failure)
192
+ ```
193
+
194
+ `==` falls through to `Comparable`, which raises `ArgumentError`, but
195
+ a custom `==` on `CoercedNumber` would give a more specific error
196
+ message.
197
+
198
+ **Action:** add `**` and `==` to `CoercedNumber`. Tests in
199
+ `test/money/money_coercion_test.rb` (or wherever they live).
200
+
201
+ ### P1-7 Add `inspect` round-trip test
202
+
203
+ `Money#inspect` is implemented in `lib/minting/money/money.rb:79`
204
+ as `"[#{currency_code} %0.#{currency.subunit}f]"` — e.g.
205
+ `[USD 9.99]`. The existing parser (`lib/minting/money/parse.rb`)
206
+ can almost read this back, but a clean round-trip would be a nice
207
+ property test:
208
+
209
+ ```ruby
210
+ m = Mint.money(9.99, 'USD')
211
+ assert_equal m, Mint::Money.parse(m.inspect.delete_prefix('[').delete_suffix(']'))
212
+ ```
213
+
214
+ Make it a one-liner or a property-based test with 10–20 random
215
+ amounts. Cheap, catches regressions in either direction.
216
+
217
+ ---
218
+
219
+ ## 🟡 P2 — Roadmap items already advertised in README
220
+
221
+ ### P2-1 I18n / locale-aware formatting *[epic]*
222
+
223
+ Foundation is already there: `to_s(decimal: ',', thousand: '.')`
224
+ works. What's missing is locale-keyed defaults:
225
+
226
+ ```ruby
227
+ Mint.money(9.99, 'USD').to_s(locale: :'pt-BR') #=> "US$ 9,99"
228
+ Mint.money(9.99, 'USD').to_s(locale: :'fr') #=> "9,99 $US"
229
+ ```
230
+
231
+ **Action plan (multi-PR):**
232
+
233
+ 1. Add `Mint::Locale` value object + a small `locales.yaml` (or
234
+ build off `currencies.yaml` since most formatting info is per-
235
+ currency already).
236
+ 2. Add `I18n` integration behind a `require 'minting/i18n'` opt-in.
237
+ 3. Default to "no locale" so existing code is unaffected.
238
+
239
+ ### P2-2 Exchange rates *[epic]*
240
+
241
+ Mentioned in the README Roadmap. The hard part isn't the math, it's
242
+ the rate source. Suggested shape:
243
+
244
+ ```ruby
245
+ Mint.bank = Mint::Bank::MemoryStore.new(
246
+ 'USD' => { 'EUR' => 0.92, 'JPY' => 149.50 }
247
+ )
248
+ Mint.money(10, 'USD').exchange('EUR') #=> [EUR 9.20]
249
+ ```
250
+
251
+ Multi-PR epic:
252
+
253
+ 1. `Mint::Bank` interface with `#exchange(money, target)`.
254
+ 2. `Mint::Bank::MemoryStore` (in-memory, good for tests).
255
+ 3. `Mint::Bank::ECB` or `Mint::Bank::OpenExchangeRates` (network).
256
+ 4. Update `minting-rails` to expose bank config in the Railtie.
257
+
258
+ ### P2-3 Reek / RuboCop tightening
259
+
260
+ `.rubocop_todo.yml` still has 9 pre-existing offenses, all in
261
+ `test/performance/*` and `test/mint_benchmark.rb`. They're
262
+ "`Metrics::BlockLength`" / "`Metrics::MethodLength`" — fixable by
263
+ extracting helper methods or by scoping the cop to non-bench code.
264
+
265
+ ---
266
+
267
+ ## 🟢 P3 — Polish
268
+
269
+ ### P3-1 ✅ Move AI-generated docs to `doc/agents/`
270
+
271
+ Done in commit `ff08578`. Consider also moving `pkg/` artifacts to
272
+ a release script that builds on tag rather than committing pre-built
273
+ gems.
274
+
275
+ ### P3-2 `Gemfile.lock` policy
276
+
277
+ `Gemfile.lock` is currently committed. The project is a *gem*, not
278
+ an app, so the conventional choice is to *not* commit it (let
279
+ consumers' Bundler resolve). Or, if you prefer reproducibility for
280
+ CI, commit it and document the choice in `CONTRIBUTING.md`.
281
+
282
+ ### P3-3 Add `SECURITY.md` and `CODE_OF_CONDUCT.md`
283
+
284
+ Standard hygiene for a public Ruby gem. One PR each, copy-paste
285
+ from the GitHub templates.
286
+
287
+ ### P3-4 Document `Money#each_currency` / `Mint.currencies.each_value`
288
+
289
+ The README's "Features" list doesn't mention that you can iterate
290
+ registered currencies. Trivial doc addition; potential ergonomic
291
+ win for tooling authors.
292
+
293
+ ### P3-5 Decide on `pkg/` retention
294
+
295
+ `pkg/minting-*.gem` is in `.gitignore`, but old builds may still
296
+ sit there. Run `rake clobber_pkg` (if it exists) or just `rm -rf
297
+ pkg/*` once. Then add a `git clean -nd pkg` check to `Rakefile` if
298
+ you want belt-and-braces.
299
+
300
+ ---
301
+
302
+ ## Suggested next PR (after `from_fractional` merges)
303
+
304
+ **Title:** *Delete dead `to_d` guard, document `bigdecimal` as a hard dependency*
305
+
306
+ **Scope:**
307
+
308
+ - `lib/minting/money/conversion.rb`: remove
309
+ `raise NoMethodError, 'decimal gem required' unless defined?(BigDecimal)`.
310
+ - `CHANGELOG.md`: add a one-liner under `[Unreleased]` → `### Other`.
311
+ - `test/money/money_conversion_test.rb`: add a sanity test that
312
+ `to_d` returns a `BigDecimal` (already covered indirectly, but
313
+ make it explicit).
314
+
315
+ One-line source change, one-line test, one-line changelog. ~10 min.
316
+
317
+ ---
318
+
319
+ ## Metrics to watch going forward
320
+
321
+ | Metric | Current | Target |
322
+ |---|---|---|
323
+ | Line coverage | 100% (284/284) | ≥ 100% — don't regress |
324
+ | Test count | 80 runs, 342 assertions | Grow with each new method |
325
+ | `to_s` Hash format coverage | 0 tests | ≥ 6 (P1-3) |
326
+ | Bench variance vs baseline | unmeasured | ≤ 20% (P0-4) |
327
+ | Public API methods with YARD | ~all | 100% (already achieved) |
328
+ | Open `rubocop_todo` offenses | 9 in `test/performance/*` | 0 (P2-3) |
329
+ | `Gemfile.lock` policy | committed | documented (P3-2) |
330
+ | `to_d` runtime guard | present but dead | removed (suggested next PR) |
331
+
332
+ ---
333
+
334
+ *Last updated alongside PR #2 (`Money.from_fractional`). Refresh
335
+ after each release.*
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html >
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="utf-8">
6
+
7
+ <link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen">
8
+
9
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen">
10
+
11
+
12
+
13
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
14
+
15
+ <script type="text/javascript" charset="utf-8" src="js/full_list.js"></script>
16
+
17
+
18
+ <title>Class List</title>
19
+ <base id="base_target" target="_parent">
20
+ </head>
21
+ <body>
22
+ <div id="content">
23
+ <div class="fixed_header">
24
+ <h1 id="full_list_header">Class List</h1>
25
+ <div id="full_list_nav">
26
+
27
+ <span><a target="_self" href="class_list.html">
28
+ Classes
29
+ </a></span>
30
+
31
+ <span><a target="_self" href="method_list.html">
32
+ Methods
33
+ </a></span>
34
+
35
+ <span><a target="_self" href="file_list.html">
36
+ Files
37
+ </a></span>
38
+
39
+ </div>
40
+
41
+ <div id="search">
42
+ <label for="search-class">Search:</label>
43
+ <input id="search-class" type="text">
44
+ </div>
45
+ </div>
46
+
47
+ <ul id="full_list" class="class">
48
+ <li id="object_" class="odd"><div class="item" style="padding-left:30px"><span class='object_link'><a href="top-level-namespace.html" title="Top Level Namespace (root)">Top Level Namespace</a></span></div></li>
49
+ <li id='object_Mint' class='even'><div class='item' style='padding-left:30px'><a tabindex='0' class='toggle' role='button' aria-label='Mint child nodes' aria-expanded='false' aria-controls='object_Mint'></a> <span class='object_link'><a href="Mint.html" title="Mint (module)">Mint</a></span><small class='search_info'>Top Level Namespace</small></div><div aria-labelledby='object_Mint'><ul><li id='object_Mint::Currency' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/Currency.html" title="Mint::Currency (class)">Currency</a></span> &lt; Object<small class='search_info'>Mint</small></div></li><li id='object_Mint::Money' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/Money.html" title="Mint::Money (class)">Money</a></span> &lt; Object<small class='search_info'>Mint</small></div></li></ul></div></li><li id='object_Minting' class='odd'><div class='item' style='padding-left:30px'><span class='object_link'><a href="Minting.html" title="Minting (module)">Minting</a></span><small class='search_info'>Top Level Namespace</small></div></li>
50
+
51
+ </ul>
52
+ </div>
53
+ </body>
54
+ </html>
@@ -0,0 +1 @@
1
+ /* Override this file with custom rules */
@@ -0,0 +1,206 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif;
4
+ font-size: 13px;
5
+ height: 101%;
6
+ overflow-x: hidden;
7
+ background: #fafafa;
8
+ }
9
+
10
+ h1 {
11
+ padding: 12px 10px;
12
+ padding-bottom: 0;
13
+ margin: 0;
14
+ font-size: 1.4em;
15
+ }
16
+ .clear {
17
+ clear: both;
18
+ }
19
+ .fixed_header {
20
+ position: fixed;
21
+ background: #fff;
22
+ width: 100%;
23
+ padding-bottom: 10px;
24
+ margin-top: 0;
25
+ top: 0;
26
+ z-index: 9999;
27
+ height: 70px;
28
+ }
29
+ #search {
30
+ position: absolute;
31
+ right: 5px;
32
+ top: 9px;
33
+ padding-left: 24px;
34
+ }
35
+ #noresults {
36
+ padding: 7px 12px;
37
+ background: #fff;
38
+ }
39
+ #content.insearch #search,
40
+ #content.insearch #noresults {
41
+ background: url(data:image/gif;base64,R0lGODlhEAAQAPYAAP///wAAAPr6+pKSkoiIiO7u7sjIyNjY2J6engAAAI6OjsbGxjIyMlJSUuzs7KamppSUlPLy8oKCghwcHLKysqSkpJqamvT09Pj4+KioqM7OzkRERAwMDGBgYN7e3ujo6Ly8vCoqKjY2NkZGRtTU1MTExDw8PE5OTj4+PkhISNDQ0MrKylpaWrS0tOrq6nBwcKysrLi4uLq6ul5eXlxcXGJiYoaGhuDg4H5+fvz8/KKiohgYGCwsLFZWVgQEBFBQUMzMzDg4OFhYWBoaGvDw8NbW1pycnOLi4ubm5kBAQKqqqiQkJCAgIK6urnJyckpKSjQ0NGpqatLS0sDAwCYmJnx8fEJCQlRUVAoKCggICLCwsOTk5ExMTPb29ra2tmZmZmhoaNzc3KCgoBISEiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCAAAACwAAAAAEAAQAAAHaIAAgoMgIiYlg4kACxIaACEJCSiKggYMCRselwkpghGJBJEcFgsjJyoAGBmfggcNEx0flBiKDhQFlIoCCA+5lAORFb4AJIihCRbDxQAFChAXw9HSqb60iREZ1omqrIPdJCTe0SWI09GBACH5BAkIAAAALAAAAAAQABAAAAdrgACCgwc0NTeDiYozCQkvOTo9GTmDKy8aFy+NOBA7CTswgywJDTIuEjYFIY0JNYMtKTEFiRU8Pjwygy4ws4owPyCKwsMAJSTEgiQlgsbIAMrO0dKDGMTViREZ14kYGRGK38nHguHEJcvTyIEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDAggPg4iJAAMJCRUAJRIqiRGCBI0WQEEJJkWDERkYAAUKEBc4Po1GiKKJHkJDNEeKig4URLS0ICImJZAkuQAhjSi/wQyNKcGDCyMnk8u5rYrTgqDVghgZlYjcACTA1sslvtHRgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCQARAtOUoQRGRiFD0kJUYWZhUhKT1OLhR8wBaaFBzQ1NwAlkIszCQkvsbOHL7Y4q4IuEjaqq0ZQD5+GEEsJTDCMmIUhtgk1lo6QFUwJVDKLiYJNUd6/hoEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4uen4ICCA+IkIsDCQkVACWmhwSpFqAABQoQF6ALTkWFnYMrVlhWvIKTlSAiJiVVPqlGhJkhqShHV1lCW4cMqSkAR1ofiwsjJyqGgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCSMhREZGIYYGY2ElYebi56fhyWQniSKAKKfpaCLFlAPhl0gXYNGEwkhGYREUywag1wJwSkHNDU3D0kJYIMZQwk8MjPBLx9eXwuETVEyAC/BOKsuEjYFhoEAIfkECQgAAAAsAAAAABAAEAAAB2eAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4ueICImip6CIQkJKJ4kigynKaqKCyMnKqSEK05StgAGQRxPYZaENqccFgIID4KXmQBhXFkzDgOnFYLNgltaSAAEpxa7BQoQF4aBACH5BAkIAAAALAAAAAAQABAAAAdogACCg4SFggJiPUqCJSWGgkZjCUwZACQkgxGEXAmdT4UYGZqCGWQ+IjKGGIUwPzGPhAc0NTewhDOdL7Ykji+dOLuOLhI2BbaFETICx4MlQitdqoUsCQ2vhKGjglNfU0SWmILaj43M5oEAOwAAAAAAAAAAAA==)
42
+ no-repeat center left;
43
+ }
44
+ #full_list {
45
+ padding: 0;
46
+ list-style: none;
47
+ margin-left: 0;
48
+ margin-top: 80px;
49
+ font-size: 1.1em;
50
+ }
51
+ #full_list ul {
52
+ padding: 0;
53
+ }
54
+ #full_list li {
55
+ padding: 0;
56
+ margin: 0;
57
+ list-style: none;
58
+ }
59
+ #full_list li .item {
60
+ padding: 5px 5px 5px 12px;
61
+ }
62
+ #content.insearch #noresults {
63
+ margin-left: 7px;
64
+ }
65
+ #full_list li {
66
+ color: #666;
67
+ cursor: normal;
68
+ white-space: nowrap;
69
+ }
70
+ #full_list li.collapsed ul {
71
+ display: none;
72
+ }
73
+ #full_list li a.toggle {
74
+ cursor: default;
75
+ position: relative;
76
+ left: -5px;
77
+ top: 4px;
78
+ text-indent: -999px;
79
+ width: 10px;
80
+ height: 9px;
81
+ margin-left: -10px;
82
+ display: block;
83
+ float: left;
84
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTM5jWRgMAAAAVdEVYdENyZWF0aW9uIFRpbWUAMy8xNC8wOeNZPpQAAAE2SURBVDiNrZTBccIwEEXfelIAHUA6CZ24BGaWO+FuzZAK4k6gg5QAdGAq+Bxs2Yqx7BzyL7Llp/VfzZeQhCTc/ezuGzKKnKSzpCxXJM8fwNXda3df5RZETlIt6YUzSQDs93sl8w3wBZxCCE10GM1OcWbWjB2mWgEH4Mfdyxm3PSepBHibgQE2wLe7r4HjEidpnXMYdQPKEMJcsZ4zs2POYQOcaPfwMVOo58zsAdMt18BuoVDPxUJRacELbXv3hUIX2vYmOUvi8C8ydz/ThjXrqKqqLbDIAdsCKBd+Wo7GWa7o9qzOQHVVVXeAbs+yHHCH4aTsaCOQqunmUy1yBUAXkdMIfMlgF5EXLo2OpV/c/Up7jG4hhHcYLgWzAZXUc2b2ixsfvc/RmNNfOXD3Q/oeL9axJE1yT9IOoUu6MGUkAAAAAElFTkSuQmCC)
85
+ no-repeat bottom left;
86
+ }
87
+ #full_list li.collapsed a.toggle {
88
+ cursor: default;
89
+ background-position: top left;
90
+ }
91
+ #full_list li.deprecated {
92
+ text-decoration: line-through;
93
+ font-style: italic;
94
+ }
95
+ #full_list li.odd {
96
+ background: #f0f0f0;
97
+ }
98
+ #full_list li.even {
99
+ background: #fafafa;
100
+ }
101
+ #full_list .item:hover {
102
+ background: #ddd;
103
+ }
104
+ #full_list li small:before {
105
+ content: "(";
106
+ }
107
+ #full_list li small:after {
108
+ content: ")";
109
+ }
110
+ #full_list li small.search_info {
111
+ display: none;
112
+ }
113
+ a,
114
+ a:visited {
115
+ text-decoration: none;
116
+ color: #05a;
117
+ }
118
+ #full_list li.clicked > .item {
119
+ background: #05a;
120
+ color: #ccc;
121
+ }
122
+ #full_list li.clicked > .item a,
123
+ #full_list li.clicked > .item a:visited {
124
+ color: #eee;
125
+ }
126
+ #full_list li.clicked > .item a.toggle {
127
+ opacity: 0.5;
128
+ background-position: bottom right;
129
+ }
130
+ #full_list li.collapsed.clicked a.toggle {
131
+ background-position: top right;
132
+ }
133
+ #search input {
134
+ border: 1px solid #bbb;
135
+ border-radius: 3px;
136
+ }
137
+ #full_list_nav {
138
+ margin-left: 10px;
139
+ font-size: 0.9em;
140
+ display: block;
141
+ color: #aaa;
142
+ }
143
+ #full_list_nav a,
144
+ #nav a:visited {
145
+ color: #358;
146
+ }
147
+ #full_list_nav a:hover {
148
+ background: transparent;
149
+ color: #5af;
150
+ }
151
+ #full_list_nav span:after {
152
+ content: " | ";
153
+ }
154
+ #full_list_nav span:last-child:after {
155
+ content: "";
156
+ }
157
+
158
+ #content h1 {
159
+ margin-top: 0;
160
+ }
161
+ #full_list li small {
162
+ display: block;
163
+ font-size: 0.8em;
164
+ }
165
+ #full_list li small:before {
166
+ content: "";
167
+ }
168
+ #full_list li small:after {
169
+ content: "";
170
+ }
171
+ #full_list li small.search_info {
172
+ display: none;
173
+ }
174
+ #search {
175
+ width: 170px;
176
+ position: static;
177
+ margin: 3px;
178
+ margin-left: 10px;
179
+ font-size: 0.9em;
180
+ color: #666;
181
+ padding-left: 0;
182
+ padding-right: 24px;
183
+ }
184
+ #content.insearch #search {
185
+ background-position: center right;
186
+ }
187
+ #search input {
188
+ width: 110px;
189
+ }
190
+
191
+ #full_list.insearch ul {
192
+ display: block;
193
+ }
194
+ #full_list.insearch .item {
195
+ display: none;
196
+ }
197
+ #full_list.insearch .found {
198
+ display: block;
199
+ padding-left: 11px;
200
+ }
201
+ #full_list.insearch li a.toggle {
202
+ display: none;
203
+ }
204
+ #full_list.insearch li small.search_info {
205
+ display: block;
206
+ }