honkster-erector 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +116 -0
- data/VERSION.yml +4 -0
- data/bin/erector +14 -0
- data/lib/erector.rb +34 -0
- data/lib/erector/abstract_widget.rb +172 -0
- data/lib/erector/after_initialize.rb +34 -0
- data/lib/erector/caching.rb +93 -0
- data/lib/erector/convenience.rb +58 -0
- data/lib/erector/dependencies.rb +24 -0
- data/lib/erector/dependency.rb +30 -0
- data/lib/erector/erect/erect.rb +160 -0
- data/lib/erector/erect/erected.rb +75 -0
- data/lib/erector/erect/indenting.rb +36 -0
- data/lib/erector/erect/rhtml.treetop +233 -0
- data/lib/erector/errors.rb +12 -0
- data/lib/erector/extensions/hash.rb +21 -0
- data/lib/erector/extensions/object.rb +18 -0
- data/lib/erector/externals.rb +97 -0
- data/lib/erector/html.rb +352 -0
- data/lib/erector/inline.rb +37 -0
- data/lib/erector/jquery.rb +36 -0
- data/lib/erector/mixin.rb +12 -0
- data/lib/erector/needs.rb +94 -0
- data/lib/erector/output.rb +117 -0
- data/lib/erector/rails.rb +27 -0
- data/lib/erector/rails/extensions/action_controller.rb +16 -0
- data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
- data/lib/erector/rails/extensions/rails_widget.rb +126 -0
- data/lib/erector/rails/rails_form_builder.rb +24 -0
- data/lib/erector/rails/rails_version.rb +6 -0
- data/lib/erector/rails/template_handlers/ert_handler.rb +32 -0
- data/lib/erector/rails/template_handlers/rb_handler.rb +52 -0
- data/lib/erector/raw_string.rb +8 -0
- data/lib/erector/sass.rb +22 -0
- data/lib/erector/unicode.rb +18185 -0
- data/lib/erector/unicode_builder.rb +67 -0
- data/lib/erector/version.rb +12 -0
- data/lib/erector/widget.rb +54 -0
- data/lib/erector/widgets.rb +6 -0
- data/lib/erector/widgets/environment_badge.rb +29 -0
- data/lib/erector/widgets/external_renderer.rb +51 -0
- data/lib/erector/widgets/field_table.rb +110 -0
- data/lib/erector/widgets/form.rb +30 -0
- data/lib/erector/widgets/page.rb +165 -0
- data/lib/erector/widgets/table.rb +104 -0
- data/rails/init.rb +4 -0
- data/spec/erect/erect_rails_spec.rb +114 -0
- data/spec/erect/erect_spec.rb +175 -0
- data/spec/erect/erected_spec.rb +164 -0
- data/spec/erect/rhtml_parser_spec.rb +361 -0
- data/spec/erector/caching_spec.rb +269 -0
- data/spec/erector/convenience_spec.rb +259 -0
- data/spec/erector/dependency_spec.rb +67 -0
- data/spec/erector/externals_spec.rb +236 -0
- data/spec/erector/html_spec.rb +509 -0
- data/spec/erector/indentation_spec.rb +211 -0
- data/spec/erector/inline_spec.rb +94 -0
- data/spec/erector/jquery_spec.rb +35 -0
- data/spec/erector/mixin_spec.rb +65 -0
- data/spec/erector/needs_spec.rb +120 -0
- data/spec/erector/output_spec.rb +199 -0
- data/spec/erector/sample-file.txt +1 -0
- data/spec/erector/sass_spec.rb +33 -0
- data/spec/erector/unicode_builder_spec.rb +75 -0
- data/spec/erector/widget_spec.rb +250 -0
- data/spec/erector/widgets/field_table_spec.rb +133 -0
- data/spec/erector/widgets/form_spec.rb +31 -0
- data/spec/erector/widgets/page_spec.rb +85 -0
- data/spec/erector/widgets/table_spec.rb +99 -0
- data/spec/spec_helper.rb +95 -0
- metadata +191 -0
data/README.txt
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
= Erector
|
2
|
+
|
3
|
+
* http://erector.rubyforge.org
|
4
|
+
* mailto:erector@googlegroups.com
|
5
|
+
* http://www.pivotaltracker.com/projects/482
|
6
|
+
|
7
|
+
== DESCRIPTION
|
8
|
+
|
9
|
+
Erector is a Builder-like view framework, inspired by Markaby but overcoming
|
10
|
+
some of its flaws. In Erector all views are objects, not template files,
|
11
|
+
which allows the full power of object-oriented programming (inheritance,
|
12
|
+
modular decomposition, encapsulation) in views. See the rdoc for the
|
13
|
+
Erector::Widget class to learn how to make your own widgets, and visit the
|
14
|
+
project site at http://erector.rubyforge.org for more documentation.
|
15
|
+
|
16
|
+
No, seriously, we've got hella docs at http://erector.rubyforge.org -- go
|
17
|
+
check it out.
|
18
|
+
|
19
|
+
== SYNOPSIS
|
20
|
+
|
21
|
+
require 'erector'
|
22
|
+
|
23
|
+
class Hello < Erector::Widget
|
24
|
+
def content
|
25
|
+
html do
|
26
|
+
head do
|
27
|
+
title "Hello"
|
28
|
+
end
|
29
|
+
body do
|
30
|
+
text "Hello, "
|
31
|
+
b @target, :class => 'big'
|
32
|
+
text "!"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Hello.new(:target => 'world').to_html
|
39
|
+
=> "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world</b>!</body></html>"
|
40
|
+
|
41
|
+
include Erector::Mixin
|
42
|
+
erector { div "love", :class => "big" }
|
43
|
+
=> "<div class=\"big\">love</div>"
|
44
|
+
|
45
|
+
== REQUIREMENTS
|
46
|
+
|
47
|
+
The gem depends on rake and treetop, although this is just for using the command-line tool,
|
48
|
+
so deployed applications won't need these. The Rails-dependent code is now separated so
|
49
|
+
you can use Erector cleanly in a non-Rails app.
|
50
|
+
|
51
|
+
== INSTALL
|
52
|
+
|
53
|
+
To install as a gem:
|
54
|
+
|
55
|
+
* sudo gem install erector
|
56
|
+
|
57
|
+
Then add "require 'erector'" to any files which need erector.
|
58
|
+
|
59
|
+
To install as a Rails plugin:
|
60
|
+
|
61
|
+
* Copy the erector source to vendor/plugins/erector in your Rails directory.
|
62
|
+
|
63
|
+
When installing this way, erector is automatically available to your Rails code
|
64
|
+
(no require directive is needed).
|
65
|
+
|
66
|
+
== TESTS
|
67
|
+
|
68
|
+
Three spec rake tasks are provided: spec:core (core functionality),
|
69
|
+
spec:erect (the erector command line tool), and spec:rails (rails integration).
|
70
|
+
You do not need to have Rails installed to run the latter two; they will clone
|
71
|
+
the rails git repository and set it up for testing automatically. You can test
|
72
|
+
against a different version of Rails by changing the constants in
|
73
|
+
lib/erector/rails/rails_version.rb
|
74
|
+
|
75
|
+
'rake spec' will run the complete set of specs.
|
76
|
+
|
77
|
+
== CREDITS:
|
78
|
+
|
79
|
+
Core Team:
|
80
|
+
* Alex Chaffee
|
81
|
+
* Brian Takita
|
82
|
+
|
83
|
+
Special Thanks To:
|
84
|
+
* Abby (Chaffee's muse & Best friend)
|
85
|
+
* Jim Kingdon
|
86
|
+
* Jeff Dean
|
87
|
+
* John Firebaugh
|
88
|
+
* Nathan Sobo
|
89
|
+
* Nick Kallen
|
90
|
+
* Alon Salant
|
91
|
+
* Andy Peterson
|
92
|
+
|
93
|
+
== LICENSE:
|
94
|
+
|
95
|
+
(The MIT License)
|
96
|
+
|
97
|
+
Copyright (c) 2007-2010 Pivotal Labs and the Erector Project
|
98
|
+
|
99
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
100
|
+
a copy of this software and associated documentation files (the
|
101
|
+
"Software"), to deal in the Software without restriction, including
|
102
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
103
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
104
|
+
permit persons to whom the Software is furnished to do so, subject to
|
105
|
+
the following conditions:
|
106
|
+
|
107
|
+
The above copyright notice and this permission notice shall be
|
108
|
+
included in all copies or substantial portions of the Software.
|
109
|
+
|
110
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
111
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
112
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
113
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
114
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
115
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
116
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/VERSION.yml
ADDED
data/bin/erector
ADDED
data/lib/erector.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Erector
|
2
|
+
end
|
3
|
+
|
4
|
+
require "cgi"
|
5
|
+
require "yaml"
|
6
|
+
begin
|
7
|
+
require "sass"
|
8
|
+
rescue LoadError => e
|
9
|
+
# oh well, no Sass
|
10
|
+
end
|
11
|
+
|
12
|
+
require "erector/errors"
|
13
|
+
require "erector/extensions/object"
|
14
|
+
require "erector/extensions/hash"
|
15
|
+
require "erector/raw_string"
|
16
|
+
require "erector/dependencies"
|
17
|
+
require "erector/dependency"
|
18
|
+
require "erector/externals"
|
19
|
+
require "erector/output"
|
20
|
+
require "erector/caching"
|
21
|
+
require "erector/after_initialize"
|
22
|
+
require "erector/needs"
|
23
|
+
require "erector/html"
|
24
|
+
require "erector/convenience"
|
25
|
+
require "erector/jquery"
|
26
|
+
require "erector/sass"
|
27
|
+
require "erector/abstract_widget"
|
28
|
+
require "erector/widget"
|
29
|
+
|
30
|
+
require "erector/inline"
|
31
|
+
require "erector/unicode"
|
32
|
+
require "erector/widgets"
|
33
|
+
require "erector/version"
|
34
|
+
require "erector/mixin"
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Erector
|
2
|
+
|
3
|
+
# Abstract base class for Widget. This pattern allows Widget to include lots of nicely organized modules and still
|
4
|
+
# have proper semantics for "super" in subclasses. See the rdoc for Widget for the list of all the included modules.
|
5
|
+
class AbstractWidget
|
6
|
+
@@prettyprint_default = false
|
7
|
+
def prettyprint_default
|
8
|
+
@@prettyprint_default
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.prettyprint_default
|
12
|
+
@@prettyprint_default
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.prettyprint_default=(enabled)
|
16
|
+
@@prettyprint_default = enabled
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.inline(*args, &block)
|
20
|
+
Class.new(self) do
|
21
|
+
include Erector::Inline
|
22
|
+
end.new(*args, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
[:helpers, :assigns, :output, :parent, :block].each do |attr|
|
26
|
+
class_eval(<<-SRC, __FILE__, __LINE__ + 1)
|
27
|
+
def #{attr}
|
28
|
+
@_#{attr}
|
29
|
+
end
|
30
|
+
SRC
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(assigns = {}, &block)
|
34
|
+
unless assigns.is_a? Hash
|
35
|
+
raise "Erector widgets are initialized with only a parameter hash. (Other parameters are passed to to_html, or the #widget method.)"
|
36
|
+
end
|
37
|
+
|
38
|
+
@_assigns = assigns
|
39
|
+
|
40
|
+
assigns.each do |name, value|
|
41
|
+
instance_variable_set(name.to_s[0..0] == '@' ? name : "@#{name}", value)
|
42
|
+
end
|
43
|
+
|
44
|
+
@_parent = eval("self", block.binding) if block
|
45
|
+
@_block = block
|
46
|
+
end
|
47
|
+
|
48
|
+
# Entry point for rendering a widget (and all its children). This method
|
49
|
+
# creates a new output string (if necessary), calls this widget's #content
|
50
|
+
# method and returns the string.
|
51
|
+
#
|
52
|
+
# Options:
|
53
|
+
# output:: the string to output to. Default: a new empty string
|
54
|
+
# prettyprint:: whether Erector should add newlines and indentation.
|
55
|
+
# Default: the value of prettyprint_default (which is false
|
56
|
+
# by default).
|
57
|
+
# indentation:: the amount of spaces to indent. Ignored unless prettyprint
|
58
|
+
# is true.
|
59
|
+
# max_length:: preferred maximum length of a line. Line wraps will only
|
60
|
+
# occur at space characters, so a long word may end up creating
|
61
|
+
# a line longer than this. If nil (default), then there is no
|
62
|
+
# arbitrary limit to line lengths, and only internal newline
|
63
|
+
# characters and prettyprinting will determine newlines in the
|
64
|
+
# output.
|
65
|
+
# helpers:: a helpers object containing utility methods. Usually this is a
|
66
|
+
# Rails view object.
|
67
|
+
# content_method_name:: in case you want to call a method other than
|
68
|
+
# #content, pass its name in here.
|
69
|
+
def to_html(options = {})
|
70
|
+
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
|
71
|
+
_render(options).to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
# alias for #to_html
|
75
|
+
# @deprecated Please use {#to_html} instead
|
76
|
+
def to_s(*args)
|
77
|
+
unless defined? @@already_warned_to_s
|
78
|
+
$stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
|
79
|
+
@@already_warned_to_s = true
|
80
|
+
end
|
81
|
+
to_html(*args)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Entry point for rendering a widget (and all its children). Same as #to_html
|
85
|
+
# only it returns an array, for theoretical performance improvements when using a
|
86
|
+
# Rack server (like Sinatra or Rails Metal).
|
87
|
+
#
|
88
|
+
# # Options: see #to_html
|
89
|
+
def to_a(options = {})
|
90
|
+
_render(options).to_a
|
91
|
+
end
|
92
|
+
|
93
|
+
# Template method which must be overridden by all widget subclasses.
|
94
|
+
# Inside this method you call the magic #element methods which emit HTML
|
95
|
+
# and text to the output string. If you call "super" (or don't override
|
96
|
+
# +content+, or explicitly call "call_block") then your widget will
|
97
|
+
# execute the block that was passed into its constructor. The semantics of
|
98
|
+
# this block are confusing; make sure to read the rdoc for Erector#call_block
|
99
|
+
def content
|
100
|
+
call_block
|
101
|
+
end
|
102
|
+
|
103
|
+
# When this method is executed, the default block that was passed in to
|
104
|
+
# the widget's constructor will be executed. The semantics of this
|
105
|
+
# block -- that is, what "self" is, and whether it has access to
|
106
|
+
# Erector methods like "div" and "text", and the widget's instance
|
107
|
+
# variables -- can be quite confusing. The rule is, most of the time the
|
108
|
+
# block is evaluated using "call" or "yield", which means that its scope
|
109
|
+
# is that of the caller. So if that caller is not an Erector widget, it
|
110
|
+
# will *not* have access to the Erector methods, but it *will* have access
|
111
|
+
# to instance variables and methods of the calling object.
|
112
|
+
#
|
113
|
+
# If you want this block to have access to Erector methods then use
|
114
|
+
# Erector::Inline#content or Erector#inline.
|
115
|
+
def call_block
|
116
|
+
@_block.call(self) if @_block
|
117
|
+
end
|
118
|
+
|
119
|
+
# Emits a (nested) widget onto the current widget's output stream. Accepts
|
120
|
+
# either a class or an instance. If the first argument is a class, then
|
121
|
+
# the second argument is a hash used to populate its instance variables.
|
122
|
+
# If the first argument is an instance then the hash must be unspecified
|
123
|
+
# (or empty). If a block is passed to this method, then it gets set as the
|
124
|
+
# rendered widget's block.
|
125
|
+
#
|
126
|
+
# This is the preferred way to call one widget from inside another. This
|
127
|
+
# method assures that the same output string is used, which gives better
|
128
|
+
# performance than using +capture+ or +to_html+.
|
129
|
+
def widget(target, assigns = {}, options = {}, &block)
|
130
|
+
if target.is_a? Class
|
131
|
+
target.new(assigns, &block)._render_via(self, options)
|
132
|
+
else
|
133
|
+
unless assigns.empty?
|
134
|
+
raise "Unexpected second parameter. Did you mean to pass in assigns when you instantiated the #{target.class.to_s}?"
|
135
|
+
end
|
136
|
+
target._render_via(self, options, &block)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Creates a whole new output string, executes the block, then converts the
|
141
|
+
# output string to a string and returns it as raw text. If at all possible
|
142
|
+
# you should avoid this method since it hurts performance, and use +widget+
|
143
|
+
# instead.
|
144
|
+
def capture
|
145
|
+
original, @_output = output, Output.new
|
146
|
+
yield
|
147
|
+
original.widgets.concat(output.widgets) # todo: test!!!
|
148
|
+
output.to_s
|
149
|
+
ensure
|
150
|
+
@_output = original
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
def _render(options = {}, &block)
|
155
|
+
@_block = block if block
|
156
|
+
@_parent = options[:parent] || parent
|
157
|
+
@_helpers = options[:helpers] || parent
|
158
|
+
@_output = options[:output]
|
159
|
+
@_output = Output.new(options) unless output.is_a?(Output)
|
160
|
+
|
161
|
+
output.widgets << self.class
|
162
|
+
send(options[:content_method_name] || :content)
|
163
|
+
output
|
164
|
+
end
|
165
|
+
|
166
|
+
def _render_via(parent, options = {}, &block)
|
167
|
+
_render(options.merge(:parent => parent,
|
168
|
+
:output => parent.output,
|
169
|
+
:helpers => parent.helpers), &block)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Erector
|
2
|
+
module AfterInitialize
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def after_initialize(instance=nil, &blk)
|
9
|
+
if blk
|
10
|
+
after_initialize_parts << blk
|
11
|
+
elsif instance
|
12
|
+
if superclass.respond_to?(:after_initialize)
|
13
|
+
superclass.after_initialize instance
|
14
|
+
end
|
15
|
+
after_initialize_parts.each do |part|
|
16
|
+
instance.instance_eval &part
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise ArgumentError, "You must provide either an instance or a block"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def after_initialize_parts
|
25
|
+
@after_initialize_parts ||= []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(*args, &blk)
|
30
|
+
super
|
31
|
+
self.class.after_initialize self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Erector
|
2
|
+
class Cache
|
3
|
+
def initialize
|
4
|
+
@stores = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def store_for(klass)
|
8
|
+
@stores[klass] ||= Hash.new {|h,k| h[k] = {}}
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(*args)
|
12
|
+
value = args.pop
|
13
|
+
klass = args.shift
|
14
|
+
params = args.first.is_a?(Hash) ? args.first : {}
|
15
|
+
content_method = args.last.is_a?(Symbol) ? args.last : nil
|
16
|
+
store_for(klass)[key(params)][content_method] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](klass, params = {}, content_method = nil)
|
20
|
+
store_for(klass)[key(params)][content_method]
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(klass, params = {})
|
24
|
+
store_for(klass).delete(key(params))
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete_all(klass)
|
28
|
+
@stores.delete(klass)
|
29
|
+
end
|
30
|
+
|
31
|
+
# convert hash-key to array-key for compatibility with 1.8.6
|
32
|
+
def key(params)
|
33
|
+
params.to_a
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Caching
|
38
|
+
def self.included(base)
|
39
|
+
base.extend ClassMethods
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def cacheable(value = true)
|
44
|
+
@cachable = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def cachable(value = true)
|
48
|
+
@cachable = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def cachable?
|
52
|
+
if @cachable.nil?
|
53
|
+
superclass.respond_to?(:cachable?) && superclass.cachable?
|
54
|
+
else
|
55
|
+
@cachable
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache
|
60
|
+
@@cache ||= nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def cache=(c)
|
64
|
+
@@cache = c
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def cache
|
69
|
+
self.class.cache
|
70
|
+
end
|
71
|
+
|
72
|
+
def should_cache?
|
73
|
+
cache && block.nil? && self.class.cachable?
|
74
|
+
end
|
75
|
+
|
76
|
+
def _render(options = {})
|
77
|
+
if should_cache?
|
78
|
+
cache[self.class, assigns, options[:content_method_name]] ||= super
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def _render_via(parent, options = {})
|
85
|
+
if should_cache?
|
86
|
+
parent.output << cache[self.class, assigns, options[:content_method_name]] ||= parent.capture { super }
|
87
|
+
parent.output.widgets << self.class # todo: test!!!
|
88
|
+
else
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|