Axxord.st 8.9 KB

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