dynarex 0.6.6 → 0.7.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.
- data/lib/dynarex.rb +40 -3
- metadata +2 -3
- data/lib/dynarexx.rb +0 -267
data/lib/dynarex.rb
CHANGED
@@ -10,22 +10,37 @@ require 'builder'
|
|
10
10
|
class Dynarex
|
11
11
|
include REXML
|
12
12
|
|
13
|
+
#Create a new dynarex document from 1 of the following options:
|
14
|
+
#* a local file path
|
15
|
+
#* a URL
|
16
|
+
#* a schema string
|
17
|
+
# Dynarex.new 'contacts[title,description]/contact(name,age,dob)'
|
18
|
+
#* an XML string
|
19
|
+
# Dynarex.new '<contacts><summary><schema>contacts/contact(name,age,dob)</schema></summary><records/></contacts>'
|
20
|
+
|
13
21
|
def initialize(location)
|
14
22
|
open(location)
|
15
23
|
end
|
16
24
|
|
25
|
+
# Returns the hash representation of the document summary.
|
26
|
+
|
17
27
|
def summary
|
18
28
|
@summary
|
19
29
|
end
|
20
30
|
|
31
|
+
#Return a Hash (which can be edited) containing all records.
|
32
|
+
|
21
33
|
def records
|
22
34
|
@records
|
23
35
|
end
|
24
36
|
|
37
|
+
#Returns a ready-only snapshot of records as a simple Hash.
|
25
38
|
def flat_records
|
26
39
|
@flat_records
|
27
40
|
end
|
28
41
|
|
42
|
+
# Returns all records as a string format specified by the summary format_mask field.
|
43
|
+
|
29
44
|
def to_s
|
30
45
|
xsl_buffer =<<EOF
|
31
46
|
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
@@ -56,24 +71,27 @@ EOF
|
|
56
71
|
display_xml()
|
57
72
|
end
|
58
73
|
|
74
|
+
#Save the document to a local file.
|
75
|
+
|
59
76
|
def save(filepath)
|
60
77
|
xml = display_xml()
|
61
78
|
File.open(filepath,'w'){|f| f.write xml}
|
62
79
|
end
|
63
80
|
|
81
|
+
#Parses 1 or more lines of text to create or update existing records.
|
82
|
+
|
64
83
|
def parse(buffer)
|
65
84
|
i = XPath.match(@doc.root, 'records/*/attribute::id').max_by(&:value).to_s.to_i
|
66
85
|
format_mask = XPath.first(@doc.root, 'summary/format_mask/text()').to_s
|
67
86
|
t = format_mask.to_s.gsub(/\[!(\w+)\]/, '(.*)').sub(/\[/,'\[').sub(/\]/,'\]')
|
68
87
|
lines = buffer.split(/\r?\n|\r(?!\n)/).map {|x|x.match(/#{t}/).captures}
|
69
88
|
fields = format_mask.scan(/\[!(\w+)\]/).flatten.map(&:to_sym)
|
70
|
-
@default_key ||= fields[0]
|
71
89
|
|
72
90
|
a = lines.map do|x|
|
73
91
|
created = Time.now.to_s
|
74
92
|
|
75
93
|
h = Hash[fields.zip(x)]
|
76
|
-
[h[@default_key
|
94
|
+
[h[@default_key], {id: '', created: created, last_modified: '', body: h}]
|
77
95
|
end
|
78
96
|
|
79
97
|
h2 = Hash[a]
|
@@ -98,6 +116,10 @@ EOF
|
|
98
116
|
h.each {|key, item| h.delete(key) if not h2.has_key? key}
|
99
117
|
self
|
100
118
|
end
|
119
|
+
|
120
|
+
#Create a record from a hash containing the field name, and the field value.
|
121
|
+
# dynarex = Dynarex.new 'contacts/contact(name,age,dob)'
|
122
|
+
# dynarex.create name: Bob, age: 52
|
101
123
|
|
102
124
|
def create(params={})
|
103
125
|
|
@@ -117,8 +139,13 @@ EOF
|
|
117
139
|
@doc.root.elements['records'].add record
|
118
140
|
|
119
141
|
load_records
|
142
|
+
self
|
120
143
|
end
|
121
144
|
|
145
|
+
#Create a record from a string, given the dynarex document contains a format mask.
|
146
|
+
# dynarex = Dynarex.new 'contacts/contact(name,age,dob)'
|
147
|
+
# dynarex.create_from_line 'Tracy 37 15-Jun-1972'
|
148
|
+
|
122
149
|
def create_from_line(line)
|
123
150
|
format_mask = XPath.first(@doc.root, 'summary/format_mask/text()').to_s
|
124
151
|
t = format_mask.to_s.gsub(/\[!(\w+)\]/, '(.*)').sub(/\[/,'\[').sub(/\]/,'\]')
|
@@ -128,8 +155,12 @@ EOF
|
|
128
155
|
fields = format_mask.scan(/\[!(\w+)\]/).flatten.map(&:to_sym)
|
129
156
|
h = Hash[fields.zip(a)]
|
130
157
|
create h
|
158
|
+
self
|
131
159
|
end
|
132
160
|
|
161
|
+
#Updates a record from an id and a hash containing field name and field value.
|
162
|
+
# dynarex.update 4, name: Jeff, age: 38
|
163
|
+
|
133
164
|
def update(id, params={})
|
134
165
|
record_name, fields = capture_fields(params)
|
135
166
|
|
@@ -139,12 +170,17 @@ EOF
|
|
139
170
|
record.add_attribute('last_modified', Time.now.to_s)
|
140
171
|
|
141
172
|
load_records
|
173
|
+
self
|
142
174
|
end
|
143
175
|
|
176
|
+
#Delete a record.
|
177
|
+
# dyarex.delete 3 # deletes record with id 3
|
178
|
+
|
144
179
|
def delete(id)
|
145
180
|
node = XPath.first(@doc.root, "records/*[@id='#{id}']")
|
146
181
|
node.parent.delete node
|
147
182
|
load_records
|
183
|
+
self
|
148
184
|
end
|
149
185
|
|
150
186
|
private
|
@@ -205,7 +241,6 @@ EOF
|
|
205
241
|
end
|
206
242
|
|
207
243
|
@default_key = fields[0]
|
208
|
-
@record_name = record_name
|
209
244
|
@records = {}
|
210
245
|
@flat_records = {}
|
211
246
|
|
@@ -246,6 +281,8 @@ EOF
|
|
246
281
|
puts @doc.to_s
|
247
282
|
end
|
248
283
|
|
284
|
+
#Returns a ready-only snapshot of records as a simple Hash.
|
285
|
+
|
249
286
|
def flat_records_to_h
|
250
287
|
XPath.match(@doc.root, 'records/*').map do |row|
|
251
288
|
XPath.match(row, '*').inject({}) do |r,node|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynarex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors: []
|
7
7
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-05-
|
12
|
+
date: 2010-05-16 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -22,7 +22,6 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
-
- lib/dynarexx.rb
|
26
25
|
- lib/dynarex.rb
|
27
26
|
has_rdoc: true
|
28
27
|
homepage:
|
data/lib/dynarexx.rb
DELETED
@@ -1,267 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
# file: dynarex.rb
|
4
|
-
|
5
|
-
require 'rexml/document'
|
6
|
-
require 'nokogiri'
|
7
|
-
require 'open-uri'
|
8
|
-
require 'builder'
|
9
|
-
|
10
|
-
class Dynarex
|
11
|
-
include REXML
|
12
|
-
|
13
|
-
def initialize(location)
|
14
|
-
open(location)
|
15
|
-
end
|
16
|
-
|
17
|
-
def summary
|
18
|
-
@summary
|
19
|
-
end
|
20
|
-
|
21
|
-
def records
|
22
|
-
@records
|
23
|
-
end
|
24
|
-
|
25
|
-
def flat_records
|
26
|
-
@flat_records
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_s
|
30
|
-
xsl_buffer =<<EOF
|
31
|
-
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
32
|
-
<xsl:output encoding="UTF-8"
|
33
|
-
method="text"
|
34
|
-
indent="no"
|
35
|
-
omit-xml-declaration="yes"/>
|
36
|
-
|
37
|
-
<xsl:template match="*">
|
38
|
-
<xsl:for-each select="records/*">[!regex_values]<xsl:text>
|
39
|
-
</xsl:text>
|
40
|
-
</xsl:for-each>
|
41
|
-
</xsl:template>
|
42
|
-
</xsl:stylesheet>
|
43
|
-
EOF
|
44
|
-
|
45
|
-
format_mask = XPath.first(@doc.root, 'summary/format_mask/text()').to_s
|
46
|
-
|
47
|
-
xslt_format = format_mask.to_s.gsub(/\s(?=\[!\w+\])/,'<xsl:text> </xsl:text>').gsub(/\[!(\w+)\]/, '<xsl:value-of select="\1"/>')
|
48
|
-
xsl_buffer.sub!(/\[!regex_values\]/, xslt_format)
|
49
|
-
|
50
|
-
xslt = Nokogiri::XSLT(xsl_buffer)
|
51
|
-
out = xslt.transform(Nokogiri::XML(@doc.to_s))
|
52
|
-
out.text
|
53
|
-
end
|
54
|
-
|
55
|
-
def to_xml
|
56
|
-
display_xml()
|
57
|
-
end
|
58
|
-
|
59
|
-
def save(filepath)
|
60
|
-
xml = display_xml()
|
61
|
-
File.open(filepath,'w'){|f| f.write xml}
|
62
|
-
end
|
63
|
-
|
64
|
-
def parse(buffer)
|
65
|
-
i = XPath.match(@doc.root, 'records/*/attribute::id').max_by(&:value).to_s.to_i
|
66
|
-
format_mask = XPath.first(@doc.root, 'summary/format_mask/text()').to_s
|
67
|
-
t = format_mask.to_s.gsub(/\[!(\w+)\]/, '(.*)').sub(/\[/,'\[').sub(/\]/,'\]')
|
68
|
-
lines = buffer.split(/\r?\n|\r(?!\n)/).map {|x|x.match(/#{t}/).captures}
|
69
|
-
fields = format_mask.scan(/\[!(\w+)\]/).flatten.map(&:to_sym)
|
70
|
-
|
71
|
-
a = lines.map do|x|
|
72
|
-
created = Time.now.to_s
|
73
|
-
|
74
|
-
h = Hash[fields.zip(x)]
|
75
|
-
[h[@default_key], {id: '', created: created, last_modified: '', body: h}]
|
76
|
-
end
|
77
|
-
|
78
|
-
h2 = Hash[a]
|
79
|
-
|
80
|
-
#replace the existing records hash
|
81
|
-
h = @records
|
82
|
-
h2.each do |key,item|
|
83
|
-
if h.has_key? key then
|
84
|
-
|
85
|
-
# overwrite the previous item and change the timestamps
|
86
|
-
h[key][:last_modified] = item[:created]
|
87
|
-
item[:body].each do |k,v|
|
88
|
-
h[key][:body][k.to_sym] = v
|
89
|
-
end
|
90
|
-
else
|
91
|
-
i += 1
|
92
|
-
item[:id] = i.to_s
|
93
|
-
h[key] = item.clone
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
h.each {|key, item| h.delete(key) if not h2.has_key? key}
|
98
|
-
self
|
99
|
-
end
|
100
|
-
|
101
|
-
def create(params={})
|
102
|
-
|
103
|
-
record_name, fields = capture_fields(params)
|
104
|
-
record = Element.new record_name
|
105
|
-
fields.each{|k,v| record.add Element.new(k.to_s).add_text(v) if v}
|
106
|
-
|
107
|
-
ids = XPath.match(@doc.root,'records/*/attribute::id').map &:value
|
108
|
-
id = ids.empty? ? 1 : ids.max.succ
|
109
|
-
|
110
|
-
attributes = {id: id, created: Time.now.to_s, last_modified: nil}
|
111
|
-
attributes.each {|k,v| record.add_attribute(k.to_s, v)}
|
112
|
-
@doc.root.elements['records'].add record
|
113
|
-
|
114
|
-
load_records
|
115
|
-
end
|
116
|
-
|
117
|
-
def update(id, params={})
|
118
|
-
record_name, fields = capture_fields(params)
|
119
|
-
|
120
|
-
# for each field update each record field
|
121
|
-
record = XPath.first(@doc.root, "records/#{record_name}[@id=#{id.to_s}]")
|
122
|
-
fields.each {|k,v| record.elements[k.to_s].text = v if v}
|
123
|
-
record.add_attribute('last_modified', Time.now.to_s)
|
124
|
-
|
125
|
-
load_records
|
126
|
-
end
|
127
|
-
|
128
|
-
def delete(id)
|
129
|
-
node = XPath.first(@doc.root, "records/*[@id='#{id}']")
|
130
|
-
node.parent.delete node
|
131
|
-
load_records
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
|
-
def capture_fields(params)
|
137
|
-
|
138
|
-
record_name, raw_fields = @schema.match(/(\w+)\(([^\)]+)/).captures
|
139
|
-
fields = Hash[raw_fields.split(',').map{|x| [x.strip.to_sym, nil]}]
|
140
|
-
|
141
|
-
fields.keys.each {|key| fields[key] = params[key] if params.has_key? key}
|
142
|
-
[record_name, fields]
|
143
|
-
end
|
144
|
-
|
145
|
-
|
146
|
-
def display_xml
|
147
|
-
|
148
|
-
xml = Builder::XmlMarkup.new( :target => buffer='', :indent => 2 )
|
149
|
-
xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
|
150
|
-
|
151
|
-
xml.send @root_name do
|
152
|
-
xml.summary do
|
153
|
-
@summary.each{|key,value| xml.send key, value}
|
154
|
-
end
|
155
|
-
xml.records do
|
156
|
-
@records.each do |k, item|
|
157
|
-
xml.send(@record_name, id: item[:id], created: item[:created], \
|
158
|
-
last_modified: item[:last_modified]) do
|
159
|
-
item[:body].each{|name,value| xml.send name, value}
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
buffer
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
def dynarex_new(s)
|
170
|
-
ptrn = %r((\w+)\[?([^\]]+)?\]?\/(\w+)\(([^\)]+)\))
|
171
|
-
root_name, raw_summary, record_name, raw_fields = s.match(ptrn).captures
|
172
|
-
summary, fields = [raw_summary || '',raw_fields].map {|x| x.split(/,/).map &:strip}
|
173
|
-
|
174
|
-
xml = Builder::XmlMarkup.new( target: buffer='', indent: 2 )
|
175
|
-
xml.instruct! :xml, version: "1.0", encoding: "UTF-8"
|
176
|
-
|
177
|
-
xml.send root_name do
|
178
|
-
xml.summary do
|
179
|
-
summary.each do |item|
|
180
|
-
xml.send item
|
181
|
-
end
|
182
|
-
xml.recordx_type 'dynarex'
|
183
|
-
xml.format_mask fields.map {|x| "[!%s]" % x}.join(' ')
|
184
|
-
xml.schema s
|
185
|
-
end
|
186
|
-
xml.records
|
187
|
-
end
|
188
|
-
buffer
|
189
|
-
end
|
190
|
-
|
191
|
-
def open(s)
|
192
|
-
if s[/\(/] then # schema
|
193
|
-
buffer = dynarex_new s
|
194
|
-
elsif s[/^https?:\/\//] then # url
|
195
|
-
buffer = Kernel.open(s, 'UserAgent' => 'Dynarex-Reader').read
|
196
|
-
elsif s[/\</] # xml
|
197
|
-
buffer = s
|
198
|
-
else # local file
|
199
|
-
buffer = File.open(s,'r').read
|
200
|
-
end
|
201
|
-
|
202
|
-
@doc = Document.new buffer
|
203
|
-
@schema = @doc.root.text('summary/schema').to_s
|
204
|
-
@root_name = @doc.root.name
|
205
|
-
@summary = summary_to_h
|
206
|
-
@default_key = XPath.first(@doc.root, 'summary/default_key/text()')
|
207
|
-
|
208
|
-
if XPath.match(@doc.root, 'records/*').length > 0 then
|
209
|
-
|
210
|
-
load_records
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
def load_records
|
215
|
-
@default_key = (XPath.first(@doc.root, 'records/*[1]/*[1]').name).to_s.to_sym unless @default_key
|
216
|
-
@record_name = XPath.first(@doc.root, 'records/*[1]').name
|
217
|
-
@records = records_to_h
|
218
|
-
@flat_records = flat_records_to_h
|
219
|
-
end
|
220
|
-
|
221
|
-
def display()
|
222
|
-
puts @doc.to_s
|
223
|
-
end
|
224
|
-
|
225
|
-
def flat_records_to_h
|
226
|
-
XPath.match(@doc.root, 'records/*').map do |row|
|
227
|
-
XPath.match(row, '*').inject({}) do |r,node|
|
228
|
-
r[node.name.to_s.to_sym] = node.text.to_s
|
229
|
-
r
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def records_to_h()
|
235
|
-
i = XPath.match(@doc.root, 'records/*/attribute::id').max_by(&:value).to_s.to_i
|
236
|
-
|
237
|
-
ah = XPath.match(@doc.root, 'records/*').map do |row|
|
238
|
-
|
239
|
-
created = Time.now.to_s
|
240
|
-
last_modified = ''
|
241
|
-
|
242
|
-
if row.attribute('id') then
|
243
|
-
id = row.attribute('id').value.to_s
|
244
|
-
else
|
245
|
-
i += 1; id = i.to_s
|
246
|
-
end
|
247
|
-
created = row.attribute('created').value.to_s if row.attribute('created')
|
248
|
-
last_modified = row.attribute('last_modified').value.to_s if row.attribute('last_modified')
|
249
|
-
body = XPath.match(row, '*').inject({}) do |r,node|
|
250
|
-
r[node.name.to_s.to_sym] = node.text.to_s
|
251
|
-
r
|
252
|
-
end
|
253
|
-
[body[@default_key],{id: id, created: created, last_modified: \
|
254
|
-
last_modified, body: body}]
|
255
|
-
end
|
256
|
-
Hash[*ah.flatten]
|
257
|
-
end
|
258
|
-
|
259
|
-
def summary_to_h
|
260
|
-
XPath.match(@doc.root, 'summary/*').inject({}) do |r,node|
|
261
|
-
r[node.name.to_s.to_sym] = node.text.to_s
|
262
|
-
r
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
end
|
267
|
-
|