jsus 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -3,3 +3,4 @@ bin/*
3
3
  -
4
4
  features/**/*.feature
5
5
  UNLICENSE
6
+ CHANGELOG
data/.yardopts CHANGED
@@ -1,9 +1,8 @@
1
1
  --markup="markdown" lib/**/*.rb
2
2
  --no-private
3
- --no-protected
4
3
  --main README.md
5
4
  --hide-tag todo
6
5
  -
7
- LICENSE
6
+ UNLICENSE
8
7
  CHANGELOG
9
8
  docs/*.textile
data/CHANGELOG CHANGED
@@ -1,4 +1,19 @@
1
1
  = Jsus Changelog
2
+ == Version 0.3.4
3
+ Middleware changes:
4
+ * support for /compressed/ path component, which acts like /require/, but
5
+ compresses the result in the end.
6
+ * Cache result is now stored in subfolders of cache directory (easier to manage
7
+ with nginx and alike)
8
+ * Improved error reporting (choice of one of multiple modes in middleware settings)
9
+
10
+ CLI changes:
11
+ * --no-cycle-search option for CLI to improve compilation times
12
+
13
+ Other changes:
14
+ * Compression routines moved out to Jsus::Util::Compressor class. For now, only
15
+ yui-compressor method is available
16
+
2
17
  == Version 0.3.3
3
18
  * Unfortunate accident with rubygems
4
19
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.3
1
+ 0.3.4
data/bin/jsus CHANGED
@@ -73,6 +73,10 @@ module Jsus
73
73
  options[:compress] = true
74
74
  end
75
75
 
76
+ opts.on('--no-cycle-search', 'disables search for circular dependencies which may take quite some time during compilation') do
77
+ Jsus.look_for_cycles = false
78
+ end
79
+
76
80
  opts.on_tail('-v', '--verbose', 'verbose mode, shows various debug messages') do
77
81
  Jsus.verbose = true
78
82
  end
@@ -276,9 +280,7 @@ module Jsus
276
280
  end
277
281
 
278
282
  def compress_package
279
- require 'yui/compressor'
280
- compressor = YUI::JavaScriptCompressor.new(:munge => true)
281
- compressed_content = compressor.compress(@package_content)
283
+ compressed_content = Jsus::Util::Compressor.new(@package_content).result
282
284
  if compressed_content != ""
283
285
  @compression_ratio = compressed_content.size.to_f / @package_content.size.to_f
284
286
  compressed_file_name = @package.filename.sub(/.js$/, ".min.js")
@@ -289,8 +291,6 @@ module Jsus
289
291
  puts "Compressor command used: #{compressor.command.join(' ')}"
290
292
  end
291
293
  checkpoint(:compress)
292
- rescue LoadError
293
- puts 'ERROR: You need "yui-compressor" gem in order to use --compress option'
294
294
  end
295
295
 
296
296
  def generate_supplemental_files
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{jsus}
8
- s.version = "0.3.3"
8
+ s.version = "0.3.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Mark Abramov"]
12
- s.date = %q{2011-06-19}
12
+ s.date = %q{2011-08-30}
13
13
  s.default_executable = %q{jsus}
14
14
  s.description = %q{Javascript packager and dependency resolver}
15
15
  s.email = %q{markizko@gmail.com}
@@ -104,9 +104,11 @@ Gem::Specification.new do |s|
104
104
  "lib/jsus/tag.rb",
105
105
  "lib/jsus/util.rb",
106
106
  "lib/jsus/util/code_generator.rb",
107
+ "lib/jsus/util/compressor.rb",
107
108
  "lib/jsus/util/documenter.rb",
108
109
  "lib/jsus/util/file_cache.rb",
109
110
  "lib/jsus/util/inflection.rb",
111
+ "lib/jsus/util/logger.rb",
110
112
  "lib/jsus/util/tree.rb",
111
113
  "lib/jsus/util/validator.rb",
112
114
  "lib/jsus/util/validator/base.rb",
@@ -166,6 +168,8 @@ Gem::Specification.new do |s|
166
168
  "spec/data/JsonPackage/Source/SheetParser.CSS.js",
167
169
  "spec/data/JsonPackage/Source/sg-regex-tools.js",
168
170
  "spec/data/JsonPackage/package.json",
171
+ "spec/data/MissingDependencies/Source/Widget.js",
172
+ "spec/data/MissingDependencies/package.yml",
169
173
  "spec/data/MooforgeValidation/README",
170
174
  "spec/data/MooforgeValidation/app/javascripts/Orwik/Source/Library/InvalidNoAuthors.js",
171
175
  "spec/data/MooforgeValidation/app/javascripts/Orwik/Source/Library/InvalidNoLicense.js",
@@ -200,6 +204,7 @@ Gem::Specification.new do |s|
200
204
  "spec/jsus/util/documenter_spec.rb",
201
205
  "spec/jsus/util/file_cache_spec.rb",
202
206
  "spec/jsus/util/inflection_spec.rb",
207
+ "spec/jsus/util/logger_spec.rb",
203
208
  "spec/jsus/util/tree_spec.rb",
204
209
  "spec/jsus/util/validator/base_spec.rb",
205
210
  "spec/jsus/util/validator/mooforge_spec.rb",
@@ -22,6 +22,7 @@ module Jsus
22
22
  autoload :Pool, 'jsus/pool'
23
23
  autoload :Util, 'jsus/util'
24
24
  autoload :Middleware, 'jsus/middleware'
25
+ autoload :Compressor, 'jsus/compressor'
25
26
 
26
27
  # In verbose mode jsus shows a lot of warnings like missing dependencies.
27
28
  # Default: false
@@ -37,6 +38,7 @@ module Jsus
37
38
  # @api public
38
39
  def self.verbose=(verbose)
39
40
  @verbose = verbose
41
+ logger.level = verbose ? Logger::DEBUG : Logger::ERROR
40
42
  end
41
43
 
42
44
  # @return [String] Jsus version
@@ -63,4 +65,27 @@ module Jsus
63
65
  def self.look_for_cycles=(value)
64
66
  @look_for_cycles = value
65
67
  end
68
+
69
+ # Jsus logger used for all the output. By default uses Logger::ERROR level
70
+ # severity and screen as output device.
71
+ #
72
+ # @return [Jsus::Util::Logger]
73
+ def self.logger
74
+ Thread.current[:jsus_logger] ||= Jsus::Util::Logger.new(STDOUT).tap do |logger|
75
+ logger.level = Logger::ERROR
76
+ logger.formatter = lambda {|severity, time, progname, msg|
77
+ "[#{time.strftime("%Y-%m-%d %H:%M:%S")}] [JSUS:#{severity}] #{msg}\n"
78
+ }
79
+ end
80
+ end # self.logger
81
+
82
+ # Reassign jsus logger whenever needed (E.g. use rails logger)
83
+ #
84
+ # @param value Logger responding to #info, #warn, #debug, #error, #fatal,
85
+ # and #buffer
86
+ # @note In case you use non-jsus logger, you might want to extend it with
87
+ # Jsus::Util::Logger::Buffering module.
88
+ def self.logger=(value)
89
+ Thread.current[:jsus_logger] = value
90
+ end # self.logger=
66
91
  end
@@ -9,7 +9,7 @@ module Jsus
9
9
  class Container
10
10
  # Instantiates a container from given sources.
11
11
  #
12
- # @param [SourceFile]
12
+ # @param [*SourceFile] sources
13
13
  def initialize(*sources)
14
14
  sources.each do |source|
15
15
  push(source)
@@ -20,7 +20,7 @@ module Jsus
20
20
 
21
21
  # Pushes an item to the container
22
22
  #
23
- # @param [SourceFile] source pushed file
23
+ # @param [SourceFile] source source pushed file
24
24
  def push(source)
25
25
  if source
26
26
  if source.kind_of?(Array) || source.kind_of?(Container)
@@ -86,7 +86,7 @@ module Jsus
86
86
  #
87
87
  # Optionally accepts a filesystem point to calculate relative paths from.
88
88
  #
89
- # @param [String] root point from which the relative paths are calculated.
89
+ # @param [String] root root point from which the relative paths are calculated.
90
90
  # When omitted, full paths are returned.
91
91
  # @return [Array] ordered list of required files
92
92
  # @api public
@@ -131,18 +131,17 @@ module Jsus
131
131
  result = []
132
132
  if Jsus.look_for_cycles?
133
133
  cycles = graph.cycles
134
+ error_msg = []
134
135
  unless cycles.empty?
135
- puts "*" * 30
136
- puts "ACHTUNG! WARNING! ATTENTION!"
137
- puts "*" * 30
138
- puts "Jsus has discovered you have circular dependencies in your code."
139
- puts "Please resolve them immediately!"
140
- puts "List of circular dependencies:"
136
+ error_msg << "Jsus has discovered you have circular dependencies in your code."
137
+ error_msg << "Please resolve them immediately!"
138
+ error_msg << "List of circular dependencies:"
141
139
  cycles.each do |cycle|
142
- puts "-" * 30
143
- puts (cycle + [cycle.first]).map {|sf| sf.filename}.join(" => ")
140
+ error_msg << "-" * 30
141
+ error_msg << (cycle + [cycle.first]).map {|sf| sf.filename}.join(" => ")
144
142
  end
145
- puts "*" * 30
143
+ error_msg = error_msg.join("\n")
144
+ Jsus.logger.fatal(error_msg)
146
145
  end
147
146
  end
148
147
  graph.topsort_iterator.each { |item| result << item }
@@ -33,7 +33,7 @@ module Jsus
33
33
  #
34
34
  class Middleware
35
35
  include Rack
36
- class <<self
36
+ class << self
37
37
  # Default settings for Middleware
38
38
  DEFAULT_SETTINGS = {
39
39
  :packages_dir => ".",
@@ -41,7 +41,9 @@ module Jsus
41
41
  :cache_path => nil,
42
42
  :prefix => "jsus",
43
43
  :cache_pool => true,
44
- :includes_root => "."
44
+ :includes_root => ".",
45
+ :log_method => nil, # [:alert, :html, :console]
46
+ :postproc => [] # ["mooltie8", "moocompat12"]
45
47
  }.freeze
46
48
 
47
49
  # @return [Hash] Middleware current settings
@@ -94,7 +96,7 @@ module Jsus
94
96
  end # class <<self
95
97
 
96
98
  # Default rack initialization routine
97
- # @param [#call] rack chain caller
99
+ # @param [#call] app rack chain caller
98
100
  # @api public
99
101
  def initialize(app)
100
102
  @app = app
@@ -106,17 +108,23 @@ module Jsus
106
108
  # Jsus::Middleware#call method dups current rack app and executes
107
109
  # Jsus::Middleware#_call on it.
108
110
  #
109
- # @param [Hash] rack env
111
+ # @param [Hash] env rack env
110
112
  # @return [#each] rack response
111
113
  # @api semipublic
112
114
  def _call(env)
115
+ Jsus.logger.buffer.clear
113
116
  path = Utils.unescape(env["PATH_INFO"])
114
117
  return @app.call(env) unless handled_by_jsus?(path)
115
118
  path.sub!(path_prefix_regex, "")
116
119
  components = path.split("/")
117
120
  return @app.call(env) unless components.size >= 2
121
+
122
+ request_options[:path] = path
118
123
  if components[0] == "require"
119
124
  generate_requires(components[1])
125
+ elsif components[0] == "compressed"
126
+ request_options[:compress] = true
127
+ generate_requires(components[1])
120
128
  elsif components[0] == "include"
121
129
  generate_includes(components[1])
122
130
  else
@@ -126,7 +134,7 @@ module Jsus
126
134
 
127
135
  # Rack calling point
128
136
  #
129
- # @param [Hash] rack env
137
+ # @param [Hash] env rack env
130
138
  # @return [#each] rack response
131
139
  # @api public
132
140
  def call(env)
@@ -135,16 +143,39 @@ module Jsus
135
143
 
136
144
  protected
137
145
 
146
+ # Current request options
147
+ # @return [Hash]
148
+ def request_options
149
+ @options ||= {}
150
+ end # request_options
151
+
152
+ # Rack response of not found
153
+ # @return [#each] 404 response
154
+ # @api semipublic
155
+ def not_found!
156
+ [404, {"Content-Type" => "text/plain"}, ["Jsus doesn't know anything about this entity"]]
157
+ end # not_found!
158
+
159
+ # Respond with given text
160
+ # @param [String] text text to respond with
161
+ # @return [#each] 200 response
162
+ # @api semipublic
163
+ def respond_with(text)
164
+ response = formatted_errors + postproc(text)
165
+ cache_response!(response) if cache?
166
+ [200, {"Content-Type" => "text/javascript"}, [response]]
167
+ end # respond_with
168
+
138
169
  # Generates response for /require/ requests.
139
170
  #
140
- # @param [String] path component to required sources
171
+ # @param [String] path_string path component to required sources
141
172
  # @return [#each] rack response
142
173
  # @api semipublic
143
174
  def generate_requires(path_string)
144
175
  files = path_string_to_files(path_string)
145
176
  if !files.empty?
146
177
  response = Container.new(*files).map {|f| f.content }.join("\n")
147
- cache.write(escape_path_for_cache_key(path_string), response) if cache?
178
+ response = Jsus::Util::Compressor.new(response).result if request_options[:compress]
148
179
  respond_with(response)
149
180
  else
150
181
  not_found!
@@ -153,7 +184,7 @@ module Jsus
153
184
 
154
185
  # Generates response for /include/ requests.
155
186
  #
156
- # @param [String] path component to included sources
187
+ # @param [String] path_string path component to included sources
157
188
  # @return [#each] rack response
158
189
  # @api semipublic
159
190
  def generate_includes(path_string)
@@ -168,7 +199,7 @@ module Jsus
168
199
 
169
200
  # Returns list of exlcuded and included source files for given path strings.
170
201
  #
171
- # @param [String] string with + and ~
202
+ # @param [String] path_string string with + and ~
172
203
  # @return [Hash] hash with source files to include and to exclude
173
204
  # @api semipublic
174
205
  def path_string_to_files(path_string)
@@ -179,6 +210,26 @@ module Jsus
179
210
  files
180
211
  end # path_string_to_files
181
212
 
213
+ # Post-processes output (removes different compatibility tags)
214
+ #
215
+ # @param [String] source source to post-process
216
+ # @return [String] post-processed source
217
+ def postproc(source)
218
+ Array(self.class.settings[:postproc]).inject(source) do |result, processor|
219
+ case processor.strip
220
+ when /^moocompat12$/i
221
+ result.gsub(/\/\/<1.2compat>.*?\/\/<\/1.2compat>/m, '').
222
+ gsub(/\/\*<1.2compat>\*\/.*?\/\*<\/1.2compat>\*\//m, '')
223
+ when /^mooltie8$/i
224
+ result.gsub(/\/\/<ltIE8>.*?\/\/<\/ltIE8>/m, '').
225
+ gsub(/\/\*<ltIE8>\*\/.*?\/\*<\/ltIE8>\*\//m, '')
226
+ else
227
+ Jsus.logger.error "Unknown post-processor: #{processor}"
228
+ result
229
+ end
230
+ end
231
+ end
232
+
182
233
  # Parses human-readable string with + and ~ operations into a more usable hash.
183
234
  # @note + is a space after url decoding
184
235
  #
@@ -203,8 +254,9 @@ module Jsus
203
254
  end # parse_path_string
204
255
 
205
256
  # Returns a list of associated files for given source file or source package.
206
- # @param [String] canonical source file or source package name or wildcard
207
- # e.g. "Mootools.Core", "Mootools.Core/*", "Mootools.Core/Class", "**/*"
257
+ # @param [String] source_file_or_package canonical source file or source
258
+ # package name or wildcard. E.g. "Mootools.Core", "Mootools.Core/*",
259
+ # "Mootools.Core/Class", "**/*"
208
260
  # @return [Array] list of source files for given input
209
261
  # @api semipublic
210
262
  def get_associated_files(source_file_or_package)
@@ -225,24 +277,9 @@ module Jsus
225
277
  end
226
278
  end # get_associated_files
227
279
 
228
- # Rack response of not found
229
- # @return [#each] 404 response
230
- # @api semipublic
231
- def not_found!
232
- [404, {"Content-Type" => "text/plain"}, ["Jsus doesn't know anything about this entity"]]
233
- end # not_found!
234
-
235
- # Respond with given text
236
- # @param [String] text to respond with
237
- # @return [#each] 200 response
238
- # @api semipublic
239
- def respond_with(text)
240
- [200, {"Content-Type" => "text/javascript"}, [text]]
241
- end # respond_with
242
-
243
280
  # Check whether given path is handled by jsus middleware.
244
281
  #
245
- # @param [String] path
282
+ # @param [String] path path
246
283
  # @return [Boolean]
247
284
  # @api semipublic
248
285
  def handled_by_jsus?(path)
@@ -283,6 +320,13 @@ module Jsus
283
320
  self.class.cache
284
321
  end # cache
285
322
 
323
+ # Saves response into the filesystem
324
+ # @param [String] response text to store
325
+ # @return [String] filename
326
+ def cache_response!(response)
327
+ cache.write(escape_path_for_cache_key(request_options[:path]), response)
328
+ end # cache_response!
329
+
286
330
  # @return [Boolean] whether pool is shared between requests
287
331
  # @api semipublic
288
332
  def cache_pool?
@@ -292,11 +336,34 @@ module Jsus
292
336
  # You might or might not need to do some last minute conversions for your cache
293
337
  # key. Default behaviour is merely a nginx hack, you may have to use your own
294
338
  # function for your web-server.
295
- # @param [String] request path minus the prefix
339
+ # @param [String] path request path minus the prefix
296
340
  # @return [String] normalized cache key for given request path
297
341
  # @api semipublic
298
342
  def escape_path_for_cache_key(path)
299
343
  path.gsub(" ", "+")
300
344
  end # escape_path_for_cache_key
345
+
346
+ # Outputs errors in one or multiple ways.
347
+ # Set middleware setting :log_method to array with a combination of any of the following:
348
+ # :alert -- generates javascript alert with warning text
349
+ # :console -- generates console logging entry
350
+ # :html -- injects error / warning messages directly into html body
351
+ # @return [String] javascript code containing errors output for various methods
352
+ # @api semipublic
353
+ def formatted_errors
354
+ Array(self.class.settings[:log_method]).inject("") do |result, log_method|
355
+ result << errors.map do |severity, error|
356
+ case log_method
357
+ when :alert then "alert(#{error.inspect});"
358
+ when :console then "console.log(#{error.inspect});"
359
+ when :html then "document.body.innerHTML = '<font color=red>' + #{error.inspect} + '</font><br/>' + document.body.innerHTML;"
360
+ end
361
+ end.compact.join("\n") + "\n"
362
+ end
363
+ end # formatted_errors
364
+
365
+ def errors
366
+ Jsus.logger.buffer
367
+ end # errors
301
368
  end # class Middleware
302
369
  end # module Jsus
@@ -14,7 +14,7 @@ module Jsus
14
14
  #
15
15
  # Creates a package from given directory.
16
16
  #
17
- # @param [String] path to directory containing a package
17
+ # @param [String] directory path to directory containing a package
18
18
  # @param [Hash] options
19
19
  # @option options [Jsus::Pool] :pool which pool the package should belong to.
20
20
  # @raise an error when the given directory doesn't contain a package.yml or package.json
@@ -27,6 +27,7 @@ module Jsus
27
27
  elsif File.exists?(File.join(directory, 'package.json'))
28
28
  self.header = JSON.load(File.open(File.join(directory, 'package.json'), 'r:utf-8') {|f| f.read })
29
29
  else
30
+ Jsus::Middleware.errors << "Directory #{directory} does not contain a valid package.yml / package.json file!"
30
31
  raise "Directory #{directory} does not contain a valid package.yml / package.json file!"
31
32
  end
32
33
  Dir.chdir(directory) do
@@ -39,7 +40,7 @@ module Jsus
39
40
  source_files << source_file
40
41
  end
41
42
  else
42
- puts "Warning: #{source} is not found for #{name}" if Jsus.verbose?
43
+ Jsus.logger.warn "#{source} is not found for #{name}"
43
44
  end
44
45
  end
45
46
  end
@@ -130,7 +131,7 @@ module Jsus
130
131
  end
131
132
 
132
133
  # Compiles source files and linked external source files into a given category.
133
- # @param [String, nil] directory to output the result into
134
+ # @param [String, nil] directory directory to output the result into
134
135
  # @return [String] content of merged source files
135
136
  # @api public
136
137
  def compile(directory = ".")
@@ -139,8 +140,8 @@ module Jsus
139
140
  end
140
141
 
141
142
  # Generates tree structure for files in package into a json file.
142
- # @param [String] directory to output the result
143
- # @param [String] resulting filename
143
+ # @param [String] directory directory to output the result
144
+ # @param [String] filename resulting filename
144
145
  # @return [Hash] hash with tree structure
145
146
  # @api public
146
147
  def generate_tree(directory = ".", filename = "tree.json")
@@ -162,8 +163,8 @@ module Jsus
162
163
  end
163
164
 
164
165
  # Generates info about resulting compiled package into a json file.
165
- # @param [String] directory to output the result
166
- # @param [String] resulting filename
166
+ # @param [String] directory directory to output the result
167
+ # @param [String] filename resulting filename
167
168
  # @return [Hash] hash with scripts info
168
169
  # @api public
169
170
  def generate_scripts_info(directory = ".", filename = "scripts.json")
@@ -228,13 +229,13 @@ module Jsus
228
229
  # Private API
229
230
 
230
231
 
231
- # @param [Hash] parsed header
232
+ # @param [Hash] new_header parsed header
232
233
  # @api private
233
234
  def header=(new_header)
234
235
  @header = new_header
235
236
  end
236
237
 
237
- # @param [Enumerable] external dependencies
238
+ # @param [Enumerable] new_value external dependencies
238
239
  # @api private
239
240
  def linked_external_dependencies=(new_value)
240
241
  @linked_external_dependencies = new_value
@@ -12,7 +12,7 @@ module Jsus
12
12
  #
13
13
  # Inits packager with the given sources.
14
14
  #
15
- # @param [SourceFile] source files
15
+ # @param [*SourceFile] sources source files
16
16
  # @api public
17
17
  def initialize(*sources)
18
18
  self.container = Container.new(*sources)
@@ -27,7 +27,7 @@ module Jsus
27
27
  # Concatenates all the sources' contents into a single string.
28
28
  # If given a filename, outputs into a file.
29
29
  #
30
- # @param [String, nil] output file name
30
+ # @param [String, nil] output_file output file name
31
31
  # @return [String] concatenated source files
32
32
  # @api public
33
33
  def pack(output_file = nil)
@@ -12,7 +12,7 @@ module Jsus
12
12
  #
13
13
  # Basic constructor.
14
14
  #
15
- # @param [Array, String, nil] directory or list of directories to load source
15
+ # @param [Array, String, nil] dir_or_dirs directory or list of directories to load source
16
16
  # packages from.
17
17
  # @api public
18
18
  def initialize(dir_or_dirs = nil)
@@ -49,7 +49,7 @@ module Jsus
49
49
  #
50
50
  # If given a source file, returns the input.
51
51
  #
52
- # @param [String, Jsus::Tag, Jsus::SourceFile]
52
+ # @param [String, Jsus::Tag, Jsus::SourceFile] source_or_key
53
53
  # @return [Jsus::SourceFile]
54
54
  # @api public
55
55
  def lookup(source_or_key)
@@ -70,7 +70,7 @@ module Jsus
70
70
  #
71
71
  # Looks up for dependencies for given file recursively.
72
72
  #
73
- # @param [String, Jsus::Tag, Jsus::SourceFile]
73
+ # @param [String, Jsus::Tag, Jsus::SourceFile] source_or_source_key
74
74
  # @return [Jsus::Container] container with all the dependencies
75
75
  # @api public
76
76
  def lookup_dependencies(source_or_source_key)
@@ -87,7 +87,7 @@ module Jsus
87
87
  result.sort!
88
88
  end
89
89
 
90
- # @param [String, Jsus::Tag]
90
+ # @param [String, Jsus::Tag] tag_or_tag_key
91
91
  # @return [Array] array with source files with extensions for given tag.
92
92
  # @api public
93
93
  def lookup_extensions(tag_or_tag_key)
@@ -98,7 +98,8 @@ module Jsus
98
98
  #
99
99
  # Pushes an item into a pool.
100
100
  #
101
- # @param [Jsus::SourceFile, Jsus::Package, Array] items to push
101
+ # @param [Jsus::SourceFile, Jsus::Package, Array] source_or_sources_or_package
102
+ # items to push
102
103
  # @return [self]
103
104
  # @api public
104
105
  def <<(source_or_sources_or_package)
@@ -112,8 +113,8 @@ module Jsus
112
113
  extensions_map[source.extends] << source
113
114
  else
114
115
  source.provides.each do |p|
115
- if provides_map[p] && provides_map[p] != source && provides_map[p].filename != source.filename && Jsus.verbose?
116
- puts "Redeclared #{p.to_s} in #{source.filename} (previously declared in #{provides_map[p].filename})"
116
+ if provides_map[p] && provides_map[p] != source && provides_map[p].filename != source.filename
117
+ Jsus.logger.warn "Redeclared #{p.to_s} in #{source.filename} (previously declared in #{provides_map[p].filename})"
117
118
  end
118
119
  provides_map[p] = source
119
120
  end
@@ -147,7 +148,7 @@ module Jsus
147
148
  # You probably will find yourself using #include_dependencies instead.
148
149
  # This method caches results locally, use flush_cache! to drop.
149
150
  #
150
- # @param [String, Jsus::Tag, Jsus::SourceFile]
151
+ # @param [String, Jsus::Tag, Jsus::SourceFile] source_or_source_key
151
152
  # @return [Array] array of direct dependencies for given entity
152
153
  # @api private
153
154
  def lookup_direct_dependencies(source_or_source_key)
@@ -158,7 +159,7 @@ module Jsus
158
159
  #
159
160
  # Performs the actual lookup for #lookup_direct_dependencies
160
161
  #
161
- # @param [String, Jsus::Tag, Jsus::SourceFile]
162
+ # @param [String, Jsus::Tag, Jsus::SourceFile] source
162
163
  # @return [Array] array of direct dependencies for given entity
163
164
  # @api private
164
165
  def lookup_direct_dependencies!(source)
@@ -166,8 +167,8 @@ module Jsus
166
167
 
167
168
  source.dependencies.map do |dependency|
168
169
  result = provides_tree.glob("/#{dependency}")
169
- if (!result || (result.is_a?(Array) && result.empty?)) && Jsus.verbose?
170
- puts "#{source.filename} is missing #{dependency.is_a?(SourceFile) ? dependency.filename : dependency.to_s}"
170
+ if (!result || (result.is_a?(Array) && result.empty?))
171
+ Jsus.logger.warn "#{source.filename} is missing #{dependency.is_a?(SourceFile) ? dependency.filename : dependency.to_s}"
171
172
  end
172
173
  result
173
174
  end.flatten.map {|tag| lookup(tag) }
@@ -187,7 +188,7 @@ module Jsus
187
188
 
188
189
 
189
190
  # Registers the source in both trees
190
- # @param [Jsus::SourceFile]
191
+ # @param [Jsus::SourceFile] source
191
192
  # @api private
192
193
  def add_source_to_trees(source)
193
194
  if source.package
@@ -39,7 +39,7 @@ module Jsus
39
39
  #
40
40
  # Initializes a SourceFile given the filename and options
41
41
  #
42
- # @param [String]
42
+ # @param [String] filename
43
43
  # @param [Hash] options
44
44
  # @option options [Jsus::Pool] :pool owning pool
45
45
  # @option options [Jsus::Package] :package owning package
@@ -158,7 +158,7 @@ module Jsus
158
158
  extends && !extends.empty?
159
159
  end
160
160
 
161
- # @return [Array] array of included extensions for given source.
161
+ # @return [Array] new_value array of included extensions for given source.
162
162
  # @api public
163
163
  def extensions
164
164
  @extensions ||= []
@@ -166,7 +166,7 @@ module Jsus
166
166
  @extensions
167
167
  end
168
168
 
169
- # @param [Array] list of extensions for given file
169
+ # @param [Array] new_value list of extensions for given file
170
170
  # @api semipublic
171
171
  def extensions=(new_value)
172
172
  @extensions = new_value
@@ -219,7 +219,7 @@ module Jsus
219
219
  end
220
220
 
221
221
  # Parses header and gets info from it.
222
- # @param [String] header content
222
+ # @param [String] new_header header content
223
223
  # @api private
224
224
  def header=(new_header)
225
225
  @header = new_header
@@ -242,7 +242,7 @@ module Jsus
242
242
  end
243
243
  end
244
244
 
245
- # @param [String] file content
245
+ # @param [String] new_value file content
246
246
  # @api private
247
247
  def content=(new_value)
248
248
  @content = new_value
@@ -261,7 +261,7 @@ module Jsus
261
261
  @content
262
262
  end
263
263
 
264
- # @param [Enumerable] list of tags
264
+ # @param [Enumerable] tag_list list of tags
265
265
  # @return [Array] normalized tags list
266
266
  # @api private
267
267
  def parse_tag_list(tag_list)
@@ -284,7 +284,7 @@ module Jsus
284
284
 
285
285
  # Assigns an instance of Jsus::Pool to the source file.
286
286
  # Also performs push to that pool.
287
- # @param [Jsus::Pool]
287
+ # @param [Jsus::Pool] new_value
288
288
  # @api private
289
289
  def pool=(new_value)
290
290
  @pool = new_value
@@ -32,7 +32,7 @@ module Jsus
32
32
  # Between all those, tags b,c,d and e are equal, meaning they all use
33
33
  # the same spot in Hash or wherever else.
34
34
  #
35
- # @param [String] tag name
35
+ # @param [String] name tag name
36
36
  # @param [Hash] options
37
37
  # @option options [String] :package_name owner package name
38
38
  # @option options [Jsus::Package] :package :owner package
@@ -1,11 +1,13 @@
1
1
  module Jsus
2
2
  # Utility namespace.
3
3
  module Util
4
+ autoload :Compressor, 'jsus/util/compressor'
4
5
  autoload :Tree, 'jsus/util/tree'
5
6
  autoload :Documenter, 'jsus/util/documenter'
6
7
  autoload :Validator, 'jsus/util/validator'
7
8
  autoload :Inflection, 'jsus/util/inflection'
8
9
  autoload :FileCache, 'jsus/util/file_cache'
9
10
  autoload :CodeGenerator, 'jsus/util/code_generator'
11
+ autoload :Logger, 'jsus/util/logger'
10
12
  end # Util
11
13
  end # Jsus
@@ -3,6 +3,7 @@ module Jsus
3
3
  # Code generation routines.
4
4
  module CodeGenerator
5
5
  class <<self
6
+ # @param [Array] paths list of paths
6
7
  # @return [String] javascript for includes for a list of given paths
7
8
  # @api public
8
9
  def generate_includes(paths)
@@ -0,0 +1,21 @@
1
+ module Jsus
2
+ module Util
3
+ class Compressor
4
+ attr_reader :result
5
+ def initialize(source, options = {}) # todo - non-java compressor
6
+ @result = compress_with_yui(source)
7
+ end # initialize
8
+
9
+ def compress_with_yui(source)
10
+ begin
11
+ require 'yui/compressor'
12
+ compressor = YUI::JavaScriptCompressor.new(:munge => true)
13
+ compressed_content = compressor.compress(source)
14
+ rescue LoadError
15
+ Jsus.logger.fatal 'ERROR: You need "yui-compressor" gem in order to use --compress option'
16
+ end
17
+ compressed_content
18
+ end # compress_with_yui
19
+ end # class Compressor
20
+ end # module Util
21
+ end # module Jsus
@@ -24,7 +24,7 @@ module Jsus
24
24
 
25
25
  # Generates documentation tree into the given directory.
26
26
  #
27
- # @param [String] output directory
27
+ # @param [String] doc_dir output directory
28
28
  # @api public
29
29
  def generate(doc_dir = Dir.pwd)
30
30
  #FileUtils.rm_rf(doc_dir)
@@ -49,7 +49,7 @@ module Jsus
49
49
 
50
50
  # Adds a source file to the documented source tree
51
51
  #
52
- # @param [Jsus::SourceFile] pushed source
52
+ # @param [Jsus::SourceFile] source pushed source
53
53
  # @api public
54
54
  def <<(source) # :nodoc:
55
55
  filename = File.basename(source.filename)
@@ -88,7 +88,7 @@ module Jsus
88
88
 
89
89
  # Sets documenter to exclusive scope for documentation.
90
90
  # Exclusive scope overrides all the other scopes.
91
- # @param [Array, String] documentation scope
91
+ # @param [Array, String] scope documentation scope
92
92
  # @api public
93
93
  def only(scope)
94
94
  result = clone
@@ -99,7 +99,7 @@ module Jsus
99
99
  # Sets documenter to additive scope for documentation.
100
100
  # Additive scopes match any of the pathspecs given
101
101
  #
102
- # @param [Array, String] documentation scope
102
+ # @param [Array, String] scope documentation scope
103
103
  # @api public
104
104
  def or(scope)
105
105
  result = clone
@@ -56,11 +56,10 @@ module Jsus
56
56
 
57
57
  # Generates path by cache key.
58
58
  #
59
- # Default strategy: append key to cache directory
60
- # (slashes are replaced with dots)
59
+ # Default strategy: relative path references via ../ are escaped.
61
60
  # @api private
62
61
  def generate_path(key)
63
- key = key.gsub(File::SEPARATOR, ".")
62
+ key = key.gsub(%r{(^|/)\.\./}, ".")
64
63
  File.join(@path, key)
65
64
  end # generate_path
66
65
  end # class FileCache
@@ -0,0 +1,35 @@
1
+ require 'logger'
2
+ module Jsus
3
+ module Util
4
+ # Extension of ruby logger allowing for message buffering.
5
+ class Logger < ::Logger
6
+ module Buffering
7
+ # Buffer storing logged messages
8
+ def buffer
9
+ @buffer ||= []
10
+ end # buffer
11
+
12
+ def buffer=(value)
13
+ @buffer = value
14
+ end # buffer=
15
+
16
+ def add(severity, message = nil, progname = nil, &block)
17
+ unless @logdev.nil? or severity < @level
18
+ if message.nil?
19
+ if block_given?
20
+ message = yield
21
+ else
22
+ message = progname
23
+ progname = @progname
24
+ end
25
+ end
26
+ buffer << [severity, message]
27
+ end
28
+ super
29
+ end # add
30
+ end # module Buffering
31
+
32
+ include Buffering
33
+ end # class Logger
34
+ end # module Util
35
+ end # module Jsus
@@ -57,6 +57,9 @@ module Jsus
57
57
  attr_accessor :path_components
58
58
 
59
59
  # Initializes full path and value for the node
60
+ # @param [String] full_path full path to node
61
+ # @param [Object] value
62
+ # @api public
60
63
  def initialize(full_path, value = nil)
61
64
  self.full_path = full_path
62
65
  self.value = value
@@ -81,7 +84,7 @@ module Jsus
81
84
  @children ||= []
82
85
  end
83
86
 
84
- # @param [String] basename
87
+ # @param [String] name basename
85
88
  # @return [Jsus::Util::Tree::Node] direct node child with given basename
86
89
  # @api public
87
90
  def find_child(name)
@@ -89,7 +92,7 @@ module Jsus
89
92
  end
90
93
 
91
94
  # Creates a child with given name and value
92
- # @param [String] node name
95
+ # @param [String] name node name
93
96
  # @param [Object] value
94
97
  # @return [Jsus::Util::Tree::Node]
95
98
  # @api public
@@ -18,7 +18,8 @@ module Jsus
18
18
  end
19
19
  alias_method :sources, :source_files
20
20
 
21
- # @param [Jsus::Pool, Jsus::Container, Array] source files for validation
21
+ # @param [Jsus::Pool, Jsus::Container, Array] pool_or_array_or_container
22
+ # source files for validation
22
23
  # @api public
23
24
  def source_files=(pool_or_array_or_container)
24
25
  case pool_or_array_or_container
@@ -45,7 +46,7 @@ module Jsus
45
46
  end
46
47
 
47
48
  # Shortcut for creating and validating a list of items
48
- # @param [*Array] args for #new
49
+ # @param [*Array] args passed to #new
49
50
  # @api public
50
51
  def self.validate(*args)
51
52
  new(*args).validate
@@ -13,4 +13,12 @@ authors:
13
13
  provides: [Core]
14
14
 
15
15
  ...
16
- */
16
+ */
17
+
18
+ //<ltIE8>
19
+ var IE7 = true;
20
+ //</ltIE8>
21
+
22
+ //<1.2compat>
23
+ var compatibilityMode = true;
24
+ //</1.2compat>
@@ -0,0 +1,17 @@
1
+ /*
2
+ ---
3
+
4
+ script: Widget.js
5
+
6
+ description: An abstract class for all your widgets out there.
7
+
8
+ license: MIT-style license
9
+
10
+ authors:
11
+ - Mark Abramov
12
+
13
+ requires: [MissingDependency]
14
+ provides: [Widget]
15
+
16
+ ...
17
+ */
@@ -0,0 +1,9 @@
1
+ name: Package
2
+ filename: package.js
3
+ web: http://github.com/jsus/jsus
4
+ description: "Package with a missing dependency, raises an error"
5
+ license: MIT-Style License, http://mootools.net/license
6
+ copyright: "Mark Abramov"
7
+ authors: "Mark Abramov"
8
+ sources:
9
+ - "Source/Widget.js"
@@ -7,6 +7,8 @@ describe Jsus::Middleware do
7
7
  def new_server
8
8
  Sinatra.new do
9
9
  use Jsus::Middleware
10
+ Jsus.logger = Jsus::Util::Logger.new('/dev/null')
11
+ Jsus.verbose = true
10
12
  set :port, 4567
11
13
  set :raise_errors, true
12
14
  set :show_exceptions, false
@@ -24,6 +26,7 @@ describe Jsus::Middleware do
24
26
  before(:all) do
25
27
  @server = new_server
26
28
  @server_thread = Thread.new { suppress_output { @server.run! } }
29
+ Jsus::Middleware.settings = {:cache_pool => false}
27
30
  end
28
31
 
29
32
  after(:all) { @server_thread.kill }
@@ -256,14 +259,85 @@ describe Jsus::Middleware do
256
259
  end
257
260
 
258
261
  describe "caching" do
262
+ let(:packages_dir) { File.expand_path("spec/data/ComplexDependencies") }
259
263
  let(:cache_path) { "spec/tmp" }
260
- before(:each) { Jsus::Middleware.settings = {:cache => true, :cache_path => cache_path} }
264
+ before(:each) { Jsus::Middleware.settings = {:cache => true, :cache_path => cache_path, :packages_dir => packages_dir} }
261
265
  after(:each) { FileUtils.rm_rf(cache_path) }
262
266
  let(:path) { "/javascripts/jsus/require/Package.js" }
263
267
  it "should save output of requests to files" do
264
268
  result = get(path).body
265
- File.exists?("#{cache_path}/Package.js").should be_true
266
- File.read("#{cache_path}/Package.js").should == result
269
+ File.exists?("#{cache_path}/require/Package.js").should be_true
270
+ File.read("#{cache_path}/require/Package.js").should == result
271
+ end
272
+
273
+ it "should not allow relative file paths hacks" do
274
+ FileUtils.rm_f("/tmp/testzor")
275
+ new_path = path + "/../../../../../../../../../../../../../../../tmp/testzor"
276
+ result = get(new_path).body
277
+ File.exists?("/tmp/testzor").should be_false
267
278
  end
268
279
  end
280
+
281
+ describe "post processing" do
282
+ let(:packages_dir) { File.expand_path("spec/data/ComplexDependencies") }
283
+ before(:each) { Jsus::Middleware.settings = {:packages_dir => packages_dir} }
284
+ let("path") { "/javascripts/jsus/require/Package.js" }
285
+ it "should not do anything if postprocs setting is empty" do
286
+ Jsus::Middleware.settings = {:postproc => []}
287
+ get(path).body.should include("//<ltIE8>")
288
+ end
289
+
290
+ it "should not do anything if postprocs setting is nil" do
291
+ Jsus::Middleware.settings = {:postproc => nil}
292
+ get(path).body.should include("//<ltIE8>")
293
+ end
294
+
295
+ it "should remove <ltIE8> tags if postproc setting contains mooltIE8" do
296
+ get(path).body.should include("//<ltIE8>")
297
+ Jsus::Middleware.settings = {:postproc => "mooltIE8"}
298
+ get(path).body.should_not include("//<ltIE8>")
299
+ end
300
+
301
+ it "should remove <1.2compat> tags if postproc setting contains moocompat12" do
302
+ get(path).body.should include("//<1.2compat>")
303
+ Jsus::Middleware.settings = {:postproc => "moocompat12"}
304
+ get(path).body.should_not include("//<1.2compat>")
305
+ end
306
+ end # describe "post processing"
307
+
308
+ describe "errors logging" do
309
+ let(:packages_dir) { File.expand_path("spec/data/MissingDependencies") }
310
+ before(:each) { Jsus::Middleware.settings = {:packages_dir => packages_dir} }
311
+
312
+ let(:path) { "/javascripts/jsus/require/Package.js" }
313
+ context "by default" do
314
+ it "should not output errors" do
315
+ output = get(path).body
316
+ output.should_not include("console.log")
317
+ output.should_not include("alert")
318
+ output.should_not include("document.body.innerHTML")
319
+ end
320
+ end # context "by default"
321
+
322
+ context "with console log method" do
323
+ before(:each) { Jsus::Middleware.settings = {:log_method => [:console] } }
324
+ it "should output errors to js console" do
325
+ get(path).body.should include("console.log")
326
+ end
327
+ end # context "with console log method"
328
+
329
+ context "with alert method" do
330
+ before(:each) { Jsus::Middleware.settings = {:log_method => [:alert] } }
331
+ it "should output errors via js alerts" do
332
+ get(path).body.should include("alert")
333
+ end
334
+ end # context "with console log method"
335
+
336
+ context "with html method" do
337
+ before(:each) { Jsus::Middleware.settings = {:log_method => [:html] } }
338
+ it "should output errors via html modification" do
339
+ get(path).body.should include("document.body.innerHTML")
340
+ end
341
+ end # context "with console log method"
342
+ end # describe "errors logging"
269
343
  end
@@ -20,6 +20,13 @@ describe Jsus::Util::FileCache do
20
20
  fn = subject.write(key, value)
21
21
  File.exists?("/tmp/test").should be_false
22
22
  end
23
+
24
+ it "should allow creation of subfolders" do
25
+ key = "test/hello"
26
+ fn = subject.write(key, value)
27
+ File.exists?("#{cache_dir}/#{key}").should be_true
28
+ File.read("#{cache_dir}/#{key}").should == value
29
+ end
23
30
  end
24
31
 
25
32
  describe "#read" do
@@ -37,6 +44,11 @@ describe Jsus::Util::FileCache do
37
44
  File.open("/tmp/test", "w+") {|f| f.puts "Hello, world!" }
38
45
  subject.read(key).should == nil
39
46
  end
47
+
48
+ it "should allow to read from subfolders" do
49
+ fn = subject.write("test/hello", value)
50
+ subject.read("test/hello").should == fn
51
+ end
40
52
  end
41
53
 
42
54
  describe "#fetch" do
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jsus::Util::Logger do
4
+ subject { described_class.new("/dev/null") }
5
+
6
+ context "buffering" do
7
+ it "should store incoming messages in the buffer" do
8
+ subject.info "Something happened"
9
+ subject.buffer.should == [[Logger::INFO, "Something happened"]]
10
+ end
11
+
12
+ it "should not store messages not crossing the threshold" do
13
+ subject.level = Logger::FATAL
14
+ subject.info "Something happened"
15
+ subject.buffer.should == []
16
+ end
17
+
18
+ it "should allow block form logging" do
19
+ subject.info { "Something happened" }
20
+ subject.buffer.should == [[Logger::INFO, "Something happened"]]
21
+ end
22
+ end # context "buffering"
23
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsus
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 3
10
- version: 0.3.3
9
+ - 4
10
+ version: 0.3.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Mark Abramov
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-19 00:00:00 +04:00
18
+ date: 2011-08-30 00:00:00 +04:00
19
19
  default_executable: jsus
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -325,9 +325,11 @@ files:
325
325
  - lib/jsus/tag.rb
326
326
  - lib/jsus/util.rb
327
327
  - lib/jsus/util/code_generator.rb
328
+ - lib/jsus/util/compressor.rb
328
329
  - lib/jsus/util/documenter.rb
329
330
  - lib/jsus/util/file_cache.rb
330
331
  - lib/jsus/util/inflection.rb
332
+ - lib/jsus/util/logger.rb
331
333
  - lib/jsus/util/tree.rb
332
334
  - lib/jsus/util/validator.rb
333
335
  - lib/jsus/util/validator/base.rb
@@ -387,6 +389,8 @@ files:
387
389
  - spec/data/JsonPackage/Source/SheetParser.CSS.js
388
390
  - spec/data/JsonPackage/Source/sg-regex-tools.js
389
391
  - spec/data/JsonPackage/package.json
392
+ - spec/data/MissingDependencies/Source/Widget.js
393
+ - spec/data/MissingDependencies/package.yml
390
394
  - spec/data/MooforgeValidation/README
391
395
  - spec/data/MooforgeValidation/app/javascripts/Orwik/Source/Library/InvalidNoAuthors.js
392
396
  - spec/data/MooforgeValidation/app/javascripts/Orwik/Source/Library/InvalidNoLicense.js
@@ -421,6 +425,7 @@ files:
421
425
  - spec/jsus/util/documenter_spec.rb
422
426
  - spec/jsus/util/file_cache_spec.rb
423
427
  - spec/jsus/util/inflection_spec.rb
428
+ - spec/jsus/util/logger_spec.rb
424
429
  - spec/jsus/util/tree_spec.rb
425
430
  - spec/jsus/util/validator/base_spec.rb
426
431
  - spec/jsus/util/validator/mooforge_spec.rb