capsule 1.0.0 → 1.1.0
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/.ruby +35 -0
- data/AGPL3.txt +661 -0
- data/COPYING.rdoc +24 -0
- data/HISTORY.rdoc +27 -0
- data/README.rdoc +154 -0
- data/lib/capsule.rb +65 -94
- data/lib/capsule/autoimport.rb +43 -0
- metadata +34 -39
- data/HISTORY +0 -11
- data/LICENSE +0 -674
- data/MANIFEST +0 -19
- data/README +0 -61
- data/RELEASE +0 -12
- data/meta/abstract +0 -2
- data/meta/authors +0 -3
- data/meta/created +0 -1
- data/meta/homepage +0 -1
- data/meta/license +0 -1
- data/meta/package +0 -1
- data/meta/project +0 -1
- data/meta/released +0 -1
- data/meta/repository +0 -1
- data/meta/summary +0 -2
- data/meta/version +0 -1
data/COPYING.rdoc
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
= COPYRIGHT NOTICES
|
2
|
+
|
3
|
+
== Capsule
|
4
|
+
|
5
|
+
Copyright (C)2007 Thomas Sawyer
|
6
|
+
|
7
|
+
Unless otherwise agreed upon by the copyright holder this software is
|
8
|
+
made available under the terms of the GNU Affero General Public License
|
9
|
+
(see AGPL3.txt.)
|
10
|
+
|
11
|
+
(http://rubyworks.github.com/capsule)
|
12
|
+
|
13
|
+
|
14
|
+
== Script
|
15
|
+
|
16
|
+
Capsule is a derivation of Joel VanderWerf's Script library.
|
17
|
+
|
18
|
+
Usable under the Ruby license.
|
19
|
+
|
20
|
+
Copyright (C)2004 Joel VanderWerf.
|
21
|
+
|
22
|
+
Questions to mailto:vjoel@users.sourceforge.net.
|
23
|
+
|
24
|
+
(http://redshift.sourceforge.net/script/)
|
data/HISTORY.rdoc
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
= RELEASE HISTORY
|
2
|
+
|
3
|
+
== 1.1.0 / 2011-05-12
|
4
|
+
|
5
|
+
This release makes two small enhancements to the Capsule class.
|
6
|
+
It will automatically try `.rb` extension of script names that
|
7
|
+
lack an extension and it now provides an `:extend` option which
|
8
|
+
can be set to `false` to deactive a capsules sef extension
|
9
|
+
(`extend self`).
|
10
|
+
|
11
|
+
Changes:
|
12
|
+
|
13
|
+
* Search for scripts with common ruby extensions.
|
14
|
+
* Add :extend option to control self extension.
|
15
|
+
|
16
|
+
|
17
|
+
== 1.0.0 / 2009-07-01
|
18
|
+
|
19
|
+
This is the initial stand-alone release of Capsule,
|
20
|
+
spun-off from Ruby Facets.
|
21
|
+
|
22
|
+
Changes:
|
23
|
+
|
24
|
+
* 1 Major Enhancement
|
25
|
+
|
26
|
+
* Happy Birthday!
|
27
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
= Capsule
|
2
|
+
|
3
|
+
|
4
|
+
== Description
|
5
|
+
|
6
|
+
Capsule is a subclass of Module. A module which is an instance of the Capsule
|
7
|
+
class encapsulates in its scope the top-level methods, top-level constants, and
|
8
|
+
instance variables defined in a Ruby script (and its dependent files)
|
9
|
+
loaded by a Ruby program. This allows use of script files to define objects that
|
10
|
+
can be loaded into a program in much the same way that objects can be loaded
|
11
|
+
from YAML or Marshaled files. There is also an autoimport method which functions
|
12
|
+
like Ruby's autoload but based on is Capsule.load.
|
13
|
+
|
14
|
+
|
15
|
+
== Resources
|
16
|
+
|
17
|
+
* home: http://rubyworks.github.com/capsule
|
18
|
+
* work: http://github.com/rubyworks/capsule
|
19
|
+
* mail: http://groups.google.com/group/rubyworks-mailinglist
|
20
|
+
|
21
|
+
|
22
|
+
== Synopsis
|
23
|
+
|
24
|
+
To encapsulate a script in a Capsule:
|
25
|
+
|
26
|
+
myscript = Capsule.new('myscript.rb')
|
27
|
+
|
28
|
+
If the script is in Ruby's $LOAD_PATH, then you can use +Capsule.load+.
|
29
|
+
|
30
|
+
myscript = Capsule.load('myscript.rb')
|
31
|
+
|
32
|
+
Here is an example:
|
33
|
+
|
34
|
+
# myscript.rb
|
35
|
+
|
36
|
+
VALUE = [1,2,3]
|
37
|
+
|
38
|
+
def run
|
39
|
+
puts "#{self} is running."
|
40
|
+
end
|
41
|
+
|
42
|
+
And the encapsulating program:
|
43
|
+
|
44
|
+
# program.rb:
|
45
|
+
|
46
|
+
require 'capsule'
|
47
|
+
|
48
|
+
myscript = Capsule.load("myscript.rb")
|
49
|
+
|
50
|
+
p myscript::VALUE
|
51
|
+
|
52
|
+
myscript.run
|
53
|
+
|
54
|
+
Running `program.rb` will result in:
|
55
|
+
|
56
|
+
$ ruby program.rb
|
57
|
+
[1, 2, 3]
|
58
|
+
#<Capsule:myscript.rb> is running.
|
59
|
+
|
60
|
+
|
61
|
+
== Usage
|
62
|
+
|
63
|
+
Capsule modules are instantiated with <tt>Capsule.new(main_file)</tt> or the alias
|
64
|
+
<tt>Capsule.load(main_file)</tt>. All the top-level constants and top-level
|
65
|
+
methods that are defined in the +main_file+ and its dependent local files (see
|
66
|
+
below) are scoped in the same Capsule module, and are thereby available to the
|
67
|
+
calling program.
|
68
|
+
|
69
|
+
The +main_file+ can load or require other files with +load+ and +require+, as
|
70
|
+
usual. These methods, in the Capsule context, add some behavior to the +Kernel+
|
71
|
+
+load+ and +require+ methods: <tt>Capsule#load</tt> and <tt>Capsule#require</tt>
|
72
|
+
first search for files relative to the +main_file+'s dir. Files loaded in this
|
73
|
+
way ("dependent local files") are treated like the script file itself: top-level
|
74
|
+
definitions are added to the script module that is returned by +load+ or
|
75
|
+
+require+.
|
76
|
+
|
77
|
+
Both <tt>Capsule#load</tt> and <tt>Capsule#require</tt> fall back to the Kernel
|
78
|
+
versions if the file is not found locally. Hence, other ruby libraries can be
|
79
|
+
loaded and required as usual, assuming their names do not conflict with local
|
80
|
+
file names. Definitions from those files go into the usual scope (typically
|
81
|
+
global). The normal ruby +load+ and +require+ behavior can be forced by calling
|
82
|
+
<tt>Kernel.load</tt> and <tt>Kernel.require</tt>.
|
83
|
+
|
84
|
+
A Capsule immitates the way the top-level ruby context works, so a ruby file that
|
85
|
+
was originally intended to be run from the top level, defining top-level
|
86
|
+
constants and top-level methods, can also be run as a Capsule, and its top-level
|
87
|
+
constants and top-level methods are wrapped in the script's scope. The
|
88
|
+
difference between this behavior and simply wrapping the loaded definitions in
|
89
|
+
an _anonymous_ module using <tt>Kernel.load(main_file, true)</tt> is that the
|
90
|
+
top-level methods and top-level constants defined in the script are accessible
|
91
|
+
using the Capsule instance.
|
92
|
+
|
93
|
+
The top-level definitions of a Capsule can be accessed after it has been
|
94
|
+
loaded, as follows:
|
95
|
+
|
96
|
+
<tt>script.meth</tt>
|
97
|
+
|
98
|
+
- Call a method defined using <tt>def meth</tt> or <tt>def self.meth</tt> in
|
99
|
+
the script file.
|
100
|
+
|
101
|
+
<tt>script::K</tt>
|
102
|
+
|
103
|
+
- Access a class, module, or constant defined using <tt>K = val</tt> in the
|
104
|
+
script file.
|
105
|
+
|
106
|
+
An "input" can be passed to the script before loading. Simply call Capsule.new
|
107
|
+
(or Capsule.load) with a block. The block is passed a single argument, the
|
108
|
+
Capsule module, and executed before the files are loaded into the Capsule's
|
109
|
+
scope. Setting a constant in this block makes the constant available to the
|
110
|
+
script during loading. For example:
|
111
|
+
|
112
|
+
script = Capsule.load("my-script.rb") { |capsule| capsule::INPUT = 3 }
|
113
|
+
|
114
|
+
Note that all methods defined in the script file are both instance methods of
|
115
|
+
the module and methods of the module instance (the effect of
|
116
|
+
<tt>Module#module_function</tt>). So <tt>include</tt>-ing a Capsule module in a
|
117
|
+
class will give instances of the class all the methods and constants defined in
|
118
|
+
the script, and they will reference the instance's instance variables,
|
119
|
+
rather than the Capsule module's instance variables.
|
120
|
+
|
121
|
+
The Capsule class was inspired by Nobu Nokada's suggestion in
|
122
|
+
http://ruby-talk.org/62727, in a thread (started in http://ruby-talk.org/62660)
|
123
|
+
about how to use ruby script files as specifications of objects.
|
124
|
+
|
125
|
+
|
126
|
+
== Installation
|
127
|
+
|
128
|
+
To install with RubyGems simply open a console and type:
|
129
|
+
|
130
|
+
gem install capsule
|
131
|
+
|
132
|
+
Local installation requires Setup.rb (gem install setup),
|
133
|
+
then download the tarball package and type:
|
134
|
+
|
135
|
+
tar -xvzf capsule-1.0.0.tgz
|
136
|
+
cd capsule-1.0.0
|
137
|
+
sudo setup.rb all
|
138
|
+
|
139
|
+
Windows users use 'ruby setup.rb all'.
|
140
|
+
|
141
|
+
|
142
|
+
== Legal
|
143
|
+
|
144
|
+
(AGPL 3 License)
|
145
|
+
|
146
|
+
Copyright (c) 2007 Thomas Sawyer
|
147
|
+
Copyright (c) 2004 Joel VanderWerf
|
148
|
+
|
149
|
+
Capsule is based on Joel VanderWerf's Script library.
|
150
|
+
|
151
|
+
Unless otherwise agreed upon by the copyright holder, this program is
|
152
|
+
ditributed under the terms of the GNU Affero General Public License v3.
|
153
|
+
|
154
|
+
See COPYING.rdoc file for details.
|
data/lib/capsule.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
#require 'rbconfig'
|
2
|
+
require 'capsule/autoimport'
|
2
3
|
|
3
|
-
# A Capsule is subclass of Module. It encapsulates an extenal script
|
4
|
-
# as a funcitons module.
|
5
|
-
#
|
6
4
|
# A module which is an instance of the Capsule class encapsulates in its scope
|
7
5
|
# the top-level methods, top-level constants, and instance variables defined in
|
8
6
|
# a ruby script file (and its subfiles) loaded by a ruby program. This allows
|
@@ -15,7 +13,13 @@ class Capsule < Module
|
|
15
13
|
|
16
14
|
#DLEXT = Config::CONFIG['DLEXT']
|
17
15
|
|
18
|
-
#
|
16
|
+
# Ruby script extensions to automatically try when searching for
|
17
|
+
# a script on the load_path.
|
18
|
+
|
19
|
+
SUFFIXES = ['.rb', '.rbs'] #, '.rbw', '.so', '.bundle', '.dll', '.sl', '.jar']
|
20
|
+
|
21
|
+
# The script file with which the import was instantiated.
|
22
|
+
|
19
23
|
attr_reader :main_file
|
20
24
|
|
21
25
|
# The directory in which main_file is located, and relative to which
|
@@ -23,29 +27,29 @@ class Capsule < Module
|
|
23
27
|
#attr_reader :dir
|
24
28
|
|
25
29
|
# An array of paths to search for scripts. This has the same
|
26
|
-
# semantics as <tt>$:</tt>, alias <tt>$LOAD_PATH</tt>,
|
30
|
+
# semantics as <tt>$:</tt>, alias <tt>$LOAD_PATH</tt>, except
|
27
31
|
# that it is local to this script. The path of the current
|
28
|
-
# script is added automatically
|
32
|
+
# script is added automatically.
|
33
|
+
|
29
34
|
attr_reader :load_path
|
30
35
|
|
31
36
|
# A hash that maps <tt>filename=>true</tt> for each file that has been
|
32
37
|
# required locally by the script. This has the same semantics as <tt>$"</tt>,
|
33
38
|
# alias <tt>$LOADED_FEATURES</tt>, except that it is local to this script.
|
39
|
+
|
34
40
|
attr_reader :loaded_features
|
35
41
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
file =
|
43
|
-
|
44
|
-
|
45
|
-
#break if file = Dir.glob(File.join(path, main_file)+'{,.rb,.'+DLEXT+'}')[0]
|
46
|
-
end
|
47
|
-
new(file || main_file, options=nil, &block)
|
42
|
+
# As with #new but will search Ruby's $LOAD_PATH first.
|
43
|
+
# This will also try `.rb` extensions, like require does.
|
44
|
+
|
45
|
+
def self.load(main_file, options=nil, &block)
|
46
|
+
file = nil
|
47
|
+
$LOAD_PATH.each do |path|
|
48
|
+
file = File.join(path, main_file)
|
49
|
+
break if file = File.file?(file)
|
50
|
+
break if file = Dir.glob(file + '{' + SUFFIXES.join(',') + '}').first
|
48
51
|
end
|
52
|
+
new(file || main_file, options=nil, &block)
|
49
53
|
end
|
50
54
|
|
51
55
|
# Creates new Capsule, and loads _main_file_ in the scope of the script. If a
|
@@ -53,25 +57,24 @@ class Capsule < Module
|
|
53
57
|
# constants can be defined as inputs to the script.
|
54
58
|
|
55
59
|
def initialize(main_file, options=nil, &block)
|
56
|
-
extend self
|
57
|
-
|
58
60
|
options ||= {}
|
59
61
|
|
60
62
|
@main_file = File.expand_path(main_file)
|
63
|
+
|
61
64
|
@load_path = options[:load_path] || []
|
62
|
-
#@load_path |= [File.dirname(@main_file)] # before or after?
|
63
65
|
@loaded_features = options[:loaded_features] || {}
|
64
66
|
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
@extend = true # default
|
68
|
+
@extend = options[:extend] if options.key?(:extend)
|
69
|
+
|
70
|
+
## add script's path to load_path
|
71
|
+
## TODO: should we be doing this?
|
72
|
+
@load_path |= [File.dirname(@main_file)]
|
73
|
+
|
74
|
+
## if @extend (the default) module extends itself
|
75
|
+
extend self if @extend
|
72
76
|
|
73
77
|
module_eval(&block) if block
|
74
|
-
extend self
|
75
78
|
|
76
79
|
load_in_module(main_file)
|
77
80
|
end
|
@@ -79,7 +82,7 @@ class Capsule < Module
|
|
79
82
|
# Lookup feature in load path.
|
80
83
|
|
81
84
|
def load_path_lookup(feature)
|
82
|
-
paths = File.join('{' + @load_path.join(',') + '}', feature + '{
|
85
|
+
paths = File.join('{' + @load_path.join(',') + '}', feature + '{' + SUFFIXES + '}')
|
83
86
|
files = Dir.glob(paths)
|
84
87
|
match = files.find{ |f| ! @loaded_features.include?(f) }
|
85
88
|
return match
|
@@ -98,13 +101,11 @@ class Capsule < Module
|
|
98
101
|
#
|
99
102
|
# Typically called from within the main file to load additional sub files, or
|
100
103
|
# from those sub files.
|
101
|
-
#
|
102
|
-
#--
|
103
|
-
# TODO Need to add load_path lookup.
|
104
|
-
#++
|
105
104
|
|
106
105
|
def load(file, wrap = false)
|
107
|
-
|
106
|
+
file = load_path_lookup(feature)
|
107
|
+
return super unless file
|
108
|
+
load_in_module(file) #File.join(@dir, file))
|
108
109
|
true
|
109
110
|
rescue MissingFile
|
110
111
|
super
|
@@ -120,8 +121,7 @@ class Capsule < Module
|
|
120
121
|
# extension or is not found locally.
|
121
122
|
#
|
122
123
|
#--
|
123
|
-
#
|
124
|
-
# and one should have to call include_script instead? Think about this.
|
124
|
+
# TODO: Should this be using #include_script instead?
|
125
125
|
#++
|
126
126
|
|
127
127
|
def require(feature)
|
@@ -136,25 +136,40 @@ class Capsule < Module
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
-
#
|
140
|
-
|
139
|
+
# Checks the class of each +mods+. If a String, then calls #include_script,
|
140
|
+
# otherwise behaves like normal #include.
|
141
141
|
|
142
|
-
|
143
|
-
|
144
|
-
|
142
|
+
def include(*mods)
|
143
|
+
mods.reverse_each do |mod|
|
144
|
+
case mod
|
145
|
+
when String
|
146
|
+
include_script(mod)
|
147
|
+
else
|
148
|
+
super(mod)
|
149
|
+
extend self if @extend
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
145
153
|
|
146
|
-
|
147
|
-
|
154
|
+
# Create a new Capsule for a script and include it into the current capsule.
|
155
|
+
|
156
|
+
def include_script(file)
|
157
|
+
include self.class.new(file, :load_path=>load_path, :loaded_features=>loaded_features, :extend=>false)
|
148
158
|
rescue Errno::ENOENT => e
|
149
159
|
if /#{file}$/ =~ e.message
|
150
160
|
raise MissingFile, e.message
|
151
161
|
else
|
152
162
|
raise
|
153
163
|
end
|
164
|
+
extend self if @extend
|
154
165
|
end
|
155
166
|
|
156
|
-
|
157
|
-
|
167
|
+
# Loads _file_ in this module's context. Note that <tt>\_\_FILE\_\_</tt> and
|
168
|
+
# <tt>\_\_LINE\_\_</tt> work correctly in _file_.
|
169
|
+
# Called by #load and #require; not normally called directly.
|
170
|
+
|
171
|
+
def load_in_module(file)
|
172
|
+
module_eval(IO.read(file), File.expand_path(file))
|
158
173
|
rescue Errno::ENOENT => e
|
159
174
|
if /#{file}$/ =~ e.message
|
160
175
|
raise MissingFile, e.message
|
@@ -163,59 +178,15 @@ class Capsule < Module
|
|
163
178
|
end
|
164
179
|
end
|
165
180
|
|
166
|
-
#
|
167
|
-
def include(*mods)
|
168
|
-
super
|
169
|
-
extend self
|
170
|
-
end
|
181
|
+
# Give inspection of Capsule with script file name.
|
171
182
|
|
172
|
-
def
|
183
|
+
def inspect # :nodoc:
|
173
184
|
"#<#{self.class}:#{main_file}>"
|
174
185
|
end
|
175
186
|
|
176
|
-
|
177
|
-
|
178
|
-
# TODO Is autoimport bets name for this?
|
179
|
-
|
180
|
-
class Module
|
181
|
-
|
182
|
-
const_missing_definition_for_autoimport = lambda do
|
183
|
-
#$autoimport_activated = true
|
184
|
-
alias const_missing_before_autoimport const_missing
|
185
|
-
|
186
|
-
def const_missing(sym) # :nodoc:
|
187
|
-
filename = @autoimport && @autoimport[sym]
|
188
|
-
if filename
|
189
|
-
mod = Import.load(filename)
|
190
|
-
const_set sym, mod
|
191
|
-
else
|
192
|
-
const_missing_before_autoimport(sym)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
187
|
+
# Raised by #load_in_module, caught by #load and #require.
|
196
188
|
|
197
|
-
|
198
|
-
# in filename using Capsule.load and defines the constant to be equal to the
|
199
|
-
# resulting Capsule module.
|
200
|
-
#
|
201
|
-
# Use like Module#autoload--however, the underlying opertation is #load rather
|
202
|
-
# than #require, because scripts, unlike libraries, can be loaded more than
|
203
|
-
# once. See examples/autoscript-example.rb
|
189
|
+
class MissingFile < ::LoadError; end
|
204
190
|
|
205
|
-
define_method(:autoimport) do |mod, file|
|
206
|
-
if @autoimport.empty? #unless $autoimport_activated
|
207
|
-
const_missing_definition_for_autoimport.call
|
208
|
-
end
|
209
|
-
(@autoimport ||= {})[mod] = file
|
210
|
-
end
|
211
191
|
end
|
212
192
|
|
213
|
-
|
214
|
-
module Kernel
|
215
|
-
|
216
|
-
# Calls Object.autoimport
|
217
|
-
def autoimport(mod, file)
|
218
|
-
Object.autoimport(mod, file)
|
219
|
-
end
|
220
|
-
|
221
|
-
end
|