diff --git a/src/spectator/context_definitions.cr b/src/spectator/context_definitions.cr new file mode 100644 index 0000000..ef4a2bd --- /dev/null +++ b/src/spectator/context_definitions.cr @@ -0,0 +1,6 @@ +module Spectator + module ContextDefinitions + ALL = {} of Path => Object + MAPPING = {} of String => Context + end +end diff --git a/src/spectator/dsl.cr b/src/spectator/dsl.cr index c3fafa7..d74bd64 100644 --- a/src/spectator/dsl.cr +++ b/src/spectator/dsl.cr @@ -41,23 +41,24 @@ module Spectator end macro context(what, type = "Context", &block) + {% parent_module = @type %} {% safe_name = what.id.stringify.chars.map { |c| SPECIAL_CHAR_MAPPING[c] || c }.join("").gsub(/\W+/, "_") %} {% module_name = (type.id + safe_name.camelcase).id %} - {% context_module = CONTEXT_MODULE %} - {% parent_given_vars = GIVEN_VARIABLES %} + {% absolute_module_name = [parent_module, module_name].join("::").id %} + {% what_arg = what.is_a?(StringLiteral) ? what : what.stringify %} + {% parent_given = ::Spectator::ContextDefinitions::ALL[parent_module.id][:given] %} module {{module_name.id}} include ::Spectator::DSL - PARENT_CONTEXT = {{context_module.id}}::CURRENT_CONTEXT - CURRENT_CONTEXT = ::Spectator::Context.new({{what.is_a?(StringLiteral) ? what : what.stringify}}, PARENT_CONTEXT) - - CONTEXT_MODULE = {{context_module.id}}::{{module_name.id}} - GIVEN_VARIABLES = [ - {{ parent_given_vars.join(", ").id }} - ]{% if parent_given_vars.empty? %} of Object{% end %} + {% ::Spectator::ContextDefinitions::ALL[absolute_module_name] = { + name: module_name, + parent: parent_module, + given: parent_given.map { |e| e } # Duplicate elements without dup method. + } %} + ::Spectator::ContextDefinitions::MAPPING[{{absolute_module_name.stringify}}] = Context.new({{what_arg}}, ::Spectator::ContextDefinitions::MAPPING[{{parent_module.stringify}}]) module Locals - include {{context_module.id}}::Locals + include {{parent_module}}::Locals {% if what.is_a?(Path) %} def described_class @@ -71,9 +72,10 @@ module Spectator end macro it(description, &block) + {% parent_module = @type %} {% safe_name = description.id.stringify.chars.map { |c| SPECIAL_CHAR_MAPPING[c] || c }.join("").gsub(/\W+/, "_") %} {% class_name = (safe_name.camelcase + "Example").id %} - {% given_vars = GIVEN_VARIABLES %} + {% given_vars = ::Spectator::ContextDefinitions::ALL[parent_module.id][:given] %} {% var_names = given_vars.map { |v| v[:name] } %} class {{class_name.id}} < ::Spectator::Example include Locals @@ -104,15 +106,16 @@ module Spectator end end + %current_context = ::Spectator::ContextDefinitions::MAPPING[{{parent_module.stringify}}] {% if given_vars.empty? %} - CURRENT_CONTEXT.examples << {{class_name.id}}.new(CURRENT_CONTEXT) + %current_context.examples << {{class_name.id}}.new(%current_context) {% else %} {% for given_var in given_vars %} {% var_name = given_var[:name] %} {% collection = given_var[:collection] %} {{collection}}.each do |{{var_name}}| {% end %} - CURRENT_CONTEXT.examples << {{class_name.id}}.new(CURRENT_CONTEXT, {{var_names.join(", ").id}}) + %current_context.examples << {{class_name.id}}.new(%current_context, {{var_names.join(", ").id}}) {% for given_var in given_vars %} end {% end %} @@ -154,9 +157,11 @@ module Spectator end macro given(collection, &block) + {% parent_module = @type %} context({{collection}}, "Given") do {% var_name = block.args.empty? ? "value".id : block.args.first %} - {% if GIVEN_VARIABLES.find { |v| v[:name] == var_name.id } %} + {% given_vars = ::Spectator::ContextDefinitions::ALL[parent_module.id][:given] %} + {% if given_vars.find { |v| v[:name] == var_name.id } %} {% raise "Duplicate given variable name \"#{var_name.id}\"" %} {% end %} @@ -181,30 +186,30 @@ module Spectator end end - {% GIVEN_VARIABLES << {name: var_name, collection: collection, setter: setter} %} + {% given_vars << {name: var_name, collection: collection, setter: setter} %} {{block.body}} end end macro before_all(&block) - CURRENT_CONTEXT.before_all_hooks << -> {{block}} + ::Spectator::ContextDefinitions::MAPPING[{{parent_module.stringify}}].before_all_hooks << -> {{block}} end macro before_each(&block) - CURRENT_CONTEXT.before_each_hooks << -> {{block}} + ::Spectator::ContextDefinitions::MAPPING[{{parent_module.stringify}}].before_each_hooks << -> {{block}} end macro after_all(&block) - CURRENT_CONTEXT.after_all_hooks << -> {{block}} + ::Spectator::ContextDefinitions::MAPPING[{{parent_module.stringify}}].after_all_hooks << -> {{block}} end macro after_each(&block) - CURRENT_CONTEXT.after_each_hooks << -> {{block}} + ::Spectator::ContextDefinitions::MAPPING[{{parent_module.stringify}}].after_each_hooks << -> {{block}} end macro around_each(&block) - CURRENT_CONTEXT.around_each_hooks << -> {{block}} + ::Spectator::ContextDefinitions::MAPPING[{{parent_module.stringify}}].around_each_hooks << -> {{block}} end def include_examples diff --git a/src/spectator/examples.cr b/src/spectator/examples.cr index 1dccad5..ed577a2 100644 --- a/src/spectator/examples.cr +++ b/src/spectator/examples.cr @@ -4,9 +4,12 @@ module Spectator module Examples include ::Spectator::DSL - CURRENT_CONTEXT = ::Spectator::Context::ROOT - CONTEXT_MODULE = ::Spectator::Examples - GIVEN_VARIABLES = [] of Object + {% ::Spectator::ContextDefinitions::ALL[@type.id] = { + name: "ROOT", + parent: nil, + given: [] of Object + } %} + ::Spectator::ContextDefinitions::MAPPING[{{@type.stringify}}] = ::Spectator::Context::ROOT module Locals end