allow private constructor. also add docopt example

You can do this by setting:

```
def initialize(helix, ...) {
}
```

Right now this only works on the constructor and only unexported is
supported.
This commit is contained in:
Terence Lee 2018-06-01 16:11:53 +09:00 committed by Godfrey Chan
parent 7dbc5cf3e9
commit 088a636192
18 changed files with 299 additions and 5 deletions

View File

@ -20,8 +20,7 @@ travis-ci = { repository = "tildeio/helix", branch = "master" }
appveyor = { repository = "tildeio/helix", branch = "master", service = "github" }
[workspace]
members = ["examples/calculator", "examples/console", "examples/game_of_life", "examples/geometry", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank", "examples/json_builder"]
members = ["examples/calculator", "examples/console", "examples/docopt", "examples/game_of_life", "examples/geometry", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank", "examples/json_builder"]
[dependencies]
libc = "0.2.0"

View File

@ -4,7 +4,7 @@ task :test do
sh "bundle exec rake"
end
examples = ENV["EXAMPLES"] || "duration calculator console game_of_life geometry membership text_transform turbo_blank json_builder"
examples = ENV["EXAMPLES"] || "duration calculator console docopt game_of_life geometry membership text_transform turbo_blank json_builder"
sh "bash ./examples/runner default #{examples}"
end
@ -15,7 +15,7 @@ task :install do
sh "bundle"
end
examples = ENV["EXAMPLES"] || "duration calculator console game_of_life geometry membership text_transform turbo_blank json_builder"
examples = ENV["EXAMPLES"] || "duration calculator console docopt game_of_life geometry membership text_transform turbo_blank json_builder"
sh "bash ./examples/runner install #{examples}"
end

View File

@ -167,6 +167,9 @@ extern "C" {
#[link_name = "HELIX_OBJ_FROZEN"]
pub fn OBJ_FROZEN(v: VALUE) -> bool;
#[link_name = "HELIX_CLASS_OF"]
pub fn CLASS_OF(v: VALUE) -> VALUE;
#[link_name = "HELIX_T_OBJECT"]
pub static T_OBJECT: isize;
@ -210,9 +213,10 @@ extern "C" {
pub fn rb_define_module_under(namespace: VALUE, name: c_string) -> VALUE;
pub fn rb_define_class(name: c_string, superclass: VALUE) -> VALUE;
pub fn rb_define_class_under(namespace: VALUE, name: c_string, superclass: VALUE) -> VALUE;
pub fn rb_define_alloc_func(klass: VALUE, func: extern "C" fn(klass: VALUE) -> VALUE);
pub fn rb_define_alloc_func(class: VALUE, func: extern "C" fn(class: VALUE) -> VALUE);
pub fn rb_define_method(class: VALUE, name: c_string, func: c_func, arity: isize);
pub fn rb_define_singleton_method(class: VALUE, name: c_string, func: c_func, arity: isize);
pub fn rb_undef_method(class: VALUE, name: c_string);
pub fn rb_enc_get_index(obj: VALUE) -> isize;
pub fn rb_utf8_encindex() -> isize;
pub fn rb_sprintf(specifier: c_string, ...) -> VALUE;

3
examples/docopt/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target
*.bundle
*.gem

View File

@ -0,0 +1,14 @@
[package]
name = "docopt"
version = "0.1.0"
authors = ["Godhuda <engineering+godhuda@tilde.io>"]
[lib]
crate-type = ["cdylib"]
[dependencies.helix]
path = "../.."
[dependencies]
docopt = "*"

6
examples/docopt/Gemfile Normal file
View File

@ -0,0 +1,6 @@
source 'https://rubygems.org'
gem 'helix_runtime', path: '../../ruby'
gem 'rake', '~> 10.0'
gem 'rspec', '~> 3.4'
gem 'colorize'

20
examples/docopt/Rakefile Executable file
View File

@ -0,0 +1,20 @@
require 'bundler/setup'
require 'helix_runtime/build_task'
require 'rspec/core/rake_task'
require_relative '../shared.rb'
# For Windows
$stdout.sync = true
HelixRuntime::BuildTask.new do |t|
t.build_root = File.expand_path("../..", __dir__)
t.helix_lib_dir = File.expand_path("../../ruby/windows_build", __dir__)
t.pre_build = HelixRuntime::Tests.pre_build
end
RSpec::Core::RakeTask.new(:spec) do |t|
t.verbose = false
end
task :spec => :build
task :default => :spec

View File

@ -0,0 +1,2 @@
require 'helix_runtime'
require 'docopt/native'

View File

@ -0,0 +1,95 @@
require "spec_helper"
USAGE = <<USAGE
Naval Fate.
Usage:
naval_fate ship new <name>...
naval_fate ship <name> move <x> <y> [--speed=<kn>] [--acc=<kns>]
naval_fate ship shoot <x> <y>
naval_fate mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate (-h | --help)
naval_fate --version
naval_fate (-o | --option)
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--acc=<kns> Speed in knots per second.
--moored Moored (anchored) mine.
--drifting Drifting mine.
-o --option Test long and short option.
USAGE
describe "Docopt" do
it "can not be constructed" do
expect { Docopt.new }.to raise_error(NoMethodError)
end
it "should parse and return bools" do
argv = "naval_fate --help".split
options = Docopt.parse(USAGE, argv)
expect(options["--help"]).to be true
end
it "should parse strings" do
x = "1"
y = "2"
argv = "naval_fate ship shoot #{x} #{y}".split
options = Docopt.parse(USAGE, argv)
expect(options["ship"]).to eq(true)
expect(options["shoot"]).to eq(true)
expect(options["<x>"]).to eq(x)
expect(options["<y>"]).to eq(y)
end
it "should set short and long options are when provided" do
argv = "naval_fate -o".split
options = Docopt.parse(USAGE, argv)
expect(options["--option"]).to eq(true)
argv = "naval_fate --option".split
options = Docopt.parse(USAGE, argv)
expect(options["--option"]).to eq(true)
end
it "should parse an array of strings" do
names = %w(enterprise mission)
argv = "naval_fate ship new #{names.join(" ")}".split
options = Docopt.parse(USAGE, argv)
expect(options["ship"]).to eq(true)
expect(options["<name>"]).to eq(names)
end
it "should use default values" do
argv = "naval_fate ship foo move 0 0".split
options = Docopt.parse(USAGE, argv)
expect(options["--speed"]).to eq("10")
end
it "should return nil for unused options" do
argv = "naval_fate ship foo move 0 0".split
options = Docopt.parse(USAGE, argv)
expect(options["--acc"]).to be_nil
end
it "should handle complex cases" do
name = "enterprise"
x = "1"
y = "2"
speed = "10"
argv = "naval_fate ship #{name} move #{x} #{y} --speed=#{speed}".split
options = Docopt.parse(USAGE, argv)
expect(options["ship"]).to eq(true)
expect(options["move"]).to eq(true)
expect(options["<name>"]).to eq([name])
expect(options["<x>"]).to eq(x)
expect(options["<y>"]).to eq(y)
expect(options["--speed"]).to eq(speed)
end
end

View File

@ -0,0 +1,5 @@
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'docopt'
RSpec.configure do |config|
end

View File

@ -0,0 +1,52 @@
#![recursion_limit="1024"]
#[macro_use]
extern crate helix;
extern crate docopt;
use docopt::ArgvMap;
use helix::{ToRuby, ToRubyResult};
ruby! {
class Docopt {
struct {
options: ArgvMap,
}
#[ruby_visibility=unexported]
def initialize(helix, options: ArgvMap) {
Docopt { helix, options }
}
def parse(usage: String, argv: Vec<String>) -> Result<Docopt, String> {
let result = docopt::Docopt::new(usage)
.and_then(|d| d.help(false).argv(argv.into_iter()).parse());
match result {
Ok(args) => Ok(Docopt::new(args)),
Err(error) => match error {
docopt::Error::WithProgramUsage(e, msg) => {
Err(format!("{}\n\n{}\n", e, msg))
},
e => {
Err(format!("{}", e))
}
}
}
}
#[ruby_name="[]"]
def get(&self, key: String) -> ToRubyResult {
match self.options.map.find(&key) {
None => ().to_ruby(),
Some(value) => match *value {
docopt::Value::Counted(uint) => uint.to_ruby(),
docopt::Value::Plain(None) => ().to_ruby(),
ref plain @ docopt::Value::Plain(Some(_)) => plain.as_str().to_ruby(),
ref switch @ docopt::Value::Switch(_) => switch.as_bool().to_ruby(),
ref list @ docopt::Value::List(_) => list.as_vec().to_ruby()
},
}
}
}
}

View File

@ -1,3 +1,5 @@
#![recursion_limit="1024"]
#[macro_use]
extern crate helix;
extern crate rand;

View File

@ -73,6 +73,10 @@ bool HELIX_rb_str_ascii_only_p(VALUE str) {
return rb_enc_str_coderange(str) == ENC_CODERANGE_7BIT;
}
VALUE HELIX_CLASS_OF(VALUE v) {
return CLASS_OF(v);
}
VALUE HELIX_Data_Wrap_Struct(VALUE klass, HELIX_RUBY_DATA_FUNC mark, HELIX_RUBY_DATA_FUNC free, void* data) {
return Data_Wrap_Struct(klass, mark, free, data);
}

View File

@ -72,6 +72,8 @@ HELIX_EXTERN VALUE HELIX_rb_utf8_str_new(const char* str, long len);
HELIX_EXTERN bool HELIX_rb_str_valid_encoding_p(VALUE str);
HELIX_EXTERN bool HELIX_rb_str_ascii_only_p(VALUE str);
HELIX_EXTERN VALUE HELIX_CLASS_OF(VALUE v);
// typedef VALUE (*HELIX_rb_alloc_func_t)(VALUE);
// void HELIX_rb_define_alloc_func(VALUE klass, HELIX_rb_alloc_func_t func);

View File

@ -70,4 +70,10 @@ impl ClassDefinition {
}
}
}
pub fn undefine_class_method(&self, name: c_string) {
unsafe {
sys::rb_undef_method(sys::CLASS_OF(self.class.0), name);
}
}
}

View File

@ -165,6 +165,7 @@ macro_rules! codegen_method {
type: initializer,
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
self: {
ownership: {},
name: $self:tt
@ -182,6 +183,7 @@ macro_rules! codegen_method {
type: class_method,
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
self: (),
args: [ $($args:tt)* ],
ret: { $($ret:tt)* },
@ -196,6 +198,7 @@ macro_rules! codegen_method {
type: instance_method,
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
self: {
ownership: { $($ownership:tt)* },
name: $self:tt

View File

@ -86,6 +86,7 @@ macro_rules! codegen_define_method {
type: class_method,
rust_name: $rust_name:tt,
ruby_name: { $($ruby_name:tt)* },
ruby_visibility: $ruby_visibility:tt,
self: (),
args: [ $($arg:tt : $argty:ty),* ],
ret: { $($ret:tt)* },
@ -141,6 +142,7 @@ macro_rules! codegen_define_method {
type: instance_method,
rust_name: $rust_name:tt,
ruby_name: { $($ruby_name:tt)* },
ruby_visibility: $ruby_visibility:tt,
self: { ownership: { $($ownership:tt)* }, name: $self:tt },
args: [ $($arg:tt : $argty:ty),* ],
ret: { $($ret:tt)* },
@ -189,6 +191,29 @@ macro_rules! codegen_define_method {
$def.define_method($crate::MethodDefinition::instance(name, method, arity))
});
($def:tt, {
type: class,
rust_name: $cls_rust_name:tt,
$($rest:tt)*
}, {
type: initializer,
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: unexported,
self: $self:tt,
args: [ $($arg:tt : $argty:ty),* ],
ret: { $($ret:tt)* },
body: $body:tt
}) => ({
impl $cls_rust_name {
pub fn new($($arg : $argty),*) -> $($ret)* {
$cls_rust_name::$rust_name(unsafe { $crate::sys::Qnil } , $($arg),*)
}
}
$def.undefine_class_method(cstr!("new"))
});
($def:tt, {
type: class,
rust_name: $cls_rust_name:tt,
@ -200,6 +225,7 @@ macro_rules! codegen_define_method {
type: initializer,
rust_name: $rust_name:tt,
ruby_name: { $($ruby_name:tt)* },
ruby_visibility: $ruby_visibility:tt,
self: { ownership: {}, name: $self:tt },
args: [ $($arg:tt : $argty:ty),* ],
ret: { $($ret:tt)* },

View File

@ -294,6 +294,7 @@ macro_rules! parse {
stack: {
rust_name: uninitialized,
ruby_name: uninitialized,
ruby_visibility: public,
$($stack)*
}
}
@ -301,6 +302,28 @@ macro_rules! parse {
// STATE: parse_method_attributes
{
state: parse_method_attributes,
buffer: { #[ruby_visibility = $ruby_visibility:tt] $($rest:tt)* },
stack: {
rust_name: uninitialized,
ruby_name: $ruby_name:tt,
ruby_visibility: public,
$($stack:tt)*
}
} => {
parse! {
state: parse_method_attributes,
buffer: { $($rest)* },
stack: {
rust_name: uninitialized,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
$($stack)*
}
}
};
{
state: parse_method_attributes,
buffer: { #[ruby_name = $ruby_name:tt] $($rest:tt)* },
@ -383,6 +406,7 @@ macro_rules! parse {
stack: {
rust_name: initialize,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class: $class:tt,
$($stack:tt)*
}
@ -394,6 +418,7 @@ macro_rules! parse {
state: parse_arguments_initialize,
buffer: { $($args)* },
stack: {
ruby_visibility: $ruby_visibility,
class_body: { $($rest)* },
class: $class,
$($stack)*
@ -407,6 +432,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
$($stack:tt)*
}
} => {
@ -416,6 +442,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
class_body: { $($rest)* },
$($stack)*
}
@ -428,6 +455,7 @@ macro_rules! parse {
state: parse_arguments_initialize,
buffer: { $helix_arg:tt, $($args:tt)+ },
stack: {
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -442,6 +470,7 @@ macro_rules! parse {
type: initializer,
rust_name: initialize,
ruby_name: { "initialize" },
ruby_visibility: $ruby_visibility,
self: {
ownership: { },
name: $helix_arg
@ -459,6 +488,7 @@ macro_rules! parse {
state: parse_arguments_initialize,
buffer: { $helix_arg:tt },
stack: {
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -473,6 +503,7 @@ macro_rules! parse {
type: initializer,
rust_name: initialize,
ruby_name: { "initialize" },
ruby_visibility: $ruby_visibility,
self: {
ownership: { },
name: $helix_arg
@ -494,6 +525,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -508,6 +540,7 @@ macro_rules! parse {
type: instance_method,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: {
ownership: { &mut },
name: $self_arg
@ -527,6 +560,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -541,6 +575,7 @@ macro_rules! parse {
type: instance_method,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: {
ownership: { &mut },
name: $self_arg
@ -560,6 +595,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -574,6 +610,7 @@ macro_rules! parse {
type: instance_method,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: {
ownership: { & },
name: $self_arg
@ -593,6 +630,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -607,6 +645,7 @@ macro_rules! parse {
type: instance_method,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: {
ownership: { & },
name: $self_arg
@ -626,6 +665,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -640,6 +680,7 @@ macro_rules! parse {
type: instance_method,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: {
ownership: { },
name: $self_arg
@ -659,6 +700,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -673,6 +715,7 @@ macro_rules! parse {
type: instance_method,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: {
ownership: { },
name: $self_arg
@ -692,6 +735,7 @@ macro_rules! parse {
stack: {
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
class_body: $class_body:tt,
$($stack:tt)*
}
@ -704,6 +748,7 @@ macro_rules! parse {
type: class_method,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: (),
args: [ $($args)* ],
ret: uninitialized,
@ -724,6 +769,7 @@ macro_rules! parse {
type: $type:tt,
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
self: $self:tt,
args: $args:tt,
ret: uninitialized,
@ -742,6 +788,7 @@ macro_rules! parse {
type: $type,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: $self,
args: $args,
ret: { $ret },
@ -760,6 +807,7 @@ macro_rules! parse {
type: initializer,
rust_name: $rust_method_name:tt,
ruby_name: $ruby_method_name:tt,
ruby_visibility: $ruby_visibility:tt,
self: $self:tt,
args: $args:tt,
ret: uninitialized,
@ -784,6 +832,7 @@ macro_rules! parse {
type: initializer,
rust_name: $rust_method_name,
ruby_name: $ruby_method_name,
ruby_visibility: $ruby_visibility,
self: $self,
args: $args,
ret: { $rust_class_name },
@ -810,6 +859,7 @@ macro_rules! parse {
type: $type:tt,
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
ruby_visibility: $ruby_visibility:tt,
self: $self:tt,
args: $args:tt,
ret: uninitialized,
@ -826,6 +876,7 @@ macro_rules! parse {
type: $type,
rust_name: $rust_name,
ruby_name: $ruby_name,
ruby_visibility: $ruby_visibility,
self: $self,
args: $args,
ret: { () },