erector 0.2.83 → 0.3.105

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt CHANGED
@@ -3,19 +3,14 @@
3
3
  * http://erector.rubyforge.org
4
4
  * mailto:alex@pivotallabs.com
5
5
 
6
-
7
6
  == DESCRIPTION
8
7
 
9
8
  Erector is a Builder-like view framework, inspired by Markaby but overcoming
10
9
  some of its flaws. In Erector all views are objects, not template files,
11
10
  which allows the full power of object-oriented programming (inheritance,
12
- modular decomposition, encapsulation) in views.
13
-
14
- == FEATURES/PROBLEMS:
15
-
16
- While Erector is in use on several projects, it is still at a relatively
17
- early stage. In particular, not all features are documented (although
18
- the most important ones are).
11
+ modular decomposition, encapsulation) in views. See the rdoc for the
12
+ Erector::Widget class to learn how to make your own widgets, and visit the
13
+ project site at http://erector.rubyforge.org for more documentation.
19
14
 
20
15
  == SYNOPSIS
21
16
 
@@ -23,16 +18,26 @@ the most important ones are).
23
18
 
24
19
  class Hello < Erector::Widget
25
20
  def render
26
- div do
27
- text "Hello!"
21
+ html do
22
+ head do
23
+ title "Hello"
24
+ end
25
+ body do
26
+ text "Hello, "
27
+ b "world!", :class => 'big'
28
+ end
28
29
  end
29
30
  end
30
31
  end
31
32
 
33
+ Hello.new.to_s
34
+ => "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world!</b></body></html>"
35
+
32
36
  == REQUIREMENTS
33
37
 
34
- The gem depends on hoe and rake, although this is just for building
35
- erector (those who just use erector won't need these).
38
+ The gem depends on rake and treetop, although this is just for using the "erect" tool,
39
+ so deployed applications won't need these. Currently it also requires rails, although
40
+ we plan to separate the rails-dependent code so you can use Erector cleanly in a non-Rails app.
36
41
 
37
42
  == INSTALL
38
43
 
@@ -73,210 +78,3 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
73
78
  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
74
79
  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
75
80
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
76
-
77
- == MOTIVATION
78
-
79
- Why use Erector? This section will soon become a real essay or blog post,
80
- but briefly:
81
-
82
- * Markaby-style DOM Domain language
83
- * Your views are real classes, written in a real language, allowing
84
- * Functional decomposition
85
- * Inheritance
86
- * Composition, not partials
87
- * Well-defined semantics for variables, loops, blocks
88
- * Dependency injection via constructor params
89
- * As little magic as possible (e.g. no automagic copying of "assigns" variable from your controller)
90
- * yield works again (Markaby broke it)
91
- * Very testable
92
- * form_for ERB code is craaaaazy (not to mention the quagmire of options vs. htmloptions)
93
- * Output is streamed, improving performance over string copy
94
-
95
-
96
- == USER DOCUMENTATION
97
-
98
- The basic way to construct some HTML/XML with erector is to
99
- subclass Erector::Widget and implement a render method:
100
-
101
- class Hello < Erector::Widget
102
- def render
103
- html do
104
- head do
105
- title "Hello"
106
- end
107
- body do
108
- text "Hello, "
109
- b "world!"
110
- end
111
- end
112
- end
113
- end
114
- Hello.new.to_s
115
- #=> <html><head><title>Hello</title></head><body>Hello, <b>world!</b></body></html>
116
-
117
- Here are the basics:
118
-
119
- element('foo') # <foo></foo>
120
- empty_element('foo') # <foo />
121
- html # <html></html> (likewise for other common html tags)
122
- b "foo" # <b>foo</b>
123
- text 'foo' # foo
124
- text '&<>' # &amp;&lt;&gt; (what you generally want, especially
125
- # if the text came from the user or a database)
126
- text raw('&<>') # &<> (back door for raw html)
127
- rawtext('&<>') # &<> (alias for text(raw()))
128
- html { text foo } # <html>foo</html>
129
- html "foo" # <html>foo</html>
130
- html foo # <html>bar</html> (if the method foo returns the string "bar")
131
- a(:href => 'foo.html') # <a href="foo.html"></a>
132
- a(:href => 'q?a&b') # <a href="q?a&amp;b"></a> (quotes as for text)
133
- a(:href => raw('&amp;')) # <a href="&amp;"></a>
134
- text nbsp("Save Doc") # Save&#160;Doc (turns spaces into non-breaking spaces)
135
- instruct # <?xml version="1.0" encoding="UTF-8"?>
136
-
137
- TODO: document more obscure features like capture, Table, :class => ['one', 'two']
138
-
139
- === Using Erector from Ruby on Rails
140
-
141
- Your views are just ruby classes. Your controller instantiates the relevant view and calls render.
142
- For example:
143
-
144
- app/controllers/welcome_controller.rb:
145
-
146
- class WelcomeController < ApplicationController
147
-
148
- def index
149
- render :template => 'welcome/show'
150
- end
151
-
152
- end
153
-
154
- app/views/welcome/show.rb:
155
-
156
- class Views::Welcome::Show < Erector::Widget
157
-
158
- def render
159
- html do
160
- head do
161
- title "Welcome page"
162
- end
163
-
164
- body do
165
- p "Hello, world"
166
- end
167
- end
168
- end
169
-
170
- end
171
-
172
- For Rails to find these .rb files during render, you must first either copy the erector source to
173
- vendor/plugins/erector, or add `require 'erector'` to config/environment.rb. You also should delete (or rename)
174
- any other view files with the same base name that might be getting in the way.
175
-
176
- === Erect
177
-
178
- To make Rails integration as smooth as possible, we've written a little tool that will help you
179
- erect your existing Rails app. The "erect" tool will convert HTML or HTML/ERB into an Erector class.
180
- It ships as part of the Erector gem, so to try it out, install the gem, then run
181
-
182
- erect app/views/foos/*.html.erb
183
-
184
- or just
185
-
186
- erect app/views
187
-
188
- and then delete the original files when you're satisfied.
189
-
190
- Here's a little command-line howto for erecting a scaffold Rails app:
191
-
192
- rails foo
193
- cd foo
194
- script/generate scaffold post title:string body:text published:boolean
195
-
196
- erect app/views/posts/*.erb
197
-
198
- mate app/views/posts
199
- sleep 30 # this should be enough time for you to stop drooling
200
- rm app/views/posts/*.erb
201
- (echo ""; echo "require 'erector'") >> config/environment.rb
202
- rake db:migrate
203
- script/server
204
- open http://localhost:3000/posts
205
-
206
- === Layout Inheritance
207
-
208
- Erector replaces the typical Rails layout mechanism with a more natural construct, the use of inheritance. Want a common
209
- layout? Just implement a layout superclass and inherit from it. Implement render in the superclass and implement template
210
- methods in its subclasses. There's one trick you'll need to use this layout for non-erector templates. Here's an example.
211
-
212
- `application.rb` - The Erector layout superclass
213
-
214
- class Views::Layouts::Application < Erector::Widget
215
- attr_accessor :content
216
-
217
- def render
218
- html do
219
- head { } # head content here
220
- # body content here
221
- body do
222
- text content
223
- end
224
- end
225
- end
226
- end
227
-
228
- `application.mab` - The markaby template (adjust for other appropriately templating technologies)
229
-
230
- widget = Views::Layouts::Application.new(self)
231
- widget.content = content_for_layout
232
- self << widget.to_s
233
-
234
- Here the abstract layout widget is used in a concrete fashion by the template-based layout. Normally, the `content` method
235
- would be implemented by subclassing widgets, but the layout template sets it directly and then calls to_s on the layout widget.
236
- This allows the same layout to be shared in a backward compatible way.
237
-
238
- === Other ways to call erector
239
-
240
- Instead of subclassing Erector::Widget and implementing a render
241
- method, you can pass a block to Erector::Widget.new. For example:
242
-
243
- html = Erector::Widget.new do
244
- p "Hello, world!"
245
- end
246
- html.to_s #=> <p>Hello, world!</p>
247
-
248
- This lets you define mini-widgets on the fly.
249
-
250
- == DEVELOPER NOTES
251
-
252
- * Check out project from rubyforge:
253
-
254
- svn co svn+ssh://developername@rubyforge.org/var/svn/erector/trunk erector
255
-
256
- * Install gems:
257
-
258
- sudo gem install rake rails rspec rubyforge hpricot treetop
259
-
260
- * Run specs:
261
-
262
- rake
263
-
264
- * Check out the available rake tasks:
265
-
266
- rake -T
267
-
268
-
269
- === VERSIONING POLICY
270
-
271
- * Versions are of the form major.minor.tiny
272
- * Tiny revisions fix bugs or documentation
273
- * Tiny revisions are roughly equal to the svn revision number when they were made
274
- * Minor revisions add API calls, or change behavior
275
- * Minor revisions may also remove API calls, but these must be clearly announced in History.txt, with instructions on how to migrate
276
- * Major revisions are about marketing more than technical needs. We will stay in major version 0 until we're happy taking the "alpha" label off it. And if we ever do a major overhaul of the API, especially one that breaks backwards compatibility, we will probably want to increment the major version.
277
- * We will not be shy about incrementing version numbers -- if we end up going to version 0.943.67454 then so be it.
278
- * Developers should attempt to add lines in History.txt to reflect their checkins. These should reflect feature-level changes, not just one line per checkin. The top section of History.txt is used as the Release Notes by the "rake publish" task and will appear on the RubyForge file page.
279
- * Someone making a release must fill in the version number in History.txt as well as in Rakefile. Note that "rake release" requires a "VERSION=1.2.3" parameter to confirm you're releasing the version you intend.
280
- * As soon as a release is made and published, the publisher should go into History.txt and make a new section. Since we won't yet know what the next version will be called, the new section will be noted by a single "===" at the top of the file.
281
-
282
-
data/lib/erector.rb CHANGED
@@ -5,11 +5,12 @@ require "erector/extensions/action_view_template_handler"
5
5
  require "erector/extensions/object"
6
6
  require "erector/helpers"
7
7
  require "erector/html_parts"
8
+ require "erector/raw_string"
8
9
  require "erector/widget"
9
10
  require "erector/widgets"
10
11
 
11
12
  ##
12
13
  # Erector view framework
13
14
  module Erector
14
- VERSION = "0.2.83"
15
+ VERSION = "0.3.105"
15
16
  end
data/lib/erector/erect.rb CHANGED
@@ -90,6 +90,7 @@ module Erector
90
90
  say " --> #{e.filename}\n"
91
91
  rescue => e
92
92
  puts e
93
+ puts e.backtrace.join("\n\t")
93
94
  puts
94
95
  end
95
96
  end
@@ -109,6 +110,7 @@ module Erector
109
110
 
110
111
  if widget_class < Erector::Widget
111
112
  widget = widget_class.new
113
+ #todo: skip if it's missing a no-arg constructor
112
114
  dir = output_dir || File.dirname(file)
113
115
  FileUtils.mkdir_p(dir)
114
116
  output_file = "#{dir}/#{filename}.html"
@@ -121,7 +123,7 @@ module Erector
121
123
  end
122
124
  rescue => e
123
125
  puts e
124
- puts
126
+ puts e.backtrace.join("\n\t")
125
127
  end
126
128
  end
127
129
  end
@@ -1,22 +1,24 @@
1
- class ActionController::Base
2
- def render_widget(widget_class, assigns=@assigns)
3
- render :text => render_widget_to_string(widget_class, assigns)
4
- end
1
+ module ActionController #:nodoc:
2
+ class Base
3
+ def render_widget(widget_class, assigns=@assigns)
4
+ render :text => render_widget_to_string(widget_class, assigns)
5
+ end
5
6
 
6
- def render_widget_to_string(widget_class, assigns = @assigns)
7
- add_variables_to_assigns
8
- @rendered_widget = widget_class.new(@template, assigns.merge(:params => params))
9
- @rendered_widget.to_s
10
- end
11
-
12
- def render_with_erector_widget(*options, &block)
13
- if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
14
- render_widget widget, *options, &block
15
- else
16
- render_without_erector_widget *options, &block
7
+ def render_widget_to_string(widget_class, assigns = @assigns)
8
+ add_variables_to_assigns
9
+ @rendered_widget = widget_class.new(@template, assigns.merge(:params => params))
10
+ @rendered_widget.to_s
17
11
  end
18
- end
19
- alias_method_chain :render, :erector_widget
20
12
 
21
- attr_reader :rendered_widget
22
- end
13
+ def render_with_erector_widget(*options, &block)
14
+ if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
15
+ render_widget widget, *options, &block
16
+ else
17
+ render_without_erector_widget *options, &block
18
+ end
19
+ end
20
+ alias_method_chain :render, :erector_widget
21
+
22
+ attr_reader :rendered_widget
23
+ end
24
+ end
@@ -1,32 +1,57 @@
1
- module ActionView
2
- module TemplateHandlers
1
+ module ActionView #:nodoc:
2
+ module TemplateHandlers #:nodoc:
3
3
  class Erector
4
4
  def initialize(view)
5
5
  @view = view
6
6
  end
7
7
 
8
8
  def render(template, local_assigns)
9
- banana = @view.first_render
10
- paths = banana.split('/')
11
- dot_rb = /\.rb$/
12
- file_path = "#{RAILS_ROOT}/app/views/#{banana}.rb"
13
- if File.exists?(file_path)
14
- require_dependency file_path
9
+
10
+ if template =~ /class ([\w:_]+)/
11
+ classname_from_template = $1
12
+ template_path = Inflector::underscore(classname_from_template)
15
13
  else
16
- partial_file_path = file_path.gsub(/\/([^\/]*)$/, '/_\1')
17
- if File.exists?(partial_file_path)
18
- require_dependency partial_file_path
14
+ template_path = @view.first_render
15
+ end
16
+
17
+ paths = template_path.split('/')
18
+ if paths.first == 'views'
19
+ paths.shift
20
+ template_path = paths.join('/')
21
+ end
22
+
23
+ found = false
24
+ @view.view_paths.each_with_index do |view_path, i|
25
+ full_path = "#{view_path}/#{template_path}.rb"
26
+ if File.exists?(full_path)
27
+ require_dependency full_path
19
28
  else
20
- return
29
+ partial_file_path = full_path.gsub(/\/([^\/]*)$/, '/_\1')
30
+ if File.exists?(partial_file_path)
31
+ require_dependency partial_file_path
32
+ found = true
33
+ break
34
+ end
21
35
  end
22
36
  end
37
+ return unless found
38
+
39
+ dot_rb = /\.rb$/
23
40
  widget_class = paths.inject(Views) do |current_module, node|
24
41
  current_module.const_get(node.gsub(dot_rb, '').camelize)
25
42
  end
26
43
 
27
44
  rendered_widget = widget_class.new(@view, @view.assigns)
45
+ assign_locals(rendered_widget, local_assigns)
28
46
  rendered_widget.to_s
29
47
  end
48
+
49
+ private
50
+ # TODO: move into widget
51
+ def assign_locals(widget, local_assigns)
52
+ widget.assign_locals(local_assigns)
53
+ end
54
+
30
55
  end
31
56
  end
32
57
  end
@@ -2,4 +2,17 @@ class Object
2
2
  def metaclass
3
3
  class << self; self; end
4
4
  end
5
- end
5
+
6
+ def html_escape
7
+ return CGI.escapeHTML(to_s)
8
+ end
9
+
10
+ def html_unescape
11
+ CGI.unescapeHTML(to_s)
12
+ end
13
+
14
+ # OMGWTF :-)
15
+ def escape_single_quotes
16
+ self.gsub(/[']/, '\\\\\'')
17
+ end
18
+ end
@@ -1,41 +1,80 @@
1
1
  module Erector
2
+
3
+ # Wrappers or replacements for the Rails helpers that are available from Rails views
2
4
  module Helpers
3
5
 
6
+ # helpers returning raw text
4
7
  [
5
8
  :image_tag,
6
9
  :javascript_include_tag,
10
+ :define_javascript_functions,
7
11
  :stylesheet_link_tag,
8
12
  :sortable_element,
9
- :sortable_element_js
13
+ :sortable_element_js,
14
+ :text_field_with_auto_complete,
10
15
  ].each do |helper_name|
11
16
  define_method helper_name do |*args|
12
- text raw(helpers.send(helper_name, *args))
17
+ begin
18
+ text raw(helpers.send(helper_name, *args))
19
+ rescue => e
20
+ puts e.backtrace.join("\n\t")
21
+ raise e
22
+ end
13
23
  end
14
24
  end
15
25
 
26
+ # helpers returning raw text whose first parameter is HTML escaped
16
27
  [
17
- :link_to_function,
18
28
  :link_to,
19
29
  :link_to_remote,
20
- :mail_to
21
- ].each do |link_helper|
22
- define_method link_helper do |link_text, *args|
23
- text raw(helpers.send(link_helper, h(link_text), *args))
30
+ :mail_to,
31
+ :button_to,
32
+ :submit_tag,
33
+ ].each do |helper_name|
34
+
35
+ method_def =<<-METHOD_DEF
36
+ def #{helper_name}(link_text, *args, &block)
37
+ text raw(helpers.#{helper_name}(h(link_text), *args, &block))
24
38
  end
39
+ METHOD_DEF
40
+ eval(method_def)
25
41
  end
26
42
 
27
43
  def error_messages_for(*args)
28
- fake_erbout do
29
- helpers.error_messages_for(*args)
30
- end
44
+ text raw(helpers.error_messages_for(*args))
31
45
  end
32
46
 
33
- def form_for(*args, &block)
34
- fake_erbout do
35
- helpers.form_for(*args, &block)
47
+ # return text, take block
48
+ [
49
+ :link_to_function,
50
+ :form_for,
51
+ :form_tag,
52
+ :text_field_tag,
53
+ :password_field_tag,
54
+ :check_box_tag
55
+ ].each do |method_to_proxy_with_block|
56
+ method_def =<<-METHOD_DEF
57
+ def #{method_to_proxy_with_block}(*args, &block)
58
+ text raw(helpers.#{method_to_proxy_with_block}(*args, &block))
36
59
  end
60
+ METHOD_DEF
61
+ eval(method_def)
37
62
  end
38
63
 
64
+ # render text, take block
65
+ [
66
+ :error_messages_for,
67
+ ].each do |method_to_proxy_with_block|
68
+ method_def =<<-METHOD_DEF
69
+ def #{method_to_proxy_with_block}(*args, &block)
70
+ fake_erbout do
71
+ helpers.#{method_to_proxy_with_block}(*args, &block)
72
+ end
73
+ end
74
+ METHOD_DEF
75
+ eval(method_def)
76
+ end
77
+
39
78
  def javascript_include_merged(key)
40
79
  helpers.javascript_include_merged(key)
41
80
  end
@@ -51,6 +90,10 @@ module Erector
51
90
  def session
52
91
  helpers.controller.session
53
92
  end
93
+
94
+ def controller
95
+ helpers.controller
96
+ end
54
97
 
55
98
  def cycle(*args)
56
99
  helpers.cycle(*args)
@@ -68,4 +111,4 @@ module Erector
68
111
  helpers.pluralize(*args)
69
112
  end
70
113
  end
71
- end
114
+ end