Data files
A model described by data (a machine’s gears, a rack’s slots) reads it from a JSON file. Data nested deeper than a parameter can hold belongs here.
# machine.json: {"name": "braider", "gears": [{"module": 1.5, "teeth": 12}, {"module": 2, "teeth": 20}]}import machine from "./machine.json"
gears = machine[:gears].map { |g| cylinder(r: g[:module] * g[:teeth] / 2, h: 5) }first = machine.dig(:gears, 0, :teeth) # 12plate = machine.fetch(:plate, nil) # nil: the file has no plategearsimport_json
Section titled “import_json”import_json(path)import_json(path, keys)Reads the JSON file at path and returns its value. With keys, an Array of key names, it
returns the values of those keys of the file’s object, in an Array in the same order.
| Parameter | Type | Default | Description |
|---|---|---|---|
path |
String | — | The file, from the project’s directory. |
keys |
Array | — | Keys of the file’s top-level object, as Symbols or Strings. |
Returns: the file’s value (what a file becomes), or an Array of the
values of keys.
Raises: ImportError when the file can’t be read or isn’t valid JSON, or when keys is
given and the file isn’t an object; KeyError for a key the object doesn’t have.
values = import_json("data/machine.json", [:name, :gears])puts values[0] # braiderImporting a JSON file
Section titled “Importing a JSON file”import name from "./file.json"import { key, other as alias } from "./file.json"import name from "./file.data" with { type: "json" }An import of a file whose name ends in .json reads it as data instead of running it as a
module. Its path is relative to the file that imports it, as a module’s is, and a package’s own
files are imported by its alias (import screws from "hardware/screws.json"). import_json
reads from the project’s directory instead. with { type: "json" } (or :json) reads a file
named otherwise. See Imports and exports for the rest of the
syntax.
Keys in braces bind keys of the file’s object to names of their own, as renaming one:
import { name, gears as gear_list } from "./machine.json"puts "#{name}: #{gear_list.size} gears" # braider: 2 gearsA key the object lacks is a KeyError listing the keys it has. A file that isn’t an object
(a list of parts, [...]) is an ImportError: import it whole, without braces.
What a file becomes
Section titled “What a file becomes”| JSON | KupCAD |
|---|---|
| object | JsonObject |
| array | Array |
| number | Number |
| string | String |
true, false |
true, false |
null |
nil |
This holds at any depth: the objects in an array are JsonObjects too. The file’s top level may be any of these, so a file that is a list gives an Array:
# gears.json: [{"teeth": 12}, {"teeth": 20}]import gears from "./gears.json"puts gears[1][:teeth] # 20Reading a JsonObject
Section titled “Reading a JsonObject”A JsonObject is read-only, and finds each key by its String or by the Symbol of the same name:
machine["gears"] and machine[:gears] are the same entry. There is no dot access
(machine.gears), since a key named size or keys would be hidden by the method of that name.
[]givesnilfor a key the object doesn’t have, as a Map’s does.fetch(key)anddig(key, ...)raise instead, naming the place in the file and what is there, so a missing key shows where it is missing rather than as anilfar from the file:KeyError: machine.json: gears[1] has no key "pitch" (its keys: module, teeth).fetch(key, default)gives the default for a missing key.to_hgives a Map that can be changed, its objects Maps and its Arrays copies, all the way down, keyed by the file’s Strings.
Assigning to a key (machine[:name] = "x") is a TypeError. The
JsonObject page lists every method.
Limits
Section titled “Limits”Each of these is an ImportError naming the file and the line and column, or the place in the
data:
- a file larger than 64 MiB;
- arrays and objects nested deeper than 256 levels;
- a number past the largest (
1e400); - a key written twice in one object;
- anything that isn’t valid JSON, such as a trailing comma.
Rebuilds
Section titled “Rebuilds”kupcad watch and the editor’s preview watch the data files a run read, along with its
modules: a change to one runs the scripts that read it again. A file that was missing at the
last run is watched too, so creating it rebuilds.