Compiler/Line Number Metadata
wied03 opened this issue · 4 comments
Add an optional compiler helper that will add_special
call nodes for describe, context, it, etc.
The modified code will still call describe, call, etc. but it will add a caller
metadata attribute in implicitly to the arguments list. Caller should be an array with 1 element containing the filename and line number of the spec.
Will need to handle cases where:
- User has already supplied the caller
- Existing metadata being passed as a hash variable
- Existing metadata passed with { }
- Existing metadata passed with an implicit hash
- No metadata being passed
- No arguments at all (not sure if
describe;do;end
is valid)
It should be bootstrap compatible and configurable as to when this runs, some ideas:
- Only files ending in
_spec.rb
- Use a compiler setting (we do have access to the
compiler
object in the call node, we can add an additional setting) - Opt in (need to require the compiler patch, this one is not mutually exclusive)
Testing:
- Unit test with MRI - see note below
- Unit test with bootstrap/Opal compilation
- Maybe include in RSpec's specs so they can find location? It might be too difficult to test this so it might be easier to just have a separate integration test
Compiler test, the compiled version with the patch on:
describe 'foo' do
end
should equal this with the patch off:
describe 'foo', caller: ['/some/file/path.rb:123'] do
end
@elia - Sound interesting? I think it could start as a monkey patch but maybe the compiler could add a framework some day.
Does opal-rspec support latest opal master? If it does you can do it with a custom AST rewriter:
class RSpecMetadataRewriter < Opal::Rewriters::Base
RSPEC_METHODS = %i(describe context it)
def on_send(node)
node = super
recv, meth, *args = *node
if RSPEC_METHODS.include?(meth)
file = node.loc.expression.source_buffer.name
line = node.loc.line
column = node.loc.column
loc_metadata = "#{file}:#{line}:#{column}"
loc_metadata_pair = s(:pair, s(:sym, :loc), s(:str, loc_metadata))
if args.last.type == :iter
*args, iter = *args
end
if args.last.type == :hash
*args, last_hash_arg = *args
else
last_hash_arg = s(:hash)
end
pairs = *last_hash_arg
pairs += [loc_metadata_pair]
last_hash_arg = last_hash_arg.updated(nil, pairs)
args = [*args, last_hash_arg, iter].compact
node.updated(nil, [recv, meth, *args])
else
node
end
end
end
Opal::Rewriter::LIST << RSpecMetadataRewriter
test.rb:
def describe(*args)
puts "describe"
puts args
yield
end
def context(*args)
puts "context"
puts args
yield
end
def it(*args)
puts "it"
puts args
end
describe 'MyClass1', type: :some_type do
context 'when something happens' do
it 'does the following'
end
end
➜ opal git:(master) ✗ opal test.rb
describe
["MyClass1", {"type"=>"some_type", "loc"=>"test:18:0"}]
context
["when something happens", {"loc"=>"test:19:2"}]
it
["does the following", {"loc"=>"test:20:4"}]
➜ opal git:(master) ✗ opal ../opal/test.rb
describe
["MyClass1", {"type"=>"some_type", "loc"=>"../opal/test:18:0"}]
context
["when something happens", {"loc"=>"../opal/test:19:2"}]
it
["does the following", {"loc"=>"../opal/test:20:4"}]
It did but it's currently broken (Sprockets is the first reason).
I'm inclined to rely on source-maps for browser and possibly for node too.