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 +4 -4
- data/README.md +16 -13
- data/lib/trajectory/data_access/data_store.rb +9 -0
- data/lib/trajectory/domain/iterations.rb +9 -0
- data/lib/trajectory/domain/project.rb +41 -2
- data/lib/trajectory/domain/story.rb +5 -1
- data/lib/trajectory/exceptions.rb +1 -0
- data/lib/trajectory/exceptions/velocity_always_equal_to_zero_error.rb +4 -0
- data/lib/trajectory/version.rb +1 -1
- data/spec/unit/data_access/data_store_spec.rb +7 -0
- data/spec/unit/domain/iterations_spec.rb +18 -0
- data/spec/unit/domain/project_spec.rb +39 -0
- data/spec/unit/domain/story_spec.rb +10 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d010f9c28f42b607cbc7d7ffd045438cad5077b1
|
4
|
+
data.tar.gz: 388c7eb91ab985f2f9d74da3ef6af81d29ecabda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
[](https://travis-ci.org/lminaudier/trajectory)
|
4
4
|
[](https://codeclimate.com/github/lminaudier/trajectory)
|
5
|
+
[](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
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
-
|
13
|
-
-
|
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
|
-
|
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
|
-
|
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
|
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/
|
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
|
-
|
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
|
-
@
|
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
|
-
#
|
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
|
data/lib/trajectory/version.rb
CHANGED
@@ -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.
|
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-
|
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
|