hobo 1.0.3 → 1.1.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +0 -66
- data/README +3 -0
- data/Rakefile +7 -7
- data/doctest/model.rdoctest +0 -2
- data/doctest/multi_model_forms.rdoctest +0 -2
- data/doctest/scopes.rdoctest +13 -21
- data/lib/active_record/association_collection.rb +7 -16
- data/lib/hobo.rb +10 -65
- data/lib/hobo/accessible_associations.rb +1 -5
- data/lib/hobo/authentication_support.rb +1 -1
- data/lib/hobo/controller.rb +5 -5
- data/lib/hobo/hobo_helper.rb +0 -84
- data/lib/hobo/lifecycles/lifecycle.rb +37 -31
- data/lib/hobo/lifecycles/transition.rb +1 -2
- data/lib/hobo/model.rb +13 -21
- data/lib/hobo/model_controller.rb +8 -8
- data/lib/hobo/rapid_helper.rb +12 -1
- data/lib/hobo/scopes/automatic_scopes.rb +26 -13
- data/lib/hobo/scopes/named_scope_extensions.rb +16 -28
- data/lib/hobo/user_controller.rb +1 -0
- data/lib/hobo/view_hints.rb +1 -5
- data/rails_generators/hobo/templates/initializer.rb +1 -1
- data/rails_generators/hobo_front_controller/templates/summary.dryml +4 -2
- data/rails_generators/hobo_model/hobo_model_generator.rb +12 -0
- data/rails_generators/hobo_model/templates/model.rb +9 -2
- data/rails_generators/hobo_rapid/templates/hobo-rapid.js +98 -23
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +1 -1
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +3 -1
- data/{dryml_generators → rapid_generators}/rapid/cards.dryml.erb +0 -0
- data/{dryml_generators → rapid_generators}/rapid/forms.dryml.erb +0 -0
- data/{dryml_generators → rapid_generators}/rapid/pages.dryml.erb +1 -1
- data/taglibs/rapid.dryml +2 -0
- data/taglibs/rapid_core.dryml +10 -9
- data/taglibs/rapid_forms.dryml +70 -35
- data/taglibs/rapid_lifecycles.dryml +17 -4
- data/taglibs/rapid_plus.dryml +3 -3
- data/taglibs/rapid_summary.dryml +11 -0
- data/taglibs/rapid_user_pages.dryml +39 -28
- data/tasks/hobo_tasks.rake +1 -1
- metadata +45 -61
- data/hobo.gemspec +0 -226
- data/lib/hobo/dryml.rb +0 -188
- data/lib/hobo/dryml/dryml_builder.rb +0 -140
- data/lib/hobo/dryml/dryml_doc.rb +0 -159
- data/lib/hobo/dryml/dryml_generator.rb +0 -263
- data/lib/hobo/dryml/dryml_support_controller.rb +0 -13
- data/lib/hobo/dryml/parser.rb +0 -3
- data/lib/hobo/dryml/parser/attribute.rb +0 -41
- data/lib/hobo/dryml/parser/base_parser.rb +0 -254
- data/lib/hobo/dryml/parser/document.rb +0 -57
- data/lib/hobo/dryml/parser/element.rb +0 -27
- data/lib/hobo/dryml/parser/elements.rb +0 -45
- data/lib/hobo/dryml/parser/source.rb +0 -58
- data/lib/hobo/dryml/parser/text.rb +0 -13
- data/lib/hobo/dryml/parser/tree_parser.rb +0 -67
- data/lib/hobo/dryml/part_context.rb +0 -137
- data/lib/hobo/dryml/scoped_variables.rb +0 -42
- data/lib/hobo/dryml/tag_parameters.rb +0 -36
- data/lib/hobo/dryml/taglib.rb +0 -123
- data/lib/hobo/dryml/template.rb +0 -1019
- data/lib/hobo/dryml/template_environment.rb +0 -613
- data/lib/hobo/dryml/template_handler.rb +0 -187
- data/lib/hobo/static_tags +0 -98
- data/taglibs/core.dryml +0 -104
@@ -1,36 +0,0 @@
|
|
1
|
-
module Hobo
|
2
|
-
|
3
|
-
module Dryml
|
4
|
-
|
5
|
-
class NoParameterError < RuntimeError; end
|
6
|
-
|
7
|
-
class TagParameters < Hash
|
8
|
-
|
9
|
-
def initialize(parameters, exclude_names=nil)
|
10
|
-
if exclude_names.blank?
|
11
|
-
update(parameters)
|
12
|
-
else
|
13
|
-
parameters.each_pair { |k, v| self[k] = v unless k.in?(exclude_names) }
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def method_missing(name, default_content="")
|
18
|
-
if name.to_s =~ /\?$/
|
19
|
-
has_key?(name.to_s[0..-2].to_sym)
|
20
|
-
else
|
21
|
-
self[name]._?.call(default_content) || ""
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
undef_method :default
|
26
|
-
|
27
|
-
# Question: does this do anything? -Tom
|
28
|
-
def [](param_name)
|
29
|
-
fetch(param_name, nil)
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
data/lib/hobo/dryml/taglib.rb
DELETED
@@ -1,123 +0,0 @@
|
|
1
|
-
module Hobo
|
2
|
-
|
3
|
-
module Dryml
|
4
|
-
|
5
|
-
class Taglib
|
6
|
-
|
7
|
-
@cache = {}
|
8
|
-
|
9
|
-
class << self
|
10
|
-
|
11
|
-
def get(options)
|
12
|
-
src_file = taglib_filename(options)
|
13
|
-
taglib = @cache[src_file]
|
14
|
-
if taglib
|
15
|
-
taglib.reload
|
16
|
-
else
|
17
|
-
taglib = Taglib.new(src_file)
|
18
|
-
@cache[src_file] = taglib
|
19
|
-
end
|
20
|
-
taglib
|
21
|
-
end
|
22
|
-
|
23
|
-
def clear_cache
|
24
|
-
@cache = {}
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def taglib_filename(options)
|
30
|
-
plugin = options[:plugin]
|
31
|
-
base = if plugin == "hobo"
|
32
|
-
"#{HOBO_ROOT}/taglibs"
|
33
|
-
elsif plugin
|
34
|
-
"#{RAILS_ROOT}/vendor/plugins/#{plugin}/taglibs"
|
35
|
-
elsif options[:src] =~ /\//
|
36
|
-
"#{RAILS_ROOT}/app/views"
|
37
|
-
elsif options[:template_dir] =~ /^#{HOBO_ROOT}/
|
38
|
-
options[:template_dir]
|
39
|
-
else
|
40
|
-
"#{RAILS_ROOT}/#{options[:template_dir].gsub(/^\//, '')}" # remove leading / if there is one
|
41
|
-
end
|
42
|
-
|
43
|
-
src = options[:src] || plugin
|
44
|
-
filename = "#{base}/#{src}.dryml"
|
45
|
-
raise DrymlException, "No such taglib: #{base} #{options.inspect} #{filename}" unless File.exists?(filename)
|
46
|
-
filename
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
def initialize(src_file)
|
52
|
-
@src_file = src_file
|
53
|
-
load
|
54
|
-
end
|
55
|
-
|
56
|
-
def reload
|
57
|
-
load if File.mtime(@src_file) > @last_load_time
|
58
|
-
end
|
59
|
-
|
60
|
-
def load
|
61
|
-
@module = Module.new do
|
62
|
-
|
63
|
-
@tag_attrs = {}
|
64
|
-
@tag_aliases = []
|
65
|
-
|
66
|
-
class << self
|
67
|
-
|
68
|
-
def included(base)
|
69
|
-
@tag_aliases.each do |tag, feature|
|
70
|
-
if base.respond_to? :alias_method_chain_on_include
|
71
|
-
base.alias_method_chain_on_include tag, feature
|
72
|
-
else
|
73
|
-
base.send(:alias_method_chain, tag, feature)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def _register_tag_attrs(tag, attrs)
|
79
|
-
@tag_attrs[tag] = attrs
|
80
|
-
end
|
81
|
-
attr_reader :tag_attrs
|
82
|
-
|
83
|
-
def alias_method_chain_on_include(tag, feature)
|
84
|
-
@tag_aliases << [tag, feature]
|
85
|
-
end
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
template = Template.new(File.read(@src_file), @module, @src_file)
|
91
|
-
template.compile([], [])
|
92
|
-
@last_load_time = File.mtime(@src_file)
|
93
|
-
end
|
94
|
-
|
95
|
-
def import_into(class_or_module, as)
|
96
|
-
if as
|
97
|
-
# Define a method on class_or_module named whatever 'as'
|
98
|
-
# is. The first time the method is called it creates and
|
99
|
-
# returns an object that provides the taglib's tags as
|
100
|
-
# methods. On subsequent calls the object is cached in an
|
101
|
-
# instance variable "@_#{as}_taglib"
|
102
|
-
|
103
|
-
taglib_module = @module
|
104
|
-
ivar = "@_#{as}_taglib"
|
105
|
-
class_or_module.send(:define_method, as) do
|
106
|
-
instance_variable_get(ivar) or begin
|
107
|
-
as_class = Class.new(TemplateEnvironment) { include taglib_module }
|
108
|
-
as_object = as_class.new
|
109
|
-
as_object.copy_instance_variables_from(self)
|
110
|
-
instance_variable_set(ivar, as_object)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
else
|
114
|
-
class_or_module.send(:include, @module)
|
115
|
-
class_or_module.tag_attrs.update(@module.tag_attrs) if @module.respond_to?(:tag_attrs)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
data/lib/hobo/dryml/template.rb
DELETED
@@ -1,1019 +0,0 @@
|
|
1
|
-
require 'rexml/document'
|
2
|
-
require 'pathname'
|
3
|
-
|
4
|
-
module Hobo::Dryml
|
5
|
-
|
6
|
-
class Template
|
7
|
-
|
8
|
-
DRYML_NAME = "[a-zA-Z\-][a-zA-Z0-9\-]*"
|
9
|
-
DRYML_NAME_RX = /^#{DRYML_NAME}$/
|
10
|
-
|
11
|
-
RUBY_NAME = "[a-zA-Z_][a-zA-Z0-9_]*"
|
12
|
-
RUBY_NAME_RX = /^#{RUBY_NAME}$/
|
13
|
-
|
14
|
-
CODE_ATTRIBUTE_CHAR = "&"
|
15
|
-
|
16
|
-
NO_METADATA_TAGS = %w(doctype if else unless repeat do with name type-name)
|
17
|
-
|
18
|
-
SPECIAL_ATTRIBUTES = %w(param merge merge-params merge-attrs
|
19
|
-
for-type
|
20
|
-
if unless repeat
|
21
|
-
part part-locals
|
22
|
-
restore)
|
23
|
-
|
24
|
-
VALID_PARAMETER_TAG_ATTRIBUTES = %w(param replace)
|
25
|
-
|
26
|
-
@build_cache = {}
|
27
|
-
|
28
|
-
class << self
|
29
|
-
attr_reader :build_cache
|
30
|
-
|
31
|
-
def clear_build_cache
|
32
|
-
@build_cache.clear()
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize(src, environment, template_path)
|
37
|
-
@src = src
|
38
|
-
@environment = environment # a class or a module
|
39
|
-
@template_path = template_path.sub(%r(^#{Regexp.escape(RAILS_ROOT)}/), "")
|
40
|
-
|
41
|
-
@builder = Template.build_cache[@template_path] || DRYMLBuilder.new(self)
|
42
|
-
@builder.set_environment(environment)
|
43
|
-
|
44
|
-
@last_element = nil
|
45
|
-
end
|
46
|
-
|
47
|
-
attr_reader :tags, :template_path
|
48
|
-
|
49
|
-
def compile(local_names=[], auto_taglibs=[])
|
50
|
-
now = Time.now
|
51
|
-
|
52
|
-
unless @template_path.ends_with?(EMPTY_PAGE)
|
53
|
-
p = Pathname.new template_path
|
54
|
-
p = Pathname.new(RAILS_ROOT) + p unless p.absolute?
|
55
|
-
mtime = p.mtime
|
56
|
-
|
57
|
-
if !@builder.ready?(mtime)
|
58
|
-
@builder.start
|
59
|
-
parsed = true
|
60
|
-
# parse the DRYML file creating a list of build instructions
|
61
|
-
if is_taglib?
|
62
|
-
process_src
|
63
|
-
else
|
64
|
-
create_render_page_method
|
65
|
-
end
|
66
|
-
|
67
|
-
# store build instructions in the cache
|
68
|
-
Template.build_cache[@template_path] = @builder
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# compile the build instructions
|
73
|
-
@builder.build(local_names, auto_taglibs, mtime)
|
74
|
-
|
75
|
-
logger.info(" DRYML: Compiled #{template_path} in #{'%.2fs' % (Time.now - now)}") if parsed
|
76
|
-
end
|
77
|
-
|
78
|
-
|
79
|
-
def create_render_page_method
|
80
|
-
erb_src = process_src
|
81
|
-
|
82
|
-
@builder.add_build_instruction(:render_page, :src => erb_src, :line_num => 1)
|
83
|
-
end
|
84
|
-
|
85
|
-
|
86
|
-
def is_taglib?
|
87
|
-
@environment.class == Module
|
88
|
-
end
|
89
|
-
|
90
|
-
|
91
|
-
def process_src
|
92
|
-
@doc = Hobo::Dryml::Parser::Document.new(@src, @template_path)
|
93
|
-
result = children_to_erb(@doc.root)
|
94
|
-
restore_erb_scriptlets(result)
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
def restore_erb_scriptlets(src)
|
99
|
-
@doc.restore_erb_scriptlets(src)
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
|
-
def children_to_erb(nodes)
|
104
|
-
nodes.map { |x| node_to_erb(x) }.join
|
105
|
-
end
|
106
|
-
|
107
|
-
|
108
|
-
def node_to_erb(node)
|
109
|
-
case node
|
110
|
-
|
111
|
-
# v important this comes before REXML::Text, as REXML::CData < REXML::Text
|
112
|
-
when REXML::CData
|
113
|
-
REXML::CData::START + node.to_s + REXML::CData::STOP
|
114
|
-
|
115
|
-
when REXML::Comment
|
116
|
-
REXML::Comment::START + node.to_s + REXML::Comment::STOP
|
117
|
-
|
118
|
-
when REXML::Text
|
119
|
-
strip_suppressed_whiteaspace(node.to_s)
|
120
|
-
|
121
|
-
when REXML::Element
|
122
|
-
element_to_erb(node)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
|
127
|
-
def strip_suppressed_whiteaspace(s)
|
128
|
-
s # s.gsub(/ -(\s*\n\s*)/, '<% \1 %>')
|
129
|
-
end
|
130
|
-
|
131
|
-
|
132
|
-
def element_to_erb(el)
|
133
|
-
dryml_exception("old-style parameter tag (<#{el.name}>)", el) if el.name.starts_with?(":")
|
134
|
-
|
135
|
-
@last_element = el
|
136
|
-
case el.dryml_name
|
137
|
-
|
138
|
-
when "include"
|
139
|
-
include_element(el)
|
140
|
-
# return just the newlines to keep line-number matching - the
|
141
|
-
# include has no presence in the erb source
|
142
|
-
tag_newlines(el)
|
143
|
-
|
144
|
-
when "set-theme"
|
145
|
-
require_attribute(el, "name", /^#{DRYML_NAME}$/)
|
146
|
-
@builder.add_build_instruction(:set_theme, :name => el.attributes['name'])
|
147
|
-
|
148
|
-
# return nothing - set_theme has no presence in the erb source
|
149
|
-
tag_newlines(el)
|
150
|
-
|
151
|
-
when "def"
|
152
|
-
def_element(el)
|
153
|
-
|
154
|
-
when "extend"
|
155
|
-
extend_element(el)
|
156
|
-
|
157
|
-
when "set"
|
158
|
-
set_element(el)
|
159
|
-
|
160
|
-
when "set-scoped"
|
161
|
-
set_scoped_element(el)
|
162
|
-
|
163
|
-
when "param-content"
|
164
|
-
param_content_element(el)
|
165
|
-
|
166
|
-
else
|
167
|
-
if el.dryml_name.not_in?(Hobo.static_tags) || el.attributes['param'] || el.attributes['restore']
|
168
|
-
tag_call(el)
|
169
|
-
else
|
170
|
-
static_element_to_erb(el)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
|
176
|
-
def include_element(el)
|
177
|
-
require_toplevel(el)
|
178
|
-
require_attribute(el, "as", /^#{DRYML_NAME}$/, true)
|
179
|
-
options = {}
|
180
|
-
%w(src module plugin as).each do |attr|
|
181
|
-
options[attr.to_sym] = el.attributes[attr] if el.attributes[attr]
|
182
|
-
end
|
183
|
-
@builder.add_build_instruction(:include, options)
|
184
|
-
end
|
185
|
-
|
186
|
-
|
187
|
-
def import_module(mod, as=nil)
|
188
|
-
@builder.import_module(mod, as)
|
189
|
-
end
|
190
|
-
|
191
|
-
|
192
|
-
def set_element(el)
|
193
|
-
assigns = el.attributes.map do |name, value|
|
194
|
-
next if name.in?(SPECIAL_ATTRIBUTES)
|
195
|
-
dryml_exception("invalid name in <set> (remember to use '-' rather than '_')", el) unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
|
196
|
-
"#{ruby_name name} = #{attribute_to_ruby(value)}; "
|
197
|
-
end.join
|
198
|
-
code = apply_control_attributes("begin; #{assigns}; end", el)
|
199
|
-
"<% #{code}#{tag_newlines(el)} %>"
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
def set_scoped_element(el)
|
204
|
-
variables = el.attributes.map do |name, value|
|
205
|
-
dryml_exception("invalid name in <set-scoped> (remember to use '-' rather than '_')", el) unless name =~ DRYML_NAME_RX
|
206
|
-
":#{ruby_name name} => #{attribute_to_ruby(value)} "
|
207
|
-
end
|
208
|
-
"<% scope.new_scope(#{variables * ', '}) { #{tag_newlines(el)} %>#{children_to_erb(el)}<% } %>"
|
209
|
-
end
|
210
|
-
|
211
|
-
|
212
|
-
def declared_attributes(def_element)
|
213
|
-
attrspec = def_element.attributes["attrs"]
|
214
|
-
attr_names = attrspec ? attrspec.split(/\s*,\s*/).map{ |n| n.underscore.to_sym } : []
|
215
|
-
invalids = attr_names & ([:with, :field, :this] + SPECIAL_ATTRIBUTES.*.to_sym)
|
216
|
-
dryml_exception("invalid attrs in def: #{invalids * ', '}", def_element) unless invalids.empty?
|
217
|
-
attr_names
|
218
|
-
end
|
219
|
-
|
220
|
-
|
221
|
-
def ruby_name(dryml_name)
|
222
|
-
dryml_name.gsub('-', '_')
|
223
|
-
end
|
224
|
-
|
225
|
-
|
226
|
-
def with_containing_tag_name(el)
|
227
|
-
old = @containing_tag_name
|
228
|
-
@containing_tag_name = el.dryml_name
|
229
|
-
yield
|
230
|
-
@containing_tag_name = old
|
231
|
-
end
|
232
|
-
|
233
|
-
|
234
|
-
def define_polymorphic_dispatcher(el, name)
|
235
|
-
# FIXME: The new erb context ends up being set-up twice
|
236
|
-
src = %(
|
237
|
-
def #{name}(attributes={}, parameters={})
|
238
|
-
_tag_context(attributes) do
|
239
|
-
attributes.delete :with
|
240
|
-
attributes.delete :field
|
241
|
-
call_polymorphic_tag('#{name}', attributes, parameters) { #{name}__base(attributes.except, parameters) }
|
242
|
-
end
|
243
|
-
end
|
244
|
-
)
|
245
|
-
@builder.add_build_instruction(:eval, :src => src, :line_num => element_line_num(el))
|
246
|
-
end
|
247
|
-
|
248
|
-
|
249
|
-
def extend_element(el)
|
250
|
-
def_element(el, true)
|
251
|
-
end
|
252
|
-
|
253
|
-
|
254
|
-
def type_specific_suffix
|
255
|
-
el = @def_element
|
256
|
-
for_type = el.attributes['for']
|
257
|
-
if for_type
|
258
|
-
type_name = if defined?(HoboFields) && for_type =~ /^[a-z]/
|
259
|
-
# It's a symbolic type name - look up the Ruby type name
|
260
|
-
klass = HoboFields.to_class(for_type) or
|
261
|
-
dryml_exception("No such type in polymorphic tag definition: '#{for_type}'", el)
|
262
|
-
klass.name
|
263
|
-
else
|
264
|
-
for_type
|
265
|
-
end.underscore.gsub('/', '__')
|
266
|
-
"__for_#{type_name}"
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
|
271
|
-
def def_element(el, extend_tag=false)
|
272
|
-
require_toplevel(el)
|
273
|
-
require_attribute(el, "tag", DRYML_NAME_RX)
|
274
|
-
require_attribute(el, "attrs", /^\s*#{DRYML_NAME}(\s*,\s*#{DRYML_NAME})*\s*$/, true)
|
275
|
-
require_attribute(el, "alias-of", DRYML_NAME_RX, true)
|
276
|
-
|
277
|
-
@def_element = el
|
278
|
-
|
279
|
-
unsafe_name = el.attributes["tag"]
|
280
|
-
name = Hobo::Dryml.unreserve(unsafe_name)
|
281
|
-
suffix = type_specific_suffix
|
282
|
-
if suffix
|
283
|
-
name += suffix
|
284
|
-
unsafe_name += suffix
|
285
|
-
end
|
286
|
-
|
287
|
-
if el.attributes['polymorphic']
|
288
|
-
%w(for alias-of).each do |attr|
|
289
|
-
dryml_exception("def cannot have both 'polymorphic' and '#{attr}' attributes") if el.attributes[attr]
|
290
|
-
end
|
291
|
-
|
292
|
-
define_polymorphic_dispatcher(el, ruby_name(name))
|
293
|
-
name += "__base"
|
294
|
-
unsafe_name += "__base"
|
295
|
-
end
|
296
|
-
|
297
|
-
alias_of = el.attributes['alias-of']
|
298
|
-
dryml_exception("def with alias-of must be empty", el) if alias_of and el.size > 0
|
299
|
-
|
300
|
-
alias_of and @builder.add_build_instruction(:alias_method,
|
301
|
-
:new => ruby_name(name).to_sym,
|
302
|
-
:old => ruby_name(Hobo::Dryml.unreserve(alias_of)).to_sym)
|
303
|
-
|
304
|
-
res = if alias_of
|
305
|
-
"<% #{tag_newlines(el)} %>"
|
306
|
-
else
|
307
|
-
src = tag_method(name, el, extend_tag) +
|
308
|
-
"<% _register_tag_attrs(:#{ruby_name name}, #{declared_attributes(el).inspect.underscore}) %>"
|
309
|
-
|
310
|
-
logger.debug(restore_erb_scriptlets(src)) if el.attributes["debug-source"]
|
311
|
-
|
312
|
-
@builder.add_build_instruction(:def,
|
313
|
-
:src => restore_erb_scriptlets(src),
|
314
|
-
:line_num => element_line_num(el))
|
315
|
-
# keep line numbers matching up
|
316
|
-
"<% #{"\n" * src.count("\n")} %>"
|
317
|
-
end
|
318
|
-
@def_element = nil
|
319
|
-
res
|
320
|
-
end
|
321
|
-
|
322
|
-
def self.descendents(el,&block)
|
323
|
-
return if el.elements.empty?
|
324
|
-
el.elements.each do |child|
|
325
|
-
block.call(child)
|
326
|
-
descendents(child,&block)
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
# Using REXML::XPath is slow
|
331
|
-
def self.descendent_select(el)
|
332
|
-
result = []
|
333
|
-
descendents(el) { |desc|
|
334
|
-
result << desc if yield(desc)
|
335
|
-
}
|
336
|
-
result
|
337
|
-
end
|
338
|
-
|
339
|
-
def param_names_in_definition(el)
|
340
|
-
self.class.descendent_select(el) { |el| el.attribute 'param' }.map do |e|
|
341
|
-
name = get_param_name(e)
|
342
|
-
dryml_exception("invalid param name: #{name.inspect}", e) unless
|
343
|
-
is_code_attribute?(name) || name =~ RUBY_NAME_RX || name =~ /#\{/
|
344
|
-
name.to_sym unless is_code_attribute?(name)
|
345
|
-
end.compact
|
346
|
-
end
|
347
|
-
|
348
|
-
|
349
|
-
def tag_method(name, el, extend_tag=false)
|
350
|
-
name = ruby_name name
|
351
|
-
param_names = param_names_in_definition(el)
|
352
|
-
|
353
|
-
if extend_tag
|
354
|
-
@extend_key = 'a' + Digest::SHA1.hexdigest(el.to_s)[0..10]
|
355
|
-
alias_statement = "; alias_method_chain_on_include :#{name}, :#{@extend_key}"
|
356
|
-
name = "#{name}_with_#{@extend_key}"
|
357
|
-
end
|
358
|
-
|
359
|
-
src = "<% def #{name}(all_attributes={}, all_parameters={}); " +
|
360
|
-
"parameters = Hobo::Dryml::TagParameters.new(all_parameters, #{param_names.inspect.underscore}); " +
|
361
|
-
"all_parameters = Hobo::Dryml::TagParameters.new(all_parameters); " +
|
362
|
-
tag_method_body(el) +
|
363
|
-
"; end#{alias_statement} %>"
|
364
|
-
@extend_key = nil
|
365
|
-
src
|
366
|
-
end
|
367
|
-
|
368
|
-
|
369
|
-
def tag_method_body(el)
|
370
|
-
attrs = declared_attributes(el)
|
371
|
-
|
372
|
-
# A statement to assign values to local variables named after the tag's attrs
|
373
|
-
# The trailing comma on `attributes` is supposed to be there!
|
374
|
-
setup_locals = attrs.map{|a| "#{Hobo::Dryml.unreserve(a).underscore}, "}.join + "attributes, = " +
|
375
|
-
"_tag_locals(all_attributes, #{attrs.inspect})"
|
376
|
-
|
377
|
-
start = "_tag_context(all_attributes) do #{setup_locals}"
|
378
|
-
|
379
|
-
"#{start} " +
|
380
|
-
# reproduce any line breaks in the start-tag so that line numbers are preserved
|
381
|
-
tag_newlines(el) + "%>" +
|
382
|
-
wrap_tag_method_body_with_metadata(children_to_erb(el)) +
|
383
|
-
"<% output_buffer; end"
|
384
|
-
end
|
385
|
-
|
386
|
-
|
387
|
-
def wrap_source_with_metadata(content, kind, name, *args)
|
388
|
-
if (!include_source_metadata) || name.in?(NO_METADATA_TAGS)
|
389
|
-
content
|
390
|
-
else
|
391
|
-
metadata = [kind, name] + args + [@template_path]
|
392
|
-
"<!--[DRYML|#{metadata * '|'}[-->" + content + "<!--]DRYML]-->"
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
|
397
|
-
def wrap_tag_method_body_with_metadata(content)
|
398
|
-
name = @def_element.attributes['tag']
|
399
|
-
for_ = @def_element.attributes['for']
|
400
|
-
name += " for #{for_}" if for_
|
401
|
-
wrap_source_with_metadata(content, "def", name, element_line_num(@def_element))
|
402
|
-
end
|
403
|
-
|
404
|
-
|
405
|
-
def wrap_tag_call_with_metadata(el, content)
|
406
|
-
name = el.expanded_name
|
407
|
-
param = el.attributes['param']
|
408
|
-
|
409
|
-
if param == "&true"
|
410
|
-
name += " param"
|
411
|
-
elsif param
|
412
|
-
name += " param='#{param}'"
|
413
|
-
end
|
414
|
-
|
415
|
-
wrap_source_with_metadata(content, "call", name, element_line_num(el))
|
416
|
-
end
|
417
|
-
|
418
|
-
|
419
|
-
def param_content_local_name(name)
|
420
|
-
"_#{ruby_name name}__default_content"
|
421
|
-
end
|
422
|
-
|
423
|
-
|
424
|
-
def param_content_element(name_or_el)
|
425
|
-
name = if name_or_el.is_a?(String)
|
426
|
-
name_or_el
|
427
|
-
else
|
428
|
-
el = name_or_el
|
429
|
-
el.attributes['for'] || @containing_tag_name
|
430
|
-
end
|
431
|
-
local_name = param_content_local_name(name)
|
432
|
-
"<%= #{local_name} && #{local_name}.call %>"
|
433
|
-
end
|
434
|
-
|
435
|
-
|
436
|
-
def part_element(el, content)
|
437
|
-
require_attribute(el, "part", DRYML_NAME_RX)
|
438
|
-
|
439
|
-
if contains_param?(el)
|
440
|
-
delegated_part_element(el, content)
|
441
|
-
else
|
442
|
-
simple_part_element(el, content)
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
|
447
|
-
def simple_part_element(el, content)
|
448
|
-
part_name = el.attributes['part']
|
449
|
-
dom_id = el.attributes['id'] || part_name
|
450
|
-
part_name = ruby_name(part_name)
|
451
|
-
part_locals = el.attributes["part-locals"]
|
452
|
-
|
453
|
-
part_src = "<% def #{part_name}_part(#{part_locals._?.gsub('@', '')}) #{tag_newlines(el)}; new_context do %>" +
|
454
|
-
content +
|
455
|
-
"<% end; end %>"
|
456
|
-
@builder.add_part(part_name, restore_erb_scriptlets(part_src), element_line_num(el))
|
457
|
-
|
458
|
-
newlines = "\n" * part_src.count("\n")
|
459
|
-
args = [attribute_to_ruby(dom_id), ":#{part_name}", part_locals].compact
|
460
|
-
"<%= call_part(#{args * ', '}) #{newlines} %>"
|
461
|
-
end
|
462
|
-
|
463
|
-
|
464
|
-
def delegated_part_element(el, content)
|
465
|
-
# TODO
|
466
|
-
end
|
467
|
-
|
468
|
-
|
469
|
-
def contains_param?(el)
|
470
|
-
# TODO
|
471
|
-
false
|
472
|
-
end
|
473
|
-
|
474
|
-
|
475
|
-
def part_delegate_tag_name(el)
|
476
|
-
"#{@def_name}_#{el.attributes['part']}__part_delegate"
|
477
|
-
end
|
478
|
-
|
479
|
-
|
480
|
-
def current_def_name
|
481
|
-
@def_element && @def_element.attributes['tag']
|
482
|
-
end
|
483
|
-
|
484
|
-
|
485
|
-
def get_param_name(el)
|
486
|
-
param_name = el.attributes["param"]
|
487
|
-
|
488
|
-
if param_name
|
489
|
-
def_tag = find_ancestor(el) {|e| e.name == "def" || e.name == "extend" }
|
490
|
-
dryml_exception("param is not allowed outside of tag definitions", el) if def_tag.nil?
|
491
|
-
|
492
|
-
ruby_name(param_name == "&true" ? el.dryml_name : param_name)
|
493
|
-
else
|
494
|
-
nil
|
495
|
-
end
|
496
|
-
end
|
497
|
-
|
498
|
-
|
499
|
-
def inside_def_for_type?
|
500
|
-
@def_element && @def_element.attributes['for']
|
501
|
-
end
|
502
|
-
|
503
|
-
|
504
|
-
def call_name(el)
|
505
|
-
dryml_exception("invalid tag name (remember to use '-' rather than '_')", el) unless el.dryml_name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
|
506
|
-
|
507
|
-
name = Hobo::Dryml.unreserve(ruby_name(el.dryml_name))
|
508
|
-
if call_to_self_from_type_specific_def?(el)
|
509
|
-
"#{name}__base"
|
510
|
-
elsif old_tag_call?(el)
|
511
|
-
name = name[4..-1] # remove 'old-' prefix
|
512
|
-
name += type_specific_suffix if inside_def_for_type?
|
513
|
-
"#{name}_without_#{@extend_key}"
|
514
|
-
else
|
515
|
-
name
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
|
520
|
-
def old_tag_call?(el)
|
521
|
-
@def_element && el.dryml_name == "old-#{current_def_name}"
|
522
|
-
end
|
523
|
-
|
524
|
-
|
525
|
-
def call_to_self_from_type_specific_def?(el)
|
526
|
-
inside_def_for_type? && el.dryml_name == current_def_name &&!el.attributes['for-type']
|
527
|
-
end
|
528
|
-
|
529
|
-
|
530
|
-
def polymorphic_call_type(el)
|
531
|
-
t = el.attributes['for-type']
|
532
|
-
if t.nil?
|
533
|
-
nil
|
534
|
-
elsif t == "&true"
|
535
|
-
'this_type'
|
536
|
-
elsif t =~ /^[A-Z]/
|
537
|
-
t
|
538
|
-
elsif t =~ /^[a-z]/ && defined? HoboFields.to_class
|
539
|
-
klass = HoboFields.to_class(t)
|
540
|
-
klass.name
|
541
|
-
elsif is_code_attribute?(t)
|
542
|
-
t[1..-1]
|
543
|
-
else
|
544
|
-
dryml_exception("invalid for-type attribute", el)
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
|
549
|
-
def tag_call(el)
|
550
|
-
name = call_name(el)
|
551
|
-
param_name = get_param_name(el)
|
552
|
-
attributes = tag_attributes(el)
|
553
|
-
newlines = tag_newlines(el)
|
554
|
-
|
555
|
-
parameters = tag_newlines(el) + parameter_tags_hash(el)
|
556
|
-
|
557
|
-
is_param_restore = el.attributes['restore']
|
558
|
-
|
559
|
-
call = if param_name
|
560
|
-
param_name = attribute_to_ruby(param_name, :symbolize => true)
|
561
|
-
args = "#{attributes}, #{parameters}, all_parameters, #{param_name}"
|
562
|
-
to_call = if is_param_restore
|
563
|
-
# The tag is available in a local variable
|
564
|
-
# holding a proc
|
565
|
-
param_restore_local_name(name)
|
566
|
-
elsif (call_type = polymorphic_call_type(el))
|
567
|
-
"find_polymorphic_tag(:#{ruby_name name}, #{call_type})"
|
568
|
-
else
|
569
|
-
":#{ruby_name name}"
|
570
|
-
end
|
571
|
-
"call_tag_parameter(#{to_call}, #{args})"
|
572
|
-
else
|
573
|
-
if is_param_restore
|
574
|
-
# The tag is a proc available in a local variable
|
575
|
-
"#{param_restore_local_name(name)}.call(#{attributes}, #{parameters})"
|
576
|
-
elsif (call_type = polymorphic_call_type(el))
|
577
|
-
"send(find_polymorphic_tag(:#{ruby_name name}, #{call_type}), #{attributes}, #{parameters})"
|
578
|
-
elsif attributes == "{}" && parameters == "{}"
|
579
|
-
if name =~ /^[A-Z]/
|
580
|
-
# it's a tag with a cap name - not a local
|
581
|
-
"#{ruby_name name}()"
|
582
|
-
else
|
583
|
-
# could be a tag or a local variable
|
584
|
-
"#{ruby_name name}.to_s"
|
585
|
-
end
|
586
|
-
else
|
587
|
-
"#{ruby_name name}(#{attributes}, #{parameters})"
|
588
|
-
end
|
589
|
-
end
|
590
|
-
|
591
|
-
call = apply_control_attributes(call, el)
|
592
|
-
call = maybe_make_part_call(el, "<% concat(#{call}) %>")
|
593
|
-
wrap_tag_call_with_metadata(el, call)
|
594
|
-
end
|
595
|
-
|
596
|
-
|
597
|
-
def merge_attribute(el)
|
598
|
-
merge = el.attributes['merge']
|
599
|
-
dryml_exception("merge cannot have a RHS", el) if merge && merge != "&true"
|
600
|
-
merge
|
601
|
-
end
|
602
|
-
|
603
|
-
|
604
|
-
def parameter_tags_hash(el, containing_tag_name=nil)
|
605
|
-
call_type = nil
|
606
|
-
|
607
|
-
metadata_name = containing_tag_name || el.expanded_name
|
608
|
-
|
609
|
-
param_items = el.map do |node|
|
610
|
-
case node
|
611
|
-
when REXML::Text
|
612
|
-
text = node.to_s
|
613
|
-
unless text.blank?
|
614
|
-
dryml_exception("mixed content and parameter tags", el) if call_type == :named_params
|
615
|
-
call_type = :default_param_only
|
616
|
-
end
|
617
|
-
text
|
618
|
-
when REXML::Element
|
619
|
-
e = node
|
620
|
-
is_parameter_tag = e.parameter_tag?
|
621
|
-
|
622
|
-
# Make sure there isn't a mix of parameter tags and normal content
|
623
|
-
case call_type
|
624
|
-
when nil
|
625
|
-
call_type = is_parameter_tag ? :named_params : :default_param_only
|
626
|
-
when :named_params
|
627
|
-
dryml_exception("mixed parameter tags and non-parameter tags (did you forget a ':'?)", el) unless is_parameter_tag
|
628
|
-
when :default_param_only
|
629
|
-
dryml_exception("mixed parameter tags and non-parameter tags (did you forget a ':'?)", el) if is_parameter_tag
|
630
|
-
end
|
631
|
-
|
632
|
-
if is_parameter_tag
|
633
|
-
parameter_tag_hash_item(e, metadata_name) + ", "
|
634
|
-
end
|
635
|
-
end
|
636
|
-
end.join
|
637
|
-
|
638
|
-
if call_type == :default_param_only || (call_type.nil? && param_items.length > 0) || (el.children.empty? && el.has_end_tag?)
|
639
|
-
with_containing_tag_name(el) do
|
640
|
-
param_items = " :default => #{default_param_proc(el, containing_tag_name)}, "
|
641
|
-
end
|
642
|
-
end
|
643
|
-
|
644
|
-
param_items.concat without_parameters(el)
|
645
|
-
|
646
|
-
merge_params = el.attributes['merge-params'] || merge_attribute(el)
|
647
|
-
if merge_params
|
648
|
-
extra_params = if merge_params == "&true"
|
649
|
-
"parameters"
|
650
|
-
elsif is_code_attribute?(merge_params)
|
651
|
-
merge_params[1..-1]
|
652
|
-
else
|
653
|
-
merge_param_names = merge_params.split(/\s*,\s*/).*.gsub("-", "_")
|
654
|
-
"all_parameters & #{merge_param_names.inspect}"
|
655
|
-
end
|
656
|
-
"merge_parameter_hashes({#{param_items}}, (#{extra_params}) || {})"
|
657
|
-
else
|
658
|
-
"{#{param_items}}"
|
659
|
-
end
|
660
|
-
end
|
661
|
-
|
662
|
-
|
663
|
-
def without_parameters(el)
|
664
|
-
without_names = el.attributes.keys.map { |name| name =~ /^without-(.*)/ and $1 }.compact
|
665
|
-
without_names.map { |name| ":#{ruby_name name}_replacement => proc {|__discard__| '' }, " }.join
|
666
|
-
end
|
667
|
-
|
668
|
-
|
669
|
-
def parameter_tag_hash_item(el, metadata_name)
|
670
|
-
name = el.name.dup
|
671
|
-
if name.sub!(/^before-/, "")
|
672
|
-
before_parameter_tag_hash_item(name, el, metadata_name)
|
673
|
-
elsif name.sub!(/^after-/, "")
|
674
|
-
after_parameter_tag_hash_item(name, el, metadata_name)
|
675
|
-
elsif name.sub!(/^prepend-/, "")
|
676
|
-
prepend_parameter_tag_hash_item(name, el, metadata_name)
|
677
|
-
elsif name.sub!(/^append-/, "")
|
678
|
-
append_parameter_tag_hash_item(name, el, metadata_name)
|
679
|
-
else
|
680
|
-
hash_key = ruby_name name
|
681
|
-
hash_key += "_replacement" if el.attribute("replace")
|
682
|
-
if (param_name = get_param_name(el))
|
683
|
-
":#{hash_key} => merge_tag_parameter(#{param_proc(el, metadata_name)}, all_parameters[:#{param_name}])"
|
684
|
-
else
|
685
|
-
":#{hash_key} => #{param_proc(el, metadata_name)}"
|
686
|
-
end
|
687
|
-
end
|
688
|
-
end
|
689
|
-
|
690
|
-
|
691
|
-
def before_parameter_tag_hash_item(name, el, metadata_name)
|
692
|
-
param_name = get_param_name(el)
|
693
|
-
dryml_exception("param declaration not allowed on 'before' parameters", el) if param_name
|
694
|
-
content = children_to_erb(el) + "<% concat(#{param_restore_local_name(name)}.call({}, {})) %>"
|
695
|
-
":#{ruby_name name}_replacement => #{replace_parameter_proc(el, metadata_name, content)}"
|
696
|
-
end
|
697
|
-
|
698
|
-
|
699
|
-
def after_parameter_tag_hash_item(name, el, metadata_name)
|
700
|
-
param_name = get_param_name(el)
|
701
|
-
dryml_exception("param declaration not allowed on 'after' parameters", el) if param_name
|
702
|
-
content = "<% concat(#{param_restore_local_name(name)}.call({}, {})) %>" + children_to_erb(el)
|
703
|
-
":#{ruby_name name}_replacement => #{replace_parameter_proc(el, metadata_name, content)}"
|
704
|
-
end
|
705
|
-
|
706
|
-
|
707
|
-
def append_parameter_tag_hash_item(name, el, metadata_name)
|
708
|
-
":#{ruby_name name} => proc { [{}, { :default => proc { |#{param_content_local_name(name)}| new_context { %>" +
|
709
|
-
param_content_element(name) + children_to_erb(el) +
|
710
|
-
"<% } } } ] }"
|
711
|
-
end
|
712
|
-
|
713
|
-
|
714
|
-
def prepend_parameter_tag_hash_item(name, el, metadata_name)
|
715
|
-
":#{ruby_name name} => proc { [{}, { :default => proc { |#{param_content_local_name(name)}| new_context { %>" +
|
716
|
-
children_to_erb(el) + param_content_element(name) +
|
717
|
-
"<% } } } ] }"
|
718
|
-
end
|
719
|
-
|
720
|
-
|
721
|
-
def default_param_proc(el, containing_param_name=nil)
|
722
|
-
content = children_to_erb(el)
|
723
|
-
content = wrap_source_with_metadata(content, "param", containing_param_name,
|
724
|
-
element_line_num(el)) if containing_param_name
|
725
|
-
"proc { |#{param_content_local_name(el.dryml_name)}| new_context { %>#{content}<% } #{tag_newlines(el)}}"
|
726
|
-
end
|
727
|
-
|
728
|
-
|
729
|
-
def param_restore_local_name(name)
|
730
|
-
"_#{ruby_name name}_restore"
|
731
|
-
end
|
732
|
-
|
733
|
-
|
734
|
-
def wrap_replace_parameter(el, name)
|
735
|
-
wrap_source_with_metadata(children_to_erb(el), "replace", name, element_line_num(el))
|
736
|
-
end
|
737
|
-
|
738
|
-
|
739
|
-
def param_proc(el, metadata_name_prefix)
|
740
|
-
metadata_name = "#{metadata_name_prefix}><#{el.name}"
|
741
|
-
|
742
|
-
nl = tag_newlines(el)
|
743
|
-
|
744
|
-
if (repl = el.attribute("replace"))
|
745
|
-
dryml_exception("replace attribute must not have a value", el) if repl.has_rhs?
|
746
|
-
dryml_exception("replace parameters must not have attributes", el) if el.attributes.length > 1
|
747
|
-
|
748
|
-
replace_parameter_proc(el, metadata_name)
|
749
|
-
else
|
750
|
-
attributes = el.attributes.dup
|
751
|
-
# Providing one of 'with' or 'field' but not the other should cancel out the other
|
752
|
-
attributes[:with] = "&nil" if attributes.key?(:field) && !attributes.key?(:with)
|
753
|
-
attributes[:field] = "&nil" if !attributes.key?(:field) && attributes.key?(:with)
|
754
|
-
attribute_items = attributes.map do |name, value|
|
755
|
-
if name.in?(VALID_PARAMETER_TAG_ATTRIBUTES)
|
756
|
-
# just ignore
|
757
|
-
elsif name.in?(SPECIAL_ATTRIBUTES)
|
758
|
-
dryml_exception("attribute '#{name}' is not allowed on parameter tags (<#{el.name}:>)", el)
|
759
|
-
else
|
760
|
-
":#{ruby_name name} => #{attribute_to_ruby(value, el)}"
|
761
|
-
end
|
762
|
-
end.compact
|
763
|
-
|
764
|
-
nested_parameters_hash = parameter_tags_hash(el, metadata_name)
|
765
|
-
"proc { [{#{attribute_items * ', '}}, #{nested_parameters_hash}] #{nl}}"
|
766
|
-
end
|
767
|
-
end
|
768
|
-
|
769
|
-
|
770
|
-
def replace_parameter_proc(el, metadata_name, content=nil)
|
771
|
-
content ||= wrap_replace_parameter(el, metadata_name)
|
772
|
-
param_name = el.dryml_name.sub(/^(before|after|append|prepend)-/, "")
|
773
|
-
"proc { |#{param_restore_local_name(param_name)}| new_context { %>#{content}<% } #{tag_newlines(el)}}"
|
774
|
-
end
|
775
|
-
|
776
|
-
|
777
|
-
def maybe_make_part_call(el, call)
|
778
|
-
part_name = el.attributes['part']
|
779
|
-
if part_name
|
780
|
-
part_id = el.attributes['id'] || part_name
|
781
|
-
"<div class='part-wrapper' id='<%= #{attribute_to_ruby part_id} %>'>#{part_element(el, call)}</div>"
|
782
|
-
else
|
783
|
-
call
|
784
|
-
end
|
785
|
-
end
|
786
|
-
|
787
|
-
|
788
|
-
def field_shorthand_element?(el)
|
789
|
-
el.expanded_name =~ /:./
|
790
|
-
end
|
791
|
-
|
792
|
-
|
793
|
-
def tag_attributes(el)
|
794
|
-
attributes = el.attributes
|
795
|
-
items = attributes.map do |n,v|
|
796
|
-
dryml_exception("invalid attribute name '#{n}' (remember to use '-' rather than '_')", el) unless n =~ DRYML_NAME_RX
|
797
|
-
|
798
|
-
next if n.in?(SPECIAL_ATTRIBUTES) || n =~ /^without-/
|
799
|
-
next if el.attributes['part'] && n == 'id' # The id is rendered on the <div class="part-wrapper"> instead
|
800
|
-
|
801
|
-
":#{ruby_name n} => #{attribute_to_ruby(v)}"
|
802
|
-
end.compact
|
803
|
-
|
804
|
-
# if there's a ':' el.name is just the part after the ':'
|
805
|
-
items << ":field => \"#{ruby_name el.name}\"" if field_shorthand_element?(el)
|
806
|
-
|
807
|
-
hash = "{#{items.join(", ")}}"
|
808
|
-
|
809
|
-
if merge_attribute(el)
|
810
|
-
"merge_attrs(#{hash}, attributes)"
|
811
|
-
elsif el.attributes['merge-attrs']
|
812
|
-
merge_attrs = compile_merge_attrs(el)
|
813
|
-
"merge_attrs(#{hash}, #{merge_attrs} || {})"
|
814
|
-
else
|
815
|
-
hash
|
816
|
-
end
|
817
|
-
end
|
818
|
-
|
819
|
-
|
820
|
-
def compile_merge_attrs(el)
|
821
|
-
merge_attrs = el.attributes['merge-attrs']
|
822
|
-
if merge_attrs == "&true"
|
823
|
-
"attributes"
|
824
|
-
elsif is_code_attribute?(merge_attrs)
|
825
|
-
"(#{merge_attrs[1..-1]})"
|
826
|
-
else
|
827
|
-
merge_attr_names = merge_attrs.split(/\s*,\s*/).*.gsub("-", "_").*.to_sym
|
828
|
-
"(all_attributes & #{merge_attr_names.inspect})"
|
829
|
-
end
|
830
|
-
end
|
831
|
-
|
832
|
-
|
833
|
-
def static_tag_to_method_call(el)
|
834
|
-
part = el.attributes["part"]
|
835
|
-
attrs = el.attributes.map do |n, v|
|
836
|
-
next if n.in? SPECIAL_ATTRIBUTES
|
837
|
-
val = restore_erb_scriptlets(v).gsub('"', '\"').gsub(/<%=(.*?)%>/, '#{\1}')
|
838
|
-
%('#{n}' => "#{val}")
|
839
|
-
end.compact
|
840
|
-
|
841
|
-
# If there's a part but no id, the id defaults to the part name
|
842
|
-
if part && !el.attributes["id"]
|
843
|
-
attrs << ":id => '#{part}'"
|
844
|
-
end
|
845
|
-
|
846
|
-
# Convert the attributes hash to a call to merge_attrs if
|
847
|
-
# there's a merge-attrs attribute
|
848
|
-
attrs = if el.attributes['merge-attrs']
|
849
|
-
merge_attrs = compile_merge_attrs(el)
|
850
|
-
"merge_attrs({#{attrs * ', '}}, #{merge_attrs} || {})"
|
851
|
-
else
|
852
|
-
"{" + attrs.join(', ') + "}"
|
853
|
-
end
|
854
|
-
|
855
|
-
if el.children.empty?
|
856
|
-
dryml_exception("part attribute on empty static tag", el) if part
|
857
|
-
|
858
|
-
"<%= " + apply_control_attributes("element(:#{el.name}, #{attrs}, nil, true, #{!el.has_end_tag?} #{tag_newlines(el)})", el) + " %>"
|
859
|
-
else
|
860
|
-
if part
|
861
|
-
body = part_element(el, children_to_erb(el))
|
862
|
-
else
|
863
|
-
body = children_to_erb(el)
|
864
|
-
end
|
865
|
-
|
866
|
-
output_tag = "element(:#{el.name}, #{attrs}, new_context { %>#{body}<% })"
|
867
|
-
"<% concat(" + apply_control_attributes(output_tag, el) + ") %>"
|
868
|
-
end
|
869
|
-
end
|
870
|
-
|
871
|
-
|
872
|
-
def static_element_to_erb(el)
|
873
|
-
if promote_static_tag_to_method_call?(el)
|
874
|
-
static_tag_to_method_call(el)
|
875
|
-
else
|
876
|
-
start_tag_src = el.start_tag_source.gsub(REXML::CData::START, "").gsub(REXML::CData::STOP, "")
|
877
|
-
|
878
|
-
# Allow #{...} as an alternate to <%= ... %>
|
879
|
-
start_tag_src.gsub!(/=\s*('.*?'|".*?")/) do |s|
|
880
|
-
s.gsub(/#\{(.*?)\}/, '<%= \1 %>')
|
881
|
-
end
|
882
|
-
|
883
|
-
if el.has_end_tag?
|
884
|
-
start_tag_src + children_to_erb(el) + "</#{el.name}>"
|
885
|
-
else
|
886
|
-
start_tag_src
|
887
|
-
end
|
888
|
-
end
|
889
|
-
end
|
890
|
-
|
891
|
-
|
892
|
-
def promote_static_tag_to_method_call?(el)
|
893
|
-
%w(part merge-attrs if unless repeat).any? {|x| el.attributes[x]}
|
894
|
-
end
|
895
|
-
|
896
|
-
|
897
|
-
def apply_control_attributes(expression, el)
|
898
|
-
controls = %w(if unless repeat).map_hash { |x| el.attributes[x] }.compact
|
899
|
-
|
900
|
-
dryml_exception("You can't have multiple control attributes on the same element", el) if
|
901
|
-
controls.length > 1
|
902
|
-
|
903
|
-
attr = controls.keys.first
|
904
|
-
val = controls.values.first
|
905
|
-
if val.nil?
|
906
|
-
expression
|
907
|
-
else
|
908
|
-
control = if !el.attribute(attr).has_rhs?
|
909
|
-
"this"
|
910
|
-
elsif is_code_attribute?(val)
|
911
|
-
"#{val[1..-1]}"
|
912
|
-
else
|
913
|
-
val.gsub!('-', '_')
|
914
|
-
attr == "repeat" ? %("#{val}") : "this.#{val}"
|
915
|
-
end
|
916
|
-
|
917
|
-
x = gensym
|
918
|
-
case attr
|
919
|
-
when "if"
|
920
|
-
"(if !(#{control}).blank?; (#{x} = #{expression}; Hobo::Dryml.last_if = true; #{x}) " +
|
921
|
-
"else (Hobo::Dryml.last_if = false; ''); end)"
|
922
|
-
when "unless"
|
923
|
-
"(if (#{control}).blank?; (#{x} = #{expression}; Hobo::Dryml.last_if = true; #{x}) " +
|
924
|
-
"else (Hobo::Dryml.last_if = false; ''); end)"
|
925
|
-
when "repeat"
|
926
|
-
"repeat_attribute(#{control}) { #{expression} }"
|
927
|
-
end
|
928
|
-
end
|
929
|
-
end
|
930
|
-
|
931
|
-
|
932
|
-
def attribute_to_ruby(*args)
|
933
|
-
options = args.extract_options!
|
934
|
-
attr, el = args
|
935
|
-
|
936
|
-
dryml_exception('erb scriptlet not allowed in this attribute (use #{ ... } instead)', el) if
|
937
|
-
attr.is_a?(String) && attr.index("[![HOBO-ERB")
|
938
|
-
|
939
|
-
if options[:symbolize] && attr =~ /^[a-zA-Z_][^a-zA-Z0-9_]*[\?!]?/
|
940
|
-
":#{attr}"
|
941
|
-
else
|
942
|
-
res = if attr.nil?
|
943
|
-
"nil"
|
944
|
-
elsif is_code_attribute?(attr)
|
945
|
-
"(#{attr[1..-1]})"
|
946
|
-
else
|
947
|
-
if attr !~ /"/
|
948
|
-
'"' + attr + '"'
|
949
|
-
elsif attr !~ /'/
|
950
|
-
"'#{attr}'"
|
951
|
-
else
|
952
|
-
dryml_exception("invalid quote(s) in attribute value")
|
953
|
-
end
|
954
|
-
end
|
955
|
-
options[:symbolize] ? (res + ".to_sym") : res
|
956
|
-
end
|
957
|
-
end
|
958
|
-
|
959
|
-
def find_ancestor(el)
|
960
|
-
e = el.parent
|
961
|
-
until e.is_a? REXML::Document
|
962
|
-
return e if yield(e)
|
963
|
-
e = e.parent
|
964
|
-
end
|
965
|
-
return nil
|
966
|
-
end
|
967
|
-
|
968
|
-
def require_toplevel(el, message=nil)
|
969
|
-
message ||= "can only be at the top level"
|
970
|
-
dryml_exception("<#{el.dryml_name}> #{message}", el) if el.parent != @doc.root
|
971
|
-
end
|
972
|
-
|
973
|
-
def require_attribute(el, name, rx=nil, optional=false)
|
974
|
-
val = el.attributes[name]
|
975
|
-
if val
|
976
|
-
dryml_exception("invalid #{name}=\"#{val}\" attribute on <#{el.dryml_name}>", el) unless rx && val =~ rx
|
977
|
-
else
|
978
|
-
dryml_exception("missing #{name} attribute on <#{el.dryml_name}>", el) unless optional
|
979
|
-
end
|
980
|
-
end
|
981
|
-
|
982
|
-
def dryml_exception(message, el=nil)
|
983
|
-
el ||= @last_element
|
984
|
-
raise DrymlException.new(message, template_path, element_line_num(el))
|
985
|
-
end
|
986
|
-
|
987
|
-
def element_line_num(el)
|
988
|
-
@doc.element_line_num(el)
|
989
|
-
end
|
990
|
-
|
991
|
-
def tag_newlines(el)
|
992
|
-
src = el.start_tag_source
|
993
|
-
"\n" * src.count("\n")
|
994
|
-
end
|
995
|
-
|
996
|
-
def is_code_attribute?(attr_value)
|
997
|
-
attr_value =~ /^\&/ && attr_value !~ /^\&\S+;/
|
998
|
-
end
|
999
|
-
|
1000
|
-
def logger
|
1001
|
-
ActionController::Base.logger rescue nil
|
1002
|
-
end
|
1003
|
-
|
1004
|
-
def gensym(name="__tmp")
|
1005
|
-
@gensym_counter ||= 0
|
1006
|
-
@gensym_counter += 1
|
1007
|
-
"#{name}_#{@gensym_counter}"
|
1008
|
-
end
|
1009
|
-
|
1010
|
-
def include_source_metadata
|
1011
|
-
# disabled for now -- we're still getting broken rendering with this feature on
|
1012
|
-
return false
|
1013
|
-
@include_source_metadata = RAILS_ENV == "development" && !ENV['DRYML_EDITOR'].blank? if @include_source_metadata.nil?
|
1014
|
-
@include_source_metadata
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
end
|
1018
|
-
|
1019
|
-
end
|