Show HN: NSString + Ruby
github.com
github.com
For example, I ended up putting together some defines like this after building a bunch of UI programmatically.
#define RGB(r, g, b) [NSColor colorWithDeviceRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
#define RGBA(r, g, b, a) [NSColor colorWithDeviceRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:(a)]
RGB(255, 104, 0) may be a bit too C-ish, but it sure beats the long form. Have similar GRAY(x) and GRAYA(x) defines for producing grayscale colors.Definitely interested to see more projects along these lines.
#define RGB(r,g,b) RGBA(r,g,b,1) #define RGB(color) \
(((color & 0xFF0000) >> 16) / 255.f), \
(((color & 0x00FF00) >> 8) / 255.f), \
(((color & 0x0000FF)) / 255.f)
Pass in an HTML-style color hex triplet.SO handy.
In detail: http://clang.llvm.org/docs/ObjectiveCLiterals.html
Suggestions: you might want to allow for three-digit hex strings and eight/four digit hex strings with alpha, I found that convenient in mine. And in the documentation it would be way cool if you made the names of the colors self-referential.
Nits: I would have made all the system color methods like "systemThingColor", capitalized RGB and HSB, and made "RGBDictionary" return @{@"red": ...} (and made static NSStrings for the keys so nobody needs to go to the .m file to see what will happen). Obviously I am a pedant and I live right on the edge of the 80 character limit; of these I'd only suggest making static dictionary keys as an actually desirable change.
Nice work, you shouldn't be ashamed to share it at all.
Take for example the capitalize methods. In ruby, you have "capitalize" and "capitalize!" to distinguish the immutable and mutable versions. To make this distinction in ObjC, you generally use nouns vs. verbs. So for the equivalent of ruby's "capitalize", you'd use "capitalizedString" (which is in fact provided by NSString). But the mutable version, the equivalent of ruby's "capitalize" should simply be "capitalize", not "capitalizedStringInPlace".
a) The author might disagree with my asessment. A pull request would imply that I consider it an error, but I think it's more a question of style.
b) I already waste too much time on HN. Forking a project, trying to understand all of it, making changes, writing a pull request, etc. would probably take me all day but I need to clean the kitchen and take care of the kids and work on some overdue client work that I have been postponing for far too long.
The reasoning for this is for consistency, while still maintaining autocomplete proximity.
(Also, the comments in the header file and the README file contain the name "capitalizedStringInPlace" instead of "capitalizeInPlace")
Another inconsistency is that most mutating methods in ObjectiveC don't return self, but either void or a boolean or an integer that reports if the method succeeded. But I assume that this inconsistency is on purpose because you want to make the methods chainable.
I don't disagree with you though. I'd prefer if the interface for this was more idiomatic.
Returning void is sort of pointless. A boolean, maybe. I feel like returning self is just making better use of things, really, and probably doesn't really cause any confusion.
Returning void makes it easier to spot errors. If you accidentally use "capitalizeInPlace" instead of "capitalizedString" your code will compile without issues. If the mutating method returned void, it would result in a compile error.
If a future version of iOS adds an -sum method, for example, you'd better hope it has the exact same semantics as yours, otherwise if the system calls -sum expecting its implementation to be used, bad things (or worse, bad things with no apparent cause) will happen when your implementation is used instead.
And you changing the category won't help the versions already out in the wild. Hopefully, automatic updates will mitigate this to some degree but not everyone will turn them on.
There are workarounds (like prefixing methods with a pseudo-namespace), but they're all a bit ugly. Might be worth considering though.
I'll donate $100 to you if you want to continue with NS+Ruby classes.
If you want to be really convinced, check out RMQ: https://github.com/infinitered/rmq. Has made my life so easy.
https://developer.apple.com/library/ios/documentation/Cocoa/...
- (NSString *)format: ...;
It would be oh-so convenient, meaning you could write formatting operations like this: newstr = [@"%d %@ %s" format: 42, @"blah", "yep"];
Given the constraints we have to work with, I'd say your best bet would be to simply write a function: newstr = fmt(@"%d %@ %s", 42, @"blah", "yep"); + (id)stringWithFormat:(NSString *)format, ...;
This of course already exists, but if you wanted to do a Rubyfied version of it, then just follow the same general pattern.The reality is, that NSString needs native formatting sugar. Anything else would really just be a hack. I'm not particularly even happy with the [@"Hello ":@"World] syntax, either.
newstr = [@"%d %@" format 42, @"blah"];
Implement it as a method that takes a dummy parameter, plus a macro: - (NSString *)format:(int)dummy, ...
#define format format:0,
Awful way to do it, but the resulting syntax is not entirely terrible. NSString *newS = @"blah " + @(42) + @"!"; - (NSString *)format:(id)first, ... {
NSUInteger argCount = 0;
BOOL prevPercent = NO;
for (NSUInteger i = 0; i < [self length]; ++i) {
unichar c = [self characterAtIndex:i];
if (prevPercent && c != '%')
++argCount;
prevPercent = c == '%';
}
NSMutableArray *argArr = [NSMutableArray new];
va_list args;
va_start(args, first);
for (NSUInteger i = 0; i < argCount; ++i)
[argArr addObject:va_arg(args, id)];
va_end(args);
switch (argCount) {
case 0: return [NSString stringWithFormat:self, first];
case 1: return [NSString stringWithFormat:self, first, argArr[0]];
case 2: return [NSString stringWithFormat:self, first, argArr[0], argArr[1]];
// ...
}
}
Only supports NSObject arguments, obviously. If you actually wanted to do this approach you'd want to do a better job of parsing the format string and use a more cleverer way of storing the args.Or, for the completely insane approach that probably doesn't actually work (on top of being entirely unportable):
- (NSString *)format:(id)first, ... {
NSUInteger argCount = ...;
va_list args;
va_start(args, first);
void *argList = malloc(sizeof(id) * (argCount + 1));
memcpy(argList, (void*)&first, sizeof(id));
memcpy(argList + sizeof(id), &va_arg(args, void *), sizeof(id) * argCount);
va_end(args);
NSString *ret = [[NSString alloc] initWithFormat:self arguments:argList];
free(argList);
return ret;
}