modulation 1.1 → 1.2
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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +17 -17
- data/lib/modulation/builder.rb +1 -1
- data/lib/modulation/core.rb +28 -14
- data/lib/modulation/ext.rb +1 -1
- data/lib/modulation/version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a279ab2f1b9cad5e4056290d045cd644e7637676e73bfcc8b0d6b2b737a92a2
|
4
|
+
data.tar.gz: 616806e62a57b0889aae801ea18d852d79fdd0e5829d45c22aa241656aa8e795
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3bf1925ce24c315817907e8ea044a8223238bc03871ea5ada5d3368d3e4daaeae5d978a443a4fdb49ece4e8f4c7530b0dab2dfdf0731b46af63b4f2d18fa976
|
7
|
+
data.tar.gz: b69caae7916bac12eb51fb448dc7bab4113058643b29d558a34c0da5bbc2557744bedcecfb5939f06d7d63139c31b016a6ab52fad784e2f0242ddfc6e5784028
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
|
16
16
|
|
17
17
|
Modulation provides an alternative way of organizing your Ruby code. Modulation
|
18
|
-
lets you explicitly import and export declarations in order to better control
|
18
|
+
lets you explicitly import and export declarations in order to better control
|
19
19
|
dependencies in your codebase. Modulation helps you refrain from littering
|
20
20
|
the global namespace with a myriad modules, or complex multi-level nested
|
21
21
|
module hierarchies.
|
@@ -25,10 +25,10 @@ from, and you'll have full control over which parts of a module's code you wish
|
|
25
25
|
to expose to the outside world. Modulation can also help you write Ruby code in
|
26
26
|
a functional style, minimizing boilerplate code.
|
27
27
|
|
28
|
-
> Note: Modulation is not a replacement for RubyGems. Rather, Modulation is
|
28
|
+
> Note: Modulation is not a replacement for RubyGems. Rather, Modulation is
|
29
29
|
> intended for managing dependencies between source files *inside* your Ruby
|
30
|
-
> applications. Though it does support loading gems that were written using
|
31
|
-
> Modulation, it is not intended as a comprehensive solution for using
|
30
|
+
> applications. Though it does support loading gems that were written using
|
31
|
+
> Modulation, it is not intended as a comprehensive solution for using
|
32
32
|
> third-party libraries.
|
33
33
|
|
34
34
|
## Features
|
@@ -61,17 +61,17 @@ issues:
|
|
61
61
|
to any other file in your codebase. All "globals" (classes, modules,
|
62
62
|
constants) are loaded, well, globally, in a single namespace. Name conflicts
|
63
63
|
are easy in Ruby.
|
64
|
-
- To avoid class name conflicts, classes need to be nested under a single
|
64
|
+
- To avoid class name conflicts, classes need to be nested under a single
|
65
65
|
hierarchical tree, sometime reaching 4 levels or more. Just look at Rails.
|
66
66
|
- Since a `#require`d class or module can be loaded in any file and then made
|
67
67
|
available to all files, it's easy to lose track of where it was loaded, and
|
68
68
|
where it is used.
|
69
69
|
- There's no easy way to hide implementation-specific classes or methods. Yes,
|
70
|
-
there's `#private`, `#private_constant` etc, but by default everything is
|
70
|
+
there's `#private`, `#private_constant` etc, but by default everything is
|
71
71
|
`#public`!
|
72
72
|
- Extracting functionality is harder when modules are namespaced and
|
73
73
|
dependencies are implicit.
|
74
|
-
- Writing reusable functional code requires wrapping it in modules using
|
74
|
+
- Writing reusable functional code requires wrapping it in modules using
|
75
75
|
`class << self`, `def self.foo ...`, `extend self` or `include Singleton`
|
76
76
|
(the pain of implementing singletons in Ruby has been
|
77
77
|
[discussed](https://practicingruby.com/articles/ruby-and-the-singleton-pattern-dont-get-along)
|
@@ -83,7 +83,7 @@ issues:
|
|
83
83
|
> "official" API.
|
84
84
|
|
85
85
|
Personally, I have found that managing dependencies with `#require` in large
|
86
|
-
codebases is... not as elegant or painfree as I would expect from a
|
86
|
+
codebases is... not as elegant or painfree as I would expect from a
|
87
87
|
first-class development environment. I also wanted to have a better solution
|
88
88
|
for writing in a functional style.
|
89
89
|
|
@@ -105,12 +105,12 @@ gem 'modulation'
|
|
105
105
|
Modulation builds on the idea of a Ruby `Module` as a
|
106
106
|
["collection of methods and constants"](https://ruby-doc.org/core-2.6.5/Module.html).
|
107
107
|
Using modulation, each Ruby source file becomes a module. Modules usually
|
108
|
-
export method and constant declarations (usually an API for a specific,
|
108
|
+
export method and constant declarations (usually an API for a specific,
|
109
109
|
well-defined functionality) to be shared with other modules. Modules can also
|
110
110
|
import declarations from other modules. Anything not exported remains hidden
|
111
111
|
inside the module and normally cannot be accessed from the outside.
|
112
112
|
|
113
|
-
Each source file is evaluated in the context of a newly-created `Module`
|
113
|
+
Each source file is evaluated in the context of a newly-created `Module`
|
114
114
|
instance, with some additional methods for introspection and miscellaneous
|
115
115
|
operations such as [hot reloading](#reloading-modules).
|
116
116
|
|
@@ -136,7 +136,7 @@ class Session
|
|
136
136
|
end
|
137
137
|
```
|
138
138
|
|
139
|
-
A module may also expose a set of methods without using `class << self`, for
|
139
|
+
A module may also expose a set of methods without using `class << self`, for
|
140
140
|
example when writing in a functional style:
|
141
141
|
|
142
142
|
*seq.rb*
|
@@ -210,7 +210,7 @@ user = Models::User.new(...)
|
|
210
210
|
...
|
211
211
|
```
|
212
212
|
|
213
|
-
Alternatively, a module interested in a single declaration from another module
|
213
|
+
Alternatively, a module interested in a single declaration from another module
|
214
214
|
can use the following technique:
|
215
215
|
|
216
216
|
```ruby
|
@@ -319,7 +319,7 @@ end
|
|
319
319
|
|
320
320
|
### Default exports
|
321
321
|
|
322
|
-
A module may wish to expose just a single class or constant, in which case it
|
322
|
+
A module may wish to expose just a single class or constant, in which case it
|
323
323
|
can use `#export_default`:
|
324
324
|
|
325
325
|
*user.rb*
|
@@ -340,7 +340,7 @@ User.new(...)
|
|
340
340
|
|
341
341
|
The default exported value can also be defined directly thus:
|
342
342
|
|
343
|
-
*config.rb*
|
343
|
+
*config.rb*
|
344
344
|
```ruby
|
345
345
|
export_default(
|
346
346
|
host: 'localhost',
|
@@ -388,7 +388,7 @@ end
|
|
388
388
|
|
389
389
|
### Accessing the global namespace
|
390
390
|
|
391
|
-
If you need to access the global namespace inside a module just prefix the
|
391
|
+
If you need to access the global namespace inside a module just prefix the
|
392
392
|
class name with double colons:
|
393
393
|
|
394
394
|
```ruby
|
@@ -449,7 +449,7 @@ gem called [eg](https://github.com/digital-fabric/eg/).
|
|
449
449
|
### Unit testing modules
|
450
450
|
|
451
451
|
Methods and constants that are not exported can be tested using the `#__expose!`
|
452
|
-
method. Thus you can keep implementation details hidden, while being able to
|
452
|
+
method. Thus you can keep implementation details hidden, while being able to
|
453
453
|
easily test them:
|
454
454
|
|
455
455
|
*parser.rb*
|
@@ -583,7 +583,7 @@ end
|
|
583
583
|
> it without even being aware it was reloaded, providing its API has not
|
584
584
|
> changed.
|
585
585
|
|
586
|
-
Reloading of modules with default exports is also possible. Modulation will
|
586
|
+
Reloading of modules with default exports is also possible. Modulation will
|
587
587
|
extend the exported value with a `#__reload!` method. The value will need to be
|
588
588
|
reassigned:
|
589
589
|
|
data/lib/modulation/builder.rb
CHANGED
data/lib/modulation/core.rb
CHANGED
@@ -40,7 +40,7 @@ module Modulation
|
|
40
40
|
|
41
41
|
case abs_path
|
42
42
|
when String
|
43
|
-
@loaded_modules[abs_path] ||
|
43
|
+
@loaded_modules[abs_path] || import_module_from_file(abs_path, caller)
|
44
44
|
when :require_gem
|
45
45
|
raise_error(LoadError.new(GEM_REQUIRE_ERROR_MESSAGE), caller)
|
46
46
|
else
|
@@ -49,20 +49,22 @@ module Modulation
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Imports all source files in given directory
|
52
|
-
#
|
52
|
+
#
|
53
|
+
# @param path [String] relative directory path
|
53
54
|
# @param caller_location [String] caller location
|
54
55
|
# @return [Array] array of module objects
|
55
56
|
def import_all(path, caller_location = caller(CALLER_RANGE).first)
|
56
57
|
abs_path = Paths.absolute_dir_path(path, caller_location)
|
57
58
|
Dir["#{abs_path}/**/*.rb"].map do |fn|
|
58
|
-
@loaded_modules[fn] ||
|
59
|
+
@loaded_modules[fn] || import_module_from_file(fn, caller)
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
62
63
|
# Imports all source files in given directory, returning a hash mapping
|
63
|
-
# filenames to modules
|
64
|
-
#
|
65
|
-
# @
|
64
|
+
# filenames to modules.
|
65
|
+
#
|
66
|
+
# @param path [String] relative directory path
|
67
|
+
# @param options [Hash] options
|
66
68
|
# @param caller_location [String] caller location
|
67
69
|
# @return [Hash] hash mapping filenames to modules
|
68
70
|
def import_map(path, options = {},
|
@@ -70,36 +72,48 @@ module Modulation
|
|
70
72
|
abs_path = Paths.absolute_dir_path(path, caller_location)
|
71
73
|
use_symbols = options[:symbol_keys]
|
72
74
|
Dir["#{abs_path}/*.rb"].each_with_object({}) do |fn, h|
|
73
|
-
mod = @loaded_modules[fn] ||
|
75
|
+
mod = @loaded_modules[fn] || import_module_from_file(fn, caller)
|
74
76
|
name = File.basename(fn) =~ /^(.+)\.rb$/ && Regexp.last_match(1)
|
75
77
|
h[use_symbols ? name.to_sym : name] = mod
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
79
|
-
|
80
|
-
|
81
|
+
# Returns a hash mapping filenames to modules, with modules being imported upon
|
82
|
+
# This allows an application to load dependencies only when they are needed.
|
83
|
+
#
|
84
|
+
# @param path [String] relative directory path
|
85
|
+
# @param options [Hash] options
|
86
|
+
# @param caller_location [String] caller location
|
87
|
+
# @return [Hash] hash mapping filenames to modules
|
88
|
+
def auto_import_map(path, options = {}, caller_location = caller(CALLER_RANGE).first)
|
81
89
|
abs_path = Paths.absolute_dir_path(path, caller_location)
|
82
90
|
Hash.new do |h, k|
|
83
91
|
fn = Paths.check_path(File.join(abs_path, k.to_s))
|
84
|
-
h[k] = find_auto_import_module(fn,
|
92
|
+
h[k] = find_auto_import_module(fn, options)
|
85
93
|
end
|
86
94
|
end
|
87
95
|
|
88
|
-
|
96
|
+
# Imports and returns the given filename. If the module does not exist,
|
97
|
+
# returns the value in `options[:not_found]`, otherwise raises an error.
|
98
|
+
#
|
99
|
+
# @param filename [String] filename
|
100
|
+
# @param options [Hash] hash of options
|
101
|
+
# @return [Module] imported module
|
102
|
+
def find_auto_import_module(filename, options)
|
89
103
|
if filename
|
90
104
|
return @loaded_modules[filename] ||
|
91
|
-
|
105
|
+
import_module_from_file(filename, caller)
|
92
106
|
end
|
93
107
|
|
94
108
|
return options[:not_found] if options.key?(:not_found)
|
95
109
|
|
96
|
-
raise "Module not found #{
|
110
|
+
raise "Module not found #{filename}"
|
97
111
|
end
|
98
112
|
|
99
113
|
# Creates a new module from a source file
|
100
114
|
# @param path [String] source file name
|
101
115
|
# @return [Module] module
|
102
|
-
def
|
116
|
+
def import_module_from_file(path, import_caller)
|
103
117
|
Builder.make(location: path, caller: import_caller)
|
104
118
|
end
|
105
119
|
|
data/lib/modulation/ext.rb
CHANGED
@@ -64,7 +64,7 @@ class Module
|
|
64
64
|
# @return [void]
|
65
65
|
def extend_from(path)
|
66
66
|
mod = import(path, caller(CALLER_RANGE).first)
|
67
|
-
Modulation::Builder.add_module_methods(mod, self.
|
67
|
+
Modulation::Builder.add_module_methods(mod, self.singleton_class)
|
68
68
|
Modulation::Builder.add_module_constants(mod, self)
|
69
69
|
end
|
70
70
|
|
data/lib/modulation/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: modulation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,10 +66,12 @@ dependencies:
|
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 4.0.1
|
69
|
-
description:
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
description: |2
|
70
|
+
Modulation provides an alternative way of organizing your Ruby code. Modulation
|
71
|
+
lets you explicitly import and export declarations in order to better control
|
72
|
+
dependencies in your codebase. Modulation helps you refrain from littering
|
73
|
+
the global namespace with a myriad modules, or complex multi-level nested
|
74
|
+
module hierarchies.
|
73
75
|
email: ciconia@gmail.com
|
74
76
|
executables:
|
75
77
|
- mdl
|
@@ -120,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
122
|
- !ruby/object:Gem::Version
|
121
123
|
version: '0'
|
122
124
|
requirements: []
|
123
|
-
rubygems_version: 3.
|
125
|
+
rubygems_version: 3.3.7
|
124
126
|
signing_key:
|
125
127
|
specification_version: 4
|
126
128
|
summary: 'Modulation: explicit dependency management for Ruby'
|