faster_pathname 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0ccbc8073d0305e0b0884acb20129df0f8e26269
4
+ data.tar.gz: 85a9c53a151c5700e0f05e16f819dfc244e98119
5
+ SHA512:
6
+ metadata.gz: 6a15c9cfae6cc12c8055b7483edd0dd6275b4ed4d4412d9703c1ff6a3af72220fd8fff51fc54afadf19077eb56dc22150cc47dc21b666202a3e0b9ceac57859b
7
+ data.tar.gz: 17a611ee4b55a2aae84e7fab98beb3bed399ede70d5b20d29c33c282e36bec28f5b5e4a1b045defcb97e556be5f8c72435bf8280287efe53b714f470d66290bc
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - jruby
5
+ - 2.0.0
6
+ - 2.1.0
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: jruby
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in faster_pathname.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Chris Heald
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # FasterPathname
2
+
3
+ ![Travis build status](https://travis-ci.org/cheald/faster_pathname.png)
4
+
5
+ Pathname is slow. This makes Sprockets slow. This gem patches some of the slowest methods in
6
+ Pathname to make Sprockets fast.
7
+
8
+ See [this issue](https://github.com/sstephenson/sprockets/issues/506) for an overview of the issue.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'faster_pathname'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install faster_pathname
23
+
24
+ ## Usage
25
+
26
+ 1. Install gem
27
+ 2. Enjoy Sprockets being 25% faster
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ if RUBY_VERSION =~ /^1\.9/
7
+ t.test_files = FileList['test/test_pathname_19.rb']
8
+ else
9
+ t.test_files = FileList['test/test_pathname.rb']
10
+ end
11
+ t.verbose = true
12
+ end
13
+
14
+ task :default => :test
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'faster_pathname/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "faster_pathname"
8
+ spec.version = FasterPathname::VERSION
9
+ spec.authors = ["Chris Heald"]
10
+ spec.email = ["cheald@gmail.com"]
11
+ spec.summary = %q{Patches some parts of the Pathname Ruby stdlib class for improved performance}
12
+ spec.description = %q{Patches some parts of the Pathname Ruby stdlib class for improved performance}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,8 @@
1
+ require "faster_pathname/version"
2
+ require "pathname"
3
+
4
+ if File::ALT_SEPARATOR
5
+ require File.expand_path("../ruby/pathname_windows", __FILE__)
6
+ else
7
+ require File.expand_path("../ruby/pathname", __FILE__)
8
+ end
@@ -0,0 +1,3 @@
1
+ module FasterPathname
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,18 @@
1
+ class Pathname
2
+ def relative?
3
+ @path[0] != File::SEPARATOR
4
+ end
5
+
6
+ def join(*args)
7
+ last = args.last
8
+ if last.to_s[0] == File::SEPARATOR
9
+ if last.is_a? Pathname
10
+ last
11
+ else
12
+ Pathname.new last
13
+ end
14
+ else
15
+ Pathname.new(File.join @path, *args.map(&:to_s))
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ class Pathname
2
+ def relative?
3
+ @path[0] != File::SEPARATOR && @path[0] != File::ALT_SEPARATOR
4
+ end
5
+
6
+ def join(*args)
7
+ last = args.last
8
+ first_char = last.to_s[0]
9
+ if first_char == File::SEPARATOR || first_char == File::ALT_SEPARATOR
10
+ if last.is_a? Pathname
11
+ last
12
+ else
13
+ Pathname.new last
14
+ end
15
+ else
16
+ Pathname.new(File.join @path, *args.map(&:to_s))
17
+ end
18
+ end
19
+ end
data/test/envutil.rb ADDED
@@ -0,0 +1,396 @@
1
+ # -*- coding: us-ascii -*-
2
+ require "open3"
3
+ require "timeout"
4
+
5
+ module EnvUtil
6
+ def rubybin
7
+ unless ENV["RUBYOPT"]
8
+
9
+ end
10
+ if ruby = ENV["RUBY"]
11
+ return ruby
12
+ end
13
+ ruby = "ruby"
14
+ rubyexe = ruby+".exe"
15
+ 3.times do
16
+ if File.exist? ruby and File.executable? ruby and !File.directory? ruby
17
+ return File.expand_path(ruby)
18
+ end
19
+ if File.exist? rubyexe and File.executable? rubyexe
20
+ return File.expand_path(rubyexe)
21
+ end
22
+ ruby = File.join("..", ruby)
23
+ end
24
+ if defined?(RbConfig.ruby)
25
+ RbConfig.ruby
26
+ else
27
+ "ruby"
28
+ end
29
+ end
30
+ module_function :rubybin
31
+
32
+ LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
33
+
34
+ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false,
35
+ encoding: nil, timeout: 10, reprieve: 1, **opt)
36
+ in_c, in_p = IO.pipe
37
+ out_p, out_c = IO.pipe if capture_stdout
38
+ err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
39
+ opt[:in] = in_c
40
+ opt[:out] = out_c if capture_stdout
41
+ opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
42
+ if encoding
43
+ out_p.set_encoding(encoding) if out_p
44
+ err_p.set_encoding(encoding) if err_p
45
+ end
46
+ c = "C"
47
+ child_env = {}
48
+ LANG_ENVS.each {|lc| child_env[lc] = c}
49
+ if Array === args and Hash === args.first
50
+ child_env.update(args.shift)
51
+ end
52
+ args = [args] if args.kind_of?(String)
53
+ pid = spawn(child_env, EnvUtil.rubybin, *args, **opt)
54
+ in_c.close
55
+ out_c.close if capture_stdout
56
+ err_c.close if capture_stderr && capture_stderr != :merge_to_stdout
57
+ if block_given?
58
+ return yield in_p, out_p, err_p, pid
59
+ else
60
+ th_stdout = Thread.new { out_p.read } if capture_stdout
61
+ th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
62
+ in_p.write stdin_data.to_str
63
+ in_p.close
64
+ if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
65
+ stdout = th_stdout.value if capture_stdout
66
+ stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
67
+ else
68
+ signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
69
+ begin
70
+ Process.kill signal, pid
71
+ rescue Errno::ESRCH
72
+ break
73
+ else
74
+ end until signal == :KILL or (sleep reprieve; signal = :KILL; false)
75
+ raise Timeout::Error
76
+ end
77
+ out_p.close if capture_stdout
78
+ err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
79
+ Process.wait pid
80
+ status = $?
81
+ return stdout, stderr, status
82
+ end
83
+ ensure
84
+ [th_stdout, th_stderr].each do |th|
85
+ th.kill if th
86
+ end
87
+ [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
88
+ io.close if io && !io.closed?
89
+ end
90
+ [th_stdout, th_stderr].each do |th|
91
+ th.join if th
92
+ end
93
+ end
94
+ module_function :invoke_ruby
95
+
96
+ alias rubyexec invoke_ruby
97
+ class << self
98
+ alias rubyexec invoke_ruby
99
+ end
100
+
101
+ def verbose_warning
102
+ class << (stderr = "")
103
+ alias write <<
104
+ end
105
+ stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
106
+ yield stderr
107
+ return $stderr
108
+ ensure
109
+ stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
110
+ end
111
+ module_function :verbose_warning
112
+
113
+ def suppress_warning
114
+ verbose, $VERBOSE = $VERBOSE, nil
115
+ yield
116
+ ensure
117
+ $VERBOSE = verbose
118
+ end
119
+ module_function :suppress_warning
120
+
121
+ def under_gc_stress
122
+ stress, GC.stress = GC.stress, true
123
+ yield
124
+ ensure
125
+ GC.stress = stress
126
+ end
127
+ module_function :under_gc_stress
128
+
129
+ def with_default_external(enc)
130
+ verbose, $VERBOSE = $VERBOSE, nil
131
+ origenc, Encoding.default_external = Encoding.default_external, enc
132
+ $VERBOSE = verbose
133
+ yield
134
+ ensure
135
+ verbose, $VERBOSE = $VERBOSE, nil
136
+ Encoding.default_external = origenc
137
+ $VERBOSE = verbose
138
+ end
139
+ module_function :with_default_external
140
+
141
+ def with_default_internal(enc)
142
+ verbose, $VERBOSE = $VERBOSE, nil
143
+ origenc, Encoding.default_internal = Encoding.default_internal, enc
144
+ $VERBOSE = verbose
145
+ yield
146
+ ensure
147
+ verbose, $VERBOSE = $VERBOSE, nil
148
+ Encoding.default_internal = origenc
149
+ $VERBOSE = verbose
150
+ end
151
+ module_function :with_default_internal
152
+ end
153
+
154
+ module Test
155
+ module Unit
156
+ module Assertions
157
+ public
158
+ def assert_valid_syntax(code, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
159
+ code = code.dup.force_encoding("ascii-8bit")
160
+ code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
161
+ "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n"
162
+ }
163
+ code.force_encoding("us-ascii")
164
+ verbose, $VERBOSE = $VERBOSE, nil
165
+ yield if defined?(yield)
166
+ case
167
+ when Array === fname
168
+ fname, line = *fname
169
+ when defined?(fname.path) && defined?(fname.lineno)
170
+ fname, line = fname.path, fname.lineno
171
+ else
172
+ line = 0
173
+ end
174
+ assert_nothing_raised(SyntaxError, mesg) do
175
+ assert_equal(:ok, catch {|tag| eval(code, binding, fname, line)}, mesg)
176
+ end
177
+ ensure
178
+ $VERBOSE = verbose
179
+ end
180
+
181
+ def assert_syntax_error(code, error, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
182
+ code = code.dup.force_encoding("ascii-8bit")
183
+ code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
184
+ "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ng}\n"
185
+ }
186
+ code.force_encoding("us-ascii")
187
+ verbose, $VERBOSE = $VERBOSE, nil
188
+ yield if defined?(yield)
189
+ case
190
+ when Array === fname
191
+ fname, line = *fname
192
+ when defined?(fname.path) && defined?(fname.lineno)
193
+ fname, line = fname.path, fname.lineno
194
+ else
195
+ line = 0
196
+ end
197
+ e = assert_raise(SyntaxError, mesg) do
198
+ catch {|tag| eval(code, binding, fname, line)}
199
+ end
200
+ assert_match(error, e.message, mesg)
201
+ ensure
202
+ $VERBOSE = verbose
203
+ end
204
+
205
+ def assert_normal_exit(testsrc, message = '', child_env: nil, **opt)
206
+ assert_valid_syntax(testsrc, caller_locations(1, 1)[0])
207
+ if child_env
208
+ child_env = [child_env]
209
+ else
210
+ child_env = []
211
+ end
212
+ out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, opt)
213
+ assert !status.signaled?, FailDesc[status, message, out]
214
+ end
215
+
216
+ FailDesc = proc do |status, message = "", out = ""|
217
+ pid = status.pid
218
+ faildesc = proc do
219
+ signo = status.termsig
220
+ signame = Signal.list.invert[signo]
221
+ sigdesc = "signal #{signo}"
222
+ if signame
223
+ sigdesc = "SIG#{signame} (#{sigdesc})"
224
+ end
225
+ if status.coredump?
226
+ sigdesc << " (core dumped)"
227
+ end
228
+ full_message = ''
229
+ if message and !message.empty?
230
+ full_message << message << "\n"
231
+ end
232
+ full_message << "pid #{pid} killed by #{sigdesc}"
233
+ if out and !out.empty?
234
+ full_message << "\n#{out.gsub(/^/, '| ')}"
235
+ full_message << "\n" if /\n\z/ !~ full_message
236
+ end
237
+ full_message
238
+ end
239
+ faildesc
240
+ end
241
+
242
+ def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, **opt)
243
+ stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, opt)
244
+ if block_given?
245
+ raise "test_stdout ignored, use block only or without block" if test_stdout != []
246
+ raise "test_stderr ignored, use block only or without block" if test_stderr != []
247
+ yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp }, status)
248
+ else
249
+ errs = []
250
+ [[test_stdout, stdout], [test_stderr, stderr]].each do |exp, act|
251
+ begin
252
+ if exp.is_a?(Regexp)
253
+ assert_match(exp, act, message)
254
+ else
255
+ assert_equal(exp, act.lines.map {|l| l.chomp }, message)
256
+ end
257
+ rescue MiniTest::Assertion => e
258
+ errs << e.message
259
+ message = nil
260
+ end
261
+ end
262
+ raise MiniTest::Assertion, errs.join("\n---\n") unless errs.empty?
263
+ status
264
+ end
265
+ end
266
+
267
+ def assert_ruby_status(args, test_stdin="", message=nil, **opt)
268
+ out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, opt)
269
+ message ||= "ruby exit status is not success:"
270
+ assert(status.success?, FailDesc[status, message, out])
271
+ end
272
+
273
+ ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV")
274
+
275
+ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt)
276
+ unless file and line
277
+ loc, = caller_locations(1,1)
278
+ file ||= loc.path
279
+ line ||= loc.lineno
280
+ end
281
+ src = <<eom
282
+ require 'test/unit';include Test::Unit::Assertions;begin;#{src}
283
+ ensure
284
+ puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}"
285
+ end
286
+ class Test::Unit::Runner
287
+ @@stop_auto_run = true
288
+ end
289
+ eom
290
+ args = args.dup
291
+ $:.each{|l| args.unshift "-I#{l}" }
292
+ stdout, stderr, status = EnvUtil.invoke_ruby(args, src, true, true, opt)
293
+ abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig))
294
+ assert(!abort, FailDesc[status, stderr])
295
+ self._assertions += stdout[/^assertions=(\d+)/, 1].to_i
296
+ res = Marshal.load(stdout.unpack("m")[0])
297
+ if res
298
+ res.backtrace.each do |l|
299
+ l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
300
+ end
301
+ raise res
302
+ end
303
+
304
+ # really is it succeed?
305
+ unless ignore_stderr
306
+ # the body of assert_separately must not output anything to detect errror
307
+ assert_equal("", stderr, "assert_separately failed with error message")
308
+ end
309
+ assert_equal(0, status, "assert_separately failed: '#{stderr}'")
310
+ end
311
+
312
+ def assert_warning(pat, message = nil)
313
+ stderr = EnvUtil.verbose_warning { yield }
314
+ message = ' "' + message + '"' if message
315
+ msg = proc {"warning message #{stderr.inspect} is expected to match #{pat.inspect}#{message}"}
316
+ assert(pat === stderr, msg)
317
+ end
318
+
319
+ def assert_warn(*args)
320
+ assert_warning(*args) {$VERBOSE = false; yield}
321
+ end
322
+
323
+ def assert_no_memory_leak(args, prepare, code, message=nil, limit: 1.5)
324
+ token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m"
325
+ token_dump = token.dump
326
+ token_re = Regexp.quote(token)
327
+ args = [
328
+ "--disable=gems",
329
+ "-r", File.expand_path("../memory_status", __FILE__),
330
+ *args,
331
+ "-v", "-",
332
+ ]
333
+ cmd = [
334
+ 'END {STDERR.puts '"#{token_dump}"'"FINAL=#{Memory::Status.new.size}"}',
335
+ prepare,
336
+ 'STDERR.puts('"#{token_dump}"'"START=#{$initial_size = Memory::Status.new.size}")',
337
+ code,
338
+ ].join("\n")
339
+ _, err, status = EnvUtil.invoke_ruby(args, cmd, true, true)
340
+ before = err.sub!(/^#{token_re}START=(\d+)\n/, '') && $1.to_i
341
+ after = err.sub!(/^#{token_re}FINAL=(\d+)\n/, '') && $1.to_i
342
+ assert_equal([true, ""], [status.success?, err], message)
343
+ assert_operator(after.fdiv(before), :<, limit, message)
344
+ end
345
+
346
+ def assert_is_minus_zero(f)
347
+ assert(1.0/f == -Float::INFINITY, "#{f} is not -0.0")
348
+ end
349
+
350
+ def assert_file
351
+ AssertFile
352
+ end
353
+
354
+ class << (AssertFile = Struct.new(:message).new)
355
+ include Assertions
356
+ def assert_file_predicate(predicate, *args)
357
+ if /\Anot_/ =~ predicate
358
+ predicate = $'
359
+ neg = " not"
360
+ end
361
+ result = File.__send__(predicate, *args)
362
+ result = !result if neg
363
+ mesg = "Expected file " << args.shift.inspect
364
+ mesg << mu_pp(args) unless args.empty?
365
+ mesg << "#{neg} to be #{predicate}"
366
+ mesg << " #{message}" if message
367
+ assert(result, mesg)
368
+ end
369
+ alias method_missing assert_file_predicate
370
+
371
+ def for(message)
372
+ clone.tap {|a| a.message = message}
373
+ end
374
+ end
375
+ end
376
+ end
377
+ end
378
+
379
+ begin
380
+ require 'rbconfig'
381
+ rescue LoadError
382
+ else
383
+ module RbConfig
384
+ @ruby = EnvUtil.rubybin
385
+ class << self
386
+ undef ruby if method_defined?(:ruby)
387
+ attr_reader :ruby
388
+ end
389
+ dir = File.dirname(ruby)
390
+ name = File.basename(ruby, CONFIG['EXEEXT'])
391
+ CONFIG['bindir'] = dir
392
+ CONFIG['ruby_install_name'] = name
393
+ CONFIG['RUBY_INSTALL_NAME'] = name
394
+ Gem::ConfigMap[:bindir] = dir if defined?(Gem::ConfigMap)
395
+ end
396
+ end