main_like_module 0.2.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.
- checksums.yaml +7 -0
- data/HISTORY.md +23 -0
- data/LICENSE.txt +23 -0
- data/README.md +163 -0
- data/lib/main_like_module.rb +62 -0
- metadata +73 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 77dd7a196964c1966a43660852408ba60eee5a6cea176b286ff822ad602acbf5
|
|
4
|
+
data.tar.gz: 1eb7d996917ce87927a4064806efc183a00fb0e5062b1e5708dd7d8bc0577652
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6f1b021260ba408ab94d9dd0f12f4e103313daf32286554883bda7f5ab0d6783cd1b4aaf900216bf0d753317caeaae244eff911016f821f67ba3b789c7330d82
|
|
7
|
+
data.tar.gz: 753e1fd13c10d9f5509a1ce976a73c84911d29f713458b13f9b7a64af74bf383cd9e70cef701f625834995c9982a90b02ac615420e8e9644533fc12462b20ca7
|
data/HISTORY.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# RELEASE HISTORY
|
|
2
|
+
|
|
3
|
+
## 0.2.0 / 2026-04-07
|
|
4
|
+
|
|
5
|
+
Maintenance release modernizing the project.
|
|
6
|
+
|
|
7
|
+
Changes:
|
|
8
|
+
|
|
9
|
+
* Replace .ruby/var metadata system with standard gemspec.
|
|
10
|
+
* Replace Travis CI with GitHub Actions (Ruby 3.1–3.4).
|
|
11
|
+
* Switch tests from citron/ae to minitest.
|
|
12
|
+
* Add Rakefile.
|
|
13
|
+
* Rewrite README to elaborate on the design argument.
|
|
14
|
+
* Require Ruby >= 3.1.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## 0.1.0 / 2011-12-23
|
|
18
|
+
|
|
19
|
+
Initial and quite hopefully the only release ever needed.
|
|
20
|
+
|
|
21
|
+
Changes:
|
|
22
|
+
|
|
23
|
+
* Spun project off from Ruby Facets.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright (c) 2006 Rubyworks
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
7
|
+
this list of conditions and the following disclaimer.
|
|
8
|
+
|
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
and/or other materials provided with the distribution.
|
|
12
|
+
|
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
14
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
15
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
16
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
17
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
18
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
19
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
20
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
21
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
22
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
23
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Main Like Module
|
|
2
|
+
|
|
3
|
+
[Source Code](https://github.com/rubyworks/main_like_module) |
|
|
4
|
+
[Report Issue](https://github.com/rubyworks/main_like_module/issues)
|
|
5
|
+
|
|
6
|
+
[](https://rubygems.org/gems/main_like_module)
|
|
7
|
+
[](https://github.com/rubyworks/main_like_module/actions/workflows/test.yml)
|
|
8
|
+
|
|
9
|
+
**Main Like Module** is a small demonstration library that fills in the
|
|
10
|
+
missing parts of Ruby's toplevel object so that it behaves more like a
|
|
11
|
+
fully functional module context.
|
|
12
|
+
|
|
13
|
+
This library is *not* meant to be a useful tool that you should depend on
|
|
14
|
+
in real code. It exists to make a point about a quirk in Ruby's design,
|
|
15
|
+
and to do what it can about that quirk given the language as it stands.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## The Argument
|
|
19
|
+
|
|
20
|
+
When you start a Ruby program, you are not in some abstract "global" space.
|
|
21
|
+
You are inside an object — a real, live object that Ruby refers to internally
|
|
22
|
+
as `main`. You can prove it to yourself trivially:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
puts self #=> main
|
|
26
|
+
puts self.class #=> Object
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
So `main` is just an instance of `Object`. That, by itself, is not strange.
|
|
30
|
+
What *is* strange is what Ruby does when you define methods or constants
|
|
31
|
+
at the toplevel.
|
|
32
|
+
|
|
33
|
+
### Problem 1: Toplevel Pollutes Object
|
|
34
|
+
|
|
35
|
+
When you write a method definition at the toplevel of a Ruby program, Ruby
|
|
36
|
+
does not attach it to `main` — it attaches it to `Object` itself, as a
|
|
37
|
+
private instance method. The consequence is that *every* object in the
|
|
38
|
+
entire system inherits it.
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
def hello
|
|
42
|
+
'hi'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
"any string".send(:hello) #=> "hi"
|
|
46
|
+
42.send(:hello) #=> "hi"
|
|
47
|
+
Class.new.new.send(:hello) #=> "hi"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
That is a remarkable amount of action-at-a-distance for what looks like a
|
|
51
|
+
simple, innocuous toplevel definition. The toplevel — which one might
|
|
52
|
+
reasonably expect to be the *least* invasive scope in the language — is
|
|
53
|
+
in fact the *most* invasive.
|
|
54
|
+
|
|
55
|
+
A more sensible design would be for `main` to be a self-extended module:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
module Toplevel
|
|
59
|
+
extend self
|
|
60
|
+
# the user's program runs here
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Under that design, methods defined at the toplevel would be confined to the
|
|
65
|
+
`Toplevel` module. They would still be available for the user's script (because
|
|
66
|
+
`extend self` makes them callable as instance methods on the module itself),
|
|
67
|
+
but they would not silently bolt themselves onto every object in the system.
|
|
68
|
+
Constants defined at the toplevel would be `Toplevel::FOO` rather than the
|
|
69
|
+
ambient `::FOO` they are today.
|
|
70
|
+
|
|
71
|
+
### Problem 2: The Toplevel Proxy Is Incomplete
|
|
72
|
+
|
|
73
|
+
Ruby already does a partial job of making `main` *behave like* a module.
|
|
74
|
+
Methods such as `private`, `public`, and `include` work at the toplevel —
|
|
75
|
+
they are forwarded to `Object`. But the proxy is incomplete: many of the
|
|
76
|
+
methods you would expect to find on a module context simply do not work
|
|
77
|
+
there. For example:
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
define_method(:foo) { 'bar' }
|
|
81
|
+
#=> NoMethodError: undefined method `define_method' for main:Object
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This is inconsistent. If `main` is meant to act like the module body of
|
|
85
|
+
`Object`, then *all* of `Module`'s instance methods ought to work there,
|
|
86
|
+
not a hand-picked subset.
|
|
87
|
+
|
|
88
|
+
## What This Library Does
|
|
89
|
+
|
|
90
|
+
The first problem — toplevel polluting `Object` — cannot be fixed from
|
|
91
|
+
userland. It is baked into the parser and the interpreter. Only a change
|
|
92
|
+
to Ruby itself could address it.
|
|
93
|
+
|
|
94
|
+
The second problem *can* be addressed, and that is what this library does.
|
|
95
|
+
On `require`, it walks `Module`'s instance methods and, for each one not
|
|
96
|
+
already available at the toplevel, defines a singleton method on `main`
|
|
97
|
+
that forwards the call to `Object.class_eval`. The result is that anything
|
|
98
|
+
you can do inside a `module` body, you can now do at the toplevel as well.
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
require 'main_like_module'
|
|
102
|
+
|
|
103
|
+
define_method(:greet) { |name| "Hello, #{name}!" }
|
|
104
|
+
|
|
105
|
+
greet('World') #=> "Hello, World!"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Public, private, and protected `Module` methods are all proxied with their
|
|
109
|
+
correct visibility.
|
|
110
|
+
|
|
111
|
+
Note carefully: this does *nothing* about Problem 1. The methods you define
|
|
112
|
+
at the toplevel still end up on `Object`. This library only completes the
|
|
113
|
+
proxy; it does not redesign the toplevel.
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## Should You Use This?
|
|
117
|
+
|
|
118
|
+
Probably not. If you want a clean module context, the right thing to do is
|
|
119
|
+
to write one:
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
module MyApp
|
|
123
|
+
extend self
|
|
124
|
+
|
|
125
|
+
def greet(name)
|
|
126
|
+
"Hello, #{name}!"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
MyApp.greet('World')
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
That gives you everything `main_like_module` aspires to, without monkey-
|
|
134
|
+
patching the toplevel and without polluting `Object`. Treat the toplevel
|
|
135
|
+
as a launch pad into your own namespace and you will avoid the whole
|
|
136
|
+
mess.
|
|
137
|
+
|
|
138
|
+
This gem exists as a demonstration: a small, working illustration of how
|
|
139
|
+
*close* Ruby comes to having a sane toplevel, and how a few dozen lines
|
|
140
|
+
of metaprogramming are enough to close part of the gap.
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
## Installation
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
$ gem install main_like_module
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Or in a `Gemfile`:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
gem 'main_like_module'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
## Copyrights
|
|
157
|
+
|
|
158
|
+
Main Like Module is copyrighted open source software.
|
|
159
|
+
|
|
160
|
+
Copyright (c) 2006 Rubyworks (BSD-2-Clause)
|
|
161
|
+
|
|
162
|
+
It can be distributed and modified in accordance with the **BSD-2-Clause**
|
|
163
|
+
license. See LICENSE.txt for details.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Main like Module
|
|
2
|
+
#
|
|
3
|
+
# Main, ie. the top-level object, is not fully in-sync with
|
|
4
|
+
# Module. So, certain methods like #define_method do not work.
|
|
5
|
+
# This library fixes this.
|
|
6
|
+
#
|
|
7
|
+
# Techinally it is this authors opinion that the top-level object
|
|
8
|
+
# most likely would be better-off as a self-extended module, and
|
|
9
|
+
# methods defined in it do not automatically get added to the
|
|
10
|
+
# Object class.
|
|
11
|
+
#
|
|
12
|
+
# On the other hand. It is probably best to never use the toplevel
|
|
13
|
+
# except as a jumping in point to youre own namespace.
|
|
14
|
+
#
|
|
15
|
+
# Note that none of this would be needed if Main were just a self extended
|
|
16
|
+
# module.
|
|
17
|
+
|
|
18
|
+
def_meth = Proc.new do |m|
|
|
19
|
+
if /=$/ =~ m.to_s
|
|
20
|
+
eval <<-END
|
|
21
|
+
def self.#{m}(arg)
|
|
22
|
+
Object.class_eval do
|
|
23
|
+
#{m}(arg)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
END
|
|
27
|
+
else
|
|
28
|
+
eval <<-END
|
|
29
|
+
def self.#{m}( *args, &block )
|
|
30
|
+
Object.class_eval do
|
|
31
|
+
#{m}( *args, &block )
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
END
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
public
|
|
39
|
+
|
|
40
|
+
(Module.public_instance_methods - public_methods).each do |m|
|
|
41
|
+
next if m == "initialize"
|
|
42
|
+
next if m =~ /^\W+$/
|
|
43
|
+
def_meth[m]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
(Module.private_instance_methods - private_methods).each do |m|
|
|
49
|
+
next if m == "initialize"
|
|
50
|
+
next if m =~ /^\W+$/
|
|
51
|
+
def_meth[m]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
protected
|
|
55
|
+
|
|
56
|
+
(Module.protected_instance_methods - protected_methods).each do |m|
|
|
57
|
+
next if m == "initialize"
|
|
58
|
+
next if m =~ /^\W+$/
|
|
59
|
+
def_meth[m]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Copyright (c) 2006 Thomas Sawyer (Ruby License)
|
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: main_like_module
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Trans
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '13'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '13'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: minitest
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5'
|
|
40
|
+
description: A small demonstration library that fills in the missing parts of Ruby's
|
|
41
|
+
toplevel object so that it behaves like a fully functional module context.
|
|
42
|
+
email:
|
|
43
|
+
- transfire@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- HISTORY.md
|
|
49
|
+
- LICENSE.txt
|
|
50
|
+
- README.md
|
|
51
|
+
- lib/main_like_module.rb
|
|
52
|
+
homepage: https://github.com/rubyworks/main_like_module
|
|
53
|
+
licenses:
|
|
54
|
+
- BSD-2-Clause
|
|
55
|
+
metadata: {}
|
|
56
|
+
rdoc_options: []
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '3.1'
|
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
requirements: []
|
|
70
|
+
rubygems_version: 3.6.9
|
|
71
|
+
specification_version: 4
|
|
72
|
+
summary: Completes the toplevel proxy of the Object class.
|
|
73
|
+
test_files: []
|