lebowski 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,670 @@
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
+ # ProxyObject is the root object for all objects that are to proxy an object within a
11
+ # web brower. This provides all of the core functionality allowing you to communicate with
12
+ # a remote object and access other remote objects through the use of relative property paths.
13
+ #
14
+ # In the case where you have created a custom SproutCore object and want to have a proxy for it
15
+ # then your proxy must inherit from the SCObject class that inherits this class.
16
+ #
17
+ class ProxyObject
18
+
19
+ include Lebowski::Foundation
20
+ include Lebowski::Foundation::Mixins::WaitActions
21
+
22
+ attr_reader :parent, # The parent object of this object. Must derive from Lebowski::Foundation::ProxyObject
23
+ :rel_path, # The relative path to the remote object using SC property path notation
24
+ :driver # The SproutCore driver that is used to actually communicate with the remote object
25
+
26
+ attr_accessor :name # A name for this object. Useful for printing out statements and debugging
27
+
28
+ #
29
+ # Creates a new proxy object instance.
30
+ #
31
+ # Try to refrain from overriding this method. Instead, if you wish to perform some operations
32
+ # during the time an object is being initialized, override the init_ext method
33
+ #
34
+ # @param parent {Object} parent object of this object. Must inherit from Lebowski::Foundation::ProxyObject
35
+ # @param rel_path {String} a relative path to the remote object (e.g. 'foo', 'foo.bar')
36
+ # @param driver {Object} used to remotely communicate with the remote object.
37
+ #
38
+ # @see #init_ext
39
+ #
40
+ def initialize(parent=nil, rel_path=nil, driver=nil)
41
+
42
+ if not init_expected_parent_type.nil?
43
+ if not parent.kind_of? init_expected_parent_type
44
+ raise ArgumentInvalidTypeError.new "parent", parent, init_expected_parent_type
45
+ end
46
+ end
47
+
48
+ @parent = parent
49
+ @rel_path = rel_path
50
+ @driver = driver
51
+ @guid = nil
52
+ @defined_paths = {}
53
+ @defined_proxies = {}
54
+ @name = ""
55
+
56
+ init_ext()
57
+ end
58
+
59
+ #
60
+ # Override this method for any initialization procedures during the time the object is being
61
+ # inialized
62
+ #
63
+ # @see #initialize
64
+ #
65
+ def init_ext()
66
+
67
+ end
68
+
69
+ #
70
+ # Use when you wish to represent a proxied object as something else other then the proxy
71
+ # returned by this object when accessed with a relative path using []. This is useful
72
+ # in cases where you have a view that is simply composed of other views but itself is not
73
+ # custom view inherited from SC.View. As an example, it is common in SproutCore to
74
+ # create a complex view that lives only within an SC.Page, like so:
75
+ #
76
+ # MyApp.mainPage = SC.Page.create({
77
+ #
78
+ # composedView: SC.View.design({
79
+ # layout: { top: 0, bottom: 0, left: 0, right: 0 },
80
+ # childViews: 'buttonOne buttonTwo statusLabel'.w(),
81
+ #
82
+ # buttonOne: SC.ButtonView.design({
83
+ # ...
84
+ # }),
85
+ #
86
+ # buttonTwo: SC.ButtonView.design({
87
+ # ...
88
+ # }),
89
+ #
90
+ # statusLabel: SC.LabelView.design({
91
+ # ...
92
+ # })
93
+ # })
94
+ #
95
+ # })
96
+ #
97
+ # Since the root view (composedView) is just a basic SC.View, accessing it using
98
+ # the proxy's [] convention would just give you back a basic View proxy. This then
99
+ # means you have to access the child views explicitly every time you want to interact
100
+ # with the composed view. This can be brittle since the view's structure can change.
101
+ # Instead you can make a proxy to abstract away the interal structure and make it
102
+ # easier to work with the view. Therefore, we could make a proxy as follows:
103
+ #
104
+ # ComposedView < Lebowski::Foundation::Views::View
105
+ #
106
+ # def click_button_one()
107
+ # self['buttonOne'].click
108
+ # end
109
+ #
110
+ # def click_button_two()
111
+ # self['buttonTwo'].click
112
+ # end
113
+ #
114
+ # def status()
115
+ # return self['statusLabel.value']
116
+ # end
117
+ #
118
+ # end
119
+ #
120
+ # With the proxy above, you can then do the following:
121
+ #
122
+ # view = App['mainPage.composedView', View]
123
+ # view = view.represent_as(ComposedView)
124
+ # view.click_button_one
125
+ # status = view.status
126
+ #
127
+ def represent_as(type)
128
+ if not (type.kind_of?(Class) and type.ancestors.member?(ProxyObject))
129
+ raise ArgumentInvalidTypeError.new "type", type, 'class < ProxyObject'
130
+ end
131
+
132
+ obj = type.new @parent, @rel_path, @driver
133
+ return obj
134
+ end
135
+
136
+ #
137
+ # Returns the absolute path of this object based on the parent object heirarchy. As
138
+ # an example, this object's parent has an absolute path of 'foo' and this object has
139
+ # relative path of 'bar', then the absolute path will be 'foo.bar'
140
+ #
141
+ def abs_path()
142
+ if not @abs_path.nil?
143
+ return @abs_path
144
+ end
145
+
146
+ if @parent.nil? or @parent.abs_path.nil?
147
+ return @rel_path
148
+ end
149
+
150
+ @abs_path = "#{@parent.abs_path}.#{rel_path}"
151
+
152
+ return @abs_path
153
+ end
154
+
155
+ #
156
+ # Returns the absolute path given a relative path. Say, for example, that a
157
+ # proxy object has an absolute path of 'mainPage.mainPane.someView'. When
158
+ # given a relative path of 'foo.bar', the returned value would be:
159
+ #
160
+ # 'mainPage.mainPane.someView.foo.bar'
161
+ #
162
+ def abs_path_with(rel_path)
163
+ path = abs_path
164
+ return rel_path if path.nil?
165
+ return "#{path}.#{rel_path}"
166
+ end
167
+
168
+ def define(key, rel_path, expected_type=nil)
169
+ if (not key.kind_of?(String)) or key.empty? or (not key.match(/[\. ]/).nil?)
170
+ raise ArgumentError.raise "key must be a valid string"
171
+ end
172
+
173
+ if (not rel_path.kind_of?(String)) or rel_path.empty?
174
+ raise ArgumentError.raise "rel_path must be a valid string"
175
+ end
176
+
177
+ if @defined_paths.has_key? key
178
+ raise ArgumentError.raise "key '#{key}' already defined as path '#{@defined_paths[key]}'"
179
+ end
180
+
181
+ first_path_part = rel_path_first_part(rel_path)
182
+ sub_path = rel_path_sub_path(rel_path, first_path_part)
183
+
184
+ type = ""
185
+ err_abs_path = abs_path_with(rel_path)
186
+
187
+ if @defined_paths.has_key? first_path_part
188
+ obj = @defined_paths[first_path_part]
189
+ type = obj.sc_type_of(sub_path)
190
+ err_abs_path = obj.abs_path_with(sub_path)
191
+ else
192
+ type = sc_type_of(rel_path)
193
+ end
194
+
195
+ if not (type == SC_T_OBJECT or type == SC_T_HASH)
196
+ err_msg = "Error trying to define key '#{key}'. "
197
+ err_msg << "Relative path '#{rel_path}' does not point to an object. "
198
+ err_msg << "Path is refencing: #{type}. Absolute path = #{err_abs_path}"
199
+ raise ArgumentError.new err_msg
200
+ end
201
+
202
+ obj = self[rel_path, expected_type]
203
+
204
+ @defined_paths[key] = obj
205
+
206
+ return obj
207
+ end
208
+
209
+ def defined()
210
+ return @defined_paths.clone if (not @defined_paths.nil?)
211
+ return {}
212
+ end
213
+
214
+ def proxy(klass, rel_path)
215
+ obj = self[rel_path]
216
+ if not obj.kind_of?(ProxyObject)
217
+ raise ArgumentError.new "rel_path does not point to an object that can be proxied: #{obj} (#{obj.class})"
218
+ end
219
+
220
+ if not (klass.kind_of?(Class) and klass.ancestors.member?(ProxyObject))
221
+ raise ArgumentInvalidTypeError.new "klass", klass, 'class < ProxyObject'
222
+ end
223
+
224
+ @defined_proxies[rel_path] = obj.represent_as(klass)
225
+ end
226
+
227
+ #
228
+ # Given a relative path, unravel it to access an object. Unraveling means to take
229
+ # any defined paths in the given relative path and convert the entire path back
230
+ # into a full relative path without definitions.
231
+ #
232
+ def unravel_relative_path(rel_path)
233
+ if not @defined_proxies[rel_path].nil?
234
+ return @defined_proxies[rel_path]
235
+ end
236
+
237
+ first_path_part = rel_path_first_part(rel_path)
238
+ sub_path = rel_path_sub_path(rel_path, first_path_part)
239
+
240
+ return rel_path if (not @defined_paths.has_key?(first_path_part))
241
+
242
+ obj = @defined_paths[first_path_part]
243
+ return obj if sub_path.empty?
244
+
245
+ result = obj.unravel_relative_path(sub_path)
246
+
247
+ return "#{obj.rel_path}.#{result}" if result.kind_of?(String)
248
+ return result if result.kind_of?(Lebowski::Foundation::SCObject)
249
+
250
+ raise StandardError.new "Unexpected result unreeling rel path: #{result}"
251
+ end
252
+
253
+ #
254
+ # Gets the remote SproutCore GUID for this object
255
+ #
256
+ def sc_guid()
257
+ # We only need to fetch the remote GUID once since it never changes for a given instance
258
+ @guid = @driver.get_sc_guid(abs_path) if @guid.nil?
259
+ return @guid
260
+ end
261
+
262
+ #
263
+ # Gets the remote SC class name for this object
264
+ #
265
+ def sc_class()
266
+ # We only need to fetch the remote SC class name once since it never changes for a given instance
267
+ @class_name = @driver.get_sc_object_class_name(abs_path) if @class_name.nil?
268
+ return @class_name
269
+ end
270
+
271
+ #
272
+ # Gets all the remote SproutCore classes that the proxied object derives from. This will return
273
+ # an array of strings representing the names of the classes. As an example, if the
274
+ # proxy was communicating with an object that was of type SC.ButtonView then the
275
+ # result would be the following:
276
+ #
277
+ # ['SC.ButtonView', 'SC.View', 'SC.Object']
278
+ #
279
+ # The last item in the array is always 'SC.Object' since that is the root object for all
280
+ # SproutCore objects.
281
+ #
282
+ def sc_all_classes()
283
+ @all_class_names = @driver.get_sc_object_class_names(abs_path) if @all_class_names.nil?
284
+ return @all_class_names
285
+ end
286
+
287
+ #
288
+ # Checks if the remote proxied object is a kind of given SC class
289
+ #
290
+ def sc_kind_of?(type)
291
+ if not (type.kind_of?(Class) or type.kind_of?(String))
292
+ raise ArgumentInvalidTypeError.new "type", type, 'class < SCObject', String
293
+ end
294
+
295
+ if type.kind_of?(Class) and type.ancestors.member?(SCObject)
296
+ type = type.represented_sc_class
297
+ end
298
+
299
+ type = type.downcase
300
+ result = sc_all_classes.detect do |val|
301
+ val.downcase == type
302
+ end
303
+ return (not result.nil?)
304
+ end
305
+
306
+ def sc_type_of(rel_path)
307
+ return @driver.get_sc_type_of(abs_path_with(rel_path))
308
+ end
309
+
310
+ def sc_path_defined?(rel_path)
311
+ return (not sc_type_of(rel_path) == SC_T_UNDEFINED)
312
+ end
313
+
314
+ def none?(rel_path)
315
+ type = sc_type_of(rel_path)
316
+ return (type == SC_T_UNDEFINED or type == SC_T_NULL)
317
+ end
318
+
319
+ def object?(rel_path)
320
+ type = sc_type_of(rel_path)
321
+ return (type == SC_T_OBJECT or type == SC_T_HASH)
322
+ end
323
+
324
+ #
325
+ # The primary method used to access a proxied object's properties. Accessing
326
+ # a property is done using a relative property path. The path is a chain of
327
+ # properties connected using dots '.'. The type is automatically determined, but
328
+ # in cases where a particular type is expected, you can optionally supply what the
329
+ # expected type should be.
330
+ #
331
+ # As an example, to access an object's property called 'foo', you can do the
332
+ # following:
333
+ #
334
+ # value = object['foo']
335
+ #
336
+ # If you expect the value's type to be, say, an number, you can do the following:
337
+ #
338
+ # value = object['foo', :number]
339
+ #
340
+ # In the case where you expect the value to be a type of object you can do one
341
+ # of the following:
342
+ #
343
+ # value = object['foo', 'SC.SomeObject']
344
+ #
345
+ # value = object['foo', SomeObject]
346
+ #
347
+ # In the first case, you are supply the object type as a string, in the second case
348
+ # you are supplying the expected type with a proxy class. For the second option to
349
+ # work you must first supply the proxy to the proxy factory.
350
+ #
351
+ # To access a property through a chain of objects, you supply a relative path, like
352
+ # so:
353
+ #
354
+ # value = object['path.to.some.property']
355
+ #
356
+ # Remember that the path is relative to the object you passed the path to. The approach
357
+ # is used to work with how you would normally access properties using the SproutCore
358
+ # framework.
359
+ #
360
+ # The fundamental types detected within the web browser are the following:
361
+ #
362
+ # Null - null in JavaScript
363
+ # Error - SproutCore error object
364
+ # String - string in JavaScript
365
+ # Number - number in JavaScript
366
+ # Boolean - boolean in JavaScript
367
+ # Hash - a JavaScript hash object
368
+ # Object - a SproutCore object
369
+ # Array - a JavaScript array
370
+ # Class - A SproutCore class
371
+ #
372
+ # Based on the value's type within the browser, this method will translate the value as
373
+ # follows:
374
+ #
375
+ # Null -> nil
376
+ # Error -> :error
377
+ # String -> standard string
378
+ # Number -> standard number
379
+ # Boolean -> standard boolean
380
+ # Hash -> a generic proxy object
381
+ # Object -> closest matching proxy object
382
+ # Array -> standard array for basic types; an object array (ObjectArray) for objects
383
+ # Class -> a generic proxy object
384
+ #
385
+ # If the given relative path tries to reference a property that is not defined then :undefined
386
+ # is returned.
387
+ #
388
+ # The two special cases are when the basic type of the relative path is a SproutCore object or
389
+ # an array. In the case of a SproutCore object, the closest matching object type will be returned
390
+ # based on what proxies have been provided to the proxy factory. For instance, let's say you have
391
+ # custom view that derives from SC.View. If no proxy has been made for the custom view then
392
+ # the next closest proxy will be returned, which would be a View proxy that is already part
393
+ # of the lebowski framework. If your require a proxy to interact with the custom view then you
394
+ # need to add that proxy to the proxy framework.
395
+ #
396
+ # When the type is an array, the proxy object will check the content of the array to determine
397
+ # their type. If all the content in the array are of the same type then it will return a
398
+ # corresponding array made up content with that type. So, for example, if an object has
399
+ # a property that is an array of strings then a basic array of string will be returned. In the
400
+ # case where the array contains either hash objects or SproutCore objects then an ObjectArray
401
+ # will be returned.
402
+ #
403
+ def [](rel_path, expected_type=nil)
404
+
405
+ result = unravel_relative_path(rel_path)
406
+
407
+ if (not result.kind_of?(String))
408
+ if (not expected_type.nil?)
409
+ got_expected_type = (expected_type == :object or result.sc_kind_of?(expected_type))
410
+ if (not got_expected_type)
411
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, "object", result)
412
+ end
413
+ end
414
+ return result
415
+ end
416
+
417
+ rel_path = result
418
+ type = sc_type_of(rel_path)
419
+
420
+ case type
421
+ when SC_T_NULL
422
+ return handle_type_null(rel_path, expected_type)
423
+
424
+ when SC_T_UNDEFINED
425
+ return handle_type_undefined(rel_path, expected_type)
426
+
427
+ when SC_T_ERROR
428
+ return handle_type_error(rel_path, expected_type)
429
+
430
+ when SC_T_STRING
431
+ return handle_type_string(rel_path, expected_type)
432
+
433
+ when SC_T_NUMBER
434
+ return handle_type_number(rel_path, expected_type)
435
+
436
+ when SC_T_BOOL
437
+ return handle_type_bool(rel_path, expected_type)
438
+
439
+ when SC_T_ARRAY
440
+ return handle_type_array(rel_path, expected_type)
441
+
442
+ when SC_T_HASH
443
+ return handle_type_hash(rel_path, expected_type)
444
+
445
+ when SC_T_OBJECT
446
+ return handle_type_object(rel_path, expected_type)
447
+
448
+ when SC_T_CLASS
449
+ return handle_type_class(rel_path, expected_type)
450
+
451
+ else
452
+ raise StandardError.new "Unrecognized returned type '#{type}' for path #{abs_path_with(rel_path)}"
453
+ end
454
+ end
455
+
456
+ #
457
+ # Override the == operator so that a proxy object can be compared to another
458
+ # proxy object via their SproutCore GUIDs
459
+ #
460
+ def ==(obj)
461
+ return (self.sc_guid == obj.sc_guid) if obj.kind_of?(ProxyObject)
462
+ return super(obj)
463
+ end
464
+
465
+ alias_method :eql?, :==
466
+
467
+ #
468
+ # Override method_missing so that we can access a proxied object's properties using
469
+ # a more conventional Ruby approach. So instead of accessing an object's property
470
+ # using the [] convention, we can instead do the following:
471
+ #
472
+ # value = proxied_object.foo # compared to proxied_object['foo']
473
+ #
474
+ # This will also translate the name of property into camel case that is normally
475
+ # used in JavaScript. So, if an object in JavaScript has a property with the
476
+ # name 'fooBar', you can access that property using the standard Ruby convention
477
+ # like so:
478
+ #
479
+ # value = proxied_object.foo_bar
480
+ #
481
+ # It will be converted back into 'fooBar'. If the property does not exist
482
+ # on the proxied object then an exception will be thrown. If you want to access
483
+ # property without an exception being thrown then use the [] convention using
484
+ # a relative property path string.
485
+ #
486
+ def method_missing(sym, *args, &block)
487
+ if (not sym.to_s =~ /\?$/) and (args.length == 0)
488
+ camel_case = to_camel_case(sym.to_s)
489
+ return self[camel_case] if sc_path_defined?(camel_case)
490
+ end
491
+ super
492
+ end
493
+
494
+ private
495
+
496
+ def init_expected_parent_type()
497
+ return nil
498
+ end
499
+
500
+ def rel_path_first_part(rel_path)
501
+ first_path_part = rel_path.match(/^((\w|-)*)\./)
502
+ first_path_part = rel_path if first_path_part.nil?
503
+ first_path_part = first_path_part[1] if first_path_part.kind_of?(MatchData)
504
+ return first_path_part
505
+ end
506
+
507
+ def rel_path_sub_path(rel_path, first_path_part)
508
+ sub_path = (first_path_part != rel_path) ? rel_path.sub(/^(\w|-)*\./, "") : ""
509
+ return sub_path
510
+ end
511
+
512
+ def handle_type_null(rel_path, expected_type)
513
+ if (not expected_type.nil?) and not (expected_type == :null)
514
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_NULL, :null)
515
+ end
516
+
517
+ return nil
518
+ end
519
+
520
+ def handle_type_undefined(rel_path, expected_type)
521
+ if (not expected_type.nil?) and not (expected_type == :undefined)
522
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_UNDEFINED, :undefined)
523
+ end
524
+
525
+ return :undefined
526
+ end
527
+
528
+ def handle_type_error(rel_path, expected_type)
529
+ if (not expected_type.nil?) and not (expected_type == :error)
530
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_ERROR, :error)
531
+ end
532
+
533
+ return :error
534
+ end
535
+
536
+ def handle_type_bool(rel_path, expected_type)
537
+ value = @driver.get_sc_path_boolean_value(abs_path_with(rel_path))
538
+
539
+ if (not expected_type.nil?) and not (expected_type == :boolean or expected_type == :bool)
540
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_BOOL, value)
541
+ end
542
+
543
+ return value
544
+ end
545
+
546
+ def handle_type_string(rel_path, expected_type)
547
+ value = @driver.get_sc_path_string_value(abs_path_with(rel_path))
548
+
549
+ if (not expected_type.nil?) and not (expected_type == :string)
550
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_STRING, value)
551
+ end
552
+
553
+ return value
554
+ end
555
+
556
+ def handle_type_number(rel_path, expected_type)
557
+ value = @driver.get_sc_path_number_value(abs_path_with(rel_path))
558
+
559
+ if (not expected_type.nil?) and not (expected_type == :number)
560
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_NUMBER, value)
561
+ end
562
+
563
+ return value
564
+ end
565
+
566
+ def handle_type_class(rel_path, expected_type)
567
+ # TODO: Need to handle this case better
568
+ value = ProxyObject.new self, rel_path, @driver
569
+
570
+ if (not expected_type.nil?) and not (expected_type == :class)
571
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_CLASS, value)
572
+ end
573
+
574
+ return value
575
+ end
576
+
577
+ def handle_type_hash(rel_path, expected_type)
578
+ value = ProxyObject.new self, rel_path, @driver
579
+
580
+ if (not expected_type.nil?) and not (expected_type == :object or expected_type == :hash)
581
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_HASH, value)
582
+ end
583
+
584
+ return value
585
+ end
586
+
587
+ def handle_type_object(rel_path, expected_type)
588
+
589
+ value = nil
590
+
591
+ class_names = @driver.get_sc_object_class_names(abs_path_with(rel_path))
592
+ matching_class = class_names.detect { |name| ProxyFactory.has_key?(name) }
593
+ if matching_class.nil?
594
+ value = ProxyFactory.create_proxy(Lebowski::Foundation::SCObject, self, rel_path)
595
+ else
596
+ value = ProxyFactory.create_proxy(matching_class, self, rel_path)
597
+ end
598
+
599
+ if not expected_type.nil?
600
+ got_expected_type = (expected_type == :object or value.sc_kind_of?(expected_type))
601
+ if not got_expected_type
602
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_OBJECT, value)
603
+ end
604
+ end
605
+
606
+ return value
607
+
608
+ end
609
+
610
+ def handle_type_array(rel_path, expected_type)
611
+ content_type = @driver.get_sc_type_of_array_content(abs_path_with(rel_path))
612
+
613
+ got_expected_type = false
614
+
615
+ # TODO: This needs a better solution
616
+ case content_type
617
+ when SC_T_NUMBER
618
+ got_expected_type = (expected_type == :array or expected_type == :number_array)
619
+ value = @driver.get_sc_path_number_array_value(abs_path_with(rel_path))
620
+ when SC_T_STRING
621
+ got_expected_type = (expected_type == :array or expected_type == :string_array)
622
+ value = @driver.get_sc_path_string_array_value(abs_path_with(rel_path))
623
+ when SC_T_BOOL
624
+ got_expected_type = (expected_type == :array or expected_type == :boolean_array or expected_type == :bool_array)
625
+ value = @driver.get_sc_path_boolean_array_value(abs_path_with(rel_path))
626
+ when SC_T_HASH
627
+ got_expected_type = (expected_type == :array or expected_type == :object_array or expected_type == :hash_array)
628
+ value = ObjectArray.new self, rel_path
629
+ when SC_T_OBJECT
630
+ got_expected_type = (expected_type == :array or expected_type == :object_array)
631
+ value = ObjectArray.new self, rel_path
632
+ when "empty"
633
+ got_expected_type = (expected_type == :array)
634
+ value = []
635
+ else
636
+ got_expected_type = (expected_type == :array)
637
+ # TODO: Replace with correct logic. Temporary for now
638
+ value = []
639
+ end
640
+
641
+ if (not expected_type.nil?) and (not got_expected_type)
642
+ raise UnexpectedTypeError.new(abs_path_with(rel_path), expected_type, SC_T_ARRAY, value)
643
+ end
644
+
645
+ return value
646
+ end
647
+
648
+ #
649
+ # Will return a string in camel case format for any value that follows the Ruby
650
+ # variable and method naming convention (e.g. my_variable_name). As an example:
651
+ #
652
+ # Util.to_camel_case(:some_long_name) # => "someLongName"
653
+ # Util.to_camel_case("function_foo_bar") # => "functionFooBar"
654
+ #
655
+ def to_camel_case(value)
656
+ camel_case_str = ""
657
+ word_counter = 1
658
+ words = value.to_s.split('_')
659
+ return words[0] if words.length == 1
660
+ words.each do |word|
661
+ camel_case_str << ((word_counter == 1) ? word : word.sub(/./) { |s| s.upcase })
662
+ word_counter = word_counter.next
663
+ end
664
+ return camel_case_str
665
+ end
666
+
667
+ end
668
+
669
+ end
670
+ end