diff --git a/Gemfile b/Gemfile index f25fd5d..3fcb138 100644 --- a/Gemfile +++ b/Gemfile @@ -4,5 +4,3 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in serbea.gemspec gemspec - -gem "erubi", github: "jaredcwhite/erubi", branch: "config-literal-prefix" diff --git a/README.md b/README.md index 302068c..c14b02d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Serbea combines the best ideas from "brace-style" template languages such as Liq * Supports every convention of ERB and builds upon it with new features (which is why it's "awesomer!"). * Builtin frontmatter support so you can access the variables written into the top YAML within your templates. In any Rails view, including layouts, you'll have access to the `@frontmatter` ivar which is a merged `HashWithDotAccess::Hash` with data from any part of the view tree (partials, pages, layout). * The filters accessible within Serbea templates are either helpers (where the variable gets passed as the first argument) or instance methods of the variable itself, so you can build extremely expressive pipelines that take advantage of the code you already know and love. (For example, in Rails you could write `{{ "My Link" | link_to: route_path }}`). -* The `Serbea::Pipeline.exec` method lets you pass a pipeline template in, along with an optional input value or included helpers module, and you'll get the output as a object of any type (not converted to a string like in traditional templates). For example: `Serbea::Pipeline.exec("[1,2,3] |> map: ->(i) { i * 10 }")` will return `[10, 20, 30]`. +* The `Serbea::Pipeline.exec` method lets you pass a pipeline template in, along with an optional input value or included helpers module, and you'll get the output as a object of any type (not converted to a string like in traditional templates). For example: `Serbea::Pipeline.exec("arr |> map: ->(i) { i * 10 }", arr: [1,2,3])` will return `[10, 20, 30]`. ## What It Looks Like diff --git a/lib/serbea.rb b/lib/serbea.rb index 313f70b..31ab5be 100644 --- a/lib/serbea.rb +++ b/lib/serbea.rb @@ -10,7 +10,7 @@ class SerbeaTemplate < ErubiTemplate def prepare @options.merge!( outvar: "@_erbout", - bufval: "Serbea::Buffer.new", + bufval: "Serbea::OutputBuffer.new", literal_prefix: "{%", literal_postfix: "%}", engine_class: Serbea::TemplateEngine diff --git a/lib/serbea/helpers.rb b/lib/serbea/helpers.rb index 47a1d33..3e12d2b 100644 --- a/lib/serbea/helpers.rb +++ b/lib/serbea/helpers.rb @@ -8,11 +8,12 @@ def self.included(mod) def capture(obj = nil, &block) previous_buffer_state = @_erbout - @_erbout = Serbea::Buffer.new + @_erbout = Serbea::OutputBuffer.new # For compatibility with ActionView, not used by Bridgetown normally previous_ob_state = @output_buffer - @output_buffer = Serbea::Buffer.new + @output_buffer = Serbea::OutputBuffer.new + result = instance_exec(obj, &block) if @output_buffer != "" @@ -32,11 +33,11 @@ def pipeline(context, value) def helper(name, &helper_block) self.class.define_method(name) do |*args, &block| previous_buffer_state = @_erbout - @_erbout = Serbea::Buffer.new + @_erbout = Serbea::OutputBuffer.new # For compatibility with ActionView, not used by Bridgetown normally previous_ob_state = @output_buffer - @output_buffer = Serbea::Buffer.new + @output_buffer = Serbea::OutputBuffer.new result = helper_block.call(*args, &block) if @output_buffer != "" diff --git a/lib/serbea/pipeline.rb b/lib/serbea/pipeline.rb index 6ef4e4b..9387ae5 100644 --- a/lib/serbea/pipeline.rb +++ b/lib/serbea/pipeline.rb @@ -1,12 +1,13 @@ require "active_support/core_ext/string/output_safety" +require "active_support/core_ext/object/blank" module Serbea class Pipeline - def self.exec(template, input: (no_input_passed = true; nil), include_helpers: nil) + def self.exec(template, locals = {}, include_helpers: nil, **kwargs) anon = Class.new do include Serbea::Helpers - attr_accessor :input, :output + attr_accessor :output end if include_helpers @@ -14,12 +15,11 @@ def self.exec(template, input: (no_input_passed = true; nil), include_helpers: n end pipeline_obj = anon.new - pipeline_obj.input = input unless no_input_passed full_template = "{{ #{template} | assign_to: :output }}" tmpl = Tilt::SerbeaTemplate.new { full_template } - tmpl.render(pipeline_obj) + tmpl.render(pipeline_obj, locals.presence || kwargs) pipeline_obj.output end @@ -54,8 +54,9 @@ def self.value_methods_denylist @value_methods_denylist ||= Set.new end - def initialize(context, value) - @context = context + def initialize(binding, value) + @binding = binding + @context = binding.receiver @value = value end @@ -83,6 +84,19 @@ def filter(name, *args, **kwargs) else @value = @context.send(name, @value, *args) end + elsif @binding.local_variables.include?(name) + var = @binding.local_variable_get(name) + if var.respond_to?(:call) + unless kwargs.empty? + @value = var.call(@value, *args, **kwargs) + else + @value = var.call(@value, *args) + end + else + "Serbea warning: Filter #{name} does not respond to call".tap do |warning| + self.class.raise_on_missing_filters ? raise(warning) : STDERR.puts(warning) + end + end else "Serbea warning: Filter not found: #{name}".tap do |warning| self.class.raise_on_missing_filters ? raise(warning) : STDERR.puts(warning) diff --git a/lib/serbea/template_engine.rb b/lib/serbea/template_engine.rb index bb1a387..fe92f57 100644 --- a/lib/serbea/template_engine.rb +++ b/lib/serbea/template_engine.rb @@ -1,7 +1,7 @@ require "strscan" module Serbea - class Buffer < String + class OutputBuffer < String def concat_to_s(input) concat input.to_s end @@ -125,7 +125,7 @@ def process_serbea_input(template, properties) end end - segments[0] = "pipeline(self, (#{segments[0].strip}))" + segments[0] = "pipeline(binding, (#{segments[0].strip}))" segments[1..-1].each_with_index do |segment, index| filter, args = segment.strip.match(/([^ :]*)(.*)/m).captures segments[index + 1] = ".filter(:" + filter diff --git a/lib/version.rb b/lib/version.rb index 72cd167..7b5a7f6 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -1,3 +1,3 @@ module Serbea - VERSION = "0.8.1" + VERSION = "0.9.0" end \ No newline at end of file diff --git a/serbea.gemspec b/serbea.gemspec index 0d5016c..d1a3b8d 100644 --- a/serbea.gemspec +++ b/serbea.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_runtime_dependency("activesupport", "~> 6.0") - spec.add_runtime_dependency("erubi", "~> 1.9") + spec.add_runtime_dependency("erubi", ">= 1.10") spec.add_runtime_dependency("hash_with_dot_access", "~> 1.1") spec.add_runtime_dependency("tilt", "~> 2.0") diff --git a/test/template.serb b/test/template.serb index 1e24397..cd5b540 100644 --- a/test/template.serb +++ b/test/template.serb @@ -148,3 +148,6 @@ Prepend permanent? {{ [1, 3, 6] \| [2, 3, 9] | join: "-" | append: " Foo \| bar!" | gsub: /ba\|r/, "x" }} {{ LambdaTest.new | scope: "lambda test", ->(x) { x * 20 }, ->{} | center: 50 }} + +{% use_local_helper = ->(input) { input.downcase } %} +{{ "YO" | use_local_helper }} diff --git a/test/test.rb b/test/test.rb index 22753e0..c2ddeee 100644 --- a/test/test.rb +++ b/test/test.rb @@ -78,7 +78,7 @@ def page def form(classname:) previous_buffer_state = @_erbout - @_erbout = Serbea::Buffer.new + @_erbout = Serbea::OutputBuffer.new fields = Fields.new str = "