From f718153b987ad9b2fbba81b1db248e0fda83a57e Mon Sep 17 00:00:00 2001 From: oetiker Date: Wed, 27 Sep 2006 21:48:05 +0000 Subject: [PATCH] =?utf8?q?added=20ruby=20bindings=20...=20thanks=20to=20Lo?= =?utf8?q?=C3=AFs=20LHERBIER=20lois.lherbier=20covadis.ch?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit git-svn-id: svn://svn.oetiker.ch/rrdtool/branches/1.2/program@901 a5681a0c-68f1-0310-ab6d-d61299d08faa --- bindings/Makefile.am | 11 +- bindings/ruby/CHANGES | 3 + bindings/ruby/README | 22 ++++ bindings/ruby/extconf.rb | 18 ++++ bindings/ruby/main.c | 259 +++++++++++++++++++++++++++++++++++++++++++++++ bindings/ruby/test.rb | 50 +++++++++ configure.ac | 52 +++++++++- 7 files changed, 412 insertions(+), 3 deletions(-) create mode 100644 bindings/ruby/CHANGES create mode 100644 bindings/ruby/README create mode 100644 bindings/ruby/extconf.rb create mode 100644 bindings/ruby/main.c create mode 100755 bindings/ruby/test.rb diff --git a/bindings/Makefile.am b/bindings/Makefile.am index 2bd1a20..a095ef8 100644 --- a/bindings/Makefile.am +++ b/bindings/Makefile.am @@ -14,12 +14,20 @@ EXTRA_DIST = perl-piped/MANIFEST perl-piped/README perl-piped/Makefile.PL perl-p # add the following to the all target -all-local: @COMP_PERL@ +all-local: @COMP_PERL@ @COMP_RUBY@ install-data-local: test -f perl-piped/Makefile && cd perl-piped && $(MAKE) install || true test -f perl-shared/Makefile && cd perl-shared && $(MAKE) install || true + test -f ruby/Makefile && cd ruby && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) install || true +# rules for buildung the ruby module +ruby: ruby/Makefile + cd ruby && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) + +ruby/Makefile: ruby/extconf.rb + cd ruby && $(RUBY) extconf.rb + # rules for building the perl module perl_piped: perl-piped/Makefile cd perl-piped && $(MAKE) @@ -39,5 +47,6 @@ clean-local: test -f perl-piped/Makefile && rm perl-piped/Makefile || true test -f perl-shared/Makefile && cd perl-shared && $(MAKE) clean || true test -f perl-shared/Makefile && rm -f perl-shared/Makefile || true + test -f ruby/Makefile && rm -f ruby/Makefile || true ##END## diff --git a/bindings/ruby/CHANGES b/bindings/ruby/CHANGES new file mode 100644 index 0000000..c84ff29 --- /dev/null +++ b/bindings/ruby/CHANGES @@ -0,0 +1,3 @@ +2006-07-25 Loïs Lherbier + add y_min and y_max parameters to rrd_graph + update test.rb to send only strings to RRD.fetch diff --git a/bindings/ruby/README b/bindings/ruby/README new file mode 100644 index 0000000..888a062 --- /dev/null +++ b/bindings/ruby/README @@ -0,0 +1,22 @@ +# +# ruby librrd bindings +# author: Miles Egan +# + +- Introduction + +This module provides ruby bindings for librrd, with functionality +comparable to the native perl bindings. See test.rb for a script that +exercises all ruby-librrd functionality. + +- Installation + +Installation is standard. Simply run: + +ruby extconf.rb +make +make install + +I hope this works for you. Please let me know if you have any +problems or suggestions. Someday when I'm feeling less lazy I'll +actually document this thing. Thanks to Tobi for rrdtool! diff --git a/bindings/ruby/extconf.rb b/bindings/ruby/extconf.rb new file mode 100644 index 0000000..dc30712 --- /dev/null +++ b/bindings/ruby/extconf.rb @@ -0,0 +1,18 @@ +# $Id: extconf.rb,v 1.2 2001/11/28 18:30:16 miles Exp $ +# Lost ticket pays maximum rate. + +require 'mkmf' + +if /linux/ =~ RUBY_PLATFORM + $LDFLAGS += '-Wl,--rpath -Wl,$(EPREFIX)/lib' +elsif /solaris/ =~ RUBY_PLATFORM + $LDFLAGS += '-R$(EPREFIX)/lib' +elsif /hpux/ =~ RUBY_PLATFORM + $LDFLAGS += '+b$(EPREFIX)/lib'; +elsif /aix/ =~ RUBY_PLATFORM + $LDFLAGS += '-Wl,-blibpath:$(EPREFIX)/lib' +end + +dir_config("rrd","../../src","../../src/.libs") +have_library("rrd", "rrd_create") +create_makefile("RRD") diff --git a/bindings/ruby/main.c b/bindings/ruby/main.c new file mode 100644 index 0000000..492eb39 --- /dev/null +++ b/bindings/ruby/main.c @@ -0,0 +1,259 @@ +/* $Id$ + * Substantial penalty for early withdrawal. + */ + +#include +#include +#include + +typedef struct string_arr_t { + int len; + char **strings; +} string_arr; + +VALUE mRRD; +VALUE rb_eRRDError; + +typedef int (*RRDFUNC)(int argc, char ** argv); +#define RRD_CHECK_ERROR \ + if (rrd_test_error()) \ + rb_raise(rb_eRRDError, rrd_get_error()); \ + rrd_clear_error(); + +string_arr string_arr_new(VALUE rb_strings) +{ + string_arr a; + char buf[64]; + int i; + + Check_Type(rb_strings, T_ARRAY); + a.len = RARRAY(rb_strings)->len + 1; + + a.strings = malloc(a.len * sizeof(char *)); + a.strings[0] = "dummy"; /* first element is a dummy element */ + + for (i = 0; i < a.len - 1; i++) { + VALUE v = rb_ary_entry(rb_strings, i); + switch (TYPE(v)) { + case T_STRING: + a.strings[i + 1] = strdup(STR2CSTR(v)); + break; + case T_FIXNUM: + snprintf(buf, 63, "%d", FIX2INT(v)); + a.strings[i + 1] = strdup(buf); + break; + default: + rb_raise(rb_eTypeError, "invalid argument"); + break; + } + } + + return a; +} + +void string_arr_delete(string_arr a) +{ + int i; + + /* skip dummy first entry */ + for (i = 1; i < a.len; i++) { + free(a.strings[i]); + } + + free(a.strings); +} + +void reset_rrd_state() +{ + optind = 0; + opterr = 0; + rrd_clear_error(); +} + +VALUE rrd_call(RRDFUNC func, VALUE args) +{ + string_arr a; + + a = string_arr_new(args); + reset_rrd_state(); + func(a.len, a.strings); + string_arr_delete(a); + + RRD_CHECK_ERROR + + return Qnil; +} + +VALUE rb_rrd_create(VALUE self, VALUE args) +{ + return rrd_call(rrd_create, args); +} + +VALUE rb_rrd_dump(VALUE self, VALUE args) +{ + return rrd_call(rrd_dump, args); +} + +VALUE rb_rrd_fetch(VALUE self, VALUE args) +{ + string_arr a; + unsigned long i, j, k, step, ds_cnt; + rrd_value_t *raw_data; + char **raw_names; + VALUE data, names, result; + time_t start, end; + + a = string_arr_new(args); + reset_rrd_state(); + rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names, &raw_data); + string_arr_delete(a); + + RRD_CHECK_ERROR + + names = rb_ary_new(); + for (i = 0; i < ds_cnt; i++) { + rb_ary_push(names, rb_str_new2(raw_names[i])); + free(raw_names[i]); + } + free(raw_names); + + k = 0; + data = rb_ary_new(); + for (i = start; i <= end; i += step) { + VALUE line = rb_ary_new2(ds_cnt); + for (j = 0; j < ds_cnt; j++) { + rb_ary_store(line, j, rb_float_new(raw_data[k])); + k++; + } + rb_ary_push(data, line); + } + free(raw_data); + + result = rb_ary_new2(4); + rb_ary_store(result, 0, INT2FIX(start)); + rb_ary_store(result, 1, INT2FIX(end)); + rb_ary_store(result, 2, names); + rb_ary_store(result, 2, data); + return result; +} + +VALUE rb_rrd_graph(VALUE self, VALUE args) +{ + string_arr a; + char **calcpr, **p; + VALUE result, print_results; + int i, xsize, ysize; + double ymin, ymax; + + a = string_arr_new(args); + reset_rrd_state(); + rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax); + string_arr_delete(a); + + RRD_CHECK_ERROR + + result = rb_ary_new2(3); + print_results = rb_ary_new(); + p = calcpr; + for (p = calcpr; p && *p; p++) { + rb_ary_push(print_results, rb_str_new2(*p)); + free(*p); + } + free(calcpr); + rb_ary_store(result, 0, print_results); + rb_ary_store(result, 1, INT2FIX(xsize)); + rb_ary_store(result, 2, INT2FIX(ysize)); + return result; +} + +/* +VALUE rb_rrd_info(VALUE self, VALUE args) +{ + string_arr a; + info_t *p; + VALUE result; + + a = string_arr_new(args); + data = rrd_info(a.len, a.strings); + string_arr_delete(a); + + RRD_CHECK_ERROR + + result = rb_hash_new(); + while (data) { + VALUE key = rb_str_new2(data->key); + switch (data->type) { + case RD_I_VAL: + if (isnan(data->u_val)) { + rb_hash_aset(result, key, Qnil); + } + else { + rb_hash_aset(result, key, rb_float_new(data->u_val)); + } + break; + case RD_I_CNT: + rb_hash_aset(result, key, INT2FIX(data->u_cnt)); + break; + case RD_I_STR: + rb_hash_aset(result, key, rb_str_new2(data->u_str)); + free(data->u_str); + break; + } + p = data; + data = data->next; + free(p); + } + return result; +} +*/ + +VALUE rb_rrd_last(VALUE self, VALUE args) +{ + string_arr a; + time_t last; + + a = string_arr_new(args); + reset_rrd_state(); + last = rrd_last(a.len, a.strings); + string_arr_delete(a); + + RRD_CHECK_ERROR + + return rb_funcall(rb_cTime, rb_intern("at"), 1, INT2FIX(last)); +} + +VALUE rb_rrd_resize(VALUE self, VALUE args) +{ + return rrd_call(rrd_resize, args); +} + +VALUE rb_rrd_restore(VALUE self, VALUE args) +{ + return rrd_call(rrd_restore, args); +} + +VALUE rb_rrd_tune(VALUE self, VALUE args) +{ + return rrd_call(rrd_tune, args); +} + +VALUE rb_rrd_update(VALUE self, VALUE args) +{ + return rrd_call(rrd_update, args); +} + +void Init_RRD() +{ + mRRD = rb_define_module("RRD"); + rb_eRRDError = rb_define_class("RRDError", rb_eStandardError); + + rb_define_module_function(mRRD, "create", rb_rrd_create, -2); + rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2); + rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2); + rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2); + rb_define_module_function(mRRD, "last", rb_rrd_last, -2); + rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2); + rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2); + rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2); + rb_define_module_function(mRRD, "update", rb_rrd_update, -2); +} diff --git a/bindings/ruby/test.rb b/bindings/ruby/test.rb new file mode 100755 index 0000000..3cdcca6 --- /dev/null +++ b/bindings/ruby/test.rb @@ -0,0 +1,50 @@ +#!/usr/bin/env ruby +# $Id: test.rb,v 1.2 2002/10/22 17:34:00 miles Exp $ +# Driver does not carry cash. + +require "RRD" + +name = "test" +rrd = "#{name}.rrd" +start = Time.now.to_i + +puts "creating #{rrd}" +RRD.create( + rrd, + "--start", "#{start - 1}", + "--step", "300", + "DS:a:GAUGE:600:U:U", + "DS:b:GAUGE:600:U:U", + "RRA:AVERAGE:0.5:1:300") +puts + +puts "updating #{rrd}" +start.to_i.step(start.to_i + 300 * 300, 300) { |i| + RRD.update(rrd, "#{i}:#{rand(100)}:#{Math.sin(i / 800) * 50 + 50}") +} +puts + +puts "fetching data from #{rrd}" +(fstart, fend, data) = RRD.fetch(rrd, "--start", start.to_s, "--end", (start + 300 * 300).to_s, "AVERAGE") +puts "got #{data.length} data points from #{fstart} to #{fend}" +puts + +puts "generating graph #{name}.png" +RRD.graph( + "#{name}.png", + "--title", " RubyRRD Demo", + "--start", "#{start} + 1 h", + "--end", "#{start} + 1000 min", + "--interlace", + "--imgformat", "PNG", + "--width=450", + "DEF:a=#{rrd}:a:AVERAGE", + "DEF:b=#{rrd}:b:AVERAGE", + "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF", + "AREA:b#00b6e4:beta", + "AREA:line#0022e9:alpha", + "LINE3:line#ff0000") +puts + +print "This script has created #{name}.png in the current directory\n"; +print "This demonstrates the use of the TIME and % RPN operators\n"; diff --git a/configure.ac b/configure.ac index 2d4a242..a39cd8d 100644 --- a/configure.ac +++ b/configure.ac @@ -372,7 +372,7 @@ AC_LINK_IFELSE( ), [AC_DEFINE(NEED_MALLOC_MALLOC_H) AC_MSG_RESULT([yes we do])], - [AC_MSG_ERROR([Can't figure how to compile malloc])] + [AC_MSG_ERROR([Can not figure how to compile malloc])] ) ] ) @@ -383,6 +383,8 @@ CONFIGURE_PART(Findr 3rd-Party Libraries) AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no]) +CORE_LIBS="$LIBS" + EX_CHECK_ALL(art_lgpl_2, art_vpath_add_point, libart_lgpl/libart.h, libart-2.0, 2.3.17, ftp://ftp.gnome.org/pub/GNOME/sources/libart_lgpl/2.3/, /usr/include/libart-2.0) EX_CHECK_ALL(z, zlibVersion, zlib.h, zlib, 1.2.3, http://www.gzip.org/zlib/, "") EX_CHECK_ALL(png, png_access_version_number, png.h, libpng, 1.2.10, http://prdownloads.sourceforge.net/libpng/, "") @@ -392,6 +394,11 @@ if test "$EX_CHECK_ALL_ERR" = "YES"; then AC_MSG_ERROR([Please fix the library issues listed above and try again.]) fi +ALL_LIBS="$LIBS" +LIBS= + +AC_SUBST(CORE_LIBS) +AC_SUBST(ALL_LIBS) CONFIGURE_PART(Prep for Building Language Bindings) @@ -483,6 +490,44 @@ AC_SUBST(PERL) AC_SUBST(COMP_PERL) AC_SUBST(PERL_VERSION) +dnl Check for Ruby. +AC_PATH_PROG(RUBY, ruby, no) + +AC_ARG_ENABLE(ruby,[ --disable-ruby do not build the ruby modules], +[],[enable_ruby=yes]) + + +if test "x$RUBY" = "xno" -o x$enable_ruby = xno; then + COMP_RUBY= +else + COMP_RUBY="ruby" + +fi + +AC_MSG_CHECKING(Ruby Modules to build) +AC_MSG_RESULT(${COMP_RUBY:-No Ruby Modules will be built}) + +dnl pass additional ruby options when generating Makefile from Makefile.PL +AC_ARG_ENABLE(ruby-site-install, +[ --enable-ruby-site-install by default the rrdtool ruby modules are installed + together with rrdtool in $prefix/lib/ruby. You have to + add $prefix/lib/ruby/$ruby_version/$sitearch to you $: variable + for ruby to find the RRD.so file.], +[RUBY_MAKE_OPTIONS=],[RUBY_MAKE_OPTIONS="sitedir=$prefix/lib/ruby"]) + + +AC_ARG_WITH(ruby-options, +[ --with-ruby-options=[OPTIONS] options to pass on command-line when + generating Makefile from extconf.rb. If you set this + option, interesting things may happen unless you know + what you are doing!], +[RUBY_MAKE_OPTIONS=$withval]) + +AC_SUBST(RUBY_MAKE_OPTIONS) +AC_SUBST(RUBY) +AC_SUBST(COMP_RUBY) + + enable_tcl_site=no AC_ARG_ENABLE(tcl,[ --disable-tcl do not build the tcl modules], @@ -592,6 +637,9 @@ echo " Perl Modules: $COMP_PERL" echo " Perl Binary: $PERL" echo " Perl Version: $PERL_VERSION" echo " Perl Options: $PERL_MAKE_OPTIONS" +echo " Ruby Modules: $COMP_RUBY" +echo " Ruby Binary: $RUBY" +echo " Ruby Options: $RUBY_MAKE_OPTIONS" echo " Build Tcl Bindings: $enable_tcl" echo " Build Python Bindings: $enable_python" echo " Build rrdcgi: $enable_rrdcgi" @@ -602,7 +650,7 @@ echo "Type 'make' to compile the software and use 'make install' to " echo "install everything to: $prefix." echo echo " ... that wishlist is NO JOKE. If you find RRDtool useful" -echo "make me happy. Go to http://people.ee.ethz.ch/oetiker/wish and" +echo "make me happy. Go to http://tobi.oetiker.ch/wish and" echo "place an order." echo echo " -- Tobi Oetiker " -- 2.11.0