Class: EolScenario

Inherits:
Object
  • Object
show all
Includes:
IndifferentVariableHash
Defined in:
lib/eol_scenarios/eol_scenario.rb,
lib/eol_scenarios/spec.rb

Overview

a Scenario is some set of data/logic that can be loaded up easily to run an application against.

if you need to enter abunchof data manually into the a website to test something you’re working on, this is a good candidate for a scenario.

we can also use scenarios for loading up the base foundation of data that’s required to load the web application

TODO define what is public/private and document public API in README and

actually give private methods a private visibility

Defined Under Namespace

Modules: Spec

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_path) ⇒ EolScenario

Returns a new instance of EolScenario.



19
20
21
22
# File 'lib/eol_scenarios/eol_scenario.rb', line 19

def initialize file_path
  @file_path = file_path
  source_code # does some parsing ... eager load this!  otherwise Scenario[:first].some_var won't work
end

Class Attribute Details

.before_blocksObject

an array of the paths where scenarios can be found

any .rb file found in these directories is assumed to be a scenario



103
104
105
# File 'lib/eol_scenarios/eol_scenario.rb', line 103

def before_blocks
  @before_blocks
end

.load_pathsObject

an array of the paths where scenarios can be found

any .rb file found in these directories is assumed to be a scenario



103
104
105
# File 'lib/eol_scenarios/eol_scenario.rb', line 103

def load_paths
  @load_paths
end

.verboseObject

an array of the paths where scenarios can be found

any .rb file found in these directories is assumed to be a scenario



103
104
105
# File 'lib/eol_scenarios/eol_scenario.rb', line 103

def verbose
  @verbose
end

Instance Attribute Details

#file_pathObject

Returns the value of attribute file_path.



17
18
19
# File 'lib/eol_scenarios/eol_scenario.rb', line 17

def file_path
  @file_path
end

Class Method Details

.[](*names) ⇒ Object

returns a scenario by name, eg. Scenario

if 1 name is passed in, we’ll return that scenario or nil

if more than 1 name is passed in, we’ll return an array of scenarios (or an empty array)



140
141
142
143
144
145
146
147
# File 'lib/eol_scenarios/eol_scenario.rb', line 140

def [] *names
  # puts "Scenario#{ names.inspect }" if Scenario.verbose
  if names.length == 1
    all.find {|scenario| scenario.name.downcase == names.first.to_s.downcase }
  else
    names.map {|name| self[ name ] }.compact
  end
end

.allObject

returns all Scenarios found using Scenario#load_paths



106
107
108
109
110
111
112
113
# File 'lib/eol_scenarios/eol_scenario.rb', line 106

def all
  load_paths.inject([]) do |all_scenarios, load_path|
    Dir[ File.join(load_path, '**', '*.rb') ].each do |found_scenario_file|
      all_scenarios << EolScenario.new(found_scenario_file)
    end
    all_scenarios
  end
end

.before(&block) ⇒ Object

run some block of code before any scenarios run

good for last-minute require statements and whatnot



128
129
130
131
# File 'lib/eol_scenarios/eol_scenario.rb', line 128

def before &block
  @before_blocks ||= []
  @before_blocks << block if block
end

.load(*scenarios) ⇒ Object

loads a Scenario, evaluating its code

we do this here so we can easily eval in a certain context, if we want to add a context later

Scenario.load @scenario1, @scenario2
Scenario.load :names, 'work', :too


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/eol_scenarios/eol_scenario.rb', line 157

def load *scenarios
puts "EolScenario.load(#{ scenarios.map {|s| s.to_s }.join(', ') })" if EolScenario.verbose
  @before_blocks.each { |b| b.call } if @before_blocks and not @before_blocks.empty?
  
  # TODO should be able to define some block that scenarios get evaluated in!
  #      or some things that scenarios might want to require or ...

  options = ( scenarios.last.is_a?(Hash) ) ? scenarios.pop : { }
  options[:unique] ||= true # whether each scenario passed has to be unique ... will likely change this to be true by default
  options[:whiny] = true if options[:whiny].nil?

  # make sure everything is actually a Scenario object
  #
  # after this, we can safely assume that everything is a scenario!
  #
  scenarios.map! do |scenario|
    scenario.is_a?(EolScenario) ? scenario : self[scenario]
  end
  scenarios.compact!

  scenarios = scenarios.inject([]) do |all, scenario|
    all += EolScenario[ nil, *scenario.dependencies ] if scenario.dependencies
    all << scenario
    all
  end
  scenarios.compact!

  puts "[ after dependencies, scenarios => #{ scenarios.map {|s| s.to_s }.join(', ') } ]" if EolScenario.verbose

  scenarios = scenarios.inject([]) do |all, scenario|
    existing_scenario = all.find {|s| s.name == scenario.name }
    if existing_scenario
      # the last scenario with the given name "wins" (but we need to persist order)
      index_of_existing_scenario = all.index existing_scenario
      all.delete_at index_of_existing_scenario
      all.insert index_of_existing_scenario, scenario
    else
      all << scenario
    end
    all
  end if options[:unique]

  puts "scenarios to load: #{ scenarios.map {|s| s.to_s }.join(', ') }" if EolScenario.verbose
  scenarios.each do |scenario|
    scenario = self[scenario] unless scenario.is_a?EolScenario # try getting using self[] if not a scenario
    puts "... loading #{ scenario.name } (#{ scenario.summary })" if EolScenario.verbose
    begin
      if scenario.is_a? EolScenario
        puts "loading scenario: #{ scenario.file_path }" if EolScenario.verbose

        # TODO update to eval ... should load in a custom context ...
        #      the eval should also catch exceptions and print the 
        #      line number that threw the exception, etc etc
        Kernel::load scenario.file_path
      else
        puts "Unsure how to load scenario: #{ scenario.inspect }" if options[:whiny]
      end
    rescue => ex
      puts "An Exception was thrown by scenario: #{ scenario.name }" if options[:whiny]
      raise ex
    end
  end
end

Instance Method Details

#descriptionObject



76
77
78
# File 'lib/eol_scenarios/eol_scenario.rb', line 76

def description
  header.gsub(/^#* ?/, '').gsub(/^---.*/m, '').strip # gets rid of comment hashes and yaml
end

#description_without_summaryObject



80
81
82
83
84
# File 'lib/eol_scenarios/eol_scenario.rb', line 80

def description_without_summary
  parts = description.split("\n")
  parts.shift
  parts.join("\n")
end

#first_lineObject



56
57
58
# File 'lib/eol_scenarios/eol_scenario.rb', line 56

def first_line
  header.split("\n").first #.gsub(/^#* ?/, '')
end

#headerObject

Comment header, any comments at the top of the source code



92
93
94
# File 'lib/eol_scenarios/eol_scenario.rb', line 92

def header
  source_code.gsub /\n^[^#].*/m, ''
end

#infoObject

returns a formatted string, showing information about this current scenario



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/eol_scenarios/eol_scenario.rb', line 31

def info
  str = <<INFO
Scenario: #{ name }
Summary: #{ summary }
Description: #{ description_without_summary }
INFO
  variables.each do |key, value|
    str << "#{ key }: #{ value.inspect }\n"
  end
  str
end

#loadObject

evaluates the code of the scenario



87
88
89
# File 'lib/eol_scenarios/eol_scenario.rb', line 87

def load
  self.class.load self # pass the loading off to the class
end

#nameObject Also known as: to_s



24
25
26
# File 'lib/eol_scenarios/eol_scenario.rb', line 24

def name
  File.basename(file_path).sub(/\.rb$/, '')
end

#source_codeObject



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/eol_scenarios/eol_scenario.rb', line 60

def source_code
  unless @source_code
    # the first time we read in the source code, 
    # see if there are any variables in the header 
    # and, if so, set them via IndifferentVariableHash
    @source_code = File.read file_path
    yaml_frontmatter = header.gsub(/^#* ?/, '')[/^---.*/m]
    if yaml_frontmatter
      require 'yaml'
      header_variables = YAML::load(yaml_frontmatter)
      variables.merge!(header_variables) if header_variables
    end
  end
  @source_code
end

#summaryObject

if the first line of the scenario’s source code is a comment, we use it as the scenario’s summary

ideally, all scenarios should have a short simple summary



48
49
50
51
52
53
54
# File 'lib/eol_scenarios/eol_scenario.rb', line 48

def summary
  if first_line =~ /^#/
    first_line.sub(/^#*/, '').strip
  else
    ''
  end
end