-
-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide a Liquid find tag as easier alternative to where_exp (#101)
* Provide a Liquid find tag as easier alternative to where_exp * Rebase, allow commas inside of strings for find tag
- Loading branch information
1 parent
9603e8c
commit 991a0ef
Showing
5 changed files
with
198 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
bridgetown-core/lib/bridgetown-core/filters/condition_helpers.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# frozen_string_literal: true | ||
|
||
module Bridgetown | ||
module Filters | ||
module ConditionHelpers | ||
# ----------- The following set of code was *adapted* from Liquid::If | ||
# ----------- ref: https://git.io/vp6K6 | ||
|
||
# Parse a string to a Liquid Condition | ||
def parse_condition(exp) | ||
parser = Liquid::Parser.new(exp) | ||
condition = parse_binary_comparison(parser) | ||
|
||
parser.consume(:end_of_string) | ||
condition | ||
end | ||
|
||
# Generate a Liquid::Condition object from a Liquid::Parser object additionally processing | ||
# the parsed expression based on whether the expression consists of binary operations with | ||
# Liquid operators `and` or `or` | ||
# | ||
# - parser: an instance of Liquid::Parser | ||
# | ||
# Returns an instance of Liquid::Condition | ||
def parse_binary_comparison(parser) | ||
condition = parse_comparison(parser) | ||
first_condition = condition | ||
while (binary_operator = parser.id?("and") || parser.id?("or")) | ||
child_condition = parse_comparison(parser) | ||
condition.send(binary_operator, child_condition) | ||
condition = child_condition | ||
end | ||
first_condition | ||
end | ||
|
||
# Generates a Liquid::Condition object from a Liquid::Parser object based | ||
# on whether the parsed expression involves a "comparison" operator | ||
# (e.g. <, ==, >, !=, etc) | ||
# | ||
# - parser: an instance of Liquid::Parser | ||
# | ||
# Returns an instance of Liquid::Condition | ||
def parse_comparison(parser) | ||
left_operand = Liquid::Expression.parse(parser.expression) | ||
operator = parser.consume?(:comparison) | ||
|
||
# No comparison-operator detected. Initialize a Liquid::Condition using only left operand | ||
return Liquid::Condition.new(left_operand) unless operator | ||
|
||
# Parse what remained after extracting the left operand and the `:comparison` operator | ||
# and initialize a Liquid::Condition object using the operands and the comparison-operator | ||
Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression)) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# frozen_string_literal: true | ||
|
||
module Bridgetown | ||
module Tags | ||
class Find < Liquid::Tag | ||
include Bridgetown::Filters::ConditionHelpers | ||
include Bridgetown::LiquidExtensions | ||
|
||
SYNTAX = %r!^(.*?) (where|in) (.*?),(.*)$!.freeze | ||
CONDITIONS_SEP = "~FINDSEP~" | ||
|
||
def initialize(tag_name, markup, tokens) | ||
super | ||
if markup.strip =~ SYNTAX | ||
@new_var_name = Regexp.last_match(1).strip | ||
@single_or_group = Regexp.last_match(2) | ||
@arr_name = Regexp.last_match(3).strip | ||
@conditions = process_conditions(Regexp.last_match(4).strip) | ||
else | ||
raise SyntaxError, <<~MSG | ||
Syntax Error in tag 'find' while parsing the following markup: | ||
#{markup} | ||
Valid syntax: find <varname> where|in <array>, <condition(s)> | ||
MSG | ||
end | ||
end | ||
|
||
def render(context) | ||
@group = lookup_variable(context, @arr_name) | ||
return "" unless @group.respond_to?(:select) | ||
|
||
@group = @group.values if @group.is_a?(Hash) | ||
|
||
expression = @conditions.split(CONDITIONS_SEP).map do |condition| | ||
"__find_tag_item__.#{condition.strip}" | ||
end.join(" and ") | ||
@liquid_condition = parse_condition(expression) | ||
|
||
context[@new_var_name] = if @single_or_group == "where" | ||
group_evaluate(context) | ||
else | ||
single_evaluate(context) | ||
end | ||
|
||
"" | ||
end | ||
|
||
private | ||
|
||
def process_conditions(conditions) | ||
processed_conditions = +"" | ||
in_quotes = false | ||
|
||
conditions.each_char do |c| | ||
in_quotes = !in_quotes if c == '"' | ||
|
||
processed_conditions << (c == "," && !in_quotes ? CONDITIONS_SEP : c) | ||
end | ||
|
||
processed_conditions | ||
end | ||
|
||
def group_evaluate(context) | ||
context.stack do | ||
@group.select do |object| | ||
context["__find_tag_item__"] = object | ||
@liquid_condition.evaluate(context) | ||
end | ||
end || [] | ||
end | ||
|
||
def single_evaluate(context) | ||
context.stack do | ||
@group.find do |object| | ||
context["__find_tag_item__"] = object | ||
@liquid_condition.evaluate(context) | ||
end | ||
end || nil | ||
end | ||
end | ||
end | ||
end | ||
|
||
Liquid::Template.register_tag("find", Bridgetown::Tags::Find) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters