autoloaded 1.0.0 → 1.1.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +25 -0
  4. data/License.md +1 -1
  5. data/README.md +25 -6
  6. data/autoloaded.gemspec +1 -1
  7. data/lib/autoloaded.rb +101 -4
  8. data/lib/autoloaded/constant.rb +22 -2
  9. data/lib/autoloaded/refine.rb +1 -0
  10. data/lib/autoloaded/version.rb +2 -1
  11. data/spec/autoloaded/constant_spec.rb +13 -5
  12. data/spec/autoloaded_spec.rb +107 -23
  13. data/spec/fixtures/autoloaded_with_conventional_filename.rb +10 -0
  14. data/spec/fixtures/{namespace_that_is_autoloaded_conventionally → autoloaded_with_conventional_filename}/N-est-ed.rb +0 -0
  15. data/spec/fixtures/{namespace_that_is_autoloaded_conventionally → autoloaded_with_conventional_filename}/nest_ed.rb +0 -0
  16. data/spec/fixtures/autoloaded_with_conventional_filename/nested.rb +5 -0
  17. data/spec/fixtures/autoloaded_with_conventional_filename/old_school_autoload.rb +5 -0
  18. data/spec/fixtures/autoloaded_with_conventional_filename_only.rb +10 -0
  19. data/spec/fixtures/autoloaded_with_conventional_filename_only/nested.rb +5 -0
  20. data/spec/fixtures/autoloaded_with_conventional_filename_only/old_school_autoload.rb +5 -0
  21. data/spec/fixtures/autoloaded_with_unconventional_filenames.rb +10 -0
  22. data/spec/fixtures/autoloaded_with_unconventional_filenames/N-est-ed.rb +5 -0
  23. data/spec/fixtures/{namespace_that_is_autoloaded_unconventionally → autoloaded_with_unconventional_filenames}/nest_ed.rb +0 -0
  24. data/spec/fixtures/autoloaded_with_unconventional_filenames/old_school_autoload.rb +5 -0
  25. data/spec/fixtures/not_autoloaded.rb +5 -0
  26. data/spec/fixtures/not_autoloaded/old_school_autoload.rb +5 -0
  27. data/spec/matchers.rb +79 -37
  28. data/spec/support/util.rb +43 -0
  29. data/spec/support/without_side_effects.rb +1 -1
  30. metadata +34 -22
  31. data/History.markdown +0 -9
  32. data/spec/fixtures/namespace_that_is_autoloaded_conventionally.rb +0 -7
  33. data/spec/fixtures/namespace_that_is_autoloaded_conventionally/nested.rb +0 -5
  34. data/spec/fixtures/namespace_that_is_autoloaded_unconventionally.rb +0 -7
  35. data/spec/fixtures/namespace_that_is_autoloaded_unconventionally/N-est-ed.rb +0 -5
  36. data/spec/fixtures/namespace_that_is_not_autoloaded.rb +0 -1
  37. data/spec/fixtures/namespace_that_is_not_autoloaded/nested.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 87da530be678da00a0d6d8df5ef7d6ca2c773f04
4
- data.tar.gz: a97be0b94d97e9a251cab40509e61c49e65f97e1
3
+ metadata.gz: b7221b689c0211923b999e3da078512a464179c5
4
+ data.tar.gz: f15a23ea558e0064748939779f3d4c3891bfc455
5
5
  SHA512:
6
- metadata.gz: 5ae33778e6eb8560c101c5a44f995e321b3963e96721fc22b69c55eaa69b6648d9921080e44842f7c6f9460e151376ee8bc39dda02541630a938eb9c426e2d52
7
- data.tar.gz: c745087aa0cd848990c3c222d475559e4d27fe86c9f86eeffd50941df790d75dfd15dd0cb15b2b91a4270d5112bd5cae32de78024a27f9c0af9d9ced11609cbb
6
+ metadata.gz: b9d88de3898d6351c0367c76cde73ed73661b8753ed17b60d9ba592c2c306585c1f79f4875713b3351dd12c0a450e721f51b359087af90c178beb93bada66975
7
+ data.tar.gz: 975e195c4b69254dc9a47945af0e78ec696b5f784f3e6f613cece9343da983944912af173f7fb8ad02aa5f95a50a3b063a42bce6e30dc9bf6571758f3c52c3a9
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --file License.md --no-private --protected --title "Autoloaded"
1
+ --file History.md --file License.md --no-private --protected --title "Autoloaded"
@@ -0,0 +1,25 @@
1
+ # Version history for the _Autoloaded_ project
2
+
3
+ ## <a name="v1.1.0"></a>v1.1.0, Tue 11/04/2013
4
+
5
+ * Correct/improve autoload behavior
6
+ * Instead of returning the source **directory** path from
7
+ [_Module#autoload?_][Ruby-Core-Module-autoload], return one or more matching
8
+ source **file** path(s)
9
+ * Use Ruby load path (`$:`) to handle relative source file paths
10
+ * Explain _Module#autoload?_ and
11
+ [_Module#constants_][Ruby-Core-Module-constants] in the [readme][readme] and
12
+ in [inline documentation][inline-documentation]
13
+
14
+ ## <a name="v1.0.0"></a>v1.0.0, Wed 10/29/2013
15
+
16
+ * Add support for Ruby v2.0
17
+
18
+ ## <a name="v0.0.3"></a>v0.0.3, Fri 10/24/2014
19
+
20
+ (First release)
21
+
22
+ [Ruby-Core-Module-autoload]: http://ruby-doc.org/core/Module.html#method-i-autoload-3F "‘Module#autoload’ method in the Ruby Core Library"
23
+ [Ruby-Core-Module-constants]: http://ruby-doc.org/core/Module.html#method-i-constants "‘Module#constants’ method in the Ruby Core Library"
24
+ [readme]: http://github.com/njonsson/autoloaded/blob/master/README.md "Autoloaded readme"
25
+ [inline-documentation]: http://www.rubydoc.info/github/njonsson/autoloaded "Autoloaded inline documentation"
data/License.md CHANGED
@@ -1,4 +1,4 @@
1
- # The MIT License
1
+ # The MIT License
2
2
 
3
3
  Source code for _Autoloaded_ is Copyright © 2014 [Nils Jonsson][mail] and
4
4
  [contributors][contributors].
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Autoloaded graphic]][spider-gear-image]
1
+ [![Autoloaded graphic]][spider-gear-image]
2
2
 
3
3
  # Autoloaded
4
4
 
@@ -70,12 +70,27 @@ module MyAwesomeGem
70
70
  end
71
71
  ```
72
72
 
73
- Note that your preferred casing of constants is accommodated automatically:
73
+ Note that your preferred casing of constants is accommodated automatically.
74
74
 
75
75
  ```ruby
76
- MyAwesomeGem::DB::MySQL.new
77
- MyAwesomeGem::DB::PostgreSQL.new
78
- MyAwesomeGem::DB::SQLServer.new
76
+ # Unlike Kernel#autoload and Module#autoload, Autoloaded is not clairvoyant about
77
+ # what constants will be autoloaded.
78
+ MyAwesomeGem::DB.constants # => []
79
+
80
+ # But like Kernel#autoload and Module#autoload, Autoloaded does tell you which
81
+ # source files will be autoloaded. (The difference is that it may return an array
82
+ # of potential matches instead of just one filename.)
83
+ MyAwesomeGem::DB.autoload? :MySQL # => 'db/mysql'
84
+ MyAwesomeGem::DB.autoload? :PostgreSQL # => 'db/postgresql'
85
+ MyAwesomeGem::DB.autoload? :SQLServer # => 'db/sql_server'
86
+ MyAwesomeGem::DB.autoload? :Nonexistent # => nil
87
+
88
+ MyAwesomeGem::DB::MySQL
89
+ MyAwesomeGem::DB.constants # => [:MySQL]
90
+ MyAwesomeGem::DB::PostgreSQL
91
+ MyAwesomeGem::DB.constants # => [:MySQL, :PostgreSQL]
92
+ MyAwesomeGem::DB::SQLServer
93
+ MyAwesomeGem::DB.constants # => [:MySQL, :PostgreSQL, :SQLServer]
79
94
  ```
80
95
 
81
96
  _Autoloaded_ does not perform deep autoloading of nested namespaces and
@@ -99,7 +114,7 @@ module MyAwesomeGem; end
99
114
  # lib/my_awesome_gem/db.rb
100
115
  module MyAwesomeGem
101
116
 
102
- # WRONG! Autoloading will not occur.
117
+ # WRONG!
103
118
  extend Autoloaded
104
119
 
105
120
  module DB
@@ -109,6 +124,10 @@ module MyAwesomeGem
109
124
  end
110
125
 
111
126
  end
127
+
128
+ # some_other_file.rb
129
+ require 'my_awesome_gem'
130
+ MyAwesomeGem::DB # NameError is raised!
112
131
  ```
113
132
 
114
133
  ## Contributing
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  snake_case correspondence between the names of constants
26
26
  and source files.
27
27
  end_description
28
- spec.homepage = 'https://github.com/njonsson/autoloaded'
28
+ spec.homepage = 'http://njonsson.github.io/autoloaded'
29
29
  spec.license = 'MIT'
30
30
 
31
31
  spec.files = `git ls-files -z`.split("\x0")
@@ -1,3 +1,92 @@
1
+ # coding: utf-8
2
+ #
3
+ # When used to extend a module, the _Autoloaded_ module dynamically loads
4
+ # constants into that module from source files contained in its filesystem path.
5
+ #
6
+ # @note You must extend a namespace with _Autoloaded_ from within the file in
7
+ # which the namespace is defined. This is because _Autoloaded_ utilizes the
8
+ # source file path of the namespace to establish which directory will be
9
+ # autoloaded. That path is discoverable only via the stack trace of `extend
10
+ # Autoloaded`.
11
+ #
12
+ # Suppose you have the following source files:
13
+ #
14
+ # * lib/
15
+ # * my_awesome_gem/
16
+ # * db/
17
+ # * mysql.rb
18
+ # * postgresql.rb
19
+ # * sql_server.rb
20
+ # * db.rb
21
+ # * my_awesome_gem.rb
22
+ #
23
+ # The following statements establish autoloading — one statement per namespace:
24
+ #
25
+ # # lib/my_awesome_gem.rb
26
+ # module MyAwesomeGem
27
+ #
28
+ # extend Autoloaded
29
+ #
30
+ # end
31
+ #
32
+ # # lib/my_awesome_gem/db.rb
33
+ # module MyAwesomeGem
34
+ #
35
+ # module DB
36
+ #
37
+ # extend Autoloaded
38
+ #
39
+ # end
40
+ #
41
+ # end
42
+ #
43
+ # Note that your preferred casing of constants is accommodated automatically.
44
+ #
45
+ # # Unlike Kernel#autoload and Module#autoload, Autoloaded is not clairvoyant about
46
+ # # what constants will be autoloaded.
47
+ # MyAwesomeGem::DB.constants # => []
48
+ #
49
+ # # But like Kernel#autoload and Module#autoload, Autoloaded does tell you which
50
+ # # source files will be autoloaded. (The difference is that it may return an array
51
+ # # of potential matches instead of just one filename.)
52
+ # MyAwesomeGem::DB.autoload? :MySQL # => 'db/mysql'
53
+ # MyAwesomeGem::DB.autoload? :PostgreSQL # => 'db/postgresql'
54
+ # MyAwesomeGem::DB.autoload? :SQLServer # => 'db/sql_server'
55
+ # MyAwesomeGem::DB.autoload? :Nonexistent # => nil
56
+ #
57
+ # MyAwesomeGem::DB::MySQL
58
+ # MyAwesomeGem::DB.constants # => [:MySQL]
59
+ # MyAwesomeGem::DB::PostgreSQL
60
+ # MyAwesomeGem::DB.constants # => [:MySQL, :PostgreSQL]
61
+ # MyAwesomeGem::DB::SQLServer
62
+ # MyAwesomeGem::DB.constants # => [:MySQL, :PostgreSQL, :SQLServer]
63
+ #
64
+ # _Autoloaded_ does not perform deep autoloading of nested namespaces and
65
+ # directories. This is by design.
66
+ #
67
+ # In the following example, autoloading of the _MyAwesomeGem_ namespace will not occur because the name of the source file in which the `extend` statement is invoked does not match the name of the namespace.
68
+ #
69
+ # # lib/my_awesome_gem.rb
70
+ # module MyAwesomeGem; end
71
+ #
72
+ # # lib/my_awesome_gem/db.rb
73
+ # module MyAwesomeGem
74
+ #
75
+ # # WRONG!
76
+ # extend Autoloaded
77
+ #
78
+ # module DB
79
+ #
80
+ # extend Autoloaded
81
+ #
82
+ # end
83
+ #
84
+ # end
85
+ #
86
+ # # some_other_file.rb
87
+ # require 'my_awesome_gem'
88
+ # MyAwesomeGem::DB # NameError is raised!
89
+ #
1
90
  module Autoloaded
2
91
 
3
92
  def self.extended(other_module)
@@ -5,14 +94,22 @@ module Autoloaded
5
94
  dir_path = "#{::File.dirname caller_file_path}/#{::File.basename caller_file_path, '.rb'}"
6
95
  other_module.module_eval <<-end_module_eval, __FILE__, __LINE__
7
96
  def self.autoload?(symbol)
8
- #{dir_path.inspect}
97
+ if (old_school = super)
98
+ return old_school
99
+ end
100
+
101
+ require 'autoloaded/constant'
102
+ filenames = []
103
+ ::Autoloaded::Constant.new(symbol).each_matching_filename_in #{dir_path.inspect} do |filename|
104
+ filenames << filename
105
+ end
106
+ (filenames.length <= 1) ? filenames.first : filenames
9
107
  end
10
108
 
11
109
  def self.const_missing(symbol)
12
110
  require 'autoloaded/constant'
13
111
  ::Autoloaded::Constant.new(symbol).each_matching_filename_in #{dir_path.inspect} do |filename|
14
- without_ext = "\#{::File.dirname filename}/\#{::File.basename filename}"
15
- require without_ext
112
+ require filename
16
113
  if const_defined?(symbol)
17
114
  begin
18
115
  return const_get(symbol)
@@ -21,7 +118,7 @@ module Autoloaded
21
118
  end
22
119
  end
23
120
 
24
- super symbol
121
+ super
25
122
  end
26
123
  end_module_eval
27
124
  end
@@ -5,6 +5,7 @@ using ::Autoloaded::Refine::String::ToSourceFilename
5
5
 
6
6
  module Autoloaded; end
7
7
 
8
+ # @private
8
9
  class Autoloaded::Constant
9
10
 
10
11
  attr_reader :name
@@ -28,6 +29,25 @@ private
28
29
  '.rb'
29
30
  end
30
31
 
32
+ def filename_without_extension(filename)
33
+ "#{::File.dirname filename}/#{::File.basename filename, extension}"
34
+ end
35
+
36
+ def filename_without_load_path_prefix(filename)
37
+ first_applicable_load_path = $:.detect do |path|
38
+ filename.start_with? path
39
+ end
40
+ if first_applicable_load_path
41
+ regexp = /^#{::Regexp.escape first_applicable_load_path}\/?/
42
+ filename = filename.gsub(regexp, '')
43
+ end
44
+ filename
45
+ end
46
+
47
+ def format_filename(filename)
48
+ filename_without_load_path_prefix filename_without_extension(filename)
49
+ end
50
+
31
51
  def name_signature
32
52
  @name_signature ||= signature(name)
33
53
  end
@@ -52,7 +72,7 @@ private
52
72
  filename_signature = signature(::File.basename(filename, extension))
53
73
  if (filename_signature == name_signature) &&
54
74
  unless_in.add?(::File.basename(filename))
55
- block.call filename
75
+ block.call format_filename(filename)
56
76
  end
57
77
  end
58
78
  end
@@ -63,7 +83,7 @@ private
63
83
  end
64
84
  qualified = ::File.join(directory, name_source_filename)
65
85
  if ::File.file?(qualified) && unless_in.add?(name_source_filename)
66
- block.call qualified
86
+ block.call format_filename(qualified)
67
87
  end
68
88
  end
69
89
 
@@ -1,5 +1,6 @@
1
1
  module Autoloaded
2
2
 
3
+ # @private
3
4
  module Refine; end
4
5
 
5
6
  end
@@ -1,5 +1,6 @@
1
1
  module Autoloaded
2
2
 
3
- VERSION = '1.0.0'
3
+ # The current version of the _Autoloaded_ project.
4
+ VERSION = '1.1.0'
4
5
 
5
6
  end
@@ -3,8 +3,16 @@ require 'autoloaded/constant'
3
3
  RSpec.describe Autoloaded::Constant do
4
4
  let(:constant_class) { described_class }
5
5
 
6
+ specify("sanity check: load path includes our 'spec' directory") {
7
+ expect($:).to include(File.expand_path('../..', __FILE__))
8
+ }
9
+
6
10
  describe 'for' do
7
- let(:directory) { 'spec/fixtures/filenames' }
11
+ let(:directory) { File.expand_path '../../fixtures/filenames', __FILE__ }
12
+
13
+ let(:load_pathed_directory) {
14
+ directory.gsub(/^#{File.expand_path '../..', __FILE__}\/?/, '')
15
+ }
8
16
 
9
17
  {AFilename: %w(a_filename a-file-name a-filename a_file_name afile_name afile-name AFilename),
10
18
  A_FILENAME: %w(a_filename a-file-name a-filename a_file_name afile_name afile-name AFilename),
@@ -14,9 +22,9 @@ RSpec.describe Autoloaded::Constant do
14
22
  describe constant_name.inspect do
15
23
  let(:constant) { constant_class.new constant_name }
16
24
 
17
- let(:full_filenames) {
25
+ let(:load_pathed_filenames) {
18
26
  filenames.collect do |filename|
19
- "#{directory}/#{filename}.rb"
27
+ "#{load_pathed_directory}/#{filename}"
20
28
  end
21
29
  }
22
30
 
@@ -32,13 +40,13 @@ RSpec.describe Autoloaded::Constant do
32
40
  describe 'first yielded argument' do
33
41
  subject(:first_yielded_argument) { yielded_args.first }
34
42
 
35
- it { is_expected.to eq(full_filenames.first) }
43
+ it { is_expected.to eq(load_pathed_filenames.first) }
36
44
  end
37
45
 
38
46
  describe 'subsequent yielded arguments' do
39
47
  subject(:subsequent_yielded_arguments) { yielded_args[1..-1] }
40
48
 
41
- it { is_expected.to match_array(full_filenames[1..-1]) }
49
+ it { is_expected.to match_array(load_pathed_filenames[1..-1]) }
42
50
  end
43
51
  end
44
52
  end
@@ -2,47 +2,131 @@ require 'autoloaded'
2
2
  require 'matchers'
3
3
 
4
4
  RSpec.describe Autoloaded do
5
+ before :each do
6
+ class << self
7
+ alias_method :define_constant, :define_constants
8
+ end
9
+ end
10
+
5
11
  describe 'not extending a namespace' do
6
- let(:source_file) { 'spec/fixtures/namespace_that_is_not_autoloaded.rb' }
12
+ subject(:source_file) { 'spec/fixtures/not_autoloaded.rb' }
13
+
14
+ it { is_expected.to define_constant(:NotAutoloaded) }
15
+
16
+ it { is_expected.to define_constant('NotAutoloaded::OldSchoolAutoload') }
7
17
 
8
- specify('does not dynamically define a nested constant') {
9
- expect(source_file).not_to autoload_a_constant_named('NamespaceThatIsNotAutoloaded::Nested')
18
+ it { is_expected.not_to define_constant('NotAutoloaded::Nested') }
19
+
20
+ it {
21
+ is_expected.to set_up_autoload_for_constant('NotAutoloaded::OldSchoolAutoload').
22
+ from_file('fixtures/not_autoloaded/old_school_autoload')
10
23
  }
11
24
 
12
- specify('does not pollute the namespace') {
13
- expect(source_file).to define_only_constants_named().
14
- in_a_namespace_named(:NamespaceThatIsNotAutoloaded)
25
+ it {
26
+ is_expected.not_to set_up_autoload_for_constant('NotAutoloaded::Nested')
15
27
  }
16
28
  end
17
29
 
18
- describe 'extending a namespace' do
19
- describe 'whose source files have conventional names' do
20
- let(:source_file) {
21
- 'spec/fixtures/namespace_that_is_autoloaded_conventionally.rb'
30
+ describe 'extending a namespace whose constituent source files have' do
31
+ describe 'conventional names used for autoloading' do
32
+ subject(:source_file) {
33
+ 'spec/fixtures/autoloaded_with_conventional_filename.rb'
22
34
  }
23
35
 
24
- specify('dynamically defines a nested constant stored in a conventionally-named file') {
25
- expect(source_file).to autoload_a_constant_named('NamespaceThatIsAutoloadedConventionally::Nested')
36
+ it { is_expected.to define_constant(:AutoloadedWithConventionalFilename) }
37
+
38
+ it {
39
+ is_expected.to define_constant('AutoloadedWithConventionalFilename::OldSchoolAutoload')
26
40
  }
27
41
 
28
- specify('does not pollute the namespace') {
29
- expect(source_file).to define_only_constants_named(:Nested).
30
- in_a_namespace_named(:NamespaceThatIsAutoloadedConventionally)
42
+ it {
43
+ is_expected.to define_constant('AutoloadedWithConventionalFilename::Nested').
44
+ dynamically
45
+ }
46
+
47
+ it {
48
+ is_expected.not_to define_constant('AutoloadedWithConventionalFilename::NONEXISTENT')
49
+ }
50
+
51
+ it {
52
+ is_expected.to set_up_autoload_for_constant('AutoloadedWithConventionalFilename::OldSchoolAutoload').
53
+ from_file('fixtures/autoloaded_with_conventional_filename/old_school_autoload')
54
+ }
55
+
56
+ it {
57
+ is_expected.to set_up_autoload_for_constant('AutoloadedWithConventionalFilename::Nested').
58
+ from_files('fixtures/autoloaded_with_conventional_filename/nested',
59
+ 'fixtures/autoloaded_with_conventional_filename/N-est-ed',
60
+ 'fixtures/autoloaded_with_conventional_filename/nest_ed')
61
+ }
62
+
63
+ it {
64
+ is_expected.not_to set_up_autoload_for_constant('AutoloadedWithConventionalFilename::NONEXISTENT')
31
65
  }
32
66
  end
33
67
 
34
- describe 'whose source files have unconventional names' do
35
- let(:source_file) {
36
- 'spec/fixtures/namespace_that_is_autoloaded_unconventionally.rb'
68
+ describe 'conventional names' do
69
+ subject(:source_file) {
70
+ 'spec/fixtures/autoloaded_with_conventional_filename_only.rb'
71
+ }
72
+
73
+ it { is_expected.to define_constant(:AutoloadedWithConventionalFilenameOnly) }
74
+
75
+ it {
76
+ is_expected.to define_constant('AutoloadedWithConventionalFilenameOnly::OldSchoolAutoload')
77
+ }
78
+
79
+ it {
80
+ is_expected.to define_constant('AutoloadedWithConventionalFilenameOnly::Nested').
81
+ dynamically
82
+ }
83
+
84
+ it {
85
+ is_expected.to set_up_autoload_for_constant('AutoloadedWithConventionalFilenameOnly::OldSchoolAutoload').
86
+ from_file('fixtures/autoloaded_with_conventional_filename_only/old_school_autoload')
87
+ }
88
+
89
+ it {
90
+ is_expected.to set_up_autoload_for_constant('AutoloadedWithConventionalFilenameOnly::Nested').
91
+ from_file('fixtures/autoloaded_with_conventional_filename_only/nested')
92
+ }
93
+
94
+ it {
95
+ is_expected.not_to set_up_autoload_for_constant('AutoloadedWithConventionalFilenameOnly::NONEXISTENT')
96
+ }
97
+ end
98
+
99
+ describe 'unconventional names' do
100
+ subject(:source_file) {
101
+ 'spec/fixtures/autoloaded_with_unconventional_filenames.rb'
102
+ }
103
+
104
+ it {
105
+ is_expected.to define_constant(:AutoloadedWithUnconventionalFilenames)
106
+ }
107
+
108
+ it {
109
+ is_expected.to define_constant('AutoloadedWithUnconventionalFilenames::OldSchoolAutoload')
110
+ }
111
+
112
+ it {
113
+ is_expected.to define_constants('AutoloadedWithUnconventionalFilenames::Nested').
114
+ dynamically
115
+ }
116
+
117
+ it {
118
+ is_expected.to set_up_autoload_for_constant('AutoloadedWithUnconventionalFilenames::OldSchoolAutoload').
119
+ from_file('fixtures/autoloaded_with_unconventional_filenames/old_school_autoload')
37
120
  }
38
121
 
39
- specify('dynamically defines a nested constant stored in a unconventionally-named file') {
40
- expect(source_file).to autoload_a_constant_named('NamespaceThatIsAutoloadedUnconventionally::Nested')
122
+ it {
123
+ is_expected.to set_up_autoload_for_constant('AutoloadedWithUnconventionalFilenames::Nested').
124
+ from_files('fixtures/autoloaded_with_unconventional_filenames/N-est-ed',
125
+ 'fixtures/autoloaded_with_unconventional_filenames/nest_ed')
41
126
  }
42
127
 
43
- specify('does not pollute the namespace') {
44
- expect(source_file).to define_only_constants_named(:Nested).
45
- in_a_namespace_named(:NamespaceThatIsAutoloadedUnconventionally)
128
+ it {
129
+ is_expected.not_to set_up_autoload_for_constant('AutoloadedWithUnconventionalFilenames::NONEXISTENT')
46
130
  }
47
131
  end
48
132
  end
@@ -0,0 +1,10 @@
1
+ require 'autoloaded'
2
+
3
+ module AutoloadedWithConventionalFilename
4
+
5
+ autoload :OldSchoolAutoload,
6
+ 'fixtures/autoloaded_with_conventional_filename/old_school_autoload'
7
+
8
+ extend ::Autoloaded
9
+
10
+ end
@@ -0,0 +1,5 @@
1
+ module AutoloadedWithConventionalFilename
2
+
3
+ module Nested; end
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ module AutoloadedWithConventionalFilename
2
+
3
+ module OldSchoolAutoload; end
4
+
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'autoloaded'
2
+
3
+ module AutoloadedWithConventionalFilenameOnly
4
+
5
+ autoload :OldSchoolAutoload,
6
+ 'fixtures/autoloaded_with_conventional_filename_only/old_school_autoload'
7
+
8
+ extend ::Autoloaded
9
+
10
+ end
@@ -0,0 +1,5 @@
1
+ module AutoloadedWithConventionalFilenameOnly
2
+
3
+ module Nested; end
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ module AutoloadedWithConventionalFilenameOnly
2
+
3
+ module OldSchoolAutoload; end
4
+
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'autoloaded'
2
+
3
+ module AutoloadedWithUnconventionalFilenames
4
+
5
+ autoload :OldSchoolAutoload,
6
+ 'fixtures/autoloaded_with_unconventional_filenames/old_school_autoload'
7
+
8
+ extend ::Autoloaded
9
+
10
+ end
@@ -0,0 +1,5 @@
1
+ module AutoloadedWithUnconventionalFilenames
2
+
3
+ module Nested; end
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ module AutoloadedWithUnconventionalFilenames
2
+
3
+ module OldSchoolAutoload; end
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ module NotAutoloaded
2
+
3
+ autoload :OldSchoolAutoload, 'fixtures/not_autoloaded/old_school_autoload'
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ module NotAutoloaded
2
+
3
+ module OldSchoolAutoload; end
4
+
5
+ end
@@ -1,72 +1,114 @@
1
+ require 'support/util'
1
2
  require 'support/without_side_effects'
2
3
 
3
- RSpec::Matchers.define :autoload_a_constant_named do |constant_name|
4
+ RSpec::Matchers.define :define_constants do |*constant_names|
4
5
  match do |source_file|
5
6
  # Ensure the file exists.
6
7
  File.open source_file, 'r' do
7
8
  end
8
9
 
9
- constant_tokens = constant_name.split('::')
10
- constant_up_till_last = constant_tokens[0...-1].join('::')
11
- constant_last = constant_tokens.last
12
-
13
10
  without_side_effects do
14
- begin
15
- eval constant_name.to_s
16
- rescue NameError
17
- else
18
- raise("#{constant_name} is already defined")
11
+ constant_names.each do |constant_name|
12
+ if Util.constantize(constant_name)
13
+ fail "constant #{constant_name} is already defined outside #{source_file}"
14
+ end
19
15
  end
20
16
 
21
17
  load source_file
22
18
 
23
- begin
24
- eval constant_name.to_s
25
- rescue NameError
19
+ any_statically_defined = false
20
+ if dynamically?
21
+ any_statically_defined = constant_names.any? do |constant_name|
22
+ current_scope = Object
23
+ constant_name.split(Util.namespace_delimiter).all? do |token|
24
+ current_scope.constants.include?(token.to_sym).tap do |result|
25
+ if result
26
+ current_scope = Util.constantize([current_scope.name, token].join(Util.namespace_delimiter))
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ if any_statically_defined
26
34
  false
27
35
  else
28
- eval(constant_up_till_last).autoload? constant_last.to_sym
36
+ constant_names.all? do |constant_name|
37
+ namespace, unqualified_constant_name = Util.namespace_and_unqualified_constant_name(constant_name,
38
+ raise_if_namespace_invalid: true)
39
+ (!dynamically? ||
40
+ !namespace.constants.include?(unqualified_constant_name.to_sym)) &&
41
+ Util.constantize(constant_name)
42
+ end
29
43
  end
30
44
  end
31
45
  end
32
- end
33
46
 
34
- RSpec::Matchers.define :define_only_constants_named do |*constant_names|
35
- attr_reader :expected_constants, :extraneous_defined_constants, :namespace_name
47
+ chain :dynamically do
48
+ @dynamically = true
49
+ end
36
50
 
37
- match do |source_file|
38
- @expected_constants, @extraneous_defined_constants = [], []
51
+ description do
52
+ fragments = []
53
+ fragments << case constant_names.length
54
+ when 0
55
+ 'no constants'
56
+ when 1
57
+ "constant #{constant_names.first}"
58
+ else
59
+ "constants #{constant_names.join ' and '}"
60
+ end
61
+ fragments << 'dynamically' if dynamically?
62
+ "define #{fragments.join ' '}"
63
+ end
64
+
65
+ def dynamically?
66
+ @dynamically
67
+ end
68
+ end
39
69
 
70
+ RSpec::Matchers.define :set_up_autoload_for_constant do |constant_name|
71
+ match do |source_file|
40
72
  # Ensure the file exists.
41
73
  File.open source_file, 'r' do
42
74
  end
43
75
 
44
- unless namespace_name
45
- raise "missing .in_a_namespace_named(:Namespace) clause"
46
- end
76
+ without_side_effects do
77
+ namespace, unqualified_constant_name = Util.namespace_and_unqualified_constant_name(constant_name)
78
+ if namespace && namespace.autoload?(unqualified_constant_name)
79
+ fail "#{namespace.name}::#{unqualified_constant_name} is already set up for autoload outside #{source_file}"
80
+ end
47
81
 
48
- defined_constants = without_side_effects do
49
82
  load source_file
50
83
 
51
- namespace = eval(namespace_name.to_s)
52
-
53
- # Trigger autoloading.
54
- constant_names.each do |constant_name|
55
- namespace.const_get constant_name
84
+ namespace, unqualified_constant_name = Util.namespace_and_unqualified_constant_name(constant_name,
85
+ raise_if_namespace_invalid: true)
86
+ if filename_or_filenames
87
+ Array(filename_or_filenames).sort == Array(namespace.autoload?(unqualified_constant_name)).sort
88
+ else
89
+ namespace.autoload? unqualified_constant_name
56
90
  end
57
-
58
- namespace.constants.sort.collect(&:to_sym)
59
91
  end
60
- @expected_constants = constant_names.sort.collect(&:to_sym)
61
- @extraneous_defined_constants = defined_constants - expected_constants
62
- extraneous_defined_constants.empty?
63
92
  end
64
93
 
65
- chain :in_a_namespace_named do |namespace_name|
66
- @namespace_name = namespace_name
94
+ chain :from_file do |filename|
95
+ @filename_or_filenames = filename
96
+ end
97
+
98
+ chain :from_files do |*filenames|
99
+ @filename_or_filenames = filenames
67
100
  end
68
101
 
69
- failure_message do |source_file|
70
- "expected only #{expected_constants.join ' and '} to be defined in #{namespace_name} but also found #{extraneous_defined_constants.join ' and '}"
102
+ description do
103
+ fragments = []
104
+ fragments << "constant #{constant_name}"
105
+ if filename_or_filenames
106
+ unless (filenames = Array(filename_or_filenames)).empty?
107
+ fragments << "from file#{(filenames.length == 1) ? nil : 's'} #{filenames.join ' and '}"
108
+ end
109
+ end
110
+ "set up #{Module.name}#autoload? for #{fragments.join ' '}"
71
111
  end
112
+
113
+ attr_reader :filename_or_filenames
72
114
  end
@@ -0,0 +1,43 @@
1
+ module Util
2
+
3
+ class << self
4
+
5
+ def constantize(name)
6
+ return nil unless name
7
+
8
+ begin
9
+ eval name.to_s
10
+ rescue NameError
11
+ nil
12
+ end
13
+ end
14
+
15
+ def namespace_and_unqualified_constant_name(constant_name,
16
+ raise_if_namespace_invalid: false)
17
+ namespace_name, unqualified_constant_name = split_namespace_and_constant(constant_name)
18
+ if namespace_name && (namespace = constantize(namespace_name)).nil?
19
+ if raise_if_namespace_invalid
20
+ raise "namespace of #{constant_name} is not defined"
21
+ end
22
+ end
23
+ [namespace, unqualified_constant_name]
24
+ end
25
+
26
+ def namespace_delimiter
27
+ '::'
28
+ end
29
+
30
+ private
31
+
32
+ def split_namespace_and_constant(constant_name)
33
+ if (last_delimiter_index = constant_name.to_s.rindex(namespace_delimiter))
34
+ return [constant_name[0...last_delimiter_index],
35
+ constant_name[(last_delimiter_index + namespace_delimiter.length)..-1]]
36
+ end
37
+
38
+ return [nil, constant_name]
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -10,7 +10,7 @@ def without_side_effects
10
10
 
11
11
  begin
12
12
  out_writer.write Marshal.dump(yield)
13
- rescue => e
13
+ rescue Exception => e
14
14
  clean_backtrace = e.backtrace.reject do |frame|
15
15
  frame.include? __FILE__
16
16
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: autoloaded
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nils Jonsson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-29 00:00:00.000000000 Z
11
+ date: 2014-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -70,7 +70,7 @@ files:
70
70
  - ".yardopts"
71
71
  - Gemfile
72
72
  - Guardfile
73
- - History.markdown
73
+ - History.md
74
74
  - License.md
75
75
  - README.md
76
76
  - Rakefile
@@ -90,6 +90,18 @@ files:
90
90
  - spec/autoloaded/refine/string/to_source_filename_spec.rb
91
91
  - spec/autoloaded/version_spec.rb
92
92
  - spec/autoloaded_spec.rb
93
+ - spec/fixtures/autoloaded_with_conventional_filename.rb
94
+ - spec/fixtures/autoloaded_with_conventional_filename/N-est-ed.rb
95
+ - spec/fixtures/autoloaded_with_conventional_filename/nest_ed.rb
96
+ - spec/fixtures/autoloaded_with_conventional_filename/nested.rb
97
+ - spec/fixtures/autoloaded_with_conventional_filename/old_school_autoload.rb
98
+ - spec/fixtures/autoloaded_with_conventional_filename_only.rb
99
+ - spec/fixtures/autoloaded_with_conventional_filename_only/nested.rb
100
+ - spec/fixtures/autoloaded_with_conventional_filename_only/old_school_autoload.rb
101
+ - spec/fixtures/autoloaded_with_unconventional_filenames.rb
102
+ - spec/fixtures/autoloaded_with_unconventional_filenames/N-est-ed.rb
103
+ - spec/fixtures/autoloaded_with_unconventional_filenames/nest_ed.rb
104
+ - spec/fixtures/autoloaded_with_unconventional_filenames/old_school_autoload.rb
93
105
  - spec/fixtures/filenames/AFilename.rb
94
106
  - spec/fixtures/filenames/a-file-name.rb
95
107
  - spec/fixtures/filenames/a-filename.rb
@@ -97,19 +109,13 @@ files:
97
109
  - spec/fixtures/filenames/a_filename.rb
98
110
  - spec/fixtures/filenames/afile-name.rb
99
111
  - spec/fixtures/filenames/afile_name.rb
100
- - spec/fixtures/namespace_that_is_autoloaded_conventionally.rb
101
- - spec/fixtures/namespace_that_is_autoloaded_conventionally/N-est-ed.rb
102
- - spec/fixtures/namespace_that_is_autoloaded_conventionally/nest_ed.rb
103
- - spec/fixtures/namespace_that_is_autoloaded_conventionally/nested.rb
104
- - spec/fixtures/namespace_that_is_autoloaded_unconventionally.rb
105
- - spec/fixtures/namespace_that_is_autoloaded_unconventionally/N-est-ed.rb
106
- - spec/fixtures/namespace_that_is_autoloaded_unconventionally/nest_ed.rb
107
- - spec/fixtures/namespace_that_is_not_autoloaded.rb
108
- - spec/fixtures/namespace_that_is_not_autoloaded/nested.rb
112
+ - spec/fixtures/not_autoloaded.rb
113
+ - spec/fixtures/not_autoloaded/old_school_autoload.rb
109
114
  - spec/matchers.rb
110
115
  - spec/spec_helper.rb
116
+ - spec/support/util.rb
111
117
  - spec/support/without_side_effects.rb
112
- homepage: https://github.com/njonsson/autoloaded
118
+ homepage: http://njonsson.github.io/autoloaded
113
119
  licenses:
114
120
  - MIT
115
121
  metadata: {}
@@ -139,6 +145,18 @@ test_files:
139
145
  - spec/autoloaded/refine/string/to_source_filename_spec.rb
140
146
  - spec/autoloaded/version_spec.rb
141
147
  - spec/autoloaded_spec.rb
148
+ - spec/fixtures/autoloaded_with_conventional_filename.rb
149
+ - spec/fixtures/autoloaded_with_conventional_filename/N-est-ed.rb
150
+ - spec/fixtures/autoloaded_with_conventional_filename/nest_ed.rb
151
+ - spec/fixtures/autoloaded_with_conventional_filename/nested.rb
152
+ - spec/fixtures/autoloaded_with_conventional_filename/old_school_autoload.rb
153
+ - spec/fixtures/autoloaded_with_conventional_filename_only.rb
154
+ - spec/fixtures/autoloaded_with_conventional_filename_only/nested.rb
155
+ - spec/fixtures/autoloaded_with_conventional_filename_only/old_school_autoload.rb
156
+ - spec/fixtures/autoloaded_with_unconventional_filenames.rb
157
+ - spec/fixtures/autoloaded_with_unconventional_filenames/N-est-ed.rb
158
+ - spec/fixtures/autoloaded_with_unconventional_filenames/nest_ed.rb
159
+ - spec/fixtures/autoloaded_with_unconventional_filenames/old_school_autoload.rb
142
160
  - spec/fixtures/filenames/AFilename.rb
143
161
  - spec/fixtures/filenames/a-file-name.rb
144
162
  - spec/fixtures/filenames/a-filename.rb
@@ -146,16 +164,10 @@ test_files:
146
164
  - spec/fixtures/filenames/a_filename.rb
147
165
  - spec/fixtures/filenames/afile-name.rb
148
166
  - spec/fixtures/filenames/afile_name.rb
149
- - spec/fixtures/namespace_that_is_autoloaded_conventionally.rb
150
- - spec/fixtures/namespace_that_is_autoloaded_conventionally/N-est-ed.rb
151
- - spec/fixtures/namespace_that_is_autoloaded_conventionally/nest_ed.rb
152
- - spec/fixtures/namespace_that_is_autoloaded_conventionally/nested.rb
153
- - spec/fixtures/namespace_that_is_autoloaded_unconventionally.rb
154
- - spec/fixtures/namespace_that_is_autoloaded_unconventionally/N-est-ed.rb
155
- - spec/fixtures/namespace_that_is_autoloaded_unconventionally/nest_ed.rb
156
- - spec/fixtures/namespace_that_is_not_autoloaded.rb
157
- - spec/fixtures/namespace_that_is_not_autoloaded/nested.rb
167
+ - spec/fixtures/not_autoloaded.rb
168
+ - spec/fixtures/not_autoloaded/old_school_autoload.rb
158
169
  - spec/matchers.rb
159
170
  - spec/spec_helper.rb
171
+ - spec/support/util.rb
160
172
  - spec/support/without_side_effects.rb
161
173
  has_rdoc:
@@ -1,9 +0,0 @@
1
- # Version history for the _Autoloaded_ project
2
-
3
- ## <a name="v1.0.0"></a>v1.0.0, Wed 10/29/2013
4
-
5
- * Add support for Ruby v2.0
6
-
7
- ## <a name="v0.0.3"></a>v0.0.3, Fri 10/24/2014
8
-
9
- (First release)
@@ -1,7 +0,0 @@
1
- require 'autoloaded'
2
-
3
- module NamespaceThatIsAutoloadedConventionally
4
-
5
- extend ::Autoloaded
6
-
7
- end
@@ -1,5 +0,0 @@
1
- module NamespaceThatIsAutoloadedConventionally
2
-
3
- module Nested; end
4
-
5
- end
@@ -1,7 +0,0 @@
1
- require 'autoloaded'
2
-
3
- module NamespaceThatIsAutoloadedUnconventionally
4
-
5
- extend ::Autoloaded
6
-
7
- end
@@ -1,5 +0,0 @@
1
- module NamespaceThatIsAutoloadedUnconventionally
2
-
3
- module Nested; end
4
-
5
- end
@@ -1 +0,0 @@
1
- module NamespaceThatIsNotAutoloaded; end
@@ -1,5 +0,0 @@
1
- module NamespaceThatIsNotAutoloaded
2
-
3
- module Nested; end
4
-
5
- end