trajectory 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +19 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +57 -0
  9. data/Rakefile +18 -0
  10. data/config/env.yml +3 -0
  11. data/lib/trajectory/client.rb +24 -0
  12. data/lib/trajectory/core_ext/hash.rb +8 -0
  13. data/lib/trajectory/core_ext.rb +1 -0
  14. data/lib/trajectory/data_access/api.rb +54 -0
  15. data/lib/trajectory/data_access/data_store.rb +70 -0
  16. data/lib/trajectory/data_access.rb +2 -0
  17. data/lib/trajectory/domain/idea.rb +70 -0
  18. data/lib/trajectory/domain/ideas.rb +23 -0
  19. data/lib/trajectory/domain/iteration.rb +85 -0
  20. data/lib/trajectory/domain/iterations.rb +46 -0
  21. data/lib/trajectory/domain/project.rb +168 -0
  22. data/lib/trajectory/domain/projects.rb +55 -0
  23. data/lib/trajectory/domain/stories.rb +56 -0
  24. data/lib/trajectory/domain/story.rb +119 -0
  25. data/lib/trajectory/domain/update.rb +17 -0
  26. data/lib/trajectory/domain/user.rb +28 -0
  27. data/lib/trajectory/domain/users.rb +30 -0
  28. data/lib/trajectory/domain.rb +11 -0
  29. data/lib/trajectory/exceptions/bad_environment_error.rb +7 -0
  30. data/lib/trajectory/exceptions/missing_attribute_error.rb +12 -0
  31. data/lib/trajectory/exceptions/velocity_equal_to_zero_error.rb +11 -0
  32. data/lib/trajectory/exceptions.rb +3 -0
  33. data/lib/trajectory/version.rb +3 -0
  34. data/lib/trajectory.rb +15 -0
  35. data/spec/fabricators/iteration_fabricator.rb +18 -0
  36. data/spec/fabricators/project_fabricator.rb +11 -0
  37. data/spec/fabricators/story_fabricator.rb +18 -0
  38. data/spec/fixtures/vcr_cassettes/client.yml +853 -0
  39. data/spec/fixtures/vcr_cassettes/projects_and_ideas.yml +98 -0
  40. data/spec/fixtures/vcr_cassettes/projects_and_ideas_association.yml +52 -0
  41. data/spec/fixtures/vcr_cassettes/projects_and_iterations.yml +49 -0
  42. data/spec/fixtures/vcr_cassettes/projects_and_iterations_association.yml +49 -0
  43. data/spec/fixtures/vcr_cassettes/projects_and_stories.yml +101 -0
  44. data/spec/fixtures/vcr_cassettes/projects_and_stories_association.yml +147 -0
  45. data/spec/integration/client_spec.rb +96 -0
  46. data/spec/spec_helper.rb +13 -0
  47. data/spec/support/env.rb +11 -0
  48. data/spec/support/fabrication.rb +1 -0
  49. data/spec/support/simple_cov.rb +11 -0
  50. data/spec/support/timecop.rb +1 -0
  51. data/spec/support/vcr.rb +8 -0
  52. data/spec/unit/client_spec.rb +27 -0
  53. data/spec/unit/core_ext/hash_spec.rb +11 -0
  54. data/spec/unit/data_access/api_spec.rb +100 -0
  55. data/spec/unit/data_access/data_store_spec.rb +86 -0
  56. data/spec/unit/domain/idea_spec.rb +46 -0
  57. data/spec/unit/domain/ideas_spec.rb +22 -0
  58. data/spec/unit/domain/iteration_spec.rb +51 -0
  59. data/spec/unit/domain/iterations_spec.rb +47 -0
  60. data/spec/unit/domain/project_spec.rb +163 -0
  61. data/spec/unit/domain/projects_spec.rb +84 -0
  62. data/spec/unit/domain/stories_spec.rb +84 -0
  63. data/spec/unit/domain/story_spec.rb +71 -0
  64. data/spec/unit/domain/user_spec.rb +29 -0
  65. data/spec/unit/domain/users_spec.rb +36 -0
  66. data/spec/unit/exceptions/bad_environment_error_spec.rb +9 -0
  67. data/spec/unit/exceptions/missing_attribute_error_spec.rb +10 -0
  68. data/spec/unit/exceptions/velocity_equal_to_zero_error_spec.rb +10 -0
  69. data/trajectory.gemspec +21 -0
  70. 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
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ bin
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format doc
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p0
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - jruby-19mode
6
+ - rbx-19mode
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,3 @@
1
+ test:
2
+ trajectory_api_key: 76900a72434cfe249da911eea2a6c94a
3
+ trajectory_account_keyword: fake-user-for-api-test
@@ -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,8 @@
1
+ class Hash
2
+ def symbolize_keys!
3
+ keys.each do |key|
4
+ self[(key.to_sym rescue key) || key] = delete(key)
5
+ end
6
+ self
7
+ end
8
+ 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,2 @@
1
+ require 'trajectory/data_access/api'
2
+ require 'trajectory/data_access/data_store'
@@ -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