base.rb 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. # Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
  2. require_dependency 'mixin/rails_logger'
  3. class Sequencer
  4. class Unit
  5. class Base
  6. include ::Mixin::RailsLogger
  7. attr_reader :state
  8. # Creates the class macro `uses` that allows a Unit to
  9. # declare the attributes it will use via parameter or block.
  10. # On the other hand it returns the declared attributes if
  11. # called without parameters.
  12. #
  13. # This method can be called multiple times and will add the
  14. # given attributes to the list. It takes care of handling
  15. # duplicates so no uniq check is required. It's safe to use
  16. # for inheritance structures and modules.
  17. #
  18. # It additionally creates a getter instance method for each declared
  19. # attribute like e.g. attr_reader does. This allows direct access
  20. # to an attribute via `attribute_name`. See examples.
  21. #
  22. # @param [Array<Symbol>] attributes an optional list of attributes that the Unit uses
  23. #
  24. # @yield [] A block returning a list of attributes
  25. #
  26. # @example Via regular Array<Symbol> parameter
  27. # uses :instance, :action, :connection
  28. #
  29. # @example Via block
  30. # uses do
  31. # additional = method(parameter)
  32. # [:some, additional]
  33. # end
  34. #
  35. # @example Listing declared attributes
  36. # Unit::Name.uses
  37. # # => [:instance, :action, :connection, :some, :suprise]
  38. #
  39. # @example Using declared attribute in the Unit via state object
  40. # state.use(:instance).id
  41. #
  42. # @example Using declared attribute in the Unit via getter
  43. # instance.id
  44. #
  45. # @return [Array<Symbol>] the list of all declared uses of a Unit.
  46. def self.uses(*attributes, &block)
  47. declaration_accessor(
  48. key: __method__,
  49. attributes: attributes(*attributes, &block)
  50. ) do |attribute|
  51. use_getter(attribute)
  52. end
  53. end
  54. # Creates the class macro `optional` that allows a Unit to
  55. # declare the attributes it will use via parameter or block.
  56. # On the other hand it returns the declared attributes if
  57. # called without parameters.
  58. #
  59. # This method can be called multiple times and will add the
  60. # given attributes to the list. It takes care of handling
  61. # duplicates so no uniq check is required. It's safe to use
  62. # for inheritance structures and modules.
  63. #
  64. # It additionally creates a getter instance method for each declared
  65. # attribute like e.g. attr_reader does. This allows direct access
  66. # to an attribute via `attribute_name`. See examples.
  67. #
  68. # @param [Array<Symbol>] attributes an optional list of attributes that the Unit optional
  69. #
  70. # @yield [] A block returning a list of attributes
  71. #
  72. # @example Via regular Array<Symbol> parameter
  73. # optional :instance, :action, :connection
  74. #
  75. # @example Via block
  76. # optional do
  77. # additional = method(parameter)
  78. # [:some, additional]
  79. # end
  80. #
  81. # @example Listing declared attributes
  82. # Unit::Name.optional
  83. # # => [:instance, :action, :connection, :some, :suprise]
  84. #
  85. # @example Using declared attribute in the Unit via state object
  86. # state.use(:instance).id
  87. #
  88. # @example Using declared attribute in the Unit via getter
  89. # instance.id
  90. #
  91. # @return [Array<Symbol>] the list of all declared optionals of a Unit.
  92. def self.optional(*attributes, &block)
  93. declaration_accessor(
  94. key: __method__,
  95. attributes: attributes(*attributes, &block)
  96. ) do |attribute|
  97. use_getter(attribute)
  98. end
  99. end
  100. # Creates the class macro `provides` that allows a Unit to
  101. # declare the attributes it will provided via parameter or block.
  102. # On the other hand it returns the declared attributes if
  103. # called without parameters.
  104. #
  105. # This method can be called multiple times and will add the
  106. # given attributes to the list. It takes care of handling
  107. # duplicates so no uniq check is required. It's safe to use
  108. # for inheritance structures and modules.
  109. #
  110. # It additionally creates a setter instance method for each declared
  111. # attribute like e.g. attr_writer does. This allows direct access
  112. # to an attribute via `self.attribute_name = `. See examples.
  113. #
  114. # A Unit should usually not provide more than one or two attributes.
  115. # If your Unit provides it's doing to much and should be splitted
  116. # into multiple Units.
  117. #
  118. # @param [Array<Symbol>] attributes an optional list of attributes that the Unit provides
  119. #
  120. # @yield [] A block returning a list of attributes
  121. #
  122. # @example Via regular Array<Symbol> parameter
  123. # provides :instance, :action, :connection
  124. #
  125. # @example Via block
  126. # provides do
  127. # additional = method(parameter)
  128. # [:some, additional]
  129. # end
  130. #
  131. # @example Listing declared attributes
  132. # Unit::Name.provides
  133. # # => [:instance, :action, :connection, :some, :suprise]
  134. #
  135. # @example Providing declared attribute in the Unit via state object parameter
  136. # state.provide(:action, :created)
  137. #
  138. # @example Providing declared attribute in the Unit via state object block
  139. # state.provide(:instance) do
  140. # # ...
  141. # instance
  142. # end
  143. #
  144. # @example Providing declared attribute in the Unit via setter
  145. # self.action = :created
  146. #
  147. # @return [Array<Symbol>] the list of all declared provides of a Unit.
  148. def self.provides(*attributes, &block)
  149. declaration_accessor(
  150. key: __method__,
  151. attributes: attributes(*attributes, &block)
  152. ) do |attribute|
  153. provide_setter(attribute)
  154. end
  155. end
  156. def self.attributes(*attributes)
  157. # exectute block if given and add
  158. # the result to the (possibly empty)
  159. # list of given attributes
  160. attributes.concat(yield) if block_given?
  161. attributes
  162. end
  163. # This method is the heart of the #uses and #provides method.
  164. # It takes the declaration key and decides based on the given
  165. # parameters if the given attributes should get stored or
  166. # the stored values returned.
  167. def self.declaration_accessor(key:, attributes:)
  168. # if no attributes were given (storing)
  169. # return the already stored list of attributes
  170. return declarations(key).to_a if attributes.blank?
  171. # loop over all given attributes and
  172. # add them to the list of already stored
  173. # attributes for the given declaration key
  174. attributes.each do |attribute|
  175. next if !declarations(key).add?(attribute)
  176. # yield callback if given to create
  177. # getter or setter or whatever
  178. yield(attribute) if block_given?
  179. end
  180. end
  181. # This method creates the convenience method
  182. # getter for the given attribute.
  183. def self.use_getter(attribute)
  184. define_method(attribute) do
  185. instance_variable_cached(attribute) do
  186. state.use(attribute)
  187. end
  188. end
  189. end
  190. # This method creates the convenience method
  191. # setter for the given attribute.
  192. def self.provide_setter(attribute)
  193. define_method("#{attribute}=") do |value|
  194. state.provide(attribute, value)
  195. end
  196. end
  197. # This method is the attribute store for the given declaration key.
  198. def self.declarations(key)
  199. instance_variable_cached("#{key}_declarations") do
  200. declarations_initial(key)
  201. end
  202. end
  203. # This method initializes the attribute store for the given declaration key.
  204. # It checks if a parent class already has an existing store and duplicates it
  205. # for independent usage. Otherwise it creates a new one.
  206. def self.declarations_initial(key)
  207. return Set.new([]) if !superclass.respond_to?(:declarations)
  208. superclass.send(:declarations, key).dup
  209. end
  210. # This method creates an accessor to a cached instance variable for the given scope.
  211. # It will create a new variable with the result of the given block as an initial value.
  212. # On later calls it will return the already initialized, cached variable state.
  213. # The variable will be created by default as a class variable. If a instance scope is
  214. # passed it will create an instance variable instead.
  215. def self.instance_variable_cached(key, scope: self)
  216. cache = "@#{key}"
  217. value = scope.instance_variable_get(cache)
  218. return value if value
  219. value = yield
  220. scope.instance_variable_set(cache, value)
  221. end
  222. # This method is an instance wrapper around the class method .instance_variable_cached.
  223. # It will behave the same but passed the instance scope to create an
  224. # cached instance variable.
  225. def instance_variable_cached(key, &block)
  226. self.class.instance_variable_cached(key, scope: self, &block)
  227. end
  228. # This method is an convenience wrapper to create an instance
  229. # and then directly processing it.
  230. def self.process(*args)
  231. new(*args).process
  232. end
  233. def initialize(state)
  234. @state = state
  235. end
  236. def process
  237. raise "Missing implementation of '#{__method__}' method for '#{self.class.name}'"
  238. end
  239. end
  240. end
  241. end