autoloaded 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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