csspool 0.1.1 → 0.2.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 (42) hide show
  1. data/.DS_Store +0 -0
  2. data/CHANGELOG.txt +16 -0
  3. data/Manifest.txt +11 -0
  4. data/README.txt +11 -3
  5. data/Rakefile +2 -2
  6. data/lib/css/sac/conditions/attribute_condition.rb +4 -0
  7. data/lib/css/sac/conditions/combinator_condition.rb +4 -0
  8. data/lib/css/sac/conditions/condition.rb +8 -0
  9. data/lib/css/sac/lexical_unit.rb +98 -6
  10. data/lib/css/sac/parser.rb +15 -4
  11. data/lib/css/sac/selectors/child_selector.rb +4 -0
  12. data/lib/css/sac/selectors/conditional_selector.rb +4 -0
  13. data/lib/css/sac/selectors/descendant_selector.rb +4 -0
  14. data/lib/css/sac/selectors/element_selector.rb +4 -0
  15. data/lib/css/sac/selectors/selector.rb +8 -0
  16. data/lib/css/sac/selectors/sibling_selector.rb +4 -0
  17. data/lib/css/sac.rb +1 -0
  18. data/lib/css/stylesheet/rule.rb +12 -0
  19. data/lib/css/stylesheet/stylesheet.rb +94 -0
  20. data/lib/css/stylesheet.rb +3 -0
  21. data/lib/csspool.rb +1 -0
  22. data/lib/parser.y +2 -2
  23. data/test/test_attribute_condition.rb +9 -0
  24. data/test/test_child_selector.rb +12 -0
  25. data/test/test_combinator_condition.rb +9 -0
  26. data/test/test_condition.rb +18 -0
  27. data/test/test_conditional_selector.rb +12 -0
  28. data/test/test_descendant_selector.rb +12 -0
  29. data/test/test_element_selector.rb +9 -0
  30. data/test/test_lexical_color.rb +49 -0
  31. data/test/test_lexical_function.rb +31 -0
  32. data/test/test_lexical_ident.rb +30 -0
  33. data/test/test_lexical_number.rb +68 -0
  34. data/test/test_lexical_string.rb +23 -0
  35. data/test/test_lexical_unit.rb +11 -1
  36. data/test/test_lexical_uri.rb +28 -0
  37. data/test/test_parser.rb +16 -0
  38. data/test/test_selector.rb +18 -1
  39. data/test/test_selector_parser.rb +8 -0
  40. data/test/test_sibling_selector.rb +9 -0
  41. data/test/test_stylesheet.rb +95 -0
  42. metadata +13 -2
data/.DS_Store CHANGED
Binary file
data/CHANGELOG.txt CHANGED
@@ -1,3 +1,19 @@
1
+ == 0.2.0
2
+
3
+ * Added CSS::SAC::Parser#parse_rule to parse a single rule.
4
+ * Added CSS::StyleSheet#find_rule for finding a particular rule.
5
+ * Added CSS::StyleSheet#rules_matching for finding all rules matching a node.
6
+ * Added CSS::StyleSheet#create_rule for creating a new rule.
7
+ * Added CSS::StyleSheet#find_all_rules_matching for finding all rules that match
8
+ any node in the passed in document.
9
+ * Added .eql? to selector AST
10
+ * Added .hash to selector AST
11
+ * Added .eql? to LexicalUnits
12
+ * Added .hash to LexicalUnits
13
+ * Added CSS::StyleSheet#to_css
14
+ * Added CSS::StyleSheet#reduce!
15
+ * CSS::StyleSheet is now the default document handler
16
+
1
17
  == 0.1.1
2
18
 
3
19
  * Added specificity to selectors.
data/Manifest.txt CHANGED
@@ -32,6 +32,10 @@ lib/css/sac/selectors/simple_selector.rb
32
32
  lib/css/sac/token.rb
33
33
  lib/css/sac/tokenizer.rb
34
34
  lib/css/sac/visitable.rb
35
+ lib/css/stylesheet.rb
36
+ lib/css/stylesheet/rule.rb
37
+ lib/css/stylesheet/stylesheet.rb
38
+ lib/csspool.rb
35
39
  lib/parser.y
36
40
  lib/property_parser.y
37
41
  lib/property_parser.y.erb
@@ -50,7 +54,13 @@ test/test_descendant_selector.rb
50
54
  test/test_element_selector.rb
51
55
  test/test_id_condition.rb
52
56
  test/test_lexeme.rb
57
+ test/test_lexical_color.rb
58
+ test/test_lexical_function.rb
59
+ test/test_lexical_ident.rb
60
+ test/test_lexical_number.rb
61
+ test/test_lexical_string.rb
53
62
  test/test_lexical_unit.rb
63
+ test/test_lexical_uri.rb
54
64
  test/test_one_of_condition.rb
55
65
  test/test_parse_error.rb
56
66
  test/test_parser.rb
@@ -61,6 +71,7 @@ test/test_selector_parser.rb
61
71
  test/test_sibling_selector.rb
62
72
  test/test_simple_selector.rb
63
73
  test/test_specificity.rb
74
+ test/test_stylesheet.rb
64
75
  test/test_token.rb
65
76
  test/test_tokenizer.rb
66
77
  test/test_xpath.rb
data/README.txt CHANGED
@@ -18,7 +18,15 @@ Building CSSpool requires:
18
18
 
19
19
  - rubygems, hoe, flexmock
20
20
 
21
- == Example
21
+ == Examples
22
+
23
+ === Tidy a stylesheet
24
+
25
+ parser = CSS::SAC::Parser.new
26
+ doc = parser.parse(File.read(ARGV[0]))
27
+ puts doc.to_css
28
+
29
+ === Find custom attributes
22
30
 
23
31
  This example prints out all properties from a particular CSS file.
24
32
 
@@ -41,8 +49,8 @@ See SAC[http://www.w3.org/Style/CSS/SAC/] for more information on SAC.
41
49
 
42
50
  == Authors
43
51
 
44
- * {Aaron Patterson}[http://tenderlovemaking.com] <aaronp@rubyforge.com>
45
- * {John Barnette}[http://geeksomnia.com] <jbarnette@rubyforge.com>
52
+ * {Aaron Patterson}[http://tenderlovemaking.com] <aaronp@rubyforge.org>
53
+ * {John Barnette}[http://geeksomnia.com] <jbarnette@rubyforge.org>
46
54
 
47
55
  == LICENSE
48
56
 
data/Rakefile CHANGED
@@ -7,14 +7,14 @@ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "lib")
7
7
  GENERATED_PARSER = "lib/css/sac/generated_parser.rb"
8
8
  GENERATED_PROPERTY_PARSER = "lib/css/sac/generated_property_parser.rb"
9
9
 
10
- Hoe.new('csspool', '0.1.1') do |p|
10
+ Hoe.new('csspool', '0.2.0') do |p|
11
11
  p.rubyforge_name = 'csspool'
12
12
  p.author = 'Aaron Patterson'
13
13
  p.email = 'aaronp@rubyforge.org'
14
14
  p.summary = "Parses CSS"
15
15
  p.description = p.paragraphs_of('README.txt', 3).join("\n\n")
16
16
  p.url = p.paragraphs_of('README.txt', 1).first.strip
17
- p.changes = p.paragraphs_of('CHANGELOG.txt', 0..2).join("\n\n")
17
+ p.changes = p.paragraphs_of('CHANGELOG.txt', 0..1).join("\n\n")
18
18
  p.clean_globs = [GENERATED_PARSER]
19
19
  end
20
20
 
@@ -46,6 +46,10 @@ module CSS
46
46
  super && local_name == other.local_name && value == other.value &&
47
47
  specified == other.specified
48
48
  end
49
+
50
+ def hash
51
+ [local_name, value, specified].hash
52
+ end
49
53
  end
50
54
  end
51
55
  end
@@ -30,6 +30,10 @@ module CSS
30
30
  def ==(other)
31
31
  super && first == other.first && second == other.second
32
32
  end
33
+
34
+ def hash
35
+ [first, second].hash
36
+ end
33
37
  end
34
38
  end
35
39
  end
@@ -14,6 +14,14 @@ module CSS
14
14
  self.class === other && condition_type == other.condition_type
15
15
  end
16
16
 
17
+ def hash
18
+ condition_type.hash
19
+ end
20
+
21
+ def eql?(other)
22
+ self == other
23
+ end
24
+
17
25
  def to_css
18
26
  nil
19
27
  end
@@ -9,6 +9,14 @@ module CSS
9
9
  :parameters,
10
10
  :function_name
11
11
 
12
+ def ==(other)
13
+ self.class === other && self.lexical_unit_type == other.lexical_unit_type
14
+ end
15
+
16
+ def eql?(other)
17
+ self == other
18
+ end
19
+
12
20
  alias :to_s :string_value
13
21
  end
14
22
 
@@ -25,22 +33,51 @@ module CSS
25
33
  self.parameters = params
26
34
  self.lexical_unit_type = FUNCTIONS[self.function_name] || :SAC_FUNCTION
27
35
  end
36
+
37
+ def ==(other)
38
+ super && %w{ function_name parameters }.all? { |x|
39
+ self.send(x.to_sym) == other.send(x.to_sym)
40
+ }
41
+ end
42
+
43
+ def hash
44
+ ([self.function_name] + parameters).hash
45
+ end
28
46
  end
29
47
 
30
48
  class Color < LexicalUnit
31
49
  def initialize(value)
32
50
  self.string_value = value
33
51
  self.lexical_unit_type = :SAC_RGBCOLOR
34
- if value =~ /^#(\d{1,2})(\d{1,2})(\d{1,2})$/
35
- self.parameters = [
36
- Number.new($1.hex, '', :SAC_INTEGER),
37
- Number.new($2.hex, '', :SAC_INTEGER),
38
- Number.new($3.hex, '', :SAC_INTEGER)
39
- ]
52
+ if value =~ /^#([A-F\d]{1,2})([A-F\d]{1,2})([A-F\d]{1,2})$/
53
+ self.parameters = [$1, $2, $3].map { |x|
54
+ x.length == 1 ? (x * 2).hex : x.hex
55
+ }.map { |x|
56
+ Number.new(x, '', :SAC_INTEGER)
57
+ }
40
58
  else
41
59
  self.parameters = [LexicalIdent.new(value)]
42
60
  end
43
61
  end
62
+
63
+ def ==(other)
64
+ super && self.parameters == other.parameters
65
+ end
66
+
67
+ def hash
68
+ self.parameters.hash
69
+ end
70
+
71
+ def to_s
72
+ if self.parameters.length < 3
73
+ super
74
+ else
75
+ hex = self.parameters.map { |x|
76
+ sprintf("%02X", x.integer_value).split('').uniq
77
+ }.flatten
78
+ hex.length != 3 ? super : "##{hex.join()}"
79
+ end
80
+ end
44
81
  end
45
82
 
46
83
  class LexicalString < LexicalUnit
@@ -48,6 +85,14 @@ module CSS
48
85
  self.string_value = value
49
86
  self.lexical_unit_type = :SAC_STRING_VALUE
50
87
  end
88
+
89
+ def ==(other)
90
+ super && self.string_value == other.string_value
91
+ end
92
+
93
+ def hash
94
+ self.string_value.hash
95
+ end
51
96
  end
52
97
 
53
98
  class LexicalIdent < LexicalUnit
@@ -55,6 +100,14 @@ module CSS
55
100
  self.string_value = value
56
101
  self.lexical_unit_type = :SAC_IDENT
57
102
  end
103
+
104
+ def ==(other)
105
+ super && self.string_value == other.string_value
106
+ end
107
+
108
+ def hash
109
+ self.string_value.hash
110
+ end
58
111
  end
59
112
 
60
113
  class LexicalURI < LexicalUnit
@@ -62,6 +115,18 @@ module CSS
62
115
  self.string_value = value.gsub(/^url\(/, '').gsub(/\)$/, '')
63
116
  self.lexical_unit_type = :SAC_URI
64
117
  end
118
+
119
+ def ==(other)
120
+ super && self.string_value == other.string_value
121
+ end
122
+
123
+ def hash
124
+ self.string_value.hash
125
+ end
126
+
127
+ def to_s
128
+ "url(#{string_value})"
129
+ end
65
130
  end
66
131
 
67
132
  class Number < LexicalUnit
@@ -106,6 +171,33 @@ module CSS
106
171
  self.lexical_unit_type = UNITS[self.dimension_unit_text] ||
107
172
  (value =~ /\./ ? :SAC_NUMBER : :SAC_INTEGER)
108
173
  end
174
+
175
+ def ==(other)
176
+ return true if self.float_value == 0 && other.float_value == 0
177
+ return false unless super
178
+
179
+ %w{ float_value integer_value dimension_unit_text }.all? { |x|
180
+ self.send(x.to_sym) == other.send(x.to_sym)
181
+ }
182
+ end
183
+
184
+ def hash
185
+ if self.float_value == 0
186
+ self.float_value.hash
187
+ else
188
+ %w{ float_value integer_value dimension_unit_text }.map { |x|
189
+ self.send(x.to_sym)
190
+ }.hash
191
+ end
192
+ end
193
+
194
+ def to_s
195
+ if self.float_value == 0
196
+ "0"
197
+ else
198
+ super
199
+ end
200
+ end
109
201
  end
110
202
  end
111
203
  end
@@ -14,15 +14,15 @@ module CSS
14
14
  module SAC
15
15
  class Parser < CSS::SAC::GeneratedParser
16
16
  # The version of CSSPool you're using
17
- VERSION = '0.1.1'
17
+ VERSION = '0.2.0'
18
18
 
19
19
  TOKENIZER = Tokenizer.new
20
20
 
21
21
  attr_accessor :document_handler, :error_handler, :logger
22
22
 
23
- def initialize(document_handler = nil)
23
+ def initialize(document_handler = StyleSheet.new(self))
24
24
  @error_handler = ErrorHandler.new
25
- @document_handler = document_handler || DocumentHandler.new()
25
+ @document_handler = document_handler
26
26
  @property_parser = PropertyParser.new()
27
27
  @tokenizer = TOKENIZER
28
28
  @logger = nil
@@ -40,7 +40,18 @@ module CSS
40
40
  end
41
41
 
42
42
  alias :parse :parse_style_sheet
43
- alias :parse_rule :parse_style_sheet
43
+
44
+ def parse_rule(rule)
45
+ returner = Class.new(DocumentHandler) {
46
+ attr_accessor :selector
47
+ alias :start_selector :selector=
48
+ }.new
49
+ old_document_handler = self.document_handler
50
+ self.document_handler = returner
51
+ self.parse("#{rule} { }")
52
+ self.document_handler = old_document_handler
53
+ returner.selector
54
+ end
44
55
 
45
56
  # Returns the parser version. We return CSS2, but its actually
46
57
  # CSS2.1. No font-face tags. Sorry.
@@ -30,6 +30,10 @@ module CSS
30
30
  def ==(other)
31
31
  super && parent == other.parent && selector == other.selector
32
32
  end
33
+
34
+ def hash
35
+ [parent, selector].hash
36
+ end
33
37
  end
34
38
  end
35
39
  end
@@ -37,6 +37,10 @@ module CSS
37
37
  def ==(other)
38
38
  super && condition == other.condition && selector == other.selector
39
39
  end
40
+
41
+ def hash
42
+ [condition, selector].hash
43
+ end
40
44
  end
41
45
  end
42
46
  end
@@ -30,6 +30,10 @@ module CSS
30
30
  def ==(other)
31
31
  super && selector == other.selector && ancestor == other.ancestor
32
32
  end
33
+
34
+ def hash
35
+ [selector, ancestor].hash
36
+ end
33
37
  end
34
38
  end
35
39
  end
@@ -29,6 +29,10 @@ module CSS
29
29
  def ==(other)
30
30
  super && name == other.name
31
31
  end
32
+
33
+ def hash
34
+ name.hash
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -14,6 +14,14 @@ module CSS
14
14
  self.class === other && selector_type == other.selector_type
15
15
  end
16
16
 
17
+ def hash
18
+ selector_type.hash
19
+ end
20
+
21
+ def eql?(other)
22
+ self == other
23
+ end
24
+
17
25
  def =~(node)
18
26
  MatchesVisitor.new(node).accept(self)
19
27
  end
@@ -29,6 +29,10 @@ module CSS
29
29
  def ==(other)
30
30
  super && selector == other.selector && sibling == other.sibling
31
31
  end
32
+
33
+ def hash
34
+ [selector, sibling].hash
35
+ end
32
36
  end
33
37
  end
34
38
  end
data/lib/css/sac.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "css/sac/parser"
2
+ require "css/stylesheet"
2
3
 
3
4
  module CSS
4
5
  module SAC
@@ -0,0 +1,12 @@
1
+ require 'set'
2
+ module CSS
3
+ class StyleSheet
4
+ class Rule
5
+ attr_accessor :selector, :properties
6
+ def initialize(selector, properties = [])
7
+ @selector = selector
8
+ @properties = Set.new(properties)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,94 @@
1
+ module CSS
2
+ class StyleSheet < CSS::SAC::DocumentHandler
3
+ attr_reader :rules
4
+
5
+ def initialize(sac)
6
+ @sac = sac
7
+ @rules = []
8
+ @current_rules = []
9
+ end
10
+
11
+ def start_selector(selectors)
12
+ selectors.each { |selector|
13
+ @current_rules << Rule.new(selector)
14
+ }
15
+ end
16
+
17
+ def end_selector(selectors)
18
+ @rules += @current_rules
19
+ @current_rules = []
20
+ reduce!
21
+ end
22
+
23
+ def find_rule(rule)
24
+ rule = self.create_rule(rule) if rule.is_a?(String)
25
+ rules.find { |x| x.selector == rule.selector }
26
+ end
27
+ alias :[] :find_rule
28
+
29
+ # Find all rules used in +hpricot_document+
30
+ def find_all_rules_matching(hpricot_document)
31
+ used_rules = []
32
+ hpricot_document.search('//').each do |node|
33
+ if matching = (rules_matching(node))
34
+ used_rules += matching
35
+ end
36
+ end
37
+ used_rules.uniq
38
+ end
39
+
40
+ # Find all rules that match +node+. +node+ must quack like an Hpricot
41
+ # node.
42
+ def rules_matching(node)
43
+ rules.find_all { |rule|
44
+ rule.selector =~ node
45
+ }
46
+ end
47
+ alias :=~ :rules_matching
48
+
49
+ def create_rule(rule)
50
+ Rule.new(@sac.parse_rule(rule).first)
51
+ end
52
+
53
+ def property(name, value, important)
54
+ @current_rules.each { |selector|
55
+ selector.properties << [name, value, important]
56
+ }
57
+ end
58
+
59
+ # Get a hash of rules by property
60
+ def rules_by_property
61
+ rules_by_property = Hash.new { |h,k| h[k] = [] }
62
+ @rules.each { |sel|
63
+ props = sel.properties.to_a.sort_by { |x| x.hash } # HACK?
64
+ rules_by_property[props] << sel
65
+ }
66
+ rules_by_property
67
+ end
68
+
69
+ def to_css
70
+ rules_by_property.map do |properties, rules|
71
+ rules.map { |rule| rule.selector.to_css }.sort.join(', ') + " {\n" +
72
+ properties.map { |key,value,important|
73
+ # Super annoying. If the property is font-family, its supposed to
74
+ # be commas
75
+ join_val = ('font-family' == key) ? ', ' : ' '
76
+ values = [value].flatten.join(join_val)
77
+ "#{key}:#{values}#{important ? ' !important' : ''};"
78
+ }.join("\n") + "\n}"
79
+ end.sort.join("\n")
80
+ end
81
+
82
+ private
83
+ # Remove duplicate rules
84
+ def reduce!
85
+ unique_rules = {}
86
+ @rules.each do |rule|
87
+ (unique_rules[rule.selector] ||= rule).properties += rule.properties
88
+ end
89
+ @rules = unique_rules.values
90
+ self
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,3 @@
1
+ require "css/stylesheet/stylesheet"
2
+ require "css/stylesheet/rule"
3
+
data/lib/csspool.rb ADDED
@@ -0,0 +1 @@
1
+ require 'css/sac'
data/lib/parser.y CHANGED
@@ -203,7 +203,7 @@ rule
203
203
  |
204
204
  ;
205
205
  expr
206
- : term operator expr { result = [val[0], val.last] }
206
+ : term operator expr { result = val }
207
207
  | term
208
208
  ;
209
209
  term
@@ -227,7 +227,7 @@ rule
227
227
  ;
228
228
  function
229
229
  : FUNCTION s_0toN expr ')' s_0toN {
230
- result = Function.new(val[0], val[2].flatten)
230
+ result = Function.new(val[0], val[2].flatten.select { |x| x !~ /,/ })
231
231
  }
232
232
  | FUNCTION s_0toN expr error ')' s_0toN { yyerrok; result = [val[0], val[2], val[3]] }
233
233
  ;
@@ -1,6 +1,15 @@
1
1
  require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class AttributeConditionTest < ConditionTestCase
4
+ def test_hash
5
+ first = AttributeCondition.new(1, 1, 1)
6
+ second = AttributeCondition.new(1, 1, 1)
7
+ assert_equal first.hash, second.hash
8
+
9
+ third = AttributeCondition.new(2, 1, 1)
10
+ assert_not_equal first.hash, third.hash
11
+ end
12
+
4
13
  def test_equals2
5
14
  first = AttributeCondition.new(1, 1, 1)
6
15
  second = AttributeCondition.new(1, 1, 1)
@@ -1,6 +1,18 @@
1
1
  require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class ChildSelectorTest < SelectorTestCase
4
+ def test_hash
5
+ first = ChildSelector.new(1,1)
6
+ second = ChildSelector.new(1,1)
7
+ assert_equal first.hash, second.hash
8
+
9
+ third = ChildSelector.new(1,2)
10
+ assert_not_equal first.hash, third.hash
11
+
12
+ fourth = ChildSelector.new(2,1)
13
+ assert_not_equal first.hash, fourth.hash
14
+ end
15
+
4
16
  def test_equals2
5
17
  first = ChildSelector.new(1,1)
6
18
  second = ChildSelector.new(1,1)
@@ -10,6 +10,15 @@ class CombinatorConditionTest < ConditionTestCase
10
10
  assert_not_equal first, third
11
11
  end
12
12
 
13
+ def test_hash
14
+ first = CombinatorCondition.new(1,1)
15
+ second = CombinatorCondition.new(1,1)
16
+ assert_equal first.hash, second.hash
17
+
18
+ third = CombinatorCondition.new(1,2)
19
+ assert_not_equal first.hash, third.hash
20
+ end
21
+
13
22
  def test_equals_tilde
14
23
  first = ClassCondition.new('foo')
15
24
  second = AttributeCondition.new('name', 'aaron', true)
@@ -10,4 +10,22 @@ class ConditionTest < ConditionTestCase
10
10
  assert_not_equal first, third
11
11
  assert_not_equal first, 1
12
12
  end
13
+
14
+ def test_eql?
15
+ first = Condition.new(1)
16
+ second = Condition.new(1)
17
+ assert first.eql?(second)
18
+
19
+ third = Condition.new(2)
20
+ assert !first.eql?(third)
21
+ end
22
+
23
+ def test_hash
24
+ first = Condition.new(1)
25
+ second = Condition.new(1)
26
+ assert_equal first.hash, second.hash
27
+
28
+ third = Condition.new(2)
29
+ assert_not_equal first.hash, third.hash
30
+ end
13
31
  end
@@ -1,6 +1,18 @@
1
1
  require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class ConditionalSelectorTest < SelectorTestCase
4
+ def test_hash
5
+ first = ConditionalSelector.new(1,1)
6
+ second = ConditionalSelector.new(1,1)
7
+ assert_equal first.hash, second.hash
8
+
9
+ third = ConditionalSelector.new(1,2)
10
+ assert_not_equal first.hash, third.hash
11
+
12
+ fourth = ConditionalSelector.new(2,1)
13
+ assert_not_equal first.hash, fourth.hash
14
+ end
15
+
4
16
  def test_equals2
5
17
  first = ConditionalSelector.new(1,1)
6
18
  second = ConditionalSelector.new(1,1)
@@ -1,6 +1,18 @@
1
1
  require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class DescendantSelectorTest < SelectorTestCase
4
+ def test_hash
5
+ first = DescendantSelector.new(1,1)
6
+ second = DescendantSelector.new(1,1)
7
+ assert_equal first.hash, second.hash
8
+
9
+ third = DescendantSelector.new(1,2)
10
+ assert_not_equal first.hash, third.hash
11
+
12
+ fourth = DescendantSelector.new(2,1)
13
+ assert_not_equal first.hash, fourth.hash
14
+ end
15
+
4
16
  def test_equals2
5
17
  first = DescendantSelector.new(1,1)
6
18
  second = DescendantSelector.new(1,1)
@@ -1,6 +1,15 @@
1
1
  require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class ElementSelectorTest < SelectorTestCase
4
+ def test_hash
5
+ first = ElementSelector.new(1)
6
+ second = ElementSelector.new(1)
7
+ assert_equal first.hash, second.hash
8
+
9
+ third = ElementSelector.new(2)
10
+ assert_not_equal first.hash, third.hash
11
+ end
12
+
4
13
  def test_equals2
5
14
  first = ElementSelector.new(1)
6
15
  second = ElementSelector.new(1)
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class LexicalColorTest < Test::Unit::TestCase
4
+ include CSS::SAC
5
+
6
+ def test_to_s
7
+ first = Color.new('#FFFFFF')
8
+ assert_equal('#FFF', first.to_s)
9
+
10
+ second = Color.new('#FEFEFE')
11
+ assert_equal('#FEFEFE', second.to_s)
12
+
13
+ third = Color.new('red')
14
+ assert_equal('red', third.to_s)
15
+
16
+ fourth = Color.new('#0066FF')
17
+ assert_equal('#06F', fourth.to_s)
18
+ end
19
+
20
+ def test_hash
21
+ first = Color.new('#FFFFFF')
22
+ second = Color.new('#FFFFFF')
23
+ assert_equal first.hash, second.hash
24
+
25
+ third = Color.new('red')
26
+ assert_not_equal first.hash, third.hash
27
+ end
28
+
29
+ def test_equals2
30
+ first = Color.new('#FFFFFF')
31
+ second = Color.new('#FFFFFF')
32
+ assert_equal(first, second)
33
+
34
+ third = Color.new('#FFF')
35
+ assert_equal(first, third)
36
+
37
+ fourth = Color.new('#FFA')
38
+ assert_not_equal(first, fourth)
39
+ end
40
+
41
+ def test_equals2_string
42
+ first = Color.new('red')
43
+ second = Color.new('red')
44
+ assert_equal(first, second)
45
+
46
+ third = Color.new('#FFF')
47
+ assert_not_equal(first, third)
48
+ end
49
+ end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class LexicalFunctionTest < Test::Unit::TestCase
4
+ include CSS::SAC
5
+
6
+ def test_to_s
7
+ first = Function.new('counter(', %w{ 1 2 3 4 })
8
+ assert_equal('counter(1, 2, 3, 4)', first.to_s)
9
+ end
10
+
11
+ def test_equals2
12
+ first = Function.new('counter(', %w{ 1 2 3 4 })
13
+ second = Function.new('counter(', %w{ 1 2 3 4 })
14
+ assert_equal(first, second)
15
+
16
+ third = Function.new('rect(', %w{ 1 2 3 4 })
17
+ assert_not_equal(first, third)
18
+
19
+ fourth = Function.new('counter(', %w{ 1 2 3 5 })
20
+ assert_not_equal(first, fourth)
21
+ end
22
+
23
+ def test_hash
24
+ first = Function.new('counter(', %w{ 1 2 3 4 })
25
+ second = Function.new('counter(', %w{ 1 2 3 4 })
26
+ assert_equal first.hash, second.hash
27
+
28
+ third = Function.new('counter(', %w{ 1 2 4 4 })
29
+ assert_not_equal first.hash, third.hash
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class LexicalIdentTest < Test::Unit::TestCase
4
+ include CSS::SAC
5
+
6
+ def test_equals2
7
+ first = LexicalIdent.new('one')
8
+ second = LexicalIdent.new('one')
9
+ assert_equal first, second
10
+
11
+ third = LexicalIdent.new('two')
12
+ assert_not_equal first, third
13
+ end
14
+
15
+ def test_eql?
16
+ first = LexicalIdent.new('one')
17
+ second = LexicalIdent.new('one')
18
+
19
+ assert first.eql?(second)
20
+ end
21
+
22
+ def test_hash
23
+ first = LexicalIdent.new('one')
24
+ second = LexicalIdent.new('one')
25
+ assert_equal first.hash, second.hash
26
+
27
+ third = LexicalIdent.new('two')
28
+ assert_not_equal first.hash, third.hash
29
+ end
30
+ end
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class LexicalNumberTest < Test::Unit::TestCase
4
+ def test_to_s
5
+ first = CSS::SAC::Number.new('10', 'px')
6
+ assert_equal('10px', first.to_s)
7
+
8
+ second = CSS::SAC::Number.new('0', 'em')
9
+ assert_equal('0', second.to_s)
10
+ end
11
+
12
+ def test_hash
13
+ first = CSS::SAC::Number.new('10', 'px')
14
+ second = CSS::SAC::Number.new('10', 'px')
15
+ assert_equal first.hash, second.hash
16
+
17
+ third = CSS::SAC::Number.new('11', 'px')
18
+ assert_not_equal first.hash, third.hash
19
+
20
+ fourth = CSS::SAC::Number.new('10', 'em')
21
+ assert_not_equal first.hash, fourth.hash
22
+ end
23
+
24
+ def test_hash_0_unit
25
+ first = CSS::SAC::Number.new('0', 'px')
26
+ second = CSS::SAC::Number.new('0', 'px')
27
+ assert_equal first.hash, second.hash
28
+
29
+ third = CSS::SAC::Number.new('0', 'em')
30
+ assert_equal first.hash, third.hash
31
+ end
32
+
33
+ def test_equals2
34
+ first = CSS::SAC::Number.new('10', 'px')
35
+ second = CSS::SAC::Number.new('10', 'px')
36
+ assert_equal first, second
37
+
38
+ third = CSS::SAC::Number.new('10', 'ex')
39
+ assert_not_equal first, third
40
+
41
+ fourth = CSS::SAC::Number.new('11', 'px')
42
+ assert_equal(11, fourth.integer_value)
43
+ assert_not_equal first, fourth
44
+ end
45
+
46
+ def test_equals2_zero
47
+ first = CSS::SAC::Number.new('0', 'px')
48
+ second = CSS::SAC::Number.new('0', 'em')
49
+ assert_equal first, second
50
+
51
+ first_float = CSS::SAC::Number.new('0.0', 'px')
52
+ second_float = CSS::SAC::Number.new('0.0', 'em')
53
+ assert_equal first_float, second_float
54
+
55
+ third_float = CSS::SAC::Number.new('0.1', 'em')
56
+ assert_equal 0.1, third_float.float_value
57
+ assert_not_equal first_float, third_float
58
+ end
59
+
60
+ def test_equals2_type
61
+ first = CSS::SAC::Number.new(10, '', :SAC_INTEGER)
62
+ second = CSS::SAC::Number.new(10, '', :SAC_INTEGER)
63
+ assert_equal first, second
64
+
65
+ third = CSS::SAC::Number.new(11, '', :SAC_INTEGER)
66
+ assert_not_equal first, third
67
+ end
68
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class LexicalStringTest < Test::Unit::TestCase
4
+ include CSS::SAC
5
+
6
+ def test_equals2
7
+ first = LexicalString.new('hey bro')
8
+ second = LexicalString.new('hey bro')
9
+ assert_equal(first, second)
10
+
11
+ third = LexicalString.new('hey bro again')
12
+ assert_not_equal(first, third)
13
+ end
14
+
15
+ def test_hash
16
+ first = LexicalString.new('hey bro')
17
+ second = LexicalString.new('hey bro')
18
+ assert_equal(first.hash, second.hash)
19
+
20
+ third = LexicalString.new('hey bro!')
21
+ assert_not_equal(first.hash, third.hash)
22
+ end
23
+ end
@@ -1,6 +1,8 @@
1
1
  require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class LexicalUnitTest < Test::Unit::TestCase
4
+ include CSS::SAC
5
+
4
6
  def setup
5
7
  @sac = CSS::SAC::Parser.new()
6
8
  end
@@ -13,6 +15,14 @@ class LexicalUnitTest < Test::Unit::TestCase
13
15
  flexmock_verify
14
16
  end
15
17
 
18
+ def test_equals2
19
+ first = LexicalUnit.new
20
+ second = LexicalUnit.new
21
+ assert_equal(first, second)
22
+ second.lexical_unit_type = 'asdfadsf'
23
+ assert_not_equal(first, second)
24
+ end
25
+
16
26
  def test_color
17
27
  flexmock(@sac.document_handler).
18
28
  should_receive(:property).with('background-color', on { |list|
@@ -20,7 +30,7 @@ class LexicalUnitTest < Test::Unit::TestCase
20
30
  list.first.lexical_unit_type == :SAC_RGBCOLOR &&
21
31
  list.first.string_value == "#345" &&
22
32
  list.first.parameters.length == 3 &&
23
- list.first.parameters.map { |x| x.integer_value } == [3,4,5] &&
33
+ list.first.parameters.map { |x| x.integer_value } == [51,68,85] &&
24
34
  list.first.integer_value.nil?
25
35
  }, false).once
26
36
 
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class LexicalURITest < Test::Unit::TestCase
4
+ include CSS::SAC
5
+
6
+ def test_to_s
7
+ first = LexicalURI.new('url(http://tenderlovemaking.com/)')
8
+ assert_equal('url(http://tenderlovemaking.com/)', first.to_s)
9
+ end
10
+
11
+ def test_hash
12
+ first = LexicalURI.new('url(http://tenderlovemaking.com/)')
13
+ second = LexicalURI.new('url(http://tenderlovemaking.com/)')
14
+ assert_equal first.hash, second.hash
15
+
16
+ third = LexicalURI.new('url(http://www.tenderlovemaking.com/)')
17
+ assert_not_equal first.hash, third.hash
18
+ end
19
+
20
+ def test_equals2
21
+ first = LexicalURI.new('url(http://tenderlovemaking.com/)')
22
+ second = LexicalURI.new('url(http://tenderlovemaking.com/)')
23
+ assert_equal first, second
24
+
25
+ third = LexicalURI.new('url(http://www.tenderlovemaking.com/)')
26
+ assert_not_equal first, third
27
+ end
28
+ end
data/test/test_parser.rb CHANGED
@@ -10,6 +10,22 @@ class ParserTest < Test::Unit::TestCase
10
10
  flexmock_close
11
11
  end
12
12
 
13
+ def test_parse_rule
14
+ class << @sac.document_handler
15
+ attr_accessor :selector
16
+ alias :start_selector :selector=
17
+ end
18
+ @sac.parse('h1 > div { }')
19
+
20
+ expected_rule = @sac.document_handler.selector
21
+ assert expected_rule
22
+
23
+ rule = @sac.parse_rule('h1 > div')
24
+ assert rule
25
+ assert_equal 1, rule.length
26
+ assert_equal expected_rule, rule
27
+ end
28
+
13
29
  def test_page
14
30
  flexmock(@sac.document_handler).
15
31
  should_receive(:start_page).ordered.
@@ -3,12 +3,20 @@ require File.dirname(__FILE__) + "/helper"
3
3
  class SelectorTest < SelectorTestCase
4
4
  class MySelector < Selector; end
5
5
 
6
+ def test_hash
7
+ first = Selector.new(1)
8
+ second = Selector.new(1)
9
+ assert_equal first.hash, second.hash
10
+
11
+ third = Selector.new(2)
12
+ assert_not_equal first.hash, third.hash
13
+ end
14
+
6
15
  def test_equals2
7
16
  first = Selector.new(1)
8
17
  second = Selector.new(1)
9
18
  assert_equal first, second
10
19
 
11
-
12
20
  third = Selector.new(2)
13
21
  assert_not_equal first, third
14
22
 
@@ -16,4 +24,13 @@ class SelectorTest < SelectorTestCase
16
24
  assert_equal first, fourth
17
25
  assert_not_equal first, 1
18
26
  end
27
+
28
+ def test_eql?
29
+ first = Selector.new(1)
30
+ second = Selector.new(1)
31
+ assert first.eql?(second)
32
+
33
+ third = Selector.new(2)
34
+ assert !first.eql?(third)
35
+ end
19
36
  end
@@ -17,6 +17,14 @@ class SelectorParserTest < Test::Unit::TestCase
17
17
  assert_equal(2, selectors.length)
18
18
  end
19
19
 
20
+ def test_eql?
21
+ @sac.parse('h1, h1 { }')
22
+ selectors = @sac.document_handler.selectors
23
+ assert_equal(2, selectors.length)
24
+ assert_equal selectors[0], selectors[1]
25
+ assert selectors[0].eql?(selectors[1])
26
+ end
27
+
20
28
  def test_adjacent
21
29
  @sac.parse('h1 + h2 { }')
22
30
  selectors = @sac.document_handler.selectors
@@ -1,6 +1,15 @@
1
1
  require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class SiblingSelectorTest < SelectorTestCase
4
+ def test_hash
5
+ first = SiblingSelector.new(1,1)
6
+ second = SiblingSelector.new(1,1)
7
+ assert_equal first.hash, second.hash
8
+
9
+ third = SiblingSelector.new(1,2)
10
+ assert_not_equal first.hash, third.hash
11
+ end
12
+
4
13
  def test_equals2
5
14
  first = SiblingSelector.new(1,1)
6
15
  second = SiblingSelector.new(1,1)
@@ -0,0 +1,95 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class StyleSheetTest < SelectorTestCase
4
+ def setup
5
+ @sac = CSS::SAC::Parser.new
6
+ end
7
+
8
+ def test_rules_matching
9
+ doc = @sac.parse("div > p { letter-spacing: 1px; }")
10
+ node = parent_child_tree('div', 'p')
11
+ expected = doc.rules.first
12
+
13
+ found = doc.rules_matching(node)
14
+ assert_equal 1, found.length
15
+ assert_equal expected.selector, found.first.selector
16
+
17
+ found2 = (doc =~ node)
18
+ assert_equal 1, found2.length
19
+ assert_equal expected.selector, found2.first.selector
20
+ end
21
+
22
+ def test_find_rule
23
+ doc = @sac.parse("h1 { letter-spacing: 1px; }")
24
+ expected = doc.rules.first
25
+ assert_equal expected, doc.find_rule('h1')
26
+ assert_equal expected, doc['h1']
27
+ assert_equal expected, doc[expected]
28
+ end
29
+
30
+ def test_create_rule
31
+ doc = @sac.parse("h1 { letter-spacing: 1px; }")
32
+ rule = doc.create_rule('h1')
33
+ assert_equal doc.rules.first.selector, rule.selector
34
+ end
35
+
36
+ def test_reduce!
37
+ doc = @sac.parse(<<END
38
+ h1 {
39
+ background-color: red;
40
+ border-color: green;
41
+ }
42
+ h1 { letter-spacing: 1px; }
43
+ END
44
+ )
45
+ assert_equal(1, doc.rules.length)
46
+ end
47
+
48
+ def test_rules_by_property
49
+ doc = @sac.parse(<<END
50
+ h1 {
51
+ background-color: red;
52
+ padding: 100px;
53
+ border-color: green;
54
+ }
55
+ div {
56
+ border-color: green;
57
+ background-color: red;
58
+ padding: 100px;
59
+ }
60
+ END
61
+ )
62
+ assert_equal(2, doc.rules.length)
63
+ assert_equal(doc.rules[0].properties, doc.rules[1].properties)
64
+ assert_equal(1, doc.rules_by_property.keys.length)
65
+ assert_equal(2, doc.rules_by_property.values.first.length)
66
+ end
67
+
68
+ def test_to_css
69
+ doc = @sac.parse(<<END
70
+ h1 {
71
+ background-color: red;
72
+ padding: 100px;
73
+ border-color: green;
74
+ }
75
+ div {
76
+ border-color: green;
77
+ background-color: red;
78
+ padding: 100px;
79
+ }
80
+ .marquee { background: url(images/sfx1_bg.gif) bottom repeat-x;}
81
+ SELECT {font-family: Arial, Verdana, Geneva, sans-serif; font-size: small; }
82
+ END
83
+ )
84
+ css = doc.to_css
85
+ assert_match('div, h1 {', css)
86
+
87
+ [ 'border-color:green;',
88
+ 'background-color:red;',
89
+ 'url(images/sfx1_bg.gif',
90
+ 'Arial, Verdana, Geneva, sans-serif;',
91
+ 'padding:100px;'].each do |attribute|
92
+ assert_match(attribute, css)
93
+ end
94
+ end
95
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: csspool
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.1
7
- date: 2007-11-17 00:00:00 -08:00
6
+ version: 0.2.0
7
+ date: 2007-11-28 00:00:00 -08:00
8
8
  summary: Parses CSS
9
9
  require_paths:
10
10
  - lib
@@ -63,6 +63,10 @@ files:
63
63
  - lib/css/sac/token.rb
64
64
  - lib/css/sac/tokenizer.rb
65
65
  - lib/css/sac/visitable.rb
66
+ - lib/css/stylesheet.rb
67
+ - lib/css/stylesheet/rule.rb
68
+ - lib/css/stylesheet/stylesheet.rb
69
+ - lib/csspool.rb
66
70
  - lib/parser.y
67
71
  - lib/property_parser.y
68
72
  - lib/property_parser.y.erb
@@ -81,7 +85,13 @@ files:
81
85
  - test/test_element_selector.rb
82
86
  - test/test_id_condition.rb
83
87
  - test/test_lexeme.rb
88
+ - test/test_lexical_color.rb
89
+ - test/test_lexical_function.rb
90
+ - test/test_lexical_ident.rb
91
+ - test/test_lexical_number.rb
92
+ - test/test_lexical_string.rb
84
93
  - test/test_lexical_unit.rb
94
+ - test/test_lexical_uri.rb
85
95
  - test/test_one_of_condition.rb
86
96
  - test/test_parse_error.rb
87
97
  - test/test_parser.rb
@@ -92,6 +102,7 @@ files:
92
102
  - test/test_sibling_selector.rb
93
103
  - test/test_simple_selector.rb
94
104
  - test/test_specificity.rb
105
+ - test/test_stylesheet.rb
95
106
  - test/test_token.rb
96
107
  - test/test_tokenizer.rb
97
108
  - test/test_xpath.rb