est 0.1 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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