tengai 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/.gitignore +3 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +3 -0
  6. data/README.md +47 -0
  7. data/Rakefile +52 -0
  8. data/ext/horizons/body_data_sheet_parser.rb +350 -0
  9. data/ext/horizons/body_data_sheet_parser.rl +52 -0
  10. data/ext/horizons/vector_ephemeris_parser.rb +4400 -0
  11. data/ext/horizons/vector_ephemeris_parser.rl +115 -0
  12. data/lib/tengai/body.rb +22 -0
  13. data/lib/tengai/client.rb +34 -0
  14. data/lib/tengai/ephemeris.rb +56 -0
  15. data/lib/tengai/parsers/ephemeris_table_parser.rb +20 -0
  16. data/lib/tengai/requests/ephemeris_request.rb +131 -0
  17. data/lib/tengai/vector_ephemeris_table.rb +36 -0
  18. data/lib/tengai/version.rb +5 -0
  19. data/lib/tengai.rb +27 -0
  20. data/tengai.gemspec +63 -0
  21. data/test/fixtures/bodies/10.txt +23 -0
  22. data/test/fixtures/bodies/399.txt +29 -0
  23. data/test/fixtures/bodies/499.txt +25 -0
  24. data/test/fixtures/bodies/501.txt +13 -0
  25. data/test/fixtures/ephemerides/mars_observed_by_earth_from_2012_12_28_to_29.txt +270 -0
  26. data/test/fixtures/ephemerides/mars_vectors_from_solar_system_center.txt +61 -0
  27. data/test/fixtures/major_bodies.txt +300 -0
  28. data/test/fixtures.rb +21 -0
  29. data/test/integration/body_integration_test.rb +26 -0
  30. data/test/integration/ephemeris_integration_test.rb +32 -0
  31. data/test/test_helper.rb +13 -0
  32. data/test/unit/body_data_sheet_parser_test.rb +25 -0
  33. data/test/unit/body_test.rb +35 -0
  34. data/test/unit/ephemeris_table_parser_test.rb +19 -0
  35. data/test/unit/ephemeris_test.rb +22 -0
  36. data/test/unit/vector_ephemeris_parser_test.rb +47 -0
  37. data/test/unit/vector_ephemeris_table_test.rb +30 -0
  38. metadata +178 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /coverage
2
+ Gemfile.lock
3
+ tags
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ tengai
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p448
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ branches:
7
+ only:
8
+ - master
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Tengai
2
+
3
+ [![Build Status](https://travis-ci.org/zacstewart/tengai.png?branch=master)](https://travis-ci.org/zacstewart/tengai)
4
+ [![Dependency Status](https://gemnasium.com/zacstewart/tengai.png)](https://gemnasium.com/zacstewart/tengai)
5
+ [![Code Climate](https://codeclimate.com/github/zacstewart/tengai.png)](https://codeclimate.com/github/zacstewart/tengai)
6
+
7
+ _Tengai_ is a Gem for using the [NASA JPL HORIZONS System][1]. It produces emepherides–tables
8
+ plotting the course of a celestial body over time.
9
+
10
+ > The JPL HORIZONS on-line solar system data and ephemeris computation service
11
+ > provides access to key solar system data and flexible production of highly
12
+ > accurate ephemerides for solar system objects (603428 asteroids, 3184 comets,
13
+ > 176 planetary satellites, 8 planets, the Sun, L1, L2, select spacecraft, and
14
+ > system barycenters ). HORIZONS is provided by the Solar System Dynamics Group
15
+ > of the Jet Propulsion Laboratory.
16
+
17
+ ## Usage
18
+ The API is currently very limited. The data from the telnet system is very
19
+ irregularly structured, and as such parsing it is a big deal.
20
+
21
+ ```ruby
22
+ client = Tengai::Client.new # Connect a client to the telnet server
23
+ body = Tengai::Body.find(client, 499) # Get Mars
24
+ ephemeris = Tengai::Ephemeris.fetch(client, body,
25
+ start_time: Date.today, stop_time: Date.today + 1, interval: 720) # Get ephemeris data for mars
26
+ ```
27
+
28
+ ## Contributing :octocat:
29
+
30
+ The project uses [Ragel State Machine Compiler][2] to generate the parser code. If
31
+ you will be working on that, you'll need to install Ragel (`brew install
32
+ ragel`).
33
+
34
+ 1. Fork
35
+ 2. Create a feature branch
36
+ 3. Add your feature and test it
37
+ 4. Commit
38
+ 5. Push your branch
39
+ 6. Create a pull request
40
+
41
+ ## Testing :ok_hand:
42
+ _Tengai_ is tested with Test::Unit. Tests are split unto units and integration.
43
+ Run them with `rake test:units` and `rake test:integration` respectively or
44
+ just `rake` to run them all.
45
+
46
+ [1]: http://ssd.jpl.nasa.gov/?horizons
47
+ [2]: http://www.complang.org/ragel/
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ STDOUT.sync = true
2
+ require 'rake/testtask'
3
+
4
+ task default: :test
5
+
6
+ desc 'Run all tests'
7
+ multitask test: ['test:units', 'test:integration']
8
+
9
+ namespace :test do
10
+ desc 'Run all tests together (for coverage)'
11
+ Rake::TestTask.new(:all) do |t|
12
+ t.libs << 'test'
13
+ t.test_files = FileList['test/**/*_test.rb']
14
+ t.verbose = true
15
+ end
16
+
17
+ desc 'Run unit tests'
18
+ Rake::TestTask.new(:units) do |t|
19
+ t.libs << 'test'
20
+ t.test_files = FileList['test/unit/**/*_test.rb']
21
+ t.verbose = true
22
+ end
23
+
24
+ desc 'Run integration tests'
25
+ Rake::TestTask.new(:integration) do |t|
26
+ t.libs << 'test'
27
+ t.test_files = FileList['test/integration/**/*_test.rb']
28
+ t.verbose = true
29
+ end
30
+ end
31
+
32
+ namespace :ragel do
33
+ targets = %w(vector_ephemeris_parser body_data_sheet_parser)
34
+
35
+ desc 'Compiles the Ragel state machine definitions to Ruby'
36
+ task :compile do
37
+ Rake::Task['ragel:clean'].invoke
38
+ targets.each do |target|
39
+ puts "Compiling #{target}"
40
+ sh "ragel -R ext/horizons/#{target}.rl"
41
+ raise "Failed to compile #{target}" unless File.exists? "ext/horizons/#{target}.rb"
42
+ end
43
+ end
44
+
45
+ desc 'Removes compiled Ragel state machines'
46
+ task :clean do
47
+ targets.each do |target|
48
+ puts "Removing #{target}"
49
+ File.unlink "ext/horizons/#{target}.rb" if File.exists? "ext/horizons/#{target}.rb"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,350 @@
1
+
2
+ # line 1 "ext/horizons/body_data_sheet_parser.rl"
3
+ =begin
4
+
5
+ # line 32 "ext/horizons/body_data_sheet_parser.rl"
6
+
7
+ =end
8
+ require 'date'
9
+
10
+ module Tengai
11
+ class BodyDataSheetParser
12
+ def self.parse(data)
13
+ data = data.unpack('c*') if data.is_a? String
14
+ eof = data.length
15
+
16
+
17
+ # line 18 "ext/horizons/body_data_sheet_parser.rb"
18
+ begin
19
+ p ||= 0
20
+ pe ||= data.length
21
+ cs = body_data_sheet_parser_start
22
+ end
23
+
24
+ # line 43 "ext/horizons/body_data_sheet_parser.rl"
25
+
26
+ # line 27 "ext/horizons/body_data_sheet_parser.rb"
27
+ begin
28
+ _klen, _trans, _keys, _acts, _nacts = nil
29
+ _goto_level = 0
30
+ _resume = 10
31
+ _eof_trans = 15
32
+ _again = 20
33
+ _test_eof = 30
34
+ _out = 40
35
+ while true
36
+ _trigger_goto = false
37
+ if _goto_level <= 0
38
+ if p == pe
39
+ _goto_level = _test_eof
40
+ next
41
+ end
42
+ if cs == 0
43
+ _goto_level = _out
44
+ next
45
+ end
46
+ end
47
+ if _goto_level <= _resume
48
+ _keys = _body_data_sheet_parser_key_offsets[cs]
49
+ _trans = _body_data_sheet_parser_index_offsets[cs]
50
+ _klen = _body_data_sheet_parser_single_lengths[cs]
51
+ _break_match = false
52
+
53
+ begin
54
+ if _klen > 0
55
+ _lower = _keys
56
+ _upper = _keys + _klen - 1
57
+
58
+ loop do
59
+ break if _upper < _lower
60
+ _mid = _lower + ( (_upper - _lower) >> 1 )
61
+
62
+ if data[p].ord < _body_data_sheet_parser_trans_keys[_mid]
63
+ _upper = _mid - 1
64
+ elsif data[p].ord > _body_data_sheet_parser_trans_keys[_mid]
65
+ _lower = _mid + 1
66
+ else
67
+ _trans += (_mid - _keys)
68
+ _break_match = true
69
+ break
70
+ end
71
+ end # loop
72
+ break if _break_match
73
+ _keys += _klen
74
+ _trans += _klen
75
+ end
76
+ _klen = _body_data_sheet_parser_range_lengths[cs]
77
+ if _klen > 0
78
+ _lower = _keys
79
+ _upper = _keys + (_klen << 1) - 2
80
+ loop do
81
+ break if _upper < _lower
82
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1)
83
+ if data[p].ord < _body_data_sheet_parser_trans_keys[_mid]
84
+ _upper = _mid - 2
85
+ elsif data[p].ord > _body_data_sheet_parser_trans_keys[_mid+1]
86
+ _lower = _mid + 2
87
+ else
88
+ _trans += ((_mid - _keys) >> 1)
89
+ _break_match = true
90
+ break
91
+ end
92
+ end # loop
93
+ break if _break_match
94
+ _trans += _klen
95
+ end
96
+ end while false
97
+ _trans = _body_data_sheet_parser_indicies[_trans]
98
+ cs = _body_data_sheet_parser_trans_targs[_trans]
99
+ if _body_data_sheet_parser_trans_actions[_trans] != 0
100
+ _acts = _body_data_sheet_parser_trans_actions[_trans]
101
+ _nacts = _body_data_sheet_parser_actions[_acts]
102
+ _acts += 1
103
+ while _nacts > 0
104
+ _nacts -= 1
105
+ _acts += 1
106
+ case _body_data_sheet_parser_actions[_acts - 1]
107
+ when 0 then
108
+ # line 5 "ext/horizons/body_data_sheet_parser.rl"
109
+ begin
110
+ mark = p end
111
+ when 1 then
112
+ # line 7 "ext/horizons/body_data_sheet_parser.rl"
113
+ begin
114
+
115
+ _revised_on = data[mark..p].pack('c*')
116
+ end
117
+ when 2 then
118
+ # line 11 "ext/horizons/body_data_sheet_parser.rl"
119
+ begin
120
+
121
+ _name = data[mark..p - 1].pack('c*')
122
+ end
123
+ when 3 then
124
+ # line 15 "ext/horizons/body_data_sheet_parser.rl"
125
+ begin
126
+
127
+ _id = data[mark..p].pack('c*')
128
+ end
129
+ # line 130 "ext/horizons/body_data_sheet_parser.rb"
130
+ end # action switch
131
+ end
132
+ end
133
+ if _trigger_goto
134
+ next
135
+ end
136
+ end
137
+ if _goto_level <= _again
138
+ if cs == 0
139
+ _goto_level = _out
140
+ next
141
+ end
142
+ p += 1
143
+ if p != pe
144
+ _goto_level = _resume
145
+ next
146
+ end
147
+ end
148
+ if _goto_level <= _test_eof
149
+ end
150
+ if _goto_level <= _out
151
+ break
152
+ end
153
+ end
154
+ end
155
+
156
+ # line 44 "ext/horizons/body_data_sheet_parser.rl"
157
+
158
+ { revised_on: Date.parse(_revised_on),
159
+ name: _name,
160
+ id: _id.to_i }
161
+ end
162
+
163
+
164
+ # line 165 "ext/horizons/body_data_sheet_parser.rb"
165
+ class << self
166
+ attr_accessor :_body_data_sheet_parser_actions
167
+ private :_body_data_sheet_parser_actions, :_body_data_sheet_parser_actions=
168
+ end
169
+ self._body_data_sheet_parser_actions = [
170
+ 0, 1, 0, 1, 1, 1, 2, 1,
171
+ 3, 2, 0, 3
172
+ ]
173
+
174
+ class << self
175
+ attr_accessor :_body_data_sheet_parser_key_offsets
176
+ private :_body_data_sheet_parser_key_offsets, :_body_data_sheet_parser_key_offsets=
177
+ end
178
+ self._body_data_sheet_parser_key_offsets = [
179
+ 0, 0, 1, 3, 5, 7, 9, 11,
180
+ 13, 16, 20, 23, 26, 28, 31, 34,
181
+ 36, 38, 41, 44, 47, 50, 52, 62,
182
+ 72, 84, 88, 92, 98, 101, 113, 125,
183
+ 138, 151, 164, 177, 190, 203, 216, 229,
184
+ 242, 249, 260, 264, 268, 271
185
+ ]
186
+
187
+ class << self
188
+ attr_accessor :_body_data_sheet_parser_trans_keys
189
+ private :_body_data_sheet_parser_trans_keys, :_body_data_sheet_parser_trans_keys=
190
+ end
191
+ self._body_data_sheet_parser_trans_keys = [
192
+ 82, 82, 101, 82, 118, 82, 105, 82,
193
+ 115, 82, 101, 82, 100, 32, 58, 82,
194
+ 32, 82, 65, 90, 82, 97, 122, 82,
195
+ 97, 122, 32, 82, 82, 48, 57, 82,
196
+ 48, 57, 44, 82, 32, 82, 82, 48,
197
+ 57, 82, 48, 57, 82, 48, 57, 82,
198
+ 48, 57, 32, 82, 32, 82, 40, 41,
199
+ 47, 57, 65, 90, 97, 122, 32, 82,
200
+ 40, 41, 47, 57, 65, 90, 97, 122,
201
+ 32, 82, 9, 13, 40, 41, 47, 57,
202
+ 65, 90, 97, 122, 32, 82, 9, 13,
203
+ 32, 82, 9, 13, 32, 82, 9, 13,
204
+ 48, 57, 10, 48, 57, 32, 82, 9,
205
+ 13, 40, 41, 47, 57, 65, 90, 97,
206
+ 122, 32, 82, 9, 13, 40, 41, 47,
207
+ 57, 65, 90, 97, 122, 32, 82, 101,
208
+ 9, 13, 40, 41, 47, 57, 65, 90,
209
+ 97, 122, 32, 82, 118, 9, 13, 40,
210
+ 41, 47, 57, 65, 90, 97, 122, 32,
211
+ 82, 105, 9, 13, 40, 41, 47, 57,
212
+ 65, 90, 97, 122, 32, 82, 115, 9,
213
+ 13, 40, 41, 47, 57, 65, 90, 97,
214
+ 122, 32, 82, 101, 9, 13, 40, 41,
215
+ 47, 57, 65, 90, 97, 122, 32, 82,
216
+ 100, 9, 13, 40, 41, 47, 57, 65,
217
+ 90, 97, 122, 32, 58, 82, 9, 13,
218
+ 40, 41, 47, 57, 65, 90, 97, 122,
219
+ 32, 58, 82, 9, 13, 40, 41, 47,
220
+ 57, 65, 90, 97, 122, 32, 58, 82,
221
+ 9, 13, 40, 41, 47, 57, 65, 90,
222
+ 97, 122, 32, 58, 82, 9, 13, 48,
223
+ 57, 32, 82, 101, 40, 41, 47, 57,
224
+ 65, 90, 97, 122, 82, 101, 97, 122,
225
+ 82, 118, 97, 122, 32, 82, 105, 0
226
+ ]
227
+
228
+ class << self
229
+ attr_accessor :_body_data_sheet_parser_single_lengths
230
+ private :_body_data_sheet_parser_single_lengths, :_body_data_sheet_parser_single_lengths=
231
+ end
232
+ self._body_data_sheet_parser_single_lengths = [
233
+ 0, 1, 2, 2, 2, 2, 2, 2,
234
+ 3, 2, 1, 1, 2, 1, 1, 2,
235
+ 2, 1, 1, 1, 1, 2, 2, 2,
236
+ 2, 2, 2, 2, 1, 2, 2, 3,
237
+ 3, 3, 3, 3, 3, 3, 3, 3,
238
+ 3, 3, 2, 2, 3, 0
239
+ ]
240
+
241
+ class << self
242
+ attr_accessor :_body_data_sheet_parser_range_lengths
243
+ private :_body_data_sheet_parser_range_lengths, :_body_data_sheet_parser_range_lengths=
244
+ end
245
+ self._body_data_sheet_parser_range_lengths = [
246
+ 0, 0, 0, 0, 0, 0, 0, 0,
247
+ 0, 1, 1, 1, 0, 1, 1, 0,
248
+ 0, 1, 1, 1, 1, 0, 4, 4,
249
+ 5, 1, 1, 2, 1, 5, 5, 5,
250
+ 5, 5, 5, 5, 5, 5, 5, 5,
251
+ 2, 4, 1, 1, 0, 0
252
+ ]
253
+
254
+ class << self
255
+ attr_accessor :_body_data_sheet_parser_index_offsets
256
+ private :_body_data_sheet_parser_index_offsets, :_body_data_sheet_parser_index_offsets=
257
+ end
258
+ self._body_data_sheet_parser_index_offsets = [
259
+ 0, 0, 2, 5, 8, 11, 14, 17,
260
+ 20, 24, 28, 31, 34, 37, 40, 43,
261
+ 46, 49, 52, 55, 58, 61, 64, 71,
262
+ 78, 86, 90, 94, 99, 102, 110, 118,
263
+ 127, 136, 145, 154, 163, 172, 181, 190,
264
+ 199, 205, 213, 217, 221, 225
265
+ ]
266
+
267
+ class << self
268
+ attr_accessor :_body_data_sheet_parser_indicies
269
+ private :_body_data_sheet_parser_indicies, :_body_data_sheet_parser_indicies=
270
+ end
271
+ self._body_data_sheet_parser_indicies = [
272
+ 1, 0, 1, 2, 0, 1, 3, 0,
273
+ 1, 4, 0, 1, 5, 0, 1, 6,
274
+ 0, 1, 7, 0, 7, 8, 1, 0,
275
+ 8, 10, 9, 0, 1, 11, 0, 1,
276
+ 12, 0, 13, 1, 0, 1, 14, 0,
277
+ 1, 15, 0, 16, 1, 0, 17, 1,
278
+ 0, 1, 18, 0, 1, 19, 0, 1,
279
+ 20, 0, 1, 21, 0, 22, 1, 0,
280
+ 22, 24, 23, 23, 23, 23, 0, 25,
281
+ 27, 26, 26, 26, 26, 0, 29, 27,
282
+ 28, 26, 26, 26, 26, 0, 30, 1,
283
+ 30, 0, 31, 1, 31, 0, 31, 1,
284
+ 31, 32, 0, 33, 35, 34, 36, 27,
285
+ 30, 26, 26, 26, 26, 0, 31, 27,
286
+ 31, 26, 26, 26, 26, 0, 29, 27,
287
+ 37, 28, 26, 26, 26, 26, 0, 29,
288
+ 27, 38, 28, 26, 26, 26, 26, 0,
289
+ 29, 27, 39, 28, 26, 26, 26, 26,
290
+ 0, 29, 27, 40, 28, 26, 26, 26,
291
+ 26, 0, 29, 27, 41, 28, 26, 26,
292
+ 26, 26, 0, 29, 27, 42, 28, 26,
293
+ 26, 26, 26, 0, 43, 8, 27, 28,
294
+ 26, 26, 26, 26, 0, 44, 8, 27,
295
+ 30, 26, 26, 26, 26, 0, 45, 8,
296
+ 27, 31, 26, 26, 26, 26, 0, 45,
297
+ 8, 1, 31, 32, 0, 25, 27, 37,
298
+ 26, 26, 26, 26, 0, 1, 46, 11,
299
+ 0, 1, 47, 12, 0, 13, 1, 4,
300
+ 0, 33, 0
301
+ ]
302
+
303
+ class << self
304
+ attr_accessor :_body_data_sheet_parser_trans_targs
305
+ private :_body_data_sheet_parser_trans_targs, :_body_data_sheet_parser_trans_targs=
306
+ end
307
+ self._body_data_sheet_parser_trans_targs = [
308
+ 1, 2, 3, 4, 5, 6, 7, 8,
309
+ 9, 10, 42, 11, 12, 13, 14, 15,
310
+ 16, 17, 18, 19, 20, 21, 22, 23,
311
+ 41, 23, 24, 31, 25, 29, 26, 27,
312
+ 28, 45, 0, 28, 30, 32, 33, 34,
313
+ 35, 36, 37, 38, 39, 40, 43, 44
314
+ ]
315
+
316
+ class << self
317
+ attr_accessor :_body_data_sheet_parser_trans_actions
318
+ private :_body_data_sheet_parser_trans_actions, :_body_data_sheet_parser_trans_actions=
319
+ end
320
+ self._body_data_sheet_parser_trans_actions = [
321
+ 0, 0, 0, 0, 0, 0, 0, 0,
322
+ 0, 1, 1, 0, 0, 0, 0, 0,
323
+ 0, 0, 0, 0, 0, 3, 0, 1,
324
+ 1, 0, 0, 0, 5, 5, 0, 0,
325
+ 9, 0, 0, 7, 0, 0, 0, 0,
326
+ 0, 0, 0, 5, 0, 0, 0, 0
327
+ ]
328
+
329
+ class << self
330
+ attr_accessor :body_data_sheet_parser_start
331
+ end
332
+ self.body_data_sheet_parser_start = 1;
333
+ class << self
334
+ attr_accessor :body_data_sheet_parser_first_final
335
+ end
336
+ self.body_data_sheet_parser_first_final = 45;
337
+ class << self
338
+ attr_accessor :body_data_sheet_parser_error
339
+ end
340
+ self.body_data_sheet_parser_error = 0;
341
+
342
+ class << self
343
+ attr_accessor :body_data_sheet_parser_en_main
344
+ end
345
+ self.body_data_sheet_parser_en_main = 1;
346
+
347
+
348
+ # line 51 "ext/horizons/body_data_sheet_parser.rl"
349
+ end
350
+ end
@@ -0,0 +1,52 @@
1
+ =begin
2
+ %%{
3
+ machine body_data_sheet_parser;
4
+
5
+ action mark { mark = p }
6
+
7
+ action revised_on {
8
+ _revised_on = data[mark..p].pack('c*')
9
+ }
10
+
11
+ action name {
12
+ _name = data[mark..p - 1].pack('c*')
13
+ }
14
+
15
+ action id {
16
+ _id = data[mark..p].pack('c*')
17
+ }
18
+
19
+ date = upper lower{2} ' ' digit{2} ',' ' ' digit{4};
20
+ revised_on = ' '* 'Revised' ' '* ':' ' '* date >mark @revised_on;
21
+ printable = alnum | [\/()];
22
+
23
+ name = printable >mark (' '{1,2} | printable)* printable %name :>> space{3,};
24
+
25
+ id = digit+ >mark @id;
26
+
27
+ main := (
28
+ any*
29
+ revised_on ' '+ name :> id '\n'
30
+ any*
31
+ );
32
+ }%%
33
+ =end
34
+ require 'date'
35
+
36
+ module Tengai
37
+ class BodyDataSheetParser
38
+ def self.parse(data)
39
+ data = data.unpack('c*') if data.is_a? String
40
+ eof = data.length
41
+
42
+ %% write init;
43
+ %% write exec;
44
+
45
+ { revised_on: Date.parse(_revised_on),
46
+ name: _name,
47
+ id: _id.to_i }
48
+ end
49
+
50
+ %% write data;
51
+ end
52
+ end