facets 2.7.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.rdoc +135 -294
- data/MANIFEST +40 -91
- data/NOTES +1 -1
- data/README.rdoc +10 -8
- data/Rakefile +11 -34
- data/demo/{hook.rd → hook.rdoc} +2 -0
- data/demo/{scenario_require.rd → scenario_require.rdoc} +3 -0
- data/lib/core/facets-live.rb +7 -5
- data/lib/core/facets.rb +379 -359
- data/lib/core/facets/array/conjoin.rb +2 -2
- data/lib/core/facets/array/pad.rb +1 -1
- data/lib/core/facets/array/recursively.rb +2 -2
- data/lib/core/facets/array/splice.rb +1 -1
- data/lib/core/facets/binding/caller.rb +2 -4
- data/lib/core/facets/comparable/comparable.rb +2 -2
- data/lib/core/facets/dir/ascend.rb +3 -0
- data/lib/core/facets/dir/recurse.rb +4 -0
- data/lib/core/facets/duplicable.rb +6 -8
- data/lib/core/facets/enumerable/count.rb +22 -13
- data/lib/core/facets/enumerable/map_detect.rb +28 -0
- data/lib/core/facets/enumerable/mash.rb +13 -5
- data/lib/core/facets/enumerable/per.rb +3 -1
- data/lib/core/facets/hash/count.rb +14 -0
- data/lib/core/facets/hash/data.rb +14 -0
- data/lib/core/facets/kernel/__method__.rb +1 -1
- data/lib/core/facets/kernel/d.rb +9 -8
- data/lib/core/facets/kernel/eigenclass.rb +20 -0
- data/lib/core/facets/kernel/extend.rb +10 -0
- data/lib/core/facets/kernel/instance_class.rb +1 -0
- data/lib/core/facets/kernel/instance_variables.rb +6 -6
- data/lib/core/facets/kernel/meta_alias.rb +18 -0
- data/lib/core/facets/kernel/meta_class.rb +17 -0
- data/lib/core/facets/kernel/meta_def.rb +18 -0
- data/lib/core/facets/kernel/meta_eval.rb +18 -0
- data/lib/core/facets/kernel/object_hexid.rb +21 -6
- data/lib/core/facets/kernel/object_state.rb +4 -2
- data/lib/core/facets/kernel/populate.rb +3 -1
- data/lib/core/facets/kernel/with.rb +1 -1
- data/lib/core/facets/metaid.rb +6 -93
- data/lib/core/facets/module/class_def.rb +2 -0
- data/lib/core/facets/module/extend.rb +10 -11
- data/lib/core/facets/module/is.rb +5 -5
- data/lib/core/facets/module/module_def.rb +31 -0
- data/lib/core/facets/string/camelcase.rb +14 -12
- data/lib/core/facets/string/cleanlines.rb +35 -0
- data/lib/core/facets/string/edit_distance.rb +62 -0
- data/lib/core/facets/string/indent.rb +86 -4
- data/lib/core/facets/string/index_all.rb +24 -0
- data/lib/core/facets/string/lines.rb +3 -6
- data/lib/core/facets/string/margin.rb +2 -1
- data/lib/core/facets/string/newlines.rb +35 -0
- data/lib/core/facets/string/op_div.rb +14 -0
- data/lib/core/facets/string/range.rb +2 -22
- data/lib/core/facets/string/range_all.rb +1 -0
- data/lib/core/facets/string/range_of_line.rb +1 -0
- data/lib/core/facets/string/similarity.rb +92 -0
- data/lib/core/facets/string/start_with.rb +6 -6
- data/lib/core/facets/string/titlecase.rb +1 -1
- data/lib/more/facets/basicobject.rb +16 -15
- data/lib/more/facets/blankslate.rb +8 -0
- data/lib/more/facets/class_extend.rb +126 -1
- data/lib/more/facets/continuation.rb +53 -54
- data/lib/more/facets/dictionary.rb +9 -63
- data/lib/more/facets/erb.rb +63 -0
- data/lib/more/facets/filelist.rb +5 -5
- data/lib/more/facets/hashbuilder.rb +101 -0
- data/lib/more/facets/inheritor.rb +36 -45
- data/lib/more/facets/ini.rb +267 -0
- data/lib/more/facets/instance_eval.rb +4 -4
- data/lib/more/facets/ioredirect.rb +7 -60
- data/lib/more/facets/linkedlist.rb +195 -0
- data/lib/more/facets/matcher.rb +140 -0
- data/lib/more/facets/memoizer.rb +64 -0
- data/lib/more/facets/methodspace.rb +9 -4
- data/lib/more/facets/module/class_extend.rb +2 -121
- data/lib/more/facets/ostruct.rb +9 -9
- data/lib/more/facets/pathlist.rb +1 -9
- data/lib/more/facets/pathname.rb +11 -4
- data/lib/more/facets/plugin_manager.rb +50 -0
- data/lib/more/facets/random.rb +25 -3
- data/lib/more/facets/roman.rb +174 -0
- data/lib/more/facets/semaphore.rb +92 -0
- data/lib/more/facets/shellwords.rb +21 -48
- data/lib/more/facets/succ.rb +1 -1
- data/meta/{modified → released} +0 -0
- data/meta/repository +1 -0
- data/meta/suite +1 -0
- data/meta/version +1 -1
- data/script/conflicts +63 -0
- data/script/methods +49 -0
- data/test/core/binding/test_caller.rb +11 -4
- data/test/core/enumerable/test_count.rb +19 -10
- data/test/core/enumerable/test_map_detect.rb +75 -0
- data/test/core/enumerable/test_take.rb +1 -1
- data/test/core/kernel/test_object_hexid.rb +2 -1
- data/test/core/proc/test_to_method.rb +1 -1
- data/test/core/string/test_cleanlines.rb +11 -0
- data/test/core/string/test_indent.rb +66 -4
- data/test/core/string/test_lines.rb +2 -1
- data/test/core/string/test_newlines.rb +13 -0
- data/test/core/time/test_change.rb +1 -1
- data/test/core/time/test_stamp.rb +4 -7
- data/test/core/unboundmethod/test_name.rb +1 -1
- data/test/more/test_basicobject.rb +1 -20
- data/test/more/test_class_extend.rb +7 -0
- data/test/more/test_continuation.rb +8 -6
- data/test/more/test_inheritor.rb +12 -6
- data/test/more/test_random.rb +19 -10
- data/test/more/test_shellwords.rb +33 -0
- metadata +60 -31
- data/TODO +0 -5
- data/doc/README.core +0 -102
- data/doc/README.more +0 -61
- data/doc/manual/about.rb +0 -47
- data/doc/manual/annotations.rdoc +0 -60
- data/doc/manual/associations.rdoc +0 -55
- data/doc/manual/blockups.rdoc +0 -101
- data/doc/manual/capsule.rdoc +0 -34
- data/doc/manual/command.rdoc +0 -177
- data/doc/manual/core.rdoc +0 -37
- data/doc/manual/faq.rdoc +0 -32
- data/doc/manual/typecast.html +0 -112
- data/lib/more/facets/capsule.rb +0 -258
- data/lib/more/facets/coroutine.rb +0 -159
- data/lib/more/facets/enumerablepass.rb +0 -3
- data/lib/more/facets/fileable.rb +0 -162
- data/lib/more/facets/progressbar.rb +0 -253
- data/lib/more/facets/recorder.rb +0 -108
- data/meta/releases +0 -14
- data/test/more/test_coroutine.rb +0 -46
data/doc/manual/command.rdoc
DELETED
@@ -1,177 +0,0 @@
|
|
1
|
-
= Command
|
2
|
-
|
3
|
-
Command provides a clean and easy way to create a command
|
4
|
-
line interface for your program. The unique technique
|
5
|
-
utlizes a Commandline to Object Mapping (COM) to make
|
6
|
-
it quick and easy.
|
7
|
-
|
8
|
-
== Synopsis
|
9
|
-
|
10
|
-
Let's make an executable called 'mycmd'.
|
11
|
-
|
12
|
-
#!/usr/bin/env ruby
|
13
|
-
|
14
|
-
require 'facets/console/command'
|
15
|
-
|
16
|
-
class MyCmd < Console::Command
|
17
|
-
|
18
|
-
def _v
|
19
|
-
$VERBOSE = true
|
20
|
-
end
|
21
|
-
|
22
|
-
def jump
|
23
|
-
if $VERBOSE
|
24
|
-
puts "JUMP! JUMP! JUMP!"
|
25
|
-
else
|
26
|
-
puts "Jump"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
MyCmd.execute
|
33
|
-
|
34
|
-
Then on the command line:
|
35
|
-
|
36
|
-
% mycmd jump
|
37
|
-
Jump
|
38
|
-
|
39
|
-
% mycmd -v jump
|
40
|
-
JUMP! JUMP! JUMP!
|
41
|
-
|
42
|
-
== Subcommands
|
43
|
-
|
44
|
-
Commands can take subcommand and suboptions. To do this
|
45
|
-
simply add a module to your class with the same name
|
46
|
-
as the subcommand, in which the suboption methods are defined.
|
47
|
-
|
48
|
-
MyCmd << Console::Command
|
49
|
-
|
50
|
-
def initialize
|
51
|
-
@height = 1
|
52
|
-
end
|
53
|
-
|
54
|
-
def _v
|
55
|
-
$VERBOSE = true
|
56
|
-
end
|
57
|
-
|
58
|
-
def jump
|
59
|
-
if $VERBOSE
|
60
|
-
puts "JUMP!" * @height
|
61
|
-
else
|
62
|
-
puts "Jump" * @height
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
module Jump
|
67
|
-
def __height(h)
|
68
|
-
@height = h.to_i
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
MyCmd.start
|
75
|
-
|
76
|
-
Then on the command line:
|
77
|
-
|
78
|
-
% mycmd jump -h 2
|
79
|
-
Jump Jump
|
80
|
-
|
81
|
-
% mycmd -v jump -h 3
|
82
|
-
JUMP! JUMP! JUMP!
|
83
|
-
|
84
|
-
Another thing to notice about this example is that #start is an alias
|
85
|
-
for #execute.
|
86
|
-
|
87
|
-
== Missing Subcommands
|
88
|
-
|
89
|
-
You can use #method_missing to catch missing subcommand calls.
|
90
|
-
|
91
|
-
== Main and Default
|
92
|
-
|
93
|
-
If your command does not take subcommands then simply define
|
94
|
-
a #main method to dispatch action. All options will be treated globablly
|
95
|
-
in this case and any remaining comman-line arguments will be passed
|
96
|
-
to #main.
|
97
|
-
|
98
|
-
If on the other hand your command does take subcommands but none is given,
|
99
|
-
the #default method will be called, if defined. If not defined
|
100
|
-
an error will be raised (but only reported if $DEBUG is true).
|
101
|
-
|
102
|
-
== Global Options
|
103
|
-
|
104
|
-
You can define <i>global options</i> which are options that will be
|
105
|
-
processed no matter where they occur in the command line. In the above
|
106
|
-
examples only the options occuring before the subcommand are processed
|
107
|
-
globally. Anything occuring after the subcommand belonds strictly to
|
108
|
-
the subcommand. For instance, if we had added the following to the above
|
109
|
-
example:
|
110
|
-
|
111
|
-
global_option :_v
|
112
|
-
|
113
|
-
Then -v could appear anywhere in the command line, even on the end,
|
114
|
-
and still work as expected.
|
115
|
-
|
116
|
-
% mycmd jump -h 3 -v
|
117
|
-
|
118
|
-
== Missing Options
|
119
|
-
|
120
|
-
You can use #option_missing to catch any options that are not explicility
|
121
|
-
defined.
|
122
|
-
|
123
|
-
The method signature should look like:
|
124
|
-
|
125
|
-
option_missing(option_name, args)
|
126
|
-
|
127
|
-
Example:
|
128
|
-
def option_missing(option_name, args)
|
129
|
-
p args if $debug
|
130
|
-
case option_name
|
131
|
-
when 'p'
|
132
|
-
@a = args[0].to_i
|
133
|
-
@b = args[1].to_i
|
134
|
-
2
|
135
|
-
else
|
136
|
-
raise InvalidOptionError(option_name, args)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
Its return value should be the effective "arity" of that options -- that is,
|
141
|
-
how many arguments it consumed ("-p a b", for example, would consume 2 args:
|
142
|
-
"a" and "b"). An arity of 1 is assumed if nil or false is returned.
|
143
|
-
|
144
|
-
Be aware that when using subcommand modules, the same option_missing
|
145
|
-
method will catch missing options for global options and subcommand
|
146
|
-
options too unless an option_missing method is also defined in the
|
147
|
-
subcommand module.
|
148
|
-
|
149
|
-
== Help Documentation
|
150
|
-
|
151
|
-
You can also add help information quite easily. If the following code
|
152
|
-
is saved as 'foo' for instance.
|
153
|
-
|
154
|
-
MyCmd << Console::Command
|
155
|
-
|
156
|
-
help "Dispays the word JUMP!"
|
157
|
-
|
158
|
-
def jump
|
159
|
-
if $VERBOSE
|
160
|
-
puts "JUMP! JUMP! JUMP!"
|
161
|
-
else
|
162
|
-
puts "Jump"
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
MyCmd.execute
|
169
|
-
|
170
|
-
then by running 'foo help' on the command line, standard help information
|
171
|
-
will be displayed.
|
172
|
-
|
173
|
-
foo
|
174
|
-
|
175
|
-
jump Displays the word JUMP!
|
176
|
-
|
177
|
-
|
data/doc/manual/core.rdoc
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
= Facets Core
|
2
|
-
|
3
|
-
Core contains the *essentials* of Facets. Core consists of a number
|
4
|
-
of generally useful core extension methods, and a couple small support
|
5
|
-
classes.
|
6
|
-
|
7
|
-
By definition Core contains anything that will load automatically when
|
8
|
-
issuing:
|
9
|
-
|
10
|
-
require 'facets'
|
11
|
-
|
12
|
-
== Facets by the Method
|
13
|
-
|
14
|
-
In the 1.0 series of Facets, all extensions were stored on a per-method
|
15
|
-
basis. This had some advantages, organization and complete fine-grain control
|
16
|
-
over requires was possible, but it proved in practice to be _too_ granular.
|
17
|
-
Some method simply too close in nature, or dependency, to go without others.
|
18
|
-
|
19
|
-
So version 2.0 has grouped the extensions method into small groups.
|
20
|
-
The library is still highly granular, but not excessively. But there's
|
21
|
-
still refinement being done in this regard. You need not worry about it
|
22
|
-
though, becuase per-method redirect libraries are provided. This is useful,
|
23
|
-
not only for backward compatability, but also if you know what method you
|
24
|
-
want, but don't recall exactly which core file it is in.
|
25
|
-
|
26
|
-
Here's an example:
|
27
|
-
|
28
|
-
require 'facets-by-method/hash/collate'
|
29
|
-
|
30
|
-
== Authors
|
31
|
-
|
32
|
-
This library more than any other part of Facets is full of contributions
|
33
|
-
from sources all across the Ruby community. For all who have code here,
|
34
|
-
the Facets Team salutes you.
|
35
|
-
|
36
|
-
For a list of names, look to the README in Facet's main documetation.
|
37
|
-
|
data/doc/manual/faq.rdoc
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
= Facets FAQs
|
2
|
-
|
3
|
-
Q. How do I use the core mixins on object-by-object basis?
|
4
|
-
|
5
|
-
A. One way is to use Facets' Capsule library.
|
6
|
-
|
7
|
-
require 'facets/capsule'
|
8
|
-
|
9
|
-
MyStringExts = Capsule.load('facets/string/align')::String
|
10
|
-
|
11
|
-
s = "string"
|
12
|
-
|
13
|
-
s.extend(MyStringExts)
|
14
|
-
|
15
|
-
s.align_right(10) #=> " string"
|
16
|
-
|
17
|
-
Or handle it using in-module load methods.
|
18
|
-
|
19
|
-
require 'facets/module/require'
|
20
|
-
|
21
|
-
module MyStringExts
|
22
|
-
module_load 'facets/string/align'
|
23
|
-
include String
|
24
|
-
end
|
25
|
-
|
26
|
-
s = "string"
|
27
|
-
|
28
|
-
s.extend(MyStringExts)
|
29
|
-
|
30
|
-
s.align_right(10) #=> " string"
|
31
|
-
|
32
|
-
|
data/doc/manual/typecast.html
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
<html>
|
2
|
-
|
3
|
-
<head>
|
4
|
-
<title>Facets Typecast</title>
|
5
|
-
|
6
|
-
<style>
|
7
|
-
body { font: 12pt sans-serif;
|
8
|
-
padding: 0; margin: 0;
|
9
|
-
}
|
10
|
-
#container {
|
11
|
-
margin: 0 auto;
|
12
|
-
}
|
13
|
-
#title {
|
14
|
-
color: #dddddd;
|
15
|
-
border-bottom: 1px solid gray;
|
16
|
-
padding: 20px;
|
17
|
-
}
|
18
|
-
#project {
|
19
|
-
font: 22pt sans-serif;
|
20
|
-
color: black;
|
21
|
-
}
|
22
|
-
#package {
|
23
|
-
font: bold 42pt sans-serif;
|
24
|
-
color: red;
|
25
|
-
}
|
26
|
-
#corpus {
|
27
|
-
padding: 20px;
|
28
|
-
}
|
29
|
-
</style>
|
30
|
-
</head>
|
31
|
-
|
32
|
-
<body>
|
33
|
-
<div id="container">
|
34
|
-
|
35
|
-
<div id="title">
|
36
|
-
<div id="project">Facets</div>
|
37
|
-
<div id="package">Typecast</div>
|
38
|
-
</div>
|
39
|
-
|
40
|
-
<div id="corpus">
|
41
|
-
<h2>Overview</h2>
|
42
|
-
<p>Typecast provides a simple generic type conversion system. All the ruby core
|
43
|
-
conversions are available by default.</p>
|
44
|
-
|
45
|
-
<p>To implement a new type conversion, you have two choices. Given:</p>
|
46
|
-
|
47
|
-
<pre>
|
48
|
-
class CustomType
|
49
|
-
def initialize(my_var)
|
50
|
-
@my_var = my_var
|
51
|
-
end
|
52
|
-
end
|
53
|
-
</pre>
|
54
|
-
|
55
|
-
<p> 1) Define a to_class_name instance method </p>
|
56
|
-
|
57
|
-
<pre>
|
58
|
-
class CustomType
|
59
|
-
def to_string
|
60
|
-
my_var.to_s
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
c = CustomType.new 1234
|
65
|
-
s.cast_to String => "1234" (String)
|
66
|
-
</pre>
|
67
|
-
|
68
|
-
<p> 2. Define a from_class_name class method </p>
|
69
|
-
|
70
|
-
<pre>
|
71
|
-
class CustomType
|
72
|
-
def self.from_string(str)
|
73
|
-
self.new(str)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
"1234".cast_to CustomType => #<CustomType:0xb7d1958c @my_var="1234">
|
78
|
-
</pre>
|
79
|
-
|
80
|
-
<p>Those two methods are equivalent in the result. It was coded like that to
|
81
|
-
avoid the pollution of core classes with tons of to_* methods.</p>
|
82
|
-
|
83
|
-
<p>The standard methods to_s, to_f, to_i, to_a and to_sym are also used by
|
84
|
-
this system if available.</p>
|
85
|
-
|
86
|
-
<p>Usage looks like</p>
|
87
|
-
|
88
|
-
<pre>
|
89
|
-
"1234".cast_to Float => 1234.0 (Float)
|
90
|
-
Time.cast_from("6:30") => 1234.0 (Time)
|
91
|
-
</pre>
|
92
|
-
|
93
|
-
<h2>FAQ</h2>
|
94
|
-
|
95
|
-
<p>Why didn't you name the `cast_to` method to `to` ?</p>
|
96
|
-
|
97
|
-
<p>Even if it would make the syntax more friendly, I suspect it could cause
|
98
|
-
a lot of collisions with already existing code. The goal is that each
|
99
|
-
time you call cast_to, you either get your result, either a
|
100
|
-
TypeCastException.</p>
|
101
|
-
</div>
|
102
|
-
|
103
|
-
<div id="copy">
|
104
|
-
Copyright © 2004 Thomas Sawyer and all respective authors.
|
105
|
-
Ruby License
|
106
|
-
</div>
|
107
|
-
|
108
|
-
</div>
|
109
|
-
</body>
|
110
|
-
|
111
|
-
</html>
|
112
|
-
|
data/lib/more/facets/capsule.rb
DELETED
@@ -1,258 +0,0 @@
|
|
1
|
-
# = Capsule
|
2
|
-
#
|
3
|
-
# A Capsule is subclass of Module. It encapsulates an extenal script
|
4
|
-
# as a funcitons module.
|
5
|
-
#
|
6
|
-
# A module which is an instance of the Capsule class encapsulates in its scope
|
7
|
-
# the top-level methods, top-level constants, and instance variables defined in
|
8
|
-
# a ruby script file (and its subfiles) loaded by a ruby program. This allows
|
9
|
-
# use of script files to define objects that can be loaded into a program in
|
10
|
-
# much the same way that objects can be loaded from YAML or Marshal files.
|
11
|
-
#
|
12
|
-
# See intro.txt[link:files/intro_txt.html] for an overview.
|
13
|
-
#
|
14
|
-
# == Authors
|
15
|
-
#
|
16
|
-
# * Joel VanderWerf
|
17
|
-
# * Thomas Sawyer
|
18
|
-
#
|
19
|
-
# == Todo
|
20
|
-
#
|
21
|
-
# * The name of this is rather weak. Think of a better one.
|
22
|
-
#
|
23
|
-
# == Copying
|
24
|
-
#
|
25
|
-
# Copyright (c) 2005 Thomas Sawyer, Joel VanderWerf
|
26
|
-
#
|
27
|
-
# Ruby License
|
28
|
-
#
|
29
|
-
# This module is free software. You may use, modify, and/or redistribute this
|
30
|
-
# software under the same terms as Ruby.
|
31
|
-
#
|
32
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
33
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
34
|
-
# FOR A PARTICULAR PURPOSE.
|
35
|
-
|
36
|
-
#require 'rbconfig'
|
37
|
-
|
38
|
-
# = Capsule
|
39
|
-
#
|
40
|
-
# A Capsule is subclass of Module. It encapsulates an extenal script
|
41
|
-
# as a funcitons module.
|
42
|
-
#
|
43
|
-
# A module which is an instance of the Capsule class encapsulates in its scope
|
44
|
-
# the top-level methods, top-level constants, and instance variables defined in
|
45
|
-
# a ruby script file (and its subfiles) loaded by a ruby program. This allows
|
46
|
-
# use of script files to define objects that can be loaded into a program in
|
47
|
-
# much the same way that objects can be loaded from YAML or Marshal files.
|
48
|
-
#
|
49
|
-
# See intro.txt[link:files/intro_txt.html] for an overview.
|
50
|
-
|
51
|
-
class Capsule < Module
|
52
|
-
|
53
|
-
#DLEXT = Config::CONFIG['DLEXT']
|
54
|
-
|
55
|
-
# The script file with which the Import was instantiated.
|
56
|
-
attr_reader :main_file
|
57
|
-
|
58
|
-
# The directory in which main_file is located, and relative to which
|
59
|
-
# #load searches for files before falling back to Kernel#load.
|
60
|
-
#attr_reader :dir
|
61
|
-
|
62
|
-
# An array of paths to search for scripts. This has the same
|
63
|
-
# semantics as <tt>$:</tt>, alias <tt>$LOAD_PATH</tt>, excpet
|
64
|
-
# that it is local to this script. The path of the current
|
65
|
-
# script is added automatically (equivalent to '.')
|
66
|
-
attr_reader :load_path
|
67
|
-
|
68
|
-
# A hash that maps <tt>filename=>true</tt> for each file that has been
|
69
|
-
# required locally by the script. This has the same semantics as <tt>$"</tt>,
|
70
|
-
# alias <tt>$LOADED_FEATURES</tt>, except that it is local to this script.
|
71
|
-
attr_reader :loaded_features
|
72
|
-
|
73
|
-
class << self
|
74
|
-
# As with #new but will search Ruby's $LOAD_PATH first.
|
75
|
-
#--
|
76
|
-
# Will also try .rb, .so, .dll, et al extensions, like require does.
|
77
|
-
#++
|
78
|
-
def load(main_file, options=nil, &block)
|
79
|
-
file = nil
|
80
|
-
$LOAD_PATH.each do |path|
|
81
|
-
break if file = File.file?(File.join(path, main_file))
|
82
|
-
#break if file = Dir.glob(File.join(path, main_file)+'{,.rb,.'+DLEXT+'}')[0]
|
83
|
-
end
|
84
|
-
new(file || main_file, options=nil, &block)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Creates new Capsule, and loads _main_file_ in the scope of the script. If a
|
89
|
-
# block is given, the script is passed to it before loading from the file, and
|
90
|
-
# constants can be defined as inputs to the script.
|
91
|
-
|
92
|
-
def initialize(main_file, options=nil, &block)
|
93
|
-
extend self
|
94
|
-
|
95
|
-
options ||= {}
|
96
|
-
|
97
|
-
@main_file = File.expand_path(main_file)
|
98
|
-
@load_path = options[:load_path] || []
|
99
|
-
#@load_path |= [File.dirname(@main_file)] # before or after?
|
100
|
-
@loaded_features = options[:loaded_features] || {}
|
101
|
-
|
102
|
-
# TODO In order to load/require at the instance level.
|
103
|
-
# This needs to be in a separate namespace however
|
104
|
-
# b/c it can interfere with what is expected.
|
105
|
-
#[ :require, :load ].each{ |meth|
|
106
|
-
# m = method(meth)
|
107
|
-
# define_method(meth) do |*args| m.call(*args) end
|
108
|
-
#}
|
109
|
-
|
110
|
-
module_eval(&block) if block
|
111
|
-
extend self
|
112
|
-
|
113
|
-
load_in_module(main_file)
|
114
|
-
end
|
115
|
-
|
116
|
-
# Lookup feature in load path.
|
117
|
-
|
118
|
-
def load_path_lookup(feature)
|
119
|
-
paths = File.join('{' + @load_path.join(',') + '}', feature + '{,.rb,.rbs}')
|
120
|
-
files = Dir.glob(paths)
|
121
|
-
match = files.find{ |f| ! @loaded_features.include?(f) }
|
122
|
-
return match
|
123
|
-
end
|
124
|
-
|
125
|
-
# Loads _file_ into the capsule. Searches relative to the local dir, that is,
|
126
|
-
# the dir of the file given in the original call to
|
127
|
-
# <tt>Capsule.load(file)</tt>, loads the file, if found, into this Capsule's
|
128
|
-
# scope, and returns true. If the file is not found, falls back to
|
129
|
-
# <tt>Kernel.load</tt>, which searches on <tt>$LOAD_PATH</tt>, loads the file,
|
130
|
-
# if found, into global scope, and returns true. Otherwise, raises
|
131
|
-
# <tt>LoadError</tt>.
|
132
|
-
#
|
133
|
-
# The _wrap_ argument is passed to <tt>Kernel.load</tt> in the fallback case,
|
134
|
-
# when the file is not found locally.
|
135
|
-
#
|
136
|
-
# Typically called from within the main file to load additional sub files, or
|
137
|
-
# from those sub files.
|
138
|
-
#
|
139
|
-
#--
|
140
|
-
# TODO Need to add load_path lookup.
|
141
|
-
#++
|
142
|
-
|
143
|
-
def load(file, wrap = false)
|
144
|
-
load_in_module(File.join(@dir, file))
|
145
|
-
true
|
146
|
-
rescue MissingFile
|
147
|
-
super
|
148
|
-
end
|
149
|
-
|
150
|
-
# Analogous to <tt>Kernel#require</tt>. First tries the local dir, then falls
|
151
|
-
# back to <tt>Kernel#require</tt>. Will load a given _feature_ only once.
|
152
|
-
#
|
153
|
-
# Note that extensions (*.so, *.dll) can be required in the global scope, as
|
154
|
-
# usual, but not in the local scope. (This is not much of a limitation in
|
155
|
-
# practice--you wouldn't want to load an extension more than once.) This
|
156
|
-
# implementation falls back to <tt>Kernel#require</tt> when the argument is an
|
157
|
-
# extension or is not found locally.
|
158
|
-
#
|
159
|
-
#--
|
160
|
-
# This was using load_in_module rather than include_script. Maybe is still should
|
161
|
-
# and one should have to call include_script instead? Think about this.
|
162
|
-
#++
|
163
|
-
|
164
|
-
def require(feature)
|
165
|
-
file = load_path_lookup(feature)
|
166
|
-
return super unless file
|
167
|
-
begin
|
168
|
-
@loaded_features[file] = true
|
169
|
-
load_in_module(file)
|
170
|
-
rescue MissingFile
|
171
|
-
@loaded_features[file] = false
|
172
|
-
super
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# Raised by #load_in_module, caught by #load and #require.
|
177
|
-
class MissingFile < LoadError; end
|
178
|
-
|
179
|
-
# Loads _file_ in this module's context. Note that <tt>\_\_FILE\_\_</tt> and
|
180
|
-
# <tt>\_\_LINE\_\_</tt> work correctly in _file_.
|
181
|
-
# Called by #load and #require; not normally called directly.
|
182
|
-
|
183
|
-
def load_in_module(file)
|
184
|
-
module_eval(IO.read(file), File.expand_path(file))
|
185
|
-
rescue Errno::ENOENT => e
|
186
|
-
if /#{file}$/ =~ e.message
|
187
|
-
raise MissingFile, e.message
|
188
|
-
else
|
189
|
-
raise
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def include_script(file)
|
194
|
-
include self.class.new(file, :load_path=>load_path, :loaded_features=>loaded_features)
|
195
|
-
rescue Errno::ENOENT => e
|
196
|
-
if /#{file}$/ =~ e.message
|
197
|
-
raise MissingFile, e.message
|
198
|
-
else
|
199
|
-
raise
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
#
|
204
|
-
def include(*mods)
|
205
|
-
super
|
206
|
-
extend self
|
207
|
-
end
|
208
|
-
|
209
|
-
def to_s # :nodoc:
|
210
|
-
"#<#{self.class}:#{main_file}>"
|
211
|
-
end
|
212
|
-
|
213
|
-
end
|
214
|
-
|
215
|
-
# TODO Is autoimport bets name for this?
|
216
|
-
|
217
|
-
class Module
|
218
|
-
|
219
|
-
const_missing_definition_for_autoimport = lambda do
|
220
|
-
#$autoimport_activated = true
|
221
|
-
alias const_missing_before_autoimport const_missing
|
222
|
-
|
223
|
-
def const_missing(sym) # :nodoc:
|
224
|
-
filename = @autoimport && @autoimport[sym]
|
225
|
-
if filename
|
226
|
-
mod = Import.load(filename)
|
227
|
-
const_set sym, mod
|
228
|
-
else
|
229
|
-
const_missing_before_autoimport(sym)
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
# When the constant named by symbol +mod+ is referenced, loads the script
|
235
|
-
# in filename using Capsule.load and defines the constant to be equal to the
|
236
|
-
# resulting Capsule module.
|
237
|
-
#
|
238
|
-
# Use like Module#autoload--however, the underlying opertation is #load rather
|
239
|
-
# than #require, because scripts, unlike libraries, can be loaded more than
|
240
|
-
# once. See examples/autoscript-example.rb
|
241
|
-
|
242
|
-
define_method(:autoimport) do |mod, file|
|
243
|
-
if @autoimport.empty? #unless $autoimport_activated
|
244
|
-
const_missing_definition_for_autoimport.call
|
245
|
-
end
|
246
|
-
(@autoimport ||= {})[mod] = file
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
|
251
|
-
module Kernel
|
252
|
-
|
253
|
-
# Calls Object.autoimport
|
254
|
-
def autoimport(mod, file)
|
255
|
-
Object.autoimport(mod, file)
|
256
|
-
end
|
257
|
-
|
258
|
-
end
|