named_imports 0.2.0 → 0.2.1

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: cfc9a0d0b8e5f11fc766551ff061c718b590fe78c8a0d389731899953a38aadc
4
- data.tar.gz: 11e50d338348cc168e96fc314bc41981407c3483a5d78db814ed4bb61cd5543e
3
+ metadata.gz: bb15d783f22496923d8eb7770bc934347bbd84a0019fad85380e3e43a29361d7
4
+ data.tar.gz: fb90680e03f17db882f40cd38e93e3d51c729177d90e67bc7b6f5d82fd99fe8d
5
5
  SHA512:
6
- metadata.gz: 01572a85739777dca2eb49050098e9e07ca7c93f7ba7a5716442bc36690e6a6c296b26ed2a1c8b473f68c6ea4097ae67d860a9850a19dadeb58b60b61ef617a7
7
- data.tar.gz: 0074367a1fddfb4b935026e0604ca80555d1bcaf6e6cf5c574dfbf9865ccac6e6ac8462271cc27218c30955300cfb80ad4cd08705d5169c34ca43b2860edb80a
6
+ metadata.gz: 498ea8dd7a20b2e9a5070e2edfa97b31911b471fa93fe28800ad076f69b031b1132ea5bc65d12db206b52c5bb0dd1903dd41ff611fa1b637cb5aa16947bf0445
7
+ data.tar.gz: 88c609ed4e0649b998752474c78d6496978b1adc5144fe294a0ed770561f0120668977c75885fe8e605748f22a137f08b3d2541becd23e8ab2a92d1c2a9db8f1
data/lib/named_imports.rb CHANGED
@@ -1,79 +1,101 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "named_imports/version"
4
+ require_relative "named_imports/error"
4
5
 
5
6
  module NamedImports
6
- class Error < StandardError; end
7
-
8
7
  class << self
9
- def from(path, constants, context = Object)
10
- path_where_import_occurs = caller(2..2).first
11
- full_path = full_path_for_import(path, path_where_import_occurs)
8
+ def from(raw_path, constant_names, context = Object)
9
+ into_path, import_line = caller_path_and_line
10
+ from_path = full_path_for_import(from_path: raw_path, into_path: into_path)
11
+ sandbox = sandbox_eval(file_path: from_path)
12
+ load_constants(constant_names, from_sandbox: sandbox, into_context: context)
13
+ end
12
14
 
13
- File.open(full_path, "r") do |file|
14
- file_content = file.read
15
- anon_mod = Module.new
15
+ def import(context = Object, &block)
16
+ into_path, import_line = caller_path_and_line
17
+ constant_names = []
16
18
 
17
- anon_mod.define_singleton_method(:from) do |path, constants|
18
- NamedImports.from(path, constants, anon_mod)
19
- end
19
+ begin
20
+ block.call
21
+ rescue => e
22
+ constant_name = constant_name_from_error(e)
20
23
 
21
- anon_mod.define_singleton_method(:import) do |&block|
22
- NamedImports.import(&block)
24
+ if constant_name.nil?
25
+ raise NamedImports::Error::ImportBlockError.new(into_path, import_line, e)
23
26
  end
24
27
 
25
- anon_mod.class_eval(file_content, full_path)
26
-
27
- constants.each do |constant_name|
28
- context.send(:remove_const, constant_name) if context.const_defined?(constant_name, false)
29
- context.const_set(constant_name, anon_mod.const_get(constant_name))
28
+ if !constant_names.include?(constant_name)
29
+ constant_names << constant_name
30
+ context.const_set(constant_name, nil)
30
31
  end
32
+
33
+ retry
31
34
  end
35
+
36
+ constant_names
32
37
  end
33
38
 
34
- def import(context = Object)
35
- constants = []
39
+ def make_named_imports_available(in_context: Object)
40
+ def_method = in_context == Object ? :define_method : :define_singleton_method
36
41
 
37
- begin
38
- yield
39
- rescue NameError => e
40
- raise e unless /uninitialized constant /.match?(e.message)
42
+ in_context.send(def_method, :from) do |path, constants|
43
+ NamedImports.from(path, constants, in_context)
44
+ end
41
45
 
42
- constant_match = e.message.match(/uninitialized constant (?:#<Module:[^>]+>::)?(\w*(?:::\w+)*)$/)
43
- constant_name = constant_match && constant_match[1]
46
+ in_context.send(def_method, :import) do |&block|
47
+ NamedImports.import(in_context, &block)
48
+ end
49
+ end
44
50
 
45
- if constant_name.nil?
46
- raise NameError, "unable to import constant: #{e.message.gsub(/uninitialized constant /, '')}"
47
- end
51
+ private
48
52
 
49
- already_there = constants.include?(constant_name)
53
+ def caller_path_and_line
54
+ caller_path_info = caller(3..3).first
55
+ caller_path_info.match(/\A(.+?):(\d+):/)[1..2]
56
+ end
50
57
 
51
- unless already_there
52
- constants << constant_name
53
- context.const_set(constant_name, nil)
54
- end
58
+ def full_path_for_import(from_path:, into_path:)
59
+ ext = %r{[^/]+\.[^/]+$}.match?(from_path) ? "" : ".rb"
60
+ from_path_with_ext = "#{from_path}#{ext}"
61
+ importer_dir = File.dirname(into_path)
62
+ File.expand_path(from_path_with_ext, importer_dir)
63
+ end
55
64
 
56
- retry
57
- end
65
+ def catchable_missing_constant_error?(error)
66
+ error.is_a?(NameError) && error.message =~ /uninitialized constant /
67
+ end
58
68
 
59
- constants
69
+ def constant_name_from_error(error)
70
+ return unless error.is_a?(NameError)
71
+ constant_matcher = /uninitialized constant (?:#<Module:[^>]+>::)?(\w*(?:::\w+)*)$/
72
+ constant_match = error.message.match(constant_matcher)
73
+ constant_match && constant_match[1]
60
74
  end
61
75
 
62
- private
76
+ def read_content(file_path:)
77
+ File.open(file_path, &:read)
78
+ end
63
79
 
64
- def full_path_for_import(imported_path, importer_path)
65
- ext = %r{[^/]+\.[^/]+$}.match?(imported_path) ? "" : ".rb"
66
- imported_path_with_ext = "#{imported_path}#{ext}"
67
- importer_dir = File.dirname(importer_path)
68
- File.expand_path(imported_path_with_ext, importer_dir)
80
+ def sandbox_eval(file_path:)
81
+ sandbox_module = Module.new
82
+ make_named_imports_available(in_context: sandbox_module)
83
+ ruby_code = read_content(file_path: file_path)
84
+ sandbox_module.class_eval(ruby_code, file_path)
85
+ sandbox_module
69
86
  end
70
- end
71
87
 
72
- Object.define_method(:from) do |path, constants|
73
- NamedImports.from(path, constants)
74
- end
88
+ def load_constants(constant_names, from_sandbox:, into_context:)
89
+ constant_names.each do |constant_name|
90
+ if into_context.const_defined?(constant_name, false)
91
+ into_context.send(:remove_const, constant_name)
92
+ end
75
93
 
76
- Object.define_method(:import) do |&block|
77
- NamedImports.import(&block)
94
+ sandboxed_constant = from_sandbox.const_get(constant_name)
95
+ into_context.const_set(constant_name, sandboxed_constant)
96
+ end
97
+ end
78
98
  end
99
+
100
+ make_named_imports_available
79
101
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NamedImports
4
+ module Error
5
+ class Base < ::StandardError
6
+ end
7
+
8
+ class ImportBlockError < NamedImports::Error::Base
9
+ def initialize(importer_file, importer_line, original_error = nil)
10
+ error_details = []
11
+ error_details << "Something went wrong when evaluating the named import at #{importer_file}:#{importer_line}."
12
+ error_details << "This is likely to be an error in your 'import' block."
13
+ if original_error
14
+ original_error_class = original_error.class.to_s
15
+ article = original_error_class[0].match(/aeiou/i) ? "an" : "a"
16
+ error_details << "The original error was #{article} #{original_error_class}: #{original_error.message}"
17
+ end
18
+
19
+ error_message = error_details.join(" ")
20
+ super(error_message)
21
+
22
+ set_backtrace(original_error.backtrace) if original_error
23
+ end
24
+ end
25
+
26
+ class ImportError < ::NameError
27
+ end
28
+
29
+ class ConstantImportError < NamedImports::Error::ImportError
30
+ def initialize(imported_name, imported_file, importer_file, importer_line)
31
+ super("Unable to import #{imported_name} from #{imported_file} (import occurs in #{importer_file}:#{importer_line})")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NamedImports
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: named_imports
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keegan Leitz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-22 00:00:00.000000000 Z
11
+ date: 2021-03-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -156,6 +156,7 @@ files:
156
156
  - bin/console
157
157
  - bin/setup
158
158
  - lib/named_imports.rb
159
+ - lib/named_imports/error.rb
159
160
  - lib/named_imports/version.rb
160
161
  - named_imports.gemspec
161
162
  homepage: https://github.com/kjleitz/named_imports