lebowski 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. data/History.md +3 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +84 -0
  4. data/README.md +146 -0
  5. data/Rakefile +42 -0
  6. data/bin/lebowski +26 -0
  7. data/bin/lebowski-spec +29 -0
  8. data/bin/lebowski-start-server +24 -0
  9. data/lib/lebowski.rb +15 -0
  10. data/lib/lebowski/core.rb +35 -0
  11. data/lib/lebowski/foundation.rb +52 -0
  12. data/lib/lebowski/foundation/application.rb +315 -0
  13. data/lib/lebowski/foundation/core.rb +61 -0
  14. data/lib/lebowski/foundation/core_query.rb +231 -0
  15. data/lib/lebowski/foundation/dom_element.rb +114 -0
  16. data/lib/lebowski/foundation/errors/argument_invalid_type.rb +31 -0
  17. data/lib/lebowski/foundation/errors/unexpected_type.rb +30 -0
  18. data/lib/lebowski/foundation/mixins/collection_item_view_support.rb +87 -0
  19. data/lib/lebowski/foundation/mixins/delegate_support.rb +22 -0
  20. data/lib/lebowski/foundation/mixins/inline_text_field_support.rb +29 -0
  21. data/lib/lebowski/foundation/mixins/key_check.rb +35 -0
  22. data/lib/lebowski/foundation/mixins/list_item_view_support.rb +36 -0
  23. data/lib/lebowski/foundation/mixins/positioned_element.rb +20 -0
  24. data/lib/lebowski/foundation/mixins/stall_support.rb +79 -0
  25. data/lib/lebowski/foundation/mixins/user_actions.rb +302 -0
  26. data/lib/lebowski/foundation/mixins/wait_actions.rb +44 -0
  27. data/lib/lebowski/foundation/object_array.rb +305 -0
  28. data/lib/lebowski/foundation/panes/alert.rb +117 -0
  29. data/lib/lebowski/foundation/panes/main.rb +20 -0
  30. data/lib/lebowski/foundation/panes/menu.rb +100 -0
  31. data/lib/lebowski/foundation/panes/modal.rb +21 -0
  32. data/lib/lebowski/foundation/panes/palette.rb +21 -0
  33. data/lib/lebowski/foundation/panes/pane.rb +24 -0
  34. data/lib/lebowski/foundation/panes/panel.rb +25 -0
  35. data/lib/lebowski/foundation/panes/picker.rb +43 -0
  36. data/lib/lebowski/foundation/panes/sheet.rb +21 -0
  37. data/lib/lebowski/foundation/proxy_factory.rb +87 -0
  38. data/lib/lebowski/foundation/proxy_object.rb +670 -0
  39. data/lib/lebowski/foundation/sc_object.rb +38 -0
  40. data/lib/lebowski/foundation/views/button.rb +20 -0
  41. data/lib/lebowski/foundation/views/checkbox.rb +63 -0
  42. data/lib/lebowski/foundation/views/collection.rb +304 -0
  43. data/lib/lebowski/foundation/views/container.rb +32 -0
  44. data/lib/lebowski/foundation/views/disclosure.rb +59 -0
  45. data/lib/lebowski/foundation/views/grid.rb +21 -0
  46. data/lib/lebowski/foundation/views/label.rb +30 -0
  47. data/lib/lebowski/foundation/views/list.rb +67 -0
  48. data/lib/lebowski/foundation/views/list_item.rb +280 -0
  49. data/lib/lebowski/foundation/views/menu_item.rb +27 -0
  50. data/lib/lebowski/foundation/views/radio.rb +32 -0
  51. data/lib/lebowski/foundation/views/segmented.rb +97 -0
  52. data/lib/lebowski/foundation/views/select_field.rb +139 -0
  53. data/lib/lebowski/foundation/views/support/simple_item_array.rb +249 -0
  54. data/lib/lebowski/foundation/views/text_field.rb +108 -0
  55. data/lib/lebowski/foundation/views/view.rb +108 -0
  56. data/lib/lebowski/runtime.rb +7 -0
  57. data/lib/lebowski/runtime/errors/remote_control_command_execution_error.rb +9 -0
  58. data/lib/lebowski/runtime/errors/remote_control_command_timeout_error.rb +9 -0
  59. data/lib/lebowski/runtime/errors/remote_control_error.rb +9 -0
  60. data/lib/lebowski/runtime/errors/selenium_server_error.rb +9 -0
  61. data/lib/lebowski/runtime/object_encoder.rb +123 -0
  62. data/lib/lebowski/runtime/sprout_core_driver.rb +14 -0
  63. data/lib/lebowski/runtime/sprout_core_extensions.rb +600 -0
  64. data/lib/lebowski/scui.rb +18 -0
  65. data/lib/lebowski/scui/mixins/node_item_view_support.rb +136 -0
  66. data/lib/lebowski/scui/mixins/terminal_view_support.rb +25 -0
  67. data/lib/lebowski/scui/views/combo_box.rb +119 -0
  68. data/lib/lebowski/scui/views/date_picker.rb +148 -0
  69. data/lib/lebowski/scui/views/linkit.rb +36 -0
  70. data/lib/lebowski/spec.rb +17 -0
  71. data/lib/lebowski/spec/core.rb +21 -0
  72. data/lib/lebowski/spec/matchers/be.rb +63 -0
  73. data/lib/lebowski/spec/matchers/has.rb +40 -0
  74. data/lib/lebowski/spec/matchers/match_supporters/has_object_function.rb +67 -0
  75. data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_no_prefix.rb +50 -0
  76. data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_prefix_has.rb +50 -0
  77. data/lib/lebowski/spec/matchers/match_supporters/match_supporter.rb +29 -0
  78. data/lib/lebowski/spec/matchers/method_missing.rb +24 -0
  79. data/lib/lebowski/spec/operators/operator.rb +20 -0
  80. data/lib/lebowski/spec/operators/that.rb +116 -0
  81. data/lib/lebowski/spec/util.rb +26 -0
  82. data/lib/lebowski/version.rb +17 -0
  83. data/resources/selenium-server.jar +0 -0
  84. data/resources/user-extensions.js +1421 -0
  85. metadata +198 -0
@@ -0,0 +1,302 @@
1
+ module Lebowski
2
+ module Foundation
3
+ module Mixins
4
+
5
+ #
6
+ # Mixin containing a set of commonly performed user actions. Mix this into any
7
+ # class that is able to perform these set of actions.
8
+ #
9
+ module UserActions
10
+ include PositionedElement
11
+ include KeyCheck
12
+ include StallSupport
13
+
14
+ def mouse_move()
15
+ @driver.sc_mouse_move action_target, *action_locator_args
16
+ stall :mouse_move
17
+ end
18
+
19
+ def mouse_move_at(x, y)
20
+ @driver.sc_mouse_move_at action_target, x, y, *action_locator_args
21
+ stall :mouse_move
22
+ end
23
+
24
+ def mouse_up_at(x, y)
25
+ @driver.sc_mouse_up_at action_target, x, y, *action_locator_args
26
+ stall :mouse_up
27
+ end
28
+
29
+ def mouse_down_at(x, y)
30
+ @driver.sc_mouse_down_at action_target, x, y, *action_locator_args
31
+ stall :mouse_down
32
+ end
33
+
34
+ #
35
+ # Used to perform a mouse up on this view in the remote application
36
+ #
37
+ def mouse_up()
38
+ @driver.sc_mouse_up action_target, *action_locator_args
39
+ stall :mouse_up
40
+ end
41
+
42
+ #
43
+ # Used to perform a mouse down on this view in the remote application
44
+ #
45
+ def mouse_down()
46
+ @driver.sc_mouse_down action_target, *action_locator_args
47
+ stall :mouse_down
48
+ end
49
+
50
+ #
51
+ # Used to perform a single click on this view in the remote application
52
+ #
53
+ def click()
54
+ mouse_down
55
+ mouse_up
56
+ stall :click
57
+ end
58
+
59
+ def click_at(x, y)
60
+ mouse_down_at x, y
61
+ mouse_up_at x, y
62
+ stall :click
63
+ end
64
+
65
+ #
66
+ # Used to perform a single basic click on this view in the remote application
67
+ #
68
+ def basic_click()
69
+ @driver.sc_basic_click action_target, *action_locator_args
70
+ stall :click
71
+ end
72
+
73
+ #
74
+ # Used to perform a double click on this view in the remote application
75
+ #
76
+ def double_click()
77
+ @driver.sc_double_click action_target, *action_locator_args
78
+ stall :double_click
79
+ end
80
+
81
+ #
82
+ # Used to perform a mouse up with right button on this view in the remote application
83
+ #
84
+ def right_mouse_up()
85
+ @driver.sc_right_mouse_up action_target, *action_locator_args
86
+ stall :right_mouse_up
87
+ end
88
+
89
+ #
90
+ # Used to perform a mouse down with right button on this view in the remote application
91
+ #
92
+ def right_mouse_down()
93
+ @driver.sc_right_mouse_down action_target, *action_locator_args
94
+ stall :right_mouse_down
95
+ end
96
+
97
+ def right_mouse_up_at(x, y)
98
+ @driver.sc_right_mouse_up_at action_target, x, y, *action_locator_args
99
+ stall :right_mouse_up
100
+ end
101
+
102
+ def right_mouse_down_at(x, y)
103
+ @driver.sc_right_mouse_down_at action_target, x, y, *action_locator_args
104
+ stall :right_mouse_down
105
+ end
106
+
107
+ #
108
+ # Used to perform a single right click on this view in the remote application
109
+ #
110
+ def right_click()
111
+ right_mouse_down_at 0, 0
112
+ right_mouse_up_at 0, 0
113
+ stall :right_click
114
+ end
115
+
116
+ def right_click_at(x, y)
117
+ right_mouse_down_at x, y
118
+ right_mouse_up_at x, y
119
+ stall :right_click
120
+ end
121
+
122
+ #
123
+ # Used to perform a key down on this view in the remote application
124
+ #
125
+ # You can either type a printable character or a function key. If you want to type a printable
126
+ # character then the 'key' parameter just has to be a string, such as 'a'. If you want to type
127
+ # a function key such as F1, then the 'key' parameter must be the corresponding symbol.
128
+ #
129
+ # Example:
130
+ #
131
+ # view.key_down 'a' # key down for printable character 'a'
132
+ # view.key_down :delete # key down for function key delete
133
+ # view.key_down :meta_key # key down for the meta key
134
+ #
135
+ def key_down(key)
136
+ focus
137
+ @driver.sc_key_down action_target, key, *action_locator_args
138
+ stall :key_down
139
+ end
140
+
141
+ #
142
+ # Used to perform a key up on this view in the remote application
143
+ #
144
+ # You can either type a printable character or a function key. If you want to type a printable
145
+ # character then the 'key' parameter just has to be a string, such as 'a'. If you want to type
146
+ # a function key such as F1, then the 'key' parameter must be the corresponding symbol.
147
+ #
148
+ # Example:
149
+ #
150
+ # view.key_up 'a' # key up for printable character 'a'
151
+ # view.key_up :delete # key up for function key delete
152
+ # view.key_up :meta_key # key up for the meta key
153
+ #
154
+ def key_up(key)
155
+ focus
156
+ @driver.sc_key_up action_target, key, *action_locator_args
157
+ stall :key_up
158
+ end
159
+
160
+ #
161
+ # Used to type a key on this view in the remote application. This will cause a key down followed
162
+ # by a key up
163
+ #
164
+ # You can either type a printable character or a function key. If you want to type a printable
165
+ # character then the 'key' parameter just has to be a string, such as 'a'. If you want to type
166
+ # a function key such as F1, then the 'key' parameter must be the corresponding symbol.
167
+ #
168
+ # Example:
169
+ #
170
+ # view.type_key 'a' # type printable character 'a'
171
+ # view.type_key :delete # type function key delete
172
+ #
173
+ def type_key(key)
174
+ focus
175
+ @driver.sc_type_key action_target, key, *action_locator_args
176
+ stall :type_key
177
+ end
178
+
179
+ def type(text)
180
+ focus
181
+ @driver.sc_type action_target, text, *action_locator_args
182
+ stall :type_key
183
+ end
184
+
185
+ def focus()
186
+ @driver.sc_focus action_target, *action_locator_args
187
+ end
188
+
189
+ def drag(x, y, relative_to=nil)
190
+ if (not x.kind_of? Integer) or (not y.kind_of? Integer)
191
+ raise ArgumentError.new "Must supply valid x-y coordinates: x = #{x}, y = #{y}"
192
+ end
193
+
194
+ relative_to = relative_to.kind_of?(Hash) ? relative_to[:relative_to] : relative_to
195
+
196
+ self.scroll_to_visible
197
+ mouse_down_at 0, 0
198
+ mouse_move_at 0, 0
199
+ relative_to.scroll_to_visible if relative_to.kind_of?(PositionedElement)
200
+
201
+ rel_x = 0
202
+ rel_y = 0
203
+
204
+ if not relative_to.nil?
205
+ position = self.position
206
+ rel_x = rel_x + position.x * -1
207
+ rel_y = rel_y + position.y * -1
208
+ if relative_to.kind_of? PositionedElement
209
+ position = relative_to.position
210
+ rel_x = rel_x + position.x
211
+ rel_y = rel_y + position.y
212
+ elsif relative_to == :window
213
+ else
214
+ raise ArgumentError.new "relative to source must be a positioned element: #{relative_to.class}"
215
+ end
216
+ end
217
+
218
+ rel_x = rel_x + x
219
+ rel_y = rel_y + y
220
+
221
+ mouse_move_at rel_x, rel_y
222
+ mouse_up_at rel_x, rel_y
223
+
224
+ stall :drag
225
+ end
226
+
227
+ def drag_to(source, offset_x=nil, offset_y=nil)
228
+ if not (source.kind_of? PositionedElement or source == :window)
229
+ raise ArgumentError.new "source must be an positioned element: #{source.class}"
230
+ end
231
+
232
+ offset_x = offset_x.nil? ? 0 : offset_x
233
+ offset_y = offset_y.nil? ? 0 : offset_y
234
+
235
+ drag offset_x, offset_y, source
236
+ end
237
+
238
+ def drag_on_to(source)
239
+ drag_to source, 1, 1
240
+ end
241
+
242
+ def drag_before(item)
243
+ assert_item_has_collection_item_view_support(item, 'item')
244
+ return if not item.can_drag_before?
245
+ item.apply_drag_before self
246
+ end
247
+
248
+ def drag_after(item)
249
+ assert_item_has_collection_item_view_support(item, 'item')
250
+ return if not item.can_drag_after?
251
+ item.apply_drag_after self
252
+ end
253
+
254
+ def drag_to_start_of(view)
255
+ assert_is_collection_view(view, 'view');
256
+ return if not view.can_drag_to_start_of?
257
+ view.apply_drag_to_start_of self
258
+ end
259
+
260
+ def drag_to_end_of(view)
261
+ assert_is_collection_view(view, 'view');
262
+ return if not view.can_drag_to_end_of?
263
+ view.apply_drag_to_end_of self
264
+ end
265
+
266
+ protected
267
+
268
+ #
269
+ # Override this to supply the target, which can either be one of the following:
270
+ #
271
+ # :view
272
+ # :core_query_element
273
+ #
274
+ def action_target()
275
+
276
+ end
277
+
278
+ #
279
+ # Override this to supply the arguments to generate the locator
280
+ #
281
+ def action_locator_args()
282
+
283
+ end
284
+
285
+ private
286
+
287
+ def assert_is_collection_view(value, name)
288
+ if not value.kind_of? Lebowski::Foundation::Views::CollectionView
289
+ raise ArgumentInvalidTypeError.new name, value, Lebowski::Foundation::Views::CollectionView
290
+ end
291
+ end
292
+
293
+ def assert_item_has_collection_item_view_support(value, name)
294
+ if not value.respond_to? :has_collection_item_view_support
295
+ raise ArgumentError.new "#{name} must have collection item view support (#{CollectionItemViewSupport})"
296
+ end
297
+ end
298
+
299
+ end
300
+ end
301
+ end
302
+ end
@@ -0,0 +1,44 @@
1
+ module Lebowski
2
+ module Foundation
3
+
4
+ module Mixins
5
+
6
+ #
7
+ # Mixin provides actions to wait on something. By waiting no further action
8
+ # will be taken until a condition has been met or a timeout has been reached.
9
+ #
10
+ module WaitActions
11
+ include Lebowski::Foundation
12
+
13
+ DEFAULT_TIMEOUT = 10 # seconds
14
+
15
+ def wait_until(timeout=nil, &block)
16
+ timeout = timeout.nil? ? DEFAULT_TIMEOUT : timeout
17
+
18
+ if not timeout.kind_of? Integer or timeout <= 0
19
+ raise ArgumentError.new "Must supply a valid timeout that is greater than 0"
20
+ end
21
+
22
+ if not block_given?
23
+ raise ArgumentError.new "Must supply a block"
24
+ end
25
+
26
+ start_time = Time.now
27
+ current_time = Time.now
28
+
29
+ while (current_time - start_time) < timeout
30
+ result = yield self
31
+ return if (result == true)
32
+ sleep 0.5
33
+ current_time = Time.now
34
+ end
35
+
36
+ raise TimeoutError.new "Timed out after #{timeout} seconds"
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,305 @@
1
+ # ==========================================================================
2
+ # Project: Lebowski Framework - The SproutCore Test Automation Framework
3
+ # License: Licensed under MIT license (see License.txt)
4
+ # ==========================================================================
5
+
6
+ module Lebowski
7
+ module Foundation
8
+
9
+ #
10
+ # Represents an array of proxied objects. Use this for any proxy object that has a property
11
+ # that is an array of objects within the browser. Provides a set of common functionality to
12
+ # check and access proxied objects within the array.
13
+ #
14
+ # Many of the methods for the class accept a filter as input. The filter can either be a
15
+ # type of SproutCore object or a hash made of properties with values that must be matched.
16
+ # For instance, let's say that you want to find all the objects that are of the type
17
+ # 'SC.Foo'. You would do the following:
18
+ #
19
+ # found = obj.object_array.find_all('SC.Foo')
20
+ #
21
+ # Only those objects in the array that are of type SC.Foo will be returned. As another
22
+ # example, we want to find all the objects in the array that have a property called
23
+ # "foo" have a matching value of "bar". You would do the following:
24
+ #
25
+ # found = obj.object_array.find_all({ :foo => 'bar' })
26
+ #
27
+ # Only those objects that have the property foo with value 'bar' will be returned. Instead
28
+ # of just a string value, you can also provided a regular expression, like so:
29
+ #
30
+ # found = obj.object_array.find_all({ :foo => /bar/i })
31
+ #
32
+ # You are not limited to just one key-value pair; you can have as many as you like:
33
+ #
34
+ # found = object.object_array.find_all({ :foo => /bar/i, :title => 'hello' })
35
+ #
36
+ # Only objects that satisfy all of the filter's conditions will be returned.
37
+ #
38
+ class ObjectArray
39
+ include Lebowski::Foundation
40
+
41
+ def initialize(parent, array_rel_path, array_length_property_name=nil, *params)
42
+ init_validate_parent parent
43
+
44
+ @parent = parent
45
+ @array_rel_path = array_rel_path
46
+ @array_length_property_name = array_length_property_name.nil? ? 'length' : array_length_property_name
47
+ @driver = parent.driver
48
+ @prefilter = {}
49
+
50
+ if not params.empty?
51
+ if params[0].kind_of?(Hash) and params[0].has_key?(:prefilter)
52
+ @prefilter = params[0][:prefilter]
53
+ else
54
+ @prefilter = params[0]
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ def prefilter()
61
+ return @prefilter.clone
62
+ end
63
+
64
+ def filter(filter)
65
+ merged_filter = merge_filter_with_prefilter(filter)
66
+ return create_filtered_object_array(@parent, @array_rel_path, @array_length_property_name, merged_filter)
67
+ end
68
+
69
+ def find_indexes(filter=nil)
70
+ raise ArgumentError.new "No filter was supplied" if (filter.nil? and @prefilter.empty?)
71
+
72
+ if filter.nil?
73
+ filter = {}
74
+ elsif filter.kind_of? String
75
+ filter = { :sc_type => filter }
76
+ elsif filter.kind_of?(Class) and filter.ancestors.member?(SCObject)
77
+ filter = { :sc_type => filter.represented_sc_class }
78
+ elsif filter.kind_of?(Hash)
79
+ if filter.has_key? :sc_type
80
+ type = filter[:sc_type]
81
+ if type.kind_of?(Class) and type.ancestors.member?(SCObject)
82
+ filter[:sc_type] = type.represented_sc_class
83
+ end
84
+ end
85
+ else
86
+ raise ArgumentInvalidTypeError.new "filter", filter, 'class < SCObject', String, Hash
87
+ end
88
+
89
+ filter = merge_filter_with_prefilter(filter)
90
+
91
+ processed_filter = find_indexes_process_filter(filter)
92
+ raise StandardError.new "process filter can not be nil" if processed_filter.nil?
93
+
94
+ if not processed_filter.empty?
95
+ sc_path = @parent.abs_path_with(@array_rel_path)
96
+ indexes = @driver.get_sc_object_array_index_lookup(sc_path, processed_filter)
97
+ return indexes if indexes.empty?
98
+ else
99
+ val = unfiltered_count
100
+ return [] if val <= 0
101
+ indexes = (0..(val - 1))
102
+ end
103
+
104
+ processed_indexes = find_indexes_process_indexes(indexes)
105
+ raise StandardError.new "process indexes can not be nil" if processed_indexes.nil?
106
+
107
+ return processed_indexes
108
+
109
+ end
110
+
111
+ #
112
+ # Returns the number of child views this view has
113
+ #
114
+ def count(filter=nil, &block)
115
+ if @prefilter.empty? and filter.nil? and (not block_given?)
116
+ return unfiltered_count
117
+ elsif not block_given?
118
+ return find_indexes(filter).length
119
+ end
120
+
121
+ counter = 0
122
+ each filter do |view, index|
123
+ result = yield view, index
124
+ counter = counter.next if (result == true)
125
+ end
126
+ return counter
127
+ end
128
+
129
+ def [](index, expected_type=nil)
130
+ error = ArgumentError.new "index is out of bounds: #{index}"
131
+
132
+ if (not index.kind_of? Integer) or index < 0
133
+ raise error
134
+ end
135
+
136
+ if @prefilter.empty?
137
+ raise error if (index >= count)
138
+ return create_object(index, expected_type)
139
+ else
140
+ indexes = find_indexes
141
+ raise error if (index >= indexes.length)
142
+ return create_object(indexes[index], expected_type)
143
+ end
144
+ end
145
+
146
+ def first(expected_type=nil)
147
+ return self[0, expected_type]
148
+ end
149
+
150
+ def last(expected_type=nil)
151
+ return self[count - 1, expected_type]
152
+ end
153
+
154
+ def each(filter=nil, &block)
155
+ raise ArgumentError.new "block is required" if (not block_given?)
156
+
157
+ indexes = []
158
+ if @prefilter.empty? and filter.nil?
159
+ value = count
160
+ return if (value == 0)
161
+ indexes = (0..(value - 1))
162
+
163
+ indexes.each do |index|
164
+ yield create_object(index), index
165
+ end
166
+ else
167
+ indexes = find_indexes(filter)
168
+ return if indexes.empty?
169
+
170
+ (0..(indexes.length - 1)).each do |i|
171
+ yield create_object(indexes[i]), i
172
+ end
173
+ end
174
+ end
175
+
176
+ alias_method :each_with_index, :each
177
+
178
+ def find_all(filter=nil, &block)
179
+ if filter.nil? and (not block_given?)
180
+ raise ArugmentError.new "Must provide at least a filter or a block"
181
+ end
182
+
183
+ collected = []
184
+ each filter do |view, index|
185
+ if block_given?
186
+ result = yield view, index
187
+ collected << result if not result.nil?
188
+ else
189
+ collected << view
190
+ end
191
+ end
192
+ return collected
193
+ end
194
+
195
+ def find_first(filter, expected_type=nil)
196
+ if filter.nil?
197
+ raise ArgumentError.new "filter can not be nil"
198
+ end
199
+
200
+ indexes = find_indexes(filter)
201
+ return nil if indexes.empty?
202
+ return create_object(indexes[0], expected_type)
203
+ end
204
+
205
+ def find_last(filter, expected_type=nil)
206
+ if filter.nil?
207
+ raise ArgumentError.new "filter can not be nil"
208
+ end
209
+
210
+ indexes = find_indexes(filter)
211
+ return nil if indexes.empty?
212
+ return create_object(indexes[indexes.length - 1], expected_type)
213
+ end
214
+
215
+ def index_of(obj)
216
+ return -1 if (not obj.kind_of? ProxyObject)
217
+ indexes = find_indexes({ :sc_guid => obj.sc_guid })
218
+ return indexes.empty? ? -1 : indexes[0]
219
+ end
220
+
221
+ def member?(obj)
222
+ return (index_of(obj) >= 0)
223
+ end
224
+
225
+ def all?(filter=nil, &block)
226
+ return (count(filter, &block) == count)
227
+ end
228
+
229
+ def any?(filter=nil, &block)
230
+ return (count(filter, &block) > 0)
231
+ end
232
+
233
+ alias_method :some?, :any?
234
+
235
+ def none?(filter=nil, &block)
236
+ return (count(filter, &block) == 0)
237
+ end
238
+
239
+ def one?(filter=nil, &block)
240
+ return (count(filter, &block) == 1)
241
+ end
242
+
243
+ def empty?()
244
+ return (count == 0)
245
+ end
246
+
247
+ protected
248
+
249
+ def create_object(index, expected_type=nil)
250
+ rel_path = "#{@array_rel_path}.#{index}"
251
+ return @parent[rel_path, expected_type]
252
+ end
253
+
254
+ def create_filtered_object_array(parent, array_rel_path, array_length_property_name, prefilter)
255
+ klass = self.class
256
+ return klass.new parent, array_rel_path, array_length_property_name, prefilter
257
+ end
258
+
259
+ def init_validate_parent(parent)
260
+ if not parent.kind_of? ProxyObject
261
+ raise ArgumentInvalidTypeError.new "parent", parent, ProxyObject
262
+ end
263
+ end
264
+
265
+ def find_indexes_process_filter(filter)
266
+ return filter
267
+ end
268
+
269
+ def find_indexes_process_indexes(indexes)
270
+ return indexes
271
+ end
272
+
273
+ private
274
+
275
+ def unfiltered_count()
276
+ return @parent["#{@array_rel_path}.#{@array_length_property_name}"]
277
+ end
278
+
279
+ def merge_filter_with_prefilter(filter)
280
+
281
+ if filter.kind_of? String
282
+ filter = { :sc_type => filter }
283
+ elsif filter.kind_of?(Class) and filter.ancestors.member?(SCObject)
284
+ filter = { :sc_type => filter.represented_sc_class }
285
+ elsif not filter.kind_of?(Hash)
286
+ raise ArgumentInvalidTypeError.new "filter", filter, Hash
287
+ end
288
+
289
+ merged_filter = {}
290
+
291
+ @prefilter.each do |key, value|
292
+ merged_filter[key] = value
293
+ end
294
+
295
+ filter.each do |key, value|
296
+ merged_filter[key] = value if (not merged_filter.has_key? key)
297
+ end
298
+
299
+ return merged_filter
300
+ end
301
+
302
+ end
303
+
304
+ end
305
+ end