supermarin/ObjectiveRecord — pull request #82 by stephencelis (2014-03-08 19:43:12+00:00)
Title: Chained fetch request builder (ObjectiveRelation)

This commit provides to ObjectiveRecord what Arel[1] provided to Active Record.

[1]: https://github.com/rails/arel

It simplifies the interface by using a chained request builder. Instead
of:
``` objective-c
[Person where:@{@"firstName": @"Stephen"} order:@"lastName" limit:@5];
```

We can use:
```objective-c
[[[Person where:@"firstName = %@", @"Stephen"] order:@"lastName"] limit:5]
```
Because of chaining, we can remove most of the methods that build complex fetch requests all at once, and we can support features that were previously omitted (perhaps due to the number of method signature permutations required to make them work).

Even `+countWhere:` can be removed:
```objective-c
[[Person where:@"firstName = %@", @"Stephen"] count];
```

Each step in the chain returns a lazy-loaded fetch request. So you can hold onto a base request and build on it in different ways. E.g.,
```objective-c
// Create 2 people in a private context.
id privatePeople = [Person inContext:privateContext];
[privatePeople create:@{@"firstName": @"Marin"}];
[privatePeople create:@{@"firstName": @"Stephen"}];

// Pagination
id page = [Person limit: 5];
// ...
page = [page offset:[page offset] + [page limit]];
```

A fetch request is not executed before you ask for data that would require a fetch, e.g. by iterating, fetching a specific result, or a count.

Fetch request builders conform to NSFastEnumeration and forward array selectors to the result set, so act on them as you would act on the final NSArray:
```objective-c
for (Person *person in [Person all]) {
// ...
}

[[Person where:@"admin = NO"] setValue:@YES forKey:@"admin"];
```
---
In addition to `-offset:`, `-reverseOrder` was easy to add. There's a lot of flexibility here. We could (and should) consider adding things like:

- `-not` (which could build predicates using `+notPredicateWithSubpredicate:`)
- `-updateAll:` (simple wrapper that iterates over results and calls `-update:` on each one)

Considerations:

- Naming: `ObjectiveRelation` is like `ActiveRecord::Relation`, but we may want to consider a more conventional naming convention for custom classes here. Maybe something with a 3-character prefix?

- The `id` return values. This isn't ideal, but I couldn't figure out a better (and still simple) way of preventing Xcode from complaining that ObjectiveRelation objects didn't respond to NSArray selectors. Using `id`, however, means you can't call things like `.first` and `.count` on the return values without first casting them. We could, alternatively, return something like `id`, but it was a bit verbosely ugly. Might be the direction to move, though.