How in node to access pug attributes in javascript via a script include

If you found this article then you have a very specific kinda problem, just like I did.

I have a node app on which I am running express to serve pages, i’m also wanting to use Pug to organise my ‘views’.  All of this works fine until you throw javascript into the mix.

Sure, you could just embed the code directly in your .pug page, but that’s not very tidy now is it.

//index.pug
doctype html
  html(lang="en")
    head
    script.
      var foo = bar;
      console.log(foo);
      

Note that there is a . at the end of script.. This allows you to write code directly in the template, rather than have pug evaluate everything within it.

For my purposes I want to keep js away from the template to keep things neat and tidy so it needs to be kept in a specific .js file.

Including a script file in pug/jade is quite simple:

script
  include ./assets/js/tidy.js

But then how would you reference the pug attributes that you have access to within index.pug in the external javascript file?

The first change I had to made was in my app.js file where I was passing in the data to the .pug template. Originally I was passing just the object (so I could then reference the attributes directly) but this makes things tricky for the next step, so I changed the object I was passing in so that I could pick up the data by name.

//app.js
app.get('/product_options', (req, res) => {
  Shopify.get_product_options(req.query.pid).then(product =>
    res.render('product_options', {product: product})
  );
});

This means that I can reference product from my template directly, which will be handy when we stringify its content in order to pass it over to the js file.

//index.pug
doctype html
html(lang="en")
  head
    script.
      var l = !{JSON.stringify(product)}
    script  
      include ../assets/js/product_options.js

Note the !{} interpolation syntax, rather than the standard {}, this prevents any escaping and allows us to take the string value of the products option and write it out to the page in the var l = assignment.

We should now be able to reference l from any external js.

//tidy.js
$( document ).ready(function(){
  console.log(l);
});

It feels like there should be a better way of handling this, so if you know of one please leave a comment and let me know. It might not be pretty but it achieves good separation and allows you to benefit from pugs, otherwise, excellent templating.

Python Multi-Part Post files and data

Sending data and a file using python, to a rest api?  Then you’ll need a multipart post request and this post might save you a little bit of time!

I’ll show you how to construct a basic script that will allow you to send both form data and an arbitrary file (in this case a csv), and point out where I initially had a few issues.

The finished article:

import requests

file = { 'file' : ('csvtest.csv', open('csvtest.csv', 'rt'), 'text/csv') }
payload = { 'parser' : '{object trimmed for comment}' } 
headers = { 'accept': "application/json", 'authorization': "Basic ZGxhZG1pbjp0aGlua2JpZw==", } 
url = "http://localhost:8400/proxy/v1/schema-discovery/hive/sample‌-file" 
req = requests.post(url, data=payload, files=file, headers=headers) pprint(req.text) 

Lets go through that part by part:

import requests

This makes the ‘requests‘ library available to Python.  Its very similar in nature to the http.client package, but has additional attributes that you can call that make putting together a multipart request a lot more simple.

file = { 'file' : ('csvtest.csv', open('csvtest.csv', 'rt'), 'text/csv') }

This constructs a tuple with the name of the file, the file contents, and the file type.  In a lot of the documentation I found the name and type were optional, but for the api I was using I needed to specify both for it to be accepted.

One additional thing to note is the open() method.  Again, most tutorials I found suggested opening in binary mode ‘rb’, but in order to get the text to appear in the request body within the multipart request I needed it setting as text (‘rt’).

payload = { 'parser' : '{object trimmed for comment}' }

This the data I wanted to send alongside the file. The parser value was a huge json string, so i’ve trimmed it here for readability.

headers = { 'accept': "application/json", 'authorization': "Basic ZGxhZG1pbjp0aGlua2JpZw==", }

The thing to note here is the absence of the ‘content-type’ header. By supplying the header it seems to confuse things, as in the last step where we make the post request we specify both file and data which is recognised and the relevant headers built for us when the request is build.

url = "http://localhost:8400/proxy/v1/schema-discovery/hive/sample‌-file"

The URI for the Rest API I am trying to hit.

req = requests.post(url, data=payload, files=file, headers=headers)

This is the basic construct for the request.  The verb (“post”) is set as a method (although you can call this in a different way).  We add by name the data, files and headers and make the request.

The package then creates the request body and sets the relevant multipart boundaries and inserts the relevant data.

I believe that you can pass an array of files into the files argument although I have not tested that here.

Hopefully this will save someone the time it took me to figure it out.