added ruby bindings ... thanks to Loïs LHERBIER lois.lherbier covadis.ch
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Wed, 27 Sep 2006 21:48:05 +0000 (21:48 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Wed, 27 Sep 2006 21:48:05 +0000 (21:48 +0000)
git-svn-id: svn://svn.oetiker.ch/rrdtool/branches/1.2/program@901 a5681a0c-68f1-0310-ab6d-d61299d08faa

bindings/Makefile.am
bindings/ruby/CHANGES [new file with mode: 0644]
bindings/ruby/README [new file with mode: 0644]
bindings/ruby/extconf.rb [new file with mode: 0644]
bindings/ruby/main.c [new file with mode: 0644]
bindings/ruby/test.rb [new file with mode: 0755]
configure.ac

index 2bd1a20..a095ef8 100644 (file)
@@ -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
 
 
 # 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
 
 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)
 # 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 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##
 
 ##END##
diff --git a/bindings/ruby/CHANGES b/bindings/ruby/CHANGES
new file mode 100644 (file)
index 0000000..c84ff29
--- /dev/null
@@ -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 (file)
index 0000000..888a062
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# ruby librrd bindings
+# author: Miles Egan <miles@caddr.com>
+#
+
+- 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 (file)
index 0000000..dc30712
--- /dev/null
@@ -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 (file)
index 0000000..492eb39
--- /dev/null
@@ -0,0 +1,259 @@
+/* $Id$
+ * Substantial penalty for early withdrawal.
+ */
+
+#include <unistd.h>
+#include <ruby.h>
+#include <rrd.h>
+
+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 (executable)
index 0000000..3cdcca6
--- /dev/null
@@ -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";
index 2d4a242..a39cd8d 100644 (file)
@@ -372,7 +372,7 @@ AC_LINK_IFELSE(
                         ),
           [AC_DEFINE(NEED_MALLOC_MALLOC_H)
            AC_MSG_RESULT([yes we do])],
                         ),
           [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])
 
 
 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/, "")
 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
 
   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)
   
 
 CONFIGURE_PART(Prep for Building Language Bindings)
   
@@ -483,6 +490,44 @@ AC_SUBST(PERL)
 AC_SUBST(COMP_PERL)
 AC_SUBST(PERL_VERSION)
 
 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],
 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 "           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"
 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 "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 <tobi@oetiker.ch>"
 echo "place an order."
 echo 
 echo "                               -- Tobi Oetiker <tobi@oetiker.ch>"