Trapped-Backend.st 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. Object subclass: #ListKeyedEntity
  68. instanceVariableNames: 'axon 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. - model: (with a wrapped object)
  80. - axon: (with a subclass of `AxonBase`)!
  81. !ListKeyedEntity methodsFor: 'accessing'!
  82. axon
  83. ^ axon
  84. !
  85. axon: anAxon
  86. axon := anAxon
  87. interestFactory: [ :description :block |
  88. (description notEmpty and: [ description last isNil ])
  89. ifTrue: [ InterestedInTrapPathSubtree new aspect: description allButLast block: block; yourself ]
  90. ifFalse: [ InterestedInTrapPath new aspect: description block: block; yourself ]];
  91. yourself
  92. !
  93. model: anObject
  94. payload := anObject.
  95. self changed: #()
  96. ! !
  97. !ListKeyedEntity methodsFor: 'action'!
  98. changed: anAspect
  99. self axon changed: anAspect
  100. !
  101. watch: path do: aBlock
  102. self axon on: path hook: [ self read: path do: aBlock ]
  103. ! !
  104. ListKeyedEntity subclass: #ListKeyedDirectEntity
  105. instanceVariableNames: ''
  106. package: 'Trapped-Backend'!
  107. !ListKeyedDirectEntity commentStamp!
  108. I am ListKeyedEntity that directly manipulate
  109. the wrapped model object.!
  110. !ListKeyedDirectEntity methodsFor: 'action'!
  111. modify: path do: aBlock
  112. | newValue eavModel |
  113. eavModel := path asEavModel.
  114. newValue := aBlock value: (eavModel on: payload).
  115. [ eavModel on: payload put: newValue ] ensure: [ self changed: path ]
  116. !
  117. read: path do: aBlock
  118. | eavModel |
  119. eavModel := path asEavModel.
  120. aBlock value: (eavModel on: payload)
  121. ! !
  122. ListKeyedEntity subclass: #ListKeyedIsolatedEntity
  123. instanceVariableNames: ''
  124. package: 'Trapped-Backend'!
  125. !ListKeyedIsolatedEntity commentStamp!
  126. I am ListKeyedEntity that guards access
  127. to the wrapped model object via Isolator.!
  128. !ListKeyedIsolatedEntity methodsFor: 'accessing'!
  129. model: anObject
  130. super model: (Isolator on: anObject)
  131. ! !
  132. !ListKeyedIsolatedEntity methodsFor: 'action'!
  133. modify: path do: aBlock
  134. | eavModel |
  135. eavModel := ({{#root}},path) asEavModel.
  136. [ payload model: eavModel modify: aBlock ] ensure: [ self changed: path ]
  137. !
  138. read: path do: aBlock
  139. | eavModel |
  140. eavModel := ({{#root}},path) asEavModel.
  141. payload model: eavModel read: aBlock
  142. ! !
  143. !Array methodsFor: '*Trapped-Backend'!
  144. asTrapAtPut: value sendTo: anObject
  145. ^anObject perform: (self first, ':') asSymbol withArguments: { value }
  146. !
  147. asTrapAtSendTo: anObject
  148. ^[anObject perform: self first] on: MessageNotUnderstood do: [^nil]
  149. ! !
  150. !Number methodsFor: '*Trapped-Backend'!
  151. asTrapAtPut: value sendTo: anObject
  152. ^anObject at: self put: value
  153. !
  154. asTrapAtSendTo: anObject
  155. ^anObject ifNotNil: [ anObject at: self ifAbsent: [nil] ]
  156. ! !
  157. !Object methodsFor: '*Trapped-Backend'!
  158. asTrapAtPut: value sendTo: anObject
  159. self error: 'Trapped cannot put at ', self class name, ' type key.'
  160. !
  161. asTrapAtSendTo: anObject
  162. ^nil
  163. ! !
  164. !SequenceableCollection methodsFor: '*Trapped-Backend'!
  165. asEavModel
  166. | model |
  167. model := EavModel new.
  168. model getBlock: [ :anObject |
  169. self inject: anObject into: [ :soFar :segment |
  170. segment asTrapAtSendTo: soFar ]].
  171. self isEmpty ifFalse: [
  172. model putBlock: [ :anObject :value | | penultimate |
  173. penultimate := self allButLast inject: anObject into: [ :soFar :segment |
  174. soFar ifNotNil: [ segment asTrapAtSendTo: soFar ]].
  175. self last asTrapAtPut:value sendTo: penultimate ]].
  176. ^model
  177. ! !
  178. !String methodsFor: '*Trapped-Backend'!
  179. asTrapAtPut: value sendTo: anObject
  180. ^anObject at: self put: value
  181. !
  182. asTrapAtSendTo: anObject
  183. ^anObject ifNotNil: [ anObject at: self ifAbsent: [nil] ]
  184. ! !