UNPKG

8.04 kBtext/coffeescriptView Raw
1Gaia = require("lin").Gaia
2which = require("which")
3_ = require("massagist")._
4Massage = require("massagist").Massage
5points = require("./points")
6phase = require("./phase")
7
8
9class 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
205module.exports = Ephemeris