mirror of https://gitee.com/bigwinds/arangodb
390 lines
11 KiB
C++
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:
|