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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/History.md +4 -0
  4. data/README.md +411 -60
  5. data/autoloaded.gemspec +19 -14
  6. data/lib/autoloaded.rb +104 -91
  7. data/lib/autoloaded/autoloader.rb +260 -0
  8. data/lib/autoloaded/compatibility/refine_and_using.rb +2 -0
  9. data/lib/autoloaded/constant.rb +5 -2
  10. data/lib/autoloaded/deprecation.rb +50 -0
  11. data/lib/autoloaded/inflection.rb +71 -0
  12. data/lib/autoloaded/load_pathed_directory.rb +112 -0
  13. data/lib/autoloaded/refine.rb +7 -1
  14. data/lib/autoloaded/refine/string.rb +7 -0
  15. data/lib/autoloaded/refine/string/to_source_filename.rb +12 -0
  16. data/lib/autoloaded/specification.rb +97 -0
  17. data/lib/autoloaded/specifications.rb +66 -0
  18. data/lib/autoloaded/version.rb +3 -1
  19. data/lib/autoloaded/warning.rb +125 -0
  20. data/spec/autoloaded/autoloader_spec.rb +469 -0
  21. data/spec/autoloaded/constant_spec.rb +0 -2
  22. data/spec/autoloaded/deprecation_spec.rb +23 -0
  23. data/spec/autoloaded/inflection_spec.rb +30 -0
  24. data/spec/autoloaded/load_pathed_directory_spec.rb +120 -0
  25. data/spec/autoloaded/refine/string/to_source_filename_spec.rb +0 -2
  26. data/spec/autoloaded/specification_spec.rb +98 -0
  27. data/spec/autoloaded/specifications_spec.rb +191 -0
  28. data/spec/autoloaded/version_spec.rb +0 -2
  29. data/spec/autoloaded/warning_spec.rb +115 -0
  30. data/spec/autoloaded_macro_sharedspec.rb +24 -0
  31. data/spec/autoloaded_spec.rb +277 -95
  32. data/spec/fixtures/autoloaded_with_conventional_filename.rb +3 -1
  33. data/spec/fixtures/autoloaded_with_conventional_filename/nested.rb +12 -1
  34. data/spec/fixtures/autoloaded_with_conventional_filename/nested/doubly_nested.rb +9 -0
  35. data/spec/fixtures/autoloaded_with_unconventional_filename.rb +12 -0
  36. data/spec/fixtures/autoloaded_with_unconventional_filename/N-est-ed.rb +7 -0
  37. data/spec/fixtures/autoloaded_with_unconventional_filename/nest_ed.rb +1 -0
  38. data/spec/fixtures/autoloaded_with_unconventional_filename/old_school_autoload.rb +5 -0
  39. data/spec/fixtures/not_autoloaded/nested.rb +1 -0
  40. data/spec/fixtures/old_api/autoloaded_with_conventional_filename.rb +10 -0
  41. data/spec/fixtures/old_api/autoloaded_with_conventional_filename/N-est-ed.rb +1 -0
  42. data/spec/fixtures/old_api/autoloaded_with_conventional_filename/nest_ed.rb +1 -0
  43. data/spec/fixtures/old_api/autoloaded_with_conventional_filename/nested.rb +5 -0
  44. data/spec/fixtures/old_api/autoloaded_with_conventional_filename/old_school_autoload.rb +5 -0
  45. data/spec/fixtures/{autoloaded_with_conventional_filename_only.rb → old_api/autoloaded_with_conventional_filename_only.rb} +1 -1
  46. data/spec/fixtures/{autoloaded_with_conventional_filename_only → old_api/autoloaded_with_conventional_filename_only}/nested.rb +0 -0
  47. data/spec/fixtures/{autoloaded_with_conventional_filename_only → old_api/autoloaded_with_conventional_filename_only}/old_school_autoload.rb +0 -0
  48. data/spec/fixtures/{autoloaded_with_unconventional_filenames.rb → old_api/autoloaded_with_unconventional_filenames.rb} +1 -1
  49. data/spec/fixtures/{autoloaded_with_unconventional_filenames → old_api/autoloaded_with_unconventional_filenames}/N-est-ed.rb +0 -0
  50. data/spec/fixtures/{autoloaded_with_unconventional_filenames → old_api/autoloaded_with_unconventional_filenames}/nest_ed.rb +0 -0
  51. data/spec/fixtures/{autoloaded_with_unconventional_filenames → old_api/autoloaded_with_unconventional_filenames}/old_school_autoload.rb +0 -0
  52. data/spec/fixtures/old_api/not_autoloaded.rb +6 -0
  53. data/spec/fixtures/old_api/not_autoloaded/nested.rb +1 -0
  54. data/spec/fixtures/old_api/not_autoloaded/old_school_autoload.rb +5 -0
  55. data/spec/matchers.rb +4 -33
  56. data/spec/spec_helper.rb +2 -0
  57. 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/version'
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
- Dynamically and flexibly loads source files in a
15
- directory when a corresponding constant is dereferenced.
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 = spec.summary +
18
- ' ' +
19
- <<-end_description.gsub(/^\s+/, '').gsub(/\n(?=\S)/, ' ').chomp
20
- Offers several advantages over other autoloading
21
- facilities such as those provided by the Ruby Core
22
- library and the ActiveSupport gem: (a) it does not
23
- require a separate `autoload` statement for each
24
- constant, and (b) it does not enforce CamelCase to
25
- snake_case correspondence between the names of constants
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
- # 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!
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 :Constant, 'autoloaded/constant'
93
- autoload :Refine, 'autoloaded/refine'
94
- autoload :VERSION, 'autoloaded/version'
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