debian-mirror-gitlab/app/models/blob.rb

255 lines
6.2 KiB
Ruby
Raw Normal View History

2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2018-12-13 13:39:08 +05:30
# Blob is a Rails-specific wrapper around Gitlab::Git::Blob, SnippetBlob and Ci::ArtifactBlob
2016-06-02 11:05:42 +05:30
class Blob < SimpleDelegator
2018-12-13 13:39:08 +05:30
include Presentable
include BlobLanguageFromGitAttributes
2020-01-01 13:55:28 +05:30
include BlobActiveModel
2018-12-13 13:39:08 +05:30
2016-06-02 11:05:42 +05:30
CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute
CACHE_TIME_IMMUTABLE = 3600 # Cache blobs referred to by an immutable reference for 1 hour
2017-08-17 22:00:37 +05:30
# Finding a viewer for a blob happens based only on extension and whether the
# blob is binary or text, which means 1 blob should only be matched by 1 viewer,
# and the order of these viewers doesn't really matter.
#
# However, when the blob is an LFS pointer, we cannot know for sure whether the
# file being pointed to is binary or text. In this case, we match only on
# extension, preferring binary viewers over text ones if both exist, since the
# large files referred to in "Large File Storage" are much more likely to be
# binary than text.
#
# `.stl` files, for example, exist in both binary and text forms, and are
# handled by different viewers (`BinarySTL` and `TextSTL`) depending on blob
# type. LFS pointers to `.stl` files are assumed to always be the binary kind,
# and use the `BinarySTL` viewer.
RICH_VIEWERS = [
BlobViewer::Markup,
BlobViewer::Notebook,
BlobViewer::SVG,
2020-01-01 13:55:28 +05:30
BlobViewer::OpenApi,
2017-08-17 22:00:37 +05:30
BlobViewer::Image,
BlobViewer::Sketch,
BlobViewer::Balsamiq,
BlobViewer::Video,
2019-12-21 20:55:43 +05:30
BlobViewer::Audio,
2017-08-17 22:00:37 +05:30
BlobViewer::PDF,
BlobViewer::BinarySTL,
2017-09-10 17:25:29 +05:30
BlobViewer::TextSTL
].sort_by { |v| v.binary? ? 0 : 1 }.freeze
AUXILIARY_VIEWERS = [
BlobViewer::GitlabCiYml,
BlobViewer::RouteMap,
BlobViewer::Readme,
BlobViewer::License,
BlobViewer::Contributing,
BlobViewer::Changelog,
BlobViewer::Cartfile,
BlobViewer::ComposerJson,
BlobViewer::Gemfile,
BlobViewer::Gemspec,
BlobViewer::GodepsJson,
BlobViewer::PackageJson,
BlobViewer::Podfile,
BlobViewer::Podspec,
BlobViewer::PodspecJson,
BlobViewer::RequirementsTxt,
BlobViewer::YarnLock
2017-08-17 22:00:37 +05:30
].freeze
attr_reader :project
2016-09-13 17:45:13 +05:30
2016-06-02 11:05:42 +05:30
# Wrap a Gitlab::Git::Blob object, or return nil when given nil
#
# This method prevents the decorated object from evaluating to "truthy" when
# given a nil value. For example:
#
# blob = Blob.new(nil)
# puts "truthy" if blob # => "truthy"
#
# blob = Blob.decorate(nil)
# puts "truthy" if blob # No output
2017-08-17 22:00:37 +05:30
def self.decorate(blob, project = nil)
2016-06-02 11:05:42 +05:30
return if blob.nil?
2017-08-17 22:00:37 +05:30
new(blob, project)
end
2018-03-17 18:26:18 +05:30
def self.lazy(project, commit_id, path)
2019-02-15 15:39:39 +05:30
BatchLoader.for([commit_id, path]).batch(key: project.repository) do |items, loader, args|
args[:key].blobs_at(items).each do |blob|
loader.call([blob.commit_id, blob.path], blob) if blob
2018-03-17 18:26:18 +05:30
end
end
end
2017-08-17 22:00:37 +05:30
def initialize(blob, project = nil)
@project = project
super(blob)
2016-06-02 11:05:42 +05:30
end
2018-03-17 18:26:18 +05:30
def inspect
"#<#{self.class.name} oid:#{id[0..8]} commit:#{commit_id[0..8]} path:#{path}>"
end
2016-09-29 09:46:39 +05:30
# Returns the data of the blob.
#
# If the blob is a text based blob the content is converted to UTF-8 and any
# invalid byte sequences are replaced.
def data
2019-02-15 15:39:39 +05:30
if binary_in_repo?
2016-09-29 09:46:39 +05:30
super
else
@data ||= super.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
end
end
2017-09-10 17:25:29 +05:30
def load_all_data!
2019-07-31 22:56:46 +05:30
# Endpoint needed: https://gitlab.com/gitlab-org/gitaly/issues/756
2018-03-17 18:26:18 +05:30
Gitlab::GitalyClient.allow_n_plus_1_calls do
super(project.repository) if project
end
2017-09-10 17:25:29 +05:30
end
2017-08-17 22:00:37 +05:30
def empty?
raw_size == 0
end
def external_storage_error?
if external_storage == :lfs
!project&.lfs_enabled?
else
false
end
2016-06-02 11:05:42 +05:30
end
2017-08-17 22:00:37 +05:30
def stored_externally?
return @stored_externally if defined?(@stored_externally)
@stored_externally = external_storage && !external_storage_error?
2016-09-13 17:45:13 +05:30
end
2017-08-17 22:00:37 +05:30
# Returns the size of the file that this blob represents. If this blob is an
# LFS pointer, this is the size of the file stored in LFS. Otherwise, this is
# the size of the blob itself.
def raw_size
if stored_externally?
external_size
else
size
end
2016-08-24 12:49:21 +05:30
end
2017-08-17 22:00:37 +05:30
# Returns whether the file that this blob represents is binary. If this blob is
# an LFS pointer, we assume the file stored in LFS is binary, unless a
# text-based rich blob viewer matched on the file's extension. Otherwise, this
# depends on the type of the blob itself.
2019-02-15 15:39:39 +05:30
def binary?
2017-08-17 22:00:37 +05:30
if stored_externally?
if rich_viewer
rich_viewer.binary?
2018-12-13 13:39:08 +05:30
elsif known_extension?
2017-08-17 22:00:37 +05:30
false
elsif _mime_type
_mime_type.binary?
else
true
end
2016-06-02 11:05:42 +05:30
else
2019-02-15 15:39:39 +05:30
binary_in_repo?
2017-08-17 22:00:37 +05:30
end
end
def extension
@extension ||= extname.downcase.delete('.')
end
2017-09-10 17:25:29 +05:30
def file_type
2018-03-17 18:26:18 +05:30
name = File.basename(path)
Gitlab::FileDetector.type_of(path) || Gitlab::FileDetector.type_of(name)
2017-09-10 17:25:29 +05:30
end
2017-08-17 22:00:37 +05:30
def video?
2019-12-21 20:55:43 +05:30
UploaderHelper::SAFE_VIDEO_EXT.include?(extension)
end
def audio?
UploaderHelper::SAFE_AUDIO_EXT.include?(extension)
2017-08-17 22:00:37 +05:30
end
def readable_text?
2019-02-15 15:39:39 +05:30
text_in_repo? && !stored_externally? && !truncated?
2017-08-17 22:00:37 +05:30
end
def simple_viewer
@simple_viewer ||= simple_viewer_class.new(self)
end
def rich_viewer
return @rich_viewer if defined?(@rich_viewer)
@rich_viewer = rich_viewer_class&.new(self)
end
2017-09-10 17:25:29 +05:30
def auxiliary_viewer
return @auxiliary_viewer if defined?(@auxiliary_viewer)
@auxiliary_viewer = auxiliary_viewer_class&.new(self)
end
2017-08-17 22:00:37 +05:30
def rendered_as_text?(ignore_errors: true)
2017-09-10 17:25:29 +05:30
simple_viewer.is_a?(BlobViewer::Text) && (ignore_errors || simple_viewer.render_error.nil?)
2017-08-17 22:00:37 +05:30
end
def show_viewer_switcher?
rendered_as_text? && rich_viewer
end
2017-09-10 17:25:29 +05:30
def expanded?
!!@expanded
end
def expand!
@expanded = true
2017-08-17 22:00:37 +05:30
end
private
def simple_viewer_class
if empty?
BlobViewer::Empty
2019-02-15 15:39:39 +05:30
elsif binary?
2017-08-17 22:00:37 +05:30
BlobViewer::Download
else # text
BlobViewer::Text
2016-06-02 11:05:42 +05:30
end
end
2017-08-17 22:00:37 +05:30
def rich_viewer_class
2017-09-10 17:25:29 +05:30
viewer_class_from(RICH_VIEWERS)
end
def auxiliary_viewer_class
viewer_class_from(AUXILIARY_VIEWERS)
end
def viewer_class_from(classes)
2017-08-17 22:00:37 +05:30
return if empty? || external_storage_error?
2017-09-10 17:25:29 +05:30
verify_binary = !stored_externally?
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
classes.find { |viewer_class| viewer_class.can_render?(self, verify_binary: verify_binary) }
2017-08-17 22:00:37 +05:30
end
2016-06-02 11:05:42 +05:30
end
2019-12-04 20:38:33 +05:30
Blob.prepend_if_ee('EE::Blob')