masterview 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +31 -1
- data/README +70 -69
- data/RELEASE_NOTES +70 -64
- data/Rakefile +26 -27
- data/TODO +13 -29
- data/doc/about.html +246 -0
- data/doc/configuration.html +49 -36
- data/doc/developer.html +423 -41
- data/doc/directives.html +139 -51
- data/doc/guide.html +19 -9
- data/doc/index.html +90 -224
- data/doc/installation.html +36 -28
- data/doc/media_list.html +30 -20
- data/doc/simple_diagram.html +3 -5
- data/doc/stylesheets/masterview.css +16 -1
- data/examples/rails_app_config/masterview/settings.rb +2 -1
- data/init.rb +1 -1
- data/lib/#ChangeLog# +6 -0
- data/lib/masterview/analyzer.rb +48 -34
- data/lib/masterview/attr_string_parser.rb +5 -1
- data/lib/masterview/case_insensitive_hash.rb +69 -0
- data/lib/masterview/{pathname_extensions.rb → core_ext/pathname.rb} +0 -0
- data/lib/masterview/{string_extensions.rb → core_ext/string.rb} +0 -0
- data/lib/masterview/deprecated/directive_base.rb +362 -0
- data/lib/masterview/directive_base.rb +201 -179
- data/lib/masterview/directive_dsl.rb +457 -0
- data/lib/masterview/directive_helpers.rb +28 -141
- data/lib/masterview/directive_load_path.rb +388 -0
- data/lib/masterview/directive_metadata.rb +377 -0
- data/lib/masterview/directive_registry.rb +259 -69
- data/lib/masterview/directives/.metadata +16 -0
- data/lib/masterview/directives/attr.rb +9 -8
- data/lib/masterview/directives/block.rb +11 -14
- data/lib/masterview/directives/check_box.rb +13 -18
- data/lib/masterview/directives/collection_select.rb +15 -29
- data/lib/masterview/directives/content.rb +9 -3
- data/lib/masterview/directives/else.rb +15 -13
- data/lib/masterview/directives/elsif.rb +14 -13
- data/lib/masterview/directives/eval.rb +20 -0
- data/lib/masterview/directives/form.rb +56 -9
- data/lib/masterview/directives/form_remote.rb +26 -0
- data/lib/masterview/directives/global_inline_erb.rb +10 -14
- data/lib/masterview/directives/hidden_field.rb +11 -20
- data/lib/masterview/directives/if.rb +13 -12
- data/lib/masterview/directives/image_tag.rb +20 -28
- data/lib/masterview/directives/import.rb +5 -12
- data/lib/masterview/directives/import_render.rb +7 -19
- data/lib/masterview/directives/insert_generated_comment.rb +8 -11
- data/lib/masterview/directives/javascript_include.rb +21 -12
- data/lib/masterview/directives/link_to.rb +14 -8
- data/lib/masterview/directives/link_to_function.rb +22 -0
- data/lib/masterview/directives/link_to_if.rb +15 -13
- data/lib/masterview/directives/link_to_remote.rb +13 -8
- data/lib/masterview/directives/omit_tag.rb +32 -16
- data/lib/masterview/directives/password_field.rb +10 -22
- data/lib/masterview/directives/radio_button.rb +11 -22
- data/lib/masterview/directives/replace.rb +7 -8
- data/lib/masterview/directives/select.rb +11 -24
- data/lib/masterview/directives/stylesheet_link.rb +20 -12
- data/lib/masterview/directives/submit.rb +11 -5
- data/lib/masterview/directives/text_area.rb +10 -23
- data/lib/masterview/directives/text_field.rb +10 -22
- data/lib/masterview/exceptions.rb +21 -0
- data/lib/masterview/extras/app/controllers/masterview_controller.rb +102 -75
- data/lib/masterview/extras/app/views/layouts/masterview_admin.rhtml +24 -23
- data/lib/masterview/extras/app/views/layouts/masterview_admin_config.rhtml +81 -0
- data/lib/masterview/extras/app/views/masterview/admin/configuration.rhtml +5 -1
- data/lib/masterview/extras/app/views/masterview/admin/create.rhtml +2 -2
- data/lib/masterview/extras/app/views/masterview/admin/directives.rhtml +5 -0
- data/lib/masterview/extras/app/views/masterview/admin/features.rhtml +5 -79
- data/lib/masterview/extras/app/views/masterview/admin/interact.rhtml +5 -0
- data/lib/masterview/extras/app/views/masterview/admin/list.rhtml +3 -71
- data/lib/masterview/extras/init_mv_admin_pages.rb +42 -23
- data/lib/masterview/filter_helpers.rb +26 -0
- data/lib/masterview/initializer.rb +99 -53
- data/lib/masterview/io.rb +19 -15
- data/lib/masterview/keyword_expander.rb +7 -2
- data/lib/masterview/masterview_info.rb +229 -23
- data/lib/masterview/masterview_version.rb +2 -2
- data/lib/masterview/parser.rb +275 -105
- data/lib/masterview/parser_helpers.rb +54 -0
- data/lib/masterview/rails_ext/action_controller_erb_direct.rb +29 -0
- data/lib/masterview/rails_ext/action_controller_reparse_checking.rb +27 -0
- data/lib/masterview/{extras/init_rails_erb_mv_direct.rb → rails_ext/action_view_erb_direct.rb} +12 -59
- data/lib/masterview/template_spec.rb +3 -2
- data/lib/masterview.rb +21 -12
- data/lib/rexml/parsers/baseparser_with_doctype_fix.rb +473 -0
- data/lib/rexml/parsers/sax2parser_with_doctype_fix.rb +243 -0
- data/test/directive_test_helper.rb +135 -0
- data/test/fixtures/directives/id_check.rb +18 -0
- data/test/fixtures/directives/test_directive_events.rb +70 -0
- data/test/test_helper.rb +18 -5
- data/test/tmp/views/layouts/product.rhtml +10 -10
- data/test/tmp/views/product/_form.rhtml +4 -4
- data/test/tmp/views/product/_product.rhtml +3 -3
- data/test/tmp/views/product/destroy.rhtml +5 -5
- data/test/tmp/views/product/edit.rhtml +4 -4
- data/test/tmp/views/product/list.rhtml +3 -3
- data/test/tmp/views/product/new.rhtml +4 -4
- data/test/tmp/views/product/show.rhtml +2 -2
- data/test/unit/attr_string_parser_test.rb +105 -0
- data/test/unit/case_insensitive_hash_mod_test.rb +104 -0
- data/test/unit/config_settings_test.rb +13 -1
- data/test/unit/default_generate_mio_filter_test.rb +3 -3
- data/test/unit/deprecated_directive_base_test.rb +30 -0
- data/test/unit/directive_attr_test.rb +111 -35
- data/test/unit/directive_base_test.rb +520 -1
- data/test/unit/directive_block_test.rb +30 -22
- data/test/unit/directive_content_test.rb +24 -11
- data/test/unit/directive_else_test.rb +18 -15
- data/test/unit/directive_elsif_test.rb +17 -15
- data/test/unit/directive_form_remote_test.rb +59 -0
- data/test/unit/directive_form_test.rb +31 -39
- data/test/unit/directive_global_inline_erb_test.rb +28 -17
- data/test/unit/directive_grid_test_notready.rb +38 -0
- data/test/unit/directive_helpers_test.rb +39 -0
- data/test/unit/directive_hidden_field_test.rb +44 -29
- data/test/unit/directive_if_test.rb +10 -7
- data/test/unit/directive_image_tag_test.rb +69 -61
- data/test/unit/directive_import_render_test.rb +28 -38
- data/test/unit/directive_import_test.rb +16 -14
- data/test/unit/directive_insert_generated_comment_test.rb +32 -0
- data/test/unit/directive_javascript_include_test.rb +40 -43
- data/test/unit/directive_link_to_function_test.rb +40 -0
- data/test/unit/directive_link_to_if_test.rb +52 -12
- data/test/unit/directive_link_to_remote_test.rb +58 -0
- data/test/unit/directive_link_to_test.rb +46 -31
- data/test/unit/directive_load_path_test.rb +257 -0
- data/test/unit/directive_metadata_test.rb +313 -0
- data/test/unit/directive_omit_tag_test.rb +73 -21
- data/test/unit/directive_password_field_test.rb +44 -38
- data/test/unit/directive_registry_test.rb +44 -0
- data/test/unit/directive_replace_test.rb +28 -12
- data/test/unit/directive_stylesheet_link_test.rb +43 -36
- data/test/unit/directive_submit_test.rb +29 -30
- data/test/unit/directive_text_area_test.rb +40 -36
- data/test/unit/directive_text_field_test.rb +44 -38
- data/test/unit/example_directive_child_events_test.rb +41 -0
- data/test/unit/example_test.rb +31 -4
- data/test/unit/file_mio_test.rb +18 -13
- data/test/unit/filter_helpers_test.rb +10 -8
- data/test/unit/find_directive_parent_test.rb +174 -0
- data/test/unit/keyword_expander_test.rb +4 -2
- data/test/unit/mio_test.rb +18 -11
- data/test/unit/mtime_string_hash_mio_tree_test.rb +5 -1
- data/test/unit/parser_test.rb +41 -29
- data/test/unit/pathname_extensions_test.rb +1 -1
- data/test/unit/run_parser_test.rb +2 -2
- data/test/unit/simplified_directive_base_test.rb +256 -0
- data/test/unit/string_hash_mio_test.rb +5 -1
- data/test/unit/template_file_watcher_test.rb +2 -2
- data/test/unit/template_test.rb +221 -46
- metadata +86 -45
- data/lib/masterview/directives/testfilter.rb +0 -55
- data/lib/masterview/extras/init_rails_reparse_checking.rb +0 -62
@@ -0,0 +1,457 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Directive DSL
|
4
|
+
|
5
|
+
class MyDirective < DirectiveBase
|
6
|
+
attr_arg :obj, :quote => true
|
7
|
+
attr_arg :method { 'override' }
|
8
|
+
attr_arg :foo { |x| x.downcase }
|
9
|
+
attr_arg :collection, :quote => true
|
10
|
+
attr_arg :value_method, :quote => true
|
11
|
+
attr_arg :text_method, :quote => true
|
12
|
+
attr_arg :options, :default => {}
|
13
|
+
attr_arg :html_options, :append_element_attrs => [:common_html, :size]
|
14
|
+
attr_arg :name do |value, args|
|
15
|
+
if value.respond_to?(:include?) and value.include?('=>')
|
16
|
+
args.unshift value
|
17
|
+
nil
|
18
|
+
else
|
19
|
+
value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
attr_arg :params, :varargs => true #collect remaining args (if any) put in array, like *params
|
23
|
+
|
24
|
+
event :before_stag do
|
25
|
+
render erb_content( 'text_area', :obj, :method, :foo )
|
26
|
+
end
|
27
|
+
|
28
|
+
event :content, :render => :nothing #rolls up all content between after_stag and before_etag
|
29
|
+
|
30
|
+
also can be done
|
31
|
+
|
32
|
+
event :content do
|
33
|
+
render :nothing # causes nothing to be rendered
|
34
|
+
end
|
35
|
+
|
36
|
+
event :stag do
|
37
|
+
# unless render something or render :nothing is called, then dcs.render will still occur implicitely
|
38
|
+
end
|
39
|
+
|
40
|
+
event :etag do
|
41
|
+
con = render_result() # get the dcs.render result so we can modify it
|
42
|
+
con.gsub!( /foo/, 'bar' )
|
43
|
+
render con
|
44
|
+
end
|
45
|
+
|
46
|
+
event :element do # this will allow replacement of entire element and children
|
47
|
+
render erb_eval( 'execute something' )
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
event listing
|
52
|
+
----------------
|
53
|
+
before_stag
|
54
|
+
stag
|
55
|
+
after_stag
|
56
|
+
content
|
57
|
+
before_etag
|
58
|
+
etag
|
59
|
+
after_etag
|
60
|
+
element
|
61
|
+
|
62
|
+
these might not be exposed as events but still using direct methods
|
63
|
+
characters
|
64
|
+
comment
|
65
|
+
|
66
|
+
|
67
|
+
=end
|
68
|
+
|
69
|
+
module MasterView
|
70
|
+
|
71
|
+
# The DirectiveProcessing module contains the internal mechanisms
|
72
|
+
# which support the DirectiveDSL notation for attribute argument definition
|
73
|
+
# and document processing event processing definition in directive
|
74
|
+
# implementation classes.
|
75
|
+
module DirectiveProcessing
|
76
|
+
|
77
|
+
# holds the attr_arg definition, how a positional subargument will be
|
78
|
+
# parsed out of the directive attr_value
|
79
|
+
class AttrArgDef
|
80
|
+
attr_reader :name, :options, :proc
|
81
|
+
def initialize(name, options, proc)
|
82
|
+
@name = name
|
83
|
+
@options = options
|
84
|
+
@proc = proc
|
85
|
+
end
|
86
|
+
|
87
|
+
# returns the optional default if one was specified
|
88
|
+
def optional_default
|
89
|
+
(@options.nil?) ? nil : @options[:default]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# holds the event definition which contains the meat of what will happen
|
94
|
+
# for each parser event. The events are more finely grained than the rexml
|
95
|
+
# listener events.
|
96
|
+
class EventDef
|
97
|
+
BaseEventMapping = {
|
98
|
+
/^(before_|after_)?(child_.*|descendant_.*)?stag$/ => ['\2stag'],
|
99
|
+
/^(before_|after_)?(child_.*|descendant_.*)?etag$/ => ['\2etag'],
|
100
|
+
/^(child_.*|descendant_.*)?content$/ => ['\1etag'],
|
101
|
+
/^(child_.*|descendant_.*)?element$/ => ['\1stag', '\1etag']
|
102
|
+
}
|
103
|
+
|
104
|
+
# true if is valid event name
|
105
|
+
def self.valid_event_mapping?(event_name_sym)
|
106
|
+
event_name_str = event_name_sym.to_s
|
107
|
+
BaseEventMapping.keys.any? { |x| x =~ event_name_str }
|
108
|
+
end
|
109
|
+
|
110
|
+
# returns base method sym array for event_name_sym
|
111
|
+
# :before_stag => :stag,
|
112
|
+
# :stag => :stag,
|
113
|
+
# :after_stag => :stag,
|
114
|
+
# :content => :etag,
|
115
|
+
# :before_etag => :etag,
|
116
|
+
# :etag => :etag,
|
117
|
+
# :after_etag => :etag,
|
118
|
+
# :element => :stag, :etag
|
119
|
+
def self.base_methods_from_event(event_name_sym)
|
120
|
+
event_name_str = event_name_sym.to_s
|
121
|
+
base_pair = BaseEventMapping.find {|k,v| k =~ event_name_str }
|
122
|
+
base_name_unsub_array = base_pair[1]
|
123
|
+
base_name_unsub_array.collect { |x| event_name_str.sub(base_pair[0], x).to_sym }
|
124
|
+
end
|
125
|
+
|
126
|
+
# return array of events to check for the base
|
127
|
+
# :stag => [:before_stag, :stag, :after_stag],
|
128
|
+
# :etag => [:content, :before_etag, :etag, :after_etag, :element]
|
129
|
+
def self.method_events_from_base(base_sym)
|
130
|
+
base_str = base_sym.to_s
|
131
|
+
if match = base_str.match( /(.*)stag/ )
|
132
|
+
[
|
133
|
+
('before_'+base_str).to_sym,
|
134
|
+
base_sym,
|
135
|
+
('after_'+base_str).to_sym
|
136
|
+
]
|
137
|
+
elsif match = base_str.match( /(.*)etag/ )
|
138
|
+
[
|
139
|
+
(match[1]+'content').to_sym,
|
140
|
+
('before_'+base_str).to_sym,
|
141
|
+
base_sym,
|
142
|
+
('after_'+base_str).to_sym,
|
143
|
+
(match[1]+'element').to_sym
|
144
|
+
]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
attr_reader :name, :options, :proc
|
149
|
+
|
150
|
+
def initialize(name, options, proc)
|
151
|
+
raise "Invalid event name=#{name}" unless self.class.valid_event_mapping?(name)
|
152
|
+
@name = name
|
153
|
+
@options = options
|
154
|
+
@proc = proc
|
155
|
+
end
|
156
|
+
|
157
|
+
# execute proc/block in the context of an instance object (instance_eval)
|
158
|
+
def exec(instance_obj)
|
159
|
+
instance_obj.instance_eval &proc unless proc.nil?
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class DirectiveClassDef
|
164
|
+
attr_reader :attr_arg_defs, :event_defs
|
165
|
+
def initialize
|
166
|
+
@attr_arg_defs = [] #list in order of position
|
167
|
+
@attr_arg_def_map = {} #map to def from symname
|
168
|
+
@event_defs = {}
|
169
|
+
end
|
170
|
+
|
171
|
+
def add_attr_arg_def(attr_arg_def)
|
172
|
+
if @attr_arg_def_map[attr_arg_def.name].nil? # prevent multiple calls to this, rake calling multiple times?
|
173
|
+
@attr_arg_def_map[attr_arg_def.name] = attr_arg_def
|
174
|
+
@attr_arg_defs.push attr_arg_def
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def set_event_def(event_def)
|
179
|
+
@event_defs[event_def.name] = event_def
|
180
|
+
end
|
181
|
+
|
182
|
+
def find_attr_arg_def_by_name(name)
|
183
|
+
@attr_arg_def_map[name]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class RenderAccumulator
|
188
|
+
attr_accessor :render_call_found
|
189
|
+
attr_writer :render_result, :render_result_block
|
190
|
+
def initialize
|
191
|
+
@method_level_accumulator = []
|
192
|
+
reset_event_level_vars
|
193
|
+
end
|
194
|
+
|
195
|
+
def reset_event_level_vars
|
196
|
+
reset_event_level_content_array
|
197
|
+
@render_call_found = false
|
198
|
+
@render_result = nil
|
199
|
+
@render_result_block = nil
|
200
|
+
end
|
201
|
+
|
202
|
+
def reset_event_level_content_array
|
203
|
+
@event_level_accumulator = []
|
204
|
+
end
|
205
|
+
|
206
|
+
def reset_event_level_content_array_render_nothing
|
207
|
+
reset_event_level_content_array
|
208
|
+
@render_call_found = true # we don't want to render anything
|
209
|
+
end
|
210
|
+
|
211
|
+
def method_level_content_array
|
212
|
+
@method_level_accumulator
|
213
|
+
end
|
214
|
+
|
215
|
+
def event_level_content_array
|
216
|
+
@event_level_accumulator
|
217
|
+
end
|
218
|
+
|
219
|
+
def add_event_content_to_method_content
|
220
|
+
@method_level_accumulator.concat @event_level_accumulator
|
221
|
+
@event_level_accumulator = []
|
222
|
+
end
|
223
|
+
|
224
|
+
def replace_all_method_content_with_event_content
|
225
|
+
@method_level_accumulator = @event_level_accumulator
|
226
|
+
@event_level_accumulator = []
|
227
|
+
end
|
228
|
+
|
229
|
+
def call_render_result_block
|
230
|
+
result = nil
|
231
|
+
unless @render_result_block.nil?
|
232
|
+
result = @render_result_block.call
|
233
|
+
@render_result_block = nil #only allow this call once
|
234
|
+
end
|
235
|
+
result
|
236
|
+
end
|
237
|
+
|
238
|
+
def add_to_event_content(value)
|
239
|
+
@render_call_found = true
|
240
|
+
@event_level_accumulator.push(value)
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
# Mixin for directive implementation classes to support the DSL for
|
248
|
+
# attr_arg attribute value argument parsing and event handler
|
249
|
+
# registration for document template event processing.
|
250
|
+
#
|
251
|
+
# Relies on the mixing class to provide accessor services to
|
252
|
+
# element processing context information (element_attrs, element_tag)
|
253
|
+
# and the attr_value directive attribute value string,
|
254
|
+
# along with erb output services (erb_content, erb_eval).
|
255
|
+
# Assumes DirectiveHelpers also mixed in.
|
256
|
+
#
|
257
|
+
module DirectiveDSL
|
258
|
+
|
259
|
+
module ClassMethods
|
260
|
+
# retrieve my class specific DirectiveClassDef, creating if necessary
|
261
|
+
def directive_class_def
|
262
|
+
@@directive_class_defs ||= {}
|
263
|
+
@@directive_class_defs[object_id] ||= DirectiveProcessing::DirectiveClassDef.new
|
264
|
+
end
|
265
|
+
|
266
|
+
# add a postional subargument parsing def which will be applied to attr_value
|
267
|
+
def attr_arg(name, options={}, &block)
|
268
|
+
self.directive_class_def.add_attr_arg_def(DirectiveProcessing::AttrArgDef.new(name, options, block))
|
269
|
+
end
|
270
|
+
|
271
|
+
# set the definition for an event which will be invoked at appropriate time in
|
272
|
+
# directive rendering lifecycle, also create the base method if it does not
|
273
|
+
# already exist.
|
274
|
+
def event(name, options={}, &block)
|
275
|
+
directive_class_def = self.directive_class_def
|
276
|
+
directive_class_def.set_event_def(DirectiveProcessing::EventDef.new(name, options, block))
|
277
|
+
base_event_name_sym_array = DirectiveProcessing::EventDef.base_methods_from_event(name)
|
278
|
+
base_event_name_sym_array.each do |base_event_name_sym|
|
279
|
+
base_event_name = base_event_name_sym.to_s
|
280
|
+
directive_class_def.set_event_def(DirectiveProcessing::EventDef.new(base_event_name_sym,{},nil)) if directive_class_def.event_defs[base_event_name_sym].nil?
|
281
|
+
event_mapping = DirectiveProcessing::EventDef.method_events_from_base(base_event_name_sym)
|
282
|
+
unless self.class.respond_to?( base_event_name_sym ) or event_mapping.nil? # unless already defined method or no mapping
|
283
|
+
event_eval_code = <<-END
|
284
|
+
def #{base_event_name}(dcs)
|
285
|
+
@method_content_ref ||= {} # create references to content created by method
|
286
|
+
prepare_arg_instance_vars
|
287
|
+
@render_accumulator = DirectiveProcessing::RenderAccumulator.new
|
288
|
+
#{event_mapping.inspect}.each do |full_event_name|
|
289
|
+
event_def = self.class.directive_class_def.event_defs[full_event_name]
|
290
|
+
unless event_def.nil?
|
291
|
+
prepare(event_def)
|
292
|
+
event_def.exec(self)
|
293
|
+
handle_render(event_def)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
@method_content_ref[:#{base_event_name}] = @render_accumulator.method_level_content_array # add reference
|
297
|
+
@method_content_ref[:#{base_event_name}] # return the reference so we can empty later if necessary
|
298
|
+
end
|
299
|
+
END
|
300
|
+
self.class_eval event_eval_code # todo could pass in filename, but would have to use self.to_s.downcase and trim off leading modules
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# add class methods for DSL declarations to class which is mixing in DirectiveDSL
|
307
|
+
def self.included(mixing_class) #:nodoc:
|
308
|
+
mixing_class.extend(ClassMethods)
|
309
|
+
end
|
310
|
+
|
311
|
+
def prepare_arg_instance_vars
|
312
|
+
attr_arg_defs = self.class.directive_class_def.attr_arg_defs
|
313
|
+
unless attr_arg_defs.empty? # if we have any attr_arg_defs, prepare the parsed instance vars for these args
|
314
|
+
args = parse(self.attr_value)
|
315
|
+
attr_arg_defs.each do |attr_arg_def|
|
316
|
+
eval_attr_arg(args, attr_arg_def)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def prepare(event_def)
|
322
|
+
@render_accumulator.reset_event_level_vars
|
323
|
+
case event_def.name.to_s
|
324
|
+
when /^before_(.*_)?stag$/
|
325
|
+
when /^after_(.*_)?stag$/
|
326
|
+
when /^(.*_)?stag$/
|
327
|
+
@render_accumulator.render_result_block = lambda { @render_accumulator.render_result = @directive_call_stack.render }
|
328
|
+
when /^(.*_)?content$/
|
329
|
+
@render_accumulator.render_result_block = lambda { @directive_call_stack.context[:tag].content.join }
|
330
|
+
when /^before_(.*_)?etag$/
|
331
|
+
when /^after_(.*_)?etag$/
|
332
|
+
when /^(.*_)?etag$/
|
333
|
+
@render_accumulator.render_result_block = lambda { @render_accumulator.render_result = @directive_call_stack.render }
|
334
|
+
when /^(.*_)?element$/
|
335
|
+
@render_accumulator.render_result_block = lambda { (
|
336
|
+
@method_content_ref[event_def.name.to_s.sub(/^(.*_)?element$/, '\1stag').to_sym] + # fine proper stag content ref
|
337
|
+
@directive_call_stack.context[:tag].content +
|
338
|
+
@render_accumulator.event_level_content_array).join }
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def render_result
|
343
|
+
@render_accumulator.call_render_result_block
|
344
|
+
end
|
345
|
+
|
346
|
+
def render(value)
|
347
|
+
if value == :nothing
|
348
|
+
@render_accumulator.reset_event_level_content_array_render_nothing
|
349
|
+
else
|
350
|
+
@render_accumulator.add_to_event_content(value)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def handle_render(event_def)
|
355
|
+
return if event_def.options[:render] == :nothing and (event_def.name != :content and event_def.name != :element)
|
356
|
+
case event_def.name.to_s
|
357
|
+
when /^before_(.*_)?stag$/
|
358
|
+
@render_accumulator.add_event_content_to_method_content
|
359
|
+
when /^after_(.*_)?stag$/
|
360
|
+
@render_accumulator.add_event_content_to_method_content
|
361
|
+
when /^(.*_)?stag$/
|
362
|
+
render(render_result) unless @render_accumulator.render_call_found # call default dcs.render if render call not made
|
363
|
+
@render_accumulator.add_event_content_to_method_content
|
364
|
+
when /^(.*_)?content$/
|
365
|
+
if event_def.options[:render] == :nothing
|
366
|
+
@directive_call_stack.context[:tag].content = nil
|
367
|
+
else
|
368
|
+
@directive_call_stack.context[:tag].content = @render_accumulator.event_level_content_array
|
369
|
+
end
|
370
|
+
when /^before_(.*_)?etag$/
|
371
|
+
@render_accumulator.add_event_content_to_method_content
|
372
|
+
when /^after_(.*_)?etag$/
|
373
|
+
@render_accumulator.add_event_content_to_method_content
|
374
|
+
when /^(.*_)?etag$/
|
375
|
+
render(render_result) unless @render_accumulator.render_call_found # call default dcs.render if render call not made
|
376
|
+
@render_accumulator.add_event_content_to_method_content
|
377
|
+
when /^(.*_)?element$/ # we need to affect only things on and below our callstack so surrounging directives will render (like if)
|
378
|
+
@method_content_ref[event_def.name.to_s.sub(/^(.*_)?element$/, '\1stag').to_sym].clear # we will zero the stag reference which will clear it from the output
|
379
|
+
@directive_call_stack.context[:tag].content = nil
|
380
|
+
@render_accumulator.reset_event_level_content_array if event_def.options[:render] == :nothing # zero content if render :nothing
|
381
|
+
@render_accumulator.replace_all_method_content_with_event_content # replace everything with event content
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
# convert symbol name to instance var name, it appends a @ if not
|
386
|
+
# already starting with it. Handles strings or symbols.
|
387
|
+
# returns symbol of complete name :@name
|
388
|
+
def name_to_instance_var_name(name)
|
389
|
+
str_name = name.to_s
|
390
|
+
str_name = '@'+str_name unless str_name[0] == '@'
|
391
|
+
str_name.to_sym
|
392
|
+
end
|
393
|
+
|
394
|
+
# evaluate the attr_arg using the current attr_value, attributes, etc.
|
395
|
+
# This is the method that takes the definition, determines the instance value
|
396
|
+
# and stores it.
|
397
|
+
def eval_attr_arg(args, attr_arg_def)
|
398
|
+
name = attr_arg_def.name
|
399
|
+
options = attr_arg_def.options
|
400
|
+
proc = attr_arg_def.proc
|
401
|
+
instance_var_name = name_to_instance_var_name(name)
|
402
|
+
if(options and options[:varargs])
|
403
|
+
value = args.clone # value set to array of all remaining args
|
404
|
+
args.clear
|
405
|
+
else
|
406
|
+
value = args.shift
|
407
|
+
if options
|
408
|
+
value = quote_if(value) if options[:quote]
|
409
|
+
if merge_array = options[:append_element_attrs]
|
410
|
+
merge_array = [merge_array] unless merge_array.is_a? Array
|
411
|
+
merge_opts = {}
|
412
|
+
merge_array.each do |merge_item|
|
413
|
+
if merge_item == :common_html
|
414
|
+
merge_opts.merge! common_html_options
|
415
|
+
else
|
416
|
+
merge_item_sym = merge_item.to_sym
|
417
|
+
if (v = element_attrs[merge_item_sym]) : merge_opts[merge_item_sym] = v; end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
value = merge_hash_into_str(merge_opts, value)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
unless proc.nil?
|
425
|
+
if proc.arity < 1 # simply set value to block's return
|
426
|
+
value = proc.call
|
427
|
+
elsif proc.arity == 1 # |x| - pass value into block to allow it to manipulate
|
428
|
+
value = proc.call(value)
|
429
|
+
elsif proc.arity == 2 # |value, args| - pass value and remaining args array
|
430
|
+
value = proc.call(value, args)
|
431
|
+
elsif proc.arity == 3 # |value, args, instance| - pass value, remaining args, and directive instance
|
432
|
+
value = proc.call(value, args, self)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
self.instance_variable_set(instance_var_name, value) # set @foo = value
|
436
|
+
end
|
437
|
+
|
438
|
+
# safely checks for existence of instance variable without throwing NameError
|
439
|
+
# true if instance_variable exists
|
440
|
+
def instance_variable_exists?(instance_variable_name)
|
441
|
+
self.instance_variables.include?(instance_variable_name.to_s)
|
442
|
+
end
|
443
|
+
|
444
|
+
#inside characters, cdata, or comment you can call this to get the characters passed
|
445
|
+
def data
|
446
|
+
@directive_call_stack.context[:content_part]
|
447
|
+
end
|
448
|
+
|
449
|
+
#set the data that will be passed to characters, cdata, or comment directives
|
450
|
+
def data=(data)
|
451
|
+
@directive_call_stack.context[:content_part]=data
|
452
|
+
end
|
453
|
+
|
454
|
+
|
455
|
+
end
|
456
|
+
|
457
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module MasterView
|
2
2
|
|
3
3
|
# Mixin services for directive implementation classes.
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# Subclasses of MasterView::DirectiveBase inherit this mixin.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
module DirectiveHelpers
|
8
8
|
|
9
9
|
CRLF = "\r\n"
|
@@ -18,27 +18,6 @@ module MasterView
|
|
18
18
|
# end of ERB whose evaluation results in content in the document
|
19
19
|
ERB_CONTENT_END = ' %>'
|
20
20
|
|
21
|
-
####OBSOLETE: SWEEP AND REMOVE
|
22
|
-
ERB_START = ERB_EVAL_START #:nodoc: #WAS: '<% '
|
23
|
-
ERB_EVAL = ERB_CONTENT_START #:nodoc: #WAS: '<%= '
|
24
|
-
ERB_END = ERB_CONTENT_END #:nodoc: #WAS: ' %>'
|
25
|
-
|
26
|
-
#convenience constants defined to allow priority to directives
|
27
|
-
#higher priority (lower value) will be executed first in chain
|
28
|
-
module DirectivePriorities
|
29
|
-
Highest = 0
|
30
|
-
UltraHigh = 0x3FFFFFFF/16
|
31
|
-
VeryHigh = 0x3FFFFFFF/8
|
32
|
-
High = 0x3FFFFFFF/4
|
33
|
-
MediumHigh = 0x3FFFFFFF/3
|
34
|
-
Medium = 0x3FFFFFFF/2
|
35
|
-
MediumLow = (0x3FFFFFFF/3)*2
|
36
|
-
Low = (0x3FFFFFFF/4)*3
|
37
|
-
VeryLow = (0x3FFFFFFF/8)*7
|
38
|
-
UltraLow = (0x3FFFFFFF/16)*15
|
39
|
-
Lowest = 0x3FFFFFFF
|
40
|
-
end
|
41
|
-
|
42
21
|
# convert render_partial_name to file_name, ex foo/bar to foo/_bar.rhtml
|
43
22
|
def render_partial_name_to_file_name(render_partial_name, default_extension)
|
44
23
|
pathname = Pathname.for_path(render_partial_name)
|
@@ -49,22 +28,32 @@ module MasterView
|
|
49
28
|
path = (dir_pathname+filename).to_s
|
50
29
|
end
|
51
30
|
|
52
|
-
|
53
|
-
# find the last string that fully matches exactly the
|
31
|
+
|
32
|
+
# find the last string that fully matches exactly the
|
54
33
|
# parent tags content string array
|
55
|
-
# It looks for something that has been output as a unit in
|
34
|
+
# It looks for something that has been output as a unit in
|
56
35
|
# the array not a substring
|
57
|
-
# returns the ref to the string which you can operate on
|
36
|
+
# returns the ref to the string which you can operate on
|
58
37
|
# using replace
|
59
38
|
def find_last_in_parent(tag, full_string)
|
60
39
|
ret = nil
|
61
40
|
parent = tag.parent
|
62
41
|
unless parent.nil?
|
63
42
|
parent.content.reverse.each do |str|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
43
|
+
if str.kind_of? Array # if it is a nested array check inside of it
|
44
|
+
str.reverse.each do |s|
|
45
|
+
if s == full_string
|
46
|
+
ret = s
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
break if ret
|
51
|
+
else
|
52
|
+
if str == full_string
|
53
|
+
ret = str
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
68
57
|
end
|
69
58
|
end
|
70
59
|
ret
|
@@ -89,124 +78,22 @@ module MasterView
|
|
89
78
|
m[1]
|
90
79
|
end
|
91
80
|
|
92
|
-
#returns an array of args by parsing and evaling the str value passed in
|
93
|
-
#uses evaling, so can't have any variables only simple strings, numbers, booleans
|
94
|
-
def parse_eval_into_array(value)
|
95
|
-
return [] if value.nil? || value.empty?
|
96
|
-
val = value.strip
|
97
|
-
args = []
|
98
|
-
until val.empty?
|
99
|
-
if val =~ /^[:'"%\[{&*]/ #starts with quote or ruby lang char
|
100
|
-
v = nil
|
101
|
-
val = '{'+val+'}' if val =~ /^:/ #starts with colon, assume hash so wrap with brackets
|
102
|
-
eval 'v = '+ val #rest is all evaled
|
103
|
-
if v.is_a? Array
|
104
|
-
args += v
|
105
|
-
else
|
106
|
-
args << v
|
107
|
-
end
|
108
|
-
break
|
109
|
-
else
|
110
|
-
unquoted_string = val.slice!( /^[^,]+/ ) #pull off everything up to a comma
|
111
|
-
unquoted_string.strip!
|
112
|
-
args.push unquoted_string
|
113
|
-
val.slice!( /^,/ ) #strip off comma if exists
|
114
|
-
val.strip!
|
115
|
-
end
|
116
|
-
end
|
117
|
-
args
|
118
|
-
end
|
119
|
-
|
120
|
-
#returns a hash, for values that are not already part of hash it adds them using default_key
|
121
|
-
#uses evaling so it cannot have any variables or non-simple types
|
122
|
-
def parse_eval_into_hash(value, default_key)
|
123
|
-
h = {}
|
124
|
-
a = parse_eval_into_array(value)
|
125
|
-
a.each do |v|
|
126
|
-
if v.is_a?(Hash)
|
127
|
-
h.merge!(v)
|
128
|
-
else #it adds any additional non-hash args using default key, if key,val exists, it changes to array and appends
|
129
|
-
prev = h[default_key]
|
130
|
-
if prev.nil? #nil just add it
|
131
|
-
h[default_key] = v
|
132
|
-
elsif prev.is_a?(Array) #was array, concat
|
133
|
-
h[default_key] = prev+v
|
134
|
-
else #anything else, make it into array
|
135
|
-
h[default_key] = [prev, v]
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
h
|
140
|
-
end
|
141
|
-
|
142
81
|
#parse into array of strings, containing the various arguments without evaling
|
143
82
|
#looks for %q{}, %q[], hash, array, function call using (), values deliminated by commas,
|
144
83
|
def parse(str)
|
145
84
|
AttrStringParser.parse(str)
|
146
85
|
end
|
147
86
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
def remove_prepended_strings(full_string)
|
152
|
-
return full_string if full_string.nil? || full_string.strip.empty?
|
153
|
-
hashes = full_string.scan( /(\{?)\s*(\S+\s*=>.*)/ ).flatten
|
154
|
-
hashes.join.strip
|
155
|
-
end
|
156
|
-
|
157
|
-
#merge hash_to_merge values into the hash contained in the full_string, hash_arg is zero based index of which
|
158
|
-
#hash this needes to be merged to if there are multiple ones.
|
159
|
-
def merge_into_embedded_hash(full_string, hash_arg, hash_to_merge)
|
160
|
-
return full_string if hash_to_merge.empty?
|
161
|
-
full_string ||= ""
|
162
|
-
sorted_hash_to_merge = hash_to_merge.sort { |a,b| a.to_s <=> b.to_s } #sort, remember the keys might be symbols so use to_s
|
163
|
-
str_to_merge = sorted_hash_to_merge.collect{ |h,v| "#{h.inspect} => #{v.inspect}" }.join(', ')
|
164
|
-
|
165
|
-
hashes = full_string.scan( /(\{?[^{}]+=>[^{}]+\}?)\s*,?\s*/ ).flatten
|
166
|
-
hash_str = hashes[hash_arg] #be careful to use methods to update string in place or else put back in hash
|
167
|
-
|
168
|
-
if hash_str.nil?
|
169
|
-
hashes.each do |v| #make sure each prior hash has brackets, since we are adding a hash
|
170
|
-
unless v.index '}'
|
171
|
-
v.insert(0, '{')
|
172
|
-
v.insert(-1, '}')
|
173
|
-
end
|
174
|
-
end
|
175
|
-
hashes[hash_arg] = hash_str = ""
|
176
|
-
end
|
177
|
-
|
178
|
-
closing_brack = hash_str.index '}'
|
179
|
-
if closing_brack
|
180
|
-
hash_str.insert(closing_brack, ', '+str_to_merge)
|
181
|
-
else
|
182
|
-
hash_str << ', ' unless hash_str.empty?
|
183
|
-
hash_str << str_to_merge
|
184
|
-
end
|
185
|
-
|
186
|
-
hashes.join(', ')
|
187
|
-
end
|
188
|
-
|
189
|
-
#return attributes with lowercase keys
|
190
|
-
def lowercase_attribute_keys(attributes)
|
191
|
-
lcattrs = {}
|
192
|
-
attributes.each { |k,v| lcattrs[k.downcase] = v }
|
193
|
-
lcattrs
|
194
|
-
end
|
195
|
-
|
196
|
-
#return attributes with lowercase keys and values
|
197
|
-
def lowercase_attribute_keys_and_values(attributes)
|
198
|
-
lcattrs = {}
|
199
|
-
attributes.each { |k,v| lcattrs[k.downcase] = v.downcase }
|
200
|
-
lcattrs
|
87
|
+
#add single quotes around string
|
88
|
+
def quote(str, quote_char='\'')
|
89
|
+
quote_char+str+quote_char
|
201
90
|
end
|
202
91
|
|
203
|
-
#
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
sorted_strings = sorted.collect{ |k,v| "#{k.inspect} => '#{v}'"} # create strings
|
209
|
-
sorted_strings.join(', ') # finally combine them
|
92
|
+
# adds single quotes around string if it is a simple
|
93
|
+
# word [a-zA-Z0-9_]* otherwise return existing string
|
94
|
+
# also quote if empty string
|
95
|
+
def quote_if(str)
|
96
|
+
(str =~ /^\w*$/) ? quote(str) : str
|
210
97
|
end
|
211
98
|
|
212
99
|
end
|