paggio 0.1.0
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.
- 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: []
|