Trapped-Backend.st 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. Smalltalk createPackage: 'Trapped-Backend'!
  2. Object subclass: #EavModel
  3. instanceVariableNames: 'getBlock putBlock'
  4. package: 'Trapped-Backend'!
  5. !EavModel commentStamp!
  6. External actor value model.!
  7. !EavModel methodsFor: 'accessing'!
  8. getBlock: aBlock
  9. getBlock := aBlock
  10. !
  11. on: anObject
  12. "Returns value of model applied on object"
  13. ^getBlock value: anObject
  14. !
  15. on: anObject put: anObject2
  16. "Puts a value via model applied on object"
  17. ^putBlock value: anObject value: anObject2
  18. !
  19. putBlock: aBlock
  20. putBlock := aBlock
  21. ! !
  22. !EavModel methodsFor: 'initialization'!
  23. initialize
  24. super initialize.
  25. getBlock := [ self error: 'No getter block.' ].
  26. putBlock := [ self error: 'No putter block.' ].
  27. ! !
  28. AxonInterestBase subclass: #InterestedInTrapPath
  29. instanceVariableNames: ''
  30. package: 'Trapped-Backend'!
  31. !InterestedInTrapPath methodsFor: 'testing'!
  32. accepts: anAspect
  33. ^anAspect size <= aspect size and: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
  34. ! !
  35. AxonInterestBase subclass: #InterestedInTrapPathSubtree
  36. instanceVariableNames: ''
  37. package: 'Trapped-Backend'!
  38. !InterestedInTrapPathSubtree methodsFor: 'testing'!
  39. accepts: anAspect
  40. ^anAspect size <= aspect size
  41. ifTrue: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
  42. ifFalse: [aspect = (anAspect copyFrom: 1 to: aspect size)]
  43. ! !
  44. Object subclass: #Isolator
  45. instanceVariableNames: 'root'
  46. package: 'Trapped-Backend'!
  47. !Isolator methodsFor: 'accessing'!
  48. root
  49. ^root
  50. !
  51. root: anObject
  52. root := anObject
  53. ! !
  54. !Isolator methodsFor: 'action'!
  55. model: anEavModel modify: aBlock
  56. | newValue |
  57. newValue := aBlock value: (anEavModel on: self).
  58. anEavModel on: self put: newValue deepCopy
  59. !
  60. model: anEavModel read: aBlock
  61. aBlock value: (anEavModel on: self) deepCopy
  62. ! !
  63. !Isolator class methodsFor: 'instance creation'!
  64. on: anObject
  65. ^self new root: anObject
  66. ! !
  67. AxonizedObject subclass: #ListKeyedEntity
  68. instanceVariableNames: 'payload'
  69. package: 'Trapped-Backend'!
  70. !ListKeyedEntity commentStamp!
  71. I am base class for #('string-at-index' #selector numeric-at-index)-array-path-keyed entities,
  72. that moderate access to the wrapped model object via read;do and modify:do:
  73. and allow pub-sub via watch:do:.
  74. The wrapped model can be any smalltalk object.
  75. My subclasses need to provide implementation for:
  76. - read:do:
  77. - modify:do:
  78. and must issue these calls when initializing:
  79. - axon: (with a subclass of `AxonBase`)
  80. - model: (with a wrapped object, after `axon:`)!
  81. !ListKeyedEntity methodsFor: 'accessing'!
  82. model: anObject
  83. self axon
  84. interestFactory: [ :description :block |
  85. (description notEmpty and: [ description last isNil ])
  86. ifTrue: [ InterestedInTrapPathSubtree new aspect: description allButLast block: block; yourself ]
  87. ifFalse: [ InterestedInTrapPath new aspect: description block: block; yourself ]].
  88. payload := anObject.
  89. self changed: #()
  90. ! !
  91. !ListKeyedEntity methodsFor: 'action'!
  92. watch: path do: aBlock
  93. self axon on: path hook: [ self read: path do: aBlock ]
  94. ! !
  95. ListKeyedEntity subclass: #ListKeyedDirectEntity
  96. instanceVariableNames: ''
  97. package: 'Trapped-Backend'!
  98. !ListKeyedDirectEntity commentStamp!
  99. I am ListKeyedEntity that directly manipulate
  100. the wrapped model object.!
  101. !ListKeyedDirectEntity methodsFor: 'action'!
  102. modify: path do: aBlock
  103. | newValue eavModel |
  104. eavModel := path asEavModel.
  105. newValue := aBlock value: (eavModel on: payload).
  106. [ eavModel on: payload put: newValue ] ensure: [ self changed: path ]
  107. !
  108. read: path do: aBlock
  109. | eavModel |
  110. eavModel := path asEavModel.
  111. aBlock value: (eavModel on: payload)
  112. ! !
  113. ListKeyedEntity subclass: #ListKeyedIsolatedEntity
  114. instanceVariableNames: ''
  115. package: 'Trapped-Backend'!
  116. !ListKeyedIsolatedEntity commentStamp!
  117. I am ListKeyedEntity that guards access
  118. to the wrapped model object via Isolator.!
  119. !ListKeyedIsolatedEntity methodsFor: 'accessing'!
  120. model: anObject
  121. super model: (Isolator on: anObject)
  122. ! !
  123. !ListKeyedIsolatedEntity methodsFor: 'action'!
  124. modify: path do: aBlock
  125. | eavModel |
  126. eavModel := ({{#root}},path) asEavModel.
  127. [ payload model: eavModel modify: aBlock ] ensure: [ self changed: path ]
  128. !
  129. read: path do: aBlock
  130. | eavModel |
  131. eavModel := ({{#root}},path) asEavModel.
  132. payload model: eavModel read: aBlock
  133. ! !
  134. !Array methodsFor: '*Trapped-Backend'!
  135. asTrapAtPut: value sendTo: anObject
  136. ^anObject perform: (self first, ':') asSymbol withArguments: { value }
  137. !
  138. asTrapAtSendTo: anObject
  139. ^[anObject perform: self first] on: MessageNotUnderstood do: [^nil]
  140. ! !
  141. !Number methodsFor: '*Trapped-Backend'!
  142. asTrapAtPut: value sendTo: anObject
  143. ^anObject at: self put: value
  144. !
  145. asTrapAtSendTo: anObject
  146. ^anObject ifNotNil: [ anObject at: self ifAbsent: [nil] ]
  147. ! !
  148. !Object methodsFor: '*Trapped-Backend'!
  149. asTrapAtPut: value sendTo: anObject
  150. self error: 'Trapped cannot put at ', self class name, ' type key.'
  151. !
  152. asTrapAtSendTo: anObject
  153. ^nil
  154. ! !
  155. !SequenceableCollection methodsFor: '*Trapped-Backend'!
  156. asEavModel
  157. | model |
  158. model := EavModel new.
  159. model getBlock: [ :anObject |
  160. self inject: anObject into: [ :soFar :segment |
  161. segment asTrapAtSendTo: soFar ]].
  162. self isEmpty ifFalse: [
  163. model putBlock: [ :anObject :value | | penultimate |
  164. penultimate := self allButLast inject: anObject into: [ :soFar :segment |
  165. soFar ifNotNil: [ segment asTrapAtSendTo: soFar ]].
  166. self last asTrapAtPut:value sendTo: penultimate ]].
  167. ^model
  168. ! !
  169. !String methodsFor: '*Trapped-Backend'!
  170. asTrapAtPut: value sendTo: anObject
  171. ^anObject at: self put: value
  172. !
  173. asTrapAtSendTo: anObject
  174. ^anObject ifNotNil: [ anObject at: self ifAbsent: [nil] ]
  175. ! !