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,141 @@
1
+ require "erector/attributes"
2
+ require "erector/text"
3
+
4
+ module Erector
5
+ class Promise
6
+ extend Attributes
7
+ extend Text
8
+
9
+ def initialize(output, tag_name, attributes = {}, self_closing = false, newliney = true, &inside_renderer)
10
+ raise "bad output: #{output.inspect}" unless output.is_a? Output
11
+ raise "forgot self-closing" unless [false, true].include? self_closing
12
+
13
+ @output = output
14
+
15
+ # todo: pointer to Tag object?
16
+ @tag_name = tag_name
17
+ @self_closing = self_closing
18
+ @newliney = newliney
19
+
20
+ @attributes = {}
21
+ _set_attributes attributes
22
+ @text = nil
23
+ @inside_renderer = inside_renderer
24
+ _mark
25
+ end
26
+
27
+ def _set_attributes attributes
28
+ attributes.each_pair do |k,v|
29
+ @attributes[k.to_s] = v
30
+ end
31
+ end
32
+
33
+ def _mark
34
+ @mark = @output.mark
35
+ end
36
+
37
+ def _rewind
38
+ @output.rewind @mark
39
+ end
40
+
41
+ def _render
42
+ _rewind
43
+ _render_open_tag
44
+ begin
45
+ _render_inside_tag
46
+ ensure
47
+ _render_close_tag
48
+ end
49
+ end
50
+
51
+ def _render_open_tag
52
+
53
+ @output.newline if !@self_closing and @newliney and !@output.at_line_start?
54
+
55
+ @output << RawString.new( "<#{@tag_name}#{Promise.format_attributes(@attributes)}")
56
+ if @self_closing
57
+ @output << RawString.new( " />")
58
+ @output.newline if @newliney
59
+ else
60
+ @output << RawString.new( ">")
61
+ @output.indent
62
+ end
63
+ end
64
+
65
+ def _render_inside_tag
66
+ return if @self_closing
67
+ if @text
68
+ @output << @text
69
+ end
70
+ if @inside_renderer
71
+ @inside_renderer.call
72
+ end
73
+ end
74
+
75
+ def _render_close_tag
76
+ return if @self_closing
77
+
78
+ @output.undent
79
+ @output<< RawString.new("</#{@tag_name}>")
80
+ if @newliney
81
+ @output.newline
82
+ end
83
+ end
84
+
85
+ def method_missing(method_name, *args, &block)
86
+ method_name = method_name.to_s
87
+ if Erector::Widget.hyphenize_underscores
88
+ method_name = method_name.gsub(/_/, "-")
89
+ end
90
+
91
+ if method_name =~ /\!$/
92
+ id_str = method_name[0...-1]
93
+ raise ArgumentError, "setting id #{id_str} but id #{@attributes["id"]} already present" if @attributes["id"]
94
+ @attributes["id"] = id_str
95
+ else
96
+ if @attributes["class"]
97
+ @attributes["class"] += " "
98
+ else
99
+ @attributes["class"] = ""
100
+ end
101
+ @attributes["class"] += method_name.to_s
102
+ end
103
+
104
+ if block_given?
105
+ @inside_renderer = block
106
+ end
107
+
108
+ if args.last.is_a? Hash
109
+ attributes = args.pop
110
+ _set_attributes attributes
111
+ end
112
+
113
+ # todo: allow multiple args
114
+ # todo: allow promise args
115
+ @text = args.first
116
+
117
+ _render
118
+
119
+ self
120
+ end
121
+
122
+ # are these accessors necessary?
123
+
124
+ def _tag_name
125
+ @tag_name
126
+ end
127
+
128
+ def _attributes
129
+ @attributes
130
+ end
131
+
132
+ def _open_tag
133
+ @open_tag
134
+ end
135
+
136
+ def _close_tag
137
+ @close_tag
138
+ end
139
+
140
+ end
141
+ end
@@ -0,0 +1,44 @@
1
+ module Erector
2
+ module Rails
3
+ class FormBuilder
4
+ class_attribute :parent_builder_class
5
+ self.parent_builder_class = ActionView::Base.default_form_builder
6
+
7
+ def self.wrapping(parent_builder_class)
8
+ return self if parent_builder_class.nil?
9
+ Class.new(self).tap do |klass|
10
+ klass.parent_builder_class = parent_builder_class
11
+ end
12
+ end
13
+
14
+ attr_reader :parent, :template
15
+
16
+ def initialize(object_name, object, template, options, proc = nil)
17
+ @template = template
18
+ @parent = parent_builder_class.new(object_name, object, template, options, proc)
19
+ end
20
+
21
+ def method_missing(method_name, *args, &block)
22
+ if parent.respond_to?(method_name)
23
+ return_value = parent.send(method_name, *args, &block)
24
+ if return_value.is_a?(String)
25
+ template.concat(return_value)
26
+ nil
27
+ else
28
+ return_value
29
+ end
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def self.method_missing(method_name, *args, &block)
36
+ if parent_builder_class.respond_to?(method_name)
37
+ parent_builder_class.send(method_name, *args, &block)
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ module Erector
2
+ class Railtie < ::Rails::Railtie
3
+ # config.generators.template_engine :rb
4
+
5
+ # TODO: automatically add app directory to app.config.autoload_paths,
6
+ # so that Views::Foo::Bar autoloads, and 'require "views/foo/bar.html"'
7
+ # works. For now, you must add the following to config/application.rb:
8
+ #
9
+ # config.autoload_paths += %W(#{config.root}/app)
10
+ #
11
+ # (Maybe ::Rails.configuration.autoload_paths will work?)
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Erector
2
+ module Rails
3
+ class TemplateHandler
4
+ def call(template)
5
+ require_dependency template.identifier
6
+ widget_class_name = "views/#{template.identifier =~ %r(views/([^.]*)(\..*)?\.rb) && $1}".camelize
7
+ is_partial = File.basename(template.identifier) =~ /^_/
8
+ <<-SRC
9
+ Erector::Rails.render(#{widget_class_name}, self, local_assigns, #{!!is_partial})
10
+ SRC
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ ActionView::Template.register_template_handler :rb, Erector::Rails::TemplateHandler.new
@@ -0,0 +1,6 @@
1
+ require 'action_controller/metal/renderers'
2
+
3
+ ActionController.add_renderer :widget do |widget, options|
4
+ self.content_type ||= options[:content_type] || Mime[:html]
5
+ Erector::Rails.render(widget, view_context, {}, false, options)
6
+ end
@@ -0,0 +1,221 @@
1
+ require "erector/rails/template_handler"
2
+ require "erector/rails/railtie"
3
+ require "erector/rails/widget_renderer"
4
+ require "erector/rails/form_builder"
5
+
6
+ module Erector
7
+ module Rails
8
+ class << self
9
+ def should_assign?(name, widget_class, is_partial)
10
+ (!widget_class.ignore_extra_controller_assigns || widget_class.needs?(name)) &&
11
+ (!is_partial || widget_class.controller_assigns_propagate_to_partials)
12
+ end
13
+
14
+ def assigns_for(widget_class, view, local_assigns, is_partial)
15
+ assigns = {}
16
+
17
+ view.assigns.each do |name, value|
18
+ name = name.to_sym
19
+ assigns[name] = value if should_assign?(name, widget_class, is_partial)
20
+ end
21
+
22
+ assigns.merge!(filter_local_assigns_for_partial(widget_class, local_assigns)) if is_partial
23
+
24
+ assigns
25
+ end
26
+
27
+ def filter_local_assigns_for_partial(widget_class, local_assigns)
28
+ widget_class_variable_name = widget_class.name.underscore
29
+ widget_class_variable_name = $1 if widget_class_variable_name =~ %r{.*/(.*?)$}
30
+
31
+ local_assigns.reject do |name, value|
32
+ name == :object || name == widget_class_variable_name.to_sym
33
+ end
34
+ end
35
+
36
+ def render(widget, view, local_assigns = {}, is_partial = false, options = {})
37
+ widget = widget.new(assigns_for(widget, view, local_assigns, is_partial)) if widget.is_a?(Class)
38
+ view.with_output_buffer do
39
+ # Set parent and helpers to the view and use Rails's output buffer.
40
+ widget.to_html(options.merge(:helpers => view,
41
+ :parent => view,
42
+ :output => Output.new(:buffer => lambda { view.output_buffer })))
43
+ end
44
+ end
45
+
46
+ # Wrappers for rails helpers that produce markup. Erector needs to
47
+ # manually emit their result.
48
+ def def_simple_rails_helper(method_name)
49
+ module_eval <<-METHOD_DEF, __FILE__, __LINE__+1
50
+ def #{method_name}(*args, &block)
51
+ text helpers.#{method_name}(*args, &block)
52
+ end
53
+ METHOD_DEF
54
+ end
55
+
56
+ def def_rails_form_helper(method_name)
57
+ module_eval <<-METHOD_DEF, __FILE__, __LINE__+1
58
+ def #{method_name}(*args, &block)
59
+ options = args.extract_options!
60
+ args << options.merge(:builder => FormBuilder.wrapping(options[:builder]))
61
+ text helpers.#{method_name}(*args, &block)
62
+ end
63
+ METHOD_DEF
64
+ end
65
+ end
66
+
67
+ extend ActiveSupport::Concern
68
+
69
+ included do
70
+ # Often, large Rails applications will assign many controller instance variables.
71
+ # Sometimes these aren't used by a view: ApplicationController might assign
72
+ # variables that are used by many, but not all, views; and various other things
73
+ # may accumulate, especially if you've been using templating systems that are
74
+ # more forgiving than Erector. If you then migrate to Erector, you're stuck using
75
+ # no "needs" declaration at all, because it needs to contain every assigned
76
+ # variable, or Erector will raise an exception.
77
+ #
78
+ # If you set this to true (and it's inherited through to subclasses), however,
79
+ # then "needs" declarations on the widget will cause excess controller variables
80
+ # to be ignored -- they'll be unavailable to the widget (so 'needs' still means
81
+ # something), but they won't cause widget instantiation to fail, either. This
82
+ # can let a large Rails project transition to Erector more smoothly.
83
+ class_attribute :ignore_extra_controller_assigns
84
+ self.ignore_extra_controller_assigns = true
85
+
86
+ # In ERb templates, controller instance variables are available to any partial
87
+ # that gets rendered by the view, no matter how deeply-nested. This effectively
88
+ # makes controller instance variables "globals". In small view hierarchies this
89
+ # probably isn't an issue, but in large ones it can make debugging and
90
+ # reasoning about the code very difficult.
91
+ #
92
+ # If you set this to true (and it's inherited through to subclasses), then any
93
+ # widget that's getting rendered as a partial will only have access to locals
94
+ # explicitly passed to it (render :partial => ..., :locals => ...). (This
95
+ # doesn't change the behavior of widgets that are explicitly rendered, as they
96
+ # don't have this issue.) This can allow for cleaner encapsulation of partials,
97
+ # as they must be passed everything they use and can't rely on controller
98
+ # instance variables.
99
+ class_attribute :controller_assigns_propagate_to_partials
100
+ self.controller_assigns_propagate_to_partials = true
101
+ end
102
+
103
+ # We need to delegate #capture to helpers.capture, so that when
104
+ # the captured block is executed, both erector and Rails output
105
+ # from within the block go to the appropriate buffer.
106
+ def capture(&block)
107
+ if helpers.respond_to?(:capture)
108
+ raw(helpers.capture(&block).to_s)
109
+ else
110
+ super
111
+ end
112
+ end
113
+
114
+ # Wrap Rails' render method, to capture output from partials etc.
115
+ def render(*args, &block)
116
+ captured = helpers.capture do
117
+ helpers.concat(helpers.render(*args, &block))
118
+ helpers.output_buffer.to_s
119
+ end
120
+ rawtext(captured)
121
+ end
122
+
123
+ # Rails content_for is output if and only if no block given
124
+ def content_for(*args,&block)
125
+ if block
126
+ helpers.content_for(*args,&block)
127
+ else
128
+ rawtext(helpers.content_for(*args))
129
+ ''
130
+ end
131
+ end
132
+
133
+ # Delegate to non-markup producing helpers via method_missing,
134
+ # returning their result directly.
135
+ def method_missing(name, *args, &block)
136
+ if helpers.respond_to?(name)
137
+ helpers.send(name, *args, &block)
138
+ else
139
+ super
140
+ end
141
+ end
142
+
143
+ # Since we delegate method_missing to helpers, we need to delegate
144
+ # respond_to? as well.
145
+ def respond_to?(name)
146
+ super || helpers.respond_to?(name)
147
+ end
148
+
149
+ [
150
+ # UrlHelper
151
+ :link_to,
152
+ :button_to,
153
+ :link_to_unless_current,
154
+ :link_to_unless,
155
+ :link_to_if,
156
+ :mail_to,
157
+
158
+ # FormTagHelper
159
+ :form_tag,
160
+ :select_tag,
161
+ :text_field_tag,
162
+ :label_tag,
163
+ :hidden_field_tag,
164
+ :file_field_tag,
165
+ :password_field_tag,
166
+ :text_area_tag,
167
+ :check_box_tag,
168
+ :radio_button_tag,
169
+ :submit_tag,
170
+ :image_submit_tag,
171
+ :field_set_tag,
172
+
173
+ # FormHelper
174
+ :form_for,
175
+ :text_field,
176
+ :password_field,
177
+ :hidden_field,
178
+ :file_field,
179
+ :text_area,
180
+ :check_box,
181
+ :radio_button,
182
+
183
+ # AssetTagHelper
184
+ :auto_discovery_link_tag,
185
+ :javascript_include_tag,
186
+ :stylesheet_link_tag,
187
+ :favicon_link_tag,
188
+ :image_tag,
189
+
190
+ # ScriptaculousHelper
191
+ :sortable_element,
192
+ :sortable_element_js,
193
+ :text_field_with_auto_complete,
194
+ :draggable_element,
195
+ :drop_receiving_element,
196
+
197
+ # PrototypeHelper
198
+ :link_to_remote,
199
+ :button_to_remote,
200
+ :periodically_call_remote,
201
+ :form_remote_tag,
202
+ :submit_to_remote,
203
+ :update_page_tag,
204
+
205
+ # JavaScriptHelper
206
+ :javascript_tag,
207
+
208
+ # CsrfHelper
209
+ :csrf_meta_tag,
210
+ :csrf_meta_tags
211
+ ].each do |method_name|
212
+ def_simple_rails_helper(method_name)
213
+ end
214
+
215
+ [:form_for, :fields_for].each do |method_name|
216
+ def_rails_form_helper(method_name)
217
+ end
218
+
219
+ Erector::Widget.send :include, self
220
+ end
221
+ end