Axxord.st 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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: 'accessing'!
  74. addInterest: anInterest
  75. self
  76. add: (anInterest flag; yourself);
  77. dirty: true
  78. ! !
  79. !Axon methodsFor: 'change-update'!
  80. changed: anAspect
  81. | needsToRun |
  82. needsToRun := false.
  83. self do: [ :each |
  84. (each accepts: anAspect) ifTrue: [
  85. each flag.
  86. needsToRun := true ]].
  87. self dirty: needsToRun
  88. !
  89. changedAll
  90. | needsToRun |
  91. needsToRun := false.
  92. self do: [ :each |
  93. each flag.
  94. needsToRun := true ].
  95. self dirty: needsToRun
  96. ! !
  97. !Axon methodsFor: 'primitive ops'!
  98. add: anInterest
  99. self subclassResponsibility
  100. !
  101. clean
  102. self subclassResponsibility
  103. !
  104. do: aBlock
  105. self subclassResponsibility
  106. ! !
  107. !Axon methodsFor: 'private'!
  108. dirty: aBoolean
  109. aBoolean ifTrue: [[ self run ] fork]
  110. !
  111. run
  112. [
  113. | needsClean |
  114. needsClean := false.
  115. self do: [ :each |
  116. each isFlagged ifTrue: [ each run ].
  117. each isClosed ifTrue: [ needsClean := true ]
  118. ].
  119. needsClean ifTrue: [ self clean ]
  120. ] on: Error do: [ self dirty: true ]
  121. ! !
  122. Axon subclass: #SimpleAxon
  123. instanceVariableNames: 'queue'
  124. package: 'Axxord'!
  125. !SimpleAxon methodsFor: 'initialization'!
  126. initialize
  127. super initialize.
  128. queue := OrderedCollection new
  129. ! !
  130. !SimpleAxon methodsFor: 'primitive ops'!
  131. add: aSubscription
  132. queue add: aSubscription.
  133. !
  134. clean
  135. queue := queue reject: [ :each | each isClosed ]
  136. !
  137. do: aBlock
  138. queue do: aBlock
  139. ! !
  140. Object subclass: #AxonInterest
  141. instanceVariableNames: 'flagged'
  142. package: 'Axxord'!
  143. !AxonInterest methodsFor: 'accessing'!
  144. flag
  145. flagged := true
  146. ! !
  147. !AxonInterest methodsFor: 'action'!
  148. close
  149. self subclassResponsibility
  150. !
  151. enact
  152. self subclassResponsibility
  153. !
  154. run
  155. [ flagged := false. self enact ]
  156. on: AxonOff do: [ self close ]
  157. ! !
  158. !AxonInterest methodsFor: 'initialization'!
  159. initialize
  160. super initialize.
  161. flagged := false.
  162. ! !
  163. !AxonInterest methodsFor: 'testing'!
  164. accepts: anAspect
  165. "Should return true if change for anAspect is relevant for this AxonInterest"
  166. self subclassResponsibility
  167. !
  168. isClosed
  169. self subclassResponsibility
  170. !
  171. isFlagged
  172. ^flagged
  173. ! !
  174. AxonInterest subclass: #PluggableInterest
  175. instanceVariableNames: 'acceptBlock enactBlock'
  176. package: 'Axxord'!
  177. !PluggableInterest methodsFor: 'accessing'!
  178. accept: aBlock enact: anotherBlock
  179. acceptBlock := aBlock.
  180. enactBlock := anotherBlock
  181. ! !
  182. !PluggableInterest methodsFor: 'action'!
  183. close
  184. acceptBlock := nil.
  185. enactBlock := nil
  186. !
  187. enact
  188. enactBlock value
  189. ! !
  190. !PluggableInterest methodsFor: 'initialization'!
  191. initialize
  192. super initialize.
  193. self close
  194. ! !
  195. !PluggableInterest methodsFor: 'testing'!
  196. accepts: anAspect
  197. ^ acceptBlock value: anAspect
  198. !
  199. isClosed
  200. ^ acceptBlock isNil
  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. !JSObjectProxy methodsFor: '*Axxord'!
  236. asAxisIn: anObject ifAbsent: aBlock
  237. ^ aBlock value
  238. !
  239. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  240. ^ aBlock value
  241. !
  242. atAxes: aCollection ifAbsent: aBlock
  243. ^ aCollection inject: self into: [ :soFar :segment |
  244. segment asAxisIn: soFar ifAbsent: [ ^ aBlock value ]]
  245. !
  246. atAxes: aCollection ifAbsent: aBlock put: value
  247. | penultimate |
  248. penultimate := self atAxes: aCollection allButLast ifAbsent: aBlock.
  249. ^ aCollection last asAxisIn: penultimate ifAbsent: aBlock put: value
  250. !
  251. axes: aCollection consume: aBlock
  252. | value |
  253. value := self atAxes: aCollection ifAbsent: [ ^self ].
  254. ^ aBlock value: value
  255. !
  256. axes: aCollection transform: aBlock
  257. | value newValue |
  258. value := self atAxes: aCollection ifAbsent: [ ^self ].
  259. newValue := aBlock value: value.
  260. value == newValue ifFalse: [ self atAxes: aCollection ifAbsent: [ ^self ] put: newValue ].
  261. self axxord ifNotNil: [:axon | axon changed: aCollection]
  262. !
  263. axxord
  264. <inlineJS: 'return $self["@jsObject"].$axxord$'>
  265. !
  266. axxord: anAxon
  267. <inlineJS: '$self["@jsObject"].$axxord$ = anAxon'>
  268. ! !
  269. !Number methodsFor: '*Axxord'!
  270. asAxisIn: anObject ifAbsent: aBlock
  271. (anObject respondsTo: #at:ifAbsent:)
  272. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  273. ifFalse: aBlock
  274. !
  275. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  276. (anObject respondsTo: #at:put:)
  277. ifTrue: [ ^ anObject at: self put: anotherObject ]
  278. ifFalse: aBlock
  279. ! !
  280. !Object methodsFor: '*Axxord'!
  281. asAxisIn: anObject ifAbsent: aBlock
  282. ^ aBlock value
  283. !
  284. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  285. ^ aBlock value
  286. !
  287. atAxes: aCollection ifAbsent: aBlock
  288. ^ aCollection inject: self into: [ :soFar :segment |
  289. segment asAxisIn: soFar ifAbsent: [ ^ aBlock value ]]
  290. !
  291. atAxes: aCollection ifAbsent: aBlock put: value
  292. | penultimate |
  293. penultimate := self atAxes: aCollection allButLast ifAbsent: aBlock.
  294. ^ aCollection last asAxisIn: penultimate ifAbsent: aBlock put: value
  295. !
  296. axes: aCollection consume: aBlock
  297. | value |
  298. value := self atAxes: aCollection ifAbsent: [ ^self ].
  299. ^ aBlock value: value
  300. !
  301. axes: aCollection transform: aBlock
  302. | value newValue |
  303. value := self atAxes: aCollection ifAbsent: [ ^self ].
  304. newValue := aBlock value: value.
  305. value == newValue ifFalse: [ self atAxes: aCollection ifAbsent: [ ^self ] put: newValue ].
  306. self axxord ifNotNil: [:axon | axon changed: aCollection]
  307. !
  308. axxord
  309. <inlineJS: 'return self.$axxord$'>
  310. !
  311. axxord: anAxon
  312. <inlineJS: 'self.$axxord$ = anAxon'>
  313. ! !
  314. !String methodsFor: '*Axxord'!
  315. asAxisIn: anObject ifAbsent: aBlock
  316. (anObject respondsTo: #at:ifAbsent:)
  317. ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
  318. ifFalse: aBlock
  319. !
  320. asAxisIn: anObject ifAbsent: aBlock put: anotherObject
  321. (anObject respondsTo: #at:put:)
  322. ifTrue: [ ^ anObject at: self put: anotherObject ]
  323. ifFalse: aBlock
  324. ! !