Skip to content

Commit

Permalink
bunch of fixes in handling ruby exceptions and calling modules in YCP…
Browse files Browse the repository at this point in the history
… namespace
  • Loading branch information
jreidinger committed Jan 23, 2013
1 parent 5e6aae3 commit 2ed5146
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 78 deletions.
15 changes: 14 additions & 1 deletion src/binary/Y2RubyUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

using namespace std;

static VALUE const_get_wrapper(VALUE input)
{
VALUE *data = (VALUE*) input;
return rb_funcall( data[0], rb_intern("const_get"), 1, data[1]);
}

VALUE y2ruby_nested_const_get(const std::string &name)
{
VALUE module = rb_mKernel;
Expand All @@ -14,7 +20,14 @@ VALUE y2ruby_nested_const_get(const std::string &name)
stringutil::split( name, name_levels, "::", false);

for ( unsigned i = 0; i < name_levels.size(); ++i ) {
module = rb_funcall( module, rb_intern("const_get"), 1, rb_str_new2(name_levels[i].c_str()) );
int error = 0;
// tricky part as rb_protect takes only one param, so get to it more of them
VALUE data[2];
data[0] = module;
data[1] = rb_str_new2(name_levels[i].c_str());
module = rb_protect(const_get_wrapper, (VALUE)data, &error );
if ( error )
return Qnil;
}
return module;
}
10 changes: 8 additions & 2 deletions src/binary/YRuby.cc
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,14 @@ YRuby::callInner (string module_name, string function, bool method,
VALUE module = y2ruby_nested_const_get(module_name);
if (module == Qnil)
{
y2error ("The Ruby module '%s' is not provided by its rb file", module_name.c_str());
return YCPVoid();
y2milestone ("The Ruby module '%s' is not provided by its rb file. Try YCP prefix.", module_name.c_str());
string alternative_name = string("YCP::")+module_name;
module = y2ruby_nested_const_get(alternative_name);
if (module == Qnil)
{
y2error ("The Ruby module '%s' is not provided by its rb file", alternative_name.c_str());
return YCPVoid();
}
}

// first element of the list is ignored
Expand Down
132 changes: 85 additions & 47 deletions src/binary/YRubyNamespace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,64 +174,102 @@ YRubyNamespace::YRubyNamespace (string name)
VALUE module = y2ruby_nested_const_get(name);
if (module == Qnil)
{
y2error ("The Ruby module '%s' is not provided by its rb file", name.c_str());
return;
y2milestone ("The Ruby module '%s' is not provided by its rb file. Try it with YCP prefix.", name.c_str());
//modules can live in YCP namespace, it is OK
string alternative_name = string("YCP::")+name;
module = y2ruby_nested_const_get(alternative_name);
if (module == Qnil)
{
y2error ("The Ruby module '%s' is not provided by its rb file", alternative_name.c_str());
return;
}
}
y2milestone("The module '%s' was found", name.c_str());

// we will perform operator- to determine the module methods
VALUE moduleklassmethods = rb_funcall( rb_cModule, rb_intern("methods"), 0);
VALUE mymodulemethods = rb_funcall( module, rb_intern("methods"), 0);
VALUE methods = rb_funcall( mymodulemethods, rb_intern("-"), 1, moduleklassmethods );

if (methods == Qnil)
long i = 0; //trace number of added method, so we can add extra one at the end
//detect if module use new approach for exporting methods or old one
if (rb_respond_to(module, rb_intern("published_methods" )))
{
y2error ("Can't see methods in module '%s'", name.c_str());
return;
}
VALUE methods = rb_funcall(module, rb_intern("published_methods"),0);
methods = rb_funcall(methods,rb_intern("values"),0);
for (i = 0; i < RARRAY_LEN(methods); ++i)
{
VALUE method = rb_ary_entry(methods,i);
VALUE method_name = rb_funcall(method, rb_intern("method_name"), 0);
VALUE type = rb_funcall(method,rb_intern("type"),0);
string signature = StringValueCStr(type);
constTypePtr sym_tp = Type::fromSignature(signature);

constFunctionTypePtr fun_tp = (constFunctionTypePtr) sym_tp;

// symbol entry for the function
SymbolEntry *fun_se = new SymbolEntry ( this,
i,// position. arbitrary numbering. must stay consistent when?
RSTRING_PTR(method_name), // passed to Ustring, no need to strdup
SymbolEntry::c_function,
sym_tp);
fun_se->setGlobal (true);
// enter it to the symbol table
enterSymbol (fun_se, 0);
y2milestone("method: '%s' added", RSTRING_PTR(method_name));

int i;
for(i = 0; i < RARRAY_LEN(methods); i++)
{
VALUE current = rb_funcall( methods, rb_intern("at"), 1, rb_fix_new(i) );
if (rb_type(current) == RUBY_T_SYMBOL) {
current = rb_funcall( current, rb_intern("to_s"), 0);
}
y2milestone("New method: '%s'", RSTRING_PTR(current));
}
else
{
// we will perform operator- to determine the module methods
VALUE moduleklassmethods = rb_funcall( rb_cModule, rb_intern("methods"), 0);
VALUE mymodulemethods = rb_funcall( module, rb_intern("methods"), 0);
VALUE methods = rb_funcall( mymodulemethods, rb_intern("-"), 1, moduleklassmethods );

// figure out arity.
Check_Type(module,T_MODULE);
VALUE methodobj = rb_funcall( module, rb_intern("method"), 1, current );
if ( methodobj == Qnil )
if (methods == Qnil)
{
y2error ("Cannot access method object '%s'", RSTRING_PTR(current));
continue;
y2error ("Can't see methods in module '%s'", name.c_str());
return;
}
string signature = "any( ";
VALUE rbarity = rb_funcall( methodobj, rb_intern("arity"), 0);
int arity = NUM2INT(rbarity);
for ( int k=0; k < arity; ++k )

for(i = 0; i < RARRAY_LEN(methods); i++)
{
signature += "any";
if ( k < (arity - 1) )
signature += ",";
VALUE current = rb_funcall( methods, rb_intern("at"), 1, rb_fix_new(i) );
if (rb_type(current) == RUBY_T_SYMBOL) {
current = rb_funcall( current, rb_intern("to_s"), 0);
}
y2milestone("New method: '%s'", RSTRING_PTR(current));

// figure out arity.
Check_Type(module,T_MODULE);
VALUE methodobj = rb_funcall( module, rb_intern("method"), 1, current );
if ( methodobj == Qnil )
{
y2error ("Cannot access method object '%s'", RSTRING_PTR(current));
continue;
}
string signature = "any( ";
VALUE rbarity = rb_funcall( methodobj, rb_intern("arity"), 0);
int arity = NUM2INT(rbarity);
for ( int k=0; k < arity; ++k )
{
signature += "any";
if ( k < (arity - 1) )
signature += ",";
}
signature += ")";
y2internal("going to parse signature: '%s'", signature.c_str());
constTypePtr sym_tp = Type::fromSignature(signature);

constFunctionTypePtr fun_tp = (constFunctionTypePtr) sym_tp;

// symbol entry for the function
SymbolEntry *fun_se = new SymbolEntry ( this,
i,// position. arbitrary numbering. must stay consistent when?
RSTRING_PTR(current), // passed to Ustring, no need to strdup
SymbolEntry::c_function,
sym_tp);
fun_se->setGlobal (true);
// enter it to the symbol table
enterSymbol (fun_se, 0);
y2milestone("method: '%s' added", RSTRING_PTR(current));
}
signature += ")";
y2internal("going to parse signature: '%s'", signature.c_str());
constTypePtr sym_tp = Type::fromSignature(signature);

constFunctionTypePtr fun_tp = (constFunctionTypePtr) sym_tp;

// symbol entry for the function
SymbolEntry *fun_se = new SymbolEntry ( this,
i,// position. arbitrary numbering. must stay consistent when?
RSTRING_PTR(current), // passed to Ustring, no need to strdup
SymbolEntry::c_function,
sym_tp);
fun_se->setGlobal (true);
// enter it to the symbol table
enterSymbol (fun_se, 0);
y2milestone("method: '%s' added", RSTRING_PTR(current));
}
//add to all modules method last_exception to get last exception raised inside module
constTypePtr sym_tp = Type::fromSignature("any()");
Expand Down
3 changes: 3 additions & 0 deletions src/ruby/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
add_definitions(-DY2LOG=\"Ruby\")

FILE(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/*/*.rb")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/ycp.rb DESTINATION ${RUBY_VENDORLIB_DIR} )
INSTALL(FILES ${files} DESTINATION ${RUBY_VENDORLIB_DIR}/ycp)

3 changes: 2 additions & 1 deletion src/ruby/ycp/exportable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def published_variables
end

module ClassMethods
class ExportData < OpenStruct; end
class ExportData < OpenStruct;end

def published_methods
@__published_methods ||= {}
Expand All @@ -25,6 +25,7 @@ def published_variables
def publish options
raise "Missing signature" unless options[:type]
if options[:method]
options[:method_name] = options[:method].to_s #tricky part to not be overcloaked by internal method
published_methods[options[:method]] = ExportData.new options
elsif options[:variable]
published_variables[options[:variable]] = ExportData.new options
Expand Down
1 change: 1 addition & 0 deletions tests/ruby/exportable_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def self.test(a,b)
class ExportableTest < YCP::TestCase
def test_publish_methods
assert_equal [:test], Test.published_methods.keys
assert_equal :test, Test.published_methods.values.first.method
assert_equal "string(integer,term)", Test.published_methods[:test].type
end

Expand Down
4 changes: 4 additions & 0 deletions tests/ycp/UI.ycp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
UI::OpenDialog( `opt( `defaultsize ), `Label( "Hello, World!" ) );
UI::UserInput();
}
20 changes: 20 additions & 0 deletions tests/ycp/common_module.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require "ycp"

module YCP
module CommonModule
include Exportable

publish :method => :method_a, :type => "string(integer,integer)"
def self.method_a first, second
(first+second).to_s
end

publish :variable => :name, :type => "string"
self.name = "Cool name"

publish :method => :formated_name, :type => "string()"
def self.formated_name
return exhibit+" Fancy Formated!!!"
end
end
end
31 changes: 4 additions & 27 deletions tests/ycp/ruby_from_ycp.ycp
Original file line number Diff line number Diff line change
@@ -1,30 +1,7 @@
//
{
import "InRuby";
string result = tostring(InRuby::multiply_by_eight(10));
y2milestone("result: %1", result);
result = tostring(InRuby::raising_code());
if (result==nil)
{
result = tostring(InRuby::last_exception());
y2milestone("result: %1", result);
if(result != "Wow exception!")
return false;
}
else
{
y2error("exception completely ignored result: %1",result);
return false;
}
map h = tomap(InRuby::get_hash());
y2milestone("get hash: %1",h);
if ( tostring(h["a"]:"") != "b" || tostring(h["b"]:"") != "c" )
{
y2error("map is not properly returned: %1",result);
return false;
}
import "CamelCase";
result = tostring(CamelCase::inc(10));
y2milestone("result: %1", result);
return true;
import "CommonModule";

string res = CommonModule::method_a(5,6);
y2milestone ("Should be 11 => %1", res);
}
1 change: 1 addition & 0 deletions tests/ycp/run_ycp_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ fi
# DEBUG=valgrind
# DEBUG="strace -s1000 -o log -e trace=file"
: ${PREFIX=/usr}
echo $DEBUG $PREFIX/lib/YaST2/bin/y2base -l - -M $DIR $1 UI
$DEBUG $PREFIX/lib/YaST2/bin/y2base -l - -M $DIR $1 UI

0 comments on commit 2ed5146

Please sign in to comment.