Class: FunctionChain::RelayChain

Inherits:
BaseChain show all
Defined in:
lib/function_chain/relay_chain.rb

Overview

RelayChain

RelayChain is object like a connect to function’s input from function’s output. (methods well as can connect Proc.)

Chain is object, so can call later.

Supported call chain type is like a

filter3(filter2(filter1(value))).

Unsupported call chain type is like a

.user.name

(PullChain to support such type.)

Example

class Decorator
  def decorate1(value)
    "( #{value} )"
  end
  def decorate2(value)
    "{ #{value} }"
  end
end
chain = RelayChain.new(Decorator.new, :decorate1, :decorate2)
chain.call("Hello") # => { ( Hello ) }

similar.

# Strings separated by a slash
chain = RelayChain.new(Decorator.new, "decorate1/decorate2")
chain.call("Hello")

# use >> operator.
chain = RelayChain.new(Decorator.new)
chain >> :decorate1 >> :decorate2
chain.call("Hello")

# use Method object
chain = RelayChain.new
chain >> decorator.method(:decorate1) >> decorator.method(:decorate2)
chain.call("Hello")

# use add method
chain.add(:decorate1).add(:decorate2).call("Hello")

# use add_all method
chain.add_all(:decorate1, :decorate2).call("Hello")

insert, insert_all method is insert function to chain. delete_at method is delete function from chain. clear method is delete all function from chain.

How to connect method of differed instance

Example, following two class. Introduce how to connect method of these class.

class Decorator
  def decorate1(value) "( #{value} )" end
  def decorate2(value) "{ #{value} }" end
end
class Decorator2
  def decorate(value) "[ #{value} ]" end
end

Solution1:Array, format is [instance, Symbol or String of method]

chain = RelayChain.new(Decorator.new)
chain >> :decorate1 >> :decorate2 >> [Decorator2.new, :decorate]
# String ver.
# chain >> :decorate1 >> :decorate2 >> [Decorator2.new, "decorate"]
chain.call("Hello") # => [ { ( Hello ) } ]

Solution2:String, use registered instance.

chain = RelayChain.new(Decorator.new)
# register name and instance
chain.add_receiver("d2", Decorator2.new)
# use registered instance
chain >> "/decorate1/decorate2/d2.decorate"
chain.call("Hello") # => [ { ( Hello ) } ]

# add_receiver_table method is register name and instance at once.
chain.add_receiver_table({"x" => X.new, "y" => Y.new})

Case of method’s output and method’s input mismatch

Following example, decorate output is 1, and union input is 2. How to do connect these methods?

class Decorator
  def decorate(value)
    "#{value} And"
  end
  def union(value1, value2)
    "#{value1} #{value2}"
  end
end

Solution1:define connect method.

class Decorator
  def connect(value)
    return value, "Palmer"
  end
end
chain = RelayChain.new(Decorator.new)
chain >> :decorate >> :connect >> :union
chain.call("Emerson, Lake") # => Emerson, Lake And Palmer

Solution2:add lambda or Proc to between these methods.

lambda's format is following.
  lambda {|chain, *args| chain.call(next function's arguments) }.
lambda's parameter:chain is chain object.
lambda's parameter:*args is previous function's output.
can call next function by chain object.

chain = RelayChain.new(Decorator.new)
arg_adder = lambda { |chain, value| chain.call(value, "Jerry") }
chain >> :decorate >> arg_adder >> :union
chain.call("Tom") # => Tom And Jerry

Appendix

Chain stop by means of lambda.

class Decorator
  def decorate1(value) "( #{value} )" end
  def decorate2(value) "{ #{value} }" end
end

def create_stopper(&stop_condition)
  lambda do |chain, value|
    # if stop conditions are met then return value
    if stop_condition.call(value)
      value
    else
      chain.call(value)
    end
  end
end

chain = RelayChain.new(Decorator.new, :decorate1, :decorate2)

# insert_all conditional chain stopper
chain.insert(1, create_stopper { |value| value =~ /\d/ })
chain.call("Van Halen 1984") # => ( Van Halen 1984 ) not enclosed to {}
chain.call("Van Halen Jump") # => { ( Van Halen Jump ) } enclosed to {}

Instance Method Summary collapse

Methods inherited from BaseChain

#add, #add_all, #clear, #delete_at, #insert, #insert_all, #to_s

Constructor Details

#initialize(common_receiver = nil, *functions) ⇒ RelayChain

Initialize chain

initialize(common_receiver = nil, *functions) common_receiver:used if the instance is omitted *functions: more than one symbol, string, array, method, proc



150
151
152
153
154
# File 'lib/function_chain/relay_chain.rb', line 150

def initialize(common_receiver = nil, *functions)
  @common_receiver = common_receiver
  @index = 0
  add_all(*functions)
end

Instance Method Details

#add_receiver(name, receiver) ⇒ Object

add receiver use by string type function.

add_receiver(name, receiver) name:receiver’s name receiver:register this receiver



179
180
181
182
# File 'lib/function_chain/relay_chain.rb', line 179

def add_receiver(name, receiver)
  receiver_table[name] = receiver
  self
end

#add_receiver_table(table) ⇒ Object

register name and instance at once.

add_receiver_table(table) table:hash name as String => receiver, …



188
189
190
191
# File 'lib/function_chain/relay_chain.rb', line 188

def add_receiver_table(table)
  receiver_table.merge! table
  self
end

#call(*args) ⇒ Object

Call to all added function.



157
158
159
160
161
162
163
164
165
166
# File 'lib/function_chain/relay_chain.rb', line 157

def call(*args)
  begin
    return if last?
    chain_element = chain_elements[@index]
    @index += 1
    chain_element.call(self, *args)
  ensure
    @index = 0
  end
end

#last?Boolean

Whether chain last

Returns:

  • (Boolean)


169
170
171
# File 'lib/function_chain/relay_chain.rb', line 169

def last?
  @index == chain_elements.length
end