Class: Torque::PostgreSQL::Attributes::LTree
- Inherits:
-
Array
- Object
- Array
- Torque::PostgreSQL::Attributes::LTree
- Defined in:
- lib/torque/postgresql/attributes/ltree.rb
Overview
A label path, as stored by the ltree data type. It is an Array of
labels, so it flows through Ruby like any other list, which also means
that an ltree[] column simply becomes an Array of these
Constant Summary collapse
- LABEL =
/\A[[:alnum:]_-]+\z/
Class Method Summary collapse
- .[](*labels) ⇒ Object
-
.compatible(value) ⇒ Object
The path that the object describes for itself.
-
.compatible?(value) ⇒ Boolean
Whether the object knows how to describe itself as a path, which is what allows any class to be used where a path is expected.
-
.load(value) ⇒ Object
Values coming from the database are valid by construction, so they skip both the normalization and the validation.
-
.resolve_record(value) ⇒ Object
A record stands for its primary key, which is what makes a path built out of other records work the same way as one built out of labels.
-
.sanitize(value) ⇒ Object
Apply the configured replacements, so callers can feed a source that does not satisfy PostgreSQL's rules for a label on its own.
Instance Method Summary collapse
- #/(other) ⇒ Object (also: #+)
-
#ancestor_of?(other) ⇒ Boolean
(also: #covers?)
Same as the
@>operator, which includes the path itself. -
#descendant_of?(other) ⇒ Boolean
(also: #covered_by?)
Same as the
<@operator, which includes the path itself. -
#index_of(subpath, offset = 0) ⇒ Object
The position where the given subpath starts, or -1 when it is not present.
-
#initialize(labels = nil, normalize: true) ⇒ LTree
constructor
A new instance of LTree.
-
#lca(*others) ⇒ Object
The longest common ancestor, which never includes the last label of any of the paths, exactly like PostgreSQL's own
lca. -
#parent ⇒ Object
A path with a single label has no parent, and neither does an empty one.
- #root ⇒ Object
- #root? ⇒ Boolean
- #to_s ⇒ Object
Constructor Details
#initialize(labels = nil, normalize: true) ⇒ LTree
Returns a new instance of LTree.
68 69 70 71 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 68 def initialize(labels = nil, normalize: true) super() concat(normalize ? normalized(labels) : Array.wrap(labels)) end |
Class Method Details
.[](*labels) ⇒ Object
15 16 17 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 15 def [](*labels) new(labels) end |
.compatible(value) ⇒ Object
The path that the object describes for itself
33 34 35 36 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 33 def compatible(value) method = PostgreSQL.config.ltree.compatible_method value.public_send(method) if compatible?(value) end |
.compatible?(value) ⇒ Boolean
Whether the object knows how to describe itself as a path, which is what allows any class to be used where a path is expected
27 28 29 30 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 27 def compatible?(value) method = PostgreSQL.config.ltree.compatible_method method.present? && value.respond_to?(method) end |
.load(value) ⇒ Object
Values coming from the database are valid by construction, so they skip both the normalization and the validation
21 22 23 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 21 def load(value) new(value.to_s.split('.'), normalize: false) end |
.resolve_record(value) ⇒ Object
A record stands for its primary key, which is what makes a path built out of other records work the same way as one built out of labels
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 41 def resolve_record(value) return value unless value.is_a?(::ActiveRecord::Base) id = value.id raise ArgumentError, <<~MSG.squish if id.nil? Unable to use #{value.class.name} as a label because its #{value.class.primary_key} is still empty. MSG raise ArgumentError, <<~MSG.squish if id.is_a?(::Array) Unable to use #{value.class.name} as a label because it has a composite primary key, which cannot be a single label. MSG id end |
.sanitize(value) ⇒ Object
Apply the configured replacements, so callers can feed a source that does not satisfy PostgreSQL's rules for a label on its own
60 61 62 63 64 65 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 60 def sanitize(value) replacements = PostgreSQL.config.ltree.sanitize return value if replacements.blank? value.gsub(Regexp.union(replacements.keys), replacements) end |
Instance Method Details
#/(other) ⇒ Object Also known as: +
90 91 92 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 90 def /(other) self.class.new(to_a + self.class.new(other), normalize: false) end |
#ancestor_of?(other) ⇒ Boolean Also known as: covers?
Same as the @> operator, which includes the path itself
97 98 99 100 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 97 def ancestor_of?(other) other = self.class.new(other) size <= other.size && other.first(size) == to_a end |
#descendant_of?(other) ⇒ Boolean Also known as: covered_by?
Same as the <@ operator, which includes the path itself
104 105 106 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 104 def descendant_of?(other) self.class.new(other).ancestor_of?(self) end |
#index_of(subpath, offset = 0) ⇒ Object
The position where the given subpath starts, or -1 when it is not
present. Named apart from index so that Array's own contract, of
returning nil when the element is missing, stays intact
121 122 123 124 125 126 127 128 129 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 121 def index_of(subpath, offset = 0) subpath = self.class.new(subpath) offset += size if offset.negative? return -1 if subpath.empty? || offset.negative? range = offset..(size - subpath.size) position = range.find { |i| self[i, subpath.size] == subpath.to_a } position || -1 end |
#lca(*others) ⇒ Object
The longest common ancestor, which never includes the last label of
any of the paths, exactly like PostgreSQL's own lca
111 112 113 114 115 116 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 111 def lca(*others) paths = [self, *others].map { |path| self.class.new(path)[0..-2].to_a } result = paths.shift || [] paths.each { |path| result = common_prefix(result, path) } self.class.new(result, normalize: false) end |
#parent ⇒ Object
A path with a single label has no parent, and neither does an empty one
86 87 88 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 86 def parent self.class.new(self[0..-2], normalize: false) unless root? end |
#root ⇒ Object
81 82 83 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 81 def root self.class.new(first, normalize: false) end |
#root? ⇒ Boolean
77 78 79 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 77 def root? size <= 1 end |
#to_s ⇒ Object
73 74 75 |
# File 'lib/torque/postgresql/attributes/ltree.rb', line 73 def to_s join('.') end |