1 | Gaia = require("lin").Gaia
|
2 | which = require("which")
|
3 | _ = require("massagist")._
|
4 | Massage = require("massagist").Massage
|
5 | points = require("./points")
|
6 | phase = require("./phase")
|
7 |
|
8 |
|
9 | class Ephemeris
|
10 |
|
11 | # The defaults-overriding options will become part of
|
12 | # the help command, the man-pages / docs and the gh-pages.
|
13 | # For example the `-o` that overrides `@settings.out` can be:
|
14 | #
|
15 | # * `points` (phase is based on it)
|
16 | # * `phase` (your CLI Formatting Friend)
|
17 | # * `points,table` (raw cliff output of points)
|
18 | # * `json` (the precious json)
|
19 | # * `json,codedown` (markdown code block html)
|
20 | # * `print` (python's print)
|
21 | # * `pprint` (python's pretty-substitutes swe labels)
|
22 | # * `inspect` (the pretty eyes thing)
|
23 | # * see `Massage` for more options...
|
24 |
|
25 | defaults:
|
26 | "root": "#{__dirname}/../"
|
27 | "out": "json"
|
28 | "time": null
|
29 | "geo": {"lat": null, "lon": null}
|
30 | "stuff": [ [0, 1, 2, 3]
|
31 | , [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 15, 17, 18, 19, 20]
|
32 | , [136199, 7066, 50000, 90377, 20000]
|
33 | ]
|
34 | "houses": "W"
|
35 |
|
36 |
|
37 | # Configure ephemeris specifics, this is valid precious input.
|
38 | configure: (specifics, cb) ->
|
39 | @specifics = specifics if specifics?
|
40 | @settings = _.allFurther @defaults, @specifics
|
41 |
|
42 | unless @settings.data?.match /^\//
|
43 | # If not absolute, then relative (to eden) ephemeris data path.
|
44 | @settings.data = "#{@settings.root}#{@settings.data}"
|
45 |
|
46 | # The @settings.ut and @settings.geo - being reset.
|
47 | @gaia = new Gaia @settings["geo"], @settings["time"]
|
48 | @settings.geo = {}
|
49 | @settings.geo.lat = @gaia.lat
|
50 | @settings.geo.lon = @gaia.lon
|
51 | @settings.ut = @gaia.ut
|
52 |
|
53 | # The @ephemeris is a function that implements
|
54 | # [precious json](http://astrolet.github.com/precious/json.7.html) -
|
55 | # input & output conforming to the spec. It is optional,
|
56 | # as we may just want to use this class for generating ephemeris input -
|
57 | # the output results being deferred for later perhaps.
|
58 | # It's only set once, as it isn't expected to change.
|
59 | unless @ephemeris?
|
60 | if _.isString @settings.precious
|
61 | @ephemeris = require(@settings.precious).ephemeris
|
62 |
|
63 | # This can be called in various contexts. Example: the callback can be used
|
64 | # when the ephemeris paths are not immediately known (a global precious).
|
65 | # It's passed via the `@constructor` / `@preciousPaths`
|
66 | # - see the following sections...
|
67 | cb() if cb?
|
68 | @
|
69 |
|
70 |
|
71 | # Because precious is not a dependency, nor is gravity,
|
72 | # get the paths from a global precious install.
|
73 | preciousPaths: (cb) ->
|
74 | which "precious", (er, thing) =>
|
75 | if er?
|
76 | # TODO: handle precious not installed
|
77 | console.error(er.message)
|
78 | @defaults.precious = null
|
79 | else
|
80 | apath = thing.substring 0, thing.lastIndexOf '/'
|
81 | apath += '/../lib/node_modules/precious/'
|
82 | @defaults.data = apath + 'node_modules/gravity/data/'
|
83 | @defaults.prep = apath + 'bin/'
|
84 | @defaults.precious = apath + 'index.js'
|
85 | cb()
|
86 |
|
87 |
|
88 | # Pass a callback if you need to call @run *immediately*, or something...
|
89 | constructor: (specifics = {}, cb) ->
|
90 | switch specifics.precious
|
91 | # We don't want to call precious and don't care where may be installed.
|
92 | # Nothing special to do here. This is how we use eden to get
|
93 | # json input configuration for perhaps later calling precious with.
|
94 | when false then ;
|
95 | # Precious manually installed locally in `./node_modules`.
|
96 | # Takes one's `specifics` word for it, without checking.
|
97 | # This may be how things will be done in the future -
|
98 | # by default if precious becomes a dependency.
|
99 | when true
|
100 | apath = './node_modules/'
|
101 | @defaults.data = 'precious/node_modules/gravity/data/'
|
102 | @defaults.prep = '.bin/'
|
103 | @defaults.precious = 'precious/index.js'
|
104 | specifics.precious = undefined # so that the default takes
|
105 | # Undefined may become null. Unknown what the paths are.
|
106 | # Expecting a global precious install. There may be none, which is null.
|
107 | # Null is bad - because undefined implies intent to use precious.
|
108 | when undefined
|
109 | return @preciousPaths => @configure specifics, cb
|
110 | # Unless all the paths have been given as specifics, throw an error.
|
111 | # Of-course, it's assumed that the paths are valid.
|
112 | # More about completeness than intended use.
|
113 | else
|
114 | unless _.isString specifics.precious
|
115 | throw "Invalid precious specifics!"
|
116 |
|
117 | return @configure specifics, cb
|
118 |
|
119 |
|
120 | # A way to change just the output format, with possible method-chaining.
|
121 | out: (treats) ->
|
122 | @settings.out = treats
|
123 | @
|
124 |
|
125 | # Just for processing *points* output as json (at this point).
|
126 | # It also sets up the *points* json for *phase*.
|
127 | pre: (stream) ->
|
128 | process = true
|
129 | the_points = "points"
|
130 | becoming = "json"
|
131 | # This is so that the_points can be passed as JSON to `Massage` further.
|
132 | if _.isArray(@settings.out) and @settings.out[0] is the_points
|
133 | [process, @settings.out[0]] = [the_points, becoming]
|
134 | # Only the data changes for the following, `@settings.out` remains as is.
|
135 | else if _.include [the_points, "phase"], @settings.out
|
136 | process = the_points
|
137 |
|
138 | # Specific processing (e.g. points), or else return the same stream
|
139 | # without changing anything at all.
|
140 | if process is the_points
|
141 | points stream
|
142 | else
|
143 | stream
|
144 |
|
145 |
|
146 | # This is the reason `Ephemeris` exists, though running it sometimes just gets
|
147 | # the `@settings`, so that the precious ephemeris can perhaps later be invoked
|
148 | # conveniently with - or else simply for whatever reason - besides any bugs...
|
149 | run: (stream) ->
|
150 | # However, Ephemeris can't always be `@run` nor it is necessarily desirable.
|
151 | if @settings.precious is false
|
152 | # Note: this isn't fit for rerun as `@settings` are being changed...
|
153 | if _.isString(@settings.out) and @settings.out isnt "json"
|
154 | @settings.out = ["json", @settings.out]
|
155 | # Clean-up the `@settings` for later use.
|
156 | delete @settings[key] for key in ['root', 'data', 'precious']
|
157 | if _.isArray @settings.out
|
158 | massage = new Massage @settings.out
|
159 | massage.write JSON.stringify(@settings), stream
|
160 | else stream.write JSON.stringify(@settings)
|
161 | return
|
162 | else unless _.isFunction @ephemeris
|
163 | throw "No ephemeris to run!"
|
164 | else
|
165 | ephemeris = @ephemeris @settings
|
166 |
|
167 | # An array of massage steps. Expected to be something valid that
|
168 | # Massage can handle. The `eden` (cli) sets up an `Array`
|
169 | # if `--out` is a comma-delimited sequence.
|
170 | if _.isArray @settings.out
|
171 | streamin = @pre ephemeris.stdout
|
172 | massage = new Massage @settings.out
|
173 | massage.pipe streamin, stream, "utf8"
|
174 |
|
175 | # The rest of these are special cases or else straight output of whatever
|
176 | # precious returns.
|
177 |
|
178 | # These are the non-array single massage steps for which json is assumed
|
179 | # being implied for a starting point. Basically a list of valid,
|
180 | # single massage steps for a more readable json output.
|
181 | else if _.include ["inspect", "indent"], @settings.out
|
182 | massage = new Massage ["json", @settings.out]
|
183 | massage.pipe ephemeris.stdout, stream, "utf8"
|
184 |
|
185 | # The most readable output of `eden` and
|
186 | # the default in the context of cli usage.
|
187 | else if @settings.out is "phase"
|
188 | phase (@pre ephemeris.stdout), stream
|
189 |
|
190 | # Unprocessed - straight from *precious*, whatever didn't get caught above.
|
191 | # For example `eden -o pprint`.
|
192 | # Unless @pre modifies the data (points).
|
193 | else @pre(ephemeris.stdout).pipe stream
|
194 |
|
195 | # Special (error) cases.
|
196 | ephemeris.stderr.on "data", (data) -> console.log data.toString("ascii")
|
197 | ephemeris.on "exit", (code) ->
|
198 | if code isnt 0 then console.log 'ephemeris exited with code ' + code;
|
199 |
|
200 | # Know when all the data has been got.
|
201 | # Useful for post-formatting with trailing `\n` by the cli, for example.
|
202 | ephemeris.stdout.on "end", -> stream.emit "end"
|
203 |
|
204 |
|
205 | module.exports = Ephemeris
|