Technology Musings

May 09, 2013

Platforms / My Beef with Model Validators in Rails

JB

When I first started using Rails, it was a dream come true.  I estimate that I reduced the amount of code I was writing by 60-75%.  This is probably still true.  However, as I've gotten to know the platform better, there are some philosophical issues that I have with Rails.  Sometimes it is more the Rails community and its conventions rather than rails itself, but nonetheless, it continually manifests itself.

My current beef - validators!  Having written lots and lots of code that interacts with lots and lots of systems, I have to say that you should never, ever, ever put in model validators.  Ever.  Validation is a process issue, not a data one.  In theory it should be a data issue, but it never ever works out that way.

Here's what happens.  Let's say I have a telephone number validator for a model.  It makes sure the numbers are in the format (918) 245-2234.  If not, it gives a nice error message.  However, now let's connect that same model to an external system.  Let's now say that the author of the external system was not as picky about phone number formats.  What happens now?  Well, every record that gets migrated from that system that isn't formatted according to your desires will now get kicked out!  And it might not even show up during testing!  In addition, your pretty error messages mean practically nothing on system->system communication.  So, what you have is the system randomly breaking down.

This causes even more problems in long-term maintenance.  I've had many bugs pop up because someone added a validator that broke several processes.  It worked in all of their tests, but they weren't thinking about all of the ways the system was used.  For instance, we decided to lock down the characters used in our usernames a little more.  The programmer decided to implement it as a validator.  Well, the problem is that several users already had usernames that didn't match the validator.  So what happened?  Those records just stopped saving.  Not just when there was someone who was at the terminal, where they could see the error message, but every job started erroring out because of users who had faulty names.

Because of these issues, I *always* do validation in my controller, and *never* in the model.  It is true that there are probably workarounds for all of these issues, but the point is that they are *not* that exceptional!  They are everywhere!  What would be nice is to have validators which could be run optionally, in specific situations.  So, a controller could declare that it was using :data_entry validations, and then the model would only run those validators.  So, it would look something like this:

In the model:

validates_presence_of :email, :for => [:data_entry]

In the controller:

uses_validators :data_entry

One of the issues I have found with dynamic languages such as ruby is that errors arise when "magic" is sprinkled in unexpected places.  Ruby magic is awesome, but when I do something simple and it won't save, it is sometimes hard to even locate where the error is.  Explicitly declaring which validator set you are using allows programmers to more easily see what they are doing, and only apply magic code where it goes.

Someday I'll write a package like this --- when I get extra time :)

May 07, 2013

Platforms /

Why the tabs-vs-spaces debate matters:

Inconsistent indentation: 2 spaces were used for indentation, but the rest of the document was indented using 1 tab.

Everyone - indent with tabs.  Then you can control the indentation using your editor's settings.  DO NOT indent with spaces.  And DO NOT use any programming language or configuration system that cares about indentation.

October 01, 2012

Platforms / Rails, CoffeeScript, and Java

JB

I *hate* the direction that Rails is moving.  It is becoming a bloated piece of garbage.  I got into Rails precisely because Rails got out of my way most of the time, but was easy to use right where I needed it.  ActiveRecord was a thin shell over SQL.  Therefore, if there was a problem, I could easily diagnose it, because I knew SQL.  It added a few bits of magic, but those were usually pretty understandable.  Likewise with the javascript helpers and the like.  If you needed something done, just pop into Javascript and patch it up.

This contrasted heavily with J2EE.  I used J2EE for a while, but it turns out that if you want to use J2EE you have to commit to it like it's a religion.  If you don't know 20 different platform libraries, you can't even do a hello world in J2EE.  When you finally get it working, you don't know what the heck is even going on.  Some magic is happening somewhere, but it is beyond what is plainly visible.  This, and the fact that Ruby is very metaprogramming-ish, was the reason I chose Rails.

However, starting with Rails 3, the Rails team decided that it was simply going to do everything for you, and everything was going to be done in domain-specific languages.  Thus, instead of the required list of languages/platforms being Ruby/Rails/SQL/Javascript, it has balooned out to Ruby/Rails/SQL/Javascript/Coffeescript/SASS/ECO/Node.js, and probably a few others I'm forgetting.  The asset pipeline is kind of cool, but it's yet another place for things to go wrong.  I think the anti-CSS measure of auto-h()ing code is terrible.  If you wanted to go that way, instead you should use Perl's taint feature, and just give errors if you display tainted values.  The idea that random code should transform my strings is horrible.

Finally, I understand the reason for getting rid of the javascripty stuff from rails core, but some of those were really what made the platform really rock.  I think they have missed the mark - they put the ease-of-use in the wrong places, and instead have made sure that no one that doesn't have a relgious commitment to the software stack will ever find it usable.  It has basically gone the way of J2EE, and I am now looking for a new platform to love.

August 15, 2012

Platforms / Autoreleased with No Pool in Place

JB

Before Apple's ARC, you would often have to manage memory semi-manually.  Before operation queues, you would also have to manage setting up autorelease pools on new threads.  If you didn't, you would get an error such as:

autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug

However, the other day, I started getting these myself!  I was a little shocked - what on earth was I doing that required an autorelease pool?  Well, eventually I figured it out.  It turns out that if you have custom +load() functions on your classes (these get called whenever the classes load - I use them for registering handlers and such), this is another situation in which you have to set up your own autorelease pool if you do any object creation. 

So, in your +load() function (or, depending on what you are doing, in functions that +load calls), just wrap it with

@autoreleasepool {
Your Code Here
}

And it works!

July 13, 2012

Platforms / iOS Bug - insertNewObjectForEntityName: deleteObject and rollback

JB

I've been chasing down a wierd bug where, on an NSManagedObjectContext, the hasChanges method keeps returning YES even after a rollback.  It turns out, if you do the following sequence, hasChanges will *never* be cleared:

MyClass *c = [NSEntityDescription insertNewObjectForEntityName:@"MyClass" inContext:ctx];
//Do other stuff
[ctx deleteObject:c];
// Do other stuff
[ctx rollback];
[ctx hasChanges]; // Returns YES!

In my own case, I got around it by not deleting the object, and just rolling back if I wanted it gone, but that won't work for everyone.  So, I did:

MyClass *c = [NSEntityDescription insertNewObjectForEntityName:@"MyClass" inContext:ctx];
//Do other stuff
[ctx rollback]; // Don't delete! just roll back
//Do other stuff
[ctx rollback]; // Now it works
[ctx hasChanges]; // Returns NO like it should

May 10, 2012

Platforms / iOS - using storyboard relationships for custom view controller containment

JB

One thing that iOS forgot to do with the release of storyboards and iOS 5 is support for custom "relationship" segues.  In addition to normal segues, UINavigationController and UITabBarController have what are called "relationships", which allow you to define containment information for these controllers.  So, for the navigation controller, you define the root view controller, and for the tab bar controller, you define the list of view controllers.  This is all done in a neat interface in the storyboard editor (which is the replacement for interface builder).

Another cool feature in iOS is "containment", which is basically a way of letting you write view controllers which contain other view controllers.  This was not really allowed in iOS 4, but they've added some basic support for it.  Unfortunately, the way that you might want to use containment with storyboarding is through relationships, but iOS doesn't provide a way to do custom relationships in storyboards - or even their default ones on custom view controllers.

My own task was to create a tab bar controller with the tabs at the *top*.  I had a few ideas on how I might approach this problem.  Subclassing UITabBarController didn't seem like a good idea.  So I tried several others.

One possible way was duck typing.  I thought that perhaps I could pull a fast on on the storyboard editor.  What I would do is define my own class that mimicked UITabBarController without actually inheriting from it.  Then, I could create my relationships with the storyboard editor, and then substitute my own non-tab-bar controller at the last minute.  This failed miserably.  

Firts of all, the storyboard editor doesn't like you to substitute non-subclasses for the class that it wants.  If you try to set it to a non-subclass of UITabBarController, it won't give you an error, but it won't save the change, either.

I tried to work around this by making my class inherit from UITabBarController while I was editing the storyboard, and then change it back for the compile.  This worked momentarily, but in the end, it turns out that the "relationship" segues aren't set via getters and setters.  I imagine there is an internal API that is being used.

Therefore, I next tried overriding initWithCoder and reading the nib values myself.  Unfortunately, I was not successful at guessing the keys for decoding.

Then, it hit me.  What I need to do is to mix subclassing UITabBarController with not mixing UITabBarController.  So here's what I did:

class 1: MyTabBarController

This is just a shell class, but it inherits from UITabBarController.  

class 2: MyTabBarControllerImplementation

This is your real implementation class, and only needs to inherit from UIViewController.  Make it work like you want it to.

What you are going to do is designate your tab bar controllers to be of type MyTabBarController in the storyboard editor.  Then, when viewDidLoad is called on MyTabBarController, the view controller de-registers the view controllers from itself, creates an instance of MyTabBarControllerImplementation, moves the view controllers over there, and then replaces itself with MyTabBarControllerImplementation.

Anyway, here's the code.  It's really rough, but works at the moment.  I'm open to improvements:

MyTabBarController.h:

#import <UIKit/UIKit.h>
@interface MyTabBarController : UITabBarController
@end

MyTabBarController.m:

#import "MyTabBarController.h"
#import "MyTabBarControllerImplementation.h"
@implementation JBTabController
- (void)viewDidLoad
{
    [super viewDidLoad];
    JBTestContainerViewController *vc = [[JBTestContainerViewController alloc] init];
    NSArray *vclist = self.viewControllers;
    [self setViewControllers:[NSArray array] animated:NO];
    vc.viewControllers = vclist;
    NSMutableArray *navlist = [self.navigationController.viewControllers mutableCopy];
    [navlist replaceObjectAtIndex:(navlist.count - 1) withObject:vc];    
    [self.navigationController setViewControllers:navlist animated:NO];
}
@end

MyTabBarControllerImplementation.h:

 

#import <UIKit/UIKit.h>
@interface MyTabBarControllerImplementation : UIViewController<UITabBarDelegate>
@property (nonatomic) int selectedIndex;
@property (strong, nonatomic) NSArray *viewControllers;
@property (strong, nonatomic) UITabBar *bar;
@end

MyTabBarControllerImplementation.m:

 

#import "MyTabBarControllerImplementation.h"
@implementation MyTabBarControllerImplementation
@synthesize viewControllers = _viewControllers;
@synthesize selectedIndex = _selectedIndex;
@synthesize bar = _bar;
-(CGRect) subviewFrame {
    CGRect f = self.view.frame;
    return CGRectMake(0, 30, f.size.width, f.size.height - 30);
}
-(void) reloadBarInfo {
    NSMutableArray *itms = [NSMutableArray arrayWithCapacity:_viewControllers.count];
    for(UIViewController *vc in _viewControllers) {
        UITabBarItem *itm = [[UITabBarItem alloc] initWithTitle:vc.title image:nil tag:0];
        [itms addObject:itm];
        [vc didMoveToParentViewController:self];
    }
    _bar.items = itms;    
    _bar.selectedItem = [itms objectAtIndex:0];
    _bar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    UIViewController *firstVC = [_viewControllers objectAtIndex:0];
    firstVC.view.frame = [self subviewFrame];
    firstVC.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self.view addSubview:firstVC.view];
}
-(void) viewDidLoad {
    self.bar = [[UITabBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 30) ];
    for(UIViewController *vc in _viewControllers) {
        [self addChildViewController:vc];
    }
    _bar.delegate = self;
    [self.view addSubview:_bar];
    [self reloadBarInfo];
}
-(void) tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
    int idx = [_bar.items indexOfObject:item];
    UIViewController *newVC = [_viewControllers objectAtIndex:idx];
    UIViewController *curVC = [_viewControllers objectAtIndex:_selectedIndex];
    if(newVC == curVC) {
        return; // Don't switch - same one selected!
    }
    [curVC.view removeFromSuperview];
    newVC.view.frame = [self subviewFrame];
    newVC.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self.view addSubview:newVC.view];
    [self transitionFromViewController:curVC toViewController:newVC duration:0 options:0 animations:nil completion:nil];
     self.selectedIndex = idx;
}
@end

And there you have it!

 

 

 

June 21, 2011

Platforms / Introducing Newm: An Objective-C Framework for Web Applications

JB

I am working on starting a web application framework called Newm. It is kind of like an Objective-C-on-Rails.  Check it out!

June 18, 2011

Platforms / Objective-C Hackery

JB

Been working on several objective-c projects.  I love to get into the fun tricks you can do with languages and stuff.  Here's some interesting tidbits I found:

 

June 07, 2011

Platforms / Miscellaneous RFID Links

JB

Learning RFID for a project I'm going to work on soon.  Here are some handy dandy links!

May 24, 2011

Platforms / Database Scaling with PostgreSQL

JB

I've been digging around on various database scaling options for PostgreSQL.  Here's some interesting links I've stumbled upon: