module YARD module Handlers module Lucid class FeatureHandler < Base handles CodeObjects::Lucid::Feature # This method is currently not needed. It would be needed if there was # further processing to be done on the features. def process end # Register, once, when all files are finished. This will trigger the # final matching of feature steps to step definitions and steps to # transforms. YARD::Parser::SourceParser.after_parse_list do |files,globals| YARD::Registry.all(:feature).each do |feature| FeatureHandler.match_steps_to_step_definitions(feature) end end class << self @@step_definitions = nil @@step_transforms = nil def match_steps_to_step_definitions(statement) # Create a cache of all of the steps and the transforms. @@step_definitions = cache(:stepdefinition) unless @@step_definitions @@step_transforms = cache(:steptransform) unless @@step_transforms if statement # For the background and the scenario, find the steps that have # definitions. process_scenario(statement.background) if statement.background statement.scenarios.each do |scenario| if scenario.outline? scenario.scenarios.each_with_index do |example,index| process_scenario(example) end else process_scenario(scenario) end end else log.warn "Empty feature file. A feature failed to process correctly or contains no feature" end rescue YARD::Handlers::NamespaceMissingError rescue Exception => exception log.error "Skipping feature because an error has occurred." log.debug "\n#{exception}\n#{exception.backtrace.join("\n")}\n" end # The cache is used to store all comparable items with their # compare_value as the key and the item as the value. This will # reject any compare values that contain escapes -- #{} -- because # that means the values have unpacked constants. def cache(type) YARD::Registry.all(type).inject({}) do |hash,item| hash[item.regex] = item if item.regex hash end end def process_scenario(scenario) scenario.steps.each {|step| process_step(step) } end def process_step(step) match_step_to_step_definition_and_transforms(step) end # Given a step object, attempt to match that step to a transform. def match_step_to_step_definition_and_transforms(step) @@step_definitions.each_pair do |stepdef,stepdef_object| stepdef_matches = step.value.match(stepdef) if stepdef_matches step.definition = stepdef_object stepdef_matches[-1..1].each do |match| @@step_transforms.each do |steptrans,steptransform_object| if steptrans.match(match) step.transforms << steptransform_object steptransform_object.steps << step end end end # A this point the step has been matched to the definition and # to any transforms. As a design note, if the step were to # match again, it would be possible to display any steps that # are considered ambiguous. # This would be a nice to have. break end end end end end end end end