简体   繁体   English

如何在SmallTalk(Pharo)中获取邮件发件人?

[英]How do I get a message sender in SmallTalk (Pharo)?

I'm having trouble getting the sender of a message in SmallTalk. 我在使用SmallTalk时收到邮件的发件人时遇到问题。 What I want to accomplish is to modify the return value of a method (A) from another method (B) which is called by the first one (A). 我想要完成的是从另一个方法(B)修改方法(A)的返回值,该方法由第一个方法(A)调用。 Again... A calls B, and I want B to return a value from A's context. 再次...... A调用B,我希望B从A的上下文中返回一个值。

Example code: 示例代码:

This would be A: 这将是A:

A

| aResult aPartialResult |

aPartialResult := self B.

"do things with aPartialResult"

^aResult.

And this would be B: 这将是B:

B

| aResult |

[ aResult := "do something" ]
                        on: Exception
                        do: ["make A return something"].
^aResult.

The thing is that I want to the exceptions that could be raised in B to be handled in B too. 问题是我想要在B中提出的异常也可以在B中处理。 That's why I don't just raise an exception in B to handle it in A and easily return from there. 这就是为什么我不只是在B中引发一个例外来处理A中的一个例子而且很容易从那里返回。

I thought I would be able to do this using thisContext, but the sender is nil. 我以为我可以使用thisContext做到这一点,但发件人是零。 It wouldn't hurt to also get an answer on why is that... 同样得到答案的原因也不会有什么害处......

Thanks in advance! 提前致谢!

Guillermo, Exception handling can seamlessly replace a few bad ideas here: Guillermo,异常处理可以在这里无缝地替换一些不好的想法:

  1. using thisContext (which is almost never necessary and usually a bad idea) 使用thisContext(这几乎不是必需的,通常是一个坏主意)
  2. passing strings around eg '1|', UserInterface invalidCartIdErrorMessage 传递字符串,例如'1 |',UserInterface invalidCartIdErrorMessage
  3. Using return: with those strings to pass errors along 使用return:使用这些字符串传递错误

Also, retrieveCart:onErrorReturnFrom: is doing too much. 另外,retrieveCart:onErrorReturnFrom:做得太多了。 With all the error handlers, the actual logic gets lost. 使用所有错误处理程序,实际逻辑会丢失。

So, the first thing I would to is create Error subclasses to represent your domain concepts eg AddBookError, CartExpiredError, InvalidCartError 所以,我要做的第一件事是创建Error子类来表示你的域概念,例如AddBookError,CartExpiredError,InvalidCartError

Then you just set the error message, maybe like: 然后你只需设置错误信息,也许像:

CartExpiredError>>initialize

    super initialize.
    self messageText: '1|', UserInterface cartHasExpiredErrorMessage.

The next thing (really two steps) is to replace the raw dictionary methods with private accessors, which can use your new Error classes, like so: 接下来的事情(实际上是两个步骤)是用私有访问器替换原始字典方法,私有访问器可以使用新的错误类,如下所示:

timestampFor: aCartId

    ^ cartCreationDateAndTime at: aCartId ifAbsent: [ InvalidCartError signal ].

and

cartNumber: aCartId 

     ^ carts at: aCartId ifAbsent: [ InvalidCartError signal ].

Cart>>add: aQuantity booksWithISBN: aBookISBN

    fail ifTrue: [ AddBookError signal ].

Now, retrieveCart:onErrorReturnFrom: can become: 现在,retrieveCart:onErrorReturnFrom:可以变为:

retrieveCart: aCartId

    | aCartCreationDateAndTime |
    aCartCreationDateAndTime := self timestampFor: aCartId.
    Time now > (aCartCreationDateAndTime + 30 minutes) ifTrue:  [ CartExpiredError signal ].
    ^ self cartNumber: aCartId.

And finally, the greatly-simplified A becomes: 最后,大大简化的A成为:

add: aQuantity booksWithISBN: aBookISBN toCart: aCartId

     | aCart |
    [aCart := self retrieveCart: aCartId.
    aCart add: aQuantity booksWithISBN: aBookISBN]
        on: Error
        do: [ :e |  ^ e messageText ].
    ^ '0|OK'.

This can still be cleaned (eg Make a superclass for all the Error classes that prepends '1|' to the messageText), and obviously you will have to work this simplified version into your actual project, but can you start to see how exceptions can make your life easier? 这仍然可以被清理(例如,为preTending'1 |'添加到messageText的所有Error类创建一个超类),显然你必须将这个简化版本用于你的实际项目,但是你能开始看看例外如何让你的生活更轻松?

Here is a working mockup of the code, with passing tests on github 是代码的工作模型,在github上传递测试

nb The one other thing I noticed was aCartCreationDateAndTime. nb我注意到的另一件事是aCartCreationDateAndTime。 It would seem more natural to have this be a property of the cart, but maybe that doesn't make sense in the actual application... 将它作为购物车的属性似乎更自然,但也许这在实际应用中没有意义......

A simple way is that A pass a Block with a return inside to B, like : 一个简单的方法是A传递一个带有返回B的块,如:

A
   | aResult aPartialResult |
   aPartialResult := self BonSpecialConditionDo: [:partial | ^partial].
   ...snip...

Then 然后

BonSpecialConditionDo: aBlock
    | partialResult |
    partialResult := self doSomethingPartial.
    ^[self doSomething]
        on: SomeException
        do: [:exc | aBlock value: partialResult]

Beware, catching Exception is considered dangerous (you catch too many things). 注意,捕获异常被认为是危险的(你捕获了太多东西)。

EDIT: I just removed an un-necessary return ^ inside the handler 编辑:我刚刚在处理程序中删除了一个不必要的返回^

EDIT: doing it with superpowers (but don't kill a fly with a hammer) 编辑:与超级大国一起做(但不要用锤子杀死苍蝇)

B
    | partialResult |
    partialResult := self doSomethingPartial.
    ^[self doSomething]
        on: SomeException
        do: [:exc | thisContext home sender home return: partialResult ]

I thought you could access thisContext through the Exception exc (this is the instance variable handlerContext), but there does not seem to be any convenient message for accessing this internal state... 我以为你可以通过Exception exc访问thisContext(这是实例变量handlerContext),但似乎没有任何方便的消息来访问这个内部状态...

Got it! 得到它了!

Here's A: 这是A:

A

| aResult aPartialResult |

aPartialResult := self BOnErrorReturnFrom: thisContext.

"do things with aPartialResult"

^aResult.

And this would be B: 这将是B:

BOnErrorReturnFrom: aContext

| aResult |

[ aResult := "do something" ]
                        on: Exception
                        do: [aContext return: "whatever you want :)"].
^aResult.

It wasn't so hard after I realized that the keyword "thisContext", when used in a BlockClosure, doesn't return the ContextPart in which the block was declared but something else (still not sure what it is, though). 在我意识到关键字“thisContext”在BlockClosure中使用后,并没有返回声明了块的ContextPart而是返回其他内容(但仍然不确定它是什么)之后并没那么难。

In response to Sean: 回应肖恩:

What I'm attempting to do with this is to avoid repetition of code. 我试图做的是避免重复代码。 The object which implements A and B is the inner (model side) part of a REST interface, so I want it to return a string and only a string (I don't want exceptions nor anything different than a string object to get through). 实现A和B的对象是REST接口的内部(模型侧)部分,所以我希望它返回一个字符串,只返回一个字符串(我不想要异常,也不需要任何不同于字符串对象的东西) 。 In my specific problem, A (sorry for breaking the Smalltalk message naming convention, but I think that changing that now will lead to further confusion...) receives a cart ID and will do something with that cart. 在我的具体问题中,A(很抱歉打破了Smalltalk消息命名约定,但我认为现在更改它会导致进一步的混淆......)会收到购物车ID并会对该购物车执行某些操作。 A has to retrieve the cart using that ID, make some validations on the cart and, in case of error, return an ad hoc message (always as a string). A必须使用该ID检索购物车,在购物车上进行一些验证,如果有错误,则返回ad hoc消息(始终作为字符串)。 This retrieval-and-validation code will be repeated in each message that has to retrieve a cart. 将在每个必须检索购物车的消息中重复此检索和验证代码。

This is the proper code I finally got: 这是我最终获得的正确代码:

This is A: (Pay no attention to the #try message. It just assures that no exception gets out without being converted to a string. If anyone knows how to do this in a better way, please tell me how!) 这是A :(不要注意#try消息。它只是确保没有异常会被转换为字符串。如果有人知道如何以更好的方式做到这一点,请告诉我如何!)

add: aQuantity booksWithISBN: aBookISBN toCart: aCartId

    | aCart aContext |

    aContext := thisContext.

    ^self try: [

        aCart := self retrieveCart: aCartId onErrorReturnFrom: aContext.

        [aCart add: aQuantity booksWithISBN: aBookISBN]
                                                    on: Error
                                                    do: [ :anError | ^'1|', (self formatAsResponse: anError messageText) ].

        ^'0|OK'.
    ].

This is B: 这是B:

retrieveCart: aCartId onErrorReturnFrom: aContext

    | aCartCreationDateAndTime aCart |

    [aCartCreationDateAndTime := cartCreationDateAndTime at: aCartId asInteger.]
                                                                                    on: KeyNotFound 
                                                                                    do: [ aContext return: ('1|', (UserInterface invalidCartIdErrorMessage)).].

    (systemClock now > (aCartCreationDateAndTime + 30 minutes)) 
                                                            ifTrue:  [aContext return: ('1|', (UserInterface cartHasExpiredErrorMessage))].

    [aCart := carts at: aCartId asInteger.]
                                    on: KeyNotFound
                                    do: [ aContext return: ('1|', (UserInterface invalidCartIdErrorMessage))].

    ^aCart.

A 一种

| | aResult aPartialResult | aResult aPartialResult |

aPartialResult := self B. aPartialResult:= self B.

"do things with aPartialResult" “用aPartialResult做事”

^aResult. ^ aResult。

B

| | aResult | aResult |

[ aResult := "do something" ] on: Exception do: ["make A return something"]. [aResult:=“做某事”]:例外:[“让A返回一些东西”]。 ^aResult. ^ aResult。

i would write it as 我会把它写成

A 一种

| | aResult aPartialResult | aResult aPartialResult |

aPartialResult := self B. aPartialResult ifNotNil:[ aPartialResult:= self B. aPartialResult ifNotNil:[

"do things with aPartialResult"]. “用aPartialResult做事”。

^aResult. ^ aResult。

B

| | aResult | aResult |

[ aResult := "do something" ] on: Exception do: [:e|^nil]. [aResult:=“做某事”]:例外:[:e | ^ nil]。 ^aResult. ^ aResult。

well then thats just me!!! 那么那就是我!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM