Ronald Oussoren avatar Ronald Oussoren committed 854d009

Implement "Unordered Accessors" for Key-Value Coding
as hidden selectors for set properties, with limited tests.

Comments (0)

Files changed (2)

pyobjc-core/Lib/objc/_properties.py

 
         return self&other
 
+def makeSetAccessors(name):
+    def countOf(self):
+        return len(getattr(self, name))
+
+    def enumeratorOf(self):
+        return iter(getattr(self, name))
+
+    def memberOf(self, value):
+        collection =  getattr(self, name)
+        if value not in collection:
+            return None
+
+        for item in collection:
+            if item == value:
+                return item
+
+    def add(self, value):
+        getattr(self, name).add(value)
+
+    def remove(self, value):
+        getattr(self, name).discard(value)
+
+    return countOf, enumeratorOf, memberOf, add, remove
 
 
 class set_property (object_property):
             object_property.__set__(self, object, v)
         return v
 
+    def __pyobjc_class_setup__(self, name, class_dict, instance_methods, class_methods):
+        super(set_property, self).__pyobjc_class_setup__(name, class_dict, instance_methods, class_methods)
+
+        # (Mutable) Unordered Accessors
+        # FIXME: should only do the mutable bits when we're actually a mutable property
+
+        name = self._name
+        Name = name[0].upper() + name[1:]
+
+        countOf, enumeratorOf, memberOf, add, remove = makeSetAccessors(self._name)
+
+        countOf = selector(countOf, 
+                selector  = 'countOf%s'%(Name,),
+                signature = _C_NSUInteger + '@:',
+        )
+        countOf.isHidden = True
+        instance_methods.add(countOf)
+
+        enumeratorOf = selector(enumeratorOf, 
+                selector  = 'enumeratorOf%s'%(Name,),
+                signature = '@@:',
+        )
+        enumeratorOf.isHidden = True
+        instance_methods.add(enumeratorOf)
+
+        memberOf = selector(memberOf, 
+                selector  = 'memberOf%s:'%(Name,),
+                signature = '@@:@',
+        )
+        memberOf.isHidden = True
+        instance_methods.add(memberOf)
+
+        add1 = selector(add, 
+                selector  = 'add%s:'%(Name,),
+                signature = 'v@:@',
+        )
+        add1.isHidden = True
+        instance_methods.add(add1)
+
+        add2 = selector(add, 
+                selector  = 'add%sObject:'%(Name,),
+                signature = 'v@:@',
+        )
+        add2.isHidden = True
+        instance_methods.add(add2)
+
+        remove1 = selector(remove, 
+                selector  = 'remove%s:'%(Name,),
+                signature = 'v@:@',
+        )
+        remove1.isHidden = True
+        instance_methods.add(remove1)
+
+        remove2 = selector(remove, 
+                selector  = 'remove%sObject:'%(Name,),
+                signature = 'v@:@',
+        )
+        remove2.isHidden = True
+        instance_methods.add(remove2)
+
 
 NSMutableDictionary = lookUpClass('NSMutableDictionary')
 

pyobjc-core/PyObjCTest/test_set_property.py

             self.assertEquals(observer.values[-1][-1]['old'], set([1,2,3]))
             self.assertEquals(observer.values[-1][-1]['new'], set([2,3,4]))
 
+    def testObjCAccessors(self):
+        # Check that the right ObjC array accessors are defined and work properly
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"setASet:"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"aSet"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"countOfASet"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"enumeratorOfASet"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"memberOfASet:"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"addASet:"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"addASetObject:"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"removeASet:"))
+        self.assertTrue(TestSetPropertyHelper.instancesRespondToSelector_(b"removeASetObject:"))
+
+        o = TestSetPropertyHelper.alloc().init()
+        self.assertEquals(0, o.pyobjc_instanceMethods.countOfASet())
+        self.assertRaises(AttributeError, getattr, o, 'countOfASet')
+        o.aSet.add(1)
+        o.aSet.add(2)
+
+        v = list(sorted(o.pyobjc_instanceMethods.enumeratorOfASet()))
+        self.assertEquals(v, [1,2])
+
+        class Testing (object):
+            def __hash__(self):
+                return 42
+
+            def __eq__(self, other):
+                return isinstance(other, Testing)
+    
+        p = Testing()
+        o.aSet.add(p)
+
+        v = o.pyobjc_instanceMethods.memberOfASet_(Testing())
+        self.assertIs(p, v)
+
+        self.assertNotIn(9, o.aSet)
+        o.pyobjc_instanceMethods.addASet_(9)
+        self.assertIn(9, o.aSet)
+
+        self.assertNotIn(10, o.aSet)
+        o.pyobjc_instanceMethods.addASetObject_(10)
+        self.assertIn(10, o.aSet)
+
+        self.assertIn(9, o.aSet)
+        o.pyobjc_instanceMethods.removeASet_(9)
+        self.assertNotIn(9, o.aSet)
+
+        self.assertIn(10, o.aSet)
+        o.pyobjc_instanceMethods.removeASetObject_(10)
+        self.assertNotIn(10, o.aSet)
+
+
 if __name__ == "__main__":
     main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.