Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Accessing NSArray count #21

Closed
priteshshah1983 opened this Issue Aug 5, 2012 · 10 comments

Comments

Projects
None yet
2 participants

Is it possible to access NSArray's count property in the Mustache template? For example, I have an attendees NSArray and I would like display some snippet exactly once if attendees.count > 0.

{{#attendees.count}}Display me!{{/attendees.count}} // crashes!

If I use {{#attendees}}Display me!{{/attendees}} // Display me! is displayed multiple times.

Do I need to define a filter? Or is there an easier way?

Owner

groue commented Aug 5, 2012

Hi, Pritesh. If you do not mind sharing your templates with some other platforms, the easiest way is to use ’count’, as you have tried. The new way, with filters, is ’{{^ isEmpty(attendees) }}Display me!{{/}}’. It does not invalidate the ’count’ trick, though.

Testing for ’count’ should not crash, and indeed ’{{#attendees.count}}Display me!{{/attendees.count}}’ does NOT crash.

The crash you're experiencing is thus unrelated to this emptiness test. It happens somewhere else. Do you have a stack trace or something?

My bad, it doesn't crash the app, but it throws an exception and I have Xcode set to add a breakpoint on all exceptions. Here is the stack trace: http://tinypic.com/r/1fe7it/6

Please note that I have added the code posted by you (see below), but {{attendees.count}} still throws an exception and doesn't work.

#ifdef DEBUG
[GRMustache preventNSUndefinedKeyExceptionAttack];
#endif

--- Edit ---

Adding a simple example:

NSArray *array1 = [NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:2], nil];

NSString *rendering = [GRMustacheTemplate renderObject:array1
                                            fromString:@"Count is {{count}}"
                                                 error:NULL];

Should this work? I get the same error and stack trace as explained above.

Thanks for your help!

Owner

groue commented Aug 5, 2012

OK. Tell me if I understand well:

First, the rendering is done just fine, and you do not experience any crash.

However, despite the prevention of exceptions that should keep your debugger quiet, your breakpoint is still activated.

Does this describe your situation?

Owner

groue commented Aug 5, 2012

OK I get it.

valueForKey has a special behavior when applied to arrays: it returns another array filled with the key applied to its element. Thus {{ count }} generates an array of the key ‘count‘ applied to the NSNumber items, which do not know this key, and raise exceptions which are not prevented by preventNSUndefinedKeyExceptionAttack (it only prevents exceptions raised by the target object itself, the array, not other objects).

Well, this is interesting. I will consider having this guard method catch exceptions sent by array items (its purpose it not to catch them all, but only most of them - I'll check the documentation in case this is not absolutely clear. If there is a bug, it's a documentation bug since its impossible to prevent all exceptions)

So, today, here are your options:

  • live with those exceptions (and maybe deactivate the breakpoint).
  • use the isEmpty filter
  • declare another property ’hasAttendees’ that returns YES if the attendees array is not empty.

Sorry for the inconvenience.

Owner

groue commented Aug 5, 2012

Second thought:

I plan to keep valueForKey: as the one and only way to provide values to the rendering engine. NSArray implementation returns another array, even if the key is count. So, there is absolutely no way {{ count }} can ever return an actual number.

This leads to a real bug in GRMustache: {{attendees.name}} {{.}} {{/attendees.name}} outputs the concatenation of the names of the attendees, instead of the empty string. That's because attendees.name yields an array of names, instead of the expected value nil (array do not have any name property).

So, thank you very much Pritesh for letting this bug emerge. Its resolution may affect your count problem, I'll let you know.

@groue groue added a commit that referenced this issue Aug 5, 2012

@groue groue Add failing test for issue #21 847751e

Thanks Gwendal! I didn't understand the valueForKey: behavior before reading your explanation. I'm planning to declare another property 'hasAttendees' as you suggested to keep it simple for now.

Owner

groue commented Aug 5, 2012

That would have been my choice as well :-) Happy Mustache, and thanks again for the indirect bug report :-)

@groue groue added a commit that referenced this issue Aug 6, 2012

@groue groue Fix failing test for issue #21 c9578ed

@groue groue added a commit that referenced this issue Aug 6, 2012

@groue groue Fix for issue #21: +[GRMustacheContext valueForKey:inObject:] returns…
… nil when object is a Foundation collection object whose valueForKey: implementation returns another collection.
526f85c
Owner

groue commented Aug 6, 2012

@priteshshah1983 GRMustache 4.3.2 fixes the {{#attendees.name}} {{.}} {{/attendees.name}} bug. As a consequence, {{#attendees.count}}Display me!{{/attendees.count}} will never render, even for non-empty arrays, but won't raise any exception, because the default implementation of valueForKey: is now avoided for NSArray, NSSet and NSOrderedSet, the three foundation classes that return new collections instead of performing a property/method lookup.

@groue groue closed this Aug 6, 2012

Thanks for fixing this!

Owner

groue commented Oct 21, 2012

@priteshshah1983 GRMustache 5.5.1 restores the ability to use the count key in templates, while preventing the bugs we have discovered together. See https://github.com/groue/GRMustache/blob/master/Guides/runtime/context_stack.md#nsarray-nsset-nsorderedset

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment