pathological 0.2.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.
- data/.rvmrc +57 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +19 -0
- data/README.md +167 -0
- data/Rakefile +2 -0
- data/TODO.md +70 -0
- data/lib/pathological.rb +3 -0
- data/lib/pathological/base.rb +192 -0
- data/lib/pathological/bundlerize.rb +9 -0
- data/lib/pathological/debug.rb +4 -0
- data/lib/pathological/excluderoot.rb +4 -0
- data/lib/pathological/noexceptions.rb +4 -0
- data/lib/pathological/parentdir.rb +8 -0
- data/lib/pathological/version.rb +3 -0
- data/pathological.gemspec +33 -0
- data/test/pathological/base_test.rb +180 -0
- data/test/unit.watchr.rb +10 -0
- metadata +123 -0
data/.rvmrc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# WARNING: DO NOT MANUALLY EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING.
|
4
|
+
# This file was generated automatically using ofe/tools/rvmrc_create.sh (which is in turn a wrapper around the
|
5
|
+
# rvm command rvm --rvmrc). It was generated in the following way:
|
6
|
+
#
|
7
|
+
# $ rvmrc_create.sh use 1.9.2@pathological --create
|
8
|
+
#
|
9
|
+
# Please use rvmrc_create.sh if you want to change what this project rvmrc does.
|
10
|
+
|
11
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
12
|
+
# development environment upon cd'ing into the directory
|
13
|
+
|
14
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
|
15
|
+
environment_id="ruby-1.9.2-p180@pathological"
|
16
|
+
|
17
|
+
#
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficicent compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
#
|
23
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
|
24
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] ; then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
|
27
|
+
[[ -s ".rvm/hooks/after_use" ]] && . ".rvm/hooks/after_use"
|
28
|
+
else
|
29
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
30
|
+
rvm --create use "$environment_id"
|
31
|
+
fi
|
32
|
+
|
33
|
+
#
|
34
|
+
# If you use an RVM gemset file to install a list of gems (*.gems), you can have
|
35
|
+
# it be automatically loaded. Uncomment the following and adjust the filename if
|
36
|
+
# necessary.
|
37
|
+
#
|
38
|
+
# filename=".gems"
|
39
|
+
# if [[ -s "$filename" ]] ; then
|
40
|
+
# rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
|
41
|
+
# fi
|
42
|
+
|
43
|
+
#
|
44
|
+
# If you use bundler and would like to run bundle each time you enter the
|
45
|
+
# directory, you can uncomment the following code.
|
46
|
+
#
|
47
|
+
# # Ensure that Bundler is installed. Install it if it is not.
|
48
|
+
# if ! command -v bundle >/dev/null; then
|
49
|
+
# printf "The rubygem 'bundler' is not installed. Installing it now.\n"
|
50
|
+
# gem install bundler
|
51
|
+
# fi
|
52
|
+
#
|
53
|
+
# # Bundle while reducing excess noise.
|
54
|
+
# printf "Bundling your gems. This may take a few minutes on a fresh clone.\n"
|
55
|
+
# bundle | grep -v '^Using ' | grep -v ' is complete' | sed '/^$/d'
|
56
|
+
#
|
57
|
+
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private lib/pathological/base.rb - README.md LICENSE
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pathological (0.1.4)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
fakefs (0.4.0)
|
10
|
+
minitest (2.5.1)
|
11
|
+
rdiscount (1.6.8)
|
12
|
+
rr (1.0.3)
|
13
|
+
scope (0.2.3)
|
14
|
+
minitest
|
15
|
+
yard (0.7.2)
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
fakefs
|
22
|
+
pathological!
|
23
|
+
rdiscount (~> 1.6.8)
|
24
|
+
rr (>= 1.0.3)
|
25
|
+
scope (>= 0.2.3)
|
26
|
+
yard (~> 0.7.2)
|
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,167 @@
|
|
1
|
+
Pathological
|
2
|
+
============
|
3
|
+
|
4
|
+
Pathological is a Ruby tool that provides a lightweight mechanism for managing your project's load path.
|
5
|
+
|
6
|
+
The problem
|
7
|
+
-----------
|
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
|
+
|
28
|
+
Getting started with pathological is easy. First, make a file called `Pathfile` at your project root:
|
29
|
+
|
30
|
+
$ cd path/to/myproject
|
31
|
+
$ touch Pathfile
|
32
|
+
|
33
|
+
Now require the gem at the start of any executable ruby file:
|
34
|
+
|
35
|
+
#!/usr/bin/env ruby
|
36
|
+
|
37
|
+
require "rubygems" # If you're using 1.8
|
38
|
+
require "pathological"
|
39
|
+
# other requires...
|
40
|
+
|
41
|
+
Now your project root will be in your load path. If your project has, for example, `lib/foo.rb`, then `require
|
42
|
+
lib/foo` will work in any of your ruby files. This works because when Pathological is required it will search
|
43
|
+
up the directory tree until it finds a `Pathfile`. (It will raise an error if there this cannot be found).
|
44
|
+
|
45
|
+
`Pathfile`s should be kept in version control.
|
46
|
+
|
47
|
+
Adding other paths to your load path
|
48
|
+
------------------------------------
|
49
|
+
|
50
|
+
To add more paths to your load path, just put the paths in your `Pathfile`. The paths are relative to the
|
51
|
+
location of the `Pathfile`. The paths will be inserted in the order they appear; the project root itself will
|
52
|
+
be first. If any of the paths are not valid directories, then an exception will be raised when Pathological is
|
53
|
+
required.
|
54
|
+
|
55
|
+
#### Example
|
56
|
+
|
57
|
+
Suppose that you have a directory structure like this:
|
58
|
+
|
59
|
+
repos/
|
60
|
+
|-shared_lib/
|
61
|
+
| `-common.rb
|
62
|
+
`-my_project/
|
63
|
+
|-Pathfile
|
64
|
+
|-run_my_project.rb
|
65
|
+
`-foo.rb
|
66
|
+
|
67
|
+
and that `Pathfile` contains the following:
|
68
|
+
|
69
|
+
../shared_lib
|
70
|
+
|
71
|
+
Then inside `run_my_project.rb`:
|
72
|
+
|
73
|
+
require "rubygems"
|
74
|
+
require "pathological"
|
75
|
+
require "foo"
|
76
|
+
require "common"
|
77
|
+
# ...
|
78
|
+
|
79
|
+
Installation
|
80
|
+
------------
|
81
|
+
|
82
|
+
Pathological is packaged as a Rubygem and hence can be trivially installed with
|
83
|
+
|
84
|
+
$ gem install pathological
|
85
|
+
|
86
|
+
Advanced usage
|
87
|
+
--------------
|
88
|
+
|
89
|
+
In some cases, you might want slightly different behavior. This customization is done through the use of
|
90
|
+
custom modes. You may use any combination of modes.
|
91
|
+
|
92
|
+
#### debug
|
93
|
+
|
94
|
+
This adds debugging statements to `STDOUT` that explain what Pathological is doing.
|
95
|
+
|
96
|
+
#### excluderoot
|
97
|
+
|
98
|
+
In this mode, the project root (where the `Pathfile` is located) is not added to the load path (so only paths
|
99
|
+
specified *in* the `Pathfile` will be loaded).
|
100
|
+
|
101
|
+
#### noexceptions
|
102
|
+
|
103
|
+
This is used if you don't want to raise exceptions if you have bad paths (i.e. non-existent paths or not
|
104
|
+
directories) in your `Pathfile`.
|
105
|
+
|
106
|
+
#### bundlerize
|
107
|
+
|
108
|
+
Bundlerize mode enables Bundler to work with your project regardless of your current directory, in the same
|
109
|
+
way as Pathological, by attempting to set the `BUNDLE_GEMFILE` environment variable to match the directory
|
110
|
+
where the `Pathfile` is located. Note that you have to run this before requiring `bundler/setup`. Also, this
|
111
|
+
will not take effect if you are running with `bundle exec`.
|
112
|
+
|
113
|
+
#### parentdir
|
114
|
+
|
115
|
+
This mode makes Pathological add the unique parents of all paths it finds (instead of the paths themselves).
|
116
|
+
The purpose of parentdir is to enable Pathological to work in a drop-in fashion with legacy code written with
|
117
|
+
all requires being relative to the root of the codebase. Note that this will allow one to require files
|
118
|
+
located in any child of the parents, not just from the directories specified in the `Pathfile`. This mode
|
119
|
+
should be avoided if possible.
|
120
|
+
|
121
|
+
There are two ways to specify modes. First, you can enable any modes you want using the Pathological API:
|
122
|
+
|
123
|
+
require "pathological/base"
|
124
|
+
Pathological.debug_mode
|
125
|
+
Pathological.parentdir_mode
|
126
|
+
Pathological.add_paths!
|
127
|
+
|
128
|
+
A quicker way is also provided: if you only need to use one special mode, then there is a dedicated file you
|
129
|
+
can require:
|
130
|
+
|
131
|
+
require "pathological/bundlerize"
|
132
|
+
|
133
|
+
Public API
|
134
|
+
----------
|
135
|
+
|
136
|
+
For even more configurable custom integration with Pathological, a public API is provided. See the generated
|
137
|
+
documentation for details on the following public methods:
|
138
|
+
|
139
|
+
Pathological#add_paths!
|
140
|
+
Pathological#find_load_paths
|
141
|
+
Pathological#find_pathfile
|
142
|
+
Pathological#reset!
|
143
|
+
|
144
|
+
Authors
|
145
|
+
-------
|
146
|
+
|
147
|
+
Pathological was written by the following Ooyala engineers:
|
148
|
+
|
149
|
+
* [Daniel MacDougall](mailto:dmac@ooyala.com)
|
150
|
+
* [Caleb Spare](mailto:caleb@ooyala.com)
|
151
|
+
* [Sami Abu-El-Haija](mailto:sami@ooyala.com)
|
152
|
+
|
153
|
+
Credits
|
154
|
+
-------
|
155
|
+
|
156
|
+
* Harry Robertson for the idea to *not* use a dot-prefixed configuration file
|
157
|
+
|
158
|
+
Contributing
|
159
|
+
------------
|
160
|
+
|
161
|
+
If you would like to commit a patch, great! Just do the usual github pull request stuff and we'll check it
|
162
|
+
out.
|
163
|
+
|
164
|
+
License
|
165
|
+
-------
|
166
|
+
|
167
|
+
Pathological is licensed under the MIT license.
|
data/Rakefile
ADDED
data/TODO.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
TODO
|
2
|
+
====
|
3
|
+
|
4
|
+
Tasks
|
5
|
+
-----
|
6
|
+
|
7
|
+
* Bundler compatibility.
|
8
|
+
* Push to the Ooyala github account; release on rubygems.org
|
9
|
+
* Mode for adding one directory above given paths to the current.
|
10
|
+
* Public API:
|
11
|
+
* Get all loaded files that come from Pathological directories
|
12
|
+
|
13
|
+
Design decisions
|
14
|
+
----------------
|
15
|
+
|
16
|
+
* Should we allow for including other Pathfiles? Proposed syntaxes:
|
17
|
+
|
18
|
+
include path/to/other/Pathfile
|
19
|
+
import path/to/other/Pathfile # Rakefile
|
20
|
+
i path/to/other/Pathfile
|
21
|
+
source path/to/other/Pathfile # Bash
|
22
|
+
. path/to/other/Pathfile # Bash
|
23
|
+
path/to/other/Pathfile # Distinguishable from regular path if Pathfile is not a directory
|
24
|
+
|
25
|
+
**Not needed now --Caleb**
|
26
|
+
|
27
|
+
* Do we like `>` to signify directives? Alternatives:
|
28
|
+
|
29
|
+
> exclude-root # Current syntax
|
30
|
+
exclude-root # Only problem is if you want to include a directory with the same name as a directive
|
31
|
+
|
32
|
+
We could also prefix each type of line to make things unambiguous:
|
33
|
+
|
34
|
+
p path/to/lib/
|
35
|
+
d exclude-root
|
36
|
+
|
37
|
+
**Fine for now --Caleb**
|
38
|
+
|
39
|
+
* Right now there's a small problem with comments: if your path includes the character `#`, then the rest
|
40
|
+
will be chopped off (interpreted as a comment). We could remedy this by only allowing for comments to
|
41
|
+
start at the beginning of lines:
|
42
|
+
|
43
|
+
# Yes
|
44
|
+
../lib/ # No
|
45
|
+
|
46
|
+
**Let's leave this alone for now; probably a non-issue --Caleb**
|
47
|
+
|
48
|
+
* Right our require paths tend to look like this (using `shared_lib/` as an example):
|
49
|
+
|
50
|
+
``` ruby
|
51
|
+
require "shared_lib/utils/foo_util"
|
52
|
+
```
|
53
|
+
|
54
|
+
To support this, you would need your `Pathfile` to include the directory *above* `shared_lib/`. The
|
55
|
+
downside of this is that it doesn't play well with the idea of using our `Pathfile`s to tell our deploy
|
56
|
+
scripts what to include. We could potentially add a new construct to allow the user to specify which
|
57
|
+
subdirectories they would be using. Example (just off the top of my head):
|
58
|
+
|
59
|
+
path/to/repo/dir/{shared_lib, common, vendor}
|
60
|
+
|
61
|
+
However, we'd still have to add `path/to/repo/dir` to the `$LOAD_PATH`, so the only way to enforce this at
|
62
|
+
require time would be to use a custom `require`. This is all quite a high cost to pay in terms of design
|
63
|
+
simplicity, but yet being able to use Pathfiles as a single place to document what dependencies to pull in
|
64
|
+
seems very appealing. Any ideas?
|
65
|
+
|
66
|
+
**After chatting with some people, we're going to leave this as is and truncate the `shared_lib` from our
|
67
|
+
paths. --Caleb**
|
68
|
+
|
69
|
+
**Actually, I think I'm going to add an optional mode to add one directory _above_ to the load path
|
70
|
+
instead of the given path, in order to accomomdate this use case. --Caleb**
|
data/lib/pathological.rb
ADDED
@@ -0,0 +1,192 @@
|
|
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 executed file resides.
|
61
|
+
full_path = real_path(directory || $0)
|
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
|
+
# Find all loaded ruby files that come from directories added to the load path by Pathological. This is only
|
80
|
+
# available in Ruby 1.9 due to the behavior of +$"+. This method works in a "best guess" fashion by
|
81
|
+
# determining the +$LOAD_PATH+ entry to which loaded file corresponds.
|
82
|
+
#
|
83
|
+
# @return [Array<String>] the array of paths
|
84
|
+
def self.find_loaded_files
|
85
|
+
unless RUBY_VERSION =~ /^1\.9/
|
86
|
+
raise PathologicalException, "#find_loaded_files is only available in Ruby 1.9."
|
87
|
+
end
|
88
|
+
$".select { |file| @@loaded_paths.any? { ... } }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Convenience functions for the various modes in which Pathological may run.
|
92
|
+
|
93
|
+
def self.debug_mode; @@debug = true; end
|
94
|
+
def self.bundlerize_mode
|
95
|
+
pathfile = Pathological.find_pathfile
|
96
|
+
raise NoPathfileException unless pathfile
|
97
|
+
bundle_gemfile = File.join(File.dirname(pathfile), "Gemfile")
|
98
|
+
unless File.file? bundle_gemfile
|
99
|
+
raise PathologicalException, "No Gemfile found in #{File.dirname(pathfile)}."
|
100
|
+
end
|
101
|
+
ENV["BUNDLE_GEMFILE"] = bundle_gemfile
|
102
|
+
end
|
103
|
+
def self.parentdir_mode; @@add_parents = true; end
|
104
|
+
def self.noexceptions_mode; @@no_exceptions = true; end
|
105
|
+
def self.excluderoot_mode; @@exclude_root = true; end
|
106
|
+
|
107
|
+
# Reset all Pathological options (useful if you want to require a different Pathfile)
|
108
|
+
def self.reset!
|
109
|
+
# Debug mode -- print out information about load paths
|
110
|
+
@@debug = false
|
111
|
+
# Parentdir mode -- add unique parents of specified directories.
|
112
|
+
@@add_parents = false
|
113
|
+
# Noexceptions mode -- don't raise exceptions if the Pathfile contains bad paths
|
114
|
+
@@no_exceptions = false
|
115
|
+
# Excluderoot mode -- don't add the project root (where the Pathfile lives) to the load path
|
116
|
+
@@exclude_root = false
|
117
|
+
|
118
|
+
@@loaded_paths ||= []
|
119
|
+
@@loaded_paths.each { |path| $LOAD_PATH.delete path }
|
120
|
+
@@loaded_paths = []
|
121
|
+
end
|
122
|
+
|
123
|
+
# private module methods
|
124
|
+
|
125
|
+
# Print debugging info
|
126
|
+
#
|
127
|
+
# @private
|
128
|
+
# @param [String] message the debugging message
|
129
|
+
# @return [void]
|
130
|
+
def self.debug(message); puts "[Pathological Debug] >> #{message}" if @@debug; end
|
131
|
+
|
132
|
+
# Turn a path into an absolute path with no useless parts and no symlinks.
|
133
|
+
#
|
134
|
+
# @private
|
135
|
+
# @param [String] the path
|
136
|
+
# @return [String] the absolute real path
|
137
|
+
def self.real_path(path); Pathname.new(path).realpath.to_s; end
|
138
|
+
|
139
|
+
# Parse a pathfile and return the appropriate paths.
|
140
|
+
#
|
141
|
+
# @private
|
142
|
+
# @param [IO] pathfile handle to the pathfile to parse
|
143
|
+
# @return [Array<String>] array of paths found
|
144
|
+
def self.parse_pathfile(pathfile)
|
145
|
+
root = File.dirname(real_path(pathfile.path))
|
146
|
+
raw_paths = [root]
|
147
|
+
pathfile.each do |line|
|
148
|
+
# Trim comments
|
149
|
+
line = line.split(/#/, 2)[0].strip
|
150
|
+
next if line.empty?
|
151
|
+
raw_path = File.expand_path(File.join(root, line.strip))
|
152
|
+
raw_paths << (@@add_parents ? File.dirname(raw_path) : raw_path)
|
153
|
+
end
|
154
|
+
|
155
|
+
paths = []
|
156
|
+
raw_paths.each do |path|
|
157
|
+
unless File.directory? path
|
158
|
+
unless @@no_exceptions
|
159
|
+
raise PathologicalException, "Bad path in Pathfile: #{path}"
|
160
|
+
end
|
161
|
+
debug "Ignoring non-existent path: #{path}"
|
162
|
+
next
|
163
|
+
end
|
164
|
+
next if @@exclude_root && File.expand_path(path) == File.expand_path(root)
|
165
|
+
paths << path
|
166
|
+
end
|
167
|
+
@@exclude_root ? paths.reject { |path| File.expand_path(path) == File.expand_path(root) } : paths
|
168
|
+
end
|
169
|
+
|
170
|
+
# Determine whether a path is a (strict) descendent of another path
|
171
|
+
#
|
172
|
+
# @private
|
173
|
+
# @param [String] ancestor_path the path of the prospective ancestor
|
174
|
+
# @param [String] descendent_path the path of the prospective descendent
|
175
|
+
# @return [Boolean] whether descendent_path is some strict descendent of ancestor_path
|
176
|
+
def self.is_descendent?(ancestor_path, descendent_path)
|
177
|
+
# Delete trailing slash to match File#dirname
|
178
|
+
ancestor_path = ancestor_path[0..-2] if ancestor_path.size > 1 && ancestor_path.end_with? "/"
|
179
|
+
current_path = File.dirname(descendent_path)
|
180
|
+
previous_path = nil
|
181
|
+
until current_path == previous_path
|
182
|
+
return true if current_path == ancestor_path
|
183
|
+
previous_path, current_path = current_path, File.dirname(current_path)
|
184
|
+
end
|
185
|
+
false
|
186
|
+
end
|
187
|
+
|
188
|
+
private_class_method :debug, :real_path, :parse_pathfile
|
189
|
+
|
190
|
+
# Reset options
|
191
|
+
Pathological.reset!
|
192
|
+
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,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,33 @@
|
|
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"
|
7
|
+
s.version = Pathological::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">=0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
12
|
+
|
13
|
+
s.authors = "Daniel MacDougall", "Caleb Spare"
|
14
|
+
s.email = "dmac@ooyala.com", "caleb@ooyala.com"
|
15
|
+
s.homepage = "http://www.ooyala.com"
|
16
|
+
s.rubyforge_project = "pathological"
|
17
|
+
|
18
|
+
s.summary = "A nice way to manage your project's require paths."
|
19
|
+
s.description = <<-DESCRIPTION
|
20
|
+
Pathological provides a way to manage a project's require paths by using a small config file that
|
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.3"
|
29
|
+
s.add_development_dependency "scope", ">= 0.2.3"
|
30
|
+
s.add_development_dependency "yard", "~> 0.7.2"
|
31
|
+
s.add_development_dependency "rdiscount", "~> 1.6.8"
|
32
|
+
s.add_development_dependency "fakefs"
|
33
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "scope"
|
3
|
+
require "rr"
|
4
|
+
require "minitest/autorun"
|
5
|
+
require "stringio"
|
6
|
+
require "fakefs/safe"
|
7
|
+
|
8
|
+
# It's kind of funny that we need to do this hack, given that Pathological is intended to work around it...
|
9
|
+
$:.unshift(File.join(File.dirname(__FILE__), "../../lib"))
|
10
|
+
require "pathological/base"
|
11
|
+
|
12
|
+
module Pathological
|
13
|
+
class BaseTest < Scope::TestCase
|
14
|
+
include RR::Adapters::MiniTest
|
15
|
+
def assert_load_path(expected_load_path)
|
16
|
+
assert_equal expected_load_path.uniq.sort, @load_path.uniq.sort
|
17
|
+
end
|
18
|
+
|
19
|
+
context "Pathological" do
|
20
|
+
setup_once { FakeFS.activate! }
|
21
|
+
setup do
|
22
|
+
Pathological.reset!
|
23
|
+
@load_path = []
|
24
|
+
FakeFS::FileSystem.clear
|
25
|
+
# FakeFS has not implemented the necessary calls for Pathname#realpath to work.
|
26
|
+
stub(Pathological).real_path(anything) { |p| p }
|
27
|
+
end
|
28
|
+
teardown_once { FakeFS.deactivate! }
|
29
|
+
|
30
|
+
context "#add_paths!" do
|
31
|
+
should "not raise an error but print a warning when there's no pathfile" do
|
32
|
+
mock(Pathological).find_pathfile { nil }
|
33
|
+
mock(STDERR).puts(anything) { |m| assert_match /^Warning/, m }
|
34
|
+
Pathological.add_paths! @load_path
|
35
|
+
assert_load_path []
|
36
|
+
end
|
37
|
+
|
38
|
+
should "append the requested paths" do
|
39
|
+
paths = ["foo"]
|
40
|
+
Pathological.add_paths! @load_path, paths
|
41
|
+
assert_load_path paths
|
42
|
+
end
|
43
|
+
|
44
|
+
should "append the paths that #find_load_paths finds" do
|
45
|
+
paths = ["foo"]
|
46
|
+
mock(Pathological).find_load_paths { paths }
|
47
|
+
Pathological.add_paths! @load_path
|
48
|
+
assert_load_path paths
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "#find_load_paths" do
|
53
|
+
should "raise a NoPathfileException on a nil pathfile" do
|
54
|
+
mock(Pathological).find_pathfile { nil }
|
55
|
+
assert_raises(NoPathfileException) { Pathological.find_load_paths(nil) }
|
56
|
+
end
|
57
|
+
|
58
|
+
should "use #find_pathfile to find the Pathfile and #parse_pathfile to parse it." do
|
59
|
+
paths = ["path1"]
|
60
|
+
mock(Pathological).find_pathfile { "foo" }
|
61
|
+
mock(File).open("foo") { "bar" }
|
62
|
+
mock(Pathological).parse_pathfile("bar") { paths }
|
63
|
+
assert_equal paths, Pathological.find_load_paths
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "#find_pathfile" do
|
68
|
+
setup do
|
69
|
+
@working_directory = "/foo/bar/baz"
|
70
|
+
FileUtils.mkdir_p @working_directory
|
71
|
+
FileUtils.cd @working_directory
|
72
|
+
end
|
73
|
+
|
74
|
+
should "find a pathfile in this directory" do
|
75
|
+
pathfile = "/foo/bar/baz/Pathfile"
|
76
|
+
FileUtils.touch pathfile
|
77
|
+
assert_equal pathfile, Pathological.find_pathfile(@working_directory)
|
78
|
+
end
|
79
|
+
|
80
|
+
should "find a pathfile in a parent directory" do
|
81
|
+
pathfile = "/foo/bar/Pathfile"
|
82
|
+
FileUtils.touch pathfile
|
83
|
+
assert_equal pathfile, Pathological.find_pathfile(@working_directory)
|
84
|
+
end
|
85
|
+
|
86
|
+
should "find a pathfile at the root" do
|
87
|
+
pathfile = "/Pathfile"
|
88
|
+
FileUtils.touch pathfile
|
89
|
+
assert_equal pathfile, Pathological.find_pathfile(@working_directory)
|
90
|
+
end
|
91
|
+
|
92
|
+
should "locate a pathfile in the real path even if we're running from a symlinked directory" do
|
93
|
+
pathfile = "/foo/bar/baz/Pathfile"
|
94
|
+
FileUtils.touch pathfile
|
95
|
+
FileUtils.touch "/Pathfile" # Shouldn't find this one
|
96
|
+
symlinked_directory = "/foo/bar/quux"
|
97
|
+
FileUtils.ln_s @working_directory, symlinked_directory
|
98
|
+
stub(Pathological).real_path(anything) { |path| path.gsub("quux", "baz") }
|
99
|
+
assert_equal pathfile, Pathological.find_pathfile(@working_directory)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "loading pathological" do
|
104
|
+
setup do
|
105
|
+
@pathfile_contents = ""
|
106
|
+
@pathfile = "/Pathfile"
|
107
|
+
$0 = "/my_file"
|
108
|
+
FileUtils.touch @pathfile
|
109
|
+
FileUtils.cd "/"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Load in pathfile contents and load Pathological
|
113
|
+
def load_and_run!
|
114
|
+
File.open(@pathfile, "w") { |f| f.write(@pathfile_contents) }
|
115
|
+
Pathological.add_paths!(@load_path)
|
116
|
+
end
|
117
|
+
|
118
|
+
should "use an empty Pathfile correctly" do
|
119
|
+
load_and_run!
|
120
|
+
assert_load_path ["/"]
|
121
|
+
end
|
122
|
+
|
123
|
+
should "add some paths as appropriate" do
|
124
|
+
paths = ["/foo/bar", "/baz"]
|
125
|
+
paths.each do |path|
|
126
|
+
FileUtils.mkdir_p path
|
127
|
+
@pathfile_contents << "#{path}\n"
|
128
|
+
end
|
129
|
+
load_and_run!
|
130
|
+
assert_load_path(paths << "/")
|
131
|
+
end
|
132
|
+
|
133
|
+
should "throw exceptions on bad load paths" do
|
134
|
+
path = "/foo/bar"
|
135
|
+
@pathfile_contents << "#{path}"
|
136
|
+
assert_raises(PathologicalException) { load_and_run! }
|
137
|
+
end
|
138
|
+
|
139
|
+
should "print some debug info in debug mode" do
|
140
|
+
Pathological.debug_mode
|
141
|
+
mock(Pathological).puts(anything).at_least(3)
|
142
|
+
Pathological.add_paths!
|
143
|
+
end
|
144
|
+
|
145
|
+
should "set $BUNDLE_GEMFILE correctly in bundlerize mode" do
|
146
|
+
FileUtils.touch "/Gemfile"
|
147
|
+
Pathological.bundlerize_mode
|
148
|
+
Pathological.add_paths!
|
149
|
+
assert_equal "/Gemfile", ENV["BUNDLE_GEMFILE"]
|
150
|
+
end
|
151
|
+
|
152
|
+
should "add the correct directories in parentdir mode" do
|
153
|
+
paths = ["/foo/bar/baz1", "/foo/bar/baz2", "/foo/quux"]
|
154
|
+
paths.each { |path| FileUtils.mkdir_p path }
|
155
|
+
@pathfile_contents = paths.join("\n")
|
156
|
+
Pathological.parentdir_mode
|
157
|
+
load_and_run!
|
158
|
+
assert_load_path ["/", "/foo/bar", "/foo"]
|
159
|
+
end
|
160
|
+
|
161
|
+
should "not raise exceptions on bad paths in noexceptions mode" do
|
162
|
+
path = "/foo/bar"
|
163
|
+
@pathfile_contents << path
|
164
|
+
Pathological.noexceptions_mode
|
165
|
+
load_and_run!
|
166
|
+
assert_load_path ["/"]
|
167
|
+
end
|
168
|
+
|
169
|
+
should "not add the project root in excluderoot mode" do
|
170
|
+
path = "/foo/bar"
|
171
|
+
FileUtils.mkdir_p path
|
172
|
+
@pathfile_contents << path
|
173
|
+
Pathological.excluderoot_mode
|
174
|
+
load_and_run!
|
175
|
+
assert_load_path ["/foo/bar"]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
data/test/unit.watchr.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Watchr script for unit tests
|
2
|
+
|
3
|
+
def run_test(test)
|
4
|
+
system("bundle exec ruby #{test}")
|
5
|
+
end
|
6
|
+
|
7
|
+
watch(/^test\/.*_test\.rb/) { |md| run_test(md[0]) }
|
8
|
+
watch(/^lib\/(.*)\.rb/) { |md| run_test("test/#{md[1]}_test.rb") }
|
9
|
+
|
10
|
+
Signal.trap("INT") { abort("\n") }
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pathological
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Daniel MacDougall
|
9
|
+
- Caleb Spare
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2011-09-09 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rr
|
17
|
+
requirement: &70277329396620 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.0.3
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70277329396620
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: scope
|
28
|
+
requirement: &70277329396080 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.3
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *70277329396080
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: yard
|
39
|
+
requirement: &70277329395580 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.7.2
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70277329395580
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rdiscount
|
50
|
+
requirement: &70277329395080 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.6.8
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70277329395080
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: fakefs
|
61
|
+
requirement: &70277329394700 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *70277329394700
|
70
|
+
description: ! " Pathological provides a way to manage a project's require paths
|
71
|
+
by using a small config file that\n indicates all directories to include in the
|
72
|
+
load path.\n"
|
73
|
+
email:
|
74
|
+
- dmac@ooyala.com
|
75
|
+
- caleb@ooyala.com
|
76
|
+
executables: []
|
77
|
+
extensions: []
|
78
|
+
extra_rdoc_files: []
|
79
|
+
files:
|
80
|
+
- .rvmrc
|
81
|
+
- .yardopts
|
82
|
+
- Gemfile
|
83
|
+
- Gemfile.lock
|
84
|
+
- LICENSE
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- TODO.md
|
88
|
+
- lib/pathological.rb
|
89
|
+
- lib/pathological/base.rb
|
90
|
+
- lib/pathological/bundlerize.rb
|
91
|
+
- lib/pathological/debug.rb
|
92
|
+
- lib/pathological/excluderoot.rb
|
93
|
+
- lib/pathological/noexceptions.rb
|
94
|
+
- lib/pathological/parentdir.rb
|
95
|
+
- lib/pathological/version.rb
|
96
|
+
- pathological.gemspec
|
97
|
+
- test/pathological/base_test.rb
|
98
|
+
- test/unit.watchr.rb
|
99
|
+
homepage: http://www.ooyala.com
|
100
|
+
licenses: []
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project: pathological
|
119
|
+
rubygems_version: 1.8.7
|
120
|
+
signing_key:
|
121
|
+
specification_version: 2
|
122
|
+
summary: A nice way to manage your project's require paths.
|
123
|
+
test_files: []
|