-
Notifications
You must be signed in to change notification settings - Fork 8
Refactor native extension #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,80 +2,124 @@ | |
extern crate ruru; | ||
extern crate inflector; | ||
|
||
// // dash: kebab-case | ||
use inflector::cases::kebabcase::to_kebab_case; | ||
// // underscore: snake_case | ||
use inflector::cases::snakecase::to_snake_case; | ||
// // camel_lower: camelCase | ||
use inflector::cases::camelcase::to_camel_case; | ||
// // camel: ClassCase (PascalCase) | ||
use inflector::cases::classcase::to_class_case; | ||
|
||
use ruru::{Class, Object, RString, Hash, Array, Symbol, AnyObject, VM}; | ||
use ruru::types::ValueType; | ||
use inflector::cases::{camelcase, classcase, kebabcase, snakecase}; | ||
|
||
class!(CaseTransform); | ||
use ruru::{Class, Object, VerifiedObject, RString, Hash, Array, Symbol, AnyObject}; | ||
use ruru::types::ValueType; | ||
use ruru::result::Error as RuruError; | ||
|
||
methods! ( | ||
CaseTransform, | ||
itself, | ||
trait Transform: Object { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Then we make type-specific implementations for
by implementing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to read up on traits and impls for myself:
|
||
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject; | ||
} | ||
|
||
fn deepTransformKeys(hash: Hash, block: &Fn(String) -> String) -> Hash { | ||
let result = Hash::new(); | ||
impl Transform for AnyObject { | ||
fn transform(&self, _transform_function: &Fn(String) -> String) -> AnyObject { | ||
self.clone() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why was this removed? I saw it one one of the example repos, though I don't know what its purpose was There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The line was moved to the bottom of the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
} | ||
} | ||
|
||
hash.unwrap().each(|key, value| { | ||
let newValue = if value.ty() == ValueType::Hash { deepTransformKeys(value, block).to_any_object() } else { value }; | ||
let newKey = RString::new(block(key.unwrap().to_string())); | ||
result.store(newKey, newValue); | ||
}); | ||
impl Transform for RString { | ||
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject { | ||
let result = transform_function(self.to_string(); | ||
|
||
result | ||
RString::new(&result)).to_any_object() | ||
} | ||
} | ||
|
||
impl Transform for Symbol { | ||
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject { | ||
let result = transform_function(self.to_string()); | ||
|
||
fn transformArray(value: Array, transformMethod: &Fn(AnyObject) -> AnyObject) -> Array { | ||
value.map(|item| transformMethod(item)).unwrap() | ||
Symbol::new(&result).to_any_object() | ||
} | ||
} | ||
|
||
impl Transform for Hash { | ||
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject { | ||
let mut result = Hash::new(); | ||
|
||
self.each(|key, value| { | ||
let new_key = transform(key, transform_function); | ||
let new_value = match value.ty() { | ||
ValueType::Hash => transform(value, transform_function), | ||
_ => value, | ||
}; | ||
|
||
fn transformHash(value: Hash, transformMethod: &Fn(AnyObject) -> AnyObject) -> Hash { | ||
deepTransformKeys(value, |key| transformMethod(key)) | ||
result.store(new_key, new_value); | ||
}); | ||
|
||
result.to_any_object() | ||
} | ||
} | ||
|
||
impl Transform for Array { | ||
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject { | ||
// Temp hack to consume &self for iterator | ||
let result = unsafe { self.to_any_object().to::<Array>() }; | ||
|
||
fn transformSymbol(value: Symbol, transformMethod: &Fn(AnyObject) -> AnyObject) -> Symbol { | ||
let transformed = transformMethod(value); | ||
Symbol::new(transformed); | ||
result.into_iter() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. arrays are iterable by default? edit: not* There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They kinda are iterable, but after explicit conversion to iterators :) Same for slices, vectors etc |
||
.map(|item| transform(item, transform_function)) | ||
.collect::<Array>() | ||
.to_any_object() | ||
} | ||
} | ||
|
||
fn transform( | ||
value: AnyObject, | ||
objectTransform: &Fn(AnyObject) -> AnyObject, | ||
keyTransform: &Fn(String) -> String | ||
) -> AnyObject { | ||
match value.unwrap().ty() { | ||
ValueType::Array => transformArray(value, objectTransform).to_any_object(), | ||
ValueType::Hash => transformHash(value, objectTransform).to_any_object(), | ||
ValueType::Symbol => transformSymbol(value, objectTransform).to_any_object(), | ||
ValueType::RString => keyTransform(value).to_any_object(), | ||
ValueType::Object => value | ||
} | ||
trait TryTransform: Object { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
fn try_transform<T>(&self, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
An example of usage of object.try_transform::<Hash>(key_transform) // if the object is a hash, try to do a hash transformation The interesting part here is the constraints for |
||
transform_function: &Fn(String) -> String) | ||
-> Result<AnyObject, RuruError> | ||
where T: VerifiedObject + Transform | ||
{ | ||
self.try_convert_to::<T>().map(|object| object.transform(transform_function)) | ||
} | ||
} | ||
|
||
impl TryTransform for AnyObject {} | ||
|
||
fn transform(object: AnyObject, key_transform: &Fn(String) -> String) -> AnyObject { | ||
let result = object.try_transform::<RString>(key_transform) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try to transform the object as a |
||
.or_else(|_| object.try_transform::<Symbol>(key_transform)) | ||
.or_else(|_| object.try_transform::<Array>(key_transform)) | ||
.or_else(|_| object.try_transform::<Hash>(key_transform)) | ||
.or_else(|_| object.try_transform::<AnyObject>(key_transform)); | ||
|
||
result.unwrap() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can safely unwrap here, because |
||
} | ||
|
||
fn to_pascal_case(key: String) -> String { | ||
classcase::to_class_case(snakecase::to_snake_case(key)) | ||
} | ||
|
||
fn to_camel_case(key: String) -> String { | ||
camelcase::to_camel_case(snakecase::to_snake_case(key)) | ||
} | ||
|
||
fn to_dashed_case(key: String) -> String { | ||
kebabcase::to_kebab_case(snakecase::to_snake_case(key)) | ||
} | ||
|
||
fn to_snake_case(key: String) -> String { | ||
snakecase::to_snake_case(key) | ||
} | ||
|
||
fn toPascalCase(key: String) -> String { to_class_case(to_snake_case(key.unwrap())) } | ||
fn toCamelCase(key: String) -> String { to_camel_case(to_snake_case(key.unwrap())) } | ||
fn toDashedCase(key: String) -> String { to_kebab_case(to_snake_case(key.unwrap())) } | ||
fn toSnakeCase(key: String) -> String { to_snake_case(key.unwrap()) } | ||
class!(CaseTransform); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here it is! |
||
|
||
methods! ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All the functions which are not used directly in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this makes sense! |
||
CaseTransform, | ||
_itself, | ||
|
||
fn camel(value: AnyObject) -> AnyObject { transform(value.unwrap().to_any_object(), &camel, &toPascalCase) } | ||
fn camelLower(value: AnyObject) -> AnyObject { transform(value.unwrap().to_any_object(), &camelLower, &toCamelCase) } | ||
fn dash(value: AnyObject) -> AnyObject { transform(value.unwrap().to_any_object(), &dash, &toDashedCase) } | ||
fn underscore(value: AnyObject) -> AnyObject { transform(value.unwrap(), &underscore, &toSnakeCase) } | ||
fn unaltered(value: AnyObject) -> AnyObject { value.unwrap().to_any_object() } | ||
fn camel(object: AnyObject) -> AnyObject { transform(value.unwrap(), &to_pascal_case) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And we can safely unwrap in this methods, because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. legit |
||
fn camel_lower(object: AnyObject) -> AnyObject { transform(object.unwrap(), &to_camel_case) } | ||
fn dash(object: AnyObject) -> AnyObject { transform(object.unwrap(), &to_dashed_case) } | ||
fn underscore(object: AnyObject) -> AnyObject { transform(object.unwrap(), &to_snake_case) } | ||
fn unaltered(object: AnyObject) -> AnyObject { object.unwrap() } | ||
); | ||
|
||
#[no_mangle] | ||
pub extern fn initialize_case_transform() { | ||
Class::new("CaseTransform", None).define(|itself| { | ||
pub extern "C" fn initialize_case_transform() { | ||
Class::from_existing("CaseTransform").define(|itself| { | ||
itself.def_self("camel", camel); | ||
itself.def_self("camel_lower", camelLower); | ||
itself.def_self("camel_lower", camel_lower); | ||
itself.def_self("dash", dash); | ||
itself.def_self("underscore", underscore); | ||
itself.def_self("unaltered", unaltered); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is handy!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a way to import the methods into the 'global' namespace of the file? so you don't have to do
classcase::to_class_case
?