modulation 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00220ad836145e69affe04c22c0a4c699ecb68e7b5598671bf7820834572ce78
4
- data.tar.gz: 0d2546469547d80482952f47ae89fed0996902419d33f51be8b529d65db6283a
3
+ metadata.gz: 2a279ab2f1b9cad5e4056290d045cd644e7637676e73bfcc8b0d6b2b737a92a2
4
+ data.tar.gz: 616806e62a57b0889aae801ea18d852d79fdd0e5829d45c22aa241656aa8e795
5
5
  SHA512:
6
- metadata.gz: f56cc035c0332697d5e7a89dc46c42e880f8dcd214832ec62aa7be26f3b4d329e61cd127f064570b838b0bba768c984f4de1704570c0c79a31fc40c654d55068
7
- data.tar.gz: '04894f9d7878931ae4d2adb3e51888de2e66b593638703242a8b819d681d35e608b5540c0f19f9ded9d01ba808d350360e1fd528dcc34e37e0e30abe6e027018'
6
+ metadata.gz: f3bf1925ce24c315817907e8ea044a8223238bc03871ea5ada5d3368d3e4daaeae5d978a443a4fdb49ece4e8f4c7530b0dab2dfdf0731b46af63b4f2d18fa976
7
+ data.tar.gz: b69caae7916bac12eb51fb448dc7bab4113058643b29d558a34c0da5bbc2557744bedcecfb5939f06d7d63139c31b016a6ab52fad784e2f0242ddfc6e5784028
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 1.2 2022-11-17
2
+
3
+ - Fix `extend_from` to not extend ancestors
4
+
1
5
  ## 1.1 2021-08-18
2
6
 
3
7
  - Add automatic `to_proc` method for callable modules
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
 
@@ -70,7 +70,7 @@ module Modulation
70
70
  method(:call).to_proc
71
71
  end
72
72
  end
73
-
73
+
74
74
  mod
75
75
  end
76
76
  end
@@ -40,7 +40,7 @@ module Modulation
40
40
 
41
41
  case abs_path
42
42
  when String
43
- @loaded_modules[abs_path] || create_module_from_file(abs_path, caller)
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
- # @ param path [String] relative directory path
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] || create_module_from_file(fn, caller)
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
- # @ param path [String] relative directory path
65
- # @ param options [Hash] options
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] || create_module_from_file(fn, caller)
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
- def auto_import_map(path, options = {},
80
- caller_location = caller(CALLER_RANGE).first)
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, path, options)
92
+ h[k] = find_auto_import_module(fn, options)
85
93
  end
86
94
  end
87
95
 
88
- def find_auto_import_module(filename, path, options)
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
- create_module_from_file(filename, caller)
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 #{path}"
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 create_module_from_file(path, import_caller)
116
+ def import_module_from_file(path, import_caller)
103
117
  Builder.make(location: path, caller: import_caller)
104
118
  end
105
119
 
@@ -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.class)
67
+ Modulation::Builder.add_module_methods(mod, self.singleton_class)
68
68
  Modulation::Builder.add_module_constants(mod, self)
69
69
  end
70
70
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Modulation
4
- VERSION = '1.1'
4
+ VERSION = '1.2'
5
5
  end
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.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: 2021-08-18 00:00:00.000000000 Z
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: " Modulation provides an alternative way of organizing your Ruby code.
70
- Modulation\nlets you explicitly import and export declarations in order to better
71
- control \ndependencies in your codebase. Modulation helps you refrain from littering\nthe
72
- global namespace with a myriad modules, or complex multi-level nested\nmodule hierarchies.\n"
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.1.6
125
+ rubygems_version: 3.3.7
124
126
  signing_key:
125
127
  specification_version: 4
126
128
  summary: 'Modulation: explicit dependency management for Ruby'