Building a Julia module to get information about spaceships

Posted on Tue 06 October 2020 in julia

Package creation

Still a bit hazy on how to do this - more details here. I especially like the sound of develop so included that on the basis that it was probably good. Simple, straightforward names seem to be a thing in Julia, so I took a punt as well as Codecov which sounded pretty benign.

In [5]:
using PkgTemplates

t = Template(;
    user="scrambldchannel",
    dir="/home/alex/git/",
    plugins=[
        Git(; manifest=true, ssh=true),
        Codecov(),
        Develop(),
    ],
); # semi colons suppress output, it's a matlab thing apparently

Build package scaffold from template

This uses the template to create a new package based on the the details supplied.

In [11]:
t("SWAPI")
┌ Info: Running prehooks
└ @ PkgTemplates /home/alex/.julia/packages/PkgTemplates/ZhTWM/src/template.jl:130
┌ Info: Running hooks
└ @ PkgTemplates /home/alex/.julia/packages/PkgTemplates/ZhTWM/src/template.jl:130
 Activating environment at `~/git/SWAPI/Project.toml`
   Updating registry at `~/.julia/registries/General`
    
   Updating git-repo `https://github.com/JuliaRegistries/General.git`

   Updating `~/git/SWAPI/Project.toml`
 [no changes]
   Updating `~/git/SWAPI/Manifest.toml`
 [no changes]
 Activating environment at `~/.julia/environments/v1.4/Project.toml`
┌ Info: Running posthooks
└ @ PkgTemplates /home/alex/.julia/packages/PkgTemplates/ZhTWM/src/template.jl:130
Path `/home/alex/git/SWAPI` exists and looks like the correct package. Using existing path.
  Resolving package versions...
   Updating `~/.julia/environments/v1.4/Project.toml`
  [a10bf705] + SWAPI v0.1.0 [`~/git/SWAPI`]
   Updating `~/.julia/environments/v1.4/Manifest.toml`
  [a10bf705] + SWAPI v0.1.0 [`~/git/SWAPI`]
┌ Info: New package is at /home/alex/git/SWAPI
└ @ PkgTemplates /home/alex/.julia/packages/PkgTemplates/ZhTWM/src/template.jl:140

Using Revise

In theory this should let me hack on my module and reflect changes as I go but it's easier to enabled it by default by adding a couple of config files in ~/.julia/config. Note, I got stuck here for a while before realising I wasn't on >= 1.5 - see here for details

In [13]:
using Pkg
In [27]:
# It was at about this point that I started giving up managing the environment in a notebook and moved to the REPl

Pkg.add("Revise")
  Resolving package versions...
   Updating `~/.julia/environments/v1.4/Project.toml`
  [295af30f] + Revise v2.7.6
   Updating `~/.julia/environments/v1.4/Manifest.toml`
  [6f1432cf] + LoweredCodeUtils v0.4.9
  [295af30f] + Revise v2.7.6
In [28]:
using Revise

You really want to run it automatically if developing

Do it before using the package you want to develop if doing manually.

_
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.4.1
 _/ |\__'_|_|_|\__'_|  |  Ubuntu ⛬  julia/1.4.1+dfsg-1
|__/                   |

~/.julia/config/startup.jl

atreplinit() do repl
    try
        @eval using Revise
        @async Revise.wait_steal_repl_backend()
    catch e
        @warn(e.msg)
    end
end

~/.julia/config/startup_ijulia.jl

try
    @eval using Revise
catch e
    @warn(e.msg)
end

Simple module overview

SWAPI/src/SWAPI.jl

module SWAPI

    export request_data

    include("request.jl")

end

SWAPI/src/request.jl

using HTTP
using JSON

function request_data(url)
    response = HTTP.get(url)
    return JSON.print(JSON.parse(response), 4)
end

Local packages can be loaded like this

In [34]:
Pkg.add(PackageSpec(path="/home/alex/git/SWAPI"))

   Updating git-repo `/home/alex/git/SWAPI`
  Resolving package versions...
   Updating `~/.julia/environments/v1.4/Project.toml`
  [61ae70ba] ~ SWAPI v0.1.0 [`~/.julia/dev/SWAPI`] ⇒ v0.1.0 #master (/home/alex/git/SWAPI)
   Updating `~/.julia/environments/v1.4/Manifest.toml`
  [61ae70ba] ~ SWAPI v0.1.0 [`~/.julia/dev/SWAPI`] ⇒ v0.1.0 #master (/home/alex/git/SWAPI)

Developing modules workflow

This seemed a bit counter intuitive for me, it clones the repo somewhere else. I'm not sure whether this is actually necessary. Surely I can just edit it in its existing repository?

In [35]:
Pkg.develop("SWAPI")
Path `/home/alex/.julia/dev/SWAPI` exists and looks like the correct package. Using existing path.
  Resolving package versions...
   Updating `~/.julia/environments/v1.4/Project.toml`
  [61ae70ba] ~ SWAPI v0.1.0 #master (/home/alex/git/SWAPI) ⇒ v0.1.0 [`~/.julia/dev/SWAPI`]
   Updating `~/.julia/environments/v1.4/Manifest.toml`
  [61ae70ba] ~ SWAPI v0.1.0 #master (/home/alex/git/SWAPI) ⇒ v0.1.0 [`~/.julia/dev/SWAPI`]

Note path to SWAPI has changed

In [36]:
Pkg.status()
Status `~/.julia/environments/v1.4/Project.toml`
  [cd3eb016] HTTP v0.8.19
  [7073ff75] IJulia v1.21.4
  [682c06a0] JSON v0.21.1
  [14b8a8f1] PkgTemplates v0.7.10
  [295af30f] Revise v2.7.6
  [61ae70ba] SWAPI v0.1.0 [`~/.julia/dev/SWAPI`]
In [37]:
# some warnings here, I need to update the deps for the package

using SWAPI
┌ Info: Precompiling SWAPI [61ae70ba-0cff-4632-b6d2-2c7df9ba8302]
└ @ Base loading.jl:1260
┌ Warning: Package SWAPI does not have HTTP in its dependencies:
│ - If you have SWAPI checked out for development and have
│   added HTTP as a dependency but haven't updated your primary
│   environment's manifest file, try `Pkg.resolve()`.
│ - Otherwise you may need to report an issue with SWAPI
└ Loading HTTP into SWAPI from project dependency, future warnings for SWAPI are suppressed.
WARNING: Method definition request_planet(Any) in module SWAPI at /home/alex/.julia/dev/SWAPI/src/request_person.jl:4 overwritten at /home/alex/.julia/dev/SWAPI/src/request_planet.jl:4.
  ** incremental compilation may be fatally broken for this module **

Now we can get Han Solo baby

In [52]:
response = SWAPI.request_data("https://swapi.dev/api/people/14/")
JSON.print(JSON.parse(String(response.body)), 4)
{
    "skin_color": "fair",
    "eye_color": "brown",
    "edited": "2014-12-20T21:17:50.334000Z",
    "films": [
        "http://swapi.dev/api/films/1/",
        "http://swapi.dev/api/films/2/",
        "http://swapi.dev/api/films/3/"
    ],
    "starships": [
        "http://swapi.dev/api/starships/10/",
        "http://swapi.dev/api/starships/22/"
    ],
    "name": "Han Solo",
    "height": "180",
    "birth_year": "29BBY",
    "mass": "80",
    "vehicles": [],
    "created": "2014-12-10T16:49:14.582000Z",
    "hair_color": "brown",
    "url": "http://swapi.dev/api/people/14/",
    "species": [],
    "gender": "male",
    "homeworld": "http://swapi.dev/api/planets/22/"
}

Functions namespace seems to be implicit

This call gives the same result as the previous one. Presumably there are precedence rules in cases of namespace clashes. Anyway, let's look at old Ben's stats.

In [53]:
response = request_data("https://swapi.dev/api/people/10/")
JSON.print(JSON.parse(String(response.body)), 4)
{
    "skin_color": "fair",
    "eye_color": "blue-gray",
    "edited": "2014-12-20T21:17:50.325000Z",
    "films": [
        "http://swapi.dev/api/films/1/",
        "http://swapi.dev/api/films/2/",
        "http://swapi.dev/api/films/3/",
        "http://swapi.dev/api/films/4/",
        "http://swapi.dev/api/films/5/",
        "http://swapi.dev/api/films/6/"
    ],
    "starships": [
        "http://swapi.dev/api/starships/48/",
        "http://swapi.dev/api/starships/59/",
        "http://swapi.dev/api/starships/64/",
        "http://swapi.dev/api/starships/65/",
        "http://swapi.dev/api/starships/74/"
    ],
    "name": "Obi-Wan Kenobi",
    "height": "182",
    "birth_year": "57BBY",
    "mass": "77",
    "vehicles": [
        "http://swapi.dev/api/vehicles/38/"
    ],
    "created": "2014-12-10T16:16:29.192000Z",
    "hair_color": "auburn, white",
    "url": "http://swapi.dev/api/people/10/",
    "species": [],
    "gender": "male",
    "homeworld": "http://swapi.dev/api/planets/20/"
}

But what about the spaceships?!?

Sorry, I kinda teased you with that x-wing clickbait. I've managed to mess up this notebook. Working with julia's built in package manager isn't really working for me in there and jumping between too many windows was making my head spin...

In [ ]: