ginatra 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -15
  3. data/.travis.yml +7 -6
  4. data/CONTRIBUTING.md +30 -0
  5. data/Gemfile +1 -9
  6. data/LICENSE.txt +30 -0
  7. data/README.md +63 -114
  8. data/Rakefile +10 -12
  9. data/bin/ginatra +79 -63
  10. data/config.ru +35 -3
  11. data/ginatra.gemspec +29 -18
  12. data/lib/ginatra.rb +161 -148
  13. data/lib/ginatra/config.rb +18 -121
  14. data/lib/ginatra/errors.rb +10 -0
  15. data/lib/ginatra/helpers.rb +154 -139
  16. data/lib/ginatra/repo.rb +67 -82
  17. data/lib/ginatra/repo_list.rb +25 -18
  18. data/lib/ginatra/repo_stats.rb +93 -0
  19. data/lib/ginatra/version.rb +4 -0
  20. data/lib/git/webby.rb +292 -0
  21. data/lib/git/webby/extensions.rb +10 -0
  22. data/lib/git/webby/http_backend.rb +177 -0
  23. data/lib/sinatra/partials.rb +1 -1
  24. data/public/css/application.css +6 -0
  25. data/public/css/custom.css +57 -0
  26. data/public/css/lib/bootstrap-responsive.min.css +9 -0
  27. data/public/css/lib/bootstrap.min.css +9 -0
  28. data/public/css/lib/highlight.css +209 -0
  29. data/public/img/glyphicons-halflings-white.png +0 -0
  30. data/public/img/glyphicons-halflings.png +0 -0
  31. data/public/img/spin.gif +0 -0
  32. data/public/js/application.js +5 -0
  33. data/public/js/custom.js +51 -0
  34. data/public/js/lib/bootstrap.min.js +6 -0
  35. data/public/js/lib/jquery.lazyload.min.js +2 -0
  36. data/public/js/lib/jquery.min.js +2 -0
  37. data/public/js/lib/jquery.pjax.js +739 -0
  38. data/repos/README.md +21 -8
  39. data/spec/ginatra/helpers_spec.rb +95 -0
  40. data/spec/ginatra/repo_list_spec.rb +66 -0
  41. data/spec/ginatra/repo_spec.rb +78 -0
  42. data/spec/ginatra/repo_stats_spec.rb +27 -0
  43. data/spec/ginatra_spec.rb +121 -0
  44. data/spec/spec_helper.rb +8 -17
  45. data/views/404.erb +18 -0
  46. data/views/500.erb +18 -0
  47. data/views/_footer.erb +7 -0
  48. data/views/_header.erb +12 -6
  49. data/views/_tree_nav.erb +53 -0
  50. data/views/atom.erb +32 -0
  51. data/views/blob.erb +27 -8
  52. data/views/commit.erb +95 -17
  53. data/views/empty_repo.erb +10 -0
  54. data/views/index.erb +27 -11
  55. data/views/layout.erb +16 -20
  56. data/views/log.erb +74 -54
  57. data/views/stats.erb +89 -0
  58. data/views/tree.erb +32 -20
  59. metadata +168 -94
  60. data/bin/ginatra-daemon +0 -87
  61. data/bin/ginatra-directory +0 -55
  62. data/bin/ginatra-server +0 -27
  63. data/bin/ginatra-setup +0 -28
  64. data/lib/ginatra/graph_commit.rb +0 -77
  65. data/public/img/add.png +0 -0
  66. data/public/img/diff.png +0 -0
  67. data/public/img/doc.png +0 -0
  68. data/public/img/rm.png +0 -0
  69. data/public/img/tree.png +0 -0
  70. data/public/src/branch-graph.js +0 -170
  71. data/public/src/colour.css +0 -86
  72. data/public/src/commit.css +0 -211
  73. data/public/src/ginatra.js +0 -7
  74. data/public/src/github.css +0 -129
  75. data/public/src/graph.css +0 -9
  76. data/public/src/highlight.pack.js +0 -1
  77. data/public/src/index.css +0 -92
  78. data/public/src/lists.css +0 -25
  79. data/public/src/raphael.js +0 -7
  80. data/public/src/reset.css +0 -49
  81. data/public/src/table.css +0 -33
  82. data/public/src/type.css +0 -30
  83. data/rackup.ru +0 -5
  84. data/spec/graph_commit_spec.rb +0 -54
  85. data/spec/repo_list_spec.rb +0 -84
  86. data/spec/repo_spec.rb +0 -61
  87. data/views/_actor_box.erb +0 -13
  88. data/views/_commit_info_box.erb +0 -27
  89. data/views/_tree_part.erb +0 -11
  90. data/views/atom.builder +0 -32
  91. data/views/graph.erb +0 -15
data/lib/ginatra/repo.rb CHANGED
@@ -1,113 +1,103 @@
1
- require "grit"
2
-
3
- module Grit
4
- class Commit
5
- # this lets us add a link between commits and refs directly
6
- attr_accessor :refs
7
-
8
- def ==(other_commit)
9
- id == other_commit.id
10
- end
11
- end
12
- end
1
+ require 'rugged'
13
2
 
14
3
  module Ginatra
15
-
16
- # A thin wrapper to the Grit::repo class so that we can add a name and a url-sanitised-name
17
- # to a repo, and also intercept and add refs to the commit objects.
18
4
  class Repo
19
-
20
5
  attr_reader :name, :param, :description
21
6
 
22
7
  # Create a new repository, and sort out clever stuff including assigning
23
8
  # the param, the name and the description.
24
9
  #
25
- # @todo cleanup!
26
- #
27
10
  # @param [String] path a path to the repository you want created
28
11
  # @return [Ginatra::Repo] a repository instance
29
12
  def initialize(path)
30
- @repo = Grit::Repo.new(path)
13
+ @repo = Rugged::Repository.new(path)
31
14
  @param = File.split(path).last
32
15
  @name = @param
33
- @description = @repo.description
34
- @description = "Please edit the #{@repo.path}/description file for this repository and set the description for it." if /^Unnamed repository;/.match(@description)
16
+ @description = File.read("#{@repo.path}description").strip
17
+ @description = '' if @description.match(/\AUnnamed repository;/)
35
18
  end
36
19
 
37
- def ==(other_repo)
38
- # uses method_missing
39
- path == other_repo.path
20
+ # Return a commit corresponding to sha in the repo.
21
+ #
22
+ # @param [String] sha the commit id or tag name
23
+ # @return [Rugged::Commit] the commit object
24
+ def commit(sha)
25
+ @repo.lookup(sha)
40
26
  end
41
27
 
42
- # Return a commit corresponding to the commit to the repo,
43
- # but with the refs already attached.
44
- #
45
- # @see Ginatra::Repo#add_refs
46
- #
47
- # @raise [Ginatra::InvalidCommit] if the commit doesn't exist.
48
- #
49
- # @param [String] id the commit id
50
- # @return [Grit::Commit] the commit object.
51
- def commit(id)
52
- @commit = @repo.commit(id)
53
- raise(Ginatra::InvalidCommit.new(id)) if @commit.nil?
54
- add_refs(@commit,{})
55
- @commit
28
+ # Return a commit corresponding to tag in the repo.
29
+ def commit_by_tag(name)
30
+ target = @repo.ref("refs/tags/#{name}").target
31
+
32
+ if target.is_a? Rugged::Tag::Annotation
33
+ target = target.target
34
+ end
35
+
36
+ target
56
37
  end
57
38
 
58
- # Return a list of commits to a certain branch, including pagination options and all the refs.
39
+ # Return a list of commits in a certain branch, including pagination options and all the refs.
59
40
  #
60
41
  # @param [String] start the branch to look for commits in
61
42
  # @param [Integer] max_count the maximum count of commits
62
43
  # @param [Integer] skip the number of commits in the branch to skip before taking the count.
63
44
  #
64
- # @raise [Ginatra::Error] if max_count is less than 0. silly billy!
65
- #
66
- # @return [Array<Grit::Commit>] the array of commits.
67
- def commits(start = 'master', max_count = 10, skip = 0)
68
- raise(Ginatra::Error.new("max_count cannot be less than 0")) if max_count < 0
69
- refs_cache = {}
70
- @repo.commits(start, max_count, skip).each do |commit|
71
- add_refs(commit,refs_cache)
72
- end
45
+ # @return [Array<Rugged::Commit>] the array of commits.
46
+ def commits(branch='master', max_count=10, skip=0)
47
+ raise Ginatra::InvalidRef unless branch_exists?(branch)
48
+
49
+ walker = Rugged::Walker.new(@repo)
50
+ walker.sorting(Rugged::SORT_TOPO)
51
+ walker.push(@repo.ref("refs/heads/#{branch}").target)
52
+
53
+ commits = walker.collect {|commit| commit }
54
+ commits[skip, max_count]
73
55
  end
74
56
 
75
- # Return a list of commits like --all, including pagination options and all the refs.
76
- #
77
- # @param [Integer] max_count the maximum count of commits
78
- # @param [Integer] skip the number of commits in the branch to skip before taking the count.
79
- #
80
- # @raise [Ginatra::Error] if max_count is less than 0. silly billy!
81
- #
82
- # @return [Array<GraphCommit>] the array of commits.
83
- def all_commits(max_count = 10, skip = 0)
84
- raise(Ginatra::Error.new("max_count cannot be less than 0")) if max_count < 0
85
- commits = Grit::Commit.find_all(@repo, nil, {:max_count => max_count, :skip => skip})
86
- ref_cache = {}
87
- commits.collect do |commit|
88
- add_refs(commit, ref_cache)
89
- GraphCommit.new(commit)
57
+ # Returns list of branches sorted by name alphabetically
58
+ def branches
59
+ @repo.branches.each(:local).sort_by {|b| b.name }
60
+ end
61
+
62
+ # Returns list of branches containing the commit
63
+ def branches_with(commit)
64
+ b = []
65
+ branches.each do |branch|
66
+ walker = Rugged::Walker.new(@repo)
67
+ walker.sorting(Rugged::SORT_TOPO)
68
+ walker.push(@repo.ref("refs/heads/#{branch.name}").target)
69
+ walker.collect { |c| b << branch if c.oid == commit }
90
70
  end
71
+ b
91
72
  end
92
- # Adds the refs corresponding to Grit::Commit objects to the respective Commit objects.
93
- #
94
- # @todo Perhaps move into commit class.
95
- #
96
- # @param [Grit::Commit] commit the commit you want refs added to
97
- # @param [Hash] empty hash with scope out of loop to speed things up
98
- # @return [Array] the array of refs added to the commit. they are also on the commit object.
99
- def add_refs(commit, ref_cache)
100
- if ref_cache.empty?
101
- @repo.refs.each {|ref| ref_cache[ref.commit.id] ||= [];ref_cache[ref.commit.id] << ref}
73
+
74
+ # Checks existence of branch by name
75
+ def branch_exists?(branch_name)
76
+ @repo.branches.exists?(branch_name)
77
+ end
78
+
79
+ # Find blob by oid
80
+ def find_blob(oid)
81
+ Rugged::Blob.new @repo, oid
82
+ end
83
+
84
+ # Find tree by tree oid or branch name
85
+ def find_tree(oid)
86
+ if branch_exists?(oid)
87
+ last_commit_sha = @repo.ref("refs/heads/#{oid}").target.oid
88
+ lookup(last_commit_sha).tree
89
+ else
90
+ lookup(oid)
102
91
  end
103
- commit.refs = ref_cache[commit.id] if ref_cache.include? commit.id
104
- commit.refs ||= []
92
+ end
93
+
94
+ # Returns Rugged::Repository instance
95
+ def to_rugged
96
+ @repo
105
97
  end
106
98
 
107
99
  # Catch all
108
100
  #
109
- # Warning! contains: Magic
110
- #
111
101
  # @todo update respond_to? method
112
102
  def method_missing(sym, *args, &block)
113
103
  if @repo.respond_to?(sym)
@@ -121,10 +111,5 @@ module Ginatra
121
111
  def respond_to?(sym)
122
112
  @repo.respond_to?(sym) || super
123
113
  end
124
-
125
- # not sure why we need this but whatever.
126
- def to_s
127
- @name.to_s
128
- end
129
114
  end
130
115
  end
@@ -1,13 +1,13 @@
1
1
  require 'singleton'
2
2
 
3
3
  module Ginatra
4
- # A singleton class that lets us make and use a constantly updating
4
+ # A singleton class that lets us make and use a constantly updating
5
5
  # list of repositories.
6
6
  class RepoList
7
7
  include Singleton
8
8
  attr_accessor :list
9
-
10
- # This creates the list, then does the first refresh to
9
+
10
+ # This creates the list, then does the first refresh to
11
11
  # populate it.
12
12
  #
13
13
  # It returns what refresh returns.
@@ -15,9 +15,9 @@ module Ginatra
15
15
  self.list = []
16
16
  self.refresh
17
17
  end
18
-
19
- # The preferred way to access the list publicly.
20
- #
18
+
19
+ # The preferred way to access the list publicly.
20
+ #
21
21
  # @return [Array<Ginatra::Repo>] a list of ginatra repos.
22
22
  def self.list
23
23
  self.instance.refresh
@@ -28,9 +28,16 @@ module Ginatra
28
28
  # and adds them if they're not already there.
29
29
  def refresh
30
30
  list.clear
31
- Ginatra::Config.git_dirs.map do |git_dir|
32
- files = Dir.glob(git_dir)
33
- files.each { |e| add(e) unless Ginatra::Config.ignored_files.include?(File.split(e).last) }
31
+ Ginatra.config.git_dirs.map do |git_dir|
32
+
33
+ if Dir.exist?(git_dir.chop)
34
+ files = Dir.glob(git_dir).sort
35
+ else
36
+ dir = File.expand_path("../../../#{git_dir}", __FILE__)
37
+ files = Dir.glob(dir).sort
38
+ end
39
+
40
+ files.each { |e| add(e) unless Ginatra.config.ignored_files.include?(File.split(e).last) }
34
41
  end
35
42
  list
36
43
  end
@@ -39,23 +46,23 @@ module Ginatra
39
46
  # globs. Checks to see that it's not there first
40
47
  #
41
48
  # @param [String] path the path of the git repo
42
- # @param [String] param the param of the repo if it differs,
49
+ # @param [String] param the param of the repo if it differs,
43
50
  # for looking to see if it's already on the list
44
- def add(path, param = File.split(path).last)
51
+ def add(path, param=File.split(path).last)
45
52
  unless self.has_repo?(param)
46
53
  begin
47
54
  list << Repo.new(path)
48
- rescue Grit::InvalidGitRepositoryError
55
+ rescue Rugged::RepositoryError
49
56
  # If the path is not a git repository, then this error is raised
50
57
  # and causes an error page to result.
51
58
  # Is it preferable to just log that the error happened and not show the error page?
52
- Ginatra::Config.logger.info "Invalid git repository at #{path}. Did you create the directory and forget to run 'git init' inside that directory?"
59
+ raise Ginatra::Error, "Invalid git repository at #{path}. Did you create the directory and forget to run 'git init' inside that directory?"
53
60
  end
54
61
  end
55
62
  list
56
63
  end
57
64
 
58
- # checks to see if the list contains a repo with a param
65
+ # checks to see if the list contains a repo with a param
59
66
  # matching the one passed in.
60
67
  #
61
68
  # @param [String] local_param param to check.
@@ -65,7 +72,7 @@ module Ginatra
65
72
  !list.find { |r| r.param == local_param }.nil?
66
73
  end
67
74
 
68
- # quick way to look up if there is a repo with a given param in the list.
75
+ # quick way to look up if there is a repo with a given param in the list.
69
76
  # If not, it refreshes the list and tries again.
70
77
  #
71
78
  # @param [String] local_param the param to lookup
@@ -76,7 +83,9 @@ module Ginatra
76
83
  repo
77
84
  else
78
85
  refresh
79
- list.find { |r| r.param == local_param }
86
+ repo = list.find { |r| r.param == local_param }
87
+ raise Ginatra::RepoNotFound if repo.nil?
88
+ repo
80
89
  end
81
90
  end
82
91
 
@@ -88,8 +97,6 @@ module Ginatra
88
97
  end
89
98
 
90
99
  # allows missing methods to cascade to the instance,
91
- #
92
- # Caution! contains: Magic
93
100
  def self.method_missing(sym, *args, &block)
94
101
  instance.send(sym, *args, &block)
95
102
  end
@@ -0,0 +1,93 @@
1
+ module Ginatra
2
+ class RepoStats
3
+ # @param [Ginatra::Repo] repo Ginatra::Repo instance
4
+ # @param [String] branch_name Branch name of repository
5
+ # @return [Ginatra::RepoStats]
6
+ def initialize(repo, branch_name)
7
+ @repo = repo
8
+ @branch = branch_name
9
+ end
10
+
11
+ # Contributors to repository
12
+ #
13
+ # @return [Array] Information about contributors sorted by commits count
14
+ def contributors
15
+ contributors = {}
16
+ ref = @repo.ref("refs/heads/#{@branch}")
17
+ walker = Rugged::Walker.new(@repo.to_rugged)
18
+ walker.push(ref.target)
19
+
20
+ walker.each do |commit|
21
+ author = commit.author
22
+ email = author[:email]
23
+
24
+ if contributors[email]
25
+ contributors[email] = {
26
+ author: author[:name],
27
+ commits_count: contributors[email][:commits_count] + 1
28
+ }
29
+ else
30
+ contributors[email] = {
31
+ author: author[:name],
32
+ commits_count: 1
33
+ }
34
+ end
35
+ end
36
+
37
+ contributors.sort_by {|c| c.last[:commits_count] }.reverse
38
+ end
39
+
40
+ # Detect common OSS licenses
41
+ #
42
+ # @return [String] License name
43
+ def license
44
+ last_commit = @repo.ref("refs/heads/#{@branch}").target
45
+ license = @repo.blob_at(last_commit.oid, 'LICENSE') || @repo.blob_at(last_commit.oid, 'LICENSE.txt')
46
+
47
+ if license.nil?
48
+ 'N/A'
49
+ else
50
+ license_text = license.text
51
+
52
+ case license_text
53
+ when /Apache License/
54
+ 'Apache'
55
+ when /GNU GENERAL PUBLIC LICENSE/
56
+ 'GPL'
57
+ when /GNU LESSER GENERAL PUBLIC LICENSE/
58
+ 'LGPL'
59
+ when /Permission is hereby granted, free of charge,/
60
+ 'MIT'
61
+ when /Redistribution and use in source and binary forms/
62
+ 'BSD'
63
+ else
64
+ 'N/A'
65
+ end
66
+ end
67
+ end
68
+
69
+ # Repository created at time
70
+ #
71
+ # @return [Time] Date of first commit to repository
72
+ def created_at
73
+ ref = @repo.ref("refs/heads/#{@branch}")
74
+
75
+ walker = Rugged::Walker.new(@repo.to_rugged)
76
+ walker.sorting(Rugged::SORT_TOPO)
77
+ walker.push(ref.target)
78
+ commit = walker.to_a.last
79
+ Time.at(commit.time)
80
+ end
81
+
82
+ # Commits count in defined branch
83
+ #
84
+ # @return [Integer] Commits count
85
+ def commits_count
86
+ ref = @repo.ref("refs/heads/#{@branch}")
87
+
88
+ walker = Rugged::Walker.new(@repo.to_rugged)
89
+ walker.push(ref.target)
90
+ walker.count
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,4 @@
1
+ module Ginatra
2
+ VERSION = "4.0.0"
3
+ RELEASE_NAME = "Aurora"
4
+ end
data/lib/git/webby.rb ADDED
@@ -0,0 +1,292 @@
1
+ # Standard requirements
2
+ require 'json'
3
+ require 'yaml'
4
+
5
+ # 3rd part requirements
6
+ require 'sinatra/base'
7
+
8
+ # Internal requirements
9
+ require 'git/webby/extensions'
10
+
11
+ # See <b>Git::Webby</b> for documentation.
12
+ module Git
13
+
14
+ # The main goal of the <b>Git::Webby</b> is implement the following useful
15
+ # features.
16
+ #
17
+ # - Smart-HTTP, based on _git-http-backend_.
18
+ # - Authentication flexible based on database or configuration file like <tt>.htpasswd</tt>.
19
+ # - API to get information about repository.
20
+ #
21
+ # This class configure the needed variables used by application. See
22
+ # Config::DEFAULTS for the values will be initialized by default.
23
+ #
24
+ # Basically, the +default+ attribute set the values that will be necessary
25
+ # by all applications.
26
+ #
27
+ # The HTTP-Backend application is configured by +http_backend+ attribute
28
+ # to set the Git RCP CLI. More details about this feature, see the
29
+ # {git-http-backend official
30
+ # page}[http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html]
31
+ #
32
+ # [*default*]
33
+ # Default configuration. All attributes will be used by all modular
34
+ # applications.
35
+ #
36
+ # *project_root* ::
37
+ # Sets the root directory where repositories have been
38
+ # placed.
39
+ # *git_path* ::
40
+ # Path to the git command line.
41
+ #
42
+ # [*http_backend*]
43
+ # HTTP-Backend configuration.
44
+ #
45
+ # *authenticate* ::
46
+ # Sets if authentication is required.
47
+ #
48
+ # *get_any_file* ::
49
+ # Like +http.getanyfile+.
50
+ #
51
+ # *upload_pack* ::
52
+ # Like +http.uploadpack+.
53
+ #
54
+ # *receive_pack* ::
55
+ # Like +http.receivepack+.
56
+ module Webby
57
+
58
+ class ProjectHandler #:nodoc:
59
+
60
+ # Path to git command
61
+ attr_reader :path
62
+
63
+ attr_reader :project_root
64
+
65
+ attr_reader :repository
66
+
67
+ def initialize(project_root, path = "/usr/bin/git")
68
+ @repository = nil
69
+ @path = check_path(File.expand_path(path))
70
+ @project_root = check_path(File.expand_path(project_root))
71
+ end
72
+
73
+ def path_to(*args)
74
+ File.join(@repository || @project_root, *(args.compact.map(&:to_s)))
75
+ end
76
+
77
+ def repository=(name)
78
+ @repository = check_path(path_to(name))
79
+ end
80
+
81
+ def cli(command, *args)
82
+ %Q[#{@path} #{args.unshift(command.to_s.gsub("_","-")).compact.join(" ")}]
83
+ end
84
+
85
+ def run(command, *args)
86
+ chdir{ %x[#{cli command, *args}] }
87
+ end
88
+
89
+ def read_file(*file)
90
+ File.read(path_to(*file))
91
+ end
92
+
93
+ def loose_object_path(*hash)
94
+ path_to(:objects, *hash)
95
+ end
96
+
97
+ def pack_idx_path(pack)
98
+ path_to(:objects, :pack, pack)
99
+ end
100
+
101
+ def info_packs_path
102
+ path_to(:objects, :info, :packs)
103
+ end
104
+
105
+ private
106
+
107
+ def repository_path(name)
108
+ bare = name =~ /\w\.git/ ? name : %W[#{name} .git]
109
+ check_path(path_to(*bare))
110
+ end
111
+
112
+ def check_path(path)
113
+ path && !path.empty? && File.ftype(path) && path
114
+ end
115
+
116
+ def chdir(&block)
117
+ Dir.chdir(@repository || @project_root, &block)
118
+ end
119
+
120
+ def ftype
121
+ { "120" => "l", "100" => "-", "040" => "d" }
122
+ end
123
+ end
124
+
125
+ class Htpasswd #:nodoc:
126
+
127
+ def initialize(file)
128
+ require 'webrick/httpauth/htpasswd'
129
+ @handler = WEBrick::HTTPAuth::Htpasswd.new(file)
130
+ yield self if block_given?
131
+ end
132
+
133
+ def find(username) #:yield: password, salt
134
+ password = @handler.get_passwd(nil, username, false)
135
+ if block_given?
136
+ yield password ? [password, password[0,2]] : [nil, nil]
137
+ else
138
+ password
139
+ end
140
+ end
141
+
142
+ def authenticated?(username, password)
143
+ self.find username do |crypted, salt|
144
+ crypted && salt && crypted == password.crypt(salt)
145
+ end
146
+ end
147
+
148
+ def create(username, password)
149
+ @handler.set_passwd(nil, username, password)
150
+ end
151
+ alias update create
152
+
153
+ def destroy(username)
154
+ @handler.delete_passwd(nil, username)
155
+ end
156
+
157
+ def include?(username)
158
+ users.include? username
159
+ end
160
+
161
+ def size
162
+ users.size
163
+ end
164
+
165
+ def write!
166
+ @handler.flush
167
+ end
168
+
169
+ private
170
+
171
+ def users
172
+ @handler.each{|username, password| username }
173
+ end
174
+ end
175
+
176
+ module GitHelpers #:nodoc:
177
+
178
+ def git
179
+ @git ||= ProjectHandler.new(settings.project_root, settings.git_path)
180
+ end
181
+
182
+ def repository
183
+ git.repository ||= (params[:repository] || params[:captures].first)
184
+ git
185
+ end
186
+
187
+ def content_type_for_git(name, *suffixes)
188
+ content_type("application/x-git-#{name}-#{suffixes.compact.join("-")}")
189
+ end
190
+
191
+ end
192
+
193
+ module AuthenticationHelpers #:nodoc:
194
+
195
+ def htpasswd
196
+ @htpasswd ||= Htpasswd.new(git.path_to("htpasswd"))
197
+ end
198
+
199
+ def authentication
200
+ @authentication ||= Rack::Auth::Basic::Request.new request.env
201
+ end
202
+
203
+ def authenticated?
204
+ request.env["REMOTE_USER"] && request.env["git.webby.authenticated"]
205
+ end
206
+
207
+ def authenticate(username, password)
208
+ checked = [ username, password ] == authentication.credentials
209
+ validated = authentication.provided? && authentication.basic?
210
+ granted = htpasswd.authenticated? username, password
211
+ if checked and validated and granted
212
+ request.env["git.webby.authenticated"] = true
213
+ request.env["REMOTE_USER"] = authentication.username
214
+ else
215
+ nil
216
+ end
217
+ end
218
+
219
+ def unauthorized!(realm = Git::Webby::info)
220
+ headers "WWW-Authenticate" => %(Basic realm="#{realm}")
221
+ throw :halt, [ 401, "Authorization Required" ]
222
+ end
223
+
224
+ def bad_request!
225
+ throw :halt, [ 400, "Bad Request" ]
226
+ end
227
+
228
+ def authenticate!
229
+ return if authenticated?
230
+ unauthorized! unless authentication.provided?
231
+ bad_request! unless authentication.basic?
232
+ unauthorized! unless authenticate(*authentication.credentials)
233
+ request.env["REMOTE_USER"] = authentication.username
234
+ end
235
+
236
+ def access_granted?(username, password)
237
+ authenticated? || authenticate(username, password)
238
+ end
239
+
240
+ end # AuthenticationHelpers
241
+
242
+ # Servers
243
+ autoload :HttpBackend, "git/webby/http_backend"
244
+
245
+ class << self
246
+
247
+ def config
248
+ @config ||= {
249
+ :default => {
250
+ :project_root => "/home/git",
251
+ :git_path => "/usr/bin/git"
252
+ },
253
+ :http_backend => {
254
+ :authenticate => true,
255
+ :get_any_file => true,
256
+ :upload_pack => true,
257
+ :receive_pack => false
258
+ }
259
+ }.to_struct
260
+ end
261
+
262
+ # Configure Git::Webby modules using keys. See Config for options.
263
+ def configure(&block)
264
+ yield config
265
+ config
266
+ end
267
+
268
+ def load_config_file(file)
269
+ YAML.load_file(file).to_struct.each_pair do |app, options|
270
+ options.each_pair do |option, value|
271
+ config[app][option] = value
272
+ end
273
+ end
274
+ config
275
+ rescue IndexError
276
+ abort "configuration option not found"
277
+ end
278
+
279
+ end
280
+
281
+ class Application < Sinatra::Base #:nodoc:
282
+
283
+ set :project_root, lambda { Git::Webby.config.default.project_root }
284
+ set :git_path, lambda { Git::Webby.config.default.git_path }
285
+
286
+ mime_type :json, "application/json"
287
+
288
+ end
289
+
290
+ end
291
+
292
+ end