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
@@ -0,0 +1,168 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Project
|
3
|
+
include Virtus
|
4
|
+
|
5
|
+
NUMBER_OF_WORKING_DAYS_BY_WEEK = 5.0
|
6
|
+
|
7
|
+
attr_writer :stories, :users_collection
|
8
|
+
|
9
|
+
# @return [Integer] the unique identifier of the project.
|
10
|
+
# @raise [MissingAttributeError] if id is nil
|
11
|
+
attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
|
12
|
+
|
13
|
+
# @return [String] the project name
|
14
|
+
attribute :name, String
|
15
|
+
# @!method archived?
|
16
|
+
# @return [true, false] true if the project has been archived, false otherwise
|
17
|
+
attribute :archived, Boolean
|
18
|
+
# @return [DateTime] creation date of the project
|
19
|
+
attribute :created_at, DateTime
|
20
|
+
# @return [Integer] the current velocity of the project
|
21
|
+
attribute :estimated_velocity, Integer
|
22
|
+
# @return [Array<Integer>] the velocities of past iterations
|
23
|
+
attribute :historic_velocity, Array[Integer]
|
24
|
+
# @return [String] project keyword identifier
|
25
|
+
attribute :keyword, String
|
26
|
+
# @return [DateTime] last modification date of the project
|
27
|
+
attribute :updated_at, DateTime
|
28
|
+
# @return [Integer] number of completed iterations in the project
|
29
|
+
attribute :completed_iterations_count, Integer
|
30
|
+
# @return [Integer] number of completed stories in the project
|
31
|
+
attribute :completed_stories_count, Integer
|
32
|
+
|
33
|
+
# Returns true if two projects are the sames i.e they share the same id
|
34
|
+
# attribute
|
35
|
+
#
|
36
|
+
# @param other [Project] the other object to compare
|
37
|
+
# @return [true, false]
|
38
|
+
def ==(other)
|
39
|
+
id == other.id
|
40
|
+
end
|
41
|
+
|
42
|
+
# Fetch all stories that belongs to the project
|
43
|
+
#
|
44
|
+
# @return [Stories] the stories collection
|
45
|
+
def stories
|
46
|
+
@stories ||= DataStore.stories_for_project(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Fetch all iterations that belongs to the project
|
50
|
+
#
|
51
|
+
# @return [Iterations] the iterations collection
|
52
|
+
def iterations
|
53
|
+
@iterations ||= DataStore.iterations_for_project(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Fetch all ideas that belongs to the project
|
57
|
+
#
|
58
|
+
# @return [Ideas] the ideas collection
|
59
|
+
def ideas
|
60
|
+
@ideas ||= DataStore.ideas_for_project(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Fetch all users that belongs to the project
|
64
|
+
#
|
65
|
+
# @return [Users] the users collection
|
66
|
+
def users
|
67
|
+
@users_collection ||= DataStore.users_for_project(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Fetch updates that belongs to the project since a given date
|
71
|
+
#
|
72
|
+
# @param since [DateTime] the date
|
73
|
+
# @return [Updates] the updates collection
|
74
|
+
def updates(since = DateTime.now)
|
75
|
+
DataStore.updates_for_project(self, since)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Fetch a user from the project given its id or false if it does not exist
|
79
|
+
#
|
80
|
+
# @return [User, false] the user or false
|
81
|
+
def find_user_by_id(id)
|
82
|
+
users.find_by_id(id)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Fetch the stories in a given iteration of a project
|
86
|
+
#
|
87
|
+
# @param iteration [Iteration] the iteration
|
88
|
+
# @return [Stories] the user
|
89
|
+
def stories_in_iteration(iteration)
|
90
|
+
stories.in_iteration(iteration)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the sum of all points of each story of the project
|
94
|
+
#
|
95
|
+
# @return [Integer] the points accumulation
|
96
|
+
def total_points
|
97
|
+
stories.inject(0) do |accumulator, story|
|
98
|
+
accumulator += story.points
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns estimated end date of the project with the actual estimated velocity
|
103
|
+
#
|
104
|
+
# @return [Date] the estimated date
|
105
|
+
def estimated_end_date
|
106
|
+
Date.today + remaining_days
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the estimated number of days (weekend included) remaining before the end of the project.
|
110
|
+
#
|
111
|
+
# This is usefull to estimate the project end date.
|
112
|
+
#
|
113
|
+
# @return [Integer] the number of days
|
114
|
+
# @raise [VelocityEqualToZeroError] if estimated velocity is equal to zero (i.e the project can't be finished because no one actually works on it)
|
115
|
+
def remaining_days
|
116
|
+
raise VelocityEqualToZeroError.new(self) if estimated_velocity_per_day == 0
|
117
|
+
(remaining_points / estimated_velocity_per_day).ceil
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns sum of points of not completed stories
|
121
|
+
#
|
122
|
+
# @return [Integer] the number of points
|
123
|
+
def remaining_points
|
124
|
+
stories.not_completed.inject(0) do |accumulator, story|
|
125
|
+
accumulator += story.points
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the estimated velocity by day over a week
|
130
|
+
#
|
131
|
+
# @return [Integer] the estimated velocity
|
132
|
+
def estimated_velocity_per_day
|
133
|
+
estimated_velocity / 7.0
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns the estimated number of working days (weekend excluded) remaining before the end of the project.
|
137
|
+
#
|
138
|
+
# This is usefull to be able to evaluate project budget as not all days are billable.
|
139
|
+
#
|
140
|
+
# @return [Integer] the number of days
|
141
|
+
# @raise [VelocityEqualToZeroError] if estimated velocity is equal to zero (i.e the project can't be finished because no one actually works on it)
|
142
|
+
def remaining_working_days
|
143
|
+
raise VelocityEqualToZeroError.new(self) if estimated_velocity_per_working_day == 0
|
144
|
+
(remaining_points / estimated_velocity_per_working_day).ceil
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns the estimated velocity by day over billable days (actually 5 days)
|
148
|
+
#
|
149
|
+
# @return [Integer] the estimated velocity
|
150
|
+
def estimated_velocity_per_working_day
|
151
|
+
estimated_velocity / NUMBER_OF_WORKING_DAYS_BY_WEEK
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the completion percentage of the project
|
155
|
+
#
|
156
|
+
# @return [Float] the percentage
|
157
|
+
def percent_complete
|
158
|
+
(accepted_points.to_f / total_points * 100.0).round(1)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns the sum of accepted story points
|
162
|
+
#
|
163
|
+
# @return [Integer] the number of points
|
164
|
+
def accepted_points
|
165
|
+
total_points - remaining_points
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Trajectory
|
4
|
+
class Projects < SimpleDelegator
|
5
|
+
alias :projects :__getobj__
|
6
|
+
|
7
|
+
# Creates a new collection of {Projects}
|
8
|
+
#
|
9
|
+
# @param projects [Array<Project>] a arbitrary lenght list of {Project} objects
|
10
|
+
def initialize(*projects)
|
11
|
+
super(projects)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Create a new collection of {Project} from a JSON array of attributes from trajectory API
|
15
|
+
#
|
16
|
+
# @param json_attributes [Hash] the hash of attributes of each project of the collection
|
17
|
+
def self.from_json(json_attributes)
|
18
|
+
new(*json_attributes.map do |attributes|
|
19
|
+
Project.new(attributes.symbolize_keys!)
|
20
|
+
end)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Fetch the project with the given id in the collection. If it is not found,
|
24
|
+
# it returns false
|
25
|
+
#
|
26
|
+
# @param id [Integer] the project id
|
27
|
+
# @return [Project, false] the found project or false
|
28
|
+
def find_by_id(id)
|
29
|
+
projects.find { |project| project.id == id } || false
|
30
|
+
end
|
31
|
+
|
32
|
+
# Fetch the project with the given keyword in the collection. If it is not found,
|
33
|
+
# it returns false
|
34
|
+
#
|
35
|
+
# @param keyword [String] the project keyword
|
36
|
+
# @return [Project, false] the found project or false
|
37
|
+
def find_by_keyword(keyword)
|
38
|
+
projects.find { |project| project.keyword == keyword } || false
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the archived projects of the collection
|
42
|
+
#
|
43
|
+
# @return [Projects] the filtered collection
|
44
|
+
def archived
|
45
|
+
projects.select { |project| project.archived? }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the active projects of the collection
|
49
|
+
#
|
50
|
+
# @return [Projects] the filtered collection
|
51
|
+
def active
|
52
|
+
projects.select { |project| !project.archived? }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Trajectory
|
4
|
+
class Stories < SimpleDelegator
|
5
|
+
alias :stories :__getobj__
|
6
|
+
|
7
|
+
# Creates a new collection of {Story}
|
8
|
+
#
|
9
|
+
# @param stories [Array<Story>] a arbitrary lenght list of {Story} objects
|
10
|
+
def initialize(*stories)
|
11
|
+
super(stories)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Create a new collection of {Story} from a JSON array of attributes from trajectory API
|
15
|
+
#
|
16
|
+
# @param project [Project] the project the stories belongs to
|
17
|
+
# @param json_attributes [Hash] the hash of attributes of each story of the collection
|
18
|
+
def self.from_json(project, json_attributes)
|
19
|
+
new(*json_attributes.map do |attributes|
|
20
|
+
attributes = attributes.symbolize_keys!.merge({project_id: project.id})
|
21
|
+
Story.new(attributes)
|
22
|
+
end)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns started stories of the collection
|
26
|
+
#
|
27
|
+
# @return [Stories] started stories collection
|
28
|
+
def started
|
29
|
+
stories.select(&:started?)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns unstarted stories of the collection
|
33
|
+
#
|
34
|
+
# @return [Stories] unstarted stories collection
|
35
|
+
def unstarted
|
36
|
+
stories.select(&:unstarted?)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns not completed stories of the collection
|
40
|
+
#
|
41
|
+
# @return [Stories] not completed stories collection
|
42
|
+
def not_completed
|
43
|
+
stories.reject(&:completed?)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns stories of the collection that are in the given iteration
|
47
|
+
#
|
48
|
+
# @param iteration [Iteration] an iteration
|
49
|
+
# @return [Stories] stories collection in iteration
|
50
|
+
def in_iteration(iteration)
|
51
|
+
stories.select do |story|
|
52
|
+
story.in_iteration?(iteration)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Story
|
3
|
+
include Virtus
|
4
|
+
|
5
|
+
# @return [Integer] the unique identifier of the story
|
6
|
+
# @raise [MissingAttributeError] if id is nil
|
7
|
+
attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
|
8
|
+
# @return [String] the name of the user assigned to the story
|
9
|
+
attribute :assignee_name, String
|
10
|
+
# @return [String] the type of story as "Feature", "Bug", "Todo" or "Milestone"
|
11
|
+
attribute :task_type, String
|
12
|
+
# The Integer position of the story in the backlog.
|
13
|
+
# Lower is higher.
|
14
|
+
# @return [Integer] the position of the story
|
15
|
+
attribute :position, Integer
|
16
|
+
# @return [DateTime] the creation date of the story
|
17
|
+
attribute :created_at, DateTime
|
18
|
+
# @return [Array<String>] the valid states the story can transition to
|
19
|
+
# @todo Replace String by Symbol
|
20
|
+
attribute :state_events, Array[String]
|
21
|
+
# @return [String] the title of the story
|
22
|
+
attribute :title, String
|
23
|
+
# @return [true, false] true if design is needed for the story, false otherwise
|
24
|
+
attribute :design_needed, Boolean
|
25
|
+
# @return [DateTime] the last modification date of the story
|
26
|
+
attribute :updated_at, DateTime
|
27
|
+
# @return [String] the subject of the idea the story is attached to
|
28
|
+
attribute :idea_subject, String
|
29
|
+
# @!method archived?
|
30
|
+
# @return [true, false] true if the story has been archived, false otherwise
|
31
|
+
attribute :archived, Boolean
|
32
|
+
# @return [Integer] estimation in points of the story complexity
|
33
|
+
attribute :points, Integer
|
34
|
+
# @!method development_needed?
|
35
|
+
# @return [true, false] true if development is needed for the story, false otherwise
|
36
|
+
attribute :development_needed, Boolean
|
37
|
+
# @!method deleted?
|
38
|
+
# @return [ture, false] true if the story has been deleted, false otherwise
|
39
|
+
attribute :deleted, Boolean
|
40
|
+
# @return [String] name of the user that created the story
|
41
|
+
attribute :user_name, String
|
42
|
+
# @return [Integer] id of the user that created the story
|
43
|
+
# @see #user
|
44
|
+
attribute :user_id, Integer
|
45
|
+
# @return [Integer] number of comments of the story
|
46
|
+
attribute :comments_count, Integer
|
47
|
+
# @return [Symbol] state of the story in [:started, :unstarted, :delivered, :accepted, :rejected]
|
48
|
+
attribute :state, Symbol
|
49
|
+
# @return [Integer] project id the story belongs to
|
50
|
+
# @see #project
|
51
|
+
attribute :project_id, Integer
|
52
|
+
# @return [Integer] iteration id the story belongs to
|
53
|
+
# @see #iteration
|
54
|
+
attribute :iteration_id, Integer
|
55
|
+
|
56
|
+
# Returns true if two stories are the sames i.e they share the same id
|
57
|
+
# attribute
|
58
|
+
#
|
59
|
+
# @param other [Story] the other object to compare
|
60
|
+
# @return [true, false]
|
61
|
+
def ==(other)
|
62
|
+
id == other.id
|
63
|
+
end
|
64
|
+
|
65
|
+
# Fetch the project the story belongs to
|
66
|
+
#
|
67
|
+
# @return [Project]
|
68
|
+
def project
|
69
|
+
@project ||= DataStore.find_project_by_id(project_id)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns true if the story is started i.e in :started state
|
73
|
+
#
|
74
|
+
# @return [true, false]
|
75
|
+
def started?
|
76
|
+
state == :started
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns true if the story is not started i.e in :unstarted state
|
80
|
+
#
|
81
|
+
# @return [true, false]
|
82
|
+
def unstarted?
|
83
|
+
state == :unstarted
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns true if the story is not completed i.e not in :accepted state
|
87
|
+
#
|
88
|
+
# @return [true, false]
|
89
|
+
def not_completed?
|
90
|
+
!completed?
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns true if the story is completed i.e in :accepted state
|
94
|
+
#
|
95
|
+
# @return [true, false]
|
96
|
+
def completed?
|
97
|
+
state == :accepted
|
98
|
+
end
|
99
|
+
|
100
|
+
# Fetch the user that created the story
|
101
|
+
#
|
102
|
+
# @return [User]
|
103
|
+
def user
|
104
|
+
@user ||= DataStore.find_user_of_project_with_id(project, user_id)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns true if the story belongs to the given iteration, false otherwise
|
108
|
+
#
|
109
|
+
# @param iteration [Iteration] an iteration
|
110
|
+
# @return [true, false]
|
111
|
+
def in_iteration?(iteration)
|
112
|
+
iteration_id == iteration.id
|
113
|
+
end
|
114
|
+
|
115
|
+
# @todo Not Yet Implemented
|
116
|
+
def iteration
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Update
|
3
|
+
# @attr_reader stories [Stories] the updated stories by the update
|
4
|
+
# @attr_reader iterations [iterations] the updated iterations by the update
|
5
|
+
attr_reader :stories, :iterations
|
6
|
+
|
7
|
+
# Creates a new update with given updated stories and iterations
|
8
|
+
#
|
9
|
+
# @param stories [Stories] collection of updated stories
|
10
|
+
# @param iterations [Iterations] collection of updated iterations
|
11
|
+
# :nocov:
|
12
|
+
def initialize(stories, iterations)
|
13
|
+
@stories = stories
|
14
|
+
@iterations = iterations
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class User
|
3
|
+
include Virtus
|
4
|
+
|
5
|
+
# @return [Integer] the unique identifier of the User
|
6
|
+
# @raise [MissingAttributeError] if id is nil
|
7
|
+
attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
|
8
|
+
# @return [String] the full name of the user
|
9
|
+
attribute :name, String
|
10
|
+
# @return [DateTime] the creation date of the user account
|
11
|
+
attribute :created_at, DateTime
|
12
|
+
# @return [DateTime] the last modification date of the user account
|
13
|
+
attribute :updated_at, DateTime
|
14
|
+
# @return [String] the url to the avatar image of the user (uses gravatar)
|
15
|
+
attribute :gravatar_url, String
|
16
|
+
# @return [String] the email of the user
|
17
|
+
attribute :email, String
|
18
|
+
|
19
|
+
# Returns true if two users are the sames i.e they share the same id
|
20
|
+
# attribute
|
21
|
+
#
|
22
|
+
# @param other [User] the other object to compare
|
23
|
+
# @return [true, false]
|
24
|
+
def ==(other)
|
25
|
+
id == other.id
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Trajectory
|
2
|
+
class Users < SimpleDelegator
|
3
|
+
alias :users :__getobj__
|
4
|
+
|
5
|
+
# Creates a new collection of {User}
|
6
|
+
#
|
7
|
+
# @param users [Array<User>] a arbitrary lenght list of {User} objects
|
8
|
+
def initialize(*users)
|
9
|
+
super(users)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a new collection of {User} from a JSON array of attributes from trajectory API
|
13
|
+
#
|
14
|
+
# @param json_attributes [Hash] the hash of attributes of each user of the collection
|
15
|
+
def self.from_json(json_attributes)
|
16
|
+
new(*json_attributes.map do |attributes|
|
17
|
+
User.new(attributes.symbolize_keys!)
|
18
|
+
end)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the the first user with the given id in the collection or false if
|
22
|
+
# no user can be found with the id
|
23
|
+
#
|
24
|
+
# @param id [Integer] the id of the user to find
|
25
|
+
# @return [User, false] the user with the given id
|
26
|
+
def find_by_id(id)
|
27
|
+
users.find { |user| user.id == id } || false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'trajectory/domain/story'
|
2
|
+
require 'trajectory/domain/stories'
|
3
|
+
require 'trajectory/domain/project'
|
4
|
+
require 'trajectory/domain/projects'
|
5
|
+
require 'trajectory/domain/iteration'
|
6
|
+
require 'trajectory/domain/iterations'
|
7
|
+
require 'trajectory/domain/idea'
|
8
|
+
require 'trajectory/domain/ideas'
|
9
|
+
require 'trajectory/domain/user'
|
10
|
+
require 'trajectory/domain/users'
|
11
|
+
require 'trajectory/domain/update'
|
data/lib/trajectory.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
require 'httparty'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require "trajectory/version"
|
6
|
+
|
7
|
+
require 'trajectory/core_ext'
|
8
|
+
require 'trajectory/exceptions'
|
9
|
+
require 'trajectory/domain'
|
10
|
+
require 'trajectory/data_access'
|
11
|
+
|
12
|
+
require 'trajectory/client'
|
13
|
+
|
14
|
+
module Trajectory
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Fabricator(:iteration, class_name: Trajectory::Iteration) do
|
2
|
+
id { sequence }
|
3
|
+
percent_complete 90.0
|
4
|
+
started_stories_count 4
|
5
|
+
created_at DateTime.now
|
6
|
+
estimated_velocity 12
|
7
|
+
delivered_stories_count 28
|
8
|
+
updated_at DateTime.now
|
9
|
+
unstarted_stories_count 31
|
10
|
+
starts_on DateTime.now
|
11
|
+
current false
|
12
|
+
stories_count 59
|
13
|
+
complete false
|
14
|
+
accepted_points 51
|
15
|
+
estimated_points 72
|
16
|
+
comments_count 8
|
17
|
+
accepted_stories_count 24
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Fabricator(:project, class_name: Trajectory::Project) do
|
2
|
+
archived false
|
3
|
+
created_at DateTime.now
|
4
|
+
estimated_velocity 20
|
5
|
+
historic_velocity [10, 9, 12, 13, 18]
|
6
|
+
id { sequence(:id, 1) }
|
7
|
+
keyword 'keyword'
|
8
|
+
updated_at DateTime.now
|
9
|
+
completed_iterations_count 10
|
10
|
+
completed_stories_count 42
|
11
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Fabricator(:story, class_name: Trajectory::Story) do
|
2
|
+
assignee_name 'An asignee'
|
3
|
+
task_type 'Feature'
|
4
|
+
position { sequence }
|
5
|
+
created_at DateTime.now
|
6
|
+
state_events []
|
7
|
+
title 'A story title'
|
8
|
+
design_needed true
|
9
|
+
updated_at DateTime.now
|
10
|
+
idea_subject 'the related idea subject'
|
11
|
+
archived false
|
12
|
+
points 1
|
13
|
+
id { sequence }
|
14
|
+
development_needed true
|
15
|
+
deleted false
|
16
|
+
user_name 'A user'
|
17
|
+
comments_count 24
|
18
|
+
end
|