nali 0.2.2 → 0.2.3

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 (58) hide show
  1. data/README.md +19 -14
  2. data/lib/client/javascripts/nali/application.js.coffee +59 -0
  3. data/lib/client/javascripts/nali/collection.js.coffee +188 -0
  4. data/lib/{assets → client}/javascripts/nali/connection.js.coffee +32 -32
  5. data/lib/{assets → client}/javascripts/nali/controller.js.coffee +28 -30
  6. data/lib/{assets → client}/javascripts/nali/cookie.js.coffee +5 -5
  7. data/lib/client/javascripts/nali/deferred.js.coffee +16 -0
  8. data/lib/client/javascripts/nali/extensions.js.coffee +18 -0
  9. data/lib/{assets → client}/javascripts/nali/index.js +2 -1
  10. data/lib/{assets → client}/javascripts/nali/jbone.min.js +1 -1
  11. data/lib/{assets → client}/javascripts/nali/model.js.coffee +208 -118
  12. data/lib/{assets → client}/javascripts/nali/nali.js.coffee +32 -32
  13. data/lib/client/javascripts/nali/notice.js.coffee +29 -0
  14. data/lib/{assets → client}/javascripts/nali/router.js.coffee +20 -22
  15. data/lib/{assets → client}/javascripts/nali/view.js.coffee +63 -60
  16. data/lib/generator/Gemfile +8 -8
  17. data/lib/generator/app/{assets → client}/javascripts/application.js.coffee +1 -1
  18. data/lib/generator/app/{assets → client}/javascripts/controllers/homes.js.coffee +4 -4
  19. data/lib/generator/app/client/javascripts/models/home.js.coffee +5 -0
  20. data/lib/generator/app/client/javascripts/views/home/index.js.coffee +9 -0
  21. data/lib/generator/app/{assets → client}/stylesheets/application.css.sass +1 -1
  22. data/lib/generator/app/{assets → client}/stylesheets/home/index.css.sass +5 -5
  23. data/lib/generator/app/{assets → client}/stylesheets/notices/error.css.sass +1 -1
  24. data/lib/generator/app/{assets → client}/stylesheets/notices/info.css.sass +1 -1
  25. data/lib/generator/app/{assets → client}/stylesheets/notices/warning.css.sass +1 -1
  26. data/lib/generator/app/{templates → client/templates}/application.html.erb +1 -1
  27. data/lib/generator/app/{templates → client/templates}/home/index.html +1 -1
  28. data/lib/generator/app/client/templates/notice/error.html +1 -0
  29. data/lib/generator/app/client/templates/notice/info.html +1 -0
  30. data/lib/generator/app/client/templates/notice/warning.html +1 -0
  31. data/lib/generator/app/server/clients.rb +15 -0
  32. data/lib/generator/app/{controllers → server/controllers}/application_controller.rb +2 -2
  33. data/lib/generator/app/{models → server/models}/access.yml +0 -0
  34. data/lib/generator/{config → app/server}/routes.rb +3 -3
  35. data/lib/generator/config/environments/development.rb +2 -2
  36. data/lib/generator/config/environments/production.rb +4 -4
  37. data/lib/generator/config/environments/test.rb +5 -5
  38. data/lib/nali/application.rb +24 -23
  39. data/lib/nali/connection.rb +14 -31
  40. data/lib/nali/controller.rb +36 -16
  41. data/lib/nali/generator.rb +38 -23
  42. data/lib/nali/helpers.rb +3 -3
  43. data/lib/nali/model.rb +36 -22
  44. data/lib/nali/tasks.rb +16 -25
  45. data/lib/nali/version.rb +2 -2
  46. metadata +85 -86
  47. data/lib/assets/javascripts/nali/application.js.coffee +0 -28
  48. data/lib/assets/javascripts/nali/collection.js.coffee +0 -150
  49. data/lib/assets/javascripts/nali/extensions.js.coffee +0 -19
  50. data/lib/assets/javascripts/nali/notice.js.coffee +0 -14
  51. data/lib/generator/app/assets/javascripts/models/home.js.coffee +0 -3
  52. data/lib/generator/app/assets/javascripts/views/home/index.js.coffee +0 -1
  53. data/lib/generator/app/templates/notice/error.html +0 -1
  54. data/lib/generator/app/templates/notice/info.html +0 -1
  55. data/lib/generator/app/templates/notice/warning.html +0 -1
  56. data/lib/generator/config/clients.rb +0 -19
  57. data/lib/generator/config/environments/development.rb~ +0 -32
  58. data/lib/generator/config/environments/production.rb~ +0 -80
data/README.md CHANGED
@@ -1,26 +1,31 @@
1
- # Welcome to Nali
1
+ # Добро пожаловать в Nali
2
2
 
3
- Nali is the framework for developing async web applications
3
+ **Nali** - это фреймворк для разработки асинхронных веб приложений, включающий в себя веб-сервер и инструменты для создания как клиентской, так и серверной части. Серверная часть разрабатывается на языке **Ruby**, клиентская часть на **Coffeescript** (или Javascript), **Sass** (или less, scss, css) и **Html** (или erb, haml, slim)
4
4
 
5
- ## Getting Started
5
+ ## Начало работы
6
6
 
7
- 1. Install Nali at the command prompt if you haven't yet:
7
+ 1. Установите Nali с помощью командной строки:
8
8
 
9
- gem install nali
9
+ ***gem install nali***
10
10
 
11
- 2. At the command prompt, create a new Nali application:
11
+ 2. Для создания нового приложения выполните команду:
12
12
 
13
- nali new appName
13
+ ***nali new appName***
14
14
 
15
- where "appName" is the application name.
15
+ где "appName" - это имя вашего приложения
16
16
 
17
- 3. Change directory to `appName` and run:
17
+ 3. Перейдите в директорию `appName`:
18
18
 
19
- bundle install
19
+ ***cd appName***
20
+
21
+ 4. Выполните команду для установки зависимостей:
20
22
 
21
- 4. Start the web server
23
+ ***bundle install***
22
24
 
23
- bundle exec thin start
25
+ 5. Запустите веб-сервер командой:
24
26
 
25
- 5. Using a browser, go to `http://localhost:3000` and you'll see:
26
- "Welcome to Nali"
27
+ ***bundle exec thin start***
28
+
29
+ 6. Откройте браузер и перейдите по адресу `http://localhost:3000`
30
+
31
+ Вы увидите первую подготовленную страницу "Welcome to Nali"
@@ -0,0 +1,59 @@
1
+ Nali.extend Application:
2
+
3
+ domEngine: jBone.noConflict()
4
+ useWebSockets: true
5
+ wsServer: 'ws://' + window.location.host
6
+ defaultUrl: 'home'
7
+ notFoundUrl: 'home'
8
+ htmlContainer: 'body'
9
+ title: 'Application'
10
+ keepAliveDelay: 20
11
+
12
+ run: ( options ) ->
13
+ @::starting()
14
+ @[ key ] = value for key, value of options
15
+ @onReadyDOM ->
16
+ @::_ = @domEngine
17
+ @htmlContainer = @_ @htmlContainer
18
+ @setTitle @title
19
+ @Router.start()
20
+ @runConnection()
21
+
22
+ onReadyDOM: ( callback ) ->
23
+ document.addEventListener 'DOMContentLoaded', =>
24
+ document.removeEventListener 'DOMContentLoaded', arguments.callee, false
25
+ callback.call @
26
+ , false
27
+ @
28
+
29
+ runConnection: ->
30
+ if @useWebSockets
31
+ @Connection.subscribe @, 'open', @onConnectionOpen
32
+ @Connection.subscribe @, 'close', @onConnectionClose
33
+ @Connection.subscribe @, 'error', @onConnectionError
34
+ @Connection.open()
35
+ else @redirect()
36
+ @
37
+
38
+ onConnectionOpen: ->
39
+ @redirect()
40
+
41
+ onConnectionClose: ->
42
+
43
+ onConnectionError: ->
44
+
45
+ setTitle: ( @title ) ->
46
+ @titleBox ?= if ( exists = @_ 'head title' ).lenght then exists else @_( '<title>' ).appendTo 'head'
47
+ @titleBox[0].innerText = @title
48
+ @trigger 'update.title'
49
+ @
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
@@ -0,0 +1,188 @@
1
+ Nali.extend Collection:
2
+
3
+ toShowViews: []
4
+ visibleViews: []
5
+ length: 0
6
+
7
+ cloning: ->
8
+ @subscribeTo @Model, "create.#{ @model._name.lower() }", @onModelCreated
9
+ @subscribeTo @Model, "update.#{ @model._name.lower() }", @onModelUpdated
10
+ @adaptations = apply: [], cancel: []
11
+ @ordering = {}
12
+ @adaptCollection()
13
+ @
14
+
15
+ new: ( model, filters ) ->
16
+ @clone model: model, filters: filters
17
+
18
+ onModelCreated: ( extModel, model ) ->
19
+ @add model if not @freezed and model.isCorrect @filters
20
+ @
21
+
22
+ onModelUpdated: ( extModel, model ) ->
23
+ if model.written()
24
+ if model in @
25
+ if @freezed or model.isCorrect @filters
26
+ @reorder()
27
+ @trigger 'update.model', model
28
+ else @remove model
29
+ else if not @freezed and model.isCorrect @filters
30
+ @add model
31
+ @
32
+
33
+ onModelDestroyed: ( model ) ->
34
+ @remove model unless @freezed
35
+ @
36
+
37
+ adaptCollection: ->
38
+ for name, method of @model when /^_\w+$/.test( name ) and typeof method is 'function'
39
+ do ( name, method ) =>
40
+ @[ name = name[ 1.. ] ] = ( args... ) =>
41
+ @each ( model ) -> model[ name ] args...
42
+ @
43
+ @
44
+
45
+ adaptModel: ( model, type = 'apply' ) ->
46
+ adaptation.call @, model for adaptation in @adaptations[ type ]
47
+ @
48
+
49
+ adaptation: ( apply, cancel ) ->
50
+ @each ( model ) -> apply.call @, model
51
+ @adaptations.apply.push apply
52
+ @adaptations.cancel.unshift cancel if cancel
53
+ @
54
+
55
+ add: ( models... ) ->
56
+ for model in [].concat models...
57
+ Array::push.call @, model
58
+ @adaptModel model
59
+ @subscribeTo model, 'destroy', @onModelDestroyed
60
+ @reorder()
61
+ @trigger 'update.length.add', model
62
+ @trigger 'update.length', 'add', model
63
+ @
64
+
65
+ remove: ( model ) ->
66
+ @adaptModel model, 'cancel'
67
+ Array::splice.call @, @indexOf( model ), 1
68
+ @unsubscribeFrom model
69
+ @reorder()
70
+ @trigger 'update.length.remove', model
71
+ @trigger 'update.length', 'remove', model
72
+ @
73
+
74
+ removeAll: ->
75
+ @each ( model ) -> @remove model
76
+ @length = 0
77
+ @
78
+
79
+ each: ( callback ) ->
80
+ callback.call @, model, index for model, index in @
81
+ @
82
+
83
+ pluck: ( property ) ->
84
+ model[ property ] for model in @
85
+
86
+ indexOf: ( model ) ->
87
+ Array::indexOf.call @, model
88
+
89
+ sort: ( sorter ) ->
90
+ Array::sort.call @, sorter
91
+ @
92
+
93
+ toArray: ->
94
+ Array::slice.call @, 0
95
+
96
+ freeze: ->
97
+ @freezed = true
98
+ @
99
+
100
+ unfreeze: ->
101
+ @freezed = false
102
+ @
103
+
104
+ where: ( filters ) ->
105
+ result = []
106
+ result.push model for model in @ when model.isCorrect filters
107
+ result
108
+
109
+ order: ( @ordering ) ->
110
+ @reorder()
111
+ @
112
+
113
+ reorder: ->
114
+ if @ordering.by?
115
+ clearTimeout @ordering.timer if @ordering.timer?
116
+ @ordering.timer = setTimeout =>
117
+ if typeof @ordering.by is 'function'
118
+ @sort @ordering.by
119
+ else
120
+ @sort ( one, two ) =>
121
+ one = one[ @ordering.by ]
122
+ two = two[ @ordering.by ]
123
+ if @ordering.as is 'number'
124
+ one = + one
125
+ two = + two
126
+ if @ordering.as is 'string'
127
+ one = '' + one
128
+ two = '' + two
129
+ ( if one > two then 1 else if one < two then -1 else 0 ) * ( if @ordering.desc then -1 else 1 )
130
+ @orderViews()
131
+ delete @ordering.timer
132
+ , 5
133
+ @
134
+
135
+ orderViews: ->
136
+ if @inside
137
+ children = Array::slice.call @inside.children
138
+ children.sort ( one, two ) => @indexOf( one.view.model ) - @indexOf( two.view.model )
139
+ @inside.appendChild child for child in children
140
+ @
141
+
142
+ show: ( viewName, insertTo, isRelation = false ) ->
143
+ @adaptation ( model ) ->
144
+ view = model.view viewName
145
+ if isRelation
146
+ view.subscribeTo @, 'reset', view.hide
147
+ else unless @visible
148
+ @visible = true
149
+ @prepareViewToShow view
150
+ @hideVisibleViews()
151
+ else
152
+ @::visibleViews.push view
153
+ view.show insertTo
154
+ @inside ?= view.element[0].parentNode
155
+ , ( model ) ->
156
+ model.hide viewName
157
+ @
158
+
159
+ prepareViewToShow: ( view ) ->
160
+ unless view in @::toShowViews
161
+ @::toShowViews.push view
162
+ @prepareViewToShow layout if ( layout = view.layout() )?.childOf? 'View'
163
+ @
164
+
165
+ hideVisibleViews: ->
166
+ view.hide() for view in @::visibleViews when not( view in @::toShowViews )
167
+ @::visibleViews = @::toShowViews
168
+ @::toShowViews = []
169
+ @
170
+
171
+ first: ->
172
+ @[0]
173
+
174
+ last: ->
175
+ @[ @length - 1 ]
176
+
177
+ reset: ->
178
+ @inside = null
179
+ @adaptations.length = 0
180
+ @trigger 'reset'
181
+ @
182
+
183
+ destroy: ->
184
+ @trigger 'destroy'
185
+ @destroyObservation()
186
+ @removeAll()
187
+ @reset()
188
+ @
@@ -1,10 +1,9 @@
1
1
  Nali.extend Connection:
2
2
 
3
3
  initialize: ->
4
- @subscribeTo @Application, 'start', @open
5
- @::query = ( args... ) => @query args...
4
+ @::expand query: ( args... ) => @query args...
6
5
  @
7
-
6
+
8
7
  open: ->
9
8
  @dispatcher = new WebSocket @Application.wsServer
10
9
  @dispatcher.onopen = ( event ) => @onOpen event
@@ -13,30 +12,30 @@ Nali.extend Connection:
13
12
  @dispatcher.onmessage = ( event ) => @onMessage JSON.parse event.data
14
13
  @keepAlive()
15
14
  @
16
-
17
- connected: false
15
+
18
16
  keepAliveTimer: null
19
17
  journal: []
20
-
18
+ reconnectDelay: 0
19
+
21
20
  onOpen: ( event ) ->
22
- @connected = true
21
+ @reconnectDelay = 0
23
22
  @trigger 'open'
24
-
25
- onMessage: ( message ) ->
26
- @[ message.action ] message
27
-
28
- onClose: ( event ) ->
29
- @connected = false
30
- @trigger 'close'
31
-
23
+
32
24
  onError: ( event ) ->
33
25
  console.warn 'Connection error %O', event
34
-
26
+
27
+ onClose: ( event ) ->
28
+ @trigger 'close'
29
+ setTimeout ( => @open() ), @reconnectDelay * 100
30
+ @reconnectDelay += 1
31
+
32
+ onMessage: ( message ) ->
33
+ @[ message.action ] message
34
+
35
35
  send: ( msg ) ->
36
- @open() unless @connected
37
36
  @dispatcher.send JSON.stringify msg
38
37
  @
39
-
38
+
40
39
  keepAlive: ->
41
40
  clearTimeout @keepAliveTimer if @keepAliveTimer
42
41
  if @Application.keepAliveDelay
@@ -45,38 +44,39 @@ Nali.extend Connection:
45
44
  @send ping: true
46
45
  , @Application.keepAliveDelay * 1000
47
46
  @
48
-
47
+
49
48
  pong: ->
50
49
  @keepAlive()
51
50
  @
52
-
51
+
53
52
  sync: ( message ) ->
54
- @Model.sync message.params
53
+ @Model.sync message.params
55
54
  @
56
-
55
+
57
56
  notice: ( { model, notice, params } ) ->
58
57
  if model?
59
58
  [ model, id ] = model.split '.'
60
59
  @Model.notice model: model, id: id, notice: notice, params: params
61
- else @Notice[ notice ] params
62
- @
63
-
60
+ else @Notice[ notice ] params
61
+ @
62
+
64
63
  success: ( message ) ->
65
64
  @journal[ message.journal_id ].success? message.params
66
- delete @journal[ message.journal_id ]
65
+ delete @journal[ message.journal_id ]
67
66
  @
68
-
67
+
69
68
  failure: ( message ) ->
70
69
  @journal[ message.journal_id ].failure? message.params
71
- delete @journal[ message.journal_id ]
70
+ delete @journal[ message.journal_id ]
72
71
  @
73
-
72
+
74
73
  query: ( to, params, success, failure ) ->
74
+ return success?() unless @Application.useWebSockets
75
75
  [ controller, action ] = to.split '.'
76
76
  @journal.push callbacks = success: success, failure: failure
77
- @send
77
+ @send
78
78
  controller: controller
79
79
  action: action
80
- params: params
80
+ params: params
81
81
  journal_id: @journal.indexOf callbacks
82
- @
82
+ @
@@ -2,12 +2,13 @@ Nali.extend Controller:
2
2
 
3
3
  extension: ->
4
4
  if @_name isnt 'Controller'
5
- @prepareActions()
6
- @modelSysname = @_name.replace /s$/, ''
5
+ @prepareActions()
6
+ @modelName = @_name.replace /s$/, ''
7
7
  @
8
-
9
- actions: {}
10
-
8
+
9
+ new: ( collection, filters, params ) ->
10
+ @clone collection: collection, filters: filters, params: params
11
+
11
12
  prepareActions: ->
12
13
  @_actions = {}
13
14
  for name, action of @actions when not ( name in [ 'default', 'before', 'after' ] )
@@ -19,20 +20,20 @@ Nali.extend Controller:
19
20
  @_actions[ name ] = filters: filters, params: params, methods: [ action ]
20
21
  @prepareBefores()
21
22
  @prepareAfters()
22
- @
23
-
23
+ @
24
+
24
25
  prepareBefores: ->
25
- if @actions.before?
26
- list = @analizeFilters 'before'
26
+ if @actions?.before?
27
+ list = @analizeFilters 'before'
27
28
  @_actions[ name ].methods = actions.concat @_actions[ name ].methods for name, actions of list
28
29
  @
29
-
30
+
30
31
  prepareAfters: ->
31
- if @actions.after?
32
- list = @analizeFilters 'after'
32
+ if @actions?.after?
33
+ list = @analizeFilters 'after'
33
34
  @_actions[ name ].methods = @_actions[ name ].methods.concat actions for name, actions of list
34
35
  @
35
-
36
+
36
37
  analizeFilters: ( type ) ->
37
38
  list = {}
38
39
  for names, action of @actions[ type ]
@@ -41,30 +42,27 @@ Nali.extend Controller:
41
42
  when names is '*' then [ true, [] ]
42
43
  else [ false, names.split /\s*,\s*/ ]
43
44
  for name of @_actions when ( invert and not ( name in names ) ) or ( not invert and name in names )
44
- ( list[ name ] ?= [] ).push action
45
+ ( list[ name ] ?= [] ).push action
45
46
  list
46
-
47
+
47
48
  run: ( action, filters, params ) ->
48
- controller = @clone
49
- collection: @Model.extensions[ @modelSysname ].where filters
50
- params: params
51
- controller.runAction action
52
- if controller.stopped
53
- controller.collection.destroy()
54
- else
55
- controller.collection.show action
56
- @Router.setUrl()
49
+ collection = @Model.extensions[ @modelName ].where filters
50
+ @new( collection, filters, params ).runAction action
57
51
  @
58
-
52
+
59
53
  runAction: ( name ) ->
60
- method.call @ for method in @_actions[ name ].methods when not @stopped
54
+ method.call @ for method in @_actions[ name ].methods when not @stopped
55
+ if @stopped then @collection.destroy()
56
+ else
57
+ @collection.show name
58
+ @Router.changeUrl()
61
59
  @
62
-
60
+
63
61
  stop: ->
64
62
  @stopped = true
65
- @
66
-
63
+ @
64
+
67
65
  redirect: ( args... ) ->
68
66
  @Router.go args...
69
67
  @stop()
70
- @
68
+ @