blob.rb 6,3 КБ
Newer Older
Robert Speicher's avatar
Robert Speicher включено в состав коммита
1
2
# Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects
class Blob < SimpleDelegator
Jacob Vosmaer's avatar
Jacob Vosmaer включено в состав коммита
3
4
5
  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

Douwe Maan's avatar
Douwe Maan включено в состав коммита
6
7
  MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte

Douwe Maan's avatar
Douwe Maan включено в состав коммита
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  # 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.
Douwe Maan's avatar
Douwe Maan включено в состав коммита
22
  RICH_VIEWERS = [
Douwe Maan's avatar
Douwe Maan включено в состав коммита
23
24
25
26
    BlobViewer::Markup,
    BlobViewer::Notebook,
    BlobViewer::SVG,

Douwe Maan's avatar
Douwe Maan включено в состав коммита
27
28
    BlobViewer::Image,
    BlobViewer::Sketch,
Luke "Jared" Bennett's avatar
Luke "Jared" Bennett включено в состав коммита
29
    BlobViewer::Balsamiq,
Douwe Maan's avatar
Douwe Maan включено в состав коммита
30

Douwe Maan's avatar
Douwe Maan включено в состав коммита
31
    BlobViewer::Video,
Douwe Maan's avatar
Douwe Maan включено в состав коммита
32

Douwe Maan's avatar
Douwe Maan включено в состав коммита
33
34
    BlobViewer::PDF,

Douwe Maan's avatar
Douwe Maan включено в состав коммита
35
    BlobViewer::BinarySTL,
Rémy Coutable's avatar
Rémy Coutable включено в состав коммита
36
    BlobViewer::TextSTL
Douwe Maan's avatar
Douwe Maan включено в состав коммита
37
  ].sort_by { |v| v.binary? ? 0 : 1 }.freeze
Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
38

Douwe Maan's avatar
Douwe Maan включено в состав коммита
39
40
41
  AUXILIARY_VIEWERS = [
    BlobViewer::GitlabCiYml,
    BlobViewer::RouteMap,
Douwe Maan's avatar
Douwe Maan включено в состав коммита
42

Douwe Maan's avatar
Douwe Maan включено в состав коммита
43
    BlobViewer::Readme,
Douwe Maan's avatar
Douwe Maan включено в состав коммита
44
    BlobViewer::License,
Douwe Maan's avatar
Douwe Maan включено в состав коммита
45
    BlobViewer::Contributing,
Douwe Maan's avatar
Douwe Maan включено в состав коммита
46
47
48
49
50
51
52
53
54
55
56
57
58
    BlobViewer::Changelog,

    BlobViewer::Cartfile,
    BlobViewer::ComposerJson,
    BlobViewer::Gemfile,
    BlobViewer::Gemspec,
    BlobViewer::GodepsJson,
    BlobViewer::PackageJson,
    BlobViewer::Podfile,
    BlobViewer::Podspec,
    BlobViewer::PodspecJson,
    BlobViewer::RequirementsTxt,
    BlobViewer::YarnLock
Douwe Maan's avatar
Douwe Maan включено в состав коммита
59
  ].freeze
Douwe Maan's avatar
Douwe Maan включено в состав коммита
60

Douwe Maan's avatar
Douwe Maan включено в состав коммита
61
62
  attr_reader :project

Robert Speicher's avatar
Robert Speicher включено в состав коммита
63
64
65
66
67
68
69
70
71
72
  # 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
Douwe Maan's avatar
Douwe Maan включено в состав коммита
73
  def self.decorate(blob, project = nil)
Robert Speicher's avatar
Robert Speicher включено в состав коммита
74
75
    return if blob.nil?

Douwe Maan's avatar
Douwe Maan включено в состав коммита
76
77
78
    new(blob, project)
  end

Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
79
  def self.lazy(project, commit_id, path)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
80
81
82
83
84
85
86
87
88
    BatchLoader.for({ project: project, commit_id: commit_id, path: path }).batch do |items, loader|
      items_by_project = items.group_by { |i| i[:project] }

      items_by_project.each do |project, items|
        items = items.map { |i| i.values_at(:commit_id, :path) }

        project.repository.blobs_at(items).each do |blob|
          loader.call({ project: blob.project, commit_id: blob.commit_id, path: blob.path }, blob) if blob
        end
Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
89
90
91
92
      end
    end
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
93
  def initialize(blob, project = nil)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
94
95
96
    @project = project

    super(blob)
Robert Speicher's avatar
Robert Speicher включено в состав коммита
97
98
  end

Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
99
100
101
102
  def inspect
    "#<#{self.class.name} oid:#{id[0..8]} commit:#{commit_id[0..8]} path:#{path}>"
  end

Yorick Peterse's avatar
Yorick Peterse включено в состав коммита
103
104
105
106
107
108
109
110
111
112
113
114
  # 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
    if binary?
      super
    else
      @data ||= super.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
    end
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
115
  def load_all_data!
Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
116
117
118
119
    # Endpoint needed: gitlab-org/gitaly#756
    Gitlab::GitalyClient.allow_n_plus_1_calls do
      super(project.repository) if project
    end
Douwe Maan's avatar
Douwe Maan включено в состав коммита
120
121
  end

Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
122
  def no_highlighting?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
123
124
125
126
127
    raw_size && raw_size > MAXIMUM_TEXT_HIGHLIGHT_SIZE
  end

  def empty?
    raw_size == 0
Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg включено в состав коммита
128
129
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  def external_storage_error?
    if external_storage == :lfs
      !project&.lfs_enabled?
    else
      false
    end
  end

  def stored_externally?
    return @stored_externally if defined?(@stored_externally)

    @stored_externally = external_storage && !external_storage_error?
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
144
145
146
  # 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.
Douwe Maan's avatar
Douwe Maan включено в состав коммита
147
  def raw_size
Douwe Maan's avatar
Douwe Maan включено в состав коммита
148
149
    if stored_externally?
      external_size
Douwe Maan's avatar
Douwe Maan включено в состав коммита
150
151
152
    else
      size
    end
Douwe Maan's avatar
Douwe Maan включено в состав коммита
153
154
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
155
156
157
158
  # 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.
Douwe Maan's avatar
Douwe Maan включено в состав коммита
159
  def raw_binary?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
160
    if stored_externally?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
161
162
      if rich_viewer
        rich_viewer.binary?
Ken's avatar
Ken включено в состав коммита
163
      elsif Linguist::Language.find_by_extension(name).any?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
164
165
166
        false
      elsif _mime_type
        _mime_type.binary?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
167
168
169
      else
        true
      end
Douwe Maan's avatar
Douwe Maan включено в состав коммита
170
171
172
    else
      binary?
    end
Robert Speicher's avatar
Robert Speicher включено в состав коммита
173
174
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
175
176
  def extension
    @extension ||= extname.downcase.delete('.')
Sam Rose's avatar
Sam Rose включено в состав коммита
177
178
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
179
  def file_type
Sean McGivern's avatar
Sean McGivern включено в состав коммита
180
181
182
    name = File.basename(path)

    Gitlab::FileDetector.type_of(path) || Gitlab::FileDetector.type_of(name)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
183
184
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
185
186
  def video?
    UploaderHelper::VIDEO_EXT.include?(extension)
Phil Hughes's avatar
Phil Hughes включено в состав коммита
187
188
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
189
  def readable_text?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
190
    text? && !stored_externally? && !truncated?
Phil Hughes's avatar
Phil Hughes включено в состав коммита
191
192
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
193
194
195
196
197
198
199
  def simple_viewer
    @simple_viewer ||= simple_viewer_class.new(self)
  end

  def rich_viewer
    return @rich_viewer if defined?(@rich_viewer)

Douwe Maan's avatar
Douwe Maan включено в состав коммита
200
    @rich_viewer = rich_viewer_class&.new(self)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
201
202
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
203
204
205
206
207
208
  def auxiliary_viewer
    return @auxiliary_viewer if defined?(@auxiliary_viewer)

    @auxiliary_viewer = auxiliary_viewer_class&.new(self)
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
209
  def rendered_as_text?(ignore_errors: true)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
210
    simple_viewer.is_a?(BlobViewer::Text) && (ignore_errors || simple_viewer.render_error.nil?)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
211
212
213
  end

  def show_viewer_switcher?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
214
215
216
    rendered_as_text? && rich_viewer
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
217
218
219
220
  def expanded?
    !!@expanded
  end

Douwe Maan's avatar
Douwe Maan включено в состав коммита
221
  def expand!
Douwe Maan's avatar
Douwe Maan включено в состав коммита
222
    @expanded = true
Douwe Maan's avatar
Douwe Maan включено в состав коммита
223
224
225
226
  end

  private

Douwe Maan's avatar
Douwe Maan включено в состав коммита
227
228
229
230
231
232
233
234
235
236
237
  def simple_viewer_class
    if empty?
      BlobViewer::Empty
    elsif raw_binary?
      BlobViewer::Download
    else # text
      BlobViewer::Text
    end
  end

  def rich_viewer_class
Douwe Maan's avatar
Douwe Maan включено в состав коммита
238
239
240
241
242
243
244
245
    viewer_class_from(RICH_VIEWERS)
  end

  def auxiliary_viewer_class
    viewer_class_from(AUXILIARY_VIEWERS)
  end

  def viewer_class_from(classes)
Douwe Maan's avatar
Douwe Maan включено в состав коммита
246
    return if empty? || external_storage_error?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
247

Douwe Maan's avatar
Douwe Maan включено в состав коммита
248
    verify_binary = !stored_externally?
Douwe Maan's avatar
Douwe Maan включено в состав коммита
249

Douwe Maan's avatar
Douwe Maan включено в состав коммита
250
    classes.find { |viewer_class| viewer_class.can_render?(self, verify_binary: verify_binary) }
Douwe Maan's avatar
Douwe Maan включено в состав коммита
251
  end
Robert Speicher's avatar
Robert Speicher включено в состав коммита
252
end