autoloaded 1.2.0 → 1.3.0

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