Class: Cataract::Declarations

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/cataract/declarations.rb,
lib/cataract/pure.rb,
ext/cataract/cataract.c

Overview

Container for CSS property declarations with merge and cascade support.

The Declarations class provides a convenient Ruby interface for working with CSS property-value pairs. It wraps an array of Declaration structs (defined in C) and provides hash-like access, iteration, and merging capabilities.

Examples:

Create from hash

decls = Cataract::Declarations.new('color' => 'red', 'margin' => '10px')
decls['color'] #=> "red"

Create from Declaration array

rule = Cataract.parse_css("body { color: red; }").rules.first
decls = Cataract::Declarations.new(rule.declarations)
decls['color'] #=> "red"

Create from CSS string

decls = Cataract::Declarations.new("color: red; margin: 10px")
decls.size #=> 2

Work with !important

decls = Cataract::Declarations.new('color' => 'red !important')
decls.important?('color') #=> true
decls['color'] #=> "red !important"

Instance Method Summary collapse

Constructor Details

#initialize(properties = {}) ⇒ Declarations

Create a new Declarations container.

Parameters:

  • properties (Hash, Array<Declaration>, String) (defaults to: {})

    Initial declarations

    • Hash: Property name => value pairs
    • Array: Array of Declaration structs from parser
    • String: CSS declaration block (e.g., "color: red; margin: 10px")


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/cataract/declarations.rb', line 37

def initialize(properties = {})
  case properties
  when Array
    # Array of Declaration structs from C parser - store directly
    @values = properties
  when Hash
    # Hash from user - convert to internal storage
    @values = []
    properties.each { |prop, value| self[prop] = value }
  when String
    # String "color: red; background: blue" - parse it
    @values = parse_declaration_string(properties)
  else
    @values = []
  end
end

Instance Method Details

#==(other) ⇒ Boolean

Compare this Declarations with another object.

Examples:

decls1 = Cataract::Declarations.new('color' => 'red')
decls2 = Cataract::Declarations.new('color' => 'red')
decls1 == decls2 #=> true
decls1 == "color: red;" #=> true (string comparison)

Parameters:

Returns:

  • (Boolean)

    true if equal



306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/cataract/declarations.rb', line 306

def ==(other)
  case other
  when Declarations
    # Compare arrays of Declaration structs
    to_a == other.to_a
  when String
    # Allow string comparison for convenience
    to_s == other
  else
    false
  end
end

#delete(property) ⇒ Array<Declaration>

Delete a property from the declaration block.

Examples:

decls.delete('color')
decls.key?('color') #=> false

Parameters:

  • property (String, Symbol)

    The CSS property name to delete

Returns:

  • (Array<Declaration>)

    The modified declarations array



160
161
162
163
# File 'lib/cataract/declarations.rb', line 160

def delete(property)
  prop = normalize_property(property)
  @values.delete_if { |v| v.property == prop }
end

#each {|property, value, important| ... } ⇒ Enumerator?

Iterate through each property-value pair.

Examples:

decls.each do |property, value, important|
  puts "#{property}: #{value}#{important ? ' !important' : ''}"
end

Yield Parameters:

  • property (String)

    The property name

  • value (String)

    The property value (without !important)

  • important (Boolean)

    Whether the property has !important flag

Returns:

  • (Enumerator, nil)

    Returns enumerator if no block given



176
177
178
179
180
181
182
# File 'lib/cataract/declarations.rb', line 176

def each
  return enum_for(:each) unless block_given?

  @values.each do |val|
    yield val.property, val.value, val.important
  end
end

#empty?Boolean

Check if the declaration block is empty.

Returns:

  • (Boolean)

    true if no properties are defined



195
196
197
# File 'lib/cataract/declarations.rb', line 195

def empty?
  @values.empty?
end

#get_property(property) ⇒ String? Also known as: []

Get the value of a CSS property.

Returns the property value with !important suffix if present. Property names are case-insensitive.

Examples:

decls['color'] #=> "red"
decls['Color'] #=> "red" (case-insensitive)
decls['font-weight'] #=> "bold !important"

Parameters:

  • property (String, Symbol)

    The CSS property name

Returns:

  • (String, nil)

    The property value with !important suffix, or nil if not found



66
67
68
69
70
71
72
73
# File 'lib/cataract/declarations.rb', line 66

def get_property(property)
  prop = normalize_property(property)
  val = find_value(prop)
  return nil if val.nil?

  suffix = val.important ? ' !important' : ''
  "#{val.value}#{suffix}"
end

#important?(property) ⇒ Boolean

Check if a property has the !important flag.

Examples:

decls['color'] = 'red !important'
decls.important?('color') #=> true
decls['margin'] = '10px'
decls.important?('margin') #=> false

Parameters:

  • property (String, Symbol)

    The CSS property name

Returns:

  • (Boolean)

    true if the property has !important, false otherwise



147
148
149
150
# File 'lib/cataract/declarations.rb', line 147

def important?(property)
  val = find_value(normalize_property(property))
  val ? val.important : false
end

#initialize_copy(source) ⇒ Object

Ensure dup/clone create a proper independent copy, duplicating the internal declarations array so mutations (via []=, delete, merge!, etc.) on the copy don't affect the source. Declaration structs themselves are treated as immutable value objects throughout this class (always replaced wholesale, never mutated in place), so a shallow copy of the array is sufficient - defining this via initialize_copy (rather than overriding #dup directly) ensures #clone gets the same fix, since both go through initialize_copy.

Parameters:

  • source (Declarations)

    Source Declarations being copied



291
292
293
294
# File 'lib/cataract/declarations.rb', line 291

def initialize_copy(source)
  super
  @values = source.instance_variable_get(:@values).dup
end

#key?(property) ⇒ Boolean Also known as: has_property?

Check if a property is defined in this declaration block.

Examples:

decls.key?('color') #=> true
decls.has_property?('font-size') #=> false

Parameters:

  • property (String, Symbol)

    The CSS property name

Returns:

  • (Boolean)

    true if the property exists



132
133
134
# File 'lib/cataract/declarations.rb', line 132

def key?(property)
  !find_value(normalize_property(property)).nil?
end

#merge(other) ⇒ Declarations

Merge another set of declarations (non-mutating).

Creates a copy of this Declarations object and merges the other into it.

Examples:

decls1 = Cataract::Declarations.new('color' => 'red')
decls2 = Cataract::Declarations.new('margin' => '10px')
merged = decls1.merge(decls2)
merged.to_h #=> {"color" => "red", "margin" => "10px"}
decls1.to_h #=> {"color" => "red"} (unchanged)

Parameters:

Returns:

  • (Declarations)

    New Declarations with merged properties



277
278
279
# File 'lib/cataract/declarations.rb', line 277

def merge(other)
  dup.merge!(other)
end

#merge!(other) ⇒ self

Merge another set of declarations into this one (mutating).

Properties from the other declarations will overwrite properties in this one. The !important flag is preserved during merge.

Examples:

decls1 = Cataract::Declarations.new('color' => 'red')
decls2 = Cataract::Declarations.new('margin' => '10px')
decls1.merge!(decls2)
decls1.to_h #=> {"color" => "red", "margin" => "10px"}

Parameters:

Returns:

  • (self)

    Returns self for method chaining

Raises:

  • (ArgumentError)

    If other is not Declarations or Hash



252
253
254
255
256
257
258
259
260
261
262
# File 'lib/cataract/declarations.rb', line 252

def merge!(other)
  case other
  when Declarations
    other.each { |prop, value, important| self[prop] = important ? "#{value} !important" : value }
  when Hash
    other.each { |prop, value| self[prop] = value }
  else
    raise ArgumentError, 'Can only merge Declarations or Hash objects'
  end
  self
end

#set_property(property, value) ⇒ void Also known as: []=

This method returns an undefined value.

Set the value of a CSS property.

Property names are normalized to lowercase. Trailing semicolons are stripped. The !important flag can be included in the value string.

Examples:

decls['color'] = 'red'
decls['margin'] = '10px !important'
decls['Color'] = 'blue' # Overwrites 'color' (case-insensitive)

Parameters:

  • property (String, Symbol)

    The CSS property name

  • value (String)

    The property value (may include !important)



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/cataract/declarations.rb', line 89

def set_property(property, value)
  prop = normalize_property(property)

  # Parse !important and strip trailing semicolons (css_parser compatibility)
  clean_value = value.to_s.strip
  # Remove trailing semicolons (guard to avoid allocation when no semicolon present)
  # value_str = value_str.sub(/;+$/, '') if value_str.end_with?(';')
  clean_value.sub!(/;+$/, '') if clean_value.end_with?(';')

  is_important = clean_value.end_with?('!important')
  if is_important
    clean_value.sub!(/\s*!important\s*$/, '').strip!
  else
    clean_value.strip!
  end

  # Reject malformed declarations with no value (e.g., "color: !important")
  # css_parser silently ignores these
  return if clean_value.empty?

  # Find existing value or create new one
  # Properties from C parser are already normalized, so direct comparison
  existing_index = @values.find_index { |v| v.property == prop }

  # Create a new Declaration struct
  new_val = Cataract::Declaration.new(prop, clean_value, is_important)

  if existing_index
    @values[existing_index] = new_val
  else
    @values << new_val
  end
end

#sizeInteger Also known as: length

Get the number of declarations.

Returns:

  • (Integer)

    Number of properties in the declaration block



187
188
189
# File 'lib/cataract/declarations.rb', line 187

def size
  @values.size
end

#to_aArray<Declaration>

Convert to an array of Declaration structs.

Returns the internal array of Declaration structs, which is useful for creating Rule objects or passing to C functions.

Returns:

  • (Array<Declaration>)

    Array of Declaration structs



234
235
236
# File 'lib/cataract/declarations.rb', line 234

def to_a
  @values
end

#to_hHash<String, String>

Convert to a hash of property => value pairs.

Values include !important suffix if present.

Examples:

decls.to_h #=> {"color" => "red", "margin" => "10px !important"}

Returns:

  • (Hash<String, String>)

    Hash of property names to values



219
220
221
222
223
224
225
226
# File 'lib/cataract/declarations.rb', line 219

def to_h
  result = {}
  each do |property, value, is_important|
    suffix = is_important ? ' !important' : ''
    result[property] = "#{value}#{suffix}"
  end
  result
end

#to_sString Also known as: to_str

Instance method: Declarations#to_s Converts declarations to CSS string

Returns:

  • (String)

    CSS declarations like "color: red; margin: 10px !important;"



42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/cataract/pure.rb', line 42

def to_s
  result = String.new
  @values.each_with_index do |decl, i|
    result << decl.property
    result << ': '
    result << decl.value
    result << ' !important' if decl.important
    result << ';'
    result << ' ' if i < @values.length - 1 # Add space after semicolon except for last
  end
  result
end