Axxord.st 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. Smalltalk createPackage: 'Axxord'!
  2. Object subclass: #Axes
  3. instanceVariableNames: ''
  4. package: 'Axxord'!
  5. !Axes class methodsFor: 'parsing'!
  6. parse: message
  7. | result stack anArray |
  8. anArray := message tokenize: ' '.
  9. result := #().
  10. stack := { result }.
  11. anArray do: [ :each |
  12. | asNum inner close |
  13. close := 0.
  14. inner := each.
  15. [ inner notEmpty and: [ inner first = '(' ]] whileTrue: [ inner := inner allButFirst. stack add: (stack last add: #()) ].
  16. [ inner notEmpty and: [ inner last = ')' ]] whileTrue: [ inner := inner allButLast. close := close + 1 ].
  17. (inner notEmpty and: [ inner first = '~' ]) ifTrue: [ inner := { inner allButFirst } ].
  18. asNum := inner isString ifTrue: [ (inner ifEmpty: [ 'NaN' ]) asNumber ] ifFalse: [ inner ].
  19. asNum = asNum ifTrue: [ stack last add: asNum ] ifFalse: [
  20. inner ifNotEmpty: [ stack last add: inner ] ].
  21. close timesRepeat: [ stack removeLast ] ].
  22. ^ result
  23. ! !
  24. Object subclass: #Axon
  25. instanceVariableNames: 'factory'
  26. package: 'Axxord'!
  27. !Axon commentStamp!
  28. I represent a pub-sub based on a key (called 'aspect').
  29. I manage aspect-block subscriptions (called 'interests') as well as run blocks of dirtied interests.
  30. The interest objects are responsible of decision if the change of an aspect is relevant for them.
  31. Interest object must be subclasses of `AxonInterest`.
  32. My subclasses must provide implementation for:
  33. - add:
  34. - do:
  35. - clean!
  36. !Axon methodsFor: 'action'!
  37. addInterest: anInterest
  38. self
  39. add: (anInterest flag; yourself);
  40. dirty: true
  41. !
  42. changed: anAspect
  43. | needsToRun |
  44. needsToRun := false.
  45. self do: [ :each |
  46. (each accepts: anAspect) ifTrue: [
  47. each flag.
  48. needsToRun := true ]].
  49. self dirty: needsToRun
  50. !
  51. changedAll
  52. | needsToRun |
  53. needsToRun := false.
  54. self do: [ :each |
  55. each flag.
  56. needsToRun := true ].
  57. self dirty: needsToRun
  58. !
  59. dirty: aBoolean
  60. aBoolean ifTrue: [[ self run ] fork]
  61. !
  62. run
  63. [
  64. | needsClean |
  65. needsClean := false.
  66. self do: [ :each |
  67. each isFlagged ifTrue: [ each run ].
  68. each isEnabled ifFalse: [ needsClean := true ]
  69. ].
  70. needsClean ifTrue: [ self clean ]
  71. ] on: Error do: [ self dirty: true ]
  72. ! !
  73. !Axon methodsFor: 'injecting'!
  74. registerIn: anObject
  75. <inlineJS: 'anObject.$axon$=self'>
  76. ! !
  77. Axon subclass: #DumbAxon
  78. instanceVariableNames: ''
  79. package: 'Axxord'!
  80. !DumbAxon commentStamp!
  81. I am an axon that does nothing.!
  82. !DumbAxon methodsFor: 'as yet unclassified'!
  83. add: anInterest
  84. "pass"
  85. !
  86. clean
  87. "pass"
  88. !
  89. do: aBlock
  90. "pass"
  91. ! !
  92. Axon subclass: #SimpleAxon
  93. instanceVariableNames: 'queue'
  94. package: 'Axxord'!
  95. !SimpleAxon methodsFor: 'accessing'!
  96. add: aSubscription
  97. queue add: aSubscription.
  98. ! !
  99. !SimpleAxon methodsFor: 'bookkeeping'!
  100. clean
  101. queue := queue select: [ :each | each isEnabled ]
  102. ! !
  103. !SimpleAxon methodsFor: 'enumeration'!
  104. do: aBlock
  105. queue do: aBlock
  106. ! !
  107. !SimpleAxon methodsFor: 'initialization'!
  108. initialize
  109. super initialize.
  110. queue := OrderedCollection new
  111. ! !
  112. Object subclass: #AxonInterest
  113. instanceVariableNames: 'aspect actionBlock flagged'
  114. package: 'Axxord'!
  115. !AxonInterest methodsFor: 'accessing'!
  116. aspect: anAspect block: aBlock
  117. aspect := anAspect.
  118. actionBlock := aBlock
  119. !
  120. flag
  121. flagged := true
  122. ! !
  123. !AxonInterest methodsFor: 'action'!
  124. run
  125. [ flagged := false. actionBlock value ]
  126. on: AxonOff do: [ actionBlock := nil ]
  127. ! !
  128. !AxonInterest methodsFor: 'initialization'!
  129. initialize
  130. super initialize.
  131. aspect := nil.
  132. actionBlock := nil.
  133. flagged := false.
  134. ! !
  135. !AxonInterest methodsFor: 'testing'!
  136. accepts: anAspect
  137. "Should return true if change for anAspect is relevant for this AxonInterest"
  138. self subclassResponsibility
  139. !
  140. isEnabled
  141. ^actionBlock notNil
  142. !
  143. isFlagged
  144. ^flagged
  145. ! !
  146. AxonInterest subclass: #InterestedInEqual
  147. instanceVariableNames: ''
  148. package: 'Axxord'!
  149. !InterestedInEqual methodsFor: 'testing'!
  150. accepts: anAspect
  151. ^ anAspect = aspect
  152. ! !
  153. Error subclass: #AxonOff
  154. instanceVariableNames: ''
  155. package: 'Axxord'!
  156. !AxonOff commentStamp!
  157. Signal me from the subscription block to unsubscribe it.!
  158. !Array methodsFor: '*Axxord'!
  159. asAxisIn: anObject ifAbsent: aBlock
  160. | receiver selector result |
  161. selector := self first.
  162. receiver := anObject yourself. "JSObjectProxy hack"
  163. [ result := receiver perform: selector ]
  164. on: MessageNotUnderstood do: [ :mnu |
  165. ((mnu message selector = selector
  166. and: [ mnu receiver == receiver ])
  167. and: [ mnu message arguments isEmpty ])
  168. ifFalse: [ mnu resignal ].
  169. ^ aBlock value ].
  170. ^ result
  171. !
  172. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  173. | receiver selector arguments result |
  174. selector := self first asMutator.
  175. receiver := anObject yourself. "JSObjectProxy hack"
  176. arguments := { anotherObject }.
  177. [ result := receiver perform: selector withArguments: arguments ]
  178. on: MessageNotUnderstood do: [ :mnu |
  179. ((mnu message selector = selector
  180. and: [ mnu receiver == receiver ])
  181. and: [ mnu message arguments = arguments ])
  182. ifFalse: [ mnu resignal ].
  183. ^ aBlock value ].
  184. ^ result
  185. ! !
  186. !Number methodsFor: '*Axxord'!
  187. asAxisIn: anObject ifAbsent: aBlock
  188. (anObject respondsTo: #at:ifAbsent:)
  189. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  190. ifFalse: aBlock
  191. !
  192. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  193. (anObject respondsTo: #at:put:)
  194. ifTrue: [ ^ anObject at: self put: anotherObject ]
  195. ifFalse: aBlock
  196. ! !
  197. !Object methodsFor: '*Axxord'!
  198. asAxisIn: anObject ifAbsent: aBlock
  199. ^ aBlock value
  200. !
  201. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  202. ^ aBlock value
  203. !
  204. atAxes: aCollection ifAbsent: aBlock
  205. ^ aCollection inject: self into: [ :soFar :segment |
  206. segment asAxisIn: soFar ifAbsent: [ ^ aBlock value ]]
  207. !
  208. atAxes: aCollection ifAbsent: aBlock put: value
  209. | penultimate |
  210. penultimate := self atAxes: aCollection allButLast ifAbsent: aBlock.
  211. ^ aCollection last asAxisIn: penultimate ifAbsent: aBlock put: value
  212. !
  213. axes: aCollection consume: aBlock
  214. | value |
  215. value := self atAxes: aCollection ifAbsent: [ ^self ].
  216. ^ aBlock value: value
  217. !
  218. axes: aCollection transform: aBlock
  219. | value |
  220. aCollection last. "raise if empty"
  221. value := self atAxes: aCollection ifAbsent: [ ^self ].
  222. value := aBlock value: value.
  223. value := self atAxes: aCollection ifAbsent: [ ^self ] put: value.
  224. self registeredAxon ifNotNil: [:axon | axon changed: aCollection]
  225. !
  226. registeredAxon
  227. <inlineJS: 'return self.$axon$'>
  228. ! !
  229. !String methodsFor: '*Axxord'!
  230. asAxisIn: anObject ifAbsent: aBlock
  231. (anObject respondsTo: #at:ifAbsent:)
  232. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  233. ifFalse: aBlock
  234. !
  235. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  236. (anObject respondsTo: #at:put:)
  237. ifTrue: [ ^ anObject at: self put: anotherObject ]
  238. ifFalse: aBlock
  239. ! !