est 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -19,8 +19,8 @@ Run it locally and read its output:
19
19
  $ est --help
20
20
  ```
21
21
 
22
- Every estimate should be in its own file, with `.yml` extension (YAML format).
23
- Here is an example of an estimate file for a simple web app:
22
+ Every estimate should be in its own file, with `.est` extension (YAML format).
23
+ Here is an example of an estimate file `simple.est` for a simple web app:
24
24
 
25
25
  ```yaml
26
26
  date: 19-12-2014
data/Rakefile CHANGED
@@ -66,6 +66,6 @@ Cucumber::Rake::Task.new(:features) do |t|
66
66
  Rake::Cleaner.cleanup_files(['coverage'])
67
67
  t.profile = 'travis'
68
68
  end
69
- Cucumber::Rake::Task.new(:"features:html") do |t|
69
+ Cucumber::Rake::Task.new(:'features:html') do |t|
70
70
  t.profile = 'html_report'
71
71
  end
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ * Copyright (c) 2014 TechnoPark Corp.
4
+ * Copyright (c) 2014 Yegor Bugayenko
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the 'Software'), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ -->
24
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
25
+ version="1.0" xmlns="http://www.w3.org/1999/xhtml">
26
+ <xsl:output method="text" omit-xml-declaration="yes"/>
27
+ <xsl:template match="/estimate">
28
+ <xsl:text>Total: </xsl:text>
29
+ <xsl:value-of select="total"/>
30
+ <xsl:text>&#10;</xsl:text>
31
+ <xsl:apply-templates select="ests/est"/>
32
+ </xsl:template>
33
+ <xsl:template match="ests/est">
34
+ <xsl:value-of select="date"/>
35
+ <xsl:text>: </xsl:text>
36
+ <xsl:value-of select="total"/>
37
+ <xsl:text> hours by </xsl:text>
38
+ <xsl:value-of select="author"/>
39
+ <xsl:text>&#10;</xsl:text>
40
+ </xsl:template>
41
+ </xsl:stylesheet>
@@ -28,7 +28,6 @@
28
28
  <xs:element name="date" type="xs:date"/>
29
29
  <xs:element name="author" type="xs:string"/>
30
30
  </xs:all>
31
- <xs:attribute name="id" use="required" type="xs:int"/>
32
31
  </xs:complexType>
33
32
  <xs:element name="estimate">
34
33
  <xs:complexType>
@@ -23,7 +23,7 @@
23
23
  -->
24
24
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
25
25
  version="1.0" xmlns="http://www.w3.org/1999/xhtml">
26
- <xsl:template match="/">
26
+ <xsl:template match="/estimate">
27
27
  <xsl:text disable-output-escaping="yes">&lt;!DOCTYPE html&gt;</xsl:text>
28
28
  <html lang="en">
29
29
  <head>
@@ -60,8 +60,16 @@
60
60
  <xsl:text>Total: </xsl:text>
61
61
  <xsl:value-of select="total"/>
62
62
  </p>
63
+ <xsl:apply-templates select="ests/est"/>
63
64
  </div>
64
65
  </body>
65
66
  </html>
66
67
  </xsl:template>
68
+ <xsl:template match="ests/est">
69
+ <xsl:value-of select="date"/>
70
+ <xsl:text>: </xsl:text>
71
+ <strong><xsl:value-of select="total"/></strong>
72
+ <xsl:text> hours by </xsl:text>
73
+ <xsl:value-of select="author"/>
74
+ </xsl:template>
67
75
  </xsl:stylesheet>
data/bin/est CHANGED
@@ -48,7 +48,7 @@ opts = Slop.parse(ARGV, strict: true, help: true) do
48
48
  on(
49
49
  't',
50
50
  'format',
51
- 'Format to use (xml|html)',
51
+ 'Format to use (xml|html|text)',
52
52
  argument: :required
53
53
  )
54
54
  end
@@ -69,16 +69,23 @@ Encoding.default_external = Encoding::UTF_8
69
69
  Encoding.default_internal = Encoding::UTF_8
70
70
  file = opts.file? ? File.new(opts[:file], 'w') : STDOUT
71
71
  output = Est::Base.new(opts).xml
72
- if opts[:format]
73
- if opts[:format] == 'html'
74
- xslt = File.join(
75
- File.dirname(File.dirname(__FILE__)),
76
- 'assets', 'est.xsl'
77
- )
78
- output = Nokogiri::XSLT(File.read(xslt)).transform(Nokogiri::XML(output))
79
- else
80
- fail 'invalid format, use html or xml' if opts[:format] != 'xml'
81
- end
82
- output
72
+ if opts[:format].nil? || opts[:format] == 'text'
73
+ xslt = File.join(
74
+ File.dirname(File.dirname(__FILE__)),
75
+ 'assets', 'est-text.xsl'
76
+ )
77
+ output = Nokogiri::XSLT(File.read(xslt)).transform(Nokogiri::XML(output))
78
+ elsif opts[:format] == 'html'
79
+ Est.log.info 'using HTML format'
80
+ xslt = File.join(
81
+ File.dirname(File.dirname(__FILE__)),
82
+ 'assets', 'est.xsl'
83
+ )
84
+ output = Nokogiri::XSLT(File.read(xslt)).transform(Nokogiri::XML(output))
85
+ elsif opts[:format] == 'xml'
86
+ Est.log.info 'using XML format'
87
+ else
88
+ fail 'invalid format, use html, text, or xml'
83
89
  end
84
90
  file << output
91
+ Est.log.info "output saved into #{file}"
@@ -0,0 +1,19 @@
1
+ date: 20-12-2014
2
+ author: Yegor Bugayenko
3
+ method: champions.pert
4
+ scope:
5
+ 1: ruby gem scaffolding
6
+ 2: integration tests with Cucumber
7
+ 3: user documentation
8
+ 4: first estimation model
9
+ 5: more estimation models
10
+ 6: continuous integration configs
11
+ champions:
12
+ 4:
13
+ worst-case: 6
14
+ best-case: 1
15
+ most-likely: 2
16
+ 5:
17
+ worst-case: 10
18
+ best-case: 2
19
+ most-likely: 4
@@ -11,10 +11,9 @@ Feature: Command Line Processing
11
11
  When I run bin/est with "--version"
12
12
  Then Exit code is zero
13
13
 
14
- Scenario: Simple estimate calculating
14
+ Scenario: Simple estimate calculating, in XML
15
15
  Given I have a "sample.est" file with content:
16
16
  """
17
- id: 789
18
17
  date: 19-08-2014
19
18
  author: Yegor Bugayenko
20
19
  method: champions.pert
@@ -38,11 +37,42 @@ Feature: Command Line Processing
38
37
  best-case: 8
39
38
  most-likely: 16
40
39
  """
41
- When I run bin/est with "-v -d . -f out.xml"
40
+ When I run bin/est with "-v -d . -t xml -f out.xml"
42
41
  Then Exit code is zero
43
42
  And Stdout contains "reading ."
44
43
  And XML file "out.xml" matches "/estimate[total='54']"
45
44
 
45
+ Scenario: Simple estimate calculating, in Text
46
+ Given I have a "sample.est" file with content:
47
+ """
48
+ date: 19-08-2014
49
+ author: Yegor Bugayenko
50
+ method: champions.pert
51
+ scope:
52
+ 1: basic Sinatra scaffolding
53
+ 2: front-end HAML files
54
+ 3: SASS stylesheet
55
+ 4: five model classes with unit tests
56
+ 5: PostgreSQL migrations
57
+ 6: Cucumber tests for PostgreSQL
58
+ 7: Capybara tests for HTML front
59
+ 8: CasperJS tests
60
+ 9: achieve 80% test coverage
61
+ champions:
62
+ 7:
63
+ worst-case: 40
64
+ best-case: 10
65
+ most-likely: 18
66
+ 4:
67
+ worst-case: 30
68
+ best-case: 8
69
+ most-likely: 16
70
+ """
71
+ When I run bin/est with "-d ."
72
+ Then Exit code is zero
73
+ And Stdout contains "Total: 54"
74
+ And Stdout contains "2014-08-19: 54 hours by Yegor Bugayenko"
75
+
46
76
  Scenario: Rejects unknown options
47
77
  Given I have a "test.est" file with content:
48
78
  """
data/lib/est.rb CHANGED
@@ -70,7 +70,7 @@ module Est
70
70
  def xml
71
71
  dir = @opts.dir? ? @opts[:dir] : Dir.pwd
72
72
  Est.log.info "reading #{dir}"
73
- estimates = Estimates.new(dir)
73
+ estimates = Estimates::Const.new(Estimates.new(dir))
74
74
  sanitize(
75
75
  Nokogiri::XML::Builder.new do |xml|
76
76
  xml << "<?xml-stylesheet type='text/xsl' href='#{xsl}'?>"
@@ -78,7 +78,7 @@ module Est
78
78
  xml.total estimates.total
79
79
  xml.ests do
80
80
  estimates.iterate.each do |est|
81
- xml.est('id' => est.id) do
81
+ xml.est do
82
82
  xml.date est.date
83
83
  xml.total est.total
84
84
  xml.author est.author
@@ -40,11 +40,6 @@ module Est
40
40
  fail "failed to read file #{file}" unless @yaml
41
41
  end
42
42
 
43
- # Get id.
44
- def id
45
- @yaml['id']
46
- end
47
-
48
43
  # Get date.
49
44
  def date
50
45
  Date.strptime(@yaml['date'], '%d-%m-%Y')
@@ -61,5 +56,17 @@ module Est
61
56
  fail "unsupported method #{method}" unless method == 'champions.pert'
62
57
  Champions.new(@yaml).total
63
58
  end
59
+
60
+ # Constant estimate.
61
+ class Const
62
+ attr_reader :date, :author, :total
63
+ # Ctor.
64
+ # +est+:: Estimate
65
+ def initialize(est)
66
+ @date = est.date
67
+ @author = est.author
68
+ @total = est.total
69
+ end
70
+ end
64
71
  end
65
72
  end
@@ -42,16 +42,36 @@ module Est
42
42
  # Get total estimate.
43
43
  def total
44
44
  estimates = iterate
45
- fail 'not enough estimates' if estimates.empty?
46
- estimates.reduce(0) { |a, e| a + e.total } / estimates.size
45
+ estimates.reduce(0) do |a, e|
46
+ Est.log.info "#{e.date}/#{e.author}: #{e.total}"
47
+ a + e.total
48
+ end / estimates.size
47
49
  end
48
50
 
49
51
  # Iterate them all
50
52
  def iterate
51
- Dir.entries(@dir)
52
- .reject { |f| f.index('.') == 0 }
53
- .select { |f| f =~ /^.*\.est$/ }
54
- .map { |f| Estimate.new(File.join(@dir, f)) }
53
+ unless @iterate
54
+ @iterate = Dir.entries(@dir)
55
+ .reject { |f| f.index('.') == 0 }
56
+ .select { |f| f =~ /^.*\.est$/ }
57
+ .map { |f| File.join(@dir, f) }
58
+ .each { |f| Est.log.info "#{f} found" }
59
+ .map { |f| Estimate.new(f) }
60
+ .map { |f| Estimate::Const.new(f) }
61
+ end
62
+ fail "no .est files found in #{@dir}" if @iterate.empty?
63
+ @iterate
64
+ end
65
+
66
+ # Const estimates.
67
+ class Const
68
+ attr_reader :total, :iterate
69
+ # Ctor.
70
+ # +est+:: Original estimates
71
+ def initialize(est)
72
+ @iterate = est.iterate
73
+ @total = est.total
74
+ end
55
75
  end
56
76
  end
57
77
  end
@@ -31,6 +31,7 @@ require 'yaml'
31
31
  # License:: MIT
32
32
  module Est
33
33
  # Scope Champions.
34
+ # see http://www.technoparkcorp.com/innovations/scope-champions/
34
35
  class Champions
35
36
  # Ctor.
36
37
  # +yaml+:: YAML config
@@ -40,11 +41,21 @@ module Est
40
41
 
41
42
  # Get total estimate.
42
43
  def total
43
- reqs = @yaml['scope']
44
+ n = @yaml['scope'].size
44
45
  champs = @yaml['champions']
45
- (champs.map do |_, e|
46
- (e['best-case'].to_i + e['worst'].to_i + e['most-likely'].to_i * 4) / 6
47
- end.reduce(&:+) * 0.54 * (reqs.size / champs.size)).to_i
46
+ m = champs.size
47
+ k = 0.54
48
+ sum = champs.map do |i, e|
49
+ total = (e['best-case'].to_i +
50
+ e['worst'].to_i +
51
+ e['most-likely'].to_i * 4) / 6
52
+ Est.log.info "#{i}: (#{e['best-case']} + #{e['worst-case']} +"\
53
+ " #{e['most-likely']} * 4) / 6 = #{total}"
54
+ total
55
+ end.reduce(&:+)
56
+ total = sum * k * (n / m)
57
+ Est.log.info "#{sum} * #{k} * (#{n} / #{m}) = #{total}"
58
+ total.to_i
48
59
  end
49
60
  end
50
61
  end
@@ -26,5 +26,5 @@
26
26
  # Copyright:: Copyright (c) 2014 Yegor Bugayenko
27
27
  # License:: MIT
28
28
  module Est
29
- VERSION = '0.1'
29
+ VERSION = '0.2'
30
30
  end
@@ -38,7 +38,6 @@ class TestEst < Minitest::Test
38
38
  File.write(
39
39
  File.join(dir, 'sample.est'),
40
40
  '''
41
- id: 5
42
41
  date: 12-07-2014
43
42
  author: Yegor Bugayenko
44
43
  method: champions.pert
@@ -70,8 +69,7 @@ class TestEst < Minitest::Test
70
69
  '/estimate/@version',
71
70
  '/estimate/@date',
72
71
  '/estimate[total="54"]',
73
- '/estimate/ests[count(est)=1]',
74
- '/estimate/ests/est[@id="5"]'
72
+ '/estimate/ests[count(est)=1]'
75
73
  ]
76
74
  )
77
75
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: est
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-12-19 00:00:00.000000000 Z
12
+ date: 2014-12-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -206,11 +206,13 @@ files:
206
206
  - LICENSE.txt
207
207
  - README.md
208
208
  - Rakefile
209
+ - assets/est-text.xsl
209
210
  - assets/est.xsd
210
211
  - assets/est.xsl
211
212
  - bin/est
212
213
  - cucumber.yml
213
214
  - est.gemspec
215
+ - est/first.est
214
216
  - features/cli.feature
215
217
  - features/gem_package.feature
216
218
  - features/step_definitions/steps.rb