pathological-v2 0.3.1

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
+ SHA256:
3
+ metadata.gz: 2282cfdb2f31372fd8740f570abfaf4a9adb81480ea7f536e74a2bdfa48b894f
4
+ data.tar.gz: 394d74d57b85f8d461b15b5016b15e59b404b081bf22ea5195a9ef2ea74e81c7
5
+ SHA512:
6
+ metadata.gz: d7fc1436cf05fa81fb3647b89d25fd82cfe78a40cce8e9c2313d514e25ff67df8edf1e027983347f087d926fb6e443f25a3114cbcd89d1cc0b65bd41cdc91f4e
7
+ data.tar.gz: ae0c8ace2fafd593eb2afea7b71d679a0bbe900a8d5acc7cce28f9e4b60964600aed3488479785f6d9c994140f7726932d1927f558767712856dd4a8f1a59b78
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.3.0"
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private lib/pathological/base.rb - README.md LICENSE
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Dependencies are specified in pathological.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Ooyala, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # Pathological
2
+
3
+ Pathological is a Ruby tool that provides a lightweight mechanism for managing your project's load path.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/ooyala/pathological.png)](http://travis-ci.org/ooyala/pathological)
6
+
7
+ ## The problem
8
+
9
+ When you're writing a gem, you don't have to worry about paths much, because Rubygems makes sure that `lib/`
10
+ makes it into your path for you. On the other hand, if you have large Ruby projects which aren't organized as
11
+ gems, you may encounter some of the following problems:
12
+
13
+ * If you don't have relative requires, you have to run your project from the project root.
14
+ * If you want relative requires, you have something nasty like this in your code:
15
+
16
+ require File.expand_path(File.join(File.dirname(__FILE__), 'myfile'))
17
+
18
+ * Ruby 1.9.2 breaks your load path if you are expecting `.` to be in it. You might have to use
19
+ `require_relative` or similar to remedy this.
20
+ * You have symlinks to shared libraries or non-gemified vendor code living all over your project in order
21
+ to keep your load paths sane.
22
+
23
+ Pathological provides one way to manage these issues.
24
+
25
+ ## Using pathological
26
+
27
+ Getting started with pathological is easy. First, make a file called `Pathfile` at your project root:
28
+
29
+ $ cd path/to/myproject
30
+ $ touch Pathfile
31
+
32
+ Now require the gem at the start of any executable ruby file:
33
+
34
+ ``` ruby
35
+ #!/usr/bin/env ruby
36
+
37
+ require "bundler/setup" # If you're using bundler
38
+ require "pathological"
39
+
40
+ # other requires...
41
+ ```
42
+
43
+ Now your project root will be in your load path. If your project has, for example, `lib/foo.rb`, then `require
44
+ lib/foo` will work in any of your ruby files. This works because when Pathological is required it will search
45
+ up the directory tree until it finds a `Pathfile`. (It will raise an error if one cannot be found).
46
+
47
+ Note that Pathological should be the first require in your main file so that it will be loaded first. An
48
+ exception to this is when you're using Bundler, in which case you should `require bundler/setup` before
49
+ Pathological (and of course you should have `gem "pathological"` in your `Gemfile`).
50
+
51
+ `Pathfile`s should be kept in version control.
52
+
53
+ ## Adding other paths to your load path
54
+
55
+ To add more paths to your load path, just put the paths in your `Pathfile`. The paths are relative to the
56
+ location of the `Pathfile`. The paths will be inserted in the order they appear; the project root itself will
57
+ be first. If any of the paths are not valid directories, then an exception will be raised when Pathological is
58
+ required.
59
+
60
+ #### Example
61
+
62
+ Suppose that you have a directory structure like this:
63
+
64
+ repos/
65
+ |-shared_lib/
66
+ | `-common.rb
67
+ `-my_project/
68
+ |-Pathfile
69
+ |-run_my_project.rb
70
+ `-foo.rb
71
+
72
+ and that `Pathfile` contains the following:
73
+
74
+ ../shared_lib
75
+
76
+ Then inside `run_my_project.rb`:
77
+
78
+ ``` ruby
79
+ require "pathological"
80
+ require "foo"
81
+ require "common"
82
+ # ...
83
+ ```
84
+
85
+ ## Installation
86
+
87
+ Pathological is packaged as a Rubygem and hence can be trivially installed with
88
+
89
+ $ gem install pathological
90
+
91
+ ## Advanced usage
92
+
93
+ In some cases, you might want slightly different behavior. This customization is done through the use of
94
+ custom modes. You may use any combination of modes.
95
+
96
+ #### debug
97
+
98
+ This adds debugging statements to `STDOUT` that explain what Pathological is doing.
99
+
100
+ #### excluderoot
101
+
102
+ In this mode, the project root (where the `Pathfile` is located) is not added to the load path (so only paths
103
+ specified *in* the `Pathfile` will be loaded).
104
+
105
+ #### noexceptions
106
+
107
+ This is used if you don't want to raise exceptions if you have bad paths (i.e. non-existent paths or not
108
+ directories) in your `Pathfile`.
109
+
110
+ #### bundlerize
111
+
112
+ Bundlerize mode enables Bundler to work with your project regardless of your current directory, in the same
113
+ way as Pathological, by attempting to set the `BUNDLE_GEMFILE` environment variable to match the directory
114
+ where the `Pathfile` is located. Note that you have to run this before requiring `bundler/setup`. Also, this
115
+ will not take effect if you are running with `bundle exec`.
116
+
117
+ #### parentdir
118
+
119
+ This mode makes Pathological add the unique parents of all paths it finds (instead of the paths themselves).
120
+ The purpose of parentdir is to enable Pathological to work in a drop-in fashion with legacy code written with
121
+ all requires being relative to the root of the codebase. Note that this will allow one to require files
122
+ located in any child of the parents, not just from the directories specified in the `Pathfile`. This mode
123
+ should be avoided if possible.
124
+
125
+ There are two ways to specify modes. First, you can enable any modes you want using the Pathological API:
126
+
127
+ ``` ruby
128
+ require "pathological/base"
129
+ Pathological.debug_mode
130
+ Pathological.parentdir_mode
131
+ Pathological.add_paths!
132
+ ```
133
+
134
+ A quicker way is also provided: if you only need to use one special mode, then there is a dedicated file you
135
+ can require:
136
+
137
+ ``` ruby
138
+ require "pathological/debug"
139
+ ```
140
+
141
+ ## Public API
142
+
143
+ For even more configurable custom integration with Pathological, a public API is provided. See the generated
144
+ documentation for details on the following public methods:
145
+
146
+ * `Pathological#add_paths!`
147
+ * `Pathological#find_load_paths`
148
+ * `Pathological#find_pathfile`
149
+ * `Pathological#reset!`
150
+ * `Pathological#copy_outside_paths!`
151
+
152
+ ## Notes and gotchas
153
+
154
+ Pathological is intended as a replacement for manually munging the load path in your application when you want
155
+ to load code from other locations. If you can turn each of your dependencies into a gem and then use bundler
156
+ to manage the dependencies, **do that instead**.
157
+
158
+ Pathological does its best to figure out where the Pathfile it should use is located, based on the call stack.
159
+ For instance, if you run `/foo/bar.rb` and that requires Pathological, then it will search for `/foo/Pathfile`
160
+ and -- failing that -- `/Pathfile`. However, if `/foo/bar.rb` does not require Pathological, but loads a ruby
161
+ file in `/baz/quux.rb`, and *that* file requires Pathological, then Pathological searches for `/baz/Pathfile`
162
+ and then `/Pathfile`.
163
+
164
+ Any code loading situation which does not preserve a sane load path will be incompatible with Pathological.
165
+ For instance, if you `eval()` some code that requires Pathological, Pathological has no way of telling where
166
+ that code originally lived, and will probably behave in an unexpected way. One place this commonly occurs is
167
+ with rack webservers (e.g., Rackup, Unicorn) that load a `config.ru`. This file is `instance_eval`ed and so
168
+ requiring Pathological from your `config.ru` will not work as expected. In these cases, require Pathological
169
+ in your app directly.
170
+
171
+ ## Authors
172
+
173
+ Pathological was written by the following Ooyala engineers:
174
+
175
+ * [Daniel MacDougall](mailto:dmac@ooyala.com)
176
+ * [Caleb Spare](mailto:caleb@ooyala.com)
177
+ * [Sami Abu-El-Haija](mailto:sami@ooyala.com)
178
+ * [Evan Chan](mailto:ev@ooyala.com)
179
+
180
+ ## Credits
181
+
182
+ * Harry Robertson for the idea to *not* use a dot-prefixed configuration file
183
+
184
+ ## Metadata
185
+
186
+ * [Hosted on Github](https://github.com/ooyala/pathological)
187
+ * [Rubygems page](https://rubygems.org/gems/pathological)
188
+ * [Documentation](http://rubydoc.info/github/ooyala/pathological/master/frames)
189
+
190
+ ## Contributing
191
+
192
+ If you would like to commit a patch, great! Just do the usual github pull request stuff and we'll check it
193
+ out[.](http://www.randomkittengenerator.com/)
194
+
195
+ ## License
196
+
197
+ Pathological is licensed under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler"
2
+ require "rake/testtask"
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ task :test => ["test:units"]
7
+
8
+ namespace :test do
9
+ Rake::TestTask.new(:units) do |task|
10
+ task.libs << "test"
11
+ task.test_files = FileList["test/unit/**/*_test.rb"]
12
+ end
13
+ end
14
+
15
+ task :default => ["test:units"]
data/TODO.md ADDED
@@ -0,0 +1,66 @@
1
+ TODO
2
+ ====
3
+
4
+ Tasks
5
+ -----
6
+
7
+ * Get all loaded files that come from Pathological directories (probably only in 1.9)
8
+
9
+ Design decisions
10
+ ----------------
11
+
12
+ * Should we allow for including other Pathfiles? Proposed syntaxes:
13
+
14
+ include path/to/other/Pathfile
15
+ import path/to/other/Pathfile # Rakefile
16
+ i path/to/other/Pathfile
17
+ source path/to/other/Pathfile # Bash
18
+ . path/to/other/Pathfile # Bash
19
+ path/to/other/Pathfile # Distinguishable from regular path if Pathfile is not a directory
20
+
21
+ **Not needed now --Caleb**
22
+
23
+ * Do we like `>` to signify directives? Alternatives:
24
+
25
+ > exclude-root # Current syntax
26
+ exclude-root # Only problem is if you want to include a directory with the same name as a directive
27
+
28
+ We could also prefix each type of line to make things unambiguous:
29
+
30
+ p path/to/lib/
31
+ d exclude-root
32
+
33
+ **Fine for now --Caleb**
34
+
35
+ * Right now there's a small problem with comments: if your path includes the character `#`, then the rest
36
+ will be chopped off (interpreted as a comment). We could remedy this by only allowing for comments to
37
+ start at the beginning of lines:
38
+
39
+ # Yes
40
+ ../lib/ # No
41
+
42
+ **Let's leave this alone for now; probably a non-issue --Caleb**
43
+
44
+ * Right our require paths tend to look like this (using `shared_lib/` as an example):
45
+
46
+ ``` ruby
47
+ require "shared_lib/utils/foo_util"
48
+ ```
49
+
50
+ To support this, you would need your `Pathfile` to include the directory *above* `shared_lib/`. The
51
+ downside of this is that it doesn't play well with the idea of using our `Pathfile`s to tell our deploy
52
+ scripts what to include. We could potentially add a new construct to allow the user to specify which
53
+ subdirectories they would be using. Example (just off the top of my head):
54
+
55
+ path/to/repo/dir/{shared_lib, common, vendor}
56
+
57
+ However, we'd still have to add `path/to/repo/dir` to the `$LOAD_PATH`, so the only way to enforce this at
58
+ require time would be to use a custom `require`. This is all quite a high cost to pay in terms of design
59
+ simplicity, but yet being able to use Pathfiles as a single place to document what dependencies to pull in
60
+ seems very appealing. Any ideas?
61
+
62
+ **After chatting with some people, we're going to leave this as is and truncate the `shared_lib` from our
63
+ paths. --Caleb**
64
+
65
+ **Actually, I think I'm going to add an optional mode to add one directory _above_ to the load path
66
+ instead of the given path, in order to accomomdate this use case. --Caleb**
@@ -0,0 +1,3 @@
1
+ require "pathological/base"
2
+
3
+ Pathological.add_paths!
@@ -0,0 +1,264 @@
1
+ require "pathname"
2
+
3
+ module Pathological
4
+ PATHFILE_NAME = "Pathfile"
5
+
6
+ class PathologicalException < RuntimeError; end
7
+ class NoPathfileException < PathologicalException; end
8
+
9
+ # Add paths to the load path.
10
+ #
11
+ # @param [String] load_path the load path to use.
12
+ # @param [Array<String>] paths the array of new load paths (if +nil+, the result of {find_load_paths}).
13
+ def self.add_paths!(load_path = $LOAD_PATH, paths = nil)
14
+ begin
15
+ paths ||= find_load_paths
16
+ rescue NoPathfileException
17
+ STDERR.puts "Warning: using Pathological, but no Pathfile was found."
18
+ return
19
+ end
20
+ paths.each do |path|
21
+ if load_path.include? path
22
+ debug "Skipping <#{path}>, which is already in the load path."
23
+ else
24
+ debug "Adding <#{path}> to load path."
25
+ load_path << path
26
+ @@loaded_paths << path
27
+ end
28
+ end
29
+ end
30
+
31
+ # For some pathfile, parse it and find all the load paths that it references.
32
+ #
33
+ # @param [String, nil] pathfile the pathfile to inspect. Uses {find_pathfile} if +nil+.
34
+ # @return [Array<String>] the resulting array of paths.
35
+ def self.find_load_paths(pathfile = nil)
36
+ pathfile ||= find_pathfile
37
+ raise NoPathfileException unless pathfile
38
+ begin
39
+ pathfile_handle = File.open(pathfile)
40
+ rescue Errno::ENOENT
41
+ raise NoPathfileException
42
+ rescue
43
+ raise PathologicalException, "There was an error opening the pathfile <#{pathfile}>."
44
+ end
45
+ parse_pathfile(pathfile_handle)
46
+ end
47
+
48
+ # Find the pathfile by searching up from a starting directory. Symlinks are expanded out.
49
+ #
50
+ # @param [String] directory the starting directory. Defaults to the directory containing the running file.
51
+ # @return [String, nil] the absolute path to the pathfile (if it exists), otherwise +nil+.
52
+ def self.find_pathfile(directory = nil)
53
+ # If we're in IRB, use the working directory as the root of the search path for the Pathfile.
54
+ if $0 != __FILE__ && $0 == "irb"
55
+ directory = Dir.pwd
56
+ debug "In IRB -- using the cwd (#{directory}) as the search root for Pathfile."
57
+ end
58
+ return nil if directory && !File.directory?(directory)
59
+ # Find the full, absolute path of this directory, resolving symlinks. If no directory was given, use the
60
+ # directory where the file requiring pathological resides.
61
+ full_path = real_path(directory || requiring_filename)
62
+ current_path = directory ? full_path : File.dirname(full_path)
63
+ loop do
64
+ debug "Searching <#{current_path}> for Pathfile."
65
+ pathfile = File.join(current_path, PATHFILE_NAME)
66
+ if File.file? pathfile
67
+ debug "Pathfile found: <#{pathfile}>."
68
+ return pathfile
69
+ end
70
+ new_path = File.dirname current_path
71
+ if new_path == current_path
72
+ debug "Reached filesystem root, but no Pathfile found."
73
+ return nil
74
+ end
75
+ current_path = new_path
76
+ end
77
+ end
78
+
79
+ # Copies directories in pathfile to a destination, such that the destination has no references to
80
+ # directories outside of the destination in the load path.
81
+ #
82
+ # Hierarchy of destination directory:
83
+ # destination/
84
+ # Pathfile # new paths
85
+ # dependency_directory/
86
+ # dependency1 # Copied from original location
87
+ #
88
+ # This is very useful for deployment, for example.
89
+ #
90
+ # @param [String] copy_outside_paths the directory to stage dependencies in
91
+ # @param [String] dependency_directory the subdir within destination to put dependencies in
92
+ # @param [String] pathfile_search_path the directory at which to begin the search for the Pathfile by
93
+ # walking up the directory tree
94
+ #
95
+ # TODO(ev): Break this function up into a set of more functional primitives
96
+ def self.copy_outside_paths!(destination, options = {})
97
+ options = { :dependency_directory => "pathological_dependencies" }.merge(options)
98
+ saved_exclude_root = @@exclude_root
99
+ begin
100
+ self.excluderoot_mode
101
+ pathfile = self.find_pathfile(options[:pathfile_search_path])
102
+ # Nothing to do if there's no Pathfile
103
+ return unless pathfile && File.file?(pathfile)
104
+
105
+ foreign_paths = self.find_load_paths(pathfile).uniq
106
+ return if foreign_paths.empty?
107
+
108
+ path_root = File.join(destination, options[:dependency_directory])
109
+ FileUtils.mkdir_p path_root
110
+
111
+ # Copy in each path and save the relative paths to write to the rewritten Pathfile. We copy each unique
112
+ # path into the folder not as the basename, but as the longest suffix of the path necessary to make it
113
+ # unique. (Otherwise this won't work if you have two entries with the same basename in the Pathfile,
114
+ # such as "foo/lib" and "bar/lib".)
115
+ common_prefix = find_longest_common_prefix(foreign_paths)
116
+ new_pathfile_paths = foreign_paths.map do |foreign_path|
117
+ path_short_name = foreign_path.gsub(/^#{common_prefix}/, "")
118
+ symlinked_name = File.join(path_root, path_short_name)
119
+ FileUtils.mkdir_p File.split(symlinked_name)[0]
120
+ debug "About to move #{foreign_path} to #{symlinked_name}..."
121
+ copy_directory(foreign_path, symlinked_name)
122
+ File.join(options[:dependency_directory], path_short_name)
123
+ end
124
+ # Overwrite the Pathfile with the new relative paths.
125
+ File.open(File.join(destination, "Pathfile"), "w") do |file|
126
+ new_pathfile_paths.each { |path| file.puts path }
127
+ end
128
+ ensure
129
+ @@exclude_root = saved_exclude_root
130
+ end
131
+ end
132
+
133
+ # Convenience functions for the various modes in which Pathological may run.
134
+
135
+ def self.debug_mode; @@debug = true; end
136
+ def self.bundlerize_mode
137
+ pathfile = self.find_pathfile
138
+ raise NoPathfileException unless pathfile
139
+ bundle_gemfile = File.join(File.dirname(pathfile), "Gemfile")
140
+ unless File.file? bundle_gemfile
141
+ raise PathologicalException, "No Gemfile found in #{File.dirname(pathfile)}."
142
+ end
143
+ ENV["BUNDLE_GEMFILE"] = bundle_gemfile
144
+ end
145
+ def self.parentdir_mode; @@add_parents = true; end
146
+ def self.noexceptions_mode; @@no_exceptions = true; end
147
+ def self.excluderoot_mode; @@exclude_root = true; end
148
+
149
+ # Reset all Pathological options (useful if you want to require a different Pathfile)
150
+ def self.reset!
151
+ # Debug mode -- print out information about load paths
152
+ @@debug = false
153
+ # Parentdir mode -- add unique parents of specified directories.
154
+ @@add_parents = false
155
+ # Noexceptions mode -- don't raise exceptions if the Pathfile contains bad paths
156
+ @@no_exceptions = false
157
+ # Excluderoot mode -- don't add the project root (where the Pathfile lives) to the load path
158
+ @@exclude_root = false
159
+
160
+ @@loaded_paths ||= []
161
+ @@loaded_paths.each { |path| $LOAD_PATH.delete path }
162
+ @@loaded_paths = []
163
+ end
164
+
165
+ # private module methods
166
+
167
+ # Print debugging info
168
+ #
169
+ # @private
170
+ # @param [String] message the debugging message
171
+ # @return [void]
172
+ def self.debug(message); puts "[Pathological Debug] >> #{message}" if @@debug; end
173
+
174
+ # Turn a path into an absolute path with no useless parts and no symlinks.
175
+ #
176
+ # @private
177
+ # @param [String] the path
178
+ # @return [String] the absolute real path
179
+ def self.real_path(path); Pathname.new(path).realpath.to_s; end
180
+
181
+ # Parse a pathfile and return the appropriate paths.
182
+ #
183
+ # @private
184
+ # @param [IO] pathfile handle to the pathfile to parse
185
+ # @return [Array<String>] array of paths found
186
+ def self.parse_pathfile(pathfile)
187
+ root = File.dirname(real_path(pathfile.path))
188
+ raw_paths = [root]
189
+ pathfile.each do |line|
190
+ # Trim comments
191
+ line = line.split(/#/, 2)[0].strip
192
+ next if line.empty?
193
+ raw_path = Pathname.new(line).absolute? ? line : File.expand_path(File.join(root, line))
194
+ raw_paths << (@@add_parents ? File.dirname(raw_path) : raw_path)
195
+ end
196
+
197
+ paths = []
198
+ raw_paths.each do |path|
199
+ unless File.directory? path
200
+ unless @@no_exceptions
201
+ raise PathologicalException, "Bad path in Pathfile: #{path}"
202
+ end
203
+ debug "Ignoring non-existent path: #{path}"
204
+ next
205
+ end
206
+ next if @@exclude_root && File.expand_path(path) == File.expand_path(root)
207
+ paths << path
208
+ end
209
+ @@exclude_root ? paths.reject { |path| File.expand_path(path) == File.expand_path(root) } : paths
210
+ end
211
+
212
+ # Find the longest common path prefix amongst a list of paths
213
+ #
214
+ # @private
215
+ # @param [List<String>] a list of paths
216
+ # @return [String] the longest common prefix, or "/"
217
+ def self.find_longest_common_prefix(paths)
218
+ if paths.size == 1
219
+ common_prefix = File.split(paths[0])[0]
220
+ else
221
+ common_prefix = "/"
222
+ paths[0].split("/").reject(&:empty?).each do |part|
223
+ new_prefix = "#{common_prefix}#{part}/"
224
+ break unless paths.all? { |path| path.start_with? new_prefix }
225
+ common_prefix = new_prefix
226
+ end
227
+ end
228
+ common_prefix
229
+ end
230
+
231
+ # Copies a directory and all its symlinks to a destination.
232
+ # @private
233
+ def self.copy_directory(source, dest)
234
+ rsync_command = "rsync -r --archive --links --copy-unsafe-links --delete #{source}/ '#{dest}'"
235
+ debug `#{rsync_command}`
236
+ end
237
+
238
+ # Searches the call stack for the file that required pathological. If no file can be found, falls back to
239
+ # the currently executing file ($0). This handles the case where the app was launched by another executable
240
+ # (rake, thin, etc.)
241
+ #
242
+ # @return [String] name of file requiring pathological, or the currently executing file.
243
+ def self.requiring_filename
244
+ # Match paths like .../gems/pathological-0.2.2.1/lib/pathological/base.rb and also
245
+ # .../gems/pathological-0.2.2.1/lib/pathological.rb
246
+ pathological_file_pattern = %r{/pathological(/[^/]+|)\.rb}
247
+ requiring_file = Kernel.caller.find do |stack_line|
248
+ trimed_ruby_version = RUBY_VERSION[0..3].to_f rescue 1.9
249
+ if trimed_ruby_version >= 1.9
250
+ # In Ruby >=1.9, top-level files will have the string "top (required)" included in the stack listing.
251
+ stack_line.include?("top (required)") && stack_line !~ pathological_file_pattern
252
+ else
253
+ # In Ruby <=1.8, top-level files are listed with their relative path and without a line number.
254
+ stack_line !~ /:\d+:in/ && stack_line !~ pathological_file_pattern
255
+ end
256
+ end
257
+ requiring_file ? requiring_file.match(/(.+):\d+/)[1] : $0 rescue $0
258
+ end
259
+
260
+ private_class_method :debug, :real_path, :parse_pathfile, :find_longest_common_prefix, :copy_directory
261
+
262
+ # Reset options
263
+ Pathological.reset!
264
+ end
@@ -0,0 +1,9 @@
1
+ # Make bundler compatible with Pathological (that is, enable Bundler projects to be run from anywhere as
2
+ # Pathological allows) by setting the BUNDLE_GEMFILE env variable.
3
+ #
4
+ # To use this, you *must* require pathological/bundlerize before you require bundler/setup.
5
+
6
+ require "pathological/base"
7
+
8
+ Pathological.bundlerize_mode
9
+ Pathological.add_paths!
@@ -0,0 +1,4 @@
1
+ require "pathological/base"
2
+
3
+ Pathological.debug_mode
4
+ Pathological.add_paths!
@@ -0,0 +1,4 @@
1
+ require "pathological/base"
2
+
3
+ Pathological.excluderoot_mode
4
+ Pathological.add_paths!
@@ -0,0 +1,4 @@
1
+ require "pathological/base"
2
+
3
+ Pathological.noexceptions_mode
4
+ Pathological.add_paths!
@@ -0,0 +1,8 @@
1
+ # In parentdir mode, Pathological will not add the individual specified paths to $LOAD_PATH, but will instead
2
+ # add the unique parents of the specified paths. This is to enable compatibility with legacy code where
3
+ # require paths are all written relative to a common repository root.
4
+
5
+ require "pathological/base"
6
+
7
+ Pathological.parentdir_mode
8
+ Pathological.add_paths!
@@ -0,0 +1,3 @@
1
+ module Pathological
2
+ VERSION = "0.3.1"
3
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.unshift File.expand_path("../lib", __FILE__)
3
+ require "pathological/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "pathological-v2"
7
+ s.version = Pathological::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">=2.3.0") if s.respond_to? :required_rubygems_version=
11
+ s.specification_version = 2 if s.respond_to? :specification_version=
12
+
13
+ s.authors = "Maheshwaran G"
14
+ s.email = "maheshwarang@brightcove.com"
15
+ s.homepage = "http://www.ooyala.com"
16
+ s.rubyforge_project = "pathological-v2"
17
+
18
+ s.summary = "A nice way to manage your project's require paths."
19
+ s.description = <<-DESCRIPTION
20
+ Extension of Pathological Gem to support Ruby > 1.9 version
21
+ indicates all directories to include in the load path.
22
+ DESCRIPTION
23
+
24
+ s.files = `git ls-files`.split("\n").reject { |f| f == ".gitignore" }
25
+ s.require_paths = ["lib"]
26
+
27
+ # Require rr >= 1.0.3 and scope >= 0.2.3 for mutual compatibility.
28
+ s.add_development_dependency "rr", "=1.0.4"
29
+ s.add_development_dependency "scope", "~> 0.2.3"
30
+ s.add_development_dependency "fakefs", "~> 0.4.0"
31
+ s.add_development_dependency "rake", "~>0.9.2.2"
32
+ s.add_development_dependency "minitest", "=4.3.3"
33
+ s.add_development_dependency "dedent"
34
+ end
@@ -0,0 +1,296 @@
1
+ require "rubygems"
2
+ require "scope"
3
+ require "rr"
4
+ require "minitest/autorun"
5
+ require "stringio"
6
+ require "fakefs/safe"
7
+ require "dedent"
8
+
9
+ # It's kind of funny that we need to do this hack, given that Pathological is intended to work around it...
10
+ $:.unshift(File.join(File.dirname(__FILE__), "../../lib"))
11
+ require "pathological/base"
12
+
13
+ module Pathological
14
+ class BaseTest < Scope::TestCase
15
+ include RR::Adapters::MiniTest
16
+ def assert_load_path(expected_load_path)
17
+ assert_equal expected_load_path.uniq.sort, @load_path.uniq.sort
18
+ end
19
+
20
+ context "Pathological" do
21
+ setup_once { FakeFS.activate! }
22
+ setup do
23
+ Pathological.reset!
24
+ @load_path = []
25
+ FakeFS::FileSystem.clear
26
+ # FakeFS has not implemented the necessary calls for Pathname#realpath to work.
27
+ stub(Pathological).real_path(anything) { |p| p }
28
+ end
29
+ teardown_once { FakeFS.deactivate! }
30
+
31
+ context "#add_paths!" do
32
+ should "not raise an error but print a warning when there's no pathfile" do
33
+ mock(Pathological).find_pathfile { nil }
34
+ mock(STDERR).puts(anything) { |m| assert_match /^Warning/, m }
35
+ Pathological.add_paths! @load_path
36
+ assert_load_path []
37
+ end
38
+
39
+ should "append the requested paths" do
40
+ paths = ["foo"]
41
+ Pathological.add_paths! @load_path, paths
42
+ assert_load_path paths
43
+ end
44
+
45
+ should "append the paths that #find_load_paths finds" do
46
+ paths = ["foo"]
47
+ mock(Pathological).find_load_paths { paths }
48
+ Pathological.add_paths! @load_path
49
+ assert_load_path paths
50
+ end
51
+ end
52
+
53
+ context "#find_load_paths" do
54
+ should "raise a NoPathfileException on a nil pathfile" do
55
+ mock(Pathological).find_pathfile { nil }
56
+ assert_raises(NoPathfileException) { Pathological.find_load_paths(nil) }
57
+ end
58
+
59
+ should "use #find_pathfile to find the Pathfile and #parse_pathfile to parse it." do
60
+ paths = ["path1"]
61
+ mock(Pathological).find_pathfile { "foo" }
62
+ mock(File).open("foo") { "bar" }
63
+ mock(Pathological).parse_pathfile("bar") { paths }
64
+ assert_equal paths, Pathological.find_load_paths
65
+ end
66
+ end
67
+
68
+ context "#find_pathfile" do
69
+ setup do
70
+ @working_directory = "/foo/bar/baz"
71
+ FileUtils.mkdir_p @working_directory
72
+ FileUtils.cd @working_directory
73
+ end
74
+
75
+ should "find a pathfile in this directory" do
76
+ pathfile = "/foo/bar/baz/Pathfile"
77
+ FileUtils.touch pathfile
78
+ assert_equal pathfile, Pathological.find_pathfile(@working_directory)
79
+ end
80
+
81
+ should "find a pathfile in a parent directory" do
82
+ pathfile = "/foo/bar/Pathfile"
83
+ FileUtils.touch pathfile
84
+ assert_equal pathfile, Pathological.find_pathfile(@working_directory)
85
+ end
86
+
87
+ should "find a pathfile at the root" do
88
+ pathfile = "/Pathfile"
89
+ FileUtils.touch pathfile
90
+ assert_equal pathfile, Pathological.find_pathfile(@working_directory)
91
+ end
92
+
93
+ should "locate a pathfile in the real path even if we're running from a symlinked directory" do
94
+ pathfile = "/foo/bar/baz/Pathfile"
95
+ FileUtils.touch pathfile
96
+ FileUtils.touch "/Pathfile" # Shouldn't find this one
97
+ symlinked_directory = "/foo/bar/quux"
98
+ FileUtils.ln_s @working_directory, symlinked_directory
99
+ stub(Pathological).real_path(anything) { |path| path.gsub("quux", "baz") }
100
+ assert_equal pathfile, Pathological.find_pathfile(@working_directory)
101
+ end
102
+ end
103
+
104
+ context "#requiring_filename" do
105
+ setup do
106
+ @full_19_stacktrace = <<-EOS.dedent.split("\n").reject(&:empty?)
107
+ /Users/test/ruby/gems/1.9.1/gems/pathological-0.2.2.1/lib/pathological/base.rb:61:in `find_pathfile'
108
+ /Users/test/gems/pathological-0.2.2.1/lib/pathological/base.rb:36:in `find_load_paths'
109
+ /Users/test/gems/pathological-0.2.2.1/lib/pathological/base.rb:15:in `add_paths!'
110
+ /Users/test/gems/pathological-0.2.2.1/lib/pathological.rb:3:in `<top (required)>'
111
+ /Users/test/ruby/1.9.1/rubygems/custom_require.rb:59:in `require'
112
+ /Users/test/ruby/1.9.1/rubygems/custom_require.rb:59:in `rescue in require'
113
+ /Users/test/ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
114
+ /Users/test/repos/pathological/test/rackup/app.rb:1:in `<top (required)>'
115
+ /Users/test/.rubies/1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
116
+ EOS
117
+ @full_18_stacktrace = <<-EOS.dedent.split("\n").reject(&:empty?)
118
+ /Users/test/ruby/gems/1.8/gems/pathological-0.2.5/lib/pathological/base.rb:61:in `find_pathfile'
119
+ /Users/test/ruby/gems/1.8/gems/pathological-0.2.5/lib/pathological/base.rb:36:in `find_load_paths'
120
+ /Users/test/ruby/gems/1.8/gems/pathological-0.2.5/lib/pathological/base.rb:15:in `add_paths!'
121
+ /Users/test/ruby/gems/1.8/gems/pathological-0.2.5/lib/pathological.rb:3
122
+ /Users/test/ruby/site_ruby/1.8/rubygems/custom_require.rb:58:in `gem_original_require'
123
+ /Users/test/ruby/site_ruby/1.8/rubygems/custom_require.rb:58:in `require'
124
+ app.rb:2
125
+ EOS
126
+ @bad_stacktrace = <<-EOS.dedent.split("\n").reject(&:empty?)
127
+ /Users/test/repos/pathological/test/rackup/app.rb !!! `<top (required)>'
128
+ EOS
129
+ @empty_stacktrace = []
130
+ end
131
+
132
+ should "find root file from a stacktrace" do
133
+ if RUBY_VERSION.start_with?("1.9") || RUBY_VERSION.start_with?("2.3")
134
+ stub(Kernel).caller { @full_19_stacktrace }
135
+ else
136
+ stub(Kernel).caller { @full_18_stacktrace }
137
+ end
138
+ assert_equal "app.rb", File.basename(Pathological.requiring_filename)
139
+ end
140
+
141
+ should "return executing file from malformed stacktrace" do
142
+ stub(Kernel).caller { @bad_stacktrace }
143
+ assert Pathological.requiring_filename
144
+ end
145
+
146
+ should "return executing file if unable to find root file in stacktrace" do
147
+ stub(Kernel).caller { @empty_stacktrace }
148
+ assert Pathological.requiring_filename
149
+ end
150
+ end
151
+
152
+ context "loading pathological" do
153
+ setup do
154
+ @pathfile_contents = ""
155
+ @pathfile = "/Pathfile"
156
+ $0 = "/my_file"
157
+ FileUtils.touch @pathfile
158
+ FileUtils.cd "/"
159
+ end
160
+
161
+ # Load in pathfile contents and load Pathological
162
+ def load_and_run!
163
+ File.open(@pathfile, "w") { |f| f.write(@pathfile_contents) }
164
+ Pathological.add_paths!(@load_path)
165
+ end
166
+
167
+ should "use an empty Pathfile correctly" do
168
+ load_and_run!
169
+ assert_load_path ["/"]
170
+ end
171
+
172
+ should "add some paths as appropriate" do
173
+ paths = ["/foo/bar", "/baz"]
174
+ paths.each do |path|
175
+ FileUtils.mkdir_p path
176
+ @pathfile_contents << "#{path}\n"
177
+ end
178
+ load_and_run!
179
+ assert_load_path(paths << "/")
180
+ end
181
+
182
+ should "throw exceptions on bad load paths" do
183
+ path = "/foo/bar"
184
+ @pathfile_contents << "#{path}"
185
+ assert_raises(PathologicalException) { load_and_run! }
186
+ end
187
+
188
+ should "respect absolute paths" do
189
+ other = "/a/b"
190
+ @pathfile = "/src/Pathfile"
191
+ FileUtils.mkdir "/src"
192
+ FileUtils.mkdir_p other
193
+ FileUtils.touch @pathfile
194
+ File.open(@pathfile, "w") { |f| f.write(other) }
195
+ assert Pathological.find_load_paths("/src/Pathfile").include? other
196
+ end
197
+
198
+ should "print some debug info in debug mode" do
199
+ Pathological.debug_mode
200
+ mock(Pathological).puts(anything).at_least(3)
201
+ Pathological.add_paths!
202
+ end
203
+
204
+ should "set $BUNDLE_GEMFILE correctly in bundlerize mode" do
205
+ FileUtils.touch "/Gemfile"
206
+ Pathological.bundlerize_mode
207
+ Pathological.add_paths!
208
+ assert_equal "/Gemfile", ENV["BUNDLE_GEMFILE"]
209
+ end
210
+
211
+ should "add the correct directories in parentdir mode" do
212
+ paths = ["/foo/bar/baz1", "/foo/bar/baz2", "/foo/quux"]
213
+ paths.each { |path| FileUtils.mkdir_p path }
214
+ @pathfile_contents = paths.join("\n")
215
+ Pathological.parentdir_mode
216
+ load_and_run!
217
+ assert_load_path ["/", "/foo/bar", "/foo"]
218
+ end
219
+
220
+ should "not raise exceptions on bad paths in noexceptions mode" do
221
+ path = "/foo/bar"
222
+ @pathfile_contents << path
223
+ Pathological.noexceptions_mode
224
+ load_and_run!
225
+ assert_load_path ["/"]
226
+ end
227
+
228
+ should "not add the project root in excluderoot mode" do
229
+ path = "/foo/bar"
230
+ FileUtils.mkdir_p path
231
+ @pathfile_contents << path
232
+ Pathological.excluderoot_mode
233
+ load_and_run!
234
+ assert_load_path ["/foo/bar"]
235
+ end
236
+ end
237
+
238
+ context "#copy_outside_paths!" do
239
+ setup do
240
+ @destination = "/tmp/staging"
241
+ @pathfile = "/Pathfile"
242
+ FileUtils.cd "/"
243
+ FileUtils.mkdir_p @destination
244
+ @source_paths = ["/src/github1", "/src/moofoo"]
245
+ @source_paths.each { |src_dir| FileUtils.mkdir_p src_dir }
246
+ end
247
+
248
+ should "return immediately if there is no Pathfile" do
249
+ mock(Pathological).find_pathfile(nil) { nil }
250
+ Pathological.copy_outside_paths! @destination
251
+ refute File.directory?(File.join(@destination, "pathological_dependencies"))
252
+ end
253
+
254
+ should "return immediately if Pathfile is empty" do
255
+ File.open(@pathfile, "w") { |f| f.puts "\n# What the heck is this file?\n\n" }
256
+ Pathological.copy_outside_paths! @destination
257
+ refute File.directory?(File.join(@destination, "pathological_dependencies"))
258
+ end
259
+
260
+ should "copy source dirs as links and rewrite Pathfile" do
261
+ File.open(@pathfile, "w") { |f| f.puts @source_paths.join("\n") }
262
+
263
+ final_path = File.join(@destination, "pathological_dependencies")
264
+ @source_paths.each do |source_path|
265
+ mock(Pathological).copy_directory(source_path, final_path + source_path.gsub("/src", "")).once
266
+ end
267
+ Pathological.copy_outside_paths! @destination
268
+
269
+ assert File.directory?(final_path)
270
+ destination_paths = @source_paths.map do |source_path|
271
+ "pathological_dependencies#{source_path.gsub("/src", "")}"
272
+ end
273
+ assert_equal destination_paths, File.read(File.join(@destination, "Pathfile")).split("\n")
274
+ end
275
+
276
+ should "copy source dirs as links and rewrite Pathfile in a different directory" do
277
+ other = "other/dir"
278
+ FileUtils.mkdir_p other
279
+ File.open(File.join(other, "Pathfile"), "w") { |f| f.puts @source_paths.join("\n") }
280
+
281
+ final_path = File.join(@destination, "pathological_dependencies")
282
+ @source_paths.each do |source_path|
283
+ mock(Pathological).copy_directory(source_path, final_path + source_path.gsub("/src", "")).once
284
+ end
285
+ Pathological.copy_outside_paths! @destination, :pathfile_search_path => other
286
+
287
+ assert File.directory?(final_path)
288
+ destination_paths = @source_paths.map do |source_path|
289
+ "pathological_dependencies#{source_path.gsub("/src", "")}"
290
+ end
291
+ assert_equal destination_paths, File.read(File.join(@destination, "Pathfile")).split("\n")
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pathological-v2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Maheshwaran G
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-01-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rr
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.4
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: scope
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.3
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: fakefs
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.4.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.4.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.2.2
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.2.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 4.3.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 4.3.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: dedent
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |2
98
+ Extension of Pathological Gem to support Ruby > 1.9 version
99
+ indicates all directories to include in the load path.
100
+ email: maheshwarang@brightcove.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".travis.yml"
106
+ - ".yardopts"
107
+ - Gemfile
108
+ - LICENSE
109
+ - README.md
110
+ - Rakefile
111
+ - TODO.md
112
+ - lib/pathological.rb
113
+ - lib/pathological/base.rb
114
+ - lib/pathological/bundlerize.rb
115
+ - lib/pathological/debug.rb
116
+ - lib/pathological/excluderoot.rb
117
+ - lib/pathological/noexceptions.rb
118
+ - lib/pathological/parentdir.rb
119
+ - lib/pathological/version.rb
120
+ - pathological.gemspec
121
+ - test/unit/pathological/base_test.rb
122
+ homepage: http://www.ooyala.com
123
+ licenses: []
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 2.3.0
139
+ requirements: []
140
+ rubygems_version: 3.1.2
141
+ signing_key:
142
+ specification_version: 2
143
+ summary: A nice way to manage your project's require paths.
144
+ test_files: []