darwinjs-rails 1.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.
data/doc/base.md ADDED
@@ -0,0 +1,147 @@
1
+ Darwin.Base is the common parent of all Darwin classes.
2
+
3
+ You can use it as well in any of your lib classes to
4
+ benefits of its features :
5
+
6
+ * option pattern
7
+ * class events
8
+ * mixins
9
+
10
+
11
+ # Option pattern
12
+
13
+ Any class inheriting from Darwin.Base can declare options
14
+ that propagate to children classes, but not to parent classes,
15
+ much like rails' `#class_attribute`. Observe the following :
16
+
17
+ ```coffee
18
+ class MyParentClass extends Darwin.Base
19
+ @options {
20
+ foo: 'Foo'
21
+ }
22
+
23
+ class MyChildClass extends MyParentClass
24
+ @options {
25
+ foo: 'overriden foo'
26
+ bar: 'bar'
27
+ }
28
+
29
+ class MyOtherChildClass extends MyParentClass
30
+ @options {
31
+ bar: 'other bar'
32
+ }
33
+
34
+ new MyParentClass().options.foo # => 'Foo'
35
+ new MyChildClass().options.foo # => 'overriden foo'
36
+ new MyOtherChildClass().options.foo # => 'Foo'
37
+ new MyChildClass().options.bar # => 'bar'
38
+ new MyParentClass().options.bar # => undefined
39
+ ```
40
+
41
+ Additionnaly, options can be overriden at initialization time :
42
+
43
+ ```coffee
44
+ new MyChildClass( bar: 'hello' ).options.bar # => 'hello'
45
+ ```
46
+
47
+ If you override the constructor methods, don't forget to accept
48
+ options parameter and call super with it :
49
+
50
+ ```coffee
51
+ class MyClass extends Darwin.Base
52
+ constructor: ( options ) ->
53
+ super( options )
54
+ # do stuff
55
+ ```
56
+
57
+
58
+ # Class events
59
+
60
+ With jQuery, you can only bind and fire events on DOM elements.
61
+
62
+ Darwin.Base provides a mean to attach events on classes :
63
+
64
+ ```coffee
65
+ class MyClass extends Darwin.Base
66
+ @options {
67
+ my_name: 'Joe'
68
+ }
69
+
70
+ say_hi: ->
71
+ @trigger( 'said', "Hi, my name is #{@options.my_name}." )
72
+
73
+ my_instance = new MyClass()
74
+ my_instance.on( 'said', ( message ) -> ( console.log( message ) ) )
75
+ my_instance.say_hi() # logs : Hi, my name is Joe.
76
+ ```
77
+
78
+ This allows to easily encapsulate features in classes and have them
79
+ to communicate.
80
+
81
+
82
+ # Mixins
83
+
84
+ A feature that coffeescript lacks of is the ability to include or
85
+ extend mixins. Darwin.Base provides that :
86
+
87
+ ```coffee
88
+ InstanceMethods =
89
+ do_something: ->
90
+ console.log 'I did something.'
91
+
92
+ do_nothing: ->
93
+ console.log 'I swear I did nothing.'
94
+
95
+
96
+ ClassMethods =
97
+ foo: ->
98
+ console.log 'foo'
99
+
100
+ bar: ->
101
+ console.log 'bar'
102
+
103
+
104
+ class MyClass extends Darwin.Base
105
+ @include InstanceMethods
106
+ @extend ClassMethods
107
+
108
+ my_instance = new MyClass()
109
+ my_instance.do_something() # logs : 'I did something.'
110
+ MyClass.foo() # logs : 'foo'
111
+ ```
112
+
113
+ Just like ruby modules, special methods allow you to
114
+ operate after module is included or extended :
115
+
116
+ ```coffee
117
+ InstanceMethods =
118
+ included: ->
119
+ @extend ClassMethods
120
+
121
+ do_something: ->
122
+ console.log 'I did something.'
123
+
124
+ do_nothing: ->
125
+ console.log 'I swear I did nothing.'
126
+
127
+
128
+ ClassMethods =
129
+ extended: ->
130
+ console.log 'extended'
131
+
132
+ foo: ->
133
+ console.log 'foo'
134
+
135
+ bar: ->
136
+ console.log 'bar'
137
+
138
+
139
+ class MyClass extends Darwin.Base
140
+ @include InstanceMethods
141
+
142
+ my_instance = new MyClass() # logs : 'extended'
143
+ my_instance.do_something() # logs : 'I did something.'
144
+ MyClass.foo() # logs : 'foo'
145
+ ```
146
+
147
+
data/doc/controller.md ADDED
@@ -0,0 +1,266 @@
1
+ Darwin.Controller handles everything event or request related.
2
+
3
+ This is the main entry point for all feature. A Controller
4
+ typically initialize a View and bind events to its selectors
5
+ (though you may use a controller without any View if you need
6
+ to).
7
+
8
+ # Basic usage
9
+
10
+ Here is a typical controller example :
11
+
12
+ ```coffee
13
+ class App.Controllers.Users.Index extends Darwin.Controller
14
+ @options {
15
+ View: App.Views.Users.Index
16
+
17
+ events:
18
+ 'Toggle user block': { el: 'user_trigger', type: 'click' }
19
+ 'Delete user': { el: 'user_block', delegate: 'delete_user', type: 'click' }
20
+ }
21
+
22
+
23
+ user_trigger_clicked: ->
24
+ if @view.users_shown()
25
+ @view.hide_users()
26
+ else
27
+ @view.show_users()
28
+
29
+
30
+ delete_user_clicked: ( $link ) ->
31
+ if confirm( 'Really delete user ?' )
32
+ $.get( $link.attr( 'href' ), =>
33
+ @view.remove_user( $link )
34
+ )
35
+ ```
36
+
37
+ All you need to add in your html to use it is a data-module attribute :
38
+
39
+ ```html
40
+ <div id="users_index" data-module="Users.Index">
41
+ <-- ... -->
42
+ </div>
43
+ ```
44
+
45
+ `options.View` declares which view you will use. View is automatically
46
+ initialized at controller initialization.
47
+
48
+ `options.events` declares which events are handled by the controller.
49
+ You should use this rather than binding events manually for x reasons :
50
+
51
+ * It offers a central configuration point, so other developers can see
52
+ at a glance what controller do.
53
+ * It plays well with view selectors, so that you don't have to change
54
+ every single file if your DOM structure change.
55
+ * Most important, callbacks are wrapped so they are disabled if an error
56
+ occurs on page.
57
+
58
+ Event declaration keys are arbitrary strings. This should act as a
59
+ documentation for your controller. Most of the time, knowning the element
60
+ on which the callback is bound and the event type is useless to understand
61
+ what it's supposed to do :
62
+
63
+ ```coffee
64
+ { el: 'user_trigger', type: 'click' }
65
+ ```
66
+
67
+ Ok, something happens on user_trigger's click, but what does it do ?
68
+
69
+ ```coffee
70
+ 'Toggle user block': { el: 'user_trigger', type: 'click' }
71
+ ```
72
+
73
+ Now, it's clearer.
74
+
75
+ Callback method names are automatically computed after element and
76
+ event type, so `{ el: 'user_trigger', type: 'click' }` will call the
77
+ `user_trigger_clicked` method. Every callback is passed the element
78
+ as first parameter and the event object as second parameter :
79
+
80
+ ```coffee
81
+ user_trigger_clicked: ( $trigger, event ) ->
82
+ ```
83
+
84
+ Note that if you use delegation, delegated element name will be used
85
+ instead of element name :
86
+
87
+ ```coffee
88
+ class App.Controllers.Users.Index extends Darwin.Controller
89
+ @options {
90
+ View: App.Views.Users.Index
91
+
92
+ events:
93
+ 'Delete user': { el: 'user_block', delegate: 'delete_user', type: 'click' }
94
+ }
95
+
96
+
97
+ delete_user_clicked: ( $link ) ->
98
+ ```
99
+
100
+ # Event declaration options
101
+
102
+ Here are all options allowed in event declaration :
103
+
104
+ ## el
105
+
106
+ A view selector name. Element will be retrieved using `@view.get( <name> )`.
107
+
108
+ ## type
109
+
110
+ The event on which to bind the callback. This can be any jQuery event the
111
+ element responds to. So, if you have a plugin that trigger a custom `display`
112
+ event on element, you can use it there.
113
+
114
+ ## delegate
115
+
116
+ Use delegation instead of static binding. The selector name
117
+ is retrieved from view, just as `el`.
118
+
119
+ Delegation not only let bind events on element that are not
120
+ in the page when callback is bound, but also let bind a single
121
+ event instead of hundredth. You probably should use it as much
122
+ as possible.
123
+
124
+ ```coffee
125
+ class App.Controllers.Users.Index extends Darwin.Controller
126
+ @options {
127
+ View: App.Views.Users.Index
128
+
129
+ events:
130
+ 'Delete user': { el: 'user_block', delegate: 'delete_user', type: 'click' }
131
+ }
132
+
133
+
134
+ delete_user_clicked: ( $link ) ->
135
+ ```
136
+
137
+ This binds a single callback to handle as many users you want,
138
+ and also handle users you've added in the list after controller
139
+ initialization.
140
+
141
+ ## controller_method
142
+
143
+ Use a custom method name rather than automatically computed one.
144
+ You may use this, for example, if you want to use the same callback
145
+ for two events.
146
+
147
+ Sometime, automatically computed name will just not do it. For example,
148
+ Darwin.Controller does not use any special inflection other than
149
+ adding a 'd' rather than 'ed' for words that end with 'e'. So,
150
+ here are the computed method names :
151
+
152
+ ```coffee
153
+ { el: 'foo', type: 'click' } # => foo_clicked
154
+ { el: 'foo', type: 'change' } # => foo_changed
155
+ { el: 'foo', type: 'show' } # => foo_showed
156
+ ```
157
+
158
+ For proper english, you can use `controller_method` :
159
+
160
+ ```coffee
161
+ 'Update page title': { el: 'foo', type: 'show', controller_method: 'foo_shown' }
162
+ ```
163
+
164
+ ## view_method
165
+
166
+ If your callback simply call a view method, you don't need
167
+ to create a controller method for that :
168
+
169
+ ```coffee
170
+ foo_clicked: ->
171
+ @view.show_something()
172
+ ```
173
+
174
+ Instead, you can declare this method in event definition :
175
+
176
+ ```coffee
177
+ 'Show something': { el: 'foo', type: 'click', view_method: 'show_something' }
178
+ ```
179
+
180
+ `controller_method` and `view_method` are mutually exclusive.
181
+
182
+ ## stop
183
+
184
+ By default, any click event calls `event.preventDefault()` and
185
+ `event.stopPropagation()`, because it's what we need most of the time.
186
+
187
+ You may not want that, for example, when you want to ask a confirmation
188
+ to user. In that case, you can declare `stop: false` :
189
+
190
+ ```coffee
191
+ class App.Controllers.Users.Index extends Darwin.Controller
192
+ @options {
193
+ View: App.Views.Users.Index
194
+
195
+ events:
196
+ 'Delete user': { el: 'delete_user', type: 'click', stop: false }
197
+ }
198
+
199
+
200
+ delete_user_clicked: ( $link, event ) ->
201
+ event.preventDefault() unless confirm( 'seriously ?' )
202
+ ```
203
+
204
+ Reversively, you can force stopping on other events :
205
+
206
+ ```coffee
207
+ 'Troll ie users': { el: 'user_checkbox', type: 'change', stop: true }
208
+ ```
209
+
210
+ ## cancel_delay
211
+
212
+ Sometime, you want to wait for a short time on an event before
213
+ executing its callback, and potentialy cancel it if the same
214
+ event occurs.
215
+
216
+ You will use that typically on an ajax autocomplete search box :
217
+ when user press a key, you wait for 500ms before firing the request
218
+ to see if she did not added an other letter. You can use cancel_delay
219
+ for that :
220
+
221
+ ```coffee
222
+ 'Autocomplete search': { el: 'search_input', type: 'change', cancel_delay: 500 }
223
+ ```
224
+
225
+ cancel_delay value is in milliseconds.
226
+
227
+
228
+ ## ensure_element
229
+
230
+ To preserve `this` context as being the controller, event binding
231
+ use event.target to retrieve element on which event occurs. This
232
+ has implications. If you have :
233
+
234
+ ```html
235
+ <a href="/do_stuff"><img src="my_image.jpg"></a>
236
+ ```
237
+
238
+ and you bind event on the link, `event.target` may be the image
239
+ element if you clicked on it. Darwin is aware of that on will
240
+ always retrieve the element you asked when passing it to callback.
241
+
242
+ But sometime, you may just want to have that target element.
243
+ You can achieve this using `ensure_element` :
244
+
245
+ ```coffee
246
+ 'Remove clicked element': { el: 'my_block', type: 'click', ensure_element: false }
247
+ ```
248
+
249
+ You probably should use delegation to filter what you want
250
+ more precisely, though.
251
+
252
+
253
+ # Progressive enhancement and graceful degradation
254
+
255
+ Like Darwin.View, Darwin.Controller has a `run()` and
256
+ a `destructor()` method.
257
+
258
+ You may, for example, use `run()` to instantiate plugins.
259
+ Put their teardown calls in `destructor()`.
260
+
261
+ If you override `destructor()`, don't forget to call `super`,
262
+ as controller destruction already handle a lot of stuff under
263
+ the hood, like calling its view destructor, unbinding events
264
+ and removing references to controller.
265
+
266
+
@@ -0,0 +1,228 @@
1
+ Darwin is a javascript framework for people that take error
2
+ handling seriously and want to achieve it through progressive
3
+ enhancement and graceful degradation.
4
+
5
+ Darwin will also let developer write clean and encapsulated
6
+ code that encourages self documentation.
7
+
8
+
9
+ # Module
10
+
11
+ At the core of darwin is the concept of module : a module
12
+ is a specific feature, bound to a specific block of your
13
+ page. This block will be the root of your feature and will
14
+ act as a sandbox. Of course, you may decide to have the whole
15
+ page content except layout as root, or you can choose to
16
+ have a lot of modules which will speak to each other via
17
+ events and callbacks. You can even have modules inside
18
+ modules : that's up to you, darwin will support you anyway.
19
+
20
+ A module is made of a controller and an optional (but almost
21
+ always present) view. Controllers are declared on
22
+ `App.Controllers` and views are declared on `App.Views`. You
23
+ can (and are expected to) create namespaces within those.
24
+
25
+ The suggested file structure is this one :
26
+
27
+ ```
28
+ app/assets/javascripts/
29
+ controllers/
30
+ users.coffee # declare namespace : App.Controllers.Users = {}
31
+ users/
32
+ index.coffee # Controller for index action
33
+ show.cofee # Controller for show action
34
+ followers.coffee # Controller for the "followers" block
35
+ views/
36
+ users.coffee # declare namespace : App.Views.Users = {}
37
+ users/
38
+ index.coffee # View for index action
39
+ show.cofee # View for show action
40
+ followers.coffee # View for the "followers" block
41
+ ```
42
+
43
+ Your application.coffee file should `require_tree` views/ before
44
+ controllers/, as controllers depend on views.
45
+
46
+ You can easily create a module using the generator provided :
47
+
48
+ ```
49
+ $ rails generate darwin admin_area/users/index
50
+
51
+ create app/assets/javascripts/controllers/admin_area.coffee
52
+ create app/assets/javascripts/views/admin_area.coffee
53
+ create app/assets/javascripts/controllers/admin_area/users.coffee
54
+ create app/assets/javascripts/views/admin_area/users.coffee
55
+ create app/assets/javascripts/controllers/admin_area/users/index.coffee
56
+ create app/assets/javascripts/views/admin_area/users/index.coffee
57
+ ```
58
+
59
+ You can also create modules for an whole CRUD resource using camelcase
60
+ form :
61
+
62
+ ```
63
+ $ rails g darwinjs:assets AdminArea::Contact
64
+ create app/assets/javascripts/controllers/admin_area.coffee
65
+ create app/assets/javascripts/views/admin_area.coffee
66
+ create app/assets/javascripts/controllers/admin_area/contacts.coffee
67
+ create app/assets/javascripts/views/admin_area/contacts.coffee
68
+ create app/assets/javascripts/controllers/admin_area/contacts/index.coffee
69
+ create app/assets/javascripts/views/admin_area/contacts/index.coffee
70
+ create app/assets/javascripts/controllers/admin_area/contacts/edit.coffee
71
+ create app/assets/javascripts/views/admin_area/contacts/edit.coffee
72
+ create app/assets/javascripts/controllers/admin_area/contacts/show.coffee
73
+ create app/assets/javascripts/views/admin_area/contacts/show.coffee
74
+ create app/assets/javascripts/controllers/admin_area/contacts/new.coffee
75
+ create app/assets/javascripts/views/admin_area/contacts/new.coffee
76
+ create app/assets/javascripts/controllers/admin_area/contacts/form.coffee
77
+ create app/assets/javascripts/views/admin_area/contacts/form.coffee
78
+ ```
79
+
80
+ Module can be autoloaded, using the `data-module` attribute :
81
+
82
+ ```
83
+ <div id="users" data-module="Users.Index">
84
+ ...
85
+ </div>
86
+ ```
87
+
88
+ This will instantiate `App.Controllers.Users.Index` (which in turn
89
+ instantiates its view).
90
+
91
+ To learn more about autoloader, see [Loader](loader.md).
92
+
93
+
94
+ # Controller
95
+
96
+ A controller will handle anything event or request specific.
97
+ An option set let you define all your events so you can see
98
+ at one glance what the controller is about. See the
99
+ options.events block here :
100
+
101
+ ```coffee
102
+ class App.Controllers.Users.Index extends Darwin.Controller
103
+ @options {
104
+ View: App.Views.Users.Index
105
+
106
+ events:
107
+ 'Toggle user block': { el: 'user_trigger', type: 'click' }
108
+ 'Show user info': { el: 'user_more_trigger', type: 'click' }
109
+ 'Delete user on server': { el: 'user_delete', type: 'click' }
110
+ }
111
+
112
+
113
+ user_trigger_clicked: ->
114
+ if @view.users_shown()
115
+ @view.hide_users()
116
+ else
117
+ @view.show_users()
118
+
119
+
120
+ user_more_trigger_clicked: ( $link ) ->
121
+ @view.show_info_for( $link )
122
+
123
+
124
+ user_delete_clicked: ( $link ) ->
125
+ if confirm( 'Really delete user ?' )
126
+ $.get( $link.attr( 'href' ), =>
127
+ @view.remove_user( $link )
128
+ )
129
+ ```
130
+
131
+ Such self documentation will make sure any developer coming after
132
+ you will understand immediately what the controller is about.
133
+
134
+ As you probably noticed, callback names are automatically computed
135
+ to match your event declaration, so `{ el: user_trigger, type: click}`
136
+ call a `user_trigger_clicked` callback. Two parameters are passed
137
+ to your callback : the jquery extended element and the event object.
138
+
139
+ As most of the time, you want to stop an event after a click, this
140
+ also is the default. So :
141
+
142
+ ```coffee
143
+ class App.Controllers.Users.Index extends Darwin.Controller
144
+ @options {
145
+ View: App.Views.Users.Index
146
+
147
+ events:
148
+ 'Toggle user block': { el: 'user_trigger', type: 'click' }
149
+ }
150
+
151
+ user_trigger_clicked: ( $link )->
152
+ @view.show_user( $link )
153
+ ```
154
+
155
+ Is equivalent to, without darwin :
156
+
157
+ ```
158
+ $('.user_trigger' ).click( ( event ) ->
159
+ $link = $(this)
160
+ event.preventDefault()
161
+ show_user( $link )
162
+ )
163
+ ```
164
+
165
+ Read about [Controller](controller.md) to learn more.
166
+
167
+ # View
168
+
169
+ A view handle everything DOM related. Its main purpose is
170
+ to act as central point of configuration for selectors and
171
+ to do DOM manipulation :
172
+
173
+ ```coffee
174
+ class App.Controllers.Users.Index extends Darwin.Controller
175
+ @options {
176
+ selectors:
177
+ close: 'a.close'
178
+ show_more: '.content .more a'
179
+ users:
180
+ 'sel': '#users'
181
+ delete: 'a[data-method="delete"]'
182
+ }
183
+ ```
184
+
185
+ Now, everywhere in your view methods, you can use `@get( 'close' )`
186
+ to retrieve your close element. In controllers, you can use
187
+ `@view.get( 'close' )` as well. This has two benefits :
188
+
189
+ 1. You have a single point of configuration. If you decide to
190
+ change your DOM, you only have to change configuration here and
191
+ it is reflected on your whole module.
192
+ 2. All elements are cached by default. Don't worry anymore about
193
+ DOM hitting performances.
194
+
195
+ A view is also responsible for setup / teardown your DOM, via
196
+ its `run()` and `destructor()` method. `run()` is called when
197
+ module is initialized ; `destructor()` is called when an error
198
+ occurs (or you manually destruct the module).
199
+
200
+ ```coffee
201
+ class App.Controllers.Users.Index extends Darwin.Controller
202
+ @options {
203
+ selectors:
204
+ submits: 'input[type="submit"]'
205
+ }
206
+
207
+ run: ->
208
+ @get( 'submits' ).hide()
209
+
210
+
211
+ destructor: ->
212
+ @get( 'submits' ).show()
213
+ ```
214
+
215
+ So, `run()` is for progressive enhancement, and `destructor()` is
216
+ for graceful degradation.
217
+
218
+ This will ensure your featureis still # usable even if a
219
+ javascript error occurs.
220
+
221
+ To learn more about views, see [View](view.md).
222
+
223
+
224
+ Finaly, Darwin add some sugar to coffeescript (namely: options pattern,
225
+ mixins and class events). See [Base](base.md), darwin's base class to
226
+ learn more.
227
+
228
+