Last active
February 4, 2016 18:54
-
-
Save NoTimeForHero/41322907bc6ad2cb3769 to your computer and use it in GitHub Desktop.
Ruby extension based on C++ xBase Library for DBF support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Include the Ruby headers and goodies | |
//#include "ruby.h" | |
#include <xbase64/xbase64.h> | |
#include "ruby.h" | |
#include <iostream> | |
#include <sys/stat.h> | |
#include <map> | |
#include <string> | |
#include <vector> | |
#include <time.h> | |
#include <algorithm> | |
#include <iconv.h> | |
#include <iconvpp.c> | |
#define LIB_VERSION "0.9.0 Alpha" | |
using namespace std; | |
struct field_type { | |
string name; | |
string type; | |
int fid; | |
int length; | |
}; | |
struct filter { | |
string name; | |
string type; | |
VALUE value; | |
int fid; | |
char condition; | |
}; | |
typedef std::map<std::string,field_type> fields_map; | |
typedef std::vector<filter> filter_map; | |
typedef std::vector<std::string> selected_fields; | |
static VALUE DBFLib; | |
static VALUE Record; | |
static VALUE read_record(VALUE,xbDbf*,fields_map*,bool); | |
extern "C" void Init_dbflib(); | |
static VALUE lib_alloc(VALUE); | |
static VALUE lib_init(int,VALUE*,VALUE); | |
static VALUE lib_open(VALUE,VALUE); | |
static VALUE lib_find(int,VALUE*,VALUE); | |
static VALUE lib_show_field(int,VALUE*,VALUE); | |
static VALUE lib_path(VALUE); | |
static VALUE lib_fields(VALUE); | |
static VALUE lib_records(VALUE); | |
static VALUE lib_filters(VALUE); | |
static VALUE lib_enum(VALUE); | |
static VALUE lib_all(VALUE); | |
static VALUE lib_where(int,VALUE*,VALUE); | |
static VALUE lib_record_init(VALUE,VALUE); | |
static VALUE lib_record_call(int,VALUE*,VALUE); | |
static VALUE lib_record_attributes(VALUE); | |
static VALUE lib_record_to_json(int,VALUE*,VALUE); | |
static VALUE lib_close(VALUE); | |
static VALUE lib_version(VALUE); | |
static VALUE lib_manual_free(VALUE); | |
static VALUE lib_charset(VALUE,VALUE,VALUE); | |
static char * valueToChar(VALUE,bool); | |
class lib_storage { | |
private: | |
static const int signature = 0x734af; // I don't know why it needed | |
public: | |
xbXBase *x; | |
xbDbf *dbf; | |
string path; | |
bool isDbfLoaded; | |
bool needIconv; | |
string charsetSrc; | |
string charsetTrg; | |
iconvpp::converter *conver; | |
int size; | |
fields_map *fields; | |
filter_map *filters; | |
selected_fields *selected; | |
lib_storage() { | |
x = new xbXBase(); | |
dbf = new xbDbf(x); | |
path = ""; | |
isDbfLoaded = false; | |
needIconv = false; | |
} | |
~lib_storage() { | |
std::cout << "[C++] This destructor may be called only in manually mode..." << std::endl; | |
} | |
}; | |
string iconvert(string from, string to, string text) | |
{ | |
std::cout << from << " -> " << to << " : " << text << endl; | |
char *outbuf; | |
char *ip = (char *) text.c_str(); | |
char *op = outbuf; | |
iconv_t cnv = iconv_open(to.c_str(), from.c_str()); | |
if (cnv == (iconv_t) - 1) { | |
iconv_close(cnv); | |
return ""; | |
} | |
//outbuf = (char*)malloc(6); | |
if ((outbuf = (char *) malloc(text.length()*2 + 1)) == NULL) { | |
iconv_close(cnv); | |
return ""; | |
} | |
//size_t icount = text.length(), ocount = text.length()*2; | |
size_t icount = 5, ocount = 5*2; | |
if (iconv(cnv, &ip, &icount, &op, &ocount) != (size_t) - 1) { | |
outbuf[text.length()*2 - ocount] = '\0'; | |
//text = outbuf; | |
} | |
//else{ | |
//text = ""; | |
//} | |
std::cout << from << " -> " << to << " result: " << text << endl; | |
//delete []outbuf; | |
iconv_close(cnv); | |
return text; | |
} | |
std::string cleaner(std::string input) { | |
input.erase(0, input.find_first_not_of(' ')); //prefixing spaces | |
input.erase(input.find_last_not_of(' ')+1); //surfixing spaces | |
return input; | |
} | |
// Converts ruby symbol or string to char array | |
static char * valueToChar(VALUE value, bool throwError = true) { | |
int type = TYPE(value); | |
switch (type) { | |
case T_STRING: | |
return RSTRING(value)->ptr; | |
case T_SYMBOL: | |
// calling intern function Symbol.to_s on argument | |
return RSTRING(rb_funcall(value, rb_intern("to_s"), 0))->ptr; | |
default: | |
if (throwError) rb_raise(rb_eTypeError, "can't convert this type to text"); | |
return new char[0]; | |
} | |
} | |
// The initialization method for this module | |
extern "C" void Init_dbflib() { | |
DBFLib = rb_define_class("DBFLib", rb_cObject); | |
rb_define_alloc_func(DBFLib, lib_alloc); | |
rb_define_private_method(DBFLib, "initialize", RUBY_METHOD_FUNC(lib_init), -1); | |
rb_define_singleton_method(DBFLib, "version", RUBY_METHOD_FUNC(lib_version), 0); | |
rb_define_method(DBFLib, "open", RUBY_METHOD_FUNC(lib_open), 1); | |
rb_define_method(DBFLib, "path", RUBY_METHOD_FUNC(lib_path), 0); | |
rb_define_method(DBFLib, "fields", RUBY_METHOD_FUNC(lib_fields), 0); | |
rb_define_method(DBFLib, "records", RUBY_METHOD_FUNC(lib_records), 0); | |
rb_define_method(DBFLib, "filters", RUBY_METHOD_FUNC(lib_filters), 0); | |
rb_define_method(DBFLib, "find", RUBY_METHOD_FUNC(lib_find), -1); | |
rb_define_method(DBFLib, "enum", RUBY_METHOD_FUNC(lib_enum), 0); | |
rb_define_method(DBFLib, "all", RUBY_METHOD_FUNC(lib_all), 0); | |
rb_define_method(DBFLib, "where", RUBY_METHOD_FUNC(lib_where), -1); | |
rb_define_method(DBFLib, "close", RUBY_METHOD_FUNC(lib_close), 0); | |
rb_define_method(DBFLib, "free", RUBY_METHOD_FUNC(lib_manual_free), 0); | |
rb_define_method(DBFLib, "charset", RUBY_METHOD_FUNC(lib_charset), 2); | |
rb_define_method(DBFLib, "show_field", RUBY_METHOD_FUNC(lib_show_field), -1); | |
//Record = rb_define_class_under(DBFLib, "Record", rb_cHash); | |
Record = rb_define_class_under(DBFLib, "Record", rb_cObject); | |
rb_define_private_method(Record, "initialize", RUBY_METHOD_FUNC(lib_record_init), 1); | |
rb_define_method(Record, "method_missing", RUBY_METHOD_FUNC(lib_record_call), -1); | |
rb_define_method(Record, "attributes", RUBY_METHOD_FUNC(lib_record_attributes), 0); | |
rb_define_method(Record, "to_json", RUBY_METHOD_FUNC(lib_record_to_json), -1); | |
// Need this library for Date type in xBase | |
rb_require("date"); | |
} | |
static lib_storage *getData(VALUE self) { | |
lib_storage *storage; | |
Data_Get_Struct(self, lib_storage, storage); | |
return storage; | |
} | |
void lib_free(void *data) { | |
//std::cout << "C Free called" << std::endl; | |
ruby_xfree(data); | |
} | |
static VALUE lib_version(VALUE self) { | |
return rb_str_new2(LIB_VERSION); | |
} | |
static VALUE lib_alloc(VALUE self) { | |
lib_storage *data = new lib_storage(); | |
//std::cout << "C Alloc called" << std::endl; | |
return Data_Wrap_Struct(self, NULL, lib_free, data); | |
} | |
static VALUE lib_init(int argc, VALUE *argv, VALUE self) { | |
if (argc > 1) rb_raise(rb_eArgError, "wrong number of arguments ( > 1 )"); | |
lib_open(self, argv[0]); | |
return Qnil; | |
} | |
static VALUE lib_manual_free(VALUE self) { | |
getData(self)->dbf->~xbDbf(); | |
getData(self)->fields->~map(); | |
getData(self)->filters->~vector(); | |
getData(self)->~lib_storage(); | |
getData(self)->conver->~converter(); | |
getData(self)->selected->~vector(); | |
self = Qnil; | |
return Qnil; | |
} | |
static VALUE lib_close(VALUE self) { | |
getData(self)->dbf->CloseDatabase(); | |
getData(self)->fields->clear(); | |
getData(self)->filters->clear(); | |
getData(self)->selected->clear(); | |
getData(self)->isDbfLoaded = false; | |
return Qnil; | |
} | |
static VALUE lib_show_field(int argc, VALUE *argv, VALUE self) { | |
for (int i=0;i<argc;i++) { | |
if (TYPE(argv[i]) != T_STRING && TYPE(argv[i]) != T_SYMBOL) continue; | |
getData(self)->selected->push_back(string(valueToChar(argv[i]))); | |
} | |
return INT2NUM(argc); | |
} | |
static VALUE lib_open(VALUE self, VALUE rbPath) { | |
Check_Type(rbPath, T_STRING); // must be a string | |
char *path = RSTRING(rbPath)->ptr; | |
//std::cout << "Failed must be here?" << (getData(self)->isDbfLoaded ? "true" : "false") << std::endl; | |
if (getData(self)->isDbfLoaded) lib_close(self); | |
getData(self)->fields = new map<string,field_type>(); | |
getData(self)->filters = new vector<filter>(); | |
getData(self)->selected = new vector<std::string>(); | |
getData(self)->path = string(path); | |
getData(self)->isDbfLoaded = true; | |
xbXBase *x = getData(self)->x; | |
xbDbf *dbf = getData(self)->dbf; | |
xbShort status; | |
if(( status = dbf->OpenDatabase( path )) != XB_NO_ERROR ) { | |
rb_raise(rb_eIOError, x->GetErrorMessage(status)); | |
} | |
fields_map *fields = getData(self)->fields; | |
fields->clear(); | |
for (int i = 0; i < dbf->FieldCount(); i++ ) { | |
string type; | |
switch(dbf->GetFieldType(i)) { | |
case 'D': | |
type = "Date"; | |
break; | |
case 'C': | |
type = "String"; | |
break; | |
case 'N': | |
type = "Numeric"; | |
break; | |
case 'M': | |
type = "Memo"; | |
break; | |
default: | |
type = string("Unknown ") + dbf->GetFieldType(i); | |
} | |
field_type data; | |
data.name = dbf->GetFieldName(i); | |
data.type = type; | |
data.length = dbf->GetFieldLen(i); | |
data.fid = i; | |
fields->insert(fields->end(), std::pair<string,field_type>(dbf->GetFieldName(i), data)); | |
} | |
if(( status = dbf->GetLastRecord()) != XB_NO_ERROR ) | |
rb_raise(rb_eRuntimeError, x->GetErrorMessage(status)); | |
getData(self)->size = dbf->GetCurRecNo(); | |
if(( status = dbf->GetFirstRecord()) != XB_NO_ERROR ) | |
rb_raise(rb_eRuntimeError, x->GetErrorMessage(status)); | |
return Qtrue; | |
} | |
static VALUE lib_path(VALUE self) { | |
if (!getData(self)->isDbfLoaded) return Qnil; | |
return rb_str_new2(getData(self)->path.c_str()); | |
} | |
static VALUE lib_fields(VALUE self) { | |
if (!getData(self)->isDbfLoaded) return Qnil; | |
VALUE result = rb_hash_new(); | |
//xbDbf *dbf = getData(self)->dbf; | |
fields_map *fields = getData(self)->fields; | |
for (fields_map::iterator it = fields->begin(); it != fields->end(); ++it) { | |
VALUE data = rb_hash_new(); | |
rb_hash_aset(data, rb_str_new2("Type"), rb_str_new2(it->second.type.c_str())); | |
rb_hash_aset(data, rb_str_new2("Length"), INT2NUM(it->second.length)); | |
rb_hash_aset(data, rb_str_new2("ID"), INT2NUM(it->second.fid)); | |
rb_hash_aset(result, rb_str_new2(it->second.name.c_str()), data); | |
} | |
return result; | |
} | |
static VALUE lib_charset(VALUE self, VALUE input, VALUE target) { | |
if (TYPE(input) != T_STRING || TYPE(target) != T_STRING) rb_raise(rb_eArgError, "All arguments must be a valid string!"); | |
lib_storage *settings = getData(self); | |
settings->needIconv = true; | |
//settings-> | |
string charsetSrc = valueToChar(input); | |
//settings-> | |
string charsetTrg = valueToChar(target); | |
settings->conver = new iconvpp::converter(charsetTrg, charsetSrc); | |
return Qtrue; | |
} | |
static VALUE lib_records(VALUE self) { | |
if (!getData(self)->isDbfLoaded) return Qnil; | |
return INT2NUM(getData(self)->size); | |
} | |
static VALUE lib_filters(VALUE self) { | |
if (!getData(self)->isDbfLoaded) return Qnil; | |
filter_map *filters = getData(self)->filters; | |
VALUE data = rb_ary_new(); | |
for (filter_map::iterator it = filters->begin(); it != filters->end(); ++it) { | |
string type = "???"; | |
if (it->condition == 0) type = "="; | |
if (it->condition == 1) type = ">"; | |
if (it->condition == 2) type = "<"; | |
if (it->condition == 3) type = "<>"; | |
VALUE element = rb_hash_new(); | |
rb_hash_aset(element, rb_str_new2("field"), rb_str_new2(it->name.c_str())); | |
rb_hash_aset(element, rb_str_new2("type"), rb_str_new2(it->type.c_str())); | |
rb_hash_aset(element, rb_str_new2("value"), it->value); | |
rb_hash_aset(element, rb_str_new2("id"), INT2NUM(it->fid)); | |
rb_hash_aset(element, rb_str_new2("condition"), rb_str_new2(type.c_str())); | |
rb_ary_push(data, element); | |
} | |
return data; | |
} | |
static VALUE lib_enum(VALUE self) { | |
if (!getData(self)->isDbfLoaded) return Qnil; | |
xbDbf *dbf = getData(self)->dbf; | |
for (int i = 0; i < dbf->FieldCount(); i++ ) { | |
RETURN_ENUMERATOR(self, 0, NULL); | |
rb_yield( rb_str_new2( dbf->GetFieldName(i) )); | |
} | |
return Qnil; | |
} | |
static VALUE read_record(VALUE self, xbDbf *dbf, fields_map *fields, bool only_selected) { | |
VALUE data = rb_hash_new(); | |
lib_storage *settings = getData(self); | |
selected_fields *selected = settings->selected;; | |
for (fields_map::iterator it = fields->begin(); it != fields->end(); ++it) { | |
if (only_selected && std::find(selected->begin(),selected->end(), it->second.name) == selected->end()) continue; | |
xbShort fid = dbf->GetFieldNo(it->second.name.c_str()); | |
if (it->second.type == "Numeric") { | |
int value = dbf->GetLongField(fid); | |
rb_hash_aset(data, rb_str_new2(it->second.name.c_str()), INT2NUM(value) ); | |
continue; | |
} | |
if (it->second.type == "String") { | |
string value = cleaner(string(dbf->GetStringField(fid))); | |
if (settings->needIconv) getData(self)->conver->convert(value, value); | |
rb_hash_aset(data, rb_str_new2(it->second.name.c_str()), rb_str_new2( value.c_str() )); | |
continue; | |
} | |
if (it->second.type == "Date") { | |
string raw_date = cleaner(string(dbf->GetStringField(fid))); | |
if (raw_date.size() == 8) { | |
/* | |
VALUE *args = new VALUE[3]; | |
args[0] = INT2NUM(atoi(raw_date.substr(0,4).c_str())); | |
args[1] = INT2NUM(atoi(raw_date.substr(4,2).c_str())); | |
args[2] = INT2NUM(atoi(raw_date.substr(6,2).c_str())); | |
VALUE date = rb_class_new_instance(3, args, rb_path2class("Date")); | |
date = rb_funcall(date, rb_intern("inspect"), 0); | |
*/ | |
VALUE date = rb_str_new2(string(raw_date.substr(6,2) + "." + raw_date.substr(4,2) + "." + raw_date.substr(0,4)).c_str()); | |
rb_hash_aset(data, rb_str_new2(it->second.name.c_str()), date); | |
//delete []args; | |
} else { | |
// strange date = nil | |
rb_hash_aset(data, rb_str_new2(it->second.name.c_str()), Qnil); | |
} | |
continue; | |
} | |
} | |
VALUE *args = new VALUE[1]; | |
args[0] = data; | |
data = rb_class_new_instance(1, args, Record); | |
delete []args; | |
return data; | |
} | |
static VALUE lib_all(VALUE self) { | |
getData(self)->filters->clear(); | |
getData(self)->selected->clear(); | |
return self; | |
} | |
static VALUE lib_where(int argc, VALUE *argv, VALUE self) { | |
if (!getData(self)->isDbfLoaded) return Qnil; | |
if (TYPE(argv[0]) != T_STRING && TYPE(argv[0]) != T_SYMBOL) rb_raise(rb_eArgError,"xBase Field (first argument) must be a string or a symbol!"); | |
if (argc < 2 || argc > 3) rb_raise(rb_eArgError,"Too much parameters to filter (2 or 3 required)"); | |
fields_map *fields = getData(self)->fields; | |
filter_map *filters = getData(self)->filters; | |
char condition = 0; | |
char *fieldName = valueToChar(argv[0]); | |
string type; | |
int fid = 0; | |
VALUE value = argv[1]; | |
if (argc == 3) { | |
if (TYPE(argv[2]) != T_STRING && TYPE(argv[2]) != T_SYMBOL) rb_raise(rb_eArgError,"Condition (third argument) must be a string or a symbol!"); | |
char *rType = valueToChar(argv[2]); | |
if (strcmp(rType, ">")==0 && strlen(rType)==1) condition = 1; | |
if (strcmp(rType, "<")==0 && strlen(rType)==1) condition = 2; | |
if (strcmp(rType, "<>")==0 && strlen(rType)==2) condition = 3; | |
if (strcmp(rType, "!=")==0 && strlen(rType)==2) condition = 3; | |
delete []rType; | |
} | |
bool finded = false; | |
for (fields_map::iterator it = fields->begin(); it != fields->end(); ++it) { | |
if (it->second.name == fieldName) { // String == char comparasion (must work with std::string overloaded == operation) | |
type = it->second.type; | |
fid = it->second.fid; | |
finded = true; | |
break; | |
} | |
} | |
if (!finded) rb_raise(rb_eArgError,"xBase Field doesn't exists!"); | |
if (TYPE(argv[1]) == T_ARRAY) { | |
rb_raise(rb_eScriptError,"Not Implemented yet!"); | |
} | |
filter data; | |
data.name = string(fieldName); | |
data.condition = condition; | |
data.value = value; | |
data.type = type; | |
data.fid = fid; | |
/* | |
if (TYPE(type) == T_FIXNUM || TYPE(type) == T_SYMBOL) type = rb_funcall(type, rb_intern("to_s"), 0); | |
// Check variable for object | |
if (TYPE(type) == T_OBJECT) { | |
// Check object for a date class | |
if (rb_funcall(type, rb_intern("instance_of?"), 1, rb_path2class("Date")) == Qtrue) { | |
type = rb_funcall(type, rb_intern("strftime"), 1, rb_str_new2("%Y%m%d")); | |
//type = rb_funcall(type, rb_intern("to_s"), 0); | |
} | |
} | |
if (TYPE(type) == T_STRING) { | |
data.value = type;; | |
} else { | |
rb_raise(rb_eArgError,"Unknown value (second argument) type!"); | |
} | |
*/ | |
filters->push_back(data); | |
return self; | |
} | |
static VALUE lib_find(int argc, VALUE *argv, VALUE self) { | |
if (!getData(self)->isDbfLoaded) return Qnil; | |
if (argc > 2 || argc < 1) { | |
rb_raise(rb_eArgError, "This method accepts only 1 parameter"); | |
} | |
bool useYield = true; | |
if (argc == 2) { | |
if (argv[1] == Qfalse) useYield = false; | |
if (argv[1] == Qtrue) useYield = true; | |
} | |
lib_storage *settings = getData(self); | |
filter_map *filters = settings->filters; | |
fields_map *fields = settings->fields; | |
selected_fields *selected = settings->selected; | |
xbDbf *dbf = settings->dbf; | |
xbXBase *x = settings->x; | |
xbShort status; | |
int matches = 0; | |
bool only_selected = (selected->size() > 0); | |
bool error = true; | |
bool first = false; | |
bool count = false; | |
if (TYPE(argv[0]) == T_FIXNUM) { | |
if (argc > 2) rb_raise(rb_eArgError, "Too much parameters to get one record (required 1 param)"); | |
int id = NUM2INT(argv[0]); | |
status = dbf->GetRecord(id); | |
if (status != XB_NO_ERROR) rb_raise(rb_eIOError, x->GetErrorMessage(status)); | |
VALUE data = read_record(self, dbf, fields, only_selected); | |
if (useYield) { | |
RETURN_ENUMERATOR(self, 0, NULL); | |
rb_yield( data ); | |
} else { | |
return data; | |
} | |
return Qnil; | |
} | |
if (TYPE(argv[0]) != T_STRING && TYPE(argv[0]) != T_SYMBOL) rb_raise(rb_eArgError, "Unknown type to search (string or fixnum expected)"); | |
if (TYPE(argv[0]) == T_SYMBOL) argv[0] = rb_funcall(argv[0], rb_intern("to_s"), 0); | |
char *data = RSTRING(argv[0])->ptr; | |
if (strcmp(data, "first") == 0) { | |
error = false; | |
first = true; | |
} | |
if (strcmp(data, "all") == 0) { | |
error = false; | |
} | |
if (strcmp(data, "count") == 0) { | |
error = false; | |
count = true; | |
} | |
if (error) rb_raise(rb_eArgError, "Unknown find parameter"); | |
VALUE dataArray; | |
if (!useYield) dataArray = rb_ary_new(); | |
//status = dbf->GetFirstRecord(); | |
//if (status != XB_NO_ERROR) rb_raise(rb_eIOError, x->GetErrorMessage(status)); | |
int total = dbf->NoOfRecords(); | |
for (int id=1; id < total; id++) { | |
bool finded = true; | |
//std::cout << "Record " << id << " of " << total << std::endl; | |
status = dbf->GetRecord(id); | |
if (status != XB_NO_ERROR) rb_raise(rb_eIOError, x->GetErrorMessage(status)); | |
for (filter_map::iterator it = filters->begin(); it != filters->end(); ++it) { | |
int fid = it->fid; | |
if (it->type == "Numeric" && TYPE(it->value) != T_FIXNUM) rb_raise(rb_eArgError, "Find argument error: expected fixnum got unknown"); | |
if (it->type == "String" && TYPE(it->value) != T_STRING) rb_raise(rb_eArgError, "Find argument error: expected string got unknown"); | |
if (it->type == "Numeric" && it->condition == 0 && NUM2INT(it->value) != dbf->GetLongField(fid)) finded = false; | |
if (it->type == "Numeric" && it->condition == 1 && NUM2INT(it->value) < dbf->GetLongField(fid)) finded = false; | |
if (it->type == "Numeric" && it->condition == 2 && NUM2INT(it->value) > dbf->GetLongField(fid)) finded = false; | |
if (it->type == "Numeric" && it->condition == 3 && NUM2INT(it->value) == dbf->GetLongField(fid)) finded = false; | |
if (it->type == "String") { | |
string fieldVal = cleaner(string(dbf->GetStringField(fid))); | |
if (settings->needIconv) getData(self)->conver->convert(fieldVal,fieldVal); //fieldVal = iconvert(settings->charsetSrc, settings->charsetTrg, fieldVal); | |
if (it->condition == 0 && string(RSTRING(it->value)->ptr) != fieldVal) finded = false; | |
if (it->condition == 3 && string(RSTRING(it->value)->ptr) == fieldVal) finded = false; | |
} | |
if (it->type == "Date") { | |
} | |
//std::cout << "Filter " << it->name << "(type " << it->type << " id " << it->fid << ") return " << (finded ? "true" : "false") << std::endl; | |
} | |
if (!finded) continue; // skip record | |
if (count) { | |
matches++; | |
continue; | |
} | |
VALUE data = read_record(self, dbf, fields, only_selected); | |
if (useYield) { | |
RETURN_ENUMERATOR(self, 0, NULL); | |
rb_yield( data ); | |
} else { | |
rb_ary_push(dataArray, data); | |
} | |
if (first) break; // show only one record | |
} | |
if (!useYield) return dataArray; | |
if (count) return INT2NUM(matches); | |
return Qnil; | |
} | |
static VALUE lib_record_init(VALUE self, VALUE array) { | |
Check_Type(array, T_HASH); | |
rb_iv_set(self, "@data", array); | |
return Qnil; | |
} | |
static VALUE lib_record_attributes(VALUE self) { | |
return rb_iv_get(self, "@data"); | |
} | |
static VALUE lib_record_to_json(int argc, VALUE *argv, VALUE self) { | |
VALUE data = rb_iv_get(self, "@data"); | |
data = rb_funcall(data, rb_intern("to_json"), 0); | |
return data; | |
} | |
static VALUE lib_record_call(int argc, VALUE *argv, VALUE self) { | |
if (argc < 1) rb_raise(rb_eSyntaxError, "Oops. You can't call missing_method manually!"); | |
if (argc > 2) rb_raise(rb_eArgError, "Too much parameters (2 max)!"); | |
VALUE hash = rb_iv_get(self, "@data"); | |
VALUE key = argv[0]; | |
if (argc == 1) { | |
if (TYPE(key) == T_SYMBOL) key = rb_funcall(key, rb_intern("to_s"), 0); | |
return rb_hash_aref(hash, key); | |
} | |
if (argc == 2) { | |
if (strcmp("[]", valueToChar(argv[0])) != 0) rb_raise(rb_eSyntaxError, "Very strange call..."); | |
key = argv[1]; | |
if (TYPE(key) == T_SYMBOL) key = rb_funcall(key, rb_intern("to_s"), 0); | |
return rb_hash_aref(hash, key); | |
} | |
// Protection from Segmentation Fault | |
rb_raise(rb_eSyntaxError, "Something strange were happened!"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment