jsus 0.3.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.travis.yml +2 -0
  2. data/CHANGELOG +5 -0
  3. data/Gemfile +7 -4
  4. data/VERSION +1 -1
  5. data/bin/jsus +1 -5
  6. data/cucumber.yml +1 -1
  7. data/features/command-line/external_dependency_resolution.feature +4 -4
  8. data/features/command-line/generate_includes.feature +34 -0
  9. data/features/command-line/mooforge_compatibility_layer.feature +1 -1
  10. data/features/command-line/postproc.feature +12 -3
  11. data/features/command-line/structure_json.feature +5 -40
  12. data/features/data/ExternalDependencyWithExternalDependency/Leonardo/Source/Core.js +2 -2
  13. data/features/step_definitions/cli_steps.rb +15 -9
  14. data/features/support/env.rb +1 -1
  15. data/jsus.gemspec +25 -7
  16. data/lib/extensions/rgl.rb +2 -1
  17. data/lib/jsus/cli.rb +43 -27
  18. data/lib/jsus/container.rb +71 -51
  19. data/lib/jsus/middleware.rb +2 -3
  20. data/lib/jsus/package.rb +27 -129
  21. data/lib/jsus/packager.rb +10 -6
  22. data/lib/jsus/pool.rb +48 -20
  23. data/lib/jsus/source_file.rb +119 -198
  24. data/lib/jsus/tag.rb +72 -106
  25. data/lib/jsus/util.rb +14 -4
  26. data/lib/jsus/util/compressor.rb +1 -1
  27. data/lib/jsus/util/documenter.rb +1 -1
  28. data/lib/jsus/util/mixins.rb +7 -0
  29. data/lib/jsus/util/mixins/operates_on_sources.rb +29 -0
  30. data/lib/jsus/util/post_processor.rb +35 -0
  31. data/lib/jsus/util/post_processor/base.rb +32 -0
  32. data/lib/jsus/util/post_processor/moocompat12.rb +19 -0
  33. data/lib/jsus/util/post_processor/mooltie8.rb +19 -0
  34. data/lib/jsus/util/post_processor/semicolon.rb +18 -0
  35. data/lib/jsus/util/validator/base.rb +3 -23
  36. data/lib/jsus/util/watcher.rb +8 -6
  37. data/spec/data/Extensions/app/javascripts/Core/Source/Hash.js +13 -0
  38. data/spec/data/Extensions/app/javascripts/Core/Source/Mash.js +13 -0
  39. data/spec/data/Extensions/app/javascripts/Core/package.yml +3 -1
  40. data/spec/data/Extensions/app/javascripts/Orwik/Extensions/Mash.js +16 -0
  41. data/spec/data/Extensions/app/javascripts/Orwik/package.yml +2 -1
  42. data/spec/data/OutsideDependencies/app/javascripts/Orwik/package.yml +3 -3
  43. data/spec/data/SimpleSources/dependent_source_one.js +15 -0
  44. data/spec/data/SimpleSources/replacement_source_one.js +13 -0
  45. data/spec/data/SimpleSources/simple_source_one.js +13 -0
  46. data/spec/data/SimpleSources/simple_source_two.js +13 -0
  47. data/spec/data/extension_1.js +15 -0
  48. data/spec/data/extension_2.js +15 -0
  49. data/spec/data/replacement.js +15 -0
  50. data/spec/jsus/container_spec.rb +72 -14
  51. data/spec/jsus/package_spec.rb +10 -128
  52. data/spec/jsus/packager_spec.rb +11 -11
  53. data/spec/jsus/pool_spec.rb +13 -22
  54. data/spec/jsus/source_file_spec.rb +66 -215
  55. data/spec/jsus/tag_spec.rb +24 -69
  56. data/spec/jsus/util/documenter_spec.rb +1 -1
  57. data/spec/jsus/util/post_processor/moocompat12_spec.rb +23 -0
  58. data/spec/jsus/util/post_processor/mooltie8_spec.rb +23 -0
  59. data/spec/jsus/util/post_processor/semicolon_spec.rb +21 -0
  60. data/spec/jsus/util/post_processors/base_spec.rb +6 -0
  61. data/spec/jsus/util/tree_spec.rb +3 -3
  62. data/spec/jsus/util/validator/base_spec.rb +4 -2
  63. data/spec/jsus/util/watcher_spec.rb +12 -2
  64. data/spec/shared/mixins_segs.rb +38 -0
  65. data/spec/spec_helper.rb +6 -0
  66. metadata +28 -10
  67. data/features/data/tmp2/package.js +0 -35
  68. data/features/data/tmp2/scripts.json +0 -13
  69. data/features/data/tmp2/tree.json +0 -26
  70. data/lib/jsus/compiler.rb +0 -28
  71. data/spec/shared/class_stubs.rb +0 -31
@@ -12,16 +12,21 @@ module Jsus
12
12
  #
13
13
  # Inits packager with the given sources.
14
14
  #
15
- # @param [*SourceFile] sources source files
15
+ # @param [Container, Array] sources source files
16
16
  # @api public
17
- def initialize(*sources)
18
- self.container = Container.new(*sources)
17
+ def initialize(sources)
18
+ @sources = case sources
19
+ when Array
20
+ Container.new(*sources)
21
+ else
22
+ sources
23
+ end
19
24
  end
20
25
 
21
26
  # @return [Jsus::Container] container with source files
22
27
  # @api public
23
28
  def sources
24
- container
29
+ @sources
25
30
  end
26
31
 
27
32
  # Concatenates all the sources' contents into a single string.
@@ -31,8 +36,7 @@ module Jsus
31
36
  # @return [String] concatenated source files
32
37
  # @api public
33
38
  def pack(output_file = nil)
34
- result = sources.map {|s| s.content }.join("\n")
35
-
39
+ result = sources.map {|s| s.source }.join("\n")
36
40
  if output_file
37
41
  FileUtils.mkdir_p(File.dirname(output_file))
38
42
  File.open(output_file, "w") {|f| f << result }
@@ -23,7 +23,7 @@ module Jsus
23
23
  # one level of symlinks
24
24
  # See also: http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
25
25
  Dir[File.join(dir, '**{,/*/**}', 'package.{yml,json}')].uniq.each do |package_path|
26
- Package.new(File.dirname(package_path), :pool => self)
26
+ self << Package.new(File.dirname(package_path))
27
27
  end
28
28
  end
29
29
  end
@@ -54,15 +54,17 @@ module Jsus
54
54
  # @api public
55
55
  def lookup(source_or_key)
56
56
  case source_or_key
57
- when String
58
- lookup(Tag[source_or_key])
59
- when Tag
60
- replacement_map[source_or_key] || provides_map[source_or_key]
61
- when SourceFile
62
- source_or_key
63
- else
64
- raise "Illegal lookup query. Expected String, Tag or SourceFile, " <<
65
- "given #{source_or_key.inspect}, an instance of #{source_or_key.class.name}."
57
+ when nil
58
+ nil
59
+ when String
60
+ lookup(Tag[source_or_key])
61
+ when Tag
62
+ provides_map[source_or_key]
63
+ when SourceFile
64
+ source_or_key
65
+ else
66
+ raise "Illegal lookup query. Expected String, Tag or SourceFile, " <<
67
+ "given #{source_or_key.inspect}, an instance of #{source_or_key.class.name}."
66
68
  end
67
69
  end
68
70
 
@@ -75,7 +77,7 @@ module Jsus
75
77
  # @api public
76
78
  def lookup_dependencies(source_or_source_key)
77
79
  source = lookup(source_or_source_key)
78
- result = Container.new
80
+ result = []
79
81
  looked_up = []
80
82
  if source
81
83
  dependencies = lookup_direct_dependencies(source)
@@ -84,15 +86,22 @@ module Jsus
84
86
  dependencies = dependencies.map {|d| lookup_direct_dependencies(d).to_a }.flatten.uniq
85
87
  end
86
88
  end
87
- result.sort!
89
+ result
88
90
  end
89
91
 
90
- # @param [String, Jsus::Tag] tag_or_tag_key
91
- # @return [Array] array with source files with extensions for given tag.
92
+ # Returns replacement for given source file.
93
+ # @param [Jsus::SourceFile]
94
+ # @return [Jsus::SourceFile, nil]
95
+ # @api public
96
+ def lookup_replacement(source)
97
+ source.provides.map {|tag| replacement_map[tag] }.compact[0]
98
+ end # lookup_replacement
99
+
100
+ # @param [Jsus::SourceFile]
101
+ # @return [Array] array with source files with extensions for given source file
92
102
  # @api public
93
- def lookup_extensions(tag_or_tag_key)
94
- tag = Tag[tag_or_tag_key]
95
- extensions_map[tag]
103
+ def lookup_extensions(source)
104
+ source.provides.map {|tag| extensions_map[tag] }.flatten.compact
96
105
  end
97
106
 
98
107
  #
@@ -119,13 +128,13 @@ module Jsus
119
128
  provides_map[p] = source
120
129
  end
121
130
 
122
- replacement_map[source.replaces] = source if source.replaces if source.replaces
131
+ replacement_map[source.replaces] = source if source.replaces
123
132
  end
124
133
  when source_or_sources_or_package.kind_of?(Package)
125
134
  package = source_or_sources_or_package
126
135
  packages << package
127
- package.source_files.each {|s| s.pool = self }
128
- package.extensions.each {|e| e.pool = self }
136
+ package.source_files.each {|s| self << s }
137
+ package.extensions.each {|e| self << e }
129
138
  when source_or_sources_or_package.kind_of?(Array) || source_or_sources_or_package.kind_of?(Container)
130
139
  sources = source_or_sources_or_package
131
140
  sources.each {|s| self << s}
@@ -133,6 +142,25 @@ module Jsus
133
142
  self
134
143
  end
135
144
 
145
+ # @param [Jsus::Package]
146
+ # @return [Array]
147
+ # @api public
148
+ def compile_package(package)
149
+ result = []
150
+ package.source_files.each do |source|
151
+ result << source
152
+ result << lookup_dependencies(source) unless source.extension? || source.replacement?
153
+ end
154
+ result = result.flatten.compact
155
+ extra_files = []
156
+ result.each_with_index do |source, i|
157
+ extra_files << lookup_replacement(source)
158
+ extra_files << lookup_extensions(source)
159
+ end
160
+ result = (result + extra_files).flatten.compact
161
+ Container.new(result)
162
+ end # compile_package
163
+
136
164
  #
137
165
  # Drops any cached info
138
166
  # @api public
@@ -8,32 +8,44 @@ module Jsus
8
8
  # It contains general info about source as well as file content.
9
9
  #
10
10
  class SourceFile
11
- # Filename relative to package root
12
- attr_accessor :relative_filename
13
- # Full filename
14
- attr_accessor :filename
15
11
  # Package owning the sourcefile
12
+ # Is not directly used in SourceFile, but might be useful for introspection.
16
13
  attr_accessor :package
17
14
 
15
+ # Original filename (immutable)
16
+ attr_accessor :original_filename
17
+
18
+ # Full filename (when initialized from file)
19
+ attr_accessor :filename
20
+ alias_method :path, :filename
21
+ alias_method :path=, :filename=
22
+
23
+ # Original source code (immutable)
24
+ attr_reader :original_source
25
+
26
+ # Source code (mutable)
27
+ attr_accessor :source
28
+
29
+ # Default namespace for source
30
+ attr_reader :namespace
31
+
32
+
18
33
  # Constructors
19
34
 
20
35
  # Basic constructor.
21
36
  #
22
- # You probably should use SourceFile.from_file instead of this one.
23
- #
37
+ # Initializes a file from source.
38
+ # @param [String] source original source for the file
24
39
  # @param [Hash] options
25
- # @option options [Jsus::Package] :package package to assign source file to.
26
- # @option options [String] :relative_filename used in Package to generate
27
- # tree structure of the source files
28
- # @option options [String] :filename full filename for the given source file
29
- # @option options [String] :content file content of the source file
30
- # @option options [Jsus::Pool] :pool owner pool for that file
31
- # @option options [String] :header header of the file
40
+ # @option options [String] :namespace source file namespace
32
41
  # @api semipublic
33
- def initialize(options = {})
34
- [:package, :header, :relative_filename, :filename, :content, :pool].each do |field|
35
- send("#{field}=", options[field]) if options[field]
36
- end
42
+ def initialize(source, options = {})
43
+ @namespace = options[:namespace]
44
+ @original_source = source.dup
45
+ prepare_original_source
46
+ @source = @original_source.dup
47
+ parse_header
48
+ @original_source.freeze
37
49
  end
38
50
 
39
51
  #
@@ -41,44 +53,28 @@ module Jsus
41
53
  #
42
54
  # @param [String] filename
43
55
  # @param [Hash] options
44
- # @option options [Jsus::Pool] :pool owning pool
45
- # @option options [Jsus::Package] :package owning package
56
+ # @option options [String] :namespace namespace to which the source file by default belongs
46
57
  # @return [Jsus::SourceFile]
47
- # @raise [Jsus::BadSourceFileException] when file cannot be parsed
58
+ # @raise [Jsus::BadSourceFileException] when file cannot be parsed or does not exist
48
59
  # @api public
49
60
  def self.from_file(filename, options = {})
50
- if File.exists?(filename)
51
- source = File.open(filename, 'r:utf-8') {|f| f.read }
52
- bom = RUBY_VERSION =~ /1.9/ ? "\uFEFF" : "\xEF\xBB\xBF"
53
- source.gsub!(bom, "")
54
- yaml_data = source.match(%r(^/\*\s*(---.*?)\*/)m)
55
- if (yaml_data && yaml_data[1] && header = YAML.load(yaml_data[1]))
56
- options[:header] = header
57
- options[:relative_filename] = filename
58
- options[:filename] = File.expand_path(filename)
59
- options[:content] = source
60
- new(options)
61
- else
62
- raise BadSourceFileException, "#{filename} is missing a header or header is invalid"
63
- end
64
- else
65
- raise BadSourceFileException, "Referenced #{filename} does not exist. #{options[:package] ? "Referenced from package #{options[:package].name}" : ""}"
66
- end
61
+ filename = File.expand_path(filename)
62
+ raise BadSourceFileException, "File does not exist." unless File.exists?(filename)
63
+ source = Jsus::Util.read_file(filename)
64
+ source_file = new(source, options)
65
+ source_file.filename = source_file.original_filename = filename
66
+ source_file.original_filename.freeze
67
+ source_file
67
68
  rescue Exception => e
68
- if !e.kind_of?(BadSourceFileException) # if we didn't raise the error; like in YAML, for example
69
- raise "Exception #{e.inspect} happened on #{filename}. Please take appropriate measures"
70
- else # if we did it, just reraise
71
- raise e
72
- end
69
+ e.message.sub! /^/, "Unexpected exception happened while processing #{filename}: "
70
+ Jsus.logger.error e.message
71
+ raise e
73
72
  end
74
73
 
75
- # Public API
76
-
77
74
  # @return [Hash] a header parsed from YAML-formatted source file first comment.
78
75
  # @api public
79
76
  def header
80
- self.header = {} unless @header
81
- @header
77
+ @header ||= {}
82
78
  end
83
79
 
84
80
  # @return [String] description of the source file.
@@ -87,54 +83,32 @@ module Jsus
87
83
  header["description"]
88
84
  end
89
85
 
90
- # @return [Array] list of dependencies for given file
91
- # @api public
92
- def dependencies
93
- @dependencies
94
- end
95
- alias_method :requires, :dependencies
86
+ # @return [String] license of source file
87
+ def license
88
+ header["license"]
89
+ end # license
96
90
 
97
- #
98
- # @param [Hash] options
99
- # @option options [Boolean] :short whether inner dependencies should not
100
- # prepend package name, e.g. 'Class' instead of 'Core/Class' when in
101
- # package 'Core'.
102
- #
103
- # Note Doesn't change anything for external dependencies
104
- #
105
- # @return [Array] array with names of dependencies. Unordered.
106
- # @api public
107
- def dependencies_names(options = {})
108
- dependencies.map {|d| d.name(options) }
109
- end
110
- alias_method :requires_names, :dependencies_names
111
-
112
- # @return [Array] array of external dependencies tags. Unordered.
91
+ # @return [Array] list of authors
113
92
  # @api public
114
- def external_dependencies
115
- dependencies.select {|d| d.external? }
116
- end
93
+ def authors
94
+ @authors
95
+ end # authors
117
96
 
118
- # @returns [Array] array with names for external dependencies. Unordered.
97
+ # @return [Array] list of dependencies for given file
119
98
  # @api public
120
- def external_dependencies_names
121
- external_dependencies.map {|d| d.name }
99
+ def requires
100
+ @requires
122
101
  end
102
+ alias_method :dependencies, :requires
103
+ alias_method :requirements, :requires
123
104
 
124
105
  # @return [Array] array with provides tags.
125
106
  # @api public
126
107
  def provides
127
108
  @provides
128
109
  end
110
+ alias_method :provisions, :provides
129
111
 
130
- # @param [Hash] options
131
- # @option options [Boolean] :short whether provides should not prepend package
132
- # name, e.g. 'Class' instead of 'Core/Class' when in package 'Core'.
133
- # @return [Array] array with provides names.
134
- # @api public
135
- def provides_names(options = {})
136
- provides.map {|p| p.name(options)}
137
- end
138
112
 
139
113
  # @return [Jsus::Tag] tag for replaced file, if any
140
114
  # @api public
@@ -142,7 +116,6 @@ module Jsus
142
116
  @replaces
143
117
  end
144
118
 
145
-
146
119
  # @returns [Jsus::Tag] tag for source file, for which this one is an extension.
147
120
  # @example file Foo.js in package Core provides ['Class', 'Hash']. File
148
121
  # Bar.js in package Bar extends 'Core/Class'. That means its contents would be
@@ -158,45 +131,23 @@ module Jsus
158
131
  extends && !extends.empty?
159
132
  end
160
133
 
161
- # @return [Array] new_value array of included extensions for given source.
134
+ # @return [Boolean] whether the source file is an extension.
162
135
  # @api public
163
- def extensions
164
- @extensions ||= []
165
- @extensions = @extensions.flatten.compact.uniq
166
- @extensions
167
- end
168
-
169
- # @param [Array] new_value list of extensions for given file
170
- # @api semipublic
171
- def extensions=(new_value)
172
- @extensions = new_value
173
- end
174
-
175
- # Looks up for extensions in the pool and then includes
176
- # extensions for all the provides tag this source file has.
177
- # Caches the result.
178
- #
179
- # @api semipublic
180
- def include_extensions
181
- @included_extensions ||= include_extensions!
182
- end
136
+ def replacement?
137
+ replaces && !replaces.empty?
138
+ end # replacement?
183
139
 
184
- # @see #include_extensions
185
- # @api semipublic
186
- def include_extensions!
187
- if pool
188
- provides.each do |p|
189
- extensions << pool.lookup_extensions(p)
190
- end
191
- end
192
- end
140
+ # @api private
141
+ def reset
142
+ @source = @original_source.dup
143
+ @filename = @original_filename.dup if @original_filename
144
+ end # reset_linked
193
145
 
194
146
  # @return [Array] array of files required by this files including all the filenames for extensions.
195
147
  # SourceFile filename always goes first, all the extensions are unordered.
196
148
  # @api public
197
149
  def required_files
198
- include_extensions
199
- [filename, extensions.map {|e| e.filename}].flatten
150
+ [filename].flatten
200
151
  end
201
152
 
202
153
  # @return [Hash] hash containing basic info with dependencies/provides tags' names
@@ -206,8 +157,8 @@ module Jsus
206
157
  def to_hash
207
158
  {
208
159
  "desc" => description,
209
- "requires" => dependencies_names(:short => true),
210
- "provides" => provides_names(:short => true)
160
+ "requires" => requires.map {|tag| tag.namespace == namespace ? tag.name : tag.full_name},
161
+ "provides" => provides.map {|tag| tag.name}
211
162
  }
212
163
  end
213
164
 
@@ -215,87 +166,7 @@ module Jsus
215
166
  # @return [String]
216
167
  # @api public
217
168
  def inspect
218
- self.to_hash.inspect
219
- end
220
-
221
- # Parses header and gets info from it.
222
- # @param [String] new_header header content
223
- # @api private
224
- def header=(new_header)
225
- @header = new_header
226
- # prepare defaults
227
- @header["description"] ||= ""
228
- # handle tags
229
- @dependencies = parse_tag_list(Array(@header["requires"]))
230
- @provides = parse_tag_list(Array(@header["provides"]))
231
-
232
- @extends = case @header["extends"]
233
- when Array then Tag.new(@header["extends"][0])
234
- when String then Tag.new(@header["extends"])
235
- else nil
236
- end
237
-
238
- @replaces = case @header["replaces"]
239
- when Array then Tag.new(@header["replaces"][0])
240
- when String then Tag.new(@header["replaces"])
241
- else nil
242
- end
243
- end
244
-
245
- # @param [String] new_value file content
246
- # @api private
247
- def content=(new_value)
248
- @content = new_value
249
- end
250
-
251
- # @return [String] file contents, *including* extensions
252
- # @api semipublic
253
- def content
254
- include_extensions
255
- [@content, extensions.map {|e| e.content}].flatten.compact.join("\n")
256
- end
257
-
258
- # @return [String] Original file contents
259
- # @api semipublic
260
- def original_content
261
- @content
262
- end
263
-
264
- # @param [Enumerable] tag_list list of tags
265
- # @return [Array] normalized tags list
266
- # @api private
267
- def parse_tag_list(tag_list)
268
- tag_list.map do |tag_name|
269
- case tag_name
270
- when String
271
- Tag.new(tag_name, :package => package)
272
- when Hash
273
- tags = []
274
- tag_name.each do |pkg_name, sources|
275
- normalized_package_name = pkg_name.sub(/(.+)\/.*$/, "\\1")
276
- Array(sources).each do |source|
277
- tags << Tag.new([normalized_package_name, source].join("/"))
278
- end
279
- end
280
- tags
281
- end
282
- end.flatten
283
- end # parse_tag_list
284
-
285
- # Assigns an instance of Jsus::Pool to the source file.
286
- # Also performs push to that pool.
287
- # @param [Jsus::Pool] new_value
288
- # @api private
289
- def pool=(new_value)
290
- @pool = new_value
291
- @pool << self if @pool
292
- end
293
-
294
- # A pool which the source file is assigned to. Used in #include_extensions!
295
- # @return [Jsus::Pool]
296
- # @api semipublic
297
- def pool
298
- @pool
169
+ self.to_hash.merge("namespace" => namespace).inspect
299
170
  end
300
171
 
301
172
  # @api public
@@ -312,5 +183,55 @@ module Jsus
312
183
  def hash
313
184
  [self.class, filename].hash
314
185
  end
186
+
187
+ private
188
+
189
+ # @api private
190
+ def prepare_original_source
191
+ bom = RUBY_VERSION[/1.9/] ? "\uFEFF" : "\xEF\xBB\xBF"
192
+ original_source.gsub!(bom, "")
193
+ end # prepare_original_source
194
+
195
+ # @api private
196
+ def parse_header
197
+ yaml_data = source.match(%r(^/\*\s*(---.*?)\*/)m)
198
+ if yaml_data && yaml_data[1] && header = YAML.load(yaml_data[1])
199
+ @header = header
200
+ @authors = Array(@header["author"] || @header["authors"])
201
+ @requires = process_tag_list(@header["requires"])
202
+ @provides = process_tag_list(@header["provides"])
203
+ @replaces = process_tag(@header["replaces"]) if @header["replaces"]
204
+ @extends = process_tag(@header["extends"]) if @header["extends"]
205
+ else
206
+ raise BadSourceFileException, "#{filename} is missing a header or header is invalid"
207
+ end
208
+ end # parse_header
209
+
210
+ # @api private
211
+ def process_tag(tag_name)
212
+ if tag_name.kind_of?(String)
213
+ tag_name.sub!(%r{^\.?/}, "") # remove leading slash / dot+slash
214
+ if tag_name.index("/") || !namespace
215
+ Tag[tag_name]
216
+ else
217
+ Tag["#{namespace}/#{tag_name}"]
218
+ end
219
+ elsif tag_name.kind_of?(Hash)
220
+ # Quirky mootools tags
221
+ ns, tag = tag_name.first[0], tag_name.first[1]
222
+ # Removes strings like "/1.3.0" from the end of namespace part
223
+ ns = ns.sub(%r{/(\d+\.?)+\d+$}, "")
224
+ ns = Util::Inflection.random_case_to_mixed_case(ns)
225
+ "#{ns}/#{tag}"
226
+ else
227
+ nil
228
+ end
229
+ end # process_tag
230
+
231
+ # @api private
232
+ def process_tag_list(tag_list)
233
+ Array(tag_list).map {|tag| process_tag(tag) }.compact
234
+ end # process_tag_list
235
+
315
236
  end
316
237
  end