erector-rails4 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +1 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +5 -0
  8. data/Gemfile +6 -0
  9. data/Gemfile.lock +143 -0
  10. data/README.md +4 -0
  11. data/Rakefile +24 -0
  12. data/erector-rails4.gemspec +36 -0
  13. data/erector-rails4.sublimeproject +20 -0
  14. data/lib/erector/abstract_widget.rb +231 -0
  15. data/lib/erector/after_initialize.rb +29 -0
  16. data/lib/erector/attributes.rb +29 -0
  17. data/lib/erector/cache.rb +37 -0
  18. data/lib/erector/caching.rb +65 -0
  19. data/lib/erector/convenience.rb +98 -0
  20. data/lib/erector/dependencies.rb +24 -0
  21. data/lib/erector/dependency.rb +31 -0
  22. data/lib/erector/element.rb +113 -0
  23. data/lib/erector/externals.rb +104 -0
  24. data/lib/erector/html.rb +12 -0
  25. data/lib/erector/html_widget.rb +220 -0
  26. data/lib/erector/inline.rb +37 -0
  27. data/lib/erector/jquery.rb +28 -0
  28. data/lib/erector/mixin.rb +12 -0
  29. data/lib/erector/needs.rb +95 -0
  30. data/lib/erector/output.rb +144 -0
  31. data/lib/erector/promise.rb +141 -0
  32. data/lib/erector/rails/form_builder.rb +44 -0
  33. data/lib/erector/rails/railtie.rb +13 -0
  34. data/lib/erector/rails/template_handler.rb +16 -0
  35. data/lib/erector/rails/widget_renderer.rb +6 -0
  36. data/lib/erector/rails.rb +221 -0
  37. data/lib/erector/raw_string.rb +12 -0
  38. data/lib/erector/sass.rb +32 -0
  39. data/lib/erector/tag.rb +66 -0
  40. data/lib/erector/text.rb +123 -0
  41. data/lib/erector/unicode.rb +18185 -0
  42. data/lib/erector/unicode_builder.rb +67 -0
  43. data/lib/erector/version.rb +5 -0
  44. data/lib/erector/widget.rb +94 -0
  45. data/lib/erector/widgets.rb +5 -0
  46. data/lib/erector/xml_widget.rb +131 -0
  47. data/lib/erector.rb +28 -0
  48. data/spec/dummy/Rakefile +7 -0
  49. data/spec/dummy/app/controllers/application.rb +6 -0
  50. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  51. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  52. data/spec/dummy/app/views/layouts/erb_as_layout.html.erb +2 -0
  53. data/spec/dummy/app/views/layouts/widget_as_layout.rb +8 -0
  54. data/spec/dummy/app/views/test/_erb.erb +1 -0
  55. data/spec/dummy/app/views/test/_erector.rb +5 -0
  56. data/spec/dummy/app/views/test/_partial_with_locals.rb +7 -0
  57. data/spec/dummy/app/views/test/bare.rb +5 -0
  58. data/spec/dummy/app/views/test/erb_from_erector.html.rb +5 -0
  59. data/spec/dummy/app/views/test/erector_from_erb.html.erb +1 -0
  60. data/spec/dummy/app/views/test/erector_with_locals_from_erb.html.erb +6 -0
  61. data/spec/dummy/app/views/test/implicit_assigns.html.rb +5 -0
  62. data/spec/dummy/app/views/test/needs.html.rb +7 -0
  63. data/spec/dummy/app/views/test/needs_subclass.html.rb +5 -0
  64. data/spec/dummy/app/views/test/protected_instance_variable.html.rb +5 -0
  65. data/spec/dummy/app/views/test/render_default.html.rb +5 -0
  66. data/spec/dummy/app/views/test/render_default_erb_with_layout.html.erb +1 -0
  67. data/spec/dummy/app/views/test/render_default_widget_with_layout.html.rb +5 -0
  68. data/spec/dummy/app/views/test/render_partial.html.rb +5 -0
  69. data/spec/dummy/app/views/test/render_with_widget_as_layout.rb +5 -0
  70. data/spec/dummy/app/views/test/render_with_widget_as_layout_using_content_for.rb +8 -0
  71. data/spec/dummy/config/application.rb +44 -0
  72. data/spec/dummy/config/boot.rb +10 -0
  73. data/spec/dummy/config/database.yml +22 -0
  74. data/spec/dummy/config/environment.rb +5 -0
  75. data/spec/dummy/config/environments/development.rb +22 -0
  76. data/spec/dummy/config/environments/production.rb +49 -0
  77. data/spec/dummy/config/environments/test.rb +36 -0
  78. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/spec/dummy/config/initializers/inflections.rb +10 -0
  80. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  81. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  82. data/spec/dummy/config/initializers/session_store.rb +8 -0
  83. data/spec/dummy/config/locales/en.yml +5 -0
  84. data/spec/dummy/config/routes.rb +3 -0
  85. data/spec/dummy/config.ru +4 -0
  86. data/spec/dummy/db/seeds.rb +7 -0
  87. data/spec/dummy/script/rails +6 -0
  88. data/spec/dummy/spec/form_builder_spec.rb +21 -0
  89. data/spec/dummy/spec/rails_helpers_spec.rb +236 -0
  90. data/spec/dummy/spec/rails_spec_helper.rb +10 -0
  91. data/spec/dummy/spec/rails_widget_spec.rb +83 -0
  92. data/spec/dummy/spec/render_spec.rb +369 -0
  93. data/spec/erector/after_initialize_spec.rb +45 -0
  94. data/spec/erector/cache_spec.rb +133 -0
  95. data/spec/erector/caching_spec.rb +184 -0
  96. data/spec/erector/convenience_spec.rb +250 -0
  97. data/spec/erector/dependency_spec.rb +67 -0
  98. data/spec/erector/hello_from_readme.rb +18 -0
  99. data/spec/erector/hello_from_readme_spec.rb +11 -0
  100. data/spec/erector/html_spec.rb +585 -0
  101. data/spec/erector/indentation_spec.rb +211 -0
  102. data/spec/erector/inline_spec.rb +94 -0
  103. data/spec/erector/jquery_spec.rb +35 -0
  104. data/spec/erector/mixin_spec.rb +65 -0
  105. data/spec/erector/needs_spec.rb +141 -0
  106. data/spec/erector/output_spec.rb +293 -0
  107. data/spec/erector/promise_spec.rb +173 -0
  108. data/spec/erector/sample-file.txt +1 -0
  109. data/spec/erector/sass_spec.rb +57 -0
  110. data/spec/erector/tag_spec.rb +67 -0
  111. data/spec/erector/unicode_builder_spec.rb +75 -0
  112. data/spec/erector/widget_spec.rb +310 -0
  113. data/spec/erector/xml_widget_spec.rb +73 -0
  114. data/spec/spec_helper.rb +31 -0
  115. metadata +368 -0
@@ -0,0 +1,12 @@
1
+ require "erector/element"
2
+ require "erector/attributes"
3
+ require "erector/promise"
4
+ require "erector/text"
5
+ require "erector/tag"
6
+
7
+ module Erector
8
+ module HTML
9
+
10
+
11
+ end
12
+ end
@@ -0,0 +1,220 @@
1
+ require "erector/xml_widget"
2
+
3
+ module Erector
4
+
5
+ # A Widget is the center of the Erector universe.
6
+ #
7
+ # To create a widget, extend Erector::Widget and implement the +content+
8
+ # method. Inside this method you may call any of the tag methods like +span+
9
+ # or +p+ to emit HTML/XML tags.
10
+ #
11
+ # You can also define a widget on the fly by passing a block to +new+. This
12
+ # block will get executed when the widget's +content+ method is called. See
13
+ # the userguide for important details about the scope of this block when run --
14
+ # http://erector.rubyforge.org/userguide.html#blocks
15
+ #
16
+ # To render a widget from the outside, instantiate it and call its +to_html+
17
+ # method.
18
+ #
19
+ # A widget's +new+ method optionally accepts an options hash. Entries in
20
+ # this hash are converted to instance variables.
21
+ #
22
+ # You can add runtime input checking via the +needs+ macro. See #needs.
23
+ # This mechanism is meant to ameliorate development-time confusion about
24
+ # exactly what parameters are supported by a given widget, avoiding
25
+ # confusing runtime NilClass errors.
26
+ #
27
+ # To call one widget from another, inside the parent widget's +content+
28
+ # method, instantiate the child widget and call the +widget+ method. This
29
+ # assures that the same output stream is used, which gives better
30
+ # performance than using +capture+ or +to_html+. It also preserves the
31
+ # indentation and helpers of the enclosing class.
32
+ #
33
+ # In this documentation we've tried to keep the distinction clear between
34
+ # methods that *emit* text and those that *return* text. "Emit" means that
35
+ # it writes to the output stream; "return" means that it returns a string
36
+ # like a normal method and leaves it up to the caller to emit that string if
37
+ # it wants.
38
+ #
39
+ # This class extends AbstractWidget and includes several modules,
40
+ # so be sure to check all of those places for API documentation for the
41
+ # various methods of Widget:
42
+ #
43
+ # * AbstractWidget
44
+ # * Element
45
+ # * Attributes
46
+ # * Text
47
+ # * Needs
48
+ # * Caching
49
+ # * Externals
50
+ # * AfterInitialize
51
+ #
52
+ # * HTML
53
+ # * Convenience
54
+ # * JQuery
55
+ # * Sass
56
+ #
57
+ # Also read the API Cheatsheet in the user guide
58
+ # at http://erector.rubyforge.org/userguide#apicheatsheet
59
+ class HTMLWidget < Erector::XMLWidget
60
+
61
+ include Erector::HTML
62
+ include Erector::Convenience
63
+ include Erector::JQuery
64
+ include Erector::Sass
65
+
66
+ tag 'area', :self_closing
67
+ tag 'base', :self_closing
68
+ tag 'br', :self_closing
69
+ tag 'col', :self_closing
70
+ tag 'embed', :self_closing
71
+ tag 'frame', :self_closing
72
+ tag 'hr', :self_closing
73
+ tag 'img', :self_closing, :inline
74
+ tag 'input', :self_closing, :inline
75
+ tag 'link', :self_closing
76
+ tag 'meta', :self_closing
77
+ tag 'param', :self_closing
78
+
79
+ tag 'a', :inline
80
+ tag 'abbr', :inline
81
+ tag 'acronym', :inline
82
+ tag 'address'
83
+ tag 'article'
84
+ tag 'aside'
85
+ tag 'audio'
86
+
87
+ tag 'b', :inline
88
+ tag 'bdo', :inline
89
+ tag 'big', :inline
90
+ tag 'blockquote'
91
+ tag 'body'
92
+ tag 'button', :inline
93
+
94
+ tag 'canvas'
95
+ tag 'caption', :inline
96
+ tag 'center'
97
+ tag 'cite', :inline
98
+ tag 'code', :inline
99
+ tag 'colgroup'
100
+ tag 'command'
101
+
102
+ tag 'datalist'
103
+ tag 'dd'
104
+ tag 'del'
105
+ tag 'details'
106
+ tag 'dfn', :inline
107
+ tag 'dialog'
108
+ tag 'div'
109
+ tag 'dl'
110
+ tag 'dt', :inline
111
+
112
+ tag 'em', :inline
113
+
114
+ tag 'fieldset'
115
+ tag 'figure'
116
+ tag 'footer'
117
+ tag 'form'
118
+ tag 'frameset'
119
+
120
+ tag 'h1'
121
+ tag 'h2'
122
+ tag 'h3'
123
+ tag 'h4'
124
+ tag 'h5'
125
+ tag 'h6'
126
+ tag 'head'
127
+ tag 'header'
128
+ tag 'hgroup'
129
+ tag 'html'
130
+ tag 'i', :inline
131
+
132
+ tag 'iframe'
133
+ tag 'ins'
134
+ tag 'keygen'
135
+ tag 'kbd', :inline
136
+ tag 'label', :inline
137
+ tag 'legend', :inline
138
+ tag 'li'
139
+
140
+ tag 'map'
141
+ tag 'mark'
142
+ tag 'meter'
143
+
144
+ tag 'nav'
145
+ tag 'noframes'
146
+ tag 'noscript'
147
+
148
+ tag 'object'
149
+ tag 'ol'
150
+ tag 'optgroup'
151
+ tag 'option'
152
+
153
+ tag 'p'
154
+ tag 'pre'
155
+ tag 'progress'
156
+
157
+ tag 'q', :inline
158
+ tag 'ruby'
159
+ tag 'rt'
160
+ tag 'rp'
161
+ tag 's'
162
+
163
+ tag 'samp', :inline
164
+ tag 'script'
165
+ tag 'section'
166
+ tag 'select', :inline
167
+ tag 'small', :inline
168
+ tag 'source'
169
+ tag 'span', :inline
170
+ tag 'strike'
171
+
172
+ tag 'strong', :inline
173
+ tag 'style'
174
+ tag 'sub', :inline
175
+ tag 'sup', :inline
176
+
177
+ tag 'table'
178
+ tag 'tbody'
179
+ tag 'td'
180
+ tag 'textarea', :inline
181
+ tag 'tfoot'
182
+
183
+ tag 'th'
184
+ tag 'thead'
185
+ tag 'time'
186
+ tag 'title'
187
+ tag 'tr'
188
+ tag 'tt', :inline
189
+
190
+ tag 'u'
191
+ tag 'ul'
192
+
193
+ tag 'var', :inline
194
+ tag 'video'
195
+
196
+
197
+ # alias for AbstractWidget#render
198
+ def to_html(options = {})
199
+ raise "Erector::Widget#to_html takes an options hash, not a symbol. Try calling \"to_html(:content_method_name=> :#{options})\"" if options.is_a? Symbol
200
+ _render(options).to_s
201
+ end
202
+
203
+ # alias for #to_html
204
+ # @deprecated Please use {#to_html} instead
205
+ def to_s(*args)
206
+ unless defined? @@already_warned_to_s
207
+ $stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
208
+ @@already_warned_to_s = true
209
+ end
210
+ to_html(*args)
211
+ end
212
+
213
+ end
214
+
215
+ public
216
+
217
+ # Alias to make it easier to spell
218
+ HtmlWidget = HTMLWidget
219
+
220
+ end
@@ -0,0 +1,37 @@
1
+ module Erector
2
+ def self.inline(*args, &block)
3
+ InlineWidget.new(*args, &block)
4
+ end
5
+
6
+ module Inline
7
+ # Executes the widget's block (the one that was passed in the
8
+ # constructor). Since "self" is pointing to the new widget, the block does
9
+ # not naturally have access to parent method methods, so an
10
+ # Erector::Inline widget uses some method_missing black magic to propagate
11
+ # messages to the parent object. Since it executes inside the *called*
12
+ # widget's context, when the block refers to instance variables, it's
13
+ # talking about those of this widget, not the caller. It does, of course,
14
+ # have access to bound local variables of the caller, so you can use those
15
+ # to smuggle in instance variables.
16
+ def call_block
17
+ # note that instance_eval seems to pass in self as a parameter to the block
18
+ instance_eval(&block) if block
19
+ end
20
+
21
+ private
22
+ # This is part of the sub-widget/parent feature (see #widget method).
23
+ def method_missing(name, *args, &block)
24
+ if parent && parent.respond_to?(name)
25
+ block ||= lambda {} # captures self HERE
26
+ parent.send(name, *args, &block)
27
+ else
28
+ super
29
+ end
30
+ end
31
+ end
32
+
33
+ class InlineWidget < Widget
34
+ include Inline
35
+ end
36
+
37
+ end
@@ -0,0 +1,28 @@
1
+ module Erector
2
+ module JQuery
3
+ # Emits a jQuery script, inside its own script tag, that is to be run on document ready or load.
4
+ #
5
+ # Usage (from inside a widget method):
6
+ # jquery "alert('hi')" :: a jquery ready handler
7
+ # jquery "alert('hi')", :id => 'foo' :: a jquery ready handler, with attributes in the script tag
8
+ # jquery :load, "alert('hi')" :: a jquery load handler
9
+ #
10
+ def jquery(*args)
11
+ event = if args.first.is_a? Symbol
12
+ args.shift
13
+ else
14
+ :ready
15
+ end
16
+ txt = args.shift
17
+ attributes = args.shift || {}
18
+
19
+ javascript attributes do
20
+ rawtext "\n"
21
+ rawtext "jQuery(document).#{event}(function($){\n"
22
+ rawtext txt
23
+ rawtext "\n});"
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ module Erector
2
+ module Mixin
3
+ # Executes the block as if it were the content body of a fresh Erector::Inline,
4
+ # and returns the #to_html value. Since it executes inside the new widget it does not
5
+ # have access to instance variables of the caller, although it does
6
+ # have access to bound variables. Funnily enough, the options are passed in to both
7
+ # to_html *and* to the widget itself, so they show up as instance variables.
8
+ def erector(options = {}, &block)
9
+ Erector.inline(options, &block).to_html(options)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,95 @@
1
+ module Erector
2
+ module Needs
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ # Class method by which widget classes can declare that they need certain
9
+ # parameters. If needed parameters are not passed in to #new, then an
10
+ # exception will be thrown (with a hopefully useful message about which
11
+ # parameters are missing). This is intended to catch silly bugs like
12
+ # passing in a parameter called 'name' to a widget that expects a
13
+ # parameter called 'title'.
14
+ #
15
+ # You can also declare default values for parameters using hash syntax.
16
+ # You can put #needs declarations on multiple lines or on the same line;
17
+ # the only caveat is that if there are default values, they all have to be
18
+ # at the end of the line (so they go into the magic hash parameter).
19
+ #
20
+ # If a widget has no #needs declaration then it will accept any
21
+ # combination of parameters just like normal. If a widget wants to declare
22
+ # that it takes no parameters, use the special incantation "needs nil"
23
+ # (and don't declare any other needs, or kittens will cry).
24
+ #
25
+ # Usage:
26
+ # class FancyForm < Erector::Widget
27
+ # needs :title, :show_okay => true, :show_cancel => false
28
+ # ...
29
+ # end
30
+ #
31
+ # That means that
32
+ # FancyForm.new(:title => 'Login')
33
+ # will succeed, as will
34
+ # FancyForm.new(:title => 'Login', :show_cancel => true)
35
+ # but
36
+ # FancyForm.new(:name => 'Login')
37
+ # will fail.
38
+ #
39
+ def needs(*args)
40
+ args.each do |arg|
41
+ (@needs ||= []) << (arg.nil? ? nil : (arg.is_a? Hash) ? arg : arg.to_sym)
42
+ end
43
+ end
44
+
45
+ def get_needs
46
+ @needs ||= []
47
+
48
+ ancestors[1..-1].inject(@needs.dup) do |needs, ancestor|
49
+ needs.push(*ancestor.get_needs) if ancestor.respond_to?(:get_needs)
50
+ needs
51
+ end
52
+ end
53
+
54
+ def needed_variables
55
+ @needed_variables ||= get_needs.map{|need| need.is_a?(Hash) ? need.keys : need}.flatten
56
+ end
57
+
58
+ def needed_defaults
59
+ @needed_defaults ||= get_needs.inject({}) do |defaults, need|
60
+ defaults = need.merge(defaults) if need.is_a? Hash
61
+ defaults
62
+ end
63
+ end
64
+
65
+ def needs?(name)
66
+ needed_variables.empty? || needed_variables.include?(name)
67
+ end
68
+ end
69
+
70
+ def initialize(assigns = {})
71
+ super
72
+
73
+ assigned = assigns.keys
74
+
75
+ # set variables with default values
76
+ self.class.needed_defaults.each do |name, value|
77
+ unless assigned.include?(name)
78
+ value = [NilClass, FalseClass, TrueClass, Fixnum, Float, Symbol].include?(value.class) ? value : value.dup
79
+ instance_variable_set("@#{name}", value)
80
+ assigned << name
81
+ end
82
+ end
83
+
84
+ missing = self.class.needed_variables - assigned
85
+ unless missing.empty? || missing == [nil]
86
+ raise ArgumentError, "Missing parameter#{missing.size == 1 ? '' : 's'} for #{self.class.name}: #{missing.join(', ')}"
87
+ end
88
+
89
+ excess = assigned - self.class.needed_variables
90
+ unless self.class.needed_variables.empty? || excess.empty?
91
+ raise ArgumentError, "Excess parameter#{excess.size == 1 ? '' : 's'} for #{self.class.name}: #{excess.join(', ')}"
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,144 @@
1
+ require 'erector/abstract_widget'
2
+
3
+ module Erector
4
+ class Output
5
+ SPACES_PER_INDENT = 2
6
+
7
+ attr_reader :prettyprint, :widgets, :indentation, :max_length
8
+
9
+ def initialize(options = {})
10
+ @prettyprint = options.fetch(:prettyprint, AbstractWidget.prettyprint_default)
11
+ @indentation = options.fetch(:indentation, 0)
12
+ @current_line_length = 0
13
+ @max_length = options[:max_length]
14
+ @widgets = []
15
+
16
+ @get_buffer = if options[:buffer] and options[:buffer].respond_to? :call
17
+ options[:buffer]
18
+ elsif options[:buffer]
19
+ lambda { options[:buffer] }
20
+ else
21
+ buffer = []
22
+ lambda { buffer }
23
+ end
24
+ end
25
+
26
+ def buffer
27
+ @get_buffer.call
28
+ end
29
+
30
+ def <<(s)
31
+ # raise s.inspect unless s.is_a? String
32
+ #
33
+ s = s.to_s unless s.is_a? String
34
+ append_indentation
35
+ if @max_length && s.length + @current_line_length > @max_length
36
+ leading_spaces = s =~ /^( +)/ ? $1.size : 0
37
+ trailing_spaces = s =~ /( +)$/ ? $1.size : 0
38
+
39
+ append(" " * leading_spaces)
40
+ need_space = false
41
+ words = s.split(/ /)
42
+ words.each do |word|
43
+ if (need_space ? 1 : 0) + word.length > space_left
44
+ append_newline
45
+ append_indentation
46
+ need_space = false
47
+ end
48
+ append(" ") if need_space
49
+ append(word)
50
+ need_space = true
51
+ end
52
+ append(" " * trailing_spaces)
53
+ else
54
+ append(s)
55
+ end
56
+ self
57
+ end
58
+
59
+ # Inserts a blank string into the output stream and returns a pointer to
60
+ # it. If the caller holds on to this pointer, she can later go back and
61
+ # insert text earlier in the stream. This is used for, e.g., inserting
62
+ # stuff inside the HEAD element that is not known until after the entire
63
+ # page renders.
64
+ def placeholder
65
+ s = ""
66
+ buffer << s
67
+ s
68
+ end
69
+
70
+ def to_s
71
+ RawString.new(buffer.kind_of?(String) ? buffer : buffer.join)
72
+ end
73
+
74
+ def to_a
75
+ buffer.kind_of?(Array) ? buffer : [buffer]
76
+ end
77
+
78
+ def newline
79
+ if prettyprint
80
+ append_newline
81
+ end
82
+ end
83
+
84
+ def at_line_start?
85
+ @current_line_length == 0
86
+ end
87
+
88
+ def indent
89
+ @indentation += 1 if prettyprint
90
+ end
91
+
92
+ def undent
93
+ @indentation -= 1 if prettyprint
94
+ end
95
+
96
+ # always append a newline, regardless of prettyprint setting
97
+ #todo: test
98
+ def append_newline
99
+ buffer << "\n"
100
+ @current_line_length = 0
101
+ end
102
+
103
+ def mark
104
+ @mark = buffer.size
105
+ end
106
+
107
+ def rewind pos = @mark
108
+ if buffer.kind_of?(Array)
109
+ buffer.slice!(pos..-1)
110
+ elsif (Object.const_defined?(:ActiveSupport) and
111
+ buffer.kind_of?(ActiveSupport::SafeBuffer))
112
+ # monkey patch to get around SafeBuffer's well-meaning paranoia
113
+ # see http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/
114
+ # and http://weblog.rubyonrails.org/2011/6/8/potential-xss-vulnerability-in-ruby-on-rails-applications
115
+ String.instance_method(:slice!).bind(buffer).call(pos..-1)
116
+ elsif buffer.kind_of?(String)
117
+ buffer.slice!(pos..-1)
118
+ else
119
+ raise "Don't know how to rewind a #{buffer.class}"
120
+ end
121
+
122
+ end
123
+
124
+ protected
125
+
126
+ def append(s)
127
+ buffer << s
128
+ @current_line_length += s.length
129
+ end
130
+
131
+ def space_left
132
+ @max_length - @current_line_length
133
+ end
134
+
135
+ def append_indentation
136
+ if prettyprint and at_line_start?
137
+ spaces = " " * ([@indentation, 0].max * SPACES_PER_INDENT)
138
+ buffer << spaces
139
+ @current_line_length += spaces.length
140
+ end
141
+ end
142
+
143
+ end
144
+ end