iMRuby 0.1.3

iMRuby 0.1.3

ping.cao 维护。



iMRuby 0.1.3

  • ping.cao

log

iMRuby

iMRuby是ObjC(Objective-C)与Ruby(Mruby)之间的桥梁

示例

demo

// objective-c file
@interface MRBViewController ()

@property (nonatomic, strong) MRBContext *context;

@end

@implementation MRBViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // 执行view.rb
    NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"view" ofType:@"rb"];
    NSString *script = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
    self.context = [[MRBContext alloc] init];
    self.context.exceptionHandler = ^(NSError * _Nonnull exception) {
        NSLog(@"%@", exception.userInfo[@"msg"]);
    };
    
    [self.context registerConst:@"Target" value:self];
    [self.context evaluateScript:script];
    MRBValue *superView = [MRBValue valueWithObject:self.view inContext:self.context];
    [self.context callFunc:@"create_view" args:@[superView]];
    
}

- (void)touchAction:(id)sender
{
    [self.context evaluateScript:@"showAlertView"];
}

@end
# ruby file
require_cocoa "UIColor"
require_cocoa "UIView"
require_cocoa "UIImage"
require_cocoa "UIImageView"
require_cocoa "UILabel"
require_cocoa "UIFont"
require_cocoa "UIButton"
require_cocoa "UIAlertController"
require_cocoa "UIAlertAction"

def create_view(super_view)
    # Clolors
    red = UIColor.redColor
    blue = UIColor.blueColor
    green = UIColor.greenColor
    yellow = UIColor.yellowColor
    white = UIColor.whiteColor
    
    super_view.setBackgroundColor_(white)
    
    # UIlabel
    super_view.addSubview_(createLabel("UILabel", 40, 64))
    
    label = UILabel.alloc.init
    label.setFrame_({'x' => 200, 'y' => 64, 'width' => 150, 'height' => 50})
    label.setText_("Hello World!")
    label.setFont_(UIFont.systemFontOfSize_(24))
    label.setTextColor_(red)
    label.setBackgroundColor_(green)
    super_view.addSubview_(label)
    
    # UIView
    super_view.addSubview_(createLabel("UIView", 40, 120))
    
    blue_sub_view = UIView.alloc.init
    blue_sub_view.setFrame_({'x' => 200, 'y' => 120, 'width' => 100, 'height' => 100})
    blue_sub_view.setBackgroundColor_(blue)
    super_view.addSubview_(blue_sub_view)
    
    # UIImageView
    super_view.addSubview_(createLabel("UIImageView", 40, 230))
    
    image = UIImage.imageNamed_("logo")
    image_view = UIImageView.alloc.initWithImage_(image)
    image_view.setFrame_({'x'=>200, 'y'=> 230, 'width' => 100, 'height' => 100})
    super_view.addSubview_(image_view)
    
    # UIButton
    super_view.addSubview_(createLabel("UIButton", 40, 340))

    btn = UIButton.buttonWithType_(0)
    ## MRBCocoa::Const::Target is defined in OC
    ## [self.context registerConst:@"Target" value:self];
    ## “self” is the view controller
    ## "touchAction:" action is defined in OC and execute ruby script to show alert
    btn.addTarget_action_forControlEvents_(MRBCocoa::Const::Target, "touchAction:", 1<<6)
    btn.setTitle_forState_("Click to show alert", 0)
    btn.setFrame_({'x' => 200, 'y' => 340, 'width' => 170, 'height' => 50})
    btn.setBackgroundColor_(red)
    super_view.addSubview_(btn)
end

def showAlertView
    # UIAlertController
    completion = Proc.new {puts "show..."}
    completion_block = completion.to_cocoa_block("void,void")
    alertController = UIAlertController.alertControllerWithTitle_message_preferredStyle_("iMRuby", "Hello World!", 1)
    
    ok_handler = Proc.new {|action| puts "OK..."}
    ok_handler_block = ok_handler.to_cocoa_block("void, UIAlertAction")
    okAction = UIAlertAction.actionWithTitle_style_handler_("OK", 0, ok_handler_block)
    
    alertController.addAction_(okAction)
    
    MRBCocoa::Const::Target.presentViewController_animated_completion_(alertController, true, completion_block)
end

def createLabel(text, x, y)
    label = UILabel.alloc.init
    label.setFrame_({'x' => x, 'y' => y, 'width' => 150, 'height' => 50})
    label.setText_(text)
    label.setFont_(UIFont.systemFontOfSize_(24))
    label.setTextColor_(UIColor.blackColor)
    return label
end

安装

iMRuby通过CocoaPods提供。要安装它,只需将以下行添加到Podfile中

pod 'iMRuby'

类UML

class_uml

使用

iMRuby的使用类似于iOS中的JavascriptCore。文章链接

Ruby和OC互相转换

OC类型 Ruby类型
nil nil
NSString 字符串,符号
NSNumber 整数字面量、浮点数、布尔值
NSDictionary 哈希
NSArray 数组
NSDate 时间
NSBlock 包装对象 Ruby 类 MRBCocoa::Block
id/NSObject 包装对象 Ruby 类 MRBCocoa::Object
包装对象 Ruby 类 MRBCocoa::Klass
CGPoint 特定的哈希 {'x' => 1, 'y' => 1}
CGSize 特定的哈希 {'宽度' => 1, '高度' => 1}
CGRect 特定的哈希 {'x' => 1, 'y' => 1, 'width' => 1, 'height' => 1}
NSRange 特定的哈希 {'位置' => 0, '长度' => 1}

创建 MRBContext 实例

self.context = [[MRBContext alloc] init];

运行 Ruby 脚本

[self.context evaluateScript:@"puts \"Happy Niu year!\""];
# => Happy Niu year!

捕获异常

self.context.exceptionHandler = ^(NSError * _Nonnull exception) {
        NSLog(@"%@", exception.userInfo[@"msg"]);
    };
[self.context evaluateScript:@"happy_nui_year(2021)"];
# => undefined method 'happy_niu_year' (NOMethodError)

注册常量

[self.context registerConst:@"Niu" value:@"Happy Niu year!"];
[self.context evaluateScript:@"puts MRBCocoa::Const::Niu"];
# => Happy Niu year!

注册方法

[self.context registerFunc:@"happy_niu_year" block:^NSInteger(int a, int b){
    NSLog(@"start...");
    int sum = a + b;
    NSLog(@"finish...");
    return sum;
 }];
 MRBValue *sum = [self.context evaluateScript:@"MRBCocoa.happy_niu_year(2020,1)"];
 NSInteger niu_year = sum.toInt64;
 NSLog(@"%@", @(niu_year));
# => start...
# => finish...
# => 2021

通过下标注册方法或常量

self.context[@"Niu"] = @"Happy Niu year!";
self.context[@"happy_niu_year"] = ^NSInteger(int a, int b) {
     NSLog(@"start...");
     int sum = a + b;
     NSLog(@"finish...");
    return sum;
};
MRBValue *sum = [self.context evaluateScript:@"puts MRBCocoa::Const::Niu;MRBCocoa.happy_niu_year(2020,1)"];
NSInteger niu_year = sum.toInt64;
NSLog(@"%@", @(niu_year));

调用OC Ruby方法

# ruby.rb
def test_func(str)
    puts str
end
    MRBValue *strParam = [MRBValue valueWithString:@"hello" inContext:self.context];
    [self.context callFunc:@"test_func" args:@[strParam]];

调用Ruby OC对象方法

// OC .m file
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
- (NSString *)say_something:(NSString *)message;
- (void)coding:(NSString *)code finished:(BOOL(^)(NSString *name, int age))finished;
@end
@implementation Person
- (NSString *)say_something:(NSString *)message
{
    NSLog(@"%@", message);
    return [self.name stringByAppendingString:message];
}
- (void)coding:(NSString *)code finished:(BOOL(^)(NSString *name, int age))finished
{
    NSLog(@"I am coding %@", code);
    if (finished) {
        BOOL f = finished(self.name, self.age);
        NSLog(@"Block return value: %@", @(f));
    }
}
@end
# ruby .rb file
require_cocoa 'Person'

person = Person.alloc.init
person.setName_('anan')
person.setAge_(2)
message = person.say__something_("happy Niu year!")
puts message

finished = Proc.new {|name, age| puts "I am #{name}, #{age} year old"; true}
finished_block = finished.to_cocoa_block("BOOL, NSString *, int");
person.coding_finished_("Ruby", finished_block)

调用OC方法规则

  1. 将':'替换为'_'
- (NSString *)saySomething:(NSString *)message;
message = person.saySomething_("happy Niu year!")
  1. 将'_'替换为'__'
- (NSString *)say_something:(NSString *)message;
message = person.say__something_("happy Niu year!")
  1. 如果'_'或'__'在OC方法名称的开头,不做任何操作
- (NSString *)__say_something:(NSString *)message;
message = person.__say__something_("happy Niu year!")
  1. 如果OC方法有NSBlock参数,可以使用to_cocoa_blockProc转换为NSBlock
- (void)coding:(NSString *)code finished:(BOOL(^)(NSString *name, int age))finished;

// ruby
finished = Proc.new {|name, age| puts "I am #{name}, #{age} year old"; true}
finished_block = finished.to_cocoa_block("BOOL, NSString *, int");
person.coding_finished_("Ruby", finished_block)

注意

如果你在Apple硅芯片(M1)的模拟器上运行示例,您需要在“构建设置”->“架构”->“排除架构”中添加arm64给任何iOS模拟器SDK尝试添加arm64到构建设置 -> 架构 -> 排除架构,以适用于任何iOS模拟器SDK。 error

作者

ping.cao

许可协议

iMRuby可在MIT许可证下使用。有关更多信息,请参阅LICENSE文件。