trajectory 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/lminaudier/trajectory.png?branch=master)](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
|