AlexDenisov / iActiveRecord

ActiveRecord for iOS without CoreData, only SQLite
http://alexdenisov.github.com/iActiveRecord/
MIT License
354 stars 50 forks source link

Added ability to lazily persist model relationships without individual saves #75

Closed neilabdev closed 10 years ago

neilabdev commented 10 years ago

Desiring the ability to be able to create objects without saving individual parent/child models until desired time for persistence. For example, if your building models from an XML feed, you may want to save the parent object at the end element and add/associate child elements at their corresponding end tags in the body. This is difficult if you must save the parent before each child object is added, etc. Here is an example:

   # ActiveRecord/UnitTests/Specs/DependencySpec.mm
    it(@"Queued BelongsTo", ^{
        User *john = [User newRecord];
        john.name = @"John";

        User *peter = [User newRecord];
        peter.name = @"Peter";

        Group *students = [Group newRecord];
        students.title = @"Students";

        [students addUser:john];
        [students addUser:peter];
        [students save];

        [john dropRecord];
        [Group count] should equal(0);
        [User count] should equal(0);
    });

vs

it(@"BelongsTo", ^{
        User *john = [User newRecord];
        john.name = @"John";
        [john save];
        User *peter = [User newRecord];
        peter.name = @"Peter";
        [peter save];

        Group *students = [Group newRecord];
        students.title = @"Students";
        [students save];

        [students addUser:john];
        [students addUser:peter];
        [john dropRecord];
        [Group count] should equal(0);
        [User count] should equal(0);
    });
AlexDenisov commented 10 years ago

Hi, @valerius. Thank you for pull request. It seems that something got broken. Could you check it?

neilabdev commented 10 years ago

It "appears unrelated to the branch" (famous last words). However, the failure is specifically suggesting an issue with CGD background saves which work locally:

describe(@"GCD", ^{

    it(@"should save records in background", ^{
        __block NSInteger count = 0;

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            User *user = [User newRecord];
            user.name = @"alex";
            [user save];
            dispatch_async(dispatch_get_main_queue(), ^{
                count = [User count];
            });
        });

        in_time(count) should be_greater_than(0);
    });
});

Not sure how I can make it fail on my machine which passes. I intentionally make it fail by changing 'be_greater_than(0)' to '100' which does fail, but leaving it as zero works locally. Your CI suggest:

FAILURE GCD should save records in background
/Users/travis/build/AlexDenisov/iActiveRecord/UnitTests/Specs/GCDSpec.mm:35 Expected <0> to be greater than <0>
Finished in 3.6870 seconds
118 examples, 1 failures
GCD should save records in background
/Users/travis/build/AlexDenisov/iActiveRecord/UnitTests/Specs/GCDSpec.mm:35 Expected <0> to be greater than <0>

Perhaps I can send up a blank branch without the changes and if if that works...

AlexDenisov commented 10 years ago

Does it work on your local machine?

neilabdev commented 10 years ago

Yup. No issues as far as the test indicate. I'm sending a blank/master pull shortly without my changes. If that passes, I presume it may be the environment. If it passes, its a difference in our setup which may indicate a real bug or misconfiguration I would suspect.

AlexDenisov commented 10 years ago

yeah, i'm not sure that failed test is ok by itself.

neilabdev commented 10 years ago

Well, apparently this one passed: PR # 75 which is blank off master. Will take a look.

Side note: I also have a category in my local project which allows the following:

User *user1 = [User new: @{@"first_name" : @"First Name", @"last_name":@"last name"}];
Group *group = [Group create: @{ @"title":"this is my title"}]; // this one is persisted

[group addUser: user1 ];
[group addUser: [User new: @{@"first_name" : @"First Name 2", @"last_name":@"last name 3"}] ];

AND

@implementation User: ActiveRecord {}
   field_int_imp(age) // field_int_dec(age) in header
   field_bool_imp(active) 
@end

User * user = User.new

user.int_age = 10
user.is_active = YES 

// or the usual

user.age = [NSNumber numberWithInt: 10];
user.active = [NSNumber numberWithBool: YES];

But thought it was a separate ticket and not sure if I like the macro names yet, so... nevertheless If your interested I can send upstream when I'm content. However, this functions are simply macros so I can do it using a category and doesn't have to be in the main project... unlike this ticket which fails for a mysterious reason.

AlexDenisov commented 10 years ago

I'd like to accept PR with additions for creating (new:, create:)

neilabdev commented 10 years ago

Gladly, Will do just the 'create','new' helpers for now. Must get it to build on the CI first, which I thought it did the last attempt, but now it doesn't appear to have run the test despite believing I saw 0 zero failures. Nevertheless, As noted, it currently in a category but will refactor it out.

Where I was going is below, though all functions aren't implemented yet:

@interface ActiveRecord (Extensions)
+ (instancetype) findById: (id) record_id;
+ (instancetype) findByKey: (id) key value: (id) value;
+ (NSArray *) findAllByKey: (id) key value: (id) value;
+ (NSArray *) findAllByConditions: (NSDictionary *) conditions; // Not Implemented Yet
+ (instancetype) findByConditions: (NSDictionary *) conditions; // Not Implemented Yet
+ (instancetype) new: (NSDictionary *) values;
+ (instancetype) create: (NSDictionary *) values;
+ (void)addSearchOn:(NSString *)aField; // Not Implemented Yet. For full text searches using FTS
- (instancetype) recordSaved;
@end

User *user1 = [User findbyId: @1] ; //
User *user2 =[User findByKey: @"name" value: @"james"];
NSArray *users = [User findAllByConditions: @{ @"first_name" : @"James", @"last_name":@"whitfield" }];

Let you know when its up... the sooner I can replicate the test failure locally it should be pretty easy to fix :)

AlexDenisov commented 10 years ago

Cool, thank you :)

neilabdev commented 10 years ago

After debugging for a while, it looks like test for failing for two major reasons: 1. Proper synchronization of the Podfile/Podspec in the CI environment... I think my last change fixes this, so test will run with the current code in the existing branch/directory, whatever it may be, without tinkering with the Podspec source git :commit hash (used :path instead of :podspec in Podfile). 2. All your test, except one, would ensure the SQLite database was created except, NSDecimalNumber, which if its not created, causes and generic "out of memory" error. This, plus the facts specs don't always run in the same order, could cause intermittent errors if that spec started first. Solution was a one liner and is fixed in this branch. The CI is not kicking off anymore, but if it did, I hope it passes as it does locally.