teius 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/Makefile +138 -0
- data/ext/teius.c +88 -36
- data/ext/teius.o +0 -0
- data/ext/teius.so +0 -0
- data/lib/teius.rb +35 -0
- data/lib/teius.so +0 -0
- data/test/teius_test.rb +57 -3
- metadata +8 -3
data/ext/Makefile
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
|
2
|
+
SHELL = /bin/sh
|
3
|
+
|
4
|
+
#### Start of system configuration section. ####
|
5
|
+
|
6
|
+
srcdir = .
|
7
|
+
topdir = d:/msys/local/lib/ruby/1.8/i386-mingw32
|
8
|
+
hdrdir = $(topdir)
|
9
|
+
VPATH = $(srcdir);$(topdir);$(hdrdir)
|
10
|
+
|
11
|
+
DESTDIR = d:
|
12
|
+
prefix = $(DESTDIR)/msys/local
|
13
|
+
exec_prefix = $(DESTDIR)/msys/local
|
14
|
+
sitedir = $(prefix)/lib/ruby/site_ruby
|
15
|
+
rubylibdir = $(libdir)/ruby/$(ruby_version)
|
16
|
+
archdir = $(rubylibdir)/$(arch)
|
17
|
+
sbindir = $(exec_prefix)/sbin
|
18
|
+
datadir = $(prefix)/share
|
19
|
+
includedir = $(prefix)/include
|
20
|
+
infodir = $(prefix)/info
|
21
|
+
sysconfdir = $(prefix)/etc
|
22
|
+
mandir = $(prefix)/man
|
23
|
+
libdir = $(DESTDIR)/msys/local/lib
|
24
|
+
sharedstatedir = $(prefix)/com
|
25
|
+
oldincludedir = $(DESTDIR)/usr/include
|
26
|
+
sitearchdir = $(sitelibdir)/$(sitearch)
|
27
|
+
bindir = $(exec_prefix)/bin
|
28
|
+
localstatedir = $(prefix)/var
|
29
|
+
sitelibdir = $(sitedir)/$(ruby_version)
|
30
|
+
libexecdir = $(exec_prefix)/libexec
|
31
|
+
|
32
|
+
CC = gcc
|
33
|
+
LIBRUBY = lib$(LIBRUBY_SO).a
|
34
|
+
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
35
|
+
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
36
|
+
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
|
37
|
+
|
38
|
+
CFLAGS = -g -O2
|
39
|
+
CPPFLAGS = -I. -I$(topdir) -I$(hdrdir) -I$(srcdir) -Id:/msys/local/include/libxml2
|
40
|
+
CXXFLAGS = $(CFLAGS)
|
41
|
+
DLDFLAGS = -Wl,--enable-auto-import,--export-all
|
42
|
+
LDSHARED = gcc -shared -s
|
43
|
+
AR = ar
|
44
|
+
EXEEXT = .exe
|
45
|
+
|
46
|
+
RUBY_INSTALL_NAME = ruby
|
47
|
+
RUBY_SO_NAME = msvcrt-ruby18
|
48
|
+
arch = i386-mingw32
|
49
|
+
sitearch = i386-msvcrt
|
50
|
+
ruby_version = 1.8
|
51
|
+
ruby = d:/msys/local/bin/ruby
|
52
|
+
RUBY = $(ruby)
|
53
|
+
RM = rm -f
|
54
|
+
MAKEDIRS = mkdir -p
|
55
|
+
INSTALL = /bin/install -c
|
56
|
+
INSTALL_PROG = $(INSTALL) -m 0755
|
57
|
+
INSTALL_DATA = $(INSTALL) -m 644
|
58
|
+
COPY = cp
|
59
|
+
|
60
|
+
#### End of system configuration section. ####
|
61
|
+
|
62
|
+
preload =
|
63
|
+
|
64
|
+
libpath = d:/msys/local/lib $(libdir)
|
65
|
+
LIBPATH = -L"d:/msys/local/lib" -L"$(libdir)"
|
66
|
+
DEFFILE =
|
67
|
+
|
68
|
+
CLEANFILES =
|
69
|
+
DISTCLEANFILES =
|
70
|
+
|
71
|
+
extout =
|
72
|
+
extout_prefix =
|
73
|
+
target_prefix =
|
74
|
+
LOCAL_LIBS =
|
75
|
+
LIBS = $(LIBRUBYARG_SHARED) -lxml2 -lwsock32
|
76
|
+
SRCS = teius.c
|
77
|
+
OBJS = teius.o
|
78
|
+
TARGET = teius
|
79
|
+
DLLIB = $(TARGET).so
|
80
|
+
STATIC_LIB =
|
81
|
+
|
82
|
+
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
83
|
+
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
84
|
+
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
85
|
+
|
86
|
+
TARGET_SO = $(DLLIB)
|
87
|
+
CLEANLIBS = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map
|
88
|
+
CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak
|
89
|
+
|
90
|
+
all: $(DLLIB)
|
91
|
+
static: $(STATIC_LIB)
|
92
|
+
|
93
|
+
clean:
|
94
|
+
@-$(RM) $(CLEANLIBS:/=\) $(CLEANOBJS:/=\) $(CLEANFILES:/=\)
|
95
|
+
|
96
|
+
distclean: clean
|
97
|
+
@-$(RM) Makefile extconf.h conftest.* mkmf.log
|
98
|
+
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES:/=\)
|
99
|
+
|
100
|
+
realclean: distclean
|
101
|
+
install: install-so install-rb
|
102
|
+
|
103
|
+
install-so: $(RUBYARCHDIR)
|
104
|
+
install-so: $(RUBYARCHDIR)/$(DLLIB)
|
105
|
+
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
|
106
|
+
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
107
|
+
install-rb: pre-install-rb install-rb-default
|
108
|
+
install-rb-default: pre-install-rb-default
|
109
|
+
pre-install-rb: Makefile
|
110
|
+
pre-install-rb-default: Makefile
|
111
|
+
$(RUBYARCHDIR):
|
112
|
+
$(MAKEDIRS) $@
|
113
|
+
|
114
|
+
site-install: site-install-so site-install-rb
|
115
|
+
site-install-so: install-so
|
116
|
+
site-install-rb: install-rb
|
117
|
+
|
118
|
+
.SUFFIXES: .c .m .cc .cxx .cpp .o
|
119
|
+
|
120
|
+
.cc.o:
|
121
|
+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $<
|
122
|
+
|
123
|
+
.cxx.o:
|
124
|
+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $<
|
125
|
+
|
126
|
+
.cpp.o:
|
127
|
+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $<
|
128
|
+
|
129
|
+
.c.o:
|
130
|
+
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
|
131
|
+
|
132
|
+
$(DLLIB): $(OBJS)
|
133
|
+
@-$(RM) $@
|
134
|
+
$(LDSHARED) $(DLDFLAGS) $(LIBPATH) -o $@ $(OBJS) $(LOCAL_LIBS) $(LIBS)
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
$(OBJS): ruby.h defines.h
|
data/ext/teius.c
CHANGED
@@ -6,12 +6,14 @@
|
|
6
6
|
|
7
7
|
static VALUE cParseError;
|
8
8
|
static VALUE cXpathError;
|
9
|
-
static VALUE
|
9
|
+
static VALUE cNodeNotFound;
|
10
|
+
static VALUE cNodeNotUnique;
|
10
11
|
static VALUE cNode;
|
11
12
|
static VALUE cDocument;
|
12
13
|
static ID sRequired;
|
13
14
|
static ID sFirst;
|
14
15
|
static ID sAll;
|
16
|
+
static ID sUnique;
|
15
17
|
|
16
18
|
static void doc_free(void *p) {
|
17
19
|
xmlFreeDoc(p);
|
@@ -62,24 +64,24 @@ static VALUE node_find(int argc, VALUE *argv, VALUE self) {
|
|
62
64
|
rb_scan_args(argc, argv, "21", &rType, &rXpath, &rOptions);
|
63
65
|
int required = 0;
|
64
66
|
if (argc == 3) {
|
65
|
-
VALUE rRequired = rb_hash_aref(rOptions, sRequired);
|
66
|
-
required = rRequired != Qnil;
|
67
|
+
VALUE rRequired = rb_hash_aref(rOptions, ID2SYM(sRequired));
|
68
|
+
required = rRequired != Qnil && rRequired != Qfalse;
|
67
69
|
}
|
68
70
|
|
69
71
|
xmlNodePtr node = NULL;
|
70
72
|
Data_Get_Struct(self, xmlNode, node);
|
71
73
|
xmlDocPtr doc = node->doc;
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
+
xmlXPathContextPtr context;
|
76
|
+
xmlXPathObjectPtr result;
|
75
77
|
|
76
|
-
|
78
|
+
context = xmlXPathNewContext(doc);
|
77
79
|
context->node = node;
|
78
80
|
|
79
81
|
char *xpath = StringValuePtr(rXpath);
|
80
82
|
result = xmlXPathEvalExpression(xpath, context);
|
81
83
|
if (result == NULL) {
|
82
|
-
|
84
|
+
xmlErrorPtr err = xmlGetLastError();
|
83
85
|
xmlXPathFreeContext(context);
|
84
86
|
rb_raise(cXpathError, "Couldn't evaluate xpath [%s]: %s",
|
85
87
|
xpath, err->message);
|
@@ -88,19 +90,24 @@ static VALUE node_find(int argc, VALUE *argv, VALUE self) {
|
|
88
90
|
xmlNodeSetPtr node_set = result->nodesetval;
|
89
91
|
int size = node_set != NULL ? node_set->nodeNr : 0;
|
90
92
|
|
93
|
+
ID type = SYM2ID(rType);
|
91
94
|
if (size == 0 && required) {
|
92
95
|
xmlXPathFreeObject(result);
|
93
96
|
xmlXPathFreeContext(context);
|
94
|
-
rb_raise(
|
97
|
+
rb_raise(cNodeNotFound, "No such node in %s: %s", node->name, xpath);
|
98
|
+
} else if (size > 1 && type == sUnique) {
|
99
|
+
xmlXPathFreeObject(result);
|
100
|
+
xmlXPathFreeContext(context);
|
101
|
+
rb_raise(cNodeNotUnique, "node at path %s from %s is not unique",
|
102
|
+
xpath, node->name);
|
95
103
|
}
|
96
104
|
|
97
105
|
VALUE rResult;
|
98
|
-
|
99
|
-
if (type == sFirst) { /* Just return first node */
|
106
|
+
if (type == sFirst || type == sUnique) { /* Just return first node */
|
100
107
|
if (size == 0) {
|
101
108
|
rResult = Qnil;
|
102
109
|
} else {
|
103
|
-
|
110
|
+
rResult = Data_Wrap_Struct(cNode, 0, 0, node_set->nodeTab[0]);
|
104
111
|
}
|
105
112
|
} else if (type == sAll) { /* Return ruby array of all nodes */
|
106
113
|
int i;
|
@@ -108,15 +115,15 @@ static VALUE node_find(int argc, VALUE *argv, VALUE self) {
|
|
108
115
|
for (i=0; i < size; i++) {
|
109
116
|
xmlNodePtr cur_node = node_set->nodeTab[i];
|
110
117
|
|
111
|
-
/* Create
|
118
|
+
/* Create Node and store it in arr */
|
112
119
|
VALUE rNode = Data_Wrap_Struct(cNode, 0, 0, cur_node);
|
113
120
|
rb_ary_store(arr, i, rNode);
|
114
121
|
}
|
115
|
-
rResult = arr;
|
122
|
+
rResult = arr;
|
116
123
|
} else {
|
117
124
|
xmlXPathFreeObject(result);
|
118
125
|
xmlXPathFreeContext(context);
|
119
|
-
rb_raise(rb_eArgError, "Unknown type: :%s (should be :first or :all)",
|
126
|
+
rb_raise(rb_eArgError, "Unknown type: :%s (should be :first, :unique or :all)",
|
120
127
|
rb_id2name(type));
|
121
128
|
}
|
122
129
|
|
@@ -132,18 +139,56 @@ static VALUE node_name(VALUE self) {
|
|
132
139
|
return rb_str_new2(node->name);
|
133
140
|
}
|
134
141
|
|
142
|
+
static VALUE node_xpath(VALUE self) {
|
143
|
+
xmlNodePtr node = NULL;
|
144
|
+
Data_Get_Struct(self, xmlNode, node);
|
145
|
+
return rb_str_new2(xmlGetNodePath(node));
|
146
|
+
}
|
147
|
+
|
148
|
+
static VALUE node_pointer(VALUE self) {
|
149
|
+
xmlNodePtr node = NULL;
|
150
|
+
Data_Get_Struct(self, xmlNode, node);
|
151
|
+
return INT2NUM((int)node);
|
152
|
+
}
|
153
|
+
|
135
154
|
static VALUE parse_file(VALUE self, VALUE rFilename) {
|
136
|
-
|
155
|
+
xmlDocPtr doc = xmlReadFile(StringValuePtr(rFilename), NULL, 0);
|
137
156
|
if (doc == NULL) {
|
138
|
-
|
139
|
-
|
140
|
-
|
157
|
+
xmlErrorPtr err = xmlGetLastError();
|
158
|
+
rb_raise(cParseError, "could not parse file %s: %s",
|
159
|
+
StringValuePtr(rFilename), err->message);
|
141
160
|
}
|
142
161
|
|
143
|
-
|
144
|
-
|
162
|
+
/* Load new document */
|
163
|
+
VALUE rDoc = Data_Wrap_Struct(cDocument, 0, doc_free, doc);
|
145
164
|
|
146
|
-
|
165
|
+
return rDoc;
|
166
|
+
}
|
167
|
+
|
168
|
+
static VALUE parse_string(VALUE self, VALUE rDocString) {
|
169
|
+
xmlDocPtr doc = xmlReadDoc(StringValuePtr(rDocString), NULL, NULL, 0);
|
170
|
+
if (doc == NULL) {
|
171
|
+
xmlErrorPtr err = xmlGetLastError();
|
172
|
+
rb_raise(cParseError, "could not parse string: %s", err->message);
|
173
|
+
}
|
174
|
+
|
175
|
+
/* Load new document */
|
176
|
+
VALUE rDoc = Data_Wrap_Struct(cDocument, 0, doc_free, doc);
|
177
|
+
|
178
|
+
return rDoc;
|
179
|
+
}
|
180
|
+
|
181
|
+
static VALUE document_to_s(VALUE self, VALUE rDocString) {
|
182
|
+
xmlDocPtr doc = NULL;
|
183
|
+
xmlChar *mem;
|
184
|
+
int size;
|
185
|
+
|
186
|
+
Data_Get_Struct(self, xmlDoc, doc);
|
187
|
+
xmlDocDumpMemory(doc, &mem, &size);
|
188
|
+
VALUE rStr = rb_str_new2(mem);
|
189
|
+
xmlFree(mem);
|
190
|
+
|
191
|
+
return rStr;
|
147
192
|
}
|
148
193
|
|
149
194
|
void Init_teius() {
|
@@ -153,25 +198,32 @@ void Init_teius() {
|
|
153
198
|
sRequired = rb_intern("required");
|
154
199
|
sFirst = rb_intern("first");
|
155
200
|
sAll = rb_intern("all");
|
201
|
+
sUnique = rb_intern("unique");
|
156
202
|
|
157
203
|
/* Modules */
|
158
204
|
VALUE mTeius = rb_define_module("Teius");
|
159
205
|
|
160
206
|
/* Exceptions */
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
"
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
207
|
+
cParseError = rb_define_class_under(mTeius, "ParseError", rb_eStandardError);
|
208
|
+
cXpathError = rb_define_class_under(mTeius, "XpathError", rb_eStandardError);
|
209
|
+
cNodeNotFound = rb_define_class_under(mTeius,
|
210
|
+
"NodeNotFound", rb_eStandardError);
|
211
|
+
cNodeNotUnique = rb_define_class_under(mTeius,
|
212
|
+
"NodeNotUnique", rb_eStandardError);
|
213
|
+
|
214
|
+
/* Node */
|
215
|
+
cNode = rb_define_class_under(mTeius, "Node", rb_cObject);
|
216
|
+
rb_define_method(cNode, "value", node_value, 0);
|
217
|
+
rb_define_method(cNode, "document", node_document, 0);
|
218
|
+
rb_define_method(cNode, "attributes", node_attributes, 0);
|
219
|
+
rb_define_method(cNode, "find", node_find, -1);
|
220
|
+
rb_define_method(cNode, "name", node_name, 0);
|
221
|
+
rb_define_method(cNode, "xpath", node_xpath, 0);
|
222
|
+
rb_define_method(cNode, "pointer", node_pointer, 0);
|
223
|
+
|
224
|
+
/* Document */
|
175
225
|
cDocument = rb_define_class_under(mTeius, "Document", cNode);
|
176
|
-
|
226
|
+
rb_define_singleton_method(cDocument, "parse_file", parse_file, 1);
|
227
|
+
rb_define_singleton_method(cDocument, "parse_string", parse_string, 1);
|
228
|
+
rb_define_method(cNode, "to_s", document_to_s, 0);
|
177
229
|
}
|
data/ext/teius.o
ADDED
Binary file
|
data/ext/teius.so
ADDED
Binary file
|
data/lib/teius.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'teius.so'
|
2
|
+
|
3
|
+
module Teius
|
4
|
+
class Node
|
5
|
+
def fetch(xpath)
|
6
|
+
node = find(:unique, xpath, :required => true)
|
7
|
+
return node ? node.value : nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
self.pointer == other.pointer
|
12
|
+
end
|
13
|
+
|
14
|
+
def eql?(other)
|
15
|
+
self.pointer == other.pointer
|
16
|
+
end
|
17
|
+
|
18
|
+
def hash
|
19
|
+
self.pointer
|
20
|
+
end
|
21
|
+
|
22
|
+
def parent
|
23
|
+
self.find :first, '..'
|
24
|
+
end
|
25
|
+
|
26
|
+
def siblings
|
27
|
+
self.find :all, 'preceding-sibling::*|following-sibling::*'
|
28
|
+
end
|
29
|
+
|
30
|
+
def children
|
31
|
+
self.find :all, 'child::*'
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/teius.so
ADDED
Binary file
|
data/test/teius_test.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
$:.unshift File.join(File.dirname(__FILE__), '..', '
|
2
|
-
require 'rubygems'
|
3
|
-
# need require_gem?
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
4
2
|
require 'test/unit'
|
5
3
|
require 'teius'
|
6
4
|
|
@@ -80,5 +78,61 @@ class TeiusTest < Test::Unit::TestCase
|
|
80
78
|
assert_equal 'en-US', atts['language']
|
81
79
|
assert_equal 8, atts.length
|
82
80
|
end
|
81
|
+
|
82
|
+
def test_unique_find
|
83
|
+
el = @doc.find :unique, '/sports-content/sports-metadata/sports-title'
|
84
|
+
assert_equal 'Score Update: Chicago vs. Green Bay (Final)', el.value
|
85
|
+
assert_raise(NodeNotUnique) { @doc.find :unique, '//sports-content-code' }
|
86
|
+
assert_nil @doc.find(:unique, '/bogus')
|
87
|
+
assert_raise(NodeNotFound) { @doc.find :unique, '/bogus', { :required => true } }
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_fetch
|
91
|
+
assert_equal 'Score Update: Chicago vs. Green Bay (Final)',
|
92
|
+
@doc.fetch('/sports-content/sports-metadata/sports-title')
|
93
|
+
end
|
83
94
|
|
95
|
+
def test_equality
|
96
|
+
n1 = @doc.find :first, '//team[1]'
|
97
|
+
assert_not_nil n1
|
98
|
+
n2 = @doc.find :first, '/sports-content/sports-event/team[1]'
|
99
|
+
assert_not_nil n2
|
100
|
+
assert_equal n1, n2
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_hash
|
104
|
+
n1 = @doc.find :first, '//team[1]'
|
105
|
+
assert_not_nil n1
|
106
|
+
n2 = @doc.find :first, '/sports-content/sports-event/team[1]'
|
107
|
+
assert n1.eql?(n2)
|
108
|
+
hash = Hash.new
|
109
|
+
hash[n1] = 'test'
|
110
|
+
assert_equal 'test', hash[n2]
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_parse_string
|
114
|
+
doc = Document.parse_string '<library><book /></library>'
|
115
|
+
book = doc.find :first, '//book'
|
116
|
+
assert_not_nil book
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_xpath
|
120
|
+
n1 = @doc.find :first, '//team[1]'
|
121
|
+
n2 = @doc.find :first, '/sports-content/sports-event/team[1]'
|
122
|
+
assert_equal '/sports-content/sports-event/team[1]', n1.xpath
|
123
|
+
assert_equal n1, n2
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_to_s
|
127
|
+
txt = @doc.to_s
|
128
|
+
assert_match /<\?xml version=/, txt
|
129
|
+
assert_match /Chicago 3 for 11 yards/, txt
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_siblings
|
133
|
+
e = @doc.find :first, '//sports-content-code[5]'
|
134
|
+
sibs = e.siblings
|
135
|
+
assert_equal 9, sibs.size
|
136
|
+
end
|
137
|
+
|
84
138
|
end
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
!ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
rubygems_version: 0.8.11
|
3
3
|
specification_version: 1
|
4
4
|
name: teius
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
date: 2006-
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-05-05 00:00:00 +02:00
|
8
8
|
summary: Light-weight Ruby API to LibXML.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -29,7 +29,12 @@ authors:
|
|
29
29
|
- Joshua Harvey
|
30
30
|
files:
|
31
31
|
- ext/extconf.rb
|
32
|
+
- ext/Makefile
|
32
33
|
- ext/teius.c
|
34
|
+
- ext/teius.o
|
35
|
+
- ext/teius.so
|
36
|
+
- lib/teius.rb
|
37
|
+
- lib/teius.so
|
33
38
|
- test/teius_test.rb
|
34
39
|
- xml/xt.3608774-update-mid-event.xml
|
35
40
|
- xml/xt.3608787-update-post-event.xml
|