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.
- data/History.md +3 -0
- data/License.txt +20 -0
- data/Manifest.txt +84 -0
- data/README.md +146 -0
- data/Rakefile +42 -0
- data/bin/lebowski +26 -0
- data/bin/lebowski-spec +29 -0
- data/bin/lebowski-start-server +24 -0
- data/lib/lebowski.rb +15 -0
- data/lib/lebowski/core.rb +35 -0
- data/lib/lebowski/foundation.rb +52 -0
- data/lib/lebowski/foundation/application.rb +315 -0
- data/lib/lebowski/foundation/core.rb +61 -0
- data/lib/lebowski/foundation/core_query.rb +231 -0
- data/lib/lebowski/foundation/dom_element.rb +114 -0
- data/lib/lebowski/foundation/errors/argument_invalid_type.rb +31 -0
- data/lib/lebowski/foundation/errors/unexpected_type.rb +30 -0
- data/lib/lebowski/foundation/mixins/collection_item_view_support.rb +87 -0
- data/lib/lebowski/foundation/mixins/delegate_support.rb +22 -0
- data/lib/lebowski/foundation/mixins/inline_text_field_support.rb +29 -0
- data/lib/lebowski/foundation/mixins/key_check.rb +35 -0
- data/lib/lebowski/foundation/mixins/list_item_view_support.rb +36 -0
- data/lib/lebowski/foundation/mixins/positioned_element.rb +20 -0
- data/lib/lebowski/foundation/mixins/stall_support.rb +79 -0
- data/lib/lebowski/foundation/mixins/user_actions.rb +302 -0
- data/lib/lebowski/foundation/mixins/wait_actions.rb +44 -0
- data/lib/lebowski/foundation/object_array.rb +305 -0
- data/lib/lebowski/foundation/panes/alert.rb +117 -0
- data/lib/lebowski/foundation/panes/main.rb +20 -0
- data/lib/lebowski/foundation/panes/menu.rb +100 -0
- data/lib/lebowski/foundation/panes/modal.rb +21 -0
- data/lib/lebowski/foundation/panes/palette.rb +21 -0
- data/lib/lebowski/foundation/panes/pane.rb +24 -0
- data/lib/lebowski/foundation/panes/panel.rb +25 -0
- data/lib/lebowski/foundation/panes/picker.rb +43 -0
- data/lib/lebowski/foundation/panes/sheet.rb +21 -0
- data/lib/lebowski/foundation/proxy_factory.rb +87 -0
- data/lib/lebowski/foundation/proxy_object.rb +670 -0
- data/lib/lebowski/foundation/sc_object.rb +38 -0
- data/lib/lebowski/foundation/views/button.rb +20 -0
- data/lib/lebowski/foundation/views/checkbox.rb +63 -0
- data/lib/lebowski/foundation/views/collection.rb +304 -0
- data/lib/lebowski/foundation/views/container.rb +32 -0
- data/lib/lebowski/foundation/views/disclosure.rb +59 -0
- data/lib/lebowski/foundation/views/grid.rb +21 -0
- data/lib/lebowski/foundation/views/label.rb +30 -0
- data/lib/lebowski/foundation/views/list.rb +67 -0
- data/lib/lebowski/foundation/views/list_item.rb +280 -0
- data/lib/lebowski/foundation/views/menu_item.rb +27 -0
- data/lib/lebowski/foundation/views/radio.rb +32 -0
- data/lib/lebowski/foundation/views/segmented.rb +97 -0
- data/lib/lebowski/foundation/views/select_field.rb +139 -0
- data/lib/lebowski/foundation/views/support/simple_item_array.rb +249 -0
- data/lib/lebowski/foundation/views/text_field.rb +108 -0
- data/lib/lebowski/foundation/views/view.rb +108 -0
- data/lib/lebowski/runtime.rb +7 -0
- data/lib/lebowski/runtime/errors/remote_control_command_execution_error.rb +9 -0
- data/lib/lebowski/runtime/errors/remote_control_command_timeout_error.rb +9 -0
- data/lib/lebowski/runtime/errors/remote_control_error.rb +9 -0
- data/lib/lebowski/runtime/errors/selenium_server_error.rb +9 -0
- data/lib/lebowski/runtime/object_encoder.rb +123 -0
- data/lib/lebowski/runtime/sprout_core_driver.rb +14 -0
- data/lib/lebowski/runtime/sprout_core_extensions.rb +600 -0
- data/lib/lebowski/scui.rb +18 -0
- data/lib/lebowski/scui/mixins/node_item_view_support.rb +136 -0
- data/lib/lebowski/scui/mixins/terminal_view_support.rb +25 -0
- data/lib/lebowski/scui/views/combo_box.rb +119 -0
- data/lib/lebowski/scui/views/date_picker.rb +148 -0
- data/lib/lebowski/scui/views/linkit.rb +36 -0
- data/lib/lebowski/spec.rb +17 -0
- data/lib/lebowski/spec/core.rb +21 -0
- data/lib/lebowski/spec/matchers/be.rb +63 -0
- data/lib/lebowski/spec/matchers/has.rb +40 -0
- data/lib/lebowski/spec/matchers/match_supporters/has_object_function.rb +67 -0
- data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_no_prefix.rb +50 -0
- data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_prefix_has.rb +50 -0
- data/lib/lebowski/spec/matchers/match_supporters/match_supporter.rb +29 -0
- data/lib/lebowski/spec/matchers/method_missing.rb +24 -0
- data/lib/lebowski/spec/operators/operator.rb +20 -0
- data/lib/lebowski/spec/operators/that.rb +116 -0
- data/lib/lebowski/spec/util.rb +26 -0
- data/lib/lebowski/version.rb +17 -0
- data/resources/selenium-server.jar +0 -0
- data/resources/user-extensions.js +1421 -0
- 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
|