Commit a1999955eb0b212c818a383bc54f8392194d0bc1

Authored by Dmitriy Zaporozhets
2 parents 1b25a8f4 da03a5c7

Merge pull request #2461 from gitlabhq/remove_roles

Remove models/roles and return to fat models
app/models/concerns/issuable.rb 0 → 100644
... ... @@ -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 15 #
16 16  
17 17 class Event < ActiveRecord::Base
18   - include NoteEvent
19   - include PushEvent
20   -
21 18 attr_accessible :project, :action, :data, :author_id, :project_id,
22 19 :target_id, :target_type
23 20  
... ... @@ -170,4 +167,139 @@ class Event &lt; ActiveRecord::Base
170 167 "opened"
171 168 end
172 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 305 end
... ...
app/models/issue.rb
... ... @@ -17,8 +17,7 @@
17 17 #
18 18  
19 19 class Issue < ActiveRecord::Base
20   - include IssueCommonality
21   - include Votes
  20 + include Issuable
22 21  
23 22 attr_accessible :title, :assignee_id, :closed, :position, :description,
24 23 :milestone_id, :label_list, :author_id_of_changes
... ...
app/models/merge_request.rb
... ... @@ -20,11 +20,10 @@
20 20 #
21 21  
22 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 25 class MergeRequest < ActiveRecord::Base
26   - include IssueCommonality
27   - include Votes
  26 + include Issuable
28 27  
29 28 attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id,
30 29 :author_id_of_changes
... ...
app/models/project.rb
... ... @@ -21,11 +21,7 @@
21 21 require "grit"
22 22  
23 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 26 class TransferError < StandardError; end
31 27  
... ... @@ -277,4 +273,514 @@ class Project &lt; ActiveRecord::Base
277 273 creator
278 274 end
279 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 786 end
... ...
app/models/protected_branch.rb
... ... @@ -10,7 +10,7 @@
10 10 #
11 11  
12 12 class ProtectedBranch < ActiveRecord::Base
13   - include GitHost
  13 + include Gitolited
14 14  
15 15 attr_accessible :name
16 16  
... ... @@ -22,7 +22,7 @@ class ProtectedBranch &lt; ActiveRecord::Base
22 22 after_destroy :update_repository
23 23  
24 24 def update_repository
25   - git_host.update_repository(project)
  25 + gitolite.update_repository(project)
26 26 end
27 27  
28 28 def commit
... ...
app/models/user.rb
... ... @@ -34,8 +34,6 @@
34 34 #
35 35  
36 36 class User < ActiveRecord::Base
37   - include Account
38   -
39 37 devise :database_authenticatable, :token_authenticatable, :lockable,
40 38 :recoverable, :rememberable, :trackable, :validatable, :omniauthable
41 39  
... ... @@ -192,4 +190,92 @@ class User &lt; ActiveRecord::Base
192 190 def tm_in_personal_projects
193 191 personal_projects.users_projects.where(user_id: self.id)
194 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 281 end
... ...
app/models/users_project.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12  
13 13 class UsersProject < ActiveRecord::Base
14   - include GitHost
  14 + include Gitolited
15 15  
16 16 GUEST = 10
17 17 REPORTER = 20
... ... @@ -152,7 +152,7 @@ class UsersProject &lt; ActiveRecord::Base
152 152 end
153 153  
154 154 def update_repository
155   - git_host.update_repository(project)
  155 + gitolite.update_repository(project)
156 156 end
157 157  
158 158 def project_access_human
... ...
app/observers/key_observer.rb
1 1 class KeyObserver < ActiveRecord::Observer
2   - include GitHost
  2 + include Gitolited
3 3  
4 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 6 end
7 7  
8 8 def after_destroy(key)
9 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 11 end
12 12 end
... ...
app/roles/account.rb
... ... @@ -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   -# == 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
... ... @@ -1,11 +0,0 @@
1   -# == GitHost role
2   -#
3   -# Provide a shortcut to Gitlab::Gitolite instance
4   -#
5   -# Used by Project, UsersProject
6   -#
7   -module GitHost
8   - def git_host
9   - Gitlab::Gitolite.new
10   - end
11   -end
app/roles/issue_commonality.rb
... ... @@ -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   -# == 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   -# == 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   -# == 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   -# == 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   -# == 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   -# 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   -# == 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   -# == 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
lib/gitolited.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +# == Gitolited mixin
  2 +#
  3 +# Provide a shortcut to Gitlab::Gitolite instance by gitolite
  4 +#
  5 +# Used by Project, UsersProject, etc
  6 +#
  7 +module Gitolited
  8 + def gitolite
  9 + Gitlab::Gitolite.new
  10 + end
  11 +end
... ...
lib/static_model.rb 0 → 100644
... ... @@ -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
... ...
spec/lib/issue_commonality_spec.rb 0 → 100644
... ... @@ -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
... ...
spec/lib/votes_spec.rb 0 → 100644
... ... @@ -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
... ...
spec/models/project_repository_spec.rb 0 → 100644
... ... @@ -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 133 it { should respond_to(:path_with_namespace) }
134 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 136 it "should return valid url to repo" do
145 137 project = Project.new(path: "somewhere")
146 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 65 it { should ensure_length_of(:bio).is_within(0..255) }
66 66 end
67 67  
68   - describe 'modules' do
69   - it { should include_module(Account) }
70   - end
71   -
72 68 describe "Respond to" do
73 69 it { should respond_to(:is_admin?) }
74 70 it { should respond_to(:identifier) }
... ... @@ -185,4 +181,14 @@ describe User do
185 181  
186 182 it { User.not_in_project(@project).should == [@user, @project.owner] }
187 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 194 end
... ...
spec/roles/account_role_spec.rb
... ... @@ -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   -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   -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   -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 1 # Stubs out all Git repository access done by models so that specs can run
2 2 # against fake repositories without Grit complaining that they don't exist.
3   -module StubbedRepository
  3 +class Project
4 4 def path_to_repo
5 5 if new_record? || path == 'newproject'
6 6 # There are a couple Project specs and features that expect the Project's
... ... @@ -27,5 +27,3 @@ module StubbedRepository
27 27 end
28 28 end
29 29 end
30   -
31   -Project.send(:include, StubbedRepository)
... ...