Class: Banzai::ReferenceParser::BaseParser
- Inherits:
-
Object
- Object
- Banzai::ReferenceParser::BaseParser
- Defined in:
- lib/banzai/reference_parser/base_parser.rb
Overview
Base class for reference parsing classes.
Each parser should also specify its reference type by calling `self.reference_type = …` in the body of the class. The value of this method should be a symbol such as `:issue` or `:merge_request`. For example:
class IssueParser < BaseParser
self.reference_type = :issue
end
The reference type is used to determine what nodes to pass to the `referenced_by` method.
Parser classes should either implement the instance method `references_relation` or overwrite `referenced_by`. The `references_relation` method is supposed to return an ActiveRecord::Relation used as a base relation for retrieving the objects referenced in a set of HTML nodes.
Each class can implement two additional methods:
-
`nodes_user_can_reference`: returns an Array of nodes the given user can refer to.
-
`nodes_visible_to_user`: returns an Array of nodes that are visible to the given user.
You only need to overwrite these methods if you want to tweak who can see which references. For example, the IssueParser class defines its own `nodes_visible_to_user` method so it can ensure users can only see issues they have access to.
Direct Known Subclasses
CommitParser, CommitRangeParser, DesignParser, ExternalIssueParser, IssuableParser, IterationParser, LabelParser, MentionedGroupParser, MentionedUserParser, MilestoneParser, ProjectParser, SnippetParser, UserParser
Class Attribute Summary collapse
-
.reference_options ⇒ Object
Returns the value of attribute reference_options.
-
.reference_type ⇒ Object
Returns the value of attribute reference_type.
Class Method Summary collapse
-
.data_attribute ⇒ Object
Returns the attribute name containing the value for every object to be parsed by the current parser.
Instance Method Summary collapse
- #can?(user, permission, subject = :global) ⇒ Boolean
-
#collection_cache_key(collection) ⇒ Object
Returns the cache key to use for a collection.
-
#collection_objects_for_ids(collection, ids) ⇒ Object
Queries the collection for the objects with the given IDs.
- #find_projects_for_hash_keys(hash) ⇒ Object
-
#gather_attributes_per_project(nodes, attribute) ⇒ Object
Returns a Hash containing attribute values per project ID.
-
#gather_references(nodes) ⇒ Object
Gathers the references for the given HTML nodes.
-
#grouped_objects_for_nodes(nodes, collection, attribute) ⇒ Object
Returns a Hash containing objects for an attribute grouped per the nodes that reference them.
-
#initialize(context) ⇒ BaseParser
constructor
context - An instance of `Banzai::RenderContext`.
-
#nodes_user_can_reference(user, nodes) ⇒ Object
Returns all the nodes containing references that the user can refer to.
-
#nodes_visible_to_user(user, nodes) ⇒ Object
Returns all the nodes that are visible to the given user.
-
#process(documents) ⇒ Object
Processes the list of HTML documents and returns an Array containing all the references.
- #project_for_node(node) ⇒ Object
-
#projects_for_nodes(nodes) ⇒ Object
Returns a Hash containing the projects for a given list of HTML nodes.
-
#referenced_by(nodes) ⇒ Object
Returns an Array of objects referenced by any of the given HTML nodes.
-
#references_relation ⇒ Object
Returns the ActiveRecord::Relation to use for querying references in the DB.
-
#unique_attribute_values(nodes, attribute) ⇒ Object
Returns an Array containing all unique values of an attribute of the given nodes.
Constructor Details
#initialize(context) ⇒ BaseParser
context - An instance of `Banzai::RenderContext`.
51 52 53 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 51 def initialize(context) @context = context end |
Class Attribute Details
.reference_options ⇒ Object
Returns the value of attribute reference_options
38 39 40 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 38 def @reference_options end |
.reference_type ⇒ Object
Returns the value of attribute reference_type
38 39 40 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 38 def reference_type @reference_type end |
Class Method Details
.data_attribute ⇒ Object
Returns the attribute name containing the value for every object to be parsed by the current parser.
For example, for a parser class that returns “Animal” objects this attribute would be “data-animal”.
46 47 48 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 46 def self.data_attribute @data_attribute ||= "data-#{reference_type.to_s.dasherize}" end |
Instance Method Details
#can?(user, permission, subject = :global) ⇒ Boolean
226 227 228 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 226 def can?(user, , subject = :global) Ability.allowed?(user, , subject) end |
#collection_cache_key(collection) ⇒ Object
Returns the cache key to use for a collection.
188 189 190 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 188 def collection_cache_key(collection) collection.respond_to?(:model) ? collection.model : collection end |
#collection_objects_for_ids(collection, ids) ⇒ Object
Queries the collection for the objects with the given IDs.
If the RequestStore module is enabled this method will only query any objects that have not yet been queried. For objects that have already been queried the object is returned from the cache.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 170 def collection_objects_for_ids(collection, ids) if Gitlab::SafeRequestStore.active? ids = ids.map(&:to_i).uniq cache = collection_cache[collection_cache_key(collection)] to_query = ids - cache.keys unless to_query.empty? collection.where(id: to_query).each { |row| cache[row.id] = row } end ids.uniq.map { |id| cache[id] }.compact else collection.where(id: ids) end end |
#find_projects_for_hash_keys(hash) ⇒ Object
230 231 232 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 230 def find_projects_for_hash_keys(hash) collection_objects_for_ids(Project, hash.keys) end |
#gather_attributes_per_project(nodes, attribute) ⇒ Object
Returns a Hash containing attribute values per project ID.
The returned Hash uses the following format:
{ project id => [value1, value2, ...] }
nodes - An Array of HTML nodes to process. attribute - The name of the attribute (as a String) for which to gather
values.
Returns a Hash.
107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 107 def gather_attributes_per_project(nodes, attribute) per_project = Hash.new { |hash, key| hash[key] = Set.new } nodes.each do |node| project_id = node.attr('data-project').to_i id = node.attr(attribute) per_project[project_id] << id if id end per_project end |
#gather_references(nodes) ⇒ Object
Gathers the references for the given HTML nodes. Returns visible references and a list of nodes which are not visible to the user
207 208 209 210 211 212 213 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 207 def gather_references(nodes) nodes = nodes_user_can_reference(current_user, nodes) visible = nodes_visible_to_user(current_user, nodes) not_visible = nodes - visible { visible: referenced_by(visible), not_visible: not_visible } end |
#grouped_objects_for_nodes(nodes, collection, attribute) ⇒ Object
Returns a Hash containing objects for an attribute grouped per the nodes that reference them.
The returned Hash uses the following format:
{ node => row }
nodes - An Array of HTML nodes to process.
collection - The model or ActiveRecord relation to use for retrieving
rows from the database.
attribute - The name of the attribute containing the primary key values
for every row.
Returns a Hash.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 136 def grouped_objects_for_nodes(nodes, collection, attribute) return {} if nodes.empty? ids = unique_attribute_values(nodes, attribute) collection_objects = collection_objects_for_ids(collection, ids) objects_by_id = collection_objects.index_by(&:id) nodes.each_with_object({}) do |node, hash| if node.has_attribute?(attribute) obj = objects_by_id[node.attr(attribute).to_i] hash[node] = obj if obj end end end |
#nodes_user_can_reference(user, nodes) ⇒ Object
Returns all the nodes containing references that the user can refer to.
60 61 62 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 60 def nodes_user_can_reference(user, nodes) nodes end |
#nodes_visible_to_user(user, nodes) ⇒ Object
Returns all the nodes that are visible to the given user.
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 65 def nodes_visible_to_user(user, nodes) projects = lazy { projects_for_nodes(nodes) } project_attr = 'data-project' nodes.select do |node| if node.has_attribute?(project_attr) can_read_reference?(user, projects[node], node) else true end end end |
#process(documents) ⇒ Object
Processes the list of HTML documents and returns an Array containing all the references.
194 195 196 197 198 199 200 201 202 203 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 194 def process(documents) type = self.class.reference_type = self.class. nodes = documents.flat_map do |document| Querying.css(document, "a[data-reference-type='#{type}'].gfm", ).to_a end gather_references(nodes) end |
#project_for_node(node) ⇒ Object
55 56 57 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 55 def project_for_node(node) context.project_for_node(node) end |
#projects_for_nodes(nodes) ⇒ Object
Returns a Hash containing the projects for a given list of HTML nodes.
The returned Hash uses the following format:
{ node => project }
221 222 223 224 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 221 def projects_for_nodes(nodes) @projects_for_nodes ||= grouped_objects_for_nodes(nodes, Project.includes(:project_feature), 'data-project') end |
#referenced_by(nodes) ⇒ Object
Returns an Array of objects referenced by any of the given HTML nodes.
79 80 81 82 83 84 85 86 87 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 79 def referenced_by(nodes) ids = unique_attribute_values(nodes, self.class.data_attribute) if ids.empty? references_relation.none else references_relation.where(id: ids) end end |
#references_relation ⇒ Object
Returns the ActiveRecord::Relation to use for querying references in the DB.
91 92 93 94 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 91 def references_relation raise NotImplementedError, "#{self.class} does not implement #{__method__}" end |
#unique_attribute_values(nodes, attribute) ⇒ Object
Returns an Array containing all unique values of an attribute of the given nodes.
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/banzai/reference_parser/base_parser.rb', line 153 def unique_attribute_values(nodes, attribute) values = Set.new nodes.each do |node| if node.has_attribute?(attribute) values << node.attr(attribute) end end values.to_a end |