Commit a1999955eb0b212c818a383bc54f8392194d0bc1
Exists in
master
and in
4 other branches
Merge pull request #2461 from gitlabhq/remove_roles
Remove models/roles and return to fat models
Showing
33 changed files
with
1276 additions
and
1380 deletions
Show diff stats
@@ -0,0 +1,102 @@ | @@ -0,0 +1,102 @@ | ||
1 | +# == Issuable concern | ||
2 | +# | ||
3 | +# Contains common functionality shared between Issues and MergeRequests | ||
4 | +# | ||
5 | +# Used by Issue, MergeRequest | ||
6 | +# | ||
7 | +module Issuable | ||
8 | + extend ActiveSupport::Concern | ||
9 | + | ||
10 | + included do | ||
11 | + belongs_to :project | ||
12 | + belongs_to :author, class_name: "User" | ||
13 | + belongs_to :assignee, class_name: "User" | ||
14 | + belongs_to :milestone | ||
15 | + has_many :notes, as: :noteable, dependent: :destroy | ||
16 | + | ||
17 | + validates :project, presence: true | ||
18 | + validates :author, presence: true | ||
19 | + validates :title, presence: true, length: { within: 0..255 } | ||
20 | + validates :closed, inclusion: { in: [true, false] } | ||
21 | + | ||
22 | + scope :opened, where(closed: false) | ||
23 | + scope :closed, where(closed: true) | ||
24 | + scope :of_group, ->(group) { where(project_id: group.project_ids) } | ||
25 | + scope :assigned, ->(u) { where(assignee_id: u.id)} | ||
26 | + scope :recent, order("created_at DESC") | ||
27 | + | ||
28 | + delegate :name, | ||
29 | + :email, | ||
30 | + to: :author, | ||
31 | + prefix: true | ||
32 | + | ||
33 | + delegate :name, | ||
34 | + :email, | ||
35 | + to: :assignee, | ||
36 | + allow_nil: true, | ||
37 | + prefix: true | ||
38 | + | ||
39 | + attr_accessor :author_id_of_changes | ||
40 | + end | ||
41 | + | ||
42 | + module ClassMethods | ||
43 | + def search(query) | ||
44 | + where("title like :query", query: "%#{query}%") | ||
45 | + end | ||
46 | + end | ||
47 | + | ||
48 | + def today? | ||
49 | + Date.today == created_at.to_date | ||
50 | + end | ||
51 | + | ||
52 | + def new? | ||
53 | + today? && created_at == updated_at | ||
54 | + end | ||
55 | + | ||
56 | + def is_assigned? | ||
57 | + !!assignee_id | ||
58 | + end | ||
59 | + | ||
60 | + def is_being_reassigned? | ||
61 | + assignee_id_changed? | ||
62 | + end | ||
63 | + | ||
64 | + def is_being_closed? | ||
65 | + closed_changed? && closed | ||
66 | + end | ||
67 | + | ||
68 | + def is_being_reopened? | ||
69 | + closed_changed? && !closed | ||
70 | + end | ||
71 | + | ||
72 | + # Return the number of +1 comments (upvotes) | ||
73 | + def upvotes | ||
74 | + notes.select(&:upvote?).size | ||
75 | + end | ||
76 | + | ||
77 | + def upvotes_in_percent | ||
78 | + if votes_count.zero? | ||
79 | + 0 | ||
80 | + else | ||
81 | + 100.0 / votes_count * upvotes | ||
82 | + end | ||
83 | + end | ||
84 | + | ||
85 | + # Return the number of -1 comments (downvotes) | ||
86 | + def downvotes | ||
87 | + notes.select(&:downvote?).size | ||
88 | + end | ||
89 | + | ||
90 | + def downvotes_in_percent | ||
91 | + if votes_count.zero? | ||
92 | + 0 | ||
93 | + else | ||
94 | + 100.0 - upvotes_in_percent | ||
95 | + end | ||
96 | + end | ||
97 | + | ||
98 | + # Return the total number of votes | ||
99 | + def votes_count | ||
100 | + upvotes + downvotes | ||
101 | + end | ||
102 | +end |
app/models/event.rb
@@ -15,9 +15,6 @@ | @@ -15,9 +15,6 @@ | ||
15 | # | 15 | # |
16 | 16 | ||
17 | class Event < ActiveRecord::Base | 17 | class Event < ActiveRecord::Base |
18 | - include NoteEvent | ||
19 | - include PushEvent | ||
20 | - | ||
21 | attr_accessible :project, :action, :data, :author_id, :project_id, | 18 | attr_accessible :project, :action, :data, :author_id, :project_id, |
22 | :target_id, :target_type | 19 | :target_id, :target_type |
23 | 20 | ||
@@ -170,4 +167,139 @@ class Event < ActiveRecord::Base | @@ -170,4 +167,139 @@ class Event < ActiveRecord::Base | ||
170 | "opened" | 167 | "opened" |
171 | end | 168 | end |
172 | end | 169 | end |
170 | + | ||
171 | + def valid_push? | ||
172 | + data[:ref] | ||
173 | + rescue => ex | ||
174 | + false | ||
175 | + end | ||
176 | + | ||
177 | + def tag? | ||
178 | + data[:ref]["refs/tags"] | ||
179 | + end | ||
180 | + | ||
181 | + def branch? | ||
182 | + data[:ref]["refs/heads"] | ||
183 | + end | ||
184 | + | ||
185 | + def new_branch? | ||
186 | + commit_from =~ /^00000/ | ||
187 | + end | ||
188 | + | ||
189 | + def new_ref? | ||
190 | + commit_from =~ /^00000/ | ||
191 | + end | ||
192 | + | ||
193 | + def rm_ref? | ||
194 | + commit_to =~ /^00000/ | ||
195 | + end | ||
196 | + | ||
197 | + def md_ref? | ||
198 | + !(rm_ref? || new_ref?) | ||
199 | + end | ||
200 | + | ||
201 | + def commit_from | ||
202 | + data[:before] | ||
203 | + end | ||
204 | + | ||
205 | + def commit_to | ||
206 | + data[:after] | ||
207 | + end | ||
208 | + | ||
209 | + def ref_name | ||
210 | + if tag? | ||
211 | + tag_name | ||
212 | + else | ||
213 | + branch_name | ||
214 | + end | ||
215 | + end | ||
216 | + | ||
217 | + def branch_name | ||
218 | + @branch_name ||= data[:ref].gsub("refs/heads/", "") | ||
219 | + end | ||
220 | + | ||
221 | + def tag_name | ||
222 | + @tag_name ||= data[:ref].gsub("refs/tags/", "") | ||
223 | + end | ||
224 | + | ||
225 | + # Max 20 commits from push DESC | ||
226 | + def commits | ||
227 | + @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse | ||
228 | + end | ||
229 | + | ||
230 | + def commits_count | ||
231 | + data[:total_commits_count] || commits.count || 0 | ||
232 | + end | ||
233 | + | ||
234 | + def ref_type | ||
235 | + tag? ? "tag" : "branch" | ||
236 | + end | ||
237 | + | ||
238 | + def push_action_name | ||
239 | + if new_ref? | ||
240 | + "pushed new" | ||
241 | + elsif rm_ref? | ||
242 | + "deleted" | ||
243 | + else | ||
244 | + "pushed to" | ||
245 | + end | ||
246 | + end | ||
247 | + | ||
248 | + def parent_commit | ||
249 | + project.commit(commit_from) | ||
250 | + rescue => ex | ||
251 | + nil | ||
252 | + end | ||
253 | + | ||
254 | + def last_commit | ||
255 | + project.commit(commit_to) | ||
256 | + rescue => ex | ||
257 | + nil | ||
258 | + end | ||
259 | + | ||
260 | + def push_with_commits? | ||
261 | + md_ref? && commits.any? && parent_commit && last_commit | ||
262 | + rescue Grit::NoSuchPathError | ||
263 | + false | ||
264 | + end | ||
265 | + | ||
266 | + def last_push_to_non_root? | ||
267 | + branch? && project.default_branch != branch_name | ||
268 | + end | ||
269 | + | ||
270 | + def note_commit_id | ||
271 | + target.commit_id | ||
272 | + end | ||
273 | + | ||
274 | + def note_short_commit_id | ||
275 | + note_commit_id[0..8] | ||
276 | + end | ||
277 | + | ||
278 | + def note_commit? | ||
279 | + target.noteable_type == "Commit" | ||
280 | + end | ||
281 | + | ||
282 | + def note_target | ||
283 | + target.noteable | ||
284 | + end | ||
285 | + | ||
286 | + def note_target_id | ||
287 | + if note_commit? | ||
288 | + target.commit_id | ||
289 | + else | ||
290 | + target.noteable_id.to_s | ||
291 | + end | ||
292 | + end | ||
293 | + | ||
294 | + def wall_note? | ||
295 | + target.noteable_type.blank? | ||
296 | + end | ||
297 | + | ||
298 | + def note_target_type | ||
299 | + if target.noteable_type.present? | ||
300 | + target.noteable_type.titleize | ||
301 | + else | ||
302 | + "Wall" | ||
303 | + end.downcase | ||
304 | + end | ||
173 | end | 305 | end |
app/models/issue.rb
@@ -17,8 +17,7 @@ | @@ -17,8 +17,7 @@ | ||
17 | # | 17 | # |
18 | 18 | ||
19 | class Issue < ActiveRecord::Base | 19 | class Issue < ActiveRecord::Base |
20 | - include IssueCommonality | ||
21 | - include Votes | 20 | + include Issuable |
22 | 21 | ||
23 | attr_accessible :title, :assignee_id, :closed, :position, :description, | 22 | attr_accessible :title, :assignee_id, :closed, :position, :description, |
24 | :milestone_id, :label_list, :author_id_of_changes | 23 | :milestone_id, :label_list, :author_id_of_changes |
app/models/merge_request.rb
@@ -20,11 +20,10 @@ | @@ -20,11 +20,10 @@ | ||
20 | # | 20 | # |
21 | 21 | ||
22 | require Rails.root.join("app/models/commit") | 22 | require Rails.root.join("app/models/commit") |
23 | -require Rails.root.join("app/roles/static_model") | 23 | +require Rails.root.join("lib/static_model") |
24 | 24 | ||
25 | class MergeRequest < ActiveRecord::Base | 25 | class MergeRequest < ActiveRecord::Base |
26 | - include IssueCommonality | ||
27 | - include Votes | 26 | + include Issuable |
28 | 27 | ||
29 | attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id, | 28 | attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id, |
30 | :author_id_of_changes | 29 | :author_id_of_changes |
app/models/project.rb
@@ -21,11 +21,7 @@ | @@ -21,11 +21,7 @@ | ||
21 | require "grit" | 21 | require "grit" |
22 | 22 | ||
23 | class Project < ActiveRecord::Base | 23 | class Project < ActiveRecord::Base |
24 | - include Repository | ||
25 | - include PushObserver | ||
26 | - include Authority | ||
27 | - include Team | ||
28 | - include NamespacedProject | 24 | + include Gitolited |
29 | 25 | ||
30 | class TransferError < StandardError; end | 26 | class TransferError < StandardError; end |
31 | 27 | ||
@@ -277,4 +273,514 @@ class Project < ActiveRecord::Base | @@ -277,4 +273,514 @@ class Project < ActiveRecord::Base | ||
277 | creator | 273 | creator |
278 | end | 274 | end |
279 | end | 275 | end |
276 | + | ||
277 | + def team_member_by_name_or_email(name = nil, email = nil) | ||
278 | + user = users.where("name like ? or email like ?", name, email).first | ||
279 | + users_projects.where(user: user) if user | ||
280 | + end | ||
281 | + | ||
282 | + # Get Team Member record by user id | ||
283 | + def team_member_by_id(user_id) | ||
284 | + users_projects.find_by_user_id(user_id) | ||
285 | + end | ||
286 | + | ||
287 | + # Add user to project | ||
288 | + # with passed access role | ||
289 | + def add_user_to_team(user, access_role) | ||
290 | + add_user_id_to_team(user.id, access_role) | ||
291 | + end | ||
292 | + | ||
293 | + # Add multiple users to project | ||
294 | + # with same access role | ||
295 | + def add_users_to_team(users, access_role) | ||
296 | + add_users_ids_to_team(users.map(&:id), access_role) | ||
297 | + end | ||
298 | + | ||
299 | + # Add user to project | ||
300 | + # with passed access role by user id | ||
301 | + def add_user_id_to_team(user_id, access_role) | ||
302 | + users_projects.create( | ||
303 | + user_id: user_id, | ||
304 | + project_access: access_role | ||
305 | + ) | ||
306 | + end | ||
307 | + | ||
308 | + # Add multiple users to project | ||
309 | + # with same access role by user ids | ||
310 | + def add_users_ids_to_team(users_ids, access_role) | ||
311 | + UsersProject.bulk_import(self, users_ids, access_role) | ||
312 | + end | ||
313 | + | ||
314 | + # Update multiple project users | ||
315 | + # to same access role by user ids | ||
316 | + def update_users_ids_to_role(users_ids, access_role) | ||
317 | + UsersProject.bulk_update(self, users_ids, access_role) | ||
318 | + end | ||
319 | + | ||
320 | + # Delete multiple users from project by user ids | ||
321 | + def delete_users_ids_from_team(users_ids) | ||
322 | + UsersProject.bulk_delete(self, users_ids) | ||
323 | + end | ||
324 | + | ||
325 | + # Remove all users from project team | ||
326 | + def truncate_team | ||
327 | + UsersProject.truncate_team(self) | ||
328 | + end | ||
329 | + | ||
330 | + # Compatible with all access rights | ||
331 | + # Should be rewrited for new access rights | ||
332 | + def add_access(user, *access) | ||
333 | + access = if access.include?(:admin) | ||
334 | + { project_access: UsersProject::MASTER } | ||
335 | + elsif access.include?(:write) | ||
336 | + { project_access: UsersProject::DEVELOPER } | ||
337 | + else | ||
338 | + { project_access: UsersProject::REPORTER } | ||
339 | + end | ||
340 | + opts = { user: user } | ||
341 | + opts.merge!(access) | ||
342 | + users_projects.create(opts) | ||
343 | + end | ||
344 | + | ||
345 | + def reset_access(user) | ||
346 | + users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id | ||
347 | + end | ||
348 | + | ||
349 | + def repository_readers | ||
350 | + repository_members[UsersProject::REPORTER] | ||
351 | + end | ||
352 | + | ||
353 | + def repository_writers | ||
354 | + repository_members[UsersProject::DEVELOPER] | ||
355 | + end | ||
356 | + | ||
357 | + def repository_masters | ||
358 | + repository_members[UsersProject::MASTER] | ||
359 | + end | ||
360 | + | ||
361 | + def repository_members | ||
362 | + keys = Hash.new {|h,k| h[k] = [] } | ||
363 | + UsersProject.select("keys.identifier, project_access"). | ||
364 | + joins(user: :keys).where(project_id: id). | ||
365 | + each {|row| keys[row.project_access] << [row.identifier] } | ||
366 | + | ||
367 | + keys[UsersProject::REPORTER] += deploy_keys.pluck(:identifier) | ||
368 | + keys | ||
369 | + end | ||
370 | + | ||
371 | + def allow_read_for?(user) | ||
372 | + !users_projects.where(user_id: user.id).empty? | ||
373 | + end | ||
374 | + | ||
375 | + def guest_access_for?(user) | ||
376 | + !users_projects.where(user_id: user.id).empty? | ||
377 | + end | ||
378 | + | ||
379 | + def report_access_for?(user) | ||
380 | + !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? | ||
381 | + end | ||
382 | + | ||
383 | + def dev_access_for?(user) | ||
384 | + !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty? | ||
385 | + end | ||
386 | + | ||
387 | + def master_access_for?(user) | ||
388 | + !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty? | ||
389 | + end | ||
390 | + | ||
391 | + def transfer(new_namespace) | ||
392 | + Project.transaction do | ||
393 | + old_namespace = namespace | ||
394 | + self.namespace = new_namespace | ||
395 | + | ||
396 | + old_dir = old_namespace.try(:path) || '' | ||
397 | + new_dir = new_namespace.try(:path) || '' | ||
398 | + | ||
399 | + old_repo = if old_dir.present? | ||
400 | + File.join(old_dir, self.path) | ||
401 | + else | ||
402 | + self.path | ||
403 | + end | ||
404 | + | ||
405 | + if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present? | ||
406 | + raise TransferError.new("Project with same path in target namespace already exists") | ||
407 | + end | ||
408 | + | ||
409 | + Gitlab::ProjectMover.new(self, old_dir, new_dir).execute | ||
410 | + | ||
411 | + gitolite.move_repository(old_repo, self) | ||
412 | + | ||
413 | + save! | ||
414 | + end | ||
415 | + rescue Gitlab::ProjectMover::ProjectMoveError => ex | ||
416 | + raise Project::TransferError.new(ex.message) | ||
417 | + end | ||
418 | + | ||
419 | + def name_with_namespace | ||
420 | + @name_with_namespace ||= begin | ||
421 | + if namespace | ||
422 | + namespace.human_name + " / " + name | ||
423 | + else | ||
424 | + name | ||
425 | + end | ||
426 | + end | ||
427 | + end | ||
428 | + | ||
429 | + def namespace_owner | ||
430 | + namespace.try(:owner) | ||
431 | + end | ||
432 | + | ||
433 | + def path_with_namespace | ||
434 | + if namespace | ||
435 | + namespace.path + '/' + path | ||
436 | + else | ||
437 | + path | ||
438 | + end | ||
439 | + end | ||
440 | + | ||
441 | + # This method will be called after each post receive and only if the provided | ||
442 | + # user is present in GitLab. | ||
443 | + # | ||
444 | + # All callbacks for post receive should be placed here. | ||
445 | + def trigger_post_receive(oldrev, newrev, ref, user) | ||
446 | + data = post_receive_data(oldrev, newrev, ref, user) | ||
447 | + | ||
448 | + # Create push event | ||
449 | + self.observe_push(data) | ||
450 | + | ||
451 | + if push_to_branch? ref, oldrev | ||
452 | + # Close merged MR | ||
453 | + self.update_merge_requests(oldrev, newrev, ref, user) | ||
454 | + | ||
455 | + # Execute web hooks | ||
456 | + self.execute_hooks(data.dup) | ||
457 | + | ||
458 | + # Execute project services | ||
459 | + self.execute_services(data.dup) | ||
460 | + end | ||
461 | + | ||
462 | + # Create satellite | ||
463 | + self.satellite.create unless self.satellite.exists? | ||
464 | + | ||
465 | + # Discover the default branch, but only if it hasn't already been set to | ||
466 | + # something else | ||
467 | + if default_branch.nil? | ||
468 | + update_attributes(default_branch: discover_default_branch) | ||
469 | + end | ||
470 | + end | ||
471 | + | ||
472 | + def push_to_branch? ref, oldrev | ||
473 | + ref_parts = ref.split('/') | ||
474 | + | ||
475 | + # Return if this is not a push to a branch (e.g. new commits) | ||
476 | + !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000") | ||
477 | + end | ||
478 | + | ||
479 | + def observe_push(data) | ||
480 | + Event.create( | ||
481 | + project: self, | ||
482 | + action: Event::Pushed, | ||
483 | + data: data, | ||
484 | + author_id: data[:user_id] | ||
485 | + ) | ||
486 | + end | ||
487 | + | ||
488 | + def execute_hooks(data) | ||
489 | + hooks.each { |hook| hook.execute(data) } | ||
490 | + end | ||
491 | + | ||
492 | + def execute_services(data) | ||
493 | + services.each do |service| | ||
494 | + | ||
495 | + # Call service hook only if it is active | ||
496 | + service.execute(data) if service.active | ||
497 | + end | ||
498 | + end | ||
499 | + | ||
500 | + # Produce a hash of post-receive data | ||
501 | + # | ||
502 | + # data = { | ||
503 | + # before: String, | ||
504 | + # after: String, | ||
505 | + # ref: String, | ||
506 | + # user_id: String, | ||
507 | + # user_name: String, | ||
508 | + # repository: { | ||
509 | + # name: String, | ||
510 | + # url: String, | ||
511 | + # description: String, | ||
512 | + # homepage: String, | ||
513 | + # }, | ||
514 | + # commits: Array, | ||
515 | + # total_commits_count: Fixnum | ||
516 | + # } | ||
517 | + # | ||
518 | + def post_receive_data(oldrev, newrev, ref, user) | ||
519 | + | ||
520 | + push_commits = commits_between(oldrev, newrev) | ||
521 | + | ||
522 | + # Total commits count | ||
523 | + push_commits_count = push_commits.size | ||
524 | + | ||
525 | + # Get latest 20 commits ASC | ||
526 | + push_commits_limited = push_commits.last(20) | ||
527 | + | ||
528 | + # Hash to be passed as post_receive_data | ||
529 | + data = { | ||
530 | + before: oldrev, | ||
531 | + after: newrev, | ||
532 | + ref: ref, | ||
533 | + user_id: user.id, | ||
534 | + user_name: user.name, | ||
535 | + repository: { | ||
536 | + name: name, | ||
537 | + url: url_to_repo, | ||
538 | + description: description, | ||
539 | + homepage: web_url, | ||
540 | + }, | ||
541 | + commits: [], | ||
542 | + total_commits_count: push_commits_count | ||
543 | + } | ||
544 | + | ||
545 | + # For perfomance purposes maximum 20 latest commits | ||
546 | + # will be passed as post receive hook data. | ||
547 | + # | ||
548 | + push_commits_limited.each do |commit| | ||
549 | + data[:commits] << { | ||
550 | + id: commit.id, | ||
551 | + message: commit.safe_message, | ||
552 | + timestamp: commit.date.xmlschema, | ||
553 | + url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}", | ||
554 | + author: { | ||
555 | + name: commit.author_name, | ||
556 | + email: commit.author_email | ||
557 | + } | ||
558 | + } | ||
559 | + end | ||
560 | + | ||
561 | + data | ||
562 | + end | ||
563 | + | ||
564 | + def update_merge_requests(oldrev, newrev, ref, user) | ||
565 | + return true unless ref =~ /heads/ | ||
566 | + branch_name = ref.gsub("refs/heads/", "") | ||
567 | + c_ids = self.commits_between(oldrev, newrev).map(&:id) | ||
568 | + | ||
569 | + # Update code for merge requests | ||
570 | + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all | ||
571 | + mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } | ||
572 | + | ||
573 | + # Close merge requests | ||
574 | + mrs = self.merge_requests.opened.where(target_branch: branch_name).all | ||
575 | + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | ||
576 | + mrs.each { |merge_request| merge_request.merge!(user.id) } | ||
577 | + | ||
578 | + true | ||
579 | + end | ||
580 | + | ||
581 | + def valid_repo? | ||
582 | + repo | ||
583 | + rescue | ||
584 | + errors.add(:path, "Invalid repository path") | ||
585 | + false | ||
586 | + end | ||
587 | + | ||
588 | + def empty_repo? | ||
589 | + !repo_exists? || !has_commits? | ||
590 | + end | ||
591 | + | ||
592 | + def commit(commit_id = nil) | ||
593 | + Commit.find_or_first(repo, commit_id, root_ref) | ||
594 | + end | ||
595 | + | ||
596 | + def fresh_commits(n = 10) | ||
597 | + Commit.fresh_commits(repo, n) | ||
598 | + end | ||
599 | + | ||
600 | + def commits_with_refs(n = 20) | ||
601 | + Commit.commits_with_refs(repo, n) | ||
602 | + end | ||
603 | + | ||
604 | + def commits_since(date) | ||
605 | + Commit.commits_since(repo, date) | ||
606 | + end | ||
607 | + | ||
608 | + def commits(ref, path = nil, limit = nil, offset = nil) | ||
609 | + Commit.commits(repo, ref, path, limit, offset) | ||
610 | + end | ||
611 | + | ||
612 | + def last_commit_for(ref, path = nil) | ||
613 | + commits(ref, path, 1).first | ||
614 | + end | ||
615 | + | ||
616 | + def commits_between(from, to) | ||
617 | + Commit.commits_between(repo, from, to) | ||
618 | + end | ||
619 | + | ||
620 | + def satellite | ||
621 | + @satellite ||= Gitlab::Satellite::Satellite.new(self) | ||
622 | + end | ||
623 | + | ||
624 | + def has_post_receive_file? | ||
625 | + !!hook_file | ||
626 | + end | ||
627 | + | ||
628 | + def valid_post_receive_file? | ||
629 | + valid_hook_file == hook_file | ||
630 | + end | ||
631 | + | ||
632 | + def valid_hook_file | ||
633 | + @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive')) | ||
634 | + end | ||
635 | + | ||
636 | + def hook_file | ||
637 | + @hook_file ||= begin | ||
638 | + hook_path = File.join(path_to_repo, 'hooks', 'post-receive') | ||
639 | + File.read(hook_path) if File.exists?(hook_path) | ||
640 | + end | ||
641 | + end | ||
642 | + | ||
643 | + # Returns an Array of branch names | ||
644 | + def branch_names | ||
645 | + repo.branches.collect(&:name).sort | ||
646 | + end | ||
647 | + | ||
648 | + # Returns an Array of Branches | ||
649 | + def branches | ||
650 | + repo.branches.sort_by(&:name) | ||
651 | + end | ||
652 | + | ||
653 | + # Returns an Array of tag names | ||
654 | + def tag_names | ||
655 | + repo.tags.collect(&:name).sort.reverse | ||
656 | + end | ||
657 | + | ||
658 | + # Returns an Array of Tags | ||
659 | + def tags | ||
660 | + repo.tags.sort_by(&:name).reverse | ||
661 | + end | ||
662 | + | ||
663 | + # Returns an Array of branch and tag names | ||
664 | + def ref_names | ||
665 | + [branch_names + tag_names].flatten | ||
666 | + end | ||
667 | + | ||
668 | + def repo | ||
669 | + @repo ||= Grit::Repo.new(path_to_repo) | ||
670 | + end | ||
671 | + | ||
672 | + def url_to_repo | ||
673 | + gitolite.url_to_repo(path_with_namespace) | ||
674 | + end | ||
675 | + | ||
676 | + def path_to_repo | ||
677 | + File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git") | ||
678 | + end | ||
679 | + | ||
680 | + def namespace_dir | ||
681 | + namespace.try(:path) || '' | ||
682 | + end | ||
683 | + | ||
684 | + def update_repository | ||
685 | + gitolite.update_repository(self) | ||
686 | + end | ||
687 | + | ||
688 | + def destroy_repository | ||
689 | + gitolite.remove_repository(self) | ||
690 | + end | ||
691 | + | ||
692 | + def repo_exists? | ||
693 | + @repo_exists ||= (repo && !repo.branches.empty?) | ||
694 | + rescue | ||
695 | + @repo_exists = false | ||
696 | + end | ||
697 | + | ||
698 | + def heads | ||
699 | + @heads ||= repo.heads | ||
700 | + end | ||
701 | + | ||
702 | + def tree(fcommit, path = nil) | ||
703 | + fcommit = commit if fcommit == :head | ||
704 | + tree = fcommit.tree | ||
705 | + path ? (tree / path) : tree | ||
706 | + end | ||
707 | + | ||
708 | + def open_branches | ||
709 | + if protected_branches.empty? | ||
710 | + self.repo.heads | ||
711 | + else | ||
712 | + pnames = protected_branches.map(&:name) | ||
713 | + self.repo.heads.reject { |h| pnames.include?(h.name) } | ||
714 | + end.sort_by(&:name) | ||
715 | + end | ||
716 | + | ||
717 | + # Discovers the default branch based on the repository's available branches | ||
718 | + # | ||
719 | + # - If no branches are present, returns nil | ||
720 | + # - If one branch is present, returns its name | ||
721 | + # - If two or more branches are present, returns the one that has a name | ||
722 | + # matching root_ref (default_branch or 'master' if default_branch is nil) | ||
723 | + def discover_default_branch | ||
724 | + if branch_names.length == 0 | ||
725 | + nil | ||
726 | + elsif branch_names.length == 1 | ||
727 | + branch_names.first | ||
728 | + else | ||
729 | + branch_names.select { |v| v == root_ref }.first | ||
730 | + end | ||
731 | + end | ||
732 | + | ||
733 | + def has_commits? | ||
734 | + !!commit | ||
735 | + rescue Grit::NoSuchPathError | ||
736 | + false | ||
737 | + end | ||
738 | + | ||
739 | + def root_ref | ||
740 | + default_branch || "master" | ||
741 | + end | ||
742 | + | ||
743 | + def root_ref?(branch) | ||
744 | + root_ref == branch | ||
745 | + end | ||
746 | + | ||
747 | + # Archive Project to .tar.gz | ||
748 | + # | ||
749 | + # Already packed repo archives stored at | ||
750 | + # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz | ||
751 | + # | ||
752 | + def archive_repo(ref) | ||
753 | + ref = ref || self.root_ref | ||
754 | + commit = self.commit(ref) | ||
755 | + return nil unless commit | ||
756 | + | ||
757 | + # Build file path | ||
758 | + file_name = self.path + "-" + commit.id.to_s + ".tar.gz" | ||
759 | + storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace) | ||
760 | + file_path = File.join(storage_path, file_name) | ||
761 | + | ||
762 | + # Put files into a directory before archiving | ||
763 | + prefix = self.path + "/" | ||
764 | + | ||
765 | + # Create file if not exists | ||
766 | + unless File.exists?(file_path) | ||
767 | + FileUtils.mkdir_p storage_path | ||
768 | + file = self.repo.archive_to_file(ref, prefix, file_path) | ||
769 | + end | ||
770 | + | ||
771 | + file_path | ||
772 | + end | ||
773 | + | ||
774 | + def ssh_url_to_repo | ||
775 | + url_to_repo | ||
776 | + end | ||
777 | + | ||
778 | + def http_url_to_repo | ||
779 | + http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') | ||
780 | + end | ||
781 | + | ||
782 | + # Check if current branch name is marked as protected in the system | ||
783 | + def protected_branch? branch_name | ||
784 | + protected_branches.map(&:name).include?(branch_name) | ||
785 | + end | ||
280 | end | 786 | end |
app/models/protected_branch.rb
@@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
10 | # | 10 | # |
11 | 11 | ||
12 | class ProtectedBranch < ActiveRecord::Base | 12 | class ProtectedBranch < ActiveRecord::Base |
13 | - include GitHost | 13 | + include Gitolited |
14 | 14 | ||
15 | attr_accessible :name | 15 | attr_accessible :name |
16 | 16 | ||
@@ -22,7 +22,7 @@ class ProtectedBranch < ActiveRecord::Base | @@ -22,7 +22,7 @@ class ProtectedBranch < ActiveRecord::Base | ||
22 | after_destroy :update_repository | 22 | after_destroy :update_repository |
23 | 23 | ||
24 | def update_repository | 24 | def update_repository |
25 | - git_host.update_repository(project) | 25 | + gitolite.update_repository(project) |
26 | end | 26 | end |
27 | 27 | ||
28 | def commit | 28 | def commit |
app/models/user.rb
@@ -34,8 +34,6 @@ | @@ -34,8 +34,6 @@ | ||
34 | # | 34 | # |
35 | 35 | ||
36 | class User < ActiveRecord::Base | 36 | class User < ActiveRecord::Base |
37 | - include Account | ||
38 | - | ||
39 | devise :database_authenticatable, :token_authenticatable, :lockable, | 37 | devise :database_authenticatable, :token_authenticatable, :lockable, |
40 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable | 38 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable |
41 | 39 | ||
@@ -192,4 +190,92 @@ class User < ActiveRecord::Base | @@ -192,4 +190,92 @@ class User < ActiveRecord::Base | ||
192 | def tm_in_personal_projects | 190 | def tm_in_personal_projects |
193 | personal_projects.users_projects.where(user_id: self.id) | 191 | personal_projects.users_projects.where(user_id: self.id) |
194 | end | 192 | end |
193 | + | ||
194 | + # Returns a string for use as a Gitolite user identifier | ||
195 | + # | ||
196 | + # Note that Gitolite 2.x requires the following pattern for users: | ||
197 | + # | ||
198 | + # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$ | ||
199 | + def identifier | ||
200 | + # Replace non-word chars with underscores, then make sure it starts with | ||
201 | + # valid chars | ||
202 | + email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '') | ||
203 | + end | ||
204 | + | ||
205 | + def is_admin? | ||
206 | + admin | ||
207 | + end | ||
208 | + | ||
209 | + def require_ssh_key? | ||
210 | + keys.count == 0 | ||
211 | + end | ||
212 | + | ||
213 | + def can_create_project? | ||
214 | + projects_limit > personal_projects.count | ||
215 | + end | ||
216 | + | ||
217 | + def can_create_group? | ||
218 | + is_admin? | ||
219 | + end | ||
220 | + | ||
221 | + def abilities | ||
222 | + @abilities ||= begin | ||
223 | + abilities = Six.new | ||
224 | + abilities << Ability | ||
225 | + abilities | ||
226 | + end | ||
227 | + end | ||
228 | + | ||
229 | + def can? action, subject | ||
230 | + abilities.allowed?(self, action, subject) | ||
231 | + end | ||
232 | + | ||
233 | + def last_activity_project | ||
234 | + projects.first | ||
235 | + end | ||
236 | + | ||
237 | + def first_name | ||
238 | + name.split.first unless name.blank? | ||
239 | + end | ||
240 | + | ||
241 | + def cared_merge_requests | ||
242 | + MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id) | ||
243 | + end | ||
244 | + | ||
245 | + # Remove user from all projects and | ||
246 | + # set blocked attribute to true | ||
247 | + def block | ||
248 | + users_projects.find_each do |membership| | ||
249 | + return false unless membership.destroy | ||
250 | + end | ||
251 | + | ||
252 | + self.blocked = true | ||
253 | + save | ||
254 | + end | ||
255 | + | ||
256 | + def projects_limit_percent | ||
257 | + return 100 if projects_limit.zero? | ||
258 | + (personal_projects.count.to_f / projects_limit) * 100 | ||
259 | + end | ||
260 | + | ||
261 | + def recent_push project_id = nil | ||
262 | + # Get push events not earlier than 2 hours ago | ||
263 | + events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours) | ||
264 | + events = events.where(project_id: project_id) if project_id | ||
265 | + | ||
266 | + # Take only latest one | ||
267 | + events = events.recent.limit(1).first | ||
268 | + end | ||
269 | + | ||
270 | + def projects_sorted_by_activity | ||
271 | + authorized_projects.sorted_by_activity | ||
272 | + end | ||
273 | + | ||
274 | + def several_namespaces? | ||
275 | + namespaces.size > 1 | ||
276 | + end | ||
277 | + | ||
278 | + def namespace_id | ||
279 | + namespace.try :id | ||
280 | + end | ||
195 | end | 281 | end |
app/models/users_project.rb
@@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
11 | # | 11 | # |
12 | 12 | ||
13 | class UsersProject < ActiveRecord::Base | 13 | class UsersProject < ActiveRecord::Base |
14 | - include GitHost | 14 | + include Gitolited |
15 | 15 | ||
16 | GUEST = 10 | 16 | GUEST = 10 |
17 | REPORTER = 20 | 17 | REPORTER = 20 |
@@ -152,7 +152,7 @@ class UsersProject < ActiveRecord::Base | @@ -152,7 +152,7 @@ class UsersProject < ActiveRecord::Base | ||
152 | end | 152 | end |
153 | 153 | ||
154 | def update_repository | 154 | def update_repository |
155 | - git_host.update_repository(project) | 155 | + gitolite.update_repository(project) |
156 | end | 156 | end |
157 | 157 | ||
158 | def project_access_human | 158 | def project_access_human |
app/observers/key_observer.rb
1 | class KeyObserver < ActiveRecord::Observer | 1 | class KeyObserver < ActiveRecord::Observer |
2 | - include GitHost | 2 | + include Gitolited |
3 | 3 | ||
4 | def after_save(key) | 4 | def after_save(key) |
5 | - git_host.set_key(key.identifier, key.key, key.projects) | 5 | + gitolite.set_key(key.identifier, key.key, key.projects) |
6 | end | 6 | end |
7 | 7 | ||
8 | def after_destroy(key) | 8 | def after_destroy(key) |
9 | return if key.is_deploy_key && !key.last_deploy? | 9 | return if key.is_deploy_key && !key.last_deploy? |
10 | - git_host.remove_key(key.identifier, key.projects) | 10 | + gitolite.remove_key(key.identifier, key.projects) |
11 | end | 11 | end |
12 | end | 12 | end |
app/roles/account.rb
@@ -1,95 +0,0 @@ | @@ -1,95 +0,0 @@ | ||
1 | -# == Account role | ||
2 | -# | ||
3 | -# Describe behaviour of User in application | ||
4 | -# | ||
5 | -# Used by User | ||
6 | -# | ||
7 | -module Account | ||
8 | - # Returns a string for use as a Gitolite user identifier | ||
9 | - # | ||
10 | - # Note that Gitolite 2.x requires the following pattern for users: | ||
11 | - # | ||
12 | - # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$ | ||
13 | - def identifier | ||
14 | - # Replace non-word chars with underscores, then make sure it starts with | ||
15 | - # valid chars | ||
16 | - email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '') | ||
17 | - end | ||
18 | - | ||
19 | - def is_admin? | ||
20 | - admin | ||
21 | - end | ||
22 | - | ||
23 | - def require_ssh_key? | ||
24 | - keys.count == 0 | ||
25 | - end | ||
26 | - | ||
27 | - def can_create_project? | ||
28 | - projects_limit > personal_projects.count | ||
29 | - end | ||
30 | - | ||
31 | - def can_create_group? | ||
32 | - is_admin? | ||
33 | - end | ||
34 | - | ||
35 | - def abilities | ||
36 | - @abilities ||= begin | ||
37 | - abilities = Six.new | ||
38 | - abilities << Ability | ||
39 | - abilities | ||
40 | - end | ||
41 | - end | ||
42 | - | ||
43 | - def can? action, subject | ||
44 | - abilities.allowed?(self, action, subject) | ||
45 | - end | ||
46 | - | ||
47 | - def last_activity_project | ||
48 | - projects.first | ||
49 | - end | ||
50 | - | ||
51 | - def first_name | ||
52 | - name.split.first unless name.blank? | ||
53 | - end | ||
54 | - | ||
55 | - def cared_merge_requests | ||
56 | - MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id) | ||
57 | - end | ||
58 | - | ||
59 | - # Remove user from all projects and | ||
60 | - # set blocked attribute to true | ||
61 | - def block | ||
62 | - users_projects.find_each do |membership| | ||
63 | - return false unless membership.destroy | ||
64 | - end | ||
65 | - | ||
66 | - self.blocked = true | ||
67 | - save | ||
68 | - end | ||
69 | - | ||
70 | - def projects_limit_percent | ||
71 | - return 100 if projects_limit.zero? | ||
72 | - (personal_projects.count.to_f / projects_limit) * 100 | ||
73 | - end | ||
74 | - | ||
75 | - def recent_push project_id = nil | ||
76 | - # Get push events not earlier than 2 hours ago | ||
77 | - events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours) | ||
78 | - events = events.where(project_id: project_id) if project_id | ||
79 | - | ||
80 | - # Take only latest one | ||
81 | - events = events.recent.limit(1).first | ||
82 | - end | ||
83 | - | ||
84 | - def projects_sorted_by_activity | ||
85 | - authorized_projects.sorted_by_activity | ||
86 | - end | ||
87 | - | ||
88 | - def several_namespaces? | ||
89 | - namespaces.size > 1 | ||
90 | - end | ||
91 | - | ||
92 | - def namespace_id | ||
93 | - namespace.try :id | ||
94 | - end | ||
95 | -end |
app/roles/authority.rb
@@ -1,68 +0,0 @@ | @@ -1,68 +0,0 @@ | ||
1 | -# == Authority role | ||
2 | -# | ||
3 | -# Control access to project repository based on users role in team | ||
4 | -# | ||
5 | -# Used by Project | ||
6 | -# | ||
7 | -module Authority | ||
8 | - # Compatible with all access rights | ||
9 | - # Should be rewrited for new access rights | ||
10 | - def add_access(user, *access) | ||
11 | - access = if access.include?(:admin) | ||
12 | - { project_access: UsersProject::MASTER } | ||
13 | - elsif access.include?(:write) | ||
14 | - { project_access: UsersProject::DEVELOPER } | ||
15 | - else | ||
16 | - { project_access: UsersProject::REPORTER } | ||
17 | - end | ||
18 | - opts = { user: user } | ||
19 | - opts.merge!(access) | ||
20 | - users_projects.create(opts) | ||
21 | - end | ||
22 | - | ||
23 | - def reset_access(user) | ||
24 | - users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id | ||
25 | - end | ||
26 | - | ||
27 | - def repository_readers | ||
28 | - repository_members[UsersProject::REPORTER] | ||
29 | - end | ||
30 | - | ||
31 | - def repository_writers | ||
32 | - repository_members[UsersProject::DEVELOPER] | ||
33 | - end | ||
34 | - | ||
35 | - def repository_masters | ||
36 | - repository_members[UsersProject::MASTER] | ||
37 | - end | ||
38 | - | ||
39 | - def repository_members | ||
40 | - keys = Hash.new {|h,k| h[k] = [] } | ||
41 | - UsersProject.select("keys.identifier, project_access"). | ||
42 | - joins(user: :keys).where(project_id: id). | ||
43 | - each {|row| keys[row.project_access] << [row.identifier] } | ||
44 | - | ||
45 | - keys[UsersProject::REPORTER] += deploy_keys.pluck(:identifier) | ||
46 | - keys | ||
47 | - end | ||
48 | - | ||
49 | - def allow_read_for?(user) | ||
50 | - !users_projects.where(user_id: user.id).empty? | ||
51 | - end | ||
52 | - | ||
53 | - def guest_access_for?(user) | ||
54 | - !users_projects.where(user_id: user.id).empty? | ||
55 | - end | ||
56 | - | ||
57 | - def report_access_for?(user) | ||
58 | - !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? | ||
59 | - end | ||
60 | - | ||
61 | - def dev_access_for?(user) | ||
62 | - !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty? | ||
63 | - end | ||
64 | - | ||
65 | - def master_access_for?(user) | ||
66 | - !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty? | ||
67 | - end | ||
68 | -end |
app/roles/git_host.rb
app/roles/issue_commonality.rb
@@ -1,72 +0,0 @@ | @@ -1,72 +0,0 @@ | ||
1 | -# == IssueCommonality role | ||
2 | -# | ||
3 | -# Contains common functionality shared between Issues and MergeRequests | ||
4 | -# | ||
5 | -# Used by Issue, MergeRequest | ||
6 | -# | ||
7 | -module IssueCommonality | ||
8 | - extend ActiveSupport::Concern | ||
9 | - | ||
10 | - included do | ||
11 | - belongs_to :project | ||
12 | - belongs_to :author, class_name: "User" | ||
13 | - belongs_to :assignee, class_name: "User" | ||
14 | - belongs_to :milestone | ||
15 | - has_many :notes, as: :noteable, dependent: :destroy | ||
16 | - | ||
17 | - validates :project, presence: true | ||
18 | - validates :author, presence: true | ||
19 | - validates :title, presence: true, length: { within: 0..255 } | ||
20 | - validates :closed, inclusion: { in: [true, false] } | ||
21 | - | ||
22 | - scope :opened, where(closed: false) | ||
23 | - scope :closed, where(closed: true) | ||
24 | - scope :of_group, ->(group) { where(project_id: group.project_ids) } | ||
25 | - scope :assigned, ->(u) { where(assignee_id: u.id)} | ||
26 | - scope :recent, order("created_at DESC") | ||
27 | - | ||
28 | - delegate :name, | ||
29 | - :email, | ||
30 | - to: :author, | ||
31 | - prefix: true | ||
32 | - | ||
33 | - delegate :name, | ||
34 | - :email, | ||
35 | - to: :assignee, | ||
36 | - allow_nil: true, | ||
37 | - prefix: true | ||
38 | - | ||
39 | - attr_accessor :author_id_of_changes | ||
40 | - end | ||
41 | - | ||
42 | - module ClassMethods | ||
43 | - def search(query) | ||
44 | - where("title like :query", query: "%#{query}%") | ||
45 | - end | ||
46 | - end | ||
47 | - | ||
48 | - def today? | ||
49 | - Date.today == created_at.to_date | ||
50 | - end | ||
51 | - | ||
52 | - def new? | ||
53 | - today? && created_at == updated_at | ||
54 | - end | ||
55 | - | ||
56 | - def is_assigned? | ||
57 | - !!assignee_id | ||
58 | - end | ||
59 | - | ||
60 | - def is_being_reassigned? | ||
61 | - assignee_id_changed? | ||
62 | - end | ||
63 | - | ||
64 | - def is_being_closed? | ||
65 | - closed_changed? && closed | ||
66 | - end | ||
67 | - | ||
68 | - def is_being_reopened? | ||
69 | - closed_changed? && !closed | ||
70 | - end | ||
71 | - | ||
72 | -end |
app/roles/namespaced_project.rb
@@ -1,60 +0,0 @@ | @@ -1,60 +0,0 @@ | ||
1 | -# == NamespacedProject role | ||
2 | -# | ||
3 | -# Provides extra functionality for Project related to namespaces like: | ||
4 | -# - transfer project between namespaces | ||
5 | -# - name, path including namespece | ||
6 | -# - project owner based on namespace | ||
7 | -# | ||
8 | -# Used by Project | ||
9 | -# | ||
10 | -module NamespacedProject | ||
11 | - def transfer(new_namespace) | ||
12 | - Project.transaction do | ||
13 | - old_namespace = namespace | ||
14 | - self.namespace = new_namespace | ||
15 | - | ||
16 | - old_dir = old_namespace.try(:path) || '' | ||
17 | - new_dir = new_namespace.try(:path) || '' | ||
18 | - | ||
19 | - old_repo = if old_dir.present? | ||
20 | - File.join(old_dir, self.path) | ||
21 | - else | ||
22 | - self.path | ||
23 | - end | ||
24 | - | ||
25 | - if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present? | ||
26 | - raise TransferError.new("Project with same path in target namespace already exists") | ||
27 | - end | ||
28 | - | ||
29 | - Gitlab::ProjectMover.new(self, old_dir, new_dir).execute | ||
30 | - | ||
31 | - git_host.move_repository(old_repo, self) | ||
32 | - | ||
33 | - save! | ||
34 | - end | ||
35 | - rescue Gitlab::ProjectMover::ProjectMoveError => ex | ||
36 | - raise Project::TransferError.new(ex.message) | ||
37 | - end | ||
38 | - | ||
39 | - def name_with_namespace | ||
40 | - @name_with_namespace ||= begin | ||
41 | - if namespace | ||
42 | - namespace.human_name + " / " + name | ||
43 | - else | ||
44 | - name | ||
45 | - end | ||
46 | - end | ||
47 | - end | ||
48 | - | ||
49 | - def namespace_owner | ||
50 | - namespace.try(:owner) | ||
51 | - end | ||
52 | - | ||
53 | - def path_with_namespace | ||
54 | - if namespace | ||
55 | - namespace.path + '/' + path | ||
56 | - else | ||
57 | - path | ||
58 | - end | ||
59 | - end | ||
60 | -end |
app/roles/note_event.rb
@@ -1,43 +0,0 @@ | @@ -1,43 +0,0 @@ | ||
1 | -# == NoteEvent role | ||
2 | -# | ||
3 | -# Extends Event model functionality by providing extra methods related to comment events | ||
4 | -# | ||
5 | -# Used by Event | ||
6 | -# | ||
7 | -module NoteEvent | ||
8 | - def note_commit_id | ||
9 | - target.commit_id | ||
10 | - end | ||
11 | - | ||
12 | - def note_short_commit_id | ||
13 | - note_commit_id[0..8] | ||
14 | - end | ||
15 | - | ||
16 | - def note_commit? | ||
17 | - target.noteable_type == "Commit" | ||
18 | - end | ||
19 | - | ||
20 | - def note_target | ||
21 | - target.noteable | ||
22 | - end | ||
23 | - | ||
24 | - def note_target_id | ||
25 | - if note_commit? | ||
26 | - target.commit_id | ||
27 | - else | ||
28 | - target.noteable_id.to_s | ||
29 | - end | ||
30 | - end | ||
31 | - | ||
32 | - def wall_note? | ||
33 | - target.noteable_type.blank? | ||
34 | - end | ||
35 | - | ||
36 | - def note_target_type | ||
37 | - if target.noteable_type.present? | ||
38 | - target.noteable_type.titleize | ||
39 | - else | ||
40 | - "Wall" | ||
41 | - end.downcase | ||
42 | - end | ||
43 | -end |
app/roles/push_event.rb
@@ -1,106 +0,0 @@ | @@ -1,106 +0,0 @@ | ||
1 | -# == PushEvent role | ||
2 | -# | ||
3 | -# Extends Event model functionality by providing extra methods related to push events | ||
4 | -# | ||
5 | -# Used by Event | ||
6 | -# | ||
7 | -module PushEvent | ||
8 | - def valid_push? | ||
9 | - data[:ref] | ||
10 | - rescue => ex | ||
11 | - false | ||
12 | - end | ||
13 | - | ||
14 | - def tag? | ||
15 | - data[:ref]["refs/tags"] | ||
16 | - end | ||
17 | - | ||
18 | - def branch? | ||
19 | - data[:ref]["refs/heads"] | ||
20 | - end | ||
21 | - | ||
22 | - def new_branch? | ||
23 | - commit_from =~ /^00000/ | ||
24 | - end | ||
25 | - | ||
26 | - def new_ref? | ||
27 | - commit_from =~ /^00000/ | ||
28 | - end | ||
29 | - | ||
30 | - def rm_ref? | ||
31 | - commit_to =~ /^00000/ | ||
32 | - end | ||
33 | - | ||
34 | - def md_ref? | ||
35 | - !(rm_ref? || new_ref?) | ||
36 | - end | ||
37 | - | ||
38 | - def commit_from | ||
39 | - data[:before] | ||
40 | - end | ||
41 | - | ||
42 | - def commit_to | ||
43 | - data[:after] | ||
44 | - end | ||
45 | - | ||
46 | - def ref_name | ||
47 | - if tag? | ||
48 | - tag_name | ||
49 | - else | ||
50 | - branch_name | ||
51 | - end | ||
52 | - end | ||
53 | - | ||
54 | - def branch_name | ||
55 | - @branch_name ||= data[:ref].gsub("refs/heads/", "") | ||
56 | - end | ||
57 | - | ||
58 | - def tag_name | ||
59 | - @tag_name ||= data[:ref].gsub("refs/tags/", "") | ||
60 | - end | ||
61 | - | ||
62 | - # Max 20 commits from push DESC | ||
63 | - def commits | ||
64 | - @commits ||= data[:commits].map { |commit| project.commit(commit[:id]) }.reverse | ||
65 | - end | ||
66 | - | ||
67 | - def commits_count | ||
68 | - data[:total_commits_count] || commits.count || 0 | ||
69 | - end | ||
70 | - | ||
71 | - def ref_type | ||
72 | - tag? ? "tag" : "branch" | ||
73 | - end | ||
74 | - | ||
75 | - def push_action_name | ||
76 | - if new_ref? | ||
77 | - "pushed new" | ||
78 | - elsif rm_ref? | ||
79 | - "deleted" | ||
80 | - else | ||
81 | - "pushed to" | ||
82 | - end | ||
83 | - end | ||
84 | - | ||
85 | - def parent_commit | ||
86 | - project.commit(commit_from) | ||
87 | - rescue => ex | ||
88 | - nil | ||
89 | - end | ||
90 | - | ||
91 | - def last_commit | ||
92 | - project.commit(commit_to) | ||
93 | - rescue => ex | ||
94 | - nil | ||
95 | - end | ||
96 | - | ||
97 | - def push_with_commits? | ||
98 | - md_ref? && commits.any? && parent_commit && last_commit | ||
99 | - rescue Grit::NoSuchPathError | ||
100 | - false | ||
101 | - end | ||
102 | - | ||
103 | - def last_push_to_non_root? | ||
104 | - branch? && project.default_branch != branch_name | ||
105 | - end | ||
106 | -end |
app/roles/push_observer.rb
@@ -1,149 +0,0 @@ | @@ -1,149 +0,0 @@ | ||
1 | -# == PushObserver role | ||
2 | -# | ||
3 | -# Includes methods to be triggered on push to project repository. | ||
4 | -# | ||
5 | -# | ||
6 | -# Used by Project | ||
7 | -# Triggered by PostReceive job | ||
8 | -# | ||
9 | -module PushObserver | ||
10 | - # This method will be called after each post receive and only if the provided | ||
11 | - # user is present in GitLab. | ||
12 | - # | ||
13 | - # All callbacks for post receive should be placed here. | ||
14 | - def trigger_post_receive(oldrev, newrev, ref, user) | ||
15 | - data = post_receive_data(oldrev, newrev, ref, user) | ||
16 | - | ||
17 | - # Create push event | ||
18 | - self.observe_push(data) | ||
19 | - | ||
20 | - if push_to_branch? ref, oldrev | ||
21 | - # Close merged MR | ||
22 | - self.update_merge_requests(oldrev, newrev, ref, user) | ||
23 | - | ||
24 | - # Execute web hooks | ||
25 | - self.execute_hooks(data.dup) | ||
26 | - | ||
27 | - # Execute project services | ||
28 | - self.execute_services(data.dup) | ||
29 | - end | ||
30 | - | ||
31 | - # Create satellite | ||
32 | - self.satellite.create unless self.satellite.exists? | ||
33 | - | ||
34 | - # Discover the default branch, but only if it hasn't already been set to | ||
35 | - # something else | ||
36 | - if default_branch.nil? | ||
37 | - update_attributes(default_branch: discover_default_branch) | ||
38 | - end | ||
39 | - end | ||
40 | - | ||
41 | - def push_to_branch? ref, oldrev | ||
42 | - ref_parts = ref.split('/') | ||
43 | - | ||
44 | - # Return if this is not a push to a branch (e.g. new commits) | ||
45 | - !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000") | ||
46 | - end | ||
47 | - | ||
48 | - def observe_push(data) | ||
49 | - Event.create( | ||
50 | - project: self, | ||
51 | - action: Event::Pushed, | ||
52 | - data: data, | ||
53 | - author_id: data[:user_id] | ||
54 | - ) | ||
55 | - end | ||
56 | - | ||
57 | - def execute_hooks(data) | ||
58 | - hooks.each { |hook| hook.execute(data) } | ||
59 | - end | ||
60 | - | ||
61 | - def execute_services(data) | ||
62 | - services.each do |service| | ||
63 | - | ||
64 | - # Call service hook only if it is active | ||
65 | - service.execute(data) if service.active | ||
66 | - end | ||
67 | - end | ||
68 | - | ||
69 | - # Produce a hash of post-receive data | ||
70 | - # | ||
71 | - # data = { | ||
72 | - # before: String, | ||
73 | - # after: String, | ||
74 | - # ref: String, | ||
75 | - # user_id: String, | ||
76 | - # user_name: String, | ||
77 | - # repository: { | ||
78 | - # name: String, | ||
79 | - # url: String, | ||
80 | - # description: String, | ||
81 | - # homepage: String, | ||
82 | - # }, | ||
83 | - # commits: Array, | ||
84 | - # total_commits_count: Fixnum | ||
85 | - # } | ||
86 | - # | ||
87 | - def post_receive_data(oldrev, newrev, ref, user) | ||
88 | - | ||
89 | - push_commits = commits_between(oldrev, newrev) | ||
90 | - | ||
91 | - # Total commits count | ||
92 | - push_commits_count = push_commits.size | ||
93 | - | ||
94 | - # Get latest 20 commits ASC | ||
95 | - push_commits_limited = push_commits.last(20) | ||
96 | - | ||
97 | - # Hash to be passed as post_receive_data | ||
98 | - data = { | ||
99 | - before: oldrev, | ||
100 | - after: newrev, | ||
101 | - ref: ref, | ||
102 | - user_id: user.id, | ||
103 | - user_name: user.name, | ||
104 | - repository: { | ||
105 | - name: name, | ||
106 | - url: url_to_repo, | ||
107 | - description: description, | ||
108 | - homepage: web_url, | ||
109 | - }, | ||
110 | - commits: [], | ||
111 | - total_commits_count: push_commits_count | ||
112 | - } | ||
113 | - | ||
114 | - # For perfomance purposes maximum 20 latest commits | ||
115 | - # will be passed as post receive hook data. | ||
116 | - # | ||
117 | - push_commits_limited.each do |commit| | ||
118 | - data[:commits] << { | ||
119 | - id: commit.id, | ||
120 | - message: commit.safe_message, | ||
121 | - timestamp: commit.date.xmlschema, | ||
122 | - url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}", | ||
123 | - author: { | ||
124 | - name: commit.author_name, | ||
125 | - email: commit.author_email | ||
126 | - } | ||
127 | - } | ||
128 | - end | ||
129 | - | ||
130 | - data | ||
131 | - end | ||
132 | - | ||
133 | - def update_merge_requests(oldrev, newrev, ref, user) | ||
134 | - return true unless ref =~ /heads/ | ||
135 | - branch_name = ref.gsub("refs/heads/", "") | ||
136 | - c_ids = self.commits_between(oldrev, newrev).map(&:id) | ||
137 | - | ||
138 | - # Update code for merge requests | ||
139 | - mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all | ||
140 | - mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } | ||
141 | - | ||
142 | - # Close merge requests | ||
143 | - mrs = self.merge_requests.opened.where(target_branch: branch_name).all | ||
144 | - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | ||
145 | - mrs.each { |merge_request| merge_request.merge!(user.id) } | ||
146 | - | ||
147 | - true | ||
148 | - end | ||
149 | -end |
app/roles/repository.rb
@@ -1,216 +0,0 @@ | @@ -1,216 +0,0 @@ | ||
1 | -# == Repository role | ||
2 | -# | ||
3 | -# Provides access to git repository resources like commits, branches etc.. | ||
4 | -# Allows you to manage repository via gitolite interface(git_host) | ||
5 | -# | ||
6 | -# Used by Project | ||
7 | -# | ||
8 | -module Repository | ||
9 | - include GitHost | ||
10 | - | ||
11 | - def valid_repo? | ||
12 | - repo | ||
13 | - rescue | ||
14 | - errors.add(:path, "Invalid repository path") | ||
15 | - false | ||
16 | - end | ||
17 | - | ||
18 | - def empty_repo? | ||
19 | - !repo_exists? || !has_commits? | ||
20 | - end | ||
21 | - | ||
22 | - def commit(commit_id = nil) | ||
23 | - Commit.find_or_first(repo, commit_id, root_ref) | ||
24 | - end | ||
25 | - | ||
26 | - def fresh_commits(n = 10) | ||
27 | - Commit.fresh_commits(repo, n) | ||
28 | - end | ||
29 | - | ||
30 | - def commits_with_refs(n = 20) | ||
31 | - Commit.commits_with_refs(repo, n) | ||
32 | - end | ||
33 | - | ||
34 | - def commits_since(date) | ||
35 | - Commit.commits_since(repo, date) | ||
36 | - end | ||
37 | - | ||
38 | - def commits(ref, path = nil, limit = nil, offset = nil) | ||
39 | - Commit.commits(repo, ref, path, limit, offset) | ||
40 | - end | ||
41 | - | ||
42 | - def last_commit_for(ref, path = nil) | ||
43 | - commits(ref, path, 1).first | ||
44 | - end | ||
45 | - | ||
46 | - def commits_between(from, to) | ||
47 | - Commit.commits_between(repo, from, to) | ||
48 | - end | ||
49 | - | ||
50 | - def satellite | ||
51 | - @satellite ||= Gitlab::Satellite::Satellite.new(self) | ||
52 | - end | ||
53 | - | ||
54 | - def has_post_receive_file? | ||
55 | - !!hook_file | ||
56 | - end | ||
57 | - | ||
58 | - def valid_post_receive_file? | ||
59 | - valid_hook_file == hook_file | ||
60 | - end | ||
61 | - | ||
62 | - def valid_hook_file | ||
63 | - @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive')) | ||
64 | - end | ||
65 | - | ||
66 | - def hook_file | ||
67 | - @hook_file ||= begin | ||
68 | - hook_path = File.join(path_to_repo, 'hooks', 'post-receive') | ||
69 | - File.read(hook_path) if File.exists?(hook_path) | ||
70 | - end | ||
71 | - end | ||
72 | - | ||
73 | - # Returns an Array of branch names | ||
74 | - def branch_names | ||
75 | - repo.branches.collect(&:name).sort | ||
76 | - end | ||
77 | - | ||
78 | - # Returns an Array of Branches | ||
79 | - def branches | ||
80 | - repo.branches.sort_by(&:name) | ||
81 | - end | ||
82 | - | ||
83 | - # Returns an Array of tag names | ||
84 | - def tag_names | ||
85 | - repo.tags.collect(&:name).sort.reverse | ||
86 | - end | ||
87 | - | ||
88 | - # Returns an Array of Tags | ||
89 | - def tags | ||
90 | - repo.tags.sort_by(&:name).reverse | ||
91 | - end | ||
92 | - | ||
93 | - # Returns an Array of branch and tag names | ||
94 | - def ref_names | ||
95 | - [branch_names + tag_names].flatten | ||
96 | - end | ||
97 | - | ||
98 | - def repo | ||
99 | - @repo ||= Grit::Repo.new(path_to_repo) | ||
100 | - end | ||
101 | - | ||
102 | - def url_to_repo | ||
103 | - git_host.url_to_repo(path_with_namespace) | ||
104 | - end | ||
105 | - | ||
106 | - def path_to_repo | ||
107 | - File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git") | ||
108 | - end | ||
109 | - | ||
110 | - def namespace_dir | ||
111 | - namespace.try(:path) || '' | ||
112 | - end | ||
113 | - | ||
114 | - def update_repository | ||
115 | - git_host.update_repository(self) | ||
116 | - end | ||
117 | - | ||
118 | - def destroy_repository | ||
119 | - git_host.remove_repository(self) | ||
120 | - end | ||
121 | - | ||
122 | - def repo_exists? | ||
123 | - @repo_exists ||= (repo && !repo.branches.empty?) | ||
124 | - rescue | ||
125 | - @repo_exists = false | ||
126 | - end | ||
127 | - | ||
128 | - def heads | ||
129 | - @heads ||= repo.heads | ||
130 | - end | ||
131 | - | ||
132 | - def tree(fcommit, path = nil) | ||
133 | - fcommit = commit if fcommit == :head | ||
134 | - tree = fcommit.tree | ||
135 | - path ? (tree / path) : tree | ||
136 | - end | ||
137 | - | ||
138 | - def open_branches | ||
139 | - if protected_branches.empty? | ||
140 | - self.repo.heads | ||
141 | - else | ||
142 | - pnames = protected_branches.map(&:name) | ||
143 | - self.repo.heads.reject { |h| pnames.include?(h.name) } | ||
144 | - end.sort_by(&:name) | ||
145 | - end | ||
146 | - | ||
147 | - # Discovers the default branch based on the repository's available branches | ||
148 | - # | ||
149 | - # - If no branches are present, returns nil | ||
150 | - # - If one branch is present, returns its name | ||
151 | - # - If two or more branches are present, returns the one that has a name | ||
152 | - # matching root_ref (default_branch or 'master' if default_branch is nil) | ||
153 | - def discover_default_branch | ||
154 | - if branch_names.length == 0 | ||
155 | - nil | ||
156 | - elsif branch_names.length == 1 | ||
157 | - branch_names.first | ||
158 | - else | ||
159 | - branch_names.select { |v| v == root_ref }.first | ||
160 | - end | ||
161 | - end | ||
162 | - | ||
163 | - def has_commits? | ||
164 | - !!commit | ||
165 | - rescue Grit::NoSuchPathError | ||
166 | - false | ||
167 | - end | ||
168 | - | ||
169 | - def root_ref | ||
170 | - default_branch || "master" | ||
171 | - end | ||
172 | - | ||
173 | - def root_ref?(branch) | ||
174 | - root_ref == branch | ||
175 | - end | ||
176 | - | ||
177 | - # Archive Project to .tar.gz | ||
178 | - # | ||
179 | - # Already packed repo archives stored at | ||
180 | - # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz | ||
181 | - # | ||
182 | - def archive_repo(ref) | ||
183 | - ref = ref || self.root_ref | ||
184 | - commit = self.commit(ref) | ||
185 | - return nil unless commit | ||
186 | - | ||
187 | - # Build file path | ||
188 | - file_name = self.path + "-" + commit.id.to_s + ".tar.gz" | ||
189 | - storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace) | ||
190 | - file_path = File.join(storage_path, file_name) | ||
191 | - | ||
192 | - # Put files into a directory before archiving | ||
193 | - prefix = self.path + "/" | ||
194 | - | ||
195 | - # Create file if not exists | ||
196 | - unless File.exists?(file_path) | ||
197 | - FileUtils.mkdir_p storage_path | ||
198 | - file = self.repo.archive_to_file(ref, prefix, file_path) | ||
199 | - end | ||
200 | - | ||
201 | - file_path | ||
202 | - end | ||
203 | - | ||
204 | - def ssh_url_to_repo | ||
205 | - url_to_repo | ||
206 | - end | ||
207 | - | ||
208 | - def http_url_to_repo | ||
209 | - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') | ||
210 | - end | ||
211 | - | ||
212 | - # Check if current branch name is marked as protected in the system | ||
213 | - def protected_branch? branch_name | ||
214 | - protected_branches.map(&:name).include?(branch_name) | ||
215 | - end | ||
216 | -end |
app/roles/static_model.rb
@@ -1,47 +0,0 @@ | @@ -1,47 +0,0 @@ | ||
1 | -# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database. | ||
2 | -module StaticModel | ||
3 | - extend ActiveSupport::Concern | ||
4 | - | ||
5 | - module ClassMethods | ||
6 | - # Used by ActiveRecord's polymorphic association to set object_id | ||
7 | - def primary_key | ||
8 | - 'id' | ||
9 | - end | ||
10 | - | ||
11 | - # Used by ActiveRecord's polymorphic association to set object_type | ||
12 | - def base_class | ||
13 | - self | ||
14 | - end | ||
15 | - end | ||
16 | - | ||
17 | - # Used by AR for fetching attributes | ||
18 | - # | ||
19 | - # Pass it along if we respond to it. | ||
20 | - def [](key) | ||
21 | - send(key) if respond_to?(key) | ||
22 | - end | ||
23 | - | ||
24 | - def to_param | ||
25 | - id | ||
26 | - end | ||
27 | - | ||
28 | - def new_record? | ||
29 | - false | ||
30 | - end | ||
31 | - | ||
32 | - def persisted? | ||
33 | - false | ||
34 | - end | ||
35 | - | ||
36 | - def destroyed? | ||
37 | - false | ||
38 | - end | ||
39 | - | ||
40 | - def ==(other) | ||
41 | - if other.is_a? StaticModel | ||
42 | - id == other.id | ||
43 | - else | ||
44 | - super | ||
45 | - end | ||
46 | - end | ||
47 | -end |
app/roles/team.rb
@@ -1,63 +0,0 @@ | @@ -1,63 +0,0 @@ | ||
1 | -# == Team role | ||
2 | -# | ||
3 | -# Provides functionality to manage project team | ||
4 | -# - add user/users to project | ||
5 | -# - update existing membership | ||
6 | -# - remove users from project team | ||
7 | -# | ||
8 | -# Used by Project | ||
9 | -# | ||
10 | -module Team | ||
11 | - def team_member_by_name_or_email(name = nil, email = nil) | ||
12 | - user = users.where("name like ? or email like ?", name, email).first | ||
13 | - users_projects.where(user: user) if user | ||
14 | - end | ||
15 | - | ||
16 | - # Get Team Member record by user id | ||
17 | - def team_member_by_id(user_id) | ||
18 | - users_projects.find_by_user_id(user_id) | ||
19 | - end | ||
20 | - | ||
21 | - # Add user to project | ||
22 | - # with passed access role | ||
23 | - def add_user_to_team(user, access_role) | ||
24 | - add_user_id_to_team(user.id, access_role) | ||
25 | - end | ||
26 | - | ||
27 | - # Add multiple users to project | ||
28 | - # with same access role | ||
29 | - def add_users_to_team(users, access_role) | ||
30 | - add_users_ids_to_team(users.map(&:id), access_role) | ||
31 | - end | ||
32 | - | ||
33 | - # Add user to project | ||
34 | - # with passed access role by user id | ||
35 | - def add_user_id_to_team(user_id, access_role) | ||
36 | - users_projects.create( | ||
37 | - user_id: user_id, | ||
38 | - project_access: access_role | ||
39 | - ) | ||
40 | - end | ||
41 | - | ||
42 | - # Add multiple users to project | ||
43 | - # with same access role by user ids | ||
44 | - def add_users_ids_to_team(users_ids, access_role) | ||
45 | - UsersProject.bulk_import(self, users_ids, access_role) | ||
46 | - end | ||
47 | - | ||
48 | - # Update multiple project users | ||
49 | - # to same access role by user ids | ||
50 | - def update_users_ids_to_role(users_ids, access_role) | ||
51 | - UsersProject.bulk_update(self, users_ids, access_role) | ||
52 | - end | ||
53 | - | ||
54 | - # Delete multiple users from project by user ids | ||
55 | - def delete_users_ids_from_team(users_ids) | ||
56 | - UsersProject.bulk_delete(self, users_ids) | ||
57 | - end | ||
58 | - | ||
59 | - # Remove all users from project team | ||
60 | - def truncate_team | ||
61 | - UsersProject.truncate_team(self) | ||
62 | - end | ||
63 | -end |
app/roles/votes.rb
@@ -1,39 +0,0 @@ | @@ -1,39 +0,0 @@ | ||
1 | -# == Votes role | ||
2 | -# | ||
3 | -# Provides functionality to upvote/downvote entity | ||
4 | -# based on +1 and -1 notes | ||
5 | -# | ||
6 | -# Used for Issue and Merge Request | ||
7 | -# | ||
8 | -module Votes | ||
9 | - # Return the number of +1 comments (upvotes) | ||
10 | - def upvotes | ||
11 | - notes.select(&:upvote?).size | ||
12 | - end | ||
13 | - | ||
14 | - def upvotes_in_percent | ||
15 | - if votes_count.zero? | ||
16 | - 0 | ||
17 | - else | ||
18 | - 100.0 / votes_count * upvotes | ||
19 | - end | ||
20 | - end | ||
21 | - | ||
22 | - # Return the number of -1 comments (downvotes) | ||
23 | - def downvotes | ||
24 | - notes.select(&:downvote?).size | ||
25 | - end | ||
26 | - | ||
27 | - def downvotes_in_percent | ||
28 | - if votes_count.zero? | ||
29 | - 0 | ||
30 | - else | ||
31 | - 100.0 - upvotes_in_percent | ||
32 | - end | ||
33 | - end | ||
34 | - | ||
35 | - # Return the total number of votes | ||
36 | - def votes_count | ||
37 | - upvotes + downvotes | ||
38 | - end | ||
39 | -end |
@@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
1 | +# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database. | ||
2 | +module StaticModel | ||
3 | + extend ActiveSupport::Concern | ||
4 | + | ||
5 | + module ClassMethods | ||
6 | + # Used by ActiveRecord's polymorphic association to set object_id | ||
7 | + def primary_key | ||
8 | + 'id' | ||
9 | + end | ||
10 | + | ||
11 | + # Used by ActiveRecord's polymorphic association to set object_type | ||
12 | + def base_class | ||
13 | + self | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + # Used by AR for fetching attributes | ||
18 | + # | ||
19 | + # Pass it along if we respond to it. | ||
20 | + def [](key) | ||
21 | + send(key) if respond_to?(key) | ||
22 | + end | ||
23 | + | ||
24 | + def to_param | ||
25 | + id | ||
26 | + end | ||
27 | + | ||
28 | + def new_record? | ||
29 | + false | ||
30 | + end | ||
31 | + | ||
32 | + def persisted? | ||
33 | + false | ||
34 | + end | ||
35 | + | ||
36 | + def destroyed? | ||
37 | + false | ||
38 | + end | ||
39 | + | ||
40 | + def ==(other) | ||
41 | + if other.is_a? StaticModel | ||
42 | + id == other.id | ||
43 | + else | ||
44 | + super | ||
45 | + end | ||
46 | + end | ||
47 | +end |
@@ -0,0 +1,70 @@ | @@ -0,0 +1,70 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Issue, "IssueCommonality" do | ||
4 | + let(:issue) { create(:issue) } | ||
5 | + | ||
6 | + describe "Associations" do | ||
7 | + it { should belong_to(:project) } | ||
8 | + it { should belong_to(:author) } | ||
9 | + it { should belong_to(:assignee) } | ||
10 | + it { should have_many(:notes).dependent(:destroy) } | ||
11 | + end | ||
12 | + | ||
13 | + describe "Validation" do | ||
14 | + it { should validate_presence_of(:project) } | ||
15 | + it { should validate_presence_of(:author) } | ||
16 | + it { should validate_presence_of(:title) } | ||
17 | + it { should ensure_length_of(:title).is_at_least(0).is_at_most(255) } | ||
18 | + it { should ensure_inclusion_of(:closed).in_array([true, false]) } | ||
19 | + end | ||
20 | + | ||
21 | + describe "Scope" do | ||
22 | + it { described_class.should respond_to(:opened) } | ||
23 | + it { described_class.should respond_to(:closed) } | ||
24 | + it { described_class.should respond_to(:assigned) } | ||
25 | + end | ||
26 | + | ||
27 | + it "has an :author_id_of_changes accessor" do | ||
28 | + issue.should respond_to(:author_id_of_changes) | ||
29 | + issue.should respond_to(:author_id_of_changes=) | ||
30 | + end | ||
31 | + | ||
32 | + describe ".search" do | ||
33 | + let!(:searchable_issue) { create(:issue, title: "Searchable issue") } | ||
34 | + | ||
35 | + it "matches by title" do | ||
36 | + described_class.search('able').all.should == [searchable_issue] | ||
37 | + end | ||
38 | + end | ||
39 | + | ||
40 | + describe "#today?" do | ||
41 | + it "returns true when created today" do | ||
42 | + # Avoid timezone differences and just return exactly what we want | ||
43 | + Date.stub(:today).and_return(issue.created_at.to_date) | ||
44 | + issue.today?.should be_true | ||
45 | + end | ||
46 | + | ||
47 | + it "returns false when not created today" do | ||
48 | + Date.stub(:today).and_return(Date.yesterday) | ||
49 | + issue.today?.should be_false | ||
50 | + end | ||
51 | + end | ||
52 | + | ||
53 | + describe "#new?" do | ||
54 | + it "returns true when created today and record hasn't been updated" do | ||
55 | + issue.stub(:today?).and_return(true) | ||
56 | + issue.new?.should be_true | ||
57 | + end | ||
58 | + | ||
59 | + it "returns false when not created today" do | ||
60 | + issue.stub(:today?).and_return(false) | ||
61 | + issue.new?.should be_false | ||
62 | + end | ||
63 | + | ||
64 | + it "returns false when record has been updated" do | ||
65 | + issue.stub(:today?).and_return(true) | ||
66 | + issue.touch | ||
67 | + issue.new?.should be_false | ||
68 | + end | ||
69 | + end | ||
70 | +end |
@@ -0,0 +1,132 @@ | @@ -0,0 +1,132 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Issue do | ||
4 | + let(:issue) { create(:issue) } | ||
5 | + | ||
6 | + describe "#upvotes" do | ||
7 | + it "with no notes has a 0/0 score" do | ||
8 | + issue.upvotes.should == 0 | ||
9 | + end | ||
10 | + | ||
11 | + it "should recognize non-+1 notes" do | ||
12 | + issue.notes << create(:note, note: "No +1 here") | ||
13 | + issue.should have(1).note | ||
14 | + issue.notes.first.upvote?.should be_false | ||
15 | + issue.upvotes.should == 0 | ||
16 | + end | ||
17 | + | ||
18 | + it "should recognize a single +1 note" do | ||
19 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
20 | + issue.upvotes.should == 1 | ||
21 | + end | ||
22 | + | ||
23 | + it "should recognize multiple +1 notes" do | ||
24 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
25 | + issue.notes << create(:note, note: "+1 I want this") | ||
26 | + issue.upvotes.should == 2 | ||
27 | + end | ||
28 | + end | ||
29 | + | ||
30 | + describe "#downvotes" do | ||
31 | + it "with no notes has a 0/0 score" do | ||
32 | + issue.downvotes.should == 0 | ||
33 | + end | ||
34 | + | ||
35 | + it "should recognize non--1 notes" do | ||
36 | + issue.notes << create(:note, note: "Almost got a -1") | ||
37 | + issue.should have(1).note | ||
38 | + issue.notes.first.downvote?.should be_false | ||
39 | + issue.downvotes.should == 0 | ||
40 | + end | ||
41 | + | ||
42 | + it "should recognize a single -1 note" do | ||
43 | + issue.notes << create(:note, note: "-1 This is bad") | ||
44 | + issue.downvotes.should == 1 | ||
45 | + end | ||
46 | + | ||
47 | + it "should recognize multiple -1 notes" do | ||
48 | + issue.notes << create(:note, note: "-1 This is bad") | ||
49 | + issue.notes << create(:note, note: "-1 Away with this") | ||
50 | + issue.downvotes.should == 2 | ||
51 | + end | ||
52 | + end | ||
53 | + | ||
54 | + describe "#votes_count" do | ||
55 | + it "with no notes has a 0/0 score" do | ||
56 | + issue.votes_count.should == 0 | ||
57 | + end | ||
58 | + | ||
59 | + it "should recognize non notes" do | ||
60 | + issue.notes << create(:note, note: "No +1 here") | ||
61 | + issue.should have(1).note | ||
62 | + issue.votes_count.should == 0 | ||
63 | + end | ||
64 | + | ||
65 | + it "should recognize a single +1 note" do | ||
66 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
67 | + issue.votes_count.should == 1 | ||
68 | + end | ||
69 | + | ||
70 | + it "should recognize a single -1 note" do | ||
71 | + issue.notes << create(:note, note: "-1 This is bad") | ||
72 | + issue.votes_count.should == 1 | ||
73 | + end | ||
74 | + | ||
75 | + it "should recognize multiple notes" do | ||
76 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
77 | + issue.notes << create(:note, note: "-1 This is bad") | ||
78 | + issue.notes << create(:note, note: "+1 I want this") | ||
79 | + issue.votes_count.should == 3 | ||
80 | + end | ||
81 | + end | ||
82 | + | ||
83 | + describe "#upvotes_in_percent" do | ||
84 | + it "with no notes has a 0% score" do | ||
85 | + issue.upvotes_in_percent.should == 0 | ||
86 | + end | ||
87 | + | ||
88 | + it "should count a single 1 note as 100%" do | ||
89 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
90 | + issue.upvotes_in_percent.should == 100 | ||
91 | + end | ||
92 | + | ||
93 | + it "should count multiple +1 notes as 100%" do | ||
94 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
95 | + issue.notes << create(:note, note: "+1 I want this") | ||
96 | + issue.upvotes_in_percent.should == 100 | ||
97 | + end | ||
98 | + | ||
99 | + it "should count fractions for multiple +1 and -1 notes correctly" do | ||
100 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
101 | + issue.notes << create(:note, note: "+1 I want this") | ||
102 | + issue.notes << create(:note, note: "-1 This is bad") | ||
103 | + issue.notes << create(:note, note: "+1 me too") | ||
104 | + issue.upvotes_in_percent.should == 75 | ||
105 | + end | ||
106 | + end | ||
107 | + | ||
108 | + describe "#downvotes_in_percent" do | ||
109 | + it "with no notes has a 0% score" do | ||
110 | + issue.downvotes_in_percent.should == 0 | ||
111 | + end | ||
112 | + | ||
113 | + it "should count a single -1 note as 100%" do | ||
114 | + issue.notes << create(:note, note: "-1 This is bad") | ||
115 | + issue.downvotes_in_percent.should == 100 | ||
116 | + end | ||
117 | + | ||
118 | + it "should count multiple -1 notes as 100%" do | ||
119 | + issue.notes << create(:note, note: "-1 This is bad") | ||
120 | + issue.notes << create(:note, note: "-1 Away with this") | ||
121 | + issue.downvotes_in_percent.should == 100 | ||
122 | + end | ||
123 | + | ||
124 | + it "should count fractions for multiple +1 and -1 notes correctly" do | ||
125 | + issue.notes << create(:note, note: "+1 This is awesome") | ||
126 | + issue.notes << create(:note, note: "+1 I want this") | ||
127 | + issue.notes << create(:note, note: "-1 This is bad") | ||
128 | + issue.notes << create(:note, note: "+1 me too") | ||
129 | + issue.downvotes_in_percent.should == 25 | ||
130 | + end | ||
131 | + end | ||
132 | +end |
@@ -0,0 +1,159 @@ | @@ -0,0 +1,159 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Project, "Repository" do | ||
4 | + let(:project) { create(:project) } | ||
5 | + | ||
6 | + describe "#empty_repo?" do | ||
7 | + it "should return true if the repo doesn't exist" do | ||
8 | + project.stub(repo_exists?: false, has_commits?: true) | ||
9 | + project.should be_empty_repo | ||
10 | + end | ||
11 | + | ||
12 | + it "should return true if the repo has commits" do | ||
13 | + project.stub(repo_exists?: true, has_commits?: false) | ||
14 | + project.should be_empty_repo | ||
15 | + end | ||
16 | + | ||
17 | + it "should return false if the repo exists and has commits" do | ||
18 | + project.stub(repo_exists?: true, has_commits?: true) | ||
19 | + project.should_not be_empty_repo | ||
20 | + end | ||
21 | + end | ||
22 | + | ||
23 | + describe "#discover_default_branch" do | ||
24 | + let(:master) { 'master' } | ||
25 | + let(:stable) { 'stable' } | ||
26 | + | ||
27 | + it "returns 'master' when master exists" do | ||
28 | + project.should_receive(:branch_names).at_least(:once).and_return([stable, master]) | ||
29 | + project.discover_default_branch.should == 'master' | ||
30 | + end | ||
31 | + | ||
32 | + it "returns non-master when master exists but default branch is set to something else" do | ||
33 | + project.default_branch = 'stable' | ||
34 | + project.should_receive(:branch_names).at_least(:once).and_return([stable, master]) | ||
35 | + project.discover_default_branch.should == 'stable' | ||
36 | + end | ||
37 | + | ||
38 | + it "returns a non-master branch when only one exists" do | ||
39 | + project.should_receive(:branch_names).at_least(:once).and_return([stable]) | ||
40 | + project.discover_default_branch.should == 'stable' | ||
41 | + end | ||
42 | + | ||
43 | + it "returns nil when no branch exists" do | ||
44 | + project.should_receive(:branch_names).at_least(:once).and_return([]) | ||
45 | + project.discover_default_branch.should be_nil | ||
46 | + end | ||
47 | + end | ||
48 | + | ||
49 | + describe "#root_ref" do | ||
50 | + it "returns default_branch when set" do | ||
51 | + project.default_branch = 'stable' | ||
52 | + project.root_ref.should == 'stable' | ||
53 | + end | ||
54 | + | ||
55 | + it "returns 'master' when default_branch is nil" do | ||
56 | + project.default_branch = nil | ||
57 | + project.root_ref.should == 'master' | ||
58 | + end | ||
59 | + end | ||
60 | + | ||
61 | + describe "#root_ref?" do | ||
62 | + it "returns true when branch is root_ref" do | ||
63 | + project.default_branch = 'stable' | ||
64 | + project.root_ref?('stable').should be_true | ||
65 | + end | ||
66 | + | ||
67 | + it "returns false when branch is not root_ref" do | ||
68 | + project.default_branch = nil | ||
69 | + project.root_ref?('stable').should be_false | ||
70 | + end | ||
71 | + end | ||
72 | + | ||
73 | + describe :repo do | ||
74 | + it "should return valid repo" do | ||
75 | + project.repo.should be_kind_of(Grit::Repo) | ||
76 | + end | ||
77 | + | ||
78 | + it "should return nil" do | ||
79 | + lambda { Project.new(path: "invalid").repo }.should raise_error(Grit::NoSuchPathError) | ||
80 | + end | ||
81 | + | ||
82 | + it "should return nil" do | ||
83 | + lambda { Project.new.repo }.should raise_error(TypeError) | ||
84 | + end | ||
85 | + end | ||
86 | + | ||
87 | + describe :commit do | ||
88 | + it "should return first head commit if without params" do | ||
89 | + project.commit.id.should == project.repo.commits.first.id | ||
90 | + end | ||
91 | + | ||
92 | + it "should return valid commit" do | ||
93 | + project.commit(ValidCommit::ID).should be_valid_commit | ||
94 | + end | ||
95 | + | ||
96 | + it "should return nil" do | ||
97 | + project.commit("+123_4532530XYZ").should be_nil | ||
98 | + end | ||
99 | + end | ||
100 | + | ||
101 | + describe :tree do | ||
102 | + before do | ||
103 | + @commit = project.commit(ValidCommit::ID) | ||
104 | + end | ||
105 | + | ||
106 | + it "should raise error w/o arguments" do | ||
107 | + lambda { project.tree }.should raise_error | ||
108 | + end | ||
109 | + | ||
110 | + it "should return root tree for commit" do | ||
111 | + tree = project.tree(@commit) | ||
112 | + tree.contents.size.should == ValidCommit::FILES_COUNT | ||
113 | + tree.contents.map(&:name).should == ValidCommit::FILES | ||
114 | + end | ||
115 | + | ||
116 | + it "should return root tree for commit with correct path" do | ||
117 | + tree = project.tree(@commit, ValidCommit::C_FILE_PATH) | ||
118 | + tree.contents.map(&:name).should == ValidCommit::C_FILES | ||
119 | + end | ||
120 | + | ||
121 | + it "should return root tree for commit with incorrect path" do | ||
122 | + project.tree(@commit, "invalid_path").should be_nil | ||
123 | + end | ||
124 | + end | ||
125 | + | ||
126 | + describe "fresh commits" do | ||
127 | + let(:project) { create(:project) } | ||
128 | + | ||
129 | + it { project.fresh_commits(3).count.should == 3 } | ||
130 | + it { project.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" } | ||
131 | + it { project.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" } | ||
132 | + end | ||
133 | + | ||
134 | + describe "commits_between" do | ||
135 | + let(:project) { create(:project) } | ||
136 | + | ||
137 | + subject do | ||
138 | + commits = project.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff", | ||
139 | + "8470d70da67355c9c009e4401746b1d5410af2e3") | ||
140 | + commits.map { |c| c.id } | ||
141 | + end | ||
142 | + | ||
143 | + it { should have(3).elements } | ||
144 | + it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") } | ||
145 | + it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") } | ||
146 | + end | ||
147 | + | ||
148 | + describe :valid_repo? do | ||
149 | + it "should be valid repo" do | ||
150 | + project = create(:project) | ||
151 | + project.valid_repo?.should be_true | ||
152 | + end | ||
153 | + | ||
154 | + it "should be invalid repo" do | ||
155 | + project = Project.new(name: "ok_name", path: "/INVALID_PATH/", path: "NEOK") | ||
156 | + project.valid_repo?.should be_false | ||
157 | + end | ||
158 | + end | ||
159 | +end |
spec/models/project_spec.rb
@@ -133,14 +133,6 @@ describe Project do | @@ -133,14 +133,6 @@ describe Project do | ||
133 | it { should respond_to(:path_with_namespace) } | 133 | it { should respond_to(:path_with_namespace) } |
134 | end | 134 | end |
135 | 135 | ||
136 | - describe 'modules' do | ||
137 | - it { should include_module(Repository) } | ||
138 | - it { should include_module(PushObserver) } | ||
139 | - it { should include_module(Authority) } | ||
140 | - it { should include_module(Team) } | ||
141 | - it { should include_module(NamespacedProject) } | ||
142 | - end | ||
143 | - | ||
144 | it "should return valid url to repo" do | 136 | it "should return valid url to repo" do |
145 | project = Project.new(path: "somewhere") | 137 | project = Project.new(path: "somewhere") |
146 | project.url_to_repo.should == Gitlab.config.gitolite.ssh_path_prefix + "somewhere.git" | 138 | project.url_to_repo.should == Gitlab.config.gitolite.ssh_path_prefix + "somewhere.git" |
spec/models/user_spec.rb
@@ -65,10 +65,6 @@ describe User do | @@ -65,10 +65,6 @@ describe User do | ||
65 | it { should ensure_length_of(:bio).is_within(0..255) } | 65 | it { should ensure_length_of(:bio).is_within(0..255) } |
66 | end | 66 | end |
67 | 67 | ||
68 | - describe 'modules' do | ||
69 | - it { should include_module(Account) } | ||
70 | - end | ||
71 | - | ||
72 | describe "Respond to" do | 68 | describe "Respond to" do |
73 | it { should respond_to(:is_admin?) } | 69 | it { should respond_to(:is_admin?) } |
74 | it { should respond_to(:identifier) } | 70 | it { should respond_to(:identifier) } |
@@ -185,4 +181,14 @@ describe User do | @@ -185,4 +181,14 @@ describe User do | ||
185 | 181 | ||
186 | it { User.not_in_project(@project).should == [@user, @project.owner] } | 182 | it { User.not_in_project(@project).should == [@user, @project.owner] } |
187 | end | 183 | end |
184 | + | ||
185 | + describe 'normal user' do | ||
186 | + let(:user) { create(:user, name: 'John Smith') } | ||
187 | + | ||
188 | + it { user.is_admin?.should be_false } | ||
189 | + it { user.require_ssh_key?.should be_true } | ||
190 | + it { user.can_create_group?.should be_false } | ||
191 | + it { user.can_create_project?.should be_true } | ||
192 | + it { user.first_name.should == 'John' } | ||
193 | + end | ||
188 | end | 194 | end |
spec/roles/account_role_spec.rb
@@ -1,13 +0,0 @@ | @@ -1,13 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe User, "Account" do | ||
4 | - describe 'normal user' do | ||
5 | - let(:user) { create(:user, name: 'John Smith') } | ||
6 | - | ||
7 | - it { user.is_admin?.should be_false } | ||
8 | - it { user.require_ssh_key?.should be_true } | ||
9 | - it { user.can_create_group?.should be_false } | ||
10 | - it { user.can_create_project?.should be_true } | ||
11 | - it { user.first_name.should == 'John' } | ||
12 | - end | ||
13 | -end |
spec/roles/issue_commonality_spec.rb
@@ -1,70 +0,0 @@ | @@ -1,70 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe Issue, "IssueCommonality" do | ||
4 | - let(:issue) { create(:issue) } | ||
5 | - | ||
6 | - describe "Associations" do | ||
7 | - it { should belong_to(:project) } | ||
8 | - it { should belong_to(:author) } | ||
9 | - it { should belong_to(:assignee) } | ||
10 | - it { should have_many(:notes).dependent(:destroy) } | ||
11 | - end | ||
12 | - | ||
13 | - describe "Validation" do | ||
14 | - it { should validate_presence_of(:project) } | ||
15 | - it { should validate_presence_of(:author) } | ||
16 | - it { should validate_presence_of(:title) } | ||
17 | - it { should ensure_length_of(:title).is_at_least(0).is_at_most(255) } | ||
18 | - it { should ensure_inclusion_of(:closed).in_array([true, false]) } | ||
19 | - end | ||
20 | - | ||
21 | - describe "Scope" do | ||
22 | - it { described_class.should respond_to(:opened) } | ||
23 | - it { described_class.should respond_to(:closed) } | ||
24 | - it { described_class.should respond_to(:assigned) } | ||
25 | - end | ||
26 | - | ||
27 | - it "has an :author_id_of_changes accessor" do | ||
28 | - issue.should respond_to(:author_id_of_changes) | ||
29 | - issue.should respond_to(:author_id_of_changes=) | ||
30 | - end | ||
31 | - | ||
32 | - describe ".search" do | ||
33 | - let!(:searchable_issue) { create(:issue, title: "Searchable issue") } | ||
34 | - | ||
35 | - it "matches by title" do | ||
36 | - described_class.search('able').all.should == [searchable_issue] | ||
37 | - end | ||
38 | - end | ||
39 | - | ||
40 | - describe "#today?" do | ||
41 | - it "returns true when created today" do | ||
42 | - # Avoid timezone differences and just return exactly what we want | ||
43 | - Date.stub(:today).and_return(issue.created_at.to_date) | ||
44 | - issue.today?.should be_true | ||
45 | - end | ||
46 | - | ||
47 | - it "returns false when not created today" do | ||
48 | - Date.stub(:today).and_return(Date.yesterday) | ||
49 | - issue.today?.should be_false | ||
50 | - end | ||
51 | - end | ||
52 | - | ||
53 | - describe "#new?" do | ||
54 | - it "returns true when created today and record hasn't been updated" do | ||
55 | - issue.stub(:today?).and_return(true) | ||
56 | - issue.new?.should be_true | ||
57 | - end | ||
58 | - | ||
59 | - it "returns false when not created today" do | ||
60 | - issue.stub(:today?).and_return(false) | ||
61 | - issue.new?.should be_false | ||
62 | - end | ||
63 | - | ||
64 | - it "returns false when record has been updated" do | ||
65 | - issue.stub(:today?).and_return(true) | ||
66 | - issue.touch | ||
67 | - issue.new?.should be_false | ||
68 | - end | ||
69 | - end | ||
70 | -end |
spec/roles/repository_spec.rb
@@ -1,159 +0,0 @@ | @@ -1,159 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe Project, "Repository" do | ||
4 | - let(:project) { create(:project) } | ||
5 | - | ||
6 | - describe "#empty_repo?" do | ||
7 | - it "should return true if the repo doesn't exist" do | ||
8 | - project.stub(repo_exists?: false, has_commits?: true) | ||
9 | - project.should be_empty_repo | ||
10 | - end | ||
11 | - | ||
12 | - it "should return true if the repo has commits" do | ||
13 | - project.stub(repo_exists?: true, has_commits?: false) | ||
14 | - project.should be_empty_repo | ||
15 | - end | ||
16 | - | ||
17 | - it "should return false if the repo exists and has commits" do | ||
18 | - project.stub(repo_exists?: true, has_commits?: true) | ||
19 | - project.should_not be_empty_repo | ||
20 | - end | ||
21 | - end | ||
22 | - | ||
23 | - describe "#discover_default_branch" do | ||
24 | - let(:master) { 'master' } | ||
25 | - let(:stable) { 'stable' } | ||
26 | - | ||
27 | - it "returns 'master' when master exists" do | ||
28 | - project.should_receive(:branch_names).at_least(:once).and_return([stable, master]) | ||
29 | - project.discover_default_branch.should == 'master' | ||
30 | - end | ||
31 | - | ||
32 | - it "returns non-master when master exists but default branch is set to something else" do | ||
33 | - project.default_branch = 'stable' | ||
34 | - project.should_receive(:branch_names).at_least(:once).and_return([stable, master]) | ||
35 | - project.discover_default_branch.should == 'stable' | ||
36 | - end | ||
37 | - | ||
38 | - it "returns a non-master branch when only one exists" do | ||
39 | - project.should_receive(:branch_names).at_least(:once).and_return([stable]) | ||
40 | - project.discover_default_branch.should == 'stable' | ||
41 | - end | ||
42 | - | ||
43 | - it "returns nil when no branch exists" do | ||
44 | - project.should_receive(:branch_names).at_least(:once).and_return([]) | ||
45 | - project.discover_default_branch.should be_nil | ||
46 | - end | ||
47 | - end | ||
48 | - | ||
49 | - describe "#root_ref" do | ||
50 | - it "returns default_branch when set" do | ||
51 | - project.default_branch = 'stable' | ||
52 | - project.root_ref.should == 'stable' | ||
53 | - end | ||
54 | - | ||
55 | - it "returns 'master' when default_branch is nil" do | ||
56 | - project.default_branch = nil | ||
57 | - project.root_ref.should == 'master' | ||
58 | - end | ||
59 | - end | ||
60 | - | ||
61 | - describe "#root_ref?" do | ||
62 | - it "returns true when branch is root_ref" do | ||
63 | - project.default_branch = 'stable' | ||
64 | - project.root_ref?('stable').should be_true | ||
65 | - end | ||
66 | - | ||
67 | - it "returns false when branch is not root_ref" do | ||
68 | - project.default_branch = nil | ||
69 | - project.root_ref?('stable').should be_false | ||
70 | - end | ||
71 | - end | ||
72 | - | ||
73 | - describe :repo do | ||
74 | - it "should return valid repo" do | ||
75 | - project.repo.should be_kind_of(Grit::Repo) | ||
76 | - end | ||
77 | - | ||
78 | - it "should return nil" do | ||
79 | - lambda { Project.new(path: "invalid").repo }.should raise_error(Grit::NoSuchPathError) | ||
80 | - end | ||
81 | - | ||
82 | - it "should return nil" do | ||
83 | - lambda { Project.new.repo }.should raise_error(TypeError) | ||
84 | - end | ||
85 | - end | ||
86 | - | ||
87 | - describe :commit do | ||
88 | - it "should return first head commit if without params" do | ||
89 | - project.commit.id.should == project.repo.commits.first.id | ||
90 | - end | ||
91 | - | ||
92 | - it "should return valid commit" do | ||
93 | - project.commit(ValidCommit::ID).should be_valid_commit | ||
94 | - end | ||
95 | - | ||
96 | - it "should return nil" do | ||
97 | - project.commit("+123_4532530XYZ").should be_nil | ||
98 | - end | ||
99 | - end | ||
100 | - | ||
101 | - describe :tree do | ||
102 | - before do | ||
103 | - @commit = project.commit(ValidCommit::ID) | ||
104 | - end | ||
105 | - | ||
106 | - it "should raise error w/o arguments" do | ||
107 | - lambda { project.tree }.should raise_error | ||
108 | - end | ||
109 | - | ||
110 | - it "should return root tree for commit" do | ||
111 | - tree = project.tree(@commit) | ||
112 | - tree.contents.size.should == ValidCommit::FILES_COUNT | ||
113 | - tree.contents.map(&:name).should == ValidCommit::FILES | ||
114 | - end | ||
115 | - | ||
116 | - it "should return root tree for commit with correct path" do | ||
117 | - tree = project.tree(@commit, ValidCommit::C_FILE_PATH) | ||
118 | - tree.contents.map(&:name).should == ValidCommit::C_FILES | ||
119 | - end | ||
120 | - | ||
121 | - it "should return root tree for commit with incorrect path" do | ||
122 | - project.tree(@commit, "invalid_path").should be_nil | ||
123 | - end | ||
124 | - end | ||
125 | - | ||
126 | - describe "fresh commits" do | ||
127 | - let(:project) { create(:project) } | ||
128 | - | ||
129 | - it { project.fresh_commits(3).count.should == 3 } | ||
130 | - it { project.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" } | ||
131 | - it { project.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" } | ||
132 | - end | ||
133 | - | ||
134 | - describe "commits_between" do | ||
135 | - let(:project) { create(:project) } | ||
136 | - | ||
137 | - subject do | ||
138 | - commits = project.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff", | ||
139 | - "8470d70da67355c9c009e4401746b1d5410af2e3") | ||
140 | - commits.map { |c| c.id } | ||
141 | - end | ||
142 | - | ||
143 | - it { should have(3).elements } | ||
144 | - it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") } | ||
145 | - it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") } | ||
146 | - end | ||
147 | - | ||
148 | - describe :valid_repo? do | ||
149 | - it "should be valid repo" do | ||
150 | - project = create(:project) | ||
151 | - project.valid_repo?.should be_true | ||
152 | - end | ||
153 | - | ||
154 | - it "should be invalid repo" do | ||
155 | - project = Project.new(name: "ok_name", path: "/INVALID_PATH/", path: "NEOK") | ||
156 | - project.valid_repo?.should be_false | ||
157 | - end | ||
158 | - end | ||
159 | -end |
spec/roles/votes_spec.rb
@@ -1,132 +0,0 @@ | @@ -1,132 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe Issue do | ||
4 | - let(:issue) { create(:issue) } | ||
5 | - | ||
6 | - describe "#upvotes" do | ||
7 | - it "with no notes has a 0/0 score" do | ||
8 | - issue.upvotes.should == 0 | ||
9 | - end | ||
10 | - | ||
11 | - it "should recognize non-+1 notes" do | ||
12 | - issue.notes << create(:note, note: "No +1 here") | ||
13 | - issue.should have(1).note | ||
14 | - issue.notes.first.upvote?.should be_false | ||
15 | - issue.upvotes.should == 0 | ||
16 | - end | ||
17 | - | ||
18 | - it "should recognize a single +1 note" do | ||
19 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
20 | - issue.upvotes.should == 1 | ||
21 | - end | ||
22 | - | ||
23 | - it "should recognize multiple +1 notes" do | ||
24 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
25 | - issue.notes << create(:note, note: "+1 I want this") | ||
26 | - issue.upvotes.should == 2 | ||
27 | - end | ||
28 | - end | ||
29 | - | ||
30 | - describe "#downvotes" do | ||
31 | - it "with no notes has a 0/0 score" do | ||
32 | - issue.downvotes.should == 0 | ||
33 | - end | ||
34 | - | ||
35 | - it "should recognize non--1 notes" do | ||
36 | - issue.notes << create(:note, note: "Almost got a -1") | ||
37 | - issue.should have(1).note | ||
38 | - issue.notes.first.downvote?.should be_false | ||
39 | - issue.downvotes.should == 0 | ||
40 | - end | ||
41 | - | ||
42 | - it "should recognize a single -1 note" do | ||
43 | - issue.notes << create(:note, note: "-1 This is bad") | ||
44 | - issue.downvotes.should == 1 | ||
45 | - end | ||
46 | - | ||
47 | - it "should recognize multiple -1 notes" do | ||
48 | - issue.notes << create(:note, note: "-1 This is bad") | ||
49 | - issue.notes << create(:note, note: "-1 Away with this") | ||
50 | - issue.downvotes.should == 2 | ||
51 | - end | ||
52 | - end | ||
53 | - | ||
54 | - describe "#votes_count" do | ||
55 | - it "with no notes has a 0/0 score" do | ||
56 | - issue.votes_count.should == 0 | ||
57 | - end | ||
58 | - | ||
59 | - it "should recognize non notes" do | ||
60 | - issue.notes << create(:note, note: "No +1 here") | ||
61 | - issue.should have(1).note | ||
62 | - issue.votes_count.should == 0 | ||
63 | - end | ||
64 | - | ||
65 | - it "should recognize a single +1 note" do | ||
66 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
67 | - issue.votes_count.should == 1 | ||
68 | - end | ||
69 | - | ||
70 | - it "should recognize a single -1 note" do | ||
71 | - issue.notes << create(:note, note: "-1 This is bad") | ||
72 | - issue.votes_count.should == 1 | ||
73 | - end | ||
74 | - | ||
75 | - it "should recognize multiple notes" do | ||
76 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
77 | - issue.notes << create(:note, note: "-1 This is bad") | ||
78 | - issue.notes << create(:note, note: "+1 I want this") | ||
79 | - issue.votes_count.should == 3 | ||
80 | - end | ||
81 | - end | ||
82 | - | ||
83 | - describe "#upvotes_in_percent" do | ||
84 | - it "with no notes has a 0% score" do | ||
85 | - issue.upvotes_in_percent.should == 0 | ||
86 | - end | ||
87 | - | ||
88 | - it "should count a single 1 note as 100%" do | ||
89 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
90 | - issue.upvotes_in_percent.should == 100 | ||
91 | - end | ||
92 | - | ||
93 | - it "should count multiple +1 notes as 100%" do | ||
94 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
95 | - issue.notes << create(:note, note: "+1 I want this") | ||
96 | - issue.upvotes_in_percent.should == 100 | ||
97 | - end | ||
98 | - | ||
99 | - it "should count fractions for multiple +1 and -1 notes correctly" do | ||
100 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
101 | - issue.notes << create(:note, note: "+1 I want this") | ||
102 | - issue.notes << create(:note, note: "-1 This is bad") | ||
103 | - issue.notes << create(:note, note: "+1 me too") | ||
104 | - issue.upvotes_in_percent.should == 75 | ||
105 | - end | ||
106 | - end | ||
107 | - | ||
108 | - describe "#downvotes_in_percent" do | ||
109 | - it "with no notes has a 0% score" do | ||
110 | - issue.downvotes_in_percent.should == 0 | ||
111 | - end | ||
112 | - | ||
113 | - it "should count a single -1 note as 100%" do | ||
114 | - issue.notes << create(:note, note: "-1 This is bad") | ||
115 | - issue.downvotes_in_percent.should == 100 | ||
116 | - end | ||
117 | - | ||
118 | - it "should count multiple -1 notes as 100%" do | ||
119 | - issue.notes << create(:note, note: "-1 This is bad") | ||
120 | - issue.notes << create(:note, note: "-1 Away with this") | ||
121 | - issue.downvotes_in_percent.should == 100 | ||
122 | - end | ||
123 | - | ||
124 | - it "should count fractions for multiple +1 and -1 notes correctly" do | ||
125 | - issue.notes << create(:note, note: "+1 This is awesome") | ||
126 | - issue.notes << create(:note, note: "+1 I want this") | ||
127 | - issue.notes << create(:note, note: "-1 This is bad") | ||
128 | - issue.notes << create(:note, note: "+1 me too") | ||
129 | - issue.downvotes_in_percent.should == 25 | ||
130 | - end | ||
131 | - end | ||
132 | -end |
spec/support/stubbed_repository.rb
1 | # Stubs out all Git repository access done by models so that specs can run | 1 | # Stubs out all Git repository access done by models so that specs can run |
2 | # against fake repositories without Grit complaining that they don't exist. | 2 | # against fake repositories without Grit complaining that they don't exist. |
3 | -module StubbedRepository | 3 | +class Project |
4 | def path_to_repo | 4 | def path_to_repo |
5 | if new_record? || path == 'newproject' | 5 | if new_record? || path == 'newproject' |
6 | # There are a couple Project specs and features that expect the Project's | 6 | # There are a couple Project specs and features that expect the Project's |
@@ -27,5 +27,3 @@ module StubbedRepository | @@ -27,5 +27,3 @@ module StubbedRepository | ||
27 | end | 27 | end |
28 | end | 28 | end |
29 | end | 29 | end |
30 | - | ||
31 | -Project.send(:include, StubbedRepository) |