paggio 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3a11d9925363cd6d0477a1b04cb820aa31b7d312
4
- data.tar.gz: 5a0240a9f6e6ac843d69e182b081ffcd53233852
2
+ SHA256:
3
+ metadata.gz: 2fefeb82ee3a488498bd68beb67ff153d799ed90d0048ab6431548af06bae9b3
4
+ data.tar.gz: b4dad7ad19b3976bb1b92fab92cde617338e4d6e02a777891ed04199bb7a5695
5
5
  SHA512:
6
- metadata.gz: 906b709105eee61adf45ff4d672125b2d9c14223833a1d92558c30f6a9eff2fc0f05ed5f370175f1ee09935618751d4b0aa1264f9e9d3eeeac8143cabdbe3963
7
- data.tar.gz: ea71aca4f89688852800f4af745297a2e3d714367fb8aa23b080b448b28d19003e008f074559a9743c8947e86ccbea792acaca7cb82b31b78359bf013bc7e6c0
6
+ metadata.gz: 8e8004f7ba60ad66eae1d64f1be17766f544459aa87b0876bffe3cef9c17997b6dfb16ae96192c33ead174efb146f9b2827245aa3f144b188296bfbb1f95f81a
7
+ data.tar.gz: f37fab29e7fa8404c2f6adec4b711b3a4b644b691fe40065711501e28b12e9369e7e8a370f6174d45bef8cc233c3ab7683cf2b571b55a92220f53f277120b20f
@@ -0,0 +1,29 @@
1
+ name: Tests
2
+ on:
3
+ push:
4
+ branches:
5
+ - master
6
+ - "*-stable"
7
+ - "*/ci-check"
8
+ pull_request:
9
+
10
+ jobs:
11
+ tests:
12
+ name: Tests
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ ruby: [2.6, 2.7, 3.0]
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - name: Checkout code
20
+ uses: actions/checkout@v2
21
+
22
+ - name: Setup Ruby
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{ matrix.ruby }}
26
+ bundler-cache: true
27
+
28
+ - name: Run tests
29
+ run: bundle exec rake
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'rake'
5
+ gem 'rspec'
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ paggio (0.3.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rake (10.1.1)
11
+ rspec (2.14.1)
12
+ rspec-core (~> 2.14.0)
13
+ rspec-expectations (~> 2.14.0)
14
+ rspec-mocks (~> 2.14.0)
15
+ rspec-core (2.14.8)
16
+ rspec-expectations (2.14.5)
17
+ diff-lcs (>= 1.1.3, < 2.0)
18
+ rspec-mocks (2.14.6)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ paggio!
25
+ rake
26
+ rspec
27
+
28
+ BUNDLED WITH
29
+ 2.1.4
data/Rakefile CHANGED
@@ -1,16 +1,10 @@
1
1
  #! /usr/bin/env ruby
2
- require 'rake'
3
2
 
4
- task :default => [:install, :test]
3
+ require 'bundler/setup'
4
+ require 'bundler/gem_tasks'
5
+ require 'rake'
5
6
 
6
- task :install do
7
- sh 'gem install --no-force rspec'
8
- sh 'gem build *.gemspec'
9
- sh 'gem install *.gem'
10
- end
7
+ require "rspec/core/rake_task"
8
+ RSpec::Core::RakeTask.new(:rspec)
11
9
 
12
- task :test do
13
- FileUtils.cd 'spec' do
14
- sh 'rspec css_spec.rb --backtrace --color --format doc'
15
- end
16
- end
10
+ task :default => :rspec
@@ -0,0 +1,54 @@
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 Animation < BasicObject
14
+ class Step < BasicObject
15
+ attr_reader :value
16
+
17
+ def initialize(value)
18
+ @value = value
19
+ @definition = Definition.new
20
+ end
21
+
22
+ def method_missing(*args, &block)
23
+ @definition.__send__(*args, &block)
24
+ end
25
+ end
26
+
27
+ attr_reader :name, :steps
28
+
29
+ def initialize(name)
30
+ @name = name
31
+ @steps = []
32
+ end
33
+
34
+ def step(value, &block)
35
+ @steps << Step.new(value)
36
+ block.call
37
+ end
38
+
39
+ def from(value, &block)
40
+ @steps << Step.new(0.%)
41
+ block.call
42
+ end
43
+
44
+ def to(value, &block)
45
+ @steps << Step.new(100.%)
46
+ block.call
47
+ end
48
+
49
+ def method_missing(*args, &block)
50
+ @steps.last.__send__(*args, &block)
51
+ end
52
+ end
53
+
54
+ end; end
@@ -35,10 +35,40 @@ class Definition < BasicObject
35
35
  Gradient.new(*args)
36
36
  end
37
37
 
38
- def url(arg)
39
- "url(#{arg.inspect})"
38
+ def url(value)
39
+ "url(#{value.to_s.inspect})"
40
40
  end
41
41
 
42
+ %w[blur brightness rotate contrast grayscale invert opacity saturate sepia].each {|name|
43
+ define_method name do |value|
44
+ "#{name}(#{value})"
45
+ end
46
+ }
47
+
48
+ def rgb(r, g, b)
49
+ "rgb(#{r}, #{g}, #{b}, #{a})"
50
+ end
51
+
52
+ def rgba(r, g, b, a)
53
+ "rgba(#{r}, #{g}, #{b}, #{a})"
54
+ end
55
+
56
+ %w[scale skew translate].each {|name|
57
+ define_method name do |a, b = nil|
58
+ if b
59
+ "#{name}(#{a}, #{b})"
60
+ else
61
+ "#{name}(#{a})"
62
+ end
63
+ end
64
+ }
65
+
66
+ %w[translateX translateY translateZ rotateX rotateY rotateZ skewX skewY scaleX scaleY].each {|name|
67
+ define_method name do |value|
68
+ "#{name}(#{value})"
69
+ end
70
+ }
71
+
42
72
  def background(*args)
43
73
  if Gradient === args.first
44
74
  if args.length > 1
@@ -67,6 +97,15 @@ class Definition < BasicObject
67
97
 
68
98
  options.each {|name, value|
69
99
  case name
100
+ when :top, :bottom, :left, :right
101
+ if ::Hash === value
102
+ value.each {|n, v|
103
+ style "border-#{name}-#{n}", v
104
+ }
105
+ else
106
+ style "border-#{name}", value
107
+ end
108
+
70
109
  when :radius
71
110
  if ::Hash === value
72
111
  value.each {|horizontal, value|
@@ -134,14 +173,58 @@ class Definition < BasicObject
134
173
  style 'filter', "alpha(opacity=#{(value * 100).to_i})"
135
174
  end
136
175
 
176
+ def animation(*args)
177
+ if Hash === args.first
178
+ if args.length == 1
179
+ options = args.first
180
+ end
181
+
182
+ options.each {|name, value|
183
+ style "-webkit-animation-#{name}", value
184
+ style "animation-#{name}", value
185
+ }
186
+ else
187
+ style 'animation', args
188
+ style '-webkit-animation', args
189
+ end
190
+ end
191
+
192
+ def transition(*args)
193
+ style 'transition', args
194
+ style '-webkit-transition', args
195
+ style '-moz-transition', args
196
+ end
197
+
198
+ def user_select(*args)
199
+ style 'user-select', args
200
+ style '-webkit-user-select', args
201
+ style '-moz-user-select', args
202
+ style '-ms-user-select', args
203
+ end
204
+
205
+ def transform(*args)
206
+ style 'transform', args
207
+ style '-webkit-transform', args
208
+ style '-moz-transform', args
209
+ style '-ms-transform', args
210
+ style '-o-transform', args
211
+ end
212
+
213
+ def filter(*args)
214
+ style 'filter', args
215
+ style '-webkit-filter', args
216
+ style '-moz-filter', args
217
+ style '-ms-filter', args
218
+ style '-o-filter', args
219
+ end
220
+
137
221
  def method_missing(name, *args, &block)
138
- name = name.to_s
139
- important = name.end_with? ?!
140
- name = name[0 .. -2] if important
222
+ name = name.to_s
141
223
 
142
- @important = true if important
224
+ if name.end_with? ?!
225
+ name = name[0 .. -2]
143
226
 
144
- if important && respond_to?(name)
227
+ @important = true
145
228
  __send__ name, *args, &block
146
229
  @important = false
147
230
 
@@ -0,0 +1,28 @@
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 Font < BasicObject
14
+ attr_reader :name
15
+
16
+ def initialize(name)
17
+ @name = name
18
+ @definition = Definition.new
19
+
20
+ font family: name
21
+ end
22
+
23
+ def method_missing(*args, &block)
24
+ @definition.__send__(*args, &block)
25
+ end
26
+ end
27
+
28
+ end; end
@@ -0,0 +1,27 @@
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 Rule < BasicObject
14
+ attr_reader :selector, :media
15
+
16
+ def initialize(selector, media)
17
+ @selector = selector
18
+ @media = media
19
+ @definition = Definition.new
20
+ end
21
+
22
+ def method_missing(*args, &block)
23
+ @definition.__send__(*args, &block)
24
+ end
25
+ end
26
+
27
+ end; end
@@ -11,6 +11,7 @@
11
11
  class Paggio; class CSS < BasicObject
12
12
 
13
13
  class Unit
14
+ TYPES = %w[em ex ch rem vh vw vmin vmax px mm cm in pt pc s deg].map(&:to_sym)
14
15
  COMPATIBLE = %w[in pt mm cm px pc].map(&:to_sym)
15
16
 
16
17
  attr_reader :type, :number
@@ -25,6 +26,18 @@ class Unit
25
26
  end
26
27
 
27
28
  def ==(other)
29
+ unless Unit === other
30
+ unless other.respond_to? :to_u
31
+ raise TypeError, "no implicit conversion of #{other.class} into Unit"
32
+ end
33
+
34
+ other = other.to_u
35
+ end
36
+
37
+ unless Unit === other
38
+ other = Unit.new(other, @type)
39
+ end
40
+
28
41
  @number == convert(other, @type)
29
42
  end
30
43
 
@@ -38,7 +51,7 @@ class Unit
38
51
  [@number, @type].hash
39
52
  end
40
53
 
41
- %w[em ex ch rem vh vw vmin vmax px mm cm in pt pc].map(&:to_sym).each {|name|
54
+ TYPES.each {|name|
42
55
  define_method name do
43
56
  Unit.new(convert(self, name), name)
44
57
  end
@@ -113,7 +126,7 @@ class Unit
113
126
  end
114
127
 
115
128
  def to_s
116
- "#{@number}#{@type}"
129
+ "#@number#@type"
117
130
  end
118
131
 
119
132
  alias to_str to_s
@@ -152,7 +165,7 @@ end
152
165
  end; end
153
166
 
154
167
  class Numeric
155
- %w[em ex ch rem vh vw vmin vmax px mm cm in pt pc].map(&:to_sym).each {|name|
168
+ Paggio::CSS::Unit::TYPES.each {|name|
156
169
  define_method name do
157
170
  Paggio::CSS::Unit.new(self, name)
158
171
  end
data/lib/paggio/css.rb CHANGED
@@ -11,12 +11,13 @@
11
11
  require 'paggio/css/unit'
12
12
  require 'paggio/css/color'
13
13
  require 'paggio/css/definition'
14
+ require 'paggio/css/rule'
15
+ require 'paggio/css/font'
16
+ require 'paggio/css/animation'
14
17
 
15
18
  class Paggio
16
19
 
17
20
  class CSS < BasicObject
18
- Rule = ::Struct.new(:selector, :definition)
19
-
20
21
  def self.selector(list)
21
22
  result = ''
22
23
 
@@ -35,20 +36,29 @@ class CSS < BasicObject
35
36
  end
36
37
  end
37
38
 
38
- attr_reader :rules
39
+ attr_reader :rules, :media, :fonts, :animations
39
40
 
40
- def initialize(&block)
41
+ def initialize(defer: false, &block)
41
42
  ::Kernel.raise ::ArgumentError, 'no block given' unless block
42
43
 
43
- @selector = []
44
- @current = []
45
- @rules = []
44
+ @selector = []
45
+ @current = []
46
+ @rules = []
47
+ @fonts = []
48
+ @animations = []
49
+
50
+ @block = block
51
+
52
+ build! unless defer
53
+ end
46
54
 
47
- if block.arity == 0
48
- instance_exec(&block)
55
+ def build!(force_call: false)
56
+ if !force_call && @block.arity == 0
57
+ instance_exec(&@block)
49
58
  else
50
- block.call(self)
59
+ @block.call(self)
51
60
  end
61
+ @block = nil
52
62
  end
53
63
 
54
64
  def rule(*names, &block)
@@ -60,19 +70,49 @@ class CSS < BasicObject
60
70
 
61
71
  names.each {|name|
62
72
  @selector << name
63
- @current << Rule.new(CSS.selector(@selector), Definition.new)
73
+ @current << Rule.new(CSS.selector(@selector), @media)
64
74
 
65
- block.call(self)
75
+ block.call
66
76
 
67
77
  @selector.pop
68
78
  @rules << @current.pop
69
79
  }
70
80
  end
71
81
 
82
+ def media(query, *args, &block)
83
+ if block
84
+ old, @media = @media, query
85
+ block.call
86
+ @media = old
87
+ else
88
+ method_missing(:media, query, *args)
89
+ end
90
+ end
91
+
92
+ def font(name, *args, &block)
93
+ if block
94
+ @current << Font.new(name)
95
+ block.call
96
+ @fonts << @current.pop
97
+ else
98
+ method_missing(:font, name, *args)
99
+ end
100
+ end
101
+
102
+ def animation(name, *args, &block)
103
+ if block
104
+ @current << Animation.new(name)
105
+ block.call
106
+ @animations << @current.pop
107
+ else
108
+ method_missing(:animation, name, *args)
109
+ end
110
+ end
111
+
72
112
  # this is needed because the methods inside the rule blocks are actually
73
113
  # called on the CSS object
74
- def method_missing(name, *args, &block)
75
- @current.last.definition.__send__(name, *args, &block)
114
+ def method_missing(*args, &block)
115
+ @current.last.__send__(*args, &block)
76
116
  end
77
117
  end
78
118
 
@@ -78,11 +78,21 @@ class Formatter
78
78
 
79
79
  def indent(&block)
80
80
  if indent?
81
- @options[:indent][:level] += 1
82
- block.call
83
- @options[:indent][:level] -= 1
81
+ if block
82
+ @options[:indent][:level] += 1
83
+ block.call
84
+ @options[:indent][:level] -= 1
85
+ else
86
+ @options[:indent][:level] += 1
87
+ end
84
88
  else
85
- block.call
89
+ block.call if block
90
+ end
91
+ end
92
+
93
+ def deindent
94
+ if indent?
95
+ @options[:indent][:level] -= 1
86
96
  end
87
97
  end
88
98
 
@@ -140,6 +150,11 @@ Formatter.for HTML::Element do |f, item|
140
150
  f.print "<#{name} #{attrs.join(' ')}>"
141
151
  end
142
152
 
153
+ next if %w[
154
+ area base br col embed hr img input keygen link
155
+ menuitem meta param source track wbr
156
+ ].include?(name.to_s.downcase)
157
+
143
158
  f.indent {
144
159
  if inner = item.instance_eval { @inner_html }
145
160
  f.print inner
@@ -166,17 +181,55 @@ Formatter.for HTML::Element do |f, item|
166
181
  f.print "</#{name}>"
167
182
  end
168
183
 
184
+ Formatter.for CSS::Definition::Style do |f, style|
185
+ f.print "#{style.name}: #{style.value}#{' !important' if style.important};"
186
+ end
187
+
169
188
  Formatter.for CSS do |f, item|
189
+ item.fonts.each {|font|
190
+ f.print '@font-face {'
191
+ f.indent {
192
+ font.each {|style|
193
+ f.format style
194
+ }
195
+ }
196
+ f.print '}'
197
+ }
198
+
199
+ item.animations.each {|animation|
200
+ ['', '-webkit-', '-moz-', '-o-'].each {|platform|
201
+ f.print "@#{platform}keyframes #{animation.name} {"
202
+ animation.steps.each {|step|
203
+ f.print "#{step.value} {"
204
+ step.each {|style|
205
+ f.format style
206
+ }
207
+ f.print '}'
208
+ }
209
+ f.print '}'
210
+ }
211
+ }
212
+
170
213
  item.rules.reverse.each {|rule|
171
- next if rule.definition.empty?
214
+ next if rule.empty?
215
+
216
+ if m = rule.media
217
+ f.print "@media #{m} {"
218
+ f.indent
219
+ end
172
220
 
173
221
  f.print "#{rule.selector} {"
174
222
  f.indent {
175
- rule.definition.each {|style|
176
- f.print "#{style.name}: #{style.value}#{' !important' if style.important};"
223
+ rule.each {|style|
224
+ f.format style
177
225
  }
178
226
  }
179
227
  f.print '}'
228
+
229
+ if rule.media
230
+ f.print '}'
231
+ f.deindent
232
+ end
180
233
  }
181
234
  end
182
235
 
@@ -27,7 +27,7 @@ class A < self
27
27
  media: :media,
28
28
  }.each {|name, attribute|
29
29
  defhelper name do |value|
30
- @attributes[attribute] = value.to_s
30
+ @attributes[name] = value.to_s
31
31
  end
32
32
  }
33
33
 
@@ -17,7 +17,7 @@ class Base < self
17
17
  target: :target,
18
18
  }.each {|name, attribute|
19
19
  defhelper name do |value|
20
- @attributes[attribute] = value.to_s
20
+ @attributes[name] = value.to_s
21
21
  end
22
22
  }
23
23
  end
@@ -22,7 +22,7 @@ class Button < self
22
22
  target: :formtarget,
23
23
  }.each {|name, attributes|
24
24
  defhelper name do |value|
25
- @attributes[attribute] = value.to_s
25
+ @attributes[name] = value.to_s
26
26
  end
27
27
  }
28
28
 
@@ -15,7 +15,7 @@ class Canvas < self
15
15
  height: :height
16
16
  }.each {|name, attribute|
17
17
  defhelper name do |value|
18
- @attributes[attribute] = value.to_s
18
+ @attributes[name] = value.to_s
19
19
  end
20
20
  }
21
21
  end
@@ -0,0 +1,24 @@
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; class Element < BasicObject
12
+
13
+ class Embed < self
14
+ { type: :type,
15
+ height: :height,
16
+ width: :width
17
+ }.each {|name, attribute|
18
+ defhelper name do |value|
19
+ @attributes[name] = value
20
+ end
21
+ }
22
+ end
23
+
24
+ end; end; end
@@ -23,7 +23,7 @@ class Img < self
23
23
  map: :usemap,
24
24
  }.each {|name, attribute|
25
25
  defhelper name do |value|
26
- @attributes[attribute] = value.to_s
26
+ @attributes[name] = value.to_s
27
27
  end
28
28
  }
29
29
 
@@ -18,9 +18,10 @@ class Input < self
18
18
  place_holder: :placeholder,
19
19
  read_only: :readonly,
20
20
  required: :required,
21
+ limit: :maxlength
21
22
  }.each {|name, attribute|
22
23
  defhelper name do |value|
23
- @attributes[attribute] = value
24
+ @attributes[name] = value
24
25
  end
25
26
  }
26
27
  end
@@ -0,0 +1,20 @@
1
+ class Paggio; class HTML < BasicObject; class Element < BasicObject
2
+
3
+ class Link < self
4
+ { cross_origin: :crossorigin,
5
+
6
+ href: :href,
7
+ href_lang: :hreflang,
8
+
9
+ media: :media,
10
+ rel: :rel,
11
+ sizes: :sizes,
12
+ type: :type,
13
+ }.each {|name, attribute|
14
+ defhelper name do |value|
15
+ @attributes[name] = value.to_s
16
+ end
17
+ }
18
+ end
19
+
20
+ end; end; end
@@ -0,0 +1,27 @@
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; class Element < BasicObject
12
+
13
+ class Object < self
14
+ { type: :type,
15
+ data: :data,
16
+ name: :name,
17
+
18
+ height: :height,
19
+ width: :width
20
+ }.each {|name, attribute|
21
+ defhelper name do |value|
22
+ @attributes[name] = value
23
+ end
24
+ }
25
+ end
26
+
27
+ end; end; end
@@ -0,0 +1,24 @@
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; class Element < BasicObject
12
+
13
+ class Optgroup < self
14
+ %w[label value].each {|name|
15
+ defhelper name do |value|
16
+ @attributes[name] = value
17
+ end
18
+ }
19
+
20
+ defhelper! :disabled
21
+ defhelper! :selected
22
+ end
23
+
24
+ end; end; end
@@ -0,0 +1,24 @@
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; class Element < BasicObject
12
+
13
+ class Option < self
14
+ %w[label value].each {|name|
15
+ defhelper name do |value|
16
+ @attributes[name] = value
17
+ end
18
+ }
19
+
20
+ defhelper! :disabled
21
+ defhelper! :selected
22
+ end
23
+
24
+ end; end; end
@@ -0,0 +1,25 @@
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; class Element < BasicObject
12
+
13
+ class Select < self
14
+ %w[form name size].each {|name|
15
+ defhelper name do |value|
16
+ @attributes[name] = value
17
+ end
18
+ }
19
+
20
+ defhelper! :auto_focus, :autofocus
21
+ defhelper! :disabled
22
+ defhelper! :required
23
+ end
24
+
25
+ end; end; end
@@ -13,8 +13,14 @@ require 'paggio/html/element/base'
13
13
  require 'paggio/html/element/blockquote'
14
14
  require 'paggio/html/element/button'
15
15
  require 'paggio/html/element/canvas'
16
+ require 'paggio/html/element/embed'
16
17
  require 'paggio/html/element/img'
17
18
  require 'paggio/html/element/input'
19
+ require 'paggio/html/element/link'
20
+ require 'paggio/html/element/object'
21
+ require 'paggio/html/element/option'
22
+ require 'paggio/html/element/optgroup'
23
+ require 'paggio/html/element/select'
18
24
  require 'paggio/html/element/td'
19
25
 
20
26
  class Paggio; class HTML < BasicObject
@@ -25,7 +31,7 @@ class Element < BasicObject
25
31
 
26
32
  const = name.capitalize
27
33
 
28
- if const_defined?(const)
34
+ if !const.to_s.include?('-') && const_defined?(const)
29
35
  const_get(const).new(owner, name, attributes)
30
36
  else
31
37
  super
@@ -51,15 +57,20 @@ class Element < BasicObject
51
57
  end
52
58
 
53
59
  def method_missing(name, content = nil, &block)
54
- if content
55
- self << ::Paggio::Utils.heredoc(content.to_s)
56
- end
57
-
58
60
  if name.to_s.end_with? ?!
59
61
  @attributes[:id] = name[0 .. -2]
60
62
  else
61
- @last = name
62
- @class_names.push(name)
63
+ @class_names << name
64
+ end
65
+
66
+ if ::Hash === content
67
+ if content.has_key?(:class) || content.has_key?(:classes)
68
+ @class_names.unshift(*(content.delete(:class).to_s.split | content.delete(:classes).to_a))
69
+ end
70
+
71
+ ::Paggio::Utils.deep_merge!(@attributes, content)
72
+ elsif content
73
+ self >> content
63
74
  end
64
75
 
65
76
  @owner.extend!(self, &block) if block
@@ -68,10 +79,9 @@ class Element < BasicObject
68
79
  end
69
80
 
70
81
  def [](*names)
71
- return unless @last
72
-
73
- @class_names.pop
74
- @class_names.push([@last, *names].join('-'))
82
+ if last = @class_names.pop
83
+ @class_names << [last, *names].join('-')
84
+ end
75
85
 
76
86
  self
77
87
  end
@@ -82,6 +92,23 @@ class Element < BasicObject
82
92
  self
83
93
  end
84
94
 
95
+ def >>(content)
96
+ self << ::Paggio::Utils.heredoc(content.to_s)
97
+ self
98
+ end
99
+
100
+ defhelper :style do |hash|
101
+ @attributes[:style] = hash.map {|name, value|
102
+ "#{name}: #{value}"
103
+ }.join(';')
104
+ end
105
+
106
+ defhelper :data do |hash|
107
+ hash.each {|name, value|
108
+ @attributes["data-#{name}"] = value.to_s
109
+ }
110
+ end
111
+
85
112
  def inspect
86
113
  if @children.empty?
87
114
  "#<HTML::Element(#{@name.upcase})>"
data/lib/paggio/html.rb CHANGED
@@ -8,7 +8,6 @@
8
8
  # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
9
  #++
10
10
 
11
-
12
11
  require 'paggio/html/helpers'
13
12
  require 'paggio/html/element'
14
13
 
@@ -17,24 +16,31 @@ class Paggio
17
16
  class HTML < BasicObject
18
17
  attr_reader :version
19
18
 
20
- def initialize(version = 5, &block)
19
+ def initialize(version = 5, defer: false, &block)
21
20
  ::Kernel.raise ::ArgumentError, 'no block given' unless block
22
21
 
23
22
  @version = version
24
23
  @roots = []
25
24
  @current = nil
26
25
 
27
- if block.arity == 0
28
- instance_exec(&block)
29
- else
30
- block.call(self)
31
- end
26
+ @block = block
27
+
28
+ build! unless defer
32
29
  end
33
30
 
34
31
  def <<(what)
35
32
  (@current || @roots) << what
36
33
  end
37
34
 
35
+ def build!(force_call: false)
36
+ if !force_call && @block.arity == 0
37
+ instance_exec(&@block)
38
+ else
39
+ @block.call(self)
40
+ end
41
+ @block = nil
42
+ end
43
+
38
44
  def root!
39
45
  @roots.first
40
46
  end
@@ -65,6 +71,11 @@ class HTML < BasicObject
65
71
  @roots.each(&block)
66
72
  end
67
73
 
74
+ def text(*fragments, &block)
75
+ fragments << yield if block
76
+ fragments.each { |fragment| self << fragment }
77
+ end
78
+
68
79
  def method_missing(name, *args, &block)
69
80
  if name.to_s.end_with? ?!
70
81
  return super
@@ -93,6 +104,9 @@ class HTML < BasicObject
93
104
  element
94
105
  end
95
106
 
107
+ # Support for custom elements
108
+ alias e method_missing
109
+
96
110
  def inspect
97
111
  if @roots.empty?
98
112
  "#<HTML(#@version)>"
data/paggio.gemspec CHANGED
@@ -1,11 +1,12 @@
1
1
  Gem::Specification.new {|s|
2
- s.name = 'paggio'
3
- s.version = '0.2.3'
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.'
2
+ s.name = 'paggio'
3
+ s.version = '0.3.0'
4
+ s.author = 'meh.'
5
+ s.email = 'meh@schizofreni.co'
6
+ s.homepage = 'http://github.com/opal/paggio'
7
+ s.platform = Gem::Platform::RUBY
8
+ s.summary = 'Ruby, HTML and CSS at war.'
9
+ s.license = 'WTFPL'
9
10
 
10
11
  s.files = `git ls-files`.split("\n")
11
12
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
data/spec/css_spec.rb CHANGED
@@ -8,7 +8,7 @@ describe Paggio::CSS do
8
8
  end
9
9
  end
10
10
 
11
- css.to_s.should == "#lol {\n\tcolor: black;\n}\n"
11
+ expect(css.to_s).to eq("#lol {\n\tcolor: black;\n}\n")
12
12
  end
13
13
 
14
14
  it 'builds border-radius correctly' do
@@ -18,7 +18,7 @@ describe Paggio::CSS do
18
18
  end
19
19
  end
20
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"
21
+ expect(css.to_s).to eq("#lol {\n\t-moz-border-radius: 5px;\n\t-webkit-border-radius: 5px;\n\tborder-radius: 5px;\n}\n")
22
22
 
23
23
  css = Paggio.css do
24
24
  rule '#lol' do
@@ -26,7 +26,7 @@ describe Paggio::CSS do
26
26
  end
27
27
  end
28
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"
29
+ expect(css.to_s).to eq("#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
30
  end
31
31
 
32
32
  it 'builds box-shadow correctly' do
@@ -36,6 +36,20 @@ describe Paggio::CSS do
36
36
  end
37
37
  end
38
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"
39
+ expect(css.to_s).to eq("#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
+
42
+ it 'builds @font-face' do
43
+ css = Paggio.css do
44
+ font 'hue' do
45
+ src url('test')
46
+ end
47
+ end
48
+
49
+ expect(css.to_s).to eq("@font-face {\n\tfont-family: hue;\n\tsrc: url(\"test\");\n}\n")
50
+ end
51
+
52
+ it 'builds @keyframes' do
53
+
40
54
  end
41
55
  end
data/spec/html_spec.rb ADDED
@@ -0,0 +1,185 @@
1
+ require 'paggio'
2
+
3
+ describe Paggio::HTML do
4
+ it 'builds an element' do
5
+ html = Paggio.html! do
6
+ div
7
+ end
8
+
9
+ expect(html.to_s).to eq("<div>\n</div>\n")
10
+ end
11
+
12
+ it 'builds an element with text content' do
13
+ html = Paggio.html! do
14
+ div "foo bar"
15
+ end
16
+
17
+ expect(html.to_s).to eq("<div>\n\tfoo bar\n</div>\n")
18
+
19
+ html = Paggio.html! do
20
+ div do
21
+ "foo bar"
22
+ end
23
+ end
24
+
25
+ expect(html.to_s).to eq("<div>\n\tfoo bar\n</div>\n")
26
+ end
27
+
28
+ it 'builds an element with attributes' do
29
+ html = Paggio.html! do
30
+ div class: :wut
31
+ end
32
+
33
+ expect(html.to_s).to eq("<div class=\"wut\">\n</div>\n")
34
+ end
35
+
36
+ it 'builds deeper trees' do
37
+ html = Paggio.html! do
38
+ div do
39
+ span do
40
+ "wut"
41
+ end
42
+ end
43
+ end
44
+
45
+ expect(html.to_s).to eq("<div>\n\t<span>\n\t\twut\n\t</span>\n</div>\n")
46
+ end
47
+
48
+ it 'sets classes with methods' do
49
+ html = Paggio.html! do
50
+ div.nice.element
51
+ end
52
+
53
+ expect(html.to_s).to eq("<div class=\"nice element\">\n</div>\n")
54
+ end
55
+
56
+ it 'nests when setting classes' do
57
+ html = Paggio.html! do
58
+ div.nice.element do
59
+ span.nicer 'lol'
60
+ end
61
+ end
62
+
63
+ expect(html.to_s).to eq("<div class=\"nice element\">\n\t<span class=\"nicer\">\n\t\tlol\n\t</span>\n</div>\n")
64
+ end
65
+
66
+ it 'joins class name properly' do
67
+ html = Paggio.html! do
68
+ i.icon[:legal]
69
+ end
70
+
71
+ expect(html.to_s).to eq("<i class=\"icon-legal\">\n</i>\n")
72
+
73
+ html = Paggio.html! do
74
+ i.icon.icon[:legal, :shmegal]
75
+ end
76
+
77
+ expect(html.to_s).to eq("<i class=\"icon icon-legal-shmegal\">\n</i>\n")
78
+ end
79
+
80
+ it 'sets the id' do
81
+ html = Paggio.html! do
82
+ div.omg!
83
+ end
84
+
85
+ expect(html.to_s).to eq("<div id=\"omg\">\n</div>\n")
86
+ end
87
+
88
+ it 'chains ids and classes' do
89
+ html = Paggio.html! do
90
+ div.omg!.wut
91
+ end
92
+
93
+ expect(html.to_s).to eq("<div id=\"omg\" class=\"wut\">\n</div>\n")
94
+ end
95
+
96
+ it 'chains ids and attributes' do
97
+ html = Paggio.html! do
98
+ div.omg! class: :wut
99
+ end
100
+
101
+ expect(html.to_s).to eq("<div id=\"omg\" class=\"wut\">\n</div>\n")
102
+ end
103
+
104
+ it 'chains classes and attributes' do
105
+ html = Paggio.html! do
106
+ div.wut width: 80
107
+ end
108
+
109
+ expect(html.to_s).to eq("<div width=\"80\" class=\"wut\">\n</div>\n")
110
+ end
111
+
112
+ it 'overrides with id from attributes' do
113
+ html = Paggio.html! do
114
+ div.omg! id: 'ikr'
115
+ end
116
+
117
+ expect(html.to_s).to eq("<div id=\"ikr\">\n</div>\n")
118
+ expect(html.to_s).not_to eq("<div id=\"omg\">\n</div>\n")
119
+ end
120
+
121
+ it 'coalesces classes from attributes' do
122
+ html = Paggio.html! do
123
+ div.wut class: 'rofl'
124
+ end
125
+
126
+ expect(html.to_s).to eq("<div class=\"rofl wut\">\n</div>\n")
127
+ expect(html.to_s).not_to eq("<div class=\"wut\" class=\"rofl\">n")
128
+
129
+ html = Paggio.html! do
130
+ div.wut class: 'rofl idk'
131
+ end
132
+
133
+ expect(html.to_s).to eq("<div class=\"rofl idk wut\">\n</div>\n")
134
+ expect(html.to_s).not_to eq("<div class=\"wut\" class=\"rofl idk\">n")
135
+ end
136
+
137
+ it 'coalesces multiple classes from attributes' do
138
+ html = Paggio.html! do
139
+ div.wut classes: %w[rofl idk]
140
+ end
141
+
142
+ expect(html.to_s).to eq("<div class=\"rofl idk wut\">\n</div>\n")
143
+ expect(html.to_s).not_to eq("<div class=\"wut\" classes=\"[&quot;rofl&quot;, &quot;idk&quot;]\">\n</div>\n")
144
+ end
145
+
146
+ it 'joins class name properly with class attributes' do
147
+ html = Paggio.html! do
148
+ i.icon(class: 'illegal')[:legal]
149
+ end
150
+
151
+ expect(html.to_s).to eq("<i class=\"illegal icon-legal\">\n</i>\n")
152
+ end
153
+
154
+ it 'allows for defered build' do
155
+ html = Paggio::HTML.new(defer: true) do
156
+ div
157
+ end
158
+ expect(html.roots!.length).to eq(0)
159
+
160
+ html.build!
161
+ expect(html.roots!.length).to eq(1)
162
+ end
163
+
164
+ it 'allows for custom elements' do
165
+ html = Paggio.html! do
166
+ e('custom-element').test.test!
167
+ end
168
+
169
+ expect(html.to_s).to eq("<custom-element id=\"test\" class=\"test\">\n</custom-element>\n")
170
+ end
171
+
172
+ it 'supports text nodes' do
173
+ html = Paggio.html! do
174
+ div {
175
+ text "test"
176
+ text { "test" }
177
+ text "test"
178
+ div
179
+ text "test"
180
+ }
181
+ end
182
+
183
+ expect(html.to_s).to eq("<div>\n\ttest\n\ttest\n\ttest\n\t<div>\n\t</div>\n\ttest\n</div>\n")
184
+ end
185
+ end
metadata CHANGED
@@ -1,28 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paggio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - meh.
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-02 00:00:00.000000000 Z
11
+ date: 2021-11-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description:
13
+ description:
14
14
  email: meh@schizofreni.co
15
15
  executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
+ - ".github/workflows/build.yml"
20
+ - ".rspec"
19
21
  - ".travis.yml"
22
+ - Gemfile
23
+ - Gemfile.lock
20
24
  - README.md
21
25
  - Rakefile
22
26
  - lib/paggio.rb
23
27
  - lib/paggio/css.rb
28
+ - lib/paggio/css/animation.rb
24
29
  - lib/paggio/css/color.rb
25
30
  - lib/paggio/css/definition.rb
31
+ - lib/paggio/css/font.rb
32
+ - lib/paggio/css/rule.rb
26
33
  - lib/paggio/css/unit.rb
27
34
  - lib/paggio/formatter.rb
28
35
  - lib/paggio/html.rb
@@ -32,8 +39,14 @@ files:
32
39
  - lib/paggio/html/element/blockquote.rb
33
40
  - lib/paggio/html/element/button.rb
34
41
  - lib/paggio/html/element/canvas.rb
42
+ - lib/paggio/html/element/embed.rb
35
43
  - lib/paggio/html/element/img.rb
36
44
  - lib/paggio/html/element/input.rb
45
+ - lib/paggio/html/element/link.rb
46
+ - lib/paggio/html/element/object.rb
47
+ - lib/paggio/html/element/optgroup.rb
48
+ - lib/paggio/html/element/option.rb
49
+ - lib/paggio/html/element/select.rb
37
50
  - lib/paggio/html/element/td.rb
38
51
  - lib/paggio/html/helpers.rb
39
52
  - lib/paggio/markdown.rb
@@ -42,10 +55,12 @@ files:
42
55
  - lib/paggio/utils.rb
43
56
  - paggio.gemspec
44
57
  - spec/css_spec.rb
45
- homepage: http://github.com/meh/paggio
46
- licenses: []
58
+ - spec/html_spec.rb
59
+ homepage: http://github.com/opal/paggio
60
+ licenses:
61
+ - WTFPL
47
62
  metadata: {}
48
- post_install_message:
63
+ post_install_message:
49
64
  rdoc_options: []
50
65
  require_paths:
51
66
  - lib
@@ -60,10 +75,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
75
  - !ruby/object:Gem::Version
61
76
  version: '0'
62
77
  requirements: []
63
- rubyforge_project:
64
- rubygems_version: 2.1.5
65
- signing_key:
78
+ rubygems_version: 3.2.22
79
+ signing_key:
66
80
  specification_version: 4
67
81
  summary: Ruby, HTML and CSS at war.
68
82
  test_files:
69
83
  - spec/css_spec.rb
84
+ - spec/html_spec.rb