Skip to content
Open
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
10 changes: 7 additions & 3 deletions lib/halibut/builder.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'halibut/core/resource'
require 'halibut/dsl'

module Halibut

Expand Down Expand Up @@ -28,10 +29,11 @@ def resource
#
class RootContext

include DSL

def initialize(resource, &blk)
@resource = resource

instance_eval(&blk) if block_given?
instance_eval_with_previous_context_available(&blk) if block_given?
end

# Sets a property on the resource.
Expand Down Expand Up @@ -108,11 +110,13 @@ def relation(rel, &blk)

class RelationContext

include DSL

def initialize(resource, rel, &blk)
@resource = resource
@rel = rel

instance_eval(&blk) if block_given?
instance_eval_with_previous_context_available(&blk) if block_given?
end

def link(href, opts={})
Expand Down
61 changes: 61 additions & 0 deletions lib/halibut/dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module Halibut

module DSL

def instance_eval_with_previous_context_available(*args, &block)
DslDelegator.new(self).instance_eval_with_previous_context_available(&block)
end

class DslDelegator

def initialize delegation_target
@delegation_target = delegation_target
end

def instance_eval_with_previous_context_available(*args, &block)
with_previous_context_available(block.binding) do
bind_block_as_instance_method_on_self(&block).call(*args)
end
end

protected

def method_missing(method, *args, &block)
if delegation_target_responds_to? method
delegation_target.send(method, *args, &block)
else
previous_context.send(method, *args, &block)
end
end

private

attr_accessor :delegation_target, :previous_context

def bind_block_as_instance_method_on_self(&block)
create_instance_method_from_block(&block).bind(self)
end

def create_instance_method_from_block &block
meth = self.class.class_eval do
define_method :___block_as_instance_method_, &block
meth = instance_method :___block_as_instance_method_
remove_method :___block_as_instance_method_
meth
end
end

def with_previous_context_available(binding, &block)
@previous_context = binding.eval('self')
result = block.call
@previous_context = nil
result
end

def delegation_target_responds_to?(method)
delegation_target.respond_to? method
end

end
end
end
18 changes: 18 additions & 0 deletions spec/builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,22 @@
end
end

describe "Using methods declared outside the builder" do
it "evaluates the methods in the context of the block's declaration scope" do
def local_method
'bar'
end

builder = Halibut::Builder.new do
property 'foo', local_method
end

resource = Halibut::Core::Resource.new
resource.set_property 'foo', local_method

builder.resource.properties['foo'].must_equal local_method
builder.resource.must_equal resource, diff(builder.resource.to_hash, resource.to_hash)
end
end

end