trajectory 0.1.1 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f0df4ea6f99eb9dace7117580eb27e3b683641d
4
- data.tar.gz: 0ce7b25d9dd4aef130c0d193313c3f0e63258565
3
+ metadata.gz: d010f9c28f42b607cbc7d7ffd045438cad5077b1
4
+ data.tar.gz: 388c7eb91ab985f2f9d74da3ef6af81d29ecabda
5
5
  SHA512:
6
- metadata.gz: 7e838a4f8139b58a9efc800cb0ffa8b83541de05424b7bb47a8a36c71cc142225fa484e582def9d4155035eca5c92f33ad6f9c70ab0a8e3a16ec1c90e0155a61
7
- data.tar.gz: 266263a8df3e23fc6ba720ab597539a09eae9a4a207fea0c64bd64b9d8b5b0df4bbc7566c5c89face5291ca6b961f6c046494363099a278f9c5e749ca08c49c7
6
+ metadata.gz: 9d704ed03e2ef065234de2ba7b63db0b3f4554d387f5b51fa8a98784418cab1bbb33a93d40363a345e3b6bc08685395153a596eabc4394de43b10b80cccaaf29
7
+ data.tar.gz: 8bc370caa554f3e9d7fd46bec8ca0da5f5348ffcdfdef8da3f6ba1ad66726595d0b127f350bbfa1848ec72e1dafe84691c09ac0d97b897f140cadc15ad50e590
data/README.md CHANGED
@@ -1,20 +1,24 @@
1
- # Trajectory Ruby Wrapper
1
+ # Trajectory API Ruby Wrapper
2
2
 
3
3
  [![Build Status](https://travis-ci.org/lminaudier/trajectory.png?branch=master)](https://travis-ci.org/lminaudier/trajectory)
4
4
  [![Code Climate](https://codeclimate.com/github/lminaudier/trajectory.png)](https://codeclimate.com/github/lminaudier/trajectory)
5
+ [![Dependency Status](https://gemnasium.com/lminaudier/trajectory.png)](https://gemnasium.com/lminaudier/trajectory)
5
6
 
6
- This gem is a wrapper to the Thoughbot's Trajectory app ([apptrajectory.com](http://apptrajectory.com)).
7
+ This gem is a wrapper to the Thoughbot's Trajectory app API ([apptrajectory.com](http://apptrajectory.com)).
7
8
 
8
- This is **work in progress** but you'll have read access to
9
- - projects
10
- - stories
11
- - iterations
12
- - ideas
13
- - updates
9
+ This is **work in progress** but at the moment, you'll have read access to
10
+ - [Project](http://rdoc.info/gems/trajectory/Trajectory/Project)
11
+ - [Story](http://rdoc.info/gems/trajectory/Trajectory/Story)
12
+ - [Iteration](http://rdoc.info/gems/trajectory/Trajectory/Iteration)
13
+ - [Idea](http://rdoc.info/gems/trajectory/Trajectory/Idea)
14
+ - [Update](http://rdoc.info/gems/trajectory/Trajectory/Update)
14
15
 
15
- Comments, Uploads wrappers are missing.
16
+ The wrapper propose some handy methods on collections of [projects](http://rdoc.info/gems/trajectory/Trajectory/Projects), [stories](http://rdoc.info/gems/trajectory/Trajectory/Stories), [iterations](http://rdoc.info/gems/trajectory/Trajectory/Iterations) and [ideas](http://rdoc.info/gems/trajectory/Trajectory/Ideas).
16
17
 
17
- No write wrapper is provided at the moment.
18
+ Comments and Uploads wrappers are not yet implemented.
19
+ Wrapper for write access is not provided at the moment.
20
+
21
+ If you have questions, you can submit an issue or ping me on twitter [@lminaudier](https://twitter.com/lminaudier).
18
22
 
19
23
  ## Installation
20
24
 
@@ -37,8 +41,7 @@ Or install it yourself as:
37
41
  #### Authentication
38
42
 
39
43
  By default, the wrapper will look for the Trajectory API key in
40
- `TRAJECTORY_API_KEY` environment variable. You can also pass it directly to the
41
- constructor.
44
+ `TRAJECTORY_API_KEY` environment variable and for the account keyword in `TRAJECTORY_ACCOUNT_KEYWORD`.
42
45
 
43
46
  require 'trajectory'
44
47
 
@@ -47,7 +50,7 @@ constructor.
47
50
 
48
51
  #### API
49
52
 
50
- See [RDoc](http://rdoc.info/github/lminaudier/trajectory/master/frames) for api details
53
+ See [RDoc](http://rdoc.info/gems/trajectory/frames) for api details
51
54
 
52
55
  ## Contributing
53
56
 
@@ -66,5 +66,14 @@ module Trajectory
66
66
  def iterations_for_project(project)
67
67
  Iterations.from_json project, Api.iterations_for_project(project)
68
68
  end
69
+
70
+ # Fetches a iteration of a given project by id
71
+ #
72
+ # @param project [Project] the project
73
+ # @param iteration_id [Integer] the iteration id
74
+ # @return [Iteration, false] the found iteration or false
75
+ def find_iteration_of_project_with_id(project, iteration_id)
76
+ project.find_iteration_by_id(iteration_id)
77
+ end
69
78
  end
70
79
  end
@@ -22,6 +22,15 @@ module Trajectory
22
22
  end)
23
23
  end
24
24
 
25
+ # Fetch the iteration with the given id in the collection. If it is not found,
26
+ # it returns false
27
+ #
28
+ # @param id [Integer] the project id
29
+ # @return [Iteration, false] the found iteration or false
30
+ def find_by_id(id)
31
+ iterations.find { |iteration| iteration.id == id } || false
32
+ end
33
+
25
34
  # Returns the current iteration of the project or false it no current iteration can be found
26
35
  #
27
36
  # @return [Iteration, false] the current iteration or false
@@ -4,7 +4,8 @@ module Trajectory
4
4
 
5
5
  NUMBER_OF_WORKING_DAYS_BY_WEEK = 5.0
6
6
 
7
- attr_writer :stories, :users_collection
7
+ # @private
8
+ attr_writer :stories, :users_collection, :iterations_collection
8
9
 
9
10
  # @return [Integer] the unique identifier of the project.
10
11
  # @raise [MissingAttributeError] if id is nil
@@ -50,7 +51,7 @@ module Trajectory
50
51
  #
51
52
  # @return [Iterations] the iterations collection
52
53
  def iterations
53
- @iterations ||= DataStore.iterations_for_project(self)
54
+ @iterations_collection ||= DataStore.iterations_for_project(self)
54
55
  end
55
56
 
56
57
  # Fetch all ideas that belongs to the project
@@ -82,6 +83,13 @@ module Trajectory
82
83
  users.find_by_id(id)
83
84
  end
84
85
 
86
+ # Fetch a iteration from the project given its id or false if it does not exist
87
+ #
88
+ # @return [Iteration, false] the iteration or false
89
+ def find_iteration_by_id(id)
90
+ iterations.find_by_id(id)
91
+ end
92
+
85
93
  # Fetch the stories in a given iteration of a project
86
94
  #
87
95
  # @param iteration [Iteration] the iteration
@@ -106,6 +114,13 @@ module Trajectory
106
114
  Date.today + remaining_days
107
115
  end
108
116
 
117
+ # Returns the number of needed iterations to complete the project
118
+ #
119
+ # @return [Integer] the number of iterations
120
+ def remaining_iterations
121
+ (remaining_points.to_f / estimated_velocity.to_f).ceil
122
+ end
123
+
109
124
  # Returns the estimated number of days (weekend included) remaining before the end of the project.
110
125
  #
111
126
  # This is usefull to estimate the project end date.
@@ -164,5 +179,29 @@ module Trajectory
164
179
  def accepted_points
165
180
  total_points - remaining_points
166
181
  end
182
+
183
+ # Returns the last non null velocity of the project or raise an error if the
184
+ # project never started
185
+ #
186
+ # @return [Integer] a non null velocity
187
+ # @raise [VelocityAlwaysEqualToZero] when historic velocity was always zero
188
+ # (i.e the project has not yet started)
189
+ def last_non_null_velocity
190
+ raise VelocityAlwaysEqualToZero if !has_started?
191
+ historic_velocity.reverse.find do |velocity|
192
+ velocity != 0
193
+ end
194
+ end
195
+
196
+ # Returns true if the project has already started some development (i.e
197
+ # stories have been accepted and a velocity has been evaluated). It returns
198
+ # false otherwise
199
+ #
200
+ # @return [true, false]
201
+ def has_started?
202
+ historic_velocity.any? do |velocity|
203
+ velocity != 0
204
+ end
205
+ end
167
206
  end
168
207
  end
@@ -111,8 +111,12 @@ module Trajectory
111
111
  iteration_id == iteration.id
112
112
  end
113
113
 
114
- # @todo Not Yet Implemented
114
+ # Returns the iteration the story belongs to or false if iteration can't be
115
+ # found
116
+ #
117
+ # @return [Iteration, false] the iteration or false
115
118
  def iteration
119
+ @iteration ||= DataStore.find_iteration_of_project_by_id(project, iteration_id)
116
120
  end
117
121
  end
118
122
  end
@@ -1,3 +1,4 @@
1
1
  require 'trajectory/exceptions/missing_attribute_error'
2
2
  require 'trajectory/exceptions/velocity_equal_to_zero_error'
3
+ require 'trajectory/exceptions/velocity_always_equal_to_zero_error'
3
4
  require 'trajectory/exceptions/bad_environment_error'
@@ -0,0 +1,4 @@
1
+ module Trajectory
2
+ class VelocityAlwaysEqualToZero < RuntimeError
3
+ end
4
+ end
@@ -1,3 +1,3 @@
1
1
  module Trajectory
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -82,5 +82,12 @@ module Trajectory
82
82
 
83
83
  DataStore.find_user_of_project_with_id(project, id)
84
84
  end
85
+
86
+ it 'delegates fetching of user of a project to the project' do
87
+ id = 12
88
+ project.should_receive(:find_iteration_by_id).with(id)
89
+
90
+ DataStore.find_iteration_of_project_with_id(project, id)
91
+ end
85
92
  end
86
93
  end
@@ -19,6 +19,24 @@ module Trajectory
19
19
  iterations[1].project_id.should == 4567
20
20
  end
21
21
 
22
+ it 'can find an iteration by id' do
23
+ iteration = double(:iteration, id: 1234)
24
+ iterations = Iterations.new(double(:iteration, id: 1),
25
+ double(:iteration, id: 2),
26
+ iteration,
27
+ double(:iteration, id: 3))
28
+
29
+ iterations.find_by_id(1234).should == iteration
30
+ end
31
+
32
+ it "returns false when it can't find a project by id" do
33
+ iterations = Iterations.new(double(:iteration, id: 1),
34
+ double(:iteration, id: 2),
35
+ double(:iteration, id: 3))
36
+
37
+ iterations.find_by_id(1234).should == false
38
+ end
39
+
22
40
  it 'can retrieve current iteration' do
23
41
  current_iteration = double(:current? => true)
24
42
  iterations = Iterations.new(double(:current? => false),
@@ -36,6 +36,15 @@ module Trajectory
36
36
  Project.new.total_points.should == 20
37
37
  end
38
38
 
39
+ it 'can evaluate remaining iterations until project completion based on the last non nul velocity' do
40
+ stories = Stories.new(double(:story, :points => 2, :completed? => false),
41
+ double(:story, :points => 10, :completed? => false),
42
+ double(:story, :points => 8, :completed? => true))
43
+ DataStore.stub(:stories_for_project).and_return(stories)
44
+
45
+ Project.new(:estimated_velocity => 2).remaining_iterations.should == 6
46
+ end
47
+
39
48
  it 'can evaluate remaining points' do
40
49
  stories = Stories.new(double(:story, :points => 2, :completed? => false),
41
50
  double(:story, :points => 10, :completed? => false),
@@ -108,6 +117,27 @@ module Trajectory
108
117
  Project.new.accepted_points.should == 19
109
118
  end
110
119
 
120
+ it 'can retrieve the last non null velocity in its history' do
121
+ Project.new(:historic_velocity => [20, 0, 2, 3, 0, 0, 0]).last_non_null_velocity.should == 3
122
+ Project.new(:historic_velocity => [0, 0, 20, 0, 2, 3, 0, 0, 0]).last_non_null_velocity.should == 3
123
+ Project.new(:historic_velocity => [0, 0, 20, 0, 2, 3]).last_non_null_velocity.should == 3
124
+ end
125
+
126
+ it 'raises an error when trying to get last non null velocity of a project that not have yet started' do
127
+ project = Project.new
128
+ project.stub(:has_started?).and_return(false)
129
+
130
+ expect do
131
+ project.last_non_null_velocity
132
+ end.to raise_error(VelocityAlwaysEqualToZero)
133
+ end
134
+
135
+ it 'can tell if a project has started based on historic velocity' do
136
+ Project.new(:historic_velocity => [0, 0, 20, 0, 2, 3]).should have_started
137
+ Project.new(:historic_velocity => [0, 0, 0, 0]).should_not have_started
138
+ Project.new(:historic_velocity => []).should_not have_started
139
+ end
140
+
111
141
  it 'delegates fetching of iterations of a project to the data store' do
112
142
  DataStore.should_receive(:iterations_for_project).with(project)
113
143
 
@@ -146,6 +176,15 @@ module Trajectory
146
176
  project.find_user_by_id(1234)
147
177
  end
148
178
 
179
+ it 'can get one of its iteration by id' do
180
+ iterations = double
181
+
182
+ project.iterations_collection = iterations
183
+ iterations.should_receive(:find_by_id).with(1234)
184
+
185
+ project.find_iteration_by_id(1234)
186
+ end
187
+
149
188
  context 'when estimated velocity is zero' do
150
189
  it 'cannot estimate remaining days until the project end' do
151
190
  expect do
@@ -33,6 +33,16 @@ module Trajectory
33
33
  Story.new.project
34
34
  end
35
35
 
36
+ it 'delegates iteration fetching to data store' do
37
+ project = 1234
38
+ story = Story.new(:iteration_id => 1234)
39
+ story.stub(:project).and_return(project)
40
+
41
+ DataStore.should_receive(:find_iteration_of_project_by_id).with(project, 1234)
42
+
43
+ story.iteration
44
+ end
45
+
36
46
  it 'knows when it is started' do
37
47
  Story.new(state: :started).should be_started
38
48
  Story.new(state: :unstarted).should_not be_started
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trajectory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Loïc Minaudier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-03-16 00:00:00.000000000 Z
11
+ date: 2013-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: virtus
@@ -77,6 +77,7 @@ files:
77
77
  - lib/trajectory/exceptions.rb
78
78
  - lib/trajectory/exceptions/bad_environment_error.rb
79
79
  - lib/trajectory/exceptions/missing_attribute_error.rb
80
+ - lib/trajectory/exceptions/velocity_always_equal_to_zero_error.rb
80
81
  - lib/trajectory/exceptions/velocity_equal_to_zero_error.rb
81
82
  - lib/trajectory/version.rb
82
83
  - spec/fabricators/iteration_fabricator.rb