Managing Resources with FRP in Scala RX - scala

I'm using scala rx for an application. I have a reactive variable holding a File (which is a PDF file). I'm using a library to render pages from this pdf file to the screen. Now the PDF library I'm using gives me an object (let's call it Doc), which I can use to render single pages. But in order to render a page from a Doc object, the Doc object must be opened (thus the resource must be acquired).
Right now I'm loading the pdf file for each page I'm rendering anew (creating a new Doc object and closing it after rendering the single page). This makes the rendering of the page functional (given a file and a page number, return an image).
Is there a way to cling to an opened resource and close it on change in FRP in general, and for scala rx in particular? How would one handle this very common situation?

You can simply enclose the Doc object. So instead of render being
def render(file: File, pageNumber: Int): Image = // blah blah blah
change it to:
def open(file: File): (Int => Image) = {
val doc = // call your library to read file
(x: Int) => doc.getPage(x)
}
and then pass the function open returns to whatever page change signal you're reacting to.
Edit: Oh, I see, so you're saying you want it to close the file whenever the file:File signal changes to be a different file. In that case you should be able do something like this:
def pageGetterRx(file: Rx[File]): Rx[Int => Image] = {
val doc: Var[Doc] = Var(null)
val o = file.foreach { f =>
Option(doc()).foreach(_.close)
doc() = PdfLib.read(f) // or however you do the reading
}
Rx {
(x: Int) => doc().getPage(x)
}
}
Edit 2: To clarify, if you impose a "assemble network of functions phase / run the network on some signal(s) phase" distinction on FRP, the above function would be called only once; in the assemble phase. To say it another way, pageGetterRx (a lousy name, I'm fully aware) doesn't participate in a FR way, instead it returns a signal of lambdas that each close over one particular file and return pages from it.

Related

How to access the child nodes in a device tree (DTS) in Zephyr using DT_FOREACH_CHILD

I'm developing an application for an nRF52 SoC to access some external devices, kind of detectors in this case, so I have defined a custom format (and its corresponding yaml file) for my device access description node. It is kind of:
n: detectors {
compatible = "foo-detectors";
// Definition of first channel
det0: det_0 {
irq-pins = <13 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Bar detector channel 1";
};
// Definition of second channel
det1: det_1 {
irq-pins = <17 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Bar detector channel 2";
};
};
Suppose I have a structure definition for holding data about the pins to which those devices are physically connected, say:
struct foo_detector_desc {
int irqpin;
int irqpin_flags;
}
Using Zephyr macros, I can use from my code the individual values for those nodes. For instance, DT_PROP_BY_IDX(DT_NODELABEL(det0), irq_pins, 0) expands to 13, DT_PROP_BY_IDX(DT_NODELABEL(det0), irq_pins, 1) expands to the value of OR'ed flags GPIO_PULL_UP | GPIO_ACTIVE_LOW.
But I don't want to create a ten-page code full of conditionals in the form DT_NODE_EXISTS(DT_ALIAS(det#)), but something more compact, flexible and maintainable.
I came across Zephyr macro DT_FOREACH_CHILD that is intended for being used in that scenario with which I can create a list with the labels, as showcased in the example embedded in the documentation:
#define LABEL_AND_COMMA(node_id) DT_LABEL(node_id),
const char *child_labels[] = {
DT_FOREACH_CHILD(DT_NODELABEL(n), LABEL_AND_COMMA)
};
Trying to use that for filling a static structures array, I tried following code but, yet it expands to two elements in the array, the structure fields are not initialised with desired values.
#define PIN_INFO_AND_COMMA(node_id) \
{ \
.pin=DT_PROP_BY_IDX(node_id, irq_pins, 0),\
.flags=DT_PROP_BY_IDX(node_id, irq_pins, 1),\
},
const struct detector_data _det_data[] = {
DT_FOREACH_CHILD(DT_NODELABEL(n), PIN_INFO_AND_COMMA)
};
I'm using Visual Studio Code with the nRF Connect plugin.
Is there a way to generate/see how those macros expand when compiled?
What is the correct way for initialising the structure fields (pins, flags)?
BR
It is possible to expand the macros one level at a time in VS Code. It is needed to click over the macro, then select it by double-clicking on it, and a bulb light will, eventually, show up. The option Insert Macro will replace the macro with its expansion.
Regarding using the DTS in code, the code I wrote in my previous post is OK, but I think I found a bug in the debugger. If I don't include any code using the value of _det_data, the debugger doesn't say the constant was optimised-out and displays wrong values when inspecting its content. That made me waste a lot of time.
For anyone interested on using DTS files for nRF devices in Zephyr, I posted all details in a thread in Nordic's developers forum (here).
BR

Repopulating form fields in Play Framework

Using the Play Framework (2.6) documentation; I am attempting to handle form submission. But I'm running into an issue in repopulating the form fields - which is what I want to do if it has errors (so users can edit their entry rather than having to re-enter).
newForm.bindFromRequest.fold(
errorForm => {
BadRequest(views.html.form(errorForm))
},
formData => {
val oWriteJso = Json.toJsObject(formData) match {
case x if(x.fields.nonEmpty) => getCreatedFieldValues(x)
case _ => None
}
val oRes = Redirect(routes.Application.index).flashing("success" -> "Entry saved!")
apiC.writeAndRedirect("c", collName, None, oWriteJso)(oRes)(request)
}
)
My issue is that the example in the documentation only shows how to pass errorForm directly to a form template (e.g. views.html.form) rather than being able to render the whole page again (i.e. using views.html.index or a Redirect) with the input form fields being populated from the previous request. I found this answer as the closest to this issue but it is a little old and I am using Scala so wasn't able to implement it. Just have no idea how anyone else is doing this or what the sensible, standard approach is. Thanks for any light on this_
If you use the Play helper functions for generating input tags in your view file. They should populate with your values from the last request.
For example you can create an HTML form in your view file using helper methods like this:
#helper.form(action = routes.Application.userPost()) {
#helper.inputText(userForm("name"))
#helper.inputText(userForm("age"))
}
You can take a look at Play documentation about helpers in view files at the following link:
https://www.playframework.com/documentation/2.7.x/ScalaForms#Showing-forms-in-a-view-template

Do Flask/Jinja2 have a provision to save rendered templates during debugging?

While debugging it's useful to view the rendered HTML and JS templates through a "view source" menu item in a browser, but doing so forces one to use the UI of the browser.
Does Jinja2 (or Flask) provide a facility to save the last n rendered templates on the server? It would then be possible to use one's favorite editor to view the rendered files, along with using one's familiar font-locking and search facilities.
It's of course possible to implement such a facility by hand, but doing so smacks too much like peppering one's programs while debugging with print statements, an approach that doesn't scale. I'm seeking a better alternative.
I'd think the easiest thing to do would be to use the after_request hook.
from flask import g
#main.route('/')
def index():
models = Model.query.all()
g.template = 'index'
return render_template('index.html', models=models)
#main.after_request
def store_template(response):
if hasattr(g, 'template'):
with open('debug/{0}-{1}'.format(datetime.now(), g.template), 'w') as f:
f.write(response.data)
return response
Here are the docs.
http://flask.pocoo.org/snippets/53/
As far as only collecting the last n templates I'd likely setup a cron job to do that. Here is an example
import os
from datetime import datetime
def make_files(n):
text = '''
<html>
</html>
'''
for a in range(n):
with open('debug/index-{0}.html'.format(datetime.now()), 'w') as f:
f.write(text)
def get_files(dir):
return [file for file in os.listdir(dir) if file.endswith('.html')]
def delete_files(dir, files, amount_kept):
rev = files[::-1]
for file in rev[amount_kept:]:
loc = dir + '/' + file
os.remove(loc)
if __name__ == '__main__':
make_files(7)
files = get_files('debug')
print files
delete_files('debug', files, 5)
files = get_files('debug')
print files
EDIT
reversed order of files inside the delete function so it will keep the most recent files. Also unable to find a way of accessing the original template name to avoid hardcoding.
EDIT 2
Alright so updated it to show how you can use flask.g to pass the template name to the after_request function
docs http://flask.pocoo.org/docs/0.11/testing/#faking-resources

Downloadable xml files in Play Framework

I am a Scala/PlayFramework noob here, so please be easy on me :).
I am trying to create an action (serving a GET request) so that when I enter the url in the browser, the browser should download the file. So far I have this:
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content)
}
What it does is basically show the XML in the browser (whereas I actually want it to download the file). Also, I have two problems with it:
I am not sure if using Play's templating "views.html..." is the best idea to create an XML template. Is it good/simple enough or should I use a different solution for this?
I have found Ok.sendFile in the Play's documentation. But it needs a java.io.File. I don't know how to create a File from HtmlFormat.Appendable. I would prefer to create a file in-memory, i.e. no new File("/tmp/temporary.xml").
EDIT: Here SepaCreditTransfer is a case class holding some data. Nothing special.
I think it's quite normal for browsers to visualize XML instead of downloading it. Have you tried to use the application/force-download content type header, like this?
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content).withHeaders("Content-Type" -> "application/force-download")
}

How can I get the body of an action as string

I have two actions. The first serves single assets and the second should serve all the single assets combined into one asset. So my idea was to call the first action which serves the single assets from the second action which serves the combined asset. Some of you will say this is a bad idea, because I could load the assets directly from file system and combine them. But this isn't possible, because the first action is chained with other actions to do some additional operations(Fingerprinting, ...) on a asset.
So here are my actions:
The first serves a single asset. In this implementation it calls only the next action in the chain.
abstract override def at(path: String, file: String): Action[AnyContent] = {
super.at(path, file)
}
The second accepts a list of files as JSON. Then it iterates over the list and calls the first action with a single file.
def consolidate = Action(parse.json) { request =>
val files = request.body.as[List[String]]
for (file <- files) {
val action = at(path, new URL(file).getPath.substring(1))
val result = action.apply(request)
}
Ok()
}
Now my problem is, how can I get the asset as string? The variable result contains a Iteratee[Array[Byte], Result]. How can I extract the asset data from it?
The play.api.test.Helper object contains a contentAsString and a contentAsBytes method. But this doesn't help me further!
The Iteratee represents the consumer side of the action. There is no documentation for the method returning the Iteratee but I'm guessing it will be used to consume the input stream of data from the client during a request. So, in short, it won't help you here.
You could take the code from the first action, the one you need to invoke in consolidate and factor it out into some method, and use that method in both actions. Is that feasible for you ?