modulation 0.11 → 0.12

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: c196472c3122b723a0d76849c604cb6b8ce13cc2613cb418bf8ecbf9c794bbe8
4
- data.tar.gz: cd027cc00a894ed92c43db09570ee301b82efb002d410d6daf9a584aca8c8357
3
+ metadata.gz: 72f329f0720d40aea09237c6cce4dc9eeb5ccfd3a94d78ae7d9ce956ea016051
4
+ data.tar.gz: 1541fdfbef9bec5ab3b7f6ed14a5eb7445f01b9c240baaf89b77959b9e0ffcfe
5
5
  SHA512:
6
- metadata.gz: 9c4d82cb9ae8ea84364dffbdd56603bf7db864d4254a587545436c983e157d315068cde11e51fbfec84cb8db08382769506e8f5273f70a64f4440d56150b18f4
7
- data.tar.gz: bf00f6e03d79ce2a2c77322aee2f433780989030cceffc031421dcec419c1f9e7bf4a9c8cf178864942e12ceb713760e4b5634a430b5b21e5840e342608d151a
6
+ metadata.gz: bb267ba2a1a29119346c0ccd9d001f77614945a43c4e7006b5d4900f9de9812e4afff393ec3eb7c1ffb9bf5e2ba539ef51e4811bbd203b08e66602711bb57856
7
+ data.tar.gz: 5fbb09abc67eaa52d345f3c9d828a46a1bcd0b77f7a36317e9d2c81e5465009bc537df80ce437c1ed64ca01e61cf1883381531206d60856a916c9a6d73562c1b
data/CHANGELOG.md ADDED
@@ -0,0 +1,56 @@
1
+ ## [0.12] 2018-08-20
2
+
3
+ * Fix sanitizing of error backtrace.
4
+ * Fix importing of gems.
5
+
6
+ ## [0.11] 2018-08-20
7
+
8
+ * Add Modulation.mock for use in testing.
9
+
10
+ ## [0.10] 2018-08-19
11
+
12
+ * Refactor and cleanup code.
13
+
14
+ ## [0.9.1] 2018-08-15
15
+
16
+ * Small fixes to README.
17
+
18
+ ## [0.9] 2018-08-13
19
+
20
+ * Add support for module reloading.
21
+
22
+ ## [0.8] 2018-08-05
23
+
24
+ * Add support for nested namespaces.
25
+ * Add support for circular dependencies.
26
+
27
+ ## [0.7] 2018-07-29
28
+
29
+ * Add `MODULE` constant for accessing module from nested namespaces within itself
30
+
31
+ ## [0.6] 2018-07-23
32
+
33
+ * Add support for using gems as imported modules (experimental feature)
34
+ * Add Modulation.full_trace! method for getting full backtrace on errors
35
+ * Fix Modulation.transform_export_default_value
36
+ * Change name to *Modulation*
37
+
38
+ ## [0.5.1] 2018-07-20
39
+
40
+ * Fix extend_from, include_from to work with ruby 2.4
41
+
42
+ ## [0.5] 2018-07-19
43
+
44
+ * Add extend_from, include_from to include imported methods in classes and modules
45
+
46
+ ## [0.4] 2018-07-19
47
+
48
+ * Refactor code
49
+ * Add tests
50
+ * Remove namespace feature (owing to the way Ruby handles constants in blocks)
51
+
52
+ ## [0.3.3] 2018-07-09
53
+
54
+ * Switch to explicit exports
55
+ * More documentation
56
+ * Better error handling
data/README.md CHANGED
@@ -393,14 +393,17 @@ end
393
393
 
394
394
  Gems written using modulation can also be loaded using `import`. If modulation
395
395
  does not find the module specified by the given relative path, it will attempt
396
- to load a gem by the same name.
397
-
398
- > **Note**: using `import` to load a gem is very much *experimental*, and might
399
- > introduce problems not encountered when loading with `require` such as
400
- > shadowing of global namespaces, or any other bizarre and unexpected
401
- > behaviors. Actually, there's not much point in using it to load a gem which
402
- > does not use Modulation. When loading gems using import, Modulation will
403
- > raise an exception if no symbols were exported by the gem.
396
+ to load a gem by the same name. It is also possible to load specific files
397
+ inside modules by specifying a sub-path:
398
+
399
+ ```ruby
400
+ require 'modulation'
401
+ MyFeature = import 'my_gem/my_feature'
402
+ ```
403
+
404
+ > **Note**: Since there's not much of a point in `import`ing gems that do not use
405
+ > Modulation to export symbols, Modulation will refuse to import any gem that
406
+ > does not depend on Modulation.
404
407
 
405
408
  ## Coding style recommendations
406
409
 
data/lib/modulation.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Modulation
4
+ DIR = File.dirname(__FILE__)
5
+ end
6
+
3
7
  require_relative 'modulation/ext'
4
8
  require_relative 'modulation/core'
@@ -26,14 +26,27 @@ module Modulation
26
26
  @full_backtrace = true
27
27
  end
28
28
 
29
+ GEM_REQUIRE_ERROR_MESSAGE = <<~EOF
30
+ Can't import from a gem that doesn't depend on Modulation. Please use `require` instead of `import`.
31
+ EOF
32
+
29
33
  # Imports a module from a file
30
34
  # If the module is already loaded, returns the loaded module.
31
35
  # @param path [String] unqualified file name
32
36
  # @param caller_location [String] caller location
33
37
  # @return [Module] loaded module object
34
38
  def import(path, caller_location = caller(1..1).first)
35
- path = Paths.absolute_path(path, caller_location)
36
- @loaded_modules[path] || create_module_from_file(path)
39
+ abs_path = Paths.absolute_path(path, caller_location) ||
40
+ Paths.lookup_gem_path(path)
41
+
42
+ case abs_path
43
+ when String
44
+ @loaded_modules[abs_path] || create_module_from_file(abs_path)
45
+ when :require_gem
46
+ raise_error(LoadError.new(GEM_REQUIRE_ERROR_MESSAGE), caller)
47
+ else
48
+ raise_error(LoadError.new("Module not found: #{path}"), caller)
49
+ end
37
50
  end
38
51
 
39
52
  # Creates a new module from a source file
@@ -42,16 +55,21 @@ module Modulation
42
55
  def create_module_from_file(path)
43
56
  Builder.make(location: path)
44
57
  rescue StandardError => e
45
- @full_backtrace ? raise : raise_with_clean_backtrace(e)
58
+ raise_error(e)
46
59
  end
47
60
 
48
- # (Re-)raises an error, filtering its backtrace to remove stack frames
49
- # occuring in Modulation code
61
+ # (Re-)raises an error, potentially filtering its backtrace to remove stack
62
+ # frames occuring in Modulation code
50
63
  # @param error [Error] raised error
64
+ # @param caller [Array] error backtrace
51
65
  # @return [void]
52
- def raise_with_clean_backtrace(error)
53
- backtrace = error.backtrace.reject { |l| l.include?(__FILE__) }
54
- raise(error, error.message, backtrace)
66
+ def raise_error(error, caller = error.backtrace)
67
+ if @full_backtrace
68
+ error.set_backtrace(caller)
69
+ else
70
+ error.set_backtrace(caller.reject { |l| l =~ /^#{Modulation::DIR}/ })
71
+ end
72
+ raise error
55
73
  end
56
74
 
57
75
  # Reloads the given module from its source file
@@ -70,7 +88,7 @@ module Modulation
70
88
  mod.tap { Builder.set_exported_symbols(mod, mod.__exported_symbols, true) }
71
89
  end
72
90
 
73
- # Maps the given path to the given mock module, restoring the previously
91
+ # Maps the given path to the given mock module, restoring the previously
74
92
  # loaded module (if any) after calling the given block
75
93
  # @param path [String] module path
76
94
  # @param mod [Module] module
@@ -13,14 +13,12 @@ module Modulation
13
13
  # @param path [String] unqualified file name
14
14
  # @param caller_location [String] caller location
15
15
  # @return [String] absolute file name
16
- def absolute_path(path, caller_location = caller(1..1).first)
17
- orig_path = path
16
+ def absolute_path(path, caller_location)
18
17
  caller_file = caller_location[CALLER_FILE_REGEXP, 1]
19
- raise 'Could not expand path' unless caller_file
18
+ return nil unless caller_file
20
19
 
21
20
  path = File.expand_path(path, File.dirname(caller_file))
22
- check_path(path) || lookup_gem(orig_path) ||
23
- (raise "Module not found: #{path}")
21
+ check_path(path)
24
22
  end
25
23
 
26
24
  # Checks that the given path references an existing file, adding the .rb
@@ -35,19 +33,44 @@ module Modulation
35
33
  end
36
34
  end
37
35
 
38
- # Resolves the provided file name into a gem. If no gem is found, returns
39
- # nil
36
+ GEM_NAME_RE = /^([^\/]+)/
37
+
38
+ # Resolves the provided path by looking for a corresponding gem. If no gem
39
+ # is found, returns nil. If the corresponding gem does not use modulation,
40
+ # returns :require_gem, which signals that the gem must be required.
40
41
  # @param name [String] gem name
41
- # @return [String] absolute path to gem main source file
42
- def lookup_gem(name)
43
- spec = Gem::Specification.find_by_name(name)
44
- unless spec.dependencies.map(&:name).include?('modulation')
45
- raise NameError, 'Cannot import gem not based on modulation'
42
+ # @return [String, Symbol] absolute path or :require_gem
43
+ def lookup_gem_path(name)
44
+ gem = name[GEM_NAME_RE, 1] || name
45
+ spec = Gem::Specification.find_by_name(gem)
46
+
47
+ if gem_uses_modulation?(spec)
48
+ find_gem_based_path(spec, name)
49
+ else
50
+ :require_gem
46
51
  end
47
- path = File.join(spec.full_require_paths, "#{name}.rb")
48
- File.file?(path) ? path : nil
49
52
  rescue Gem::MissingSpecError
50
53
  nil
51
54
  end
55
+
56
+ # Returns true if given gemspec depends on modulation, which means it can
57
+ # be loaded using `import`
58
+ # @param gemspec [Gem::Specification] gem spec
59
+ # @return [Boolean] does gem depend on modulation?
60
+ def gem_uses_modulation?(gemspec)
61
+ gemspec.dependencies.map(&:name).include?('modulation')
62
+ end
63
+
64
+ # Finds full path for gem file based on gem's require paths
65
+ # @param gemspec [Gem::Specification] gem spec
66
+ # @param path [String] given import path
67
+ # @return [String] full path
68
+ def find_gem_based_path(gemspec, path)
69
+ gemspec.full_require_paths.each do |p|
70
+ full_path = check_path(File.join(p, path))
71
+ return full_path if full_path
72
+ end
73
+ nil
74
+ end
52
75
  end
53
76
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modulation
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.11'
4
+ version: '0.12'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -9,7 +9,35 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2018-08-20 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 5.11.3
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 5.11.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 4.0.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 4.0.1
13
41
  description: "Modulation provides an better way to organize Ruby code. Modulation
14
42
  lets you \nexplicitly import and export declarations in order to better control
15
43
  \ndependencies in your codebase. Modulation helps you refrain from littering\nthe
@@ -19,7 +47,9 @@ executables: []
19
47
  extensions: []
20
48
  extra_rdoc_files:
21
49
  - README.md
50
+ - CHANGELOG.md
22
51
  files:
52
+ - CHANGELOG.md
23
53
  - README.md
24
54
  - lib/modulation.rb
25
55
  - lib/modulation/builder.rb