Axxord.st 7.6 KB

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