houston-core 0.7.0 → 0.8.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +45 -45
  3. data/app/assets/javascripts/houston/app/models/ticket.coffee +7 -5
  4. data/app/assets/javascripts/houston/app/views/new_ticket_view.coffee +1 -21
  5. data/app/assets/javascripts/houston/core/handlebars_helpers.coffee +0 -6
  6. data/app/assets/stylesheets/houston/application/commit.scss +16 -0
  7. data/app/assets/stylesheets/houston/application/navigation.scss +1 -0
  8. data/app/assets/stylesheets/houston/application/new_ticket_view.scss +0 -4
  9. data/app/assets/stylesheets/houston/application/projects.css.scss +0 -14
  10. data/app/assets/stylesheets/houston/application/release_form.scss +1 -1
  11. data/app/assets/stylesheets/houston/application/teams.scss +68 -0
  12. data/app/assets/templates/tickets/index.hbs +0 -1
  13. data/app/controllers/github/pulls_controller.rb +1 -0
  14. data/app/controllers/project_tickets_controller.rb +1 -5
  15. data/app/controllers/projects_controller.rb +10 -12
  16. data/app/controllers/teams_controller.rb +60 -0
  17. data/app/controllers/users_controller.rb +12 -12
  18. data/app/helpers/commit_helper.rb +3 -5
  19. data/app/helpers/exposure_helper.rb +1 -1
  20. data/app/helpers/ticket_helper.rb +0 -13
  21. data/app/interactors/ticket_report.rb +0 -7
  22. data/app/mailers/project_notification.rb +2 -23
  23. data/app/mailers/view_mailer.rb +0 -1
  24. data/app/models/ability.rb +28 -19
  25. data/app/models/commit.rb +0 -4
  26. data/app/models/project.rb +14 -29
  27. data/app/models/release.rb +0 -18
  28. data/app/models/role.rb +0 -22
  29. data/app/models/team.rb +43 -0
  30. data/app/models/team_user.rb +68 -0
  31. data/app/models/ticket.rb +2 -22
  32. data/app/models/user.rb +21 -30
  33. data/app/views/devise/sessions/new.html.erb +9 -7
  34. data/app/views/layouts/_mobile_navigation.html.erb +5 -3
  35. data/app/views/layouts/_navigation.html.erb +11 -10
  36. data/app/views/layouts/_tester_bar.html.erb +1 -1
  37. data/app/views/layouts/application.html.erb +1 -2
  38. data/app/views/project_tickets/_tickets.html.erb +0 -1
  39. data/app/views/project_tickets/index.xls.erb +0 -35
  40. data/app/views/project_tickets/new.html.erb +1 -2
  41. data/app/views/projects/_form.html.erb +7 -12
  42. data/app/views/projects/index.html.erb +10 -17
  43. data/app/views/releases/_changelog.html.erb +11 -14
  44. data/app/views/teams/_form.html.erb +45 -0
  45. data/app/views/teams/edit.html.erb +8 -0
  46. data/app/views/teams/index.html.erb +62 -0
  47. data/app/views/teams/new.html.erb +7 -0
  48. data/app/views/users/_form.html.erb +2 -11
  49. data/app/views/users/index.html.erb +8 -20
  50. data/app/views/users/new.html.erb +1 -1
  51. data/config/initializers/inflections.rb +7 -6
  52. data/config/routes.rb +19 -17
  53. data/db/migrate/20160812233255_repurpose_users_role.rb +16 -0
  54. data/db/migrate/20160813001242_create_teams.rb +42 -0
  55. data/db/structure.sql +110 -116
  56. data/lib/houston/boot.rb +0 -1
  57. data/lib/houston/boot/configuration.rb +26 -11
  58. data/lib/houston/boot/events.rb +0 -4
  59. data/lib/houston/version.rb +1 -1
  60. data/templates/new-instance/config/abilities.rb +68 -51
  61. data/templates/new-instance/config/conversations/mentions/unfurl_tasks.rb +1 -1
  62. data/templates/new-instance/config/events/deploy/notify_deployer_when_finished.rb +24 -16
  63. data/templates/new-instance/config/events/tests/email_when_completed.rb +11 -0
  64. data/templates/new-instance/config/integrations/ldap.rb +4 -0
  65. data/templates/new-instance/config/main.rb +10 -17
  66. data/templates/new-module/test/fixtures/users.yml +1 -2
  67. data/test/acceptance/creating_a_release_test.rb +1 -1
  68. data/test/factories/project_factory.rb +1 -0
  69. data/test/factories/user_factory.rb +3 -1
  70. data/test/fixtures/teams.yml +2 -0
  71. data/test/fixtures/teams_users.yml +5 -0
  72. data/test/fixtures/users.yml +1 -2
  73. data/test/integration/ci_integration_test.rb +2 -2
  74. data/test/support/config.rb +3 -5
  75. data/test/unit/controllers/deploys_controller_test.rb +11 -5
  76. data/test/unit/controllers/users_controller_test.rb +46 -0
  77. data/test/unit/models/release_test.rb +0 -9
  78. data/test/unit/models/serializer_test.rb +0 -8
  79. data/test/unit/models/ticket_test.rb +0 -32
  80. metadata +21 -24
  81. data/app/assets/javascripts/houston/app/table_row_expander.coffee +0 -52
  82. data/app/assets/javascripts/houston/app/views/problems_view.coffee +0 -114
  83. data/app/assets/stylesheets/houston/application/follow_up.scss +0 -44
  84. data/app/controllers/project_exceptions_controller.rb +0 -36
  85. data/app/controllers/project_pretickets_controller.rb +0 -27
  86. data/app/mailers/deploy_notification.rb +0 -33
  87. data/app/models/antecedent.rb +0 -15
  88. data/app/models/ticket_antecedent.rb +0 -39
  89. data/app/models/value_statement.rb +0 -9
  90. data/app/presenters/problem_presenter.rb +0 -49
  91. data/app/presenters/tester_presenter.rb +0 -15
  92. data/app/views/project_notification/follow_up.html.erb +0 -26
  93. data/app/views/project_pretickets/show.html.erb +0 -49
  94. data/app/views/releases/_antecedents.html.erb +0 -12
  95. data/config/initializers/hard_coded_knowledge.rb +0 -141
  96. data/db/migrate/20130427223925_create_project_quotas.rb +0 -14
  97. data/db/migrate/20140506035755_create_value_statements.rb +0 -9
  98. data/lib/houston/boot/ticket_antecedent_serializer.rb +0 -21
  99. data/test/unit/models/ticket_antecedents_test.rb +0 -38
@@ -4,6 +4,7 @@ class Project < ActiveRecord::Base
4
4
  include FeatureState
5
5
  include Houston::Props
6
6
 
7
+ belongs_to :team
7
8
  has_many :releases, dependent: :destroy
8
9
  has_many :commits, dependent: :destroy, extend: CommitSynchronizer
9
10
  has_many :tickets, dependent: :destroy, extend: TicketSynchronizer
@@ -12,22 +13,9 @@ class Project < ActiveRecord::Base
12
13
  has_many :test_runs, dependent: :destroy
13
14
  has_many :tests, dependent: :destroy
14
15
  has_many :deploys
15
- has_many :roles, -> { joins(:user).merge(User.unretired) }, dependent: :destroy, validate: false
16
- has_many :value_statements, dependent: :destroy
17
16
  has_many :pull_requests, class_name: "Github::PullRequest"
18
17
  belongs_to :head, class_name: "Commit", foreign_key: "head_sha", primary_key: "sha"
19
18
 
20
- Houston.config.project_roles.each do |role|
21
- collection_name = role.downcase.gsub(' ', '_').pluralize
22
- class_eval <<-RUBY
23
- has_many :#{collection_name}, -> { where(Role.arel_table[:name].eq("#{role}")) }, class_name: "User", through: :roles, source: :user
24
- RUBY
25
- end
26
-
27
- accepts_nested_attributes_for :roles, :allow_destroy => true, # <-- !todo: authorized access only
28
- reject_if: proc { |attrs| attrs[:user_id].blank? or attrs[:name].blank? }
29
- accepts_nested_attributes_for :value_statements, :allow_destroy => true
30
-
31
19
  before_validation :generate_default_slug, :set_default_color
32
20
  validates_presence_of :name, :slug, :color
33
21
 
@@ -50,6 +38,8 @@ class Project < ActiveRecord::Base
50
38
  Houston.config.project_colors[color]
51
39
  end
52
40
 
41
+
42
+
53
43
  def environments
54
44
  @environments ||= deploys.environments.map(&:downcase).uniq
55
45
  end
@@ -66,6 +56,8 @@ class Project < ActiveRecord::Base
66
56
  Environment.new(self, environment_name)
67
57
  end
68
58
 
59
+
60
+
69
61
  def extended_attributes
70
62
  raise NotImplementedError, "This feature has been deprecated; use props"
71
63
  end
@@ -82,14 +74,14 @@ class Project < ActiveRecord::Base
82
74
  raise NotImplementedError, "This feature has been deprecated; use props"
83
75
  end
84
76
 
85
- def testers
86
- @testers ||= User.testers
87
- end
77
+
88
78
 
89
79
  def self.[](slug)
90
80
  find_by_slug(slug)
91
81
  end
92
82
 
83
+
84
+
93
85
  def self.with_feature(feature)
94
86
  where ["? = ANY(projects.selected_features)", feature]
95
87
  end
@@ -113,24 +105,17 @@ class Project < ActiveRecord::Base
113
105
  # Teammates
114
106
  # ------------------------------------------------------------------------- #
115
107
 
108
+ delegate *Houston.config.roles.map { |role| role.downcase.gsub(" ", "_").pluralize }, to: :team
109
+
116
110
  def teammates
117
- roles.participants.to_users
111
+ return User.none if team.nil?
112
+ team.users
118
113
  end
119
114
 
120
115
  def followers # <-- redefine followers to be everyone who participates in or follows the project
121
- roles.to_users
122
- end
123
-
124
- def add_teammate(user_or_id, role="Follower")
125
- attributes = {project: self, name: role}
126
- attributes[user_or_id.is_a?(User) ? :user : :user_id] = user_or_id
127
- roles.create!(attributes)
128
- end
129
-
130
- def is_teammate?(user_or_id)
131
- roles.for_user(user_or_id).any?
116
+ puts "DEPRECATED: Project#followers is deprecated; use Project#teammates instead"
117
+ teammates
132
118
  end
133
- alias :teammate? :is_teammate?
134
119
 
135
120
  # ------------------------------------------------------------------------- #
136
121
 
@@ -3,7 +3,6 @@ class Release < ActiveRecord::Base
3
3
  after_create :load_commits!, :if => :can_read_commits?
4
4
  after_create :release_each_ticket!
5
5
  after_create :release_each_task!
6
- after_create :release_each_antecedent!
7
6
  after_create { Houston.observer.fire "release:create", release: self }
8
7
  after_save :update_search_vector, :if => :search_vector_should_change?
9
8
 
@@ -18,8 +17,6 @@ class Release < ActiveRecord::Base
18
17
 
19
18
  default_scope { order("created_at DESC") }
20
19
 
21
- delegate :maintainers, :to => :project
22
-
23
20
  validates_presence_of :user_id
24
21
  validates_uniqueness_of :deploy_id, :allow_nil => true
25
22
  validates_associated :release_changes
@@ -189,15 +186,6 @@ class Release < ActiveRecord::Base
189
186
 
190
187
 
191
188
 
192
- def antecedents
193
- @antecedents ||= (tickets.map(&:antecedents) + commits.map(&:antecedents))
194
- .flatten
195
- .uniq
196
- .sort
197
- end
198
-
199
-
200
-
201
189
  def ignore?
202
190
  !project.show_release_notes_for?(environment_name)
203
191
  end
@@ -242,12 +230,6 @@ private
242
230
  end
243
231
  end
244
232
 
245
- def release_each_antecedent!
246
- antecedents.each do |antecedent|
247
- antecedent.released!(self)
248
- end
249
- end
250
-
251
233
  # http://www.postgresql.org/docs/9.1/static/textsearch-controls.html#TEXTSEARCH-HEADLINE
252
234
  def self.ts_headline(column, query, options={})
253
235
  column = arel_table[column] if column.is_a?(Symbol)
@@ -5,29 +5,8 @@ class Role < ActiveRecord::Base
5
5
 
6
6
  validates :user_id, presence: true
7
7
  validates :project, presence: true
8
- validates :name, presence: true, inclusion: {in: Houston.config.project_roles, message: "\"%{value}\" is unknown. It must be #{Houston.config.project_roles.to_sentence(last_word_connector: ", or ")}"}
9
-
10
-
11
- Houston.config.project_roles.each do |role|
12
- method_name = role.downcase.gsub(' ', '_')
13
- class_eval <<-RUBY
14
- def #{method_name}?
15
- name == "#{role}"
16
- end
17
-
18
- def self.#{method_name.pluralize}
19
- where(name: "#{role}")
20
- end
21
- RUBY
22
- end
23
-
24
8
 
25
9
  class << self
26
-
27
- def participants
28
- where arel_table[:name].not_eq("Follower")
29
- end
30
-
31
10
  def to_users
32
11
  User.where(id: all.select(:user_id))
33
12
  end
@@ -49,7 +28,6 @@ class Role < ActiveRecord::Base
49
28
  def any?
50
29
  count > 0
51
30
  end
52
-
53
31
  end
54
32
 
55
33
  end
@@ -0,0 +1,43 @@
1
+ class Team < ActiveRecord::Base
2
+ include Houston::Props
3
+
4
+ has_and_belongs_to_many :users, readonly: true
5
+ has_many :projects
6
+ has_many :roles, -> { joins(:user).merge(User.unretired) }, class_name: "TeamUser", dependent: :destroy, validate: false
7
+
8
+ default_scope -> { order(name: :asc) }
9
+
10
+ accepts_nested_attributes_for :roles, :allow_destroy => true, # <-- !todo: authorized access only
11
+ reject_if: proc { |attrs| attrs[:user_id].blank? }
12
+
13
+
14
+
15
+ Houston.config.roles.each do |role|
16
+ collection_name = role.downcase.gsub(' ', '_').pluralize
17
+ class_eval <<-RUBY
18
+ has_many :#{collection_name}, -> { where(Role.arel_table[:name].eq("#{role}")) }, class_name: "User", through: :roles, source: :user
19
+ RUBY
20
+ end
21
+
22
+
23
+
24
+ class << self
25
+ def projects
26
+ Project.where(team_id: all.select(:id))
27
+ end
28
+
29
+ def project_ids
30
+ projects.ids
31
+ end
32
+ end
33
+
34
+
35
+
36
+ def add_teammate(user_or_id, *desired_roles)
37
+ teammate = roles.find_or_initialize_by((user_or_id.is_a?(User) ? :user : :user_id) => user_or_id)
38
+ teammate.team = self
39
+ teammate.roles = teammate.roles | desired_roles
40
+ teammate.save!
41
+ end
42
+
43
+ end
@@ -0,0 +1,68 @@
1
+ class TeamUser < ActiveRecord::Base
2
+
3
+ belongs_to :user
4
+ belongs_to :team
5
+ has_many :projects, through: :team
6
+
7
+ validates :user_id, presence: true
8
+ validates :team, presence: true
9
+ validate :roles_should_all_be_defined_by_config
10
+
11
+
12
+
13
+ Houston.config.roles.each do |role|
14
+ method_name = role.downcase.gsub(' ', '_')
15
+ class_eval <<-RUBY
16
+ def #{method_name}?
17
+ name == "#{role}"
18
+ end
19
+
20
+ def self.#{method_name.pluralize}
21
+ where(name: "#{role}")
22
+ end
23
+ RUBY
24
+ end
25
+
26
+
27
+
28
+ class << self
29
+ def to_users
30
+ User.where(id: all.select(:user_id))
31
+ end
32
+
33
+ def to_projects
34
+ Project.where(team_id: all.select(:team_id))
35
+ end
36
+
37
+ def with_role(role)
38
+ where ["? = ANY(roles)", role]
39
+ end
40
+
41
+ def for_user(user_or_id)
42
+ user_id = user_or_id.is_a?(User) ? user_or_id.id : user_or_id
43
+ where user_id: user_id
44
+ end
45
+
46
+ def any?
47
+ count > 0
48
+ end
49
+ end
50
+
51
+
52
+
53
+ def roles
54
+ super || []
55
+ end
56
+
57
+
58
+
59
+ private
60
+
61
+ def roles_should_all_be_defined_by_config
62
+ roles.each do |role|
63
+ next if Houston.config.roles.member?(role)
64
+ errors.add :roles, "includes #{role.inspect} which is not a defined role (#{Houston.config.roles.join(", ")})"
65
+ end
66
+ end
67
+
68
+ end
@@ -4,7 +4,7 @@ class Ticket < ActiveRecord::Base
4
4
  self.inheritance_column = nil
5
5
 
6
6
  versioned only: [:summary, :description, :type,
7
- :tags, :antecedents, :prerequisites,
7
+ :tags, :prerequisites,
8
8
  :closed_at, :resolution, :milestone_id]
9
9
 
10
10
  belongs_to :project
@@ -29,13 +29,11 @@ class Ticket < ActiveRecord::Base
29
29
  before_save :parse_ticket_description, if: :description_changed?
30
30
  before_save :find_reporter, if: :find_reporter?
31
31
  after_save :propagate_milestone_change, if: :milestone_id_changed?
32
- after_save :resolve_antecedents!, if: :just_resolved?
33
- after_save :close_antecedents!, if: :just_closed?
34
32
  after_save :updated_milestone_attributes
35
33
 
36
34
  attr_readonly :number, :project_id
37
35
 
38
- delegate :testers, :maintainers, :ticket_tracker, to: :project
36
+ delegate :ticket_tracker, to: :project
39
37
  delegate :nosync?, to: "self.class"
40
38
 
41
39
 
@@ -200,16 +198,6 @@ class Ticket < ActiveRecord::Base
200
198
 
201
199
 
202
200
 
203
- def antecedents
204
- (super || []).map { |s| TicketAntecedent.from_s(self, s) }
205
- end
206
-
207
- def antecedents=(antecedents)
208
- super Array(antecedents).map(&:to_s)
209
- end
210
-
211
-
212
-
213
201
  def tags
214
202
  (super || []).map(&TicketTag.method(:from_s))
215
203
  end
@@ -309,14 +297,6 @@ private
309
297
  closed_at_changed? && closed_at_was.nil?
310
298
  end
311
299
 
312
- def resolve_antecedents!
313
- antecedents.each(&:resolve!)
314
- end
315
-
316
- def close_antecedents!
317
- antecedents.each(&:close!)
318
- end
319
-
320
300
  def remote_ticket
321
301
  @remote_ticket ||= ticket_tracker.find_ticket_by_number(number)
322
302
  end
@@ -2,8 +2,11 @@ class User < ActiveRecord::Base
2
2
  include Retirement
3
3
  include Houston::Props
4
4
 
5
- has_many :roles, :dependent => :destroy
6
- has_many :credentials, :class_name => "UserCredentials", :dependent => :destroy
5
+ ROLES = %w{Owner Admin Member}.freeze
6
+
7
+ has_many :roles, class_name: "TeamUser", dependent: :destroy
8
+ has_and_belongs_to_many :teams
9
+ has_many :credentials, :class_name => "UserCredentials", dependent: :destroy
7
10
  has_many :tickets, foreign_key: "reporter_id"
8
11
  has_and_belongs_to_many :commits
9
12
  belongs_to :current_project, class_name: "Project"
@@ -12,27 +15,14 @@ class User < ActiveRecord::Base
12
15
 
13
16
  default_scope { order("last_name, first_name") }
14
17
 
15
- default_value_for :role, Houston.config.default_role
16
-
17
18
  validates :first_name, :last_name, :email, :presence => true, :length => {:minimum => 2}
18
- validates :role, presence: true, inclusion: {in: Houston.config.roles, message: "%{value} is not one of the configured roles (#{Houston.config.roles.join(", ")})"}
19
+ validates :role, presence: true, inclusion: {in: ROLES, message: "%{value} is not one of the configured roles (#{ROLES.join(", ")})"}
19
20
  validate :all_email_addresses_must_be_unique
20
21
 
21
22
 
22
23
 
23
- Houston.config.project_roles.each do |role|
24
- method_name = role.downcase.gsub(' ', '_')
25
- collection_name = method_name.pluralize
26
-
27
- class_eval <<-RUBY
28
- def self.#{collection_name}
29
- Role.#{collection_name}.to_users
30
- end
31
- RUBY
32
- end
33
-
34
- Houston.config.roles.each do |role|
35
- method_name = role.downcase.gsub(' ', '_')
24
+ ROLES.each do |role|
25
+ method_name = role.downcase.gsub(" ", "_")
36
26
  collection_name = method_name.pluralize
37
27
 
38
28
  class_eval <<-RUBY
@@ -47,15 +37,18 @@ class User < ActiveRecord::Base
47
37
  end
48
38
 
49
39
 
50
- def self.administrators
51
- where(administrator: true)
40
+ def developer?
41
+ puts "DEPRECATED: User#developer? will be removed"
42
+ legacy_role == "Developer"
52
43
  end
53
44
 
54
-
55
- def self.participants
56
- Role.participants.to_users
45
+ def tester?
46
+ puts "DEPRECATED: User#tester? will be removed"
47
+ legacy_role == "Tester"
57
48
  end
58
49
 
50
+
51
+
59
52
  def self.with_primary_email(email)
60
53
  email = email.downcase if email
61
54
  where(email: email)
@@ -72,8 +65,6 @@ class User < ActiveRecord::Base
72
65
  with_email_address(email_address).first
73
66
  end
74
67
 
75
-
76
-
77
68
  def email=(value)
78
69
  value = value.downcase if value
79
70
  super(value)
@@ -99,11 +90,11 @@ class User < ActiveRecord::Base
99
90
  end
100
91
 
101
92
  def follows?(project)
102
- roles.to_projects.member?(project)
93
+ Role.where(user: self).to_projects.member?(project)
103
94
  end
104
95
 
105
96
  def followed_projects
106
- roles.to_projects.unretired
97
+ Role.where(user: self).to_projects.unretired
107
98
  end
108
99
 
109
100
  def view_options
@@ -154,7 +145,7 @@ class User < ActiveRecord::Base
154
145
  # ------------------------------------------------------------------------- #
155
146
 
156
147
  def self.find_ldap_entry(ldap_connection, auth_key_value)
157
- filter = Net::LDAP::Filter.eq(Houston::TMI::FIELD_USED_FOR_LDAP_LOGIN, auth_key_value)
148
+ filter = Net::LDAP::Filter.eq(Houston.config.authentication_strategy_configuration[:field], auth_key_value)
158
149
  ldap_connection.ldap.search(filter: filter).first
159
150
  end
160
151
 
@@ -162,7 +153,7 @@ class User < ActiveRecord::Base
162
153
  email = entry.mail.first.downcase
163
154
  user = where(email: email).first
164
155
  if user && user.username.nil?
165
- user.update_column :username, entry[Houston::TMI::FIELD_USED_FOR_LDAP_LOGIN][0].to_s
156
+ user.update_column :username, entry[Houston.config.authentication_strategy_configuration[:field]][0].to_s
166
157
  end
167
158
  user
168
159
  end
@@ -170,7 +161,7 @@ class User < ActiveRecord::Base
170
161
  def self.create_from_ldap_entry(attributes, entry)
171
162
  create!(
172
163
  email: entry.mail.first.downcase,
173
- username: entry[Houston::TMI::FIELD_USED_FOR_LDAP_LOGIN][0].to_s,
164
+ username: entry[Houston.config.authentication_strategy_configuration[:field]][0].to_s,
174
165
  password: attributes[:password],
175
166
  first_name: entry.givenname.first,
176
167
  last_name: entry.sn.first )