dynarex 1.8.27 → 1.9.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/dynarex.rb +342 -321
- data.tar.gz.sig +0 -0
- metadata +43 -42
- metadata.gz.sig +0 -0
data/lib/dynarex.rb
CHANGED
|
@@ -30,7 +30,7 @@ hkey_gems
|
|
|
30
30
|
require dynarex
|
|
31
31
|
class Dynarex
|
|
32
32
|
media_type dynarex
|
|
33
|
-
'
|
|
33
|
+
'
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -39,35 +39,35 @@ class DynarexException < Exception
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
class DynarexRecordset < Array
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
def initialize(a, caller=nil)
|
|
44
44
|
super(a)
|
|
45
45
|
@caller = caller
|
|
46
46
|
end
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
def reject!()
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
a = self.to_a.clone
|
|
51
51
|
a2 = super
|
|
52
52
|
return nil unless a2
|
|
53
53
|
a3 = a - a2
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
@caller.delete a3.map(&:id)
|
|
56
56
|
self
|
|
57
57
|
end
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
def sum(field)
|
|
60
60
|
self.inject(0) {|r, x| r + x[field.to_sym][/\d+(\.\d+)?$/].to_f }
|
|
61
61
|
end
|
|
62
|
-
|
|
63
|
-
def index(val)
|
|
62
|
+
|
|
63
|
+
def index(val)
|
|
64
64
|
self.map(&:to_h).index val.to_h
|
|
65
65
|
end
|
|
66
|
-
|
|
67
|
-
def index_by_id(id)
|
|
66
|
+
|
|
67
|
+
def index_by_id(id)
|
|
68
68
|
self.map(&:id).index id
|
|
69
|
-
end
|
|
70
|
-
|
|
69
|
+
end
|
|
70
|
+
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
|
|
@@ -77,8 +77,8 @@ class Dynarex
|
|
|
77
77
|
|
|
78
78
|
attr_accessor :format_mask, :delimiter, :xslt_schema, :schema, :linked,
|
|
79
79
|
:order, :type, :limit, :xslt, :json_out, :unique
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
|
|
81
|
+
|
|
82
82
|
#Create a new dynarex document from 1 of the following options:
|
|
83
83
|
#* a local file path
|
|
84
84
|
#* a URL
|
|
@@ -87,9 +87,9 @@ class Dynarex
|
|
|
87
87
|
#* an XML string
|
|
88
88
|
# Dynarex.new '<contacts><summary><schema>contacts/contact(name,age,dob)</schema></summary><records/></contacts>'
|
|
89
89
|
|
|
90
|
-
def initialize(rawx=nil, username: nil, password: nil, schema: nil,
|
|
91
|
-
default_key: nil, json_out: true, debug: false,
|
|
92
|
-
delimiter: ' # ', autosave: false, order: 'ascending',
|
|
90
|
+
def initialize(rawx=nil, username: nil, password: nil, schema: nil,
|
|
91
|
+
default_key: nil, json_out: true, debug: false,
|
|
92
|
+
delimiter: ' # ', autosave: false, order: 'ascending',
|
|
93
93
|
unique: false, filepath: nil)
|
|
94
94
|
|
|
95
95
|
|
|
@@ -98,7 +98,7 @@ class Dynarex
|
|
|
98
98
|
username, password, schema, default_key, json_out, debug
|
|
99
99
|
@autosave, @unique = autosave, unique
|
|
100
100
|
@local_filepath = filepath
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
puts ('@debug: ' + @debug.inspect).debug if debug
|
|
103
103
|
@delimiter = delimiter
|
|
104
104
|
@spaces_delimited = false
|
|
@@ -108,12 +108,12 @@ class Dynarex
|
|
|
108
108
|
rawx ||= schema if schema
|
|
109
109
|
|
|
110
110
|
if rawx then
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
return import(rawx) if rawx =~ /\.txt$/
|
|
113
|
-
openx(rawx.clone)
|
|
114
|
-
|
|
113
|
+
openx(rawx.clone)
|
|
114
|
+
|
|
115
115
|
end
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
self.order = @order unless @order.to_sym == :ascending
|
|
118
118
|
|
|
119
119
|
end
|
|
@@ -125,17 +125,17 @@ class Dynarex
|
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def all()
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
refresh! if @dirty_flag
|
|
130
130
|
a = @doc.root.xpath("records/*").map {|x| recordx_to_record x}
|
|
131
131
|
DynarexRecordset.new(a, self)
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
end
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
def clone()
|
|
136
136
|
Dynarex.new(self.to_xml)
|
|
137
137
|
end
|
|
138
|
-
|
|
138
|
+
|
|
139
139
|
def default_key()
|
|
140
140
|
self.summary[:default_key]
|
|
141
141
|
end
|
|
@@ -146,10 +146,10 @@ class Dynarex
|
|
|
146
146
|
@spaces_delimited = true
|
|
147
147
|
separator = ' # '
|
|
148
148
|
end
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
@delimiter = separator
|
|
151
151
|
|
|
152
|
-
if separator.length > 0 then
|
|
152
|
+
if separator.length > 0 then
|
|
153
153
|
@summary[:delimiter] = separator
|
|
154
154
|
else
|
|
155
155
|
@summary.delete :delimiter
|
|
@@ -158,14 +158,14 @@ class Dynarex
|
|
|
158
158
|
@format_mask = @format_mask.to_s.gsub(/\s/, separator)
|
|
159
159
|
@summary[:format_mask] = @format_mask
|
|
160
160
|
end
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
def doc
|
|
163
163
|
(load_records; rebuild_doc) if @dirty_flag == true
|
|
164
164
|
@doc
|
|
165
|
-
end
|
|
165
|
+
end
|
|
166
166
|
|
|
167
167
|
# XML import
|
|
168
|
-
#
|
|
168
|
+
#
|
|
169
169
|
def foreign_import(options={})
|
|
170
170
|
o = {xml: '', schema: ''}.merge(options)
|
|
171
171
|
h = {xml: o[:xml], schema: @schema, foreign_schema: o[:schema]}
|
|
@@ -174,21 +174,21 @@ class Dynarex
|
|
|
174
174
|
openx(buffer)
|
|
175
175
|
self
|
|
176
176
|
end
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
def fields
|
|
179
179
|
@fields
|
|
180
180
|
end
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
def first
|
|
183
183
|
r = @doc.root.element("records/*")
|
|
184
|
-
r ? recordx_to_record(r) : nil
|
|
184
|
+
r ? recordx_to_record(r) : nil
|
|
185
185
|
end
|
|
186
186
|
|
|
187
187
|
def format_mask=(s)
|
|
188
188
|
@format_mask = s
|
|
189
189
|
@summary[:format_mask] = @format_mask
|
|
190
190
|
end
|
|
191
|
-
|
|
191
|
+
|
|
192
192
|
def insert(raw_params)
|
|
193
193
|
record = make_record(raw_params)
|
|
194
194
|
@doc.root.element('records/*').insert_before record
|
|
@@ -198,32 +198,32 @@ class Dynarex
|
|
|
198
198
|
def inspect()
|
|
199
199
|
"<object #%s>" % [self.object_id]
|
|
200
200
|
end
|
|
201
|
-
|
|
201
|
+
|
|
202
202
|
def linked=(bool)
|
|
203
203
|
@linked = bool == 'true'
|
|
204
204
|
end
|
|
205
|
-
|
|
205
|
+
|
|
206
206
|
def order=(value)
|
|
207
|
-
|
|
208
|
-
self.summary.merge!({order: value.to_s})
|
|
207
|
+
|
|
208
|
+
self.summary.merge!({order: value.to_s})
|
|
209
209
|
|
|
210
210
|
@order = value.to_s
|
|
211
211
|
end
|
|
212
|
-
|
|
212
|
+
|
|
213
213
|
def recordx_type()
|
|
214
214
|
@summary[:recordx_type]
|
|
215
215
|
end
|
|
216
|
-
|
|
216
|
+
|
|
217
217
|
def schema=(s)
|
|
218
218
|
openx s
|
|
219
219
|
end
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
def type=(v)
|
|
222
222
|
@order = 'descending' if v == 'feed'
|
|
223
223
|
@type = v
|
|
224
224
|
@summary[:type] = v
|
|
225
225
|
end
|
|
226
|
-
|
|
226
|
+
|
|
227
227
|
# Returns the hash representation of the document summary.
|
|
228
228
|
#
|
|
229
229
|
def summary
|
|
@@ -233,29 +233,29 @@ class Dynarex
|
|
|
233
233
|
# Return a Hash (which can be edited) containing all records.
|
|
234
234
|
#
|
|
235
235
|
def records
|
|
236
|
-
|
|
236
|
+
|
|
237
237
|
load_records if @dirty_flag == true
|
|
238
|
-
|
|
238
|
+
|
|
239
239
|
if block_given? then
|
|
240
240
|
yield(@records)
|
|
241
241
|
rebuild_doc
|
|
242
|
-
@dirty_flag = true
|
|
242
|
+
@dirty_flag = true
|
|
243
243
|
else
|
|
244
244
|
@records
|
|
245
245
|
end
|
|
246
|
-
|
|
246
|
+
|
|
247
247
|
end
|
|
248
|
-
|
|
249
|
-
# Returns a ready-only snapshot of records as a simple Hash.
|
|
248
|
+
|
|
249
|
+
# Returns a ready-only snapshot of records as a simple Hash.
|
|
250
250
|
#
|
|
251
251
|
def flat_records(select: nil)
|
|
252
|
-
|
|
252
|
+
|
|
253
253
|
fields = select
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
load_records if @dirty_flag == true
|
|
256
256
|
|
|
257
257
|
if fields then
|
|
258
|
-
|
|
258
|
+
|
|
259
259
|
case fields.class.to_s.downcase.to_sym
|
|
260
260
|
when :string
|
|
261
261
|
field = fields.to_sym
|
|
@@ -267,58 +267,58 @@ class Dynarex
|
|
|
267
267
|
@flat_records.map {|row| fields.inject({})\
|
|
268
268
|
{|r,x| r.merge(x.to_sym => row[x.to_sym])}}
|
|
269
269
|
end
|
|
270
|
-
|
|
270
|
+
|
|
271
271
|
else
|
|
272
272
|
@flat_records
|
|
273
273
|
end
|
|
274
|
-
|
|
274
|
+
|
|
275
275
|
end
|
|
276
|
-
|
|
276
|
+
|
|
277
277
|
alias to_a flat_records
|
|
278
|
-
|
|
278
|
+
|
|
279
279
|
# Returns an array snapshot of OpenStruct records
|
|
280
280
|
#
|
|
281
281
|
def ro_records
|
|
282
282
|
flat_records.map {|record| OpenStruct.new record }
|
|
283
283
|
end
|
|
284
|
-
|
|
284
|
+
|
|
285
285
|
def rm(force: false)
|
|
286
|
-
|
|
286
|
+
|
|
287
287
|
if force or all.empty? then
|
|
288
288
|
FileX.rm @local_filepath if @local_filepath
|
|
289
289
|
'file ' + @local_filepath + ' deleted'
|
|
290
290
|
else
|
|
291
291
|
'unable to rm file: document not empty'
|
|
292
292
|
end
|
|
293
|
-
|
|
293
|
+
|
|
294
294
|
end
|
|
295
|
-
|
|
296
295
|
|
|
297
|
-
|
|
296
|
+
|
|
297
|
+
def to_doc
|
|
298
298
|
self.clone().doc
|
|
299
299
|
end
|
|
300
|
-
|
|
300
|
+
|
|
301
301
|
# Typically uses the 1st field as a key and the remaining fields as the value
|
|
302
302
|
#
|
|
303
303
|
def to_h()
|
|
304
|
-
|
|
304
|
+
|
|
305
305
|
key = self.default_key.to_sym
|
|
306
306
|
fields = self.fields() - [key]
|
|
307
307
|
puts 'fields: ' + fields.inspect if @debug
|
|
308
|
-
|
|
309
|
-
flat_records.inject({}) do |r, h|
|
|
310
|
-
|
|
308
|
+
|
|
309
|
+
flat_records.inject({}) do |r, h|
|
|
310
|
+
|
|
311
311
|
puts 'h: ' + h.inspect if @debug
|
|
312
|
-
|
|
312
|
+
|
|
313
313
|
value = if h.length == 2 then
|
|
314
314
|
h[fields[0]].to_s
|
|
315
315
|
else
|
|
316
|
-
fields.map {|x| h[x]}
|
|
316
|
+
fields.map {|x| h[x]}
|
|
317
317
|
end
|
|
318
|
-
|
|
318
|
+
|
|
319
319
|
r.merge(h[key] => value)
|
|
320
320
|
end
|
|
321
|
-
|
|
321
|
+
|
|
322
322
|
end
|
|
323
323
|
|
|
324
324
|
def to_html(domain: '')
|
|
@@ -327,32 +327,32 @@ class Dynarex
|
|
|
327
327
|
xsl_buffer = RXFHelper.read(domain + @xslt, h).first
|
|
328
328
|
Rexslt.new(xsl_buffer, self.to_doc).to_s
|
|
329
329
|
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
# to_json: pretty is set to true because although the file size is larger,
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# to_json: pretty is set to true because although the file size is larger,
|
|
334
334
|
# the file can be load slightly quicker
|
|
335
|
-
|
|
335
|
+
|
|
336
336
|
def to_json(pretty: true)
|
|
337
|
-
|
|
337
|
+
|
|
338
338
|
records = self.to_a
|
|
339
339
|
summary = self.summary.to_h
|
|
340
|
-
|
|
340
|
+
|
|
341
341
|
root_name = schema()[/^\w+/]
|
|
342
342
|
record_name = schema()[/(?<=\/)[^\(]+/]
|
|
343
|
-
|
|
343
|
+
|
|
344
344
|
h = {
|
|
345
345
|
root_name.to_sym =>
|
|
346
346
|
{
|
|
347
347
|
summary: @summary,
|
|
348
|
-
records: @records.map {|_, h| {record_name.to_sym => h} }
|
|
348
|
+
records: @records.map {|_, h| {record_name.to_sym => h} }
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
|
-
|
|
351
|
+
|
|
352
352
|
pretty ? JSON.pretty_generate(h) : h.to_json
|
|
353
|
-
|
|
353
|
+
|
|
354
354
|
end
|
|
355
|
-
|
|
355
|
+
|
|
356
356
|
def to_s(header: true, delimiter: @delimiter)
|
|
357
357
|
|
|
358
358
|
xsl_buffer =<<EOF
|
|
@@ -373,22 +373,22 @@ EOF
|
|
|
373
373
|
|
|
374
374
|
raw_summary_fields = self.summary[:schema][/^\w+\[([^\]]+)\]/,1]
|
|
375
375
|
sumry = ''
|
|
376
|
-
|
|
376
|
+
|
|
377
377
|
if raw_summary_fields then
|
|
378
|
-
summary_fields = raw_summary_fields.split(',') # .map(&:to_sym)
|
|
378
|
+
summary_fields = raw_summary_fields.split(',') # .map(&:to_sym)
|
|
379
379
|
sumry = summary_fields.map {|x| x.strip!; x + ': ' + \
|
|
380
380
|
self.summary[x.to_sym].to_s}.join("\n") + "\n\n"
|
|
381
381
|
end
|
|
382
|
-
|
|
382
|
+
|
|
383
383
|
if @raw_header then
|
|
384
384
|
declaration = @raw_header
|
|
385
385
|
else
|
|
386
386
|
|
|
387
|
-
smry_fields = %i(schema)
|
|
387
|
+
smry_fields = %i(schema)
|
|
388
388
|
smry_fields << :order if self.summary[:order] == 'descending'
|
|
389
|
-
|
|
389
|
+
|
|
390
390
|
if delimiter.length > 0 then
|
|
391
|
-
smry_fields << :delimiter
|
|
391
|
+
smry_fields << :delimiter
|
|
392
392
|
else
|
|
393
393
|
smry_fields << :format_mask unless self.summary[:rawdoc_type] == 'rowx'
|
|
394
394
|
end
|
|
@@ -406,44 +406,44 @@ EOF
|
|
|
406
406
|
<xsl:text>\n</xsl:text>%s:<xsl:text> </xsl:text><xsl:value-of select='%s'/>
|
|
407
407
|
</xsl:if>" % ([field]*3)
|
|
408
408
|
end
|
|
409
|
-
|
|
409
|
+
|
|
410
410
|
puts ('a: ' + a.inspect).debug if @debug
|
|
411
411
|
|
|
412
|
-
xslt_format = a.join
|
|
412
|
+
xslt_format = a.join
|
|
413
413
|
|
|
414
414
|
xsl_buffer.sub!(/\[!regex_values\]/, xslt_format)
|
|
415
|
-
|
|
415
|
+
|
|
416
416
|
if @debug then
|
|
417
417
|
File.write '/tmp/foo.xsl', xsl_buffer
|
|
418
418
|
File.write '/tmp/foo.xml', @doc.xml
|
|
419
419
|
puts 'xsl_buffer: ' + xsl_buffer.inspect
|
|
420
420
|
end
|
|
421
|
-
|
|
421
|
+
|
|
422
422
|
out = Rexslt.new(xsl_buffer, @doc).to_s
|
|
423
|
-
|
|
423
|
+
|
|
424
424
|
docheader + "\n--+\n" + out
|
|
425
|
-
elsif self.summary[:rawdoc_type] == 'sectionx' then
|
|
426
|
-
|
|
425
|
+
elsif self.summary[:rawdoc_type] == 'sectionx' then
|
|
426
|
+
|
|
427
427
|
a = (self.fields - [:uid, 'uid']).map do |field|
|
|
428
428
|
"<xsl:if test=\"%s != ''\">
|
|
429
429
|
<xsl:text>\n</xsl:text><xsl:value-of select='%s'/>
|
|
430
430
|
</xsl:if>" % ([field]*2)
|
|
431
431
|
end
|
|
432
432
|
|
|
433
|
-
xslt_format = a.join
|
|
433
|
+
xslt_format = a.join
|
|
434
434
|
|
|
435
435
|
xsl_buffer.sub!(/\[!regex_values\]/, xslt_format)
|
|
436
436
|
puts 'xsl_buffer: ' + xsl_buffer.inspect if @debug
|
|
437
437
|
|
|
438
438
|
out = Rexslt.new(xsl_buffer, @doc).to_s
|
|
439
|
-
|
|
439
|
+
|
|
440
440
|
header ? docheader + "--#\n" + out : out
|
|
441
|
-
|
|
441
|
+
|
|
442
442
|
elsif self.delimiter.length > 0 then
|
|
443
443
|
puts 'dinddd'
|
|
444
444
|
tfo = TableFormatter.new border: false, wrap: false, \
|
|
445
445
|
divider: self.delimiter
|
|
446
|
-
tfo.source = self.to_a.map{|x| x.values}
|
|
446
|
+
tfo.source = self.to_a.map{|x| x.values}
|
|
447
447
|
docheader + tfo.display.strip
|
|
448
448
|
|
|
449
449
|
else
|
|
@@ -454,7 +454,7 @@ EOF
|
|
|
454
454
|
s1, s2 = '<xsl:text>', '</xsl:text>'
|
|
455
455
|
xslt_format = s1 + format_mask\
|
|
456
456
|
.gsub(/(?:\[!(\w+)\])/, s2 + '<xsl:value-of select="\1"/>' + s1) + s2
|
|
457
|
-
|
|
457
|
+
|
|
458
458
|
xsl_buffer.sub!(/\[!regex_values\]/, xslt_format)
|
|
459
459
|
|
|
460
460
|
puts 'xsl_buffer: ' + xsl_buffer if @debug
|
|
@@ -466,7 +466,7 @@ EOF
|
|
|
466
466
|
end
|
|
467
467
|
|
|
468
468
|
def to_table(fields: nil, markdown: false, innermarkdown: false, heading: true)
|
|
469
|
-
|
|
469
|
+
|
|
470
470
|
tfo = TableFormatter.new markdown: markdown, innermarkdown: innermarkdown
|
|
471
471
|
tfo.source = self.to_a.map {|h| fields ? fields.map {|x| h[x]} : h.values }
|
|
472
472
|
|
|
@@ -475,59 +475,59 @@ EOF
|
|
|
475
475
|
fields = raw_headings.split(self.delimiter) if raw_headings and fields.nil?
|
|
476
476
|
tfo.labels = (fields ? fields : self.fields.map{|x| x.to_s.capitalize })
|
|
477
477
|
end
|
|
478
|
-
|
|
478
|
+
|
|
479
479
|
tfo
|
|
480
|
-
|
|
480
|
+
|
|
481
481
|
end
|
|
482
|
-
|
|
483
|
-
def to_xml(opt={})
|
|
482
|
+
|
|
483
|
+
def to_xml(opt={})
|
|
484
484
|
opt = {pretty: true} if opt == :pretty
|
|
485
485
|
display_xml(opt)
|
|
486
486
|
end
|
|
487
|
-
|
|
488
|
-
# Save the document to a file.
|
|
489
|
-
|
|
487
|
+
|
|
488
|
+
# Save the document to a file.
|
|
489
|
+
|
|
490
490
|
def save(filepath=@local_filepath, options={})
|
|
491
|
-
|
|
491
|
+
|
|
492
492
|
if @debug then
|
|
493
|
-
puts 'inside Dynarex::save'
|
|
493
|
+
puts 'inside Dynarex::save'
|
|
494
494
|
puts 'filepath: ' + filepath.inspect
|
|
495
495
|
|
|
496
496
|
end
|
|
497
|
-
|
|
497
|
+
|
|
498
498
|
return unless filepath
|
|
499
|
-
|
|
499
|
+
|
|
500
500
|
opt = {pretty: true}.merge options
|
|
501
501
|
|
|
502
502
|
@local_filepath = filepath || 'dx.xml'
|
|
503
503
|
xml = display_xml(opt)
|
|
504
504
|
buffer = block_given? ? yield(xml) : xml
|
|
505
|
-
|
|
505
|
+
|
|
506
506
|
if @debug then
|
|
507
507
|
puts 'before write; filepath: ' + filepath.inspect
|
|
508
508
|
puts 'buffer: ' + buffer.inspect
|
|
509
509
|
end
|
|
510
|
-
|
|
510
|
+
|
|
511
511
|
FileX.write filepath, buffer
|
|
512
512
|
FileX.write(filepath.sub(/\.xml$/,'.json'), self.to_json) if @json_out
|
|
513
513
|
end
|
|
514
|
-
|
|
514
|
+
|
|
515
515
|
#Parses 1 or more lines of text to create or update existing records.
|
|
516
516
|
|
|
517
517
|
def parse(x=nil)
|
|
518
518
|
|
|
519
519
|
@dirty_flag = true
|
|
520
|
-
|
|
520
|
+
|
|
521
521
|
if x.is_a? Array then
|
|
522
|
-
|
|
522
|
+
|
|
523
523
|
unless @schema then
|
|
524
|
-
cols = x.first.keys.map {|c| c == 'id' ? 'uid' : c}
|
|
524
|
+
cols = x.first.keys.map {|c| c == 'id' ? 'uid' : c}
|
|
525
525
|
self.schema = "items/item(%s)" % cols.join(', ')
|
|
526
526
|
end
|
|
527
|
-
|
|
527
|
+
|
|
528
528
|
x.each {|record| self.create record }
|
|
529
|
-
return self
|
|
530
|
-
|
|
529
|
+
return self
|
|
530
|
+
|
|
531
531
|
end
|
|
532
532
|
raw_buffer, type = RXFHelper.read(x, auto: false)
|
|
533
533
|
|
|
@@ -535,15 +535,15 @@ EOF
|
|
|
535
535
|
|
|
536
536
|
buffer = block_given? ? yield(raw_buffer) : raw_buffer.clone
|
|
537
537
|
string_parse buffer
|
|
538
|
-
|
|
538
|
+
|
|
539
539
|
else
|
|
540
540
|
foreign_import x
|
|
541
541
|
end
|
|
542
|
-
|
|
543
|
-
end
|
|
542
|
+
|
|
543
|
+
end
|
|
544
544
|
|
|
545
545
|
|
|
546
|
-
alias import parse
|
|
546
|
+
alias import parse
|
|
547
547
|
|
|
548
548
|
#Create a record from a hash containing the field name, and the field value.
|
|
549
549
|
# dynarex = Dynarex.new 'contacts/contact(name,age,dob)'
|
|
@@ -553,8 +553,8 @@ EOF
|
|
|
553
553
|
|
|
554
554
|
puts 'inside create' if @debug
|
|
555
555
|
raise 'Dynarex#create(): input error: no arg provided' unless obj
|
|
556
|
-
|
|
557
|
-
case obj.class.to_s.downcase.to_sym
|
|
556
|
+
|
|
557
|
+
case obj.class.to_s.downcase.to_sym
|
|
558
558
|
when :hash
|
|
559
559
|
hash_create obj, id, attr: custom_attributes
|
|
560
560
|
when :string
|
|
@@ -566,7 +566,7 @@ EOF
|
|
|
566
566
|
end
|
|
567
567
|
|
|
568
568
|
@dirty_flag = true
|
|
569
|
-
|
|
569
|
+
|
|
570
570
|
puts 'before save ' + @autosave.inspect if @debug
|
|
571
571
|
save() if @autosave
|
|
572
572
|
|
|
@@ -575,13 +575,13 @@ EOF
|
|
|
575
575
|
|
|
576
576
|
#Create a record from a string, given the dynarex document contains a format mask.
|
|
577
577
|
# dynarex = Dynarex.new 'contacts/contact(name,age,dob)'
|
|
578
|
-
# dynarex.create_from_line 'Tracy 37 15-Jun-1972'
|
|
579
|
-
|
|
578
|
+
# dynarex.create_from_line 'Tracy 37 15-Jun-1972'
|
|
579
|
+
|
|
580
580
|
def create_from_line(line, id=nil, attr: '')
|
|
581
581
|
t = @format_mask.to_s.gsub(/\[!(\w+)\]/, '(.*)').sub(/\[/,'\[')\
|
|
582
582
|
.sub(/\]/,'\]')
|
|
583
583
|
line.match(/#{t}/).captures
|
|
584
|
-
|
|
584
|
+
|
|
585
585
|
a = line.match(/#{t}/).captures
|
|
586
586
|
h = Hash[@fields.zip(a)]
|
|
587
587
|
create h
|
|
@@ -596,8 +596,8 @@ EOF
|
|
|
596
596
|
|
|
597
597
|
|
|
598
598
|
#Updates a record from an id and a hash containing field name and field value.
|
|
599
|
-
# dynarex.update 4, name: Jeff, age: 38
|
|
600
|
-
|
|
599
|
+
# dynarex.update 4, name: Jeff, age: 38
|
|
600
|
+
|
|
601
601
|
def update(id, obj)
|
|
602
602
|
|
|
603
603
|
params = if obj.is_a? Hash then
|
|
@@ -605,32 +605,32 @@ EOF
|
|
|
605
605
|
elsif obj.is_a? RecordX
|
|
606
606
|
obj.to_h
|
|
607
607
|
end
|
|
608
|
-
|
|
608
|
+
|
|
609
609
|
fields = capture_fields(params)
|
|
610
610
|
|
|
611
611
|
# for each field update each record field
|
|
612
|
-
record = @doc.root.element("records/#{@record_name}[@id='#{id.to_s}']")
|
|
612
|
+
record = @doc.root.element("records/#{@record_name}[@id='#{id.to_s}']")
|
|
613
613
|
|
|
614
614
|
fields.each do |k,v|
|
|
615
615
|
puts "updating ... %s = '%s'" % [k,v] if @debug
|
|
616
616
|
record.element(k.to_s).text = v if v
|
|
617
617
|
end
|
|
618
|
-
|
|
618
|
+
|
|
619
619
|
record.add_attribute(last_modified: Time.now.to_s)
|
|
620
620
|
|
|
621
621
|
@dirty_flag = true
|
|
622
622
|
|
|
623
623
|
save() if @autosave
|
|
624
|
-
|
|
624
|
+
|
|
625
625
|
self
|
|
626
626
|
|
|
627
627
|
end
|
|
628
628
|
|
|
629
|
-
|
|
629
|
+
|
|
630
630
|
#Delete a record.
|
|
631
631
|
# dyarex.delete 3 # deletes record with id 3
|
|
632
|
-
|
|
633
|
-
def delete(x)
|
|
632
|
+
|
|
633
|
+
def delete(x)
|
|
634
634
|
|
|
635
635
|
return x.each {|id| self.delete id} if x.is_a? Array
|
|
636
636
|
|
|
@@ -639,86 +639,86 @@ EOF
|
|
|
639
639
|
else
|
|
640
640
|
@doc.delete x
|
|
641
641
|
end
|
|
642
|
-
|
|
642
|
+
|
|
643
643
|
@dirty_flag = true
|
|
644
644
|
save() if @autosave
|
|
645
|
-
|
|
645
|
+
|
|
646
646
|
self
|
|
647
647
|
end
|
|
648
648
|
|
|
649
649
|
def element(x)
|
|
650
650
|
@doc.root.element x
|
|
651
|
-
end
|
|
652
|
-
|
|
651
|
+
end
|
|
652
|
+
|
|
653
653
|
def sort_by!(field=nil, &element_blk)
|
|
654
654
|
|
|
655
655
|
blk = field ? ->(x){ x.text(field.to_s).to_s} : element_blk
|
|
656
656
|
r = sort_records_by! &blk
|
|
657
|
-
@dirty_flag = true
|
|
657
|
+
@dirty_flag = true
|
|
658
658
|
r
|
|
659
659
|
|
|
660
|
-
end
|
|
660
|
+
end
|
|
661
|
+
|
|
661
662
|
|
|
662
|
-
|
|
663
663
|
def record(id)
|
|
664
|
-
e = @doc.root.element("records/*[@id='#{id}']")
|
|
664
|
+
e = @doc.root.element("records/*[@id='#{id}']")
|
|
665
665
|
recordx_to_record e if e
|
|
666
666
|
end
|
|
667
|
-
|
|
667
|
+
|
|
668
668
|
alias find record
|
|
669
669
|
alias find_by_id record
|
|
670
670
|
|
|
671
671
|
def record_exists?(id)
|
|
672
672
|
!@doc.root.element("records/*[@id='#{id}']").nil?
|
|
673
673
|
end
|
|
674
|
-
|
|
674
|
+
|
|
675
675
|
def refresh()
|
|
676
676
|
@dirty_flag = true
|
|
677
677
|
end
|
|
678
|
-
|
|
678
|
+
|
|
679
679
|
def refresh!()
|
|
680
|
-
(load_records; rebuild_doc) if @dirty_flag == true
|
|
680
|
+
(load_records; rebuild_doc) if @dirty_flag == true
|
|
681
681
|
end
|
|
682
|
-
|
|
682
|
+
|
|
683
683
|
# used internally by to_rss()
|
|
684
684
|
#
|
|
685
685
|
def rss_xslt(opt={})
|
|
686
|
-
|
|
686
|
+
|
|
687
687
|
h = {limit: 11}.merge(opt)
|
|
688
688
|
doc = Rexle.new(self.to_xslt)
|
|
689
689
|
e = doc.element('//xsl:apply-templates[2]')
|
|
690
|
-
|
|
690
|
+
|
|
691
691
|
e2 = doc.root.element('xsl:template[3]')
|
|
692
692
|
item = e2.element('item')
|
|
693
693
|
new_item = item.deep_clone
|
|
694
694
|
item.delete
|
|
695
|
-
|
|
695
|
+
|
|
696
696
|
pubdate = @xslt_schema[/pubDate:/]
|
|
697
697
|
xslif = Rexle.new("<xsl:if test='position() < #{h[:limit]}'/>").root
|
|
698
698
|
|
|
699
699
|
if pubdate.nil? then
|
|
700
700
|
pubdate = Rexle.new("<pubDate><xsl:value-of select='pubDate'>" + \
|
|
701
701
|
"</xsl:value-of></pubDate>").root
|
|
702
|
-
new_item.add pubdate
|
|
702
|
+
new_item.add pubdate
|
|
703
703
|
end
|
|
704
704
|
|
|
705
|
-
xslif.add new_item
|
|
705
|
+
xslif.add new_item
|
|
706
706
|
e2.add xslif.root
|
|
707
|
-
xslt = doc.xml
|
|
707
|
+
xslt = doc.xml
|
|
708
|
+
|
|
709
|
+
xslt
|
|
708
710
|
|
|
709
|
-
xslt
|
|
710
|
-
|
|
711
711
|
end
|
|
712
|
-
|
|
712
|
+
|
|
713
713
|
def filter(&blk)
|
|
714
|
-
|
|
714
|
+
|
|
715
715
|
dx = Dynarex.new @schema
|
|
716
716
|
self.all.select(&blk).each {|x| dx.create x}
|
|
717
717
|
dx
|
|
718
|
-
|
|
718
|
+
|
|
719
719
|
end
|
|
720
720
|
|
|
721
|
-
def to_xslt(opt={})
|
|
721
|
+
def to_xslt(opt={})
|
|
722
722
|
|
|
723
723
|
h = {limit: -1}.merge(opt)
|
|
724
724
|
@xslt_schema = @xslt_schema || self.summary[:xslt_schema]
|
|
@@ -728,47 +728,47 @@ EOF
|
|
|
728
728
|
|
|
729
729
|
return xslt
|
|
730
730
|
end
|
|
731
|
-
|
|
731
|
+
|
|
732
732
|
def to_rss(opt={}, xslt=nil)
|
|
733
|
-
|
|
733
|
+
|
|
734
734
|
puts 'inside to_rss'.info if @debug
|
|
735
|
-
|
|
735
|
+
|
|
736
736
|
unless xslt then
|
|
737
|
-
|
|
737
|
+
|
|
738
738
|
h = {limit: 11}.merge(opt)
|
|
739
739
|
doc = Rexle.new(self.to_xslt)
|
|
740
740
|
e = doc.element('//xsl:apply-templates[2]')
|
|
741
|
-
|
|
741
|
+
|
|
742
742
|
e2 = doc.root.element('xsl:template[3]')
|
|
743
743
|
item = e2.element('item')
|
|
744
744
|
new_item = item.deep_clone
|
|
745
745
|
item.delete
|
|
746
|
-
|
|
746
|
+
|
|
747
747
|
pubdate = @xslt_schema[/pubDate:/]
|
|
748
748
|
xslif = Rexle.new("<xsl:if test='position() < #{h[:limit]}'/>").root
|
|
749
|
-
|
|
750
|
-
|
|
749
|
+
|
|
750
|
+
|
|
751
751
|
if pubdate.nil? then
|
|
752
752
|
pubdate2 = Rexle.new("<pubDate><xsl:value-of select='pubDate'></xsl:value-of></pubDate>").root
|
|
753
|
-
new_item.add pubdate2
|
|
753
|
+
new_item.add pubdate2
|
|
754
754
|
end
|
|
755
755
|
|
|
756
|
-
xslif.add new_item
|
|
756
|
+
xslif.add new_item
|
|
757
757
|
e2.add xslif
|
|
758
|
-
xslt = doc.xml
|
|
758
|
+
xslt = doc.xml
|
|
759
759
|
|
|
760
760
|
xslt
|
|
761
761
|
end
|
|
762
|
-
|
|
762
|
+
|
|
763
763
|
doc = Rexle.new(self.to_xml)
|
|
764
|
-
|
|
764
|
+
|
|
765
765
|
puts ('pubdate: ' + pubdate.inspect).debug if @debug
|
|
766
|
-
|
|
766
|
+
|
|
767
767
|
if pubdate.nil? then
|
|
768
768
|
doc.root.xpath('records/*').each do |x|
|
|
769
769
|
raw_dt = DateTime.parse x.attributes[:created]
|
|
770
770
|
dt = raw_dt.strftime("%a, %d %b %Y %H:%M:%S %z")
|
|
771
|
-
x.add Rexle::Element.new('pubDate').add_text dt.to_s
|
|
771
|
+
x.add Rexle::Element.new('pubDate').add_text dt.to_s
|
|
772
772
|
end
|
|
773
773
|
end
|
|
774
774
|
|
|
@@ -776,7 +776,7 @@ EOF
|
|
|
776
776
|
File.write '/tmp/blog.xml', doc.root.xml
|
|
777
777
|
puts ('xslt:' + xslt.inspect) if @debug
|
|
778
778
|
File.write '/tmp/blog.xslt', xslt
|
|
779
|
-
|
|
779
|
+
|
|
780
780
|
out = Rexslt.new(xslt, doc).to_s(declaration: false)
|
|
781
781
|
|
|
782
782
|
#Rexle.new("<rss version='2.0'>%s</rss>" % xml).xml(pretty: true)
|
|
@@ -786,23 +786,23 @@ EOF
|
|
|
786
786
|
xml = doc.xml(pretty: true)
|
|
787
787
|
xml
|
|
788
788
|
end
|
|
789
|
-
|
|
789
|
+
|
|
790
790
|
def unique=(bool)
|
|
791
791
|
self.summary.merge!({unique: bool})
|
|
792
792
|
@dirty_flag = true
|
|
793
793
|
@unique = bool
|
|
794
794
|
end
|
|
795
|
-
|
|
795
|
+
|
|
796
796
|
def xpath(x)
|
|
797
797
|
@doc.root.xpath x
|
|
798
798
|
end
|
|
799
799
|
|
|
800
800
|
def xslt=(value)
|
|
801
|
-
|
|
801
|
+
|
|
802
802
|
self.summary.merge!({xslt: value})
|
|
803
803
|
@dirty_flag = true
|
|
804
804
|
@xslt = value
|
|
805
|
-
end
|
|
805
|
+
end
|
|
806
806
|
|
|
807
807
|
private
|
|
808
808
|
|
|
@@ -810,7 +810,7 @@ EOF
|
|
|
810
810
|
@default_key = :uid
|
|
811
811
|
@summary[:default_key] = 'uid'
|
|
812
812
|
@fields << :uid
|
|
813
|
-
a.each.with_index{|x,i| x << (i+1).to_s}
|
|
813
|
+
a.each.with_index{|x,i| x << (i+1).to_s}
|
|
814
814
|
end
|
|
815
815
|
|
|
816
816
|
def create_find(fields)
|
|
@@ -823,21 +823,42 @@ EOF
|
|
|
823
823
|
end
|
|
824
824
|
|
|
825
825
|
def findx_by(field, value)
|
|
826
|
+
|
|
826
827
|
#@logger.debug "field: #{field.inspect}, value: #{value.inspect}"
|
|
827
828
|
(load_records; rebuild_doc) if @dirty_flag == true
|
|
828
|
-
|
|
829
|
-
|
|
829
|
+
|
|
830
|
+
if value.is_a? String then
|
|
831
|
+
|
|
832
|
+
r = @doc.root.element("records/*[#{field}=\"#{value}\"]")
|
|
833
|
+
r ? recordx_to_record(r) : nil
|
|
834
|
+
|
|
835
|
+
elsif value.is_a? Regexp
|
|
836
|
+
|
|
837
|
+
found = all.select {|x| x.method(field).call =~ /#{value}/i}
|
|
838
|
+
found.first if found.any?
|
|
839
|
+
|
|
840
|
+
end
|
|
841
|
+
|
|
830
842
|
end
|
|
831
843
|
|
|
832
844
|
def findx_all_by(field, value)
|
|
833
|
-
|
|
845
|
+
|
|
846
|
+
if value.is_a? String then
|
|
847
|
+
|
|
848
|
+
@doc.root.xpath("records/*[#{field}=\"#{value}\"]")\
|
|
834
849
|
.map {|x| recordx_to_record x}
|
|
850
|
+
|
|
851
|
+
elsif value.is_a? Regexp
|
|
852
|
+
|
|
853
|
+
all.select {|x| x.method(field).call =~ /#{value}/i}
|
|
854
|
+
|
|
855
|
+
end
|
|
835
856
|
end
|
|
836
857
|
|
|
837
858
|
def recordx_to_record(recordx)
|
|
838
|
-
|
|
859
|
+
|
|
839
860
|
h = recordx.attributes
|
|
840
|
-
|
|
861
|
+
|
|
841
862
|
records = recordx.xpath("*").map {|x| x.text ? x.text.unescape.to_s : '' }
|
|
842
863
|
hash = @fields.zip(records).to_h
|
|
843
864
|
RecordX.new(hash, self, h[:id], h[:created], h[:last_modified])
|
|
@@ -859,21 +880,21 @@ EOF
|
|
|
859
880
|
fields.keys.each {|key| fields[key] = params[key.to_sym] if params.has_key? key.to_sym}
|
|
860
881
|
fields
|
|
861
882
|
end
|
|
862
|
-
|
|
883
|
+
|
|
863
884
|
def display_xml(options={})
|
|
864
885
|
#@logger.debug 'inside display_xml'
|
|
865
886
|
opt = {unescape_html: false}.merge options
|
|
866
|
-
|
|
887
|
+
|
|
867
888
|
state = :external
|
|
868
889
|
#@logger.debug 'before diry'
|
|
869
890
|
if @dirty_flag == true then
|
|
870
|
-
load_records
|
|
891
|
+
load_records
|
|
871
892
|
state = :internal
|
|
872
893
|
end
|
|
873
894
|
#@logger.debug 'before rebuilt'
|
|
874
895
|
doc = rebuild_doc(state)
|
|
875
896
|
#@logger.debug 'after rebuild_doc'
|
|
876
|
-
|
|
897
|
+
|
|
877
898
|
if opt[:unescape_html] == true then
|
|
878
899
|
doc.content(opt)
|
|
879
900
|
else
|
|
@@ -887,15 +908,15 @@ EOF
|
|
|
887
908
|
raw_params.merge!(uid: id) if @default_key.to_sym == :uid
|
|
888
909
|
params = Hash[raw_params.keys.map(&:to_sym).zip(raw_params.values)]
|
|
889
910
|
|
|
890
|
-
fields = capture_fields(params)
|
|
911
|
+
fields = capture_fields(params)
|
|
891
912
|
record = Rexle::Element.new @record_name
|
|
892
913
|
|
|
893
914
|
fields.each do |k,v|
|
|
894
|
-
element = Rexle::Element.new(k.to_s)
|
|
915
|
+
element = Rexle::Element.new(k.to_s)
|
|
895
916
|
element.root.text = v.to_s.gsub('<','<').gsub('>','>') if v
|
|
896
917
|
record.add element if record
|
|
897
918
|
end
|
|
898
|
-
|
|
919
|
+
|
|
899
920
|
attributes = {id: id.to_s, created: Time.now.to_s, last_modified: nil}\
|
|
900
921
|
.merge attr
|
|
901
922
|
attributes.each {|k,v| record.add_attribute(k, v)}
|
|
@@ -912,7 +933,7 @@ EOF
|
|
|
912
933
|
buffer = RXFHelper.read(line.chomp, auto: false).first
|
|
913
934
|
|
|
914
935
|
doc = Rexle.new buffer
|
|
915
|
-
|
|
936
|
+
|
|
916
937
|
if doc.root.name == 'kvx' then
|
|
917
938
|
|
|
918
939
|
kvx = Kvx.new doc
|
|
@@ -924,17 +945,17 @@ EOF
|
|
|
924
945
|
end
|
|
925
946
|
|
|
926
947
|
end
|
|
927
|
-
|
|
948
|
+
|
|
928
949
|
def rebuild_doc(state=:internal)
|
|
929
|
-
|
|
950
|
+
|
|
930
951
|
puts 'inside rebuild_doc'.info if @debug
|
|
931
952
|
|
|
932
|
-
reserved_keywords = (
|
|
953
|
+
reserved_keywords = (
|
|
933
954
|
Object.public_methods | \
|
|
934
955
|
Kernel.public_methods | \
|
|
935
956
|
public_methods + [:method_missing]
|
|
936
957
|
)
|
|
937
|
-
|
|
958
|
+
|
|
938
959
|
xml = RexleBuilder.new
|
|
939
960
|
|
|
940
961
|
a = xml.send @root_name do
|
|
@@ -960,30 +981,30 @@ EOF
|
|
|
960
981
|
#jr160315records.reverse! if @order == 'descending' and state == :external
|
|
961
982
|
|
|
962
983
|
xml.records do
|
|
963
|
-
|
|
984
|
+
|
|
964
985
|
records.each do |k, item|
|
|
965
|
-
|
|
986
|
+
|
|
966
987
|
attributes = {}
|
|
967
|
-
|
|
988
|
+
|
|
968
989
|
item.keys.each do |key|
|
|
969
990
|
attributes[key] = item[key] || '' unless key == :body
|
|
970
991
|
end
|
|
971
|
-
|
|
992
|
+
|
|
972
993
|
if @record_name.nil? then
|
|
973
994
|
raise DynarexException, 'record_name can\'t be nil. Check the schema'
|
|
974
995
|
end
|
|
975
|
-
|
|
996
|
+
|
|
976
997
|
xml.send(@record_name, attributes) do
|
|
977
|
-
item[:body].each do |name,value|
|
|
998
|
+
item[:body].each do |name,value|
|
|
978
999
|
|
|
979
1000
|
if reserved_keywords.include? name then
|
|
980
|
-
name = ('._' + name.to_s).to_sym
|
|
1001
|
+
name = ('._' + name.to_s).to_sym
|
|
981
1002
|
end
|
|
982
|
-
|
|
1003
|
+
|
|
983
1004
|
val = value.send(value.is_a?(String) ? :to_s : :to_yaml)
|
|
984
1005
|
xml.send(name, val.gsub('>','>')\
|
|
985
1006
|
.gsub('<','<')\
|
|
986
|
-
.gsub(/(&\s|&[a-zA-Z\.]+;?)/) do |x|
|
|
1007
|
+
.gsub(/(&\s|&[a-zA-Z\.]+;?)/) do |x|
|
|
987
1008
|
x[-1] == ';' ? x : x.sub('&','&')
|
|
988
1009
|
end
|
|
989
1010
|
)
|
|
@@ -998,22 +1019,22 @@ EOF
|
|
|
998
1019
|
end
|
|
999
1020
|
|
|
1000
1021
|
doc = Rexle.new(a)
|
|
1001
|
-
|
|
1022
|
+
|
|
1002
1023
|
puts ('@xslt: ' + @xslt.inspect).debug if @debug
|
|
1003
|
-
|
|
1024
|
+
|
|
1004
1025
|
if @xslt then
|
|
1005
|
-
doc.instructions = [['xml-stylesheet',
|
|
1026
|
+
doc.instructions = [['xml-stylesheet',
|
|
1006
1027
|
"title='XSL_formatting' type='text/xsl' href='#{@xslt}'"]]
|
|
1007
1028
|
end
|
|
1008
1029
|
|
|
1009
1030
|
return doc if state != :internal
|
|
1010
1031
|
@doc = doc
|
|
1011
|
-
end
|
|
1012
|
-
|
|
1032
|
+
end
|
|
1033
|
+
|
|
1013
1034
|
def string_parse(buffer)
|
|
1014
|
-
|
|
1035
|
+
|
|
1015
1036
|
if @spaces_delimited then
|
|
1016
|
-
buffer = buffer.lines.map{|x| x.gsub(/\s{2,}/,' # ')}.join
|
|
1037
|
+
buffer = buffer.lines.map{|x| x.gsub(/\s{2,}/,' # ')}.join
|
|
1017
1038
|
end
|
|
1018
1039
|
|
|
1019
1040
|
buffer.gsub!("\r",'')
|
|
@@ -1024,25 +1045,25 @@ EOF
|
|
|
1024
1045
|
buffer.gsub!(/<./) {|x| x[1] != '?' ? x.sub(/</,'<') : x }
|
|
1025
1046
|
|
|
1026
1047
|
@raw_header = buffer[/<\?dynarex[^>]+>/]
|
|
1027
|
-
|
|
1048
|
+
|
|
1028
1049
|
if buffer[/<\?/] then
|
|
1029
1050
|
|
|
1030
1051
|
raw_stylesheet = buffer.slice!(/<\?xml-stylesheet[^>]+>/)
|
|
1031
1052
|
@xslt = raw_stylesheet[/href=["']([^"']+)/,1] if raw_stylesheet
|
|
1032
1053
|
@raw_header = buffer.slice!(/<\?dynarex[^>]+>/) + "\n"
|
|
1033
|
-
|
|
1054
|
+
|
|
1034
1055
|
header = @raw_header[/<?dynarex (.*)?>/,1]
|
|
1035
1056
|
|
|
1036
1057
|
r1 = /([\w\-]+\s*\=\s*'[^']*)'/
|
|
1037
1058
|
r2 = /([\w\-]+\s*\=\s*"[^"]*)"/
|
|
1038
1059
|
|
|
1039
|
-
r = header.scan(/#{r1}|#{r2}/).map(&:compact).flatten
|
|
1060
|
+
r = header.scan(/#{r1}|#{r2}/).map(&:compact).flatten
|
|
1040
1061
|
|
|
1041
1062
|
r.each do |x|
|
|
1042
1063
|
|
|
1043
1064
|
attr, val = x.split(/\s*=\s*["']/,2)
|
|
1044
1065
|
name = (attr + '=').to_sym
|
|
1045
|
-
|
|
1066
|
+
|
|
1046
1067
|
if self.public_methods.include? name then
|
|
1047
1068
|
self.method(name).call(unescape val)
|
|
1048
1069
|
else
|
|
@@ -1062,7 +1083,7 @@ EOF
|
|
|
1062
1083
|
if raw_summary then
|
|
1063
1084
|
|
|
1064
1085
|
a_summary = raw_summary.split(',').map(&:strip)
|
|
1065
|
-
|
|
1086
|
+
|
|
1066
1087
|
@summary ||= {}
|
|
1067
1088
|
raw_lines.shift while raw_lines.first.strip.empty?
|
|
1068
1089
|
|
|
@@ -1082,7 +1103,7 @@ EOF
|
|
|
1082
1103
|
@summary[:schema] = @schema
|
|
1083
1104
|
@summary[:format_mask] = @format_mask
|
|
1084
1105
|
@summary[:unique] = @unique if @unique
|
|
1085
|
-
|
|
1106
|
+
|
|
1086
1107
|
raw_lines.shift while raw_lines.first.strip.empty?
|
|
1087
1108
|
|
|
1088
1109
|
lines = case raw_lines.first.rstrip
|
|
@@ -1092,10 +1113,10 @@ EOF
|
|
|
1092
1113
|
yaml = YAML.load raw_lines.join("\n")
|
|
1093
1114
|
|
|
1094
1115
|
yamlize = lambda {|x| (x.is_a? Array) ? x.to_yaml : x}
|
|
1095
|
-
|
|
1116
|
+
|
|
1096
1117
|
yprocs = {
|
|
1097
1118
|
Hash: lambda {|y|
|
|
1098
|
-
y.map do |k,v|
|
|
1119
|
+
y.map do |k,v|
|
|
1099
1120
|
procs = {Hash: proc {|x| x.values}, Array: proc {v}}
|
|
1100
1121
|
values = procs[v.class.to_s.to_sym].call(v).map(&yamlize)
|
|
1101
1122
|
[k, *values]
|
|
@@ -1104,23 +1125,23 @@ EOF
|
|
|
1104
1125
|
Array: lambda {|y| y.map {|x2| x2.map(&yamlize)} }
|
|
1105
1126
|
}
|
|
1106
1127
|
|
|
1107
|
-
yprocs[yaml.class.to_s.to_sym].call yaml
|
|
1108
|
-
|
|
1128
|
+
yprocs[yaml.class.to_s.to_sym].call yaml
|
|
1129
|
+
|
|
1109
1130
|
when '--+'
|
|
1110
1131
|
|
|
1111
1132
|
rowx(raw_lines)
|
|
1112
|
-
|
|
1133
|
+
|
|
1113
1134
|
when '--#'
|
|
1114
1135
|
|
|
1115
1136
|
self.summary[:rawdoc_type] = 'sectionx'
|
|
1116
1137
|
raw_lines.shift
|
|
1117
1138
|
|
|
1118
1139
|
raw_lines.join.lstrip.split(/(?=^#[^#])/).map {|x| [x.rstrip]}
|
|
1119
|
-
|
|
1140
|
+
|
|
1120
1141
|
else
|
|
1121
1142
|
|
|
1122
1143
|
raw_lines = raw_lines.join("\n").gsub(/^(\s*#[^\n]+|\n)/,'').lines.to_a
|
|
1123
|
-
|
|
1144
|
+
|
|
1124
1145
|
if @linked then
|
|
1125
1146
|
|
|
1126
1147
|
parse_links(raw_lines)
|
|
@@ -1129,42 +1150,42 @@ EOF
|
|
|
1129
1150
|
a2 = raw_lines.map.with_index do |x,i|
|
|
1130
1151
|
|
|
1131
1152
|
next if x[/^\s+$|\n\s*#/]
|
|
1132
|
-
|
|
1153
|
+
|
|
1133
1154
|
begin
|
|
1134
|
-
|
|
1155
|
+
|
|
1135
1156
|
field_names, field_values = RXRawLineParser.new(@format_mask).parse(x)
|
|
1136
1157
|
rescue
|
|
1137
1158
|
raise "input file parser error at line " + (i + 1).to_s + ' --> ' + x
|
|
1138
1159
|
end
|
|
1139
1160
|
field_values
|
|
1140
1161
|
end
|
|
1141
|
-
|
|
1162
|
+
|
|
1142
1163
|
a2.compact!
|
|
1143
1164
|
a3 = a2.compact.map(&:first)
|
|
1144
|
-
|
|
1165
|
+
|
|
1145
1166
|
if a3 != a3.uniq then
|
|
1146
|
-
|
|
1167
|
+
|
|
1147
1168
|
if @unique then
|
|
1148
1169
|
raise DynarexException, "Duplicate id found"
|
|
1149
1170
|
else
|
|
1150
|
-
add_id(a2)
|
|
1171
|
+
add_id(a2)
|
|
1151
1172
|
end
|
|
1152
|
-
|
|
1173
|
+
|
|
1153
1174
|
end
|
|
1154
|
-
|
|
1175
|
+
|
|
1155
1176
|
a2
|
|
1156
|
-
end
|
|
1177
|
+
end
|
|
1157
1178
|
|
|
1158
1179
|
end
|
|
1159
1180
|
|
|
1160
|
-
a = lines.map.with_index do |x,i|
|
|
1161
|
-
|
|
1181
|
+
a = lines.map.with_index do |x,i|
|
|
1182
|
+
|
|
1162
1183
|
created = Time.now.to_s
|
|
1163
1184
|
|
|
1164
1185
|
h = Hash[
|
|
1165
1186
|
@fields.zip(
|
|
1166
1187
|
x.map do |t|
|
|
1167
|
-
|
|
1188
|
+
|
|
1168
1189
|
t.to_s[/^---(?:\s|\n)/] ? YAML.load(t[/^---(?:\s|\n)(.*)/,1]) : unescape(t.to_s)
|
|
1169
1190
|
end
|
|
1170
1191
|
)
|
|
@@ -1186,12 +1207,12 @@ EOF
|
|
|
1186
1207
|
item[:body].each do |k,v|
|
|
1187
1208
|
h[key][:body][k.to_sym] = v
|
|
1188
1209
|
end
|
|
1189
|
-
else
|
|
1210
|
+
else
|
|
1190
1211
|
item[:id] = (@order == 'descending' ? (h2.count) - i : i+ 1).to_s
|
|
1191
1212
|
i += 1
|
|
1192
1213
|
h[key] = item.clone
|
|
1193
|
-
end
|
|
1194
|
-
end
|
|
1214
|
+
end
|
|
1215
|
+
end
|
|
1195
1216
|
|
|
1196
1217
|
h.each {|key, item| h.delete(key) if not h2.has_key? key}
|
|
1197
1218
|
|
|
@@ -1200,9 +1221,9 @@ EOF
|
|
|
1200
1221
|
rebuild_doc
|
|
1201
1222
|
self
|
|
1202
1223
|
end
|
|
1203
|
-
|
|
1224
|
+
|
|
1204
1225
|
def sort_records_by!(&element_blk)
|
|
1205
|
-
|
|
1226
|
+
|
|
1206
1227
|
refresh_doc
|
|
1207
1228
|
a = @doc.root.xpath('records/*').sort_by &element_blk
|
|
1208
1229
|
@doc.root.delete('records')
|
|
@@ -1215,33 +1236,33 @@ EOF
|
|
|
1215
1236
|
|
|
1216
1237
|
load_records if @dirty_flag
|
|
1217
1238
|
self
|
|
1218
|
-
end
|
|
1239
|
+
end
|
|
1219
1240
|
|
|
1220
1241
|
def unescape(s)
|
|
1221
1242
|
s.gsub('<', '<').gsub('>','>')
|
|
1222
1243
|
end
|
|
1223
1244
|
|
|
1224
1245
|
def dynarex_new(s, default_key: nil)
|
|
1225
|
-
|
|
1246
|
+
|
|
1226
1247
|
@schema = schema = s
|
|
1227
1248
|
@default_key = default_key if default_key
|
|
1228
|
-
|
|
1249
|
+
|
|
1229
1250
|
ptrn = %r((\w+)\[?([^\]]+)?\]?\/(\w+)\(([^\)]+)\))
|
|
1230
1251
|
|
|
1231
1252
|
if s.match(ptrn) then
|
|
1232
|
-
|
|
1233
|
-
@root_name, raw_summary, record_name, raw_fields = s.match(ptrn).captures
|
|
1253
|
+
|
|
1254
|
+
@root_name, raw_summary, record_name, raw_fields = s.match(ptrn).captures
|
|
1234
1255
|
summary, fields = [raw_summary || '',raw_fields].map {|x| x.split(/,/).map &:strip}
|
|
1235
|
-
|
|
1256
|
+
|
|
1236
1257
|
if fields.include? 'id' then
|
|
1237
|
-
raise 'Dynarex#dynarex_new: schema field id is a reserved keyword'
|
|
1258
|
+
raise 'Dynarex#dynarex_new: schema field id is a reserved keyword'
|
|
1238
1259
|
end
|
|
1239
|
-
|
|
1260
|
+
|
|
1240
1261
|
create_find fields
|
|
1241
|
-
|
|
1262
|
+
|
|
1242
1263
|
reserved = %w(require parent)
|
|
1243
1264
|
raise 'reserved keyword' if (fields & reserved).any?
|
|
1244
|
-
|
|
1265
|
+
|
|
1245
1266
|
else
|
|
1246
1267
|
ptrn = %r((\w+)\[?([^\]]+)?\]?)
|
|
1247
1268
|
@root_name, raw_summary = s.match(ptrn).captures
|
|
@@ -1264,36 +1285,36 @@ EOF
|
|
|
1264
1285
|
def attach_record_methods()
|
|
1265
1286
|
create_find @fields
|
|
1266
1287
|
end
|
|
1267
|
-
|
|
1288
|
+
|
|
1268
1289
|
def openx(s)
|
|
1269
1290
|
#@logger.debug 'inside openx'
|
|
1270
1291
|
if s[/</] then # xml
|
|
1271
1292
|
#@logger.debug 'regular string'
|
|
1272
1293
|
#@logger.debug 's: ' + s.inspect
|
|
1273
1294
|
buffer = s
|
|
1274
|
-
|
|
1295
|
+
|
|
1275
1296
|
elsif s[/[\[\(]/] # schema
|
|
1276
1297
|
|
|
1277
1298
|
dynarex_new(s)
|
|
1278
|
-
|
|
1299
|
+
|
|
1279
1300
|
elsif s[/^https?:\/\//] then # url
|
|
1280
|
-
buffer, type = RXFHelper.read s, {username: @username,
|
|
1281
|
-
password: @password, auto: false}
|
|
1282
|
-
elsif s[/^dfs?:\/\//] then
|
|
1283
|
-
|
|
1284
|
-
@local_filepath = s
|
|
1301
|
+
buffer, type = RXFHelper.read s, {username: @username,
|
|
1302
|
+
password: @password, auto: false}
|
|
1303
|
+
elsif s[/^dfs?:\/\//] then
|
|
1304
|
+
|
|
1305
|
+
@local_filepath = s
|
|
1285
1306
|
|
|
1286
1307
|
if FileX.exists? s then
|
|
1287
1308
|
buffer = FileX.read s
|
|
1288
1309
|
elsif @schema
|
|
1289
|
-
dynarex_new @schema, default_key: @default_key
|
|
1290
|
-
end
|
|
1291
|
-
|
|
1310
|
+
dynarex_new @schema, default_key: @default_key
|
|
1311
|
+
end
|
|
1312
|
+
|
|
1292
1313
|
else # local file
|
|
1293
|
-
|
|
1314
|
+
|
|
1294
1315
|
@local_filepath = s
|
|
1295
|
-
|
|
1296
|
-
if File.exists? s then
|
|
1316
|
+
|
|
1317
|
+
if File.exists? s then
|
|
1297
1318
|
buffer = File.read s
|
|
1298
1319
|
elsif @schema
|
|
1299
1320
|
dynarex_new @schema, default_key: @default_key
|
|
@@ -1302,18 +1323,18 @@ EOF
|
|
|
1302
1323
|
end
|
|
1303
1324
|
end
|
|
1304
1325
|
#@logger.debug 'buffer: ' + buffer[0..120]
|
|
1305
|
-
|
|
1326
|
+
|
|
1306
1327
|
return import(buffer) if buffer =~ /^<\?dynarex\b/
|
|
1307
1328
|
|
|
1308
1329
|
if buffer then
|
|
1309
1330
|
|
|
1310
1331
|
raw_stylesheet = buffer.slice!(/<\?xml-stylesheet[^>]+>/)
|
|
1311
1332
|
@xslt = raw_stylesheet[/href=["']([^"']+)/,1] if raw_stylesheet
|
|
1312
|
-
|
|
1313
|
-
@doc = Rexle.new(buffer) unless @doc
|
|
1333
|
+
|
|
1334
|
+
@doc = Rexle.new(buffer) unless @doc
|
|
1314
1335
|
#@logger.debug 'openx/@doc : ' + @doc.xml.inspect
|
|
1315
1336
|
end
|
|
1316
|
-
|
|
1337
|
+
|
|
1317
1338
|
return if @doc.root.nil?
|
|
1318
1339
|
e = @doc.root.element('summary')
|
|
1319
1340
|
|
|
@@ -1322,20 +1343,20 @@ EOF
|
|
|
1322
1343
|
@summary = summary_to_h
|
|
1323
1344
|
|
|
1324
1345
|
summary_methods = (@summary.keys - self.public_methods)
|
|
1325
|
-
|
|
1346
|
+
|
|
1326
1347
|
summary_methods.each do |x|
|
|
1327
|
-
|
|
1348
|
+
|
|
1328
1349
|
instance_eval "
|
|
1329
|
-
|
|
1350
|
+
|
|
1330
1351
|
def #{x.to_sym}()
|
|
1331
1352
|
@summary[:#{x}]
|
|
1332
1353
|
end
|
|
1333
|
-
|
|
1354
|
+
|
|
1334
1355
|
def #{x.to_s}=(v)
|
|
1335
1356
|
@summary[:#{x}] = v
|
|
1336
1357
|
@doc.root.element('summary/#{x.to_s}').text = v
|
|
1337
1358
|
end
|
|
1338
|
-
"
|
|
1359
|
+
"
|
|
1339
1360
|
end
|
|
1340
1361
|
|
|
1341
1362
|
@order = @summary[:order] if @summary.has_key? :order
|
|
@@ -1357,7 +1378,7 @@ EOF
|
|
|
1357
1378
|
|
|
1358
1379
|
if @fields then
|
|
1359
1380
|
|
|
1360
|
-
@default_key = @fields[0] unless @default_key
|
|
1381
|
+
@default_key = @fields[0] unless @default_key
|
|
1361
1382
|
# load the record query handler methods
|
|
1362
1383
|
attach_record_methods
|
|
1363
1384
|
else
|
|
@@ -1365,46 +1386,46 @@ EOF
|
|
|
1365
1386
|
#jr080912 @default_key = @doc.root.xpath('records/*/*').first.name
|
|
1366
1387
|
@default_key = @doc.root.element('records/./.[1]').name
|
|
1367
1388
|
end
|
|
1368
|
-
|
|
1389
|
+
|
|
1369
1390
|
@summary[:default_key] = @default_key.to_s
|
|
1370
|
-
|
|
1391
|
+
|
|
1371
1392
|
if @doc.root.xpath('records/*').length > 0 then
|
|
1372
|
-
@record_name = @doc.root.element('records/*[1]').name
|
|
1393
|
+
@record_name = @doc.root.element('records/*[1]').name
|
|
1373
1394
|
#jr240913 load_records
|
|
1374
1395
|
@dirty_flag = true
|
|
1375
1396
|
end
|
|
1376
1397
|
|
|
1377
|
-
end
|
|
1398
|
+
end
|
|
1378
1399
|
|
|
1379
1400
|
def load_records
|
|
1380
|
-
|
|
1401
|
+
|
|
1381
1402
|
puts 'inside load_records'.info if @debug
|
|
1382
|
-
|
|
1403
|
+
|
|
1383
1404
|
@dirty_flag = false
|
|
1384
|
-
|
|
1405
|
+
|
|
1385
1406
|
if @summary[:order] then
|
|
1386
|
-
orderfield = @summary[:order][/(\w+)\s+(?:ascending|descending)/,1]
|
|
1407
|
+
orderfield = @summary[:order][/(\w+)\s+(?:ascending|descending)/,1]
|
|
1387
1408
|
sort_records_by! {|x| x.element(orderfield).text } if orderfield
|
|
1388
1409
|
end
|
|
1389
|
-
|
|
1410
|
+
|
|
1390
1411
|
@records = records_to_h
|
|
1391
|
-
|
|
1412
|
+
|
|
1392
1413
|
@records.instance_eval do
|
|
1393
1414
|
def delete_item(i)
|
|
1394
1415
|
self.delete self.keys[i]
|
|
1395
1416
|
end
|
|
1396
1417
|
end
|
|
1397
|
-
|
|
1418
|
+
|
|
1398
1419
|
#Returns a ready-only snapshot of records as a simple Hash.
|
|
1399
1420
|
@flat_records = @records.values.map{|x| x[:body]}
|
|
1400
1421
|
|
|
1401
1422
|
end
|
|
1402
|
-
|
|
1423
|
+
|
|
1403
1424
|
|
|
1404
1425
|
def display()
|
|
1405
1426
|
puts @doc.to_s
|
|
1406
1427
|
end
|
|
1407
|
-
|
|
1428
|
+
|
|
1408
1429
|
def records_to_h(order=:ascending)
|
|
1409
1430
|
|
|
1410
1431
|
i = @doc.root.xpath('max(records/*/attribute::id)') || 0
|
|
@@ -1425,25 +1446,25 @@ EOF
|
|
|
1425
1446
|
end
|
|
1426
1447
|
|
|
1427
1448
|
body = (@fields - ['uid']).inject({}) do |r,field|
|
|
1428
|
-
|
|
1449
|
+
|
|
1429
1450
|
node = row.element field.to_s
|
|
1430
1451
|
|
|
1431
1452
|
if node then
|
|
1432
1453
|
text = node.text ? node.text.unescape : ''
|
|
1433
1454
|
|
|
1434
|
-
r.merge node.name.to_sym => (text[/^---(?:\s|\n)/] ?
|
|
1455
|
+
r.merge node.name.to_sym => (text[/^---(?:\s|\n)/] ?
|
|
1435
1456
|
YAML.load(text[/^---(?:\s|\n)(.*)/,1]) : text)
|
|
1436
1457
|
else
|
|
1437
1458
|
r
|
|
1438
1459
|
end
|
|
1439
1460
|
end
|
|
1440
|
-
|
|
1461
|
+
|
|
1441
1462
|
body[:uid] = id if @default_key == 'uid'
|
|
1442
1463
|
|
|
1443
1464
|
attributes = row.attributes
|
|
1444
1465
|
result.merge body[@default_key.to_sym] => attributes.merge({id: id, body: body})
|
|
1445
1466
|
end
|
|
1446
|
-
|
|
1467
|
+
|
|
1447
1468
|
puts 'records_to_h a: ' + a.inspect if @debug
|
|
1448
1469
|
#@logger.debug 'a: ' + a.inspect
|
|
1449
1470
|
a
|
|
@@ -1459,56 +1480,56 @@ EOF
|
|
|
1459
1480
|
|
|
1460
1481
|
# get the fields
|
|
1461
1482
|
a4 = a3.map{|x| x.scan(/^\w+(?=:)/)}.flatten(1).uniq
|
|
1462
|
-
|
|
1483
|
+
|
|
1463
1484
|
abbrv_fields = a4.all? {|x| x.length == 1}
|
|
1464
|
-
|
|
1485
|
+
|
|
1465
1486
|
a5 = a3.map do |xlines|
|
|
1466
|
-
|
|
1487
|
+
|
|
1467
1488
|
puts 'xlines: ' + xlines.inspect if @debug
|
|
1468
|
-
|
|
1489
|
+
|
|
1469
1490
|
missing_fields = a4 - xlines.scan(/^\w+(?=:)/)
|
|
1470
1491
|
|
|
1471
1492
|
r = xlines.split(/\n(\w+:.*)/m)
|
|
1472
1493
|
puts 'r: ' + r.inspect if @debug
|
|
1473
|
-
|
|
1494
|
+
|
|
1474
1495
|
missing_fields.map!{|x| x + ":"}
|
|
1475
1496
|
key = (abbrv_fields ? @fields[0].to_s[0] : @fields.first.to_s) + ':'
|
|
1476
|
-
|
|
1497
|
+
|
|
1477
1498
|
if missing_fields.include? key
|
|
1478
1499
|
r.unshift key
|
|
1479
1500
|
missing_fields.delete key
|
|
1480
1501
|
end
|
|
1481
|
-
|
|
1502
|
+
|
|
1482
1503
|
r += missing_fields
|
|
1483
1504
|
r.join("\n")
|
|
1484
|
-
|
|
1505
|
+
|
|
1485
1506
|
end
|
|
1486
1507
|
puts 'a5: ' + a5.inspect if @debug
|
|
1487
|
-
|
|
1508
|
+
|
|
1488
1509
|
xml = RowX.new(a5.join("\n").strip, level: 0).to_xml
|
|
1489
1510
|
puts 'xml: ' + xml.inspect if @debug
|
|
1490
|
-
|
|
1491
|
-
a2 = Rexle.new(xml).root.xpath('item').inject([]) do |r,x|
|
|
1511
|
+
|
|
1512
|
+
a2 = Rexle.new(xml).root.xpath('item').inject([]) do |r,x|
|
|
1492
1513
|
|
|
1493
1514
|
r << @fields.map do |field|
|
|
1494
1515
|
x.text(abbrv_fields ? field.to_s.chr : field.to_s )
|
|
1495
1516
|
end
|
|
1496
|
-
|
|
1517
|
+
|
|
1497
1518
|
end
|
|
1498
1519
|
|
|
1499
|
-
a2.compact!
|
|
1500
|
-
|
|
1501
|
-
# if there is no field value for the first field then
|
|
1520
|
+
a2.compact!
|
|
1521
|
+
|
|
1522
|
+
# if there is no field value for the first field then
|
|
1502
1523
|
# the default_key is invalid. The default_key is changed to an ID.
|
|
1503
1524
|
if a2.detect {|x| x.first == ''} then
|
|
1504
1525
|
add_id(a2)
|
|
1505
1526
|
else
|
|
1506
1527
|
|
|
1507
1528
|
a3 = a2.map(&:first)
|
|
1508
|
-
add_id(a2) if a3 != a3.uniq
|
|
1509
|
-
|
|
1529
|
+
add_id(a2) if a3 != a3.uniq
|
|
1530
|
+
|
|
1510
1531
|
end
|
|
1511
|
-
|
|
1532
|
+
|
|
1512
1533
|
a2
|
|
1513
1534
|
|
|
1514
1535
|
end
|
|
@@ -1544,13 +1565,13 @@ XSL
|
|
|
1544
1565
|
@doc = Rexle.new(Rexslt.new(xsl, self.to_xml).to_s)
|
|
1545
1566
|
@dirty_flag = true
|
|
1546
1567
|
end
|
|
1547
|
-
|
|
1568
|
+
|
|
1548
1569
|
def summary_to_h
|
|
1549
1570
|
|
|
1550
1571
|
h = {recordx_type: 'dynarex'}
|
|
1551
|
-
|
|
1572
|
+
|
|
1552
1573
|
@doc.root.xpath('summary/*').inject(h) do |r,node|
|
|
1553
|
-
r.merge node.name.to_s.to_sym =>
|
|
1574
|
+
r.merge node.name.to_s.to_sym =>
|
|
1554
1575
|
node.text ? node.text.unescape : node.text.to_s
|
|
1555
1576
|
end
|
|
1556
1577
|
end
|