Skip to content

Commit

Permalink
Expose a mechanism to wrap existing recipes.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix committed Aug 19, 2024
1 parent b0eac5e commit 0b5ccb2
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 1 deletion.
48 changes: 47 additions & 1 deletion lib/bake/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ def self.load(path = Dir.pwd)
def initialize(loaders, scope = nil, root = nil)
@loaders = loaders

@stack = []
@wrappers = Hash.new do |hash, key|
hash[key] = []
end

@instances = Hash.new do |hash, key|
hash[key] = instance_for(key)
Expand Down Expand Up @@ -96,6 +98,9 @@ def initialize(loaders, scope = nil, root = nil)
attr :root

# Invoke recipes on the context using command line arguments.
#
# e.g. `context.call("gem:release:version:increment", "0,0,1")`
#
# @parameter commands [Array(String)]
def call(*commands)
last_result = nil
Expand All @@ -118,6 +123,37 @@ def lookup(command)
@recipes[command]
end

class Wrapper
def initialize(wrappers, path)
@wrappers = wrappers
@path = path
end

def before(name = @path.last, &block)
wrapper = Module.new
wrapper.define_method(name) do |*arguments, **options|
instance_exec(&block)
super(*arguments, **options)
end

@wrappers[@path] << wrapper
end

def after(name = @path.last, &block)
wrapper = Module.new
wrapper.define_method(name) do |*arguments, **options|
super(*arguments, **options)
instance_exec(&block)
end

@wrappers[@path] << wrapper
end
end

def wrap(*path, &block)
Wrapper.new(@wrappers, path).instance_exec(&block)
end

def to_s
if @root
"#{self.class} #{File.basename(@root)}"
Expand All @@ -135,9 +171,13 @@ def inspect
def recipe_for(command)
path = command.split(":")

# If the command is in the form `foo:bar`, we check two cases:
#
# (1) We check for an instance at path `foo:bar` and if it responds to `bar`.
if instance = @instances[path] and instance.respond_to?(path.last)
return instance.recipe_for(path.last)
else
# (2) We check for an instance at path `foo` and if it responds to `bar`.
*path, name = *path

if instance = @instances[path] and instance.respond_to?(name)
Expand All @@ -158,6 +198,7 @@ def instance_for(path)
def base_for(path)
base = nil

# For each loader, we check if it has a scope for the given path. If it does, we prepend it to the base:
@loaders.each do |loader|
if scope = loader.scope_for(path)
base ||= Base.derive(path)
Expand All @@ -166,6 +207,11 @@ def base_for(path)
end
end

# If we have any wrappers for the given path, we also prepend them to the base:
@wrappers[path].each do |wrapper|
base.prepend(wrapper)
end

return base
end
end
Expand Down
13 changes: 13 additions & 0 deletions test/bake/.test-project/bake/wrap.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2020-2024, by Samuel Williams.

def wrap(...)
self.invoked(...)
end

protected

def invoked(...)
end
20 changes: 20 additions & 0 deletions test/bake/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,24 @@
expect(parent.instance).to be(:respond_to?, :parent)
end
end

with '#wrap' do
it "can wrap specific methods" do
events = []

context.wrap('wrap') do
before('invoked') do
events << :before
end

after('invoked') do
events << :after
end
end

context.call('wrap')

expect(events).to be == [:before, :after]
end
end
end

0 comments on commit 0b5ccb2

Please sign in to comment.