gitlab-gollum-lib 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,171 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+
5
+ #############################################################################
6
+ #
7
+ # Helper functions
8
+ #
9
+ #############################################################################
10
+
11
+ def name
12
+ @name ||= Dir['*.gemspec'].first.split('.').first
13
+ end
14
+
15
+ def version
16
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
17
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
18
+ end
19
+
20
+ # assumes x.y.z all digit version
21
+ def next_version
22
+ # x.y.z
23
+ v = version.split '.'
24
+ # bump z
25
+ v[-1] = v[-1].to_i + 1
26
+ v.join '.'
27
+ end
28
+
29
+ def bump_version
30
+ old_file = File.read("lib/#{name}.rb")
31
+ old_version_line = old_file[/^\s*VERSION\s*=\s*.*/]
32
+ new_version = next_version
33
+ # replace first match of old vesion with new version
34
+ old_file.sub!(old_version_line, " VERSION = '#{new_version}'")
35
+
36
+ File.write("lib/#{name}.rb", old_file)
37
+
38
+ new_version
39
+ end
40
+
41
+ def date
42
+ Date.today.to_s
43
+ end
44
+
45
+ def rubyforge_project
46
+ name
47
+ end
48
+
49
+ def gemspec_file
50
+ "#{name}.gemspec"
51
+ end
52
+
53
+ def gem_file
54
+ "#{name}-#{version}.gem"
55
+ end
56
+
57
+ def replace_header(head, header_name)
58
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
59
+ end
60
+
61
+ #############################################################################
62
+ #
63
+ # Standard tasks
64
+ #
65
+ #############################################################################
66
+
67
+ task :default => :test
68
+
69
+ require 'rake/testtask'
70
+ Rake::TestTask.new(:test) do |test|
71
+ test.libs << 'lib' << 'test' << '.'
72
+ test.pattern = 'test/**/test_*.rb'
73
+ test.verbose = true
74
+ end
75
+
76
+ desc "Generate RCov test coverage and open in your browser"
77
+ task :coverage do
78
+ require 'rcov'
79
+ sh "rm -fr coverage"
80
+ sh "rcov test/test_*.rb"
81
+ sh "open coverage/index.html"
82
+ end
83
+
84
+ desc "Open an irb session preloaded with this library"
85
+ task :console do
86
+ sh "irb -rubygems -r ./lib/#{name}.rb"
87
+ end
88
+
89
+ #############################################################################
90
+ #
91
+ # Custom tasks (add your own tasks here)
92
+ #
93
+ #############################################################################
94
+
95
+ desc "Update version number and gemspec"
96
+ task :bump do
97
+ puts "Updated version to #{bump_version}"
98
+ # Execute does not invoke dependencies.
99
+ # Manually invoke gemspec then validate.
100
+ Rake::Task[:gemspec].execute
101
+ Rake::Task[:validate].execute
102
+ end
103
+
104
+ #############################################################################
105
+ #
106
+ # Packaging tasks
107
+ #
108
+ #############################################################################
109
+
110
+ desc 'Create a release build'
111
+ task :release => :build do
112
+ unless `git branch` =~ /^\* master$/
113
+ puts "You must be on the master branch to release!"
114
+ exit!
115
+ end
116
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
117
+ sh "git pull"
118
+ sh "git tag v#{version}"
119
+ sh "git push origin master"
120
+ sh "git push origin v#{version}"
121
+ sh "gem push pkg/#{name}-#{version}.gem"
122
+ end
123
+
124
+ desc 'Build gem'
125
+ task :build => :gemspec do
126
+ sh "mkdir -p pkg"
127
+ sh "gem build #{gemspec_file}"
128
+ sh "mv #{gem_file} pkg"
129
+ end
130
+
131
+ desc 'Update gemspec'
132
+ task :gemspec => :validate do
133
+ # read spec file and split out manifest section
134
+ spec = File.read(gemspec_file)
135
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
136
+
137
+ # replace name version and date
138
+ replace_header(head, :name)
139
+ replace_header(head, :version)
140
+ replace_header(head, :date)
141
+ #comment this out if your rubyforge_project has a different name
142
+ replace_header(head, :rubyforge_project)
143
+
144
+ # determine file list from git ls-files
145
+ files = `git ls-files`.
146
+ split("\n").
147
+ sort.
148
+ reject { |file| file =~ /^\./ }.
149
+ reject { |file| file =~ /^(rdoc|pkg|test|Home\.md|\.gitattributes)/ }.
150
+ map { |file| " #{file}" }.
151
+ join("\n")
152
+
153
+ # piece file back together and write
154
+ manifest = " s.files = %w[\n#{files}\n ]\n"
155
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
156
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
157
+ puts "Updated #{gemspec_file}"
158
+ end
159
+
160
+ desc 'Validate lib files and version file'
161
+ task :validate do
162
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
163
+ unless libfiles.empty?
164
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
165
+ exit!
166
+ end
167
+ unless Dir['VERSION*'].empty?
168
+ puts "A `VERSION` file at root level violates Gem best practices."
169
+ exit!
170
+ end
171
+ end
@@ -0,0 +1,33 @@
1
+ Sanitization Rules
2
+ ==================
3
+
4
+ Gollum uses the [Sanitize](http://wonko.com/post/sanitize) gem for HTML
5
+ sanitization.
6
+
7
+ See `lib/gollum-lib/sanitization.rb` for actual settings.
8
+
9
+ ## ALLOWED TAGS
10
+
11
+ a, abbr, acronym, address, area, b, big, blockquote, br, button, caption,
12
+ center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em,
13
+ fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, img, input, ins, kbd,
14
+ label, legend, li, map, menu, ol, optgroup, option, p, pre, q, s, samp,
15
+ select, small, span, strike, strong, sub, sup, table, tbody, td, textarea,
16
+ tfoot, th, thead, tr, tt, u, ul, var
17
+
18
+ ## ALLOWED ATTRIBUTES
19
+
20
+ abbr, accept, accept-charset, accesskey, action, align, alt, axis, border,
21
+ cellpadding, cellspacing, char, charoff, charset, checked, cite, class, clear,
22
+ cols, colspan, color, compact, coords, datetime, dir, disabled, enctype, for,
23
+ frame, headers, height, href, hreflang, hspace, id, ismap, label, lang,
24
+ longdesc, maxlength, media, method, multiple, name, nohref, noshade, nowrap,
25
+ prompt, readonly, rel, rev, rows, rowspan, rules, scope, selected, shape,
26
+ size, span, src, start, summary, tabindex, target, title, type, usemap,
27
+ valign, value, vspace, width
28
+
29
+ ## ALLOWED PROTOCOLS
30
+
31
+ a href: http, https, mailto, ftp, irc, apt
32
+ img src: http, https
33
+ form action: http, https
@@ -0,0 +1,75 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+ s.rubygems_version = '0.0.1'
5
+ s.required_ruby_version = ">= 1.8.7"
6
+
7
+ s.name = 'gitlab-gollum-lib'
8
+ s.version = '1.0.0'
9
+ s.date = '2013-04-02'
10
+ s.rubyforge_project = 'gollum-lib'
11
+
12
+ s.summary = "A simple, Git-powered wiki."
13
+ s.description = "A simple, Git-powered wiki with a sweet API and local frontend."
14
+
15
+ s.authors = ["Tom Preston-Werner", "Rick Olson"]
16
+ s.email = 'tom@github.com'
17
+ s.homepage = 'http://github.com/gollum/gollum-lib'
18
+
19
+ s.require_paths = %w[lib]
20
+
21
+ s.rdoc_options = ["--charset=UTF-8"]
22
+ s.extra_rdoc_files = %w[README.md LICENSE]
23
+
24
+ s.add_dependency('gitlab-grit', '~> 2.5.1')
25
+ s.add_dependency('github-markup', ['>= 0.7.5', '< 1.0.0'])
26
+ s.add_dependency('github-markdown', '~> 0.5.3')
27
+ s.add_dependency('pygments.rb', '~> 0.4.2')
28
+ s.add_dependency('sanitize', '~> 2.0.3')
29
+ s.add_dependency('nokogiri', '~> 1.5.9')
30
+ s.add_dependency('stringex', '~> 1.5.1')
31
+
32
+ s.add_development_dependency('RedCloth', '~> 4.2.9')
33
+ s.add_development_dependency('mocha', '~> 0.13.2')
34
+ s.add_development_dependency('org-ruby', '~> 0.8.1')
35
+ s.add_development_dependency('shoulda', '~> 3.4.0')
36
+ s.add_development_dependency('wikicloth', '~> 0.8.0')
37
+ s.add_development_dependency('rake', '~> 10.0.3')
38
+ s.add_development_dependency('pry', '~> 0.9.12')
39
+ # required by pry
40
+ s.add_development_dependency('rb-readline', '~> 0.4.2')
41
+ s.add_development_dependency 'minitest-reporters', '~> 0.14.10'
42
+ s.add_development_dependency('nokogiri-diff', '~> 0.1.2')
43
+
44
+ # = MANIFEST =
45
+ s.files = %w[
46
+ Gemfile
47
+ HISTORY.md
48
+ LICENSE
49
+ README.md
50
+ Rakefile
51
+ docs/sanitization.md
52
+ gollum-lib.gemspec
53
+ lib/gollum-lib.rb
54
+ lib/gollum-lib/blob_entry.rb
55
+ lib/gollum-lib/committer.rb
56
+ lib/gollum-lib/file.rb
57
+ lib/gollum-lib/file_view.rb
58
+ lib/gollum-lib/git_access.rb
59
+ lib/gollum-lib/gitcode.rb
60
+ lib/gollum-lib/grit_ext.rb
61
+ lib/gollum-lib/helpers.rb
62
+ lib/gollum-lib/markup.rb
63
+ lib/gollum-lib/markups.rb
64
+ lib/gollum-lib/page.rb
65
+ lib/gollum-lib/pagination.rb
66
+ lib/gollum-lib/remote_code.rb
67
+ lib/gollum-lib/sanitization.rb
68
+ lib/gollum-lib/web_sequence_diagram.rb
69
+ lib/gollum-lib/wiki.rb
70
+ licenses/licenses.txt
71
+ ]
72
+ # = MANIFEST =
73
+
74
+ s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
75
+ end
@@ -0,0 +1,53 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ # stdlib
3
+ require 'digest/md5'
4
+ require 'digest/sha1'
5
+ require 'ostruct'
6
+
7
+ # external
8
+ require 'grit'
9
+ require File.expand_path('../gollum-lib/grit_ext', __FILE__)
10
+ require 'github/markup'
11
+ require 'sanitize'
12
+
13
+ # internal
14
+ require File.expand_path('../gollum-lib/git_access', __FILE__)
15
+ require File.expand_path('../gollum-lib/committer', __FILE__)
16
+ require File.expand_path('../gollum-lib/pagination', __FILE__)
17
+ require File.expand_path('../gollum-lib/blob_entry', __FILE__)
18
+ require File.expand_path('../gollum-lib/wiki', __FILE__)
19
+ require File.expand_path('../gollum-lib/page', __FILE__)
20
+ require File.expand_path('../gollum-lib/file', __FILE__)
21
+ require File.expand_path('../gollum-lib/file_view', __FILE__)
22
+ require File.expand_path('../gollum-lib/markup', __FILE__)
23
+ require File.expand_path('../gollum-lib/markups', __FILE__)
24
+ require File.expand_path('../gollum-lib/sanitization', __FILE__)
25
+ require File.expand_path('../gollum-lib/web_sequence_diagram', __FILE__)
26
+
27
+ # Set ruby to UTF-8 mode
28
+ # This is required for Ruby 1.8.7 which gollum still supports.
29
+ $KCODE = 'U' if RUBY_VERSION[0,3] == '1.8'
30
+
31
+ module Gollum
32
+ VERSION = '1.0.0'
33
+
34
+ def self.assets_path
35
+ ::File.expand_path('gollum/frontend/public', ::File.dirname(__FILE__))
36
+ end
37
+
38
+ class Error < StandardError; end
39
+
40
+ class DuplicatePageError < Error
41
+ attr_accessor :dir
42
+ attr_accessor :existing_path
43
+ attr_accessor :attempted_path
44
+
45
+ def initialize(dir, existing, attempted, message = nil)
46
+ @dir = dir
47
+ @existing_path = existing
48
+ @attempted_path = attempted
49
+ super(message || "Cannot write #{@dir}/#{@attempted_path}, found #{@dir}/#{@existing_path}.")
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,95 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ class BlobEntry
4
+ # Gets the String SHA for this blob.
5
+ attr_reader :sha
6
+
7
+ # Gets the full path String for this blob.
8
+ attr_reader :path
9
+
10
+ # Gets the Fixnum size of this blob.
11
+ attr_reader :size
12
+
13
+ # Gets the Fixnum mode of this blob.
14
+ attr_reader :mode
15
+
16
+ def initialize(sha, path, size = nil, mode = nil)
17
+ @sha = sha
18
+ @path = path
19
+ @size = size
20
+ @mode = mode
21
+ @dir = @name = @blob = nil
22
+ end
23
+
24
+ # Gets the normalized directory path String for this blob.
25
+ def dir
26
+ @dir ||= self.class.normalize_dir(::File.dirname(@path))
27
+ end
28
+
29
+ # Gets the file base name String for this blob.
30
+ def name
31
+ @name ||= ::File.basename(@path)
32
+ end
33
+
34
+ # Gets a Grit::Blob instance for this blob.
35
+ #
36
+ # repo - Grit::Repo instance for the Grit::Blob.
37
+ #
38
+ # Returns an unbaked Grit::Blob instance.
39
+ def blob(repo)
40
+ @blob ||= Grit::Blob.create(repo,
41
+ :id => @sha, :name => name, :size => @size, :mode => @mode)
42
+ end
43
+
44
+ # Gets a Page instance for this blob.
45
+ #
46
+ # wiki - Gollum::Wiki instance for the Gollum::Page
47
+ #
48
+ # Returns a Gollum::Page instance.
49
+ def page(wiki, commit)
50
+ blob = self.blob(wiki.repo)
51
+ page = wiki.page_class.new(wiki).populate(blob, self.dir)
52
+ page.version = commit
53
+ page
54
+ end
55
+
56
+ # Gets a File instance for this blob.
57
+ #
58
+ # wiki - Gollum::Wiki instance for the Gollum::File
59
+ #
60
+ # Returns a Gollum::File instance.
61
+ def file(wiki, commit)
62
+ blob = self.blob(wiki.repo)
63
+ file = wiki.file_class.new(wiki).populate(blob, self.dir)
64
+ file.version = commit
65
+ file
66
+ end
67
+
68
+ def inspect
69
+ %(#<Gollum::BlobEntry #{@sha} #{@path}>)
70
+ end
71
+
72
+ # Normalizes a given directory name for searching through tree paths.
73
+ # Ensures that a directory begins with a slash, or
74
+ #
75
+ # normalize_dir("") # => ""
76
+ # normalize_dir(".") # => ""
77
+ # normalize_dir("foo") # => "/foo"
78
+ # normalize_dir("/foo/") # => "/foo"
79
+ # normalize_dir("/") # => ""
80
+ # normalize_dir("c:/") # => ""
81
+ #
82
+ # dir - String directory name.
83
+ #
84
+ # Returns a normalized String directory name, or nil if no directory
85
+ # is given.
86
+ def self.normalize_dir(dir)
87
+ return '' if dir =~ /^.:\/$/
88
+ if dir
89
+ dir = ::File.expand_path(dir, '/')
90
+ dir = '' if dir == '/'
91
+ end
92
+ dir
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,236 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ # Responsible for handling the commit process for a Wiki. It sets up the
4
+ # Git index, provides methods for modifying the tree, and stores callbacks
5
+ # to be fired after the commit has been made. This is specifically
6
+ # designed to handle multiple updated pages in a single commit.
7
+ class Committer
8
+ # Gets the instance of the Gollum::Wiki that is being updated.
9
+ attr_reader :wiki
10
+
11
+ # Gets a Hash of commit options.
12
+ attr_reader :options
13
+
14
+ # Initializes the Committer.
15
+ #
16
+ # wiki - The Gollum::Wiki instance that is being updated.
17
+ # options - The commit Hash details:
18
+ # :message - The String commit message.
19
+ # :name - The String author full name.
20
+ # :email - The String email address.
21
+ # :parent - Optional Grit::Commit parent to this update.
22
+ # :tree - Optional String SHA of the tree to create the
23
+ # index from.
24
+ # :committer - Optional Gollum::Committer instance. If provided,
25
+ # assume that this operation is part of batch of
26
+ # updates and the commit happens later.
27
+ #
28
+ # Returns the Committer instance.
29
+ def initialize(wiki, options = {})
30
+ @wiki = wiki
31
+ @options = options
32
+ @callbacks = []
33
+ end
34
+
35
+ # Public: References the Git index for this commit.
36
+ #
37
+ # Returns a Grit::Index.
38
+ def index
39
+ @index ||= begin
40
+ idx = @wiki.repo.index
41
+ if tree = options[:tree]
42
+ idx.read_tree(tree)
43
+ elsif parent = parents.first
44
+ idx.read_tree(parent.tree.id)
45
+ end
46
+ idx
47
+ end
48
+ end
49
+
50
+ # Public: The committer for this commit.
51
+ #
52
+ # Returns a Grit::Actor.
53
+ def actor
54
+ @actor ||= begin
55
+ @options[:name] = @wiki.default_committer_name if @options[:name].to_s.empty?
56
+ @options[:email] = @wiki.default_committer_email if @options[:email].to_s.empty?
57
+ Grit::Actor.new(@options[:name], @options[:email])
58
+ end
59
+ end
60
+
61
+ # Public: The parent commits to this pending commit.
62
+ #
63
+ # Returns an array of Grit::Commit instances.
64
+ def parents
65
+ @parents ||= begin
66
+ arr = [@options[:parent] || @wiki.repo.commit(@wiki.ref)]
67
+ arr.flatten!
68
+ arr.compact!
69
+ arr
70
+ end
71
+ end
72
+
73
+ # Adds a page to the given Index.
74
+ #
75
+ # dir - The String subdirectory of the Gollum::Page without any
76
+ # prefix or suffix slashes (e.g. "foo/bar").
77
+ # name - The String Gollum::Page filename_stripped.
78
+ # format - The Symbol Gollum::Page format.
79
+ # data - The String wiki data to store in the tree map.
80
+ # allow_same_ext - A Boolean determining if the tree map allows the same
81
+ # filename with the same extension.
82
+ #
83
+ # Raises Gollum::DuplicatePageError if a matching filename already exists.
84
+ # This way, pages are not inadvertently overwritten.
85
+ #
86
+ # Returns nothing (modifies the Index in place).
87
+ def add_to_index(dir, name, format, data, allow_same_ext = false)
88
+ # spaces must be dashes
89
+ dir.gsub!(' ', '-')
90
+ name.gsub!(' ', '-')
91
+
92
+ path = @wiki.page_file_name(name, format)
93
+
94
+ dir = '/' if dir.strip.empty?
95
+
96
+ fullpath = ::File.join(*[@wiki.page_file_dir, dir, path].compact)
97
+ fullpath = fullpath[1..-1] if fullpath =~ /^\//
98
+
99
+ if index.current_tree && tree = index.current_tree / (@wiki.page_file_dir || '/')
100
+ tree = tree / dir unless tree.nil?
101
+ end
102
+
103
+ if tree
104
+ downpath = path.downcase.sub(/\.\w+$/, '')
105
+
106
+ tree.blobs.each do |blob|
107
+ next if page_path_scheduled_for_deletion?(index.tree, fullpath)
108
+
109
+ existing_file = blob.name.downcase.sub(/\.\w+$/, '')
110
+ existing_file_ext = ::File.extname(blob.name).sub(/^\./, '')
111
+
112
+ new_file_ext = ::File.extname(path).sub(/^\./, '')
113
+
114
+ if downpath == existing_file && !(allow_same_ext && new_file_ext == existing_file_ext)
115
+ raise DuplicatePageError.new(dir, blob.name, path)
116
+ end
117
+ end
118
+ end
119
+
120
+ fullpath = fullpath.force_encoding('ascii-8bit') if fullpath.respond_to?(:force_encoding)
121
+
122
+ index.add(fullpath, @wiki.normalize(data))
123
+ end
124
+
125
+ # Update the given file in the repository's working directory if there
126
+ # is a working directory present.
127
+ #
128
+ # dir - The String directory in which the file lives.
129
+ # name - The String name of the page or the stripped filename
130
+ # (should be pre-canonicalized if required).
131
+ # format - The Symbol format of the page.
132
+ #
133
+ # Returns nothing.
134
+ def update_working_dir(dir, name, format)
135
+ unless @wiki.repo.bare
136
+ if @wiki.page_file_dir
137
+ dir = dir.size.zero? ? @wiki.page_file_dir : ::File.join(dir, @wiki.page_file_dir)
138
+ end
139
+
140
+ path =
141
+ if dir == ''
142
+ @wiki.page_file_name(name, format)
143
+ else
144
+ ::File.join(dir, @wiki.page_file_name(name, format))
145
+ end
146
+
147
+ path = path.force_encoding('ascii-8bit') if path.respond_to?(:force_encoding)
148
+
149
+ Dir.chdir(::File.join(@wiki.repo.path, '..')) do
150
+ if file_path_scheduled_for_deletion?(index.tree, path)
151
+ @wiki.repo.git.rm({'f' => true}, '--', path)
152
+ else
153
+ @wiki.repo.git.checkout({}, 'HEAD', '--', path)
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ # Writes the commit to Git and runs the after_commit callbacks.
160
+ #
161
+ # Returns the String SHA1 of the new commit.
162
+ def commit
163
+ sha1 = index.commit(@options[:message], parents, actor, nil, @wiki.ref)
164
+ @callbacks.each do |cb|
165
+ cb.call(self, sha1)
166
+ end
167
+ sha1
168
+ end
169
+
170
+ # Adds a callback to be fired after a commit.
171
+ #
172
+ # block - A block that expects this Committer instance and the created
173
+ # commit's SHA1 as the arguments.
174
+ #
175
+ # Returns nothing.
176
+ def after_commit(&block)
177
+ @callbacks << block
178
+ end
179
+
180
+ # Determine if a given page (regardless of format) is scheduled to be
181
+ # deleted in the next commit for the given Index.
182
+ #
183
+ # map - The Hash map:
184
+ # key - The String directory or filename.
185
+ # val - The Hash submap or the String contents of the file.
186
+ # path - The String path of the page file. This may include the format
187
+ # extension in which case it will be ignored.
188
+ #
189
+ # Returns the Boolean response.
190
+ def page_path_scheduled_for_deletion?(map, path)
191
+ parts = path.split('/')
192
+ if parts.size == 1
193
+ deletions = map.keys.select { |k| !map[k] }
194
+ downfile = parts.first.downcase.sub(/\.\w+$/, '')
195
+ deletions.any? { |d| d.downcase.sub(/\.\w+$/, '') == downfile }
196
+ else
197
+ part = parts.shift
198
+ if rest = map[part]
199
+ page_path_scheduled_for_deletion?(rest, parts.join('/'))
200
+ else
201
+ false
202
+ end
203
+ end
204
+ end
205
+
206
+ # Determine if a given file is scheduled to be deleted in the next commit
207
+ # for the given Index.
208
+ #
209
+ # map - The Hash map:
210
+ # key - The String directory or filename.
211
+ # val - The Hash submap or the String contents of the file.
212
+ # path - The String path of the file including extension.
213
+ #
214
+ # Returns the Boolean response.
215
+ def file_path_scheduled_for_deletion?(map, path)
216
+ parts = path.split('/')
217
+ if parts.size == 1
218
+ deletions = map.keys.select { |k| !map[k] }
219
+ deletions.any? { |d| d == parts.first }
220
+ else
221
+ part = parts.shift
222
+ if rest = map[part]
223
+ file_path_scheduled_for_deletion?(rest, parts.join('/'))
224
+ else
225
+ false
226
+ end
227
+ end
228
+ end
229
+
230
+ # Proxies methods t
231
+ def method_missing(name, *args)
232
+ args.map! { |item| item.respond_to?(:force_encoding) ? item.force_encoding('ascii-8bit') : item }
233
+ index.send(name, *args)
234
+ end
235
+ end
236
+ end