1
0
Fork 0
arangodb/lib/MRuby/mr-utils.cpp

390 lines
11 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief mruby utilities
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "mr-utils.h"
#include <regex.h>
#include "Basics/files.h"
#include "Basics/logging.h"
#include "Basics/tri-strings.h"
#include "mruby/array.h"
#include "mruby/compile.h"
#include "mruby/data.h"
#include "mruby/hash.h"
#include "mruby/proc.h"
#include "mruby/string.h"
#include "mruby/variable.h"
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- ruby functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the time in seconds
////////////////////////////////////////////////////////////////////////////////
static mrb_value MR_Time (mrb_state* mrb, mrb_value self) {
return mrb_float_value(mrb, TRI_microtime());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief converts json to ruby structure
////////////////////////////////////////////////////////////////////////////////
static mrb_value MR_JsonParse (mrb_state* mrb, mrb_value self) {
char* errmsg;
char* s;
/* int res; */
size_t l;
TRI_json_t* json;
/* res = */ mrb_get_args(mrb, "s", &s, &l);
if (s == NULL) {
return mrb_nil_value();
}
json = TRI_Json2String(TRI_UNKNOWN_MEM_ZONE, s, &errmsg);
if (json == NULL) {
mrb_value exc;
exc = MR_ArangoError(mrb, TRI_ERROR_HTTP_CORRUPTED_JSON, errmsg);
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, errmsg);
mrb_exc_raise(mrb, exc);
TRI_ASSERT(false);
}
return MR_ObjectJson(mrb, json);
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief converts a TRI_json_t into a V8 object
////////////////////////////////////////////////////////////////////////////////
mrb_value MR_ObjectJson (mrb_state* mrb, TRI_json_t const* json) {
switch (json->_type) {
case TRI_JSON_UNUSED:
return mrb_nil_value();
case TRI_JSON_NULL:
return mrb_nil_value();
case TRI_JSON_BOOLEAN:
return json->_value._boolean ? mrb_true_value() : mrb_false_value();
case TRI_JSON_NUMBER:
return mrb_float_value(mrb, json->_value._number);
case TRI_JSON_STRING:
case TRI_JSON_STRING_REFERENCE:
// same for STRING and STRING_REFERENCE
return mrb_str_new(mrb, json->_value._string.data, json->_value._string.length - 1);
case TRI_JSON_ARRAY: {
size_t n;
size_t i;
mrb_value a;
mrb_value key;
mrb_value val;
n = json->_value._objects._length;
a = mrb_hash_new_capa(mrb, n);
for (i = 0; i < n; i += 2) {
TRI_json_t* sub = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i);
if (! TRI_IsStringJson(sub)) {
continue;
}
key = mrb_str_new(mrb, sub->_value._string.data, sub->_value._string.length - 1);
sub = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i + 1);
val = MR_ObjectJson(mrb, sub);
mrb_hash_set(mrb, a, key, val);
}
return a;
}
case TRI_JSON_LIST: {
size_t n;
size_t i;
mrb_value a;
mrb_value val;
n = json->_value._objects._length;
a = mrb_ary_new_capa(mrb, n);
for (i = 0; i < n; ++i) {
TRI_json_t* elm = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i);
val = MR_ObjectJson(mrb, elm);
mrb_ary_set(mrb, a, i, val);
}
return a;
}
}
return mrb_nil_value();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief opens a new context
////////////////////////////////////////////////////////////////////////////////
mrb_state* MR_OpenShell () {
mrb_state* mrb = mrb_open();
mrb->ud = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(MR_state_t), true);
return mrb;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief closes context
////////////////////////////////////////////////////////////////////////////////
void MR_CloseShell (mrb_state* mrb) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, mrb->ud);
mrb_close(mrb);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a ArangoError
////////////////////////////////////////////////////////////////////////////////
mrb_value MR_ArangoError (mrb_state* mrb, int errNum, char const* errMessage) {
MR_state_t* mrs;
mrb_value exc;
mrb_value val;
mrb_sym id;
mrs = (MR_state_t*) mrb->ud;
exc = mrb_exc_new(mrb, mrs->_arangoError, errMessage, strlen(errMessage));
id = mrb_intern(mrb, "@error_num");
val = mrb_fixnum_value(errNum);
mrb_iv_set(mrb, exc, id, val);
id = mrb_intern(mrb, "@error_message");
val = mrb_str_new(mrb, errMessage, strlen(errMessage));
mrb_iv_set(mrb, exc, id, val);
return exc;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief prints an exception and stacktrace
////////////////////////////////////////////////////////////////////////////////
void TRI_LogRubyException (mrb_state* mrb, struct RObject* exc) {
LOG_ERROR("cannot log ruby exception\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes a file in the current context
////////////////////////////////////////////////////////////////////////////////
bool TRI_ExecuteRubyFile (mrb_state* mrb, char const* filename) {
bool ok;
char* content;
mrb_value result;
content = TRI_SlurpFile(TRI_UNKNOWN_MEM_ZONE, filename, NULL);
if (content == 0) {
LOG_TRACE("cannot loaded ruby file '%s': %s", filename, TRI_last_error());
return false;
}
ok = TRI_ExecuteRubyString(mrb, content, filename, false, &result);
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content);
return ok;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes all files from a directory in the current context
////////////////////////////////////////////////////////////////////////////////
bool TRI_ExecuteRubyDirectory (mrb_state* mrb, char const* path) {
TRI_vector_string_t files;
bool result;
regex_t re;
size_t i;
LOG_TRACE("loading ruby script directory: '%s'", path);
files = TRI_FilesDirectory(path);
regcomp(&re, "^(.*)\\.rb$", REG_ICASE | REG_EXTENDED);
result = true;
for (i = 0; i < files._length; ++i) {
bool ok;
char const* filename;
char* full;
filename = files._buffer[i];
if (regexec(&re, filename, 0, 0, 0) != 0) {
continue;
}
full = TRI_Concatenate2File(path, filename);
ok = TRI_ExecuteRubyFile(mrb, full);
TRI_FreeString(TRI_CORE_MEM_ZONE, full);
result = result && ok;
if (! ok) {
TRI_LogRubyException(mrb, mrb->exc);
}
}
TRI_DestroyVectorString(&files);
regfree(&re);
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes a string within a mruby context, optionally print the result
////////////////////////////////////////////////////////////////////////////////
bool TRI_ExecuteRubyString (mrb_state* mrb,
char const* script,
char const* name,
bool printResult,
mrb_value* result) {
struct mrb_parser_state* parser;
mrb_value r;
int n;
parser = mrb_parse_nstring(mrb, script, strlen(script), NULL);
if (parser == 0 || parser->tree == 0 || 0 < parser->nerr) {
LOG_DEBUG("failed to parse ruby script");
if (parser != 0 && parser->pool != 0) {
mrb_pool_close(parser->pool);
}
return false;
}
n = mrb_generate_code(mrb, parser);
mrb_pool_close(parser->pool);
if (n < 0) {
LOG_DEBUG("failed to generate ruby code: %d", n);
return false;
}
r = mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_top_self(mrb));
if (mrb->exc) {
if (printResult) {
mrb_p(mrb, mrb_obj_value(mrb->exc));
}
mrb->exc = 0;
}
else if (printResult && ! mrb_nil_p(r)) {
mrb_p(mrb, r);
}
if (result != NULL) {
*result = r;
}
return true;
}
// -----------------------------------------------------------------------------
// --SECTION-- module functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief init mruby utilities
////////////////////////////////////////////////////////////////////////////////
void TRI_InitMRUtils (mrb_state* mrb) {
MR_state_t* mrs;
struct RClass *rcl;
mrs = (MR_state_t*) mrb->ud;
rcl = mrb->kernel_module;
// .............................................................................
// timing function
// .............................................................................
mrb_define_method(mrb, rcl, "time", MR_Time, ARGS_NONE());
// .............................................................................
// arango exception
// .............................................................................
mrs->_arangoError = mrb_define_class(mrb, "ArangoError", mrb->eStandardError_class);
// .............................................................................
// json parser and generator
// .............................................................................
rcl = mrb_define_class(mrb, "ArangoJson", mrb->object_class);
mrb_define_class_method(mrb, rcl, "parse", MR_JsonParse, ARGS_REQ(1));
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: