Module: Luoma::Filters
- Defined in:
- lib/luoma/filters/date.rb,
lib/luoma/filters/json.rb,
lib/luoma/filters/math.rb,
lib/luoma/filters/size.rb,
lib/luoma/filters/sort.rb,
lib/luoma/filters/array.rb,
lib/luoma/filters/slice.rb,
lib/luoma/filters/string.rb,
lib/luoma/filters/default.rb,
sig/luoma/filters/date.rbs,
sig/luoma/filters/json.rbs,
sig/luoma/filters/math.rbs,
sig/luoma/filters/size.rbs,
sig/luoma/filters/sort.rbs,
sig/luoma/filters/array.rbs,
sig/luoma/filters/slice.rbs,
sig/luoma/filters/string.rbs,
sig/luoma/filters/default.rbs
Constant Summary collapse
- INFINITY_ARRAY =
: [Float]
[Float::INFINITY].freeze
- RE_HTML_BLOCKS =
Regexp.union( %r{<script.*?</script>}m, /<!--.*?-->/m, %r{<style.*?</style>}m )
- RE_HTML_TAGS =
/<.*?>/m
Class Method Summary collapse
-
.abs(context, left) ⇒ Object
Return the absolute value of
left. -
.all(context, left, key = :nothing, value = :nothing) ⇒ Object
Return true if all items in left are truthy.
-
.any(context, left, key = :nothing, value = :nothing) ⇒ Object
Return true if at least one item in left is truthy.
-
.append(context, left, right) ⇒ Object
Return left concatenated with right.
-
.at_least(context, left, right) ⇒ Object
Return the maximum of
leftandright. -
.at_most(context, left, right) ⇒ Object
Return the minimum of
leftandright. - .base64_decode ⇒ Object
- .base64_encode ⇒ Object
- .base64_url_safe_decode ⇒ Object
- .base64_url_safe_encode ⇒ Object
-
.capitalize(context, left) ⇒ Object
Return left with the first character in uppercase and the rest lowercase.
-
.ceil(context, left) ⇒ Object
Return
leftrounded up to the next whole number. -
.compact(context, left, key = :nothing) ⇒ Object
Return a copy of left with nil items removed.
-
.concat(context, left, right) ⇒ Object
Return left concatenated with right, or nil if right is not an array.
-
.date(context, left, format) ⇒ Object
Format date and time object left with format.
-
.default(context, left, default = "", allow_false: false) ⇒ Object
Return left, or default if obj is
nil,falseor empty. -
.divided_by(context, left, right) ⇒ Object
Return the result of dividing
leftbyright. -
.downcase(context, left) ⇒ Object
Return left with all characters converted to lowercase.
-
.escape(context, left) ⇒ untyped?
Return left with special HTML characters replaced with their HTML-safe escape sequences.
-
.escape_once(context, left) ⇒ Object
Return left with special HTML characters replaced with their HTML-safe escape sequences.
-
.find(context, left, key, value = :nothing) ⇒ untyped?
Return the first item in left where key is equal to value, or nil if no such item exists.
-
.find_index(context, left, key, value = :nothing) ⇒ untyped?
Return the index of the first item in left where key is equal to value, or nil if no such item exists.
-
.first(context, left) ⇒ Object
Return the first item in left, or
nilif left does not have a first item. - .flat_map(context, left, key) ⇒ Object
-
.flatten(context, left, depth = nil) ⇒ Object
If left is enumerable, return left flattened to depth.
-
.floor(context, left) ⇒ Object
Return
leftrounded down to the next whole number. - .has ⇒ true, false
- .ints(obj, context) ⇒ ::Array[untyped], untyped
-
.join(context, left, sep = " ") ⇒ Object
Return the concatenation of items in left separated by sep.
-
.json(context, left, pretty: false) ⇒ Object
Return left serialized in JSON format.
-
.last(context, left) ⇒ Object
Return the last item in left, or
nilif left does not have a last item. -
.lstrip(context, left) ⇒ Object
Return left with leading whitespace removed.
- .map(context, left, key) ⇒ Object
-
.max(context, left, key = :nothing) ⇒ Object
Return the maximum numeric value found in left.
-
.min(context, left, key = :nothing) ⇒ Object
Return the minimum numeric value found in left.
-
.minus(context, left, right) ⇒ Object
Return
rightsubtracted fromleft. -
.modulo(context, left, right) ⇒ Object
Return the remainder of dividing
leftbyright. -
.newline_to_br(context, left) ⇒ Object
Return left with LF or CRLF replaced with
<br />\n. - .nil_safe_casecmp(left, right) ⇒ untyped, ...
- .numeric_compare(left, right, context) ⇒ Object
-
.plus(context, left, right) ⇒ Object
Return
rightadded toleft. -
.prepend_(context, left, right) ⇒ Object
Return right concatenated with left.
- .reject(context, left, key, value = :nothing) ⇒ Object
-
.remove(context, left, pattern) ⇒ Object
Return left with all occurrences of pattern removed.
-
.remove_first(context, left, pattern) ⇒ Object
Return left with the first occurrence of pattern removed.
-
.remove_last(context, left, pattern) ⇒ Object
Return left with the last occurrence of pattern removed.
-
.replace(context, left, pattern, replacement = "") ⇒ Object
Return left with all occurrences of pattern replaced with replacement.
-
.replace_first(context, left, pattern, replacement = "") ⇒ Object
Return left with the first occurrence of pattern replaced with replacement.
-
.replace_last(context, left, pattern, replacement) ⇒ Object
Return left with the last occurrence of pattern replaced with replacement.
-
.reverse(context, left) ⇒ Object
Return left with all items in reverse order.
-
.round(context, left, ndigits = 0) ⇒ Object
Return
leftrounded tondigitsdecimal digits. -
.rstrip(context, left) ⇒ Object
Return left with trailing whitespace removed.
-
.size(context, left) ⇒ Integer
Return the size of left, or zero if left has no size.
- .slice(context, left, start_ = :nothing, stop_ = :nothing, step_ = :nothing, start: :nothing, stop: :nothing, step: :nothing) ⇒ Object
-
.sort(context, left, key = nil) ⇒ Object
Return a sorted array of items from left.
- .sort_natural(context, left, key = :nothing) ⇒ Object
- .sort_numeric(context, left, key = :nothing) ⇒ Object
-
.split(context, left, pattern) ⇒ Object
Split left on every occurrence of pattern.
- .squish(context, left) ⇒ Object
-
.strip(context, left) ⇒ Object
Return left with leading and trailing whitespace removed.
-
.strip_html(context, left) ⇒ Object
Return left with HTML tags removed.
-
.strip_newlines(context, left) ⇒ Object
Return left with CR and LF removed.
-
.sum(context, left, key = :nothing) ⇒ Object
Return the sum of all numeric values in the input array.
-
.take(context, left, count) ⇒ Array[untyped]
Return at most count items from
_leftas a new array. -
.times(context, left, right) ⇒ Object
Return the result of multiplying
leftbyright. - .truncate(context, left, max_length = 50, ellipsis = "...") ⇒ Object
- .truncatewords(context, left, max_words = 15, ellipsis = "...") ⇒ Object
-
.uniq(context, left, key = :nothing) ⇒ Object
Deduplicate items in left.
-
.upcase(context, left) ⇒ Object
Return left with all characters converted to uppercase.
- .url_decode(context, left) ⇒ Object
- .url_encode(context, left) ⇒ Object
- .where(context, left, key, value = :nothing) ⇒ Object
-
.zip(context, left, right) ⇒ Object
Combine left with right, producing an array or pairs.
Class Method Details
.abs(context, left) ⇒ Object
Return the absolute value of left.
6 7 8 9 |
# File 'lib/luoma/filters/math.rb', line 6 def self.abs(context, left) left_ = context.to_numeric(left) context.nothing?(left_) ? left_ : left_.abs end |
.all(context, left, key = :nothing, value = :nothing) ⇒ Object
Return true if all items in left are truthy.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/luoma/filters/array.rb', line 41 def self.all(context, left, key = :nothing, value = :nothing) left = context.to_enumerable(left) case key when :nothing left.all? { |i| context.truthy?(i) } when ExpressionDrop key.expr.broadcast_with_index(left).all? { |i| context.truthy?(i) } else key = context.to_string(key) if value == :nothing left.map { |i| context.fetch(i, key) }.all? { |j| context.truthy?(j) } else left.map { |i| context.fetch(i, key) }.all? { |j| context.eq?(j, value) } end end end |
.any(context, left, key = :nothing, value = :nothing) ⇒ Object
Return true if at least one item in left is truthy.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/luoma/filters/array.rb', line 22 def self.any(context, left, key = :nothing, value = :nothing) left = context.to_enumerable(left) case key when :nothing left.any? { |i| context.truthy?(i) } when ExpressionDrop key.expr.broadcast_with_index(left).any? { |i| context.truthy?(i) } else key = context.to_string(key) if value == :nothing left.map { |i| context.fetch(i, key) }.any? { |j| context.truthy?(j) } else left.map { |i| context.fetch(i, key) }.any? { |j| context.eq?(j, value) } end end end |
.append(context, left, right) ⇒ Object
Return left concatenated with right. Coerce left and right to strings if they aren't strings already.
9 10 11 |
# File 'lib/luoma/filters/string.rb', line 9 def self.append(context, left, right) context.to_string(left) + context.to_string(right) end |
.at_least(context, left, right) ⇒ Object
Return the maximum of left and right.
12 13 14 |
# File 'lib/luoma/filters/math.rb', line 12 def self.at_least(context, left, right) [context.to_numeric(left, default: nil), context.to_numeric(right, default: nil)].compact.max || :nothing end |
.at_most(context, left, right) ⇒ Object
Return the minimum of left and right.
17 18 19 |
# File 'lib/luoma/filters/math.rb', line 17 def self.at_most(context, left, right) [context.to_numeric(left, default: nil), context.to_numeric(right, default: nil)].compact.min || :nothing end |
.base64_decode ⇒ Object
95 |
# File 'sig/luoma/filters/string.rbs', line 95
def self.base64_decode: (FilterContext context, untyped left) -> untyped
|
.base64_encode ⇒ Object
93 |
# File 'sig/luoma/filters/string.rbs', line 93
def self.base64_encode: (FilterContext context, untyped left) -> untyped
|
.base64_url_safe_decode ⇒ Object
99 |
# File 'sig/luoma/filters/string.rbs', line 99
def self.base64_url_safe_decode: (FilterContext context, untyped left) -> untyped
|
.base64_url_safe_encode ⇒ Object
97 |
# File 'sig/luoma/filters/string.rbs', line 97
def self.base64_url_safe_encode: (FilterContext context, untyped left) -> untyped
|
.capitalize(context, left) ⇒ Object
Return left with the first character in uppercase and the rest lowercase. Coerce left to a string if it is not one already.
15 16 17 |
# File 'lib/luoma/filters/string.rb', line 15 def self.capitalize(context, left) context.to_string(left).capitalize end |
.ceil(context, left) ⇒ Object
Return left rounded up to the next whole number.
22 23 24 25 |
# File 'lib/luoma/filters/math.rb', line 22 def self.ceil(context, left) left_ = context.to_numeric(left) context.nothing?(left_) ? left_ : left_.ceil end |
.compact(context, left, key = :nothing) ⇒ Object
Return a copy of left with nil items removed. Coerce left to an array-like object if it is not one already.
If key is given, assume items in left are hash-like and remove items from left
where item.fetch(key, nil) is nil.
If key is not :nothing, coerce it to a string before calling fetch on items in
left.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/luoma/filters/array.rb', line 67 def self.compact(context, left, key = :nothing) left = context.to_enumerable(left) case key when :nothing left.reject { |value| value.nil? || context.nothing?(value) } when ExpressionDrop left.each_with_index.reject do |item, index| value = key.expr.call_with_index(item, index) value.nil? || context.nothing?(value) end.map(&:first) else left.reject do |item| value = context.fetch(item, key) value.nil? || context.nothing?(value) end end end |
.concat(context, left, right) ⇒ Object
Return left concatenated with right, or nil if right is not an array. Coerce left to an array if it isn't an array already.
88 89 90 |
# File 'lib/luoma/filters/array.rb', line 88 def self.concat(context, left, right) context.to_a(left).concat(context.to_a(right)) end |
.date(context, left, format) ⇒ Object
Format date and time object left with format.
Coerce left to a Time if it is not a time-like object already.
Coerce format to a string if it is not a string already.
8 9 10 11 12 13 14 15 16 17 |
# File 'lib/luoma/filters/date.rb', line 8 def self.date(context, left, format) format = context.to_string(format) return left if format.empty? if (date = context.to_date(left)) date.strftime(format) else left end end |
.default(context, left, default = "", allow_false: false) ⇒ Object
Return left, or default if obj is nil, false or empty.
If allow_false is true, left is returned if left is false.
7 8 9 10 11 12 13 14 |
# File 'lib/luoma/filters/default.rb', line 7 def self.default(context, left, default = "", allow_false: false) return default if left.is_a?(FalsyStrictUndefinedDrop) left_ = left.is_a?(Drop) ? left.to_primitive(:boolean, context.render_context) : left return left_ if allow_false && left_ == false !context.truthy?(left_) || Luoma::Predicates.empty?(context.render_context, left_) ? default : left_ end |
.divided_by(context, left, right) ⇒ Object
Return the result of dividing left by right.
If both left and right are integers, integer division is performed.
29 30 31 32 33 34 35 36 |
# File 'lib/luoma/filters/math.rb', line 29 def self.divided_by(context, left, right) lhs = context.to_numeric(left, default: :nothing) rhs = context.to_numeric(right, default: :nothing) return :nothing if context.nothing?(lhs) || context.nothing?(rhs) || rhs.zero? # steep:ignore result = lhs.to_d / rhs # steep:ignore result.frac.zero? && lhs.is_a?(::Integer) && rhs.is_a?(::Integer) ? result.to_i : result end |
.downcase(context, left) ⇒ Object
Return left with all characters converted to lowercase. Coerce left to a string if it is not one already.
21 22 23 |
# File 'lib/luoma/filters/string.rb', line 21 def self.downcase(context, left) context.to_string(left).downcase end |
.escape(context, left) ⇒ untyped?
Return left with special HTML characters replaced with their HTML-safe escape sequences. Coerce left to a string if it is not one already.
33 34 35 |
# File 'lib/luoma/filters/string.rb', line 33 def self.escape(context, left) CGI.escape_html(context.to_string(left)) unless left.nil? end |
.escape_once(context, left) ⇒ Object
Return left with special HTML characters replaced with their HTML-safe escape sequences. Coerce left to a string if it is not one already.
It is safe to use escape_once on string values that already contain HTML-escape sequences.
41 42 43 |
# File 'lib/luoma/filters/string.rb', line 41 def self.escape_once(context, left) CGI.escape_html(CGI.unescape_html(context.to_string(left))) end |
.find(context, left, key, value = :nothing) ⇒ untyped?
Return the first item in left where key is equal to value, or nil if no such item exists.
If value is not given, return the first item where key is truthy.
If key is a Lambda expression, evaluate the expression for each item in left and return the first item where the expression results in a truthy value, ignoring value.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/luoma/filters/array.rb', line 100 def self.find(context, left, key, value = :nothing) left = context.to_enumerable(left) if key.is_a?(ExpressionDrop) key.expr.broadcast_with_index(left).zip(left) do |r, i| return i if context.truthy?(r) end elsif value == :nothing key_ = context.to_string(key) left.each do |item| return item if context.truthy?(context.fetch(item, key_)) || context.eq?(item, key) end else key = context.to_string(key) left.each do |item| return item if context.eq?(context.fetch(item, key), value) || context.eq?(item, value) end end nil end |
.find_index(context, left, key, value = :nothing) ⇒ untyped?
Return the index of the first item in left where key is equal to value, or nil if no such item exists.
If value is not given, return the index of the first item where key is truthy.
If key is a Lambda expression, evaluate the expression for each item in left and return the index of the first item where the expression results in a truthy value, ignoring value.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/luoma/filters/array.rb', line 131 def self.find_index(context, left, key, value = :nothing) left = context.to_enumerable(left) if key.is_a?(ExpressionDrop) key.expr.broadcast_with_index(left).each_with_index do |item, index| return index if context.truthy?(item) end elsif value == :nothing key_ = context.to_string(key) left.each_with_index do |item, index| return index if context.truthy?(context.fetch(item, key_)) || context.eq?(item, key) end else key = context.to_string(key) left.each_with_index do |item, index| return index if context.eq?(context.fetch(item, key), value) || context.eq?(item, value) end end nil end |
.first(context, left) ⇒ Object
Return the first item in left, or nil if left does not have a first item.
157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/luoma/filters/array.rb', line 157 def self.first(context, left) case left when String left.empty? ? :nothing : left[0] when Hash, Array left.empty? ? :nothing : left.first else left_ = context.to_a(left) left_.empty? ? :nothing : left_.first end end |
.flat_map(context, left, key) ⇒ Object
196 197 198 199 200 201 202 203 204 205 |
# File 'lib/luoma/filters/array.rb', line 196 def self.flat_map(context, left, key) left = context.to_enumerable(left) if key.is_a?(ExpressionDrop) key.expr.broadcast_with_index(left).flatten!(1) else key = context.to_string(key) left.flat_map { |item| context.fetch(item, key) } end end |
.flatten(context, left, depth = nil) ⇒ Object
If left is enumerable, return left flattened to depth. Otherwise return left.
13 14 15 16 17 18 19 |
# File 'lib/luoma/filters/array.rb', line 13 def self.flatten(context, left, depth = nil) left = context.to_enumerable(left) context.to_a(left).flatten( depth.nil? ? depth : context.to_numeric(depth, default: 1).to_i # steep:ignore ) end |
.floor(context, left) ⇒ Object
Return left rounded down to the next whole number.
46 47 48 49 |
# File 'lib/luoma/filters/math.rb', line 46 def self.floor(context, left) left_ = context.to_numeric(left) context.nothing?(left_) ? left_ : left_.floor end |
.has ⇒ true, false
35 |
# File 'sig/luoma/filters/array.rbs', line 35
def self.has: (FilterContext context, untyped left, untyped key, ?untyped? value) -> (true | false)
|
.ints(obj, context) ⇒ ::Array[untyped], untyped
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/luoma/filters/sort.rb', line 102 def self.ints(obj, context) if obj.is_a?(Integer) || obj.is_a?(Float) || obj.is_a?(BigDecimal) [obj] else numeric = context.to_string(obj).scan(/(?<=\.)0+|-?\d+/) return INFINITY_ARRAY if numeric.nil? || numeric.empty? numeric.map(&:to_i) # steep:ignore end end |
.join(context, left, sep = " ") ⇒ Object
Return the concatenation of items in left separated by sep. Coerce items in left to strings if they aren't strings already.
7 8 9 |
# File 'lib/luoma/filters/array.rb', line 7 def self.join(context, left, sep = " ") context.to_enumerable(left).map { |item| context.to_string(item) }.join(context.to_string(sep)) end |
.json(context, left, pretty: false) ⇒ Object
Return left serialized in JSON format.
6 7 8 9 10 11 12 |
# File 'lib/luoma/filters/json.rb', line 6 def self.json(context, left, pretty: false) if pretty JSON.pretty_generate(left) else JSON.generate(left) end end |
.last(context, left) ⇒ Object
Return the last item in left, or nil if left does not have a last item.
173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/luoma/filters/array.rb', line 173 def self.last(context, left) case left when String left.empty? ? :nothing : left[-1] when Array left.empty? ? :nothing : left.last else left_ = context.to_a(left) left_.empty? ? :nothing : left_.last end end |
.lstrip(context, left) ⇒ Object
Return left with leading whitespace removed. Coerce left to a string if it is not one already.
47 48 49 |
# File 'lib/luoma/filters/string.rb', line 47 def self.lstrip(context, left) context.to_string(left).lstrip end |
.map(context, left, key) ⇒ Object
185 186 187 188 189 190 191 192 193 194 |
# File 'lib/luoma/filters/array.rb', line 185 def self.map(context, left, key) left = context.to_enumerable(left) if key.is_a?(ExpressionDrop) key.expr.broadcast_with_index(left) else key = context.to_string(key) left.map { |item| context.fetch(item, key) } end end |
.max(context, left, key = :nothing) ⇒ Object
Return the maximum numeric value found in left. Coerce left to an array if it's not an array already.
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/luoma/filters/array.rb', line 341 def self.max(context, left, key = :nothing) left = context.to_enumerable(left) # @type var max_: ^(Enumerable[untyped]) ?{ (Integer, untyped) -> untyped } -> untyped max_ = lambda do |enum, &key| best_item = nil # untyped best_value = nil #: Numeric? enum.each_with_index do |item, index| value = context.to_numeric(key ? key.call(item, index) : item, default: nil) next if value.nil? if best_value.nil? || context.lt?(best_value, value) best_item = item best_value = value end end best_item end if key == :nothing max_.call(left) elsif key.is_a?(ExpressionDrop) max_.call(left) { |item, index| key.expr.call_with_index(item, index) } else key = context.to_string(key) max_.call(left) { |item, _index| context.fetch(item, key, default: nil) } end end |
.min(context, left, key = :nothing) ⇒ Object
Return the minimum numeric value found in left. Coerce left to an array if it's not an array already.
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/luoma/filters/array.rb', line 308 def self.min(context, left, key = :nothing) left = context.to_enumerable(left) # @type var min_: ^(Enumerable[untyped]) ?{ (untyped, Integer) -> untyped } -> untyped min_ = lambda do |enum, &key| best_item = nil #:untyped best_value = nil #: Numeric? enum.each_with_index do |item, index| value = context.to_numeric(key ? key.call(item, index) : item, default: nil) next if value.nil? if best_value.nil? || context.lt?(value, best_value) best_item = item best_value = value end end best_item end if key == :nothing min_.call(left) elsif key.is_a?(ExpressionDrop) min_.call(left) { |item, index| key.expr.call_with_index(item, index) } else key = context.to_string(key) min_.call(left) { |item, _index| context.fetch(item, key, default: nil) } end end |
.minus(context, left, right) ⇒ Object
Return right subtracted from left.
52 53 54 55 56 |
# File 'lib/luoma/filters/math.rb', line 52 def self.minus(context, left, right) lhs = context.to_numeric(left, default: :nothing) rhs = context.to_numeric(right, default: :nothing) context.nothing?(lhs) || context.nothing?(rhs) ? :nothing : lhs - rhs # steep:ignore end |
.modulo(context, left, right) ⇒ Object
Return the remainder of dividing left by right.
59 60 61 62 63 64 65 66 |
# File 'lib/luoma/filters/math.rb', line 59 def self.modulo(context, left, right) lhs = context.to_numeric(left, default: :nothing) rhs = context.to_numeric(right, default: :nothing) return :nothing if context.nothing?(lhs) || context.nothing?(rhs) || rhs.zero? # steep:ignore result = lhs.to_d % rhs # steep:ignore result.frac.zero? && lhs.is_a?(::Integer) && rhs.is_a?(::Integer) ? result.to_i : result end |
.newline_to_br(context, left) ⇒ Object
Return left with LF or CRLF replaced with <br />\n.
64 65 66 |
# File 'lib/luoma/filters/string.rb', line 64 def self.newline_to_br(context, left) context.to_string(left).gsub(/\r?\n/, "<br />\n") end |
.nil_safe_casecmp(left, right) ⇒ untyped, ...
87 88 89 90 91 92 93 94 95 |
# File 'lib/luoma/filters/sort.rb', line 87 def self.nil_safe_casecmp(left, right) if !left.nil? && !right.nil? left.to_s.casecmp(right.to_s) elsif left.nil? && right.nil? 0 else left.nil? ? 1 : -1 end end |
.numeric_compare(left, right, context) ⇒ Object
97 98 99 100 |
# File 'lib/luoma/filters/sort.rb', line 97 def self.numeric_compare(left, right, context) res = ints(left, context) <=> ints(right, context) res || -1 end |
.plus(context, left, right) ⇒ Object
Return right added to left.
69 70 71 72 73 |
# File 'lib/luoma/filters/math.rb', line 69 def self.plus(context, left, right) lhs = context.to_numeric(left, default: :nothing) rhs = context.to_numeric(right, default: :nothing) context.nothing?(lhs) || context.nothing?(rhs) ? :nothing : lhs + rhs # steep:ignore end |
.prepend_(context, left, right) ⇒ Object
Return right concatenated with left. Coerce left and right to strings if they aren't strings already.
70 71 72 |
# File 'lib/luoma/filters/string.rb', line 70 def self.prepend_(context, left, right) context.to_string(right) + context.to_string(left) end |
.reject(context, left, key, value = :nothing) ⇒ Object
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/luoma/filters/array.rb', line 213 def self.reject(context, left, key, value = :nothing) left = context.to_enumerable(left) if key.is_a?(ExpressionDrop) left.each_with_index.reject do |item, index| context.truthy?(key.expr.call_with_index(item, index)) end.map(&:first) elsif value == :nothing key_ = context.to_string(key) left.reject do |item| context.truthy?(context.fetch(item, key_)) || context.eq?(item, key) end else key = context.to_string(key) left.reject do |item| context.eq?(context.fetch(item, key), value) || context.eq?(item, value) end end end |
.remove(context, left, pattern) ⇒ Object
Return left with all occurrences of pattern removed. All arguments are coerced to strings if they aren't strings already.
99 100 101 |
# File 'lib/luoma/filters/string.rb', line 99 def self.remove(context, left, pattern) context.to_string(left).gsub(context.to_string(pattern), context.to_string("")) end |
.remove_first(context, left, pattern) ⇒ Object
Return left with the first occurrence of pattern removed. All arguments are coerced to strings if they aren't strings already.
105 106 107 |
# File 'lib/luoma/filters/string.rb', line 105 def self.remove_first(context, left, pattern) context.to_string(left).sub(context.to_string(pattern), context.to_string("")) end |
.remove_last(context, left, pattern) ⇒ Object
Return left with the last occurrence of pattern removed. All arguments are coerced to strings if they aren't strings already.
111 112 113 114 115 116 117 118 |
# File 'lib/luoma/filters/string.rb', line 111 def self.remove_last(context, left, pattern) return left if context.nothing?(pattern) head, match, tail = context.to_string(left).rpartition(context.to_string(pattern)) return left if match.empty? head + tail end |
.replace(context, left, pattern, replacement = "") ⇒ Object
Return left with all occurrences of pattern replaced with replacement. All arguments are coerced to strings if they aren't strings already.
76 77 78 |
# File 'lib/luoma/filters/string.rb', line 76 def self.replace(context, left, pattern, replacement = "") context.to_string(left).gsub(context.to_string(pattern), context.to_string(replacement)) end |
.replace_first(context, left, pattern, replacement = "") ⇒ Object
Return left with the first occurrence of pattern replaced with replacement. All arguments are coerced to strings if they aren't strings already.
82 83 84 |
# File 'lib/luoma/filters/string.rb', line 82 def self.replace_first(context, left, pattern, replacement = "") context.to_string(left).sub(context.to_string(pattern), context.to_string(replacement)) end |
.replace_last(context, left, pattern, replacement) ⇒ Object
Return left with the last occurrence of pattern replaced with replacement. All arguments are coerced to strings if they aren't strings already.
88 89 90 91 92 93 94 95 |
# File 'lib/luoma/filters/string.rb', line 88 def self.replace_last(context, left, pattern, replacement) return left + replacement if context.nothing?(pattern) head, match, tail = context.to_string(left).rpartition(context.to_string(pattern)) return left if match.empty? head + context.to_string(replacement) + tail end |
.reverse(context, left) ⇒ Object
Return left with all items in reverse order. Coerce left to an array if it isn't an array already.
209 210 211 |
# File 'lib/luoma/filters/array.rb', line 209 def self.reverse(context, left) context.to_a(left).reverse end |
.round(context, left, ndigits = 0) ⇒ Object
Return left rounded to ndigits decimal digits.
76 77 78 79 80 81 82 |
# File 'lib/luoma/filters/math.rb', line 76 def self.round(context, left, ndigits = 0) left_ = context.to_numeric(left) return left_ if context.nothing?(left_) return left_.round if ndigits == 0 # rubocop:disable Style/NumericPredicate left_.round(context.to_i(ndigits, default: 0)) end |
.rstrip(context, left) ⇒ Object
Return left with trailing whitespace removed. Coerce left to a string if it is not one already.
53 54 55 |
# File 'lib/luoma/filters/string.rb', line 53 def self.rstrip(context, left) context.to_string(left).rstrip end |
.size(context, left) ⇒ Integer
Return the size of left, or zero if left has no size.
6 7 8 9 10 11 |
# File 'lib/luoma/filters/size.rb', line 6 def self.size(context, left) return left.length(context.render_context) if left.is_a?(Drop) return left.size if left.respond_to?(:size) 0 end |
.slice(context, left, start_ = :nothing, stop_ = :nothing, step_ = :nothing, start: :nothing, stop: :nothing, step: :nothing) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/luoma/filters/slice.rb', line 5 def self.slice( context, left, start_ = :nothing, stop_ = :nothing, step_ = :nothing, start: :nothing, stop: :nothing, step: :nothing ) left = context.to_a(left) unless left.is_a?(String) || left.is_a?(Drop) length = left.is_a?(Drop) ? left.length(context.render_context) : left.length # Give priority to keyword arguments, default to nil if neither are given. start = start_ == :nothing ? nil : start_ if start == :nothing stop = stop_ == :nothing ? nil : stop_ if stop == :nothing step = step_ == :nothing ? nil : step_ if step == :nothing step = context.to_i(step || 1, default: 0) return [] if length.zero? || step.zero? start = context.to_i(start, default: 0) unless start.nil? stop = context.to_i(stop, default: 0) unless stop.nil? normalized_start = if start.nil? step.negative? ? length - 1 : 0 elsif start&.negative? [length + start, 0].max else [start, length - 1].min end normalized_stop = if stop.nil? step.negative? ? -1 : length elsif stop&.negative? [length + stop, -1].max else [stop, length].min end if left.is_a?(Drop) left.slice(normalized_start, normalized_stop, step) else # This does not work for strings. # left[(normalized_start...normalized_stop).step(step)] # # But this does. (normalized_start...normalized_stop).step(step).map { |i| left[i] } end end |
.sort(context, left, key = nil) ⇒ Object
Return a sorted array of items from left. If left contains incomparable items, return left as an array with its original ordering.
Coerce left to an array if it's not one already.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/luoma/filters/sort.rb', line 12 def self.sort(context, left, key = nil) left = context.to_a(left) # @type var sort_: ^(Array[untyped]) ?{ (untyped, Integer) -> untyped } -> Array[untyped] sort_ = lambda do |array, &key| decorated = array.each_with_index.map do |item, index| [item, key ? key.call(item, index) : item] end begin decorated.sort! do |(_, a), (_, b)| cmp = context.cmp(a, b) case cmp when nil if a.nil? || context.nothing?(a) 1 elsif b.nil? || context.nothing?(b) -1 else raise IncomparableValuesError end else cmp end end rescue IncomparableValuesError raise LuomaError.new("Cannot sort incomparable values") if context.render_context.env.strict return array # rubocop:disable Lint/NoReturnInBeginEndBlocks end decorated.map!(&:first) end if key.nil? || context.nothing?(key) sort_.call(left) elsif key.is_a?(ExpressionDrop) sort_.call(left) { |item, index| key.expr.call_with_index(item, index) } else key = context.to_string(key) sort_.call(left) { |item, _index| context.fetch(item, key) } end end |
.sort_natural(context, left, key = :nothing) ⇒ Object
57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/luoma/filters/sort.rb', line 57 def self.sort_natural(context, left, key = :nothing) left = context.to_enumerable(left) if key.nil? || context.nothing?(key) left.sort { |a, b| nil_safe_casecmp(a, b) } elsif key.is_a?(ExpressionDrop) key.expr.broadcast_with_index(left).zip(left).sort do |a, b| nil_safe_casecmp(a.first, b.first) end.map(&:last) else key = context.to_string(key) left.sort { |a, b| nil_safe_casecmp(context.fetch(a, key), context.fetch(b, key)) } end end |
.sort_numeric(context, left, key = :nothing) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/luoma/filters/sort.rb', line 72 def self.sort_numeric(context, left, key = :nothing) left = context.to_enumerable(left) if key.nil? || context.nothing?(key) left.sort { |a, b| numeric_compare(a, b, context) } elsif key.is_a?(ExpressionDrop) key.expr.broadcast_with_index(left).zip(left).sort do |a, b| numeric_compare(a.first, b.first, context) end.map(&:last) else key = context.to_string(key) left.sort { |a, b| numeric_compare(context.fetch(a, key), context.fetch(b, key), context) } end end |
.split(context, left, pattern) ⇒ Object
Split left on every occurrence of pattern.
121 122 123 |
# File 'lib/luoma/filters/string.rb', line 121 def self.split(context, left, pattern) context.to_string(left).split(context.to_string(pattern)) end |
.squish(context, left) ⇒ Object
182 183 184 |
# File 'lib/luoma/filters/string.rb', line 182 def self.squish(context, left) context.to_string(left).strip.gsub(/\s+/, " ") unless left.nil? end |
.strip(context, left) ⇒ Object
Return left with leading and trailing whitespace removed. Coerce left to a string if it is not one already.
59 60 61 |
# File 'lib/luoma/filters/string.rb', line 59 def self.strip(context, left) context.to_string(left).strip end |
.strip_html(context, left) ⇒ Object
Return left with HTML tags removed.
134 135 136 |
# File 'lib/luoma/filters/string.rb', line 134 def self.strip_html(context, left) context.to_string(left).gsub(RE_HTML_BLOCKS, "").gsub(RE_HTML_TAGS, "") end |
.strip_newlines(context, left) ⇒ Object
Return left with CR and LF removed.
139 140 141 |
# File 'lib/luoma/filters/string.rb', line 139 def self.strip_newlines(context, left) context.to_string(left).gsub(/\r?\n/, "") end |
.sum(context, left, key = :nothing) ⇒ Object
Return the sum of all numeric values in the input array.
287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/luoma/filters/array.rb', line 287 def self.sum(context, left, key = :nothing) left = context.to_enumerable(left) if key == :nothing left.sum { |v| context.to_numeric(v, default: 0) } elsif key.is_a?(ExpressionDrop) key.expr.broadcast_with_index(left).sum { |v| context.to_numeric(v, default: 0) } else key = context.to_string(key) left.sum { |v| context.to_numeric(context.fetch(v, key, default: 0)) } end end |
.take(context, left, count) ⇒ Array[untyped]
Return at most count items from _left as a new array.
Coerce left to an array if it's not an array already.
375 376 377 378 379 |
# File 'lib/luoma/filters/array.rb', line 375 def self.take(context, left, count) count_ = context.to_i(count, default: 0) count_ = 0 if count_.negative? context.to_enumerable(left).take(count_) end |
.times(context, left, right) ⇒ Object
Return the result of multiplying left by right.
39 40 41 42 43 |
# File 'lib/luoma/filters/math.rb', line 39 def self.times(context, left, right) lhs = context.to_numeric(left, default: :nothing) rhs = context.to_numeric(right, default: :nothing) context.nothing?(lhs) || context.nothing?(rhs) ? :nothing : lhs * rhs # steep:ignore end |
.truncate(context, left, max_length = 50, ellipsis = "...") ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/luoma/filters/string.rb', line 143 def self.truncate(context, left, max_length = 50, ellipsis = "...") return if left.nil? || context.nothing?(left) left = context.to_string(left) max_length = context.to_i(max_length) return left if left.length <= max_length ellipsis = context.to_string(ellipsis) return ellipsis[0, max_length] if ellipsis.length >= max_length "#{left[0...(max_length - ellipsis.length)]}#{ellipsis}" end |
.truncatewords(context, left, max_words = 15, ellipsis = "...") ⇒ Object
156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/luoma/filters/string.rb', line 156 def self.truncatewords(context, left, max_words = 15, ellipsis = "...") return if left.nil? || context.nothing?(left) left = context.to_string(left) max_words = context.to_i(max_words).clamp(1, 10_000) words = left.split(" ", max_words + 1) return left if words.length <= max_words ellipsis = context.to_string(ellipsis) words.pop "#{words.join(" ")}#{ellipsis}" end |
.uniq(context, left, key = :nothing) ⇒ Object
Deduplicate items in left. Coerce left to an array if it isn't an array already.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/luoma/filters/array.rb', line 255 def self.uniq(context, left, key = :nothing) left = context.to_a(left) # Like Array#uniq but using our own comparator, context#eq?. # @type var uniq_: ^(Array[untyped]) ?{ (untyped, Integer) -> untyped } -> Array[untyped] uniq_ = lambda do |array, &key| decorated = [] #: Array[untyped] result = [] #: Array[untyped] array.each_with_index do |item, index| value = key ? key.call(item, index) : item unless decorated.any? { |existing| context.eq?(existing, value) } decorated << value result << item end end result end if key == :nothing uniq_.call(left) elsif key.is_a?(ExpressionDrop) uniq_.call(left) { |item, index| key.expr.call_with_index(item, index) } else key = context.to_string(key) uniq_.call(left) { |item, _index| context.fetch(item, key) } end end |
.upcase(context, left) ⇒ Object
Return left with all characters converted to uppercase. Coerce left to a string if it is not one already.
27 28 29 |
# File 'lib/luoma/filters/string.rb', line 27 def self.upcase(context, left) context.to_string(left).upcase end |
.url_decode(context, left) ⇒ Object
173 174 175 176 177 178 179 180 |
# File 'lib/luoma/filters/string.rb', line 173 def self.url_decode(context, left) return if left.nil? || context.nothing?(left) decoded = CGI.unescape(context.to_string(left)) raise context.argument_error("invalid byte sequence") unless decoded.valid_encoding? decoded end |
.url_encode(context, left) ⇒ Object
169 170 171 |
# File 'lib/luoma/filters/string.rb', line 169 def self.url_encode(context, left) CGI.escape(context.to_string(left)) unless left.nil? || context.nothing?(left) end |
.where(context, left, key, value = :nothing) ⇒ Object
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/luoma/filters/array.rb', line 233 def self.where(context, left, key, value = :nothing) left = context.to_enumerable(left) if key.is_a?(ExpressionDrop) left.each_with_index.filter do |item, index| context.truthy?(key.expr.call_with_index(item, index)) end.map(&:first) elsif value == :nothing key_ = context.to_string(key) left.filter do |item| context.truthy?(context.fetch(item, key_)) || context.eq?(item, key) end else key = context.to_string(key) left.filter do |item| context.eq?(context.fetch(item, key), value) || context.eq?(item, value) end end end |
.zip(context, left, right) ⇒ Object
Combine left with right, producing an array or pairs. Coerce left and right to arrays if they aren't arrays already.
302 303 304 |
# File 'lib/luoma/filters/array.rb', line 302 def self.zip(context, left, right) context.to_a(left).zip(context.to_a(right)) end |