Module: Bibliothecary::Analyser::ClassMethods

Defined in:
lib/bibliothecary/analyser.rb

Instance Method Summary collapse

Instance Method Details

#analyse(folder_path, file_list) ⇒ Object Also known as: analyze



105
106
107
# File 'lib/bibliothecary/analyser.rb', line 105

def analyse(folder_path, file_list)
  analyse_file_info(file_list.map { |full_path| FileInfo.new(folder_path, full_path) })
end

#analyse_contents(filename, contents) ⇒ Object Also known as: analyze_contents



121
122
123
# File 'lib/bibliothecary/analyser.rb', line 121

def analyse_contents(filename, contents)
  analyse_contents_from_info(FileInfo.new(nil, filename, contents))
end

#analyse_contents_from_info(info) ⇒ Object Also known as: analyze_contents_from_info



145
146
147
148
149
150
151
152
153
154
# File 'lib/bibliothecary/analyser.rb', line 145

def analyse_contents_from_info(info)
  # If your Parser needs to return multiple responses for one file, please override this method
  # For example see conda.rb
  kind = determine_kind_from_info(info)
  dependencies = parse_file(info.relative_path, info.contents)

  dependencies_to_analysis(info, kind, dependencies)
rescue Bibliothecary::FileParsingError => e
  Bibliothecary::Analyser::create_error_analysis(platform_name, info.relative_path, kind, e.message)
end

#analyse_file_info(file_info_list) ⇒ Object Also known as: analyze_file_info



110
111
112
113
114
115
116
117
118
# File 'lib/bibliothecary/analyser.rb', line 110

def analyse_file_info(file_info_list)
  matching_info = file_info_list
    .select(&method(:match_info?))

  matching_info.flat_map do |info|
    analyse_contents_from_info(info)
      .merge(related_paths: related_paths(info, matching_info))
  end
end

#dependencies_to_analysis(info, kind, dependencies) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/bibliothecary/analyser.rb', line 126

def dependencies_to_analysis(info, kind, dependencies)
  dependencies = dependencies || [] # work around any legacy parsers that return nil
  if generic?
    analyses = []
    grouped = dependencies.group_by { |dep| dep[:platform] }
    all_analyses = grouped.keys.map do |platform|
      deplatformed_dependencies = grouped[platform].map { |d| d.delete(:platform); d }
      Bibliothecary::Analyser::create_analysis(platform, info.relative_path, kind, deplatformed_dependencies)
    end
    # this is to avoid a larger refactor for the time being. The larger refactor
    # needs to make analyse_contents return multiple analysis, or add another
    # method that can return multiple and deprecate analyse_contents, perhaps.
    raise "File contains zero or multiple platforms, currently must have exactly one" if all_analyses.length != 1
    all_analyses.first
  else
    Bibliothecary::Analyser::create_analysis(platform_name, info.relative_path, kind, dependencies)
  end
end

#determine_can_have_lockfile(filename, contents = nil) ⇒ Object

calling this with contents=nil can produce less-informed results, but kept for back compat



170
171
172
# File 'lib/bibliothecary/analyser.rb', line 170

def determine_can_have_lockfile(filename, contents = nil)
  determine_can_have_lockfile_from_info(FileInfo.new(nil, filename, contents))
end

#determine_can_have_lockfile_from_info(info) ⇒ Object



174
175
176
177
# File 'lib/bibliothecary/analyser.rb', line 174

def determine_can_have_lockfile_from_info(info)
  first_matching_mapping_details(info)
    .fetch(:can_have_lockfile, true)
end

#determine_kind(filename, contents = nil) ⇒ Object

calling this with contents=nil can produce less-informed results, but kept for back compat



159
160
161
# File 'lib/bibliothecary/analyser.rb', line 159

def determine_kind(filename, contents = nil)
  determine_kind_from_info(FileInfo.new(nil, filename, contents))
end

#determine_kind_from_info(info) ⇒ Object



163
164
165
166
# File 'lib/bibliothecary/analyser.rb', line 163

def determine_kind_from_info(info)
  first_matching_mapping_details(info)
    .fetch(:kind, nil)
end

#generic?Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/bibliothecary/analyser.rb', line 28

def generic?
  platform_name == "generic"
end

#map_dependencies(hash, key, type) ⇒ Object



95
96
97
98
99
100
101
102
103
# File 'lib/bibliothecary/analyser.rb', line 95

def map_dependencies(hash, key, type)
  hash.fetch(key,[]).map do |name, requirement|
    {
      name: name,
      requirement: requirement,
      type: type
    }
  end
end

#mapping_entry_match?(matcher, details, info) ⇒ Boolean

Returns:

  • (Boolean)


32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/bibliothecary/analyser.rb', line 32

def mapping_entry_match?(matcher, details, info)
  if matcher.call(info.relative_path)
    # we only want to load contents if we don't have them already
    # and there's a content_matcher method to use
    return true if details[:content_matcher].nil?
    # this is the libraries.io case where we won't load all .xml
    # files (for example) just to look at their contents, we'll
    # assume they are not manifests.
    return false if info.contents.nil?
    return send(details[:content_matcher], info.contents)
  else
    return false
  end
end

#match?(filename, contents = nil) ⇒ Boolean

this is broken with contents=nil because it can’t look at file contents, so skips manifests that are ambiguously a manifest considering only the filename. However, those are the semantics that libraries.io uses since it doesn’t have the files locally.

Returns:

  • (Boolean)


73
74
75
# File 'lib/bibliothecary/analyser.rb', line 73

def match?(filename, contents = nil)
  match_info?(FileInfo.new(nil, filename, contents))
end

#match_extension(filename, case_insensitive: false) ⇒ Object



209
210
211
212
213
214
215
# File 'lib/bibliothecary/analyser.rb', line 209

def match_extension(filename, case_insensitive: false)
  if case_insensitive
    lambda { |path| path.downcase.end_with?(filename.downcase) }
  else
    lambda { |path| path.end_with?(filename) }
  end
end

#match_filename(filename, case_insensitive: false) ⇒ Object



194
195
196
197
198
199
200
# File 'lib/bibliothecary/analyser.rb', line 194

def match_filename(filename, case_insensitive: false)
  if case_insensitive
    lambda { |path| path.downcase == filename.downcase || path.downcase.end_with?("/" + filename.downcase) }
  else
    lambda { |path| path == filename || path.end_with?("/" + filename) }
  end
end

#match_filenames(*filenames) ⇒ Object



202
203
204
205
206
207
# File 'lib/bibliothecary/analyser.rb', line 202

def match_filenames(*filenames)
  lambda do |path|
    filenames.any? { |f| path == f } ||
      filenames.any? { |f| path.end_with?("/" + f) }
  end
end

#match_info?(info) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/bibliothecary/analyser.rb', line 77

def match_info?(info)
  first_matching_mapping_details(info).any?
end

#parse_file(filename, contents) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/bibliothecary/analyser.rb', line 47

def parse_file(filename, contents)
  details = first_matching_mapping_details(FileInfo.new(nil, filename, contents))

  # this can be raised if we don't check match?/match_info?,
  # OR don't have the file contents when we check them, so
  # it turns out for example that a .xml file isn't a
  # manifest after all.
  raise Bibliothecary::FileParsingError.new("No parser for this file type", filename) unless details[:parser]

  # The `parser` method should raise an exception if the file is malformed,
  # should return empty [] if the file is fine but simply doesn't contain
  # any dependencies, and should never return nil. At the time of writing
  # this comment, some of the parsers return [] or nil to mean an error
  # which is confusing to users.
  send(details[:parser], contents)

rescue Exception => e # default is StandardError but C bindings throw Exceptions
  # the C xml parser also puts a newline at the end of the message
  raise Bibliothecary::FileParsingError.new(e.message.strip, filename)
end

#parse_json_runtime_manifest(file_contents) ⇒ Object



85
86
87
88
89
90
91
92
93
# File 'lib/bibliothecary/analyser.rb', line 85

def parse_json_runtime_manifest(file_contents)
  JSON.parse(file_contents).fetch('dependencies',[]).map do |name, requirement|
    {
      name: name,
      requirement: requirement,
      type: 'runtime'
    }
  end
end

#parse_ruby_manifest(manifest) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/bibliothecary/analyser.rb', line 179

def parse_ruby_manifest(manifest)
  manifest.dependencies.inject([]) do |deps, dep|
    deps.push({
      name: dep.name,
      requirement: dep
        .requirement
        .requirements
        .sort_by(&:last)
        .map { |op, version| "#{op} #{version}" }
        .join(", "),
      type: dep.type
    })
  end.uniq
end

#platform_nameObject



81
82
83
# File 'lib/bibliothecary/analyser.rb', line 81

def platform_name
  self.name.to_s.split('::').last.downcase
end