cssminify2 2.0.1 → 2.1.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 (95) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +10 -5
  3. data/CHANGELOG.md +121 -0
  4. data/Dockerfile +16 -0
  5. data/Gemfile +1 -1
  6. data/README.md +501 -42
  7. data/cssminify2.gemspec +6 -2
  8. data/docs/ADVANCED_USAGE.md +616 -0
  9. data/docs/API_REFERENCE.md +464 -0
  10. data/docs/EXAMPLES.md +844 -0
  11. data/docs/MIGRATION_GUIDE.md +586 -0
  12. data/lib/cssminify2/cssmin.rb +89 -7
  13. data/lib/cssminify2/cssmin_enhanced.rb +424 -0
  14. data/lib/cssminify2/enhanced.rb +818 -0
  15. data/lib/cssminify2/version.rb +1 -1
  16. data/lib/cssminify2.rb +53 -4
  17. data/spec/cssminify_spec.rb +49 -34
  18. data/spec/tests/README +6 -0
  19. data/spec/tests/_munge.js +8 -0
  20. data/spec/tests/_munge.js.min +1 -0
  21. data/spec/tests/_string_combo.js +5 -0
  22. data/spec/tests/_string_combo.js.min +1 -0
  23. data/spec/tests/_string_combo2.js +4 -0
  24. data/spec/tests/_string_combo2.js.min +1 -0
  25. data/spec/tests/_string_combo3.js +5 -0
  26. data/spec/tests/_string_combo3.js.min +1 -0
  27. data/spec/tests/_syntax_error.js +73 -0
  28. data/spec/tests/_syntax_error.js.min +1 -0
  29. data/spec/tests/border-none.css +6 -1
  30. data/spec/tests/border-none.css.min +1 -1
  31. data/spec/tests/bug-flex.css +3 -0
  32. data/spec/tests/bug-flex.css.min +1 -0
  33. data/spec/tests/bug-nested-pseudoclass.css +3 -0
  34. data/spec/tests/bug-nested-pseudoclass.css.min +1 -0
  35. data/spec/tests/bug-preservetoken-calc.css +8 -0
  36. data/spec/tests/bug-preservetoken-calc.css.min +1 -0
  37. data/spec/tests/color-keyword.css +1 -0
  38. data/spec/tests/color-keyword.css.min +1 -0
  39. data/spec/tests/color.css +2 -0
  40. data/spec/tests/color.css.min +1 -1
  41. data/spec/tests/concat-charset.css +2 -2
  42. data/spec/tests/concat-charset.css.min +1 -1
  43. data/spec/tests/dataurl-singlequote-font.css +1 -1
  44. data/spec/tests/dataurl-validity.html +29 -0
  45. data/spec/tests/float.js +2 -0
  46. data/spec/tests/float.js.min +1 -0
  47. data/spec/tests/hsla-issue81.css.FAIL +4 -0
  48. data/spec/tests/hsla-issue81.css.min +1 -0
  49. data/spec/tests/ie-backslash9-hack.css +2 -0
  50. data/spec/tests/ie-backslash9-hack.css.min +1 -0
  51. data/spec/tests/issue-59.css +7 -0
  52. data/spec/tests/issue-59.css.min +1 -0
  53. data/spec/tests/issue151.css +8 -0
  54. data/spec/tests/issue151.css.min +1 -0
  55. data/spec/tests/issue172.css.FAIL +4 -0
  56. data/spec/tests/issue172.css.min +1 -0
  57. data/spec/tests/issue180.css +16 -0
  58. data/spec/tests/issue180.css.min +1 -0
  59. data/spec/tests/issue205.css +2 -0
  60. data/spec/tests/issue205.css.min +1 -0
  61. data/spec/tests/issue221.css +1 -1
  62. data/spec/tests/issue221.css.min +1 -1
  63. data/spec/tests/issue222.css +2 -2
  64. data/spec/tests/issue222.css.min +1 -1
  65. data/spec/tests/issue71.js.FAIL +4 -0
  66. data/spec/tests/issue71.js.min +1 -0
  67. data/spec/tests/issue86.js +2 -0
  68. data/spec/tests/issue86.js.min +1 -0
  69. data/spec/tests/jquery-1.6.4.js +9046 -0
  70. data/spec/tests/jquery-1.6.4.js.min +23 -0
  71. data/spec/tests/lowercasing.css +63 -0
  72. data/spec/tests/lowercasing.css.min +1 -0
  73. data/spec/tests/media-test.css +2 -2
  74. data/spec/tests/old-ie-filter-matrix.css +8 -0
  75. data/spec/tests/old-ie-filter-matrix.css.min +1 -0
  76. data/spec/tests/opera-pixel-ratio.css +14 -0
  77. data/spec/tests/opera-pixel-ratio.css.min +1 -0
  78. data/spec/tests/pointzeros.css +6 -0
  79. data/spec/tests/pointzeros.css.min +1 -0
  80. data/spec/tests/preserve-important.css +1 -0
  81. data/spec/tests/preserve-important.css.min +1 -0
  82. data/spec/tests/promise-catch-finally-issue203.js +4 -0
  83. data/spec/tests/promise-catch-finally-issue203.js.min +1 -0
  84. data/spec/tests/pseudo-first.css +2 -2
  85. data/spec/tests/rgb-issue81.css.FAIL +4 -0
  86. data/spec/tests/rgb-issue81.css.min +1 -0
  87. data/spec/tests/suite.rhino +3 -0
  88. data/spec/tests/suite.sh +49 -0
  89. data/spec/tests/zeros.css +2 -2
  90. data/spec/tests/zeros.css.min +1 -1
  91. metadata +129 -14
  92. data/spec/tests/bug2528093.css +0 -3
  93. data/spec/tests/bug2528093.css.min +0 -1
  94. data/spec/tests/keyframe.css +0 -4
  95. data/spec/tests/keyframe.css.min +0 -1
@@ -0,0 +1,616 @@
1
+ # Advanced Usage Guide
2
+
3
+ This guide covers advanced usage patterns, optimization strategies, and best practices for CSSminify2 enhanced features.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Configuration Strategies](#configuration-strategies)
8
+ - [Optimization Patterns](#optimization-patterns)
9
+ - [Performance Tuning](#performance-tuning)
10
+ - [Integration Patterns](#integration-patterns)
11
+ - [Troubleshooting](#troubleshooting)
12
+ - [Best Practices](#best-practices)
13
+
14
+ ## Configuration Strategies
15
+
16
+ ### Environment-Based Configuration
17
+
18
+ ```ruby
19
+ # config/css_compression.rb
20
+ class CSSCompressionConfig
21
+ def self.for_environment(env = Rails.env)
22
+ case env.to_sym
23
+ when :development
24
+ {
25
+ optimize_shorthand_properties: true,
26
+ strict_error_handling: true # Catch issues early
27
+ }
28
+ when :test
29
+ {
30
+ optimize_shorthand_properties: true,
31
+ merge_duplicate_selectors: true,
32
+ strict_error_handling: true
33
+ }
34
+ when :production
35
+ {
36
+ merge_duplicate_selectors: true,
37
+ optimize_shorthand_properties: true,
38
+ compress_css_variables: true,
39
+ advanced_color_optimization: true,
40
+ strict_error_handling: false # Graceful fallbacks in production
41
+ }
42
+ else
43
+ {} # Conservative defaults
44
+ end
45
+ end
46
+ end
47
+
48
+ # Usage
49
+ config = CSSCompressionConfig.for_environment
50
+ result = CSSminify2.compress_enhanced(css, config)
51
+ ```
52
+
53
+ ### Project-Specific Configurations
54
+
55
+ ```ruby
56
+ # For CSS framework projects (Bootstrap, Tailwind, etc.)
57
+ FRAMEWORK_CONFIG = {
58
+ merge_duplicate_selectors: true, # Frameworks often have duplicates
59
+ optimize_shorthand_properties: true,
60
+ compress_css_variables: false, # Preserve framework variables
61
+ advanced_color_optimization: true,
62
+ strict_error_handling: false
63
+ }.freeze
64
+
65
+ # For single-page applications with CSS-in-JS
66
+ SPA_CONFIG = {
67
+ merge_duplicate_selectors: false, # CSS-in-JS usually unique
68
+ optimize_shorthand_properties: true,
69
+ compress_css_variables: true, # Often many utility variables
70
+ advanced_color_optimization: true,
71
+ strict_error_handling: true # Controlled environment
72
+ }.freeze
73
+
74
+ # For legacy projects with lots of technical debt
75
+ LEGACY_CONFIG = {
76
+ merge_duplicate_selectors: false, # May break cascade
77
+ optimize_shorthand_properties: true, # Safe optimization
78
+ compress_css_variables: false, # May have unusual patterns
79
+ advanced_color_optimization: false, # May break IE filters
80
+ strict_error_handling: false # Lots of malformed CSS
81
+ }.freeze
82
+ ```
83
+
84
+ ## Optimization Patterns
85
+
86
+ ### Selective Optimization by File Type
87
+
88
+ ```ruby
89
+ class SmartCSSCompressor
90
+ def self.compress_by_type(css, filename)
91
+ config = case File.extname(filename)
92
+ when '.variables.css', '.custom-props.css'
93
+ { compress_css_variables: true }
94
+ when '.grid.css', '.layout.css'
95
+ { optimize_shorthand_properties: true }
96
+ when '.components.css'
97
+ { merge_duplicate_selectors: true }
98
+ when '.utilities.css'
99
+ {
100
+ optimize_shorthand_properties: true,
101
+ compress_css_variables: true
102
+ }
103
+ else
104
+ CSSminify2Enhanced::Configuration.aggressive
105
+ end
106
+
107
+ CSSminify2.compress_enhanced(css, config)
108
+ end
109
+ end
110
+ ```
111
+
112
+ ### Progressive Optimization
113
+
114
+ ```ruby
115
+ class ProgressiveCSSOptimizer
116
+ def initialize
117
+ @stats = []
118
+ end
119
+
120
+ def optimize_progressively(css)
121
+ # Start conservative
122
+ result = css
123
+ config = {}
124
+
125
+ # Apply optimizations progressively, measuring impact
126
+ optimizations = [
127
+ { optimize_shorthand_properties: true },
128
+ { merge_duplicate_selectors: true },
129
+ { compress_css_variables: true },
130
+ { advanced_color_optimization: true }
131
+ ]
132
+
133
+ optimizations.each do |optimization|
134
+ config.merge!(optimization)
135
+
136
+ stats = CSSminify2.compress_with_stats(css, config)
137
+ @stats << {
138
+ optimization: optimization.keys.first,
139
+ ratio: stats[:statistics][:compression_ratio],
140
+ size: stats[:statistics][:compressed_size]
141
+ }
142
+
143
+ result = stats[:compressed_css]
144
+ end
145
+
146
+ { result: result, progression: @stats }
147
+ end
148
+ end
149
+ ```
150
+
151
+ ### Conditional Optimization
152
+
153
+ ```ruby
154
+ def smart_compress(css, options = {})
155
+ # Analyze CSS to determine best optimizations
156
+ analysis = analyze_css(css)
157
+
158
+ config = {}
159
+
160
+ # Enable selector merging only if we detect duplicates
161
+ if analysis[:duplicate_selectors] > 5
162
+ config[:merge_duplicate_selectors] = true
163
+ end
164
+
165
+ # Enable variable compression only if we have many variables
166
+ if analysis[:css_variables] > 10
167
+ config[:compress_css_variables] = true
168
+ end
169
+
170
+ # Always safe to enable
171
+ config[:optimize_shorthand_properties] = true
172
+ config[:advanced_color_optimization] = true
173
+
174
+ CSSminify2.compress_enhanced(css, config.merge(options))
175
+ end
176
+
177
+ def analyze_css(css)
178
+ {
179
+ duplicate_selectors: css.scan(/([^{]+)\{[^}]*\}/).flatten
180
+ .group_by(&:strip)
181
+ .count { |_, v| v.length > 1 },
182
+ css_variables: css.scan(/--[\w-]+/).uniq.count,
183
+ size: css.length
184
+ }
185
+ end
186
+ ```
187
+
188
+ ## Performance Tuning
189
+
190
+ ### Batch Processing
191
+
192
+ ```ruby
193
+ class BatchCSSProcessor
194
+ def initialize(config = {})
195
+ @compressor = CSSminify2Enhanced::Compressor.new(
196
+ CSSminify2Enhanced::Configuration.new.tap do |c|
197
+ config.each { |k, v| c.send("#{k}=", v) }
198
+ end
199
+ )
200
+ end
201
+
202
+ def process_files(file_patterns)
203
+ results = {}
204
+ total_savings = 0
205
+
206
+ Dir.glob(file_patterns).each do |file|
207
+ css = File.read(file)
208
+ result = @compressor.compress(css)
209
+
210
+ output_file = file.sub(/\.css$/, '.min.css')
211
+ File.write(output_file, result)
212
+
213
+ savings = css.length - result.length
214
+ total_savings += savings
215
+
216
+ results[file] = {
217
+ original_size: css.length,
218
+ compressed_size: result.length,
219
+ savings: savings,
220
+ ratio: (savings.to_f / css.length * 100).round(2)
221
+ }
222
+ end
223
+
224
+ results.merge(total_savings: total_savings)
225
+ end
226
+ end
227
+
228
+ # Usage
229
+ processor = BatchCSSProcessor.new({
230
+ merge_duplicate_selectors: true,
231
+ optimize_shorthand_properties: true
232
+ })
233
+
234
+ results = processor.process_files(['app/assets/stylesheets/**/*.css'])
235
+ puts "Total bytes saved: #{results[:total_savings]}"
236
+ ```
237
+
238
+ ### Memory-Efficient Processing
239
+
240
+ ```ruby
241
+ class StreamingCSSProcessor
242
+ def self.process_large_file(input_path, output_path, config = {})
243
+ # For very large CSS files, process in chunks if needed
244
+ css = File.read(input_path)
245
+
246
+ if css.length > 1_000_000 # 1MB threshold
247
+ # Process in sections for memory efficiency
248
+ sections = css.split(/(?<=\})/)
249
+ compressed_sections = []
250
+
251
+ sections.each_slice(100) do |section_batch|
252
+ batch_css = section_batch.join('')
253
+ compressed = CSSminify2.compress_enhanced(batch_css, config)
254
+ compressed_sections << compressed
255
+ end
256
+
257
+ result = compressed_sections.join('')
258
+ else
259
+ result = CSSminify2.compress_enhanced(css, config)
260
+ end
261
+
262
+ File.write(output_path, result)
263
+ end
264
+ end
265
+ ```
266
+
267
+ ## Integration Patterns
268
+
269
+ ### Rails Asset Pipeline Integration
270
+
271
+ ```ruby
272
+ # config/initializers/css_compression.rb
273
+ class EnhancedCSSCompressor
274
+ def initialize(options = {})
275
+ @options = {
276
+ merge_duplicate_selectors: true,
277
+ optimize_shorthand_properties: true,
278
+ compress_css_variables: Rails.env.production?,
279
+ advanced_color_optimization: true
280
+ }.merge(options)
281
+ end
282
+
283
+ def compress(css)
284
+ CSSminify2.compress_enhanced(css, @options)
285
+ rescue => e
286
+ Rails.logger.warn "CSS compression failed: #{e.message}"
287
+ # Fallback to basic compression
288
+ CSSminify2.compress(css)
289
+ end
290
+ end
291
+
292
+ # config/application.rb
293
+ config.assets.css_compressor = EnhancedCSSCompressor.new
294
+ ```
295
+
296
+ ### Webpack Integration via Ruby Bridge
297
+
298
+ ```ruby
299
+ # lib/webpack_css_compressor.rb
300
+ class WebpackCSSCompressor
301
+ def self.compress(css, options_json = '{}')
302
+ options = JSON.parse(options_json, symbolize_names: true)
303
+
304
+ stats = CSSminify2.compress_with_stats(css, options)
305
+
306
+ # Return JSON for JavaScript consumption
307
+ {
308
+ css: stats[:compressed_css],
309
+ stats: stats[:statistics]
310
+ }.to_json
311
+ rescue => e
312
+ {
313
+ css: css, # Return original on error
314
+ error: e.message,
315
+ stats: { fallback_used: true }
316
+ }.to_json
317
+ end
318
+ end
319
+ ```
320
+
321
+ ### Jekyll Plugin
322
+
323
+ ```ruby
324
+ # _plugins/enhanced_css_minifier.rb
325
+ module Jekyll
326
+ class EnhancedCSSMinifier < Jekyll::Generator
327
+ safe true
328
+ priority :low
329
+
330
+ def generate(site)
331
+ config = site.config['cssminify'] || {}
332
+ options = {
333
+ merge_duplicate_selectors: config['merge_selectors'],
334
+ optimize_shorthand_properties: config['optimize_shorthand'],
335
+ compress_css_variables: config['compress_variables']
336
+ }.compact
337
+
338
+ site.static_files.each do |file|
339
+ next unless file.extname == '.css'
340
+
341
+ css = File.read(file.path)
342
+ compressed = CSSminify2.compress_enhanced(css, options)
343
+ File.write(file.path, compressed)
344
+ end
345
+ end
346
+ end
347
+ end
348
+ ```
349
+
350
+ ### Gulp Integration
351
+
352
+ ```ruby
353
+ # tools/css_compressor.rb
354
+ #!/usr/bin/env ruby
355
+ require 'cssminify2'
356
+ require 'json'
357
+
358
+ # Read from STDIN, write to STDOUT for Gulp integration
359
+ input = STDIN.read
360
+ options = JSON.parse(ARGV[0] || '{}', symbolize_names: true)
361
+
362
+ begin
363
+ stats = CSSminify2.compress_with_stats(input, options)
364
+
365
+ result = {
366
+ css: stats[:compressed_css],
367
+ originalSize: stats[:statistics][:original_size],
368
+ compressedSize: stats[:statistics][:compressed_size],
369
+ ratio: stats[:statistics][:compression_ratio],
370
+ success: true
371
+ }
372
+
373
+ puts JSON.generate(result)
374
+ rescue => e
375
+ puts JSON.generate({
376
+ css: input,
377
+ error: e.message,
378
+ success: false
379
+ })
380
+ exit 1
381
+ end
382
+ ```
383
+
384
+ ## Troubleshooting
385
+
386
+ ### Debug Mode for Complex Issues
387
+
388
+ ```ruby
389
+ class CSSDebugger
390
+ def self.debug_compression(css, options = {})
391
+ puts "=== CSS COMPRESSION DEBUG ==="
392
+ puts "Original size: #{css.length} characters"
393
+ puts "Configuration: #{options.inspect}"
394
+ puts ""
395
+
396
+ # Test each optimization individually
397
+ optimizations = [
398
+ :merge_duplicate_selectors,
399
+ :optimize_shorthand_properties,
400
+ :compress_css_variables,
401
+ :advanced_color_optimization
402
+ ]
403
+
404
+ results = {}
405
+
406
+ optimizations.each do |opt|
407
+ test_config = { opt => true }
408
+
409
+ begin
410
+ stats = CSSminify2.compress_with_stats(css, test_config)
411
+ results[opt] = {
412
+ success: true,
413
+ size: stats[:statistics][:compressed_size],
414
+ ratio: stats[:statistics][:compression_ratio]
415
+ }
416
+ puts "✅ #{opt}: #{results[opt][:ratio].round(2)}% compression"
417
+ rescue => e
418
+ results[opt] = {
419
+ success: false,
420
+ error: e.message
421
+ }
422
+ puts "❌ #{opt}: FAILED - #{e.message}"
423
+ end
424
+ end
425
+
426
+ # Test full configuration
427
+ begin
428
+ full_stats = CSSminify2.compress_with_stats(css, options)
429
+ puts ""
430
+ puts "🚀 Full compression: #{full_stats[:statistics][:compression_ratio].round(2)}%"
431
+ puts "Final size: #{full_stats[:statistics][:compressed_size]} characters"
432
+
433
+ if full_stats[:statistics][:fallback_used]
434
+ puts "⚠️ Fallback compression was used"
435
+ end
436
+ rescue => e
437
+ puts "❌ Full compression failed: #{e.message}"
438
+ end
439
+
440
+ results
441
+ end
442
+ end
443
+
444
+ # Usage
445
+ CSSDebugger.debug_compression(problematic_css, {
446
+ merge_duplicate_selectors: true,
447
+ optimize_shorthand_properties: true
448
+ })
449
+ ```
450
+
451
+ ### Validation and Recovery
452
+
453
+ ```ruby
454
+ def safe_compress_with_validation(css, options = {})
455
+ # Pre-compression validation
456
+ validator = CSSValidator.new(css)
457
+ warnings = validator.validate
458
+
459
+ if warnings.any?
460
+ puts "⚠️ CSS warnings detected:"
461
+ warnings.each { |w| puts " - #{w}" }
462
+ end
463
+
464
+ # Attempt compression with error recovery
465
+ begin
466
+ # Try strict mode first
467
+ result = CSSminify2.compress_enhanced(css, options.merge(
468
+ strict_error_handling: true
469
+ ))
470
+
471
+ puts "✅ Strict compression successful"
472
+ result
473
+ rescue CSSminify2Enhanced::MalformedCSSError => e
474
+ puts "⚠️ CSS validation failed, trying non-strict mode"
475
+ puts "Errors: #{e.css_errors.join(', ')}"
476
+
477
+ # Fallback to non-strict
478
+ CSSminify2.compress_enhanced(css, options.merge(
479
+ strict_error_handling: false
480
+ ))
481
+ rescue => e
482
+ puts "❌ Enhanced compression failed: #{e.message}"
483
+ puts "Using basic compression"
484
+
485
+ # Ultimate fallback
486
+ CSSminify2.compress(css)
487
+ end
488
+ end
489
+
490
+ class CSSValidator
491
+ def initialize(css)
492
+ @css = css
493
+ end
494
+
495
+ def validate
496
+ warnings = []
497
+
498
+ # Check for potential issues
499
+ warnings << "Many duplicate selectors detected" if duplicate_selectors > 20
500
+ warnings << "Very large file (>500KB)" if @css.length > 500_000
501
+ warnings << "Many CSS variables (>50)" if css_variables > 50
502
+ warnings << "Potentially malformed CSS" if syntax_issues?
503
+
504
+ warnings
505
+ end
506
+
507
+ private
508
+
509
+ def duplicate_selectors
510
+ @css.scan(/([^{]+)\{/).flatten.group_by(&:strip).count { |_, v| v.length > 1 }
511
+ end
512
+
513
+ def css_variables
514
+ @css.scan(/--[\w-]+/).uniq.count
515
+ end
516
+
517
+ def syntax_issues?
518
+ @css.count('{') != @css.count('}') ||
519
+ @css.scan(/"/).length.odd? ||
520
+ @css.scan(/'/).length.odd?
521
+ end
522
+ end
523
+ ```
524
+
525
+ ## Best Practices
526
+
527
+ ### 1. **Start Conservative, Scale Up**
528
+ ```ruby
529
+ # Begin with safe optimizations
530
+ initial_config = {
531
+ optimize_shorthand_properties: true
532
+ }
533
+
534
+ # Add features as you gain confidence
535
+ full_config = {
536
+ merge_duplicate_selectors: true,
537
+ optimize_shorthand_properties: true,
538
+ compress_css_variables: true,
539
+ advanced_color_optimization: true
540
+ }
541
+ ```
542
+
543
+ ### 2. **Use Statistics for Optimization**
544
+ ```ruby
545
+ def optimize_build_process(css_files)
546
+ css_files.each do |file|
547
+ css = File.read(file)
548
+
549
+ stats = CSSminify2.compress_with_stats(css, full_config)
550
+
551
+ # Only use advanced features if they provide significant benefit
552
+ if stats[:statistics][:compression_ratio] < 30
553
+ puts "⚠️ #{file}: Low compression ratio, consider reviewing CSS structure"
554
+ end
555
+
556
+ if stats[:statistics][:fallback_used]
557
+ puts "🚨 #{file}: Fallback used, may need manual review"
558
+ end
559
+ end
560
+ end
561
+ ```
562
+
563
+ ### 3. **Environment-Specific Configurations**
564
+ - **Development**: Enable `strict_error_handling` to catch issues early
565
+ - **Testing**: Use aggressive optimization to test edge cases
566
+ - **Production**: Use balanced configuration with graceful fallbacks
567
+
568
+ ### 4. **Monitor Compression Performance**
569
+ ```ruby
570
+ class CompressionMonitor
571
+ def self.monitor(css, options = {})
572
+ start_time = Time.now
573
+ stats = CSSminify2.compress_with_stats(css, options)
574
+ end_time = Time.now
575
+
576
+ {
577
+ result: stats[:compressed_css],
578
+ performance: {
579
+ compression_time: (end_time - start_time) * 1000, # ms
580
+ original_size: stats[:statistics][:original_size],
581
+ compressed_size: stats[:statistics][:compressed_size],
582
+ compression_ratio: stats[:statistics][:compression_ratio],
583
+ compression_speed: stats[:statistics][:original_size] / (end_time - start_time) # bytes/sec
584
+ }
585
+ }
586
+ end
587
+ end
588
+ ```
589
+
590
+ ### 5. **Testing Strategy**
591
+ ```ruby
592
+ # Test your CSS compression in your test suite
593
+ RSpec.describe 'CSS Compression' do
594
+ it 'compresses CSS without breaking functionality' do
595
+ original_css = File.read('app/assets/stylesheets/application.css')
596
+
597
+ compressed = CSSminify2.compress_enhanced(original_css, production_config)
598
+
599
+ expect(compressed.length).to be < original_css.length
600
+ expect(compressed).to include('.main-header') # Key selectors preserved
601
+ expect(compressed).not_to include('/* comments */') # Comments removed
602
+ end
603
+
604
+ it 'handles malformed CSS gracefully' do
605
+ malformed_css = '.test { color: red .broken { }'
606
+
607
+ expect {
608
+ CSSminify2.compress_enhanced(malformed_css, {
609
+ strict_error_handling: false
610
+ })
611
+ }.not_to raise_error
612
+ end
613
+ end
614
+ ```
615
+
616
+ This advanced usage guide covers the most common patterns and strategies for getting the most out of CSSminify2's enhanced features. Remember to always test thoroughly when adopting new optimization settings!