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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5cd310cbe256ffe70545ac23588ee479889fe91
4
- data.tar.gz: 188cb2e8cc974b8f3742cf0c48b4f91f207e5009
3
+ metadata.gz: f45cce4c5e22a829c92ab432750b660ccbb3c5ce
4
+ data.tar.gz: b35cfc68c4599a09d058b58d30561faad7088127
5
5
  SHA512:
6
- metadata.gz: ac2680077e63109eef45e47dde0c8b0ca8c15c16328e3c66a064868e2e8af45f72a5b52b4bc4c6eb5475a00f1d736ed51b7fd5387b5ea5f796664100def2c24f
7
- data.tar.gz: 569b5c556e145aa41323f09fe470dcd5a6a4daa67c8b30e1d38c9bc7f582ed506b84ac1fb5926033eb6815ee95b09083737572b726bc136a74a856b3645eaec6
6
+ metadata.gz: f09353b8bf0a32cf35ec40ba71fbb0a7054068ed9c854597ca7dd567a2d19541a5bcda03fea9bacef434b4a36ddd3e0a58187ca0b467b8f8a9fd34ebf359fba0
7
+ data.tar.gz: 462b363f533688c41e2b799a7e246c736cd69fe99579f89c3122fbaad3f1327567d885a6ab870c5f485d3feb57ee2bd89553ee583d4ccff39f680aadaa721fb1
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - jruby
7
+ - rbx
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
- I like trains.
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 SCSS are too mainstream. On a serious note, why have
127
- templating systems when you can just write Ruby?
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 'stringio'
12
-
13
- require 'paggio/css'
11
+ require 'paggio/utils'
14
12
  require 'paggio/html'
15
- require 'paggio/format'
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).format.string
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).format.string
30
+ Formatter.new.format(HTML.new(*args, &block)).to_s
24
31
  end
25
32
 
26
33
  def self.html!(&block)
27
- HTML.new(&block).root!.format.string
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
@@ -75,4 +75,10 @@ class CSS < BasicObject
75
75
  end
76
76
  end
77
77
 
78
+ class HTML < BasicObject
79
+ def style(&block)
80
+ (@current || @roots) << CSS.new(&block)
81
+ end
82
+ end
83
+
78
84
  end
@@ -28,11 +28,7 @@ class Definition < BasicObject
28
28
  end
29
29
 
30
30
  def each(&block)
31
- return enum_for :each unless block
32
-
33
31
  @style.each(&block)
34
-
35
- self
36
32
  end
37
33
 
38
34
  def gradient(*args)
@@ -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
+ '&' => '&amp;',
102
+ '>' => '&gt;',
103
+ '<' => '&lt;',
104
+ '"' => '&quot;',
105
+ "'" => '&#39;' })
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
 
@@ -23,23 +23,25 @@ class Element < BasicObject
23
23
  end
24
24
  end
25
25
 
26
- attr_reader :name, :attributes
27
- attr_accessor :inner_html
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 = owner
31
- @name = name
32
- @attributes = attributes
33
- @children = []
34
- @class = []
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
- @class.push(name)
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
- @class.delete(@last)
78
- @class.push([@last, *names].join('-'))
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new {|s|
2
2
  s.name = 'paggio'
3
- s.version = '0.1.0'
3
+ s.version = '0.2.0'
4
4
  s.author = 'meh.'
5
5
  s.email = 'meh@schizofreni.co'
6
6
  s.homepage = 'http://github.com/meh/paggio'
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.1.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-12 00:00:00.000000000 Z
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/format.rb
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