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.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.md +23 -0
  3. data/LICENSE.txt +23 -0
  4. data/README.md +163 -0
  5. data/lib/main_like_module.rb +62 -0
  6. 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
+ [![Gem Version](https://img.shields.io/gem/v/main_like_module.svg?style=flat)](https://rubygems.org/gems/main_like_module)
7
+ [![Build Status](https://github.com/rubyworks/main_like_module/actions/workflows/test.yml/badge.svg)](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: []