faster_pathname 1.0.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.
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