markaby 0.5 → 0.6.6

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 (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