joosy 1.2.0.alpha.38 → 1.2.0.alpha.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gruntfile.coffee +15 -7
  3. data/bower.json +4 -3
  4. data/lib/extensions/resources.js +11 -3
  5. data/lib/joosy.js +235 -300
  6. data/package.json +3 -3
  7. data/spec/helpers/ground.coffee +33 -0
  8. data/spec/helpers/matchers.coffee +65 -0
  9. data/spec/joosy/core/application_spec.coffee +8 -8
  10. data/spec/joosy/core/helpers/view_spec.coffee +9 -5
  11. data/spec/joosy/core/helpers/widgets_spec.coffee +43 -10
  12. data/spec/joosy/core/joosy_spec.coffee +42 -50
  13. data/spec/joosy/core/layout_spec.coffee +30 -34
  14. data/spec/joosy/core/modules/container_spec.coffee +79 -76
  15. data/spec/joosy/core/modules/events_spec.coffee +148 -81
  16. data/spec/joosy/core/modules/filters_spec.coffee +68 -49
  17. data/spec/joosy/core/modules/log_spec.coffee +13 -5
  18. data/spec/joosy/core/modules/module_spec.coffee +24 -14
  19. data/spec/joosy/core/modules/renderer_spec.coffee +95 -89
  20. data/spec/joosy/core/modules/time_manager_spec.coffee +11 -16
  21. data/spec/joosy/core/modules/widget_manager_spec.coffee +89 -71
  22. data/spec/joosy/core/page_spec.coffee +201 -137
  23. data/spec/joosy/core/templaters/jst_spec.coffee +62 -0
  24. data/spec/joosy/core/widget_spec.coffee +25 -29
  25. data/spec/joosy/extensions/form/form_spec.coffee +3 -1
  26. data/spec/joosy/extensions/resources/base_spec.coffee +3 -0
  27. data/spec/joosy/extensions/resources/collection_spec.coffee +3 -0
  28. data/spec/joosy/extensions/resources/rest_collection_spec.coffee +3 -0
  29. data/spec/joosy/extensions/resources/rest_spec.coffee +3 -0
  30. data/src/joosy/core/application.coffee +1 -7
  31. data/src/joosy/core/helpers/view.coffee +12 -0
  32. data/src/joosy/core/helpers/widgets.coffee +19 -9
  33. data/src/joosy/core/joosy.coffee +0 -23
  34. data/src/joosy/core/layout.coffee +7 -5
  35. data/src/joosy/core/module.coffee +24 -4
  36. data/src/joosy/core/modules/container.coffee +29 -28
  37. data/src/joosy/core/modules/events.coffee +85 -72
  38. data/src/joosy/core/modules/filters.coffee +3 -1
  39. data/src/joosy/core/modules/renderer.coffee +91 -74
  40. data/src/joosy/core/modules/widgets_manager.coffee +12 -9
  41. data/src/joosy/core/page.coffee +7 -14
  42. data/src/joosy/core/templaters/{rails_jst.coffee → jst.coffee} +21 -19
  43. data/src/joosy/core/widget.coffee +3 -3
  44. data/src/joosy/extensions/resources/base.coffee +8 -3
  45. data/src/joosy/extensions/resources/rest.coffee +8 -0
  46. data/src/joosy/extensions/resources/rest_collection.coffee +1 -0
  47. data/tasks/joosy.coffee +46 -17
  48. data/templates/application/base/pages/welcome/index.coffee +5 -5
  49. data/templates/application/base/templates/layouts/application.jst.hamlc +2 -2
  50. data/templates/application/base/templates/pages/welcome/index.jst.hamlc +2 -2
  51. data/templates/application/standalone/Gruntfile.coffee +3 -1
  52. metadata +6 -5
  53. data/spec/helpers/helper.coffee +0 -68
  54. data/spec/joosy/core/templaters/rails_jst_spec.coffee +0 -25
@@ -1,47 +1,57 @@
1
1
  describe "Joosy.Module", ->
2
2
 
3
- it "should track inheritance", ->
3
+ it "tracks inheritance", ->
4
4
  class A
5
5
  class B extends A
6
6
  class C extends B
7
7
  class D
8
+
8
9
  for a in [A, B, C, D]
9
10
  for b in [A, B, C, D]
10
11
  if (a == b) ||
11
12
  ((a == B) && (b == A)) ||
12
13
  ((a == C) && (b != D))
13
- expect(Joosy.Module.hasAncestor.apply(null, [a, b])).toBeTruthy()
14
+ expect(Joosy.Module.hasAncestor a, b).toBeTruthy()
14
15
  else
15
- expect(Joosy.Module.hasAncestor.apply(null, [a, b])).toBeFalsy()
16
+ expect(Joosy.Module.hasAncestor a, b).toBeFalsy()
16
17
 
17
- it "should include properties into prototype", ->
18
- TestModule =
18
+ # We need this check to ensure we are not overpolluting the namespace
19
+ it "has minimal set of properties", ->
20
+ class Klass extends Joosy.Module
21
+
22
+ expect(Object.extended(Klass).keys()).toEqual ['__namespace__', '__className', 'hasAncestor', 'aliasMethodChain', 'aliasStaticMethodChain', 'merge', 'include', 'extend', '__super__']
23
+ expect(Object.extended(Klass.prototype).keys()).toEqual ['constructor']
24
+
25
+ it "includes", ->
26
+ Module =
19
27
  property: 'value'
28
+
20
29
  class Klass extends Joosy.Module
21
- @include TestModule
30
+ @include Module
31
+
22
32
  expect(Klass::property).toEqual 'value'
23
- expect((new Klass()).property).toEqual 'value'
33
+ expect(Klass.property).toBeUndefined()
24
34
 
25
- it "should extend object", ->
35
+ it "extends", ->
26
36
  TestModule =
27
37
  property: 'value'
38
+
28
39
  class Klass extends Joosy.Module
29
40
  @extend TestModule
41
+
30
42
  expect(Klass.property).toEqual 'value'
31
- expect((new Klass()).property).toBeUndefined()
43
+ expect(Klass::property).toBeUndefined()
32
44
 
33
- it "should run callbacks on include and extend", ->
45
+ it "runs callbacks", ->
34
46
  TestModule =
35
47
  property: 'value'
36
48
  included: sinon.spy()
37
49
  extended: sinon.spy()
50
+
38
51
  class Klass extends Joosy.Module
39
52
  @include TestModule
40
53
  @extend TestModule
54
+
41
55
  for callback in ['included', 'extended']
42
56
  expect(TestModule[callback].callCount).toEqual 1
43
57
  expect(TestModule[callback].getCall(0).calledOn(Klass)).toBeTruthy()
44
-
45
- it "should have minimal set of properties", ->
46
- expect(Object.extended(Joosy.Module).keys()).toEqual ['__namespace__', '__className', 'hasAncestor', 'alias', 'aliasStatic', 'merge', 'include', 'extend']
47
- expect(Object.extended(Joosy.Module.prototype).keys()).toEqual []
@@ -1,136 +1,142 @@
1
1
  describe "Joosy.Modules.Renderer", ->
2
2
 
3
3
  beforeEach ->
4
- @seedGround()
5
-
6
- class @TestContainer extends Joosy.Module
4
+ class @Renderer extends Joosy.Module
7
5
  @include Joosy.Modules.Renderer
8
6
 
9
- @dummyContainer = new @TestContainer
10
-
11
- class @TestObject extends Joosy.Module
12
- @include Joosy.Modules.Events
13
-
14
- constructor: (@value) ->
7
+ @renderer = new @Renderer
15
8
 
16
- update: (@value) ->
17
- @trigger 'changed'
9
+ it "renders default template", ->
10
+ template = sinon.stub()
11
+ template.returns "result"
18
12
 
19
- @dummyObject = new @TestObject("initial")
13
+ @Renderer.view template
20
14
 
21
- Joosy.namespace 'Joosy.Helpers.Hoge', ->
22
- @multiplier = (value) ->
23
- "#{value * 5}"
15
+ expect(@renderer.__renderDefault(foo: 'bar')).toEqual 'result'
16
+ expect(template.getCall(0).args[0].foo).toEqual 'bar'
17
+ expect(template.getCall(0).args[0].__renderer).toEqual @renderer
24
18
 
25
- it "updates contents, but only while it is bound to DOM", ->
26
- @TestContainer.view (locals) ->
27
- template = -> @object.value
19
+ describe "rendering", ->
20
+ beforeEach ->
21
+ @template = (locals) =>
22
+ expect(locals.foo).toEqual 'bar'
23
+ expect(locals.__renderer).toEqual @renderer
24
+ "result"
28
25
 
29
- @renderDynamic(template, locals)
26
+ it "accepts lambda", ->
27
+ expect(@renderer.render @template, foo: 'bar').toEqual 'result'
30
28
 
31
- elem = $("<div></div>")
32
- @ground.append elem
29
+ it "accepts template", ->
30
+ target = sinon.stub Joosy.Application.templater, 'buildView'
31
+ target.returns @template
33
32
 
34
- elem.html @dummyContainer.__renderer({ object: @dummyObject })
35
- expect(elem.text()).toBe "initial"
33
+ expect(@renderer.render @template, foo: 'bar').toEqual 'result'
36
34
 
37
- @dummyObject.update "new"
35
+ Joosy.Application.templater.buildView.restore()
38
36
 
39
- waits 0
37
+ describe "dynamic rendering", ->
38
+ beforeEach ->
39
+ # Instance we are going to use to trigger dynamic rendering
40
+ class @Entity extends Joosy.Module
41
+ @include Joosy.Modules.Events
40
42
 
41
- runs ->
42
- expect(elem.text()).toBe "new"
43
+ constructor: (@value) ->
43
44
 
44
- waits 0
45
+ update: (@value) ->
46
+ @trigger 'changed'
45
47
 
46
- runs ->
47
- @dummyContainer.__removeMetamorphs()
48
- @dummyObject.update "afterwards"
48
+ @entity = new @Entity("initial")
49
49
 
50
- waits 0
50
+ it "updates content", ->
51
+ template = (locals) -> locals.entity.value
51
52
 
52
- runs ->
53
- expect(elem.text()).toBe "new"
53
+ runs ->
54
+ @$ground.html @renderer.renderDynamic(template, entity: @entity)
55
+ expect(@$ground.text()).toBe "initial"
54
56
 
55
- it "debounces morpher updates", ->
56
- @TestContainer.view (locals) ->
57
- template = -> @object.value
57
+ runs ->
58
+ @entity.update "new"
58
59
 
59
- @renderDynamic(template, locals)
60
+ waits 0
60
61
 
61
- elem = $("<div></div>")
62
- @ground.append elem
62
+ runs ->
63
+ expect(@$ground.text()).toBe "new"
63
64
 
64
- sinon.spy window, 'Metamorph'
65
+ it "does not update unloaded content", ->
66
+ template = (locals) -> locals.entity.value
65
67
 
66
- elem.html @dummyContainer.__renderer({ object: @dummyObject })
67
- expect(elem.text()).toBe "initial"
68
+ runs ->
69
+ @$ground.html @renderer.renderDynamic(template, entity: @entity)
70
+ expect(@$ground.text()).toBe "initial"
71
+ @renderer.__removeMetamorphs()
68
72
 
69
- updater = sinon.spy window.Metamorph.returnValues[0], 'html'
73
+ runs ->
74
+ @entity.update "new"
70
75
 
71
- @dummyObject.update "new"
76
+ waits 0
72
77
 
73
- waits 0
78
+ runs ->
79
+ expect(@$ground.text()).toBe "initial"
74
80
 
75
- runs ->
76
- expect(elem.text()).toBe "new"
77
- expect(updater.callCount).toEqual 1
81
+ describe "Metamorph magic", ->
82
+ beforeEach ->
83
+ sinon.spy window, 'Metamorph'
78
84
 
79
- runs ->
80
- @dummyObject.update "don't make"
81
- @dummyObject.update "me evil"
85
+ template = (locals) -> locals.entity.value
86
+ @$ground.html @renderer.renderDynamic(template, entity: @entity)
82
87
 
83
- waits 0
88
+ # With this we intercept calls to Metamorph updates
89
+ @updater = sinon.spy window.Metamorph.returnValues[0], 'html'
84
90
 
85
- runs ->
86
- expect(elem.text()).toBe "me evil"
87
- expect(updater.callCount).toEqual 2
91
+ afterEach ->
92
+ Metamorph.restore()
88
93
 
89
- it "includes rendering helpers in locals", ->
90
- @TestContainer.helpers "Hoge"
94
+ it "debounces", ->
95
+ @entity.update "new"
96
+ @entity.update "don't make"
97
+ @entity.update "me evil"
91
98
 
92
- @TestContainer.view (locals) ->
93
- template = (locals) -> @multiplier(10)
99
+ waits 0
94
100
 
95
- @render(template, locals)
101
+ runs ->
102
+ expect(@updater.callCount).toEqual 1
103
+ expect(@$ground.text()).toEqual "me evil"
96
104
 
97
- elem = $("<div></div>")
98
- @ground.append elem
105
+ it "catches manually removed nodes", ->
106
+ @$ground.html ''
99
107
 
100
- elem.html @dummyContainer.__renderer({ })
108
+ @entity.update "new"
109
+ @entity.update "don't make"
110
+ @entity.update "me evil"
101
111
 
102
- expect(elem.text()).toBe "50"
112
+ waits 0
103
113
 
104
- it "includes global rendering helpers in locals", ->
105
- Joosy.Helpers.Application.globalMultiplier = (value) ->
106
- value * 6
114
+ runs ->
115
+ expect(@updater.callCount).toEqual 0
107
116
 
108
- @TestContainer.view (locals) ->
109
- template = (locals) -> @globalMultiplier(10)
117
+ describe "helpers includer", ->
110
118
 
111
- @render(template, locals)
119
+ it "works with modules", ->
120
+ Joosy.namespace 'Joosy.Helpers.Hoge', ->
121
+ @multiplier = (value) -> "#{value * 5}"
112
122
 
113
- elem = $("<div></div>")
114
- @ground.append elem
123
+ @Renderer.helper Joosy.Helpers.Hoge
124
+ template = (locals) -> locals.multiplier(10)
115
125
 
116
- elem.html @dummyContainer.__renderer({ })
117
-
118
- expect(elem.text()).toBe "60"
119
-
120
- it "proxies onRefresh for containers", ->
121
- class Box extends Joosy.Module
122
- @include Joosy.Modules.Renderer
123
- @include Joosy.Modules.Container
126
+ expect(@renderer.render template).toBe "50"
124
127
 
125
- callback = sinon.spy()
128
+ it "works with local methods", ->
129
+ @Renderer::multiplier = (value) -> "#{value * 10}"
130
+ @Renderer.helper 'multiplier'
131
+ template = (locals) -> locals.multiplier(10)
126
132
 
127
- Box.view (locals) ->
128
- template = -> @onRefresh -> callback()
129
- @render(template, locals)
133
+ expect(@renderer.render template).toBe "100"
134
+ delete @Renderer::multiplier
130
135
 
131
- box = new Box
136
+ it "works with globals", ->
137
+ Joosy.Helpers.Application.multiplier = (value) -> "#{value * 3}"
138
+ template = (locals) -> locals.multiplier(10)
132
139
 
133
- box.__renderer({ })
134
- box.refreshElements()
140
+ expect(@renderer.render template).toBe "30"
141
+ delete Joosy.Helpers.Application.multiplier
135
142
 
136
- expect(callback.callCount).toEqual 1
@@ -1,25 +1,20 @@
1
1
  describe "Joosy.Modules.TimeManager", ->
2
2
 
3
3
  beforeEach ->
4
- class @TestTimeManager extends Joosy.Module
4
+ class @Manager extends Joosy.Module
5
5
  @include Joosy.Modules.TimeManager
6
- @box = new @TestTimeManager()
7
6
 
7
+ @manager = new @Manager
8
8
 
9
- it "should keep timeouts list", ->
10
- timer = @box.setTimeout 10000, ->
11
- expect(@box.__timeouts).toEqual [timer]
12
- window.clearTimeout timer
9
+ it "stops intervals and timeouts", ->
10
+ callback = sinon.spy()
11
+
12
+ runs ->
13
+ @manager.setTimeout 1, callback
14
+ @manager.setInterval 1, callback
15
+ @manager.__clearTime()
13
16
 
14
- it "should keep intervals list", ->
15
- timer = @box.setInterval 10000, ->
16
- expect(@box.__intervals).toEqual [timer]
17
- window.clearInterval timer
17
+ waits 2
18
18
 
19
- it "should stop intervals and timeouts", ->
20
- callback = sinon.spy()
21
19
  runs ->
22
- @box.setTimeout 10, callback
23
- @box.__clearTime()
24
- waits(10)
25
- runs -> expect(callback.callCount).toEqual(0)
20
+ expect(callback.callCount).toEqual 0
@@ -1,78 +1,96 @@
1
1
  describe "Joosy.Modules.WidgetsManager", ->
2
2
 
3
3
  beforeEach ->
4
- class @TestWidgetManager extends Joosy.Module
5
- @include Joosy.Modules.WidgetsManager
4
+ class @Manager extends Joosy.Module
6
5
  @include Joosy.Modules.Container
6
+ @include Joosy.Modules.WidgetsManager
7
+
8
+ class @Widget extends Joosy.Widget
9
+ constructor: (@argument) ->
10
+
11
+ @manager = new @Manager
12
+
13
+ describe "manager", ->
14
+
15
+ beforeEach ->
16
+ @widget = new @Widget
17
+
18
+ sinon.spy @widget, '__load'
19
+ sinon.spy @widget, '__unload'
20
+
21
+ it "registers widget", ->
22
+ result = @manager.registerWidget @$ground, @widget
23
+ expect(result instanceof @Widget).toBeTruthy()
24
+ expect(@manager.__activeWidgets).toEqual [result]
25
+ expect(@widget.__load.callCount).toEqual 1
26
+
27
+ it "unregisters widget", ->
28
+ @manager.registerWidget @$ground, @widget
29
+
30
+ expect(@manager.unregisterWidget @widget).toBeTruthy()
31
+ expect(@manager.__activeWidgets).toEqual []
32
+ expect(@widget.__unload.callCount).toEqual 1
33
+
34
+ it "unload all widgets properly", ->
35
+ 3.times => @manager.registerWidget(@$ground, @widget)
36
+ @manager.__unloadWidgets()
37
+ expect(@widget.__unload.callCount).toEqual 3
38
+
39
+ describe 'declarator', ->
40
+
41
+ it "inherits widget declarations", ->
42
+ @Manager.mapWidgets
43
+ 'test': 'widget'
7
44
 
8
- @box = new @TestWidgetManager()
9
- @widgetMock = Object.extended(
10
- __load: sinon.stub()
11
- __unload: sinon.spy()
12
- )
13
- @widgetMock.__load.returns @widgetMock
14
-
15
-
16
- it "should register and unregister widget", ->
17
- expect(@box.registerWidget @ground, @widgetMock).toBe @widgetMock
18
- expect(@box.__activeWidgets).toEqual [@widgetMock]
19
- expect(@widgetMock.__load.callCount).toEqual 1
20
-
21
- expect(@box.unregisterWidget @widgetMock).toBeTruthy()
22
- expect(@box.__activeWidgets).toEqual []
23
- expect(@widgetMock.__unload.callCount).toEqual 1
24
-
25
- it "should unload all widgets", ->
26
- 0.upto(2).each => @box.registerWidget(@ground, @widgetMock)
27
- @box.__unloadWidgets()
28
- expect(@widgetMock.__unload.callCount).toEqual 3
29
-
30
- it "should inherit widget declarations", ->
31
- @box.container = @ground
32
- @TestWidgetManager::__widgets =
33
- 'test': 'widget'
34
- class SubWidgetManagerA extends @TestWidgetManager
35
- @mapWidgets
45
+ class A extends @Manager
46
+ @mapWidgets
47
+ 'selector': 'widget'
48
+
49
+ class B extends A
50
+ @mapWidgets
51
+ 'widgets': 'widget'
52
+ 'selector': 'overriden'
53
+
54
+ expect((new A).__widgets).toEqual Object.extended
55
+ 'test': 'widget'
36
56
  'selector': 'widget'
37
- class SubWidgetManagerB extends SubWidgetManagerA
38
- @mapWidgets
39
- 'widgets': 'widget'
57
+
58
+ expect((new B).__widgets).toEqual Object.extended
59
+ 'test': 'widget'
60
+ 'widgets': 'widget'
40
61
  'selector': 'overriden'
41
- subBox = new SubWidgetManagerB()
42
- target = subBox.__widgets
43
- expect(target).toEqual Object.extended
44
- 'test': 'widget'
45
- 'widgets': 'widget'
46
- 'selector': 'overriden'
47
-
48
- it "should register widgets per declaration", ->
49
- @seedGround()
50
- @box.container = $('#application')
51
- @box.__elements = {footer: '.footer'}
52
- @box.__widgets =
53
- '$container': Joosy.Widget
54
- '$footer': Joosy.Widget
55
- '.post': sinon.stub()
56
- @box.__widgets['.post'].returns @widgetMock
57
- @box.__assignElements()
58
- @box.__setupWidgets()
59
- expect(@box.__activeWidgets.length).toEqual 5
60
- expect(@box.__widgets['.post'].callCount).toEqual 3
61
- expect(@box.__widgets['.post'].getCall(0).calledOn @box).toBeTruthy()
62
-
63
- it "should bootstrap widget properly", ->
64
- class TextWidget extends Joosy.Widget
65
- @view -> 'fluffy'
66
- constructor: (@tester) ->
67
-
68
- @seedGround()
69
- @box.container = $('#application')
70
- @box.__widgets =
71
- '.post:first': TextWidget
72
- '.widget:first': (i) -> new TextWidget i
73
- @box.__setupWidgets()
74
-
75
- expect(@ground.find('.post').html()).toEqual 'fluffy'
76
- expect(@ground.find('.widget').html()).toEqual 'fluffy'
77
- expect(@box.__activeWidgets[0].tester).toBeUndefined()
78
- expect(@box.__activeWidgets[1].tester).toEqual 0
62
+
63
+ it "registers declared widgets", ->
64
+ @$ground.seed()
65
+
66
+ @Manager.mapElements
67
+ footer: '.footer'
68
+
69
+ @Manager.mapWidgets widgets =
70
+ '$container': Joosy.Widget
71
+ '$footer': Joosy.Widget
72
+ '.post': sinon.stub().returns new @Widget
73
+
74
+ @manager.container = $('#application')
75
+ @manager.__assignElements()
76
+ @manager.__setupWidgets()
77
+
78
+ expect(@manager.__activeWidgets.length).toEqual 5
79
+ expect(widgets['.post'].callCount).toEqual 3
80
+ expect(widgets['.post'].getCall(0).calledOn @manager).toBeTruthy()
81
+
82
+ it "bootstraps declared widgets properly", ->
83
+ @$ground.seed()
84
+
85
+ @Widget.view -> 'fluffy'
86
+ @Manager.mapWidgets
87
+ '#post1': @Widget
88
+ '#widget1': (i) => new @Widget i
89
+
90
+ @manager.container = $('#application')
91
+ @manager.__setupWidgets()
92
+
93
+ expect(@$ground.find('#post1').html()).toEqual 'fluffy'
94
+ expect(@$ground.find('#widget1').html()).toEqual 'fluffy'
95
+ expect(@manager.__activeWidgets[0].argument).toBeUndefined()
96
+ expect(@manager.__activeWidgets[1].argument).toEqual 0