liquidum 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +39 -0
- data/Rakefile +37 -0
- data/app/controllers/liquidum/application_controller.rb +5 -0
- data/app/controllers/liquidum/completions_controller.rb +15 -0
- data/app/controllers/liquidum/sandbox_controller.rb +28 -0
- data/app/helpers/liquidum/application_helper.rb +4 -0
- data/app/jobs/liquidum/application_job.rb +4 -0
- data/app/models/concerns/liquidum/to_liquid.rb +17 -0
- data/app/views/liquidum/sandbox/index.html.slim +13 -0
- data/config/routes.rb +4 -0
- data/lib/liquidum/configuration.rb +23 -0
- data/lib/liquidum/drop.rb +69 -0
- data/lib/liquidum/engine.rb +15 -0
- data/lib/liquidum/liquid/drops/active_storage_attached_many_drop.rb +14 -0
- data/lib/liquidum/liquid/drops/active_storage_attached_one_drop.rb +21 -0
- data/lib/liquidum/liquid/drops/active_storage_attachment_drop.rb +19 -0
- data/lib/liquidum/liquid/drops/enumerator_lazy_drop.rb +7 -0
- data/lib/liquidum/liquid/filters/array_wrap_filter.rb +14 -0
- data/lib/liquidum/liquid/filters/encode_filter.rb +14 -0
- data/lib/liquidum/liquid/filters/format_filter.rb +14 -0
- data/lib/liquidum/liquid/filters/localize_filter.rb +15 -0
- data/lib/liquidum/liquid/filters/strptime_filter.rb +14 -0
- data/lib/liquidum/liquid/filters/sum_filter.rb +16 -0
- data/lib/liquidum/liquid/filters/timezone_filter.rb +18 -0
- data/lib/liquidum/liquid/filters/to_filter.rb +46 -0
- data/lib/liquidum/liquid/filters/translate_filter.rb +45 -0
- data/lib/liquidum/liquid/filters/where_exp_filter.rb +70 -0
- data/lib/liquidum/liquid/liquid_helpers.rb +163 -0
- data/lib/liquidum/liquid/liquid_template_extensions.rb +34 -0
- data/lib/liquidum/liquid/liquidum_block.rb +5 -0
- data/lib/liquidum/liquid/liquidum_tag.rb +5 -0
- data/lib/liquidum/liquid/parser.rb +53 -0
- data/lib/liquidum/liquid/tags/helper_tag.rb +30 -0
- data/lib/liquidum/liquid/tags/render_tag.rb +19 -0
- data/lib/liquidum/version.rb +5 -0
- data/lib/liquidum.rb +77 -0
- metadata +318 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Liquidum
|
4
|
+
module LiquidHelpers
|
5
|
+
private
|
6
|
+
|
7
|
+
def initialize(tag, args, tokens)
|
8
|
+
super
|
9
|
+
@blank = false
|
10
|
+
begin
|
11
|
+
@args = Liquidum::Tag::Parser.new(args).args
|
12
|
+
rescue Parslet::ParseFailed => e
|
13
|
+
raise Parslet::ParseFailed, e.message + " (#{tag} #{args})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def render(context)
|
18
|
+
@context = context
|
19
|
+
end
|
20
|
+
|
21
|
+
def render_body
|
22
|
+
@body.render(@context)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return named argument
|
26
|
+
def arg(name)
|
27
|
+
raise 'No @context set' unless @context
|
28
|
+
|
29
|
+
attr = @args.find { |a| a[:attr] == name.to_s }
|
30
|
+
return unless attr
|
31
|
+
|
32
|
+
if attr.key? :value
|
33
|
+
attr[:value].to_s
|
34
|
+
elsif attr.key? :lvalue
|
35
|
+
lookup(@context, attr[:lvalue].to_s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the first argument - usually reserved for literal or quoted values, not for attribute value pairs
|
40
|
+
# When the first attribute is a pair, it will return nil
|
41
|
+
def argv1
|
42
|
+
raise 'No @context set' unless @context
|
43
|
+
|
44
|
+
argv1 = @args[0]
|
45
|
+
return unless argv1
|
46
|
+
|
47
|
+
if argv1.key? :quoted
|
48
|
+
argv1[:quoted].to_s
|
49
|
+
elsif argv1.key? :literal
|
50
|
+
@context.key?(argv1[:literal].to_s) ? @context[argv1[:literal].to_s] : argv1[:literal].to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the standalone arguments
|
55
|
+
def sargs
|
56
|
+
raise 'No @context set' unless @context
|
57
|
+
|
58
|
+
@args.slice(1..-1).select { |a| a.key?(:quoted) || a.key?(:literal) }.map do |a|
|
59
|
+
if a.key? :quoted
|
60
|
+
a[:quoted].to_s
|
61
|
+
elsif a.key? :literal
|
62
|
+
lookup(@context, a[:literal].to_s) || a[:literal]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the attribute-value-pair arguments as a hash
|
68
|
+
def attr_args
|
69
|
+
raise 'No @context set' unless @context
|
70
|
+
|
71
|
+
result = {}
|
72
|
+
@args.select { |a| a.key?(:value) || a.key?(:lvalue) || a.key?(:svalue) }.map do |a|
|
73
|
+
if a.key? :value
|
74
|
+
result[a[:attr].to_sym] = a[:value].to_s
|
75
|
+
elsif a.key? :lvalue
|
76
|
+
result[a[:attr].to_sym] = lookup(@context, a[:lvalue].to_s) || a[:lvalue]
|
77
|
+
elsif a.key? :svalue
|
78
|
+
result[a[:attr].to_sym] = a[:svalue].to_sym
|
79
|
+
end
|
80
|
+
end
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns an attribute string if the attribute has a value, for use in making HTML
|
85
|
+
#
|
86
|
+
# @param [Symbol] attr
|
87
|
+
# @param [Object] value
|
88
|
+
# @param [Object] default
|
89
|
+
# @return [String]
|
90
|
+
def attr_str(attr, value, default = nil)
|
91
|
+
v = value || default
|
92
|
+
v.present? ? " #{attr}=\"#{v}\"" : ''
|
93
|
+
end
|
94
|
+
|
95
|
+
def attrs_str(*attrs, reject: [])
|
96
|
+
attrs = @args.map { |a| a[:attr] }.reject { |a| a.nil? || reject.include?(a) } if attrs.empty?
|
97
|
+
|
98
|
+
result = []
|
99
|
+
attrs.each do |attr|
|
100
|
+
result << attr_str(attr, arg(attr))
|
101
|
+
end
|
102
|
+
result.join
|
103
|
+
end
|
104
|
+
|
105
|
+
# Lookup allows access to the assigned variables through the context
|
106
|
+
def lookup(context, name)
|
107
|
+
return unless name
|
108
|
+
|
109
|
+
context[name]
|
110
|
+
end
|
111
|
+
|
112
|
+
# For use with forms and inputs
|
113
|
+
def input(purpose, name)
|
114
|
+
form_model = lookup(@context, 'form.model')
|
115
|
+
form_class_name = lookup(@context, 'form.class_name')
|
116
|
+
|
117
|
+
# binding.pry if name == 'postal_code'
|
118
|
+
|
119
|
+
# order[order_lines_attributes][xxxx][product_name]
|
120
|
+
#
|
121
|
+
# order[order_attributes]_[lines_attributes][][product_name]
|
122
|
+
|
123
|
+
parts = @context.scopes.select { |scope| scope.key? 'form' }.map do |scope|
|
124
|
+
if scope['form'].attribute
|
125
|
+
scope['form'].attribute.to_s.gsub(/([a-z\-\_]+)/) { "[#{Regexp.last_match(1)}_attributes]" }.gsub(/\[\]$/, "[#{SecureRandom.uuid}]")
|
126
|
+
else
|
127
|
+
scope['form'].class_name.underscore.gsub('/', '_')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
parts = parts.unshift("[#{name}]").reverse
|
131
|
+
|
132
|
+
return unless form_model && form_class_name && name
|
133
|
+
|
134
|
+
case purpose
|
135
|
+
when :id
|
136
|
+
parts.join('_').gsub('][', '_').tr('][', '')
|
137
|
+
when :value
|
138
|
+
# This is executed on the drop, drops provide the values for the form
|
139
|
+
form_model.send(name.to_sym)
|
140
|
+
when :name
|
141
|
+
# The original class's name dictates the name of the fields
|
142
|
+
parts.first + parts.slice(1..-1).join
|
143
|
+
when :error
|
144
|
+
parts.slice(1..-1).join('.')
|
145
|
+
when :checked
|
146
|
+
'checked' if (input(:value, name) ? 1 : 0) == 1
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns the path for the input from the main form, say: origin.contact_name
|
151
|
+
def path_for_input(name)
|
152
|
+
parts = @context.scopes.select { |scope| scope.key? 'form' }.select { |scope| scope['form'].attribute }.map do |scope|
|
153
|
+
scope['form'].attribute
|
154
|
+
end
|
155
|
+
parts = parts.unshift(name.to_s).reverse
|
156
|
+
parts.join('.')
|
157
|
+
end
|
158
|
+
|
159
|
+
def real_object_from_drop(drop)
|
160
|
+
drop.instance_variable_get('@object')
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# This is here to allow you to block certain tags for certain templates.
|
5
|
+
# Say in emails or exports it doesn't make sense to allow forms, you could exclude it as follows:
|
6
|
+
#
|
7
|
+
# template = Liquid::Template.parse(data, block_tags: %w[form])
|
8
|
+
#
|
9
|
+
module Liquid
|
10
|
+
class ParseContext
|
11
|
+
def block_tags
|
12
|
+
@options[:block_tags]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class BlockBody
|
17
|
+
alias parse_without_setting_parse_context parse
|
18
|
+
|
19
|
+
def parse(tokenizer, parse_context, &block)
|
20
|
+
@parse_context = parse_context
|
21
|
+
parse_without_setting_parse_context(tokenizer, parse_context, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def registered_tags
|
25
|
+
allowed_tags = Template.tags.dup
|
26
|
+
if @parse_context.block_tags.present?
|
27
|
+
@parse_context.block_tags.each do |tag|
|
28
|
+
allowed_tags.delete(tag)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
allowed_tags
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Liquidum
|
4
|
+
class Tag
|
5
|
+
require 'parslet'
|
6
|
+
|
7
|
+
class MiniP < Parslet::Parser
|
8
|
+
# Single character rules
|
9
|
+
rule(:squote) { str("'").repeat(1) }
|
10
|
+
rule(:dquote) { str('"').repeat(1) }
|
11
|
+
|
12
|
+
rule(:colon_or_eql) { match[":|="].repeat(1) }
|
13
|
+
|
14
|
+
rule(:space) { match('\s').repeat(1) }
|
15
|
+
rule(:space?) { space.maybe }
|
16
|
+
|
17
|
+
# Things
|
18
|
+
rule(:identifier) { (match('[a-zA-Z]') >> match('[a-zA-Z0-9\.\/\_\-\[\]\'\"]').repeat) }
|
19
|
+
rule(:symbol_literal) { colon_or_eql >> identifier.as(:svalue) }
|
20
|
+
|
21
|
+
rule(:nsqvalue) { match["^'"] }
|
22
|
+
rule(:ndqvalue) { match['^"'] }
|
23
|
+
|
24
|
+
rule(:literal_value) { identifier >> colon_or_eql >> identifier }
|
25
|
+
|
26
|
+
rule(:squoted_value) { squote >> nsqvalue.repeat.as(:value) >> squote }
|
27
|
+
rule(:dquoted_value) { dquote >> ndqvalue.repeat.as(:value) >> dquote }
|
28
|
+
rule(:quoted_value) { squoted_value | dquoted_value }
|
29
|
+
|
30
|
+
rule(:standalone_squoted_value) { squote >> nsqvalue.repeat.as(:quoted) >> squote }
|
31
|
+
rule(:standalone_dquoted_value) { dquote >> ndqvalue.repeat.as(:quoted) >> dquote }
|
32
|
+
rule(:standalone_quoted_value) { standalone_squoted_value | standalone_dquoted_value }
|
33
|
+
|
34
|
+
# Grammar parts
|
35
|
+
rule(:standalone) { identifier.as(:literal) >> space? }
|
36
|
+
rule(:quoted) { standalone_quoted_value >> space? }
|
37
|
+
rule(:attr_with_literal) { identifier.as(:attr) >> space? >> colon_or_eql >> space? >> identifier.as(:lvalue) >> space? }
|
38
|
+
rule(:attr_with_quoted) { identifier.as(:attr) >> space? >> colon_or_eql >> space? >> quoted_value >> space? }
|
39
|
+
|
40
|
+
rule(:attribute) { attr_with_quoted | attr_with_literal | quoted | standalone }
|
41
|
+
rule(:expression) { attribute.repeat }
|
42
|
+
root :expression
|
43
|
+
end
|
44
|
+
|
45
|
+
class Parser
|
46
|
+
attr_reader :args
|
47
|
+
|
48
|
+
def initialize(raw)
|
49
|
+
@args = MiniP.new.parse(raw)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Allow you to use helpers
|
4
|
+
#
|
5
|
+
# == Basic usage:
|
6
|
+
# {%helper 'user_index_path'%}
|
7
|
+
# {%helper 'check_box_tag' pet_dog%}
|
8
|
+
#
|
9
|
+
# == Advanced usage:
|
10
|
+
# {%helper 'user_index_path' user%}
|
11
|
+
#
|
12
|
+
|
13
|
+
class HelperTag < LiquidumTag
|
14
|
+
include Rails.application.routes.url_helpers
|
15
|
+
|
16
|
+
def render(context)
|
17
|
+
super
|
18
|
+
|
19
|
+
helper_args = sargs
|
20
|
+
helper_args = helper_args.concat([attr_args]) if attr_args.present?
|
21
|
+
|
22
|
+
if respond_to?(argv1.to_sym)
|
23
|
+
send(argv1.to_sym, *helper_args)
|
24
|
+
else
|
25
|
+
context.registers['controller'].helpers.send(argv1.to_sym, *helper_args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Liquid::Template.register_tag('helper', HelperTag)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Render content from variable
|
4
|
+
#
|
5
|
+
# == Basic usage:
|
6
|
+
# {%render product.description}
|
7
|
+
#
|
8
|
+
class RenderTag < LiquidumTag
|
9
|
+
def render(context)
|
10
|
+
super
|
11
|
+
|
12
|
+
return unless argv1
|
13
|
+
|
14
|
+
template = Liquid::Template.parse(argv1)
|
15
|
+
template.render(context, registers: context.registers)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Liquid::Template.register_tag('render', RenderTag)
|
data/lib/liquidum.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'liquid'
|
4
|
+
require 'tilt'
|
5
|
+
require 'sassc'
|
6
|
+
require "addressable/uri"
|
7
|
+
|
8
|
+
require 'liquidum/version'
|
9
|
+
require 'liquidum/engine'
|
10
|
+
require 'liquidum/configuration'
|
11
|
+
require 'liquidum/drop'
|
12
|
+
|
13
|
+
require 'liquidum/liquid/liquid_helpers'
|
14
|
+
require 'liquidum/liquid/liquid_template_extensions'
|
15
|
+
require 'liquidum/liquid/liquidum_block'
|
16
|
+
require 'liquidum/liquid/liquidum_tag'
|
17
|
+
require 'liquidum/liquid/parser'
|
18
|
+
|
19
|
+
module Liquidum
|
20
|
+
class Error < StandardError; end
|
21
|
+
class UnknownStepTypeError < Error; end
|
22
|
+
|
23
|
+
class LiquidumFileSystem
|
24
|
+
attr_reader :registers
|
25
|
+
def initialize(registers)
|
26
|
+
@registers = registers
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_template_file(template_path)
|
30
|
+
current_content = registers['content']
|
31
|
+
|
32
|
+
contents = current_content.site.contents.published
|
33
|
+
|
34
|
+
content = contents.include(template_path).first
|
35
|
+
content&.data || ''
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
attr_reader :config
|
41
|
+
|
42
|
+
def setup
|
43
|
+
@config = Configuration.new
|
44
|
+
yield config
|
45
|
+
end
|
46
|
+
|
47
|
+
def render(content, options = {})
|
48
|
+
template = Liquid::Template.parse(content)
|
49
|
+
options[:assigns] ||= {}
|
50
|
+
options[:registers] ||= {}
|
51
|
+
options[:registers]['file_system'] = Liquidum.config.liquidum_file_system.constantize.new(options[:registers])
|
52
|
+
result = template.render(options[:context] || options[:assigns].stringify_keys, registers: options[:registers])
|
53
|
+
|
54
|
+
if template.errors.present?
|
55
|
+
Liquidum.config.logger.error '--- Template rendering errors: ' + '-' * 49
|
56
|
+
template.errors.map do |error|
|
57
|
+
next unless error.cause
|
58
|
+
|
59
|
+
Liquidum.config.logger.error error
|
60
|
+
Liquidum.config.logger.error '=> ' + error.cause.backtrace.first.to_s + ': ' + error.cause.message
|
61
|
+
end
|
62
|
+
Liquidum.config.logger.error '-' * 80
|
63
|
+
end
|
64
|
+
|
65
|
+
assigns = options[:assigns].deep_stringify_keys
|
66
|
+
assigns = assigns.merge(template.assigns.stringify_keys) if template.assigns
|
67
|
+
options[:registers].deep_merge!(template.registers.stringify_keys) if template.registers
|
68
|
+
|
69
|
+
result = Tilt[options[:filter]].new(options[:filter_options]) { result }.render if options[:filter].present? && Tilt[options[:filter]]
|
70
|
+
if options[:layout].present?
|
71
|
+
result = render(options[:layout], assigns: assigns.merge('content' => result), registers: options[:registers])
|
72
|
+
end
|
73
|
+
|
74
|
+
result
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|