main_like_module 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77dd7a196964c1966a43660852408ba60eee5a6cea176b286ff822ad602acbf5
4
- data.tar.gz: 1eb7d996917ce87927a4064806efc183a00fb0e5062b1e5708dd7d8bc0577652
3
+ metadata.gz: 1b20df879329c7be3e7e04d3eac2206590db083cd1003c227dd4bded56c29fe8
4
+ data.tar.gz: 63897091aa9d8e8f9d7860529e1eeb4e42594f70ca3db5208298dbd322b455cd
5
5
  SHA512:
6
- metadata.gz: 6f1b021260ba408ab94d9dd0f12f4e103313daf32286554883bda7f5ab0d6783cd1b4aaf900216bf0d753317caeaae244eff911016f821f67ba3b789c7330d82
7
- data.tar.gz: 753e1fd13c10d9f5509a1ce976a73c84911d29f713458b13f9b7a64af74bf383cd9e70cef701f625834995c9982a90b02ac615420e8e9644533fc12462b20ca7
6
+ metadata.gz: 46e8a794dd5815f037f91159a62bb7d8a3a9f4ff68569831a416bc1dbbb54ed03f5547d16765a898acc30f4a984edc75f86c3858cc164cc903db841b2a2ac86f
7
+ data.tar.gz: 0eb79799a209e4798e2a6fa2ed8688183a43cfbf4460fe45a7014409e3daa3b761c516d3b8ae0287832c5c32cf6be175215c99ffefc9444b126c048251157b0d
data/HISTORY.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # RELEASE HISTORY
2
2
 
3
+ ## 0.3.0 / 2026-04-08
4
+
5
+ Fold in the `import`/`import_relative` feature from rubyworks/finder.
6
+
7
+ Changes:
8
+
9
+ * Add `Kernel#import` and `Kernel#import_relative` (require
10
+ `'main_like_module/import'`) as scope-aware alternatives to `require`
11
+ and `require_relative`. They evaluate the loaded script into the
12
+ current scope rather than into the toplevel, providing a partial
13
+ workaround for Problem 1 (toplevel pollution of Object).
14
+ * Update README to document the new feature and explain how the two
15
+ pieces of the library address Ruby's two toplevel design quirks.
16
+
17
+
3
18
  ## 0.2.0 / 2026-04-07
4
19
 
5
20
  Maintenance release modernizing the project.
data/README.md CHANGED
@@ -87,11 +87,14 @@ not a hand-picked subset.
87
87
 
88
88
  ## What This Library Does
89
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.
90
+ This library addresses both problems, though only one of them fully.
91
+
92
+ ### Completing the Toplevel Proxy (Problem 2)
93
+
94
+ ```ruby
95
+ require 'main_like_module'
96
+ ```
93
97
 
94
- The second problem *can* be addressed, and that is what this library does.
95
98
  On `require`, it walks `Module`'s instance methods and, for each one not
96
99
  already available at the toplevel, defines a singleton method on `main`
97
100
  that forwards the call to `Object.class_eval`. The result is that anything
@@ -108,9 +111,45 @@ you can do inside a `module` body, you can now do at the toplevel as well.
108
111
  Public, private, and protected `Module` methods are all proxied with their
109
112
  correct visibility.
110
113
 
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
+ ### A Partial Workaround for Toplevel Pollution (Problem 1)
115
+
116
+ ```ruby
117
+ require 'main_like_module/import'
118
+ ```
119
+
120
+ The parser-level pollution of `Object` cannot be fixed from userland —
121
+ that ship sailed when the Ruby grammar was written. But for code you
122
+ *write yourself*, this library provides `Kernel#import` and
123
+ `Kernel#import_relative` as scope-aware alternatives to `require` and
124
+ `require_relative`. They evaluate the loaded script directly into the
125
+ *current scope* rather than into the toplevel:
126
+
127
+ ```ruby
128
+ require 'main_like_module/import'
129
+
130
+ module MyApp
131
+ import 'helpers' # helpers.rb is evaluated into MyApp,
132
+ # not into Object
133
+ end
134
+
135
+ MyApp.some_helper_method #=> works
136
+ Object.private_instance_methods.include?(:some_helper_method)
137
+ #=> false -- Object stays clean
138
+ ```
139
+
140
+ `import` searches `$LOAD_PATH` the way `require` does. `import_relative`
141
+ resolves its argument relative to the calling file the way
142
+ `require_relative` does. Both raise `LoadError` on miss.
143
+
144
+ This is a *partial* workaround. It only helps for code that uses `import`
145
+ explicitly — toplevel code in scripts you don't control still pollutes
146
+ `Object`. But for organizing your own libraries into clean module
147
+ namespaces without ceremony, it is genuinely useful.
148
+
149
+ `import` was originally part of the `rubyworks/finder` gem, which has
150
+ been folded into `main_like_module` because the two libraries are really
151
+ about the same thing: making Ruby's toplevel/module loading semantics
152
+ behave more sensibly.
114
153
 
115
154
 
116
155
  ## Should You Use This?
@@ -0,0 +1,61 @@
1
+ # Kernel#import and Kernel#import_relative are variants of #require and
2
+ # #require_relative that evaluate the loaded script *into the current scope*
3
+ # instead of into the toplevel. This provides a partial workaround for the
4
+ # fact that toplevel definitions in Ruby pollute Object globally.
5
+ #
6
+ # module MyApp
7
+ # import 'somefile' # somefile's contents are loaded into MyApp,
8
+ # # not into Object
9
+ # end
10
+ #
11
+ # Originally extracted from rubyworks/finder.
12
+
13
+ module Kernel
14
+
15
+ private
16
+
17
+ # Find +feature+ in $LOAD_PATH and evaluate it into the current scope.
18
+ #
19
+ # Unlike #require, definitions in the loaded file are added to whatever
20
+ # scope #import was called from rather than to Object.
21
+ def import(feature)
22
+ file = nil
23
+ if File.file?(feature) && File.absolute_path(feature) == feature
24
+ file = feature
25
+ else
26
+ candidates = feature.end_with?('.rb') ? [feature] : ["#{feature}.rb", feature]
27
+ $LOAD_PATH.each do |dir|
28
+ candidates.each do |c|
29
+ path = File.expand_path(c, dir)
30
+ if File.file?(path)
31
+ file = path
32
+ break
33
+ end
34
+ end
35
+ break if file
36
+ end
37
+ end
38
+ raise LoadError, "no such file to import -- #{feature}" unless file
39
+ instance_eval(::File.read(file), file)
40
+ end
41
+
42
+ # Like #import, but resolves +fname+ relative to the file that called
43
+ # #import_relative (mirroring how #require_relative works).
44
+ def import_relative(fname)
45
+ call = caller.first
46
+ fail "Can't parse #{call}" unless call.rindex(/:\d+(:in [`'].*[`'])?$/)
47
+ path = $`
48
+ if /\A\((.*)\)/ =~ path # eval, etc.
49
+ raise LoadError, "import_relative is called in #{$1}"
50
+ end
51
+ base = File.expand_path(fname, File.dirname(path))
52
+ file = if File.file?(base)
53
+ base
54
+ elsif File.file?("#{base}.rb")
55
+ "#{base}.rb"
56
+ end
57
+ raise LoadError, "no such file to import -- #{base}" unless file
58
+ instance_eval(::File.read(file), file)
59
+ end
60
+
61
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: main_like_module
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trans
@@ -49,6 +49,7 @@ files:
49
49
  - LICENSE.txt
50
50
  - README.md
51
51
  - lib/main_like_module.rb
52
+ - lib/main_like_module/import.rb
52
53
  homepage: https://github.com/rubyworks/main_like_module
53
54
  licenses:
54
55
  - BSD-2-Clause