brancusi 0.0.1

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 (40) hide show
  1. data/Rakefile +13 -0
  2. data/lib/assets/javascripts/brancusi/application/application.js.coffee +114 -0
  3. data/lib/assets/javascripts/brancusi/application/application_controller.js.coffee +17 -0
  4. data/lib/assets/javascripts/brancusi/application/application_module.js.coffee +29 -0
  5. data/lib/assets/javascripts/brancusi/application/bootstrapper.js.coffee +18 -0
  6. data/lib/assets/javascripts/brancusi/application/index.js.coffee +1 -0
  7. data/lib/assets/javascripts/brancusi/application/sandbox.js.coffee +14 -0
  8. data/lib/assets/javascripts/brancusi/container/container.js.coffee +139 -0
  9. data/lib/assets/javascripts/brancusi/container/dependent_module.js.coffee +30 -0
  10. data/lib/assets/javascripts/brancusi/container/dependent_object.js.coffee +9 -0
  11. data/lib/assets/javascripts/brancusi/container/index.js.coffee +1 -0
  12. data/lib/assets/javascripts/brancusi/events/event_object.js.coffee +10 -0
  13. data/lib/assets/javascripts/brancusi/events/events_module.js.coffee +14 -0
  14. data/lib/assets/javascripts/brancusi/events/index.js.coffee +1 -0
  15. data/lib/assets/javascripts/brancusi/events/mediator.js.coffee +70 -0
  16. data/lib/assets/javascripts/brancusi/index.js.coffee +2 -0
  17. data/lib/assets/javascripts/brancusi/namespace.js.coffee +13 -0
  18. data/lib/assets/javascripts/brancusi/object_model/base_object.js.coffee +27 -0
  19. data/lib/assets/javascripts/brancusi/object_model/decorate.js.coffee +14 -0
  20. data/lib/assets/javascripts/brancusi/object_model/extend.js.coffee +10 -0
  21. data/lib/assets/javascripts/brancusi/object_model/include.js.coffee +13 -0
  22. data/lib/assets/javascripts/brancusi/object_model/index.js.coffee +1 -0
  23. data/lib/assets/javascripts/brancusi/renderer/index.js.coffee +1 -0
  24. data/lib/assets/javascripts/brancusi/renderer/region_manager.js.coffee +30 -0
  25. data/lib/assets/javascripts/brancusi/renderer/renderer.js.coffee +41 -0
  26. data/lib/assets/javascripts/brancusi/renderer/template_manager.js.coffee +0 -0
  27. data/lib/assets/javascripts/brancusi/routes/dispatcher.js.coffee +10 -0
  28. data/lib/assets/javascripts/brancusi/routes/index.js.coffee +1 -0
  29. data/lib/assets/javascripts/brancusi/routes/mapper.js.coffee +41 -0
  30. data/lib/assets/javascripts/brancusi/routes/router.js.coffee +15 -0
  31. data/lib/assets/javascripts/brancusi/support/davis_router.js.coffee +19 -0
  32. data/lib/assets/javascripts/brancusi/support/knockout_renderer.js.coffee +12 -0
  33. data/lib/assets/javascripts/davis.js +1838 -0
  34. data/lib/assets/javascripts/knockout.js +3583 -0
  35. data/lib/assets/javascripts/underscore.js +1221 -0
  36. data/lib/assets/javascripts/underscore.string.js +600 -0
  37. data/lib/brancusi.rb +4 -0
  38. data/lib/brancusi/engine.rb +4 -0
  39. data/lib/brancusi/version.rb +3 -0
  40. metadata +282 -0
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'guard/jasmine/task'
4
+
5
+ Guard::JasmineTask.new('jasmine:specs') do |task|
6
+ task.options = "-s thin -p 3001"
7
+ end
8
+
9
+ Guard::JasmineTask.new('jasmine:stories') do |task|
10
+ task.options = "-s thin -p 3001 -u http://localhost:3001/jasmine-stories"
11
+ end
12
+
13
+ task 'jasmine:all' => ['guard:jasmine:specs', 'guard:jasmine:stories']
@@ -0,0 +1,114 @@
1
+ #= require brancusi/events
2
+ #= require brancusi/routes/mapper
3
+
4
+ namespace "brancusi"
5
+
6
+ class brancusi.Application extends brancusi.EventObject
7
+
8
+ @dependency mediator: "Mediator"
9
+
10
+ # Default configuration options, which may be overridden by instances
11
+ @config:
12
+ bootstrapper: brancusi.Bootstrapper
13
+
14
+ # Module classes for the application
15
+ @Modules: {}
16
+
17
+ # Module instances
18
+ modules: {}
19
+
20
+ # Controller classes for the application
21
+ @Controllers: {}
22
+
23
+ # Controller instances
24
+ controllers: {}
25
+
26
+ # Model classes for the application
27
+ @Models: {}
28
+
29
+ @routes: new brancusi.routes.Mapper
30
+
31
+ # Instantiates the application and bootstrapper, and resolves any dependencies, modules and controllers.
32
+ #
33
+ # @return [Application] an instance of the application.
34
+ #
35
+ @create: ->
36
+ @instance = new @
37
+ bootstrapper = new @Bootstrapper
38
+ @instance.resolve(bootstrapper)
39
+
40
+ # Resolves, initializes, and runs the application.
41
+ #
42
+ # @return [Application] an instance of the application.
43
+ #
44
+ @run: ->
45
+ @create().initialize().run()
46
+
47
+ # Creates and assigns the application container with the bootstrapper, then resolves dependencies, modules and controllers.
48
+ #
49
+ # @return [Application] the application instance.
50
+ #
51
+ resolve: (bootstrapper) ->
52
+ @container = bootstrapper.configure_container(@)
53
+ @container.resolve(@)
54
+ @_resolve_modules()
55
+ @_resolve_controllers()
56
+ @
57
+
58
+ # Binds event handlers on the modules and controllers, then publishes the application.initialize event.
59
+ #
60
+ # @return [Application] the application instance.
61
+ #
62
+ initialize: ->
63
+ @_bind_events()
64
+ @mediator.publish "application.initialize", @
65
+ @
66
+
67
+ # Publishes the application.ready event.
68
+ #
69
+ # @return [Application] the application instance.
70
+ #
71
+ run: (bootstrapper) ->
72
+ @mediator.publish "application.ready"
73
+ @
74
+
75
+ # @private
76
+ # Instantiates and resolves the application modules.
77
+ #
78
+ _resolve_modules: ->
79
+ # @modules.router = @router if @router?
80
+ # @modules.renderer = @renderer if @renderer?
81
+ module_regex = /(.*)Module/ # e.g. AuthModule
82
+ for klass_name, klass of @constructor.Modules when matches = module_regex.exec(klass_name)
83
+ module_name = _.string.underscored(matches[1]) # e.g. "auth"
84
+ module = @container.resolve(new klass(module_name))
85
+ # module.sandbox.bind_subscriptions(module)
86
+ @modules[module_name] = module
87
+
88
+ # @private
89
+ # Instantiates and resolves the application controllers.
90
+ #
91
+ _resolve_controllers: ->
92
+ controller_regex = /(.*)Controller/ # e.g. HomeController
93
+ for klass_name, klass of @constructor.Controllers when matches = controller_regex.exec(klass_name)
94
+ controller_name = _.string.underscored(matches[1]) # e.g. home
95
+ controller = @container.resolve(new klass(controller_name))
96
+ # controller.sandbox.bind_subscriptions(controller)
97
+ @controllers[controller_name] = controller
98
+
99
+ # @private
100
+ # Binds event handlers on the modules and controllers.
101
+ #
102
+ _bind_events: ->
103
+ @router.sandbox.bind_subscriptions(@router)
104
+ @renderer.sandbox.bind_subscriptions(@renderer)
105
+
106
+ for module_name, module of @modules
107
+ module.sandbox.bind_subscriptions(module)
108
+
109
+ for controller_name, controller of @controllers
110
+ controller.sandbox.bind_subscriptions(controller)
111
+
112
+ # route_mapper = new @container.resolve 'RouteMapper'
113
+ # route_mapper.draw(@constructor.routes.( config?.routes || -> )
114
+ # @router?.initialize?()
@@ -0,0 +1,17 @@
1
+ #= require ./application_module
2
+
3
+ namespace "brancusi"
4
+
5
+ class brancusi.ApplicationController extends brancusi.ApplicationModule
6
+ @dependency renderer: 'Renderer'
7
+
8
+ begin_request: (action_name) ->
9
+ @request =
10
+ action: action_name
11
+
12
+ render: (args...) ->
13
+ if args.length > 0
14
+ @renderer.render_page(args...)
15
+ else
16
+ @renderer.render_page("#{@name}/#{@request.action}")
17
+
@@ -0,0 +1,29 @@
1
+ #= require brancusi/events
2
+
3
+ namespace "brancusi"
4
+
5
+ class brancusi.ApplicationModule extends brancusi.EventObject
6
+ constructor: (@name) ->
7
+
8
+ @dependency sandbox: (container) ->
9
+ container.resolve "Sandbox", [@name]
10
+
11
+ @dependency container: (container) ->
12
+ container.child().register_instance "Sandbox", @sandbox
13
+
14
+ publish: (args...) ->
15
+ @sandbox.publish(args...)
16
+
17
+ # TODO: maybe bind subscriptions automatically after resolving any EventObject (or anything with subscriptions)
18
+ # e.g.
19
+ # resolve: (ref, opts) ->
20
+ # resolution = @container.resolve(ref, opts)
21
+ # @sandbox.bind_scriptions(resolution)
22
+ # resolution.publish = @sandbox.publish
23
+ # resolution
24
+ # bind_subscriptions: (target) ->
25
+ # @sandbox.bind_subscriptions.apply( @sandbox, [target] )
26
+ # target.publish = @sandbox.publish
27
+
28
+ # create_model: ( class_name, opts ) ->
29
+ # @env.create( class_name, opts )
@@ -0,0 +1,18 @@
1
+ #= require brancusi/container
2
+
3
+ namespace "brancusi"
4
+
5
+ class brancusi.Bootstrapper
6
+
7
+ configure_container: ( application ) ->
8
+ container = new brancusi.Container()
9
+
10
+ # TODO: do we need these?
11
+ container.register_instance "Application", application
12
+ container.register_instance "Container", container
13
+
14
+ container.register_class "Sandbox", brancusi.Sandbox
15
+ container.register_class "Mediator", brancusi.Mediator, singleton: true
16
+ container.register_class "RegionManager", brancusi.renderer.RegionManager, singleton: true
17
+
18
+ container
@@ -0,0 +1 @@
1
+ #= require_tree .
@@ -0,0 +1,14 @@
1
+ #= require brancusi/container
2
+
3
+ namespace "brancusi"
4
+
5
+ class brancusi.Sandbox extends brancusi.DependentObject
6
+ @dependency mediator: "Mediator"
7
+
8
+ constructor: (@scope) ->
9
+
10
+ publish: (event, args...) =>
11
+ @mediator.publish_scoped(event, @scope, args...)
12
+
13
+ bind_subscriptions: (target) =>
14
+ @mediator.bind_subscriptions(target, @scope)
@@ -0,0 +1,139 @@
1
+ namespace "brancusi"
2
+
3
+ # Implementation of a DI container.
4
+ #
5
+ class brancusi.Container
6
+
7
+ # Creates an instance of a container.
8
+ #
9
+ # @param parent [brancusi.Container] a parent container. When provided, if resolution of a dependency fails then resolution will be attempted with the parent container instead.
10
+ #
11
+ constructor: (@parent) ->
12
+ @_mappings = {}
13
+
14
+ # Registers a class mapping with the container.
15
+ #
16
+ # @param name [String] the name of the mapping to the class.
17
+ # @param klass [Class] the class the dependency should resolve with.
18
+ # @option opts [Boolean] singleton indicates whether the resolved dependency should be memoized.
19
+ # @return [Container] the container.
20
+ #
21
+ register_class: (name, klass, opts = {}) ->
22
+ @_register_mapping name, klass, "class", opts
23
+ @
24
+
25
+
26
+ # Registers an instance mapping with the container.
27
+ #
28
+ # @param name [String] the name of the mapping to the instance.
29
+ # @param obj [Object] the instance the dependency should resolve to.
30
+ # @return [Container] the container.
31
+ #
32
+ register_instance: (name, obj) ->
33
+ @_register_mapping name, obj, "instance"
34
+ @
35
+
36
+
37
+ # Registers a factory function for resolving dependencies
38
+ #
39
+ # @param name [String] the name of the mapping
40
+ # @param fn [Function] the factory function to resolve the mapping
41
+ # @return [Container] the container
42
+ #
43
+ register_factory: (name, fn) ->
44
+ @_register_mapping name, fn, "factory"
45
+ @
46
+
47
+
48
+ # Returns an instance of the given dependency, resolving any child dependencies.
49
+ #
50
+ # @overload resolve(name)
51
+ # Resolves the dependency according to the name of the mapping.
52
+ # @param name [String] the name of the dependency mapping.
53
+ # @return [Object] the fully resolved dependency.
54
+ #
55
+ # @overload resolve(target, opts)
56
+ # Resolves any unresolved dependencies on a given object.
57
+ # @param target [Object] the object to resolve dependencies for.
58
+ # @return [Object] target.
59
+ #
60
+ resolve: (ref, opts) ->
61
+ if typeof ref == "string"
62
+ resolution = @_resolve_string(ref, opts)
63
+ else if typeof ref == "function"
64
+ resolution = @_resolve_function(ref, opts)
65
+ else if typeof ref == "object"
66
+ resolution = @_resolve_object(ref)
67
+
68
+ if resolution?
69
+ resolution
70
+ else if @parent?
71
+ @parent.resolve ref, opts unless resolution?
72
+ else
73
+ throw new Error("Unable to resolve dependency: #{ref}") unless resolution?
74
+
75
+
76
+ # Creates a child container.
77
+ # @return [Container] a new child container.
78
+ #
79
+ child: ->
80
+ new Container(@)
81
+
82
+
83
+ # Helper method to set up a mapping. Merges in the given options to the definition.
84
+ #
85
+ # @private
86
+ # @param name [String] the name of the mapping
87
+ # @param [Class|Object|Function] the dependency to map too
88
+ # @param [String] the kind of mapping. Either 'class', 'object' or 'factory'
89
+ #
90
+ _register_mapping: (name, ref, kind, opts = {}) ->
91
+ @_mappings[name] = _.defaults({ kind: kind, ref: ref }, opts)
92
+
93
+
94
+ # Resolves a dependency by name, passing the given options (if provided) to the dependency when initialized.
95
+ #
96
+ # @private
97
+ # @param name [String] the name of the dependency.
98
+ # @param opts [Array] optional array of arguments to pass to the dependency
99
+ #
100
+ _resolve_string: (name, opts) ->
101
+ mapping = @_mappings[name]
102
+ return null unless mapping?
103
+
104
+ if mapping.kind == "instance"
105
+ mapping.ref
106
+ else if mapping.kind == "class" and mapping.singleton == true
107
+ mapping.instance = @resolve( mapping.ref, opts ) unless mapping.instance?
108
+ mapping.instance
109
+ else if mapping.kind == "factory"
110
+ mapping.ref.apply(null, opts)
111
+ else
112
+ @resolve(mapping.ref, opts)
113
+
114
+
115
+ # Resolves a dependency specified by a function.
116
+ #
117
+ # @private
118
+ # @param fn [Function] the function to invoke
119
+ # @param opts [Array] an optional array of arguments to pass to the function
120
+ #
121
+ _resolve_function: (fn, opts) ->
122
+ opts ?= []
123
+ obj = new fn(opts...)
124
+ @resolve obj
125
+
126
+
127
+ # Resolves dependencies on the given object.
128
+ #
129
+ # @private
130
+ # @param target [Object] the target object.
131
+ #
132
+ _resolve_object: (target) ->
133
+ for name, args of target.constructor.dependencies
134
+ [dependency, dependency_args] = args
135
+ if typeof dependency == "function"
136
+ target[name] = dependency.apply(target, [@])
137
+ else
138
+ target[name] = @resolve dependency, dependency_args
139
+ target
@@ -0,0 +1,30 @@
1
+ namespace "brancusi"
2
+
3
+ # A module to facilitate the specification of dependencies. Dependencies are defined on the
4
+ # 'dependencies' property of the target's prototype, in the format:
5
+ #
6
+ # {attr1: ['dep1', [<args1>]], attr2: ['dep2', [<args2>]]}
7
+ #
8
+ # @mixin
9
+ #
10
+ class brancusi.DependentModule
11
+
12
+ # Adds the named dependencies to the object's ::dependencies property
13
+ #
14
+ # @overload dependency(dependency, args...)
15
+ # Defines the given dependency on the subject class
16
+ # @param dependency [Object] an object with a single field in the format {<attribute>: '<dependency name>'}
17
+ # @param args [Array] optional an array of arguments to store with the dependency, to be passed as arguments on resolution
18
+ #
19
+ # @overload dependency(dependencies)
20
+ # Defines the given dependencies on the subject class
21
+ # @param dependencies [Object] an object naming a number of dependencies
22
+ #
23
+ @dependency: (dependencies, args...) ->
24
+ @dependencies ?= {}
25
+
26
+ if @__super__? and @__super__.constructor.dependencies == @dependencies
27
+ @dependencies = _.clone(@__super__.constructor.dependencies)
28
+
29
+ for dependency_name, dependency_type of dependencies
30
+ @dependencies[dependency_name] = [dependency_type, args]
@@ -0,0 +1,9 @@
1
+ #= require brancusi/object_model
2
+ #= require ./dependent_module
3
+
4
+ namespace "brancusi"
5
+
6
+ # Base class for dependent classes.
7
+ #
8
+ class brancusi.DependentObject extends brancusi.BaseObject
9
+ @include brancusi.DependentModule
@@ -0,0 +1 @@
1
+ #= require_tree .
@@ -0,0 +1,10 @@
1
+ #= require ./events_module
2
+ #= require ../container/dependent_object
3
+
4
+ namespace "brancusi"
5
+
6
+ # Base class for AER classes.
7
+ #
8
+ class brancusi.EventObject extends brancusi.DependentObject
9
+ @include brancusi.EventsModule
10
+
@@ -0,0 +1,14 @@
1
+ namespace "brancusi"
2
+
3
+ # Provides convenience methods @on and publish for subscribing and publishing to application events with AER.
4
+ #
5
+ class brancusi.EventsModule
6
+
7
+ # Generates a named event handler for AER.
8
+ #
9
+ # @param event [String] the name of the event.
10
+ # @param handler [Function] the event handler.
11
+ #
12
+ @on: (event, handler) ->
13
+ @::["@#{event}"] = handler
14
+
@@ -0,0 +1 @@
1
+ #= require_tree .
@@ -0,0 +1,70 @@
1
+ namespace "brancusi"
2
+
3
+ # An implementation of the mediator pattern. This class should not be referred to directly, as the AER pattern is preferred.
4
+ #
5
+ class brancusi.Mediator
6
+
7
+ constructor: ->
8
+ @subscribers = {}
9
+
10
+ # Subscribes the given handler to the content for the specified event.
11
+ #
12
+ # @param event [String] the (fully qualified) name of the event.
13
+ # @param handler [Function] the event handler.
14
+ # @param context [Object] optional the context to invoke the handler against
15
+ #
16
+ subscribe: (event, handler, context) ->
17
+ @subscribers[event] ?= []
18
+ @subscribers[event].push(-> handler.apply(context, arguments))
19
+
20
+ # Invokes all handlers for the given event.
21
+ #
22
+ # @param event [String] the (fully qualified) name of the event.
23
+ # @param args... the arguments to forward to the handler.
24
+ #
25
+ publish: (event, args...) ->
26
+ @publish_scoped(event, null, args...)
27
+
28
+ # Invokes all handlers for the given event.
29
+ #
30
+ # @param event [String] the name of the event (either qualified or unqualified)
31
+ # @param scope [String] the name of the scope for unqualified event names.
32
+ # @param args... the arguments to forward to the handler.
33
+ #
34
+ publish_scoped: (event, scope, args...) ->
35
+ event = @_scoped_name(event, scope, true) if scope?
36
+ subscribers = (@subscribers?[event] || {})
37
+ for handler in subscribers
38
+ handler(args...)
39
+
40
+ # Binds all subscriptions on the target object (optionally using the given scope).
41
+ #
42
+ # @param target [Object] the target object.
43
+ # @param scope [String] optional the name of the scope of the object.
44
+ #
45
+ bind_subscriptions: (target, scope) ->
46
+ for name, handler of target when matches = /@(.*)/.exec(name)
47
+ event = matches[1]
48
+ event = @_scoped_name(event, scope) if scope?
49
+ @subscribe(event, handler, target)
50
+
51
+ # @private
52
+ # Returns the fully qualified name of the event, and optionally validates the inpute.
53
+ #
54
+ # @param input [String] the input to qualify (and maybe validate).
55
+ # @param scope [String] optional the name of the scope for unqualified event names.
56
+ # @param validate [Boolean] optional whether or not to error on invalid event names.
57
+ #
58
+ _scoped_name: (input, scope, validate) =>
59
+ regex = /^((\w+)\.)?(\w+)$/
60
+
61
+ if input.match(regex)
62
+ [_, _, event_scope, event] = regex.exec(input)
63
+ if scope?
64
+ event_scope ?= scope
65
+ "#{event_scope}.#{event}"
66
+ else
67
+ event
68
+ else if validate?
69
+ throw new Error("Invalid event name: #{input}")
70
+