1

我的问题有几个层次,所以请多多包涵?当您在该实例上调用方法时,我构建了一个模块,可将Workflow gem中的工作流添加到该实例。它必须能够以哈希或一些基本数据结构的形式接收描述,然后在运行时将其转换为将描述的工作流放到类中的东西。所以一切都必须在运行时发生。解释所有疯狂的要求是为了什么有点复杂,但我希望这仍然是一个好问题。无论如何,在这里,我能做的最好的就是简要介绍一下上下文:

  1. 构建一个类并包含我构建的这个模块。
  2. 创建您的类的实例。
  3. 调用inject_workflow(some_workflow_description)实例上的方法。这一切都必须是动态的。

对我来说棘手的部分是,当我使用public_send()or eval()or时exec(),我仍然需要发送一些嵌套的方法调用,而且它们似乎使用了 2 个不同的范围,即类和工作流(gem)。当有人使用 Workflow gem 时,他们会在自己的类中手写这些方法调用,以便正确地确定所有内容。gem 可以访问它创建方法的类。我尝试这样做的方式是,用户不会在类上手写方法,而是通过此处显示的方法将它们添加到类中。所以我无法使用块让它工作,因为我必须进行嵌套块调用,例如

workflow() do # first method call
  # first nested method call.  can't access my scope from here
  state(:state_name) do 
    # second nested method call.  can't access my scope
    event(:event_name, transitions_to: :transition_to_state)
  end
end

我正在尝试做的一件事是多次调用Workflow#state()方法,同时嵌套时间。对我来说,问题似乎是当我嵌套这样的方法时我无法获得正确的范围。nWorkflow#event(with, custom_params) 0..n

它就像我想要的那样工作(我认为......)但我不太确定我是否达到了最佳实施。事实上,我想我可能会为我所做的事情得到一些强有力的评价。我尝试使用public_send()以及我能找到的所有其他东西来避免使用class_eval()无济于事。

每当我尝试使用其中一种“更好”的方法时,我都无法完全确定范围,有时,我完全调用了错误对象上的方法。所以我认为这是我需要帮助的地方,是吗?

这是一些尝试的目的,但这是更多的伪代码,因为我永远无法获得这个版本或任何类似的版本。

# Call this as soon as you can, after .new()
def inject_workflow(description)
  public_send :workflow do 
    description[:workflow][:states].each do |state|
      state.map do |name, event|
        public_send name.to_sym do # nested call occurs in Workflow gem
          # nested call occurs in Workflow gem
          public_send :event, event[:name], transitions_to: event[:transitions_to]
        end
      end
    end
  end
end

根据我的尝试,所有这些尝试都以相同的结果结束,这是我不需要的范围,因为我正在评估 Workflow gem 中的代码,而不是模块或用户类中的代码。

无论如何,这是我的实现。如果有人能指出我正确的方向,我将不胜感激!

module WorkflowFactory
# ...
  def inject_workflow(description)       
      # Build up an array of strings that will be used to create exactly what 
      # you would hand-write in your class, if you wanted to use the gem.
      description_string_builder = ['include Workflow', 'workflow do']
      description[:workflow][:states].each do |state|
        state.map do |name, state_description|
          if state_description.nil? # if this is a final state...
            description_string_builder << "state :#{name}"
          else # because it is not a final state, add event information too.
            description_string_builder.concat([
              "state :#{name} do",
              "event :#{state_description[:event]}, transitions_to: :#{state_description[:transitions_to]}",
              "end"
            ])
          end
        end
      end
      description_string_builder << "end\n"
      begin
        # Use class_eval to run that workflow specification by 
        # passing it off to the workflow gem, just like you would when you use 
        # the gem normally.  I'm pretty sure this is where everyone's head pops...
        self.class.class_eval(description_string_builder.join("\n"))
        define_singleton_method(:has_workflow?) { true }
      rescue Exception => e
        define_singleton_method(:has_workflow?) { !!(puts e.backtrace) }
      end
    end 
  end
end


# This is the class in question.
class Job
  include WorkflowFactory
  # ... some interesting code for your class goes here
  def next!
    current_state.events.#somehow choose the correct event
  end
end

# and in some other place where you want your "job" to be able to use a workflow, you have something like this...
job = Job.new
job.done?
# => false
until job.done? do job.next! end
# progresses through the workflow and manages its own state awareness

我发誓,我在 300000 行文本下开始了这个问题。谢谢你挂在那里!如果您还没有睡着,这里还有更多文档。 我的宝石中的模块


4

0 回答 0