Pharo-users
By thread
pharo-users@lists.pharo.org
By month
Messages by month
- ----- 2026 -----
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- 7 participants
- 50354 messages
Re: [Pharo-users] [ANN] PharoLambda 1.5 - Pharo running on AWS Lambda now with saved Debug sessions via S3
by Stephane Ducasse
Tim
could you send me a "sexy" screen capture for a success story half september?
I will take you text as an input and come back to you.
Stef
On Thu, Aug 10, 2017 at 3:47 PM, Tim Mackinnon <tim(a)testit.works> wrote:
> I just wanted to thank everyone for their help in getting my pet project
> further along, so that now I can announce that PharoLambda is now working
> with the V7 minimal image and also supports post mortem debugging by saving
> a zipped fuel context onto S3.
>
> This latter item is particularly satisfying as at a recent serverless
> conference (JeffConf) there was a panel where poor development tools on
> serverless platforms was highlighted as a real problem.
>
> In our community weâve had these kinds of tools at our fingertips for ages -
> but I donât think the wider development community has really noticed.
> Debugging something short lived like a Lambda execution is quite startling,
> as the current answer is âadd more loggingâ, and we all know that sucks. To
> this end, Iâve created a little screencast showing this in action - and it
> was pretty cool because it was a real example I encountered when I got
> everything working and was trying my test application out.
>
> Iâve also put a bit of work into tuning the excellent GitLab CI tools, so
> that I can cache many of the artefacts used between different build runs
> (this might also be of interest to others using CI systems).
>
> The Gitlab project is on: https://gitlab.com/macta/PharoLambda
> And the screencast: https://www.youtube.com/watch?v=bNNCT1hLA3E
>
> Tim
>
>
> On 15 Jul 2017, at 00:39, Tim Mackinnon <tim(a)testit.works> wrote:
>
> Hi - Iâve been playing around with getting Pharo to run well on AWS Lambda.
> Itâs early days, but I though it might be interesting to share what Iâve
> learned so far.
>
> Usage examples and code at https://gitlab.com/macta/PharoLambda
>
> With help from many of the folks here, Iâve been able to get a simple
> example to run in 500ms-1200ms with a minimal Pharo 6 image. You can easily
> try it out yourself. This seems slightly better than what the GoLang folks
> have been able to do.
>
> Tim
>
>
Aug. 14, 2017
Re: [Pharo-users] [Pharo-dev] including Pillar in Pharo image by default
by Jimmie Houchin
Thank Tim,
My primary reason to submit the message was not to necessarily persuade
you per se. But to provide something historical for the mailing list as
this can be a recurring subject. Why use Pillar markup instead of
???(insert personal favorite).
If Pharo were to decide on a different markup language. The question
would still be which one, why and then how do we proceed. Then our
extensions may not be accepted by the greater body of users of said
markup. We would still be contributing to the fragmentation of markup.
As far as familiarity, I don't know. And familiarity with what. I do not
find that reStructuredText to be similar to Markdown.
It would stop people from asking why we aren't using Markdown. But it
wouldn't prevent others. Why aren't we using GFM Markdown, or Kramdown
or Commonmark or ...? Why aren't we using YAML or reST or AsciiDoc or
insert latest greatest creation markup or current flavor of the moment.
Which is why I wanted to point out that there is no consensus among
users of markup languages. At least I do not see one. Nor do I believe
that we have seen the end of creation of new markup languages.
I understand the difficulty, though I do not suffer from it as I have
not mastered any of those other languages. I have been using
Squeak/Pharo for a long time. I struggle when I look at those other
languages. To me they are the foreign ones.
And I do not see these emerging standards you refer to. When we see
Python, Ruby, Perl, C++, various projects, etc. communities having
consensus on a common markup for documentation. Then I see an emerging
standard. Until then it seems to possibly be an emerging standard for a
particular markup language which is among the set of markup languages.
If we were the only language and development environment doing our own
thing. Then we might have a very good reason to talk. But we are not.
Python with its enormous community does its own thing. I don't know that
other languages have a consensus for markup for documentation except for
Python and Pharo.
While writing this email I went and discovered that even GitHub is not
dogmatic about the subject. Obviously they have an opinion. But they
permit multiple markup languages. Quite possibly someone could write a
Pillar tool for GitHub to use and then we could just submit
Readme.pillar for our projects. :)
https://github.com/github/markup
Shows that GitHub allows for .markdown, .mdown, .mkdn, .md; .textile;
.rdoc; .org; .creole; .mediawiki, .wiki; .rst; .asciidoc, .adoc, .asc;
.pod. So it seems that there are many communities on GitHub who prefer
their own markup and tools.
We could possibly write the Pillar tool for GitHub or an exporter to the
preferred markup language of the above.
This author provides arguments for using reStructuredText over Markdown
for GitHub documents. Citing deficiencies in Markdown and expressiveness
in reST.
https://gist.github.com/dupuy/1855764
So again. I am just not seeing a consensus around any emerging standard
for "the markup language".
At the same time if you are desirous of writing in Commonmark in your
text editor. Can you not write conversion software that goes from
Commonmark to Pillar? Thus, meeting want you want and what we require?
If you were to do so, you would definitely have a good understanding of
the differences in philosophy and capabilities of each. Just a thought.
Any way, thanks for engaging in the conversation. I wasn't targeting you
personally, but rather the topic. You are not alone in your thinking.
The Pharo community is not alone in its thinking either.
Thanks.
Jimmie
On 08/14/2017 11:34 AM, Tim Mackinnon wrote:
> Jimmie et al. nicely reasoned arguments - and Doru's point about
> controlling the syntax is an interesting one that I hadnât thought about.
>
> Personally, I find having too many similar syntaxâs confusing -
> contributing to things is hard enough - having to remember that its !!
> Instead of ## and ââ instead of ** is just frustrating for me.
>
> My vote would be what Peter suggested - use
> http://spec.commonmark.org/0.28/ and put our Pillar extensions back on
> top for things that Stef was mentioning. (I think thatâs what Iâve
> understood gfm markdown is).
>
> Sure, maybe we were first with Pillar, but for me, lots of programming
> is in other languages, and I use Smalltalk where I can, and a hybrid
> of multiple languages and projects is often the reality - so a lowest
> common denominator of Markdown is just easier. The fact that we are
> quite close to what our colleagues in other languages use (regardless
> of what Python has chosen), is quite interesting.
>
> That said, if the community wants to stick to its gunâs thats fine - I
> will probably still investigate how to use Commonmark for myself, and
> will still contribute to Pillar docs where I can (and curse history) -
> but I think we are long better off trying to join emerging standards
> where we can particularly if they arenât our core language thing. And
> it just makes it less frictionless for ourselves and newcomers.
>
> Of course, if we were to move, we would need to translate a lot of
> quality docs to a new format - but I would be up for contributing to
> that if that was a deciding factor.
>
> Tim
>
>
>> On 14 Aug 2017, at 16:41, Jimmie Houchin <jlhouchin(a)gmail.com
>> <mailto:jlhouchin@gmail.com>> wrote:
>>
>> TL;DR
>>
>> Main points:
>> Their is no universally accepted markup language.
>> Other communities use their own markup and tools and their markup
>> and tools choice is not determine by other communities decisions.
>> We need a language and tool chain that we can control and maintain
>> which accomplishes our goals.
>> Our language and tools already exist and have existed for longer than
>> most of the other markup languages. Of course they existed in various
>> different forms over the years and have evolved into what they
>> currently are.
>> It might be nice to have a GFM Markdown exporter from Pillar for
>> GitHub projects.
>>
>>
>> I just want to comment on the fact that there is no universal markup
>> language that every development community has settled upon. Making
>> Markdown or some variant the markup language for Pharo only aligns us
>> with a certain part of the development community. Even Markdown is
>> not unified as is evident by the discussion.
>>
>> It is true that GitHub uses their variant of Markdown. And as long as
>> we use GitHub we will need to use their variant for documents that
>> reside on their system.
>>
>> However as a significant counter example to lets all use gfm
>> Markdown, is the Python community and their documentation.
>>
>> https://docs.python.org/devguide/documenting.html
>> """
>> 7. Documenting Python
>> The Python language has a substantial body of documentation, much of
>> it contributed by various authors. The markup used for the Python
>> documentation is reStructuredText, developed by the docutils project,
>> amended by custom directives and using a toolset named Sphinx to
>> post-process the HTML output.
>>
>> This document describes the style guide for our documentation as well
>> as the custom reStructuredText markup introduced by Sphinx to support
>> Python documentation and how it should be used.
>>
>> The documentation in HTML, PDF or EPUB format is generated from text
>> files written using the reStructuredText format and contained in the
>> CPython Git repository.
>> """
>>
>> So the Python community uses their own markup language and their own
>> tool chain. So therefore, it is not wrong for a community to go their
>> own way, for their own reasons. Even within the conventional file
>> based languages such as Python.
>>
>> The fact that you have tools such as Pandoc, suggest that there is
>> not true uniformity or unanimity among developers as to the best
>> markup language or tool chain.
>>
>> I believe that a language that we can control and maintain is better
>> than adopting some other foreign markup language that is neither
>> better, nor unanimously used by all. That would ultimately
>> potentially require extensions to accomplish our goals. Then we would
>> be maintaining someone else's language with our extensions that may
>> or may not be accepted by the larger community. This does not prevent
>> but rather encourages fragmentation of the existing Markdown.
>>
>> Regardless, Pillar markup already exists. The tools in Pharo already
>> understand it. Should someone desire to use Pharo which is far more
>> different from Python/Ruby/etc. than Pillar syntax is from Markdown.
>> Then it should be worth their effort to learn our tools.
>>
>> Pillar markup is older than Markdown, etc. It's history begins in
>> SmallWiki. It isn't as if we jumped up and decided to create
>> something new in order to be different. Our markup and tools are
>> older. They (and others) are the ones that decided to do their own
>> markup and tools. And it is okay that they did so. Nothing wrong with
>> doing so. Every community has the right to what they believe is best
>> for their community. Even if other communities disagree.
>>
>> The ability to control and maintain is highly valuable. We can
>> understand what our requirements are for today. But we can not know
>> what the requirements are in the future. Nor can we know that
>> Markdown or whomever will have such requirements when they appear. It
>> is easy to see in the beginning with the Squeak Wiki syntax to the
>> now Pillar syntax, changes that have been made to accommodate new
>> requirements as they became known. We need to maintain that ability.
>> Sure we would reserve the right to do so in any language we adopt.
>> But the then current standard bearer of said language would determine
>> whether what we do is acceptable and incorporate or whether we are
>> then in fact adding to their fragmentation. Pillar is ours. There is
>> not fragmentation when we evolve.
>>
>> However, since we have made a decision to use GitHub and GitHub has
>> made a decision to use their own GFM Markdown. It might be nice to
>> have a GFM Markdown exporter from Pillar for GitHub projects. This
>> way we can use our own tools and markup language to accomplish
>> whatever we want to accomplish. Including generating a Readme.md for
>> our GitHub projects.
>>
>> Just wanted to toss out this simple opinion and facts about the
>> situation.
>>
>> Jimmie
>>
>>
>> On 08/14/2017 04:10 AM, Tudor Girba wrote:
>>> Hi Tim,
>>>
>>> The main benefit of relying on Pillar is that we control its syntax
>>> and can easily extend it for our purposes. Also, there was quite a
>>> bit of engineering invested in it, and even though we still need to
>>> improve it, there exists a pipeline that allows people to quickly
>>> publish books.
>>>
>>> The figure embedding problem is one example of the need to customize
>>> the syntax and behavior, but this extensibility will become even
>>> more important for supporting the idea of moving the documentation
>>> inside the image. For example, the ability to refer to a class,
>>> method or other artifacts will be quite relevant soon especially
>>> that the editor will be able to embed advanced elements inside the text.
>>>
>>> Cheers,
>>> Doru
>>>
>>>
>>>> On Aug 14, 2017, at 10:46 AM, Tim Mackinnon <tim(a)testit.works> wrote:
>>>>
>>>> Hi Stef - I think yourâs is a fair requirement (in fact I hit
>>>> something similar when doing a static website using a JS markdown
>>>> framework - and this is why I mentioned Kramdown which adds a few
>>>> extras to regular markdown - but it feels like it goes a bit too far).
>>>>
>>>> My next item on my learning todo list was to try and replace that
>>>> JS generator with something from Smalltalk - so I think we can
>>>> possibly come up with something that ticks all the right boxes (Iâd
>>>> like to try anyway).
>>>>
>>>> Iâll keep working away on it and compare notes with you. I think
>>>> with Pillar, it was more that things like headers, bold and italics
>>>> are similar concepts but just use different characters - so I keep
>>>> typing the wrong thing and getting frustrated particularly when we
>>>> embrace Git and readme.md is in markdown.
>>>>
>>>>
>>>> Tim
>>>>
>>>>> On 13 Aug 2017, at 20:08, Stephane Ducasse
>>>>> <stepharo.self(a)gmail.com> wrote:
>>>>>
>>>>> Hi tim
>>>>>
>>>>> I personally do not care much about the syntax but I care about what I
>>>>> can do with it
>>>>> (ref, cite, ... )
>>>>> I cannot write books in markdown because reference to figures!!!!!!
>>>>> were missing.
>>>>>
>>>>> And of course a parser because markdown is not really nice to parse
>>>>> and I will not write a parser because I have something else to do. I
>>>>> want to make pillar smaller, simpler, nicer.
>>>>>
>>>>> Now if someone come up with a parser that parse for REAL a markdown
>>>>> that can be extended with decent behavior (figure reference, section
>>>>> reference, cite) and can be extended because there are many things
>>>>> that can be nice to have (for example I want to be able to write the
>>>>> example below) and emit a PillarModel (AST) we can talk to have
>>>>> another syntax for Pillar but not before.
>>>>>
>>>>> [[[test
>>>>> 2+3
>>>>>>>> 5
>>>>> ]]]
>>>>>
>>>>> and being able to verify that the doc is in sync.
>>>>>
>>>>>
>>>>> Stef
>>>>>
>>>>>
>>>>>
>>>>> On Sat, Aug 12, 2017 at 12:37 AM, Tim Mackinnon <tim(a)testit.works>
>>>>> wrote:
>>>>>> Of course, I/we recognise and appreciate all the work that's gone
>>>>>> into docs in pillar - but I think it should be reasonably
>>>>>> straightforward to write a converter as it is pretty closely
>>>>>> related from what I have seen.
>>>>>>
>>>>>> So I don't make the suggestion flippantly, and would want to help
>>>>>> write a converter and get us to a common ground where we can
>>>>>> differentiate on the aspects where we can excel.
>>>>>>
>>>>>> Tim
>>>>>>
>>>>>> Sent from my iPhone
>>>>>>
>>>>>>> On 11 Aug 2017, at 23:21, Peter Uhnak <i.uhnak(a)gmail.com> wrote:
>>>>>>>
>>>>>>> A long time issue with Markdown was that there was no
>>>>>>> standardization (and when I used Pillar's MD export ~2 years ago
>>>>>>> it didn't work well).
>>>>>>>
>>>>>>> However CommonMark ( http://spec.commonmark.org/0.28/ ) has
>>>>>>> become the de-facto standard, so it would make sense to support
>>>>>>> it bidirectionally with Pillar.
>>>>>>>
>>>>>>>> The readme.md that Peter is talking about is gfm markdown
>>>>>>> Well, technically it is just a CommonMark, as I am not using any
>>>>>>> github extensions.
>>>>>>> (Github uses CommonMarks and adds just couple small extensions.)
>>>>>>>
>>>>>>> Peter
>>>>>>>
>>>>>>
>>>>
>>> --
>>> www.tudorgirba.com
>>> www.feenk.com
>>>
>>> âLive like you mean it."
>>>
>>>
>>
>>
>
Aug. 14, 2017
Re: [Pharo-users] Big Glorp problem w/ type coercion, pls help
by Herby VojÄÃk
Esteban A. Maringolo wrote:
> Do you have the code somewhere loadable? Reading chunk is something I
> do only when everything crashed :D
> Esteban A. Maringolo
I will attach the .st files... not loadable as its in private on-premise
git repo :-(
Thank you very much,
Herby
> 2017-08-14 13:44 GMT-03:00 Herby VojÄÃk<herby(a)mailbox.sk>:
>> Hello!
>>
>> I encountered a problem with OneToOneMapping and type coercion. When writing
>> data, thing work; when reading data, the right child of relation fails to
>> convert.
>>
>> I tried everything possible to inject converters (even subclassing
>> GlorpBlobType), but to no avail. RelationExpression passes conversion to its
>> left child:
>>
>> convertedDbValueOf: anObject
>> "Assume that our types match, so we can ask either child to do the
>> conversion. That isn't guaranteed, but should at least work for the common
>> cases."
>> ^leftChild convertedDbValueOf: anObject.
>>
>> but the left child is FieldExpression in case of OneToOneMapping, which:
>>
>> convertedDbValueOf: anObject
>> "We don't do any conversion"
>> ^anObject
>>
>> What is strange, writing works (even the OneToOneMapping, I opened the
>> sqlite file with an explorer), but second SELECT, one using the relation
>> (`state := self dao findStateByAgent: agent` in clientSync), fails with
>> "GlorpDatabaseReadError: Could not coerce arguments". FWIW, the first one
>> _does_ convert when creating bindings, as it uses MappingExpression as left
>> child (stepped over it in debugger).
>>
>>
>>
>> Is it meant to be a strange case that primary key is something non-primitive
>> needing coercion (in this case, it is a UUID which needs coercion to
>> ByteArray, even if it is its subclass)?
>>
>>
>>
>> Here's the stack of running the test which fails:
>>
>> PharoDatabaseAccessor(DatabaseAccessor)>>handleError:for:
>> [ :ex | self handleError: ex for: command ] in [ | result |
>> self checkPermissionFor: command.
>> result := [ (self useBinding and: [ command useBinding ])
>> ifTrue: [ command executeBoundIn: self ]
>> ifFalse: [ command executeUnboundIn: self ] ]
>> on: Dialect error
>> do: [ :ex | self handleError: ex for: command ].
>> aBoolean
>> ifTrue: [ result ]
>> ifFalse: [ result upToEnd ] ] in
>> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
>> BlockClosure>>cull:
>> Context>>evaluateSignal:
>> Context>>handleSignal:
>> Error(Exception)>>signal
>> Error(Exception)>>signal:
>> ExternalLibraryFunction(Object)>>error:
>> ExternalLibraryFunction(Object)>>externalCallFailed
>> ExternalLibraryFunction(ExternalFunction)>>invokeWithArguments:
>> UDBCSQLite3Library>>apiBindBlob:atColumn:with:with:with:
>> UDBCSQLite3Library>>with:at:putBlob:
>> UDBCSQLite3Statement>>at:putByteArray:
>> UDBCSQLite3ResultSet>>execute:withIndex:withValue:
>> [ :v | i := self execute: statement withIndex: i withValue: v ] in
>> UDBCSQLite3ResultSet>>execute:withCollection:
>> OrderedCollection>>do:
>> UDBCSQLite3ResultSet>>execute:withCollection:
>> UDBCSQLite3ResultSet>>execute:with:on:
>> UDBCSQLite3Connection>>execute:with:
>> GlorpSQLite3Driver>>basicExecuteSQLString:binding:
>> PharoDatabaseAccessor>>executeCommandBound:
>> QuerySelectCommand(DatabaseCommand)>>executeBoundIn:
>> [ (self useBinding and: [ command useBinding ])
>> ifTrue: [ command executeBoundIn: self ]
>> ifFalse: [ command executeUnboundIn: self ] ] in [ | result |
>> self checkPermissionFor: command.
>> result := [ (self useBinding and: [ command useBinding ])
>> ifTrue: [ command executeBoundIn: self ]
>> ifFalse: [ command executeUnboundIn: self ] ]
>> on: Dialect error
>> do: [ :ex | self handleError: ex for: command ].
>> aBoolean
>> ifTrue: [ result ]
>> ifFalse: [ result upToEnd ] ] in
>> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
>> BlockClosure>>on:do:
>> [ | result |
>> self checkPermissionFor: command.
>> result := [ (self useBinding and: [ command useBinding ])
>> ifTrue: [ command executeBoundIn: self ]
>> ifFalse: [ command executeUnboundIn: self ] ]
>> on: Dialect error
>> do: [ :ex | self handleError: ex for: command ].
>> aBoolean
>> ifTrue: [ result ]
>> ifFalse: [ result upToEnd ] ] in
>> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
>> [ caught := true.
>> self wait.
>> blockValue := mutuallyExcludedBlock value ] in Semaphore>>critical:
>> BlockClosure>>ensure:
>> Semaphore>>critical:
>> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
>> [ session accessor executeCommand: command returnCursor: true ] in
>> SimpleQuery>>rowsFromDatabaseWithParameters:
>> BlockClosure>>on:do:
>> SimpleQuery>>rowsFromDatabaseWithParameters:
>> SimpleQuery(AbstractReadQuery)>>readFromDatabaseWithParameters:
>> SimpleQuery(AbstractReadQuery)>>executeWithParameters:in:
>> GlorpSession>>execute:
>> GlorpSession>>readOneOf:where:
>> TowergameDao>>findStateByAgent:
>> [ | agent state |
>> agent := self dao findAgentById: anObject agentId.
>> state := self dao findStateByAgent: agent.
>> ^ NeoJSONObject new
>> agentId: agent id;
>> stateVersion: state version;
>> totalAnsweredQuestions:
>> (NeoJSONObject new
>> good: 0;
>> bad: 0;
>> yourself);
>> yourself ] in Towergame>>clientSync:
>> [ myUnitOfWork := self hasUnitOfWork not.
>> myUnitOfWork
>> ifTrue: [ self beginUnitOfWork ].
>> result := aBlock numArgs = 1
>> ifTrue: [ aBlock value: self ]
>> ifFalse: [ aBlock value ].
>> myUnitOfWork
>> ifTrue: [ self commitUnitOfWork ] ] in GlorpSession>>inUnitOfWorkDo:
>> BlockClosure>>ifCurtailed:
>> GlorpSession>>inUnitOfWorkDo:
>> TowergameDao>>inUnitOfWorkDo:
>> Towergame>>clientSync:
>> TowergameSyncTests>>testPlayerChecksStateVersion
>> TowergameSyncTests(TestCase)>>performTest
>> [ self setUp.
>> self performTest ] in TowergameSyncTests(TestCase)>>runCase
>> BlockClosure>>ensure:
>> TowergameSyncTests(TestCase)>>runCase
>> [ aTestCase runCase ] in [ [ aTestCase runCase ]
>> on: Halt
>> do: [ :halt |
>> "if test was halted we should resume all background failures
>> to debug all of them together with test process"
>> failedProcesses keysDo: #resume.
>> halt pass ] ] in
>> TestExecutionEnvironment>>runTestCaseSafelly:
>> BlockClosure>>on:do:
>> [ [ aTestCase runCase ]
>> on: Halt
>> do: [ :halt |
>> "if test was halted we should resume all background failures
>> to debug all of them together with test process"
>> failedProcesses keysDo: #resume.
>> halt pass ] ] in
>> TestExecutionEnvironment>>runTestCaseSafelly:
>> BlockClosure>>on:do:
>> TestExecutionEnvironment>>runTestCaseSafelly:
>> [ self runTestCaseSafelly: aTestCase ] in [ [ self runTestCaseSafelly:
>> aTestCase ]
>> ensure: [ testCompleted := true.
>> watchDogSemaphore signal ]. "signal that test case
>> completes"
>> self checkForkedProcesses ] in TestExecutionEnvironment>>runTestCase:
>> BlockClosure>>ensure:
>> [ [ self runTestCaseSafelly: aTestCase ]
>> ensure: [ testCompleted := true.
>> watchDogSemaphore signal ]. "signal that test case
>> completes"
>> self checkForkedProcesses ] in TestExecutionEnvironment>>runTestCase:
>> BlockClosure>>ifCurtailed:
>> TestExecutionEnvironment>>runTestCase:
>> [ testEnv runTestCase: aTestCase ] in
>> DefaultExecutionEnvironment>>runTestCase:
>> [ self value: anExecutionEnvironment.
>> anExecutionEnvironment activated.
>> aBlock value ] in CurrentExecutionEnvironment class>>activate:for:
>> BlockClosure>>ensure:
>> CurrentExecutionEnvironment class>>activate:for:
>> TestExecutionEnvironment(ExecutionEnvironment)>>beActiveDuring:
>> DefaultExecutionEnvironment>>runTestCase:
>> CurrentExecutionEnvironment class>>runTestCase:
>> TowergameSyncTests(TestCase)>>runCaseManaged
>> [ aTestCase announce: TestCaseStarted withResult: self.
>> aTestCase runCaseManaged.
>> aTestCase announce: TestCaseEnded withResult: self.
>> self addPass: aTestCase ] in TestResult>>runCaseForDebug:
>> BlockClosure>>on:do:
>> TestResult>>runCaseForDebug:
>> [ result runCaseForDebug: self ] in TowergameSyncTests(TestCase)>>debug
>> BlockClosure>>ensure:
>> TowergameSyncTests(TestCase)>>debug
>> [ :each |
>> each debug.
>> self announceTest: each.
>> self changed: each ] in [ self tests
>> do: [ :each |
>> each debug.
>> self announceTest: each.
>> self changed: each ] ] in TestSuite>>debug
>> OrderedCollection>>do:
>> [ self tests
>> do: [ :each |
>> each debug.
>> self announceTest: each.
>> self changed: each ] ] in TestSuite>>debug
>> BlockClosure>>ensure:
>> TestSuite>>debug
>> [ :aSuite | aSuite debug ] in TestRunner>>debugSuite:
>> BlockClosure>>cull:
>> BlockClosure>>cull:cull:
>> [ aBlock cull: aTestSuite cull: result ] in TestRunner>>executeSuite:as:
>> BlockClosure>>ensure:
>> TestRunner>>executeSuite:as:
>> TestRunner>>debugSuite:
>> TestRunner>>debug:
>> TestRunner>>errorSelected:
>> PluggableListMorph>>changeModelSelection:
>> PluggableListMorph>>mouseUpOnSingle:
>> PluggableListMorph>>mouseUp:
>> PluggableListMorph(Morph)>>handleMouseUp:
>> MouseButtonEvent>>sentTo:
>> PluggableListMorph(Morph)>>handleEvent:
>> MorphicEventDispatcher>>dispatchDefault:with:
>> MorphicEventDispatcher>>handleMouseUp:
>> MouseButtonEvent>>sentTo:
>> [ ^ anEvent sentTo: self ] in MorphicEventDispatcher>>dispatchEvent:with:
>> BlockClosure>>ensure:
>> MorphicEventDispatcher>>dispatchEvent:with:
>> PluggableListMorph(Morph)>>processEvent:using:
>> PluggableListMorph(Morph)>>processEvent:
>> PluggableListMorph>>handleFocusEvent:
>> [ ActiveHand := self.
>> ActiveEvent := anEvent.
>> result := focusHolder
>> handleFocusEvent: (anEvent transformedBy: (focusHolder
>> transformedFrom: self)) ] in HandMorph>>sendFocusEvent:to:clear:
>> BlockClosure>>on:do:
>> WorldMorph(PasteUpMorph)>>becomeActiveDuring:
>> HandMorph>>sendFocusEvent:to:clear:
>> HandMorph>>sendEvent:focus:clear:
>> HandMorph>>sendMouseEvent:
>> HandMorph>>handleEvent:
>> HandMorph>>processEventsFromQueue:
>> HandMorph>>processEvents
>> [ :h |
>> self activeHand: h.
>> h processEvents.
>> self activeHand: nil ] in WorldState>>doOneCycleNowFor:
>> Array(SequenceableCollection)>>do:
>> WorldState>>handsDo:
>> WorldState>>doOneCycleNowFor:
>> WorldState>>doOneCycleFor:
>> WorldMorph>>doOneCycle
>> WorldMorph class>>doOneCycle
>> [ [ WorldMorph doOneCycle.
>> Processor yield.
>> false ] whileFalse: [ ] ] in MorphicUIManager>>spawnNewProcess
>> [ self value.
>> Processor terminateActive ] in BlockClosure>>newProcess
>>
>>
>>
Aug. 14, 2017
Re: [Pharo-users] Big Glorp problem w/ type coercion, pls help
by Esteban A. Maringolo
Do you have the code somewhere loadable? Reading chunk is something I
do only when everything crashed :D
Esteban A. Maringolo
2017-08-14 13:44 GMT-03:00 Herby VojÄÃk <herby(a)mailbox.sk>:
> Hello!
>
> I encountered a problem with OneToOneMapping and type coercion. When writing
> data, thing work; when reading data, the right child of relation fails to
> convert.
>
> I tried everything possible to inject converters (even subclassing
> GlorpBlobType), but to no avail. RelationExpression passes conversion to its
> left child:
>
> convertedDbValueOf: anObject
> "Assume that our types match, so we can ask either child to do the
> conversion. That isn't guaranteed, but should at least work for the common
> cases."
> ^leftChild convertedDbValueOf: anObject.
>
> but the left child is FieldExpression in case of OneToOneMapping, which:
>
> convertedDbValueOf: anObject
> "We don't do any conversion"
> ^anObject
>
> What is strange, writing works (even the OneToOneMapping, I opened the
> sqlite file with an explorer), but second SELECT, one using the relation
> (`state := self dao findStateByAgent: agent` in clientSync), fails with
> "GlorpDatabaseReadError: Could not coerce arguments". FWIW, the first one
> _does_ convert when creating bindings, as it uses MappingExpression as left
> child (stepped over it in debugger).
>
>
>
> Is it meant to be a strange case that primary key is something non-primitive
> needing coercion (in this case, it is a UUID which needs coercion to
> ByteArray, even if it is its subclass)?
>
>
>
> Here's the stack of running the test which fails:
>
> PharoDatabaseAccessor(DatabaseAccessor)>>handleError:for:
> [ :ex | self handleError: ex for: command ] in [ | result |
> self checkPermissionFor: command.
> result := [ (self useBinding and: [ command useBinding ])
> ifTrue: [ command executeBoundIn: self ]
> ifFalse: [ command executeUnboundIn: self ] ]
> on: Dialect error
> do: [ :ex | self handleError: ex for: command ].
> aBoolean
> ifTrue: [ result ]
> ifFalse: [ result upToEnd ] ] in
> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
> BlockClosure>>cull:
> Context>>evaluateSignal:
> Context>>handleSignal:
> Error(Exception)>>signal
> Error(Exception)>>signal:
> ExternalLibraryFunction(Object)>>error:
> ExternalLibraryFunction(Object)>>externalCallFailed
> ExternalLibraryFunction(ExternalFunction)>>invokeWithArguments:
> UDBCSQLite3Library>>apiBindBlob:atColumn:with:with:with:
> UDBCSQLite3Library>>with:at:putBlob:
> UDBCSQLite3Statement>>at:putByteArray:
> UDBCSQLite3ResultSet>>execute:withIndex:withValue:
> [ :v | i := self execute: statement withIndex: i withValue: v ] in
> UDBCSQLite3ResultSet>>execute:withCollection:
> OrderedCollection>>do:
> UDBCSQLite3ResultSet>>execute:withCollection:
> UDBCSQLite3ResultSet>>execute:with:on:
> UDBCSQLite3Connection>>execute:with:
> GlorpSQLite3Driver>>basicExecuteSQLString:binding:
> PharoDatabaseAccessor>>executeCommandBound:
> QuerySelectCommand(DatabaseCommand)>>executeBoundIn:
> [ (self useBinding and: [ command useBinding ])
> ifTrue: [ command executeBoundIn: self ]
> ifFalse: [ command executeUnboundIn: self ] ] in [ | result |
> self checkPermissionFor: command.
> result := [ (self useBinding and: [ command useBinding ])
> ifTrue: [ command executeBoundIn: self ]
> ifFalse: [ command executeUnboundIn: self ] ]
> on: Dialect error
> do: [ :ex | self handleError: ex for: command ].
> aBoolean
> ifTrue: [ result ]
> ifFalse: [ result upToEnd ] ] in
> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
> BlockClosure>>on:do:
> [ | result |
> self checkPermissionFor: command.
> result := [ (self useBinding and: [ command useBinding ])
> ifTrue: [ command executeBoundIn: self ]
> ifFalse: [ command executeUnboundIn: self ] ]
> on: Dialect error
> do: [ :ex | self handleError: ex for: command ].
> aBoolean
> ifTrue: [ result ]
> ifFalse: [ result upToEnd ] ] in
> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
> [ caught := true.
> self wait.
> blockValue := mutuallyExcludedBlock value ] in Semaphore>>critical:
> BlockClosure>>ensure:
> Semaphore>>critical:
> PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
> [ session accessor executeCommand: command returnCursor: true ] in
> SimpleQuery>>rowsFromDatabaseWithParameters:
> BlockClosure>>on:do:
> SimpleQuery>>rowsFromDatabaseWithParameters:
> SimpleQuery(AbstractReadQuery)>>readFromDatabaseWithParameters:
> SimpleQuery(AbstractReadQuery)>>executeWithParameters:in:
> GlorpSession>>execute:
> GlorpSession>>readOneOf:where:
> TowergameDao>>findStateByAgent:
> [ | agent state |
> agent := self dao findAgentById: anObject agentId.
> state := self dao findStateByAgent: agent.
> ^ NeoJSONObject new
> agentId: agent id;
> stateVersion: state version;
> totalAnsweredQuestions:
> (NeoJSONObject new
> good: 0;
> bad: 0;
> yourself);
> yourself ] in Towergame>>clientSync:
> [ myUnitOfWork := self hasUnitOfWork not.
> myUnitOfWork
> ifTrue: [ self beginUnitOfWork ].
> result := aBlock numArgs = 1
> ifTrue: [ aBlock value: self ]
> ifFalse: [ aBlock value ].
> myUnitOfWork
> ifTrue: [ self commitUnitOfWork ] ] in GlorpSession>>inUnitOfWorkDo:
> BlockClosure>>ifCurtailed:
> GlorpSession>>inUnitOfWorkDo:
> TowergameDao>>inUnitOfWorkDo:
> Towergame>>clientSync:
> TowergameSyncTests>>testPlayerChecksStateVersion
> TowergameSyncTests(TestCase)>>performTest
> [ self setUp.
> self performTest ] in TowergameSyncTests(TestCase)>>runCase
> BlockClosure>>ensure:
> TowergameSyncTests(TestCase)>>runCase
> [ aTestCase runCase ] in [ [ aTestCase runCase ]
> on: Halt
> do: [ :halt |
> "if test was halted we should resume all background failures
> to debug all of them together with test process"
> failedProcesses keysDo: #resume.
> halt pass ] ] in
> TestExecutionEnvironment>>runTestCaseSafelly:
> BlockClosure>>on:do:
> [ [ aTestCase runCase ]
> on: Halt
> do: [ :halt |
> "if test was halted we should resume all background failures
> to debug all of them together with test process"
> failedProcesses keysDo: #resume.
> halt pass ] ] in
> TestExecutionEnvironment>>runTestCaseSafelly:
> BlockClosure>>on:do:
> TestExecutionEnvironment>>runTestCaseSafelly:
> [ self runTestCaseSafelly: aTestCase ] in [ [ self runTestCaseSafelly:
> aTestCase ]
> ensure: [ testCompleted := true.
> watchDogSemaphore signal ]. "signal that test case
> completes"
> self checkForkedProcesses ] in TestExecutionEnvironment>>runTestCase:
> BlockClosure>>ensure:
> [ [ self runTestCaseSafelly: aTestCase ]
> ensure: [ testCompleted := true.
> watchDogSemaphore signal ]. "signal that test case
> completes"
> self checkForkedProcesses ] in TestExecutionEnvironment>>runTestCase:
> BlockClosure>>ifCurtailed:
> TestExecutionEnvironment>>runTestCase:
> [ testEnv runTestCase: aTestCase ] in
> DefaultExecutionEnvironment>>runTestCase:
> [ self value: anExecutionEnvironment.
> anExecutionEnvironment activated.
> aBlock value ] in CurrentExecutionEnvironment class>>activate:for:
> BlockClosure>>ensure:
> CurrentExecutionEnvironment class>>activate:for:
> TestExecutionEnvironment(ExecutionEnvironment)>>beActiveDuring:
> DefaultExecutionEnvironment>>runTestCase:
> CurrentExecutionEnvironment class>>runTestCase:
> TowergameSyncTests(TestCase)>>runCaseManaged
> [ aTestCase announce: TestCaseStarted withResult: self.
> aTestCase runCaseManaged.
> aTestCase announce: TestCaseEnded withResult: self.
> self addPass: aTestCase ] in TestResult>>runCaseForDebug:
> BlockClosure>>on:do:
> TestResult>>runCaseForDebug:
> [ result runCaseForDebug: self ] in TowergameSyncTests(TestCase)>>debug
> BlockClosure>>ensure:
> TowergameSyncTests(TestCase)>>debug
> [ :each |
> each debug.
> self announceTest: each.
> self changed: each ] in [ self tests
> do: [ :each |
> each debug.
> self announceTest: each.
> self changed: each ] ] in TestSuite>>debug
> OrderedCollection>>do:
> [ self tests
> do: [ :each |
> each debug.
> self announceTest: each.
> self changed: each ] ] in TestSuite>>debug
> BlockClosure>>ensure:
> TestSuite>>debug
> [ :aSuite | aSuite debug ] in TestRunner>>debugSuite:
> BlockClosure>>cull:
> BlockClosure>>cull:cull:
> [ aBlock cull: aTestSuite cull: result ] in TestRunner>>executeSuite:as:
> BlockClosure>>ensure:
> TestRunner>>executeSuite:as:
> TestRunner>>debugSuite:
> TestRunner>>debug:
> TestRunner>>errorSelected:
> PluggableListMorph>>changeModelSelection:
> PluggableListMorph>>mouseUpOnSingle:
> PluggableListMorph>>mouseUp:
> PluggableListMorph(Morph)>>handleMouseUp:
> MouseButtonEvent>>sentTo:
> PluggableListMorph(Morph)>>handleEvent:
> MorphicEventDispatcher>>dispatchDefault:with:
> MorphicEventDispatcher>>handleMouseUp:
> MouseButtonEvent>>sentTo:
> [ ^ anEvent sentTo: self ] in MorphicEventDispatcher>>dispatchEvent:with:
> BlockClosure>>ensure:
> MorphicEventDispatcher>>dispatchEvent:with:
> PluggableListMorph(Morph)>>processEvent:using:
> PluggableListMorph(Morph)>>processEvent:
> PluggableListMorph>>handleFocusEvent:
> [ ActiveHand := self.
> ActiveEvent := anEvent.
> result := focusHolder
> handleFocusEvent: (anEvent transformedBy: (focusHolder
> transformedFrom: self)) ] in HandMorph>>sendFocusEvent:to:clear:
> BlockClosure>>on:do:
> WorldMorph(PasteUpMorph)>>becomeActiveDuring:
> HandMorph>>sendFocusEvent:to:clear:
> HandMorph>>sendEvent:focus:clear:
> HandMorph>>sendMouseEvent:
> HandMorph>>handleEvent:
> HandMorph>>processEventsFromQueue:
> HandMorph>>processEvents
> [ :h |
> self activeHand: h.
> h processEvents.
> self activeHand: nil ] in WorldState>>doOneCycleNowFor:
> Array(SequenceableCollection)>>do:
> WorldState>>handsDo:
> WorldState>>doOneCycleNowFor:
> WorldState>>doOneCycleFor:
> WorldMorph>>doOneCycle
> WorldMorph class>>doOneCycle
> [ [ WorldMorph doOneCycle.
> Processor yield.
> false ] whileFalse: [ ] ] in MorphicUIManager>>spawnNewProcess
> [ self value.
> Processor terminateActive ] in BlockClosure>>newProcess
>
>
>
> And here's the code:
>
>
> Towergame.st:
>
> GlorpBlobType subclass: #GlorpBlob2Type
> instanceVariableNames: ''
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
>
> !GlorpBlob2Type methodsFor: 'types' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> converterForStType: aClass
> aClass = UUID ifTrue: [ ^ UuidConverter new ].
> ^ super converterForStType: aClass! !
>
>
> Object subclass: #TgAct
> instanceVariableNames: 'agent tool timestamp'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !TgAct commentStamp: 'HerbyVojcik 8/5/2017 19:23' prior: 0!
> I represent a relationship between a player (TgAgent)
> and a device (TgTool).
>
> In particular, I am created whenever a player logs in to the game from
> different device
> than it was last time (or first time, ever).!
>
>
> !TgAct methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> agent
> ^ agent! !
>
> !TgAct methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> agent: anObject
> agent := anObject! !
>
> !TgAct methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> timestamp: anObject
> timestamp := anObject! !
>
> !TgAct methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> tool
> ^ tool! !
>
> !TgAct methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> tool: anObject
> tool := anObject! !
>
> !TgAct methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> timestamp
> ^ timestamp! !
>
>
> !TgAct methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> agent := nil.
> timestamp := DateAndTime now asUTC.
> tool := nil.! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TgAct class
> instanceVariableNames: ''!
>
> !TgAct class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> agent: aTgAgent tool: aTgTool
> ^ self new
> agent: aTgAgent;
> tool: aTgTool;
> yourself! !
>
>
> Object subclass: #TgAgent
> instanceVariableNames: 'id'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !TgAgent commentStamp: 'HerbyVojcik 8/5/2017 19:22' prior: 0!
> I represent a towergame player.
>
> I only contain player-related information;
> the game state itself is in TgState.!
>
>
> !TgAgent methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> id := nil.! !
>
>
> !TgAgent methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> id: anObject
> id := anObject! !
>
> !TgAgent methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> id
> ^ id! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TgAgent class
> instanceVariableNames: ''!
>
> !TgAgent class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> id: aString
> ^ self new
> id: aString;
> yourself! !
>
>
> Object subclass: #TgAnswers
> instanceVariableNames: 'good bad'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !TgAnswers commentStamp: 'HerbyVojcik 8/5/2017 20:23' prior: 0!
> I represent the answered question stats.
>
> I know how many good / bad answered questions there is.!
>
>
> !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> good
> ^ good! !
>
> !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> bad: anObject
> bad := anObject! !
>
> !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> bad
> ^ bad! !
>
> !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> good: anObject
> good := anObject! !
>
>
> !TgAnswers methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> bad := 0.
> good := 0.! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TgAnswers class
> instanceVariableNames: ''!
>
> !TgAnswers class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> good: anInteger bad: anInteger2
> ^ self new
> good: anInteger;
> bad: anInteger2;
> yourself! !
>
>
> Object subclass: #TgFloors
> instanceVariableNames: 'total reinforced'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !TgFloors commentStamp: 'HerbyVojcik 8/5/2017 20:22' prior: 0!
> I represent the floor building status.
>
> I know how many floors are build and how many of them is reinforced.!
>
>
> !TgFloors methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> total
> ^ total! !
>
> !TgFloors methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> total: anObject
> total := anObject! !
>
> !TgFloors methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> reinforced
> ^ reinforced! !
>
> !TgFloors methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> reinforced: anObject
> reinforced := anObject! !
>
>
> !TgFloors methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> reinforced := 0.
> total := 0.! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TgFloors class
> instanceVariableNames: ''!
>
> !TgFloors class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> total: anInteger
> ^ self total: anInteger reinforced: 0! !
>
> !TgFloors class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> total: anInteger reinforced: anInteger2
> ^ self new
> total: anInteger;
> reinforced: anInteger2;
> yourself! !
>
>
> Object subclass: #TgState
> instanceVariableNames: 'agent version packs valuables score
> bestScore answers'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !TgState commentStamp: 'HerbyVojcik 8/5/2017 20:20' prior: 0!
> I represent the game state.
>
> I have relation to a player (TgAgent) and have a version.
> Then, I contain (directly or indirectly) other parts that
> make up the player's game state.
>
> Whenever I am changed by game progress, my version is changed as well.!
>
>
> !TgState methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> agent := nil.
> answers := nil.
> bestScore := nil.
> packs := Set new.
> score := nil.
> valuables := nil.
> version := nil.! !
>
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> score: anObject
> score := anObject! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> bestScore: anObject
> bestScore := anObject! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> agent: anObject
> agent := anObject! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> score
> ^ score! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> packs
> ^ packs! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> version
> ^ version! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> bestScore
> ^ bestScore! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> agent
> ^ agent! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> answers: anObject
> answers := anObject! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> valuables: anObject
> valuables := anObject! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> valuables
> ^ valuables! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> version: anObject
> version := anObject! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> answers
> ^ answers! !
>
> !TgState methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> packs: anObject
> packs := anObject! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TgState class
> instanceVariableNames: ''!
>
> !TgState class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> agent: aTgAgent version: aString
> ^ self new
> agent: aTgAgent;
> version: aString;
> yourself! !
>
>
> Object subclass: #TgTool
> instanceVariableNames: 'id'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !TgTool commentStamp: 'HerbyVojcik 8/5/2017 19:26' prior: 0!
> I represent the device (mobile phone, web browser, ..)
> that player uses to connect to game.!
>
>
> !TgTool methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> id := nil.! !
>
>
> !TgTool methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> id: anObject
> id := anObject! !
>
> !TgTool methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017 18:09:53'!
> id
> ^ id! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TgTool class
> instanceVariableNames: ''!
>
> !TgTool class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> id: aString
> ^ self new
> id: aString;
> yourself! !
>
>
> Object subclass: #TgValuables
> instanceVariableNames: 'coins gems'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !TgValuables commentStamp: 'HerbyVojcik 8/5/2017 20:22' prior: 0!
> I represent a purse.
>
> I know how many coins and gems there is.!
>
>
> !TgValuables methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> coins := 0.
> gems := 0.! !
>
>
> !TgValuables methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> gems: anObject
> gems := anObject! !
>
> !TgValuables methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> coins: anObject
> coins := anObject! !
>
> !TgValuables methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> gems
> ^ gems! !
>
> !TgValuables methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> coins
> ^ coins! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TgValuables class
> instanceVariableNames: ''!
>
> !TgValuables class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> coins: anInteger gems: anInteger2
> ^ self new
> coins: anInteger;
> gems: anInteger2;
> yourself! !
>
>
> Object subclass: #Towergame
> instanceVariableNames: 'dao'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
> !Towergame commentStamp: 'HerbyVojcik 5/17/2017 17:19' prior: 0!
> I am the Towergame app class.
>
> I configure and start towergame server processing.!
>
>
> !Towergame methodsFor: 'actions' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> clientSync: anObject
> self dao inUnitOfWorkDo: [
> | agent state |
> agent := self dao findAgentById: anObject agentId.
> state := self dao findStateByAgent: agent.
> ^ NeoJSONObject new
> agentId: agent id;
> stateVersion: state version;
> totalAnsweredQuestions: (NeoJSONObject new good: 0;
> bad: 0; yourself);
> yourself ]! !
>
>
> !Towergame methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> dao := nil.
> ! !
>
>
> !Towergame methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> dao: anObject
> dao := anObject! !
>
> !Towergame methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> dao
> ^ dao! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> Towergame class
> instanceVariableNames: 'default'!
>
> !Towergame class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> dao: aDao
> ^ self new
> dao: aDao;
> yourself! !
>
>
> !Towergame class methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> defaultDbLogin
> | databaseFile |
> databaseFile := Smalltalk imageDirectory asFileReference /
> 'towergame.db'.
> ^ Login new
> database: UDBCSQLite3Platform new;
> host: '';
> port: '';
> username: '';
> password: '';
> databaseName: databaseFile fullPath asZnUrl
> asString;
> yourself ! !
>
> !Towergame class methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> default
> ^ default ifNil: [ default := self
> dao: (self daoForLogin: self defaultDbLogin)
> ]! !
>
> !Towergame class methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> defaultPort
> ^ 4998! !
>
>
> !Towergame class methodsFor: 'configuration' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> configureServer
> (self serverFor: self default on: self defaultPort) start; register
> ! !
>
> !Towergame class methodsFor: 'configuration' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> configureSqlite3
>
> PharoDatabaseAccessor DefaultDriver: GlorpSQLite3Driver! !
>
> !Towergame class methodsFor: 'configuration' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> configure
> self configureSqlite3.
> self configureServer.! !
>
>
> !Towergame class methodsFor: 'factory' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> daoForLogin: aLogin
> ^ TowergameDao forLogin: aLogin! !
>
> !Towergame class methodsFor: 'factory' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> serverFor: aTowergame on: port
> ^ (ZnServer on: port)
> delegate: (TowergameDelegate on: aTowergame);
> yourself! !
>
>
> Object subclass: #TowergameDao
> instanceVariableNames: 'glorpSession glorpLogin'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
>
> !TowergameDao methodsFor: 'transactions' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> inUnitOfWorkDo: aBlock
> ^ self glorpSession inUnitOfWorkDo: aBlock! !
>
>
> !TowergameDao methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> initialize
>
> super initialize.
>
> glorpLogin := nil.
> glorpSession := nil.! !
>
>
> !TowergameDao methodsFor: 'initialize-release' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> reset
> glorpSession := nil.! !
>
>
> !TowergameDao methodsFor: 'query' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> findStateByAgent: anAgent
> ^ self glorpSession readOneOf: TgState where: [ :one | one agent =
> anAgent ]! !
>
> !TowergameDao methodsFor: 'query' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> findAgentById: anUUID
> ^ self glorpSession readOneOf: TgAgent where: [ :one | one id =
> anUUID ]! !
>
>
> !TowergameDao methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> glorpLogin: anObject
> glorpLogin := anObject! !
>
> !TowergameDao methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> glorpLogin
> ^ glorpLogin! !
>
> !TowergameDao methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> glorpSession
> glorpSession ifNil: [
> glorpSession := TowergameDescriptorSystem sessionForLogin:
> self glorpLogin ].
> glorpSession accessor isLoggedIn ifFalse: [
> glorpSession accessor login ].
> ^ glorpSession! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TowergameDao class
> instanceVariableNames: ''!
>
> !TowergameDao class methodsFor: 'instance creation' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> forLogin: aLogin
> ^ self new
> glorpLogin: aLogin;
> yourself! !
>
>
> ZnDispatcherDelegate subclass: #TowergameDelegate
> instanceVariableNames: 'towergame'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
>
> !TowergameDelegate methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> towergame
> ^ towergame! !
>
> !TowergameDelegate methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> towergame: anObject
> towergame := anObject! !
>
>
> !TowergameDelegate methodsFor: 'initialization' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> initialize
> super initialize.
> towergame := nil.
> self
> map: '/api/v1/sync'
> to: [ :request :response | self syncRequest: request
> toResponse: response ]! !
>
>
> !TowergameDelegate methodsFor: 'action' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> syncRequest: request toResponse: response
> | requestPayload responsePayload uuidKeys |
> uuidKeys := #(agentId stateVersion deviceId).
> request method == #POST ifFalse: [ ^ ZnResponse methodNotAllowed:
> request ].
> requestPayload := NeoJSONObject fromString: request contents.
> requestPayload ifNotNil: [
> uuidKeys do: [ :each | requestPayload at: each ifPresentPut:
> [ :s | UUID fromString: s ] ] ].
> responsePayload := self towergame clientSync: requestPayload.
> responsePayload ifNotNil: [
> uuidKeys do: [ :each | responsePayload at: each
> ifPresentPut: #asString ] ].
> ^ response
> entity: (ZnEntity
> with: (NeoJSONWriter toString: responsePayload)
> type: ZnMimeType applicationJson);
> yourself! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> TowergameDelegate class
> instanceVariableNames: ''!
>
> !TowergameDelegate class methodsFor: 'instance creation' stamp: 'HerbertVojÃ
> ÃÂk 8/14/2017 18:09:53'!
> on: aTowergame
> ^ self new towergame: aTowergame; yourself! !
>
>
> DescriptorSystem subclass: #TowergameDescriptorSystem
> instanceVariableNames: 'uuidConverter'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> tableForAGENT: aTable
>
> (aTable createFieldNamed: 'id' type: platform blob2) bePrimaryKey.
> ! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> classModelForTgAgent: aClassModel
> aClassModel
> newAttributeNamed: #id type: UUID! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbyVojcik 8/14/2017
> 18:24'!
> tableForSTATE: aTable
>
> (aTable createFieldNamed: 'agent' type: platform blob2) in: [
> :agentField |
> agentField bePrimaryKey.
> aTable addForeignKeyFrom: agentField to: ((self tableNamed:
> 'AGENT') fieldNamed: 'id') ].
> (aTable createFieldNamed: 'version' type: platform blob) beIndexed.
> ! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> descriptorForTgAgent: aDescriptor
> | table |
> table := self tableNamed: 'AGENT'.
> aDescriptor table: table.
> (aDescriptor newMapping: DirectMapping)
> from: #id to: (table fieldNamed: 'id').! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> tableForACT: aTable
>
> (aTable createFieldNamed: 'agent' type: platform blob2) beIndexed.
> (aTable createFieldNamed: 'tool' type: platform blob2) beIndexed.
> (aTable createFieldNamed: 'timestamp' type: platform timestamp)
> beIndexed.
> ! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> descriptorForTgState: aDescriptor
> | table |
> table := self tableNamed: 'STATE'.
> aDescriptor table: table.
> (aDescriptor newMapping: OneToOneMapping) attributeName: #agent.
> (aDescriptor newMapping: DirectMapping)
> from: #version to: (table fieldNamed: 'version').! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> converterBetweenStType: aClass andField: aField
> (aClass = UUID and: [ aField impliedSmalltalkType = ByteArray])
> ifTrue: [ ^ self uuidConverter ].
> ^ super converterBetweenStType: aClass andField: aField! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> classModelForTgState: aClassModel
> "agent version packs valuables score bestScore answers"
> aClassModel
> newAttributeNamed: #agent type: TgAgent;
> newAttributeNamed: #version type: UUID! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> classModelForTgTool: aClassModel
> aClassModel
> newAttributeNamed: #id
> ! !
>
> !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> classModelForTgAct: aClassModel
> aClassModel
> newAttributeNamed: #timestamp;
> newAttributeNamed: #agent type: TgAgent;
> newAttributeNamed: #tool type: TgTool! !
>
>
> !TowergameDescriptorSystem methodsFor: 'accessing' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> uuidConverter
> ^ uuidConverter ifNil: [ uuidConverter := UuidConverter new name:
> 'uuid'; yourself ]! !
>
>
> DatabaseConverter subclass: #UuidConverter
> instanceVariableNames: ''
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame'!
>
> !UuidConverter methodsFor: 'converting' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> convert: anObject toDatabaseRepresentationAs: aDatabaseType
> ^ anObject ifNotNil: [ ByteArray withAll: anObject ]! !
>
> !UuidConverter methodsFor: 'converting' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> convert: anObject fromDatabaseRepresentationAs: aDatabaseType
> ^ anObject ifNotNil: [ UUID withAll: anObject ]! !
> 'From Pharo6.0 of 13 May 2016 [Latest update: #60510] on 14 August 2017 at
> 6:26:30.67905 pm'!
>
> !DatabasePlatform methodsFor: '*Towergame' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> blob2
> ^self typeNamed: #blob ifAbsentPut: [GlorpBlob2Type new].! !
> 'From Pharo6.0 of 13 May 2016 [Latest update: #60510] on 14 August 2017 at
> 6:26:30.68005 pm'!
>
> !Dictionary methodsFor: '*Towergame' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> at: key ifPresentPut: aBlock
> "Lookup the given key in the receiver. If it is present, update it
> with the value of evaluating the given block with the value associated
> with the key. Otherwise, answer nil."
>
> ^ self at: key ifPresent: [ :value | self at: key put: (aBlock cull:
> value) ]! !
>
>
>
>
> TowergameTests.st:
>
> TestCase subclass: #TowergameServerTests
> instanceVariableNames: 'randomPort towergame server'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame-Tests'!
>
> !TowergameServerTests methodsFor: 'running' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> uidy: aString
> ^ UUID fromString36: aString ! !
>
> !TowergameServerTests methodsFor: 'running' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> setUp
> randomPort := 1700 + 32 atRandom.
> towergame := Mock new.
> server := Towergame serverFor: towergame on: randomPort.
> server start.
> self
> assert: server isRunning & server isListening
> description: ('Failed to start server on port {1}. Is there
> one already?' format: { server port })
> ! !
>
> !TowergameServerTests methodsFor: 'running' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> tearDown
> server stop! !
>
>
> !TowergameServerTests methodsFor: 'tests' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> testEmptySyncRequest
> | znClient response |
> (towergame stub clientSync: Arg payload) willReturn: nil.
> znClient := self znClientForSync: 'null'.
> response := znClient timeout: 1; post; response.
> response should satisfy: #isSuccess.
> response contentType should equal: ZnMimeType applicationJson.
> (STON fromString: response entity contents) should equal: nil.
> Arg payload should equal: nil! !
>
> !TowergameServerTests methodsFor: 'tests' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> testRejectEmptyGetSyncRequest
> | znClient response |
> (towergame stub clientSync: Arg payload) willReturn: nil.
> znClient := self znClientForSync: 'null'.
> response := znClient timeout: 1; get; response.
> response code should equal: ZnStatusLine methodNotAllowed code.
> towergame should not receive clientSync: Any! !
>
> !TowergameServerTests methodsFor: 'tests' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> testNonEmptySyncRequest
> | znClient response |
> (towergame stub clientSync: Arg payload) willReturn: { #agentId ->
> (self uidy: '007') } asDictionary.
> znClient := self znClientForSync:
>
> ('\{"deviceId":"{1}","agentAnsweredQuestions":\{"good":1,"bad":2\}\}'
> format: { self uidy: 'Q' }).
> response := znClient timeout: 1; post; response.
> response should satisfy: #isSuccess.
> response contentType should equal: ZnMimeType applicationJson.
> (STON fromString: response entity contents) should equal: {
> 'agentId' -> (self uidy: '007') asString } asDictionary.
> Arg payload in: [ :arg |
> arg deviceId should equal: (self uidy: 'Q').
> arg agentAnsweredQuestions should satisfy: #notNil.
> arg agentAnsweredQuestions good should equal: 1.
> arg agentAnsweredQuestions bad should equal: 2 ]
> ! !
>
>
> !TowergameServerTests methodsFor: 'private' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> znClientForSync: jsonString
> ^ ZnClient new
> url: server localUrl;
> path: '/api/v1/sync';
> entity:
> (ZnEntity
> with: jsonString
> type: ZnMimeType applicationJson)
> ! !
>
>
> TestCase subclass: #TowergameSyncTests
> instanceVariableNames: 'towergame session dao'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame-Tests'!
>
> !TowergameSyncTests methodsFor: 'tests' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> testPlayerChecksStateVersionAndIsBehind
> | result payload |
> session createTables.
> session inUnitOfWorkDo: [
> | agent state |
> agent := TgAgent id: (self uidy: '007').
> state := (TgState agent: agent version: (self uidy:
> '18-eff'))
> packs: #('foopack' 'barpack') asSet;
> valuables: (TgValuables coins: 20 gems: 3);
> score: (TgFloors total: 4 reinforced: 1);
> bestScore: (TgFloors total: 18);
> answers: (TgAnswers good: 2 bad: 3);
> yourself.
> session registerAll: {state. TgAct agent: agent tool:
> (TgTool id: (self uidy: 'Q7') ) } ].
> towergame := Towergame dao: dao.
> payload := NeoJSONObject new
> agentId: (self uidy: '007'); stateVersion: (self uidy:
> '23-fefe'); deviceId: (self uidy: 'Q7').
> result := towergame clientSync: payload.
> result where agentId should equal: (self uidy: '007').
> result where stateVersion should equal: (self uidy: '18-eff').
> result where purchasedPacks should satisfy: [ :x | x asSet should
> equal: #('foopack' 'barpack') asSet ].
> result where valuables coins should equal: 20.
> result where valuables gems should equal: 3.
> result where floorsNumber current should equal: 4.
> result where floorsNumber best should equal: 18.
> result where floorsNumber reinforced should equal: 1.
> result where agentAnsweredQuestions good should equal: 2.
> result where agentAnsweredQuestions bad should equal: 3.
> result where totalAnsweredQuestions good should equal: 2.
> result where totalAnsweredQuestions bad should equal: 3! !
>
> !TowergameSyncTests methodsFor: 'tests' stamp: 'HerbyVojcik 8/14/2017
> 18:18'!
> testPlayerChecksStateVersion
> | result payload |
> session createTables.
> session inUnitOfWorkDo: [
> | agent state |
> agent := TgAgent id: (self uidy: '007').
> state := TgState agent: agent version: (self uidy:
> '23-fefe').
> session registerAll: {state. TgAct agent: agent tool:
> (TgTool id: (self uidy: 'Q7') ) } ].
> towergame := Towergame dao: dao.
> payload := NeoJSONObject new
> agentId: (self uidy: '007'); stateVersion: (self uidy:
> '23-fefe'); deviceId: (self uidy: 'Q7').
> result := towergame clientSync: payload.
> result where agentId should equal: (self uidy: '007').
> result where stateVersion should equal: (self uidy: '23-fefe').
> result where totalAnsweredQuestions good should equal: 0.
> result where totalAnsweredQuestions bad should equal: 0! !
>
>
> !TowergameSyncTests methodsFor: 'running' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> uidy: aString
> ^ UUID fromString36: aString ! !
>
> !TowergameSyncTests methodsFor: 'running' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> setUp
> dao := Towergame daoForLogin: self loginToTemporaryDatabase.
> session := dao glorpSession.
> ! !
>
> !TowergameSyncTests methodsFor: 'running' stamp: 'HerbertVojà ÃÂk 8/14/2017
> 18:09:53'!
> tearDown
> session logout! !
>
> !TowergameSyncTests methodsFor: 'running' stamp: 'HerbyVojcik 8/14/2017
> 18:16'!
> loginToTemporaryDatabase
> ^ Login new
> database: UDBCSQLite3Platform new;
> host: '';
> port: '';
> username: '';
> password: '';
> databaseName: '';
> yourself! !
>
>
>
>
>
> BaselineOfTowergame.st:
> BaselineOf subclass: #BaselineOfTowergame
> instanceVariableNames: ''
> classVariableNames: ''
> poolDictionaries: ''
> category: 'BaselineOfTowergame'!
>
> !BaselineOfTowergame methodsFor: 'baseline' stamp: 'HerbertVojà ÃÂk
> 8/14/2017 18:09:53'!
> baseline: spec
> <baseline>
> spec for: #common do: [ spec
>
> package: 'Towergame' with: [ spec
> requires: #('GlorpSQLite' 'NeoJSON') ];
> package: 'Towergame-Tests' with: [ spec
> requires: #('Towergame' 'Mocketry') ];
>
> configuration: 'GlorpSQLite' with: [ spec
> version: #stable;
> repository:
> 'http://smalltalkhub.com/mc/Pharo/MetaRepoForPharo60/main' ];
> configuration: 'NeoJSON' with: [ spec
> version: #stable;
> repository:
> 'http://smalltalkhub.com/mc/Pharo/MetaRepoForPharo60/main' ];
> baseline: 'Mocketry' with: [ spec
> repository: 'github://dionisiydk/Mocketry:v4.0.x' ];
>
> group: 'default' with: #('Core');
> group: 'development' with: #('Core' 'Tests');
> group: 'Core' with: #('Towergame');
> group: 'Tests' with: #('Towergame-Tests') ]
> ! !
>
>
Aug. 14, 2017
Re: [Pharo-users] Honest question, new to ecosystem: are Glorp and Garage alive?
by Stephane Ducasse
> This indeed contains much more commit beyond May 2016.
>
> Maybe the ConfigurationOfGarage / Pharo6 catalog needs updating?
Definitively :)
Aug. 14, 2017
Re: [Pharo-users] including Pillar in Pharo image by default
by Stephane Ducasse
This is in our plan. Have a look at Ecstatic because I want to be able to
- use plain pillar
- generate plain HTML
There is also a little web server to display your ecstatic web site.
All this is rudimentary.
We use mustache for the templating.
Stef
On Mon, Aug 14, 2017 at 10:46 AM, Tim Mackinnon <tim(a)testit.works> wrote:
> Hi Stef - I think yourâs is a fair requirement (in fact I hit something similar when doing a static website using a JS markdown framework - and this is why I mentioned Kramdown which adds a few extras to regular markdown - but it feels like it goes a bit too far).
>
> My next item on my learning todo list was to try and replace that JS generator with something from Smalltalk - so I think we can possibly come up with something that ticks all the right boxes (Iâd like to try anyway).
>
> Iâll keep working away on it and compare notes with you. I think with Pillar, it was more that things like headers, bold and italics are similar concepts but just use different characters - so I keep typing the wrong thing and getting frustrated particularly when we embrace Git and readme.md is in markdown.
>
>
> Tim
>
>> On 13 Aug 2017, at 20:08, Stephane Ducasse <stepharo.self(a)gmail.com> wrote:
>>
>> Hi tim
>>
>> I personally do not care much about the syntax but I care about what I
>> can do with it
>> (ref, cite, ... )
>> I cannot write books in markdown because reference to figures!!!!!!
>> were missing.
>>
>> And of course a parser because markdown is not really nice to parse
>> and I will not write a parser because I have something else to do. I
>> want to make pillar smaller, simpler, nicer.
>>
>> Now if someone come up with a parser that parse for REAL a markdown
>> that can be extended with decent behavior (figure reference, section
>> reference, cite) and can be extended because there are many things
>> that can be nice to have (for example I want to be able to write the
>> example below) and emit a PillarModel (AST) we can talk to have
>> another syntax for Pillar but not before.
>>
>> [[[test
>> 2+3
>>>>> 5
>> ]]]
>>
>> and being able to verify that the doc is in sync.
>>
>>
>> Stef
>>
>>
>>
>> On Sat, Aug 12, 2017 at 12:37 AM, Tim Mackinnon <tim(a)testit.works> wrote:
>>> Of course, I/we recognise and appreciate all the work that's gone into docs in pillar - but I think it should be reasonably straightforward to write a converter as it is pretty closely related from what I have seen.
>>>
>>> So I don't make the suggestion flippantly, and would want to help write a converter and get us to a common ground where we can differentiate on the aspects where we can excel.
>>>
>>> Tim
>>>
>>> Sent from my iPhone
>>>
>>>> On 11 Aug 2017, at 23:21, Peter Uhnak <i.uhnak(a)gmail.com> wrote:
>>>>
>>>> A long time issue with Markdown was that there was no standardization (and when I used Pillar's MD export ~2 years ago it didn't work well).
>>>>
>>>> However CommonMark ( http://spec.commonmark.org/0.28/ ) has become the de-facto standard, so it would make sense to support it bidirectionally with Pillar.
>>>>
>>>>> The readme.md that Peter is talking about is gfm markdown
>>>>
>>>> Well, technically it is just a CommonMark, as I am not using any github extensions.
>>>> (Github uses CommonMarks and adds just couple small extensions.)
>>>>
>>>> Peter
>>>>
>>>
>>>
>>
>
>
Aug. 14, 2017
Big Glorp problem w/ type coercion, pls help
by Herby VojÄÃk
Hello!
I encountered a problem with OneToOneMapping and type coercion. When
writing data, thing work; when reading data, the right child of relation
fails to convert.
I tried everything possible to inject converters (even subclassing
GlorpBlobType), but to no avail. RelationExpression passes conversion to
its left child:
convertedDbValueOf: anObject
"Assume that our types match, so we can ask either child to do the
conversion. That isn't guaranteed, but should at least work for the
common cases."
^leftChild convertedDbValueOf: anObject.
but the left child is FieldExpression in case of OneToOneMapping, which:
convertedDbValueOf: anObject
"We don't do any conversion"
^anObject
What is strange, writing works (even the OneToOneMapping, I opened the
sqlite file with an explorer), but second SELECT, one using the relation
(`state := self dao findStateByAgent: agent` in clientSync), fails with
"GlorpDatabaseReadError: Could not coerce arguments". FWIW, the first
one _does_ convert when creating bindings, as it uses MappingExpression
as left child (stepped over it in debugger).
Is it meant to be a strange case that primary key is something
non-primitive needing coercion (in this case, it is a UUID which needs
coercion to ByteArray, even if it is its subclass)?
Here's the stack of running the test which fails:
PharoDatabaseAccessor(DatabaseAccessor)>>handleError:for:
[ :ex | self handleError: ex for: command ] in [ | result |
self checkPermissionFor: command.
result := [ (self useBinding and: [ command useBinding ])
ifTrue: [ command executeBoundIn: self ]
ifFalse: [ command executeUnboundIn: self ] ]
on: Dialect error
do: [ :ex | self handleError: ex for: command ].
aBoolean
ifTrue: [ result ]
ifFalse: [ result upToEnd ] ] in
PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
BlockClosure>>cull:
Context>>evaluateSignal:
Context>>handleSignal:
Error(Exception)>>signal
Error(Exception)>>signal:
ExternalLibraryFunction(Object)>>error:
ExternalLibraryFunction(Object)>>externalCallFailed
ExternalLibraryFunction(ExternalFunction)>>invokeWithArguments:
UDBCSQLite3Library>>apiBindBlob:atColumn:with:with:with:
UDBCSQLite3Library>>with:at:putBlob:
UDBCSQLite3Statement>>at:putByteArray:
UDBCSQLite3ResultSet>>execute:withIndex:withValue:
[ :v | i := self execute: statement withIndex: i withValue: v ] in
UDBCSQLite3ResultSet>>execute:withCollection:
OrderedCollection>>do:
UDBCSQLite3ResultSet>>execute:withCollection:
UDBCSQLite3ResultSet>>execute:with:on:
UDBCSQLite3Connection>>execute:with:
GlorpSQLite3Driver>>basicExecuteSQLString:binding:
PharoDatabaseAccessor>>executeCommandBound:
QuerySelectCommand(DatabaseCommand)>>executeBoundIn:
[ (self useBinding and: [ command useBinding ])
ifTrue: [ command executeBoundIn: self ]
ifFalse: [ command executeUnboundIn: self ] ] in [ | result |
self checkPermissionFor: command.
result := [ (self useBinding and: [ command useBinding ])
ifTrue: [ command executeBoundIn: self ]
ifFalse: [ command executeUnboundIn: self ] ]
on: Dialect error
do: [ :ex | self handleError: ex for: command ].
aBoolean
ifTrue: [ result ]
ifFalse: [ result upToEnd ] ] in
PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
BlockClosure>>on:do:
[ | result |
self checkPermissionFor: command.
result := [ (self useBinding and: [ command useBinding ])
ifTrue: [ command executeBoundIn: self ]
ifFalse: [ command executeUnboundIn: self ] ]
on: Dialect error
do: [ :ex | self handleError: ex for: command ].
aBoolean
ifTrue: [ result ]
ifFalse: [ result upToEnd ] ] in
PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
[ caught := true.
self wait.
blockValue := mutuallyExcludedBlock value ] in Semaphore>>critical:
BlockClosure>>ensure:
Semaphore>>critical:
PharoDatabaseAccessor(DatabaseAccessor)>>executeCommand:returnCursor:
[ session accessor executeCommand: command returnCursor: true ] in
SimpleQuery>>rowsFromDatabaseWithParameters:
BlockClosure>>on:do:
SimpleQuery>>rowsFromDatabaseWithParameters:
SimpleQuery(AbstractReadQuery)>>readFromDatabaseWithParameters:
SimpleQuery(AbstractReadQuery)>>executeWithParameters:in:
GlorpSession>>execute:
GlorpSession>>readOneOf:where:
TowergameDao>>findStateByAgent:
[ | agent state |
agent := self dao findAgentById: anObject agentId.
state := self dao findStateByAgent: agent.
^ NeoJSONObject new
agentId: agent id;
stateVersion: state version;
totalAnsweredQuestions:
(NeoJSONObject new
good: 0;
bad: 0;
yourself);
yourself ] in Towergame>>clientSync:
[ myUnitOfWork := self hasUnitOfWork not.
myUnitOfWork
ifTrue: [ self beginUnitOfWork ].
result := aBlock numArgs = 1
ifTrue: [ aBlock value: self ]
ifFalse: [ aBlock value ].
myUnitOfWork
ifTrue: [ self commitUnitOfWork ] ] in GlorpSession>>inUnitOfWorkDo:
BlockClosure>>ifCurtailed:
GlorpSession>>inUnitOfWorkDo:
TowergameDao>>inUnitOfWorkDo:
Towergame>>clientSync:
TowergameSyncTests>>testPlayerChecksStateVersion
TowergameSyncTests(TestCase)>>performTest
[ self setUp.
self performTest ] in TowergameSyncTests(TestCase)>>runCase
BlockClosure>>ensure:
TowergameSyncTests(TestCase)>>runCase
[ aTestCase runCase ] in [ [ aTestCase runCase ]
on: Halt
do: [ :halt |
"if test was halted we should resume all background failures
to debug all of them together with test process"
failedProcesses keysDo: #resume.
halt pass ] ] in TestExecutionEnvironment>>runTestCaseSafelly:
BlockClosure>>on:do:
[ [ aTestCase runCase ]
on: Halt
do: [ :halt |
"if test was halted we should resume all background failures
to debug all of them together with test process"
failedProcesses keysDo: #resume.
halt pass ] ] in TestExecutionEnvironment>>runTestCaseSafelly:
BlockClosure>>on:do:
TestExecutionEnvironment>>runTestCaseSafelly:
[ self runTestCaseSafelly: aTestCase ] in [ [ self runTestCaseSafelly:
aTestCase ]
ensure: [ testCompleted := true.
watchDogSemaphore signal ]. "signal that test case completes"
self checkForkedProcesses ] in TestExecutionEnvironment>>runTestCase:
BlockClosure>>ensure:
[ [ self runTestCaseSafelly: aTestCase ]
ensure: [ testCompleted := true.
watchDogSemaphore signal ]. "signal that test case completes"
self checkForkedProcesses ] in TestExecutionEnvironment>>runTestCase:
BlockClosure>>ifCurtailed:
TestExecutionEnvironment>>runTestCase:
[ testEnv runTestCase: aTestCase ] in
DefaultExecutionEnvironment>>runTestCase:
[ self value: anExecutionEnvironment.
anExecutionEnvironment activated.
aBlock value ] in CurrentExecutionEnvironment class>>activate:for:
BlockClosure>>ensure:
CurrentExecutionEnvironment class>>activate:for:
TestExecutionEnvironment(ExecutionEnvironment)>>beActiveDuring:
DefaultExecutionEnvironment>>runTestCase:
CurrentExecutionEnvironment class>>runTestCase:
TowergameSyncTests(TestCase)>>runCaseManaged
[ aTestCase announce: TestCaseStarted withResult: self.
aTestCase runCaseManaged.
aTestCase announce: TestCaseEnded withResult: self.
self addPass: aTestCase ] in TestResult>>runCaseForDebug:
BlockClosure>>on:do:
TestResult>>runCaseForDebug:
[ result runCaseForDebug: self ] in TowergameSyncTests(TestCase)>>debug
BlockClosure>>ensure:
TowergameSyncTests(TestCase)>>debug
[ :each |
each debug.
self announceTest: each.
self changed: each ] in [ self tests
do: [ :each |
each debug.
self announceTest: each.
self changed: each ] ] in TestSuite>>debug
OrderedCollection>>do:
[ self tests
do: [ :each |
each debug.
self announceTest: each.
self changed: each ] ] in TestSuite>>debug
BlockClosure>>ensure:
TestSuite>>debug
[ :aSuite | aSuite debug ] in TestRunner>>debugSuite:
BlockClosure>>cull:
BlockClosure>>cull:cull:
[ aBlock cull: aTestSuite cull: result ] in TestRunner>>executeSuite:as:
BlockClosure>>ensure:
TestRunner>>executeSuite:as:
TestRunner>>debugSuite:
TestRunner>>debug:
TestRunner>>errorSelected:
PluggableListMorph>>changeModelSelection:
PluggableListMorph>>mouseUpOnSingle:
PluggableListMorph>>mouseUp:
PluggableListMorph(Morph)>>handleMouseUp:
MouseButtonEvent>>sentTo:
PluggableListMorph(Morph)>>handleEvent:
MorphicEventDispatcher>>dispatchDefault:with:
MorphicEventDispatcher>>handleMouseUp:
MouseButtonEvent>>sentTo:
[ ^ anEvent sentTo: self ] in MorphicEventDispatcher>>dispatchEvent:with:
BlockClosure>>ensure:
MorphicEventDispatcher>>dispatchEvent:with:
PluggableListMorph(Morph)>>processEvent:using:
PluggableListMorph(Morph)>>processEvent:
PluggableListMorph>>handleFocusEvent:
[ ActiveHand := self.
ActiveEvent := anEvent.
result := focusHolder
handleFocusEvent: (anEvent transformedBy: (focusHolder transformedFrom:
self)) ] in HandMorph>>sendFocusEvent:to:clear:
BlockClosure>>on:do:
WorldMorph(PasteUpMorph)>>becomeActiveDuring:
HandMorph>>sendFocusEvent:to:clear:
HandMorph>>sendEvent:focus:clear:
HandMorph>>sendMouseEvent:
HandMorph>>handleEvent:
HandMorph>>processEventsFromQueue:
HandMorph>>processEvents
[ :h |
self activeHand: h.
h processEvents.
self activeHand: nil ] in WorldState>>doOneCycleNowFor:
Array(SequenceableCollection)>>do:
WorldState>>handsDo:
WorldState>>doOneCycleNowFor:
WorldState>>doOneCycleFor:
WorldMorph>>doOneCycle
WorldMorph class>>doOneCycle
[ [ WorldMorph doOneCycle.
Processor yield.
false ] whileFalse: [ ] ] in MorphicUIManager>>spawnNewProcess
[ self value.
Processor terminateActive ] in BlockClosure>>newProcess
And here's the code:
Towergame.st:
GlorpBlobType subclass: #GlorpBlob2Type
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!GlorpBlob2Type methodsFor: 'types' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
converterForStType: aClass
aClass = UUID ifTrue: [ ^ UuidConverter new ].
^ super converterForStType: aClass! !
Object subclass: #TgAct
instanceVariableNames: 'agent tool timestamp'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TgAct commentStamp: 'HerbyVojcik 8/5/2017 19:23' prior: 0!
I represent a relationship between a player (TgAgent)
and a device (TgTool).
In particular, I am created whenever a player logs in to the game from
different device
than it was last time (or first time, ever).!
!TgAct methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
agent
^ agent! !
!TgAct methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
agent: anObject
agent := anObject! !
!TgAct methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
timestamp: anObject
timestamp := anObject! !
!TgAct methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
tool
^ tool! !
!TgAct methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
tool: anObject
tool := anObject! !
!TgAct methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
timestamp
^ timestamp! !
!TgAct methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
initialize
super initialize.
agent := nil.
timestamp := DateAndTime now asUTC.
tool := nil.! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TgAct class
instanceVariableNames: ''!
!TgAct class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
agent: aTgAgent tool: aTgTool
^ self new
agent: aTgAgent;
tool: aTgTool;
yourself! !
Object subclass: #TgAgent
instanceVariableNames: 'id'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TgAgent commentStamp: 'HerbyVojcik 8/5/2017 19:22' prior: 0!
I represent a towergame player.
I only contain player-related information;
the game state itself is in TgState.!
!TgAgent methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
initialize
super initialize.
id := nil.! !
!TgAgent methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
id: anObject
id := anObject! !
!TgAgent methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
id
^ id! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TgAgent class
instanceVariableNames: ''!
!TgAgent class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
id: aString
^ self new
id: aString;
yourself! !
Object subclass: #TgAnswers
instanceVariableNames: 'good bad'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TgAnswers commentStamp: 'HerbyVojcik 8/5/2017 20:23' prior: 0!
I represent the answered question stats.
I know how many good / bad answered questions there is.!
!TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
good
^ good! !
!TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
bad: anObject
bad := anObject! !
!TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
bad
^ bad! !
!TgAnswers methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
good: anObject
good := anObject! !
!TgAnswers methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
initialize
super initialize.
bad := 0.
good := 0.! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TgAnswers class
instanceVariableNames: ''!
!TgAnswers class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
good: anInteger bad: anInteger2
^ self new
good: anInteger;
bad: anInteger2;
yourself! !
Object subclass: #TgFloors
instanceVariableNames: 'total reinforced'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TgFloors commentStamp: 'HerbyVojcik 8/5/2017 20:22' prior: 0!
I represent the floor building status.
I know how many floors are build and how many of them is reinforced.!
!TgFloors methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
total
^ total! !
!TgFloors methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
total: anObject
total := anObject! !
!TgFloors methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
reinforced
^ reinforced! !
!TgFloors methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
reinforced: anObject
reinforced := anObject! !
!TgFloors methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
initialize
super initialize.
reinforced := 0.
total := 0.! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TgFloors class
instanceVariableNames: ''!
!TgFloors class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
total: anInteger
^ self total: anInteger reinforced: 0! !
!TgFloors class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
total: anInteger reinforced: anInteger2
^ self new
total: anInteger;
reinforced: anInteger2;
yourself! !
Object subclass: #TgState
instanceVariableNames: 'agent version packs valuables score bestScore
answers'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TgState commentStamp: 'HerbyVojcik 8/5/2017 20:20' prior: 0!
I represent the game state.
I have relation to a player (TgAgent) and have a version.
Then, I contain (directly or indirectly) other parts that
make up the player's game state.
Whenever I am changed by game progress, my version is changed as well.!
!TgState methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
initialize
super initialize.
agent := nil.
answers := nil.
bestScore := nil.
packs := Set new.
score := nil.
valuables := nil.
version := nil.! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
score: anObject
score := anObject! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
bestScore: anObject
bestScore := anObject! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
agent: anObject
agent := anObject! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
score
^ score! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
packs
^ packs! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
version
^ version! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
bestScore
^ bestScore! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
agent
^ agent! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
answers: anObject
answers := anObject! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
valuables: anObject
valuables := anObject! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
valuables
^ valuables! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
version: anObject
version := anObject! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
answers
^ answers! !
!TgState methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
packs: anObject
packs := anObject! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TgState class
instanceVariableNames: ''!
!TgState class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
agent: aTgAgent version: aString
^ self new
agent: aTgAgent;
version: aString;
yourself! !
Object subclass: #TgTool
instanceVariableNames: 'id'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TgTool commentStamp: 'HerbyVojcik 8/5/2017 19:26' prior: 0!
I represent the device (mobile phone, web browser, ..)
that player uses to connect to game.!
!TgTool methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
initialize
super initialize.
id := nil.! !
!TgTool methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
id: anObject
id := anObject! !
!TgTool methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
id
^ id! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TgTool class
instanceVariableNames: ''!
!TgTool class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
id: aString
^ self new
id: aString;
yourself! !
Object subclass: #TgValuables
instanceVariableNames: 'coins gems'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TgValuables commentStamp: 'HerbyVojcik 8/5/2017 20:22' prior: 0!
I represent a purse.
I know how many coins and gems there is.!
!TgValuables methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
initialize
super initialize.
coins := 0.
gems := 0.! !
!TgValuables methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
gems: anObject
gems := anObject! !
!TgValuables methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
coins: anObject
coins := anObject! !
!TgValuables methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
gems
^ gems! !
!TgValuables methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
coins
^ coins! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TgValuables class
instanceVariableNames: ''!
!TgValuables class methodsFor: 'instance creation' stamp:
'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
coins: anInteger gems: anInteger2
^ self new
coins: anInteger;
gems: anInteger2;
yourself! !
Object subclass: #Towergame
instanceVariableNames: 'dao'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!Towergame commentStamp: 'HerbyVojcik 5/17/2017 17:19' prior: 0!
I am the Towergame app class.
I configure and start towergame server processing.!
!Towergame methodsFor: 'actions' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
clientSync: anObject
self dao inUnitOfWorkDo: [
| agent state |
agent := self dao findAgentById: anObject agentId.
state := self dao findStateByAgent: agent.
^ NeoJSONObject new
agentId: agent id;
stateVersion: state version;
totalAnsweredQuestions: (NeoJSONObject new good: 0; bad: 0; yourself);
yourself ]! !
!Towergame methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
initialize
super initialize.
dao := nil.
! !
!Towergame methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
dao: anObject
dao := anObject! !
!Towergame methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
dao
^ dao! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
Towergame class
instanceVariableNames: 'default'!
!Towergame class methodsFor: 'instance creation' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
dao: aDao
^ self new
dao: aDao;
yourself! !
!Towergame class methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
defaultDbLogin
| databaseFile |
databaseFile := Smalltalk imageDirectory asFileReference / 'towergame.db'.
^ Login new
database: UDBCSQLite3Platform new;
host: '';
port: '';
username: '';
password: '';
databaseName: databaseFile fullPath asZnUrl asString;
yourself ! !
!Towergame class methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
default
^ default ifNil: [ default := self
dao: (self daoForLogin: self defaultDbLogin)
]! !
!Towergame class methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
defaultPort
^ 4998! !
!Towergame class methodsFor: 'configuration' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
configureServer
(self serverFor: self default on: self defaultPort) start; register
! !
!Towergame class methodsFor: 'configuration' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
configureSqlite3
PharoDatabaseAccessor DefaultDriver: GlorpSQLite3Driver! !
!Towergame class methodsFor: 'configuration' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
configure
self configureSqlite3.
self configureServer.! !
!Towergame class methodsFor: 'factory' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
daoForLogin: aLogin
^ TowergameDao forLogin: aLogin! !
!Towergame class methodsFor: 'factory' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
serverFor: aTowergame on: port
^ (ZnServer on: port)
delegate: (TowergameDelegate on: aTowergame);
yourself! !
Object subclass: #TowergameDao
instanceVariableNames: 'glorpSession glorpLogin'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TowergameDao methodsFor: 'transactions' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
inUnitOfWorkDo: aBlock
^ self glorpSession inUnitOfWorkDo: aBlock! !
!TowergameDao methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
initialize
super initialize.
glorpLogin := nil.
glorpSession := nil.! !
!TowergameDao methodsFor: 'initialize-release' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
reset
glorpSession := nil.! !
!TowergameDao methodsFor: 'query' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
findStateByAgent: anAgent
^ self glorpSession readOneOf: TgState where: [ :one | one agent =
anAgent ]! !
!TowergameDao methodsFor: 'query' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
findAgentById: anUUID
^ self glorpSession readOneOf: TgAgent where: [ :one | one id = anUUID ]! !
!TowergameDao methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
glorpLogin: anObject
glorpLogin := anObject! !
!TowergameDao methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
glorpLogin
^ glorpLogin! !
!TowergameDao methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
glorpSession
glorpSession ifNil: [
glorpSession := TowergameDescriptorSystem sessionForLogin: self
glorpLogin ].
glorpSession accessor isLoggedIn ifFalse: [
glorpSession accessor login ].
^ glorpSession! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TowergameDao class
instanceVariableNames: ''!
!TowergameDao class methodsFor: 'instance creation' stamp:
'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
forLogin: aLogin
^ self new
glorpLogin: aLogin;
yourself! !
ZnDispatcherDelegate subclass: #TowergameDelegate
instanceVariableNames: 'towergame'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TowergameDelegate methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
towergame
^ towergame! !
!TowergameDelegate methodsFor: 'accessing' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
towergame: anObject
towergame := anObject! !
!TowergameDelegate methodsFor: 'initialization' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
initialize
super initialize.
towergame := nil.
self
map: '/api/v1/sync'
to: [ :request :response | self syncRequest: request toResponse:
response ]! !
!TowergameDelegate methodsFor: 'action' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
syncRequest: request toResponse: response
| requestPayload responsePayload uuidKeys |
uuidKeys := #(agentId stateVersion deviceId).
request method == #POST ifFalse: [ ^ ZnResponse methodNotAllowed:
request ].
requestPayload := NeoJSONObject fromString: request contents.
requestPayload ifNotNil: [
uuidKeys do: [ :each | requestPayload at: each ifPresentPut: [ :s |
UUID fromString: s ] ] ].
responsePayload := self towergame clientSync: requestPayload.
responsePayload ifNotNil: [
uuidKeys do: [ :each | responsePayload at: each ifPresentPut:
#asString ] ].
^ response
entity: (ZnEntity
with: (NeoJSONWriter toString: responsePayload)
type: ZnMimeType applicationJson);
yourself! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TowergameDelegate class
instanceVariableNames: ''!
!TowergameDelegate class methodsFor: 'instance creation' stamp:
'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
on: aTowergame
^ self new towergame: aTowergame; yourself! !
DescriptorSystem subclass: #TowergameDescriptorSystem
instanceVariableNames: 'uuidConverter'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
tableForAGENT: aTable
(aTable createFieldNamed: 'id' type: platform blob2) bePrimaryKey.
! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
classModelForTgAgent: aClassModel
aClassModel
newAttributeNamed: #id type: UUID! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbyVojcik
8/14/2017 18:24'!
tableForSTATE: aTable
(aTable createFieldNamed: 'agent' type: platform blob2) in: [ :agentField |
agentField bePrimaryKey.
aTable addForeignKeyFrom: agentField to: ((self tableNamed: 'AGENT')
fieldNamed: 'id') ].
(aTable createFieldNamed: 'version' type: platform blob) beIndexed.
! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
descriptorForTgAgent: aDescriptor
| table |
table := self tableNamed: 'AGENT'.
aDescriptor table: table.
(aDescriptor newMapping: DirectMapping)
from: #id to: (table fieldNamed: 'id').! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
tableForACT: aTable
(aTable createFieldNamed: 'agent' type: platform blob2) beIndexed.
(aTable createFieldNamed: 'tool' type: platform blob2) beIndexed.
(aTable createFieldNamed: 'timestamp' type: platform timestamp) beIndexed.
! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
descriptorForTgState: aDescriptor
| table |
table := self tableNamed: 'STATE'.
aDescriptor table: table.
(aDescriptor newMapping: OneToOneMapping) attributeName: #agent.
(aDescriptor newMapping: DirectMapping)
from: #version to: (table fieldNamed: 'version').! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
converterBetweenStType: aClass andField: aField
(aClass = UUID and: [ aField impliedSmalltalkType = ByteArray])
ifTrue: [ ^ self uuidConverter ].
^ super converterBetweenStType: aClass andField: aField! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
classModelForTgState: aClassModel
"agent version packs valuables score bestScore answers"
aClassModel
newAttributeNamed: #agent type: TgAgent;
newAttributeNamed: #version type: UUID! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
classModelForTgTool: aClassModel
aClassModel
newAttributeNamed: #id
! !
!TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
classModelForTgAct: aClassModel
aClassModel
newAttributeNamed: #timestamp;
newAttributeNamed: #agent type: TgAgent;
newAttributeNamed: #tool type: TgTool! !
!TowergameDescriptorSystem methodsFor: 'accessing' stamp:
'HerbertVojÃÂÃÂk 8/14/2017 18:09:53'!
uuidConverter
^ uuidConverter ifNil: [ uuidConverter := UuidConverter new name:
'uuid'; yourself ]! !
DatabaseConverter subclass: #UuidConverter
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame'!
!UuidConverter methodsFor: 'converting' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
convert: anObject toDatabaseRepresentationAs: aDatabaseType
^ anObject ifNotNil: [ ByteArray withAll: anObject ]! !
!UuidConverter methodsFor: 'converting' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
convert: anObject fromDatabaseRepresentationAs: aDatabaseType
^ anObject ifNotNil: [ UUID withAll: anObject ]! !
'From Pharo6.0 of 13 May 2016 [Latest update: #60510] on 14 August 2017
at 6:26:30.67905 pm'!
!DatabasePlatform methodsFor: '*Towergame' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
blob2
^self typeNamed: #blob ifAbsentPut: [GlorpBlob2Type new].! !
'From Pharo6.0 of 13 May 2016 [Latest update: #60510] on 14 August 2017
at 6:26:30.68005 pm'!
!Dictionary methodsFor: '*Towergame' stamp: 'HerbertVojÃÂÃÂk 8/14/2017
18:09:53'!
at: key ifPresentPut: aBlock
"Lookup the given key in the receiver. If it is present, update it
with the value of evaluating the given block with the value associated
with the key. Otherwise, answer nil."
^ self at: key ifPresent: [ :value | self at: key put: (aBlock cull:
value) ]! !
TowergameTests.st:
TestCase subclass: #TowergameServerTests
instanceVariableNames: 'randomPort towergame server'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame-Tests'!
!TowergameServerTests methodsFor: 'running' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
uidy: aString
^ UUID fromString36: aString ! !
!TowergameServerTests methodsFor: 'running' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
setUp
randomPort := 1700 + 32 atRandom.
towergame := Mock new.
server := Towergame serverFor: towergame on: randomPort.
server start.
self
assert: server isRunning & server isListening
description: ('Failed to start server on port {1}. Is there one
already?' format: { server port })
! !
!TowergameServerTests methodsFor: 'running' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
tearDown
server stop! !
!TowergameServerTests methodsFor: 'tests' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
testEmptySyncRequest
| znClient response |
(towergame stub clientSync: Arg payload) willReturn: nil.
znClient := self znClientForSync: 'null'.
response := znClient timeout: 1; post; response.
response should satisfy: #isSuccess.
response contentType should equal: ZnMimeType applicationJson.
(STON fromString: response entity contents) should equal: nil.
Arg payload should equal: nil! !
!TowergameServerTests methodsFor: 'tests' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
testRejectEmptyGetSyncRequest
| znClient response |
(towergame stub clientSync: Arg payload) willReturn: nil.
znClient := self znClientForSync: 'null'.
response := znClient timeout: 1; get; response.
response code should equal: ZnStatusLine methodNotAllowed code.
towergame should not receive clientSync: Any! !
!TowergameServerTests methodsFor: 'tests' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
testNonEmptySyncRequest
| znClient response |
(towergame stub clientSync: Arg payload) willReturn: { #agentId ->
(self uidy: '007') } asDictionary.
znClient := self znClientForSync:
('\{"deviceId":"{1}","agentAnsweredQuestions":\{"good":1,"bad":2\}\}'
format: { self uidy: 'Q' }).
response := znClient timeout: 1; post; response.
response should satisfy: #isSuccess.
response contentType should equal: ZnMimeType applicationJson.
(STON fromString: response entity contents) should equal: { 'agentId'
-> (self uidy: '007') asString } asDictionary.
Arg payload in: [ :arg |
arg deviceId should equal: (self uidy: 'Q').
arg agentAnsweredQuestions should satisfy: #notNil.
arg agentAnsweredQuestions good should equal: 1.
arg agentAnsweredQuestions bad should equal: 2 ]
! !
!TowergameServerTests methodsFor: 'private' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
znClientForSync: jsonString
^ ZnClient new
url: server localUrl;
path: '/api/v1/sync';
entity:
(ZnEntity
with: jsonString
type: ZnMimeType applicationJson)
! !
TestCase subclass: #TowergameSyncTests
instanceVariableNames: 'towergame session dao'
classVariableNames: ''
poolDictionaries: ''
category: 'Towergame-Tests'!
!TowergameSyncTests methodsFor: 'tests' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
testPlayerChecksStateVersionAndIsBehind
| result payload |
session createTables.
session inUnitOfWorkDo: [
| agent state |
agent := TgAgent id: (self uidy: '007').
state := (TgState agent: agent version: (self uidy: '18-eff'))
packs: #('foopack' 'barpack') asSet;
valuables: (TgValuables coins: 20 gems: 3);
score: (TgFloors total: 4 reinforced: 1);
bestScore: (TgFloors total: 18);
answers: (TgAnswers good: 2 bad: 3);
yourself.
session registerAll: {state. TgAct agent: agent tool: (TgTool id:
(self uidy: 'Q7') ) } ].
towergame := Towergame dao: dao.
payload := NeoJSONObject new
agentId: (self uidy: '007'); stateVersion: (self uidy: '23-fefe');
deviceId: (self uidy: 'Q7').
result := towergame clientSync: payload.
result where agentId should equal: (self uidy: '007').
result where stateVersion should equal: (self uidy: '18-eff').
result where purchasedPacks should satisfy: [ :x | x asSet should
equal: #('foopack' 'barpack') asSet ].
result where valuables coins should equal: 20.
result where valuables gems should equal: 3.
result where floorsNumber current should equal: 4.
result where floorsNumber best should equal: 18.
result where floorsNumber reinforced should equal: 1.
result where agentAnsweredQuestions good should equal: 2.
result where agentAnsweredQuestions bad should equal: 3.
result where totalAnsweredQuestions good should equal: 2.
result where totalAnsweredQuestions bad should equal: 3! !
!TowergameSyncTests methodsFor: 'tests' stamp: 'HerbyVojcik 8/14/2017
18:18'!
testPlayerChecksStateVersion
| result payload |
session createTables.
session inUnitOfWorkDo: [
| agent state |
agent := TgAgent id: (self uidy: '007').
state := TgState agent: agent version: (self uidy: '23-fefe').
session registerAll: {state. TgAct agent: agent tool: (TgTool id:
(self uidy: 'Q7') ) } ].
towergame := Towergame dao: dao.
payload := NeoJSONObject new
agentId: (self uidy: '007'); stateVersion: (self uidy: '23-fefe');
deviceId: (self uidy: 'Q7').
result := towergame clientSync: payload.
result where agentId should equal: (self uidy: '007').
result where stateVersion should equal: (self uidy: '23-fefe').
result where totalAnsweredQuestions good should equal: 0.
result where totalAnsweredQuestions bad should equal: 0! !
!TowergameSyncTests methodsFor: 'running' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
uidy: aString
^ UUID fromString36: aString ! !
!TowergameSyncTests methodsFor: 'running' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
setUp
dao := Towergame daoForLogin: self loginToTemporaryDatabase.
session := dao glorpSession.
! !
!TowergameSyncTests methodsFor: 'running' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
tearDown
session logout! !
!TowergameSyncTests methodsFor: 'running' stamp: 'HerbyVojcik 8/14/2017
18:16'!
loginToTemporaryDatabase
^ Login new
database: UDBCSQLite3Platform new;
host: '';
port: '';
username: '';
password: '';
databaseName: '';
yourself! !
BaselineOfTowergame.st:
BaselineOf subclass: #BaselineOfTowergame
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'BaselineOfTowergame'!
!BaselineOfTowergame methodsFor: 'baseline' stamp: 'HerbertVojÃÂÃÂk
8/14/2017 18:09:53'!
baseline: spec
<baseline>
spec for: #common do: [ spec
package: 'Towergame' with: [ spec
requires: #('GlorpSQLite' 'NeoJSON') ];
package: 'Towergame-Tests' with: [ spec
requires: #('Towergame' 'Mocketry') ];
configuration: 'GlorpSQLite' with: [ spec
version: #stable;
repository: 'http://smalltalkhub.com/mc/Pharo/MetaRepoForPharo60/main' ];
configuration: 'NeoJSON' with: [ spec
version: #stable;
repository: 'http://smalltalkhub.com/mc/Pharo/MetaRepoForPharo60/main' ];
baseline: 'Mocketry' with: [ spec
repository: 'github://dionisiydk/Mocketry:v4.0.x' ];
group: 'default' with: #('Core');
group: 'development' with: #('Core' 'Tests');
group: 'Core' with: #('Towergame');
group: 'Tests' with: #('Towergame-Tests') ]
! !
Aug. 14, 2017
Re: [Pharo-users] including Pillar in Pharo image by default
by Tim Mackinnon
Jimmie et al. nicely reasoned arguments - and Doru's point about controlling the syntax is an interesting one that I hadnât thought about.
Personally, I find having too many similar syntaxâs confusing - contributing to things is hard enough - having to remember that its !! Instead of ## and ââ instead of ** is just frustrating for me.
My vote would be what Peter suggested - use http://spec.commonmark.org/0.28/ <http://spec.commonmark.org/0.28/> and put our Pillar extensions back on top for things that Stef was mentioning. (I think thatâs what Iâve understood gfm markdown is).
Sure, maybe we were first with Pillar, but for me, lots of programming is in other languages, and I use Smalltalk where I can, and a hybrid of multiple languages and projects is often the reality - so a lowest common denominator of Markdown is just easier. The fact that we are quite close to what our colleagues in other languages use (regardless of what Python has chosen), is quite interesting.
That said, if the community wants to stick to its gunâs thats fine - I will probably still investigate how to use Commonmark for myself, and will still contribute to Pillar docs where I can (and curse history) - but I think we are long better off trying to join emerging standards where we can particularly if they arenât our core language thing. And it just makes it less frictionless for ourselves and newcomers.
Of course, if we were to move, we would need to translate a lot of quality docs to a new format - but I would be up for contributing to that if that was a deciding factor.
Tim
> On 14 Aug 2017, at 16:41, Jimmie Houchin <jlhouchin(a)gmail.com> wrote:
>
> TL;DR
>
> Main points:
> Their is no universally accepted markup language.
> Other communities use their own markup and tools and their markup and tools choice is not determine by other communities decisions.
> We need a language and tool chain that we can control and maintain which accomplishes our goals.
> Our language and tools already exist and have existed for longer than most of the other markup languages. Of course they existed in various different forms over the years and have evolved into what they currently are.
> It might be nice to have a GFM Markdown exporter from Pillar for GitHub projects.
>
>
> I just want to comment on the fact that there is no universal markup language that every development community has settled upon. Making Markdown or some variant the markup language for Pharo only aligns us with a certain part of the development community. Even Markdown is not unified as is evident by the discussion.
>
> It is true that GitHub uses their variant of Markdown. And as long as we use GitHub we will need to use their variant for documents that reside on their system.
>
> However as a significant counter example to lets all use gfm Markdown, is the Python community and their documentation.
>
> https://docs.python.org/devguide/documenting.html
> """
> 7. Documenting Python
> The Python language has a substantial body of documentation, much of it contributed by various authors. The markup used for the Python documentation is reStructuredText, developed by the docutils project, amended by custom directives and using a toolset named Sphinx to post-process the HTML output.
>
> This document describes the style guide for our documentation as well as the custom reStructuredText markup introduced by Sphinx to support Python documentation and how it should be used.
>
> The documentation in HTML, PDF or EPUB format is generated from text files written using the reStructuredText format and contained in the CPython Git repository.
> """
>
> So the Python community uses their own markup language and their own tool chain. So therefore, it is not wrong for a community to go their own way, for their own reasons. Even within the conventional file based languages such as Python.
>
> The fact that you have tools such as Pandoc, suggest that there is not true uniformity or unanimity among developers as to the best markup language or tool chain.
>
> I believe that a language that we can control and maintain is better than adopting some other foreign markup language that is neither better, nor unanimously used by all. That would ultimately potentially require extensions to accomplish our goals. Then we would be maintaining someone else's language with our extensions that may or may not be accepted by the larger community. This does not prevent but rather encourages fragmentation of the existing Markdown.
>
> Regardless, Pillar markup already exists. The tools in Pharo already understand it. Should someone desire to use Pharo which is far more different from Python/Ruby/etc. than Pillar syntax is from Markdown. Then it should be worth their effort to learn our tools.
>
> Pillar markup is older than Markdown, etc. It's history begins in SmallWiki. It isn't as if we jumped up and decided to create something new in order to be different. Our markup and tools are older. They (and others) are the ones that decided to do their own markup and tools. And it is okay that they did so. Nothing wrong with doing so. Every community has the right to what they believe is best for their community. Even if other communities disagree.
>
> The ability to control and maintain is highly valuable. We can understand what our requirements are for today. But we can not know what the requirements are in the future. Nor can we know that Markdown or whomever will have such requirements when they appear. It is easy to see in the beginning with the Squeak Wiki syntax to the now Pillar syntax, changes that have been made to accommodate new requirements as they became known. We need to maintain that ability. Sure we would reserve the right to do so in any language we adopt. But the then current standard bearer of said language would determine whether what we do is acceptable and incorporate or whether we are then in fact adding to their fragmentation. Pillar is ours. There is not fragmentation when we evolve.
>
> However, since we have made a decision to use GitHub and GitHub has made a decision to use their own GFM Markdown. It might be nice to have a GFM Markdown exporter from Pillar for GitHub projects. This way we can use our own tools and markup language to accomplish whatever we want to accomplish. Including generating a Readme.md for our GitHub projects.
>
> Just wanted to toss out this simple opinion and facts about the situation.
>
> Jimmie
>
>
> On 08/14/2017 04:10 AM, Tudor Girba wrote:
>> Hi Tim,
>>
>> The main benefit of relying on Pillar is that we control its syntax and can easily extend it for our purposes. Also, there was quite a bit of engineering invested in it, and even though we still need to improve it, there exists a pipeline that allows people to quickly publish books.
>>
>> The figure embedding problem is one example of the need to customize the syntax and behavior, but this extensibility will become even more important for supporting the idea of moving the documentation inside the image. For example, the ability to refer to a class, method or other artifacts will be quite relevant soon especially that the editor will be able to embed advanced elements inside the text.
>>
>> Cheers,
>> Doru
>>
>>
>>> On Aug 14, 2017, at 10:46 AM, Tim Mackinnon <tim(a)testit.works> wrote:
>>>
>>> Hi Stef - I think yourâs is a fair requirement (in fact I hit something similar when doing a static website using a JS markdown framework - and this is why I mentioned Kramdown which adds a few extras to regular markdown - but it feels like it goes a bit too far).
>>>
>>> My next item on my learning todo list was to try and replace that JS generator with something from Smalltalk - so I think we can possibly come up with something that ticks all the right boxes (Iâd like to try anyway).
>>>
>>> Iâll keep working away on it and compare notes with you. I think with Pillar, it was more that things like headers, bold and italics are similar concepts but just use different characters - so I keep typing the wrong thing and getting frustrated particularly when we embrace Git and readme.md is in markdown.
>>>
>>>
>>> Tim
>>>
>>>> On 13 Aug 2017, at 20:08, Stephane Ducasse <stepharo.self(a)gmail.com> wrote:
>>>>
>>>> Hi tim
>>>>
>>>> I personally do not care much about the syntax but I care about what I
>>>> can do with it
>>>> (ref, cite, ... )
>>>> I cannot write books in markdown because reference to figures!!!!!!
>>>> were missing.
>>>>
>>>> And of course a parser because markdown is not really nice to parse
>>>> and I will not write a parser because I have something else to do. I
>>>> want to make pillar smaller, simpler, nicer.
>>>>
>>>> Now if someone come up with a parser that parse for REAL a markdown
>>>> that can be extended with decent behavior (figure reference, section
>>>> reference, cite) and can be extended because there are many things
>>>> that can be nice to have (for example I want to be able to write the
>>>> example below) and emit a PillarModel (AST) we can talk to have
>>>> another syntax for Pillar but not before.
>>>>
>>>> [[[test
>>>> 2+3
>>>>>>> 5
>>>> ]]]
>>>>
>>>> and being able to verify that the doc is in sync.
>>>>
>>>>
>>>> Stef
>>>>
>>>>
>>>>
>>>> On Sat, Aug 12, 2017 at 12:37 AM, Tim Mackinnon <tim(a)testit.works> wrote:
>>>>> Of course, I/we recognise and appreciate all the work that's gone into docs in pillar - but I think it should be reasonably straightforward to write a converter as it is pretty closely related from what I have seen.
>>>>>
>>>>> So I don't make the suggestion flippantly, and would want to help write a converter and get us to a common ground where we can differentiate on the aspects where we can excel.
>>>>>
>>>>> Tim
>>>>>
>>>>> Sent from my iPhone
>>>>>
>>>>>> On 11 Aug 2017, at 23:21, Peter Uhnak <i.uhnak(a)gmail.com> wrote:
>>>>>>
>>>>>> A long time issue with Markdown was that there was no standardization (and when I used Pillar's MD export ~2 years ago it didn't work well).
>>>>>>
>>>>>> However CommonMark ( http://spec.commonmark.org/0.28/ ) has become the de-facto standard, so it would make sense to support it bidirectionally with Pillar.
>>>>>>
>>>>>>> The readme.md that Peter is talking about is gfm markdown
>>>>>> Well, technically it is just a CommonMark, as I am not using any github extensions.
>>>>>> (Github uses CommonMarks and adds just couple small extensions.)
>>>>>>
>>>>>> Peter
>>>>>>
>>>>>
>>>
>> --
>> www.tudorgirba.com
>> www.feenk.com
>>
>> âLive like you mean it."
>>
>>
>
>
Aug. 14, 2017
Re: [Pharo-users] including Pillar in Pharo image by default
by Jimmie Houchin
TL;DR
Main points:
Their is no universally accepted markup language.
Other communities use their own markup and tools and their markup and
tools choice is not determine by other communities decisions.
We need a language and tool chain that we can control and maintain which
accomplishes our goals.
Our language and tools already exist and have existed for longer than
most of the other markup languages. Of course they existed in various
different forms over the years and have evolved into what they currently
are.
It might be nice to have a GFM Markdown exporter from Pillar for GitHub
projects.
I just want to comment on the fact that there is no universal markup
language that every development community has settled upon. Making
Markdown or some variant the markup language for Pharo only aligns us
with a certain part of the development community. Even Markdown is not
unified as is evident by the discussion.
It is true that GitHub uses their variant of Markdown. And as long as we
use GitHub we will need to use their variant for documents that reside
on their system.
However as a significant counter example to lets all use gfm Markdown,
is the Python community and their documentation.
https://docs.python.org/devguide/documenting.html
"""
7. Documenting Python
The Python language has a substantial body of documentation, much of it
contributed by various authors. The markup used for the Python
documentation is reStructuredText, developed by the docutils project,
amended by custom directives and using a toolset named Sphinx to
post-process the HTML output.
This document describes the style guide for our documentation as well as
the custom reStructuredText markup introduced by Sphinx to support
Python documentation and how it should be used.
The documentation in HTML, PDF or EPUB format is generated from text
files written using the reStructuredText format and contained in the
CPython Git repository.
"""
So the Python community uses their own markup language and their own
tool chain. So therefore, it is not wrong for a community to go their
own way, for their own reasons. Even within the conventional file based
languages such as Python.
The fact that you have tools such as Pandoc, suggest that there is not
true uniformity or unanimity among developers as to the best markup
language or tool chain.
I believe that a language that we can control and maintain is better
than adopting some other foreign markup language that is neither better,
nor unanimously used by all. That would ultimately potentially require
extensions to accomplish our goals. Then we would be maintaining someone
else's language with our extensions that may or may not be accepted by
the larger community. This does not prevent but rather encourages
fragmentation of the existing Markdown.
Regardless, Pillar markup already exists. The tools in Pharo already
understand it. Should someone desire to use Pharo which is far more
different from Python/Ruby/etc. than Pillar syntax is from Markdown.
Then it should be worth their effort to learn our tools.
Pillar markup is older than Markdown, etc. It's history begins in
SmallWiki. It isn't as if we jumped up and decided to create something
new in order to be different. Our markup and tools are older. They (and
others) are the ones that decided to do their own markup and tools. And
it is okay that they did so. Nothing wrong with doing so. Every
community has the right to what they believe is best for their
community. Even if other communities disagree.
The ability to control and maintain is highly valuable. We can
understand what our requirements are for today. But we can not know what
the requirements are in the future. Nor can we know that Markdown or
whomever will have such requirements when they appear. It is easy to see
in the beginning with the Squeak Wiki syntax to the now Pillar syntax,
changes that have been made to accommodate new requirements as they
became known. We need to maintain that ability. Sure we would reserve
the right to do so in any language we adopt. But the then current
standard bearer of said language would determine whether what we do is
acceptable and incorporate or whether we are then in fact adding to
their fragmentation. Pillar is ours. There is not fragmentation when we
evolve.
However, since we have made a decision to use GitHub and GitHub has made
a decision to use their own GFM Markdown. It might be nice to have a GFM
Markdown exporter from Pillar for GitHub projects. This way we can use
our own tools and markup language to accomplish whatever we want to
accomplish. Including generating a Readme.md for our GitHub projects.
Just wanted to toss out this simple opinion and facts about the situation.
Jimmie
On 08/14/2017 04:10 AM, Tudor Girba wrote:
> Hi Tim,
>
> The main benefit of relying on Pillar is that we control its syntax and can easily extend it for our purposes. Also, there was quite a bit of engineering invested in it, and even though we still need to improve it, there exists a pipeline that allows people to quickly publish books.
>
> The figure embedding problem is one example of the need to customize the syntax and behavior, but this extensibility will become even more important for supporting the idea of moving the documentation inside the image. For example, the ability to refer to a class, method or other artifacts will be quite relevant soon especially that the editor will be able to embed advanced elements inside the text.
>
> Cheers,
> Doru
>
>
>> On Aug 14, 2017, at 10:46 AM, Tim Mackinnon <tim(a)testit.works> wrote:
>>
>> Hi Stef - I think yourâs is a fair requirement (in fact I hit something similar when doing a static website using a JS markdown framework - and this is why I mentioned Kramdown which adds a few extras to regular markdown - but it feels like it goes a bit too far).
>>
>> My next item on my learning todo list was to try and replace that JS generator with something from Smalltalk - so I think we can possibly come up with something that ticks all the right boxes (Iâd like to try anyway).
>>
>> Iâll keep working away on it and compare notes with you. I think with Pillar, it was more that things like headers, bold and italics are similar concepts but just use different characters - so I keep typing the wrong thing and getting frustrated particularly when we embrace Git and readme.md is in markdown.
>>
>>
>> Tim
>>
>>> On 13 Aug 2017, at 20:08, Stephane Ducasse <stepharo.self(a)gmail.com> wrote:
>>>
>>> Hi tim
>>>
>>> I personally do not care much about the syntax but I care about what I
>>> can do with it
>>> (ref, cite, ... )
>>> I cannot write books in markdown because reference to figures!!!!!!
>>> were missing.
>>>
>>> And of course a parser because markdown is not really nice to parse
>>> and I will not write a parser because I have something else to do. I
>>> want to make pillar smaller, simpler, nicer.
>>>
>>> Now if someone come up with a parser that parse for REAL a markdown
>>> that can be extended with decent behavior (figure reference, section
>>> reference, cite) and can be extended because there are many things
>>> that can be nice to have (for example I want to be able to write the
>>> example below) and emit a PillarModel (AST) we can talk to have
>>> another syntax for Pillar but not before.
>>>
>>> [[[test
>>> 2+3
>>>>>> 5
>>> ]]]
>>>
>>> and being able to verify that the doc is in sync.
>>>
>>>
>>> Stef
>>>
>>>
>>>
>>> On Sat, Aug 12, 2017 at 12:37 AM, Tim Mackinnon <tim(a)testit.works> wrote:
>>>> Of course, I/we recognise and appreciate all the work that's gone into docs in pillar - but I think it should be reasonably straightforward to write a converter as it is pretty closely related from what I have seen.
>>>>
>>>> So I don't make the suggestion flippantly, and would want to help write a converter and get us to a common ground where we can differentiate on the aspects where we can excel.
>>>>
>>>> Tim
>>>>
>>>> Sent from my iPhone
>>>>
>>>>> On 11 Aug 2017, at 23:21, Peter Uhnak <i.uhnak(a)gmail.com> wrote:
>>>>>
>>>>> A long time issue with Markdown was that there was no standardization (and when I used Pillar's MD export ~2 years ago it didn't work well).
>>>>>
>>>>> However CommonMark ( http://spec.commonmark.org/0.28/ ) has become the de-facto standard, so it would make sense to support it bidirectionally with Pillar.
>>>>>
>>>>>> The readme.md that Peter is talking about is gfm markdown
>>>>> Well, technically it is just a CommonMark, as I am not using any github extensions.
>>>>> (Github uses CommonMarks and adds just couple small extensions.)
>>>>>
>>>>> Peter
>>>>>
>>>>
>>
> --
> www.tudorgirba.com
> www.feenk.com
>
> âLive like you mean it."
>
>
Aug. 14, 2017
Re: [Pharo-users] Honest question, new to ecosystem: are Glorp and Garage alive?
by Pierce Ng
On Mon, Aug 14, 2017 at 10:01:22AM -0300, Esteban A. Maringolo wrote:
> In the Glorp booklet these are the instructions to install SQLite:
Hi Esteban,
ConfigurationOfGlorpSQLite incorporates the manual steps that I used originally.
GlorpSQLite is also in Catalog Browser in Pharo 5.0 and 6.x.
Pierce
Aug. 14, 2017