Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ jobs:
fail-fast: false
matrix:
ruby:
- "2.7"
- "3.0"
# - "3.1"
- "3.3.1"

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby 3.0.4
ruby 3.3.1
5 changes: 1 addition & 4 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
source 'https://rubygems.org'

gem 'parslet', '~> 2.0', '>= 2.0.0'
gemspec

group :development do
gem 'pry', '~> 0.10', '>= 0.10.1'
gem 'pry-stack_explorer', '~>0.4', '>= 0.4.9.1'
gem 'rspec', '~> 3.1', '>= 3.1.0'
gem 'rspec-mocks', '~> 3.1', '>= 3.1.3'
gem 'juwelier', '~> 2.4'
end

group :test do
Expand Down
103 changes: 8 additions & 95 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,95 +1,15 @@
PATH
remote: .
specs:
ruby-handlebars (0.4.1)
parslet (~> 2.0, >= 2.0.0)

GEM
remote: https://rubygems.org/
specs:
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
builder (3.2.4)
coderay (1.1.3)
debug_inspector (1.1.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
diff-lcs (1.5.0)
docile (1.4.0)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.3)
multipart-post (>= 1.2, < 3)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
git (1.11.0)
rchardet (~> 1.8)
github_api (0.19.0)
addressable (~> 2.4)
descendants_tracker (~> 0.0.4)
faraday (>= 0.8, < 2)
hashie (~> 3.5, >= 3.5.2)
oauth2 (~> 1.0)
hashie (3.6.0)
highline (2.0.3)
juwelier (2.4.9)
builder
bundler
git
github_api
highline
kamelcase (~> 0)
nokogiri
psych
rake
rdoc
semver2
jwt (2.3.0)
kamelcase (0.0.2)
semver2 (~> 3)
method_source (1.0.0)
mini_portile2 (2.8.0)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
nokogiri (1.13.6)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
oauth2 (1.4.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
parslet (2.0.0)
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-stack_explorer (0.6.1)
binding_of_caller (~> 1.0)
pry (~> 0.13)
psych (4.0.4)
stringio
public_suffix (4.0.7)
racc (1.6.0)
rack (2.2.3.1)
rake (13.0.6)
rchardet (1.8.0)
rdoc (6.4.0)
psych (>= 4.0.0)
rspec (3.11.0)
rspec-core (~> 3.11.0)
rspec-expectations (~> 3.11.0)
Expand All @@ -103,28 +23,21 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-support (3.11.0)
ruby2_keywords (0.0.5)
semver2 (3.4.2)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
stringio (3.0.2)
thread_safe (0.3.6)

PLATFORMS
ruby

DEPENDENCIES
juwelier (~> 2.4)
parslet (~> 2.0, >= 2.0.0)
pry (~> 0.10, >= 0.10.1)
pry-stack_explorer (~> 0.4, >= 0.4.9.1)
rspec (~> 3.1, >= 3.1.0)
rspec-mocks (~> 3.1, >= 3.1.3)
ruby-handlebars!
simplecov

BUNDLED WITH
2.3.12
2.7.2
3 changes: 2 additions & 1 deletion lib/ruby-handlebars.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require_relative 'ruby-handlebars/escapers/html_escaper'

module Handlebars
MissingPartial = Class.new(StandardError)
class Handlebars
attr_reader :escaper

Expand Down Expand Up @@ -42,7 +43,7 @@ def register_partial(name, content)
end

def get_partial(name)
@partials[name.to_s]
@partials[name.to_s] || raise(::Handlebars::MissingPartial, "Partial \"#{name}\" not registered.")
end

def set_escaper(escaper = nil)
Expand Down
36 changes: 32 additions & 4 deletions lib/ruby-handlebars/context.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
require "forwardable"

module Handlebars
class Context
PATH_REGEX = /\.\.\/|[^.\/]+/

class Data
extend Forwardable

def_delegators :@hash, :[]=, :keys, :key?, :empty?, :merge!, :map

def initialize(hash)
@hash = hash
end
Expand All @@ -16,6 +24,10 @@ def [](k)
to_number(k.to_s) || nil
end

def dup
self.class.new(@hash.dup) # shallow copy.
end

def has_key?(_k)
true # yeah, we'll respond to anything.
end
Expand All @@ -40,14 +52,16 @@ def initialize(hbs, data)
end

def get(path)
items = path.split('.'.freeze)
if locals.key? items.first.to_sym
items = path.scan(PATH_REGEX)
items[-1] = "#{items.shift}#{items[-1]}" if items.first == '@'

if locals.key?(items.first.to_sym)
current = locals
else
current = @data
end

until items.empty?
until items.empty? || current.nil?
current = get_attribute(current, items.shift)
end

Expand Down Expand Up @@ -78,6 +92,20 @@ def add_items(hash)
hash.map { |k, v| add_item(k, v) }
end

def with_nested_context
saved = get('../')

add_items(:'../' => locals.empty? ? @data : locals.dup)
block_result = yield
locals.merge!(:'../' => saved)

block_result
end

def with_nested_temporary_context(args)
with_nested_context { with_temporary_context(args) { yield } }
end

def with_temporary_context(args = {})
saved = args.keys.collect { |key| [key, get(key.to_s)] }.to_h

Expand All @@ -91,7 +119,7 @@ def with_temporary_context(args = {})
private

def locals
@locals ||= {}
@locals ||= Data.new({})
end

def get_attribute(item, attribute)
Expand Down
18 changes: 9 additions & 9 deletions lib/ruby-handlebars/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ def initialize(hbs, fn)
@fn = fn
end

def apply(context, arguments = [], block = [], else_block = [])
def apply(context, arguments = [], block = [], else_block = [], collapse_options = {})
apply_as(context, arguments, [], block, else_block, collapse_options)
end

def apply_as(context, arguments = [], as_arguments = [], block = [], else_block = [], collapse_options = {})
arguments = [arguments] unless arguments.is_a? Array
args = [context]
hash = {}
Expand All @@ -19,17 +23,13 @@ def apply(context, arguments = [], block = [], else_block = [])
args << arg.eval(context)
end
end
blocks = split_block(block, else_block)

@fn.call(*args, hash: hash, block: blocks[0], else_block: blocks[1])
end

def apply_as(context, arguments = [], as_arguments = [], block = [], else_block = [])
arguments = [arguments] unless arguments.is_a? Array
as_arguments = [as_arguments] unless as_arguments.is_a? Array
args = [context] + arguments.map {|arg| arg.eval(context)} + as_arguments.map(&:name) + split_block(block, else_block)
args += as_arguments.map(&:name)

blocks = split_block(block, else_block)

@fn.call(*args)
@fn.call(*args, hash: hash, block: blocks[0], else_block: blocks[1], collapse: collapse_options)
end

private
Expand Down
14 changes: 10 additions & 4 deletions lib/ruby-handlebars/helpers/default_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ module Handlebars
module Helpers
class DefaultHelper
def self.register(hbs)
hbs.register_helper(self.registry_name) do |context, parameters, **opts|
self.apply(context, parameters, **opts)
hbs.register_helper(self.registry_name) do |context, *parameters, **opts|
self.apply(context, *parameters, **opts)
end if self.respond_to?(:apply)

hbs.register_as_helper(self.registry_name) do |context, parameters, as_names, **opts|
self.apply_as(context, parameters, as_names, **opts)
hbs.register_as_helper(self.registry_name) do |context, *parameters, as_names, **opts|
self.apply_as(context, *parameters, as_names, **opts)
end if self.respond_to?(:apply_as)
end

def self.stripped_result(result, start_collapse, end_collapse)
result = result.lstrip if start_collapse&.collapse_after
result = result.rstrip if end_collapse&.collapse_before
result
end

# Should be implemented by sub-classes
# def self.registry_name
# 'myHelperName'
Expand Down
45 changes: 33 additions & 12 deletions lib/ruby-handlebars/helpers/each_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require_relative 'default_helper'

module Handlebars
UnknownEachType = Class.new(StandardError)
module Helpers
class EachHelper < DefaultHelper
def self.registry_name
Expand All @@ -11,21 +12,41 @@ def self.apply(context, items, **opts)
self.apply_as(context, items, :this, **opts)
end

def self.apply_as(context, items, name, hash:, block:, else_block:)
if (items.nil? || items.empty?)
if else_block
result = else_block.fn(context)
end
else
context.with_temporary_context(name => nil, :@index => 0, :@first => false, :@last => false) do
result = items.each_with_index.map do |item, index|
context.add_items(name => item, :@index => index, :@first => (index == 0), :@last => (index == items.length - 1))
context.add_items(item) if item.respond_to?(:map)
block.fn(context)
def self.apply_as(context, items, name, hash:, block:, else_block:, collapse:, **_opts)
if items.nil? || items.empty?
result = else_block&.fn(context)
result&.lstrip! if collapse[:else]&.collapse_after
result&.rstrip! if collapse[:close]&.collapse_before
return result
end

context.with_nested_context do
case items
when Array
items.each_with_index.map do |item, index|
add_and_execute(block, context, items, item, index, else_block, collapse, name => item)
end.join('')
when Hash
items.each_with_index.map do |(key, value), index|
add_and_execute(block, context, items, value, index, else_block, collapse, name => value, :@key => key.to_s)
end.join('')
else
raise ::Handlebars::UnknownEachType, "unknown type provided to each helper, please provide an array or hash"
end
end
result
end

def self.add_and_execute(block, context, items, item, index, else_block, collapse, **extra)
locals = {
:@index => index,
:@first => index == 0,
:@last => index == items.length - 1
}

context.with_temporary_context(locals.merge(extra.to_h)) do
context.add_items(item) if item.respond_to?(:map)
stripped_result(block.fn(context), collapse[:helper], else_block.nil? ? collapse[:close] : collapse[:else])
end
end
end
end
Expand Down
9 changes: 6 additions & 3 deletions lib/ruby-handlebars/helpers/if_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ def self.registry_name
'if'
end

def self.apply(context, condition, block:, else_block:, **_opts)
def self.apply(context, condition, block:, else_block:, collapse:, **_opts)
condition = !condition.empty? if condition.respond_to?(:empty?)
branch(condition, context, block, else_block, collapse)
end

def self.branch(condition, context, block, else_block, collapse)
if condition
block.fn(context)
stripped_result(block.fn(context), collapse[:helper], else_block.nil? ? collapse[:close] : collapse[:else])
elsif else_block
else_block.fn(context)
stripped_result(else_block.fn(context), collapse[:else], collapse[:close])
else
""
end
Expand Down
Loading