OSDN Git Service

REpostiry, Team models
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Thu, 3 Jan 2013 19:09:18 +0000 (21:09 +0200)
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Thu, 3 Jan 2013 19:09:18 +0000 (21:09 +0200)
31 files changed:
.gitignore
Gemfile
Gemfile.lock
app/contexts/commit_load_context.rb
app/contexts/notes/load_context.rb
app/controllers/application_controller.rb
app/controllers/commits_controller.rb
app/controllers/project_resource_controller.rb
app/controllers/refs_controller.rb
app/controllers/repositories_controller.rb
app/decorators/tree_decorator.rb
app/helpers/application_helper.rb
app/helpers/events_helper.rb
app/models/ability.rb
app/models/commit.rb
app/models/event.rb
app/models/gitlab_ci_service.rb
app/models/note.rb
app/models/project.rb
app/models/repository.rb [new file with mode: 0644]
app/models/team.rb [new file with mode: 0644]
app/models/tree.rb
app/models/users_project.rb
app/views/commits/_head.html.haml
app/views/layouts/project_resource.html.haml
app/views/repositories/_branch.html.haml
app/views/repositories/stats.html.haml
app/views/tree/_tree.html.haml
lib/extracts_path.rb
spec/models/note_spec.rb
spec/models/project_spec.rb

index 94a210b..2429758 100644 (file)
@@ -24,3 +24,4 @@ db/data.yml
 .DS_Store
 .chef
 vendor/bundle/*
+rails_best_practices_output.html
diff --git a/Gemfile b/Gemfile
index 4defcec..6a2d6a8 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -123,6 +123,8 @@ group :development do
   gem 'better_errors'
   gem 'binding_of_caller'
 
+  gem 'rails_best_practices'
+
   # Docs generator
   gem "sdoc"
 end
index 4b08c31..722881e 100644 (file)
@@ -132,6 +132,8 @@ GEM
     chosen-rails (0.9.8)
       railties (~> 3.0)
       thor (~> 0.14)
+    code_analyzer (0.3.1)
+      sexp_processor
     coderay (1.0.8)
     coffee-rails (3.2.2)
       coffee-script (>= 2.2.0)
@@ -302,6 +304,7 @@ GEM
     pg (0.14.1)
     polyglot (0.3.3)
     posix-spawn (0.3.6)
+    progressbar (0.12.0)
     pry (0.9.10)
       coderay (~> 1.0.5)
       method_source (~> 0.8)
@@ -335,6 +338,14 @@ GEM
     rails-dev-tweaks (0.6.1)
       actionpack (~> 3.1)
       railties (~> 3.1)
+    rails_best_practices (1.13.2)
+      activesupport
+      awesome_print
+      code_analyzer
+      colored
+      erubis
+      i18n
+      progressbar
     railties (3.2.9)
       actionpack (= 3.2.9)
       activesupport (= 3.2.9)
@@ -393,6 +404,7 @@ GEM
       multi_json (~> 1.0)
       rubyzip
     settingslogic (2.0.8)
+    sexp_processor (4.1.3)
     shoulda-matchers (1.3.0)
       activesupport (>= 3.0.0)
     simplecov (0.7.1)
@@ -512,6 +524,7 @@ DEPENDENCIES
   rack-mini-profiler
   rails (= 3.2.9)
   rails-dev-tweaks
+  rails_best_practices
   raphael-rails (= 1.5.2)
   rb-fsevent
   rb-inotify
index e43e5a0..a14291b 100644 (file)
@@ -9,7 +9,7 @@ class CommitLoadContext < BaseContext
       status: :ok
     }
 
-    commit = project.commit(params[:id])
+    commit = project.repository.commit(params[:id])
 
     if commit
       commit = CommitDecorator.decorate(commit)
index 9f8299f..05626c3 100644 (file)
@@ -18,7 +18,7 @@ module Notes
                  project.snippets.find(target_id).notes.fresh
                when "wall"
                  # this is the only case, where the order is DESC
-                 project.common_notes.order("created_at DESC, id DESC").limit(50)
+                 project.notes.common.inc_author_project.order("created_at DESC, id DESC").limit(50)
                end
 
       @notes = if after_id
index 75cd8f1..3457a1a 100644 (file)
@@ -76,6 +76,12 @@ class ApplicationController < ActionController::Base
     end
   end
 
+  def repository
+    @repository ||= project.repository
+  rescue Grit::NoSuchPathError
+    nil
+  end
+
   def add_abilities
     abilities << Ability
   end
index 3b8ebdb..534ae1e 100644 (file)
@@ -9,10 +9,10 @@ class CommitsController < ProjectResourceController
   before_filter :require_non_empty_project
 
   def show
-    @repo = @project.repo
+    @repo = @project.repository
     @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
 
-    @commits = @project.commits(@ref, @path, @limit, @offset)
+    @commits = @repo.commits(@ref, @path, @limit, @offset)
     @commits = CommitDecorator.decorate(@commits)
 
     respond_to do |format|
index 81bc3a9..ea78b3f 100644 (file)
@@ -1,3 +1,4 @@
 class ProjectResourceController < ApplicationController
   before_filter :project
+  before_filter :repository
 end
index b48d5ec..e4eb2d3 100644 (file)
@@ -31,7 +31,7 @@ class RefsController < ProjectResourceController
     contents = @tree.contents
     @logs = contents.map do |content|
       file = params[:path] ? File.join(params[:path], content.name) : content.name
-      last_commit = @project.commits(@commit.id, file, 1).last
+      last_commit = @repo.commits(@commit.id, file, 1).last
       last_commit = CommitDecorator.decorate(last_commit)
       {
         file_name: content.name,
@@ -45,10 +45,10 @@ class RefsController < ProjectResourceController
   def define_tree_vars
     params[:path] = nil if params[:path].blank?
 
-    @repo = project.repo
-    @commit = project.commit(@ref)
+    @repo = project.repository
+    @commit = @repo.commit(@ref)
     @commit = CommitDecorator.decorate(@commit)
-    @tree = Tree.new(@commit.tree, project, @ref, params[:path])
+    @tree = Tree.new(@commit.tree, @ref, params[:path])
     @tree = TreeDecorator.new(@tree)
     @hex_path = Digest::SHA1.hexdigest(params[:path] || "")
 
index 7678fbf..229cb36 100644 (file)
@@ -5,19 +5,19 @@ class RepositoriesController < ProjectResourceController
   before_filter :require_non_empty_project
 
   def show
-    @activities = @project.commits_with_refs(20)
+    @activities = @repository.commits_with_refs(20)
   end
 
   def branches
-    @branches = @project.branches
+    @branches = @repository.branches
   end
 
   def tags
-    @tags = @project.tags
+    @tags = @repository.tags
   end
 
   def stats
-    @stats = Gitlab::GitStats.new(@project.repo, @project.root_ref)
+    @stats = Gitlab::GitStats.new(@repository.raw, @repository.root_ref)
     @graph = @stats.graph
   end
 
@@ -27,7 +27,7 @@ class RepositoriesController < ProjectResourceController
     end
 
 
-    file_path = @project.archive_repo(params[:ref])
+    file_path = @repository.archive_repo(params[:ref])
 
     if file_path
       # Send file to user
index c12227a..0e760f9 100644 (file)
@@ -6,16 +6,14 @@ class TreeDecorator < ApplicationDecorator
       part_path = ""
       parts = path.split("\/")
 
-      #parts = parts[0...-1] if is_blob?
-
-      yield(h.link_to("..", "#")) if parts.count > max_links
+      yield('..', nil) if parts.count > max_links
 
       parts.each do |part|
         part_path = File.join(part_path, part) unless part_path.empty?
         part_path = part if part_path.empty?
 
         next unless parts.last(2).include?(part) if parts.count > max_links
-        yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path))))
+        yield(part, h.tree_join(ref, part_path))
       end
     end
   end
@@ -26,7 +24,7 @@ class TreeDecorator < ApplicationDecorator
 
   def up_dir_path
     file = File.join(path, "..")
-    h.project_tree_path(project, h.tree_join(ref, file))
+    h.tree_join(ref, file)
   end
 
   def readme
index 2a33bae..045929f 100644 (file)
@@ -62,9 +62,11 @@ module ApplicationHelper
   end
 
   def grouped_options_refs(destination = :tree)
+    repository = @project.repository
+
     options = [
-      ["Branch", @project.branch_names ],
-      [ "Tag", @project.tag_names ]
+      ["Branch", repository.branch_names ],
+      [ "Tag", repository.tag_names ]
     ]
 
     # If reference is commit id -
@@ -103,12 +105,12 @@ module ApplicationHelper
     if @project && !@project.new_record?
       project_nav = [
         { label: "#{@project.name} Issues",   url: project_issues_path(@project) },
-        { label: "#{@project.name} Commits",  url: project_commits_path(@project, @ref || @project.root_ref) },
+        { label: "#{@project.name} Commits",  url: project_commits_path(@project, @ref || @project.repository.root_ref) },
         { label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) },
         { label: "#{@project.name} Milestones", url: project_milestones_path(@project) },
         { label: "#{@project.name} Snippets", url: project_snippets_path(@project) },
         { label: "#{@project.name} Team",     url: project_team_index_path(@project) },
-        { label: "#{@project.name} Tree",     url: project_tree_path(@project, @ref || @project.root_ref) },
+        { label: "#{@project.name} Tree",     url: project_tree_path(@project, @ref || @project.repository.root_ref) },
         { label: "#{@project.name} Wall",     url: wall_project_path(@project) },
         { label: "#{@project.name} Wiki",     url: project_wikis_path(@project) },
       ]
index a2548a2..2b9e718 100644 (file)
@@ -20,20 +20,6 @@ module EventsHelper
     [event.action_name, target].join(" ")
   end
 
-  def event_image event
-    event_image_path = if event.push?
-                   "event_push.png"
-                 elsif event.merged?
-                   "event_mr_merged.png"
-                 end
-
-    return nil unless event_image_path
-
-    content_tag :div, class: 'event_icon' do
-      image_tag event_image_path
-    end
-  end
-
   def event_filter_link key, tooltip
     key = key.to_s
 
index 75a7163..256af1e 100644 (file)
@@ -15,17 +15,19 @@ class Ability
     def project_abilities(user, project)
       rules = []
 
+      team = project.team
+
       # Rules based on role in project
-      if project.master_access_for?(user)
+      if team.masters.include?(user)
         rules << project_master_rules
 
-      elsif project.dev_access_for?(user)
+      elsif team.developers.include?(user)
         rules << project_dev_rules
 
-      elsif project.report_access_for?(user)
+      elsif team.reporters.include?(user)
         rules << project_report_rules
 
-      elsif project.guest_access_for?(user)
+      elsif team.guests.include?(user)
         rules << project_guest_rules
       end
 
index 07c5fbd..32d942a 100644 (file)
@@ -83,8 +83,8 @@ class Commit
 
       return result unless from && to
 
-      first = project.commit(to.try(:strip))
-      last = project.commit(from.try(:strip))
+      first = project.repository.commit(to.try(:strip))
+      last = project.repository.commit(from.try(:strip))
 
       if first && last
         result[:same] = (first.id == last.id)
index 95075ff..a737bfb 100644 (file)
@@ -110,26 +110,6 @@ class Event < ActiveRecord::Base
     target_type == "MergeRequest"
   end
 
-  def new_issue?
-    target_type == "Issue" &&
-      action == Created
-  end
-
-  def new_merge_request?
-    target_type == "MergeRequest" &&
-      action == Created
-  end
-
-  def changed_merge_request?
-    target_type == "MergeRequest" &&
-      [Closed, Reopened].include?(action)
-  end
-
-  def changed_issue?
-    target_type == "Issue" &&
-      [Closed, Reopened].include?(action)
-  end
-
   def joined?
     action == Joined
   end
index 0b0b65e..4eb39c7 100644 (file)
@@ -29,10 +29,6 @@ class GitlabCiService < Service
     hook.save
   end
 
-  def commit_badge_path sha
-    project_url + "/status?sha=#{sha}"
-  end
-
   def commit_status_path sha
     project_url + "/builds/#{sha}/status.json?token=#{token}"
   end
index 28b3879..100f72b 100644 (file)
@@ -4,7 +4,6 @@
 #
 #  id            :integer          not null, primary key
 #  note          :text
-#  noteable_id   :string(255)
 #  noteable_type :string(255)
 #  author_id     :integer
 #  created_at    :datetime         not null
@@ -12,6 +11,8 @@
 #  project_id    :integer
 #  attachment    :string(255)
 #  line_code     :string(255)
+#  commit_id     :string(255)
+#  noteable_id   :integer
 #
 
 require 'carrierwave/orm/activerecord'
@@ -42,7 +43,7 @@ class Note < ActiveRecord::Base
 
   # Scopes
   scope :for_commits, ->{ where(noteable_type: "Commit") }
-  scope :common, ->{ where(noteable_id: nil, commit_id: nil) }
+  scope :common, ->{ where(noteable_type: ["", nil]) }
   scope :today, ->{ where("created_at >= :date", date: Date.today) }
   scope :last_week, ->{ where("created_at  >= :date", date: (Date.today - 7.days)) }
   scope :since, ->(day) { where("created_at  >= :date", date: (day)) }
index f60c244..f2ad390 100644 (file)
@@ -9,7 +9,7 @@
 #  created_at             :datetime         not null
 #  updated_at             :datetime         not null
 #  private_flag           :boolean          default(TRUE), not null
-#  owner_id               :integer
+#  creator_id             :integer
 #  default_branch         :string(255)
 #  issues_enabled         :boolean          default(TRUE), not null
 #  wall_enabled           :boolean          default(TRUE), not null
@@ -75,7 +75,6 @@ class Project < ActiveRecord::Base
   validate :check_limit, :repo_name
 
   # Scopes
-  scope :public_only, where(private_flag: false)
   scope :without_user, ->(user)  { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
   scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
   scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
@@ -162,6 +161,14 @@ class Project < ActiveRecord::Base
     end
   end
 
+  def team
+    @team ||= Team.new(self)
+  end
+
+  def repository
+    @repository ||= Repository.new(path_with_namespace, default_branch)
+  end
+
   def git_error?
     error_code == :gitolite
   end
@@ -198,10 +205,6 @@ class Project < ActiveRecord::Base
     [Gitlab.config.gitlab.url, path_with_namespace].join("/")
   end
 
-  def common_notes
-    notes.where(noteable_type: ["", nil]).inc_author_project
-  end
-
   def build_commit_note(commit)
     notes.new(commit_id: commit.id, noteable_type: "Commit")
   end
@@ -214,14 +217,6 @@ class Project < ActiveRecord::Base
     notes.where(commit_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
   end
 
-  def public?
-    !private_flag
-  end
-
-  def private?
-    private_flag
-  end
-
   def last_activity
     last_event
   end
@@ -284,33 +279,6 @@ class Project < ActiveRecord::Base
     users_projects.find_by_user_id(user_id)
   end
 
-  # Add user to project
-  # with passed access role
-  def add_user_to_team(user, access_role)
-    add_user_id_to_team(user.id, access_role)
-  end
-
-  # Add multiple users to project
-  # with same access role
-  def add_users_to_team(users, access_role)
-    add_users_ids_to_team(users.map(&:id), access_role)
-  end
-
-  # Add user to project
-  # with passed access role by user id
-  def add_user_id_to_team(user_id, access_role)
-    users_projects.create(
-      user_id: user_id,
-      project_access: access_role
-    )
-  end
-
-  # Add multiple users to project
-  # with same access role by user ids
-  def add_users_ids_to_team(users_ids, access_role)
-    UsersProject.bulk_import(self, users_ids, access_role)
-  end
-
   # Update multiple project users
   # to same access role by user ids
   def update_users_ids_to_role(users_ids, access_role)
@@ -322,30 +290,6 @@ class Project < ActiveRecord::Base
     UsersProject.bulk_delete(self, users_ids)
   end
 
-  # Remove all users from project team
-  def truncate_team
-    UsersProject.truncate_team(self)
-  end
-
-  # Compatible with all access rights
-  # Should be rewrited for new access rights
-  def add_access(user, *access)
-    access = if access.include?(:admin)
-               { project_access: UsersProject::MASTER }
-             elsif access.include?(:write)
-               { project_access: UsersProject::DEVELOPER }
-             else
-               { project_access: UsersProject::REPORTER }
-             end
-    opts = { user: user }
-    opts.merge!(access)
-    users_projects.create(opts)
-  end
-
-  def reset_access(user)
-    users_projects.where(project_id: self.id, user_id: user.id).destroy if self.id
-  end
-
   def repository_readers
     repository_members[UsersProject::REPORTER]
   end
@@ -368,26 +312,6 @@ class Project < ActiveRecord::Base
     keys
   end
 
-  def allow_read_for?(user)
-    !users_projects.where(user_id: user.id).empty?
-  end
-
-  def guest_access_for?(user)
-    !users_projects.where(user_id: user.id).empty?
-  end
-
-  def report_access_for?(user)
-    !users_projects.where(user_id: user.id, project_access: [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
-  end
-
-  def dev_access_for?(user)
-    !users_projects.where(user_id: user.id, project_access: [UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
-  end
-
-  def master_access_for?(user)
-    !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty?
-  end
-
   def transfer(new_namespace)
     Project.transaction do
       old_namespace = namespace
@@ -586,97 +510,21 @@ class Project < ActiveRecord::Base
   end
 
   def empty_repo?
-    !repo_exists? || !has_commits?
-  end
-
-  def commit(commit_id = nil)
-    Commit.find_or_first(repo, commit_id, root_ref)
-  end
-
-  def fresh_commits(n = 10)
-    Commit.fresh_commits(repo, n)
-  end
-
-  def commits_with_refs(n = 20)
-    Commit.commits_with_refs(repo, n)
-  end
-
-  def commits_since(date)
-    Commit.commits_since(repo, date)
-  end
-
-  def commits(ref, path = nil, limit = nil, offset = nil)
-    Commit.commits(repo, ref, path, limit, offset)
-  end
-
-  def last_commit_for(ref, path = nil)
-    commits(ref, path, 1).first
-  end
-
-  def commits_between(from, to)
-    Commit.commits_between(repo, from, to)
+    !repository || repository.empty_repo?
   end
 
   def satellite
     @satellite ||= Gitlab::Satellite::Satellite.new(self)
   end
 
-  def has_post_receive_file?
-    !!hook_file
-  end
-
-  def valid_post_receive_file?
-    valid_hook_file == hook_file
-  end
-
-  def valid_hook_file
-    @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive'))
-  end
-
-  def hook_file
-    @hook_file ||= begin
-                     hook_path = File.join(path_to_repo, 'hooks', 'post-receive')
-                     File.read(hook_path) if File.exists?(hook_path)
-                   end
-  end
-
-  # Returns an Array of branch names
-  def branch_names
-    repo.branches.collect(&:name).sort
-  end
-
-  # Returns an Array of Branches
-  def branches
-    repo.branches.sort_by(&:name)
-  end
-
-  # Returns an Array of tag names
-  def tag_names
-    repo.tags.collect(&:name).sort.reverse
-  end
-
-  # Returns an Array of Tags
-  def tags
-    repo.tags.sort_by(&:name).reverse
-  end
-
-  # Returns an Array of branch and tag names
-  def ref_names
-    [branch_names + tag_names].flatten
-  end
-
   def repo
-    @repo ||= Grit::Repo.new(path_to_repo)
+    repository.raw
   end
 
   def url_to_repo
     gitolite.url_to_repo(path_with_namespace)
   end
 
-  def path_to_repo
-    File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git")
-  end
-
   def namespace_dir
     namespace.try(:path) || ''
   end
@@ -690,21 +538,11 @@ class Project < ActiveRecord::Base
   end
 
   def repo_exists?
-    @repo_exists ||= (repo && !repo.branches.empty?)
+    @repo_exists ||= (repository && repository.branches.present?)
   rescue
     @repo_exists = false
   end
 
-  def heads
-    @heads ||= repo.heads
-  end
-
-  def tree(fcommit, path = nil)
-    fcommit = commit if fcommit == :head
-    tree = fcommit.tree
-    path ? (tree / path) : tree
-  end
-
   def open_branches
     if protected_branches.empty?
       self.repo.heads
@@ -714,61 +552,8 @@ class Project < ActiveRecord::Base
     end.sort_by(&:name)
   end
 
-  # Discovers the default branch based on the repository's available branches
-  #
-  # - If no branches are present, returns nil
-  # - If one branch is present, returns its name
-  # - If two or more branches are present, returns the one that has a name
-  #   matching root_ref (default_branch or 'master' if default_branch is nil)
-  def discover_default_branch
-    if branch_names.length == 0
-      nil
-    elsif branch_names.length == 1
-      branch_names.first
-    else
-      branch_names.select { |v| v == root_ref }.first
-    end
-  end
-
-  def has_commits?
-    !!commit
-  rescue Grit::NoSuchPathError
-    false
-  end
-
-  def root_ref
-    default_branch || "master"
-  end
-
   def root_ref?(branch)
-    root_ref == branch
-  end
-
-  # Archive Project to .tar.gz
-  #
-  # Already packed repo archives stored at
-  # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
-  #
-  def archive_repo(ref)
-    ref = ref || self.root_ref
-    commit = self.commit(ref)
-    return nil unless commit
-
-    # Build file path
-    file_name = self.path + "-" + commit.id.to_s + ".tar.gz"
-    storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace)
-    file_path = File.join(storage_path, file_name)
-
-    # Put files into a directory before archiving
-    prefix = self.path + "/"
-
-    # Create file if not exists
-    unless File.exists?(file_path)
-      FileUtils.mkdir_p storage_path
-      file = self.repo.archive_to_file(ref, prefix,  file_path)
-    end
-
-    file_path
+    repository.root_ref == branch
   end
 
   def ssh_url_to_repo
diff --git a/app/models/repository.rb b/app/models/repository.rb
new file mode 100644 (file)
index 0000000..a0351ce
--- /dev/null
@@ -0,0 +1,163 @@
+class Repository
+  # Repository directory name with namespace direcotry
+  # Examples:
+  #   gitlab/gitolite
+  #   diaspora
+  #
+  attr_accessor :path_with_namespace
+
+  # Grit repo object
+  attr_accessor :repo
+
+  # Default branch in the repository
+  attr_accessor :root_ref
+
+  def initialize(path_with_namespace, root_ref = 'master')
+    @root_ref = root_ref
+    @path_with_namespace = path_with_namespace
+    @repo = Grit::Repo.new(path_to_repo)
+  end
+
+  def raw
+    repo
+  end
+
+  def path_to_repo
+    @path_to_repo ||= File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git")
+  end
+
+  def commit(commit_id = nil)
+    Commit.find_or_first(repo, commit_id, root_ref)
+  end
+
+  def fresh_commits(n = 10)
+    Commit.fresh_commits(repo, n)
+  end
+
+  def commits_with_refs(n = 20)
+    Commit.commits_with_refs(repo, n)
+  end
+
+  def commits_since(date)
+    Commit.commits_since(repo, date)
+  end
+
+  def commits(ref, path = nil, limit = nil, offset = nil)
+    Commit.commits(repo, ref, path, limit, offset)
+  end
+
+  def last_commit_for(ref, path = nil)
+    commits(ref, path, 1).first
+  end
+
+  def commits_between(from, to)
+    Commit.commits_between(repo, from, to)
+  end
+
+  def has_post_receive_file?
+    !!hook_file
+  end
+
+  def valid_post_receive_file?
+    valid_hook_file == hook_file
+  end
+
+  def valid_hook_file
+    @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive'))
+  end
+
+  def hook_file
+    @hook_file ||= begin
+                     hook_path = File.join(path_to_repo, 'hooks', 'post-receive')
+                     File.read(hook_path) if File.exists?(hook_path)
+                   end
+  end
+
+  # Returns an Array of branch names
+  def branch_names
+    repo.branches.collect(&:name).sort
+  end
+
+  # Returns an Array of Branches
+  def branches
+    repo.branches.sort_by(&:name)
+  end
+
+  # Returns an Array of tag names
+  def tag_names
+    repo.tags.collect(&:name).sort.reverse
+  end
+
+  # Returns an Array of Tags
+  def tags
+    repo.tags.sort_by(&:name).reverse
+  end
+
+  # Returns an Array of branch and tag names
+  def ref_names
+    [branch_names + tag_names].flatten
+  end
+
+  def heads
+    @heads ||= repo.heads
+  end
+
+  def tree(fcommit, path = nil)
+    fcommit = commit if fcommit == :head
+    tree = fcommit.tree
+    path ? (tree / path) : tree
+  end
+
+  def has_commits?
+    !!commit
+  rescue Grit::NoSuchPathError
+    false
+  end
+
+  def empty_repo?
+    !has_commits?
+  end
+
+  # Discovers the default branch based on the repository's available branches
+  #
+  # - If no branches are present, returns nil
+  # - If one branch is present, returns its name
+  # - If two or more branches are present, returns the one that has a name
+  #   matching root_ref (default_branch or 'master' if default_branch is nil)
+  def discover_default_branch
+    if branch_names.length == 0
+      nil
+    elsif branch_names.length == 1
+      branch_names.first
+    else
+      branch_names.select { |v| v == root_ref }.first
+    end
+  end
+
+  # Archive Project to .tar.gz
+  #
+  # Already packed repo archives stored at
+  # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
+  #
+  def archive_repo(ref)
+    ref = ref || self.root_ref
+    commit = self.commit(ref)
+    return nil unless commit
+
+    # Build file path
+    file_name = self.path + "-" + commit.id.to_s + ".tar.gz"
+    storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace)
+    file_path = File.join(storage_path, file_name)
+
+    # Put files into a directory before archiving
+    prefix = self.path + "/"
+
+    # Create file if not exists
+    unless File.exists?(file_path)
+      FileUtils.mkdir_p storage_path
+      file = self.repo.archive_to_file(ref, prefix,  file_path)
+    end
+
+    file_path
+  end
+end
diff --git a/app/models/team.rb b/app/models/team.rb
new file mode 100644 (file)
index 0000000..894361d
--- /dev/null
@@ -0,0 +1,49 @@
+class Team
+  attr_accessor :project
+
+  def initialize(project)
+    @project = project
+    @roles = UsersProject.roles_hash
+  end
+
+  def add_user(user, access)
+    add_users_ids([user.id], access)
+  end
+
+  def add_users(users, access)
+    add_users_ids(users.map(&:id), access)
+  end
+
+  def add_users_ids(users_ids, access)
+    UsersProject.add_users_into_projects(
+      [project.id],
+      user_ids,
+      access
+    )
+  end
+
+  # Remove all users from project team
+  def truncate
+    UsersProject.truncate_team(project)
+  end
+
+  def members
+    project.users_projects
+  end
+
+  def guests
+    members.guests.map(&:user)
+  end
+
+  def reporters
+    members.reporters.map(&:user)
+  end
+
+  def developers
+    members.developers.map(&:user)
+  end
+
+  def masters
+    members.masters.map(&:user)
+  end
+end
index c3dfd4c..96395a4 100644 (file)
@@ -1,12 +1,13 @@
 class Tree
   include Linguist::BlobHelper
-  attr_accessor :path, :tree, :project, :ref
+
+  attr_accessor :path, :tree, :ref
 
   delegate  :contents, :basename, :name, :data, :mime_type,
             :mode, :size, :text?, :colorize, to: :tree
 
-  def initialize(raw_tree, project, ref = nil, path = nil)
-    @project, @ref, @path = project, ref, path
+  def initialize(raw_tree, ref = nil, path = nil)
+    @ref, @path = ref, path
     @tree = if path.present?
               raw_tree / path
             else
index f8e0078..a8e1467 100644 (file)
@@ -42,7 +42,21 @@ class UsersProject < ActiveRecord::Base
   scope :in_project, ->(project) { where(project_id: project.id) }
 
   class << self
-    def add_users_into_projects(project_ids, user_ids, project_access)
+
+    # Add users to project teams with passed access option
+    #
+    # access can be an integer representing a access code
+    # or symbol like :master representing role
+    #
+    def add_users_into_projects(project_ids, user_ids, access)
+      project_access = if @roles.has_key?(access)
+                         @roles[access]
+                       elsif @roles.values.include?(access)
+                         access
+                       else
+                         raise "Non valid access"
+                       end
+
       UsersProject.transaction do
         project_ids.each do |project_id|
           user_ids.each do |user_id|
@@ -141,6 +155,15 @@ class UsersProject < ActiveRecord::Base
       add_users_into_projects(project_ids, [user.id], project_access)
     end
 
+    def roles_hash
+      {
+        guest: GUEST,
+        reporter: REPORTER,
+        developer: DEVELOPER,
+        master: MASTER
+      }
+    end
+
     def access_roles
       {
         "Guest"     => GUEST,
index 2ec1d24..a5f3fdf 100644 (file)
@@ -2,19 +2,19 @@
   %li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
 
   = nav_link(controller: [:commit, :commits]) do
-    = link_to 'Commits', project_commits_path(@project, @project.root_ref)
+    = link_to 'Commits', project_commits_path(@project, @repository.root_ref)
   = nav_link(controller: :compare) do
     = link_to 'Compare', project_compare_index_path(@project)
 
   = nav_link(html_options: {class: branches_tab_class}) do
     = link_to project_repository_path(@project) do
       Branches
-      %span.badge= @project.branches.length
+      %span.badge= @repository.branches.length
 
   = nav_link(controller: :repositories, action: :tags) do
     = link_to tags_project_repository_path(@project) do
       Tags
-      %span.badge= @project.tags.length
+      %span.badge= @repository.tags.length
 
   = nav_link(controller: :repositories, action: :stats) do
     = link_to stats_project_repository_path(@project) do
index 7098074..e00f96a 100644 (file)
@@ -14,9 +14,9 @@
         - if @project.repo_exists?
           - if can? current_user, :download_code, @project
             = nav_link(controller: %w(tree blob blame)) do
-              = link_to 'Files', project_tree_path(@project, @ref || @project.root_ref)
+              = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
             = nav_link(controller: %w(commit commits compare repositories protected_branches)) do
-              = link_to "Commits", project_commits_path(@project, @ref || @project.root_ref)
+              = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref)
             = nav_link(path: 'projects#graph') do
               = link_to "Network", graph_project_path(@project)
 
index 2728b10..74f7426 100644 (file)
@@ -8,7 +8,7 @@
       - else
         %i.icon-unlock
       %strong= truncate(branch.name, length: 60)
-      - if branch.name == @project.root_ref
+      - if branch.name == @repository.root_ref
         %span.label default
   %td
     = link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do
index a93814a..bdf047f 100644 (file)
@@ -7,7 +7,7 @@
       %b Total commits:
       %span= @stats.commits_count
     %p
-      %b Total files in #{@project.root_ref}:
+      %b Total files in #{@repository.root_ref}:
       %span= @stats.files_count
     %p
       %b Authors:
index a632bb3..c284295 100644 (file)
@@ -3,9 +3,13 @@
     %span.arrow
     = link_to project_tree_path(@project, @ref) do
       = @project.name
-  - tree.breadcrumbs(6) do |link|
+  - tree.breadcrumbs(6) do |title, path|
     \/
-    %li= link
+    %li
+      - if path
+        = link_to truncate(title, length: 40), project_tree_path(@project, path)
+      - else
+        = link_to title, '#'
 
 .clear
 %div.tree_progress
@@ -26,7 +30,7 @@
         %tr.tree-item
           %td.tree-item-file-name
             = image_tag "file_empty.png", size: '16x16'
-            = link_to "..", tree.up_dir_path
+            = link_to "..", project_tree_path(@project, tree.up_dir_path)
           %td
           %td
           %td
index 0b7a0d4..5c96eac 100644 (file)
@@ -68,7 +68,7 @@ module ExtractsPath
       id = input
       id += '/' unless id.ends_with?('/')
 
-      valid_refs = @project.ref_names
+      valid_refs = @project.repository.ref_names
       valid_refs.select! { |v| id.start_with?("#{v}/") }
 
       if valid_refs.length != 1
@@ -114,9 +114,9 @@ module ExtractsPath
 
     @id = File.join(@ref, @path)
 
-    @commit = CommitDecorator.decorate(@project.commit(@ref))
+    @commit = CommitDecorator.decorate(@project.repository.commit(@ref))
 
-    @tree = Tree.new(@commit.tree, @project, @ref, @path)
+    @tree = Tree.new(@commit.tree, @ref, @path)
     @tree = TreeDecorator.new(@tree)
 
     raise InvalidPathError if @tree.invalid?
index 61aaf64..d1df1c3 100644 (file)
@@ -4,7 +4,6 @@
 #
 #  id            :integer          not null, primary key
 #  note          :text
-#  noteable_id   :string(255)
 #  noteable_type :string(255)
 #  author_id     :integer
 #  created_at    :datetime         not null
@@ -12,6 +11,8 @@
 #  project_id    :integer
 #  attachment    :string(255)
 #  line_code     :string(255)
+#  commit_id     :string(255)
+#  noteable_id   :integer
 #
 
 require 'spec_helper'
index ea1efbb..e09797a 100644 (file)
@@ -9,7 +9,7 @@
 #  created_at             :datetime         not null
 #  updated_at             :datetime         not null
 #  private_flag           :boolean          default(TRUE), not null
-#  owner_id               :integer
+#  creator_id             :integer
 #  default_branch         :string(255)
 #  issues_enabled         :boolean          default(TRUE), not null
 #  wall_enabled           :boolean          default(TRUE), not null