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
@@ -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
|