pathological 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|