cassandra 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +19 -0
- data/README.rdoc +29 -0
- data/cassandra.gemspec +131 -0
- data/lib/cassandra.rb +160 -0
- data/lib/properties.rb +119 -0
- data/lib/tags.rb +89 -0
- data/misc/dan.rb +64 -0
- data/misc/meyer_reset.css +53 -0
- data/site/basic.cssy +12 -0
- data/site/basic.rb +19 -0
- data/site/cssy_title.jpg +0 -0
- data/site/flower.png +0 -0
- data/site/index.mab +22 -0
- data/site/ruby.cssy +15 -0
- data/test/basics.rb +107 -0
- data/test/dan.cssy +160 -0
- data/test/fiddle.css +22 -0
- data/test/fiddle.cssy +34 -0
- data/test/helper.rb +8 -0
- metadata +80 -0
data/Manifest
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
cassandra.gemspec
|
2
|
+
lib/cassandra.rb
|
3
|
+
lib/properties.rb
|
4
|
+
lib/tags.rb
|
5
|
+
Manifest
|
6
|
+
misc/dan.rb
|
7
|
+
misc/meyer_reset.css
|
8
|
+
README.rdoc
|
9
|
+
site/basic.cssy
|
10
|
+
site/basic.rb
|
11
|
+
site/cssy_title.jpg
|
12
|
+
site/flower.png
|
13
|
+
site/index.mab
|
14
|
+
site/ruby.cssy
|
15
|
+
test/basics.rb
|
16
|
+
test/dan.cssy
|
17
|
+
test/fiddle.css
|
18
|
+
test/fiddle.cssy
|
19
|
+
test/helper.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= Cassandra, a.k.a Cssy
|
2
|
+
|
3
|
+
Cassandra is a CSS companion to Markaby. You declare selectors using methods named after the type selectors (i.e. tags) or after the class and id selectors of your choice, which are handled by method_missing. As in Markaby, id selectors are distinguished from class selectors by the "!" at the end of the method name. You can chain class/id selectors onto type selectors and each other.
|
4
|
+
|
5
|
+
Because in CSS you don't have to begin with a type selector, starting a selector chain with a class/id selector is currently allowed in Cassandra. This may go away in the future.
|
6
|
+
|
7
|
+
== Usage
|
8
|
+
|
9
|
+
|
10
|
+
@css = Cssy.new
|
11
|
+
@css.process do
|
12
|
+
|
13
|
+
body { background_color "#F8F7F1"}
|
14
|
+
div.content! {
|
15
|
+
width "700px"; margin "25px auto"
|
16
|
+
|
17
|
+
a {
|
18
|
+
color "#212F54";
|
19
|
+
text_decoration :none
|
20
|
+
font_weight :bold
|
21
|
+
}
|
22
|
+
|
23
|
+
ul.links! { list_style "url(flower.png)" }
|
24
|
+
}
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
@css.data #=> array of arrays
|
29
|
+
@css.to_s
|
data/cassandra.gemspec
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Cassandra-0.2.3
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{cassandra}
|
7
|
+
s.version = "0.2.3"
|
8
|
+
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
+
s.authors = ["Matthew King"]
|
11
|
+
s.date = %q{2008-09-08}
|
12
|
+
s.description = %q{Generates CSS using Ruby, like Markaby}
|
13
|
+
s.email = %q{automatthew@gmail.com}
|
14
|
+
s.extra_rdoc_files = ["lib/cassandra.rb", "lib/properties.rb", "lib/tags.rb", "README.rdoc"]
|
15
|
+
s.files = ["cassandra.gemspec", "lib/cassandra.rb", "lib/properties.rb", "lib/tags.rb", "Manifest", "misc/dan.rb", "misc/meyer_reset.css", "README.rdoc", "site/basic.cssy", "site/basic.rb", "site/cssy_title.jpg", "site/flower.png", "site/index.mab", "site/ruby.cssy", "test/basics.rb", "test/dan.cssy", "test/fiddle.css", "test/fiddle.cssy", "test/helper.rb"]
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.homepage = %q{}
|
18
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Cassandra", "--main", "README.rdoc"]
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{cassandra}
|
21
|
+
s.rubygems_version = %q{1.2.0}
|
22
|
+
s.summary = %q{Generates CSS using Ruby, like Markaby}
|
23
|
+
s.test_files = ["test/basics.rb", "test/helper.rb"]
|
24
|
+
|
25
|
+
if s.respond_to? :specification_version then
|
26
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
27
|
+
s.specification_version = 2
|
28
|
+
|
29
|
+
if current_version >= 3 then
|
30
|
+
else
|
31
|
+
end
|
32
|
+
else
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# # Original Rakefile source (requires the Echoe gem):
|
38
|
+
#
|
39
|
+
# require 'rubygems'
|
40
|
+
#
|
41
|
+
# Version = '0.2.3'
|
42
|
+
#
|
43
|
+
# task :default => [ :test ]
|
44
|
+
#
|
45
|
+
# begin
|
46
|
+
# gem 'echoe', '>=2.7'
|
47
|
+
# require 'echoe'
|
48
|
+
# Echoe.new('cassandra', Version) do |p|
|
49
|
+
# p.project = 'cassandra'
|
50
|
+
# p.summary = "Generates CSS using Ruby, like Markaby"
|
51
|
+
# p.author = "Matthew King"
|
52
|
+
# p.email = "automatthew@gmail.com"
|
53
|
+
# p.ignore_pattern = /^(\.git).+/
|
54
|
+
# p.test_pattern = "test/*.rb"
|
55
|
+
# end
|
56
|
+
# rescue
|
57
|
+
# "(ignored echoe gemification, as you don't have the Right Stuff)"
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# module Rake::TaskManager
|
61
|
+
# def delete_task(task_class, *args, &block)
|
62
|
+
# task_name, deps = resolve_args(args)
|
63
|
+
# @tasks.delete(task_class.scope_name(@scope, task_name).to_s)
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
# class Rake::Task
|
67
|
+
# def self.delete_task(args, &block) Rake.application.delete_task(self, args, &block) end
|
68
|
+
# end
|
69
|
+
# def delete_task(args, &block) Rake::Task.delete_task(args, &block) end
|
70
|
+
#
|
71
|
+
# delete_task :publish_docs
|
72
|
+
#
|
73
|
+
# desc "Publish rdocs to rubyforge"
|
74
|
+
# task :publish_docs => [ :clean, :docs ] do
|
75
|
+
# config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
76
|
+
# pub = Rake::SshDirPublisher.new "#{config["username"]}@rubyforge.org",
|
77
|
+
# "/var/www/gforge-projects/cassandra/rdoc",
|
78
|
+
# 'doc'
|
79
|
+
# pub.upload
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# task :publish_site => [ :compile ] do
|
83
|
+
# cmd = "scp -qr site/*.{html,css,jpg,png} automatthew@rubyforge.org:/var/www/gforge-projects/cassandra"
|
84
|
+
# puts "Uploading: #{cmd}"
|
85
|
+
# system(cmd)
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# def rubyforge_config
|
89
|
+
# @rubyforge_config ||= YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
#
|
93
|
+
#
|
94
|
+
# # List all the desired pages as dependencies on :compile
|
95
|
+
# task :compile => %w{ site/index.html site/basic.css site/ruby.css }
|
96
|
+
#
|
97
|
+
# task :clean do
|
98
|
+
# f = FileList['site/**/*.html', 'site/*.css']
|
99
|
+
# puts f; rm f
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# rule '.html' => [ '.mab' ] do |t|
|
103
|
+
# mab(t.source, t.name)
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# file 'site/index.mab' => [ 'site/basic.rb' ]
|
107
|
+
#
|
108
|
+
# rule '.css' => [ '.cssy' ] do |t|
|
109
|
+
# cssify(t.source, t.name)
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# require 'markaby'
|
113
|
+
# $:.unshift "lib"
|
114
|
+
# require 'cassandra'
|
115
|
+
#
|
116
|
+
# def mab(source, target)
|
117
|
+
# mab = Markaby::Builder.new
|
118
|
+
# result = mab.instance_eval(File.read(source))
|
119
|
+
# File.open(target, 'w') do |f|
|
120
|
+
# f.puts result
|
121
|
+
# end
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# def cssify(source, target)
|
125
|
+
# c = Cssy.new.process(File.read(source))
|
126
|
+
# File.open(target, 'w') do |f|
|
127
|
+
# f.puts c.to_s
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
#
|
data/lib/cassandra.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'properties'
|
2
|
+
require 'tags'
|
3
|
+
|
4
|
+
# Markaby-ish way to declare CSS
|
5
|
+
class Cassandra
|
6
|
+
|
7
|
+
METHODS = %w( class instance_eval send __send__ __id__ )
|
8
|
+
instance_methods.each { |m| undef_method( m ) unless METHODS.include? m }
|
9
|
+
|
10
|
+
attr_reader :data
|
11
|
+
|
12
|
+
def initialize(sel=nil)
|
13
|
+
@data = []
|
14
|
+
@selectors = [ sel ]
|
15
|
+
@properties = []
|
16
|
+
|
17
|
+
# Primitive state machine
|
18
|
+
# possible states are :closed_block, :chain, :open_block
|
19
|
+
@state = :closed_block
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.process(*args,&block)
|
23
|
+
self.new.process(*args,&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def process(*args, &block)
|
27
|
+
if block
|
28
|
+
instance_eval(&block)
|
29
|
+
else
|
30
|
+
instance_eval(args.join("\n"))
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
@data.map do |sel|
|
37
|
+
properties = sel.last.join("\n ")
|
38
|
+
"#{sel.first} {\n #{properties}\n}\n"
|
39
|
+
end.join
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
# Pushes an empty array on the properties stack and registers
|
45
|
+
# that array (against the current selector) in @data
|
46
|
+
def new_property_set
|
47
|
+
@properties.push []
|
48
|
+
@data << [@selectors[-1], @properties[-1] ]
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Declare a CSS selector using a block. May be chained and nested.
|
53
|
+
def selector(sel)
|
54
|
+
if block_given?
|
55
|
+
open_block(sel)
|
56
|
+
yield
|
57
|
+
closed_block
|
58
|
+
else
|
59
|
+
chain(sel)
|
60
|
+
end
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# Catch unknown methods and treat them as CSS class or id selectors.
|
65
|
+
def method_missing(name, &block)
|
66
|
+
sel = selectify(name)
|
67
|
+
if block_given?
|
68
|
+
open_block(sel)
|
69
|
+
yield
|
70
|
+
closed_block
|
71
|
+
else
|
72
|
+
chain(sel)
|
73
|
+
end
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# bang methods represent CSS ids. Otherwise CSS classes.
|
78
|
+
def selectify(method_name)
|
79
|
+
matches = method_name.to_s.match( /([\w_]+)!$/)
|
80
|
+
matches ? "##{matches[1]}" : ".#{method_name}"
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# define tag methods that delegate to selector
|
85
|
+
methods = HTML_TAGS.map do |tag|
|
86
|
+
<<-METHOD
|
87
|
+
def #{tag}(&block)
|
88
|
+
selector('#{tag}', &block)
|
89
|
+
end
|
90
|
+
METHOD
|
91
|
+
end.join; module_eval(methods)
|
92
|
+
|
93
|
+
# define methods for CSS properties.
|
94
|
+
CSS_PROPERTIES.each do |method_name|
|
95
|
+
define_method method_name do |*args|
|
96
|
+
css_attr = method_name.gsub('_', '-')
|
97
|
+
property(css_attr, args)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Add a property declaration string to the current selector's properties array.
|
102
|
+
def property(css_attr, *args)
|
103
|
+
@properties[-1] << "#{css_attr}: #{args.join(' ')};"
|
104
|
+
end
|
105
|
+
|
106
|
+
## State transitions
|
107
|
+
|
108
|
+
# Push the accumulated selector and a new property array onto the
|
109
|
+
# tops of their respected stacks
|
110
|
+
def open_block(new_selector)
|
111
|
+
case @state
|
112
|
+
when :closed_block, :open_block
|
113
|
+
combined_selector = [@selectors[-1], new_selector].compact.join(" ")
|
114
|
+
@selectors.push combined_selector
|
115
|
+
new_property_set
|
116
|
+
when :chain
|
117
|
+
@selectors[-1] = "#{@selectors[-1]}#{new_selector}"
|
118
|
+
new_property_set
|
119
|
+
else
|
120
|
+
raise "You can't get to :open_block from #{@state.inspect}"
|
121
|
+
end
|
122
|
+
|
123
|
+
@state = :open_block
|
124
|
+
end
|
125
|
+
|
126
|
+
# Pushes accumulated selector on the stack without generating a new properties array.
|
127
|
+
def chain(new_selector)
|
128
|
+
case @state
|
129
|
+
when :closed_block, :open_block
|
130
|
+
combined_selector = [@selectors[-1], new_selector].compact.join(" ")
|
131
|
+
@selectors.push( combined_selector)
|
132
|
+
when :chain
|
133
|
+
@selectors[-1] = "#{@selectors[-1]}#{new_selector}"
|
134
|
+
else
|
135
|
+
raise "You can't get to :chain from #{@state.inspect}"
|
136
|
+
end
|
137
|
+
|
138
|
+
@state = :chain
|
139
|
+
end
|
140
|
+
|
141
|
+
# Pop the selector string and property array for the closing block
|
142
|
+
# off of their respective stacks
|
143
|
+
def closed_block
|
144
|
+
case @state
|
145
|
+
when :open_block, :closed_block
|
146
|
+
@selectors.pop
|
147
|
+
@properties.pop
|
148
|
+
else
|
149
|
+
raise "You can't get to :closed_block from #{@state.inspect}"
|
150
|
+
end
|
151
|
+
|
152
|
+
@state = :closed_block
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
Cssy = Cassy = Cassandra
|
data/lib/properties.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
class Cassandra
|
2
|
+
CSS_PROPERTIES = %w{
|
3
|
+
azimuth
|
4
|
+
background
|
5
|
+
background_attachment
|
6
|
+
background_color
|
7
|
+
background_image
|
8
|
+
background_position
|
9
|
+
background_repeat
|
10
|
+
border
|
11
|
+
border_left
|
12
|
+
border_top
|
13
|
+
border_right
|
14
|
+
border_bottom
|
15
|
+
border_collapse
|
16
|
+
border_color
|
17
|
+
border_spacing
|
18
|
+
border_style
|
19
|
+
border_top
|
20
|
+
border_top_color
|
21
|
+
border_top_style
|
22
|
+
border_top_width
|
23
|
+
border_width
|
24
|
+
bottom
|
25
|
+
caption_side
|
26
|
+
clear
|
27
|
+
clip
|
28
|
+
color
|
29
|
+
content
|
30
|
+
counter_increment
|
31
|
+
counter_reset
|
32
|
+
cue
|
33
|
+
cue_after
|
34
|
+
cue_before
|
35
|
+
cursor
|
36
|
+
direction
|
37
|
+
display
|
38
|
+
elevation
|
39
|
+
empty_cells
|
40
|
+
float
|
41
|
+
font
|
42
|
+
font_family
|
43
|
+
font_size
|
44
|
+
font_size_adjust
|
45
|
+
font_stretch
|
46
|
+
font_style
|
47
|
+
font_variant
|
48
|
+
font_weight
|
49
|
+
height
|
50
|
+
left
|
51
|
+
letter_spacing
|
52
|
+
line_height
|
53
|
+
list_style
|
54
|
+
list_style_image
|
55
|
+
list_style_position
|
56
|
+
list_style_type
|
57
|
+
margin
|
58
|
+
margin_left
|
59
|
+
margin_top
|
60
|
+
margin_right
|
61
|
+
margin_bottom
|
62
|
+
marker_offset
|
63
|
+
marks
|
64
|
+
max_height
|
65
|
+
max_width
|
66
|
+
min_height
|
67
|
+
min_width
|
68
|
+
orphans
|
69
|
+
outline
|
70
|
+
outline_color
|
71
|
+
outline_style
|
72
|
+
outline_width
|
73
|
+
overflow
|
74
|
+
padding
|
75
|
+
padding_left
|
76
|
+
padding_top
|
77
|
+
padding_right
|
78
|
+
padding_bottom
|
79
|
+
page
|
80
|
+
page_break_after
|
81
|
+
page_break_before
|
82
|
+
page_break_inside
|
83
|
+
pause
|
84
|
+
pause_after
|
85
|
+
pause_before
|
86
|
+
pitch
|
87
|
+
pitch_range
|
88
|
+
play_during
|
89
|
+
position
|
90
|
+
quotes
|
91
|
+
richness
|
92
|
+
right
|
93
|
+
size
|
94
|
+
speak
|
95
|
+
speak_header
|
96
|
+
speak_numeral
|
97
|
+
speak_punctuation
|
98
|
+
speech_rate
|
99
|
+
stress
|
100
|
+
table_layout
|
101
|
+
text_align
|
102
|
+
text_decoration
|
103
|
+
text_indent
|
104
|
+
text_shadow
|
105
|
+
text_transform
|
106
|
+
top
|
107
|
+
unicode_bidi
|
108
|
+
vertical_align
|
109
|
+
visibility
|
110
|
+
voice_family
|
111
|
+
volume
|
112
|
+
white_space
|
113
|
+
widows
|
114
|
+
width
|
115
|
+
word_spacing
|
116
|
+
z_index
|
117
|
+
}
|
118
|
+
|
119
|
+
end
|
data/lib/tags.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
class Cassandra
|
2
|
+
HTML_TAGS = [
|
3
|
+
:a,
|
4
|
+
:abbr,
|
5
|
+
:acronym,
|
6
|
+
:address,
|
7
|
+
:area,
|
8
|
+
:b,
|
9
|
+
:base,
|
10
|
+
:bdo,
|
11
|
+
:big,
|
12
|
+
:blockquote,
|
13
|
+
:body,
|
14
|
+
:br,
|
15
|
+
:button,
|
16
|
+
:caption,
|
17
|
+
:cite,
|
18
|
+
:code,
|
19
|
+
:col,
|
20
|
+
:colgroup,
|
21
|
+
:dd,
|
22
|
+
:del,
|
23
|
+
:dfn,
|
24
|
+
:div,
|
25
|
+
:dl,
|
26
|
+
:dt,
|
27
|
+
:em,
|
28
|
+
:fieldset,
|
29
|
+
:form,
|
30
|
+
:h1,
|
31
|
+
:h2,
|
32
|
+
:h3,
|
33
|
+
:h4,
|
34
|
+
:h5,
|
35
|
+
:h6,
|
36
|
+
:head,
|
37
|
+
:hr,
|
38
|
+
:html,
|
39
|
+
:i,
|
40
|
+
:img,
|
41
|
+
:input,
|
42
|
+
:ins,
|
43
|
+
:kbd,
|
44
|
+
:label,
|
45
|
+
:legend,
|
46
|
+
:li,
|
47
|
+
:link,
|
48
|
+
:map,
|
49
|
+
:meta,
|
50
|
+
:noscript,
|
51
|
+
:object,
|
52
|
+
:ol,
|
53
|
+
:optgroup,
|
54
|
+
:option,
|
55
|
+
:p,
|
56
|
+
:param,
|
57
|
+
:pre,
|
58
|
+
:q,
|
59
|
+
:samp,
|
60
|
+
:script,
|
61
|
+
:select,
|
62
|
+
:small,
|
63
|
+
:span,
|
64
|
+
:strong,
|
65
|
+
:style,
|
66
|
+
:sub,
|
67
|
+
:sup,
|
68
|
+
:table,
|
69
|
+
:tbody,
|
70
|
+
:td,
|
71
|
+
:textarea,
|
72
|
+
:tfoot,
|
73
|
+
:th,
|
74
|
+
:thead,
|
75
|
+
:title,
|
76
|
+
:tr,
|
77
|
+
:tt,
|
78
|
+
:ul,
|
79
|
+
:var,
|
80
|
+
]
|
81
|
+
|
82
|
+
# define tag methods to delegate to selector_eval
|
83
|
+
methods = HTML_TAGS.map do |tag|
|
84
|
+
"def #{tag}(&block); selector_eval(@selector.first, '#{tag}', &block);end\n"
|
85
|
+
end.join
|
86
|
+
|
87
|
+
module_eval methods
|
88
|
+
|
89
|
+
end
|
data/misc/dan.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Experimental alternative from Dan Yoder
|
2
|
+
|
3
|
+
%w( rubygems functor ).each { |file| require file }
|
4
|
+
class CSS # :nodoc:
|
5
|
+
|
6
|
+
include Functor::Method
|
7
|
+
|
8
|
+
# Functor needs instance_exec, which needs instance_eval and send ...
|
9
|
+
METHODS = %w( class functor instance_eval instance_exec send __send__ __id__ )
|
10
|
+
instance_methods.each { |m| undef_method( m ) unless METHODS.include? m }
|
11
|
+
|
12
|
+
|
13
|
+
TAGS = %w( a abbr acronym address area b base bdo big blockquote body br button caption
|
14
|
+
cite code col colgroup dd del dfn div dl dt em fieldset form h1 h2 h3 h4 h5 h6 head hr
|
15
|
+
html i img input ins kbd label legend li link map meta noscript object ol optgroup option
|
16
|
+
p param pre q samp script select small span strong style sub sup table tbody td textarea
|
17
|
+
tfoot th thead title tr tt ul var _ )
|
18
|
+
ATTRS = %w( background border clear color display float font height letter_spacing
|
19
|
+
line-height list margin max outline padding text )
|
20
|
+
COMBINATORS = %w( + / > )
|
21
|
+
|
22
|
+
ROWS = %w( height top bottom ) ; COLS = %w( width left right )
|
23
|
+
|
24
|
+
def self.css( &block ) ; @start = block ; end
|
25
|
+
|
26
|
+
def render
|
27
|
+
__start ; instance_eval( &( self.class.module_eval { @start } ) )
|
28
|
+
@output.reverse.map { |sel, attrs| puts sel.inspect ; "#{sel.join('').strip} { #{attrs} }" }.join("\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
def select( s )
|
32
|
+
selector = @selectors[-1].dup << s
|
33
|
+
@selectors << selector ; @buffers << @buffer
|
34
|
+
@buffer = '' ; yield ; @output << [ selector, @buffer ]
|
35
|
+
@selectors.pop ; @buffer = @buffers.pop
|
36
|
+
end
|
37
|
+
|
38
|
+
# grid support
|
39
|
+
def units ; 'px' ; end
|
40
|
+
def cols( n ) "#{n}#{units}" ; end
|
41
|
+
def rows( n ) "#{n}#{units}" ; end
|
42
|
+
|
43
|
+
def method_missing( name, *args, &block ) ; __process( name.to_s, args, block ) ; self ; end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def __start ; @output = [] ; @selectors = [ [] ] ; @selector = [] ; @buffers = [] ; @buffer = '' ; end
|
48
|
+
def __open( sel ) @selector << [ sel ] ; end
|
49
|
+
def __append( sel ) ( __open( sel ) if @selector.empty? ) or ( @selector[-1] << sel ) ; end
|
50
|
+
def __close( block ) s = @selector ; @selector = [] ; select( s, &block ) ; end
|
51
|
+
def __combine( op ) @output[-1][0][-1] << op ; end
|
52
|
+
|
53
|
+
functor( :__process, lambda { | name | ATTRS.include?( name ) }, Array, nil ) { | name, args, block | __attribute( name, *args ) }
|
54
|
+
functor( :__process, lambda { | name | TAGS.include?( name ) }, [], nil ) { | sel, args, block | __open( sel ) }
|
55
|
+
functor( :__process, lambda { | name | COMBINATORS.include?( name ) }, [], nil ) { | op, args, block | __combine( op ) }
|
56
|
+
functor( :__process, String, [], nil ) { | sel, args, block | __append( sel ) }
|
57
|
+
functor( :__process, lambda { | name | TAGS.include?( name ) }, [], Proc ) { | sel, args, block | __open( sel ) ; __close( block ) }
|
58
|
+
functor( :__process, String, [], Proc ) { | sel, args, block | __append( sel ) ; __close( block ) }
|
59
|
+
functor( :__attribute, String, String ) { | name, val | @buffer << "#{name}: #{val};" }
|
60
|
+
functor( :__attribute, lambda { | name | COLS.include?( name ) }, Integer ) { | name, val | __attribute( name, cols( val ) ) }
|
61
|
+
functor( :__attribute, lambda { | name | ROWS.include?( name ) }, Integer ) { | name, val | __attribute( name, rows( val ) ) }
|
62
|
+
functor( :__attribute, String, Hash ) { | name, vals | vals.each { | key, val | __attribute( "#{name}-#{key}", val ) } }
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
/* http://meyerweb.com/eric/tools/css/reset/ */
|
2
|
+
/* v1.0 | 20080212 */
|
3
|
+
|
4
|
+
html, body, div, span, applet, object, iframe,
|
5
|
+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
6
|
+
a, abbr, acronym, address, big, cite, code,
|
7
|
+
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
8
|
+
small, strike, strong, sub, sup, tt, var,
|
9
|
+
b, u, i, center,
|
10
|
+
dl, dt, dd, ol, ul, li,
|
11
|
+
fieldset, form, label, legend,
|
12
|
+
table, caption, tbody, tfoot, thead, tr, th, td {
|
13
|
+
margin: 0;
|
14
|
+
padding: 0;
|
15
|
+
border: 0;
|
16
|
+
outline: 0;
|
17
|
+
font-size: 100%;
|
18
|
+
vertical-align: baseline;
|
19
|
+
background: transparent;
|
20
|
+
}
|
21
|
+
body {
|
22
|
+
line-height: 1;
|
23
|
+
}
|
24
|
+
ol, ul {
|
25
|
+
list-style: none;
|
26
|
+
}
|
27
|
+
blockquote, q {
|
28
|
+
quotes: none;
|
29
|
+
}
|
30
|
+
blockquote:before, blockquote:after,
|
31
|
+
q:before, q:after {
|
32
|
+
content: '';
|
33
|
+
content: none;
|
34
|
+
}
|
35
|
+
|
36
|
+
/* remember to define focus styles! */
|
37
|
+
:focus {
|
38
|
+
outline: 0;
|
39
|
+
}
|
40
|
+
|
41
|
+
/* remember to highlight inserts somehow! */
|
42
|
+
ins {
|
43
|
+
text-decoration: none;
|
44
|
+
}
|
45
|
+
del {
|
46
|
+
text-decoration: line-through;
|
47
|
+
}
|
48
|
+
|
49
|
+
/* tables still need 'cellspacing="0"' in the markup */
|
50
|
+
table {
|
51
|
+
border-collapse: collapse;
|
52
|
+
border-spacing: 0;
|
53
|
+
}
|
data/site/basic.cssy
ADDED
data/site/basic.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
@css = Cssy.new
|
2
|
+
@css.process do
|
3
|
+
|
4
|
+
body { background_color "#F8F7F1"}
|
5
|
+
div.content! {
|
6
|
+
width "700px"; margin "25px auto"
|
7
|
+
|
8
|
+
a {
|
9
|
+
color "#212F54";
|
10
|
+
text_decoration :none
|
11
|
+
font_weight :bold
|
12
|
+
}
|
13
|
+
|
14
|
+
ul.links! { list_style "url(flower.png)" }
|
15
|
+
}
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
@css.to_s
|
data/site/cssy_title.jpg
ADDED
Binary file
|
data/site/flower.png
ADDED
Binary file
|
data/site/index.mab
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'syntax/convertors/html'
|
2
|
+
convertor = Syntax::Convertors::HTML.for_syntax "ruby"
|
3
|
+
|
4
|
+
html do
|
5
|
+
head do
|
6
|
+
title "Cassandra: A CSS generator with a pure-Ruby syntax"
|
7
|
+
link :rel => 'stylesheet', :type => 'text/css', :href => "basic.css"
|
8
|
+
link :rel => 'stylesheet', :type => 'text/css', :href => "ruby.css"
|
9
|
+
end
|
10
|
+
body do
|
11
|
+
div.content! do
|
12
|
+
img.logo! :src => "cssy_title.jpg"
|
13
|
+
div.ruby.example! do
|
14
|
+
convertor.convert( File.read( "site/basic.rb" ) ).to_s
|
15
|
+
end
|
16
|
+
ul.links! do
|
17
|
+
li { a "RDocs", :href => "/rdoc" }
|
18
|
+
li { a "Developer", :href => "http://www.automatthew.com" }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/site/ruby.cssy
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
div.ruby {
|
2
|
+
margin "30px 57px"
|
3
|
+
padding_left "20px"
|
4
|
+
border_left "3px solid #e8e7e1"
|
5
|
+
|
6
|
+
comment { color "#5A525F"; font_style :italic }
|
7
|
+
keyword { color "#794938" }
|
8
|
+
attribute { color "#234A97" }
|
9
|
+
constant { color "#691C97"; font_weight :bold }
|
10
|
+
method { color "#BF4F24" }
|
11
|
+
symbol { color "#811F24" }
|
12
|
+
string { color "#0B6125" }
|
13
|
+
ident { color "#080808" }
|
14
|
+
punct { color "#080808" }
|
15
|
+
}
|
data/test/basics.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
here = File.dirname(__FILE__)
|
2
|
+
require "#{here}/helper"
|
3
|
+
|
4
|
+
describe "Cassandra" do
|
5
|
+
|
6
|
+
it "can nest blocks" do
|
7
|
+
proc = lambda do
|
8
|
+
div do
|
9
|
+
form { width "35px" }
|
10
|
+
ul do
|
11
|
+
li { background :red }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
data = [
|
17
|
+
["div", []],
|
18
|
+
["div form", ["width: 35px;"]],
|
19
|
+
["div ul", []],
|
20
|
+
["div ul li", ["background: red;"]]
|
21
|
+
]
|
22
|
+
Cssy.process(&proc).data.should == data
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can chain blocks" do
|
26
|
+
proc = lambda do
|
27
|
+
p.smurf { color :blue }
|
28
|
+
p.gnome.hat { color :red }
|
29
|
+
end
|
30
|
+
|
31
|
+
data = [
|
32
|
+
["p.smurf", [ "color: blue;"]],
|
33
|
+
["p.gnome.hat", ["color: red;"]]
|
34
|
+
]
|
35
|
+
Cssy.process(&proc).data.should == data
|
36
|
+
end
|
37
|
+
|
38
|
+
it "can nest and chain" do
|
39
|
+
proc = lambda do
|
40
|
+
div do
|
41
|
+
span.ugly { font_family "Arial" }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
data = [
|
46
|
+
["div", []],
|
47
|
+
["div span.ugly", ["font-family: Arial;"]]
|
48
|
+
]
|
49
|
+
Cssy.process(&proc).data.should == data
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can chain and nest" do
|
53
|
+
proc = lambda do
|
54
|
+
ul.monkey do
|
55
|
+
li { list_style :none }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
data = [
|
60
|
+
["ul.monkey", []],
|
61
|
+
["ul.monkey li", ["list-style: none;"]]
|
62
|
+
]
|
63
|
+
Cssy.process(&proc).data.should == data
|
64
|
+
end
|
65
|
+
|
66
|
+
it "can chain and nest and chain and ..." do
|
67
|
+
proc = lambda do
|
68
|
+
div do
|
69
|
+
div.milk!.toast do
|
70
|
+
p.jam { margin :auto }
|
71
|
+
hr { background :green }
|
72
|
+
end
|
73
|
+
|
74
|
+
p { border :none }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
data = [
|
79
|
+
["div", []],
|
80
|
+
["div div#milk.toast", []],
|
81
|
+
["div div#milk.toast p.jam", ["margin: auto;"]],
|
82
|
+
["div div#milk.toast hr", ["background: green;"]],
|
83
|
+
["div p", ["border: none;"]]
|
84
|
+
]
|
85
|
+
Cssy.process(&proc).data.should == data
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
it "processes strings" do
|
90
|
+
fiddle = [
|
91
|
+
["div", ["background: red;", "width: 9934px;"]],
|
92
|
+
["div ul li", ["color: green;"]],
|
93
|
+
["div p.ugly.truly", ["color: aqua;"]],
|
94
|
+
["div .smurf.house", ["height: 256px;"]],
|
95
|
+
["div #menu", ["padding: 10px;"]],
|
96
|
+
[".gargamel", ["margin: 0px;"]],
|
97
|
+
[".outer.middle.inner", ["top: 34px;"]]
|
98
|
+
]
|
99
|
+
c = Cssy.new
|
100
|
+
c.process(File.read( "#{here}/fiddle.cssy"))
|
101
|
+
c.data.should == fiddle
|
102
|
+
c.to_s.should == File.read( "#{here}/fiddle.css")
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
end
|
data/test/dan.cssy
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# # within a class
|
2
|
+
#
|
3
|
+
# class Widget
|
4
|
+
#
|
5
|
+
# # need this to get stylesheet methods
|
6
|
+
# include Cassandra
|
7
|
+
#
|
8
|
+
# # add in Grid Layout support
|
9
|
+
# include Cassandra::Grid
|
10
|
+
#
|
11
|
+
# # changes default units to 10 x 10 pixel blocks
|
12
|
+
# grid :columns => { :width => 10, :height => 10 }, :units => 'px'
|
13
|
+
#
|
14
|
+
# # define some reusable stuff
|
15
|
+
# def block ; display :block ; margin 0 ; padding 0 ; end
|
16
|
+
# def wrap ; clear :both ; end
|
17
|
+
# def left ; float :left ; end
|
18
|
+
# def dark ; color :gray ; end
|
19
|
+
# def style light ; color :silver ; end
|
20
|
+
# def large ; font :size => '13px' ; end
|
21
|
+
# def small ; font :size => '11px' ; end
|
22
|
+
#
|
23
|
+
# # generate a "CSS reset" and include some other stylesheets
|
24
|
+
# reset ; include Main ; include Products
|
25
|
+
#
|
26
|
+
# intro_text! / a.back do
|
27
|
+
# padding :top => 0, :left => 0, :bottom => 2, :right => 2
|
28
|
+
# font :weight => :bold ; background :url => '/images/icons-back.gif', :repeat => false
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# img.title { padding :bottom => 1 }
|
32
|
+
#
|
33
|
+
# div.wizard do
|
34
|
+
#
|
35
|
+
# left ; margin :right => 1
|
36
|
+
#
|
37
|
+
# ul.steps do
|
38
|
+
#
|
39
|
+
# block
|
40
|
+
#
|
41
|
+
# li.step do
|
42
|
+
# block ; display false;
|
43
|
+
# width 40 ; height 33.5
|
44
|
+
# margin :left => '2px' ; padding 1.5
|
45
|
+
# border '1px solid silver'
|
46
|
+
#
|
47
|
+
# add '.selected' { display :block }
|
48
|
+
#
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# div.tab-selector span do
|
54
|
+
# dark ; small ; centered ; block ; left
|
55
|
+
# height 2.5 ; width 13 ; padding '12px 8px 0 8px' ; cursor :pointer
|
56
|
+
# background :url => '/images/tab/unselected.gif', :repeat => :x
|
57
|
+
# self & selected do
|
58
|
+
# font :weight => :bold ; color :black
|
59
|
+
# background :url => '/images/tab/selected.gif', :repeat => :x
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# form do
|
64
|
+
#
|
65
|
+
# ul.properties do
|
66
|
+
#
|
67
|
+
# block
|
68
|
+
#
|
69
|
+
# li.property do
|
70
|
+
#
|
71
|
+
# block ; padding :bottom => 4
|
72
|
+
#
|
73
|
+
# label do
|
74
|
+
# block ; left ; dark ; large
|
75
|
+
# width 8 ; text_align :right
|
76
|
+
# padding :right => 2 ; font :weight => :bold
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# div.value { left ; dark }
|
80
|
+
#
|
81
|
+
# div.radio.value | div.checkbox.value do
|
82
|
+
# div.option do
|
83
|
+
# large ; padding :left => 2 ; height 2 ; cursor :pointer
|
84
|
+
# background :url => '/images/bt_radio_off.gif', :repeat => false
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# div.text.value do
|
89
|
+
#
|
90
|
+
# small ; dark ; margin :top => '-4px'
|
91
|
+
#
|
92
|
+
# input do
|
93
|
+
# height 2 ; width 22 ; border false; background :url => '/images/textfield_bg.gif', :repeat => false
|
94
|
+
# padding 0.5 ; font :weight => :bold
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# textarea { width 28 ; height 18 }
|
98
|
+
#
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# li.size.property do
|
104
|
+
#
|
105
|
+
# div.option do
|
106
|
+
# background :url => '/images/bt_radio_off.gif', :repeat => false
|
107
|
+
# padding :left => 2 ; height 2 ; large
|
108
|
+
#
|
109
|
+
# self & selected do
|
110
|
+
# background :url => '/images/bt_radio_on.gif', :repeat => false
|
111
|
+
# padding :left => 2 ; height 2 ; large
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# li.style.property do
|
119
|
+
#
|
120
|
+
# div.option do
|
121
|
+
# left ; border '1px solid silver' ; padding '1px' ; margin-left 0.5 ; opacity 0.5
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# div.option.hover | div.option.selected { border '1px solid gray' }
|
125
|
+
#
|
126
|
+
# div.option.selected { opacity 1.0 }
|
127
|
+
#
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# li.tos.property / div.option
|
131
|
+
# background :url => '/images/checkbox.gif', :repeat => false ; padding :left => 2
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# li.code.property { display false }
|
135
|
+
#
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# end
|
141
|
+
#
|
142
|
+
# div.preview do
|
143
|
+
#
|
144
|
+
# left ; height 40
|
145
|
+
#
|
146
|
+
# iframe do
|
147
|
+
# block ; height 34 ; width 49 ; position :relative
|
148
|
+
# margin 0.5 ; padding :top => 3, :left => 0
|
149
|
+
# border '1px solid silver'
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# div.ac_results do
|
153
|
+
# padding 0.5 ; margin :top => 1
|
154
|
+
# border '1px solid silver' ; text_align :left ; dark
|
155
|
+
# background :color => :white ;
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# end
|
data/test/fiddle.css
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
div {
|
2
|
+
background: red;
|
3
|
+
width: 9934px;
|
4
|
+
}
|
5
|
+
div ul li {
|
6
|
+
color: green;
|
7
|
+
}
|
8
|
+
div p.ugly.truly {
|
9
|
+
color: aqua;
|
10
|
+
}
|
11
|
+
div .smurf.house {
|
12
|
+
height: 256px;
|
13
|
+
}
|
14
|
+
div #menu {
|
15
|
+
padding: 10px;
|
16
|
+
}
|
17
|
+
.gargamel {
|
18
|
+
margin: 0px;
|
19
|
+
}
|
20
|
+
.outer.middle.inner {
|
21
|
+
top: 34px;
|
22
|
+
}
|
data/test/fiddle.cssy
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
div do
|
3
|
+
|
4
|
+
# this is a property on div
|
5
|
+
background :red
|
6
|
+
|
7
|
+
selector('ul li') do
|
8
|
+
color('green')
|
9
|
+
end
|
10
|
+
|
11
|
+
p.ugly.truly do
|
12
|
+
color('aqua')
|
13
|
+
end
|
14
|
+
|
15
|
+
selector('.smurf').house do
|
16
|
+
height('256px')
|
17
|
+
end
|
18
|
+
|
19
|
+
menu! do
|
20
|
+
padding("10px")
|
21
|
+
end
|
22
|
+
|
23
|
+
# this is a property on div, again
|
24
|
+
width('9934px')
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
gargamel do
|
29
|
+
margin("0px")
|
30
|
+
end
|
31
|
+
|
32
|
+
selector('.outer.middle.inner') do
|
33
|
+
top("34px")
|
34
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cassandra
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthew King
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-09-08 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Generates CSS using Ruby, like Markaby
|
17
|
+
email: automatthew@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- lib/cassandra.rb
|
24
|
+
- lib/properties.rb
|
25
|
+
- lib/tags.rb
|
26
|
+
- README.rdoc
|
27
|
+
files:
|
28
|
+
- cassandra.gemspec
|
29
|
+
- lib/cassandra.rb
|
30
|
+
- lib/properties.rb
|
31
|
+
- lib/tags.rb
|
32
|
+
- Manifest
|
33
|
+
- misc/dan.rb
|
34
|
+
- misc/meyer_reset.css
|
35
|
+
- README.rdoc
|
36
|
+
- site/basic.cssy
|
37
|
+
- site/basic.rb
|
38
|
+
- site/cssy_title.jpg
|
39
|
+
- site/flower.png
|
40
|
+
- site/index.mab
|
41
|
+
- site/ruby.cssy
|
42
|
+
- test/basics.rb
|
43
|
+
- test/dan.cssy
|
44
|
+
- test/fiddle.css
|
45
|
+
- test/fiddle.cssy
|
46
|
+
- test/helper.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: ""
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --line-numbers
|
52
|
+
- --inline-source
|
53
|
+
- --title
|
54
|
+
- Cassandra
|
55
|
+
- --main
|
56
|
+
- README.rdoc
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project: cassandra
|
74
|
+
rubygems_version: 1.2.0
|
75
|
+
signing_key:
|
76
|
+
specification_version: 2
|
77
|
+
summary: Generates CSS using Ruby, like Markaby
|
78
|
+
test_files:
|
79
|
+
- test/basics.rb
|
80
|
+
- test/helper.rb
|