pathological-v2 0.3.1

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
+ 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: []