ionfish-stylish 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -9,17 +9,17 @@ Creating stylesheets
9
9
  --------------------
10
10
 
11
11
  style = Stylish.generate do
12
- rule ".header", background(:color => :teal, :image => "header.png")
12
+ rule ".header", :background => {:color => "teal", :image => "header.png"}
13
13
  rule ".content" do
14
- rule "h2", font_size("2em")
15
- rule "p", margin("0 0 1em 0")
14
+ h2 :font_size => "2em"
15
+ p :margin => "0 0 1em 0"
16
16
  end
17
17
  end
18
18
 
19
19
  Calling the stylesheet's `to_s` method would produce the following
20
20
  <abbr title="Cascading Stylesheets">CSS</abbr> code.
21
21
 
22
- .header {background-color:#008080; background-image:url('header.png');}
22
+ .header {background-color:teal; background-image:url('header.png');}
23
23
  .content h2 {font-size:2em;}
24
24
  .content p {margin:0 0 1em 0;}
25
25
 
@@ -34,15 +34,15 @@ test suite passes under Ruby 1.9; the usual caveats about this apply.
34
34
  Future considerations
35
35
  ---------------------
36
36
 
37
- * The core classes and code generation DSL are only partially documented;
38
- this situation could be improved.
37
+ * Add a Border class to complement Background.
38
+ * Add better support for CSS3 properties to Background.
39
39
  * Add a native mapping construct to allow symbol lookup for alternate
40
40
  stylesheets.
41
41
  * Change stylesheet generation to a two-step process where the tree structure
42
42
  is generated in the first step and symbols (for example, those employed by
43
43
  a mapping construct) are evaluated the second.
44
- * Fundamental objects like percentages and URIs need their own classes rather
45
- than being dealt with in an ad-hoc manner by higher-level objects.
44
+ * Fundamental objects like percentages need their own classes rather than
45
+ being dealt with in an ad-hoc manner by higher-level objects.
46
46
  * Add a parser so CSS can be read as well as written.
47
47
 
48
48
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :major: 0
3
- :patch: 0
4
2
  :minor: 1
3
+ :patch: 2
4
+ :major: 0
data/lib/stylish/core.rb CHANGED
@@ -18,14 +18,49 @@ module Stylish
18
18
  :col, :tbody, :thead, :tfoot, :tr, :td, :th, :form,
19
19
  :fieldset, :label, :input, :button, :select, :datalist,
20
20
  :optgroup, :option, :textarea, :output, :details, :datagrid,
21
- :command, :bb, :menu, :legend, :div]
21
+ :command, :bb, :menu, :legend, :div, :h1, :h2, :h3, :h4,
22
+ :h5, :h6]
22
23
 
24
+ # Rule objects represent CSS rules, and serialise to them. They possess one
25
+ # or more selectors, and zero or more declarations. In addition to their
26
+ # importance as the major building-blocks of stylesheets, they act as the
27
+ # leaves of Stylish's stylesheet trees.
28
+ #
29
+ # Their serialisation is controllable to some extent by altering their
30
+ # format attribute; this should never make them lose information when they
31
+ # are serialised.
32
+ #
33
+ # E.g., by changing its format to "%s {\n %s\n}" a rule that would
34
+ # normally be serialised thus:
35
+ #
36
+ # body {font-size:81%;}
37
+ #
38
+ # Would instead be serialised like this:
39
+ #
40
+ # body {
41
+ # font-size:81%;
42
+ # }
43
+ #
23
44
  class Rule
24
45
  include Formattable, Tree::Leaf
25
46
 
26
47
  attr_reader :selectors, :declarations
27
48
 
28
- def initialize(selectors, *declarations)
49
+ # Every Rule must have at least one selector, but may have any number of
50
+ # declarations. Empty rules are often used in stylesheets to indicate
51
+ # particular combinations of selectors which may be used to produce
52
+ # particular effects.
53
+ #
54
+ # In Stylish, of course, a Rule's declarations may be amended after its
55
+ # creation:
56
+ #
57
+ # rule = Stylish::Rule.new([Stylish::Selector.new("body")])
58
+ # rule.declarations << Stylish::Declaration.new("font-weight", "bold")
59
+ # rule.to_s # => "body {font-weight:bold;}"
60
+ #
61
+ # This makes Rule objects a very flexible foundation for the higher-level
62
+ # data structures and APIs in Stylish.
63
+ def initialize(selectors, declarations)
29
64
  accept_format(/^\s*%s\s*\{\s*%s\s*\}\s*$/m, "%s {%s}")
30
65
 
31
66
  @selectors = selectors.inject(Selectors.new) do |ss, s|
@@ -39,17 +74,62 @@ module Stylish
39
74
 
40
75
  # Serialise the rule to valid CSS code.
41
76
  def to_s(scope = "")
42
- selectors = @selectors.map do |selector|
43
- (scope.empty? ? "" : scope + " ") + selector.to_s
44
- end
45
-
46
- sprintf(@format, selectors.join, @declarations.join)
77
+ sprintf(@format, selectors.join(scope), @declarations.join)
47
78
  end
48
79
  end
49
80
 
81
+ # Comment objects form the other concrete leaves of selector trees, and allow
82
+ # stylesheets to be annotated at any point (outside Rules). Their
83
+ # serialisation format follows the well-known JavaDoc and PHPDoc style, with
84
+ # a header, several lines of notes, and key-value information.
85
+ #
86
+ # This format is not amendable; those desirous of their own formatting would
87
+ # be better served by creating their own Comment class, such as the following
88
+ # more basic one:
89
+ #
90
+ # module Stylish
91
+ # module Extensions
92
+ #
93
+ # class Comment
94
+ # include Tree::Leaf
95
+ #
96
+ # attr_accessor :content
97
+ #
98
+ # def initialize(content)
99
+ # @content = content
100
+ # end
101
+ #
102
+ # def to_s(scope = "")
103
+ # "/* " + content.to_s + " */"
104
+ # end
105
+ # end
106
+ #
107
+ # end
108
+ # end
109
+ #
50
110
  class Comment
111
+ include Tree::Leaf
112
+
51
113
  attr_reader :header, :lines, :metadata
52
114
 
115
+ # Each Comment can have a header, additional lines of text content (each
116
+ # provided as its own argument), and key-value metadata passed in as a Ruby
117
+ # Hash object.
118
+ #
119
+ # comment = Comment.new("My wonderful comment",
120
+ # "It has several lines of insightful notes,",
121
+ # "filled with wisdom and the knowledge of ages.",
122
+ # {:author => "Some Egotist"})
123
+ #
124
+ # comment.to_s # => /**
125
+ # # * My wonderful comment
126
+ # # *
127
+ # # * It has several lines of insightful notes,
128
+ # # * filled with wisdom and the knowledge of ages.
129
+ # # *
130
+ # # * @author Some Egotist
131
+ # # */
132
+ #
53
133
  def initialize(*args)
54
134
  @lines, @metadata = [], {}
55
135
 
@@ -61,83 +141,129 @@ module Stylish
61
141
  @header = arg
62
142
  end
63
143
  elsif arg.is_a? Hash
64
- @metadata.merge!(arg)
144
+ @metadata.merge! arg
65
145
  end
66
146
  end
67
-
68
- def to_s
69
- if @lines.empty? && @metadata.empty?
70
- sprintf("/**\n * %s\n */", @header)
71
- else
72
- header = sprintf(" * %s", @header) unless @header.nil?
73
- lines = @lines.map {|l| ' * ' + l }.join("\n") unless @lines.empty?
74
- metadata = @metadata.to_a.map {|name, value|
75
- sprintf(" * @%s %s", name.to_s, value.to_s)
76
- }.join("\n") unless @metadata.empty?
77
-
78
- sprintf("/**\n%s\n */", [
79
- header || nil,
80
- lines || nil,
81
- metadata || nil
82
- ].compact.join("\n *\n"))
83
- end
147
+ end
148
+
149
+ # As Comment objects are the leaves of selector trees, they must implement
150
+ # the serialisation API of those trees, and thus the #to_s method has a
151
+ # scope argument, which is in practice discarded when the serialisation of
152
+ # the comment occurs.
153
+ def to_s(scope = "")
154
+ if @lines.empty? && @metadata.empty?
155
+ sprintf("/**\n * %s\n */", @header)
156
+ else
157
+ header = sprintf(" * %s", @header) unless @header.nil?
158
+ lines = @lines.map {|l| ' * ' + l }.join("\n") unless @lines.empty?
159
+ metadata = @metadata.to_a.map {|name, value|
160
+ sprintf(" * @%s %s", name.to_s, value.to_s)
161
+ }.join("\n") unless @metadata.empty?
162
+
163
+ sprintf("/**\n%s\n */", [
164
+ header || nil,
165
+ lines || nil,
166
+ metadata || nil
167
+ ].compact.join("\n *\n"))
84
168
  end
85
169
  end
86
170
  end
87
171
 
172
+ # Selector objects are just string containers, which when serialised are
173
+ # passed the scope in which they are situated. The Selector class is one of
174
+ # Stylish's most basic units, and are generally used only by internal APIs
175
+ # when constructing Rule objects, rather than by end users (although nothing
176
+ # prevents this; it is merely inconvenient to do so).
88
177
  class Selector
89
178
 
179
+ # Selectors are immutable once created; the value of a given Selector must
180
+ # be set when the object is created.
90
181
  def initialize(str)
91
182
  @selector = str.to_s
92
183
  end
93
184
 
94
- def to_s
95
- @selector
185
+ # Each Rule possesses one or more Selectors. Rules are often placed in
186
+ # selector trees, and thus when serialised a Selector must be made aware
187
+ # of the context or scope in which it is being serialised.
188
+ #
189
+ # Selector.new("p").to_s("body") # => "body p"
190
+ #
191
+ # The Selector class is also used internally by the Tree::SelectorScope
192
+ # class, to store its scope value.
193
+ def to_s(scope = "")
194
+ (scope.empty? ? "" : scope + " ") + @selector.to_s
96
195
  end
97
196
  end
98
197
 
198
+ # Selectors objects are simply used to group Selector objects for more
199
+ # convenient storage and serialisation.
99
200
  class Selectors < Array
100
201
  include Formattable
101
202
 
203
+ # Since a group of Selectors is just a specialised kind of array, all that
204
+ # is done in its initialiser, regardless of arguments, is to set the
205
+ # default serialisation format.
102
206
  def initialize(*args)
103
207
  accept_format(/^\s*,\s*$/m, ", ")
104
208
  super
105
209
  end
106
210
 
107
- def join
108
- super(@format)
211
+ # The join method overrides the superclass' method in order to always use a
212
+ # specific separator, and so that the scope that the selectors are being
213
+ # used in can be passed through when Rules etc. are serialised.
214
+ def join(scope = "")
215
+ self.inject("") do |ss, s|
216
+ (ss.empty? ? "" : ss + self.format) + s.to_s(scope)
217
+ end
109
218
  end
110
219
 
111
- def to_s
112
- self.join
220
+ # The to_s method alternative way of calling the join method.
221
+ def to_s(scope = "")
222
+ self.join(scope)
113
223
  end
114
224
  end
115
225
 
226
+ # Each Rule may have one or more Declaration objects, and usually has more
227
+ # than one. In a sense, declarations are the business end of stylesheets:
228
+ # they are where the set of elements specified by a rule's selectors are
229
+ # given their various attributes.
116
230
  class Declaration
117
231
  include Formattable
118
232
 
119
233
  attr_accessor :value
120
-
121
- def initialize(prop, val = nil)
234
+
235
+ # Each Declaration has a property name and a value.
236
+ def initialize(name, value)
122
237
  accept_format(/^\s*%s\s*:\s*%s;\s*$/m, "%s:%s;")
123
- self.value = val
124
- self.property = prop
238
+ self.value = value
239
+ self.name = name
125
240
  end
126
241
 
127
- def property
128
- @property
242
+ # The property name of the Declaration.
243
+ def name
244
+ @property_name
129
245
  end
130
246
 
131
- def property=(prop)
132
- @property = prop.to_s
247
+ # Property names are CSS identifiers.
248
+ def name=(name)
249
+ @property_name = name.to_s
133
250
  end
134
251
 
135
- def value=(val)
136
- @value = val
252
+ # The value of the Declaration's property.
253
+ def value=(value)
254
+ @value = value
137
255
  end
138
256
 
257
+ # Serialising a declaration produces a name-value pair separated by a colon
258
+ # and terminating with a semicolon, for instance
259
+ #
260
+ # declaration = Declaration.new("font-style", "italic")
261
+ # declaration.to_s # => "font-style:italic;"
262
+ #
263
+ # Since the formatting can be adjusted via the #format= accessor, the exact
264
+ # spacing of the declaration can be controlled if desired.
139
265
  def to_s
140
- sprintf(@format, @property, @value)
266
+ sprintf(@format, @property_name.to_s, @value.to_s)
141
267
  end
142
268
  end
143
269
 
@@ -171,6 +297,24 @@ module Stylish
171
297
  end
172
298
  end
173
299
 
300
+ # The Background class is a specialised kind of Declaration, geared towards
301
+ # dealing with the oddities of the background family of declarations, which
302
+ # can exist in both long- and shorthand forms.
303
+ #
304
+ # For example, these longhand background declarations
305
+ #
306
+ # background-color: #999;
307
+ # background-image: url('bg.png');
308
+ # background-repeat: repeat-x;
309
+ #
310
+ # could be compressed into a single shorthand declaration
311
+ #
312
+ # background: #999 url('bg.png') repeat-x;
313
+ #
314
+ # The Background class allows for easy conversion between these forms. It
315
+ # defaults to the longhand versions, allowing rules with stronger selector
316
+ # weighting to only override specific parts of other rules' background
317
+ # declarations.
174
318
  class Background < Declaration
175
319
  attr_reader :color,
176
320
  :image,
@@ -192,7 +336,7 @@ module Stylish
192
336
  HORIZONTAL_POSITIONS = ["left", "center", "right"]
193
337
  VERTICAL_POSITIONS = ["top", "center", "bottom"]
194
338
 
195
- # Create a new Background object
339
+ # Create a new Background object with the specified properties.
196
340
  def initialize(options)
197
341
  accept_format(/^\s*%s\s*:\s*%s;\s*$/m, "%s:%s;")
198
342
  self.value = options
@@ -206,7 +350,7 @@ module Stylish
206
350
 
207
351
  # Set the background image.
208
352
  def image=(path)
209
- @image = path if path.is_a?(String) || path.is_a?(File)
353
+ @image = Image.new(path) if path.is_a?(String)
210
354
  end
211
355
 
212
356
  # Set the background repeat.
@@ -228,11 +372,11 @@ module Stylish
228
372
  @attachment = val if ATTACHMENT_VALUES.include?(val)
229
373
  end
230
374
 
231
- # Set this to true to generate a compressed declaration, e.g.
375
+ # Set this to true to generate a shorthand declaration, e.g.
232
376
  #
233
377
  # background:#ccc url('bg.png') no-repeat 0 0;
234
378
  #
235
- # As opposed to the uncompressed version:
379
+ # As opposed to the longhand version:
236
380
  #
237
381
  # background-color:#ccc; background-image:url('bg.png');
238
382
  # background-repeat:no-repeat; background-position:0 0;
@@ -241,19 +385,19 @@ module Stylish
241
385
  @compressed = val == true || nil
242
386
  end
243
387
 
244
- # Override Declaration#property, since it's not compatible with the
388
+ # Override Declaration#name, since it's not compatible with the
245
389
  # internals of this class.
246
- def property
390
+ def name
247
391
  PROPERTIES.reject {|n, p| p.nil? }.map {|n, p|
248
392
  value = self.send(n)
249
393
  p.to_s unless value.nil?
250
394
  }.compact
251
395
  end
252
396
 
253
- # Override Declaration#property=, since it's not compatible with the
397
+ # Override Declaration#name=, since it's not compatible with the
254
398
  # internals of this class.
255
- def property=(val)
256
- raise NoMethodError, "property= is not defined for Background."
399
+ def name=(val)
400
+ raise NoMethodError, "name= is not defined for Background."
257
401
  end
258
402
 
259
403
  # Override Declaration#value, since it's not compatible with the internals
@@ -1,13 +1,88 @@
1
1
  module Stylish
2
2
 
3
+ # The generate method is the starting point for the stylesheet generation
4
+ # DSL. The method should be passed a block, and the various DSL methods then
5
+ # called within that context, e.g. as follows:
6
+ #
7
+ # style = Stylish.generate do
8
+ # body :margin => "1em"
9
+ # rule ".error" do
10
+ # p :color => "#f00"
11
+ # em :font_weight => "bold"
12
+ # end
13
+ # end
14
+ #
15
+ # When serialised, the generated stylesheet would look like this:
16
+ #
17
+ # body {margin:1em;}
18
+ # .error p {color:#f00;}
19
+ # .error em {font-weight:bold;}
20
+ #
21
+ # Further examples can be found in the example/ directory and in the
22
+ # +GenerateTest+ class.
23
+ #
24
+ # * example/tarski.rb
25
+ # * test/generate_test.rb
26
+ #
27
+ # The options argument is currently unused.
3
28
  def self.generate(options = {}, &block)
4
29
  dsl = Generate::Description.new
5
30
  dsl.instance_eval(&block)
6
31
  dsl.node
7
32
  end
8
33
 
34
+ # The +Generate+ module is a general namespace for the stylesheet generation
35
+ # DSL components. It contains various modules and classes which together are
36
+ # used to generate the intermediate data-structures of selector trees, and
37
+ # ultimately CSS code.
9
38
  module Generate
10
39
 
40
+ # The +parse_declarations+ method deals with three things: turning the
41
+ # hashes passed to the +Description#rule+ method into +Declarations+
42
+ # objects; renaming property names to use dashes rather than underscores
43
+ # (since the former are correct CSS but are invalid Ruby, at least when
44
+ # quotation marks are not used to delimit the symbol); and handling special
45
+ # cases.
46
+ #
47
+ # There are currently two main special cases: colours and backgrounds. Each
48
+ # of these property types have their own +Stylish+ classes, and thus to
49
+ # create a rich datastructure which takes advantage of these powerful
50
+ # classes the declarations passed to the stylesheet generation DSL need to
51
+ # be parsed with this in mind.
52
+ def self.parse_declarations(declarations)
53
+ declarations.to_a.inject(Declarations.new) do |ds, declaration|
54
+ key, value = declaration
55
+ key = key.to_s.sub("_", "-").to_sym
56
+
57
+ if key == :background
58
+ declaration = Background.new(value)
59
+ elsif key == :color
60
+ declaration = Declaration.new("color", Color.new(value))
61
+ else
62
+ declaration = Declaration.new(key, value)
63
+ end
64
+
65
+ ds << declaration
66
+ end
67
+ end
68
+
69
+ # Often the selector associated with a call to the rule method would simply
70
+ # be a single HTML element name. This has been factored out by adding
71
+ # methods to the Description DSL class corresponding to all the HTML
72
+ # elements. Consider the following (contrived) example.
73
+ #
74
+ # Stylish.generate do
75
+ # body do
76
+ # div do
77
+ # a :font_weight => bold
78
+ # end
79
+ # end
80
+ # end
81
+ #
82
+ # Which would then serialise to the following:
83
+ #
84
+ # body div a {font-weight:bold;}
85
+ #
11
86
  module ElementMethods
12
87
  HTML_ELEMENTS.each do |element|
13
88
  next if self.respond_to?(element)
@@ -20,40 +95,98 @@ module Stylish
20
95
  end
21
96
  end
22
97
 
98
+ # Description objects are the core of the stylesheet generation DSL. Blocks
99
+ # passed into the +Stylish#generate_ method are executed in the context of
100
+ # a +Description+ object. All the DSL methods, +rule+, +comment+, and all
101
+ # the HTML element methods are all methods on instances of the
102
+ # +Description+ class.
23
103
  class Description
24
104
  include ElementMethods
25
105
 
26
- attr_accessor :node
106
+ attr_reader :node
27
107
 
108
+ # +Description+ instances are associated with a particular node in a
109
+ # selector tree; if no node is assigned to them on creation, they
110
+ # associate with a new root, i.e. a +Stylesheet+ object.
28
111
  def initialize(context = nil)
29
112
  @node = context || Stylesheet.new
30
113
  end
31
114
 
115
+ # The +rule+ method is the most general and powerful part of the DSL. It
116
+ # can be used to add a single +Rule+, with attendant declarations, or to
117
+ # create a selector namespace within which further rules or namespaces
118
+ # can be added.
119
+ #
120
+ # The nested structure created is precisely that of a selector tree; the
121
+ # rule method adds +Rule+ leaves and +SelectorScope+ nodes to the tree,
122
+ # whose root is a +Stylesheet+ object.
123
+ #
124
+ # Either a set of declarations or a block must be passed to the method;
125
+ # failing to do so will result in an early return which creates no
126
+ # additional objects. The following example demonstrates the various ways
127
+ # in which the method can be used:
128
+ #
129
+ # Stylish.generate do
130
+ # rule ".section", :margin_bottom => "10px"
131
+ #
132
+ # rule "form" do
133
+ # rule ".notice", :color => "#00f"
134
+ # rule "input[type=submit]", :font_weight => "normal"
135
+ # end
136
+ #
137
+ # rule "body", :padding => "0.5em" do
138
+ # rule "div", :margin => "2px"
139
+ # end
140
+ # end
141
+ #
142
+ # This would produce a stylesheet with the following rules:
143
+ #
144
+ # .section {margin-bottom:10px;}
145
+ #
146
+ # form .notice {color:#00f;}
147
+ # form input[type=submit] {font-weight:normal;}
148
+ #
149
+ # body {padding:0.5em;}
150
+ # body div {margin:2px;}
151
+ #
152
+ # Usefully, a call to #rule which passes in both declarations and a block
153
+ # will produce a single +Rule+ with the declarations attached, then
154
+ # create a new +SelectorScope+ node with the same selectors and execute
155
+ # the block in that context.
156
+ #
157
+ # If several selectors and a block are passed to +rule+, new
158
+ # +SelectorScope+ nodes will be created for each selector and the block
159
+ # will be executed in all the contexts created, not just one.
32
160
  def rule(selectors, declarations = nil, &block)
33
161
  return unless declarations || block
34
162
 
35
163
  selectors = [selectors] unless selectors.is_a?(Array)
36
164
  selectors.map! {|s| Selector.new(s) }
37
165
 
38
- declarations = declarations.to_a.map do |p, v|
39
- Declaration.new(p.to_s.sub("_", "-"), v)
40
- end
166
+ declarations = Generate.parse_declarations(declarations)
41
167
 
42
168
  unless block
43
169
  @node << Rule.new(selectors, declarations)
44
170
  else
45
171
  selectors.each do |selector|
46
172
  unless declarations.empty?
47
- @node << Rule.new(selector, declarations)
173
+ @node << Rule.new([selector], declarations)
48
174
  end
49
175
 
50
- new_node = Tree::SelectorScope.new(selector.to_s)
176
+ new_node = Tree::SelectorScope.new(selector)
51
177
  @node << new_node
52
178
 
53
179
  self.class.new(new_node).instance_eval(&block)
54
180
  end
55
181
  end
56
182
  end
183
+
184
+ # Adds a +Comment+ object to the current node. This method simply hands
185
+ # its arguments off to the +Comment+ initialiser, and hence implements
186
+ # its API precisely.
187
+ def comment(*args)
188
+ @node << Comment.new(*args)
189
+ end
57
190
  end
58
191
 
59
192
  end
@@ -0,0 +1,32 @@
1
+ module Stylish
2
+
3
+ # Instances of the Image class are used to represent paths to images,
4
+ # generally background images.
5
+ class Image
6
+ include Formattable
7
+
8
+ attr_accessor :path
9
+
10
+ # Image instances are serialised to URI values. The path to the image file
11
+ # can be surrounded by either single quotes, double quotes or neither;
12
+ # single quotes are the default in Stylish.
13
+ def initialize(path)
14
+ accept_format(/^url\(\s*('|")?%s\1\s*\)$/, "url('%s')")
15
+ @path = path
16
+ end
17
+
18
+ # Serialising Image objects to a string produces the URI values seen in
19
+ # background-image declarations, e.g.
20
+ #
21
+ # image = Image.new("test.png")
22
+ # image.to_s # => "url('test.png')"
23
+ #
24
+ # background = Stylish::Background.new(:image => "test.png")
25
+ # background.to_s # => "background-image:url('test.png');"
26
+ #
27
+ def to_s
28
+ sprintf(@format, path.to_s)
29
+ end
30
+ end
31
+
32
+ end
data/lib/stylish/tree.rb CHANGED
@@ -1,11 +1,5 @@
1
1
  module Stylish
2
2
 
3
- def self.generate(options = {}, &block)
4
- dsl = Tree::Description.new
5
- dsl.instance_eval(&block)
6
- dsl.node
7
- end
8
-
9
3
  # The objects defined in the Tree module allow for the creation of nested
10
4
  # trees of selector scopes. These intermediate data structures can be used to
11
5
  # help factor out some of the repetitiveness of CSS code, and can be easily
@@ -51,7 +45,7 @@ module Stylish
51
45
  def initialize(selector)
52
46
  accept_format(/\s*/m, "\n")
53
47
 
54
- @scope = selector
48
+ @scope = Selector.new(selector)
55
49
  @nodes = []
56
50
  end
57
51
 
@@ -92,7 +86,7 @@ module Stylish
92
86
  # Recursively serialise the selector tree.
93
87
  def to_s(scope = "")
94
88
  return "" if @nodes.empty?
95
- scope = scope.empty? ? @scope : scope + " " + @scope
89
+ scope = scope.empty? ? @scope.to_s : scope + " " + @scope.to_s
96
90
  @nodes.map {|node| node.to_s(scope) }.join(@format)
97
91
  end
98
92
 
@@ -106,17 +100,22 @@ module Stylish
106
100
  leaves(Rule)
107
101
  end
108
102
 
103
+ # Recursively return all the comments in the selector tree.
104
+ def comments
105
+ leaves(Comment)
106
+ end
107
+
109
108
  # Recursively return all the leaves of any, or a given type in a selector
110
109
  # tree.
111
110
  def leaves(type = nil)
112
- @nodes.inject([]) do |rules, node|
111
+ @nodes.inject([]) do |leaves, node|
113
112
  if node.leaf?
114
- rules << node if type.nil? || node.is_a?(type)
115
- elsif node.is_a?(SelectorScope)
116
- rules.concat(node.rules)
113
+ leaves << node if type.nil? || node.is_a?(type)
114
+ else
115
+ leaves.concat(node.leaves(type))
117
116
  end
118
117
 
119
- rules
118
+ leaves
120
119
  end
121
120
  end
122
121
  end
data/lib/stylish.rb CHANGED
@@ -6,5 +6,6 @@ require 'stylish/formattable'
6
6
  require 'stylish/tree'
7
7
  require 'stylish/core'
8
8
  require 'stylish/stylesheet'
9
+ require 'stylish/image'
9
10
  require 'stylish/color'
10
11
  require 'stylish/generate'
@@ -11,19 +11,17 @@ class BackgroundTest < Test::Unit::TestCase
11
11
 
12
12
  def test_valid_background_colors
13
13
  assert_equal("#ccc", @composite.color.to_hex)
14
- assert_equal("black", Stylish::Background.new(:color => :black).color.to_s)
14
+ assert_equal("black", Stylish::Background.new(:color => "black").color.to_s)
15
15
  end
16
16
 
17
17
  def test_background_transparencies
18
- assert_equal([0, 0, 0, 0], Stylish::Background.new(:color => :transparent).color.value)
18
+ assert_equal([0, 0, 0, 0], Stylish::Background.new(:color => "transparent").color.value)
19
19
  end
20
20
 
21
21
  def test_valid_background_images
22
- assert_equal("images/test.png", @composite.image)
23
- assert_equal("background.jpg", Stylish::Background.new(:image => "background.jpg").image)
24
- File.open(__FILE__) do |file|
25
- assert_not_nil(Stylish::Background.new(:image => file))
26
- end
22
+ assert_equal("images/test.png", @composite.image.path)
23
+ assert_equal("background.jpg",
24
+ Stylish::Background.new(:image => "background.jpg").image.path)
27
25
  end
28
26
 
29
27
  def test_valid_background_repeats
@@ -42,12 +40,13 @@ class BackgroundTest < Test::Unit::TestCase
42
40
 
43
41
  def test_valid_background_attachments
44
42
  assert_equal("scroll", @composite.attachment)
45
- assert_equal("fixed", Stylish::Background.new(:attachment => "fixed").attachment)
43
+ assert_equal("fixed",
44
+ Stylish::Background.new(:attachment => "fixed").attachment)
46
45
  end
47
46
 
48
47
  def test_invalid_background_colors
49
48
  assert_raise ArgumentError do
50
- Stylish::Background.new(:color => 'sky-blue')
49
+ Stylish::Background.new(:color => "sky-blue")
51
50
  end
52
51
  end
53
52
 
@@ -78,36 +77,44 @@ class BackgroundTest < Test::Unit::TestCase
78
77
 
79
78
  def test_declaration_property
80
79
  assert_equal(["background-color", "background-repeat"],
81
- Stylish::Background.new(:color => "red", :repeat => "no-repeat").property)
80
+ Stylish::Background.new(:color => "red", :repeat => "no-repeat").name)
82
81
  end
83
82
 
84
83
  def test_declaration_property_assignment
85
- background = Stylish::Background.new(:color => "red", :repeat => "no-repeat")
84
+ background = Stylish::Background.new(:color => "red",
85
+ :repeat => "no-repeat")
86
86
 
87
87
  assert_raise(NoMethodError) do
88
- background.property = "display"
88
+ background.name = "display"
89
89
  end
90
90
  end
91
91
 
92
92
  def test_declaration_value
93
- background = Stylish::Background.new(:color => "red", :repeat => "no-repeat")
93
+ background = Stylish::Background.new(:color => "red",
94
+ :repeat => "no-repeat")
94
95
 
95
96
  assert_equal("no-repeat", background.value[1])
96
97
  assert_equal("red", background.value[0].to_s)
97
98
  end
98
99
 
99
100
  def test_declaration_value_assignment
100
- background = Stylish::Background.new(:color => "red", :repeat => "no-repeat")
101
+ background = Stylish::Background.new(:color => "red")
101
102
  background.value = {:image => "mondrian.jpg"}
102
103
 
103
- assert_equal("mondrian.jpg", background.image)
104
+ assert_equal("mondrian.jpg", background.image.path)
104
105
  end
105
106
 
106
107
  def test_declaration_value_assignment_errors
107
- background = Stylish::Background.new(:color => "red", :repeat => "no-repeat")
108
+ background = Stylish::Background.new(:color => "red")
108
109
 
109
110
  assert_raise ArgumentError do
110
111
  background.value = "mondrian.jpg"
111
112
  end
112
113
  end
114
+
115
+ def test_image_declaration_serialisation
116
+ background = Stylish::Background.new(:image => "test.png")
117
+
118
+ assert_equal("background-image:url('test.png');", background.to_s)
119
+ end
113
120
  end
@@ -2,16 +2,20 @@ require 'test/unit'
2
2
  require './lib/stylish'
3
3
 
4
4
  class DeclarationTest < Test::Unit::TestCase
5
-
6
- def test_empty_value
7
- hollow = Stylish::Declaration.new("cursor")
8
-
9
- assert_not_nil(hollow.property)
10
- assert_nil(hollow.value)
5
+
6
+ def setup
7
+ @declaration = Stylish::Declaration.new("color", Stylish::Color.new("000"))
8
+ end
9
+
10
+ def test_naming
11
+ assert_equal("color", @declaration.name)
12
+ end
13
+
14
+ def test_value
15
+ assert_equal("#000", @declaration.value.to_s)
11
16
  end
12
17
 
13
18
  def test_to_string
14
- dec = Stylish::Declaration.new("color", "#000")
15
- assert_equal("color:#000;", dec.to_s)
19
+ assert_equal("color:#000;", @declaration.to_s)
16
20
  end
17
21
  end
@@ -10,10 +10,12 @@ class DeclarationsTest < Test::Unit::TestCase
10
10
  end
11
11
 
12
12
  def test_join
13
- assert_equal("border-color:red; background-color:blue; background-image:test.png;", @ds.join)
13
+ assert_equal("border-color:red; background-color:blue; " +
14
+ "background-image:url('test.png');", @ds.join)
14
15
  end
15
16
 
16
17
  def test_to_string
17
- assert_equal("border-color:red; background-color:blue; background-image:test.png;", @ds.to_s)
18
+ assert_equal("border-color:red; background-color:blue; " +
19
+ "background-image:url('test.png');", @ds.to_s)
18
20
  end
19
21
  end
@@ -13,6 +13,15 @@ class GenerateTest < Test::Unit::TestCase
13
13
  ".unchecked {font-style:italic;}", style.to_s)
14
14
  end
15
15
 
16
+ def test_compound_rules
17
+ style = Stylish.generate do
18
+ rule ["abbr", "acronym"], :margin_bottom => "2em"
19
+ end
20
+
21
+ assert_equal(1, style.rules.length)
22
+ assert_equal("abbr, acronym {margin-bottom:2em;}", style.rules.first.to_s)
23
+ end
24
+
16
25
  def test_nested_rules
17
26
  style = Stylish.generate do
18
27
  rule "body" do
@@ -51,4 +60,24 @@ class GenerateTest < Test::Unit::TestCase
51
60
 
52
61
  assert_equal("body div p {line-height:1.5;}", style.to_s)
53
62
  end
63
+
64
+ def test_nested_declarations
65
+ style = Stylish.generate do
66
+ fieldset :background => {:color => [0, 0, 255], :image => "fieldset.png"}
67
+ end
68
+
69
+ assert_instance_of(Stylish::Background,
70
+ style.rules.first.declarations.first)
71
+ end
72
+
73
+ def test_comments
74
+ style = Stylish.generate do
75
+ comment "A glorious comment!"
76
+ comment "An inglorious comment.", "An additional note."
77
+ end
78
+
79
+ assert_equal(2, style.comments.length)
80
+ assert_equal("A glorious comment!", style.comments[0].header)
81
+ assert_equal("An additional note.", style.comments[1].lines[0])
82
+ end
54
83
  end
@@ -0,0 +1,17 @@
1
+ require 'test/unit'
2
+ require './lib/stylish'
3
+
4
+ class ImageTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @image = Stylish::Image.new("test.png")
8
+ end
9
+
10
+ def test_image_path
11
+ assert_equal("test.png", @image.path)
12
+ end
13
+
14
+ def test_image_serialisation
15
+ assert_equal("url('test.png')", @image.to_s)
16
+ end
17
+ end
data/test/rule_test.rb CHANGED
@@ -6,8 +6,8 @@ class RuleTest < Test::Unit::TestCase
6
6
  def setup
7
7
  @rule = Stylish::Rule.new([
8
8
  Stylish::Selector.new("div .alert, body .error")],
9
- Stylish::Declaration.new("font-style", "italic"),
10
- Stylish::Declaration.new("font-weight", "bold"))
9
+ [Stylish::Declaration.new("font-style", "italic"),
10
+ Stylish::Declaration.new("font-weight", "bold")])
11
11
  end
12
12
 
13
13
  def test_rule_serialisation
@@ -8,7 +8,7 @@ class StylesheetTest < Test::Unit::TestCase
8
8
  @node = Stylish::Tree::SelectorScope.new("div")
9
9
  @onde = Stylish::Tree::SelectorScope.new("span")
10
10
  @rule = Stylish::Rule.new([Stylish::Selector.new("em")],
11
- Stylish::Declaration.new("font-weight", "bold"))
11
+ [Stylish::Declaration.new("font-weight", "bold")])
12
12
  end
13
13
 
14
14
  def test_node_addition
data/test/tree_test.rb CHANGED
@@ -4,10 +4,12 @@ require './lib/stylish'
4
4
  class TreeTest < Test::Unit::TestCase
5
5
 
6
6
  def setup
7
- @tree = Stylish::Stylesheet.new
8
- @node = Stylish::Tree::SelectorScope.new(".test")
9
- @rule = Stylish::Rule.new([Stylish::Selector.new("p")],
10
- Stylish::Declaration.new("font-weight", "bold"))
7
+ @tree = Stylish::Stylesheet.new
8
+ @node = Stylish::Tree::SelectorScope.new(".test")
9
+ @rule = Stylish::Rule.new([Stylish::Selector.new("p")],
10
+ [Stylish::Declaration.new("font-weight", "bold")])
11
+ @comment = Stylish::Comment.new("Comment header",
12
+ {:author => "Some Body"})
11
13
  end
12
14
 
13
15
  def test_appending
@@ -28,11 +30,13 @@ class TreeTest < Test::Unit::TestCase
28
30
  def test_rules_collation
29
31
  @node << @rule
30
32
  @node << @rule
33
+ @node << @comment
31
34
  @tree << @node
32
35
  @tree << @node
33
36
 
37
+ assert_equal(6, @tree.leaves.length)
34
38
  assert_equal(4, @tree.rules.length)
35
- assert_equal(4, @tree.leaves.length)
39
+ assert_equal(2, @tree.comments.length)
36
40
  end
37
41
 
38
42
  def test_node_reader
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ionfish-stylish
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benedict Eastaugh
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-11 00:00:00 -07:00
12
+ date: 2009-04-13 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -30,6 +30,7 @@ files:
30
30
  - lib/stylish/core.rb
31
31
  - lib/stylish/formattable.rb
32
32
  - lib/stylish/generate.rb
33
+ - lib/stylish/image.rb
33
34
  - lib/stylish/stylesheet.rb
34
35
  - lib/stylish/tree.rb
35
36
  - test/background_test.rb
@@ -39,6 +40,7 @@ files:
39
40
  - test/declarations_test.rb
40
41
  - test/formattable_test.rb
41
42
  - test/generate_test.rb
43
+ - test/image_test.rb
42
44
  - test/rule_test.rb
43
45
  - test/selector_test.rb
44
46
  - test/selectors_test.rb
@@ -78,6 +80,7 @@ test_files:
78
80
  - test/declarations_test.rb
79
81
  - test/formattable_test.rb
80
82
  - test/generate_test.rb
83
+ - test/image_test.rb
81
84
  - test/rule_test.rb
82
85
  - test/selector_test.rb
83
86
  - test/selectors_test.rb