Axxord.st 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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: #Axolator
  25. instanceVariableNames: 'root'
  26. package: 'Axxord'!
  27. !Axolator methodsFor: 'accessing'!
  28. root
  29. ^root
  30. !
  31. root: anObject
  32. root := anObject
  33. ! !
  34. !Axolator methodsFor: 'action'!
  35. model: anEavModel modify: aBlock
  36. | newValue |
  37. newValue := aBlock value: (anEavModel on: self).
  38. anEavModel on: self put: newValue deepCopy
  39. !
  40. model: anEavModel read: aBlock
  41. aBlock value: (anEavModel on: self) deepCopy
  42. ! !
  43. !Axolator class methodsFor: 'instance creation'!
  44. on: anObject
  45. ^self new root: anObject
  46. ! !
  47. Object subclass: #Axon
  48. instanceVariableNames: 'factory'
  49. package: 'Axxord'!
  50. !Axon commentStamp!
  51. I represent a pub-sub based on a key (called 'aspect').
  52. I manage aspect-block subscriptions (called 'interests') as well as run blocks of dirtied interests.
  53. The interest objects are responsible of decision if the change of an aspect is relevant for them.
  54. Interest object must be subclasses of `AxonInterest`.
  55. My subclasses must provide implementation for:
  56. - add:
  57. - do:
  58. - clean!
  59. !Axon methodsFor: 'action'!
  60. addInterest: anInterest
  61. self
  62. add: (anInterest flag; yourself);
  63. dirty: true
  64. !
  65. changed: anAspect
  66. | needsToRun |
  67. needsToRun := false.
  68. self do: [ :each |
  69. (each accepts: anAspect) ifTrue: [
  70. each flag.
  71. needsToRun := true ]].
  72. self dirty: needsToRun
  73. !
  74. changedAll
  75. | needsToRun |
  76. needsToRun := false.
  77. self do: [ :each |
  78. each flag.
  79. needsToRun := true ].
  80. self dirty: needsToRun
  81. !
  82. dirty: aBoolean
  83. aBoolean ifTrue: [[ self run ] fork]
  84. !
  85. run
  86. [
  87. | needsClean |
  88. needsClean := false.
  89. self do: [ :each |
  90. each isFlagged ifTrue: [ each run ].
  91. each isClosed ifTrue: [ needsClean := true ]
  92. ].
  93. needsClean ifTrue: [ self clean ]
  94. ] on: Error do: [ self dirty: true ]
  95. ! !
  96. !Axon methodsFor: 'injecting'!
  97. registerIn: anObject
  98. <inlineJS: 'anObject.$axon$=self'>
  99. ! !
  100. Axon subclass: #SimpleAxon
  101. instanceVariableNames: 'queue'
  102. package: 'Axxord'!
  103. !SimpleAxon methodsFor: 'accessing'!
  104. add: aSubscription
  105. queue add: aSubscription.
  106. ! !
  107. !SimpleAxon methodsFor: 'bookkeeping'!
  108. clean
  109. queue := queue reject: [ :each | each isClosed ]
  110. ! !
  111. !SimpleAxon methodsFor: 'enumeration'!
  112. do: aBlock
  113. queue do: aBlock
  114. ! !
  115. !SimpleAxon methodsFor: 'initialization'!
  116. initialize
  117. super initialize.
  118. queue := OrderedCollection new
  119. ! !
  120. Object subclass: #AxonInterest
  121. instanceVariableNames: 'flagged'
  122. package: 'Axxord'!
  123. !AxonInterest methodsFor: 'accessing'!
  124. flag
  125. flagged := true
  126. ! !
  127. !AxonInterest methodsFor: 'action'!
  128. close
  129. self subclassResponsibility
  130. !
  131. enact
  132. self subclassResponsibility
  133. !
  134. run
  135. [ flagged := false. self enact ]
  136. on: AxonOff do: [ self close ]
  137. ! !
  138. !AxonInterest methodsFor: 'initialization'!
  139. initialize
  140. super initialize.
  141. flagged := false.
  142. ! !
  143. !AxonInterest methodsFor: 'testing'!
  144. accepts: anAspect
  145. "Should return true if change for anAspect is relevant for this AxonInterest"
  146. self subclassResponsibility
  147. !
  148. isClosed
  149. self subclassResponsibility
  150. !
  151. isFlagged
  152. ^flagged
  153. ! !
  154. AxonInterest subclass: #ConstantAspectPluggableEnactInterest
  155. instanceVariableNames: 'aspect actionBlock'
  156. package: 'Axxord'!
  157. !ConstantAspectPluggableEnactInterest methodsFor: 'accessing'!
  158. aspect: anAspect block: aBlock
  159. aspect := anAspect.
  160. actionBlock := aBlock
  161. ! !
  162. !ConstantAspectPluggableEnactInterest methodsFor: 'action'!
  163. close
  164. actionBlock := nil
  165. !
  166. enact
  167. actionBlock value
  168. ! !
  169. !ConstantAspectPluggableEnactInterest methodsFor: 'initialization'!
  170. initialize
  171. super initialize.
  172. aspect := nil.
  173. actionBlock := nil.
  174. ! !
  175. !ConstantAspectPluggableEnactInterest methodsFor: 'testing'!
  176. isClosed
  177. ^actionBlock isNil
  178. ! !
  179. ConstantAspectPluggableEnactInterest subclass: #InterestedInEqual
  180. instanceVariableNames: ''
  181. package: 'Axxord'!
  182. !InterestedInEqual methodsFor: 'testing'!
  183. accepts: anAspect
  184. ^ anAspect = aspect
  185. ! !
  186. ConstantAspectPluggableEnactInterest subclass: #InterestedThruAxes
  187. instanceVariableNames: ''
  188. package: 'Axxord'!
  189. !InterestedThruAxes methodsFor: 'testing'!
  190. accepts: anAspect
  191. ^anAspect size <= aspect size
  192. ifTrue: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
  193. ifFalse: [aspect = (anAspect copyFrom: 1 to: aspect size)]
  194. ! !
  195. ConstantAspectPluggableEnactInterest subclass: #InterestedUpToAxes
  196. instanceVariableNames: ''
  197. package: 'Axxord'!
  198. !InterestedUpToAxes methodsFor: 'testing'!
  199. accepts: anAspect
  200. ^anAspect size <= aspect size and: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
  201. ! !
  202. Error subclass: #AxonOff
  203. instanceVariableNames: ''
  204. package: 'Axxord'!
  205. !AxonOff commentStamp!
  206. Signal me from the subscription block to unsubscribe it.!
  207. !Array methodsFor: '*Axxord'!
  208. asAxisIn: anObject ifAbsent: aBlock
  209. | receiver selector result |
  210. selector := self first.
  211. receiver := anObject yourself. "JSObjectProxy hack"
  212. [ result := receiver perform: selector ]
  213. on: MessageNotUnderstood do: [ :mnu |
  214. ((mnu message selector = selector
  215. and: [ mnu receiver == receiver ])
  216. and: [ mnu message arguments isEmpty ])
  217. ifFalse: [ mnu resignal ].
  218. ^ aBlock value ].
  219. ^ result
  220. !
  221. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  222. | receiver selector arguments result |
  223. selector := self first asMutator.
  224. receiver := anObject yourself. "JSObjectProxy hack"
  225. arguments := { anotherObject }.
  226. [ result := receiver perform: selector withArguments: arguments ]
  227. on: MessageNotUnderstood do: [ :mnu |
  228. ((mnu message selector = selector
  229. and: [ mnu receiver == receiver ])
  230. and: [ mnu message arguments = arguments ])
  231. ifFalse: [ mnu resignal ].
  232. ^ aBlock value ].
  233. ^ result
  234. ! !
  235. !Number methodsFor: '*Axxord'!
  236. asAxisIn: anObject ifAbsent: aBlock
  237. (anObject respondsTo: #at:ifAbsent:)
  238. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  239. ifFalse: aBlock
  240. !
  241. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  242. (anObject respondsTo: #at:put:)
  243. ifTrue: [ ^ anObject at: self put: anotherObject ]
  244. ifFalse: aBlock
  245. ! !
  246. !Object methodsFor: '*Axxord'!
  247. asAxisIn: anObject ifAbsent: aBlock
  248. ^ aBlock value
  249. !
  250. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  251. ^ aBlock value
  252. !
  253. atAxes: aCollection ifAbsent: aBlock
  254. ^ aCollection inject: self into: [ :soFar :segment |
  255. segment asAxisIn: soFar ifAbsent: [ ^ aBlock value ]]
  256. !
  257. atAxes: aCollection ifAbsent: aBlock put: value
  258. | penultimate |
  259. penultimate := self atAxes: aCollection allButLast ifAbsent: aBlock.
  260. ^ aCollection last asAxisIn: penultimate ifAbsent: aBlock put: value
  261. !
  262. axes: aCollection consume: aBlock
  263. | value |
  264. value := self atAxes: aCollection ifAbsent: [ ^self ].
  265. ^ aBlock value: value
  266. !
  267. axes: aCollection transform: aBlock
  268. | value newValue |
  269. value := self atAxes: aCollection ifAbsent: [ ^self ].
  270. newValue := aBlock value: value.
  271. value == newValue ifFalse: [ self atAxes: aCollection ifAbsent: [ ^self ] put: newValue ].
  272. self registeredAxon ifNotNil: [:axon | axon changed: aCollection]
  273. !
  274. registeredAxon
  275. <inlineJS: 'return self.$axon$'>
  276. ! !
  277. !String methodsFor: '*Axxord'!
  278. asAxisIn: anObject ifAbsent: aBlock
  279. (anObject respondsTo: #at:ifAbsent:)
  280. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  281. ifFalse: aBlock
  282. !
  283. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  284. (anObject respondsTo: #at:put:)
  285. ifTrue: [ ^ anObject at: self put: anotherObject ]
  286. ifFalse: aBlock
  287. ! !