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.
Files changed (155) hide show
  1. data/CHANGELOG +31 -1
  2. data/README +70 -69
  3. data/RELEASE_NOTES +70 -64
  4. data/Rakefile +26 -27
  5. data/TODO +13 -29
  6. data/doc/about.html +246 -0
  7. data/doc/configuration.html +49 -36
  8. data/doc/developer.html +423 -41
  9. data/doc/directives.html +139 -51
  10. data/doc/guide.html +19 -9
  11. data/doc/index.html +90 -224
  12. data/doc/installation.html +36 -28
  13. data/doc/media_list.html +30 -20
  14. data/doc/simple_diagram.html +3 -5
  15. data/doc/stylesheets/masterview.css +16 -1
  16. data/examples/rails_app_config/masterview/settings.rb +2 -1
  17. data/init.rb +1 -1
  18. data/lib/#ChangeLog# +6 -0
  19. data/lib/masterview/analyzer.rb +48 -34
  20. data/lib/masterview/attr_string_parser.rb +5 -1
  21. data/lib/masterview/case_insensitive_hash.rb +69 -0
  22. data/lib/masterview/{pathname_extensions.rb → core_ext/pathname.rb} +0 -0
  23. data/lib/masterview/{string_extensions.rb → core_ext/string.rb} +0 -0
  24. data/lib/masterview/deprecated/directive_base.rb +362 -0
  25. data/lib/masterview/directive_base.rb +201 -179
  26. data/lib/masterview/directive_dsl.rb +457 -0
  27. data/lib/masterview/directive_helpers.rb +28 -141
  28. data/lib/masterview/directive_load_path.rb +388 -0
  29. data/lib/masterview/directive_metadata.rb +377 -0
  30. data/lib/masterview/directive_registry.rb +259 -69
  31. data/lib/masterview/directives/.metadata +16 -0
  32. data/lib/masterview/directives/attr.rb +9 -8
  33. data/lib/masterview/directives/block.rb +11 -14
  34. data/lib/masterview/directives/check_box.rb +13 -18
  35. data/lib/masterview/directives/collection_select.rb +15 -29
  36. data/lib/masterview/directives/content.rb +9 -3
  37. data/lib/masterview/directives/else.rb +15 -13
  38. data/lib/masterview/directives/elsif.rb +14 -13
  39. data/lib/masterview/directives/eval.rb +20 -0
  40. data/lib/masterview/directives/form.rb +56 -9
  41. data/lib/masterview/directives/form_remote.rb +26 -0
  42. data/lib/masterview/directives/global_inline_erb.rb +10 -14
  43. data/lib/masterview/directives/hidden_field.rb +11 -20
  44. data/lib/masterview/directives/if.rb +13 -12
  45. data/lib/masterview/directives/image_tag.rb +20 -28
  46. data/lib/masterview/directives/import.rb +5 -12
  47. data/lib/masterview/directives/import_render.rb +7 -19
  48. data/lib/masterview/directives/insert_generated_comment.rb +8 -11
  49. data/lib/masterview/directives/javascript_include.rb +21 -12
  50. data/lib/masterview/directives/link_to.rb +14 -8
  51. data/lib/masterview/directives/link_to_function.rb +22 -0
  52. data/lib/masterview/directives/link_to_if.rb +15 -13
  53. data/lib/masterview/directives/link_to_remote.rb +13 -8
  54. data/lib/masterview/directives/omit_tag.rb +32 -16
  55. data/lib/masterview/directives/password_field.rb +10 -22
  56. data/lib/masterview/directives/radio_button.rb +11 -22
  57. data/lib/masterview/directives/replace.rb +7 -8
  58. data/lib/masterview/directives/select.rb +11 -24
  59. data/lib/masterview/directives/stylesheet_link.rb +20 -12
  60. data/lib/masterview/directives/submit.rb +11 -5
  61. data/lib/masterview/directives/text_area.rb +10 -23
  62. data/lib/masterview/directives/text_field.rb +10 -22
  63. data/lib/masterview/exceptions.rb +21 -0
  64. data/lib/masterview/extras/app/controllers/masterview_controller.rb +102 -75
  65. data/lib/masterview/extras/app/views/layouts/masterview_admin.rhtml +24 -23
  66. data/lib/masterview/extras/app/views/layouts/masterview_admin_config.rhtml +81 -0
  67. data/lib/masterview/extras/app/views/masterview/admin/configuration.rhtml +5 -1
  68. data/lib/masterview/extras/app/views/masterview/admin/create.rhtml +2 -2
  69. data/lib/masterview/extras/app/views/masterview/admin/directives.rhtml +5 -0
  70. data/lib/masterview/extras/app/views/masterview/admin/features.rhtml +5 -79
  71. data/lib/masterview/extras/app/views/masterview/admin/interact.rhtml +5 -0
  72. data/lib/masterview/extras/app/views/masterview/admin/list.rhtml +3 -71
  73. data/lib/masterview/extras/init_mv_admin_pages.rb +42 -23
  74. data/lib/masterview/filter_helpers.rb +26 -0
  75. data/lib/masterview/initializer.rb +99 -53
  76. data/lib/masterview/io.rb +19 -15
  77. data/lib/masterview/keyword_expander.rb +7 -2
  78. data/lib/masterview/masterview_info.rb +229 -23
  79. data/lib/masterview/masterview_version.rb +2 -2
  80. data/lib/masterview/parser.rb +275 -105
  81. data/lib/masterview/parser_helpers.rb +54 -0
  82. data/lib/masterview/rails_ext/action_controller_erb_direct.rb +29 -0
  83. data/lib/masterview/rails_ext/action_controller_reparse_checking.rb +27 -0
  84. data/lib/masterview/{extras/init_rails_erb_mv_direct.rb → rails_ext/action_view_erb_direct.rb} +12 -59
  85. data/lib/masterview/template_spec.rb +3 -2
  86. data/lib/masterview.rb +21 -12
  87. data/lib/rexml/parsers/baseparser_with_doctype_fix.rb +473 -0
  88. data/lib/rexml/parsers/sax2parser_with_doctype_fix.rb +243 -0
  89. data/test/directive_test_helper.rb +135 -0
  90. data/test/fixtures/directives/id_check.rb +18 -0
  91. data/test/fixtures/directives/test_directive_events.rb +70 -0
  92. data/test/test_helper.rb +18 -5
  93. data/test/tmp/views/layouts/product.rhtml +10 -10
  94. data/test/tmp/views/product/_form.rhtml +4 -4
  95. data/test/tmp/views/product/_product.rhtml +3 -3
  96. data/test/tmp/views/product/destroy.rhtml +5 -5
  97. data/test/tmp/views/product/edit.rhtml +4 -4
  98. data/test/tmp/views/product/list.rhtml +3 -3
  99. data/test/tmp/views/product/new.rhtml +4 -4
  100. data/test/tmp/views/product/show.rhtml +2 -2
  101. data/test/unit/attr_string_parser_test.rb +105 -0
  102. data/test/unit/case_insensitive_hash_mod_test.rb +104 -0
  103. data/test/unit/config_settings_test.rb +13 -1
  104. data/test/unit/default_generate_mio_filter_test.rb +3 -3
  105. data/test/unit/deprecated_directive_base_test.rb +30 -0
  106. data/test/unit/directive_attr_test.rb +111 -35
  107. data/test/unit/directive_base_test.rb +520 -1
  108. data/test/unit/directive_block_test.rb +30 -22
  109. data/test/unit/directive_content_test.rb +24 -11
  110. data/test/unit/directive_else_test.rb +18 -15
  111. data/test/unit/directive_elsif_test.rb +17 -15
  112. data/test/unit/directive_form_remote_test.rb +59 -0
  113. data/test/unit/directive_form_test.rb +31 -39
  114. data/test/unit/directive_global_inline_erb_test.rb +28 -17
  115. data/test/unit/directive_grid_test_notready.rb +38 -0
  116. data/test/unit/directive_helpers_test.rb +39 -0
  117. data/test/unit/directive_hidden_field_test.rb +44 -29
  118. data/test/unit/directive_if_test.rb +10 -7
  119. data/test/unit/directive_image_tag_test.rb +69 -61
  120. data/test/unit/directive_import_render_test.rb +28 -38
  121. data/test/unit/directive_import_test.rb +16 -14
  122. data/test/unit/directive_insert_generated_comment_test.rb +32 -0
  123. data/test/unit/directive_javascript_include_test.rb +40 -43
  124. data/test/unit/directive_link_to_function_test.rb +40 -0
  125. data/test/unit/directive_link_to_if_test.rb +52 -12
  126. data/test/unit/directive_link_to_remote_test.rb +58 -0
  127. data/test/unit/directive_link_to_test.rb +46 -31
  128. data/test/unit/directive_load_path_test.rb +257 -0
  129. data/test/unit/directive_metadata_test.rb +313 -0
  130. data/test/unit/directive_omit_tag_test.rb +73 -21
  131. data/test/unit/directive_password_field_test.rb +44 -38
  132. data/test/unit/directive_registry_test.rb +44 -0
  133. data/test/unit/directive_replace_test.rb +28 -12
  134. data/test/unit/directive_stylesheet_link_test.rb +43 -36
  135. data/test/unit/directive_submit_test.rb +29 -30
  136. data/test/unit/directive_text_area_test.rb +40 -36
  137. data/test/unit/directive_text_field_test.rb +44 -38
  138. data/test/unit/example_directive_child_events_test.rb +41 -0
  139. data/test/unit/example_test.rb +31 -4
  140. data/test/unit/file_mio_test.rb +18 -13
  141. data/test/unit/filter_helpers_test.rb +10 -8
  142. data/test/unit/find_directive_parent_test.rb +174 -0
  143. data/test/unit/keyword_expander_test.rb +4 -2
  144. data/test/unit/mio_test.rb +18 -11
  145. data/test/unit/mtime_string_hash_mio_tree_test.rb +5 -1
  146. data/test/unit/parser_test.rb +41 -29
  147. data/test/unit/pathname_extensions_test.rb +1 -1
  148. data/test/unit/run_parser_test.rb +2 -2
  149. data/test/unit/simplified_directive_base_test.rb +256 -0
  150. data/test/unit/string_hash_mio_test.rb +5 -1
  151. data/test/unit/template_file_watcher_test.rb +2 -2
  152. data/test/unit/template_test.rb +221 -46
  153. metadata +86 -45
  154. data/lib/masterview/directives/testfilter.rb +0 -55
  155. data/lib/masterview/extras/init_rails_reparse_checking.rb +0 -62
@@ -0,0 +1,388 @@
1
+ require 'yaml'
2
+ require File.join( File.dirname(__FILE__), 'exceptions' )
3
+
4
+ module MasterView
5
+
6
+ # An InvalidPathError that is raised when an invalid directory path
7
+ # is encountered on the directive load path.
8
+ class InvalidDirectivePathError < InvalidPathError
9
+
10
+ # the directive load path entry containing the invalid directory path
11
+ def path_entry
12
+ @dpe || nil
13
+ end
14
+
15
+ def initialize( dpe, err_msg )
16
+ super(dpe.dir_path, err_msg)
17
+ @dpe = dpe
18
+ end
19
+
20
+ end
21
+
22
+
23
+ # The directive load path supports configuring a MasterView application
24
+ # with a list of directories from which directives are loaded.
25
+ #
26
+ # A MasterView application is always configured with the standard set of
27
+ # built-in directives, customarily referenced in templates in the
28
+ # <code>mv:</code> namespace. Additional directives can be used by
29
+ # appending directories containing the directive implementations
30
+ # to the MasterView load path specified by the Configuration.
31
+ #
32
+ module DirectiveLoadPath
33
+
34
+ # File name of metadata specs file in a directives directory
35
+ # Contains yaml specifications for directive implementations
36
+ METADATA_SPECS_FILE_NAME = '.metadata'
37
+
38
+ DEBUG_MD_DIR_SPECS = false #:nodoc: ##DEBUG##
39
+
40
+ @@path_registry = {} #:nodoc:
41
+ #cattr_reader :path_registry
42
+ def self.path_registry #:nodoc: # special hook for test cases fiddling with system state
43
+ @@path_registry
44
+ end
45
+
46
+ # Answer the directives load path specs for the current load path
47
+ def self.current
48
+ @@path_registry[:current]
49
+ end
50
+
51
+ # Answer the default directives load path specs
52
+ def self.default_path_specs #:nodoc:
53
+ @@path_registry[:default]
54
+ end
55
+
56
+ # Install the configured load path
57
+ def self.default_path_specs=(load_path) #:nodoc:
58
+ @@path_registry[:default] = load_path.nil? ? nil : clone_path(load_path)
59
+ if load_path && ! @@path_registry.has_key?(:current)
60
+ @@path_registry[:current] = clone_path(load_path)
61
+ end
62
+ end
63
+
64
+ # hook for test cases so they can ensure a clean system state
65
+ def self.reset_current() #:nodoc:
66
+ load_path = self.default_path_specs
67
+ @@path_registry[:current] = load_path.nil? ? nil : clone_path(load_path)
68
+ end
69
+
70
+ # Answer a clone copy of a directive load path
71
+ #
72
+ # Does a deep_copy to ensure a clean copy of options maps
73
+ #
74
+ def self.clone_path(path)
75
+ path.collect{ | dpe | dpe.deep_copy }
76
+ end
77
+
78
+ # A PathEntry contains the specification for a directory
79
+ # on the MasterView directives load path.
80
+ #
81
+ class PathEntry
82
+
83
+ # Answer a deep copy so we have a proper clone of the options.
84
+ def self.copy_options(options)
85
+ raise ArgumentError, "PathEntry options must be a hash: #{options.inspect}" if ! options.is_a?(Hash)
86
+ Marshal.load(Marshal.dump(options))
87
+ end
88
+
89
+ # The path name of a directory containing directive implementations
90
+ #
91
+ # The pathname is normalized into an absolute path by the validation checking.
92
+ #
93
+ attr_reader :dir_path
94
+
95
+ attr_reader :options
96
+
97
+ # Define an entry for a directive path with the path name of
98
+ # a directory containing directive implementation classes to be loaded
99
+ # into the MasterView processing configuration.
100
+ #
101
+ # Optionally specify options for the directives loaded
102
+ # from this directory:
103
+ #
104
+ # :default - metadata defaults
105
+ #
106
+ # Metadata defaults extend or override any defaults specified
107
+ # in the dir_path/.metadata file, if defined, allowing application
108
+ # customization of the default defaults.
109
+ #
110
+ def initialize( dir_path, options=nil )
111
+ @dir_path = dir_path
112
+ @options = options.nil? ? {} : self.class.copy_options(options)
113
+ end
114
+
115
+ def inspect #:nodoc:
116
+ abbrev_class_name = self.class.name.split('::')
117
+ abbrev_class_name = abbrev_class_name[1..abbrev_class_name.size].join('::')
118
+ "#{abbrev_class_name}('#{dir_path}', options=#{options.inspect})"
119
+ end
120
+
121
+ # Answer a deep copy so we have a proper clone of the options.
122
+ def deep_copy()
123
+ Marshal.load(Marshal.dump(self))
124
+ end
125
+
126
+ # Answer whether the directory exists
127
+ def exists?
128
+ File.directory?(dir_path)
129
+ end
130
+
131
+ # The metadata defaults for this directory, if any
132
+ #
133
+ # Metadata defaults specified on a load path entry supplement
134
+ # any .metadata options specified in the directory itself by
135
+ # overridding and extending any options defined statically in
136
+ # the directives directory.
137
+ #
138
+ def metadata_defaults
139
+ options.fetch(:default, {})
140
+ end
141
+
142
+ # Answer whether directives loaded from this directory use the masterview
143
+ # namespace by default.
144
+ #
145
+ # This option is ordinarily specified only for builtin masterview directives.
146
+ #
147
+ def use_masterview_namespace #:nodoc:
148
+ options.fetch(:use_masterview_namespace, false)
149
+ end
150
+
151
+ # Answer whether directives loaded from this directory use the masterview
152
+ # extensions directive namespace by default.
153
+ #
154
+ def use_extensions_namespace #:nodoc:
155
+ ! use_masterview_namespace
156
+ end
157
+
158
+ # Validate the directory path entry specification.
159
+ #
160
+ # Ensures that the directory exists and that the entry specification
161
+ # is normalized to an absolute pathname.
162
+ def validate
163
+ if ! File.directory?(dir_path)
164
+ err_msg = "Invalid directive load path directory: '#{dir_path}'"
165
+ raise InvalidDirectivePathError.new(self, err_msg)
166
+ end
167
+ if @options.has_key?(:default)
168
+ md_defaults = @options[:default]
169
+ # raises ArgumentError if props are bad; has side effect of normalizing the prop values
170
+ DirectiveMetadata.validate_metadata_props! md_defaults
171
+ end
172
+ end
173
+
174
+ # ensure normalized representation with absolute pathnames and standard options
175
+ def normalize #:nodoc:
176
+ @dir_path = File.expand_path( @dir_path )
177
+ #assert ! (['/', '\\'].contains?dir_path[-1..-1])
178
+ #tbd: (recursively) ensure keys are symbolized?
179
+ @options[:default] = {} if ! @options.has_key?(:default)
180
+ #md_defaults entries were already normalized by side effects of the validation checker
181
+ end
182
+
183
+ # Load the metadata specifications for a directives directory
184
+ def load_metadata_specs(options={})
185
+
186
+ create_if_not_defined = options.fetch(:create_if_not_defined, true)
187
+
188
+ md_file_path = "#{dir_path}/#{METADATA_SPECS_FILE_NAME}"
189
+ dir_has_md_specs = File.file?( md_file_path )
190
+ if dir_has_md_specs
191
+ STDOUT.puts "\n###Loading directive metadata specs: #{md_file_path}" if DEBUG_MD_DIR_SPECS
192
+ md_config_specs = DirectiveLoadPath.load_metadata_specs(md_file_path)
193
+ md_config_specs[:default] ||= {} # ensure that there are default specs
194
+ STDOUT.puts "...md_config_specs=#{md_config_specs.inspect}" if DEBUG_MD_DIR_SPECS
195
+ elsif create_if_not_defined
196
+ md_config_specs = { :default => {} }
197
+ else
198
+ return nil
199
+ end
200
+
201
+ md_defaults = md_config_specs[:default]
202
+ if ! md_defaults.has_key?( :description )
203
+ md_defaults[:description] = "MasterView directive from #{dir_path}"
204
+ end
205
+ # raises ArgumentError if props are bad; has side effect of normalizing the prop values
206
+ DirectiveMetadata.validate_metadata_props! md_defaults
207
+
208
+ md_config_specs
209
+
210
+ end
211
+
212
+ end
213
+
214
+ # A directive load Path is a list of PathEntry specifications
215
+ # for directories containing MasterView directive implementations.
216
+ #
217
+ class Path
218
+
219
+ include Enumerable
220
+
221
+ def initialize() #:nodoc: #??(entries=[])
222
+ @entries = []
223
+ #entries.each { | entry_spec | self << entry_spec }
224
+ end
225
+
226
+ def [](index)
227
+ @entries[index]
228
+ end
229
+
230
+ def []=(index, entry)
231
+ raise ArgumentError, "Invalid entry for {self.class.name}: #{entry.inspect}" if ! entry.is_a?(PathEntry)
232
+ @entries[index] = entry
233
+ end
234
+
235
+ def <<(entry)
236
+ if entry.is_a?(String)
237
+ entry = PathEntry.new(entry)
238
+ elsif entry.is_a?(Array) && entry.size == 2 && entry[0].is_a?(String) && entry[1].is_a?(Hash)
239
+ # wow, we're working WAY too hard at this compatability stuff
240
+ entry = PathEntry.new(entry[0], entry[1])
241
+ else
242
+ raise ArgumentError, "Invalid entry for {self.class.name}: #{entry.inspect}" if ! entry.is_a?(PathEntry)
243
+ end
244
+ @entries << entry
245
+ end
246
+
247
+ def size
248
+ @entries.size
249
+ end
250
+
251
+ def empty?
252
+ @entries.empty?
253
+ end
254
+
255
+ # Calls the given block once for each entry specification on the path,
256
+ # passing the element as parameter.
257
+ def each
258
+ @entries.each { |dpe| yield(dpe) }
259
+ self
260
+ end
261
+
262
+ # Calls the given block once for each entry specification on the path,
263
+ # passing the element's index as parameter.
264
+ def each_index
265
+ @entries.each_index { |index| yield(index) }
266
+ self
267
+ end
268
+
269
+ def include?(dir_path)
270
+ dir_path = dir_path.is_a?( PathEntry ) ? dir_path.dir_path : File.expand_path(dir_path)
271
+ dpe = self.detect{ |dpe| dpe.dir_path == dir_path }
272
+ ! dpe.nil?
273
+ end
274
+
275
+ # Answer the directory paths
276
+ def directory_paths
277
+ dir_paths = []
278
+ self.each { |dpe| dir_paths << dpe.dir_path }
279
+ dir_paths
280
+ end
281
+
282
+ # Answer a copy of a directive load path
283
+ #
284
+ # Does a deep_copy to ensure a clean copy of options maps
285
+ #
286
+ def copy()
287
+ @entries.collect{ | dpe | dpe.deep_copy }
288
+ end
289
+
290
+ end
291
+
292
+ # Answer a clean path with a single entry for each directory
293
+ # and path entries validated and normalize.
294
+ #
295
+ # Specify option :dup_policy ::= { :merge || :use_latest }
296
+ # for handling options on dup entries (merge or use the
297
+ # last-defined options).
298
+ #
299
+ # Optionally provide a block arg for handling validation exceptions
300
+ #
301
+ def self.clean_path(path, options)
302
+
303
+ dup_policy = options.fetch( :dup_policy, :use_latest )
304
+
305
+ unique_paths = []
306
+ path_entries = {}
307
+ path.each { | dpe |
308
+ begin
309
+ dpe.validate()
310
+ dpe.normalize()
311
+ dir_path = dpe.dir_path
312
+ if ! unique_paths.include?(dir_path)
313
+ unique_paths << dir_path
314
+ else
315
+ # dup entry - we've already got this dir_path registered
316
+ if dup_policy == :merge
317
+ prev_options = path_entries[dir_path].options
318
+ options = prev_options.merge(dpe.options)
319
+ else # :use_latest
320
+ options = dpe.options # last-registered path entry wins
321
+ end
322
+ # replace path entry with merged or replaced options
323
+ dpe = PathEntry.new(dir_path, options)
324
+ end
325
+ path_entries[dir_path] = dpe
326
+ rescue InvalidDirectivePathError => ex
327
+ if block_given?
328
+ yield(ex)
329
+ else
330
+ raise
331
+ end
332
+ end
333
+ }
334
+
335
+ clean_path = Path.new
336
+ unique_paths.each { | dir_path |
337
+ clean_path << path_entries[dir_path]
338
+ }
339
+ clean_path
340
+
341
+ end
342
+
343
+ # Load a metadata specifications file.
344
+ #
345
+ # Normalizes options hash keys to symbols.
346
+ # Should ordinarily be used through PathEntry.load_metadata_specs.
347
+ #
348
+ def self.load_metadata_specs(file_path)
349
+ md_config_specs = YAML::load(IO.read(file_path))
350
+ #?what does yaml do with an empy file?? return {} if md_config_specs.nil?
351
+ #assert md_config_specs.is_a?(Hash)
352
+ symbolize_options_hash_keys(md_config_specs)
353
+ end
354
+
355
+ # do in-place symbolization of keys; recurse
356
+ def self.symbolize_options_hash_keys(data) #:nodoc:
357
+ #data.symbolize_keys!
358
+ original_keys = data.keys()
359
+ original_keys.each { | key |
360
+ value = data[key]
361
+ symbolizing_value = value.is_a?(Hash)
362
+ value = symbolize_options_hash_keys(value) if symbolizing_value
363
+ if ! key.is_a?(Symbol)
364
+ data[key.to_sym] = value
365
+ data.delete(key)
366
+ elsif symbolizing_value
367
+ data[key] = value
368
+ end
369
+ }
370
+ data
371
+ end
372
+
373
+ # Compute the metadata default specs for a directives directory
374
+ # Path entry options override/extend directory .metadata specs
375
+ def self.compute_md_defaults(default_md_specs, dir_md_defaults, path_entry_defaults)
376
+ md_defaults = {}
377
+ md_defaults.merge! default_md_specs # we always start with the app-wide defaults
378
+ md_defaults.merge! dir_md_defaults # bring in specs from the directory .metadata
379
+ md_defaults.merge! path_entry_defaults # and allow app ovveride on path entry config
380
+ # raises ArgumentError if props are bad
381
+ # should be superfluous because we've already done this on each individual set of defaults
382
+ DirectiveMetadata.validate_metadata_props! md_defaults
383
+ md_defaults
384
+ end
385
+
386
+ end
387
+
388
+ end