cataract 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cataract
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
data/lib/cataract.rb CHANGED
@@ -1,34 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'cataract/version'
4
- require_relative 'cataract/error'
5
- require_relative 'cataract/constants'
6
-
7
- # Load struct definitions first (before C extension or pure Ruby)
8
- require_relative 'cataract/declaration'
9
- require_relative 'cataract/rule'
10
- require_relative 'cataract/at_rule'
11
- require_relative 'cataract/media_query'
12
- require_relative 'cataract/import_statement'
13
-
14
- # Load pure Ruby or C extension based on ENV var
15
- if %w[1 true].include?(ENV.fetch('CATARACT_PURE', nil)) || RUBY_ENGINE == 'jruby'
16
- require_relative 'cataract/pure'
17
- else
18
- require_relative 'cataract/native_extension'
19
- end
20
-
21
- # Load supporting Ruby files (used by both implementations)
22
- require_relative 'cataract/stylesheet_scope'
23
- require_relative 'cataract/stylesheet'
24
- require_relative 'cataract/declarations'
25
- require_relative 'cataract/import_resolver'
26
-
27
3
  # Cataract is a high-performance CSS parser written in C with a Ruby interface.
28
4
  #
29
5
  # It provides fast CSS parsing, rule querying, cascade merging, and serialization.
30
6
  # Designed for performance-critical applications that need to process large amounts of CSS.
31
7
  #
8
+ # Pure Ruby and native each live entirely under their own Backends::Pure /
9
+ # Backends::Native namespace (no shared method/constant is ever defined by
10
+ # a backend directly on Cataract or on a shared value type), so both can be
11
+ # required in the same process - e.g. to compare their output directly.
12
+ # Backends.active is the one this process picked by default (below); it's
13
+ # what shared value types (Stylesheet, Declarations, Rule) fall back to
14
+ # when they aren't otherwise told which backend produced them.
15
+ #
32
16
  # @example Basic usage
33
17
  # require 'cataract'
34
18
  #
@@ -44,6 +28,33 @@ require_relative 'cataract/import_resolver'
44
28
  # @see Stylesheet Main class for working with parsed CSS
45
29
  # @see Rule Represents individual CSS rules
46
30
  module Cataract
31
+ module Backends
32
+ class << self
33
+ attr_accessor :active
34
+ end
35
+ end
36
+ end
37
+
38
+ if %w[1 true].include?(ENV.fetch('CATARACT_PURE', nil)) || RUBY_ENGINE == 'jruby'
39
+ require_relative 'cataract/pure'
40
+ Cataract::Backends.active = Cataract::Backends::Pure
41
+ else
42
+ require_relative 'cataract/native'
43
+ Cataract::Backends.active = Cataract::Backends::Native
44
+ end
45
+
46
+ module Cataract
47
+ # Mirror the active backend's identity constants at the top level.
48
+ #
49
+ # Native is a Module (IMPLEMENTATION/COMPILE_FLAGS live on it directly);
50
+ # Pure is a frozen instance (they live on its class instead), so reach
51
+ # through .class only when active isn't already a Module itself.
52
+ backend_const_holder = Backends.active.is_a?(Module) ? Backends.active : Backends.active.class
53
+ IMPLEMENTATION = backend_const_holder::IMPLEMENTATION
54
+ COMPILE_FLAGS = backend_const_holder::COMPILE_FLAGS
55
+ NATIVE_EXTENSION_LOADED = true if defined?(Backends::Native) && Backends.active == Backends::Native
56
+ PURE_RUBY_LOADED = true if defined?(Backends::Pure) && Backends.active == Backends::Pure
57
+
47
58
  class << self
48
59
  # Parse a CSS string into a Stylesheet object.
49
60
  #
@@ -75,10 +86,8 @@ module Cataract
75
86
  #
76
87
  # @see Stylesheet#parse
77
88
  # @see Stylesheet.parse
78
- unless method_defined?(:parse_css)
79
- def parse_css(css, **options)
80
- Stylesheet.parse(css, **options)
81
- end
89
+ def parse_css(css, **options)
90
+ Stylesheet.parse(css, **options)
82
91
  end
83
92
 
84
93
  # Flatten CSS rules according to CSS cascade rules.
@@ -113,9 +122,11 @@ module Cataract
113
122
  #
114
123
  # @note This is a module-level convenience method. The same functionality is available
115
124
  # as an instance method: `stylesheet.flatten`
116
- # @note Implemented in C (see ext/cataract/flatten.c)
117
125
  #
118
126
  # @see Stylesheet#flatten
119
- # Cataract.flatten is defined in C via rb_define_module_function
127
+ def flatten(stylesheet_or_css)
128
+ stylesheet_or_css = Stylesheet.parse(stylesheet_or_css) if stylesheet_or_css.is_a?(String)
129
+ stylesheet_or_css.flatten
130
+ end
120
131
  end
121
132
  end
@@ -35,7 +35,7 @@ namespace :profile do
35
35
  puts "CSS size: #{css_content.bytesize} bytes"
36
36
  puts
37
37
 
38
- require_relative '../../lib/cataract/pure'
38
+ require_relative '../../lib/cataract'
39
39
  require 'json'
40
40
 
41
41
  # Use higher sampling rate (interval in microseconds, default is 1000)
@@ -74,6 +74,9 @@ namespace :profile do
74
74
  abort('stackprof gem not found. Install with: gem install stackprof')
75
75
  end
76
76
 
77
+ # Ensure we're using pure Ruby implementation
78
+ ENV['CATARACT_PURE'] = '1'
79
+
77
80
  fixture_path = File.expand_path('../../test/fixtures/bootstrap.css', __dir__)
78
81
  unless File.exist?(fixture_path)
79
82
  abort("Fixture not found: #{fixture_path}")
@@ -94,7 +97,7 @@ namespace :profile do
94
97
  puts "CSS size: #{css_content.bytesize} bytes"
95
98
  puts
96
99
 
97
- require_relative '../../lib/cataract/pure'
100
+ require_relative '../../lib/cataract'
98
101
  require 'json'
99
102
 
100
103
  # Parse once outside profiling to get stylesheet
@@ -162,7 +165,7 @@ namespace :profile do
162
165
  puts "CSS size: #{css_content.bytesize} bytes"
163
166
  puts
164
167
 
165
- require_relative '../../lib/cataract/pure'
168
+ require_relative '../../lib/cataract'
166
169
  require 'json'
167
170
 
168
171
  # Parse once outside profiling to get stylesheet
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cataract
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Cook
@@ -76,10 +76,11 @@ files:
76
76
  - lib/cataract/import_resolver.rb
77
77
  - lib/cataract/import_statement.rb
78
78
  - lib/cataract/media_query.rb
79
+ - lib/cataract/native.rb
79
80
  - lib/cataract/pure.rb
80
81
  - lib/cataract/pure/byte_constants.rb
82
+ - lib/cataract/pure/declarations.rb
81
83
  - lib/cataract/pure/flatten.rb
82
- - lib/cataract/pure/helpers.rb
83
84
  - lib/cataract/pure/parser.rb
84
85
  - lib/cataract/pure/serializer.rb
85
86
  - lib/cataract/pure/specificity.rb
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Pure Ruby CSS parser - Helper methods
4
- # NO REGEXP ALLOWED - char-by-char parsing only
5
-
6
- module Cataract
7
- # Check if a byte is whitespace (space, tab, newline, CR)
8
- # @param byte [Integer] Byte value from String#getbyte
9
- # @return [Boolean] true if whitespace
10
- def self.is_whitespace?(byte)
11
- byte == BYTE_SPACE || byte == BYTE_TAB || byte == BYTE_NEWLINE || byte == BYTE_CR
12
- end
13
-
14
- # Check if byte is a letter (a-z, A-Z)
15
- # @param byte [Integer] Byte value from String#getbyte
16
- # @return [Boolean] true if letter
17
- def self.letter?(byte)
18
- (byte >= BYTE_LOWER_A && byte <= BYTE_LOWER_Z) ||
19
- (byte >= BYTE_UPPER_A && byte <= BYTE_UPPER_Z)
20
- end
21
-
22
- # Check if byte is a digit (0-9)
23
- # @param byte [Integer] Byte value from String#getbyte
24
- # @return [Boolean] true if digit
25
- def self.digit?(byte)
26
- byte >= BYTE_DIGIT_0 && byte <= BYTE_DIGIT_9
27
- end
28
-
29
- # Check if byte is alphanumeric, hyphen, or underscore (CSS identifier char)
30
- # @param byte [Integer] Byte value from String#getbyte
31
- # @return [Boolean] true if valid identifier character
32
- def self.ident_char?(byte)
33
- letter?(byte) || digit?(byte) || byte == BYTE_HYPHEN || byte == BYTE_UNDERSCORE
34
- end
35
- end