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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +39 -0
  4. data/Rakefile +37 -0
  5. data/app/controllers/liquidum/application_controller.rb +5 -0
  6. data/app/controllers/liquidum/completions_controller.rb +15 -0
  7. data/app/controllers/liquidum/sandbox_controller.rb +28 -0
  8. data/app/helpers/liquidum/application_helper.rb +4 -0
  9. data/app/jobs/liquidum/application_job.rb +4 -0
  10. data/app/models/concerns/liquidum/to_liquid.rb +17 -0
  11. data/app/views/liquidum/sandbox/index.html.slim +13 -0
  12. data/config/routes.rb +4 -0
  13. data/lib/liquidum/configuration.rb +23 -0
  14. data/lib/liquidum/drop.rb +69 -0
  15. data/lib/liquidum/engine.rb +15 -0
  16. data/lib/liquidum/liquid/drops/active_storage_attached_many_drop.rb +14 -0
  17. data/lib/liquidum/liquid/drops/active_storage_attached_one_drop.rb +21 -0
  18. data/lib/liquidum/liquid/drops/active_storage_attachment_drop.rb +19 -0
  19. data/lib/liquidum/liquid/drops/enumerator_lazy_drop.rb +7 -0
  20. data/lib/liquidum/liquid/filters/array_wrap_filter.rb +14 -0
  21. data/lib/liquidum/liquid/filters/encode_filter.rb +14 -0
  22. data/lib/liquidum/liquid/filters/format_filter.rb +14 -0
  23. data/lib/liquidum/liquid/filters/localize_filter.rb +15 -0
  24. data/lib/liquidum/liquid/filters/strptime_filter.rb +14 -0
  25. data/lib/liquidum/liquid/filters/sum_filter.rb +16 -0
  26. data/lib/liquidum/liquid/filters/timezone_filter.rb +18 -0
  27. data/lib/liquidum/liquid/filters/to_filter.rb +46 -0
  28. data/lib/liquidum/liquid/filters/translate_filter.rb +45 -0
  29. data/lib/liquidum/liquid/filters/where_exp_filter.rb +70 -0
  30. data/lib/liquidum/liquid/liquid_helpers.rb +163 -0
  31. data/lib/liquidum/liquid/liquid_template_extensions.rb +34 -0
  32. data/lib/liquidum/liquid/liquidum_block.rb +5 -0
  33. data/lib/liquidum/liquid/liquidum_tag.rb +5 -0
  34. data/lib/liquidum/liquid/parser.rb +53 -0
  35. data/lib/liquidum/liquid/tags/helper_tag.rb +30 -0
  36. data/lib/liquidum/liquid/tags/render_tag.rb +19 -0
  37. data/lib/liquidum/version.rb +5 -0
  38. data/lib/liquidum.rb +77 -0
  39. 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LiquidumBlock < Liquid::Block
4
+ include Liquidum::LiquidHelpers
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LiquidumTag < Liquid::Tag
4
+ include Liquidum::LiquidHelpers
5
+ 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)
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Liquidum
4
+ VERSION = '1.0.17'
5
+ end
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