Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Accessing NSArray count #21

Closed
priteshshah1983 opened this Issue · 10 comments

2 participants

@priteshshah1983

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?

@groue
Owner

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?

@priteshshah1983

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!

@groue
Owner

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?

@groue
Owner

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.

@groue
Owner

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 referenced this issue from a commit
@groue Add failing test for issue #21 847751e
@priteshshah1983

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.

@groue
Owner

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

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

@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
@priteshshah1983

Thanks for fixing this!

@groue
Owner

@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
Something went wrong with that request. Please try again.