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.
- data/README.md +19 -14
- data/lib/client/javascripts/nali/application.js.coffee +59 -0
- data/lib/client/javascripts/nali/collection.js.coffee +188 -0
- data/lib/{assets → client}/javascripts/nali/connection.js.coffee +32 -32
- data/lib/{assets → client}/javascripts/nali/controller.js.coffee +28 -30
- data/lib/{assets → client}/javascripts/nali/cookie.js.coffee +5 -5
- data/lib/client/javascripts/nali/deferred.js.coffee +16 -0
- data/lib/client/javascripts/nali/extensions.js.coffee +18 -0
- data/lib/{assets → client}/javascripts/nali/index.js +2 -1
- data/lib/{assets → client}/javascripts/nali/jbone.min.js +1 -1
- data/lib/{assets → client}/javascripts/nali/model.js.coffee +208 -118
- data/lib/{assets → client}/javascripts/nali/nali.js.coffee +32 -32
- data/lib/client/javascripts/nali/notice.js.coffee +29 -0
- data/lib/{assets → client}/javascripts/nali/router.js.coffee +20 -22
- data/lib/{assets → client}/javascripts/nali/view.js.coffee +63 -60
- data/lib/generator/Gemfile +8 -8
- data/lib/generator/app/{assets → client}/javascripts/application.js.coffee +1 -1
- data/lib/generator/app/{assets → client}/javascripts/controllers/homes.js.coffee +4 -4
- data/lib/generator/app/client/javascripts/models/home.js.coffee +5 -0
- data/lib/generator/app/client/javascripts/views/home/index.js.coffee +9 -0
- data/lib/generator/app/{assets → client}/stylesheets/application.css.sass +1 -1
- data/lib/generator/app/{assets → client}/stylesheets/home/index.css.sass +5 -5
- data/lib/generator/app/{assets → client}/stylesheets/notices/error.css.sass +1 -1
- data/lib/generator/app/{assets → client}/stylesheets/notices/info.css.sass +1 -1
- data/lib/generator/app/{assets → client}/stylesheets/notices/warning.css.sass +1 -1
- data/lib/generator/app/{templates → client/templates}/application.html.erb +1 -1
- data/lib/generator/app/{templates → client/templates}/home/index.html +1 -1
- data/lib/generator/app/client/templates/notice/error.html +1 -0
- data/lib/generator/app/client/templates/notice/info.html +1 -0
- data/lib/generator/app/client/templates/notice/warning.html +1 -0
- data/lib/generator/app/server/clients.rb +15 -0
- data/lib/generator/app/{controllers → server/controllers}/application_controller.rb +2 -2
- data/lib/generator/app/{models → server/models}/access.yml +0 -0
- data/lib/generator/{config → app/server}/routes.rb +3 -3
- data/lib/generator/config/environments/development.rb +2 -2
- data/lib/generator/config/environments/production.rb +4 -4
- data/lib/generator/config/environments/test.rb +5 -5
- data/lib/nali/application.rb +24 -23
- data/lib/nali/connection.rb +14 -31
- data/lib/nali/controller.rb +36 -16
- data/lib/nali/generator.rb +38 -23
- data/lib/nali/helpers.rb +3 -3
- data/lib/nali/model.rb +36 -22
- data/lib/nali/tasks.rb +16 -25
- data/lib/nali/version.rb +2 -2
- metadata +85 -86
- data/lib/assets/javascripts/nali/application.js.coffee +0 -28
- data/lib/assets/javascripts/nali/collection.js.coffee +0 -150
- data/lib/assets/javascripts/nali/extensions.js.coffee +0 -19
- data/lib/assets/javascripts/nali/notice.js.coffee +0 -14
- data/lib/generator/app/assets/javascripts/models/home.js.coffee +0 -3
- data/lib/generator/app/assets/javascripts/views/home/index.js.coffee +0 -1
- data/lib/generator/app/templates/notice/error.html +0 -1
- data/lib/generator/app/templates/notice/info.html +0 -1
- data/lib/generator/app/templates/notice/warning.html +0 -1
- data/lib/generator/config/clients.rb +0 -19
- data/lib/generator/config/environments/development.rb~ +0 -32
- data/lib/generator/config/environments/production.rb~ +0 -80
data/README.md
CHANGED
@@ -1,26 +1,31 @@
|
|
1
|
-
#
|
1
|
+
# Добро пожаловать в Nali
|
2
2
|
|
3
|
-
Nali
|
3
|
+
**Nali** - это фреймворк для разработки асинхронных веб приложений, включающий в себя веб-сервер и инструменты для создания как клиентской, так и серверной части. Серверная часть разрабатывается на языке **Ruby**, клиентская часть на **Coffeescript** (или Javascript), **Sass** (или less, scss, css) и **Html** (или erb, haml, slim)
|
4
4
|
|
5
|
-
##
|
5
|
+
## Начало работы
|
6
6
|
|
7
|
-
1.
|
7
|
+
1. Установите Nali с помощью командной строки:
|
8
8
|
|
9
|
-
|
9
|
+
***gem install nali***
|
10
10
|
|
11
|
-
2.
|
11
|
+
2. Для создания нового приложения выполните команду:
|
12
12
|
|
13
|
-
|
13
|
+
***nali new appName***
|
14
14
|
|
15
|
-
|
15
|
+
где "appName" - это имя вашего приложения
|
16
16
|
|
17
|
-
3.
|
17
|
+
3. Перейдите в директорию `appName`:
|
18
18
|
|
19
|
-
|
19
|
+
***cd appName***
|
20
|
+
|
21
|
+
4. Выполните команду для установки зависимостей:
|
20
22
|
|
21
|
-
|
23
|
+
***bundle install***
|
22
24
|
|
23
|
-
|
25
|
+
5. Запустите веб-сервер командой:
|
24
26
|
|
25
|
-
|
26
|
-
|
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
|
-
|
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
|
-
@
|
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
|
-
@
|
5
|
+
@prepareActions()
|
6
|
+
@modelName = @_name.replace /s$/, ''
|
7
7
|
@
|
8
|
-
|
9
|
-
|
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
|
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
|
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
|
-
|
49
|
-
|
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
|
+
@
|