paggio 0.1.0 → 0.2.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 +4 -4
- data/.travis.yml +7 -0
- data/README.md +82 -3
- data/Rakefile +16 -0
- data/lib/paggio.rb +18 -7
- data/lib/paggio/css.rb +6 -0
- data/lib/paggio/css/definition.rb +0 -4
- data/lib/paggio/formatter.rb +183 -0
- data/lib/paggio/html.rb +3 -11
- data/lib/paggio/html/element.rb +48 -21
- data/lib/paggio/markdown.rb +26 -0
- data/lib/paggio/script.rb +50 -0
- data/lib/paggio/utils.rb +46 -0
- data/paggio.gemspec +1 -1
- data/spec/css_spec.rb +41 -0
- metadata +11 -4
- data/lib/paggio/format.rb +0 -117
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f45cce4c5e22a829c92ab432750b660ccbb3c5ce
|
4
|
+
data.tar.gz: b35cfc68c4599a09d058b58d30561faad7088127
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f09353b8bf0a32cf35ec40ba71fbb0a7054068ed9c854597ca7dd567a2d19541a5bcda03fea9bacef434b4a36ddd3e0a58187ca0b467b8f8a9fd34ebf359fba0
|
7
|
+
data.tar.gz: 462b363f533688c41e2b799a7e246c736cd69fe99579f89c3122fbaad3f1327567d885a6ab870c5f485d3feb57ee2bd89553ee583d4ccff39f680aadaa721fb1
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -8,6 +8,8 @@ Inspired by markaby with some specialized additions for certain kinds of
|
|
8
8
|
elements.
|
9
9
|
|
10
10
|
```ruby
|
11
|
+
require 'paggio/now'
|
12
|
+
|
11
13
|
# html! avoids outputting the doctype and <html> tags
|
12
14
|
html! do
|
13
15
|
div.content! do
|
@@ -22,7 +24,9 @@ This will output:
|
|
22
24
|
|
23
25
|
```html
|
24
26
|
<div id="content">
|
25
|
-
|
27
|
+
<p>
|
28
|
+
I like trains.
|
29
|
+
</p>
|
26
30
|
</div>
|
27
31
|
```
|
28
32
|
|
@@ -31,6 +35,8 @@ CSS
|
|
31
35
|
Inspired by SCSS with some nice CSS unit handling monkey-patching.
|
32
36
|
|
33
37
|
```ruby
|
38
|
+
require 'paggio/now'
|
39
|
+
|
34
40
|
css do
|
35
41
|
rule '.content' do
|
36
42
|
background :black
|
@@ -121,7 +127,80 @@ get '/' do
|
|
121
127
|
end
|
122
128
|
```
|
123
129
|
|
130
|
+
With Markdown
|
131
|
+
-------------
|
132
|
+
You'll need the `kramdown` gem.
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
require 'paggio/now'
|
136
|
+
require 'paggio/markdown'
|
137
|
+
|
138
|
+
html do
|
139
|
+
markdown <<-MD
|
140
|
+
Here comes a bunch of **shitty** markdown.
|
141
|
+
MD
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
Since *paggio* does internal heredoc indentation pruning, you don't have to
|
146
|
+
worry about that.
|
147
|
+
|
148
|
+
With Opal
|
149
|
+
---------
|
150
|
+
You'll need the `--pre sourcify` gem and the `opal` gem.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
require 'paggio/now'
|
154
|
+
require 'paggio/script'
|
155
|
+
|
156
|
+
html do
|
157
|
+
head do
|
158
|
+
script src: 'js/opal.js'
|
159
|
+
script src: 'js/browser.js'
|
160
|
+
|
161
|
+
script do
|
162
|
+
alert 'Yo dawg'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
Calling local methods inside the DSLs
|
169
|
+
-------------------------------------
|
170
|
+
Don't you just love `instance_eval`? Well, I do, but sometimes it's not the
|
171
|
+
best tool for the job, in fact you cannot call local methods or access instance
|
172
|
+
variables inside the DSLs since they're evaluated in another context.
|
173
|
+
|
174
|
+
Well, fear not, doing that is as easy as adding a parameter to the DSL block.
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
class Page
|
178
|
+
def initialize(title, content)
|
179
|
+
@title = title
|
180
|
+
@content = content
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_html
|
184
|
+
Paggio.html do |_|
|
185
|
+
_.html do
|
186
|
+
_.head do
|
187
|
+
title @title
|
188
|
+
end
|
189
|
+
|
190
|
+
_.body do
|
191
|
+
@content
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
puts Page.new("foo", "bar").to_html
|
199
|
+
```
|
200
|
+
|
124
201
|
Why?
|
125
202
|
----
|
126
|
-
Because HAML and
|
127
|
-
|
203
|
+
Because HAML, SCSS and CoffeeScript are too mainstream.
|
204
|
+
|
205
|
+
On a serious note, why have templating systems and syntax sugar when you can
|
206
|
+
just write Ruby?
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
task :default => [:install, :test]
|
5
|
+
|
6
|
+
task :install do
|
7
|
+
sh 'gem install --no-force rspec'
|
8
|
+
sh 'gem build *.gemspec'
|
9
|
+
sh 'gem install *.gem'
|
10
|
+
end
|
11
|
+
|
12
|
+
task :test do
|
13
|
+
FileUtils.cd 'spec' do
|
14
|
+
sh 'rspec css_spec.rb --backtrace --color --format doc'
|
15
|
+
end
|
16
|
+
end
|
data/lib/paggio.rb
CHANGED
@@ -8,22 +8,33 @@
|
|
8
8
|
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
9
|
#++
|
10
10
|
|
11
|
-
require '
|
12
|
-
|
13
|
-
require 'paggio/css'
|
11
|
+
require 'paggio/utils'
|
14
12
|
require 'paggio/html'
|
15
|
-
require 'paggio/
|
13
|
+
require 'paggio/css'
|
14
|
+
require 'paggio/formatter'
|
16
15
|
|
17
16
|
class Paggio
|
17
|
+
def self.options(options, &block)
|
18
|
+
Formatter.options(options, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.indent(options, &block)
|
22
|
+
options(indent: options, &block)
|
23
|
+
end
|
24
|
+
|
18
25
|
def self.css(*args, &block)
|
19
|
-
CSS.new(*args, &block).
|
26
|
+
Formatter.new.format(CSS.new(*args, &block)).to_s
|
20
27
|
end
|
21
28
|
|
22
29
|
def self.html(*args, &block)
|
23
|
-
HTML.new(*args, &block).
|
30
|
+
Formatter.new.format(HTML.new(*args, &block)).to_s
|
24
31
|
end
|
25
32
|
|
26
33
|
def self.html!(&block)
|
27
|
-
|
34
|
+
Formatter.new.tap {|f|
|
35
|
+
HTML.new(&block).each {|root|
|
36
|
+
f.format root
|
37
|
+
}
|
38
|
+
}.to_s
|
28
39
|
end
|
29
40
|
end
|
data/lib/paggio/css.rb
CHANGED
@@ -0,0 +1,183 @@
|
|
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
|
+
class Paggio
|
14
|
+
|
15
|
+
class Formatter
|
16
|
+
def self.to_h
|
17
|
+
@formatters ||= {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.for(klass, &block)
|
21
|
+
if block
|
22
|
+
to_h[klass] = block
|
23
|
+
else
|
24
|
+
to_h[klass]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.options(options, &block)
|
29
|
+
old = OPTIONS.dup
|
30
|
+
Utils.deep_merge!(OPTIONS, options)
|
31
|
+
|
32
|
+
result = block.call
|
33
|
+
|
34
|
+
OPTIONS.replace(old)
|
35
|
+
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
OPTIONS = {
|
40
|
+
indent: {
|
41
|
+
level: 0,
|
42
|
+
with: "\t"
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
def initialize(io = nil, options = {})
|
47
|
+
if Hash === io
|
48
|
+
@io = StringIO.new
|
49
|
+
@options = io
|
50
|
+
else
|
51
|
+
@io = io || StringIO.new
|
52
|
+
@options = options
|
53
|
+
end
|
54
|
+
|
55
|
+
@options = OPTIONS.merge(@options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def format(item)
|
59
|
+
Formatter.to_h.each {|klass, block|
|
60
|
+
if klass === item
|
61
|
+
block.call(self, item)
|
62
|
+
break
|
63
|
+
end
|
64
|
+
}
|
65
|
+
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
@io.string
|
71
|
+
end
|
72
|
+
|
73
|
+
def indent?(&block)
|
74
|
+
@options[:indent][:level]
|
75
|
+
rescue
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
def indent(&block)
|
80
|
+
if indent?
|
81
|
+
@options[:indent][:level] += 1
|
82
|
+
block.call
|
83
|
+
@options[:indent][:level] -= 1
|
84
|
+
else
|
85
|
+
block.call
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def print(text)
|
90
|
+
if level = indent?
|
91
|
+
text.lines.each {|line|
|
92
|
+
@io.puts "#{@options[:indent][:with] * level}#{line.chomp}"
|
93
|
+
}
|
94
|
+
else
|
95
|
+
@io.print text
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def escape(string)
|
100
|
+
string.to_s.gsub(/["><']|&(?!([a-zA-Z]+|(#\d+));)/, {
|
101
|
+
'&' => '&',
|
102
|
+
'>' => '>',
|
103
|
+
'<' => '<',
|
104
|
+
'"' => '"',
|
105
|
+
"'" => ''' })
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Formatter.for HTML do |f, item|
|
110
|
+
case item.version
|
111
|
+
when 5
|
112
|
+
f.print '<!DOCTYPE html>'
|
113
|
+
end
|
114
|
+
|
115
|
+
f.print '<html>'
|
116
|
+
f.indent {
|
117
|
+
item.each {|root|
|
118
|
+
f.format(root)
|
119
|
+
}
|
120
|
+
}
|
121
|
+
f.print '</html>'
|
122
|
+
end
|
123
|
+
|
124
|
+
Formatter.for HTML::Element do |f, item|
|
125
|
+
name, attributes, class_names = item.instance_eval {
|
126
|
+
[@name, @attributes, @class_names]
|
127
|
+
}
|
128
|
+
|
129
|
+
if attributes.empty? && class_names.empty?
|
130
|
+
f.print "<#{name}>"
|
131
|
+
else
|
132
|
+
attrs = attributes.map {|key, value|
|
133
|
+
%Q{#{f.escape(key)}="#{f.escape(value)}"}
|
134
|
+
}
|
135
|
+
|
136
|
+
unless class_names.empty?
|
137
|
+
attrs << %Q{class="#{f.escape(class_names.join(' '))}"}
|
138
|
+
end
|
139
|
+
|
140
|
+
f.print "<#{name} #{attrs.join(' ')}>"
|
141
|
+
end
|
142
|
+
|
143
|
+
f.indent {
|
144
|
+
if inner = item.instance_eval { @inner_html }
|
145
|
+
f.print inner
|
146
|
+
else
|
147
|
+
item.each {|child|
|
148
|
+
case child
|
149
|
+
when String
|
150
|
+
f.print f.escape(child)
|
151
|
+
|
152
|
+
when CSS
|
153
|
+
f.print '<style>'
|
154
|
+
f.indent {
|
155
|
+
f.format(child)
|
156
|
+
}
|
157
|
+
f.print '</style>'
|
158
|
+
|
159
|
+
else
|
160
|
+
f.format(child)
|
161
|
+
end
|
162
|
+
}
|
163
|
+
end
|
164
|
+
}
|
165
|
+
|
166
|
+
f.print "</#{name}>"
|
167
|
+
end
|
168
|
+
|
169
|
+
Formatter.for CSS do |f, item|
|
170
|
+
item.rules.reverse.each {|rule|
|
171
|
+
next if rule.definition.empty?
|
172
|
+
|
173
|
+
f.print "#{rule.selector} {"
|
174
|
+
f.indent {
|
175
|
+
rule.definition.each {|style|
|
176
|
+
f.print "#{style.name}: #{style.value}#{' !important' if style.important?};"
|
177
|
+
}
|
178
|
+
}
|
179
|
+
f.print '}'
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
data/lib/paggio/html.rb
CHANGED
@@ -51,7 +51,7 @@ class HTML < BasicObject
|
|
51
51
|
result = block.call(self)
|
52
52
|
|
53
53
|
if ::String === result
|
54
|
-
@current.inner_html = result
|
54
|
+
@current.instance_eval { @inner_html = result }
|
55
55
|
end
|
56
56
|
|
57
57
|
@current = old
|
@@ -60,15 +60,7 @@ class HTML < BasicObject
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def each(&block)
|
63
|
-
return enum_for :each unless block
|
64
|
-
|
65
63
|
@roots.each(&block)
|
66
|
-
|
67
|
-
self
|
68
|
-
end
|
69
|
-
|
70
|
-
def style(&block)
|
71
|
-
(@current || @roots) << CSS.new(&block)
|
72
64
|
end
|
73
65
|
|
74
66
|
def method_missing(name, *args, &block)
|
@@ -77,7 +69,7 @@ class HTML < BasicObject
|
|
77
69
|
end
|
78
70
|
|
79
71
|
if ::String === args.first
|
80
|
-
content = args.shift
|
72
|
+
content = ::Paggio::Utils.heredoc(args.shift)
|
81
73
|
end
|
82
74
|
|
83
75
|
element = Element.new(self, name, *args)
|
@@ -90,7 +82,7 @@ class HTML < BasicObject
|
|
90
82
|
@current = parent
|
91
83
|
|
92
84
|
if ::String === result
|
93
|
-
element.inner_html = result
|
85
|
+
element.instance_eval { @inner_html = result }
|
94
86
|
end
|
95
87
|
end
|
96
88
|
|
data/lib/paggio/html/element.rb
CHANGED
@@ -23,23 +23,25 @@ class Element < BasicObject
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
def self.defhelper(name, &block)
|
27
|
+
define_method name do |*args, &body|
|
28
|
+
instance_exec(*args, &block)
|
29
|
+
|
30
|
+
self.do(&block) if body
|
31
|
+
self
|
32
|
+
end
|
33
|
+
end
|
28
34
|
|
29
35
|
def initialize(owner, name, attributes = {})
|
30
|
-
@owner
|
31
|
-
@name
|
32
|
-
@attributes
|
33
|
-
@children
|
34
|
-
@
|
36
|
+
@owner = owner
|
37
|
+
@name = name
|
38
|
+
@attributes = attributes
|
39
|
+
@children = []
|
40
|
+
@class_names = []
|
35
41
|
end
|
36
42
|
|
37
43
|
def each(&block)
|
38
|
-
return enum_for :each unless block
|
39
|
-
|
40
44
|
@children.each(&block)
|
41
|
-
|
42
|
-
self
|
43
45
|
end
|
44
46
|
|
45
47
|
def <<(what)
|
@@ -48,22 +50,16 @@ class Element < BasicObject
|
|
48
50
|
self
|
49
51
|
end
|
50
52
|
|
51
|
-
def text(text)
|
52
|
-
@children << text.to_s
|
53
|
-
|
54
|
-
self
|
55
|
-
end
|
56
|
-
|
57
53
|
def method_missing(name, content = nil, &block)
|
58
54
|
if content
|
59
|
-
self << content
|
55
|
+
self << ::Paggio::Utils.heredoc(content)
|
60
56
|
end
|
61
57
|
|
62
58
|
if name.to_s.end_with? ?!
|
63
59
|
@attributes[:id] = name[0 .. -2]
|
64
60
|
else
|
65
61
|
@last = name
|
66
|
-
@
|
62
|
+
@class_names.push(name)
|
67
63
|
end
|
68
64
|
|
69
65
|
@owner.extend!(self, &block) if block
|
@@ -74,8 +70,8 @@ class Element < BasicObject
|
|
74
70
|
def [](*names)
|
75
71
|
return unless @last
|
76
72
|
|
77
|
-
@
|
78
|
-
@
|
73
|
+
@class_names.pop
|
74
|
+
@class_names.push([@last, *names].join('-'))
|
79
75
|
|
80
76
|
self
|
81
77
|
end
|
@@ -93,6 +89,37 @@ class Element < BasicObject
|
|
93
89
|
"#<HTML::Element(#{@name.upcase}): #{@children.inspect[1 .. -2]}>"
|
94
90
|
end
|
95
91
|
end
|
92
|
+
|
93
|
+
class Img < self
|
94
|
+
defhelper :src do |url|
|
95
|
+
@attributes[:src] = url.to_s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class A < self
|
100
|
+
defhelper :href do |url|
|
101
|
+
@attributes[:href] = url.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
defhelper :text do |string|
|
105
|
+
self << string
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class Input < self
|
110
|
+
{ type: :type,
|
111
|
+
name: :name,
|
112
|
+
value: :value,
|
113
|
+
size: :size,
|
114
|
+
place_holder: :placeholder,
|
115
|
+
read_only: :readonly,
|
116
|
+
required: :required
|
117
|
+
}.each {|name, attribute|
|
118
|
+
defhelper name do |value|
|
119
|
+
@element[attribute] = value
|
120
|
+
end
|
121
|
+
}
|
122
|
+
end
|
96
123
|
end
|
97
124
|
|
98
125
|
end; end
|
@@ -0,0 +1,26 @@
|
|
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 'kramdown'
|
12
|
+
|
13
|
+
class Paggio
|
14
|
+
|
15
|
+
class HTML < BasicObject
|
16
|
+
def markdown(string)
|
17
|
+
(@current || @roots) << ::Kramdown::Document.new(
|
18
|
+
::Paggio::Utils.heredoc(string))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Formatter.for Kramdown::Document do |f, item|
|
23
|
+
f.print item.to_html
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
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 'opal'
|
12
|
+
require 'sourcify'
|
13
|
+
|
14
|
+
class Paggio
|
15
|
+
|
16
|
+
class Script
|
17
|
+
def initialize(options = {}, &block)
|
18
|
+
@options = options
|
19
|
+
@block = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def build
|
23
|
+
Opal.compile(@block.to_source['proc { '.length .. -' }'.length],
|
24
|
+
@options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class HTML < BasicObject
|
29
|
+
def script(*args, &block)
|
30
|
+
if block
|
31
|
+
(@current || @roots) << Script.new(*args, &block)
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def local_variables(*)
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Formatter.for Script do |f, item|
|
43
|
+
f.print '<script><![CDATA['
|
44
|
+
f.indent {
|
45
|
+
f.print item.build
|
46
|
+
}
|
47
|
+
f.print ']]></script>'
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/paggio/utils.rb
ADDED
@@ -0,0 +1,46 @@
|
|
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 Utils
|
14
|
+
def self.heredoc(string)
|
15
|
+
indent = string.scan(/^[ \t]*(?=\S)/).min.size rescue 0
|
16
|
+
|
17
|
+
string.gsub(/^[ \t]{#{indent}}/, '')
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.deep_merge(a, b)
|
21
|
+
merger = proc {|key, v1, v2|
|
22
|
+
if Hash === v1 && Hash === v2
|
23
|
+
v1.merge(v2, &merger)
|
24
|
+
else
|
25
|
+
v2
|
26
|
+
end
|
27
|
+
}
|
28
|
+
|
29
|
+
a.merge(b, &merger)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.deep_merge!(a, b)
|
33
|
+
merger = proc {|key, v1, v2|
|
34
|
+
if Hash === v1 && Hash === v2
|
35
|
+
v1.merge!(v2, &merger)
|
36
|
+
v1
|
37
|
+
else
|
38
|
+
v2
|
39
|
+
end
|
40
|
+
}
|
41
|
+
|
42
|
+
a.merge!(b, &merger)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/paggio.gemspec
CHANGED
data/spec/css_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'paggio'
|
2
|
+
|
3
|
+
describe Paggio::CSS do
|
4
|
+
it 'builds a rule' do
|
5
|
+
css = Paggio.css do
|
6
|
+
rule '#lol' do
|
7
|
+
color 'black'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
css.to_s.should == "#lol {\n\tcolor: black;\n}\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'builds border-radius correctly' do
|
15
|
+
css = Paggio.css do
|
16
|
+
rule '#lol' do
|
17
|
+
border radius: '5px'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
css.to_s.should == "#lol {\n\t-moz-border-radius: 5px;\n\t-webkit-border-radius: 5px;\n\tborder-radius: 5px;\n}\n"
|
22
|
+
|
23
|
+
css = Paggio.css do
|
24
|
+
rule '#lol' do
|
25
|
+
border radius: { top: { left: '5px' } }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
css.to_s.should == "#lol {\n\t-moz-border-radius-topleft: 5px;\n\t-webkit-border-top-left-radius: 5px;\n\tborder-top-left-radius: 5px;\n}\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'builds box-shadow correctly' do
|
33
|
+
css = Paggio.css do
|
34
|
+
rule '#lol' do
|
35
|
+
box shadow: '0 0 5px black'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
css.to_s.should == "#lol {\n\t-moz-box-shadow: 0 0 5px black;\n\t-webkit-box-shadow: 0 0 5px black;\n\tbox-shadow: 0 0 5px black;\n}\n"
|
40
|
+
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paggio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- meh.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: meh@schizofreni.co
|
@@ -16,16 +16,22 @@ executables: []
|
|
16
16
|
extensions: []
|
17
17
|
extra_rdoc_files: []
|
18
18
|
files:
|
19
|
+
- ".travis.yml"
|
19
20
|
- README.md
|
21
|
+
- Rakefile
|
20
22
|
- lib/paggio.rb
|
21
23
|
- lib/paggio/css.rb
|
22
24
|
- lib/paggio/css/definition.rb
|
23
25
|
- lib/paggio/css/unit.rb
|
24
|
-
- lib/paggio/
|
26
|
+
- lib/paggio/formatter.rb
|
25
27
|
- lib/paggio/html.rb
|
26
28
|
- lib/paggio/html/element.rb
|
29
|
+
- lib/paggio/markdown.rb
|
27
30
|
- lib/paggio/now.rb
|
31
|
+
- lib/paggio/script.rb
|
32
|
+
- lib/paggio/utils.rb
|
28
33
|
- paggio.gemspec
|
34
|
+
- spec/css_spec.rb
|
29
35
|
homepage: http://github.com/meh/paggio
|
30
36
|
licenses: []
|
31
37
|
metadata: {}
|
@@ -49,4 +55,5 @@ rubygems_version: 2.1.5
|
|
49
55
|
signing_key:
|
50
56
|
specification_version: 4
|
51
57
|
summary: Ruby, HTML and CSS at war.
|
52
|
-
test_files:
|
58
|
+
test_files:
|
59
|
+
- spec/css_spec.rb
|
data/lib/paggio/format.rb
DELETED
@@ -1,117 +0,0 @@
|
|
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
|