wrap_in_module 0.0.3 → 0.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/LICENSE +24 -1
- data/README.md +56 -73
- data/lib/wrap_in_module.rb +86 -97
- data/lib/wrap_in_module/version.rb +1 -1
- data/spec/lib/wrap_in_module_spec.rb +4 -4
- metadata +1 -1
data/LICENSE
CHANGED
@@ -1,2 +1,25 @@
|
|
1
|
-
|
1
|
+
This project is licensed under the MIT license which can be seen below.
|
2
|
+
|
3
|
+
This project was based script.rb which is usable under the Ruby license.
|
4
|
+
Copyright (C)2004 Joel VanderWerf. Questions to
|
2
5
|
mailto:vjoel@users.sourceforge.net.
|
6
|
+
|
7
|
+
Copyright (c) 2012 ReachLocal, Inc.
|
8
|
+
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
10
|
+
this software and associated documentation files (the "Software"), to deal in
|
11
|
+
the Software without restriction, including without limitation the rights to
|
12
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
13
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
14
|
+
so, subject to the following conditions:
|
15
|
+
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
17
|
+
copies or substantial portions of the Software.
|
18
|
+
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -2,23 +2,10 @@
|
|
2
2
|
|
3
3
|
This is a Ruby gem I created to house the functionality to be able to wrap
|
4
4
|
various things inside modules. The primary focus of it was to be able to load a
|
5
|
-
|
5
|
+
Ruby file in using a module as namespace for all the top level Constants,
|
6
6
|
Methods, Classes, Modules, etc. This does the same for any sub ruby scripts
|
7
7
|
that are required as well.
|
8
8
|
|
9
|
-
I found this functionality in a ruby script, supprisingly called script.rb
|
10
|
-
written by Joel VanderWerf released under the Ruby license in 2004. I have
|
11
|
-
simply wrapped his script inside a gem so that this functionality can be easily
|
12
|
-
accessed. The following is the description provided with Joel's script with my
|
13
|
-
gem namespacing.
|
14
|
-
|
15
|
-
`WrapInModule::Script` is a subclass of Module. A module which is an instance
|
16
|
-
of the `WrapInModule::Script` class encapsulates in its scope the top-level
|
17
|
-
methods, top-level constants, and instance variables defined in a ruby script
|
18
|
-
file (and its dependent files) loaded by a ruby program. This allows use of
|
19
|
-
script files to define objects that can be loaded into a program in much the
|
20
|
-
same way that objects can be loaded from YAML or Marshal files.
|
21
|
-
|
22
9
|
## Installation
|
23
10
|
|
24
11
|
Add this line to your application's Gemfile:
|
@@ -33,14 +20,15 @@ Or install it yourself as:
|
|
33
20
|
|
34
21
|
$ gem install wrap_in_module
|
35
22
|
|
36
|
-
##
|
23
|
+
## Usage:
|
37
24
|
|
38
25
|
**program.rb:**
|
39
26
|
|
40
27
|
require 'wrap_in_module'
|
41
|
-
|
42
|
-
|
43
|
-
|
28
|
+
module MyModule
|
29
|
+
WrapInModule::wrap_file(MyModule, "my-script.rb")
|
30
|
+
p MyModule::VALUE
|
31
|
+
MyModule.run
|
44
32
|
|
45
33
|
**my-script.rb:**
|
46
34
|
|
@@ -53,77 +41,72 @@ Or install it yourself as:
|
|
53
41
|
|
54
42
|
$ ruby program.rb
|
55
43
|
[1, 2, 3]
|
56
|
-
#<Script:/tmp/my-script.rb> running.
|
57
44
|
|
58
|
-
## Usage
|
45
|
+
## Detailed Usage
|
59
46
|
|
60
|
-
`WrapInModule::
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
files (see below) are scoped in the same Script module, and are thereby
|
47
|
+
`WrapInModule::wrap_file(_module, file_path)` wraps the file specified by
|
48
|
+
*file_path* under the specified *_module*. All the top-level constants and
|
49
|
+
top-level methods that are defined in the file at *file_path* and its dependent
|
50
|
+
local files (see below) are scoped in the same module, and are thereby
|
65
51
|
available to the calling program.
|
66
52
|
|
67
|
-
The
|
68
|
-
usual. These methods, in the
|
69
|
-
to the
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
`
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
top-level constants defined in the script are accessible using the
|
92
|
-
`WrapInModule::Script` instance.
|
93
|
-
|
94
|
-
The top-level definitions of a `WrapInModule::Script` can be accessed after it
|
53
|
+
The file located at *file_path* can load or require other files with *load* and
|
54
|
+
*require*, as usual. These methods, in the *_module* context, add some behavior
|
55
|
+
to the *Kernel* *load* and *require* methods: Within the *_module* context they
|
56
|
+
first search for files relative to the *file_path*'s dir. Files loaded in this
|
57
|
+
way ("dependent local files") are treated like the file located at *file_path*:
|
58
|
+
top-level definitions are added to the module.
|
59
|
+
|
60
|
+
Both *load* and *require* fall back to the Kernel versions if the file is not
|
61
|
+
found locally. Hence, other ruby libraries can be loaded and required as usual,
|
62
|
+
assuming their names do not conflict with local file names. Definitions from
|
63
|
+
those files go into the usual scope (typically global). The normal ruby *load*
|
64
|
+
and *require* behavior can be forced by calling `Kernel.load` and
|
65
|
+
`Kernel.require`.
|
66
|
+
|
67
|
+
A `WrapInModule` wrapped file immitates the way the top-level ruby
|
68
|
+
context works, so a ruby file that was originally intended to be run from the
|
69
|
+
top level, defining top-level constants and top-level methods, can also be run
|
70
|
+
as a module, and its top-level constants and top-level methods are wrapped in
|
71
|
+
the modules scope. The difference between this behavior and simply wrapping the
|
72
|
+
loaded definitions in an _anonymous_ module using `Kernel.load(main_file,
|
73
|
+
true)` is that the top-level methods and top-level constants defined in the
|
74
|
+
module are accessible using the *_module*.
|
75
|
+
|
76
|
+
The top-level definitions of a `WrapInModule` module can be accessed after it
|
95
77
|
has been loaded, as follows:
|
96
78
|
|
97
|
-
|
79
|
+
`module.meth`
|
80
|
+
|
81
|
+
- Call a method defined using `def meth` or `def self.meth` in the specified
|
82
|
+
file.
|
98
83
|
|
99
|
-
|
100
|
-
the script file.
|
84
|
+
`module::K`
|
101
85
|
|
102
|
-
|
86
|
+
- Access a class, module, or constant defined using `K = val` in the specified
|
87
|
+
file.
|
103
88
|
|
104
|
-
|
105
|
-
script file.
|
89
|
+
## History
|
106
90
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
91
|
+
This is largely based on an older script called **script.rb** written by Joel
|
92
|
+
VanderWerf and released under the Ruby license in 2004. I found issues with the
|
93
|
+
way **script.rb** was doing things, specifically with respect to creating a
|
94
|
+
module like object rather than an actual module. This caused problems with
|
95
|
+
libraries such as ActiveModel and validations, etc. Therefore, I rewrote it to
|
96
|
+
work with normal modules.
|
113
97
|
|
114
|
-
|
98
|
+
Lots of props have to go out to Joel VanderWerf for writing **script.rb** in
|
99
|
+
the first place. It would have taken me a lot longer to accomplish this if I
|
100
|
+
didn't have Joel's **script.rb** ruby file to go off of.
|
115
101
|
|
116
|
-
|
117
|
-
the module and methods of the module instance (the effect of
|
118
|
-
<tt>Module#module_function</tt>). So <tt>include</tt>-ing a Script module in a
|
119
|
-
class will give instances of the class all the methods and constants defined in
|
120
|
-
the script, and they will reference the instance's instance variables,
|
121
|
-
rather than the Script module's instance variables.
|
102
|
+
Thanks Joel
|
122
103
|
|
123
|
-
|
104
|
+
It also looks that Joel was originally inspired by Nobu Nokada's suggestion in
|
124
105
|
http://ruby-talk.org/62727, in a thread (started in http://ruby-talk.org/62660)
|
125
106
|
about how to use ruby script files as specifications of objects.
|
126
107
|
|
108
|
+
|
109
|
+
|
127
110
|
## Legal and Contact Information
|
128
111
|
|
129
112
|
Usable under the Ruby license. Copyright (C)2004 Joel VanderWerf. Questions to
|
data/lib/wrap_in_module.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require "wrap_in_module/version"
|
2
2
|
|
3
3
|
module WrapInModule
|
4
|
-
# NOTE: The following is
|
4
|
+
# NOTE: The following is rewrite of script.rb v0.3. I had some issues with
|
5
|
+
# script.rb v0.3 so I rewrote large portions of it to work with existing
|
6
|
+
# modules rather than define pho modules.
|
5
7
|
# For more information please refer to http://redshift.sourceforge.net where
|
6
8
|
# I acquired the script-0.3.tar
|
7
9
|
|
@@ -12,128 +14,115 @@ module WrapInModule
|
|
12
14
|
# much the same way that objects can be loaded from YAML or Marshal files.
|
13
15
|
#
|
14
16
|
# See intro.txt[link:files/intro_txt.html] for an overview.
|
17
|
+
#
|
18
|
+
class MissingFile < LoadError; end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def initialize(_module, main_file) # :yields: self
|
22
|
-
@__module = _module
|
23
|
-
@__module.extend LoadInModuleMethods
|
24
|
-
@__module.extend ScriptModuleMethods
|
25
|
-
|
26
|
-
yield self if block_given?
|
20
|
+
def self.wrap_file(_module, _file_path)
|
21
|
+
_module.extend ::WrapInModule::LoadInModuleMethods
|
22
|
+
_module.module_eval do
|
23
|
+
# The file with which the Script was instantiated.
|
24
|
+
attr_reader :__main_file
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
# The directory in which main_file is located, and relative to which
|
27
|
+
# #load searches for files before falling back to Kernel#load.
|
28
|
+
attr_reader :__dir
|
29
|
+
|
30
|
+
# A hash that maps <tt>filename=>true</tt> for each file that has been
|
31
|
+
# required locally by the script. This has the same semantics as <tt>$"</tt>,
|
32
|
+
# alias <tt>$LOADED_FEATURES</tt>, except that it is local to this script.
|
33
|
+
attr_reader :__loaded_features
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# alias <tt>$LOADED_FEATURES</tt>, except that it is local to this script.
|
39
|
-
attr_reader :__loaded_features
|
35
|
+
@__main_file = File.expand_path(_file_path)
|
36
|
+
@__dir = File.dirname(@__main_file)
|
37
|
+
@__loaded_features = {}
|
38
|
+
load_in_module(@__main_file)
|
39
|
+
end
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
module LoadInModuleMethods
|
43
|
+
# Loads _file_ in this module's context. Note that <tt>\_\_FILE\_\_</tt> and
|
44
|
+
# <tt>\_\_LINE\_\_</tt> work correctly in _file_.
|
45
|
+
# Called by #load and #require; not normally called directly.
|
46
|
+
def load_in_module(__file__)
|
47
|
+
module_eval("@__script_scope ||= binding\n" + IO.read(__file__),
|
48
|
+
File.expand_path(__file__), 0)
|
49
|
+
# start numbering at 0 because of the extra line.
|
50
|
+
# The extra line does nothing in sub-script files.
|
51
|
+
rescue Errno::ENOENT
|
52
|
+
if /#{__file__}$/ =~ $!.message # No extra locals in this scope.
|
53
|
+
# raise MissingFile, $!.message
|
54
|
+
raise ::WrapInModule::MissingFile, $!.message
|
55
|
+
else
|
56
|
+
raise
|
45
57
|
end
|
46
58
|
end
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
module_eval("@__script_scope ||= binding\n" + IO.read(__file__),
|
54
|
-
File.expand_path(__file__), 0)
|
55
|
-
# start numbering at 0 because of the extra line.
|
56
|
-
# The extra line does nothing in sub-script files.
|
57
|
-
rescue Errno::ENOENT
|
58
|
-
if /#{__file__}$/ =~ $!.message # No extra locals in this scope.
|
59
|
-
raise MissingFile, $!.message
|
60
|
-
else
|
61
|
-
raise
|
62
|
-
end
|
63
|
-
end
|
60
|
+
# This is so that <tt>def meth...</tt> behaves like in Ruby's top-level
|
61
|
+
# context. The implementation simply calls
|
62
|
+
# <tt>Module#module_function(name)</tt>.
|
63
|
+
def method_added(name) # :nodoc:
|
64
|
+
module_function(name)
|
64
65
|
end
|
65
66
|
|
66
|
-
|
67
|
-
# "#<#{self.class}:#{File.join(__dir, File.basename(__main_file))}>"
|
68
|
-
# end
|
69
|
-
|
70
|
-
module ScriptModuleMethods
|
71
|
-
# This is so that <tt>def meth...</tt> behaves like in Ruby's top-level
|
72
|
-
# context. The implementation simply calls
|
73
|
-
# <tt>Module#module_function(name)</tt>.
|
74
|
-
def method_added(name) # :nodoc:
|
75
|
-
module_function(name)
|
76
|
-
end
|
77
|
-
|
78
|
-
attr_reader :__script_scope
|
67
|
+
attr_reader :__script_scope
|
79
68
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
69
|
+
# Gets list of local vars in the script. Does not see local vars in files
|
70
|
+
# loaded or required by that script.
|
71
|
+
def __local_variables
|
72
|
+
eval("local_variables", __script_scope)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Gets value of local var in the script. Does not see local vars in files
|
76
|
+
# loaded or required by that script.
|
77
|
+
def __local_variable_get(name)
|
78
|
+
eval(name.to_s, __script_scope)
|
79
|
+
end
|
91
80
|
|
92
|
-
# Raised by #load_in_module, caught by #load and #require.
|
93
|
-
class MissingFile < LoadError; end
|
94
81
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
82
|
+
# Loads _file_ into this Script. Searches relative to the local dir, that is,
|
83
|
+
# the dir of the file given in the original call to
|
84
|
+
# <tt>Script.load(file)</tt>, loads the file, if found, into this Script's
|
85
|
+
# scope, and returns true. If the file is not found, falls back to
|
86
|
+
# <tt>Kernel.load</tt>, which searches on <tt>$LOAD_PATH</tt>, loads the file,
|
87
|
+
# if found, into global scope, and returns true. Otherwise, raises
|
88
|
+
# <tt>LoadError</tt>.
|
89
|
+
#
|
90
|
+
# The _wrap_ argument is passed to <tt>Kernel.load</tt> in the fallback case,
|
91
|
+
# when the file is not found locally.
|
92
|
+
#
|
93
|
+
# Typically called from within the main file to load additional sub files, or
|
94
|
+
# from those sub files.
|
95
|
+
|
96
|
+
def load(file, wrap = false)
|
97
|
+
begin
|
110
98
|
load_in_module(File.join(@__dir, file))
|
111
99
|
true
|
112
|
-
rescue MissingFile
|
100
|
+
rescue ::WrapInModule::MissingFile
|
113
101
|
super
|
114
102
|
end
|
103
|
+
end
|
115
104
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
105
|
+
# Analogous to <tt>Kernel#require</tt>. First tries the local dir, then falls
|
106
|
+
# back to <tt>Kernel#require</tt>. Will load a given _feature_ only once.
|
107
|
+
#
|
108
|
+
# Note that extensions (*.so, *.dll) can be required in the global scope, as
|
109
|
+
# usual, but not in the local scope. (This is not much of a limitation in
|
110
|
+
# practice--you wouldn't want to load an extension more than once.) This
|
111
|
+
# implementation falls back to <tt>Kernel#require</tt> when the argument is an
|
112
|
+
# extension or is not found locally.
|
113
|
+
|
114
|
+
def require(feature)
|
115
|
+
begin
|
126
116
|
unless @__loaded_features[feature]
|
127
117
|
@__loaded_features[feature] = true
|
128
118
|
file = File.join(@__dir, feature)
|
129
119
|
file += ".rb" unless /\.rb$/ =~ file
|
130
120
|
load_in_module(file)
|
131
121
|
end
|
132
|
-
rescue MissingFile
|
122
|
+
rescue ::WrapInModule::MissingFile
|
133
123
|
@__loaded_features[feature] = false
|
134
124
|
super
|
135
125
|
end
|
136
126
|
end
|
137
127
|
end
|
138
128
|
end
|
139
|
-
|
@@ -3,26 +3,26 @@ require 'spec_helper'
|
|
3
3
|
describe "WrapInModule" do
|
4
4
|
it "loads a file with a single top level class" do
|
5
5
|
module FooMod; end
|
6
|
-
WrapInModule
|
6
|
+
WrapInModule.wrap_file(FooMod, File.dirname(__FILE__) + '/../example_data/single_top_level_class.rb')
|
7
7
|
FooMod::Hoopty.new
|
8
8
|
end
|
9
9
|
|
10
10
|
it "loads a file without overwriting existing module definition" do
|
11
11
|
module FooMod; BOB="hello there I am bob"; end
|
12
|
-
WrapInModule
|
12
|
+
WrapInModule.wrap_file(FooMod, File.dirname(__FILE__) + '/../example_data/single_top_level_class.rb')
|
13
13
|
FooMod::BOB
|
14
14
|
end
|
15
15
|
|
16
16
|
it "loads a file with a top level class that requires another file with a top level class" do
|
17
17
|
module FooMod; end
|
18
|
-
WrapInModule
|
18
|
+
WrapInModule.wrap_file(FooMod, File.dirname(__FILE__) + '/../example_data/top_level_class_requires_another_file.rb')
|
19
19
|
FooMod::Doopty.new
|
20
20
|
FooMod::Hoopty.new
|
21
21
|
end
|
22
22
|
|
23
23
|
it "verify that classes required inside of the top level loaded file are accesible in the non namespaces scope" do
|
24
24
|
module FooMod; end
|
25
|
-
WrapInModule
|
25
|
+
WrapInModule.wrap_file(FooMod, File.dirname(__FILE__) + '/../example_data/top_level_class_requires_another_file.rb')
|
26
26
|
a = FooMod::Doopty.new
|
27
27
|
a.test_hoopty
|
28
28
|
end
|