I declared update function:
update : Action -> Model -> (Model, Effects.Effects Action)
And my model is:
model = Signal.foldp update init actions.signal
But Signal.foldp expects update function with Action -> Model -> Model shape.
Effect is POST call with code:
Http.post Json.Decode.string "http://localhost/tracker/link-admin/test.php" body
|> Task.toMaybe
|> Task.map SetShortenedUrl
|> Effects.task
I have two questions:
how can I run effect from update?
how can I change my model to type check?
Thanks!
If you're going to be using Effects without StartApp, at some point you'll need to use Effects.toTask, which requires a mailbox that can accept a list of actions.
Note that the documentation of Effects.toTask warns away from its explicit use except by expert hands (full disclosure: I am no expert) and at most once per app, but I'm going to pick it apart here anyways as an exercise in understanding StartApp.
Using the StartApp source code as our guide, we can pick out just the bare minimum we need to get your example running. First off, we have to deal with the pesky mailbox problem by defining a second mailbox called messages that takes a list of actions.
messages =
Signal.mailbox []
Now, we have to redefine your actions mailbox in terms of this new messages mailbox, using only the first element of the list, or NoOp in case of an empty list. (Read up on Effects.toTask to understand why it is a list, and why we're ok in this example to treat it as a singleton list)
actions : Signal.Mailbox Action
actions =
let
singleton action =
[action]
fromList list =
case list of
[] -> NoOp
(action::_) -> action
in
{ address = Signal.forwardTo messages.address singleton
, signal = Signal.map fromList messages.signal
}
We'll need a new Signal to deal with Effects so we can split out the processing in the update function from the tasks which we'll soon send to a port. Again, picking snippets from StartApp, we can define the following signal
effectsAndModel : Signal (Model, Effects.Effects Action)
effectsAndModel =
let
updateJustModel action (model,_) = update action model
in
Signal.foldp updateJustModel init actions.signal
Now that we have the above effectsAndModel signal, we can now redefine your model signal by just peeling off the Model from the first element of the tuple.
model : Signal Model
model =
Signal.map fst effectsAndModel
Likewise, we can use the effectsAndModel signal to send the tasks out a port by peeling off the Effects from the second element of the tuple:
port tasks : Signal (Task.Task Effects.Never ())
port tasks =
Signal.map (Effects.toTask messages.address << snd) effectsAndModel
The above port, tasks is where the Effects.toTask happens which required the redefining of your actions mailbox.
I've created a simple working example with an ad hoc view method to verify your requirements at this gist. I changed it to look for a local test.txt file rather than your url.
Related
Use Case
Android fragment that consumes items of T from a ReceiveChannel<T>. Once consumed, the Ts should be removed from the ReceiveChannel<T>.
I need a ReceiveChannel<T> that supports consuming items from it. It should function as a FIFO queue.
I currently attach to the channel from my UI like:
launch(uiJob) { channel.consumeEach{ /** ... */ } }
I detach by calling uiJob.cancel().
Desired behavior:
val channel = Channel<Int>(UNLIMITED)
channel.send(1)
channel.send(2)
// ui attaches, receives `1` and `2`
channel.send(3) // ui immediately receives `3`
// ui detaches
channel.send(4)
channel.send(5)
// ui attaches, receiving `4` and `5`
Unfortunately, when I detach from the channel, the channel is closed. This causes .send(4) and .send(5) to throw exceptions because the channel is closed. I want to be able to detach from the channel and have it remain usable. How can I do this?
Channel<Int>(UNLIMITED) fits my use case perfect, except that is closes the channel when it is unsubscribed from. I want the channel to remain open. Is this possible?
Channel.consumeEach method calls Channel.consume method which has this line in documentation:
Makes sure that the given block consumes all elements from the given channel by always invoking cancel after the execution of the block.
So the solution is to simply not use consume[Each]. For example you can do:
launch(uiJob) { for (it in channel) { /** ... */ } }
You can use BroadcastChannel. However, you need to specify a limited size (such as 1), as UNLIMITED and 0 (for rendez-vous) are not supported by BroadcastChannel.
You can also use ConflatedBroadcastChannel which always gives the latest value it had to new subscribers, like LiveData is doing.
BTW, is it a big deal if you new Fragment instance receives only the latest value? If not, then just go with ConflatedBroadcastChannel. Otherwise, none of BroacastChannels may suit your use case (try it and see if you get the behavior you're looking for).
Is it possible to create a Cmd that sends no message on completion in Elm?
Specifically, I am trying to have an element grab the focus (programmically), but I don't need to be informed of the result:
Dom.focus "element-id"
|> Task.attempt FocusReceived
...
FocusReceived result ->
model ! [] -- result ignored
Is there any way to just have the Elm "engine" not send a message after this Cmd?
I know that my code (FocusReceived result -> model ! []) is a no-op, but I would like the message not to be sent at all.
No, a Msg is always required. It is a common idiom in typical Elm projects to include a Msg type constructor that does nothing named NoOp.
type Msg
= NoOp
| ...
The update function does what FocusReceived in your example does, namely, nothing.
case msg of
NoOp ->
model ! []
...
I just started using Laika for doing some TDD on my Meteor app. Though, I would like to do some integration tests, as unit tests isn't that valuable to me.
Can I do some screen capturing using PhantomJS through Laika? E.g. I want to click html links and select elements by class/id.
I have a basic (unit) test in coffee:
# tests/players_test.coffee
assert = require 'assert'
suite 'Players', ->
test 'in the server', (done, server) ->
server.eval ->
Players.insert title: 'hello there'
players = Players.find().fetch()
emit('players', players)
server.once 'players', (players) ->
assert.equal 1, players.length
done()
I would like to convert this into a integration test by using an client (added next to (done, server) in the test function) and then manually selecting tags and clicking links, filling in name etc., clicking e.g. 'register', and then checking if that user is to be found in the database.
Thanks!
Yes you can do this.
suite 'Players', ->
test 'in the server', (done, server, client) ->
client.eval ->
// get access to a DOM element (can optionally use jQuery instead)
player = document.querySelector("[data-test='player']")
// now we can call functions on the element
player.value = "Joe blogs"
player.click()
// if you know the element won't exist in the DOM yet use waitForDOM
waitForDOM "[data-test='something-else']", ->
// perform some logic now that the element exists in the DOM
emit('players', players)
server.once 'players', (players) ->
assert.equal 1, players.length
done()
You might also want to check out evalSync here: http://arunoda.github.io/laika/syntax-sugar.html
This lets you write your tests in a synchronous style. It's still being executed asynchronously but it means you don't have to wrap your head around all the different named triggers/subscriptions that the 'eval' tests have. Here are the basics of evalSync...
suite 'evalSync', ->
test 'evalSync for the win', (done, server, client) ->
client.evalSync ->
// perform some logic on the client
emit("return")
server.evalSync ->
// perform some logic on the server
emit("return")
done() // notice this is outside the 'evalSync'
As a side note, I recommend using "data-test" attributes on your elements (or some other custom data attribute). If you select by class or id in your tests and then later refactor your CSS/HTML you'll have to hunt down which classes/ids are being used by your CSS and which are being used by your tests. Using "data-test" makes it clear for you.
For a project, I have a complex master object that contains a number of subcomponents. Set up of these objects is controlled by a Constructor interface, which I bind to various lifecycle & workflow events, like so:
#grok.subscribe(schema.ICustomFolder, lifecycleevent.IObjectAddedEvent)
def setup_custom_folder(folder, event):
interfaces.IConstructor(folder).setup()
#grok.subscribe(schema.ICustomFolder, lifecycleevent.IObjectModifiedEvent)
def setup_custom_folder(folder, event):
interfaces.IConstructor(folder).update()
What I'd like to be able to do is test the Constructor methods without relying on the event handlers. I've tried doing this by creating objects directly to avoid the lifecycle events:
def test_custom_item_constructor(self):
master = createContent('model.master_object',
needed_attribute = 2
)
folder = createContent('model.custom_folder',
__parent__ = master
)
self.assertEqual(0, len(folder))
constructor = interfaces.IConstructor(folder)
constructor.setup()
self.assertEqual(2, len(folder))
The setup method creates a number of items inside the Custom_Folder instance, dependent on the provided attribute on the master object. However, this is hanging, which I think is due to neither object actually belonging to the site, so there's no acquisition of permissions. I can get this by changing the createContent on the master object to createContentInContainer and adding it to the appropriate part of the test site, but that triggers all of the lifecycle events, which end up doing the Constructor calls, which doesn't let me test them in isolation.
I've tried using mock objects for this, but that got messy dealing with the content creation that is meant to occur during the Constructor .setup.
What's the best way to approach this?
I'm not sure if this is the best way, but I managed to get the result I wanted by disabling the relevant event handlers first, and then creating the content properly within the site:
def test_custom_item_constructor(self):
zope.component.getGlobalSiteManager().unregisterHandler(
adapters.master.constructor.setup_masterobject,
required=[schema.IMasterObject, lifecycleevent.IObjectAddedEvent]
)
zope.component.getGlobalSiteManager().unregisterHandler(
adapters.custom.constructor.setup_customfolder,
required=[schema.ICustomFolder, lifecycleevent.IObjectAddedEvent]
)
master = createContentInContainer(self.portal, 'model.master_object',
needed_attribute = 2
)
folder = createContentInContainer(master, 'model.custom_folder',
__parent__ = master
)
self.assertEqual(0, len(folder))
constructor = interfaces.IConstructor(folder)
constructor.setup()
self.assertEqual(2, len(folder))
This was enough to disengage the chain of events triggered by the addition of a new master object.
I've a problem of session when I use lazy loading in the UI layer.
My piece of code (in the DAO layer)
public List<Visites> GetVisitesClientQuery(string idClient)
{
using (ISession session = Repository.TSession())
{
var results = (from v in session.Query<Visites>()
where v.Clients.Idclient == idClient
select v);
return results.ToList<Visites>();
}
}
I call it in the UI layer :
var visites = VisiteManager.Instance.GetVisitesClientQuery(lstClients.SelectedValue.ToString());
foreach (Visites v in visites)
{
foreach (Factures f in v.Factures)
{
...
}
}
v.Factures is a collection.
If I call it in the using it works (the session is opened) but in this case it doesn't work and I've this error.
Initializing[NHibernateTest.BusinessObjects.Visites#036000007935]-
failed to lazily initialize a collection of role:
NHibernateTest.BusinessObjects.Visites.Factures, no session or session was closed
Is it possible to handle a lazy loading call in the UI layer ?
The problem here, is that you handle your session management inside your repository (DAO layer), which isn't a good idea.
An ISession implementation in NHibernate represents a 'Unit Of Work'. A Unit Of Work needs to know the 'context' of the 'use-case' to be able to use this concept successfully.
Your repository however has no notion of the 'context' of the use case in which it (the repository) is used.
Therefore, it is not your DAO layer that should decide when to open an ISession, but it is your 'application layer' (or even your UI layer, if you do not have an Application Layer) which should do that , since that will be the layer that knows your context.
By doing so, you can indeed effectively use the Session as a UnitofWork. In order to save an entity, you'll have to use the same session to save the entity, as the session that you've used to load that entity. (Otherwise, you'll need to 'lock' the entity into the session).
Next to that, it will also solve your lazy loading problem. :)
You should eager load the collections you know you will be using with
session.Query().Fetch(x=>x.Factures).
That will load all Visites with the Factures.