Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to execute a method from a context in a different context? #107

Open
Paul-Bob opened this issue Oct 23, 2023 · 0 comments
Open

How to execute a method from a context in a different context? #107

Paul-Bob opened this issue Oct 23, 2023 · 0 comments

Comments

@Paul-Bob
Copy link

Hi,

We're using Docile on Avo to manage some parts of our DSL.
We're experiencing an issue where a method call within the block is not executing in the desired context.
Here's a simplified example of the problem:

require "docile"

module HasItems
  attr_reader :items

  def initialize
    @items = []
  end

  def field(id)
    @items << id
  end
end

class Panel
  include HasItems
end

class Resource
  include HasItems

  def panel(&block)
    panel = Docile.dsl_eval(Panel.new, &block)

    puts "Panel items: #{panel.items}"
  end

  def extracted_fields
    puts "Context inside extracted_fields method: #{self}"
    field :extracted
  end

  def fields
    field :id

    panel do
      puts "Context inside panel do block: #{self}"
      extracted_fields
    end
  end
end

resource = Resource.new
resource.fields
puts "Resource items: #{resource.items}"

Result:

Context inside panel do block: #<Panel:0x00007fd830d3d488>
Context inside extracted_fields method: #<Resource:0x00007fd830d3d758>
Panel items: []
Resource items: [:id, :extracted]

Expected result (ignoring the context prints):

Panel items: [:extracted]
Resource items: [:id]

We'd like to make it as easy as possible to extract methods in the most natuarl way, as a method that they can run inside our custom blocks (panel, tab, sidebar, and more).

When the panel block it's executed the extracted_fields method is not found, so it falls back to the @__fallback__ context which is the resource's context. The problem here is that the code from extracted_fields was called from the panel block with the intention to add a field to that panel, but it was added to the resource.

We've found 2 workarounds

1 - Pass the context to the method

We don't like this approach as it makes the user learn yeat another pattern that they might implement badly and doesn't feel that natural.

def extracted_fields(context)
  puts "Context inside extracted_fields method: #{self}"
  context.field :extracted
end

def fields
  field :id

  panel do
    puts "Context inside panel do block: #{self}"
    extracted_fields(self)
  end
end

2 - Make extracted_fields return a Proc and instance_exec it

It seems unnatural again to have a method run a block. But mor importantly, they have to call instance_exec inside the panel block which we don't want

def extracted_fields
 puts "Context inside extracted_fields method: #{self}"

 -> { field :extracted }
end

def fields
 field :id

 panel do
   puts "Context inside panel do block: #{self}"
   instance_exec &extracted_fields
 end
end

Both workarounds require public DSL interaction, and one of our goals is to keep the public DSL as simple as possible.

Ideal scenario(s)

The ideal scenario would be that they declare the methods on the resource file and just rub them inside the custom blocks (panel, tab, sidebar, and more).

def extracted_fields
  field :extracted
end

def fields
  field :id

  panel do
    extracted_fields
  end
end

Next ideal scenario

If that's impossible, we'd like to expose a simple method call or something similar that would be simple to implement and least destructive.

def extracted_fields
  field :extracted
end

def fields
  field :id

  panel do
    unpack_fields extracted_fields
    # or maybe
    unpack_fields -> { extracted_fields }
  end
end

Is there a more elegant solution or best practice within Docile to ensure that method calls within a block execute in the expected context while avoiding the need for additional public DSL interactions?
We'd greatly appreciate your help and guidance on this matter.

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant