nali 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
@
|