markaby 0.5 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG.rdoc +79 -0
  3. data/Markaby.gemspec +103 -0
  4. data/{README → README.rdoc} +14 -6
  5. data/Rakefile +71 -14
  6. data/VERSION +1 -0
  7. data/garlic.rb +29 -0
  8. data/init.rb +6 -0
  9. data/lib/markaby.rb +4 -9
  10. data/lib/markaby/builder.rb +156 -143
  11. data/lib/markaby/builder_tags.rb +64 -0
  12. data/lib/markaby/cssproxy.rb +36 -34
  13. data/lib/markaby/kernel_method.rb +7 -0
  14. data/lib/markaby/rails.rb +64 -39
  15. data/lib/markaby/rails/current.rb +46 -0
  16. data/lib/markaby/rails/deprecated.rb +124 -0
  17. data/lib/markaby/rails/rails_builder.rb +50 -0
  18. data/lib/markaby/tags.rb +158 -130
  19. data/lib/markaby/tilt.rb +21 -0
  20. data/spec/markaby/builder_spec.rb +118 -0
  21. data/spec/markaby/css_proxy_spec.rb +47 -0
  22. data/spec/markaby/fragment_spec.rb +10 -0
  23. data/spec/markaby/markaby_other_static.mab +1 -0
  24. data/spec/markaby/markaby_spec.rb +232 -0
  25. data/spec/markaby/rails/spec_helper.rb +21 -0
  26. data/spec/markaby/rails/views/markaby/_a_partial.mab +3 -0
  27. data/spec/markaby/rails/views/markaby/_partial_child_with_locals.mab +1 -0
  28. data/spec/markaby/rails/views/markaby/access_to_helpers.mab +1 -0
  29. data/spec/markaby/rails/views/markaby/broken.mab +7 -0
  30. data/spec/markaby/rails/views/markaby/correct_template_values.mab +5 -0
  31. data/spec/markaby/rails/views/markaby/form_for.mab +2 -0
  32. data/spec/markaby/rails/views/markaby/form_for_with_fields.mab +3 -0
  33. data/spec/markaby/rails/views/markaby/form_for_with_multiple_fields.mab +4 -0
  34. data/spec/markaby/rails/views/markaby/no_values_passed.mab +3 -0
  35. data/spec/markaby/rails/views/markaby/partial_parent.mab +1 -0
  36. data/spec/markaby/rails/views/markaby/partial_parent_with_locals.mab +7 -0
  37. data/spec/markaby/rails/views/markaby/render_erb_without_explicit_render_call.erb +1 -0
  38. data/spec/markaby/rails/views/markaby/render_explicit_but_empty_markaby_layout.mab +0 -0
  39. data/spec/markaby/rails/views/markaby/render_mab_without_explicit_render_call.mab +3 -0
  40. data/spec/markaby/rails/views/markaby/render_with_ivar.mab +3 -0
  41. data/spec/markaby/rails/views/markaby/renders_erb.rhtml +1 -0
  42. data/spec/markaby/rails_spec.rb +249 -0
  43. data/spec/markaby/rails_version_spec.rb +37 -0
  44. data/spec/markaby/tilt/erb.erb +1 -0
  45. data/spec/markaby/tilt/locals.mab +1 -0
  46. data/spec/markaby/tilt/markaby.mab +1 -0
  47. data/spec/markaby/tilt/markaby_other_static.mab +1 -0
  48. data/spec/markaby/tilt/render_twice.mab +1 -0
  49. data/spec/markaby/tilt/scope.mab +1 -0
  50. data/spec/markaby/tilt/yielding.mab +2 -0
  51. data/spec/markaby/tilt_spec.rb +86 -0
  52. data/spec/spec.opts +2 -0
  53. data/spec/spec_helper.rb +39 -0
  54. metadata +132 -52
  55. data/lib/markaby/metaid.rb +0 -16
  56. data/lib/markaby/template.rb +0 -12
  57. data/setup.rb +0 -1551
  58. data/test/test_markaby.rb +0 -109
  59. data/tools/rakehelp.rb +0 -106
@@ -0,0 +1,64 @@
1
+ module Markaby
2
+ module BuilderTags
3
+ (XHTMLTransitional.tags - [:head]).each do |k|
4
+ class_eval <<-CODE, __FILE__, __LINE__
5
+ def #{k}(*args, &block)
6
+ html_tag(#{k.inspect}, *args, &block)
7
+ end
8
+ CODE
9
+ end
10
+
11
+ # Every HTML tag method goes through an html_tag call. So, calling <tt>div</tt> is equivalent
12
+ # to calling <tt>html_tag(:div)</tt>. All HTML tags in Markaby's list are given generated wrappers
13
+ # for this method.
14
+ #
15
+ # If the @auto_validation setting is on, this method will check for many common mistakes which
16
+ # could lead to invalid XHTML.
17
+ def html_tag(sym, *args, &block)
18
+ if @auto_validation && @tagset.self_closing.include?(sym) && block
19
+ raise InvalidXhtmlError, "the `#{sym}' element is self-closing, please remove the block"
20
+ elsif args.empty? && !block
21
+ CssProxy.new(self, @streams.last, sym)
22
+ else
23
+ tag!(sym, *args, &block)
24
+ end
25
+ end
26
+
27
+ # Builds a head tag. Adds a <tt>meta</tt> tag inside with Content-Type
28
+ # set to <tt>text/html; charset=utf-8</tt>.
29
+ def head(*args, &block)
30
+ tag!(:head, *args) do
31
+ tag!(:meta, "http-equiv" => "Content-Type", "content" => "text/html; charset=utf-8") if @output_meta_tag
32
+ instance_eval(&block)
33
+ end
34
+ end
35
+
36
+ # Builds an html tag. An XML 1.0 instruction and an XHTML 1.0 Transitional doctype
37
+ # are prepended. Also assumes <tt>:xmlns => "http://www.w3.org/1999/xhtml",
38
+ # :lang => "en"</tt>.
39
+ def xhtml_transitional(attrs = {}, &block)
40
+ self.tagset = Markaby::XHTMLTransitional
41
+ xhtml_html(attrs, &block)
42
+ end
43
+
44
+ # Builds an html tag with XHTML 1.0 Strict doctype instead.
45
+ def xhtml_strict(attrs = {}, &block)
46
+ self.tagset = Markaby::XHTMLStrict
47
+ xhtml_html(attrs, &block)
48
+ end
49
+
50
+ # Builds an html tag with XHTML 1.0 Frameset doctype instead.
51
+ def xhtml_frameset(attrs = {}, &block)
52
+ self.tagset = Markaby::XHTMLFrameset
53
+ xhtml_html(attrs, &block)
54
+ end
55
+
56
+ private
57
+
58
+ def xhtml_html(attrs = {}, &block)
59
+ instruct! if @output_xml_instruction
60
+ declare!(:DOCTYPE, :html, :PUBLIC, *tagset.doctype)
61
+ tag!(:html, @root_attributes.merge(attrs), &block)
62
+ end
63
+ end
64
+ end
@@ -4,50 +4,52 @@ module Markaby
4
4
  #
5
5
  # See the README for examples.
6
6
  class CssProxy
7
+ def initialize(builder, stream, sym)
8
+ @builder = builder
9
+ @stream = stream
10
+ @sym = sym
11
+ @attrs = {}
7
12
 
8
- # Creates a CssProxy object. The +opts+ and +block+ passed in are
9
- # stored until the element is created by Builder.tag!
10
- def initialize(opts = {}, &blk)
11
- @opts = opts
12
- @blk = blk
13
+ @original_stream_length = @stream.length
14
+
15
+ @builder.tag! sym
13
16
  end
14
-
15
- # Adds attributes to an element, for internal use only. For example, if you
16
- # want to write a wrapper which sets a bunch of default attributes for a certain
17
- # tag. Like the default `img' method included with Markaby automatically sets an
18
- # empty alt attribute.
19
- def merge!(opts)
20
- @opts.merge! opts
21
- self
17
+
18
+ def respond_to?(sym, include_private = false)
19
+ include_private || !private_methods.map { |m| m.to_sym }.include?(sym.to_sym) ? true : false
22
20
  end
23
21
 
22
+ private
23
+
24
24
  # Adds attributes to an element. Bang methods set the :id attribute.
25
- # Other methods add to the :class attribute. If a block is supplied,
26
- # it is executed with a merged hash (@opts + args).
27
- def method_missing(id_or_class, *args, &blk)
28
- idc = id_or_class.to_s
29
- case idc
30
- when "pass"
31
- when /!$/
32
- @opts[:id] = $`
33
- else
34
- @opts[:class] = "#{@opts[:class]} #{idc}".strip
35
- end
36
- if args.empty? and blk.nil?
37
- self
25
+ # Other methods add to the :class attribute.
26
+ def method_missing(id_or_class, *args, &block)
27
+ if id_or_class.to_s =~ /(.*)!$/
28
+ @attrs[:id] = $1
38
29
  else
30
+ id = id_or_class
31
+ @attrs[:class] = @attrs[:class] ? "#{@attrs[:class]} #{id}".strip : id
32
+ end
33
+
34
+ unless args.empty?
39
35
  if args.last.respond_to? :to_hash
40
- @opts.merge!(args.pop.to_hash)
36
+ @attrs.merge! args.pop.to_hash
41
37
  end
42
- args.push @opts
43
- @blk.call(args, blk)
44
38
  end
45
- end
46
39
 
47
- def to_str
48
- @blk.call([[@opts]]).to_s
49
- end
50
- alias_method :to_s, :to_str
40
+ args.push(@attrs)
41
+
42
+ while @stream.length > @original_stream_length
43
+ @stream.pop
44
+ end
45
+
46
+ if block
47
+ @builder.tag! @sym, *args, &block
48
+ else
49
+ @builder.tag! @sym, *args
50
+ end
51
51
 
52
+ self
53
+ end
52
54
  end
53
55
  end
@@ -0,0 +1,7 @@
1
+ # You'll need to <tt>require 'markaby/kernel_method'</tt> for this.
2
+ module Kernel
3
+ # Shortcut for creating a quick block of Markaby.
4
+ def mab(*args, &block)
5
+ Markaby::Builder.new(*args, &block).to_s
6
+ end
7
+ end
@@ -1,46 +1,71 @@
1
1
  module Markaby
2
+ module Rails
3
+ UNSUPPORTED_RAILS_VERSIONS = [
4
+ "2.0.0",
5
+ "2.0.1",
6
+ "2.0.2",
7
+ "2.0.3",
8
+ "2.0.4",
9
+ "2.0.5",
10
+ "2.1.0",
11
+ "2.1.1",
12
+ "2.1.2",
13
+ "2.3.0"
14
+ ]
2
15
 
3
- # Markaby helpers for Rails.
4
- module ActionControllerHelpers
5
- # Returns a string of HTML built from the attached +block+. Any +options+ are
6
- # passed into the render method.
7
- #
8
- # Use this method in your controllers to output Markaby directly from inside.
9
- def render_markaby(options = {}, &block)
10
- render options.merge({ :text => Builder.new({}, self, &block).to_s })
11
- end
12
- end
16
+ DEPRECATED_RAILS_VERSIONS = [
17
+ "1.2.2",
18
+ "1.2.3",
19
+ "1.2.4",
20
+ "1.2.5",
21
+ "1.2.6"
22
+ ]
13
23
 
14
- class ActionViewTemplateHandler
15
- def initialize(action_view)
16
- @action_view = action_view
17
- end
18
- def render(template, local_assigns = {})
19
- Template.new(template).render(@action_view.assigns.merge(local_assigns), @action_view)
20
- end
21
- end
24
+ FULLY_SUPPORTED_RAILS_VERSIONS = [
25
+ "2.2.0",
26
+ "2.2.1",
27
+ "2.2.2",
28
+ "2.2.3",
29
+ "2.3.1",
30
+ "2.3.2",
31
+ "2.3.2.1",
32
+ "2.3.3",
33
+ "2.3.3.1",
34
+ "2.3.4"
35
+ ]
36
+
37
+ SUPPORTED_RAILS_VERSIONS = DEPRECATED_RAILS_VERSIONS + FULLY_SUPPORTED_RAILS_VERSIONS
22
38
 
23
- class Builder
24
- # Emulate ERB to satisfy helpers like <tt>form_for</tt>.
25
- def _erbout; self end
26
-
27
- # Content_for will store the given block in an instance variable for later use
28
- # in another template or in the layout.
29
- #
30
- # The name of the instance variable is content_for_<name> to stay consistent
31
- # with @content_for_layout which is used by ActionView's layouts.
32
- #
33
- # Example:
34
- #
35
- # content_for("header") do
36
- # h1 "Half Shark and Half Lion"
37
- # end
38
- #
39
- # If used several times, the variable will contain all the parts concatenated.
40
- def content_for(name, &block)
41
- @helpers.assigns["content_for_#{name}"] =
42
- eval("@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)")
39
+ class << self
40
+ def load
41
+ check_rails_version
42
+
43
+ if deprecated_rails_version?
44
+ require File.dirname(__FILE__) + "/rails/deprecated"
45
+ else
46
+ require File.dirname(__FILE__) + "/rails/current"
47
+ end
48
+ end
49
+
50
+ def deprecated_rails_version?
51
+ DEPRECATED_RAILS_VERSIONS.include?(detected_rails_version)
52
+ end
53
+
54
+ def check_rails_version
55
+ if UNSUPPORTED_RAILS_VERSIONS.include?(detected_rails_version)
56
+ error_message = "Cannot load markaby under rails version #{detected_rails_version}. "
57
+ error_message << "See Markaby::Rails::SUPPORTED_RAILS_VERSIONS for exactly that, or redefine this constant."
58
+ raise LoadError, error_message
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def detected_rails_version
65
+ if defined?(::Rails)
66
+ ::Rails::VERSION::STRING
67
+ end
68
+ end
43
69
  end
44
70
  end
45
-
46
71
  end
@@ -0,0 +1,46 @@
1
+ require 'markaby/rails/rails_builder'
2
+
3
+ module Markaby
4
+ module Rails
5
+ class TemplateHandler < ::ActionView::TemplateHandler
6
+ include ActionView::TemplateHandlers::Compilable
7
+
8
+ def compile(template, local_assigns={})
9
+ <<-CODE
10
+ handler = Markaby::Rails::TemplateHandler.new
11
+ handler.view = self
12
+ handler.render(lambda { #{template.source} }, local_assigns)
13
+ CODE
14
+ end
15
+
16
+ def render(template, local_assigns = (template.respond_to?(:locals) ? template.locals : {}))
17
+ builder = RailsBuilder.new(instance_variables.merge(local_assigns), @view)
18
+ @view.output_buffer = builder
19
+
20
+ template.is_a?(Proc) ?
21
+ builder.instance_eval(&template) :
22
+ builder.instance_eval(template.source)
23
+
24
+ builder.to_s
25
+ end
26
+
27
+ attr_accessor :view
28
+
29
+ private
30
+
31
+ def instance_variables
32
+ instance_variable_hash(@view)
33
+ end
34
+
35
+ def instance_variable_hash(object)
36
+ returning Hash.new do |hash|
37
+ object.instance_variables.each do |var_name|
38
+ hash[var_name.gsub("@", "")] = object.instance_variable_get(var_name)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ ActionView::Template.register_template_handler(:mab, Markaby::Rails::TemplateHandler)
@@ -0,0 +1,124 @@
1
+ require 'markaby/rails/rails_builder'
2
+
3
+ module ActionView # :nodoc:
4
+ class Base # :nodoc:
5
+ def render_template(template_extension, template, file_path = nil, local_assigns = {})
6
+ if handler = @@template_handlers[template_extension]
7
+ template ||= read_template_file(file_path, template_extension)
8
+ handler.new(self).render(template, local_assigns, file_path)
9
+ else
10
+ compile_and_render_template(template_extension, template, file_path, local_assigns)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module Markaby
17
+ module Rails
18
+ class Template
19
+
20
+ def self.builder_class=(builder)
21
+ @@builder_class = builder
22
+ end
23
+
24
+ def self.builder_class
25
+ @@builder_class ||= Builder
26
+ end
27
+
28
+ attr_accessor :source, :path
29
+
30
+ def initialize(source)
31
+ @source = source.to_s
32
+ end
33
+
34
+ def render(*args)
35
+ output = self.class.builder_class.new(*args)
36
+
37
+ if path
38
+ output.instance_eval source, path
39
+ else
40
+ output.instance_eval source
41
+ end
42
+
43
+ output.to_s
44
+ end
45
+ end
46
+
47
+ # Markaby helpers for Rails.
48
+ module ActionControllerHelpers
49
+ # Returns a string of HTML built from the attached +block+. Any +options+ are
50
+ # passed into the render method.
51
+ #
52
+ # Use this method in your controllers to output Markaby directly from inside.
53
+ def render_markaby(options = {}, &block)
54
+ render options.merge({ :text => Builder.new(options[:locals], self, &block).to_s })
55
+ end
56
+ end
57
+
58
+ class ActionViewTemplateHandler # :nodoc:
59
+ def initialize(action_view)
60
+ @action_view = action_view
61
+ end
62
+ def render(template, local_assigns, file_path)
63
+ template = Template.new(template)
64
+ template.path = file_path
65
+ template.render(@action_view.assigns.merge(local_assigns), @action_view)
66
+ end
67
+ end
68
+
69
+ class Builder < RailsBuilder # :nodoc:
70
+ def initialize(*args, &block)
71
+ super *args, &block
72
+
73
+ @assigns.each { |k, v| @helpers.instance_variable_set("@#{k}", v) }
74
+ end
75
+
76
+ def flash(*args)
77
+ @helpers.controller.send(:flash, *args)
78
+ end
79
+
80
+ # Emulate ERB to satisfy helpers like <tt>form_for</tt>.
81
+ def _erbout
82
+ @_erbout ||= FauxErbout.new(self)
83
+ end
84
+
85
+ # Content_for will store the given block in an instance variable for later use
86
+ # in another template or in the layout.
87
+ #
88
+ # The name of the instance variable is content_for_<name> to stay consistent
89
+ # with @content_for_layout which is used by ActionView's layouts.
90
+ #
91
+ # Example:
92
+ #
93
+ # content_for("header") do
94
+ # h1 "Half Shark and Half Lion"
95
+ # end
96
+ #
97
+ # If used several times, the variable will contain all the parts concatenated.
98
+ def content_for(name, &block)
99
+ @helpers.assigns["content_for_#{name}"] =
100
+ eval("@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)")
101
+ end
102
+ end
103
+
104
+ Template.builder_class = Builder
105
+
106
+ class FauxErbout < ::Builder::BlankSlate # :nodoc:
107
+ def initialize(builder)
108
+ @builder = builder
109
+ end
110
+ def nil? # see ActionView::Helpers::CaptureHelper#capture
111
+ true
112
+ end
113
+ def method_missing(*args, &block)
114
+ @builder.send *args, &block
115
+ end
116
+ end
117
+ end
118
+
119
+ if defined? ActionView::Template and ActionView::Template.respond_to? :register_template_handler
120
+ ActionView::Template
121
+ else
122
+ ActionView::Base
123
+ end.register_template_handler(:mab, Markaby::Rails::ActionViewTemplateHandler)
124
+ end
@@ -0,0 +1,50 @@
1
+ module Markaby
2
+ module Rails
3
+ class RailsBuilder < Markaby::Builder
4
+ def form_for(*args, &block)
5
+ @template.form_for(*args) do |__form_for_variable|
6
+ yield(FormHelperProxy.new(self, __form_for_variable))
7
+ end
8
+ end
9
+
10
+ # This is used for the block variable given to form_for. Typically, an erb template looks as so:
11
+ #
12
+ # <% form_for :foo do |f|
13
+ # <%= f.text_field :bar %>
14
+ # <% end %>
15
+ #
16
+ # form_for adds the form tag to the input stream, and assumes that later the user will append
17
+ # the <input> tag to the input stream himself (in erb, this is done with the <%= %> tags).
18
+ #
19
+ # We could do the following in Markaby:
20
+ #
21
+ # form_for :foo do |f|
22
+ # text f.text_field(:bar)
23
+ # end
24
+ #
25
+ # But this is ugly. This is prettier:
26
+ #
27
+ # form_for :foo do |f|
28
+ # f.text_field :bar
29
+ # end
30
+ class FormHelperProxy
31
+ def initialize(builder, proxied_object)
32
+ @builder = builder
33
+ @proxied_object = proxied_object
34
+ end
35
+
36
+ def respond_to?(sym, include_private = false)
37
+ @proxied_object.respond_to?(sym, include_private) || super
38
+ end
39
+
40
+ private
41
+
42
+ def method_missing(sym, *args, &block)
43
+ result = @proxied_object.__send__(sym, *args, &block)
44
+ @builder.text(result) if result.is_a?(String)
45
+ result
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end