trajectory 0.1.0
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 +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +57 -0
- data/Rakefile +18 -0
- data/config/env.yml +3 -0
- data/lib/trajectory/client.rb +24 -0
- data/lib/trajectory/core_ext/hash.rb +8 -0
- data/lib/trajectory/core_ext.rb +1 -0
- data/lib/trajectory/data_access/api.rb +54 -0
- data/lib/trajectory/data_access/data_store.rb +70 -0
- data/lib/trajectory/data_access.rb +2 -0
- data/lib/trajectory/domain/idea.rb +70 -0
- data/lib/trajectory/domain/ideas.rb +23 -0
- data/lib/trajectory/domain/iteration.rb +85 -0
- data/lib/trajectory/domain/iterations.rb +46 -0
- data/lib/trajectory/domain/project.rb +168 -0
- data/lib/trajectory/domain/projects.rb +55 -0
- data/lib/trajectory/domain/stories.rb +56 -0
- data/lib/trajectory/domain/story.rb +119 -0
- data/lib/trajectory/domain/update.rb +17 -0
- data/lib/trajectory/domain/user.rb +28 -0
- data/lib/trajectory/domain/users.rb +30 -0
- data/lib/trajectory/domain.rb +11 -0
- data/lib/trajectory/exceptions/bad_environment_error.rb +7 -0
- data/lib/trajectory/exceptions/missing_attribute_error.rb +12 -0
- data/lib/trajectory/exceptions/velocity_equal_to_zero_error.rb +11 -0
- data/lib/trajectory/exceptions.rb +3 -0
- data/lib/trajectory/version.rb +3 -0
- data/lib/trajectory.rb +15 -0
- data/spec/fabricators/iteration_fabricator.rb +18 -0
- data/spec/fabricators/project_fabricator.rb +11 -0
- data/spec/fabricators/story_fabricator.rb +18 -0
- data/spec/fixtures/vcr_cassettes/client.yml +853 -0
- data/spec/fixtures/vcr_cassettes/projects_and_ideas.yml +98 -0
- data/spec/fixtures/vcr_cassettes/projects_and_ideas_association.yml +52 -0
- data/spec/fixtures/vcr_cassettes/projects_and_iterations.yml +49 -0
- data/spec/fixtures/vcr_cassettes/projects_and_iterations_association.yml +49 -0
- data/spec/fixtures/vcr_cassettes/projects_and_stories.yml +101 -0
- data/spec/fixtures/vcr_cassettes/projects_and_stories_association.yml +147 -0
- data/spec/integration/client_spec.rb +96 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/env.rb +11 -0
- data/spec/support/fabrication.rb +1 -0
- data/spec/support/simple_cov.rb +11 -0
- data/spec/support/timecop.rb +1 -0
- data/spec/support/vcr.rb +8 -0
- data/spec/unit/client_spec.rb +27 -0
- data/spec/unit/core_ext/hash_spec.rb +11 -0
- data/spec/unit/data_access/api_spec.rb +100 -0
- data/spec/unit/data_access/data_store_spec.rb +86 -0
- data/spec/unit/domain/idea_spec.rb +46 -0
- data/spec/unit/domain/ideas_spec.rb +22 -0
- data/spec/unit/domain/iteration_spec.rb +51 -0
- data/spec/unit/domain/iterations_spec.rb +47 -0
- data/spec/unit/domain/project_spec.rb +163 -0
- data/spec/unit/domain/projects_spec.rb +84 -0
- data/spec/unit/domain/stories_spec.rb +84 -0
- data/spec/unit/domain/story_spec.rb +71 -0
- data/spec/unit/domain/user_spec.rb +29 -0
- data/spec/unit/domain/users_spec.rb +36 -0
- data/spec/unit/exceptions/bad_environment_error_spec.rb +9 -0
- data/spec/unit/exceptions/missing_attribute_error_spec.rb +10 -0
- data/spec/unit/exceptions/velocity_equal_to_zero_error_spec.rb +10 -0
- data/trajectory.gemspec +21 -0
- metadata +174 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6e4e38ce957170860b16f74c2ca873c005c978c5
|
4
|
+
data.tar.gz: 6e532e69b0e54ca48e3b469612644aea41e6ad67
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d7892c02afbb0156a5f7880c6f23b3343d226c696230371409ab31a053f2076ca4663c6932ff6c77fe43c29413cb88258422f0f90aaecbc83d37d96a34687e86
|
7
|
+
data.tar.gz: 7a0300e0f14c9fe74fe52ad18c65249f1a977e4f71b7156c503ab4d3fc719c2e523a3b1d77cf8e9e8b18ac304a7b5cc756d79b9140d64b60d5a0234c309a6c98
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p0
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
group :development, :test do
|
4
|
+
gem 'awesome_print'
|
5
|
+
gem 'rspec'
|
6
|
+
gem 'rake'
|
7
|
+
gem 'yard'
|
8
|
+
end
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
gem 'vcr'
|
12
|
+
gem 'webmock'
|
13
|
+
gem 'fabrication'
|
14
|
+
gem 'simplecov', :require => false
|
15
|
+
gem 'timecop'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Specify your gem's dependencies in trajectory.gemspec
|
19
|
+
gemspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Loïc Minaudier
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Trajectory Ruby Wrapper
|
2
|
+
|
3
|
+
[](https://travis-ci.org/lminaudier/trajectory)
|
4
|
+
|
5
|
+
This gem is a wrapper to the Thoughbot's Trajectory app ([apptrajectory.com](http://apptrajectory.com)).
|
6
|
+
|
7
|
+
This is **work in progress** but you'll have read access to
|
8
|
+
- projects
|
9
|
+
- stories
|
10
|
+
- iterations
|
11
|
+
- ideas
|
12
|
+
- updates
|
13
|
+
|
14
|
+
Comments, Uploads wrappers are missing.
|
15
|
+
|
16
|
+
No write wrapper is provided at the moment.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
gem 'trajectory'
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install trajectory
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
### Read Access
|
35
|
+
|
36
|
+
#### Authentication
|
37
|
+
|
38
|
+
By default, the wrapper will look for the Trajectory API key in
|
39
|
+
`TRAJECTORY_API_KEY` environment variable. You can also pass it directly to the
|
40
|
+
constructor.
|
41
|
+
|
42
|
+
require 'trajectory'
|
43
|
+
|
44
|
+
client = Trajectory::Client.new
|
45
|
+
client.projects
|
46
|
+
|
47
|
+
#### API
|
48
|
+
|
49
|
+
See [RDoc](http://rdoc.info/github/lminaudier/trajectory/master/frames) for api details
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
1. Fork it
|
54
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
55
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
56
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
57
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
5
|
+
t.rspec_opts = '--format progress'
|
6
|
+
end
|
7
|
+
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
desc "List all code todos"
|
11
|
+
task :todo do
|
12
|
+
require 'yard'
|
13
|
+
YARD::Registry.load!.all.each do |o|
|
14
|
+
if o.tag(:todo)
|
15
|
+
puts "#{o.file}:#{o.line} #{o.tag(:todo).text}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/config/env.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Client
|
3
|
+
# Creates a new trajectory client
|
4
|
+
#
|
5
|
+
# @return [Client]
|
6
|
+
def initialize
|
7
|
+
check_environment!
|
8
|
+
end
|
9
|
+
|
10
|
+
# Checks if environment variables are set.
|
11
|
+
#
|
12
|
+
# @raise [BadEvnrionmentError] if TRAJECTORY_API_KEY and TRAJECTORY_ACCOUNT_KEYWORD are not set
|
13
|
+
def check_environment!
|
14
|
+
raise BadEvnrionmentError if ENV['TRAJECTORY_API_KEY'].nil? || ENV['TRAJECTORY_ACCOUNT_KEYWORD'].nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
# Fetches all trajectory projects of the account
|
18
|
+
#
|
19
|
+
# @return [Projects] the projects collection
|
20
|
+
def projects
|
21
|
+
DataStore.projects
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'trajectory/core_ext/hash'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module Trajectory
|
4
|
+
class Api
|
5
|
+
include HTTParty
|
6
|
+
base_uri "https://www.apptrajectory.com/api/#{ENV['TRAJECTORY_API_KEY']}/accounts/#{ENV['TRAJECTORY_ACCOUNT_KEYWORD']}"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# @return [JSON] a json array of all projects attributes from trajectory API
|
10
|
+
def projects
|
11
|
+
get_json("/projects.json")
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [JSON] a json array of all users (of the given project) attributes from trajectory API
|
15
|
+
def users_for_project(project)
|
16
|
+
get_json("/projects/#{project.keyword}/users.json")
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [JSON] a json array of all stories (of the given project) attributes from trajectory API
|
20
|
+
def stories_for_project(project)
|
21
|
+
get_json("/projects/#{project.keyword}/stories/completed.json")['stories'] +
|
22
|
+
get_json("/projects/#{project.keyword}/stories.json")['stories']
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [JSON] a json array of all iterations (of the given project) attributes from trajectory API
|
26
|
+
def iterations_for_project(project)
|
27
|
+
get_json("/projects/#{project.keyword}/iterations.json")
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [JSON] a json array of all ideas (of the given project) attributes from trajectory API
|
31
|
+
def ideas_for_project(project)
|
32
|
+
get_json("/projects/#{project.keyword}/ideas.json")
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [JSON] a json array of all updates (of the given project) attributes from trajectory API
|
36
|
+
def updates_for_project(project, since)
|
37
|
+
get_json("/projects/#{project.keyword}/updates.json?since=#{since.to_s}")
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def get_json(url)
|
42
|
+
JSON.parse(get_body(url, options))
|
43
|
+
end
|
44
|
+
|
45
|
+
def options
|
46
|
+
{:headers => {'Content-Type' => 'application/json'}}
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_body(*args)
|
50
|
+
get(*args).body
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Trajectory
|
2
|
+
module DataStore
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# @return [Projects] the collection of projects of the trajectory account
|
6
|
+
def projects
|
7
|
+
@projects ||= Projects.from_json(Api.projects)
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Users] the collection of users of the trajectory account
|
11
|
+
def users_for_project(project)
|
12
|
+
Users.from_json Api.users_for_project(project)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Fetches all stories of a given project
|
16
|
+
#
|
17
|
+
# @param project [Project] the project
|
18
|
+
# @return [Stories] the collection of stories
|
19
|
+
def stories_for_project(project)
|
20
|
+
Stories.from_json project, Api.stories_for_project(project)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Fetches all ideas of a given project
|
24
|
+
#
|
25
|
+
# @param project [Project] the project
|
26
|
+
# @return [Ideas] the collection of ideas
|
27
|
+
def ideas_for_project(project)
|
28
|
+
Ideas.from_json project, Api.ideas_for_project(project)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Fetches all updates of a given project since a given date
|
32
|
+
#
|
33
|
+
# @param project [Project] the project
|
34
|
+
# @param since [DateTime] a datetime
|
35
|
+
# @return [Ideas] the collection of updates
|
36
|
+
def updates_for_project(project, since = DateTime.now)
|
37
|
+
updates = Api.updates_for_project(project, since).symbolize_keys!
|
38
|
+
|
39
|
+
stories = Stories.from_json(project, updates[:stories])
|
40
|
+
iterations = Iterations.from_json(project, updates[:iterations])
|
41
|
+
|
42
|
+
Update.new(stories, iterations)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Fetches a project by id in the collection of accessible projects
|
46
|
+
#
|
47
|
+
# @param id [Integer] the project id
|
48
|
+
# @return [Project, false] the found project or false
|
49
|
+
def find_project_by_id(id)
|
50
|
+
projects.find_by_id(id)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Fetches a user of a given project by id
|
54
|
+
#
|
55
|
+
# @param project [Project] the project
|
56
|
+
# @param user_id [Integer] the user id
|
57
|
+
# @return [User, false] the found user or false
|
58
|
+
def find_user_of_project_with_id(project, user_id)
|
59
|
+
project.find_user_by_id(user_id)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Fetches all iterations of a given project
|
63
|
+
#
|
64
|
+
# @param project [Project] the project
|
65
|
+
# @return [Iterations] the collection of iterations
|
66
|
+
def iterations_for_project(project)
|
67
|
+
Iterations.from_json project, Api.iterations_for_project(project)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Idea
|
3
|
+
include Virtus
|
4
|
+
|
5
|
+
# @return [Integer] the unique identifier of the idea.
|
6
|
+
# @raise [MissingAttributeError] if id is nil
|
7
|
+
attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
|
8
|
+
# @return [String] the subject (i.e title) of the idea
|
9
|
+
attribute :subject, String
|
10
|
+
# @return [Integer] the number of stories associated with the idea
|
11
|
+
attribute :stories_count, Integer
|
12
|
+
# @return [Integer] the number of comments on the idea
|
13
|
+
attribute :comments_count, Integer
|
14
|
+
# @return [String] Name of the last user that have commented on the idea
|
15
|
+
attribute :last_comment_user_name, String
|
16
|
+
# @return [DateTime] date of the last comment on the idea
|
17
|
+
attribute :last_comment_created_at, DateTime
|
18
|
+
# @return [DateTime] date of the last activity (i.e edit, comment, ...) on the idea
|
19
|
+
attribute :last_activity_at, DateTime
|
20
|
+
# @!method editable_by_current_user?
|
21
|
+
# @return [true, false] true if the user can edit the idea, false otherwise
|
22
|
+
attribute :editable_by_current_user, Boolean
|
23
|
+
# @return [Integer] idea of the user that created the idea
|
24
|
+
# @see #user
|
25
|
+
attribute :user_id, Integer
|
26
|
+
# @return [Array<Integer>] the ids of the users that have subscribed to the
|
27
|
+
# @see #subscribed_users
|
28
|
+
attribute :subscribed_user_ids, Array[Integer]
|
29
|
+
# @return [Integer] the id of the project the idea belongs to
|
30
|
+
# @see #project
|
31
|
+
attribute :project_id, Integer
|
32
|
+
# @return [String] the content of the idea
|
33
|
+
attribute :body, String
|
34
|
+
|
35
|
+
# Returns true if two ideas are the sames i.e they share the same id
|
36
|
+
# attribute
|
37
|
+
#
|
38
|
+
# @param other [Idea] the other object to compare
|
39
|
+
# @return [true, false]
|
40
|
+
def ==(other)
|
41
|
+
id == other.id
|
42
|
+
end
|
43
|
+
|
44
|
+
# Fetch the project the idea belongs to
|
45
|
+
#
|
46
|
+
# @return [Project]
|
47
|
+
def project
|
48
|
+
DataStore.find_project_by_id(project_id)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Fetch the user that created the idea
|
52
|
+
#
|
53
|
+
# @return [User]
|
54
|
+
def user
|
55
|
+
DataStore.find_user_of_project_with_id(project, user_id)
|
56
|
+
end
|
57
|
+
|
58
|
+
# @todo Not Yet Implemented
|
59
|
+
def subscribed_users
|
60
|
+
end
|
61
|
+
|
62
|
+
# @todo Not Yet Implemented
|
63
|
+
def percent_complete
|
64
|
+
end
|
65
|
+
|
66
|
+
# @todo Not Yet Implemented
|
67
|
+
def stories
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Ideas < SimpleDelegator
|
3
|
+
alias :ideas :__getobj__
|
4
|
+
|
5
|
+
# Creates a new collection of {Idea}
|
6
|
+
#
|
7
|
+
# @param ideas [Array<Idea>] a arbitrary lenght list of {Idea} objects
|
8
|
+
def initialize(*ideas)
|
9
|
+
super(ideas)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a new collection of {Idea} from a JSON array of attributes from trajectory API
|
13
|
+
#
|
14
|
+
# @param project [Project] the project the iterations belongs to
|
15
|
+
# @param json_attributes [Hash] the hash of attributes of each idea of the collection
|
16
|
+
def self.from_json(project, json_attributes)
|
17
|
+
new(*json_attributes.map do |attributes|
|
18
|
+
attributes = attributes.symbolize_keys!.merge({project_id: project.id})
|
19
|
+
Idea.new(attributes)
|
20
|
+
end)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Iteration
|
3
|
+
include Virtus
|
4
|
+
|
5
|
+
attr_writer :project
|
6
|
+
|
7
|
+
# @return [Integer] the unique identifier of the iteration.
|
8
|
+
# @raise [MissingAttributeError] if id is nil
|
9
|
+
attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
|
10
|
+
# @return [Float] the completion percentage of the iteration
|
11
|
+
attribute :percent_complete, Float
|
12
|
+
# @return [Integer] number of started stories in the iteration
|
13
|
+
attribute :started_stories_count, Integer
|
14
|
+
# @return [DateTime] creation date of the iteration
|
15
|
+
attribute :created_at, DateTime
|
16
|
+
# @return [Integer] estimated velocity of the project
|
17
|
+
attribute :estimated_velocity, Integer
|
18
|
+
# @return [Integer] number of delivered stories in the iteration
|
19
|
+
attribute :delivered_stories_count, Integer
|
20
|
+
# @return [DateTime] last modification date of the iteration
|
21
|
+
attribute :updated_at, DateTime
|
22
|
+
# @return [Integer] number of unstarted stories
|
23
|
+
attribute :unstarted_stories_count, Integer
|
24
|
+
# @return [DateTime] start date of the iteration
|
25
|
+
attribute :starts_on, DateTime
|
26
|
+
# @!method current?
|
27
|
+
# @return [true, false] true if iteration is the current iteration, false otherwise
|
28
|
+
attribute :current, Boolean
|
29
|
+
# @return [Integer] number of stories in the iteration
|
30
|
+
attribute :stories_count, Integer
|
31
|
+
# @!method complete?
|
32
|
+
# @return [true, false] return true if the iteration has been completed, false otherwise
|
33
|
+
attribute :complete, Boolean
|
34
|
+
# Sum of points accepted stories in the iteration
|
35
|
+
#
|
36
|
+
# @return [Integer] number of accepted points
|
37
|
+
attribute :accepted_points, Integer
|
38
|
+
# Sum of all points of all stories of the iteration
|
39
|
+
#
|
40
|
+
# @return [Integer] number of estimated points
|
41
|
+
attribute :estimated_points, Integer
|
42
|
+
# @return [Integer] number of comments of the story
|
43
|
+
attribute :comments_count, Integer
|
44
|
+
# @return [Integer] bumber of accepted stories
|
45
|
+
attribute :accepted_stories_count, Integer
|
46
|
+
# @return [Integer] d of the project the iteration belongs to
|
47
|
+
# @see #project
|
48
|
+
attribute :project_id, Integer
|
49
|
+
|
50
|
+
# @!method past?
|
51
|
+
#
|
52
|
+
# Returns true if iteration is a past one i.e has been completed
|
53
|
+
#
|
54
|
+
# @return [true, false]
|
55
|
+
alias :past? :complete?
|
56
|
+
|
57
|
+
# Returns true if the iteration is a future one i.e not started and not completed
|
58
|
+
def future?
|
59
|
+
!current? && !complete?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if two iterations are the sames i.e they share the same id
|
63
|
+
# attribute
|
64
|
+
#
|
65
|
+
# @param other [Iteration] the other object to compare
|
66
|
+
# @return [true, false]
|
67
|
+
def ==(other)
|
68
|
+
id == other.id
|
69
|
+
end
|
70
|
+
|
71
|
+
# Fetch the project the iteration belongs to
|
72
|
+
#
|
73
|
+
# @return [Project]
|
74
|
+
def project
|
75
|
+
@project ||= DataStore.find_project_by_id(project_id)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Fetch the stories that belongs to the iteration
|
79
|
+
#
|
80
|
+
# @return [Stories] the stories collection
|
81
|
+
def stories
|
82
|
+
project.stories_in_iteration(self)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Iterations < SimpleDelegator
|
3
|
+
alias :iterations :__getobj__
|
4
|
+
|
5
|
+
# Creates a new collection of {Iteration}
|
6
|
+
#
|
7
|
+
# @param iterations [Array<Iteration>] a arbitrary lenght list of {Iteration} objects
|
8
|
+
def initialize(*iterations)
|
9
|
+
super(iterations)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a new collection of {Iteration} from a JSON array of attributes from trajectory API
|
13
|
+
#
|
14
|
+
# @param project [Project] the project the iterations belongs to
|
15
|
+
# @param json_attributes [Hash] the hash of attributes of each iteration of the collection
|
16
|
+
def self.from_json(project, json_attributes)
|
17
|
+
new(*json_attributes.map do |attributes|
|
18
|
+
attributes = attributes.symbolize_keys!.merge({project_id: project.id})
|
19
|
+
attributes[:current] = attributes[:current?]
|
20
|
+
attributes.delete(:current?)
|
21
|
+
Iteration.new(attributes)
|
22
|
+
end)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the current iteration of the project or false it no current iteration can be found
|
26
|
+
#
|
27
|
+
# @return [Iteration, false] the current iteration or false
|
28
|
+
def current
|
29
|
+
iterations.find { |iteration| iteration.current? } || false
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the future iterations of the project
|
33
|
+
#
|
34
|
+
# @return [Iterations] the future iterations
|
35
|
+
def future
|
36
|
+
iterations.select { |iteration| iteration.future? }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the past iterations of the project
|
40
|
+
#
|
41
|
+
# @return [Iterations] the past iterations
|
42
|
+
def past
|
43
|
+
iterations.select { |iteration| iteration.past? }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|