mab 0.0.1 → 0.0.2

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.
Files changed (88) hide show
  1. data/COPYING +19 -0
  2. data/Gemfile +10 -0
  3. data/Gemfile.lock +12 -0
  4. data/README.md +224 -0
  5. data/Rakefile +8 -0
  6. data/bench.rb +37 -0
  7. data/lib/mab/mixin.rb +58 -42
  8. data/lib/mab/version.rb +1 -1
  9. data/mab-0.0.1.gem +0 -0
  10. data/mab.gemspec +20 -0
  11. data/markaby/CHANGELOG.rdoc +106 -0
  12. data/markaby/Markaby.gemspec +109 -0
  13. data/markaby/README.rdoc +317 -0
  14. data/markaby/Rakefile +78 -0
  15. data/markaby/VERSION +1 -0
  16. data/markaby/garlic.rb +29 -0
  17. data/markaby/init.rb +6 -0
  18. data/markaby/lib/markaby.rb +30 -0
  19. data/markaby/lib/markaby/builder.rb +314 -0
  20. data/markaby/lib/markaby/builder_tags.rb +64 -0
  21. data/markaby/lib/markaby/cssproxy.rb +55 -0
  22. data/markaby/lib/markaby/kernel_method.rb +7 -0
  23. data/markaby/lib/markaby/rails.rb +75 -0
  24. data/markaby/lib/markaby/rails/current.rb +85 -0
  25. data/markaby/lib/markaby/rails/deprecated.rb +124 -0
  26. data/markaby/lib/markaby/rails/rails_builder.rb +91 -0
  27. data/markaby/lib/markaby/sinatra.rb +18 -0
  28. data/markaby/lib/markaby/tags.rb +200 -0
  29. data/markaby/lib/markaby/tilt.rb +8 -0
  30. data/markaby/spec/markaby/builder_spec.rb +118 -0
  31. data/markaby/spec/markaby/css_proxy_test.rb +47 -0
  32. data/markaby/spec/markaby/fragment_test.rb +10 -0
  33. data/markaby/spec/markaby/markaby_other_static.mab +1 -0
  34. data/markaby/spec/markaby/markaby_spec.rb +184 -0
  35. data/markaby/spec/markaby/markaby_test.rb +251 -0
  36. data/markaby/spec/markaby/rails/spec_helper.rb +29 -0
  37. data/markaby/spec/markaby/rails/views/layouts/layout.mab +11 -0
  38. data/markaby/spec/markaby/rails/views/markaby/_a_partial.mab +3 -0
  39. data/markaby/spec/markaby/rails/views/markaby/_form_for_with_body_in_erb.erb +1 -0
  40. data/markaby/spec/markaby/rails/views/markaby/_partial_child_with_locals.mab +1 -0
  41. data/markaby/spec/markaby/rails/views/markaby/access_to_helpers.mab +1 -0
  42. data/markaby/spec/markaby/rails/views/markaby/broken.mab +7 -0
  43. data/markaby/spec/markaby/rails/views/markaby/commented_out_template.mab +1 -0
  44. data/markaby/spec/markaby/rails/views/markaby/correct_template_values.mab +5 -0
  45. data/markaby/spec/markaby/rails/views/markaby/double_output.mab +8 -0
  46. data/markaby/spec/markaby/rails/views/markaby/form_for.mab +2 -0
  47. data/markaby/spec/markaby/rails/views/markaby/form_for_with_fields.mab +3 -0
  48. data/markaby/spec/markaby/rails/views/markaby/form_for_with_multiple_fields.mab +4 -0
  49. data/markaby/spec/markaby/rails/views/markaby/no_values_passed.mab +3 -0
  50. data/markaby/spec/markaby/rails/views/markaby/partial_parent.mab +1 -0
  51. data/markaby/spec/markaby/rails/views/markaby/partial_parent_with_locals.mab +7 -0
  52. data/markaby/spec/markaby/rails/views/markaby/render_erb_without_explicit_render_call.erb +1 -0
  53. data/markaby/spec/markaby/rails/views/markaby/render_explicit_but_empty_markaby_layout.mab +0 -0
  54. data/markaby/spec/markaby/rails/views/markaby/render_mab_without_explicit_render_call.mab +3 -0
  55. data/markaby/spec/markaby/rails/views/markaby/render_with_ivar.mab +3 -0
  56. data/markaby/spec/markaby/rails/views/markaby/renders_erb.rhtml +1 -0
  57. data/markaby/spec/markaby/rails/views/markaby/renders_form_for_with_erb_body.mab +3 -0
  58. data/markaby/spec/markaby/rails/views/markaby/routes.mab +1 -0
  59. data/markaby/spec/markaby/rails/views/markaby/yielding.mab +3 -0
  60. data/markaby/spec/markaby/rails/views/markaby/yielding_content_for_with_block_helper.mab +5 -0
  61. data/markaby/spec/markaby/rails/views/markaby/yielding_two.mab +7 -0
  62. data/markaby/spec/markaby/rails/views/markaby/yielding_with_content_for.mab +3 -0
  63. data/markaby/spec/markaby/rails_test.rb +380 -0
  64. data/markaby/spec/markaby/rails_version_spec.rb +37 -0
  65. data/markaby/spec/markaby/sinatra/app.rb +49 -0
  66. data/markaby/spec/markaby/sinatra/sinatra_spec.rb +67 -0
  67. data/markaby/spec/markaby/sinatra/views/helpers.mab +1 -0
  68. data/markaby/spec/markaby/sinatra/views/layout.mab +7 -0
  69. data/markaby/spec/markaby/sinatra/views/markaby_template.mab +0 -0
  70. data/markaby/spec/markaby/sinatra/views/scope_instance_variables.mab +3 -0
  71. data/markaby/spec/markaby/sinatra/views/simple_html.mab +4 -0
  72. data/markaby/spec/markaby/sinatra/views/variables.mab +3 -0
  73. data/markaby/spec/markaby/tilt/erb.erb +1 -0
  74. data/markaby/spec/markaby/tilt/locals.mab +1 -0
  75. data/markaby/spec/markaby/tilt/markaby.mab +1 -0
  76. data/markaby/spec/markaby/tilt/markaby_other_static.mab +1 -0
  77. data/markaby/spec/markaby/tilt/render_twice.mab +1 -0
  78. data/markaby/spec/markaby/tilt/scope.mab +1 -0
  79. data/markaby/spec/markaby/tilt/yielding.mab +2 -0
  80. data/markaby/spec/markaby/tilt_spec.rb +75 -0
  81. data/markaby/spec/spec.opts +2 -0
  82. data/markaby/spec/spec_helper.rb +44 -0
  83. data/test/helper.rb +4 -0
  84. data/test/test_mab_builder.rb +56 -0
  85. data/test/test_mab_indentation.rb +66 -0
  86. data/test/test_mab_kernel_method.rb +10 -0
  87. data/test/test_mab_mixin.rb +246 -0
  88. metadata +92 -6
data/COPYING ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Magnus Holm <judofyr@gmail.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem "rake"
5
+ end
6
+
7
+ group :test do
8
+ gem "minitest"
9
+ end
10
+
@@ -0,0 +1,12 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ minitest (2.10.0)
5
+ rake (0.9.2.2)
6
+
7
+ PLATFORMS
8
+ ruby
9
+
10
+ DEPENDENCIES
11
+ minitest
12
+ rake
@@ -0,0 +1,224 @@
1
+ Mab (Markup as Ruby)
2
+ ====================
3
+
4
+ Mab let's you write HTML in plain Ruby:
5
+
6
+ ```ruby
7
+ doctype!
8
+ html do
9
+ head do
10
+ link :rel => 'stylesheet', :href => 'style.css'
11
+ script :src => 'jquery.js'
12
+ end
13
+ body :id => :frontpage do
14
+ h1 'Hello World', :class => :main
15
+ end
16
+ end
17
+ ```
18
+
19
+
20
+ Syntax
21
+ ------
22
+
23
+ ### 1. Tags and Attributes
24
+
25
+ There are four basic forms:
26
+
27
+ ```ruby
28
+ tagname(content)
29
+
30
+ tagname(content, attributes)
31
+
32
+ tagname do
33
+ content
34
+ end
35
+
36
+ tagname(attributes) do
37
+ content
38
+ end
39
+ ```
40
+
41
+ Example:
42
+
43
+ ```ruby
44
+ doctype!
45
+ html do
46
+ head do
47
+ link :rel => 'stylesheet', :href => 'style.css'
48
+ script :src => 'jquery.js'
49
+ end
50
+ body :id => :frontpage do
51
+ h1 'Hello World', :class => :main
52
+ end
53
+ end
54
+ ```
55
+
56
+ Which results in:
57
+
58
+ ```html
59
+ <!DOCTYPE html>
60
+ <html>
61
+ <head>
62
+ <link rel="stylesheet" href="style.css">
63
+ <script src="jquery"></script>
64
+ </head>
65
+ <body id="frontpage">
66
+ <h1 class="main">Hello World</h1>
67
+ </body>
68
+ </html>
69
+ ```
70
+
71
+ Notice how Mab knows that script tag must have content, so although you didn't
72
+ specify anything it closed the tag for you.
73
+
74
+ ### 2. Element Classes and IDs
75
+
76
+ You can easily add classes and IDs by hooking methods onto the container:
77
+
78
+ ```ruby
79
+ body.frontpage! do
80
+ h1.main 'Hello World'
81
+ end
82
+ ```
83
+
84
+ Which results in:
85
+
86
+ ```html
87
+ <body id="frontpage">
88
+ <h1 class="main">Hello World</h1>
89
+ </body>
90
+ ```
91
+
92
+ You can mix and match as you'd like (`div.klass.klass1.id!`), but you can only
93
+ provide content and attributes on the *last* call:
94
+
95
+ ```ruby
96
+ # This is not valid:
97
+ form(:action => :post).world do
98
+ input
99
+ end
100
+
101
+ # But this is:
102
+ form.world(:action => :post) do
103
+ input
104
+ end
105
+ ```
106
+
107
+ ### 3. Escape or Not Escape
108
+
109
+ Mab uses a very simple convention for escaping: Strings as *arguments* gets
110
+ escaped, strings in *blocks* don't:
111
+
112
+ ```ruby
113
+ div.comment "<script>alert(1)</script>"
114
+ # <div class="comment">&lt;script&gt;alert(1)&lt;/script&gt;</div>
115
+
116
+ div.comment { "I <strong>love</strong> you" }
117
+ # <div class="comment">I <strong>love</strong> you</div>
118
+ ```
119
+
120
+ Be aware that Mab ignores the string in a block if there's other tags there:
121
+
122
+ ```ruby
123
+ div.comment do
124
+ div.author "BitPuffin"
125
+ "<p>Silence!</p>"
126
+ end
127
+ ```
128
+
129
+ The p tag above won't appear in the output.
130
+
131
+ ### 4. Text
132
+
133
+ Sometimes you need to insert plain text:
134
+
135
+ ```ruby
136
+ p.author do
137
+ text 'Written by '
138
+ a 'Bluebie', :href => 'http://creativepony.com/'
139
+ end
140
+ ```
141
+
142
+ Which results in:
143
+
144
+ ```html
145
+ <p class="author">
146
+ Written by
147
+ <a href="http://creativepony.com/">Bluebie</a>
148
+ </p>
149
+ ```
150
+
151
+ There's also `text!` which doesn't escape:
152
+
153
+ ```ruby
154
+ p.author do
155
+ text! '<strong>Written</strong> by'
156
+ a 'Bluebie', :href => 'http://creativepony.com/'
157
+ ```
158
+
159
+
160
+ Invoking Mab
161
+ ------------
162
+
163
+ Using #mab:
164
+
165
+ ```ruby
166
+ require 'mab/kernel_method'
167
+
168
+ str = mab do
169
+ doctype!
170
+ html do
171
+ # ...
172
+ end
173
+ end
174
+ ```
175
+
176
+ Using Mab::Builder (or Mab::PrettyBuilder if you want indentation):
177
+
178
+ ```ruby
179
+ class Person
180
+ attr_reader :name
181
+
182
+ def initialize(name)
183
+ @name = name
184
+ end
185
+
186
+ def awesome?; true end
187
+ end
188
+
189
+ # Assign instance variables:
190
+ Mab::Builder.new(:person => Person.new('BitBuffin')) do
191
+ if @person.awesome?
192
+ h1 @person.name
193
+ else
194
+ p @person.name
195
+ end
196
+ end.to_s
197
+
198
+ # Use helper (methods and instance variables will be available):
199
+ Mab::Builder.new({}, Person.new('BitPuffin')) do
200
+ if awesome?
201
+ h1 @name
202
+ else
203
+ p @name
204
+ end
205
+ end.to_s
206
+ ```
207
+
208
+ Extending an object (*advanced usage*):
209
+
210
+ ```ruby
211
+ r = Object.new
212
+ r.extend Mab::Mixin::HTML5
213
+ r.extend Mab::Indentation
214
+
215
+ r.mab do
216
+ doctype!
217
+ html do
218
+ # ...
219
+ end
220
+ end
221
+
222
+ ```
223
+
224
+
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ end
8
+
@@ -0,0 +1,37 @@
1
+ $: << 'lib'
2
+
3
+ TMPL = <<-'EOF'
4
+ div.menu! do
5
+ [1, 2, 3, 4].map { |n| a("Section #{n}", :href => "/section/#{n}") }.join ' | '
6
+ end
7
+ EOF
8
+
9
+ require 'mab'
10
+
11
+ class MabExample
12
+ include Mab::Mixin::XHTML5
13
+
14
+ class_eval "def content() #{TMPL} end"
15
+
16
+ def to_html
17
+ mab { content }
18
+ end
19
+ end
20
+
21
+ require 'erector'
22
+
23
+ class ErectorExample < Erector::Widget
24
+ class_eval "def content() #{TMPL} end"
25
+ end
26
+
27
+ puts MabExample.new.to_html
28
+ puts ErectorExample.new.to_html
29
+
30
+ require 'benchmark/ips'
31
+
32
+ Benchmark.ips do |x|
33
+ x.report('Mab') { MabExample.new.to_html }
34
+ x.report('Erector') { ErectorExample.new.to_html }
35
+ end
36
+
37
+
@@ -4,9 +4,8 @@ module Mab
4
4
  module Mixin
5
5
  class Error < StandardError; end
6
6
  class Tag
7
- attr_accessor :name, :content, :block
7
+ attr_accessor :name, :content, :attributes, :block, :has_content
8
8
  attr_reader :options, :context, :instance
9
- attr_writer :attributes, :pos
10
9
 
11
10
  def initialize(name, options, context, instance = nil)
12
11
  @name = name
@@ -14,57 +13,71 @@ module Mab
14
13
  @context = context
15
14
  @instance = instance
16
15
  @done = false
17
- @content = false
16
+
17
+ @content = nil
18
+ @has_content = nil
19
+
20
+ @attributes = {}
21
+
18
22
  @pos = @context.size
19
23
  end
20
24
 
21
- def attributes
22
- @attributes ||= {}
25
+ def block
26
+ return @block unless block_given?
27
+ current = @block
28
+ @block = proc { yield current }
23
29
  end
24
30
 
25
- def merge_attributes(attrs)
26
- if defined?(@attributes)
31
+ def merge_attributes(*args)
32
+ args.each do |attrs|
27
33
  @attributes.merge!(attrs)
28
- else
29
- @attributes = attrs
30
34
  end
31
35
  end
32
36
 
33
- def method_missing(name, content = nil, attrs = nil, &blk)
37
+ def method_missing(name, *args, &blk)
34
38
  name = name.to_s
35
39
 
36
40
  if name[-1] == ?!
37
- attributes[:id] = name[0..-2]
41
+ @attributes[:id] = name[0..-2]
38
42
  else
39
- if attributes.has_key?(:class)
40
- attributes[:class] += " #{name}"
43
+ if @attributes.has_key?(:class)
44
+ @attributes[:class] += " #{name}"
41
45
  else
42
- attributes[:class] = name
46
+ @attributes[:class] = name
43
47
  end
44
48
  end
45
49
 
46
- insert(content, attrs, &blk)
50
+ insert(*args, &blk)
47
51
  end
48
52
 
49
- def insert(content = nil, attrs = nil, &blk)
53
+ def insert(*args, &blk)
50
54
  raise Error, "This tag is already closed" if @done
51
55
 
52
- if content.is_a?(Hash)
53
- attrs = content
54
- content = nil
56
+ if !args.empty? && !args[0].is_a?(Hash)
57
+ content = args.shift
58
+ raise Error, "Tag doesn't allow content" if @has_content == false
59
+ @has_content = true
55
60
  end
56
61
 
57
- merge_attributes(attrs) if attrs
62
+ if content
63
+ @content = CGI.escapeHTML(content.to_s)
64
+ @done = true
65
+ end
66
+
67
+ if !args.empty?
68
+ merge_attributes(*args)
69
+ @done = true
70
+ end
58
71
 
59
72
  if block_given?
73
+ raise Error, "Tag doesn't allow content" if @has_content == false
74
+ @has_content = true
60
75
  @block = blk
61
76
  @done = true
62
- elsif content
63
- content = content.to_s
64
- @content = CGI.escapeHTML(content)
65
- @done = !content.empty?
66
- elsif attrs
67
- @done = true
77
+ end
78
+
79
+ if @content && @block
80
+ raise Error, "Both content and block is not allowed"
68
81
  end
69
82
 
70
83
  @instance.mab_done(self) if @done
@@ -76,7 +89,8 @@ module Mab
76
89
  if before >= @context.children
77
90
  @content = res.to_s
78
91
  else
79
- @content = nil
92
+ # Turn the node into just an opening tag.
93
+ @has_content = false
80
94
  @instance.mab_insert("</#{@name}>")
81
95
  end
82
96
  end
@@ -102,9 +116,10 @@ module Mab
102
116
  @context[@pos] = nil
103
117
  @context.children -= 1
104
118
  end
119
+
105
120
  res = "<#{@name}#{attrs_to_s}"
106
- res << (@options[:xml] && @content == false ? ' />' : '>')
107
- res << "#{@content}</#{@name}>" if @content
121
+ res << (@options[:xml] && !@block && !@has_content ? ' />' : '>')
122
+ res << "#{@content}</#{@name}>" if @has_content
108
123
  res
109
124
  end
110
125
  end
@@ -133,11 +148,15 @@ module Mab
133
148
  end
134
149
  end
135
150
 
136
- def tag!(name, content = nil, attrs = nil, &blk)
151
+ def mab_tag(name)
137
152
  ctx = @mab_context || raise(Error, "Tags can only be written within a `mab { }`-block")
138
153
  tag = Tag.new(name, mab_options, ctx, self)
139
154
  mab_insert(tag)
140
- tag.insert(content, attrs, &blk)
155
+ tag
156
+ end
157
+
158
+ def tag!(name, *args, &blk)
159
+ mab_tag(name).insert(*args, &blk)
141
160
  end
142
161
 
143
162
  def text!(str)
@@ -180,12 +199,10 @@ module Mab
180
199
  module HTMLDefiners
181
200
  def define_tag(meth, tag)
182
201
  class_eval <<-EOF
183
- def #{meth}(content = "", attrs = nil, &blk)
184
- if content.is_a?(Hash)
185
- attrs = content
186
- content = ""
187
- end
188
- tag!(:#{tag}, content, attrs, &blk)
202
+ def #{meth}(*args, &blk)
203
+ tag = mab_tag(:#{tag})
204
+ tag.has_content = true
205
+ tag.insert(*args, &blk)
189
206
  end
190
207
  EOF
191
208
  end
@@ -198,11 +215,10 @@ module Mab
198
215
 
199
216
  def define_empty_tag(meth, tag)
200
217
  class_eval <<-EOF
201
- def #{meth}(attrs = nil)
202
- if (!attrs.nil? && !attrs.is_a?(Hash)) || block_given?
203
- raise Error, "#{meth} doesn't allow content"
204
- end
205
- tag!(:#{tag}, attrs)
218
+ def #{meth}(*args, &blk)
219
+ tag = mab_tag(:#{tag})
220
+ tag.has_content = false
221
+ tag.insert(*args, &blk)
206
222
  end
207
223
  EOF
208
224
  end