ionfish-stylish 0.1.0 → 0.1.2

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.
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