Axxord.st 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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: #DumbAxon
  101. instanceVariableNames: ''
  102. package: 'Axxord'!
  103. !DumbAxon commentStamp!
  104. I am an axon that does nothing.!
  105. !DumbAxon methodsFor: 'as yet unclassified'!
  106. add: anInterest
  107. "pass"
  108. !
  109. clean
  110. "pass"
  111. !
  112. do: aBlock
  113. "pass"
  114. ! !
  115. Axon subclass: #SimpleAxon
  116. instanceVariableNames: 'queue'
  117. package: 'Axxord'!
  118. !SimpleAxon methodsFor: 'accessing'!
  119. add: aSubscription
  120. queue add: aSubscription.
  121. ! !
  122. !SimpleAxon methodsFor: 'bookkeeping'!
  123. clean
  124. queue := queue reject: [ :each | each isClosed ]
  125. ! !
  126. !SimpleAxon methodsFor: 'enumeration'!
  127. do: aBlock
  128. queue do: aBlock
  129. ! !
  130. !SimpleAxon methodsFor: 'initialization'!
  131. initialize
  132. super initialize.
  133. queue := OrderedCollection new
  134. ! !
  135. Object subclass: #AxonInterest
  136. instanceVariableNames: 'flagged'
  137. package: 'Axxord'!
  138. !AxonInterest methodsFor: 'accessing'!
  139. flag
  140. flagged := true
  141. ! !
  142. !AxonInterest methodsFor: 'action'!
  143. close
  144. self subclassResponsibility
  145. !
  146. enact
  147. self subclassResponsibility
  148. !
  149. run
  150. [ flagged := false. self enact ]
  151. on: AxonOff do: [ self close ]
  152. ! !
  153. !AxonInterest methodsFor: 'initialization'!
  154. initialize
  155. super initialize.
  156. flagged := false.
  157. ! !
  158. !AxonInterest methodsFor: 'testing'!
  159. accepts: anAspect
  160. "Should return true if change for anAspect is relevant for this AxonInterest"
  161. self subclassResponsibility
  162. !
  163. isClosed
  164. self subclassResponsibility
  165. !
  166. isFlagged
  167. ^flagged
  168. ! !
  169. AxonInterest subclass: #ConstantAspectPluggableEnactInterest
  170. instanceVariableNames: 'aspect actionBlock'
  171. package: 'Axxord'!
  172. !ConstantAspectPluggableEnactInterest methodsFor: 'accessing'!
  173. aspect: anAspect block: aBlock
  174. aspect := anAspect.
  175. actionBlock := aBlock
  176. ! !
  177. !ConstantAspectPluggableEnactInterest methodsFor: 'action'!
  178. close
  179. actionBlock := nil
  180. !
  181. enact
  182. actionBlock value
  183. ! !
  184. !ConstantAspectPluggableEnactInterest methodsFor: 'initialization'!
  185. initialize
  186. super initialize.
  187. aspect := nil.
  188. actionBlock := nil.
  189. ! !
  190. !ConstantAspectPluggableEnactInterest methodsFor: 'testing'!
  191. isClosed
  192. ^actionBlock isNil
  193. ! !
  194. ConstantAspectPluggableEnactInterest subclass: #InterestedInEqual
  195. instanceVariableNames: ''
  196. package: 'Axxord'!
  197. !InterestedInEqual methodsFor: 'testing'!
  198. accepts: anAspect
  199. ^ anAspect = aspect
  200. ! !
  201. ConstantAspectPluggableEnactInterest subclass: #InterestedThruAxes
  202. instanceVariableNames: ''
  203. package: 'Axxord'!
  204. !InterestedThruAxes methodsFor: 'testing'!
  205. accepts: anAspect
  206. ^anAspect size <= aspect size
  207. ifTrue: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
  208. ifFalse: [aspect = (anAspect copyFrom: 1 to: aspect size)]
  209. ! !
  210. ConstantAspectPluggableEnactInterest subclass: #InterestedUpToAxes
  211. instanceVariableNames: ''
  212. package: 'Axxord'!
  213. !InterestedUpToAxes methodsFor: 'testing'!
  214. accepts: anAspect
  215. ^anAspect size <= aspect size and: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
  216. ! !
  217. Error subclass: #AxonOff
  218. instanceVariableNames: ''
  219. package: 'Axxord'!
  220. !AxonOff commentStamp!
  221. Signal me from the subscription block to unsubscribe it.!
  222. !Array methodsFor: '*Axxord'!
  223. asAxisIn: anObject ifAbsent: aBlock
  224. | receiver selector result |
  225. selector := self first.
  226. receiver := anObject yourself. "JSObjectProxy hack"
  227. [ result := receiver perform: selector ]
  228. on: MessageNotUnderstood do: [ :mnu |
  229. ((mnu message selector = selector
  230. and: [ mnu receiver == receiver ])
  231. and: [ mnu message arguments isEmpty ])
  232. ifFalse: [ mnu resignal ].
  233. ^ aBlock value ].
  234. ^ result
  235. !
  236. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  237. | receiver selector arguments result |
  238. selector := self first asMutator.
  239. receiver := anObject yourself. "JSObjectProxy hack"
  240. arguments := { anotherObject }.
  241. [ result := receiver perform: selector withArguments: arguments ]
  242. on: MessageNotUnderstood do: [ :mnu |
  243. ((mnu message selector = selector
  244. and: [ mnu receiver == receiver ])
  245. and: [ mnu message arguments = arguments ])
  246. ifFalse: [ mnu resignal ].
  247. ^ aBlock value ].
  248. ^ result
  249. ! !
  250. !Number methodsFor: '*Axxord'!
  251. asAxisIn: anObject ifAbsent: aBlock
  252. (anObject respondsTo: #at:ifAbsent:)
  253. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  254. ifFalse: aBlock
  255. !
  256. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  257. (anObject respondsTo: #at:put:)
  258. ifTrue: [ ^ anObject at: self put: anotherObject ]
  259. ifFalse: aBlock
  260. ! !
  261. !Object methodsFor: '*Axxord'!
  262. asAxisIn: anObject ifAbsent: aBlock
  263. ^ aBlock value
  264. !
  265. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  266. ^ aBlock value
  267. !
  268. atAxes: aCollection ifAbsent: aBlock
  269. ^ aCollection inject: self into: [ :soFar :segment |
  270. segment asAxisIn: soFar ifAbsent: [ ^ aBlock value ]]
  271. !
  272. atAxes: aCollection ifAbsent: aBlock put: value
  273. | penultimate |
  274. penultimate := self atAxes: aCollection allButLast ifAbsent: aBlock.
  275. ^ aCollection last asAxisIn: penultimate ifAbsent: aBlock put: value
  276. !
  277. axes: aCollection consume: aBlock
  278. | value |
  279. value := self atAxes: aCollection ifAbsent: [ ^self ].
  280. ^ aBlock value: value
  281. !
  282. axes: aCollection transform: aBlock
  283. | value newValue |
  284. value := self atAxes: aCollection ifAbsent: [ ^self ].
  285. newValue := aBlock value: value.
  286. value == newValue ifFalse: [ self atAxes: aCollection ifAbsent: [ ^self ] put: newValue ].
  287. self registeredAxon ifNotNil: [:axon | axon changed: aCollection]
  288. !
  289. registeredAxon
  290. <inlineJS: 'return self.$axon$'>
  291. ! !
  292. !String methodsFor: '*Axxord'!
  293. asAxisIn: anObject ifAbsent: aBlock
  294. (anObject respondsTo: #at:ifAbsent:)
  295. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  296. ifFalse: aBlock
  297. !
  298. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  299. (anObject respondsTo: #at:put:)
  300. ifTrue: [ ^ anObject at: self put: anotherObject ]
  301. ifFalse: aBlock
  302. ! !