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/loader.md ADDED
@@ -0,0 +1,116 @@
1
+ Darwin.Loader ensure only the needed javascript is executed.
2
+
3
+ # Autoloading
4
+
5
+ If you do something like this :
6
+
7
+ ```coffee
8
+ $( '#my_module' ).each( ( i, el ) ->
9
+ new App.Controllers.MyModule( $(el) )
10
+ )
11
+ ```
12
+
13
+ You may think it's ok even if `#my_module` is only present on a
14
+ single page of your application, because jQuery will simply do
15
+ nothing if it doesn't find the element.
16
+
17
+ It's not ok. If you do that, DOM will be hit to find the element
18
+ on every single page. Hitting the DOM is expansive, you should not
19
+ do it if it's not necessary. When you'll have 100 controllers in
20
+ your app and DOM hitted 100 times on each page load to find module
21
+ elements, slow js engines will begin to suffer before you actually
22
+ do anything.
23
+
24
+ Darwin.Loader provides a mean to only load what you need. Add a
25
+ data-module attribute to the target block, and Darwin.Loader will
26
+ do a single DOM request to retrieve '*[data-module'] and initialize
27
+ your controllers :
28
+
29
+ ```html
30
+ <div id="users" data-module="AdminArea.Users.Show">
31
+ <ul>
32
+ <li>John</li>
33
+ <li>Joe</li>
34
+ </ul>
35
+ </div>
36
+ ```
37
+
38
+ is equivalent to :
39
+
40
+ ```coffee
41
+ new App.Controllers.AdminArea.Users.Show( $( '#users' ) )
42
+ ```
43
+
44
+ To start, loader, add this in your application code :
45
+
46
+ ```coffee
47
+ Darwin.Loader.run()
48
+ ```
49
+
50
+
51
+ # Error handling
52
+
53
+ Darwin takes error handling seriously. The default behavior is to
54
+ deactivate javascript app on any error occuring in it. If your app
55
+ code produce an error, all its event callbacks are deactivated and
56
+ controllers' destructor methods are called. This is a good thing.
57
+
58
+ Consider the following :
59
+
60
+ ```html
61
+ <div id="items"></div>
62
+ <a href="#">show more</a>
63
+ ```
64
+
65
+ ```coffee
66
+ $( 'a' ).click( ->
67
+ please_crash()
68
+ $( '#items' ).load( '/more_items' )
69
+ )
70
+ ```
71
+
72
+ What happens when you click the link ? Nothing. What happens if
73
+ you click again ? And again, and again ? Still nothing. At this
74
+ point if you're a developer, your instinct will suggest you reload
75
+ the page. Other people will get angry because "it does not work" and
76
+ leave your application.
77
+
78
+ Here is the proper Darwin equivalent :
79
+
80
+ ```html
81
+ <div data-module="ShowMore">
82
+ <div id="items"></div>
83
+ <a href="/show_more">show more</a>
84
+ </div>
85
+ ```
86
+
87
+ ```coffee
88
+ class App.Views.ShowMore extends Darwin.View
89
+ @options {
90
+ link: 'a'
91
+ items: '#items'
92
+ }
93
+
94
+ class App.Controllers.ShowMore extends Darwin.Controller
95
+ @options {
96
+ View: App.Views.ShowMore
97
+
98
+ events:
99
+ 'Display more items': { el: 'link', type: 'click' }
100
+ }
101
+
102
+ link_clicked: ( $link ) ->
103
+ please_crash()
104
+ @view.get( 'items' ).load( "#{$link.attr( 'href' )} #items" )
105
+ ```
106
+
107
+ When you click the first time, error will occurs and nothing
108
+ will happen. If you click a second time, as event callbacks have
109
+ been deactivated, the link will be followed and user won't realize
110
+ something bad happened. That means, by the way, that javascript
111
+ will have been reloaded.
112
+
113
+ Additionnaly, if you provided an url in the `window.js_exception_url`,
114
+ an ajax request will be fired to report the error.
115
+
116
+
data/doc/view.md ADDED
@@ -0,0 +1,282 @@
1
+ Darwin.View is the base class for views, which handle all
2
+ DOM traversal and manipulation.
3
+
4
+ Darwin.View main purpose is to provide a central configuration
5
+ point between your javascript app and your DOM tree. It also
6
+ eases DOM elements caching.
7
+
8
+ Actually, elements caching is so important that it's the
9
+ default behavior, and you have to explicitely tell you don't
10
+ want caching on some specific selector.
11
+
12
+
13
+ # Selectors
14
+
15
+ The problem that Darwin.View addresses is breaking your js
16
+ application when you alter your DOM structure.
17
+
18
+ Consider the following :
19
+
20
+ ```html
21
+ <aside id="sidebar">
22
+ <ul class="users">
23
+ <li>John</li>
24
+ <li>Joe</li>
25
+ </ul>
26
+ <a class="hide">hide</a>
27
+ </aside>
28
+
29
+ <article>
30
+ <h1>Users activity</h1>
31
+
32
+ <p>Last week, 402 users registered.</p>
33
+ <p>2311 has been active is that period.</p>
34
+ </article>
35
+ ```
36
+
37
+ If you want to do special things on users with javascript,
38
+ you'll probably have things like that across your js codebase :
39
+
40
+ ```coffee
41
+ $( '#sidebar .users li' ).hover( do_stuff )
42
+
43
+ $( '#sidebar a.hide' ).click( ->
44
+ $( '#sidebar .users' ).hide()
45
+ $( '#sidebar a.hide' ).hide()
46
+ )
47
+ ```
48
+
49
+ Now, what if you decide user list should be called `#user_list`
50
+ rather than `.users` and should be in the article rather than in
51
+ sidebar ? You have to browse your whole codebase to change selectors,
52
+ possibly missing a few and causing crashes.
53
+
54
+ Darwin.View acts as a center point of configuration for selectors :
55
+
56
+ ```html
57
+ <div id="page">
58
+ <aside id="sidebar">
59
+ <ul class="users">
60
+ <li>John</li>
61
+ <li>Joe</li>
62
+ </ul>
63
+ <a class="hide">hide</a>
64
+ </aside>
65
+
66
+ <article>
67
+ <h1>Users activity</h1>
68
+
69
+ <p>Last week, 402 users registered.</p>
70
+ <p>2311 has been active is that period.</p>
71
+ </article>
72
+ </div>
73
+ ```
74
+
75
+ ```coffee
76
+ class MyView extends Darwin.View
77
+ @options {
78
+ selectors:
79
+ user_list:
80
+ 'sel': '#sidebar .users'
81
+ item: 'li'
82
+ hide_users: '#sidebar a.hide'
83
+
84
+ }
85
+ ```
86
+
87
+ Now, you can use the view to retrieve elements :
88
+
89
+ ```coffee
90
+ view = new MyView( '#page' ) # note : in real case, controller will initialize
91
+ # view, no need to do it yourself.
92
+
93
+ view.get( 'root' ) # return the root element for view
94
+
95
+ view.get( 'item' ).hover( do_stuff )
96
+
97
+ view.get( 'hide_users' ).click( ->
98
+ view.get( 'user_list' ).hide()
99
+ view.get( 'hide_users' ).hide()
100
+ )
101
+ ```
102
+
103
+ Please note a view is always bound to a specific part of the DOM,
104
+ the element passed at initialization (or the element on which
105
+ the data-module attribute is set, if you use Darwin.Loader's
106
+ autoload). All selectors are relative to that element. You may
107
+ retrieve it using `@get( 'root' )`.
108
+
109
+ If you decide user list should be in article body, all you
110
+ have to do is to change selectors declaration :
111
+
112
+ ```coffee
113
+ class MyView extends Darwin.View
114
+ @options {
115
+ selectors:
116
+ user_list:
117
+ 'sel': 'article .users'
118
+ item: 'li'
119
+ hide_users: 'article a.hide'
120
+
121
+ }
122
+ ```
123
+
124
+ ... and the change is reflected on your whole codebase.
125
+
126
+
127
+ ## Selector declaration
128
+
129
+ A selector can be either a css selector string or a configuration
130
+ object :
131
+
132
+ ```coffee
133
+ @options {
134
+ selectors:
135
+ foo: '#foos .foo'
136
+ bar: { 'sel': '#bars .bar' }
137
+ }
138
+ ```
139
+
140
+ It is recommanded to put configuration keys (like `sel`, here)
141
+ into a string so syntax highlighting make it easy to see selector
142
+ names (`foo`, `bar`) at one glance.
143
+
144
+ Selectors may be embedded :
145
+
146
+ ```coffee
147
+ @options {
148
+ selectors:
149
+ foo:
150
+ 'sel': '#foos'
151
+ bar: '#bars'
152
+ }
153
+ ```
154
+
155
+ `#bars` can be accessed with `view.get( 'foo_bar' )`, which will
156
+ translate to `$( '#foos #bars' )`. If the embedded selector name
157
+ is unambiguous, it can be used directly :
158
+
159
+ ```coffee
160
+ class MyView extends Darwin.View
161
+ @options {
162
+ selectors:
163
+ foo:
164
+ 'sel': '#foos'
165
+ wrong: 'p'
166
+ bar:
167
+ 'sel': '#bars'
168
+ wrong: 'a'
169
+ }
170
+
171
+ view = new MyView( $( '#page' ) )
172
+
173
+ view.get( 'bar' ) # Same as view.get( 'foo_bar' ).
174
+
175
+ view.get( 'wrong' ) # Will throw ambiguous selector error.
176
+ # Use explicit view.get( 'foo_bar_wrong' )
177
+ # or view.get( 'foo_wrong' )
178
+ ```
179
+
180
+
181
+ # Caching
182
+
183
+ The first time you ask view for a selector, it is cached, to avoid
184
+ hitting the DOM again and again when you access elements. This is
185
+ what you want most of the times.
186
+
187
+ But sometime, you do not want caching. For example, items in a
188
+ list may be added or removed.
189
+
190
+ You can specify element should not be cached by using the cache option :
191
+
192
+ ```coffee
193
+ @options {
194
+ selectors:
195
+ foo: { 'sel': '#foos', 'cache': false }
196
+ }
197
+ ```
198
+
199
+ You may also keep default caching, but empty the cache at a given
200
+ point :
201
+
202
+ ```coffee
203
+ view.clear_cache( 'foo' ) # removed cache for `foo` selector
204
+ view.clear_cache() # empty the whole cache
205
+ ```
206
+
207
+
208
+ # Dynamic getter
209
+
210
+ You can also define methods to retrieve element, be it to create
211
+ the element in first place or because you need specific logic.
212
+ Simply declare a method named after your selector and prefixed
213
+ with `get_` :
214
+
215
+ ```coffee
216
+ class MyView extend Darwin.View
217
+ get_foo: ->
218
+ @$foo ?= $( '<div id="foo"></div>' ).appendTo( @get( 'root' ) )
219
+
220
+ my_view = new MyView( $( '#page' ) )
221
+ my_view.get( 'foo' ) # creates #foo and returns it
222
+ ```
223
+
224
+ You're responsible to cache elements retrieved by getter methods.
225
+
226
+
227
+ # Progressive enhancement and graceful degradation
228
+
229
+ You should build your features so they work without javascript.
230
+ There's a good reason for that : if an error occurs in your
231
+ javascript codebase, events will be deactivated and plain html
232
+ features will ask server to handle the feature.
233
+
234
+ This means two important things :
235
+
236
+ * your javascript should prepare DOM on run
237
+ * it should restore its previous state on destruction
238
+
239
+ Take the following example :
240
+
241
+ ```html
242
+ <div id="page">
243
+ <form action="/search" method="post">
244
+ <input type="search" />
245
+ <input type="submit" />
246
+ </form>
247
+ </div>
248
+ ```
249
+
250
+ ```coffee
251
+ class MyView extends Darwin.View
252
+ @options {
253
+ selectors:
254
+ search_field: 'input[type="search"]'
255
+ submit: 'input[type="submit"]'
256
+ }
257
+
258
+
259
+ run: ->
260
+ @get( 'submit' ).hide()
261
+
262
+
263
+ destructor: ->
264
+ @get( 'submit' ).show()
265
+ ```
266
+
267
+ When your view is initialized, the submit button will be hidden.
268
+ You probably will handle search field change through ajax
269
+ request in your controller.
270
+
271
+ If an error occurs, view's destructor method will be called,
272
+ and the submit input will be shown again, so user can continue
273
+ using the feature.
274
+
275
+ Therefore, it's important any change you make in the run() method
276
+ has a pending teardown in the destructor() method.
277
+
278
+ As showing and hiding submit buttons is quite common, their
279
+ handling is natively implemented. For this kind of buttons, simply
280
+ add a `fallback-submits` class on them.
281
+
282
+
@@ -0,0 +1,9 @@
1
+ require 'coffee/rails/engine'
2
+
3
+ module Darwinjs
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ config.app_generators.javascript_engine :darwinjs
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Darwinjs
2
+ module Rails
3
+ VERSION = "1.0.1"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'darwinjs/rails/engine'
2
+ require 'darwinjs/rails/version'
@@ -0,0 +1,78 @@
1
+ require "rails/generators/named_base"
2
+
3
+ module Darwinjs
4
+ module Generators
5
+ class AssetsGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def create_module
9
+ if is_resource?
10
+ create_resource
11
+ else
12
+ create_action( ( class_path + [ file_name ] ).join( '/' ) )
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def is_resource?
19
+ name.underscore != name
20
+ end
21
+
22
+ def create_resource
23
+ %w[index edit show new form].each do |action|
24
+ create_action( ( class_path + [ file_name.pluralize, action ] ).join( '/' ) )
25
+ end
26
+ end
27
+
28
+ def create_action( action )
29
+ create_namespaces( action )
30
+ create_controller( action )
31
+ create_view( action )
32
+ end
33
+
34
+ def create_namespaces( action )
35
+ current_path = []
36
+
37
+ namespaces_for( action ).each do |namespace|
38
+ current_path << namespace
39
+ namespace_dir = current_path.join( '/' )
40
+ namespace_file = namespace_dir + '.coffee'
41
+ @namespace = current_path.map( &:camelize ).join( '.' )
42
+
43
+ unless File.exists?( controllers_path.join( namespace_file ) )
44
+ template 'controllers/namespace.coffee', controllers_path.join( namespace_file ).to_s
45
+ end
46
+
47
+ unless File.exists?( views_path.join( namespace_file ) )
48
+ template 'views/namespace.coffee', views_path.join( namespace_file ).to_s
49
+ end
50
+ end
51
+ end
52
+
53
+ def create_controller( action )
54
+ @js_path = action.split( '/' ).map( &:camelize ).join( '.' )
55
+ template 'controllers/controller.coffee', controllers_path.join( "#{action}.coffee" )
56
+ end
57
+
58
+ def create_view( action )
59
+ @js_path = action.split( '/' ).map( &:camelize ).join( '.' )
60
+ template 'views/view.coffee', views_path.join( "#{action}.coffee" )
61
+ end
62
+
63
+ def namespaces_for( action )
64
+ parts = action.split( '/' )
65
+ parts.pop
66
+ parts
67
+ end
68
+
69
+ def controllers_path
70
+ @controllers_path ||= ::Rails.root.join( 'app', 'assets', 'javascripts', 'controllers' )
71
+ end
72
+
73
+ def views_path
74
+ @views_path ||= ::Rails.root.join( 'app', 'assets', 'javascripts', 'views' )
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,5 @@
1
+ class App.Controllers.<%= @js_path %> extends Darwin.Controller
2
+ @options {
3
+ View: App.Views.<%= @js_path %>
4
+ events: {}
5
+ }
@@ -0,0 +1 @@
1
+ App.Controllers.<%= @namespace %> = {}
@@ -0,0 +1,2 @@
1
+ App.Views.<%= @namespace %> = {}
2
+
@@ -0,0 +1,5 @@
1
+ class App.Views.<%= @js_path %> extends Darwin.View
2
+ @options {
3
+ selectors: {}
4
+ }
5
+
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: darwinjs-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Olivier El Mekki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: railties
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.2'
30
+ - !ruby/object:Gem::Dependency
31
+ name: coffee-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '3.2'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.2'
46
+ - !ruby/object:Gem::Dependency
47
+ name: jquery-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Javascript framework with progressive enhancement in mind.
95
+ email:
96
+ - olivier@el-mekki.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - Gemfile
103
+ - LICENSE.txt
104
+ - README.md
105
+ - Rakefile
106
+ - app/assets/javascripts/darwin.coffee
107
+ - app/assets/javascripts/darwin/base.coffee
108
+ - app/assets/javascripts/darwin/controller.coffee
109
+ - app/assets/javascripts/darwin/loader.coffee
110
+ - app/assets/javascripts/darwin/template.coffee
111
+ - app/assets/javascripts/darwin/view.coffee
112
+ - darwinjs-rails.gemspec
113
+ - doc/base.md
114
+ - doc/controller.md
115
+ - doc/introduction.md
116
+ - doc/loader.md
117
+ - doc/view.md
118
+ - lib/darwinjs-rails.rb
119
+ - lib/darwinjs/rails/engine.rb
120
+ - lib/darwinjs/rails/version.rb
121
+ - lib/generators/darwinjs/assets/assets_generator.rb
122
+ - lib/generators/darwinjs/assets/templates/controllers/controller.coffee
123
+ - lib/generators/darwinjs/assets/templates/controllers/namespace.coffee
124
+ - lib/generators/darwinjs/assets/templates/views/namespace.coffee
125
+ - lib/generators/darwinjs/assets/templates/views/view.coffee
126
+ homepage: https://github.com/oelmekki/darwinjs-rails
127
+ licenses:
128
+ - MIT
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 1.8.24
148
+ signing_key:
149
+ specification_version: 3
150
+ summary: Darwin lets create complex javascript interfaces that do not expect they
151
+ own the application and that degrade gracefully when an error occurs
152
+ test_files: []
153
+ has_rdoc: