capsule 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|