在实例

时间:2016-10-23 23:56:29

标签: ruby metaprogramming class-eval

我的问题有几层,请耐心等待?当您在该实例上调用方法时,我构建了一个模块,可以将Workflow gem中的工作流添加到实例中。它必须能够将描述作为哈希或一些基本数据结构接收,然后将其转换为在运行时将所描述的工作流放入类中的内容。所以一切都必须在运行时发生。解释所有疯狂的要求是有点复杂,但我希望它仍然是一个很好的问题。无论如何,在这里,我能做的最好的事情就是这个:

  1. 构建一个类并包含我构建的这个模块。
  2. 创建您的班级的实例。
  3. 调用实例上的inject_workflow(some_workflow_description)方法。这一切都必须是动态的。
  4. 对我来说棘手的部分是,当我使用public_send()eval()exec()时,我仍然需要发送一些嵌套的方法调用,看起来它们使用了两个不同的范围,类'和工作流'(宝石)。当有人使用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() n次嵌套时,调用Workflow#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
    

    我发誓,在30万行文字下我开始提出这个问题。谢谢你挂在那里!如果你还没睡觉的话,还有更多的文档。 module in my gem

0 个答案:

没有答案