Skip to content

Commit 93ae821

Browse files
committed
Move Declarations into separate file
1 parent 0c5d42f commit 93ae821

File tree

4 files changed

+202
-196
lines changed

4 files changed

+202
-196
lines changed

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ gem 'rubocop'
1515
gem 'rubocop-rake'
1616
gem 'webrick'
1717

18-
gem "debug", "~> 1.9"
18+
gem 'debug', '~> 1.9'

lib/css_parser.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
require 'css_parser/version'
1212
require 'css_parser/rule_set'
13+
require 'css_parser/rule_set/declarations'
1314
require 'css_parser/regexps'
1415
require 'css_parser/parser'
1516

lib/css_parser/rule_set.rb

Lines changed: 0 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -26,201 +26,6 @@ class RuleSet
2626

2727
WHITESPACE_REPLACEMENT = '___SPACE___'
2828

29-
class Declarations
30-
class Value
31-
attr_reader :value
32-
attr_accessor :important
33-
34-
def initialize(value, important: nil)
35-
self.value = value
36-
@important = important unless important.nil?
37-
end
38-
39-
def value=(value)
40-
value = value.to_s.sub(/\s*;\s*\Z/, '')
41-
self.important = !value.slice!(CssParser::IMPORTANT_IN_PROPERTY_RX).nil?
42-
value.strip!
43-
raise ArgumentError, 'value is empty' if value.empty?
44-
45-
@value = value.freeze
46-
end
47-
48-
def to_s
49-
important ? "#{value} !important" : value
50-
end
51-
52-
def ==(other)
53-
return false unless other.is_a?(self.class)
54-
55-
value == other.value && important == other.important
56-
end
57-
end
58-
59-
extend Forwardable
60-
61-
def_delegators :declarations, :each, :each_value
62-
63-
def initialize(declarations = {})
64-
self.declarations = {}
65-
declarations.each { |property, value| add_declaration!(property, value) }
66-
end
67-
68-
# Add a CSS declaration
69-
# @param [#to_s] property that should be added
70-
# @param [Value, #to_s] value of the property
71-
#
72-
# @example
73-
# declarations['color'] = 'blue'
74-
#
75-
# puts declarations['color']
76-
# => #<CssParser::RuleSet::Declarations::Value:0x000000000305c730 @important=false, @order=1, @value="blue">
77-
#
78-
# @example
79-
# declarations['margin'] = '0px auto !important'
80-
#
81-
# puts declarations['margin']
82-
# => #<CssParser::RuleSet::Declarations::Value:0x00000000030c1838 @important=true, @order=2, @value="0px auto">
83-
#
84-
# If the property already exists its value will be over-written.
85-
# If the value is empty - property will be deleted
86-
def []=(property, value)
87-
property = normalize_property(property)
88-
89-
if value.is_a?(Value)
90-
declarations[property] = value
91-
elsif value.to_s.strip.empty?
92-
delete property
93-
else
94-
declarations[property] = Value.new(value)
95-
end
96-
rescue ArgumentError => e
97-
raise e.exception, "#{property} #{e.message}"
98-
end
99-
alias add_declaration! []=
100-
101-
def [](property)
102-
declarations[normalize_property(property)]
103-
end
104-
alias get_value []
105-
106-
def key?(property)
107-
declarations.key?(normalize_property(property))
108-
end
109-
110-
def size
111-
declarations.size
112-
end
113-
114-
# Remove CSS declaration
115-
# @param [#to_s] property property to be removed
116-
#
117-
# @example
118-
# declarations.delete('color')
119-
def delete(property)
120-
declarations.delete(normalize_property(property))
121-
end
122-
alias remove_declaration! delete
123-
124-
# Replace CSS property with multiple declarations
125-
# @param [#to_s] property property name to be replaces
126-
# @param [Hash<String => [String, Value]>] replacements hash with properties to replace with
127-
#
128-
# @example
129-
# declarations = Declarations.new('line-height' => '0.25px', 'font' => 'small-caps', 'font-size' => '12em')
130-
# declarations.replace_declaration!('font', {'line-height' => '1px', 'font-variant' => 'small-caps', 'font-size' => '24px'})
131-
# declarations
132-
# => #<CssParser::RuleSet::Declarations:0x00000000029c3018
133-
# @declarations=
134-
# {"line-height"=>#<CssParser::RuleSet::Declarations::Value:0x00000000038ac458 @important=false, @value="1px">,
135-
# "font-variant"=>#<CssParser::RuleSet::Declarations::Value:0x00000000039b3ec8 @important=false, @value="small-caps">,
136-
# "font-size"=>#<CssParser::RuleSet::Declarations::Value:0x00000000029c2c80 @important=false, @value="12em">}>
137-
def replace_declaration!(property, replacements, preserve_importance: false)
138-
property = normalize_property(property)
139-
raise ArgumentError, "property #{property} does not exist" unless key?(property)
140-
141-
replacement_declarations = self.class.new(replacements)
142-
143-
if preserve_importance
144-
importance = get_value(property).important
145-
replacement_declarations.each_value { |value| value.important = importance }
146-
end
147-
148-
replacement_keys = declarations.keys
149-
replacement_values = declarations.values
150-
property_index = replacement_keys.index(property)
151-
152-
# We should preserve subsequent declarations of the same properties
153-
# and prior important ones if replacement one is not important
154-
replacements = replacement_declarations.each.with_object({}) do |(key, replacement), result|
155-
existing = declarations[key]
156-
157-
# No existing -> set
158-
unless existing
159-
result[key] = replacement
160-
next
161-
end
162-
163-
# Replacement more important than existing -> replace
164-
if replacement.important && !existing.important
165-
result[key] = replacement
166-
replaced_index = replacement_keys.index(key)
167-
replacement_keys.delete_at(replaced_index)
168-
replacement_values.delete_at(replaced_index)
169-
property_index -= 1 if replaced_index < property_index
170-
next
171-
end
172-
173-
# Existing is more important than replacement -> keep
174-
next if !replacement.important && existing.important
175-
176-
# Existing and replacement importance are the same,
177-
# value which is declared later wins
178-
result[key] = replacement if property_index > replacement_keys.index(key)
179-
end
180-
181-
return if replacements.empty?
182-
183-
replacement_keys.delete_at(property_index)
184-
replacement_keys.insert(property_index, *replacements.keys)
185-
186-
replacement_values.delete_at(property_index)
187-
replacement_values.insert(property_index, *replacements.values)
188-
189-
self.declarations = replacement_keys.zip(replacement_values).to_h
190-
end
191-
192-
def to_s(options = {})
193-
str = declarations.reduce(String.new) do |memo, (prop, value)|
194-
importance = options[:force_important] || value.important ? ' !important' : ''
195-
memo << "#{prop}: #{value.value}#{importance}; "
196-
end
197-
# TODO: Clean-up regexp doesn't seem to work
198-
str.gsub!(/^[\s^({)]+|[\n\r\f\t]*|\s+$/mx, '')
199-
str.strip!
200-
str
201-
end
202-
203-
def ==(other)
204-
return false unless other.is_a?(self.class)
205-
206-
declarations == other.declarations && declarations.keys == other.declarations.keys
207-
end
208-
209-
protected
210-
211-
attr_reader :declarations
212-
213-
private
214-
215-
attr_writer :declarations
216-
217-
def normalize_property(property)
218-
property = property.to_s.downcase
219-
property.strip!
220-
property
221-
end
222-
end
223-
22429
extend Forwardable
22530

22631
# optional field for storing source reference

0 commit comments

Comments
 (0)