mobiusloop 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/README.md +179 -117
  4. data/bin/a2h +17 -0
  5. data/bin/bcat +17 -0
  6. data/bin/btee +17 -0
  7. data/bin/coderay +17 -0
  8. data/bin/coveralls +17 -0
  9. data/bin/htmldiff +17 -0
  10. data/bin/kramdown +17 -0
  11. data/bin/ldiff +17 -0
  12. data/bin/{mobiusloop → mobius} +0 -0
  13. data/bin/nokogiri +17 -0
  14. data/bin/pry +17 -0
  15. data/bin/rackup +17 -0
  16. data/bin/rake +17 -0
  17. data/bin/rspec +17 -0
  18. data/bin/term_cdiff +17 -0
  19. data/bin/term_colortab +17 -0
  20. data/bin/term_decolor +17 -0
  21. data/bin/term_display +17 -0
  22. data/bin/term_mandel +17 -0
  23. data/bin/thor +17 -0
  24. data/bin/tilt +17 -0
  25. data/bin/yard +17 -0
  26. data/bin/yardoc +17 -0
  27. data/bin/yri +17 -0
  28. data/build +7 -0
  29. data/docs/Outcome_Options.docx +0 -0
  30. data/examples/mobiusloop/increase_readers.goal +6 -8
  31. data/gherkin-languages.json +6 -3
  32. data/lib/cucumber/cli/main.rb +1 -1
  33. data/lib/cucumber/project_initializer.rb +11 -6
  34. data/lib/mobiusloop/config/config.yml +9 -0
  35. data/lib/mobiusloop/config/config_file.rb +20 -0
  36. data/lib/mobiusloop/hooks.rb +22 -0
  37. data/lib/mobiusloop/mobius_steps.rb +7 -5
  38. data/lib/mobiusloop/objective.rb +67 -1
  39. data/lib/mobiusloop/outcome.rb +68 -12
  40. data/lib/mobiusloop/scale.rb +3 -16
  41. data/lib/mobiusloop/version +1 -0
  42. data/mobiusloop.gemspec +17 -15
  43. data/mobiusloop.gemspec.lock +10 -0
  44. data/spec/mobiusloop/measure_spec.rb +17 -0
  45. data/spec/mobiusloop/objective_spec.rb +52 -0
  46. data/spec/mobiusloop/outcome_spec.rb +59 -0
  47. data/spec/mobiusloop/page_response_scale_spec.rb +16 -0
  48. data/spec/mobiusloop/scale_error.rb +16 -0
  49. data/spec/mobiusloop/scale_sample.rb +18 -0
  50. data/spec/mobiusloop/scale_spec.rb +23 -0
  51. metadata +183 -25
  52. data/CONTRIBUTING.md +0 -68
@@ -562,7 +562,8 @@
562
562
  ],
563
563
  "given": [
564
564
  "* ",
565
- "Given "
565
+ "Given ",
566
+ "Baseline: "
566
567
  ],
567
568
  "name": "English",
568
569
  "native": "English",
@@ -577,11 +578,13 @@
577
578
  ],
578
579
  "then": [
579
580
  "* ",
580
- "Then "
581
+ "Then ",
582
+ "Scale: "
581
583
  ],
582
584
  "when": [
583
585
  "* ",
584
- "When "
586
+ "When ",
587
+ "Target: "
585
588
  ]
586
589
  },
587
590
  "en-Scouse": {
@@ -46,7 +46,7 @@ module Cucumber
46
46
  @err.puts("Couldn't open #{e.path}")
47
47
  exit_unable_to_finish
48
48
  rescue FeatureFolderNotFoundException => e
49
- @err.puts(e.message + ". You can use `mobiusloop --init` to get started.")
49
+ @err.puts("Could not find goals directory. Try this: mobiusloop --init")
50
50
  exit_unable_to_finish
51
51
  rescue ProfilesNotDefinedError, YmlLoadError, ProfileNotFound => e
52
52
  @err.puts(e.message)
@@ -3,6 +3,8 @@ module Cucumber
3
3
  # Generates generic file structure for a cucumber project
4
4
  class ProjectInitializer
5
5
  def run
6
+ puts "file expand path = #{File.expand_path(__FILE__)}"
7
+ @version = File.read(File.expand_path("../../../lib/mobiusloop/version", __FILE__))
6
8
  # normal cucumber init, replacing /features for /goals
7
9
  create_directory('goals')
8
10
  create_directory('goals/step_definitions')
@@ -10,7 +12,9 @@ module Cucumber
10
12
  create_file('goals/support/env.rb')
11
13
 
12
14
  # extra mobiusloop initiialization
13
- copy_step_defs('mobius_steps.rb', 'goals/step_definitions') # install mobius_steps.rb step definition
15
+ copy_step_defs('mobius_steps.rb', 'goals/step_definitions')
16
+ copy_step_defs('hooks.rb', 'goals/support')
17
+ copy_step_defs('config/config.yml', 'goals/support')
14
18
  copy_step_defs('scales/page_response_scale.rb', 'goals/step_definitions') # example scale to measure response time
15
19
  copy_gherkin_languages('gherkin-languages.json') # copy modified gherkin-languages.json file to gherkin gem(s)
16
20
  copy_example_file('total_articles_scale.rb', 'goals/step_definitions')
@@ -22,22 +26,24 @@ module Cucumber
22
26
 
23
27
  def copy_step_defs(spec_file, target)
24
28
  gem_dir = `gem environment gemdir`
25
- steps_file = gem_dir.gsub("\n","") + "/gems/mobiusloop-?.?.?/lib/mobiusloop/" + spec_file
29
+ steps_file = gem_dir.gsub("\n","") + "/gems/mobiusloop-#{@version}/lib/mobiusloop/" + spec_file
26
30
  report_copying(spec_file, target)
27
31
  copy_file(steps_file, target)
28
32
  end
29
33
 
34
+ # note: this is penned to v3.2.0 of gherkin because 4.0.0 was causing issues
35
+ # this is not very elegant, but works for now
30
36
  def copy_gherkin_languages(gherkin_file)
31
37
  gem_dir = `gem environment gemdir`
32
- source_gherkin = gem_dir.gsub("\n","") + "/gems/mobiusloop-?.?.?/" + gherkin_file
33
- target_gherkin = gem_dir.gsub("\n","") + "/gems/gherkin-?.?.?/lib/gherkin/"
38
+ source_gherkin = gem_dir.gsub("\n","") + "/gems/mobiusloop-#{@version}/" + gherkin_file
39
+ target_gherkin = gem_dir.gsub("\n","") + "/gems/gherkin-3.2.0/lib/gherkin/"
34
40
  report_copying(gherkin_file, target_gherkin)
35
41
  copy_file(source_gherkin, target_gherkin)
36
42
  end
37
43
 
38
44
  def copy_example_file(file, target)
39
45
  gem_dir = `gem environment gemdir`
40
- steps_file = gem_dir.gsub("\n","") + "/gems/mobiusloop-?.?.?/examples/mobiusloop/" + file
46
+ steps_file = gem_dir.gsub("\n","") + "/gems/mobiusloop-#{@version}/examples/mobiusloop/" + file
41
47
  report_copying(file, target)
42
48
  copy_file(steps_file, target)
43
49
  end
@@ -63,7 +69,6 @@ module Cucumber
63
69
  end
64
70
 
65
71
  report_exists(file_name) || return if File.exists?(file_name)
66
-
67
72
  report_creating(file_name)
68
73
  FileUtils.send file_type, file_name
69
74
  end
@@ -0,0 +1,9 @@
1
+ # This contains configuration options
2
+
3
+ measures:
4
+ save: false # whether to save each run as .json file under goals/measures/
5
+ # set to true to enable, false to disable
6
+ progress:
7
+ good_percent: 10 # this percent or less deviation from target is "green"
8
+ bad_percent: 30 # this percent or greater deviation from target is "red"
9
+ # good must be less than bad and between these two numbers is "yellow"
@@ -0,0 +1,20 @@
1
+ require 'yaml'
2
+
3
+ class ConfigFile
4
+
5
+ # Loads YAML file at config_path and returns Hash of key/value pairs
6
+ def self.load_template(config_file)
7
+ if config_file == nil || File.exists?(config_file) == false
8
+ raise SyntaxError, "config file: #{File.expand_path(config_file)} not found"
9
+ end
10
+
11
+ #puts " config: #{File.expand_path(config_file)}".colorize(:blue)
12
+ config_yaml = YAML.load_file(config_file)
13
+ config = Hash.new
14
+ for item in config_yaml
15
+ config.store(item[0], item[1])
16
+ end
17
+ return config
18
+ end
19
+
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'mobiusloop/objective'
2
+ require 'mobiusloop/config/config_file'
3
+
4
+ Before do |scenario|
5
+ Objective.add_current(scenario.feature.name)
6
+ #puts "scenario name #{scenario.name}"
7
+ end
8
+
9
+ After do |scenario|
10
+ config_dir = File.dirname(File.absolute_path(__FILE__))
11
+ config_filepath = "#{config_dir}/config.yml"
12
+ config = ConfigFile.load_template(config_filepath)
13
+
14
+ if config.has_key?('measures') && config['measures'].has_key?('save')
15
+ persist = config['measures']['save']
16
+ if persist
17
+ objective = Objective.get_current
18
+ objective.save
19
+ end
20
+ end
21
+
22
+ end
@@ -1,5 +1,6 @@
1
1
  require 'mobiusloop/outcome'
2
2
  require 'mobiusloop/scale'
3
+ require 'mobiusloop/objective'
3
4
 
4
5
  @name = nil
5
6
  @outcome = nil
@@ -21,16 +22,17 @@ Given(/^a target of (\d+) "([^"]*)" by "([^"]*)"$/) do |arg1, arg2, arg3|
21
22
  @target_date = arg3
22
23
  end
23
24
 
24
- When(/^we measure progress with "([^"]*)"$/) do |arg1|
25
+ # Replace outcome name with scenario name (in hooks.rb)
26
+ Then(/^measure progress with "([^"]*)"$/) do |arg1|
27
+ objective = Objective.get_current
25
28
  scale_class = arg1.gsub(/\s+/, "") # turns 'My Custom Scale' into MyCustomScale
26
29
  @outcome = Outcome.new(@name, Object::const_get(scale_class).new())
30
+ objective.add_outcome(@outcome)
31
+
27
32
  @outcome.baseline = @baseline.to_i
28
33
  @outcome.baseline_date = @baseline_date
29
34
  @outcome.target = @target.to_i
30
35
  @outcome.target_date = @target_date
31
36
  puts @outcome.measure
32
- end
33
-
34
- Then(/^report progress towards targets$/) do
35
37
  puts @outcome.report
36
- end
38
+ end
@@ -1,10 +1,76 @@
1
+ require 'json'
2
+ require 'fileutils'
1
3
 
2
4
  class Objective
3
5
 
4
- attr_accessor :name
6
+ @@objectives = Array.new # class array holds instances of objectives one per .goal file
7
+
8
+ # needs to just be adding 1
9
+ def self.add_current(name)
10
+ objective = Objective.new(name)
11
+ if @@objectives.include?(objective) == false
12
+ @@objectives.push(objective)
13
+ end
14
+ self.get_current
15
+ end
16
+
17
+ def self.get_current
18
+ @@objectives.last
19
+ end
20
+
21
+ def self.get_all
22
+ @@objectives
23
+ end
24
+
25
+
26
+ attr_reader :name
27
+ attr_reader :outcomes
5
28
 
6
29
  def initialize(name)
7
30
  @name = name
31
+ @outcomes = []
32
+ end
33
+
34
+
35
+ def add_outcome(outcome)
36
+ @outcomes.push(outcome)
37
+ end
38
+
39
+
40
+ # persist the results. May be overridden by subclasses
41
+ def save
42
+ filename = Time.now.strftime("measures-%Y-%m-%d-%H%M%S.json")
43
+ dirname = "goals/measures"
44
+ hash = { "objective" => @name}
45
+ @outcomes.each {|o|
46
+ hash.store("outcome", o.name)
47
+ hash.store("scale", "#{o.scale.class.name}.rb")
48
+ hash.store("baseline", o.baseline)
49
+ hash.store("baseline date", o.baseline_date)
50
+ hash.store("target", o.target)
51
+ hash.store("target date", o.target_date)
52
+ hash.store("measure", o.last_measure.value)
53
+ hash.store("measure date", Time.now.strftime("%b %-d, %Y"))
54
+ }
55
+
56
+ if !File.exists? dirname
57
+ FileUtils.mkdir_p dirname
58
+ end
59
+
60
+ File.open("#{dirname}/#{filename}","a") do |f|
61
+ f.write(JSON.pretty_generate(hash))
62
+ f.write("\n")
63
+ end
64
+ end
65
+
66
+
67
+ def ==(other)
68
+ other.class == self.class && other.name == self.name
69
+ end
70
+
71
+
72
+ def state
73
+ self.instance_variables.map { |variable| self.instance_variable_get variable }
8
74
  end
9
75
 
10
76
  end
@@ -1,30 +1,39 @@
1
1
  # Represents a specific outcome or key result to be achieved
2
2
 
3
+ require 'json'
4
+ require 'colorize'
5
+
3
6
  class Outcome
4
7
 
8
+ FILENAME = 'measures'
9
+
5
10
  attr_reader :name
6
11
  attr_accessor :baseline
7
12
  attr_accessor :baseline_date
8
13
  attr_accessor :target
9
14
  attr_accessor :target_date
10
15
  attr_reader :scale
16
+ attr_accessor :persist
17
+ attr_reader :last_measure
11
18
 
12
19
  # register a name (String) and scale (Scale) of measurement at creation time
13
- def initialize(name, scale)
20
+ def initialize(name, scale, persist = false)
14
21
  raise "name must not be nil" if name == nil
15
22
  raise "scale must be of type Scale" unless scale.is_a? Scale
16
23
  @name = name
17
24
  @scale = scale
25
+ @persist = persist
18
26
  end
19
27
 
20
28
  # perform the measurement using the provide scale
21
29
  # delegate the details to any class extending Scale
30
+ # TODO: Change measurement to benchmark
22
31
  def measure
23
32
  begin
24
33
  start_time = Time.now
25
- @scale.record_measurement
34
+ @last_measure = @scale.measure
26
35
  elapsed_time = (Time.now - start_time)
27
- return "Success! Measure is " + format_number(@scale.measurements.last) + " " + @name + " in #{elapsed_time.round(1)} seconds!"
36
+ return "Success! Measured " + format_number(@last_measure) + " " + @name + " in #{elapsed_time.round(1)} seconds!"
28
37
  rescue Exception => e
29
38
  return "Error! Measurement failed with message: " + e.message
30
39
  end
@@ -33,7 +42,6 @@ class Outcome
33
42
  # used to report progress towards goals
34
43
  def report
35
44
  report = ""
36
- # report << status
37
45
  report << report_progress
38
46
  report << report_remaining
39
47
  report
@@ -49,13 +57,13 @@ class Outcome
49
57
  end
50
58
  end
51
59
 
60
+
52
61
  private
53
62
 
54
63
  # report the status. Very simplistic now, need to validate if there is need
55
64
  # for this or we should just drop and let others determine status
56
65
  def status
57
66
  status = percent_progress - percent_schedule
58
-
59
67
  # this value of 10 is temporary. TODO: need to make this more flexible and explainable
60
68
  if status.abs < 10
61
69
  "Hooray! You are on track!\n"
@@ -66,21 +74,69 @@ class Outcome
66
74
 
67
75
  # report percentage progress towards the target
68
76
  def report_progress
69
- "#{percent_progress.round(0)}% progress to target using #{percent_schedule.round(0)}% of the time (" + progress_days.to_s + " days)\n"
77
+ config = ConfigFile.load_template("#{Dir.pwd}/goals/support/config.yml")
78
+
79
+ progress = percent_progress.round(0)
80
+ schedule = percent_schedule.round(0)
81
+ delta = (progress - schedule).abs
82
+ days = progress_days
83
+ result = ""
84
+
85
+ # TODO: FIX LOGIC, ADD MORE TEST CASES
86
+
87
+ if config.has_key?('progress') && config['progress'].has_key?('green_threshold') &&
88
+ config['progress'].has_key?('red_threshold')
89
+ good = config['progress']['green_threshold'].to_i
90
+ bad = config['progress']['red_threshold'].to_i
91
+
92
+ if (delta <= good)
93
+ result = "#{progress}% progress to target using #{schedule}% of the time (#{days} days)\n".colorize(:green)
94
+ elsif (delta >= bad)
95
+ result = "#{progress}% progress to target using #{schedule}% of the time (#{days} days)\n".colorize(:red)
96
+ else
97
+ result = "#{progress}% progress to target using #{schedule}% of the time (#{days} days)\n".colorize(:yellow)
98
+ end
99
+ end
100
+ result
70
101
  end
71
102
 
72
- # report remaining progress towards the target
73
103
  def report_remaining
74
- if percent_remaining.round(0) <= 0
75
- "Target achieved!"
76
- else
77
- "#{percent_remaining.round(0)}% remaining to target in #{remaining_days} days\n"
104
+ config = ConfigFile.load_template("#{Dir.pwd}/goals/support/config.yml")
105
+ days_remaining = remaining_days
106
+ remaining = percent_remaining.round(0)
107
+ progress = percent_progress.round(0)
108
+ schedule = percent_schedule.round(0)
109
+ delta = (progress - schedule).abs
110
+ result = ""
111
+
112
+ if config.has_key?('progress') &&
113
+ config['progress'].has_key?('good_percent') &&
114
+ config['progress'].has_key?('bad_percent')
115
+
116
+ good = config['progress']['good_percent'].to_i
117
+ bad = config['progress']['bad_percent'].to_i
118
+
119
+ # if delta is positive, green
120
+ # if delta is negative, but still within green threshold, green
121
+ # if delta is negative, and greater than red threshold, red
122
+ # else yellow
123
+
124
+ if (delta >= 0)
125
+ result = "#{remaining}% remaining to target in #{days_remaining} days\n".colorize(:green)
126
+ elsif (delta < 0 && delta <= good)
127
+ result = "#{remaining}% remaining to target in #{days_remaining} days\n".colorize(:green)
128
+ elsif (delta < 0 && delta >= bad)
129
+ result = "#{remaining}% remaining to target in #{days_remaining} days\n".colorize(:red)
130
+ else
131
+ result = "#{remaining}% remaining to target in #{days_remaining} days\n".colorize(:yellow)
132
+ end
78
133
  end
134
+ result
79
135
  end
80
136
 
81
137
  # percentage progress towards target as of now
82
138
  def percent_progress
83
- ((@scale.measurements.last.value.to_i - @baseline.to_i).to_f /
139
+ ((@last_measure.value.to_i - @baseline.to_i).to_f /
84
140
  (@target.to_i - @baseline.to_i).to_f * 100)
85
141
  end
86
142
 
@@ -2,21 +2,13 @@
2
2
  # Designed to be extended with the measure method implemented
3
3
  # by the specific
4
4
 
5
- class Scale
6
5
 
7
- attr_reader :measurements
6
+ class Scale
8
7
 
9
8
  # creates internal array to store measurements
10
- # if sub-classes need their own initialize logic
11
- # call super first
9
+ # by default, persisting measures is turned off
10
+ # if sub-classes need their own initialize logic call super first
12
11
  def initialize
13
- @measurements = Array.new
14
- end
15
-
16
- # performs measurement and adds to internal array
17
- def record_measurement()
18
- new_measure = measure
19
- add(new_measure)
20
12
  end
21
13
 
22
14
  # method to perform a measurement, should return an instance of Measure
@@ -24,9 +16,4 @@ class Scale
24
16
  raise "Someone forgot to override Scale.measure with their unique logic"
25
17
  end
26
18
 
27
- # adds a new measure to end of array
28
- def add(measure)
29
- @measurements.push(measure)
30
- end
31
-
32
19
  end
@@ -0,0 +1 @@
1
+ 0.1.5
@@ -10,23 +10,22 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "http://mobiusloop.com"
11
11
  s.platform = Gem::Platform::RUBY
12
12
  s.required_ruby_version = ">= 1.9.3"
13
- s.add_dependency 'cucumber-core', '~> 1.4.0'
14
- s.add_dependency 'builder', '>= 2.1.2'
15
- s.add_dependency 'diff-lcs', '>= 1.1.3'
16
- s.add_dependency 'gherkin', '~> 3.2'
13
+ s.add_dependency 'cucumber-core', '~> 1.4', '= 1.4.0'
14
+ s.add_dependency 'builder', '~> 3.2', '>= 3.2.0'
15
+ s.add_dependency 'diff-lcs', '~> 1.1', '>= 1.1.3'
16
+ s.add_dependency 'gherkin', '~> 3.2', '= 3.2.0'
17
17
  s.add_dependency 'multi_json', '>= 1.7.5', '< 2.0'
18
- s.add_dependency 'multi_test', '>= 0.1.2'
19
- s.add_dependency 'cucumber-wire', '~> 0.0.1'
20
-
18
+ s.add_dependency 'multi_test', '~> 0.1', '>= 0.1.2'
19
+ s.add_dependency 'cucumber-wire', '~> 0.0.1', '>= 0.0.1'
21
20
  s.add_development_dependency 'aruba', '~> 0.6.1'
22
21
  s.add_development_dependency 'json', '~> 1.7'
23
22
  s.add_development_dependency 'nokogiri', '~> 1.5'
24
- s.add_development_dependency 'rake', '>= 0.9.2'
25
- s.add_development_dependency 'rspec', '>= 3.0'
26
- s.add_development_dependency 'simplecov', '>= 0.6.2'
23
+ s.add_development_dependency 'rake', '~> 11.1', '>= 11.1'
24
+ s.add_development_dependency 'simplecov', '~> 0.6', '>= 0.6.2'
27
25
  s.add_development_dependency 'coveralls', '~> 0.7'
28
- s.add_development_dependency 'syntax', '>= 1.0.0'
29
- s.add_development_dependency 'pry'
26
+ s.add_development_dependency 'syntax', '~> 1.0', '>= 1.0.0'
27
+ s.add_development_dependency 'pry', '~> 0'
28
+ s.add_development_dependency 'rspec', '~> 3.0', '>= 3.0'
30
29
 
31
30
  # For Documentation:
32
31
  s.add_development_dependency 'bcat', '~> 0.6.2'
@@ -34,9 +33,12 @@ Gem::Specification.new do |s|
34
33
  s.add_development_dependency 'yard', '~> 0.8.0'
35
34
 
36
35
  # Needed for examples (rake examples)
37
- s.add_development_dependency 'capybara', '>= 2.1'
38
- s.add_development_dependency 'rack-test', '>= 0.6.1'
39
- s.add_development_dependency 'sinatra', '>= 1.3.2'
36
+ s.add_development_dependency 'capybara', '~> 2.1', '>= 2.1'
37
+ s.add_development_dependency 'rack-test', '~> 0.6', '>= 0.6.1'
38
+ s.add_development_dependency 'sinatra', '~> 1.3', '>= 1.3.2'
39
+
40
+ # Added by Mobius
41
+ s.add_dependency 'colorize', '~> 0.8.1'
40
42
 
41
43
  s.rubygems_version = ">= 1.6.1"
42
44
  s.files = `git ls-files`.split("\n").reject {|path| path =~ /\.gitignore$/ }