Created
June 28, 2011 17:18
-
-
Save trek/1051646 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sproutcore-metal | |
/core.js | |
defines the SC namespace | |
/platform.js | |
defines object creation | |
defines defineProperty | |
checks for native setters/getters | |
/utils.js | |
s: guidFor | |
defines SC.meta which will give you information about a property on an object | |
define some typical functional-style fns: | |
wrap() (mostly used for faking _super), isArray(), makeArray() (used for transforming arguments into arrays) | |
/events.js | |
Code (i.e not "User") events. Defines | |
SC.addListener | |
SC.removeListener | |
SC.sendEvent | |
SC.hasListeners | |
SC.watchedEvents | |
SC.listenersFor | |
Kind of like _.js or Backbone custom events. Objects will be notified of property change events | |
on other objects | |
Events added, removed, and triggered will have names like | |
length:before | |
firstObject:before | |
lastObject:before | |
element:before | |
parentView.content:before | |
content.title:before | |
length:change | |
firstObject:change | |
lastObject:change | |
element:change | |
parentView.content:change | |
content.title:change | |
/accessors.js | |
_Object_ related differences in browser (you're probably familiar with DOM related normalizing). | |
Provides single interface for good techniques, delegating to built-in (i.e. FASTER) browser features on | |
modern browsers, falls back to hand-made (i.e. SLOWER) implementations for older browsers. | |
jquery, underscore, prototype | |
ruby's active-support | |
great way to get modern browser features into older browsers | |
adds get/set behavior | |
eventually these end up as SC.get and SC.set | |
SC.get does direct property access. If the property is undefined it will call obj.unknownProperty(key), | |
otherwise returns undefined | |
SC.getPath | |
takes a path ("string representing object space path"), walks that path, and resolves to an object | |
SC.setPath | |
uses helpers | |
SC.normalizePath | |
SC.normalizeTuple | |
/properties.js | |
SC has some special kinds of property behavior related to setting/getting | |
that it needs to build complex behavior later on. | |
What's an SC.Descriptor? "sets of behaviors on set/get property access" | |
lowest level is identical to direct property access | |
If a property on an object being "watched" by other objects, you must access it with get/set. | |
In development SC will warn when you attempt direct access on a property that should be guarded in this way. | |
Mimics native property behavior and Object.create with this behavior. Why? direct property access seems grand | |
in theory but presents a major barrier to building up layers of behavior though inheritance or components. | |
SC.create builds on SC.platform.create to make properties a part of the behavior. | |
Also adds SC.createPrototype for making objects suitable for setting as prototypes but which themselves are | |
not intended for use other than later inheritance. | |
/watching.js | |
Watching is an internal library you're not likely to use yourself, but key to understanding later | |
functionality of SC. It adds a custom Descriptor `SC.SIMPLE_PROPERTY.watched` to objects | |
This Descriptor (defined in metal/properties) augments `set`ting calls so that they invoke | |
SC's property change interface: | |
if (watching) SC.propertyWillChange(obj, keyName); | |
m.values[keyName] = value; | |
if (watching) SC.propertyDidChange(obj, keyName); | |
SC.propertyWillChange and SC.propertyDidChange are both internal implementation tools. You'll likely not call them | |
SC.propertyWillChange and SC.propertyDidChange wrap calls to SC.notifyBeforeObservers(obj, keyName) and | |
SC.notifyObservers(obj, keyName) which are key parts of the next part of the the library: Observers. | |
/observer.js | |
talk about observer pattern to sound smart. | |
uses the accessor behavior to implement property notification. Includes some tricks for only sending messages | |
once to any object that needs to receive one. Keeps an observer queue. | |
You won't be calling `SC.addObserver`, but its the heart of the property observation system. Internally it | |
uses `SC.addListener` which is part of the SC event system defined in metal/events. Although events is | |
a general eventing system like those found in other js browser app frameworks, it's only used | |
You could use it to solve problems the same way something like X or Y's event system, but you'll see later | |
that higher levels of the SC stack obviate the need. | |
For now, it's useful to understand a Observer as an object that registers an event listener to receive notices | |
of changes on a particular property of an object. Realistically the Event/Observer system could be | |
rolled into a single concept, but having events separate does lend itself to a more | |
decoupled system (better why?). | |
After registering an event handler for changes to a particular property, `SC.addObserver` starts the watching | |
process by calling `SC.watch(obj, path)` | |
When an object property changes via obj.set('myPropertyName', 'some new value'), it triggers the listener | |
passes through the augmented `set`ing behavior added by `SC.Descriptor.watching` will calls | |
SC.propertyWillChange and SC.propertyDidChange. | |
Finally, these call `SC.notifyBeforeObservers` and `SC.notifyObservers` which will notify any objects observing | |
for changes on these properties. | |
Later in the framework, this will all be used by runtime/bindings (which probably should be moved to metal lib). | |
/mixin | |
Object composition support (see the oldie-but-goodie: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/). | |
A bit like `extend` in other frameworks or libraries, but with proper respect for previous augmentations (whereas | |
`extend` typical just overwrites). | |
/computed | |
Adds support for computed properties via `SC.computed()`. Computed properties are function defined | |
on an object whose value is derived from other properties of the object. | |
The canonical example offered is usually this a `fullName` function: | |
fullName: function(){ | |
return this.get('firstName') + ' ' + this.get('lastName'); | |
}, | |
if either of the "dependent keys" (`firstName` or `lastName`) changes, then the return value of `fullName()` would change, | |
objects observing the value of `fullName` need to be notified. | |
You're unlikely to encounter `SC.computed()`. Later in the runtime lib `Function.prototype` is augmented with a `property()` | |
function that takes a list of dependent keys as an argument and proxies to this library. | |
... | |
fullName: function(){ | |
return this.get('firstName') + ' ' + this.get('lastName'); | |
}.property('firstName', 'lastName'), | |
... | |
/runtime | |
Runtime augments core javascript types. You can consider this the sproutcore "language" | |
Sometimes by extending their `prototype`, sometimes by wrapping them in new SC-specific | |
objects and adds a handful of top-level helper functions. (can turn off with SC.EXTEND_PROTOTYPES) | |
If you turn off these extensions they'll still be available in SC-specific helpers. | |
E.g. if you prefer not to pollute `String.prototype` with `dasherize`, you'll still have access to | |
`SC.String.dasherize('myStringThatNeedsDasherizing') | |
/core | |
defines NO, YES. Silly holdover from Apple days. | |
protects from spurios console calls | |
SC.typeOf - typical augmented object test to make up for javascript's mostly useless native typeof | |
SC.none - returns true for tests of both null and undefined | |
SC.empty - returns true for tests of null, undefined, and empty string | |
SC.isArray - tests for array-ness (dup!) | |
SC.compare - compares objects for sorting. returns -1, 0, 1 | |
SC.copy - copying objects | |
SC.inspect - string inspection | |
SC.isEqual - tests deeper equality | |
SC.keys - returns lists of keys on an object literal | |
SC.K - an empty function | |
SC.Error - wraps native JS error | |
SC.Logger - wraps `console` | |
/ext/string | |
adds common string manipulations to `String.prototype` | |
/ext/function | |
adds observing and computed property support to `Function.prototype` | |
/ext/mixin | |
adds support for detecting created object property names that end in `Binding` (e.g. `valueBinding`). This is used later in | |
runtime/system/bindings. | |
/mixins adds lots of useful patterns you'd find baked right into other languages: | |
/mixins/enumerable, /mixins/mutable_array, /mixins/mutable_enumerable, /mixins/array | |
Defines an enumerable interface for Array-like objects to adhere to, with lots of handy methods for working with | |
collections (maps, reduces, filters, finding, uniqifying, etc) as well as observer support for Array-like properties. | |
E.g. length could otherwise not be observed. | |
These are used a lot in higher-level libraries in the framework: | |
`SC.RecordArray = SC.Object.extend(SC.Enumerable, SC.Array, SC.MutableEnumerable, SC.MutableArray, ...` | |
/mixins/comparable | |
defines and provides support for object to object comparisons (useful for equality checks and sorting). If you want this in | |
some special object family you've written implement the method `compare` | |
/mixins/copyable | |
defines standard interface for copying objects. If you want this in some special object family you've | |
written implement the method `copy` | |
/mixins/freezable | |
defines standard interface for freezing objects (i.e. preventing further mutation of its properties). | |
/mixins/observable | |
adds support for object-level access to the metal/observing system. So, instead of using `SC.get(object, 'propName')` you | |
have access to `object.get('propName')`. Most importantly this is later mixed into SC's base Object (`SC.Object`) so that | |
nearly every object you'll be working with as a developer will have observation support. | |
/system/core_object | |
Here begins inklings of the layer you're mostly likely to interact with as a developer using SC. | |
SC.CoreObject provides support to its prototypal children for creating and destroying objects, | |
a standard object initialization process. | |
/system/object | |
Defines SC.Object which is identical to SC.CoreObject with SC.Observable mixed in to provide observation support. | |
This basically marks the waterline for what a typical developer using SproutCore will use. Everything I've mentioned previously | |
exists to augment the javascript environment to enable `SC.Object` and higher layers to properly function. | |
/system/string | |
Defines SC.String which holds useful helper methods for string manipulation (formatting, localizing, inflecting). Unless | |
you've specifically set SC.EXTEND_PROTOTYPES to false, these have also been added to `String.prototype` which is where | |
you're likely be using them. | |
/system/native_array | |
Defines SC.NativeArray mixin that bundles up most of the enumerable and array and mixins in addition to SC.Observable, SC.Copyable | |
and then applies them to the environment's native `Array` (unless you've, again, set SC.EXTEND_PROTOTYPES to false) | |
/system/set | |
Adds Sets (unordered collection of unique objects) to the environment. This is used by higher level libraries in the framework | |
although you could use it yourself if you needed Set behavior. | |
/system/namespace | |
SC.Namespace is just an descendent of SC.Object. It's intended to just be a dumb container for nesting code. It also helps | |
print more meaningful output to help you find where an object lives in an object hierarchy. | |
/system/application | |
SC.Application is the only object descendent of SC.Namespace in the framework. Identical to SC.Namespace. Comments indicate that | |
you should implement a `main` method for application initialization. However, this function will never be called by the framework | |
so I suspect it's vestigial or not yet ported. | |
/system/array_proxy | |
SC.ArrayProxy takes the place of what used to be ArrayController. Proxy is definitely a more suitable name. An array proxy wraps a | |
collection (set as its `content` property) allowing a developer to extend the behavior of collection without polluting the collection | |
itself. | |
Imagine you have a 100 customers each with their own collection of payments. As collections, each set of payments will already | |
have many useful methods that are shared by all collections. However, you may wish to add specific functionality to the collections | |
while you're working with them. One option would be to extend Array to include payment-collection specific behavior (e.g. create | |
a special PaymentsArray family of objects). | |
Another option (the one adopted by SproutCore) is to wrap the collection in a proxy while you're working with it, implement new | |
behavior on that proxy, and delegate all other behavior back to the original collection. | |
MyApp.customerPaymentsProxy = SC.ArrayProxy.create({ | |
areOutstandingPaymentsDue: function(){ | |
... | |
} | |
}) | |
When you call `MyApp.customerPaymentsProxy.get('length')` the Proxy will delegate to its `content` property. When you | |
call `MyApp.customerPaymentsProxy.get('areOutstandingPaymentsDue') it will call your custom function. | |
I've seen this pattern often called a Delegate Pattern. | |
/system/object_proxy | |
An Object-specific version of array_proxy doesn't exist yet. This somewhat complicates following the master/detail pattern | |
that is so common in UI programming. | |
/system/bindings | |
Where so much of the sproutcore magic gets connected together. Bindings provide a methodology for synchronizing | |
values in your application. You'll most likely encounter bindings when you specific property names that end in "Binding". | |
myObject = SC.Object.create({ | |
valueBinding: "MyApp.someCollectionOfThings.selection.title" | |
}) | |
This will ensure that the `value` property of myObject will be equal to the object at the path "MyApp.someCollectionOfThings.selection.title". | |
So, if the collection at the path "MyApp.someCollectionOfThings" changes (which also means that the `.selected` has changed, which further means | |
that the `.title` has changed), the `value` of `myObject` will also be changed. | |
Further, if you change `value` of `myObject`, that difference will also be synchronized to the other end of the Binding | |
(e.g. whatever object in memory is represented by the path "MyApp.someCollectionOfThings.selection" will have its `title` property set). | |
Bindings include code to keep "echo"ing of change notifications from occurring. So, if "MyApp.someCollectionOfThings.selection.title" | |
is changed and "myObject.value" is changed to synchronize to the new value "MyApp.someCollectionOfThings.selection.title" the | |
change notifications stop there (rather than continually notifying each object of new changes in an infinite loop). | |
Bindings also have support for direct creation (as opposed to being referenced by a string like the code sample above). This will let | |
you customize the Binding's behavior. So, referring to a binding by a path/string (`valueBinding: "MyApp.someCollectionOfThings.selection.title"`) | |
gets the default behavior, but you can specify very specific functionality using the Bindings object itself: | |
myObject = SC.Object.create({ | |
valueBinding: SC.Binding.SC.Binding.oneWay("MyApp.someCollectionOfThings.selection.title") | |
}) | |
This tells the binding that changes anywhere along the path "MyApp.someCollectionOfThings.selection.title" should trigger synchronization of | |
`myObject.value` but changes to `myObject.value` will not be reflected on the other side of the binding. | |
Internally, Bindings work by setting themselves as intermediate observers of the objects at both sides of | |
the binding (using `SC.addObserver` twice). Support for automatically detecting property names that match the | |
/^.+Binding/ pattern is shoehorned into Objects by SC._mixinBindings defined in runtime/lib/ext/mixin. | |
/system/run_loop | |
Defines a custom RunLoop object that layers on top of the native browser run loop (which only has a few entry points and isn't terribly | |
customizable). You won't be interacting with this much yourself but other parts of the framework (particularly Bindings, View rendering, | |
and mouse/keyboard events) will use `SC.run.schedule()` to avoid funkiness with the native browser runloop/event system | |
A good read of why a system like this is necessary: http://ejohn.org/blog/how-javascript-timers-work/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment