mab 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,78 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+ require 'rake/testtask'
4
+ require 'rake/clean'
5
+
6
+ begin
7
+ require 'hanna/rdoctask'
8
+
9
+ Rake::RDocTask.new do |rdoc|
10
+ rdoc.rdoc_dir = 'doc/rdoc'
11
+ rdoc.options << '--line-numbers'
12
+ rdoc.rdoc_files.add(['README.rdoc', 'CHANGELOG.rdoc', 'lib/**/*.rb'])
13
+ end
14
+ rescue LoadError
15
+ puts "Could not load hanna-rdoc. Please install with mislav-hanna package"
16
+ end
17
+
18
+ task :default => [:spec, :test]
19
+
20
+ Rake::TestTask.new(:test) do |t|
21
+ t.pattern = 'spec/markaby/*_test.rb'
22
+ end
23
+
24
+ RSpec::Core::RakeTask.new(:spec)
25
+
26
+ begin
27
+ require 'jeweler'
28
+
29
+ Jeweler::Tasks.new do |gemspec|
30
+ gemspec.name = "markaby"
31
+ gemspec.summary = "Markup as Ruby, write HTML in your native Ruby tongue"
32
+ gemspec.description = "Tim Fletcher and _why's ruby driven HTML templating system"
33
+ gemspec.email = "scott@railsnewbie.com"
34
+ gemspec.homepage = "http://markaby.github.com/markaby/"
35
+ gemspec.authors = ["_why", "Tim Fletcher", "John Barton", "spox", "smtlaissezfaire"]
36
+ gemspec.add_dependency 'builder', '>=2.0.0'
37
+ end
38
+ rescue LoadError
39
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
40
+ end
41
+
42
+ desc "List any Markaby specific warnings"
43
+ task :warnings do
44
+ `ruby -w test/test_markaby.rb 2>&1`.split(/\n/).each do |line|
45
+ next unless line =~ /warning:/
46
+ next if line =~ /builder-/
47
+ puts line
48
+ end
49
+ end
50
+
51
+ desc "Start a Markaby-aware IRB session"
52
+ task :irb do
53
+ sh 'irb -I lib -r markaby -r markaby/kernel_method'
54
+ end
55
+
56
+ namespace :gemspec do
57
+ task :commit do
58
+ sh "git add ."
59
+ sh "git commit -m 'Update gemspec'"
60
+ end
61
+ end
62
+
63
+ namespace :release do
64
+ task :patch => [:spec, "version:bump:patch", :update_gemspec, :rerdoc, :tag_release, :build, :push_tags]
65
+
66
+ task :update_gemspec => ["gemspec:generate", "gemspec:validate", "gemspec:commit"]
67
+ task :tag_release do
68
+ require File.dirname(__FILE__) + "/lib/markaby"
69
+ version = "v#{Markaby::VERSION}"
70
+ sh "git tag #{version}"
71
+ end
72
+
73
+ task :push_tags do
74
+ sh "git push --tags"
75
+ end
76
+ end
77
+
78
+ task :release => "release:patch"
@@ -0,0 +1 @@
1
+ 0.7.2
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/lib/markaby/rails")
2
+
3
+ garlic do
4
+ # this plugin
5
+ repo "markaby", :path => '.'
6
+
7
+ # other repos
8
+ repo "rails", :url => "git://github.com/rails/rails"
9
+
10
+ # target railses
11
+ RAILS_TAREGETS = Markaby::Rails::SUPPORTED_RAILS_VERSIONS.map do |version|
12
+ "v#{version}"
13
+ end
14
+
15
+ RAILS_TAREGETS.each do |rails|
16
+ # declare how to prepare, and run each CI target
17
+ target "Rails: #{rails}", :tree_ish => rails do
18
+ prepare do
19
+ plugin "markaby", :clone => true # so we can work in targets
20
+ end
21
+
22
+ run do
23
+ cd "vendor/plugins/markaby" do
24
+ sh "rake"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require 'markaby'
4
+ require 'markaby/rails'
5
+
6
+ Markaby::Rails.load
@@ -0,0 +1,30 @@
1
+ # = About lib/markaby.rb
2
+ #
3
+ # By requiring <tt>lib/markaby</tt>, you can load Markaby's dependency (the Builder library,)
4
+ # as well as the full set of Markaby classes.
5
+ #
6
+ # For a full list of features and instructions, see the README.
7
+ $:.unshift File.expand_path(File.dirname(__FILE__))
8
+
9
+ # Markaby is a module containing all of the great Markaby classes that
10
+ # do such an excellent job.
11
+ #
12
+ # * Markaby::Builder: the class for actually calling the Ruby methods
13
+ # which write the HTML.
14
+ # * Markaby::CSSProxy: a class which adds element classes and IDs to
15
+ # elements when used within Markaby::Builder.
16
+ # * Markaby::MetAid: metaprogramming helper methods.
17
+ # * Markaby::Tags: lists the roles of various XHTML tags to help Builder
18
+ # use these tags as they are intended.
19
+ # * Markaby::Template: a class for hooking Markaby into Rails as a
20
+ # proper templating language.
21
+ module Markaby
22
+ version_file = File.expand_path(File.dirname(__FILE__) + "/../VERSION")
23
+ VERSION = File.read(version_file).strip
24
+
25
+ class InvalidXhtmlError < StandardError; end
26
+ end
27
+
28
+ require 'builder' unless defined?(Builder)
29
+ require 'markaby/builder'
30
+ require 'markaby/cssproxy'
@@ -0,0 +1,314 @@
1
+ require 'markaby/tags'
2
+ require 'markaby/builder_tags'
3
+
4
+ module Markaby
5
+ RUBY_VERSION_ID = RUBY_VERSION.split(".").join.to_i
6
+
7
+ # The Markaby::Builder class is the central gear in the system. When using
8
+ # from Ruby code, this is the only class you need to instantiate directly.
9
+ #
10
+ # mab = Markaby::Builder.new
11
+ # mab.html do
12
+ # head { title "Boats.com" }
13
+ # body do
14
+ # h1 "Boats.com has great deals"
15
+ # ul do
16
+ # li "$49 for a canoe"
17
+ # li "$39 for a raft"
18
+ # li "$29 for a huge boot that floats and can fit 5 people"
19
+ # end
20
+ # end
21
+ # end
22
+ # puts mab.to_s
23
+ #
24
+ class Builder
25
+ include Markaby::BuilderTags
26
+
27
+ DEFAULT_OPTIONS = {
28
+ :indent => 0,
29
+ :output_helpers => true,
30
+ :output_xml_instruction => true,
31
+ :output_meta_tag => true,
32
+ :auto_validation => true,
33
+ :tagset => Markaby::XHTMLTransitional,
34
+ :root_attributes => {
35
+ :xmlns => 'http://www.w3.org/1999/xhtml',
36
+ :'xml:lang' => 'en',
37
+ :lang => 'en'
38
+ }
39
+ }
40
+
41
+ @@options = DEFAULT_OPTIONS.dup
42
+
43
+ def self.restore_defaults!
44
+ @@options = DEFAULT_OPTIONS.dup
45
+ end
46
+
47
+ def self.set(option, value)
48
+ @@options[option] = value
49
+ end
50
+
51
+ def self.get(option)
52
+ @@options[option]
53
+ end
54
+
55
+ def self.ignored_helpers
56
+ @@ignored_helpers ||= []
57
+ end
58
+
59
+ def self.ignore_helpers(*helpers)
60
+ ignored_helpers.concat helpers
61
+ end
62
+
63
+ attr_accessor :output_helpers, :tagset
64
+
65
+ # Create a Markaby builder object. Pass in a hash of variable assignments to
66
+ # +assigns+ which will be available as instance variables inside tag construction
67
+ # blocks. If an object is passed in to +helper+, its methods will be available
68
+ # from those same blocks.
69
+ #
70
+ # Pass in a +block+ to new and the block will be evaluated.
71
+ #
72
+ # mab = Markaby::Builder.new {
73
+ # html do
74
+ # body do
75
+ # h1 "Matching Mole"
76
+ # end
77
+ # end
78
+ # }
79
+ #
80
+ def initialize(assigns = {}, helper = nil, &block)
81
+ @streams = [Stream.new]
82
+ @assigns = assigns.dup
83
+ @_helper = helper
84
+ @used_ids = {}
85
+
86
+ @@options.each do |k, v|
87
+ instance_variable_set("@#{k}", @assigns.delete(k) || v)
88
+ end
89
+
90
+ @assigns.each do |k, v|
91
+ instance_variable_set("@#{k}", v)
92
+ end
93
+
94
+ if helper
95
+ helper.instance_variables.each do |iv|
96
+ instance_variable_set(iv, helper.instance_variable_get(iv))
97
+ end
98
+ end
99
+
100
+ @builder = XmlMarkup.new(:indent => @indent, :target => @streams.last)
101
+
102
+ text(capture(&block)) if block
103
+ end
104
+
105
+ def helper=(helper)
106
+ @_helper = helper
107
+ end
108
+
109
+ def metaclass(&block)
110
+ metaclass = class << self; self; end
111
+ metaclass.class_eval(&block)
112
+ end
113
+
114
+ private :metaclass
115
+
116
+ def locals=(locals)
117
+ locals.each do |key, value|
118
+ metaclass do
119
+ define_method key do
120
+ value
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ # Returns a string containing the HTML stream. Internally, the stream is stored as an Array.
127
+ def to_s
128
+ @streams.last.to_s
129
+ end
130
+
131
+ # Write a +string+ to the HTML stream without escaping it.
132
+ def text(string)
133
+ @builder << string.to_s
134
+ nil
135
+ end
136
+ alias_method :<<, :text
137
+ alias_method :concat, :text
138
+
139
+ # Captures the HTML code built inside the +block+. This is done by creating a new
140
+ # stream for the builder object, running the block and passing back its stream as a string.
141
+ #
142
+ # >> Markaby::Builder.new.capture { h1 "TEST"; h2 "CAPTURE ME" }
143
+ # => "<h1>TEST</h1><h2>CAPTURE ME</h2>"
144
+ #
145
+ def capture(&block)
146
+ @streams.push(@builder.target = Stream.new)
147
+ @builder.level += 1
148
+ str = instance_eval(&block)
149
+ str = @streams.last.join if @streams.last.any?
150
+ @streams.pop
151
+ @builder.level -= 1
152
+ @builder.target = @streams.last
153
+ str
154
+ end
155
+
156
+ # Create a tag named +tag+. Other than the first argument which is the tag name,
157
+ # the arguments are the same as the tags implemented via method_missing.
158
+ def tag!(tag, *args, &block)
159
+ ele_id = nil
160
+ if @auto_validation && @tagset
161
+ if !@tagset.tagset.has_key?(tag)
162
+ raise InvalidXhtmlError, "no element `#{tag}' for #{tagset.doctype}"
163
+ elsif args.last.respond_to?(:to_hash)
164
+ attrs = args.last.to_hash
165
+
166
+ if @tagset.forms.include?(tag) && attrs[:id]
167
+ attrs[:name] ||= attrs[:id]
168
+ end
169
+
170
+ attrs.each do |k, v|
171
+ atname = k.to_s.downcase.intern
172
+ unless k =~ /:/ or @tagset.tagset[tag].include? atname
173
+ raise InvalidXhtmlError, "no attribute `#{k}' on #{tag} elements"
174
+ end
175
+ if atname == :id
176
+ ele_id = v.to_s
177
+ if @used_ids.has_key? ele_id
178
+ raise InvalidXhtmlError, "id `#{ele_id}' already used (id's must be unique)."
179
+ end
180
+ end
181
+ if AttrsBoolean.include? atname
182
+ if v
183
+ attrs[k] = atname.to_s
184
+ else
185
+ attrs.delete k
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ if block
193
+ str = capture(&block)
194
+ block = proc { text(str) }
195
+ end
196
+
197
+ f = fragment { @builder.tag!(tag, *args, &block) }
198
+ @used_ids[ele_id] = f if ele_id
199
+ f
200
+ end
201
+
202
+ private
203
+
204
+ # This method is used to intercept calls to helper methods and instance
205
+ # variables. Here is the order of interception:
206
+ #
207
+ # * If +sym+ is a helper method, the helper method is called
208
+ # and output to the stream.
209
+ # * If +sym+ is a Builder::XmlMarkup method, it is passed on to the builder object.
210
+ # * If +sym+ is also the name of an instance variable, the
211
+ # value of the instance variable is returned.
212
+ # * If +sym+ has come this far and no +tagset+ is found, +sym+ and its arguments are passed to tag!
213
+ # * If a tagset is found, though, +NoMethodError+ is raised.
214
+ #
215
+ # method_missing used to be the lynchpin in Markaby, but it's no longer used to handle
216
+ # HTML tags. See html_tag for that.
217
+ def method_missing(sym, *args, &block)
218
+ if @_helper.respond_to?(sym, true) && !self.class.ignored_helpers.include?(sym)
219
+ r = @_helper.send(sym, *args, &block)
220
+ if @output_helpers && r.respond_to?(:to_str)
221
+ fragment { @builder << r }
222
+ else
223
+ r
224
+ end
225
+ elsif @assigns.has_key?(sym)
226
+ @assigns[sym]
227
+ elsif @assigns.has_key?(stringy_key = sym.to_s)
228
+ # Rails' ActionView assigns hash has string keys for
229
+ # instance variables that are defined in the controller.
230
+ @assigns[stringy_key]
231
+ elsif instance_variables_for(self).include?(ivar = "@#{sym}".to_sym)
232
+ instance_variable_get(ivar)
233
+ elsif @_helper && instance_variables_for(@_helper).include?(ivar)
234
+ @_helper.instance_variable_get(ivar)
235
+ elsif instance_methods_for(::Builder::XmlMarkup).include?(sym)
236
+ @builder.__send__(sym, *args, &block)
237
+ elsif !@tagset
238
+ tag!(sym, *args, &block)
239
+ else
240
+ super
241
+ end
242
+ end
243
+
244
+ if RUBY_VERSION_ID >= 191
245
+ def instance_variables_for(obj)
246
+ obj.instance_variables
247
+ end
248
+
249
+ def instance_methods_for(obj)
250
+ obj.instance_methods
251
+ end
252
+ else
253
+ def instance_variables_for(obj)
254
+ obj.instance_variables.map { |var| var.to_sym }
255
+ end
256
+
257
+ def instance_methods_for(obj)
258
+ obj.instance_methods.map { |m| m.to_sym }
259
+ end
260
+ end
261
+
262
+ def fragment
263
+ stream = @streams.last
264
+ start = stream.length
265
+ yield
266
+ length = stream.length - start
267
+ Fragment.new(stream, start, length)
268
+ end
269
+ end
270
+
271
+ class Stream < Array
272
+ alias_method :to_s, :join
273
+ end
274
+
275
+ # Every tag method in Markaby returns a Fragment. If any method gets called on the Fragment,
276
+ # the tag is removed from the Markaby stream and given back as a string. Usually the fragment
277
+ # is never used, though, and the stream stays intact.
278
+ #
279
+ # For a more practical explanation, check out the README.
280
+ class Fragment < ::Builder::BlankSlate
281
+ def initialize(*args)
282
+ @stream, @start, @length = args
283
+ @transformed_stream = false
284
+ end
285
+
286
+ [:to_s, :inspect, :==].each do |method|
287
+ undef_method method if method_defined?(method)
288
+ end
289
+
290
+ private
291
+
292
+ def method_missing(*args, &block)
293
+ transform_stream unless transformed_stream?
294
+ @str.__send__(*args, &block)
295
+ end
296
+
297
+ def transform_stream
298
+ @transformed_stream = true
299
+
300
+ # We can't do @stream.slice!(@start, @length),
301
+ # as it would invalidate the @starts and @lengths of other Fragment instances.
302
+ @str = @stream[@start, @length].to_s
303
+ @stream[@start, @length] = [nil] * @length
304
+ end
305
+
306
+ def transformed_stream?
307
+ @transformed_stream
308
+ end
309
+ end
310
+
311
+ class XmlMarkup < ::Builder::XmlMarkup
312
+ attr_accessor :target, :level
313
+ end
314
+ end