paggio 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +127 -0
- data/lib/paggio.rb +29 -0
- data/lib/paggio/css.rb +78 -0
- data/lib/paggio/css/definition.rb +241 -0
- data/lib/paggio/css/unit.rb +200 -0
- data/lib/paggio/format.rb +117 -0
- data/lib/paggio/html.rb +111 -0
- data/lib/paggio/html/element.rb +98 -0
- data/lib/paggio/now.rb +23 -0
- data/paggio.gemspec +14 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a5cd310cbe256ffe70545ac23588ee479889fe91
|
4
|
+
data.tar.gz: 188cb2e8cc974b8f3742cf0c48b4f91f207e5009
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac2680077e63109eef45e47dde0c8b0ca8c15c16328e3c66a064868e2e8af45f72a5b52b4bc4c6eb5475a00f1d736ed51b7fd5387b5ea5f796664100def2c24f
|
7
|
+
data.tar.gz: 569b5c556e145aa41323f09fe470dcd5a6a4daa67c8b30e1d38c9bc7f582ed506b84ac1fb5926033eb6815ee95b09083737572b726bc136a74a856b3645eaec6
|
data/README.md
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
paggio
|
2
|
+
======
|
3
|
+
A Ruby DSL to generate HTML and CSS.
|
4
|
+
|
5
|
+
HTML
|
6
|
+
----
|
7
|
+
Inspired by markaby with some specialized additions for certain kinds of
|
8
|
+
elements.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
# html! avoids outputting the doctype and <html> tags
|
12
|
+
html! do
|
13
|
+
div.content! do
|
14
|
+
p <<-EOS
|
15
|
+
I like trains
|
16
|
+
EOS
|
17
|
+
end
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
This will output:
|
22
|
+
|
23
|
+
```html
|
24
|
+
<div id="content">
|
25
|
+
I like trains.
|
26
|
+
</div>
|
27
|
+
```
|
28
|
+
|
29
|
+
CSS
|
30
|
+
---
|
31
|
+
Inspired by SCSS with some nice CSS unit handling monkey-patching.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
css do
|
35
|
+
rule '.content' do
|
36
|
+
background :black
|
37
|
+
color :white
|
38
|
+
|
39
|
+
rule '&:hover' do
|
40
|
+
background :white
|
41
|
+
color :black
|
42
|
+
end
|
43
|
+
|
44
|
+
rule '.stuff' do
|
45
|
+
font size: 50.px
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
This will output:
|
52
|
+
```css
|
53
|
+
.content {
|
54
|
+
background: black;
|
55
|
+
color: white;
|
56
|
+
}
|
57
|
+
|
58
|
+
.content .stuff {
|
59
|
+
font-size: 50px;
|
60
|
+
}
|
61
|
+
|
62
|
+
.content:hover {
|
63
|
+
background: white;
|
64
|
+
color: black;
|
65
|
+
}
|
66
|
+
```
|
67
|
+
|
68
|
+
With Sinatra
|
69
|
+
------------
|
70
|
+
Because why not.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
require 'sinatra'
|
74
|
+
require 'paggio'
|
75
|
+
|
76
|
+
get '/' do
|
77
|
+
Paggio.html do
|
78
|
+
head do
|
79
|
+
title "Yo, I'm on Sinatra"
|
80
|
+
|
81
|
+
style do
|
82
|
+
rule 'html', 'body' do
|
83
|
+
width 100.%
|
84
|
+
height 100.%
|
85
|
+
|
86
|
+
# reset some stuff
|
87
|
+
margin 0
|
88
|
+
padding 0
|
89
|
+
position :absolute
|
90
|
+
top 0
|
91
|
+
|
92
|
+
background :black
|
93
|
+
color :white
|
94
|
+
end
|
95
|
+
|
96
|
+
rule '#content' do
|
97
|
+
width 50.%
|
98
|
+
height 100.%
|
99
|
+
|
100
|
+
margin 0, :auto
|
101
|
+
|
102
|
+
border left: [3.px, :solid, :white],
|
103
|
+
right: [3.px, :solid, :white]
|
104
|
+
|
105
|
+
text align: :center
|
106
|
+
font size: 23.px
|
107
|
+
|
108
|
+
rule '& > div' do
|
109
|
+
padding 20.px
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
body do
|
116
|
+
div.content! do
|
117
|
+
div 'Hello world!'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
Why?
|
125
|
+
----
|
126
|
+
Because HAML and SCSS are too mainstream. On a serious note, why have
|
127
|
+
templating systems when you can just write Ruby?
|
data/lib/paggio.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'stringio'
|
12
|
+
|
13
|
+
require 'paggio/css'
|
14
|
+
require 'paggio/html'
|
15
|
+
require 'paggio/format'
|
16
|
+
|
17
|
+
class Paggio
|
18
|
+
def self.css(*args, &block)
|
19
|
+
CSS.new(*args, &block).format.string
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.html(*args, &block)
|
23
|
+
HTML.new(*args, &block).format.string
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.html!(&block)
|
27
|
+
HTML.new(&block).root!.format.string
|
28
|
+
end
|
29
|
+
end
|
data/lib/paggio/css.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'paggio/css/unit'
|
12
|
+
require 'paggio/css/definition'
|
13
|
+
|
14
|
+
class Paggio
|
15
|
+
|
16
|
+
class CSS < BasicObject
|
17
|
+
Rule = ::Struct.new(:selector, :definition)
|
18
|
+
|
19
|
+
def self.selector(list)
|
20
|
+
result = ''
|
21
|
+
|
22
|
+
list.each {|part|
|
23
|
+
if part.start_with?('&')
|
24
|
+
result += part[1 .. -1]
|
25
|
+
else
|
26
|
+
result += " " + part
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
if result[0] == " "
|
31
|
+
result[1 .. -1]
|
32
|
+
else
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :rules
|
38
|
+
|
39
|
+
def initialize(&block)
|
40
|
+
::Kernel.raise ::ArgumentError, 'no block given' unless block
|
41
|
+
|
42
|
+
@selector = []
|
43
|
+
@current = []
|
44
|
+
@rules = []
|
45
|
+
|
46
|
+
if block.arity == 0
|
47
|
+
instance_exec(&block)
|
48
|
+
else
|
49
|
+
block.call(self)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def rule(*names, &block)
|
54
|
+
return unless block
|
55
|
+
|
56
|
+
if names.any? { |n| n.include? ',' }
|
57
|
+
::Kernel.raise ::ArgumentError, 'selectors cannot contain commas'
|
58
|
+
end
|
59
|
+
|
60
|
+
names.each {|name|
|
61
|
+
@selector << name
|
62
|
+
@current << Rule.new(CSS.selector(@selector), Definition.new)
|
63
|
+
|
64
|
+
block.call(self)
|
65
|
+
|
66
|
+
@selector.pop
|
67
|
+
@rules << @current.pop
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# this is needed because the methods inside the rule blocks are actually
|
72
|
+
# called on the CSS object
|
73
|
+
def method_missing(name, *args, &block)
|
74
|
+
@current.last.definition.__send__(name, *args, &block)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
class Paggio; class CSS < BasicObject
|
12
|
+
|
13
|
+
class Definition < BasicObject
|
14
|
+
Style = ::Struct.new(:name, :value, :important?)
|
15
|
+
|
16
|
+
def initialize(&block)
|
17
|
+
@style = []
|
18
|
+
|
19
|
+
if block.arity == 0
|
20
|
+
instance_exec(&block)
|
21
|
+
else
|
22
|
+
block.call(self)
|
23
|
+
end if block
|
24
|
+
end
|
25
|
+
|
26
|
+
def empty?
|
27
|
+
@style.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
return enum_for :each unless block
|
32
|
+
|
33
|
+
@style.each(&block)
|
34
|
+
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def gradient(*args)
|
39
|
+
Gradient.new(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def background(*args)
|
43
|
+
if Gradient === args.first
|
44
|
+
if args.length > 1
|
45
|
+
raise NotImplementedError, "multiple gradients not implemented yet"
|
46
|
+
end
|
47
|
+
|
48
|
+
args.first.each {|s|
|
49
|
+
style s.name || 'background-image', s.value
|
50
|
+
}
|
51
|
+
else
|
52
|
+
if ::Hash === args.first
|
53
|
+
args.first.each {|sub, value|
|
54
|
+
style "background-#{sub}", value
|
55
|
+
}
|
56
|
+
else
|
57
|
+
style :background, args
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def border(*args)
|
63
|
+
if ::Hash === args.first
|
64
|
+
if args.length == 1
|
65
|
+
options = args.first
|
66
|
+
end
|
67
|
+
|
68
|
+
options.each {|name, value|
|
69
|
+
case name
|
70
|
+
when :radius
|
71
|
+
if ::Hash === value
|
72
|
+
value.each {|horizontal, value|
|
73
|
+
value.each {|vertical, value|
|
74
|
+
style "-moz-border-radius-#{horizontal}#{vertical}", value
|
75
|
+
style "-webkit-border-#{horizontal}-#{vertical}-radius", value
|
76
|
+
style "border-#{horizontal}-#{vertical}-radius", value
|
77
|
+
}
|
78
|
+
}
|
79
|
+
else
|
80
|
+
style '-moz-border-radius', value
|
81
|
+
style '-webkit-border-radius', value
|
82
|
+
style 'border-radius', value
|
83
|
+
end
|
84
|
+
|
85
|
+
when :color
|
86
|
+
if ::Hash === value
|
87
|
+
value.each {|name, value|
|
88
|
+
style "border-#{name}-color", value
|
89
|
+
}
|
90
|
+
else
|
91
|
+
style 'border-color', value
|
92
|
+
end
|
93
|
+
|
94
|
+
else
|
95
|
+
style "border-#{name}", value
|
96
|
+
end
|
97
|
+
}
|
98
|
+
else
|
99
|
+
style :border, args
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def box(options)
|
104
|
+
if ::Hash === options
|
105
|
+
options.each {|name, value|
|
106
|
+
case name
|
107
|
+
when :shadow
|
108
|
+
if ::Array === value
|
109
|
+
value = value.join ', '
|
110
|
+
end
|
111
|
+
|
112
|
+
style '-moz-box-shadow', value
|
113
|
+
style '-webkit-box-shadow', value
|
114
|
+
style 'box-shadow', value
|
115
|
+
|
116
|
+
else
|
117
|
+
style "box-#{name}", value
|
118
|
+
end
|
119
|
+
}
|
120
|
+
else
|
121
|
+
style :box, options
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def opacity(value)
|
126
|
+
style 'opacity', value
|
127
|
+
style '-moz-opacity', value
|
128
|
+
|
129
|
+
style '-ms-filter', %Q{"progid:DXImageTransform.Microsoft.Alpha(Opacity=#{(value * 100).to_i})"}
|
130
|
+
style 'filter', "alpha(opacity=#{(value * 100).to_i})"
|
131
|
+
end
|
132
|
+
|
133
|
+
def method_missing(name, *args, &block)
|
134
|
+
name = name.to_s
|
135
|
+
important = name.end_with? ?!
|
136
|
+
name = name[0 .. -2] if important
|
137
|
+
|
138
|
+
@important = true if important
|
139
|
+
|
140
|
+
if important && respond_to?(name)
|
141
|
+
__send__ name, *args, &block
|
142
|
+
@important = false
|
143
|
+
|
144
|
+
return
|
145
|
+
end
|
146
|
+
|
147
|
+
if args.length == 1
|
148
|
+
argument = args.first
|
149
|
+
|
150
|
+
if ::Hash === argument
|
151
|
+
argument.each {|sub, value|
|
152
|
+
style "#{name}-#{sub}", value
|
153
|
+
}
|
154
|
+
else
|
155
|
+
style name, argument
|
156
|
+
end
|
157
|
+
else
|
158
|
+
style name, args.join(' ')
|
159
|
+
end
|
160
|
+
|
161
|
+
@important = false
|
162
|
+
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
def style(name, value = nil, important = @important)
|
168
|
+
if ::Array === value
|
169
|
+
value = value.join ' '
|
170
|
+
end
|
171
|
+
|
172
|
+
if Style === name
|
173
|
+
@style << name
|
174
|
+
else
|
175
|
+
@style << Style.new(name, value, important)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class Gradient
|
180
|
+
# TODO: all of it, seriously
|
181
|
+
def initialize(*args)
|
182
|
+
options = ::Hash === args.last ? args.pop : {}
|
183
|
+
|
184
|
+
@to = options[:to]
|
185
|
+
@from = options[:from]
|
186
|
+
|
187
|
+
if @to && !@from
|
188
|
+
@from = other(@to)
|
189
|
+
elsif @from && !@to
|
190
|
+
@to = other(@from)
|
191
|
+
end
|
192
|
+
|
193
|
+
@start = args.shift
|
194
|
+
@end = args.shift
|
195
|
+
end
|
196
|
+
|
197
|
+
def each(&block)
|
198
|
+
block.call style("-moz-linear-gradient(#@to, #@start 0%, #@end 100%)")
|
199
|
+
|
200
|
+
if horizontal?
|
201
|
+
block.call style("-webkit-gradient(linear, #@from top, #@to top, color-stop(0%, #@start), color-stop(100%, #@end))")
|
202
|
+
else
|
203
|
+
block.call style("-webkit-gradient(linear, left #@from, left #@to, color-stop(0%, #@start), color-stop(100%, #@end))")
|
204
|
+
end
|
205
|
+
|
206
|
+
block.call style("-webkit-linear-gradient(#@to, #@start 0%, #@end 100%)")
|
207
|
+
block.call style("-o-linear-gradient(#@to, #@start 0%, #@end 100%)")
|
208
|
+
block.call style("-ms-linear-gradient(#@to, #@start 0%, #@end 100%)")
|
209
|
+
block.call style("linear-gradient(to #@to, #@start 0%, #@end 100%)")
|
210
|
+
end
|
211
|
+
|
212
|
+
def horizontal?
|
213
|
+
@to == :left || @to == :right
|
214
|
+
end
|
215
|
+
|
216
|
+
def vertical?
|
217
|
+
@to == :top || @to == :bottom
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
def other(side)
|
222
|
+
case side
|
223
|
+
when :left then :right
|
224
|
+
when :right then :left
|
225
|
+
when :top then :bottom
|
226
|
+
when :bottom then :top
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# FIXME: use default args
|
231
|
+
def style(*args)
|
232
|
+
if args.length == 1
|
233
|
+
Style.new(nil, args.first)
|
234
|
+
else
|
235
|
+
Style.new(*args)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
end; end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
class Paggio; class CSS < BasicObject
|
12
|
+
|
13
|
+
class Unit
|
14
|
+
COMPATIBLE = %i[in pt mm cm px pc]
|
15
|
+
|
16
|
+
attr_reader :type, :number
|
17
|
+
|
18
|
+
def initialize(number, type)
|
19
|
+
@number = number
|
20
|
+
@type = type
|
21
|
+
end
|
22
|
+
|
23
|
+
def coerce(other)
|
24
|
+
return self, other
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
@number == convert(other, @type)
|
29
|
+
end
|
30
|
+
|
31
|
+
def ===(other)
|
32
|
+
@type == other.type && @number == other.number
|
33
|
+
end
|
34
|
+
|
35
|
+
alias eql? ==
|
36
|
+
|
37
|
+
def hash
|
38
|
+
[@number, @type].hash
|
39
|
+
end
|
40
|
+
|
41
|
+
%i[em ex ch rem vh vw vmin vmax px mm cm in pt pc].each {|name|
|
42
|
+
define_method name do
|
43
|
+
Unit.new(convert(self, name), name)
|
44
|
+
end
|
45
|
+
}
|
46
|
+
|
47
|
+
def +(other)
|
48
|
+
return Unit.new(@number + other, @type) unless Unit === other
|
49
|
+
|
50
|
+
if @type == other.type
|
51
|
+
Unit.new(@number + other.number, @type)
|
52
|
+
elsif compatible?(self) and compatible?(other)
|
53
|
+
Unit.new(@number + convert(other, @type), @type)
|
54
|
+
else
|
55
|
+
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def -(other)
|
60
|
+
return Unit.new(@number - other, @type) unless Unit === other
|
61
|
+
|
62
|
+
if @type == other.type
|
63
|
+
Unit.new(@number - other.number, @type)
|
64
|
+
elsif compatible?(self) and compatible?(other)
|
65
|
+
Unit.new(@number - convert(other, @type), @type)
|
66
|
+
else
|
67
|
+
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def *(other)
|
72
|
+
return Unit.new(@number * other, @type) unless Unit === other
|
73
|
+
|
74
|
+
if @type == other.type
|
75
|
+
Unit.new(@number * other.number, @type)
|
76
|
+
elsif compatible?(self) and compatible?(other)
|
77
|
+
Unit.new(@number * convert(other, @type), @type)
|
78
|
+
else
|
79
|
+
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def /(other)
|
84
|
+
return Unit.new(@number / other, @type) unless Unit === other
|
85
|
+
|
86
|
+
if @type == other.type
|
87
|
+
Unit.new(@number / other.number, @type)
|
88
|
+
elsif compatible?(self) and compatible?(other)
|
89
|
+
Unit.new(@number / convert(other, @type), @type)
|
90
|
+
else
|
91
|
+
raise ArgumentError, "#{other.type} isn't compatible with #{@type}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def -@
|
96
|
+
Unit.new(@number * -1, @type)
|
97
|
+
end
|
98
|
+
|
99
|
+
def +@
|
100
|
+
Unit.new(@number, @type)
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_i
|
104
|
+
@number.to_i
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_f
|
108
|
+
@number.to_f
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_u
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_s
|
116
|
+
"#{@number}#{@type}"
|
117
|
+
end
|
118
|
+
|
119
|
+
alias to_str to_s
|
120
|
+
alias inspect to_s
|
121
|
+
|
122
|
+
private
|
123
|
+
def compatible?(unit)
|
124
|
+
COMPATIBLE.include?(unit.type)
|
125
|
+
end
|
126
|
+
|
127
|
+
def convert(unit, type)
|
128
|
+
value = unit.number
|
129
|
+
|
130
|
+
return value if unit.type == type
|
131
|
+
|
132
|
+
px = case unit.type
|
133
|
+
when :in then value * 96
|
134
|
+
when :pt then value * 4.0 / 3.0
|
135
|
+
when :pc then value / 12 * 4.0 / 3.0
|
136
|
+
when :mm then value * 3.77953
|
137
|
+
when :cm then value * 10 * 3.77953
|
138
|
+
when :px then value
|
139
|
+
end
|
140
|
+
|
141
|
+
case type
|
142
|
+
when :in then px / 96.0
|
143
|
+
when :pt then px / 4.0 / 3.0
|
144
|
+
when :pc then px * 12 / 4.0 / 3.0
|
145
|
+
when :mm then px / 3.77953
|
146
|
+
when :cm then px / 10 / 3.77953
|
147
|
+
when :px then px
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end; end
|
153
|
+
|
154
|
+
class Numeric
|
155
|
+
%i[em ex ch rem vh vw vmin vmax px mm cm in pt pc].each {|name|
|
156
|
+
define_method name do
|
157
|
+
Paggio::CSS::Unit.new(self, name)
|
158
|
+
end
|
159
|
+
}
|
160
|
+
|
161
|
+
def to_u
|
162
|
+
self
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
[Fixnum, Float].each {|klass|
|
167
|
+
klass.class_eval {
|
168
|
+
alias old_percent %
|
169
|
+
|
170
|
+
def %(other = nil)
|
171
|
+
if other
|
172
|
+
old_percent(other)
|
173
|
+
else
|
174
|
+
Paggio::CSS::Unit.new(self, :%)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
class String
|
181
|
+
def to_u
|
182
|
+
if matches = match(/^([\d+.]+)(.+)?$/)
|
183
|
+
value = matches[1].to_f
|
184
|
+
|
185
|
+
if unit = matches[2]
|
186
|
+
value.__send__(unit.downcase)
|
187
|
+
else
|
188
|
+
value
|
189
|
+
end
|
190
|
+
else
|
191
|
+
0
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class NilClass
|
197
|
+
def to_u
|
198
|
+
0
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
class Paggio
|
12
|
+
|
13
|
+
module Format
|
14
|
+
def self.puts(io, text, level = 0)
|
15
|
+
io.puts "#{"\t" * level}#{text}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.print(io, text, level = 0)
|
19
|
+
io.print "#{"\t" * level}#{text}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.escape(string)
|
23
|
+
string.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class HTML < BasicObject
|
28
|
+
Format = ::Paggio::Format
|
29
|
+
|
30
|
+
def format(io = ::StringIO.new, options = { indent: 0 })
|
31
|
+
case @version
|
32
|
+
when 5
|
33
|
+
Format.puts io, "<!DOCTYPE html>", options[:indent]
|
34
|
+
end
|
35
|
+
|
36
|
+
Format.puts io, "<html>", options[:indent]
|
37
|
+
|
38
|
+
each {|root|
|
39
|
+
root.format(io, indent: options[:indent] + 1)
|
40
|
+
}
|
41
|
+
|
42
|
+
Format.puts io, "</html>", options[:indent]
|
43
|
+
|
44
|
+
io
|
45
|
+
end
|
46
|
+
|
47
|
+
class Element < BasicObject
|
48
|
+
Format = ::Paggio::Format
|
49
|
+
|
50
|
+
def format(io = ::StringIO.new, options = { indent: 0 })
|
51
|
+
if @attributes.empty? && @class.empty?
|
52
|
+
Format.puts io, "<#{name}>", options[:indent]
|
53
|
+
else
|
54
|
+
attrs = @attributes.map {|name, value|
|
55
|
+
"#{Format.escape(name)}=\"#{Format.escape(value)}\""
|
56
|
+
}
|
57
|
+
|
58
|
+
unless @class.empty?
|
59
|
+
attrs << "class=\"#{Format.escape(@class.join(' '))}\""
|
60
|
+
end
|
61
|
+
|
62
|
+
Format.puts io, "<#{name} #{attrs.join(' ')}>", options[:indent]
|
63
|
+
end
|
64
|
+
|
65
|
+
each {|child|
|
66
|
+
case child
|
67
|
+
when ::String
|
68
|
+
child.lines.each {|line|
|
69
|
+
Format.puts io, line.strip, options[:indent] + 1
|
70
|
+
}
|
71
|
+
|
72
|
+
when CSS
|
73
|
+
Format.puts io, "<style>", options[:indent] + 1
|
74
|
+
child.format(io, indent: options[:indent] + 2)
|
75
|
+
Format.puts io, "</style>", options[:indent] + 1
|
76
|
+
|
77
|
+
else
|
78
|
+
child.format(io, indent: options[:indent] + 1)
|
79
|
+
end
|
80
|
+
|
81
|
+
io.puts
|
82
|
+
}
|
83
|
+
|
84
|
+
io.seek(-1, ::IO::SEEK_CUR)
|
85
|
+
|
86
|
+
Format.puts io, "</#{name}>", @children.empty? ? 0 : options[:indent]
|
87
|
+
|
88
|
+
io
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class CSS < BasicObject
|
94
|
+
Format = ::Paggio::Format
|
95
|
+
|
96
|
+
def format(io = ::StringIO.new, options = { indent: 0 })
|
97
|
+
rules.reverse.each {|rule|
|
98
|
+
next if rule.definition.empty?
|
99
|
+
|
100
|
+
Format.puts io, "#{rule.selector} {", options[:indent]
|
101
|
+
|
102
|
+
rule.definition.each {|style|
|
103
|
+
Format.puts io, "#{style.name}: #{style.value}#{' !important' if style.important?};",
|
104
|
+
options[:indent] + 1
|
105
|
+
}
|
106
|
+
|
107
|
+
Format.puts io, "}", options[:indent]
|
108
|
+
io.puts
|
109
|
+
}
|
110
|
+
|
111
|
+
io.seek(-1, ::IO::SEEK_CUR)
|
112
|
+
|
113
|
+
io
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
data/lib/paggio/html.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'paggio/html/element'
|
12
|
+
|
13
|
+
class Paggio
|
14
|
+
|
15
|
+
class HTML < BasicObject
|
16
|
+
attr_reader :version
|
17
|
+
|
18
|
+
def initialize(version = 5, &block)
|
19
|
+
::Kernel.raise ::ArgumentError, 'no block given' unless block
|
20
|
+
|
21
|
+
@version = version
|
22
|
+
@roots = []
|
23
|
+
@current = nil
|
24
|
+
|
25
|
+
if block.arity == 0
|
26
|
+
instance_exec(&block)
|
27
|
+
else
|
28
|
+
block.call(self)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def <<(what)
|
33
|
+
@current << what
|
34
|
+
end
|
35
|
+
|
36
|
+
def root!
|
37
|
+
@roots.first
|
38
|
+
end
|
39
|
+
|
40
|
+
def roots!
|
41
|
+
@roots
|
42
|
+
end
|
43
|
+
|
44
|
+
def element!
|
45
|
+
@current
|
46
|
+
end
|
47
|
+
|
48
|
+
def extend!(element = nil, &block)
|
49
|
+
old, @current = @current, element
|
50
|
+
|
51
|
+
result = block.call(self)
|
52
|
+
|
53
|
+
if ::String === result
|
54
|
+
@current.inner_html = result
|
55
|
+
end
|
56
|
+
|
57
|
+
@current = old
|
58
|
+
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def each(&block)
|
63
|
+
return enum_for :each unless block
|
64
|
+
|
65
|
+
@roots.each(&block)
|
66
|
+
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def style(&block)
|
71
|
+
(@current || @roots) << CSS.new(&block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def method_missing(name, *args, &block)
|
75
|
+
if name.to_s.end_with? ?!
|
76
|
+
return super
|
77
|
+
end
|
78
|
+
|
79
|
+
if ::String === args.first
|
80
|
+
content = args.shift
|
81
|
+
end
|
82
|
+
|
83
|
+
element = Element.new(self, name, *args)
|
84
|
+
element << content if content
|
85
|
+
|
86
|
+
if block
|
87
|
+
parent = @current
|
88
|
+
@current = element
|
89
|
+
result = block.call(self)
|
90
|
+
@current = parent
|
91
|
+
|
92
|
+
if ::String === result
|
93
|
+
element.inner_html = result
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
(@current || @roots) << element
|
98
|
+
|
99
|
+
element
|
100
|
+
end
|
101
|
+
|
102
|
+
def inspect
|
103
|
+
if @roots.empty?
|
104
|
+
"#<HTML(#@version)>"
|
105
|
+
else
|
106
|
+
"#<HTML(#@version): #{@roots.inspect[1 .. -2]}>"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
class Paggio; class HTML < BasicObject
|
12
|
+
|
13
|
+
class Element < BasicObject
|
14
|
+
def self.new(owner, name, attributes = {})
|
15
|
+
return super unless self == Element
|
16
|
+
|
17
|
+
const = name.capitalize
|
18
|
+
|
19
|
+
if const_defined?(const)
|
20
|
+
const_get(const).new(owner, name, attributes)
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :name, :attributes
|
27
|
+
attr_accessor :inner_html
|
28
|
+
|
29
|
+
def initialize(owner, name, attributes = {})
|
30
|
+
@owner = owner
|
31
|
+
@name = name
|
32
|
+
@attributes = attributes
|
33
|
+
@children = []
|
34
|
+
@class = []
|
35
|
+
end
|
36
|
+
|
37
|
+
def each(&block)
|
38
|
+
return enum_for :each unless block
|
39
|
+
|
40
|
+
@children.each(&block)
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def <<(what)
|
46
|
+
@children << what
|
47
|
+
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def text(text)
|
52
|
+
@children << text.to_s
|
53
|
+
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(name, content = nil, &block)
|
58
|
+
if content
|
59
|
+
self << content
|
60
|
+
end
|
61
|
+
|
62
|
+
if name.to_s.end_with? ?!
|
63
|
+
@attributes[:id] = name[0 .. -2]
|
64
|
+
else
|
65
|
+
@last = name
|
66
|
+
@class.push(name)
|
67
|
+
end
|
68
|
+
|
69
|
+
@owner.extend!(self, &block) if block
|
70
|
+
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def [](*names)
|
75
|
+
return unless @last
|
76
|
+
|
77
|
+
@class.delete(@last)
|
78
|
+
@class.push([@last, *names].join('-'))
|
79
|
+
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def do(&block)
|
84
|
+
@owner.extend!(self, &block)
|
85
|
+
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
if @children.empty?
|
91
|
+
"#<HTML::Element(#{@name.upcase})>"
|
92
|
+
else
|
93
|
+
"#<HTML::Element(#{@name.upcase}): #{@children.inspect[1 .. -2]}>"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end; end
|
data/lib/paggio/now.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'paggio'
|
12
|
+
|
13
|
+
def html(*args, &block)
|
14
|
+
puts Paggio.html(*args, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def html!(&block)
|
18
|
+
puts Paggio.html!(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def css(*args, &block)
|
22
|
+
puts Paggio.css(*args, &block)
|
23
|
+
end
|
data/paggio.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new {|s|
|
2
|
+
s.name = 'paggio'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.author = 'meh.'
|
5
|
+
s.email = 'meh@schizofreni.co'
|
6
|
+
s.homepage = 'http://github.com/meh/paggio'
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = 'Ruby, HTML and CSS at war.'
|
9
|
+
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
12
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
13
|
+
s.require_paths = ['lib']
|
14
|
+
}
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paggio
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- meh.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: meh@schizofreni.co
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- lib/paggio.rb
|
21
|
+
- lib/paggio/css.rb
|
22
|
+
- lib/paggio/css/definition.rb
|
23
|
+
- lib/paggio/css/unit.rb
|
24
|
+
- lib/paggio/format.rb
|
25
|
+
- lib/paggio/html.rb
|
26
|
+
- lib/paggio/html/element.rb
|
27
|
+
- lib/paggio/now.rb
|
28
|
+
- paggio.gemspec
|
29
|
+
homepage: http://github.com/meh/paggio
|
30
|
+
licenses: []
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.1.5
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Ruby, HTML and CSS at war.
|
52
|
+
test_files: []
|