module Hyperstack class Operation def add_error(key, kind, message = nil) raise ArgumentError.new("Invalid kind") unless kind.is_a?(Symbol) @errors ||= Mutations::ErrorHash.new @errors.tap do |errs| path = key.to_s.split(".") last = path.pop inner = path.inject(errs) do |cur_errors,part| cur_errors[part.to_sym] ||= Mutations::ErrorHash.new end inner[last] = Mutations::ErrorAtom.new(key, kind, :message => message) end end def has_errors? !@errors.nil? end def params @params end def abort!(arg = nil) Railway.abort!(arg) end def succeed!(arg = nil) Railway.succeed!(arg) end def initialize @_railway = self.class._Railway.new(self) end class << self def run(*args) _run(*args) end def _run(*args) new.instance_eval do @_railway.process_params(args) @_railway.process_validations @_railway.run # return the result from dispatch in case there is an error if (dispatch_result = @_railway.dispatch).rejected? dispatch_result else @_railway.result end end end def then(*args, &block) run(*args).then(&block) end def fail(*args, &block) run(*args).fail(&block) end def param(*args, &block) _Railway.add_param(*args, &block) end def inbound(*args, &block) name, opts = ParamsWrapper.get_name_and_opts(*args) _Railway.add_param(name, opts.merge(inbound: :true), &block) end def outbound(*keys) keys.each { |key| _Railway.add_param(key => nil, :type => :outbound) } #singleton_class.outbound(*keys) end def validate(*args, &block) _Railway.add_validation(*args, &block) end def add_error(param, symbol, message, *args, &block) _Railway.add_error(param, symbol, message, *args, &block) end def step(*args, &block) _Railway.add_step(*args, &block) end def failed(*args, &block) _Railway.add_failed(*args, &block) end def async(*args, &block) _Railway.add_async(*args, &block) end def on_dispatch(&block) _Railway.add_receiver(&block) end def _Railway self.singleton_class._Railway end def inherited(child) child.singleton_class.define_singleton_method(:param) do |*args, &block| _Railway.add_param(*args, &block) end child.singleton_class.define_singleton_method(:inbound) do |*args, &block| name, opts = ParamsWrapper.get_name_and_opts(*args) _Railway.add_param(name, opts.merge(inbound: :true), &block) end child.singleton_class.define_singleton_method(:outbound) do |*keys| keys.each { |key| _Railway.add_param(key => nil, :type => :outbound) } end child.singleton_class.define_singleton_method(:validate) do |*args, &block| _Railway.add_validation(*args, &block) end child.singleton_class.define_singleton_method(:add_error) do |param, symbol, message, *args, &block| _Railway.add_error(param, symbol, message, *args, &block) end child.singleton_class.define_singleton_method(:step) do |*args, &block| _Railway.add_step({scope: :class}, *args, &block) end child.singleton_class.define_singleton_method(:failed) do |*args, &block| _Railway.add_failed({scope: :class}, *args, &block) end child.singleton_class.define_singleton_method(:async) do |*args, &block| _Railway.add_async({scope: :class}, *args, &block) end child.singleton_class.define_singleton_method(:_Railway) do Hyperstack::Context.set_var(self, :@_railway) do # overcomes a bug in Opal 0.9 which returns nil for singleton superclass my_super = superclass || `self.$$singleton_of`.superclass.singleton_class if my_super == Operation.singleton_class Class.new(Railway) else Class.new(my_super._Railway).tap do |wrapper| [:@validations, :@tracks, :@receivers].each do |var| value = my_super._Railway.instance_variable_get(var) wrapper.instance_variable_set(var, value && value.dup) end end end end end end end class Railway def initialize(operation) @operation = operation end end end end