ionfish-stylish 0.1.2 → 0.1.3

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/History.txt ADDED
@@ -0,0 +1,32 @@
1
+ === Version 0.1.3 (TODO)
2
+
3
+ Variables in the DSL
4
+ * Selector and property variables
5
+ * New descope example
6
+ * Limited CSS3 background support
7
+ * Cleaned up various things
8
+
9
+
10
+ === Version 0.1.2 (2009-04-12)
11
+
12
+ Documentation release
13
+ * Documented the stylesheet generation DSL
14
+ * Updated example code in the README
15
+ * Added an Image class to handle background images
16
+
17
+
18
+ === Version 0.1.1 (2009-04-12)
19
+
20
+ More DSL features
21
+ * Added the ability to append comments through the stylesheet generation DSL
22
+ * Fixed some parsing and selector serialisation problems
23
+ * Updated the main example file to work with the new DSL
24
+ * Completed the documentation of the core classes
25
+
26
+
27
+ === Version 0.1.0 (2009-04-10)
28
+
29
+ First major release
30
+ * New selector scope tree, which serialises to a valid stylesheet
31
+ * Stylesheet generation DSL based on the tree datastructure
32
+ * HSL(A) colour space support, complementing the existing RGB support
data/README.md CHANGED
@@ -23,6 +23,8 @@ Calling the stylesheet's `to_s` method would produce the following
23
23
  .content h2 {font-size:2em;}
24
24
  .content p {margin:0 0 1em 0;}
25
25
 
26
+ A number of additional examples are available in the example/ directory.
27
+
26
28
 
27
29
  Compatibility
28
30
  -------------
@@ -36,11 +38,6 @@ Future considerations
36
38
 
37
39
  * Add a Border class to complement Background.
38
40
  * Add better support for CSS3 properties to Background.
39
- * Add a native mapping construct to allow symbol lookup for alternate
40
- stylesheets.
41
- * Change stylesheet generation to a two-step process where the tree structure
42
- is generated in the first step and symbols (for example, those employed by
43
- a mapping construct) are evaluated the second.
44
41
  * Fundamental objects like percentages need their own classes rather than
45
42
  being dealt with in an ad-hoc manner by higher-level objects.
46
43
  * Add a parser so CSS can be read as well as written.
data/Rakefile CHANGED
@@ -11,14 +11,16 @@ begin
11
11
  s.authors = ["Benedict Eastaugh"]
12
12
  end
13
13
  rescue LoadError
14
- puts "Jeweler not available. Install it with: sudo gem install "
15
- + "technicalpickles-jeweler -s http://gems.github.com"
14
+ puts "Jeweler not available. Install it with: sudo gem install " +
15
+ "technicalpickles-jeweler -s http://gems.github.com"
16
16
  end
17
17
 
18
18
  task :default => :test
19
19
 
20
20
  desc "Run the Stylish test suite"
21
21
  task :test do
22
+ require 'test/unit'
23
+
22
24
  testdir = "test"
23
25
  Dir.foreach(testdir) do |f|
24
26
  path = "#{testdir}/#{f}"
@@ -28,7 +30,16 @@ task :test do
28
30
  end
29
31
  end
30
32
 
31
- desc "Run a Stylish example"
32
- task :example do
33
- require 'example/tarski'
33
+ namespace :example do
34
+
35
+ desc "An extended generator DSL example."
36
+ task :tarski do
37
+ require 'example/tarski'
38
+ end
39
+
40
+ desc "Extract base rules from a selector tree."
41
+ task :descope do
42
+ require 'example/descope'
43
+ end
34
44
  end
45
+
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 1
3
- :patch: 2
3
+ :patch: 3
4
4
  :major: 0
@@ -0,0 +1,226 @@
1
+ module Stylish
2
+
3
+ # The Background class is a specialised kind of Declaration, geared towards
4
+ # dealing with the oddities of the background family of declarations, which
5
+ # can exist in both long- and shorthand forms.
6
+ #
7
+ # For example, these longhand background declarations
8
+ #
9
+ # background-color: #999;
10
+ # background-image: url('bg.png');
11
+ # background-repeat: repeat-x;
12
+ #
13
+ # could be compressed into a single shorthand declaration
14
+ #
15
+ # background: #999 url('bg.png') repeat-x;
16
+ #
17
+ # The Background class allows for easy conversion between these forms. It
18
+ # defaults to the longhand versions, allowing rules with stronger selector
19
+ # weighting to only override specific parts of other rules' background
20
+ # declarations.
21
+ class Background < Declaration
22
+ attr_reader :color,
23
+ :image,
24
+ :repeat,
25
+ :position,
26
+ :attachment,
27
+ :origin,
28
+ :break,
29
+ :compressed
30
+
31
+ PROPERTIES = [
32
+ [:color, "background-color"],
33
+ [:image, "background-image"],
34
+ [:repeat, "background-repeat"],
35
+ [:position, "background-position"],
36
+ [:attachment, "background-attachment"],
37
+ [:origin, "background-origin"],
38
+ [:break, "background-break"],
39
+ [:compressed]]
40
+
41
+ REPEAT_VALUES = ["repeat-x", "repeat-y", "repeat",
42
+ "space", "round", "no-repeat"]
43
+ ATTACHMENT_VALUES = ["scroll", "fixed", "local"]
44
+ HORIZONTAL_POSITIONS = ["left", "center", "right"]
45
+ VERTICAL_POSITIONS = ["top", "center", "bottom"]
46
+ ORIGIN_VALUES = ["border-box", "padding-box", "content-box"]
47
+ BREAK_VALUES = ["bounding-box", "each-box", "continuous"]
48
+
49
+ # Create a new Background object with the specified properties.
50
+ def initialize(options)
51
+ accept_format(/^\s*%s\s*:\s*%s;\s*$/m, "%s:%s;")
52
+ self.value = options
53
+ end
54
+
55
+ # Input validation for colours is handled by the Color class, which will
56
+ # raise an ArgumentError if the argument is an invalid colour value.
57
+ def color=(val)
58
+ @color = Color.new(val)
59
+ end
60
+
61
+ # Set the background image(s). As of CSS3, elements may have multiple
62
+ # background images, so this method attempts to provide a backwards-
63
+ # compatible solution.
64
+ #
65
+ # background = Background.new :image => "sky.png", :compressed => true
66
+ # background.to_s # => "background:url('sky.png');"
67
+ #
68
+ # background.image = ["ball.png", "grass.png"]
69
+ # background.to_s # => "background:url('ball.png'), url('grass.png');"
70
+ #
71
+ def image=(paths)
72
+ paths = [paths] if paths.is_a?(String)
73
+ @image = paths.inject([]) {|images, path| images << Image.new(path) }
74
+
75
+ if @image.length < 2
76
+ @image = @image.first
77
+ else
78
+ def @image.to_s
79
+ join(", ")
80
+ end
81
+ end
82
+ end
83
+
84
+ # Set the background repeat(s). As of CSS3, the background-repeat property
85
+ # may have multiple values, so this method provides a backwards-compatible
86
+ # solution.
87
+ #
88
+ # repeating = Background.new :repeat => ["repeat-x", "repeat-y"]
89
+ # repeating.to_s # => "background-repeat:repeat-x, repeat-y;"
90
+ #
91
+ def repeat=(repeats)
92
+ repeats = [repeats] if repeats.is_a? String
93
+ @repeat = repeats.find_all {|r| REPEAT_VALUES.include? r }
94
+
95
+ if @repeat.length < 2
96
+ @repeat = @repeat.first
97
+ else
98
+ def @repeat.to_s
99
+ join(", ")
100
+ end
101
+ end
102
+ end
103
+
104
+ # Only position keywords are currently handled, not percentages or lengths.
105
+ def position=(val)
106
+ xpos, ypos = val.split(/\s+/) << "center"
107
+ if HORIZONTAL_POSITIONS.include?(xpos) && VERTICAL_POSITIONS.include?(ypos)
108
+ @position = [xpos, ypos]
109
+ end
110
+ end
111
+
112
+ # The background-attachment property takes a limited range of values, so
113
+ # only a value within that range will be accepted.
114
+ def attachment=(attachments)
115
+ attachments = [attachments] if attachments.is_a? String
116
+ @attachment = attachments.find_all {|a| ATTACHMENT_VALUES.include? a }
117
+
118
+ if @attachment.length < 2
119
+ @attachment = @attachment.first
120
+ else
121
+ def @attachment.to_s
122
+ join(", ")
123
+ end
124
+ end
125
+ end
126
+
127
+ # The background-origin property specifies the background positioning area.
128
+ # It is a CSS3 property which takes multiple values.
129
+ #
130
+ # original = Background.new :origin => ["padding-box", "content-box"]
131
+ # original.to_s # => background-origin:padding-box, content-box;
132
+ #
133
+ def origin=(origins)
134
+ origins = [origins] if origins.is_a? String
135
+ @origin = origins.find_all {|o| ORIGIN_VALUES.include? o }
136
+
137
+ if @origin.length < 2
138
+ @origin = @origin.first
139
+ else
140
+ def @origin.to_s
141
+ join(", ")
142
+ end
143
+ end
144
+ end
145
+
146
+ # The background-break property, defined in CSS3, specifies how the
147
+ # background positioning area is derived when an element is broken into
148
+ # multiple boxes.
149
+ #
150
+ # broken = Background.new :break => "bounding-box"
151
+ # broken.to_s # => "background-break:bounding-box;"
152
+ #
153
+ def break=(value)
154
+ @break = value if BREAK_VALUES.include? value
155
+ end
156
+
157
+ # Set this to true to generate a shorthand declaration, e.g.
158
+ #
159
+ # background:#ccc url('bg.png') no-repeat 0 0;
160
+ #
161
+ # As opposed to the longhand version:
162
+ #
163
+ # background-color:#ccc; background-image:url('bg.png');
164
+ # background-repeat:no-repeat; background-position:0 0;
165
+ #
166
+ def compressed=(val)
167
+ @compressed = val == true || nil
168
+ end
169
+
170
+ # Override Declaration#name, since it's not compatible with the
171
+ # internals of this class.
172
+ def name
173
+ PROPERTIES.reject {|n, p| p.nil? }.map {|n, p|
174
+ value = self.send(n)
175
+ p.to_s unless value.nil?
176
+ }.compact
177
+ end
178
+
179
+ # Override Declaration#name=, since it's not compatible with the
180
+ # internals of this class.
181
+ def name=(val)
182
+ raise NoMethodError, "name= is not defined for Background."
183
+ end
184
+
185
+ # Override Declaration#value, since it's not compatible with the internals
186
+ # of this class.
187
+ def value(name_and_value = false)
188
+ PROPERTIES.reject {|n, p| p.nil? }.map {|n, p|
189
+ value = self.send(n)
190
+ next if value.nil?
191
+ name_and_value ? [p.to_s, value] : value
192
+ }.compact
193
+ end
194
+
195
+ # Override Declaration#value=, since it's not compatible with the internals
196
+ # of this class.
197
+ def value=(options)
198
+ unless options.is_a? Hash
199
+ raise ArgumentError, "Argument must be a hash of background properties"
200
+ end
201
+
202
+ PROPERTIES.each do |name, property|
203
+ self.send(:"#{name.to_s}=", options[name]) if options[name]
204
+ end
205
+ end
206
+
207
+ # Generate a string representation of a Background instance.
208
+ #
209
+ # There are two kinds of representation, each of which have slightly
210
+ # different CSS semantics. If compressed is set to true, this method will
211
+ # produce a shorthand CSS declaration such as the following:
212
+ #
213
+ # background: #fff url('bg.png') no-repeat 50% 0;
214
+ #
215
+ # Otherwise it will produce an unordered list of individual background
216
+ # declarations.
217
+ def to_s(symbols = {})
218
+ if @compressed
219
+ "background:#{self.value(true).map {|p, v| v }.compact.join(" ")};"
220
+ else
221
+ self.value(true).map {|p, v| sprintf(@format, p, v.to_s) }.join(" ")
222
+ end
223
+ end
224
+ end
225
+
226
+ end
data/lib/stylish/color.rb CHANGED
@@ -1,6 +1,3 @@
1
- require 'mathn'
2
- require 'rational'
3
-
4
1
  module Stylish #:nodoc:
5
2
 
6
3
  # The Color class is intended to eventually implement the entirety of the
@@ -214,7 +211,7 @@ module Stylish #:nodoc:
214
211
  [:red, :green, :blue].each do |color|
215
212
  reader = color
216
213
  writer = :"#{color}="
217
- color = :"@#{color.to_s}"
214
+ color = :"@#{color.to_s}"
218
215
 
219
216
  unless self.respond_to?(reader)
220
217
  self.send(:define_method, reader) do
@@ -246,11 +243,11 @@ module Stylish #:nodoc:
246
243
  # Attribute writer for the color's value. Uses the ColorStringParser inner
247
244
  # class to parse string values, and contains other logic to handle arrays.
248
245
  def value=(value)
249
- if value.is_a?(String) || value.is_a?(Symbol)
246
+ if value.is_a?(String)
250
247
  parser = ColorStringParser.new
251
248
  @type, @red, @green, @blue, @opacity = parser.parse(value)
252
249
  return unless @type.nil?
253
- elsif value.is_a?(Array) && (3..4).include?(value.length)
250
+ else
254
251
  rgb = value[0..2].inject([]) do |rgb, v|
255
252
  if v.is_a?(Integer) || v.is_a?(Float)
256
253
  rgb << v
@@ -263,14 +260,12 @@ module Stylish #:nodoc:
263
260
  rgb
264
261
  end
265
262
 
266
- if rgb.length == 3
267
- if value.length == 3
268
- @red, @green, @blue, @opacity = rgb << nil
269
- @type = :rgb and return
270
- elsif value.length == 4 and value[3].kind_of?(Numeric)
271
- @red, @green, @blue, @opacity = rgb << value[3].to_f
272
- @type = :rgba and return
273
- end
263
+ if value.length < 4
264
+ @red, @green, @blue, @opacity = rgb << nil
265
+ @type = :rgb and return
266
+ else
267
+ @red, @green, @blue, @opacity = rgb << value[3].to_f
268
+ @type = :rgba and return
274
269
  end
275
270
  end
276
271
 
@@ -289,10 +284,7 @@ module Stylish #:nodoc:
289
284
  # color.opacity # => 0.5
290
285
  #
291
286
  def opacity=(value)
292
- return unless value.is_a?(Integer) || value.is_a?(Float)
293
- return if value < 0 || value > 1
294
-
295
- @opacity = value
287
+ @opacity = value unless value < 0 || value > 1
296
288
  end
297
289
 
298
290
  # Returns a color keyword string if the RGB value of the color is equal to
@@ -381,7 +373,7 @@ module Stylish #:nodoc:
381
373
  # color.type = :rgb
382
374
  # color.to_s # => "rgb(0, 0, 0)"
383
375
  #
384
- def to_s
376
+ def to_s(symbols = {})
385
377
  return "inherit" if @type == :inherit
386
378
 
387
379
  self.send(:"to_#{self.type.to_s}")
@@ -415,7 +407,9 @@ module Stylish #:nodoc:
415
407
  huer.call(red - green, 240)
416
408
  end
417
409
 
418
- [hue].concat([saturation, lightness].map {|r| (r * 100).to_f.round.to_s + "%" })
410
+ [hue].concat([saturation, lightness].map {|r|
411
+ (r * 100).to_f.round.to_s + "%"
412
+ })
419
413
  end
420
414
 
421
415
  def hsla
data/lib/stylish/core.rb CHANGED
@@ -73,8 +73,9 @@ module Stylish
73
73
  end
74
74
 
75
75
  # Serialise the rule to valid CSS code.
76
- def to_s(scope = "")
77
- sprintf(@format, selectors.join(scope), @declarations.join)
76
+ def to_s(symbols = {}, scope = "")
77
+ sprintf(@format, selectors.join(symbols, scope),
78
+ @declarations.to_s(symbols))
78
79
  end
79
80
  end
80
81
 
@@ -150,7 +151,7 @@ module Stylish
150
151
  # the serialisation API of those trees, and thus the #to_s method has a
151
152
  # scope argument, which is in practice discarded when the serialisation of
152
153
  # the comment occurs.
153
- def to_s(scope = "")
154
+ def to_s(symbols = {}, scope = "")
154
155
  if @lines.empty? && @metadata.empty?
155
156
  sprintf("/**\n * %s\n */", @header)
156
157
  else
@@ -178,8 +179,8 @@ module Stylish
178
179
 
179
180
  # Selectors are immutable once created; the value of a given Selector must
180
181
  # be set when the object is created.
181
- def initialize(str)
182
- @selector = str.to_s
182
+ def initialize(selector)
183
+ @selector = selector
183
184
  end
184
185
 
185
186
  # Each Rule possesses one or more Selectors. Rules are often placed in
@@ -190,8 +191,9 @@ module Stylish
190
191
  #
191
192
  # The Selector class is also used internally by the Tree::SelectorScope
192
193
  # class, to store its scope value.
193
- def to_s(scope = "")
194
- (scope.empty? ? "" : scope + " ") + @selector.to_s
194
+ def to_s(symbols = {}, scope = "")
195
+ (scope.empty? ? "" : scope + " ") +
196
+ (@selector.is_a?(String) ? @selector.to_s : @selector.to_s(symbols))
195
197
  end
196
198
  end
197
199
 
@@ -211,15 +213,15 @@ module Stylish
211
213
  # The join method overrides the superclass' method in order to always use a
212
214
  # specific separator, and so that the scope that the selectors are being
213
215
  # used in can be passed through when Rules etc. are serialised.
214
- def join(scope = "")
216
+ def join(symbols = {}, scope = "")
215
217
  self.inject("") do |ss, s|
216
- (ss.empty? ? "" : ss + self.format) + s.to_s(scope)
218
+ (ss.empty? ? "" : ss + self.format) + s.to_s(symbols, scope)
217
219
  end
218
220
  end
219
221
 
220
222
  # The to_s method alternative way of calling the join method.
221
- def to_s(scope = "")
222
- self.join(scope)
223
+ def to_s(symbols = {}, scope = "")
224
+ self.join(symbols, scope)
223
225
  end
224
226
  end
225
227
 
@@ -262,12 +264,17 @@ module Stylish
262
264
  #
263
265
  # Since the formatting can be adjusted via the #format= accessor, the exact
264
266
  # spacing of the declaration can be controlled if desired.
265
- def to_s
266
- sprintf(@format, @property_name.to_s, @value.to_s)
267
+ def to_s(symbols = {})
268
+ if @value.is_a?(Generate::Variable) || @value.is_a?(Color)
269
+ value = @value.to_s(symbols)
270
+ else
271
+ value = @value.to_s
272
+ end
273
+
274
+ sprintf(@format, @property_name.to_s, value)
267
275
  end
268
276
  end
269
277
 
270
-
271
278
  # Declarations subclasses Array so that whenever #join is called, the
272
279
  # instance's format attribute will be used as the join string, rather than
273
280
  # the empty string.
@@ -292,151 +299,9 @@ module Stylish
292
299
  # format attribute. Assuming that its contents are indeed Declaration
293
300
  # objects, this will invoke their own #to_s method and generating correct
294
301
  # CSS code.
295
- def to_s
296
- self.join
297
- end
298
- end
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.
318
- class Background < Declaration
319
- attr_reader :color,
320
- :image,
321
- :repeat,
322
- :position,
323
- :attachment,
324
- :compressed
325
-
326
- PROPERTIES = [
327
- [:color, "background-color"],
328
- [:image, "background-image"],
329
- [:repeat, "background-repeat"],
330
- [:position, "background-position"],
331
- [:attachment, "background-attachment"],
332
- [:compressed]]
333
-
334
- REPEAT_VALUES = ["repeat", "repeat-x", "repeat-y", "no-repeat"]
335
- ATTACHMENT_VALUES = ["scroll", "fixed", "inherit"]
336
- HORIZONTAL_POSITIONS = ["left", "center", "right"]
337
- VERTICAL_POSITIONS = ["top", "center", "bottom"]
338
-
339
- # Create a new Background object with the specified properties.
340
- def initialize(options)
341
- accept_format(/^\s*%s\s*:\s*%s;\s*$/m, "%s:%s;")
342
- self.value = options
343
- end
344
-
345
- # Input validation for colours is handled by the Color class, which will
346
- # raise an ArgumentError if the argument is an invalid colour value.
347
- def color=(val)
348
- @color = Color.new(val)
349
- end
350
-
351
- # Set the background image.
352
- def image=(path)
353
- @image = Image.new(path) if path.is_a?(String)
354
- end
355
-
356
- # Set the background repeat.
357
- def repeat=(val)
358
- @repeat = val if REPEAT_VALUES.include?(val)
359
- end
360
-
361
- # Only position keywords are currently handled, not percentages or lengths.
362
- def position=(val)
363
- xpos, ypos = val.split(/\s+/) << "center"
364
- if HORIZONTAL_POSITIONS.include?(xpos) && VERTICAL_POSITIONS.include?(ypos)
365
- @position = [xpos, ypos]
366
- end
367
- end
368
-
369
- # The background-attachment property takes a limited range of values, so
370
- # only a value within that range will be accepted.
371
- def attachment=(val)
372
- @attachment = val if ATTACHMENT_VALUES.include?(val)
373
- end
374
-
375
- # Set this to true to generate a shorthand declaration, e.g.
376
- #
377
- # background:#ccc url('bg.png') no-repeat 0 0;
378
- #
379
- # As opposed to the longhand version:
380
- #
381
- # background-color:#ccc; background-image:url('bg.png');
382
- # background-repeat:no-repeat; background-position:0 0;
383
- #
384
- def compressed=(val)
385
- @compressed = val == true || nil
386
- end
387
-
388
- # Override Declaration#name, since it's not compatible with the
389
- # internals of this class.
390
- def name
391
- PROPERTIES.reject {|n, p| p.nil? }.map {|n, p|
392
- value = self.send(n)
393
- p.to_s unless value.nil?
394
- }.compact
395
- end
396
-
397
- # Override Declaration#name=, since it's not compatible with the
398
- # internals of this class.
399
- def name=(val)
400
- raise NoMethodError, "name= is not defined for Background."
401
- end
402
-
403
- # Override Declaration#value, since it's not compatible with the internals
404
- # of this class.
405
- def value(name_and_value = false)
406
- PROPERTIES.reject {|n, p| p.nil? }.map {|n, p|
407
- value = self.send(n)
408
- next if value.nil?
409
- name_and_value ? [p.to_s, value] : value
410
- }.compact
411
- end
412
-
413
- # Override Declaration#value=, since it's not compatible with the internals
414
- # of this class.
415
- def value=(options)
416
- unless options.is_a? Hash
417
- raise ArgumentError, "Argument must be a hash of background properties"
418
- end
419
-
420
- PROPERTIES.each do |name, property|
421
- self.send(:"#{name.to_s}=", options[name]) if options[name]
422
- end
423
- end
424
-
425
- # Generate a string representation of a Background instance.
426
- #
427
- # There are two kinds of representation, each of which have slightly
428
- # different CSS semantics. If compressed is set to true, this method will
429
- # produce a shorthand CSS declaration such as the following:
430
- #
431
- # background: #fff url('bg.png') no-repeat 50% 0;
432
- #
433
- # Otherwise it will produce an unordered list of individual background
434
- # declarations.
435
- def to_s
436
- if @compressed
437
- "background:#{self.value(true).map {|p, v| v }.compact.join(" ")};"
438
- else
439
- self.value(true).map {|p, v| sprintf(@format, p, v.to_s) }.join(" ")
302
+ def to_s(symbols = {})
303
+ self.inject("") do |a, o|
304
+ a << (a.empty? ? "" : @format) << o.to_s(symbols)
440
305
  end
441
306
  end
442
307
  end
@@ -55,10 +55,21 @@ module Stylish
55
55
  key = key.to_s.sub("_", "-").to_sym
56
56
 
57
57
  if key == :background
58
- declaration = Background.new(value)
58
+ if value.any? {|k,v| v.is_a? Symbol }
59
+ declaration = Variable.new(value, Background)
60
+ else
61
+ declaration = Background.new(value)
62
+ end
59
63
  elsif key == :color
60
- declaration = Declaration.new("color", Color.new(value))
64
+ if value.is_a? Symbol
65
+ value = Variable.new(value, Color)
66
+ else
67
+ value = Color.new(value)
68
+ end
69
+
70
+ declaration = Declaration.new("color", value)
61
71
  else
72
+ value = Variable.new(value) if value.is_a? Symbol
62
73
  declaration = Declaration.new(key, value)
63
74
  end
64
75
 
@@ -66,6 +77,52 @@ module Stylish
66
77
  end
67
78
  end
68
79
 
80
+ # Variables are elements of a selector tree that haven't been assigned
81
+ # values yet. When a tree that includes Variable objects is serialised, it
82
+ # must be passed a symbol table so that the variables may be given values.
83
+ class Variable
84
+
85
+ # When Variable objects are initialised they may be given either a simple
86
+ # symbol, or a compound object (such as a hash) which contains symbols.
87
+ # When a compound object is given, a constructor must also be given.
88
+ #
89
+ # varbg = Variable.new({:image => :button, :color => :bright})
90
+ # varbg.to_s({:button => "button.png", :bright => "0f0"})
91
+ #
92
+ # Which would give the following:
93
+ #
94
+ # background-image:url('button.png'); background-color:#0f0;
95
+ #
96
+ # Constructors can also be given for simple values, e.g. when creating a
97
+ # Color.
98
+ #
99
+ # varc = Variable.new(:bright, Color)
100
+ # varc.to_s({:bright => "f00"}) # => "#f00"
101
+ #
102
+ def initialize(name_or_hash, constructor = nil)
103
+ @name = name_or_hash
104
+ @constructor = constructor
105
+ end
106
+
107
+ # The symbol table is given as an argument to the root element of a
108
+ # selector tree when it is serialised, and passed down to each node as
109
+ # the tree is traversed. Nodes must then serialise themselves, and if
110
+ # they contain Variables they must pass them the symbol table so that
111
+ # they can be resolved to a given value.
112
+ def to_s(symbols)
113
+ if @constructor.nil?
114
+ symbols[@name]
115
+ elsif @name.is_a? Symbol
116
+ @constructor.new(symbols[@name]).to_s
117
+ else
118
+ @constructor.new(@name.to_a.inject({}) {|a, e|
119
+ a[e.first] = symbols[e.last]
120
+ a
121
+ }).to_s
122
+ end
123
+ end
124
+ end
125
+
69
126
  # Often the selector associated with a call to the rule method would simply
70
127
  # be a single HTML element name. This has been factored out by adding
71
128
  # methods to the Description DSL class corresponding to all the HTML
@@ -161,7 +218,9 @@ module Stylish
161
218
  return unless declarations || block
162
219
 
163
220
  selectors = [selectors] unless selectors.is_a?(Array)
164
- selectors.map! {|s| Selector.new(s) }
221
+ selectors.map! do |s|
222
+ Selector.new(s.is_a?(Symbol) ? Variable.new(s) : s)
223
+ end
165
224
 
166
225
  declarations = Generate.parse_declarations(declarations)
167
226
 
@@ -16,9 +16,9 @@ module Stylish
16
16
  end
17
17
 
18
18
  # Recursively serialise the tree to a stylesheet.
19
- def to_s
19
+ def to_s(symbols = {})
20
20
  return "" if @nodes.empty?
21
- @nodes.map {|node| node.to_s }.join(@format)
21
+ @nodes.map {|node| node.to_s(symbols) }.join(@format)
22
22
  end
23
23
  end
24
24
 
data/lib/stylish/tree.rb CHANGED
@@ -84,10 +84,10 @@ module Stylish
84
84
  end
85
85
 
86
86
  # Recursively serialise the selector tree.
87
- def to_s(scope = "")
87
+ def to_s(symbols = {}, scope = "")
88
88
  return "" if @nodes.empty?
89
89
  scope = scope.empty? ? @scope.to_s : scope + " " + @scope.to_s
90
- @nodes.map {|node| node.to_s(scope) }.join(@format)
90
+ @nodes.map {|node| node.to_s(symbols, scope) }.join(@format)
91
91
  end
92
92
 
93
93
  # Return the node's child nodes.
data/lib/stylish.rb CHANGED
@@ -1,11 +1,17 @@
1
- $:.unshift File.dirname(__FILE__)
2
-
3
1
  require 'pathname'
2
+ require 'mathn'
3
+ require 'rational'
4
4
 
5
- require 'stylish/formattable'
6
- require 'stylish/tree'
7
- require 'stylish/core'
8
- require 'stylish/stylesheet'
9
- require 'stylish/image'
10
- require 'stylish/color'
11
- require 'stylish/generate'
5
+ module Stylish
6
+ STYLISH_PATH = File.expand_path(File.dirname(__FILE__)) + '/stylish/'
7
+
8
+ require STYLISH_PATH + 'formattable'
9
+ require STYLISH_PATH + 'tree'
10
+ require STYLISH_PATH + 'core'
11
+ require STYLISH_PATH + 'tree'
12
+ require STYLISH_PATH + 'stylesheet'
13
+ require STYLISH_PATH + 'image'
14
+ require STYLISH_PATH + 'background'
15
+ require STYLISH_PATH + 'color'
16
+ require STYLISH_PATH + 'generate'
17
+ end
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class BackgroundTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
@@ -24,10 +21,26 @@ class BackgroundTest < Test::Unit::TestCase
24
21
  Stylish::Background.new(:image => "background.jpg").image.path)
25
22
  end
26
23
 
24
+ def test_multiple_background_images
25
+ bg = Stylish::Background.new :image =>
26
+ ["flower.png", "ball.png", "grass.png"]
27
+
28
+ assert_equal(3, bg.image.length)
29
+ assert_equal("background-image:" +
30
+ "url('flower.png'), url('ball.png'), url('grass.png');", bg.to_s)
31
+ end
32
+
27
33
  def test_valid_background_repeats
28
34
  assert_equal('no-repeat', @composite.repeat)
29
35
  end
30
36
 
37
+ def test_multiple_background_repeats
38
+ bg = Stylish::Background.new :repeat => ["repeat-x", "repeat-y"]
39
+
40
+ assert_equal(2, bg.repeat.length)
41
+ assert_equal("background-repeat:repeat-x, repeat-y;", bg.to_s)
42
+ end
43
+
31
44
  def test_valid_background_positions
32
45
  assert_equal(2, @composite.position.length)
33
46
  assert_equal("left", @composite.position[0])
@@ -44,12 +57,32 @@ class BackgroundTest < Test::Unit::TestCase
44
57
  Stylish::Background.new(:attachment => "fixed").attachment)
45
58
  end
46
59
 
60
+ def test_multiple_attachments
61
+ glued = Stylish::Background.new :attachment => ["local", "fixed"]
62
+
63
+ assert_equal(2, glued.attachment.length)
64
+ assert_equal("background-attachment:local, fixed;", glued.to_s)
65
+ end
66
+
47
67
  def test_invalid_background_colors
48
68
  assert_raise ArgumentError do
49
69
  Stylish::Background.new(:color => "sky-blue")
50
70
  end
51
71
  end
52
72
 
73
+ def test_origins
74
+ original = Stylish::Background.new :origin => ["border-box", "padding-box"]
75
+
76
+ assert_equal(2, original.origin.length)
77
+ assert_equal("background-origin:border-box, padding-box;", original.to_s)
78
+ end
79
+
80
+ def test_breaks
81
+ broken = Stylish::Background.new :break => "bounding-box"
82
+
83
+ assert_equal("background-break:bounding-box;", broken.to_s)
84
+ end
85
+
53
86
  def test_invalid_image_values
54
87
  assert_nil(Stylish::Background.new(:image => []).image)
55
88
  assert_nil(Stylish::Background.new(:image => {}).image)
data/test/color_test.rb CHANGED
@@ -1,11 +1,8 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class ColorTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
7
- @red = Stylish::Color.new(:red)
8
- @green = Stylish::Color.new(:green)
4
+ @red = Stylish::Color.new("red")
5
+ @green = Stylish::Color.new("green")
9
6
  @blue = Stylish::Color.new("#0000FF")
10
7
  @white = Stylish::Color.new("#FFF")
11
8
  @yellow = Stylish::Color.new([255, 255, 0])
@@ -68,8 +65,6 @@ class ColorTest < Test::Unit::TestCase
68
65
  end
69
66
 
70
67
  def test_case_insensitivity_of_keywords
71
- assert_equal([0, 128, 0, nil], Stylish::Color.new(:Green).value)
72
- assert_equal([0, 128, 0, nil], Stylish::Color.new(:GrEeN).value)
73
68
  assert_equal([0, 128, 0, nil], Stylish::Color.new("Green").value)
74
69
  assert_equal([0, 128, 0, nil], Stylish::Color.new("GrEeN").value)
75
70
  end
@@ -117,7 +112,7 @@ class ColorTest < Test::Unit::TestCase
117
112
 
118
113
  def test_keyword_to_string
119
114
  assert_equal("green", @green.to_s)
120
- assert_equal("yellow", Stylish::Color.new(:yellow).to_s)
115
+ assert_equal("yellow", Stylish::Color.new("yellow").to_s)
121
116
  end
122
117
 
123
118
  def test_rgb_to_string
@@ -148,8 +143,8 @@ class ColorTest < Test::Unit::TestCase
148
143
  end
149
144
 
150
145
  def test_inherit_and_transparent_to_hex
151
- assert_nil(Stylish::Color.new(:inherit).to_hex)
152
- assert_nil(Stylish::Color.new(:transparent).to_hex)
146
+ assert_nil(Stylish::Color.new("inherit").to_hex)
147
+ assert_nil(Stylish::Color.new("transparent").to_hex)
153
148
  end
154
149
 
155
150
  def test_hex_to_hex
@@ -160,8 +155,8 @@ class ColorTest < Test::Unit::TestCase
160
155
  end
161
156
 
162
157
  def test_keywords_to_hex
163
- assert_equal("#808080", Stylish::Color.new(:gray).to_hex)
164
- assert_equal("#800000", Stylish::Color.new(:maroon).to_hex)
158
+ assert_equal("#808080", Stylish::Color.new("gray").to_hex)
159
+ assert_equal("#800000", Stylish::Color.new("maroon").to_hex)
165
160
  end
166
161
 
167
162
  def test_rgb_to_hex
@@ -174,7 +169,7 @@ class ColorTest < Test::Unit::TestCase
174
169
  assert_equal("#fff", Stylish::Color.new([255, 255, 255]).to_hex)
175
170
  assert_equal("#fff", Stylish::Color.new("#ffffff").to_hex)
176
171
  assert_equal("#fb0", Stylish::Color.new("#ffbb00").to_hex)
177
- assert_equal("#0ff", Stylish::Color.new(:aqua).to_hex)
172
+ assert_equal("#0ff", Stylish::Color.new("aqua").to_hex)
178
173
  end
179
174
 
180
175
  def test_rgb_to_hsl
data/test/comment_test.rb CHANGED
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class CommentTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class DeclarationTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
@@ -1,12 +1,9 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class DeclarationsTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
7
4
  @ds = Stylish::Declarations.new
8
5
  @ds << Stylish::Declaration.new("border-color", "red")
9
- @ds << Stylish::Background.new(:color => :blue, :image => "test.png")
6
+ @ds << Stylish::Background.new(:color => "blue", :image => "test.png")
10
7
  end
11
8
 
12
9
  def test_join
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class FormattableTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class GenerateTest < Test::Unit::TestCase
5
2
 
6
3
  def test_simple_rules
data/test/image_test.rb CHANGED
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class ImageTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
data/test/rule_test.rb CHANGED
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class RuleTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
@@ -1,13 +1,10 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class SelectorTest < Test::Unit::TestCase
5
2
 
6
3
  def test_to_string
7
4
  s = Stylish::Selector.new(".test")
8
5
  assert_equal(".test", s.to_s)
9
6
 
10
- s = Stylish::Selector.new(:div)
7
+ s = Stylish::Selector.new("div")
11
8
  assert_equal("div", s.to_s)
12
9
  end
13
10
  end
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class SelectorsTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class StylesheetTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
data/test/tree_test.rb CHANGED
@@ -1,6 +1,3 @@
1
- require 'test/unit'
2
- require './lib/stylish'
3
-
4
1
  class TreeTest < Test::Unit::TestCase
5
2
 
6
3
  def setup
@@ -0,0 +1,46 @@
1
+ class VariableTest < Test::Unit::TestCase
2
+
3
+ def setup
4
+ @style = Stylish.generate do
5
+ div :font_weight => :weighty
6
+ end
7
+ end
8
+
9
+ def test_variable_inclusion
10
+ assert_instance_of(Stylish::Generate::Variable,
11
+ @style.rules.first.declarations.first.value)
12
+ end
13
+
14
+ def test_variable_serialisation
15
+ assert_equal("div {font-weight:bold;}",
16
+ @style.to_s({:weighty => "bold"}))
17
+ end
18
+
19
+ def test_selector_variables
20
+ style = Stylish.generate do
21
+ rule :some_selector, :line_height => 1.5
22
+ end
23
+
24
+ assert_equal("body p {line-height:1.5;}",
25
+ style.to_s({:some_selector => "body p"}))
26
+ end
27
+
28
+ def test_color_variables
29
+ style = Stylish.generate do
30
+ body :color => :bright_as_a_button
31
+ end
32
+
33
+ assert_equal("body {color:#57b5cc;}",
34
+ style.to_s({:bright_as_a_button => "57b5cc"}))
35
+ end
36
+
37
+ def test_background_variables
38
+ style = Stylish.generate do
39
+ body :background => {:color => :dark, :image => :buttonish}
40
+ end
41
+
42
+ assert_equal(
43
+ "body {background-color:#000; background-image:url('button.png');}",
44
+ style.to_s({:dark => "000", :buttonish => "button.png"}))
45
+ end
46
+ end
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.2
4
+ version: 0.1.3
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-13 00:00:00 -07:00
12
+ date: 2009-04-24 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -22,10 +22,12 @@ extensions: []
22
22
  extra_rdoc_files:
23
23
  - README.md
24
24
  files:
25
+ - History.txt
25
26
  - README.md
26
27
  - Rakefile
27
28
  - VERSION.yml
28
29
  - lib/stylish.rb
30
+ - lib/stylish/background.rb
29
31
  - lib/stylish/color.rb
30
32
  - lib/stylish/core.rb
31
33
  - lib/stylish/formattable.rb
@@ -46,6 +48,7 @@ files:
46
48
  - test/selectors_test.rb
47
49
  - test/stylesheet_test.rb
48
50
  - test/tree_test.rb
51
+ - test/variable_test.rb
49
52
  has_rdoc: true
50
53
  homepage: http://github.com/ionfish/stylish
51
54
  post_install_message:
@@ -86,3 +89,4 @@ test_files:
86
89
  - test/selectors_test.rb
87
90
  - test/stylesheet_test.rb
88
91
  - test/tree_test.rb
92
+ - test/variable_test.rb