Class: Cataract::Declarations

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

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



316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/cataract/declarations.rb', line 316

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



301
302
303
304
# File 'lib/cataract/declarations.rb', line 301

def initialize_copy(source)
  super
  @values = source.to_a.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



287
288
289
# File 'lib/cataract/declarations.rb', line 287

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



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/cataract/declarations.rb', line 262

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



244
245
246
# File 'lib/cataract/declarations.rb', line 244

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



229
230
231
232
233
234
235
236
# File 'lib/cataract/declarations.rb', line 229

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

Convert declarations to CSS string.

Examples:

decls.to_s #=> "color: red; margin: 10px !important;"

Returns:

  • (String)

    CSS declaration block string



205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/cataract/declarations.rb', line 205

def to_s
  result = +''
  @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