Class: Lebowski::Foundation::ProxyObject
- Inherits:
-
Object
- Object
- Lebowski::Foundation::ProxyObject
- Defined in:
- lib/lebowski/foundation/proxy_object.rb
Overview
ProxyObject is the root object for all objects that are to proxy an object within a web brower. This provides all of the core functionality allowing you to communicate with a remote object and access other remote objects through the use of relative property paths.
In the case where you have created a custom SproutCore object and want to have a proxy for it then your proxy must inherit from the SCObject class that inherits this class.
Direct Known Subclasses
Constant Summary
Constants included from Lebowski::Foundation
SC_BRANCH_CLOSED, SC_BRANCH_OPEN, SC_BUTTON1_STATUS, SC_BUTTON2_STATUS, SC_BUTTON3_STATUS, SC_LEAF_NODE, SC_MIXED_STATE, SC_PICKER_FIXED, SC_PICKER_MENU, SC_PICKER_POINTER, SC_T_ARRAY, SC_T_BOOL, SC_T_CLASS, SC_T_ERROR, SC_T_FUNCTION, SC_T_HASH, SC_T_NULL, SC_T_NUMBER, SC_T_OBJECT, SC_T_STRING, SC_T_UNDEFINED
Constants included from Mixins::WaitActions
Mixins::WaitActions::DEFAULT_TIMEOUT
Instance Attribute Summary collapse
-
#driver ⇒ Object
readonly
The parent object of this object.
-
#name ⇒ Object
A name for this object.
-
#parent ⇒ Object
readonly
The parent object of this object.
-
#rel_path ⇒ Object
readonly
The parent object of this object.
Instance Method Summary collapse
-
#==(obj) ⇒ Object
(also: #eql?)
Override the == operator so that a proxy object can be compared to another proxy object via their SproutCore GUIDs.
-
#[](rel_path, expected_type = nil) ⇒ Object
The primary method used to access a proxied object’s properties.
-
#abs_path ⇒ Object
Returns the absolute path of this object based on the parent object heirarchy.
-
#abs_path_with(rel_path) ⇒ Object
Returns the absolute path given a relative path.
-
#define(path, rel_path = nil, expected_type = nil) ⇒ Object
DEPRECATED.
-
#define_proxy(klass, rel_path) ⇒ Object
Defines a path proxy for a relative path on this proxy object.
-
#init_ext ⇒ Object
Override this method for any initialization procedures during the time the object is being inialized.
-
#initialize(parent = nil, rel_path = nil, driver = nil) ⇒ ProxyObject
constructor
Creates a new proxy object instance.
-
#method_missing(sym, *args, &block) ⇒ Object
Override method_missing so that we can access a proxied object’s properties using a more conventional Ruby approach.
- #none?(rel_path) ⇒ Boolean
- #object?(rel_path) ⇒ Boolean
-
#proxy(klass, rel_path) ⇒ Object
DEPRECATED.
-
#represent_as(type) ⇒ Object
Use when you wish to represent a proxied object as something else other then the proxy returned by this object when accessed with a relative path using [].
-
#sc_all_classes ⇒ Object
Gets all the remote SproutCore classes that the proxied object derives from.
-
#sc_class ⇒ Object
Gets the remote SC class name for this object.
-
#sc_guid ⇒ Object
Gets the remote SproutCore GUID for this object.
-
#sc_kind_of?(type) ⇒ Boolean
Checks if the remote proxied object is a kind of given SC class.
- #sc_path_defined?(rel_path) ⇒ Boolean
- #sc_type_of(rel_path) ⇒ Object
-
#unravel_relative_path(rel_path) ⇒ Object
Given a relative path, unravel it to access an object.
Methods included from Mixins::DefinePathsSupport
#define_path, #define_paths_for, #defined_path, #defined_paths, #path_defined?, #root_defined_path_part, #root_defined_path_part=
Methods included from Mixins::WaitActions
Constructor Details
#initialize(parent = nil, rel_path = nil, driver = nil) ⇒ ProxyObject
Creates a new proxy object instance.
Try to refrain from overriding this method. Instead, if you wish to perform some operations during the time an object is being initialized, override the init_ext method
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 41 def initialize(parent=nil, rel_path=nil, driver=nil) if not init_expected_parent_type.nil? if not parent.kind_of? init_expected_parent_type raise ArgumentInvalidTypeError.new "parent", parent, init_expected_parent_type end end @parent = parent @rel_path = rel_path @driver = driver @guid = nil @cached_proxy_objects = {} @defined_proxies = {} @name = "" init_ext() end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, &block) ⇒ Object
Override method_missing so that we can access a proxied object’s properties using a more conventional Ruby approach. So instead of accessing an object’s property using the [] convention, we can instead do the following:
value = proxied_object.foo # compared to proxied_object['foo']
This will also translate the name of property into camel case that is normally used in JavaScript. So, if an object in JavaScript has a property with the name ‘fooBar’, you can access that property using the standard Ruby convention like so:
value = proxied_object.
It will be converted back into ‘fooBar’. If the property does not exist on the proxied object then an exception will be thrown. If you want to access property without an exception being thrown then use the [] convention using a relative property path string.
450 451 452 453 454 455 456 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 450 def method_missing(sym, *args, &block) if (not sym.to_s =~ /\?$/) and (args.length == 0) camel_case = Util.to_camel_case(sym.to_s) return self[camel_case] if sc_path_defined?(camel_case) end super end |
Instance Attribute Details
#driver ⇒ Object (readonly)
The parent object of this object. Must derive from Lebowski::Foundation::ProxyObject
23 24 25 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 23 def driver @driver end |
#name ⇒ Object
A name for this object. Useful for printing out statements and debugging
27 28 29 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 27 def name @name end |
#parent ⇒ Object (readonly)
The parent object of this object. Must derive from Lebowski::Foundation::ProxyObject
23 24 25 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 23 def parent @parent end |
#rel_path ⇒ Object (readonly)
The parent object of this object. Must derive from Lebowski::Foundation::ProxyObject
23 24 25 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 23 def rel_path @rel_path end |
Instance Method Details
#==(obj) ⇒ Object Also known as: eql?
Override the == operator so that a proxy object can be compared to another proxy object via their SproutCore GUIDs
424 425 426 427 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 424 def ==(obj) return (self.sc_guid == obj.sc_guid) if obj.kind_of?(ProxyObject) return super(obj) end |
#[](rel_path, expected_type = nil) ⇒ Object
The primary method used to access a proxied object’s properties. Accessing a property is done using a relative property path. The path is a chain of properties connected using dots ‘.’. The type is automatically determined, but in cases where a particular type is expected, you can optionally supply what the expected type should be.
As an example, to access an object’s property called ‘foo’, you can do the following:
value = object['foo']
If you expect the value’s type to be, say, an number, you can do the following:
value = object['foo', :number]
In the case where you expect the value to be a type of object you can do one of the following:
value = object['foo', 'SC.SomeObject']
value = object['foo', SomeObject]
In the first case, you are supply the object type as a string, in the second case you are supplying the expected type with a proxy class. For the second option to work you must first supply the proxy to the proxy factory.
To access a property through a chain of objects, you supply a relative path, like so:
value = object['path.to.some.property']
Remember that the path is relative to the object you passed the path to. The approach is used to work with how you would normally access properties using the SproutCore framework.
The fundamental types detected within the web browser are the following:
Null - null in JavaScript
Error - SproutCore error object
String - string in JavaScript
Number - number in JavaScript
Boolean - boolean in JavaScript
Hash - a JavaScript hash object
Object - a SproutCore object
Array - a JavaScript array
Class - A SproutCore class
Based on the value’s type within the browser, this method will translate the value as follows:
Null -> nil
Error -> :error
String -> standard string
Number -> standard number
Boolean -> standard boolean
Hash -> a generic proxy object
Object -> closest matching proxy object
Array -> standard array for basic types; an object array (ObjectArray) for objects
Class -> a generic proxy object
If the given relative path tries to reference a property that is not defined then :undefined is returned.
The two special cases are when the basic type of the relative path is a SproutCore object or an array. In the case of a SproutCore object, the closest matching object type will be returned based on what proxies have been provided to the proxy factory. For instance, let’s say you have custom view that derives from SC.View. If no proxy has been made for the custom view then the next closest proxy will be returned, which would be a View proxy that is already part of the lebowski framework. If your require a proxy to interact with the custom view then you need to add that proxy to the proxy framework.
When the type is an array, the proxy object will check the content of the array to determine their type. If all the content in the array are of the same type then it will return a corresponding array made up content with that type. So, for example, if an object has a property that is an array of strings then a basic array of string will be returned. In the case where the array contains either hash objects or SproutCore objects then an ObjectArray will be returned.
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 386 def [](rel_path, expected_type=nil) if (not rel_path.kind_of?(String)) or rel_path.empty? raise ArgumentError.new "rel_path must be a valid string" end if @cached_proxy_objects.has_key? rel_path return @cached_proxy_objects[rel_path] end defined_proxy = @defined_proxies.has_key?(rel_path) ? @defined_proxies[rel_path] : nil path_defined = path_defined? rel_path if path_defined path = defined_path rel_path expected_type = path.expected_type if (path.has_expected_type? and expected_type.nil?) end unraveled_rel_path = unravel_relative_path rel_path value = fetch_rel_path_value unraveled_rel_path, expected_type if value.kind_of? ProxyObject value = value.represent_as(defined_proxy) if (not defined_proxy.nil?) @cached_proxy_objects[rel_path] = value if (path_defined or not defined_proxy.nil?) end return value end |
#abs_path ⇒ Object
Returns the absolute path of this object based on the parent object heirarchy. As an example, this object’s parent has an absolute path of ‘foo’ and this object has relative path of ‘bar’, then the absolute path will be ‘foo.bar’
142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 142 def abs_path() if not @abs_path.nil? return @abs_path end if @parent.nil? or @parent.abs_path.nil? return @rel_path end @abs_path = "#{@parent.abs_path}.#{rel_path}" return @abs_path end |
#abs_path_with(rel_path) ⇒ Object
Returns the absolute path given a relative path. Say, for example, that a proxy object has an absolute path of ‘mainPage.mainPane.someView’. When given a relative path of ‘foo.bar’, the returned value would be:
'mainPage.mainPane.someView.foo.bar'
163 164 165 166 167 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 163 def abs_path_with(rel_path) path = abs_path return rel_path if path.nil? return "#{path}.#{rel_path}" end |
#define(path, rel_path = nil, expected_type = nil) ⇒ Object
DEPRECATED
249 250 251 252 253 254 255 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 249 def define(path, rel_path=nil, expected_type=nil) puts "DEPRECATED: define is deprecated. use define_path instead" puts "... path = #{path}" puts "... rel_path = #{rel_path}" puts "... expected_type = #{expected_type}" define_path(path, rel_path, expected_type) end |
#define_proxy(klass, rel_path) ⇒ Object
Defines a path proxy for a relative path on this proxy object. The path proxy will be loaded only when actually requested for use.
264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 264 def define_proxy(klass, rel_path) if (not rel_path.kind_of?(String)) or rel_path.empty? raise ArgumentError.new "rel_path must be a valid string" end if not (klass.kind_of?(Class) and klass.ancestors.member?(ProxyObject)) raise ArgumentInvalidTypeError.new "klass", klass, 'class < ProxyObject' end @defined_proxies[rel_path] = klass end |
#init_ext ⇒ Object
Override this method for any initialization procedures during the time the object is being inialized
66 67 68 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 66 def init_ext() end |
#none?(rel_path) ⇒ Boolean
230 231 232 233 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 230 def none?(rel_path) type = sc_type_of(rel_path) return (type == SC_T_UNDEFINED or type == SC_T_NULL) end |
#object?(rel_path) ⇒ Boolean
235 236 237 238 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 235 def object?(rel_path) type = sc_type_of(rel_path) return (type == SC_T_OBJECT or type == SC_T_HASH) end |
#proxy(klass, rel_path) ⇒ Object
DEPRECATED
241 242 243 244 245 246 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 241 def proxy(klass, rel_path) puts "DEPRECATED: proxy is deprecated. use define_proxy instead" puts "... klass = #{klass}" puts "... rel_path = #{rel_path}" define_proxy(klass, rel_path) end |
#represent_as(type) ⇒ Object
Use when you wish to represent a proxied object as something else other then the proxy returned by this object when accessed with a relative path using []. This is useful in cases where you have a view that is simply composed of other views but itself is not custom view inherited from SC.View. As an example, it is common in SproutCore to create a complex view that lives only within an SC.Page, like so:
MyApp.mainPage = SC.Page.create({
composedView: SC.View.design({
layout: { top: 0, bottom: 0, left: 0, right: 0 },
childViews: 'buttonOne buttonTwo statusLabel'.w(),
buttonOne: SC.ButtonView.design({
...
}),
buttonTwo: SC.ButtonView.design({
...
}),
statusLabel: SC.LabelView.design({
...
})
})
})
Since the root view (composedView) is just a basic SC.View, accessing it using the proxy’s [] convention would just give you back a basic View proxy. This then means you have to access the child views explicitly every time you want to interact with the composed view. This can be brittle since the view’s structure can change. Instead you can make a proxy to abstract away the interal structure and make it easier to work with the view. Therefore, we could make a proxy as follows:
ComposedView < Lebowski::Foundation::Views::View
def ()
self['buttonOne'].click
end
def ()
self['buttonTwo'].click
end
def status()
return self['statusLabel.value']
end
end
With the proxy above, you can then do the following:
view = App['mainPage.composedView', View]
view = view.represent_as(ComposedView)
view.
status = view.status
128 129 130 131 132 133 134 135 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 128 def represent_as(type) if not (type.kind_of?(Class) and type.ancestors.member?(ProxyObject)) raise ArgumentInvalidTypeError.new "type", type, 'class < ProxyObject' end obj = type.new @parent, @rel_path, @driver return obj end |
#sc_all_classes ⇒ Object
Gets all the remote SproutCore classes that the proxied object derives from. This will return an array of strings representing the names of the classes. As an example, if the proxy was communicating with an object that was of type SC.ButtonView then the result would be the following:
['SC.ButtonView', 'SC.View', 'SC.Object']
The last item in the array is always ‘SC.Object’ since that is the root object for all SproutCore objects.
198 199 200 201 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 198 def sc_all_classes() @all_class_names = @driver.get_sc_object_class_names(abs_path) if @all_class_names.nil? return @all_class_names end |
#sc_class ⇒ Object
Gets the remote SC class name for this object
181 182 183 184 185 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 181 def sc_class() # We only need to fetch the remote SC class name once since it never changes for a given instance @class_name = @driver.get_sc_object_class_name(abs_path) if @class_name.nil? return @class_name end |
#sc_guid ⇒ Object
Gets the remote SproutCore GUID for this object
172 173 174 175 176 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 172 def sc_guid() # We only need to fetch the remote GUID once since it never changes for a given instance @guid = @driver.get_sc_guid(abs_path) if @guid.nil? return @guid end |
#sc_kind_of?(type) ⇒ Boolean
Checks if the remote proxied object is a kind of given SC class
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 206 def sc_kind_of?(type) if not (type.kind_of?(Class) or type.kind_of?(String)) raise ArgumentInvalidTypeError.new "type", type, 'class < SCObject', String end if type.kind_of?(Class) and type.ancestors.member?(SCObject) type = type.represented_sc_class end type = type.downcase result = sc_all_classes.detect do |val| val.downcase == type end return (not result.nil?) end |
#sc_path_defined?(rel_path) ⇒ Boolean
226 227 228 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 226 def sc_path_defined?(rel_path) return (not sc_type_of(rel_path) == SC_T_UNDEFINED) end |
#sc_type_of(rel_path) ⇒ Object
222 223 224 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 222 def sc_type_of(rel_path) return @driver.get_sc_type_of(abs_path_with(rel_path)) end |
#unravel_relative_path(rel_path) ⇒ Object
Given a relative path, unravel it to access an object. Unraveling means to take any defined path in the given relative path and convert the entire path back into a full relative path without definitions.
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/lebowski/foundation/proxy_object.rb', line 281 def unravel_relative_path(rel_path) path_parts = rel_path.split '.' full_rel_path = "" defined_path = nil counter = path_parts.length for path_part in path_parts do path = defined_path.nil? ? path_part : "#{defined_path}.#{path_part}" if path_defined? path defined_path = path else break end counter = counter - 1 end full_rel_path << self.defined_path(defined_path).full_rel_path if (not defined_path.nil?) if (counter > 0) full_rel_path << "." if (not defined_path.nil?) full_rel_path << path_parts.last(counter).join('.') end return full_rel_path end |