cataract 0.2.3 → 0.2.5

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
2
  SHA256:
3
- metadata.gz: a5945042f40d16bf57bcc0c5479221959e231eb2742c51019f907d1d3ab1110c
4
- data.tar.gz: 92734f20818a3c0e94b8f7adaccf5e6a37d65325223f0dbbafae2efc8ddc11d8
3
+ metadata.gz: 898883ee4a30ccd34c034b4799e7a43eccae7f79693a4309d7ac7ef130f21840
4
+ data.tar.gz: 511eaf6d4a9fd1f1a601a999f496bc3493900daa2696d20bc1a6b97eaff60eae
5
5
  SHA512:
6
- metadata.gz: b1c8dc3e693f042549bdaf2561599c76cf2f5940809f3c5005b17ee90a04cab9a99f35598f55f9d422a3f00ab04d493c76a0f291ce636711c9dabcb2879aceb2
7
- data.tar.gz: 6d239fd54239b01ec42215296964d0140e40ae1841eae2d4af12e0982d5478ad2ee49c5f6628f067ef5168bef5845f07780e63fc6e6be4875c06c21f8f0afb20
6
+ metadata.gz: 59f87ff83a95fecb4e508a71e3055d8ebc53c23ad2d1316358e4a9e53ff8532c66b5686611e1706d6d95f1309428eda2e2df3b1884cbcb8d318dc866a256920b
7
+ data.tar.gz: 85eb22a3be35e25d7cfd01fcbb2bd9148411e3cb4ee49ccfd808f57ec17a6a8c95c1dc56b29cb563cc9cc7523fbacfc4083a5953dcf3010c6cb14c872a48872c
data/.rubocop.yml CHANGED
@@ -103,10 +103,12 @@ Naming/PredicatePrefix:
103
103
  # These cosmetic style rules add overhead via method dispatch or allocation
104
104
 
105
105
  # Disable numeric predicates in hot path - method dispatch overhead
106
- # Benchmark shows `> 0` is faster than `.positive?` due to avoiding method call
106
+ # Benchmark shows `length > 0` is 30% faster than `.positive?` (method dispatch overhead)
107
+ # `!empty?` has no perf penalty vs `length > 0` but `.positive?` is significantly slower
107
108
  Style/NumericPredicate:
108
109
  Exclude:
109
110
  - 'lib/cataract/pure/**/*.rb'
111
+ - 'lib/cataract/stylesheet.rb'
110
112
 
111
113
  # Disable multiple comparison style - avoid array allocation
112
114
  # `||` chain is faster than `Array#include?` which allocates an array
@@ -114,11 +116,13 @@ Style/MultipleComparison:
114
116
  Exclude:
115
117
  - 'lib/cataract/pure/**/*.rb'
116
118
 
117
- # Disable zero-length predicate in hot path - method dispatch overhead
118
- # Direct comparison `.length > 0` is faster than `.empty?` method call
119
+ # Disable zero-length predicate - prefer `length > 0` for consistency with NumericPredicate
120
+ # While `!empty?` has no perf penalty vs `length > 0`, we use `length > 0` consistently
121
+ # to avoid mixing styles (since `.positive?` is 30% slower and must be avoided)
119
122
  Style/ZeroLengthPredicate:
120
123
  Exclude:
121
124
  - 'lib/cataract/pure/**/*.rb'
125
+ - 'lib/cataract/stylesheet.rb'
122
126
 
123
127
  # Benchmarked range vs offsets. Range has to allocated
124
128
  # so not ideal in the hot path
@@ -174,4 +178,5 @@ Cataract/BanAssertIncludes:
174
178
  - 'test/color/**/*.rb'
175
179
  - 'test/test_benchmark_doc_generator.rb'
176
180
  - 'test/test_speedup_calculator.rb'
181
+ - 'test/test_parse_errors.rb' # Parse error tests check error messages with assert_match
177
182
  - 'test/support/**/*' # Support files define assert_contains which uses assert_includes internally
data/BENCHMARKS.md CHANGED
@@ -20,17 +20,35 @@ Time to parse CSS into internal data structures
20
20
 
21
21
  | Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
22
22
  |-----------|--------|----------------|-------------|
23
- | Small CSS (64 lines, 1.0KB) | 36.46K i/s | 3.46K i/s | 14.1K i/s |
24
- | Medium CSS with @media (139 lines, 1.6KB) | 37.11K i/s | 2.1K i/s | 9.26K i/s |
25
- | Selector lists (3500 lines, 62.5KB, 500 lists) | 447.6 i/s | 57.9 i/s | 212.5 i/s |
23
+ | Small CSS (64 lines, 1.0KB) | 37.27K i/s | 3.32K i/s | 13.48K i/s |
24
+ | Medium CSS with @media (139 lines, 1.6KB) | 34.66K i/s | 2.06K i/s | 8.94K i/s |
25
+ | Selector lists (3500 lines, 62.5KB, 500 lists) | 485.7 i/s | 55.7 i/s | 213.6 i/s |
26
26
 
27
27
  ### Speedups
28
28
 
29
29
  | Comparison | Speedup |
30
30
  |------------|---------|
31
- | Native vs Pure (no YJIT) | 12.14x faster (avg) |
32
- | Native vs Pure (YJIT) | 2.89x faster (avg) |
33
- | YJIT impact on Pure Ruby | 4.14x faster (avg) |
31
+ | Native vs Pure (no YJIT) | 14.4x faster (avg) |
32
+ | Native vs Pure (YJIT) | 3.38x faster (avg) |
33
+ | YJIT impact on Pure Ruby | 4.23x faster (avg) |
34
+
35
+ ### Parse Error Checking Overhead
36
+
37
+ Parse error detection can be enabled with `raise_parse_errors: true`. This compares performance impact:
38
+
39
+ | Configuration | Native | Pure (no YJIT) | Pure (YJIT) |
40
+ |---------------|--------|----------------|-------------|
41
+ | Medium CSS (139 lines) - no error checking | 35.18K i/s | 2.07K i/s | 9.05K i/s |
42
+ | Medium CSS (139 lines) - with error checking | 34.84K i/s | 1.9K i/s | 8.19K i/s |
43
+
44
+ **Overhead Analysis:**
45
+
46
+
47
+ | Implementation | Overhead |
48
+ |----------------|----------|
49
+ | Native | ~0% (within noise) |
50
+ | Pure (no YJIT) | 8.9% slower |
51
+ | Pure (YJIT) | 10.4% slower |
34
52
 
35
53
  ---
36
54
 
@@ -40,19 +58,19 @@ Time to convert parsed CSS back to string format
40
58
 
41
59
  | Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
42
60
  |-----------|--------|----------------|-------------|
43
- | to_s (Bootstrap - 191KB) | 1.16K i/s | 34.9 i/s | 49.7 i/s |
44
- | to_s (Compact utilities - 2.9KB) | 1.16K i/s | 34.9 i/s | 49.7 i/s |
45
- | to_formatted_s (Nested CSS - 1.3KB) | 158.75K i/s | 63.62K i/s | 114.79K i/s |
46
- | to_s with selector_lists (3.4KB) | 17.95K i/s | 1.43K i/s | 1.99K i/s |
47
- | Media filtering (Bootstrap print only) | 235.05K i/s | 98.95K i/s | 149.3K i/s |
61
+ | to_s (Bootstrap - 191KB) | 1.32K i/s | 360.2 i/s | 656.6 i/s |
62
+ | to_s (Compact utilities - 2.9KB) | 1.32K i/s | 360.2 i/s | 656.6 i/s |
63
+ | to_formatted_s (Nested CSS - 1.3KB) | 176.13K i/s | 60.97K i/s | 121.5K i/s |
64
+ | to_s with selector_lists (3.4KB) | 19.03K i/s | 8.01K i/s | 14.2K i/s |
65
+ | Media filtering (Bootstrap print only) | 283.24K i/s | 97.28K i/s | 161.56K i/s |
48
66
 
49
67
  ### Speedups
50
68
 
51
69
  | Comparison | Speedup |
52
70
  |------------|---------|
53
- | Native vs Pure (no YJIT) | 10.97x faster (avg) |
54
- | Native vs Pure (YJIT) | 7.56x faster (avg) |
55
- | YJIT impact on Pure Ruby | 1.57x faster (avg) |
71
+ | Native vs Pure (no YJIT) | 3.24x faster (avg) |
72
+ | Native vs Pure (YJIT) | 1.73x faster (avg) |
73
+ | YJIT impact on Pure Ruby | 1.86x faster (avg) |
56
74
 
57
75
  ---
58
76
 
@@ -62,20 +80,20 @@ Time to calculate CSS selector specificity values
62
80
 
63
81
  | Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
64
82
  |-----------|--------|----------------|-------------|
65
- | Simple Selectors | 8.42M i/s | 508.29K i/s | 2.6M i/s |
66
- | Compound Selectors | 6.6M i/s | 186.16K i/s | 400.7K i/s |
67
- | Combinators | 5.21M i/s | 147.76K i/s | 255.26K i/s |
68
- | Pseudo-classes & Pseudo-elements | 5.39M i/s | 117.25K i/s | 196.91K i/s |
69
- | :not() Pseudo-class (CSS3) | 3.48M i/s | 103.11K i/s | 164.66K i/s |
70
- | Complex Real-world Selectors | 4.05M i/s | 52.17K i/s | 78.28K i/s |
83
+ | Simple Selectors | 7.28M i/s | 506.61K i/s | 2.45M i/s |
84
+ | Compound Selectors | 6.4M i/s | 186.62K i/s | 405.15K i/s |
85
+ | Combinators | 5.04M i/s | 147.07K i/s | 255.92K i/s |
86
+ | Pseudo-classes & Pseudo-elements | 5.17M i/s | 117.16K i/s | 195.12K i/s |
87
+ | :not() Pseudo-class (CSS3) | 3.48M i/s | 103.47K i/s | 161.34K i/s |
88
+ | Complex Real-world Selectors | 4.01M i/s | 52.41K i/s | 77.48K i/s |
71
89
 
72
90
  ### Speedups
73
91
 
74
92
  | Comparison | Speedup |
75
93
  |------------|---------|
76
- | Native vs Pure (no YJIT) | 40.75x faster (avg) |
77
- | Native vs Pure (YJIT) | 23.38x faster (avg) |
78
- | YJIT impact on Pure Ruby | 2.29x faster (avg) |
94
+ | Native vs Pure (no YJIT) | 39.54x faster (avg) |
95
+ | Native vs Pure (YJIT) | 23.05x faster (avg) |
96
+ | YJIT impact on Pure Ruby | 2.24x faster (avg) |
79
97
 
80
98
  ---
81
99
 
@@ -85,20 +103,20 @@ Time to flatten multiple CSS rule sets with same selector
85
103
 
86
104
  | Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
87
105
  |-----------|--------|----------------|-------------|
88
- | No shorthand properties (large) | 20.74K i/s | 3.15K i/s | 5.73K i/s |
89
- | Simple properties | 155.01K i/s | 73.1K i/s | 99.95K i/s |
90
- | Cascade with specificity | 194.63K i/s | 74.22K i/s | 104.8K i/s |
91
- | Important declarations | 195.45K i/s | 72.82K i/s | 105.65K i/s |
92
- | Shorthand expansion | 20.74K i/s | 3.15K i/s | 5.73K i/s |
93
- | Complex flattening | 31.19K i/s | 15.81K i/s | 21.52K i/s |
106
+ | No shorthand properties (large) | 13.91K i/s | 3.23K i/s | 6.39K i/s |
107
+ | Simple properties | 144.19K i/s | 71.11K i/s | 101.38K i/s |
108
+ | Cascade with specificity | 172.96K i/s | 69.16K i/s | 99.23K i/s |
109
+ | Important declarations | 173.35K i/s | 69.12K i/s | 100.16K i/s |
110
+ | Shorthand expansion | 13.91K i/s | 3.23K i/s | 6.39K i/s |
111
+ | Complex flattening | 29.86K i/s | 15.77K i/s | 22.09K i/s |
94
112
 
95
113
  ### Speedups
96
114
 
97
115
  | Comparison | Speedup |
98
116
  |------------|---------|
99
- | Native vs Pure (no YJIT) | 3.01x faster (avg) |
100
- | Native vs Pure (YJIT) | 1.97x faster (avg) |
101
- | YJIT impact on Pure Ruby | 1.47x faster (avg) |
117
+ | Native vs Pure (no YJIT) | 2.54x faster (avg) |
118
+ | Native vs Pure (YJIT) | 1.63x faster (avg) |
119
+ | YJIT impact on Pure Ruby | 1.53x faster (avg) |
102
120
 
103
121
  ---
104
122
 
data/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
- ## [ Unreleased ]
1
+ ## [Unreleased]
2
+
3
+ ## [0.2.5 - 2025-11-25]
4
+
5
+ - Feature: Parse error detection with `raise_parse_errors` option - validates CSS structure and raises `ParseError` exceptions for malformed input with line/column tracking
6
+ - Feature: Granular error control - enable specific checks (empty values, malformed declarations, invalid selectors, invalid selector syntax, malformed at-rules, unclosed blocks)
7
+ - Feature: Type safety validation for C extension - `Stylesheet.parse` and `Stylesheet.new` now validate argument types and raise clear `TypeError` instead of segfaulting
8
+ - Feature: Selector syntax validation using whitelist approach - catches invalid characters and sequences like `..class`, `##id`, `???`
9
+ - Fix: `add_block` with multiple `@import` statements now correctly tracks media type for each import instead of reusing the first import's media context
10
+ - Performance: Parse error checking adds minimal overhead (effectively zero for C/Pure Ruby, ~5% for Pure Ruby with YJIT)
11
+ - Testing: Fuzzer corpus enhanced with invalid CSS patterns for crash testing
12
+
13
+ ## [0.2.4 - 2025-11-23]
14
+ - MediaQuery first-class objects: Refactored media queries from simple symbols to proper structs with id, type, and conditions, enabling accurate
15
+ serialization and proper handling of complex queries like @media screen and (min-width: 768px)
16
+ - Fixed import resolution: Import statements now properly merge selector lists and media query lists from imported stylesheets with correct ID offsetting,
17
+ preventing data loss
18
+ - Sequential rule ID invariant: Parser now ensures rules[i].id == i via placeholder strategy, enabling O(1) array access instead of O(N) lookups during
19
+ serialization
20
+ - Improved nested media handling: Nested media queries in imports now combine correctly (e.g., @import "file.css" screen where file contains @media
21
+ (min-width: 768px))
2
22
 
3
23
  ## [0.2.3 - 2025-11-18]
4
24
  - Pure Parser: Bugs with url()
data/Gemfile CHANGED
@@ -10,13 +10,16 @@ gem 'rake', '~> 13.0'
10
10
  gem 'rake-compiler', '~> 1.0'
11
11
 
12
12
  # Development/benchmarking dependencies (not needed by gem users)
13
+ gem 'addressable', '~> 2.8' # for custom URI resolver testing
13
14
  gem 'benchmark-ips', '~> 2.0'
14
15
  gem 'css_parser', '~> 1.0' # for benchmarking against
15
16
  gem 'minitest'
16
17
  gem 'minitest-spec'
17
18
  gem 'nokogiri' # for docs
19
+ gem 'ruby-prof', require: false # for profiling
18
20
  gem 'simplecov', require: false
19
21
  gem 'simplecov-cobertura', require: false
22
+ gem 'stackprof', require: false # for profiling
20
23
  gem 'webmock', '~> 3.0' # for testing URL loading
21
24
 
22
25
  gem 'overcommit', '~> 0.64', group: :development