ox 1.8.6 → 1.8.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ox might be problematic. Click here for more details.
- data/README.md +8 -0
- data/ext/ox/cache.c +6 -4
- data/ext/ox/cache.h +1 -1
- data/ext/ox/cache_test.c +1 -1
- data/ext/ox/gen_load.c +2 -2
- data/ext/ox/obj_load.c +4 -4
- data/ext/ox/ox.c +20 -16
- data/ext/ox/ox.h +3 -1
- data/ext/ox/sax.c +14 -11
- data/lib/ox.rb +1 -1
- data/lib/ox/error.rb +22 -0
- data/lib/ox/version.rb +1 -1
- metadata +3 -3
- data/lib/ox/invalidpath.rb +0 -10
data/README.md
CHANGED
@@ -34,6 +34,14 @@ A fast XML parser and Object marshaller as a Ruby gem.
|
|
34
34
|
|
35
35
|
## <a name="release">Release Notes</a>
|
36
36
|
|
37
|
+
### Release 1.8.7
|
38
|
+
|
39
|
+
- Added a correct check for element open and close names.
|
40
|
+
|
41
|
+
- Changed raised Exceptions to customer classes that inherit from StandardError.
|
42
|
+
|
43
|
+
- Fixed a few minor bugs.
|
44
|
+
|
37
45
|
### Release 1.8.6
|
38
46
|
|
39
47
|
- Removed broken check for matching start and end element names in SAX mode. The names are still included in the
|
data/ext/ox/cache.c
CHANGED
@@ -46,8 +46,8 @@ struct _Cache {
|
|
46
46
|
static void slot_print(Cache cache, unsigned int depth);
|
47
47
|
|
48
48
|
static char* dupstr(const char *s) {
|
49
|
-
size_t len = strlen(s);
|
50
|
-
char *d = ALLOC_N(char, len
|
49
|
+
size_t len = strlen(s) + 1;
|
50
|
+
char *d = ALLOC_N(char, len);
|
51
51
|
|
52
52
|
memcpy(d, s, len);
|
53
53
|
|
@@ -63,7 +63,7 @@ ox_cache_new(Cache *cache) {
|
|
63
63
|
}
|
64
64
|
|
65
65
|
VALUE
|
66
|
-
ox_cache_get(Cache cache, const char *key, VALUE **slot) {
|
66
|
+
ox_cache_get(Cache cache, const char *key, VALUE **slot, char **keyp) {
|
67
67
|
unsigned char *k = (unsigned char*)key;
|
68
68
|
Cache *cp;
|
69
69
|
|
@@ -107,7 +107,9 @@ ox_cache_get(Cache cache, const char *key, VALUE **slot) {
|
|
107
107
|
}
|
108
108
|
}
|
109
109
|
*slot = &cache->value;
|
110
|
-
|
110
|
+
if (0 != keyp) {
|
111
|
+
*keyp = cache->key;
|
112
|
+
}
|
111
113
|
return cache->value;
|
112
114
|
}
|
113
115
|
|
data/ext/ox/cache.h
CHANGED
@@ -37,7 +37,7 @@ typedef struct _Cache *Cache;
|
|
37
37
|
|
38
38
|
extern void ox_cache_new(Cache *cache);
|
39
39
|
|
40
|
-
extern VALUE ox_cache_get(Cache cache, const char *key, VALUE **slot);
|
40
|
+
extern VALUE ox_cache_get(Cache cache, const char *key, VALUE **slot, char **keyp);
|
41
41
|
|
42
42
|
extern void ox_cache_print(Cache cache);
|
43
43
|
|
data/ext/ox/cache_test.c
CHANGED
data/ext/ox/gen_load.c
CHANGED
@@ -326,7 +326,7 @@ add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
|
|
326
326
|
VALUE *slot;
|
327
327
|
|
328
328
|
if (Yes == pi->options->sym_keys) {
|
329
|
-
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot))) {
|
329
|
+
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot, 0))) {
|
330
330
|
#if HAS_ENCODING_SUPPORT
|
331
331
|
if (0 != pi->options->rb_enc) {
|
332
332
|
VALUE rstr = rb_str_new2(attrs->name);
|
@@ -438,7 +438,7 @@ add_instruct(PInfo pi, const char *name, Attr attrs, const char *content) {
|
|
438
438
|
VALUE *slot;
|
439
439
|
|
440
440
|
if (Yes == pi->options->sym_keys) {
|
441
|
-
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot))) {
|
441
|
+
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot, 0))) {
|
442
442
|
#if HAS_ENCODING_SUPPORT
|
443
443
|
if (0 != pi->options->rb_enc) {
|
444
444
|
VALUE rstr = rb_str_new2(attrs->name);
|
data/ext/ox/obj_load.c
CHANGED
@@ -104,7 +104,7 @@ name2var(const char *name, void *encoding) {
|
|
104
104
|
|
105
105
|
if ('0' <= *name && *name <= '9') {
|
106
106
|
var_id = INT2NUM(atoi(name));
|
107
|
-
} else if (Qundef == (var_id = ox_cache_get(ox_attr_cache, name, &slot))) {
|
107
|
+
} else if (Qundef == (var_id = ox_cache_get(ox_attr_cache, name, &slot, 0))) {
|
108
108
|
#ifdef HAVE_RUBY_ENCODING_H
|
109
109
|
if (0 != encoding) {
|
110
110
|
VALUE rstr = rb_str_new2(name);
|
@@ -226,7 +226,7 @@ classname2class(const char *name, PInfo pi, VALUE base_class) {
|
|
226
226
|
VALUE *slot;
|
227
227
|
VALUE clas;
|
228
228
|
|
229
|
-
if (Qundef == (clas = ox_cache_get(ox_class_cache, name, &slot))) {
|
229
|
+
if (Qundef == (clas = ox_cache_get(ox_class_cache, name, &slot, 0))) {
|
230
230
|
char class_name[1024];
|
231
231
|
char *s;
|
232
232
|
const char *n = name;
|
@@ -483,7 +483,7 @@ add_text(PInfo pi, char *text, int closed) {
|
|
483
483
|
VALUE sym;
|
484
484
|
VALUE *slot;
|
485
485
|
|
486
|
-
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, text, &slot))) {
|
486
|
+
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, text, &slot, 0))) {
|
487
487
|
sym = str2sym(text, (void*)pi->options->rb_enc);
|
488
488
|
*slot = sym;
|
489
489
|
}
|
@@ -532,7 +532,7 @@ add_text(PInfo pi, char *text, int closed) {
|
|
532
532
|
char *str = ALLOCA_N(char, str_size + 1);
|
533
533
|
|
534
534
|
from_base64(text, (uchar*)str);
|
535
|
-
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, str, &slot))) {
|
535
|
+
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, str, &slot, 0))) {
|
536
536
|
sym = str2sym(str, (void*)pi->options->rb_enc);
|
537
537
|
*slot = sym;
|
538
538
|
}
|
data/ext/ox/ox.c
CHANGED
@@ -95,6 +95,7 @@ VALUE ox_encoding_sym;
|
|
95
95
|
VALUE ox_empty_string;
|
96
96
|
VALUE ox_zero_fixnum;
|
97
97
|
|
98
|
+
VALUE ox_arg_error_class;
|
98
99
|
VALUE ox_bag_clas;
|
99
100
|
VALUE ox_cdata_clas;
|
100
101
|
VALUE ox_comment_clas;
|
@@ -103,6 +104,7 @@ VALUE ox_doctype_clas;
|
|
103
104
|
VALUE ox_document_clas;
|
104
105
|
VALUE ox_element_clas;
|
105
106
|
VALUE ox_instruct_clas;
|
107
|
+
VALUE ox_parse_error_class;
|
106
108
|
VALUE ox_stringio_class;
|
107
109
|
VALUE ox_struct_class;
|
108
110
|
VALUE ox_time_class;
|
@@ -175,7 +177,7 @@ defuse_bom(char *xml, Options options) {
|
|
175
177
|
options->rb_enc = ox_utf8_encoding;
|
176
178
|
xml += 3;
|
177
179
|
} else {
|
178
|
-
rb_raise(
|
180
|
+
rb_raise(ox_parse_error_class, "Invalid BOM in XML string.\n");
|
179
181
|
}
|
180
182
|
break;
|
181
183
|
#if 0
|
@@ -184,7 +186,7 @@ defuse_bom(char *xml, Options options) {
|
|
184
186
|
options->rb_enc = ox_utf16be_encoding;
|
185
187
|
xml += 2;
|
186
188
|
} else {
|
187
|
-
rb_raise(
|
189
|
+
rb_raise(ox_parse_error_class, "Invalid BOM in XML string.\n");
|
188
190
|
}
|
189
191
|
break;
|
190
192
|
case 0xFF: /* UTF-16LE or UTF-32LE */
|
@@ -197,7 +199,7 @@ defuse_bom(char *xml, Options options) {
|
|
197
199
|
xml += 2;
|
198
200
|
}
|
199
201
|
} else {
|
200
|
-
rb_raise(
|
202
|
+
rb_raise(ox_parse_error_class, "Invalid BOM in XML string.\n");
|
201
203
|
}
|
202
204
|
break;
|
203
205
|
case 0x00: /* UTF-32BE */
|
@@ -205,7 +207,7 @@ defuse_bom(char *xml, Options options) {
|
|
205
207
|
options->rb_enc = ox_utf32be_encoding;
|
206
208
|
xml += 4;
|
207
209
|
} else {
|
208
|
-
rb_raise(
|
210
|
+
rb_raise(ox_parse_error_class, "Invalid BOM in XML string.\n");
|
209
211
|
}
|
210
212
|
break;
|
211
213
|
#endif
|
@@ -332,7 +334,7 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
332
334
|
} else if (limited_sym == v) {
|
333
335
|
ox_default_options.mode = LimMode;
|
334
336
|
} else {
|
335
|
-
rb_raise(
|
337
|
+
rb_raise(ox_parse_error_class, ":mode must be :object, :generic, :limited, or nil.\n");
|
336
338
|
}
|
337
339
|
|
338
340
|
v = rb_hash_aref(opts, effort_sym);
|
@@ -345,7 +347,7 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
345
347
|
} else if (auto_define_sym == v) {
|
346
348
|
ox_default_options.effort = AutoEffort;
|
347
349
|
} else {
|
348
|
-
rb_raise(
|
350
|
+
rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, :auto_define, or nil.\n");
|
349
351
|
}
|
350
352
|
for (o = ynos; 0 != o->attr; o++) {
|
351
353
|
v = rb_hash_lookup(opts, o->sym);
|
@@ -356,7 +358,7 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
356
358
|
} else if (Qfalse == v) {
|
357
359
|
*o->attr = No;
|
358
360
|
} else {
|
359
|
-
rb_raise(
|
361
|
+
rb_raise(ox_parse_error_class, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
|
360
362
|
}
|
361
363
|
}
|
362
364
|
return Qnil;
|
@@ -446,7 +448,7 @@ load(char *xml, int argc, VALUE *argv, VALUE self, VALUE encoding) {
|
|
446
448
|
} else if (limited_sym == v) {
|
447
449
|
options.mode = LimMode;
|
448
450
|
} else {
|
449
|
-
rb_raise(
|
451
|
+
rb_raise(ox_parse_error_class, ":mode must be :generic, :object, or :limited.\n");
|
450
452
|
}
|
451
453
|
}
|
452
454
|
if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
|
@@ -457,7 +459,7 @@ load(char *xml, int argc, VALUE *argv, VALUE self, VALUE encoding) {
|
|
457
459
|
} else if (strict_sym == v) {
|
458
460
|
options.effort = StrictEffort;
|
459
461
|
} else {
|
460
|
-
rb_raise(
|
462
|
+
rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, or :auto_define.\n");
|
461
463
|
}
|
462
464
|
}
|
463
465
|
if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
|
@@ -625,7 +627,7 @@ sax_parse(int argc, VALUE *argv, VALUE self) {
|
|
625
627
|
int convert = 0;
|
626
628
|
|
627
629
|
if (argc < 2) {
|
628
|
-
rb_raise(
|
630
|
+
rb_raise(ox_parse_error_class, "Wrong number of arguments to sax_parse.\n");
|
629
631
|
}
|
630
632
|
if (3 <= argc && rb_cHash == rb_obj_class(argv[2])) {
|
631
633
|
VALUE h = argv[2];
|
@@ -657,19 +659,19 @@ parse_dump_options(VALUE ropts, Options copts) {
|
|
657
659
|
|
658
660
|
if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
|
659
661
|
if (rb_cFixnum != rb_obj_class(v)) {
|
660
|
-
rb_raise(
|
662
|
+
rb_raise(ox_parse_error_class, ":indent must be a Fixnum.\n");
|
661
663
|
}
|
662
664
|
copts->indent = NUM2INT(v);
|
663
665
|
}
|
664
666
|
if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
|
665
667
|
if (rb_cFixnum != rb_obj_class(v)) {
|
666
|
-
rb_raise(
|
668
|
+
rb_raise(ox_parse_error_class, ":trace must be a Fixnum.\n");
|
667
669
|
}
|
668
670
|
copts->trace = NUM2INT(v);
|
669
671
|
}
|
670
672
|
if (Qnil != (v = rb_hash_lookup(ropts, ox_encoding_sym))) {
|
671
673
|
if (rb_cString != rb_obj_class(v)) {
|
672
|
-
rb_raise(
|
674
|
+
rb_raise(ox_parse_error_class, ":encoding must be a String.\n");
|
673
675
|
}
|
674
676
|
strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
|
675
677
|
}
|
@@ -681,7 +683,7 @@ parse_dump_options(VALUE ropts, Options copts) {
|
|
681
683
|
} else if (strict_sym == v) {
|
682
684
|
copts->effort = StrictEffort;
|
683
685
|
} else {
|
684
|
-
rb_raise(
|
686
|
+
rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, or :auto_define.\n");
|
685
687
|
}
|
686
688
|
}
|
687
689
|
for (o = ynos; 0 != o->attr; o++) {
|
@@ -693,7 +695,7 @@ parse_dump_options(VALUE ropts, Options copts) {
|
|
693
695
|
} else if (rb_cFalseClass == c) {
|
694
696
|
*o->attr = No;
|
695
697
|
} else {
|
696
|
-
rb_raise(
|
698
|
+
rb_raise(ox_parse_error_class, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
|
697
699
|
}
|
698
700
|
}
|
699
701
|
}
|
@@ -846,6 +848,8 @@ void Init_ox() {
|
|
846
848
|
|
847
849
|
ox_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
|
848
850
|
ox_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
|
851
|
+
ox_parse_error_class = rb_const_get_at(Ox, rb_intern("ParseError"));
|
852
|
+
ox_arg_error_class = rb_const_get_at(Ox, rb_intern("ArgError"));
|
849
853
|
ox_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
|
850
854
|
ox_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
851
855
|
|
@@ -910,5 +914,5 @@ _ox_raise_error(const char *msg, const char *xml, const char *current, const cha
|
|
910
914
|
xline++;
|
911
915
|
}
|
912
916
|
}
|
913
|
-
rb_raise(
|
917
|
+
rb_raise(ox_parse_error_class, "%s at line %d, column %d [%s:%d]\n", msg, xline, col, file, line);
|
914
918
|
}
|
data/ext/ox/ox.h
CHANGED
@@ -272,12 +272,14 @@ extern VALUE ox_utf8_encoding;
|
|
272
272
|
extern void *ox_utf8_encoding;
|
273
273
|
#endif
|
274
274
|
|
275
|
+
extern VALUE ox_arg_error_class;
|
275
276
|
extern VALUE ox_date_class;
|
276
277
|
extern VALUE ox_empty_string;
|
277
278
|
extern VALUE ox_encoding_sym;
|
279
|
+
extern VALUE ox_parse_error_class;
|
280
|
+
extern VALUE ox_stringio_class;
|
278
281
|
extern VALUE ox_struct_class;
|
279
282
|
extern VALUE ox_time_class;
|
280
|
-
extern VALUE ox_stringio_class;
|
281
283
|
extern VALUE ox_zero_fixnum;
|
282
284
|
|
283
285
|
extern VALUE ox_document_clas;
|
data/ext/ox/sax.c
CHANGED
@@ -202,11 +202,11 @@ is_white(char c) {
|
|
202
202
|
}
|
203
203
|
|
204
204
|
inline static VALUE
|
205
|
-
str2sym(const char *str, SaxDrive dr) {
|
205
|
+
str2sym(const char *str, SaxDrive dr, char **strp) {
|
206
206
|
VALUE *slot;
|
207
207
|
VALUE sym;
|
208
208
|
|
209
|
-
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, str, &slot))) {
|
209
|
+
if (Qundef == (sym = ox_cache_get(ox_symbol_cache, str, &slot, strp))) {
|
210
210
|
#if HAS_ENCODING_SUPPORT
|
211
211
|
if (0 != dr->encoding) {
|
212
212
|
VALUE rstr = rb_str_new2(str);
|
@@ -312,7 +312,7 @@ sax_drive_init(SaxDrive dr, VALUE handler, VALUE io, int convert) {
|
|
312
312
|
}
|
313
313
|
#endif
|
314
314
|
} else {
|
315
|
-
rb_raise(
|
315
|
+
rb_raise(ox_arg_error_class, "sax_parser io argument must respond to readpartial() or read().\n");
|
316
316
|
}
|
317
317
|
dr->buf = dr->base_buf;
|
318
318
|
*dr->buf = '\0';
|
@@ -427,7 +427,7 @@ sax_drive_error(SaxDrive dr, const char *msg, int critical) {
|
|
427
427
|
rb_funcall2(dr->handler, ox_error_id, 3, args);
|
428
428
|
} else if (critical) {
|
429
429
|
sax_drive_cleanup(dr);
|
430
|
-
rb_raise(
|
430
|
+
rb_raise(ox_parse_error_class, "%s at line %d, column %d\n", msg, dr->line, dr->col);
|
431
431
|
}
|
432
432
|
}
|
433
433
|
|
@@ -748,17 +748,16 @@ read_comment(SaxDrive dr) {
|
|
748
748
|
*/
|
749
749
|
static int
|
750
750
|
read_element(SaxDrive dr) {
|
751
|
+
char *ename = 0;
|
751
752
|
VALUE name = Qnil;
|
752
753
|
const char *err;
|
753
754
|
char c;
|
754
|
-
char *ename = 0;
|
755
755
|
int closed;
|
756
756
|
|
757
757
|
if ('\0' == (c = read_name_token(dr))) {
|
758
758
|
return -1;
|
759
759
|
}
|
760
|
-
|
761
|
-
name = str2sym(dr->str, dr);
|
760
|
+
name = str2sym(dr->str, dr, &ename);
|
762
761
|
if (dr->has_start_element) {
|
763
762
|
VALUE args[1];
|
764
763
|
|
@@ -794,7 +793,11 @@ read_element(SaxDrive dr) {
|
|
794
793
|
if (0 != read_children(dr, 0)) {
|
795
794
|
return -1;
|
796
795
|
}
|
797
|
-
|
796
|
+
if (0 != ename && 0 != strcmp(ename, dr->str)) {
|
797
|
+
//printf("*** ename: %s close: %s\n", ename, dr->str);
|
798
|
+
sax_drive_error(dr, "invalid format, element start and end names do not match", 1);
|
799
|
+
return -1;
|
800
|
+
}
|
798
801
|
if (0 != dr->has_end_element) {
|
799
802
|
VALUE args[1];
|
800
803
|
|
@@ -872,7 +875,7 @@ read_attrs(SaxDrive dr, char c, char termc, char term2, int is_xml) {
|
|
872
875
|
}
|
873
876
|
/* TBD use symbol cache */
|
874
877
|
if (dr->has_attr || dr->has_attr_value) {
|
875
|
-
name = str2sym(dr->str, dr);
|
878
|
+
name = str2sym(dr->str, dr, 0);
|
876
879
|
}
|
877
880
|
if (is_white(c)) {
|
878
881
|
c = next_non_white(dr);
|
@@ -1275,7 +1278,7 @@ sax_value_as_sym(VALUE self) {
|
|
1275
1278
|
if ('\0' == *dr->str) {
|
1276
1279
|
return Qnil;
|
1277
1280
|
}
|
1278
|
-
return str2sym(dr->str, dr);
|
1281
|
+
return str2sym(dr->str, dr, 0);
|
1279
1282
|
}
|
1280
1283
|
|
1281
1284
|
static VALUE
|
@@ -1308,7 +1311,7 @@ sax_value_as_i(VALUE self) {
|
|
1308
1311
|
if ('0' <= *s && *s <= '9') {
|
1309
1312
|
n = n * 10 + (*s - '0');
|
1310
1313
|
} else {
|
1311
|
-
rb_raise(
|
1314
|
+
rb_raise(ox_arg_error_class, "Not a valid Fixnum.\n");
|
1312
1315
|
}
|
1313
1316
|
}
|
1314
1317
|
if (neg) {
|
data/lib/ox.rb
CHANGED
data/lib/ox/error.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Ox
|
3
|
+
|
4
|
+
class Error < StandardError
|
5
|
+
end # Error
|
6
|
+
|
7
|
+
# An Exception that is raised as a result of a parse error while parsing a XML document.
|
8
|
+
class ParseError < Error
|
9
|
+
end # ParseError
|
10
|
+
|
11
|
+
# An Exception that is raised as a result of an invalid argument.
|
12
|
+
class ArgError < Error
|
13
|
+
end # ArgError
|
14
|
+
|
15
|
+
# An Exception raised if a path is not valid.
|
16
|
+
class InvalidPath < Error
|
17
|
+
def initialize(path)
|
18
|
+
super("#{path.join('/')} is not a valid location.")
|
19
|
+
end
|
20
|
+
end # InvalidPath
|
21
|
+
|
22
|
+
end # Ox
|
data/lib/ox/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-08 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! "A fast XML parser and object serializer that uses only standard C
|
15
15
|
lib.\n \nOptimized XML (Ox), as the name implies was written to provide
|
@@ -29,9 +29,9 @@ files:
|
|
29
29
|
- lib/ox/doctype.rb
|
30
30
|
- lib/ox/document.rb
|
31
31
|
- lib/ox/element.rb
|
32
|
+
- lib/ox/error.rb
|
32
33
|
- lib/ox/hasattrs.rb
|
33
34
|
- lib/ox/instruct.rb
|
34
|
-
- lib/ox/invalidpath.rb
|
35
35
|
- lib/ox/node.rb
|
36
36
|
- lib/ox/sax.rb
|
37
37
|
- lib/ox/version.rb
|