autoloaded 1.2.0 → 1.3.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/History.md +4 -0
- data/README.md +411 -60
- data/autoloaded.gemspec +19 -14
- data/lib/autoloaded.rb +104 -91
- data/lib/autoloaded/autoloader.rb +260 -0
- data/lib/autoloaded/compatibility/refine_and_using.rb +2 -0
- data/lib/autoloaded/constant.rb +5 -2
- data/lib/autoloaded/deprecation.rb +50 -0
- data/lib/autoloaded/inflection.rb +71 -0
- data/lib/autoloaded/load_pathed_directory.rb +112 -0
- data/lib/autoloaded/refine.rb +7 -1
- data/lib/autoloaded/refine/string.rb +7 -0
- data/lib/autoloaded/refine/string/to_source_filename.rb +12 -0
- data/lib/autoloaded/specification.rb +97 -0
- data/lib/autoloaded/specifications.rb +66 -0
- data/lib/autoloaded/version.rb +3 -1
- data/lib/autoloaded/warning.rb +125 -0
- data/spec/autoloaded/autoloader_spec.rb +469 -0
- data/spec/autoloaded/constant_spec.rb +0 -2
- data/spec/autoloaded/deprecation_spec.rb +23 -0
- data/spec/autoloaded/inflection_spec.rb +30 -0
- data/spec/autoloaded/load_pathed_directory_spec.rb +120 -0
- data/spec/autoloaded/refine/string/to_source_filename_spec.rb +0 -2
- data/spec/autoloaded/specification_spec.rb +98 -0
- data/spec/autoloaded/specifications_spec.rb +191 -0
- data/spec/autoloaded/version_spec.rb +0 -2
- data/spec/autoloaded/warning_spec.rb +115 -0
- data/spec/autoloaded_macro_sharedspec.rb +24 -0
- data/spec/autoloaded_spec.rb +277 -95
- data/spec/fixtures/autoloaded_with_conventional_filename.rb +3 -1
- data/spec/fixtures/autoloaded_with_conventional_filename/nested.rb +12 -1
- data/spec/fixtures/autoloaded_with_conventional_filename/nested/doubly_nested.rb +9 -0
- data/spec/fixtures/autoloaded_with_unconventional_filename.rb +12 -0
- data/spec/fixtures/autoloaded_with_unconventional_filename/N-est-ed.rb +7 -0
- data/spec/fixtures/autoloaded_with_unconventional_filename/nest_ed.rb +1 -0
- data/spec/fixtures/autoloaded_with_unconventional_filename/old_school_autoload.rb +5 -0
- data/spec/fixtures/not_autoloaded/nested.rb +1 -0
- data/spec/fixtures/old_api/autoloaded_with_conventional_filename.rb +10 -0
- data/spec/fixtures/old_api/autoloaded_with_conventional_filename/N-est-ed.rb +1 -0
- data/spec/fixtures/old_api/autoloaded_with_conventional_filename/nest_ed.rb +1 -0
- data/spec/fixtures/old_api/autoloaded_with_conventional_filename/nested.rb +5 -0
- data/spec/fixtures/old_api/autoloaded_with_conventional_filename/old_school_autoload.rb +5 -0
- data/spec/fixtures/{autoloaded_with_conventional_filename_only.rb → old_api/autoloaded_with_conventional_filename_only.rb} +1 -1
- data/spec/fixtures/{autoloaded_with_conventional_filename_only → old_api/autoloaded_with_conventional_filename_only}/nested.rb +0 -0
- data/spec/fixtures/{autoloaded_with_conventional_filename_only → old_api/autoloaded_with_conventional_filename_only}/old_school_autoload.rb +0 -0
- data/spec/fixtures/{autoloaded_with_unconventional_filenames.rb → old_api/autoloaded_with_unconventional_filenames.rb} +1 -1
- data/spec/fixtures/{autoloaded_with_unconventional_filenames → old_api/autoloaded_with_unconventional_filenames}/N-est-ed.rb +0 -0
- data/spec/fixtures/{autoloaded_with_unconventional_filenames → old_api/autoloaded_with_unconventional_filenames}/nest_ed.rb +0 -0
- data/spec/fixtures/{autoloaded_with_unconventional_filenames → old_api/autoloaded_with_unconventional_filenames}/old_school_autoload.rb +0 -0
- data/spec/fixtures/old_api/not_autoloaded.rb +6 -0
- data/spec/fixtures/old_api/not_autoloaded/nested.rb +1 -0
- data/spec/fixtures/old_api/not_autoloaded/old_school_autoload.rb +5 -0
- data/spec/matchers.rb +4 -33
- data/spec/spec_helper.rb +2 -0
- metadata +95 -41
data/autoloaded.gemspec
CHANGED
@@ -3,27 +3,32 @@
|
|
3
3
|
lib = File.expand_path('../lib', __FILE__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
|
6
|
-
require 'autoloaded
|
6
|
+
require 'autoloaded'
|
7
7
|
|
8
8
|
Gem::Specification.new do |spec|
|
9
9
|
spec.name = 'autoloaded'
|
10
10
|
spec.version = Autoloaded::VERSION
|
11
11
|
spec.authors = ['Nils Jonsson']
|
12
12
|
spec.email = ['autoloaded@nilsjonsson.com']
|
13
|
-
spec.summary = <<-end_summary.gsub(/^\s+/, '').gsub("\n", ' ')
|
14
|
-
|
15
|
-
|
13
|
+
spec.summary = <<-end_summary.chomp.gsub(/^\s+/, '').gsub("\n", ' ')
|
14
|
+
Eliminates the drudgery of handcrafting a Ruby Core
|
15
|
+
library `autoload` statement for each Ruby source code
|
16
|
+
file in your project. It also avoids the limitations of
|
17
|
+
rigid convention-driven facilities such as those
|
18
|
+
provided by the ActiveSupport RubyGem.
|
16
19
|
end_summary
|
17
|
-
spec.description =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
snake_case correspondence
|
26
|
-
and source files.
|
20
|
+
spec.description = <<-end_description.chomp.gsub(/^\s+/, '').gsub("\n", ' ')
|
21
|
+
If you like the ‘Module#autoload’ feature of the Ruby
|
22
|
+
Core library, you may have wished for Autoloaded. It
|
23
|
+
eliminates the drudgery of handcrafting an `autoload`
|
24
|
+
statement for each Ruby source code file in your
|
25
|
+
project. It also avoids the limitations of rigid
|
26
|
+
convention-driven facilities such as those provided by
|
27
|
+
the ActiveSupport RubyGem. Autoloaded assumes, but does
|
28
|
+
not enforce, `CamelCase`-to-`snake_case` correspondence
|
29
|
+
between the names of constants and source files. You can
|
30
|
+
combine conventions, even putting multiple autoloaded
|
31
|
+
constants in a single source file.
|
27
32
|
end_description
|
28
33
|
spec.homepage = 'http://njonsson.github.io/autoloaded'
|
29
34
|
spec.license = 'MIT'
|
data/lib/autoloaded.rb
CHANGED
@@ -1,100 +1,25 @@
|
|
1
|
-
#
|
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!
|
1
|
+
# Eliminates the drudgery of handcrafting a Ruby Core library +autoload+
|
2
|
+
# statement for each Ruby source code file in your project.
|
89
3
|
#
|
4
|
+
# @since 0.0.1
|
90
5
|
module Autoloaded
|
91
6
|
|
92
|
-
autoload :
|
93
|
-
autoload :
|
94
|
-
autoload :
|
7
|
+
autoload :Autoloader, 'autoloaded/autoloader'
|
8
|
+
autoload :Constant, 'autoloaded/constant'
|
9
|
+
autoload :Deprecation, 'autoloaded/deprecation'
|
10
|
+
autoload :Inflection, 'autoloaded/inflection'
|
11
|
+
autoload :LoadPathedDirectory, 'autoloaded/load_pathed_directory'
|
12
|
+
autoload :Refine, 'autoloaded/refine'
|
13
|
+
autoload :Specification, 'autoloaded/specification'
|
14
|
+
autoload :Specifications, 'autoloaded/specifications'
|
15
|
+
autoload :VERSION, 'autoloaded/version'
|
16
|
+
autoload :Warning, 'autoloaded/warning'
|
95
17
|
|
96
18
|
def self.extended(other_module)
|
97
19
|
caller_file_path = caller_locations.first.absolute_path
|
20
|
+
Deprecation.deprecate deprecated_usage: "extend #{name}",
|
21
|
+
sanctioned_usage: "#{name}.module { }",
|
22
|
+
source_filename: caller_file_path
|
98
23
|
dir_path = "#{::File.dirname caller_file_path}/#{::File.basename caller_file_path, '.rb'}"
|
99
24
|
other_module.module_eval <<-end_module_eval, __FILE__, __LINE__
|
100
25
|
def self.autoload?(symbol)
|
@@ -127,4 +52,92 @@ module Autoloaded
|
|
127
52
|
end_module_eval
|
128
53
|
end
|
129
54
|
|
55
|
+
# @!method self.module
|
56
|
+
# Autoloads constants that match files in the source directory.
|
57
|
+
#
|
58
|
+
# @yield [Autoloader] accepts options for regulating autoloading
|
59
|
+
#
|
60
|
+
# @return [Array of Array] the arguments passed to each +autoload+ statement
|
61
|
+
# made
|
62
|
+
#
|
63
|
+
# @raise [LocalJumpError] no block is given
|
64
|
+
#
|
65
|
+
# @since 1.3
|
66
|
+
#
|
67
|
+
# @example Autoloading a namespace
|
68
|
+
# # lib/my_awesome_gem/db.rb
|
69
|
+
#
|
70
|
+
# module MyAwesomeGem
|
71
|
+
#
|
72
|
+
# Autoloaded.module { }
|
73
|
+
#
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# @example Autoloading with optional specifications
|
77
|
+
# # lib/my_awesome_gem/db.rb
|
78
|
+
#
|
79
|
+
# module MyAwesomeGem
|
80
|
+
#
|
81
|
+
# class DB
|
82
|
+
#
|
83
|
+
# results = Autoloaded.class do |autoloaded|
|
84
|
+
# autoloaded.with :MySQL, :PostgreSQL, [:Access, :SQLServer] => 'MicroSoft'
|
85
|
+
# autoloaded.except 'SELF-DESTRUCT!'
|
86
|
+
# end
|
87
|
+
# STDOUT.puts results.inspect # See output below.
|
88
|
+
#
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# # [[:Access, 'my_awesome_gem/db/MicroSoft' ],
|
94
|
+
# # [:SQLServer, 'my_awesome_gem/db/MicroSoft' ],
|
95
|
+
# # [:MySQL, 'my_awesome_gem/db/mysql' ],
|
96
|
+
# # [:Oracle, 'my_awesome_gem/db/oracle' ],
|
97
|
+
# # [:PostgreSQL, 'my_awesome_gem/db/postgre_sql']]
|
98
|
+
def self.module(&block)
|
99
|
+
raise(::LocalJumpError, 'no block given (yield)') unless block
|
100
|
+
|
101
|
+
yield(autoloader = Autoloader.new(block.binding))
|
102
|
+
autoloader.autoload!
|
103
|
+
end
|
104
|
+
|
105
|
+
# Enables or disables warning messages depending on the specified _enabling_
|
106
|
+
# argument.
|
107
|
+
#
|
108
|
+
# @param [Object] enabling disables warnings if +nil+ or +false+
|
109
|
+
#
|
110
|
+
# @yield if a block is given, the value of _#warn?_ is reset after the block is
|
111
|
+
# called
|
112
|
+
#
|
113
|
+
# @return if a block is given, the result of calling the block
|
114
|
+
# @return [Autoloaded] if a block is not given, _Autoloaded_ (_Module_)
|
115
|
+
#
|
116
|
+
# @since 1.3
|
117
|
+
#
|
118
|
+
# @see .warn?
|
119
|
+
def self.warn(enabling, &block)
|
120
|
+
result = Warning.enable(enabling, &block)
|
121
|
+
|
122
|
+
block ? result : self
|
123
|
+
end
|
124
|
+
|
125
|
+
# Indicates whether warning messages are enabled or disabled.
|
126
|
+
#
|
127
|
+
# @return [true] if warning messages are enabled
|
128
|
+
# @return [false] if warning messages are disabled
|
129
|
+
#
|
130
|
+
# @since 1.3
|
131
|
+
#
|
132
|
+
# @see .warn
|
133
|
+
def self.warn?
|
134
|
+
Warning.enabled?
|
135
|
+
end
|
136
|
+
|
137
|
+
class << self
|
138
|
+
|
139
|
+
alias_method :class, :module
|
140
|
+
|
141
|
+
end
|
142
|
+
|
130
143
|
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
module Autoloaded
|
2
|
+
|
3
|
+
# Autoloads files in a source directory.
|
4
|
+
#
|
5
|
+
# @since 1.3
|
6
|
+
class Autoloader
|
7
|
+
|
8
|
+
# The source code context in which autoloading is to occur.
|
9
|
+
#
|
10
|
+
# @return [Binding] the source code context in which autoloading is to occur.
|
11
|
+
#
|
12
|
+
# @see #autoload!
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :host_binding
|
16
|
+
|
17
|
+
# Constructs a new _Autoloader_ with the specified _host_binding_.
|
18
|
+
#
|
19
|
+
# @param [Binding] host_binding a value for _#host_binding_
|
20
|
+
#
|
21
|
+
# @raise [ArgumentError] _host_binding_ is +nil+
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
def initialize(host_binding)
|
25
|
+
raise(::ArgumentError, "can't be nil") if host_binding.nil?
|
26
|
+
|
27
|
+
@host_binding = host_binding
|
28
|
+
@specifications = Specifications.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Issues +autoload+ statements for source files found in _#from_. The
|
32
|
+
# constants are renamed by _#with_ and _#only_. The source files are filtered
|
33
|
+
# by _#except_ and _#only_.
|
34
|
+
#
|
35
|
+
# @return [Array of Array] the arguments passed to each +autoload+ statement
|
36
|
+
# made
|
37
|
+
#
|
38
|
+
# @see http://ruby-doc.org/core/Module.html#method-i-autoload Module#autoload
|
39
|
+
# @see http://ruby-doc.org/core/Kernel.html#method-i-autoload Kernel#autoload
|
40
|
+
# @see #from
|
41
|
+
# @see #except
|
42
|
+
# @see #only
|
43
|
+
# @see #with
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
def autoload!
|
47
|
+
result = []
|
48
|
+
from_load_pathed_directory.each_source_filename do |source_filename|
|
49
|
+
source_basename = ::File.basename(source_filename)
|
50
|
+
next if specifications.except.any? { |spec| spec.match source_basename }
|
51
|
+
|
52
|
+
unless specifications.only.empty? ||
|
53
|
+
specifications.only.any? { |spec| spec.match source_basename }
|
54
|
+
next
|
55
|
+
end
|
56
|
+
|
57
|
+
first_match = (specifications.with + specifications.only).inject(nil) do |match, spec|
|
58
|
+
match || spec.match(source_basename)
|
59
|
+
end
|
60
|
+
constant_names = Array(first_match ||
|
61
|
+
Inflection.to_constant_name(source_basename))
|
62
|
+
existing_source_filenames = constant_names.collect do |const|
|
63
|
+
existing_autoload? const
|
64
|
+
end
|
65
|
+
if existing_source_filenames.all? { |file| file == source_filename }
|
66
|
+
next
|
67
|
+
end
|
68
|
+
|
69
|
+
existing_source_filenames.zip(constant_names).each do |file, const|
|
70
|
+
if file
|
71
|
+
Warning.changing_autoload constant_name: constant_full_name(const),
|
72
|
+
old_source_filename: file,
|
73
|
+
new_source_filename: source_filename,
|
74
|
+
host_source_location: host_source_location
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if existing_source_filenames.compact.empty?
|
79
|
+
constant_names.each do |const|
|
80
|
+
next unless existing_constant?(const)
|
81
|
+
|
82
|
+
Warning.existing_constant constant_name: constant_full_name(const),
|
83
|
+
source_filename: source_filename,
|
84
|
+
host_source_location: host_source_location
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
constant_names.each do |const|
|
89
|
+
establish_autoload const, source_filename
|
90
|
+
result << [const, source_filename]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
# @!method except(*arguments)
|
97
|
+
# Specifies constants and/or source files not to be autoloaded. _Symbol_
|
98
|
+
# arguments signify the names of constants and _String_ arguments signify
|
99
|
+
# the names of source files. You can specify _#except_ multiple times, and
|
100
|
+
# its effects are cumulative.
|
101
|
+
#
|
102
|
+
# Source file names specified are relative to _#from_.
|
103
|
+
#
|
104
|
+
# Valid arguments include:
|
105
|
+
#
|
106
|
+
# * _Symbol_ values
|
107
|
+
# * _String_ values
|
108
|
+
# * _Array_ values comprising _Symbol_ and/or _String_ values
|
109
|
+
# * _Hash_ values comprising _Symbol_, _String_, and/or _Array_ values
|
110
|
+
# described above
|
111
|
+
# * Any combination of the options described above
|
112
|
+
#
|
113
|
+
# @return [Autoloader] the _Autoloader_
|
114
|
+
#
|
115
|
+
# @raise [RuntimeError] _#only_ already has a specification
|
116
|
+
#
|
117
|
+
# @see #autoload!
|
118
|
+
# @see #from
|
119
|
+
# @see #only
|
120
|
+
#
|
121
|
+
# @!method only(*arguments)
|
122
|
+
# Specifies constants and/or source files to be autoloaded exclusively.
|
123
|
+
# _Symbol_ arguments signify the names of constants and _String_ arguments
|
124
|
+
# signify the names of source files. You can specify _#only_ multiple
|
125
|
+
# times, and its effects are cumulative.
|
126
|
+
#
|
127
|
+
# Source file names specified are relative to _#from_.
|
128
|
+
#
|
129
|
+
# Valid arguments include:
|
130
|
+
#
|
131
|
+
# * _Symbol_ values
|
132
|
+
# * _String_ values
|
133
|
+
# * _Array_ values comprising _Symbol_ and/or _String_ values
|
134
|
+
# * _Hash_ values comprising _Symbol_, _String_, and/or _Array_ values
|
135
|
+
# described above, which will autoload specified constants from their
|
136
|
+
# associated source files
|
137
|
+
# * Any combination of the options described above
|
138
|
+
#
|
139
|
+
# @return [Autoloader] the _Autoloader_
|
140
|
+
#
|
141
|
+
# @raise [RuntimeError] _#except_ already has a specification
|
142
|
+
#
|
143
|
+
# @see #autoload!
|
144
|
+
# @see #from
|
145
|
+
# @see #except
|
146
|
+
#
|
147
|
+
# @!method with(*arguments)
|
148
|
+
# Specifies constants and/or source files to be autoloaded whose names may
|
149
|
+
# have unpredictable spellings, stylization, or organization. _Symbol_
|
150
|
+
# arguments signify the names of constants and _String_ arguments signify
|
151
|
+
# the names of source files. You can specify _#with_ multiple times, and
|
152
|
+
# its effects are cumulative.
|
153
|
+
#
|
154
|
+
# Source file names specified are relative to _#from_.
|
155
|
+
#
|
156
|
+
# Valid arguments include:
|
157
|
+
#
|
158
|
+
# * _Symbol_ values
|
159
|
+
# * _Array_ values comprising _Symbol_ values
|
160
|
+
# * _Hash_ values comprising _Symbol_, _String_, and/or _Array_ values
|
161
|
+
# described above, which will autoload specified constants from their
|
162
|
+
# associated source files
|
163
|
+
# * Any combination of the options described above
|
164
|
+
#
|
165
|
+
# @return [Autoloader] the _Autoloader_
|
166
|
+
#
|
167
|
+
# @see #autoload!
|
168
|
+
# @see #from
|
169
|
+
%i(except only with).each do |attr|
|
170
|
+
define_method attr do |*arguments|
|
171
|
+
attr_specs = specifications.send(attr)
|
172
|
+
if arguments.empty?
|
173
|
+
return attr_specs.collect(&:value)
|
174
|
+
end
|
175
|
+
|
176
|
+
attr_specs << Specification.new(*arguments)
|
177
|
+
begin
|
178
|
+
specifications.validate! attr
|
179
|
+
rescue
|
180
|
+
attr_specs.pop
|
181
|
+
raise
|
182
|
+
end
|
183
|
+
|
184
|
+
self
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# The directory from which source files are autoloaded.
|
189
|
+
#
|
190
|
+
# Defaults to the directory corresponding to the +__FILE__+ of
|
191
|
+
# _#host_binding_. For example, if <code>eval('__FILE__',
|
192
|
+
# host_binding)</code> evaluates to +'/absolute/path/to/my_awesome_gem.rb'+,
|
193
|
+
# then the default value of _#from_ is +'/absolute/path/to/my_awesome_gem'+.
|
194
|
+
#
|
195
|
+
# @param [String] value a source directory path; optional
|
196
|
+
#
|
197
|
+
# @return [String] if _value_ is +nil+, the source directory
|
198
|
+
# @return [Autoloader] if _value_ is not +nil+, the _Autoloader_
|
199
|
+
#
|
200
|
+
# @raise [ArgumentError] _value_ is a relative path
|
201
|
+
#
|
202
|
+
# @see #autoload!
|
203
|
+
# @see #host_binding
|
204
|
+
def from(value=nil)
|
205
|
+
return((@from && @from.path) || default_from) if value.nil?
|
206
|
+
|
207
|
+
# Validate value.
|
208
|
+
@from = LoadPathedDirectory.new(value)
|
209
|
+
|
210
|
+
self
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
attr_reader :specifications
|
216
|
+
|
217
|
+
def constant_full_name(constant_name)
|
218
|
+
"#{host_eval 'name rescue nil'}::#{constant_name}"
|
219
|
+
end
|
220
|
+
|
221
|
+
def default_from
|
222
|
+
filename = host_source_filename
|
223
|
+
dirname = ::File.dirname(filename)
|
224
|
+
basename = ::File.basename(filename, ::File.extname(filename))
|
225
|
+
::File.join dirname, basename
|
226
|
+
end
|
227
|
+
|
228
|
+
def establish_autoload(constant_name, source_filename)
|
229
|
+
host_eval "autoload #{constant_name.to_sym.inspect}, #{source_filename.inspect}"
|
230
|
+
end
|
231
|
+
|
232
|
+
def existing_autoload?(constant_name)
|
233
|
+
host_eval "autoload? #{constant_name.to_sym.inspect}"
|
234
|
+
end
|
235
|
+
|
236
|
+
def existing_constant?(constant_name)
|
237
|
+
host_eval "constants.include? #{constant_name.to_sym.inspect}"
|
238
|
+
end
|
239
|
+
|
240
|
+
def from_load_pathed_directory
|
241
|
+
@from || LoadPathedDirectory.new(default_from)
|
242
|
+
end
|
243
|
+
|
244
|
+
def host_eval(statement)
|
245
|
+
# TODO: Why does adding the third and fourth arguments break Kernel#eval ?
|
246
|
+
# ::Kernel.eval statement, host_binding, __FILE__, __LINE__
|
247
|
+
::Kernel.eval statement, host_binding
|
248
|
+
end
|
249
|
+
|
250
|
+
def host_source_filename
|
251
|
+
host_eval '::File.expand_path __FILE__'
|
252
|
+
end
|
253
|
+
|
254
|
+
def host_source_location
|
255
|
+
host_eval('[__FILE__, __LINE__]').collect(&:to_s).join ':'
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|