Skip to content
This repository
Browse code

Implement presence and equality attributes.

  • Loading branch information...
commit 6c87be97ef0900da27603c793f61e4ac0535143c 1 parent e87901a
Mark Story authored January 22, 2012
42  lib/Cake/Test/Case/Utility/Set2Test.php
@@ -54,7 +54,7 @@ public static function articleData() {
54 54
 			),
55 55
 			array(
56 56
 				'Article' => array(
57  
-					'id' => '3',
  57
+					'id' => '2',
58 58
 					'user_id' => '1',
59 59
 					'title' => 'Second Article',
60 60
 					'body' => 'Second Article Body',
@@ -697,4 +697,44 @@ public function testExtractStringKey() {
697 697
 		$this->assertEquals(array('foo'), $result);
698 698
 	}
699 699
 
  700
+/**
  701
+ * Test the attribute presense selector.
  702
+ *
  703
+ * @return void
  704
+ */
  705
+	public function testExtractAttributePresence() {
  706
+		$data = self::articleData();
  707
+
  708
+		$result = Set2::extract($data, '{n}.Article[published]');
  709
+		$expected = array($data['1']['Article']);
  710
+		$this->assertEquals($expected, $result);
  711
+
  712
+		$result = Set2::extract($data, '{n}.Article[id][published]');
  713
+		$expected = array($data['1']['Article']);
  714
+		$this->assertEquals($expected, $result);
  715
+	}
  716
+
  717
+/**
  718
+ * Test = and != operators.
  719
+ *
  720
+ * @return void
  721
+ */
  722
+	public function testExtractAttributeEquality() {
  723
+		$data = self::articleData();
  724
+
  725
+		$result = Set2::extract($data, '{n}.Article[id=3]');
  726
+		$expected = array($data[2]['Article']);
  727
+		$this->assertEquals($expected, $result);
  728
+
  729
+		$result = Set2::extract($data, '{n}.Article[id = 3]');
  730
+		$expected = array($data[2]['Article']);
  731
+		$this->assertEquals($expected, $result, 'Whitespace should not matter.');
  732
+
  733
+		$result = Set2::extract($data, '{n}.Article[id!=3]');
  734
+		$this->assertEquals(1, $result[0]['id']);
  735
+		$this->assertEquals(2, $result[1]['id']);
  736
+		$this->assertEquals(4, $result[2]['id']);
  737
+		$this->assertEquals(5, $result[3]['id']);
  738
+	}
  739
+
700 740
 }
77  lib/Cake/Utility/Set2.php
@@ -63,8 +63,13 @@ public static function get(array $data, $path) {
63 63
  * - `{n}` Matches any numeric key.
64 64
  * - `{s}` Matches any string key.
65 65
  * - `[id]` Matches elements with an `id` index.
66  
- * - `[id>2]` Matches elements that have an `id` index greater than 2.  Other operators 
67  
- *   are `>`, `<`, `<=`, `>=`, `==`, and `=//` which allows you to use regular expression matching.
  66
+ * - `[id>2]` Matches elements that have an `id` index greater than 2.  
  67
+ *
  68
+ * There are a number of attribute operators:
  69
+ *
  70
+ *  - `=`, `!=` Equality.
  71
+ *  - `>`, `<`, `>=`, `<=` Value comparison.
  72
+ *  - `=/.../` Regular expression pattern match.
68 73
  *
69 74
  * Given a set of User array data, from a `$User->find('all')` call:
70 75
  *
@@ -72,7 +77,7 @@ public static function get(array $data, $path) {
72 77
  * - `{n}.User.name` Get the name of every user in the set of users.
73 78
  * - `{n}.User[id]` Get the name of every user with an id key.
74 79
  * - `{n}.User[id>=2]` Get the name of every user with an id key greater than or equal to 2.
75  
- * - `{n}.User[username=/^paul/]` Get User elements with username matching `^paul`.
  80
+ * - `{n}.User[username=/^paul/]` Get User elements with username containing `^paul`.
76 81
  *
77 82
  * @param array $data The data to extract from.
78 83
  * @param string $path The path to extract.
@@ -97,10 +102,20 @@ public static function extract(array $data, $path) {
97 102
 /**
98 103
  * Traverses $data for $path.  $callback is called for each terminal element.
99 104
  * The results of all the callbacks are returned.
  105
+ *
  106
+ * @param array $data The data to traverse.
  107
+ * @param string $path The set path to walk.
  108
+ * @param callable $callback to call on the result set.
  109
+ * @return array Results of the callback mapped over the leaf nodes of the path expression.
100 110
  */
101 111
 	protected static function _traverse(array &$data, $path, $callback) {
102 112
 		$result = array();
103  
-		$tokens = String::tokenize($path, '.', '{', '}');
  113
+
  114
+		if (strpos('[', $path) === false) {
  115
+			$tokens = explode('.', $path);
  116
+		} else {
  117
+			$tokens = String::tokenize($path, '.', '[', ']');
  118
+		}
104 119
 
105 120
 		$_key = '__set_item__';
106 121
 
@@ -132,7 +147,7 @@ protected static function _traverse(array &$data, $path, $callback) {
132 147
 							$next[] = $v;
133 148
 						}
134 149
 					}
135  
-				} else {
  150
+				} elseif (strpos($token, '[') === false) {
136 151
 					// bare string key
137 152
 					foreach ($item as $k => $v) {
138 153
 						// index or key match.
@@ -140,6 +155,13 @@ protected static function _traverse(array &$data, $path, $callback) {
140 155
 							$next[] = $v;
141 156
 						}
142 157
 					}
  158
+				} else {
  159
+					// attributes
  160
+					foreach ($item as $k => $v) {
  161
+						if (self::_matches(array($k => $v), $token)) {
  162
+							$next[] = $v;
  163
+						}
  164
+					}
143 165
 				}
144 166
 			}
145 167
 			$context = array($_key => $next);
@@ -149,6 +171,51 @@ protected static function _traverse(array &$data, $path, $callback) {
149 171
 		return array_map($callback, $context[$_key]);
150 172
 	}
151 173
 
  174
+/**
  175
+ * Checks whether or not $data matches the selector
  176
+ *
  177
+ * @param array $data Array of data to match.
  178
+ * @param string $selector The selector to match.
  179
+ * @return boolean Fitness of expression.
  180
+ */ 
  181
+	protected static function _matches(array $data, $selector) {
  182
+		preg_match_all(
  183
+			'/(?<key>[^\[]+?)? (\[ (?<attr>.+?) (?: \s* (?<op>[><!]?[=]) \s* (?<val>.*) )? \])+/x',
  184
+			$selector,
  185
+			$conditions,
  186
+			PREG_SET_ORDER
  187
+		);
  188
+
  189
+		foreach ($conditions as $cond) {
  190
+			$key = $cond['key'];
  191
+			$attr = $cond['attr'];
  192
+			$op = isset($cond['op']) ? $cond['op'] : null;
  193
+			$val = isset($cond['val']) ? $cond['val'] : null;
  194
+
  195
+			if ($key && !isset($data[$key])) {
  196
+				return false;
  197
+			}
  198
+
  199
+			// Presence test.
  200
+			if (empty($op) && empty($val)) {
  201
+				return isset($data[$key][$attr]);
  202
+			}
  203
+
  204
+			// Empty attribute = fail.
  205
+			if (!isset($data[$key][$attr])) {
  206
+				return false;
  207
+			}
  208
+			$prop = $data[$key][$attr];
  209
+
  210
+			if ($op === '=') {
  211
+				return $prop == $val;
  212
+			} elseif ($op === '!=') {
  213
+				return $prop != $val;
  214
+			}
  215
+		}
  216
+		return false;
  217
+	}
  218
+
152 219
 	public static function insert(array $data, $path, $values = null) {
153 220
 
154 221
 	}

0 notes on commit 6c87be9

Please sign in to comment.
Something went wrong with that request. Please try again.