joosy 0.1.0.RC1

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 (98) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +14 -0
  3. data/Gemfile.lock +159 -0
  4. data/Guardfile +30 -0
  5. data/MIT-LICENSE +21 -0
  6. data/README.rdoc +3 -0
  7. data/Rakefile +14 -0
  8. data/app/assets/javascripts/joosy.js.coffee +7 -0
  9. data/app/assets/javascripts/joosy/core/application.js.coffee +28 -0
  10. data/app/assets/javascripts/joosy/core/form.js.coffee +87 -0
  11. data/app/assets/javascripts/joosy/core/helpers.js.coffee +6 -0
  12. data/app/assets/javascripts/joosy/core/joosy.js.coffee +65 -0
  13. data/app/assets/javascripts/joosy/core/layout.js.coffee +47 -0
  14. data/app/assets/javascripts/joosy/core/modules/container.js.coffee +59 -0
  15. data/app/assets/javascripts/joosy/core/modules/events.js.coffee +35 -0
  16. data/app/assets/javascripts/joosy/core/modules/filters.js.coffee +39 -0
  17. data/app/assets/javascripts/joosy/core/modules/log.js.coffee +15 -0
  18. data/app/assets/javascripts/joosy/core/modules/module.js.coffee +43 -0
  19. data/app/assets/javascripts/joosy/core/modules/renderer.js.coffee +116 -0
  20. data/app/assets/javascripts/joosy/core/modules/time_manager.js.coffee +25 -0
  21. data/app/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +58 -0
  22. data/app/assets/javascripts/joosy/core/page.js.coffee +156 -0
  23. data/app/assets/javascripts/joosy/core/preloader.js.coffee +5 -0
  24. data/app/assets/javascripts/joosy/core/resource/generic.js.coffee +61 -0
  25. data/app/assets/javascripts/joosy/core/resource/rest.js.coffee +76 -0
  26. data/app/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +48 -0
  27. data/app/assets/javascripts/joosy/core/router.js.coffee +89 -0
  28. data/app/assets/javascripts/joosy/core/templaters/rails_jst.js.coffee +20 -0
  29. data/app/assets/javascripts/joosy/core/widget.js.coffee +41 -0
  30. data/app/assets/javascripts/joosy/preloaders/caching.js.coffee +94 -0
  31. data/app/assets/javascripts/joosy/preloaders/inline.js.coffee +55 -0
  32. data/app/helpers/joosy/sprockets_helper.rb +23 -0
  33. data/app/views/layouts/json_wrapper.json.erb +1 -0
  34. data/joosy.gemspec +25 -0
  35. data/lib/joosy.rb +9 -0
  36. data/lib/joosy/forms.rb +47 -0
  37. data/lib/joosy/rails/engine.rb +17 -0
  38. data/lib/joosy/rails/version.rb +5 -0
  39. data/lib/rails/generators/joosy/application_generator.rb +41 -0
  40. data/lib/rails/generators/joosy/joosy_base.rb +30 -0
  41. data/lib/rails/generators/joosy/layout_generator.rb +32 -0
  42. data/lib/rails/generators/joosy/page_generator.rb +44 -0
  43. data/lib/rails/generators/joosy/preloader_generator.rb +32 -0
  44. data/lib/rails/generators/joosy/resource_generator.rb +29 -0
  45. data/lib/rails/generators/joosy/templates/app.js.coffee +10 -0
  46. data/lib/rails/generators/joosy/templates/app/helpers/application.js.coffee +4 -0
  47. data/lib/rails/generators/joosy/templates/app/layouts/application.js.coffee +2 -0
  48. data/lib/rails/generators/joosy/templates/app/layouts/template.js.coffee +2 -0
  49. data/lib/rails/generators/joosy/templates/app/pages/application.js.coffee +1 -0
  50. data/lib/rails/generators/joosy/templates/app/pages/template.js.coffee +5 -0
  51. data/lib/rails/generators/joosy/templates/app/pages/welcome/index.js.coffee +22 -0
  52. data/lib/rails/generators/joosy/templates/app/resources/template.js.coffee +2 -0
  53. data/lib/rails/generators/joosy/templates/app/routes.js.coffee +8 -0
  54. data/lib/rails/generators/joosy/templates/app/templates/layouts/application.jst.hamlc +2 -0
  55. data/lib/rails/generators/joosy/templates/app/templates/pages/welcome/index.jst.hamlc +7 -0
  56. data/lib/rails/generators/joosy/templates/app/widgets/template.js.coffee +2 -0
  57. data/lib/rails/generators/joosy/templates/app_controller.rb +9 -0
  58. data/lib/rails/generators/joosy/templates/app_preloader.js.coffee.erb +13 -0
  59. data/lib/rails/generators/joosy/templates/preload.html.erb +26 -0
  60. data/lib/rails/generators/joosy/templates/preload.html.haml +19 -0
  61. data/lib/rails/generators/joosy/widget_generator.rb +32 -0
  62. data/spec/javascripts/helpers/spec_helper.js.coffee +44 -0
  63. data/spec/javascripts/joosy/core/application_spec.js.coffee +40 -0
  64. data/spec/javascripts/joosy/core/form_spec.js.coffee +141 -0
  65. data/spec/javascripts/joosy/core/joosy_spec.js.coffee +72 -0
  66. data/spec/javascripts/joosy/core/layout_spec.js.coffee +50 -0
  67. data/spec/javascripts/joosy/core/modules/container_spec.js.coffee +92 -0
  68. data/spec/javascripts/joosy/core/modules/events_spec.js.coffee +53 -0
  69. data/spec/javascripts/joosy/core/modules/filters_spec.js.coffee +71 -0
  70. data/spec/javascripts/joosy/core/modules/log_spec.js.coffee +15 -0
  71. data/spec/javascripts/joosy/core/modules/module_spec.js.coffee +47 -0
  72. data/spec/javascripts/joosy/core/modules/renderer_spec.js.coffee +149 -0
  73. data/spec/javascripts/joosy/core/modules/time_manager_spec.js.coffee +25 -0
  74. data/spec/javascripts/joosy/core/modules/widget_manager_spec.js.coffee +75 -0
  75. data/spec/javascripts/joosy/core/page_spec.js.coffee +175 -0
  76. data/spec/javascripts/joosy/core/resource/generic_spec.js.coffee +35 -0
  77. data/spec/javascripts/joosy/core/resource/rest_collection_spec.js.coffee +65 -0
  78. data/spec/javascripts/joosy/core/resource/rest_spec.js.coffee +108 -0
  79. data/spec/javascripts/joosy/core/router_spec.js.coffee +123 -0
  80. data/spec/javascripts/joosy/core/templaters/rails_jst_spec.js.coffee +25 -0
  81. data/spec/javascripts/joosy/core/widget_spec.js.coffee +51 -0
  82. data/spec/javascripts/joosy/preloaders/caching_spec.js.coffee +36 -0
  83. data/spec/javascripts/joosy/preloaders/inline_spec.js.coffee +16 -0
  84. data/spec/javascripts/support/assets/coolface.jpg +0 -0
  85. data/spec/javascripts/support/assets/okay.jpg +0 -0
  86. data/spec/javascripts/support/assets/test.js +1 -0
  87. data/spec/javascripts/support/jasmine.yml +74 -0
  88. data/spec/javascripts/support/jasmine_config.rb +23 -0
  89. data/spec/javascripts/support/jasmine_runner.rb +32 -0
  90. data/spec/javascripts/support/sinon-1.3.1.js +3469 -0
  91. data/spec/javascripts/support/sinon-ie-1.3.1.js +82 -0
  92. data/vendor/assets/javascripts/base64.js +135 -0
  93. data/vendor/assets/javascripts/inflection.js +656 -0
  94. data/vendor/assets/javascripts/jquery.form.js +980 -0
  95. data/vendor/assets/javascripts/jquery.hashchange.js +390 -0
  96. data/vendor/assets/javascripts/metamorph.js +409 -0
  97. data/vendor/assets/javascripts/sugar.js +6040 -0
  98. metadata +232 -0
@@ -0,0 +1,72 @@
1
+ describe "Joosy", ->
2
+
3
+ it "should properly initialize", ->
4
+ expect(Joosy.debug).toBeFalsy()
5
+ expect(Joosy.Modules).toBeDefined()
6
+ expect(Joosy.Resource).toBeDefined()
7
+
8
+ it "should declare namespaces", ->
9
+ Joosy.namespace 'Namespaces.Test1'
10
+ Joosy.namespace 'Namespaces.Test2', ->
11
+ @bingo = 'bongo'
12
+ expect(window.Namespaces.Test1).toBeDefined()
13
+ expect(window.Namespaces.Test2.bingo).toEqual('bongo')
14
+
15
+ it "should imprint namespace paths in Joosy.Module descendants", ->
16
+ Joosy.namespace 'Irish', ->
17
+ class @Pub extends Joosy.Module
18
+
19
+ Joosy.namespace 'British', ->
20
+ class @Pub extends Joosy.Module
21
+
22
+ Joosy.namespace 'Keltic', ->
23
+ class @Pub extends Irish.Pub
24
+
25
+ expect(Irish.Pub.__namespace__).toEqual ["Irish"]
26
+ expect(British.Pub.__namespace__).toEqual ["British"]
27
+ expect(Keltic.Pub.__namespace__).toEqual ["Keltic"]
28
+
29
+ Joosy.namespace 'Deeply.Nested', ->
30
+ class @Klass extends Joosy.Module
31
+
32
+ expect(Deeply.Nested.Klass.__namespace__).toEqual ["Deeply", "Nested"]
33
+
34
+ class @Flat extends Joosy.Module
35
+
36
+ expect(@Flat.__namespace__).toEqual []
37
+
38
+ it "should set up helpers", ->
39
+ Joosy.helpers 'Hoge', ->
40
+ @fuga = ->
41
+ "piyo"
42
+
43
+ expect(window.Joosy.Helpers).toBeDefined()
44
+ expect(window.Joosy.Helpers.Hoge).toBeDefined()
45
+ expect(window.Joosy.Helpers.Hoge.fuga()).toBe "piyo"
46
+
47
+ it "should generate proper UUIDs", ->
48
+ uuids = []
49
+ 2.times ->
50
+ uuids.push Joosy.uuid()
51
+ expect(uuids.unique().length).toEqual(2)
52
+ expect(uuids[0]).toMatch /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}/
53
+
54
+ it "should build proper URLs", ->
55
+ expect(Joosy.buildUrl 'http://www.org').toEqual('http://www.org')
56
+ expect(Joosy.buildUrl 'http://www.org#hash').toEqual('http://www.org#hash')
57
+ expect(Joosy.buildUrl 'http://www.org', {foo: 'bar'}).toEqual('http://www.org?foo=bar')
58
+ expect(Joosy.buildUrl 'http://www.org?bar=baz', {foo: 'bar'}).toEqual('http://www.org?bar=baz&foo=bar')
59
+
60
+ it "should preload images", ->
61
+ path = "/spec/javascripts/support/assets/"
62
+ images = [path+"okay.jpg", path+"okay.jpg"]
63
+
64
+ callback = sinon.spy()
65
+
66
+ runs -> Joosy.preloadImages path+"coolface.jpg", callback
67
+ waits(150)
68
+ runs -> expect(callback.callCount).toEqual(1)
69
+
70
+ runs -> Joosy.preloadImages images, callback
71
+ waits(150)
72
+ runs -> expect(callback.callCount).toEqual(2)
@@ -0,0 +1,50 @@
1
+ describe "Joosy.Layout", ->
2
+
3
+ beforeEach ->
4
+ class @TestLayout extends Joosy.Layout
5
+ @box = new @TestLayout()
6
+
7
+ it "should have appropriate accessors", ->
8
+ callback_names = ['beforePaint', 'paint', 'erase']
9
+ callback_names.each (func) =>
10
+ @TestLayout[func] 'callback'
11
+ expect(@TestLayout::['__' + func]).toEqual 'callback'
12
+
13
+ it "should have default view", ->
14
+ @box = new @TestLayout()
15
+ expect(@box.__renderer instanceof Function).toBeTruthy()
16
+
17
+ it "should use Router", ->
18
+ target = sinon.stub Joosy.Router, 'navigate'
19
+ @box.navigate 'there'
20
+ expect(target.callCount).toEqual 1
21
+ expect(target.alwaysCalledWithExactly 'there').toBeTruthy()
22
+ Joosy.Router.navigate.restore()
23
+
24
+ it "should load itself", ->
25
+ spies = []
26
+ spies.push sinon.spy(@box, 'refreshElements')
27
+ spies.push sinon.spy(@box, '__delegateEvents')
28
+ spies.push sinon.spy(@box, '__setupWidgets')
29
+ spies.push sinon.spy(@box, '__runAfterLoads')
30
+ @box.__load(@ground)
31
+ expect(spies).toBeSequenced()
32
+
33
+ it "should unload itself", ->
34
+ spies = []
35
+ spies.push sinon.spy(@box, 'clearTime')
36
+ spies.push sinon.spy(@box, '__unloadWidgets')
37
+ spies.push sinon.spy(@box, '__runAfterUnloads')
38
+ @box.__unload()
39
+ expect(spies).toBeSequenced()
40
+
41
+ it "should generate uuid", ->
42
+ sinon.spy Joosy, 'uuid'
43
+ @box.yield()
44
+ expect(Joosy.uuid.callCount).toEqual 1
45
+ expect(@box.uuid).toBeDefined()
46
+ Joosy.uuid.restore()
47
+
48
+ it "should uuid as selector", ->
49
+ @box.yield()
50
+ expect(@box.content().selector).toEqual '#' + @box.uuid
@@ -0,0 +1,92 @@
1
+ describe "Joosy.Modules.Container", ->
2
+
3
+ beforeEach ->
4
+ @seedGround()
5
+ class @TestContainer extends Joosy.Module
6
+ @include Joosy.Modules.Container
7
+ elements:
8
+ footer: '.footer'
9
+ events:
10
+ 'test': 'onContainerTest'
11
+ container: $('#application', @ground)
12
+ @box = new @TestContainer()
13
+
14
+
15
+ it "should have property named per declared element in container", ->
16
+ @ground.prepend('<div class="footer" />') # out of container
17
+ @box.refreshElements()
18
+ target = @box.footer.get 0
19
+ expect(target).toBeTruthy()
20
+ expect(target).toBe $('.footer', @box.container).get 0
21
+ expect(target).toBe @box.$('.footer').get 0
22
+
23
+ it "should reinitialize container", ->
24
+ old_container = @box.container
25
+ parent = old_container.parent()
26
+ callback = sinon.spy()
27
+ old_container.bind 'test', callback
28
+ old_container.trigger 'test'
29
+ new_container = Joosy.Modules.Container.swapContainer old_container, 'new content'
30
+ new_container.trigger 'test'
31
+ expect(new_container.html()).toEqual 'new content'
32
+ expect(new_container.parent().get(0)).toBe parent.get 0
33
+ expect(old_container.parent().get(0)).toBeUndefined()
34
+ expect(callback.callCount).toEqual 1
35
+
36
+ it "should inherit element declarations", ->
37
+ class SubContainerA extends @TestContainer
38
+ elements:
39
+ first: 'first'
40
+ second: 'second'
41
+ class SubContainerB extends SubContainerA
42
+ elements:
43
+ first: 'overrided'
44
+ third: 'third'
45
+ subBox = new SubContainerB()
46
+ target = subBox.__collectElements()
47
+ expect(target).toEqual Object.extended
48
+ first: 'overrided'
49
+ second: 'second'
50
+ third: 'third'
51
+ footer: '.footer'
52
+ target = (new @TestContainer()).__collectElements()
53
+ expect(target).toEqual Object.extended(footer: '.footer')
54
+
55
+ it "should resolve element selector", ->
56
+ target = @box.__extractSelector '$footer'
57
+ expect(target).toEqual '.footer'
58
+
59
+ it "should inherit event declarations", ->
60
+ class SubContainerA extends @TestContainer
61
+ events:
62
+ 'test .post': 'callback2'
63
+ 'custom' : 'method'
64
+ class SubContainerB extends SubContainerA
65
+ events:
66
+ 'test $footer': 'onFooterTest'
67
+ 'custom' : 'overrided'
68
+ subBox = new SubContainerB()
69
+ target = subBox.__collectEvents()
70
+ expect(target).toEqual Object.extended
71
+ 'test': 'onContainerTest'
72
+ 'test .post': 'callback2'
73
+ 'test $footer': 'onFooterTest'
74
+ 'custom' : 'overrided'
75
+ target = (new @TestContainer()).__collectEvents()
76
+ expect(target).toEqual Object.extended('test': 'onContainerTest')
77
+
78
+ it "should delegate events", ->
79
+ callback = 1.upto(3).map -> sinon.spy()
80
+ @box.events = Object.extended(@box.events).merge
81
+ 'test .post': callback[2]
82
+ 'test $footer': 'onFooterTest'
83
+
84
+ @box.onContainerTest = callback[0]
85
+ @box.onFooterTest = callback[1]
86
+ @box.__delegateEvents()
87
+ @box.container.trigger 'test'
88
+ $('.footer', @box.container).trigger 'test'
89
+ $('.post', @box.container).trigger 'test'
90
+ expect(callback[0].callCount).toEqual 5
91
+ expect(callback[1].callCount).toEqual 1
92
+ expect(callback[2].callCount).toEqual 3
@@ -0,0 +1,53 @@
1
+ describe "Joosy.Modules.Events", ->
2
+
3
+ beforeEach ->
4
+ class @TestEvents extends Joosy.Module
5
+ @include Joosy.Modules.Events
6
+ class @SubTestEvents extends @TestEvents
7
+ @include Joosy.Modules.Events
8
+ @box = new @TestEvents()
9
+ @sub = new @SubTestEvents()
10
+
11
+ it "should run callback once when the all listed events have occurred", ->
12
+ callback = sinon.spy()
13
+
14
+ @box.wait 'events list', callback
15
+
16
+ @box.trigger 'events'
17
+ expect(callback.callCount).toEqual 0
18
+ @box.trigger 'list'
19
+ expect(callback.callCount).toEqual 1
20
+
21
+ @box.trigger 'events'
22
+ expect(callback.callCount).toEqual 1
23
+ @box.trigger 'list'
24
+ expect(callback.callCount).toEqual 1
25
+
26
+ it "should allow for binding and unbinding to events", ->
27
+ callback = sinon.spy()
28
+
29
+ @box.bind 'event', callback
30
+
31
+ @box.trigger 'other-event'
32
+ expect(callback.callCount).toEqual 0
33
+ @box.trigger 'event'
34
+ expect(callback.callCount).toEqual 1
35
+ @box.trigger 'event'
36
+ expect(callback.callCount).toEqual 2
37
+
38
+ @box.unbind 'other-event'
39
+
40
+ @box.trigger 'event'
41
+ expect(callback.callCount).toEqual 3
42
+
43
+ @box.unbind callback
44
+
45
+ @box.trigger 'event'
46
+ expect(callback.callCount).toEqual 3
47
+
48
+ it "should handle inheritance well", ->
49
+ callback = sinon.spy()
50
+ @sub.wait 'foo', callback
51
+
52
+ expect(@sub.__oneShotEvents).toEqual [[['foo'], callback]]
53
+ expect(@box.__oneShotEvents).toBeUndefined()
@@ -0,0 +1,71 @@
1
+ describe "Joosy.Modules.Filters", ->
2
+
3
+ beforeEach ->
4
+ class @TestFilters extends Joosy.Module
5
+ @include Joosy.Modules.Filters
6
+ @box = new @TestFilters()
7
+
8
+
9
+ it "should inherit filters by copying them", ->
10
+ class SubFiltersA extends @TestFilters
11
+ @beforeLoad 'filter1'
12
+ @afterLoad 'filter2'
13
+ @afterUnload 'filter3'
14
+ class SubFiltersB extends SubFiltersA
15
+ @beforeLoad 'filter4'
16
+ @afterLoad 'filter5'
17
+ @afterUnload 'filter6'
18
+ target = new SubFiltersB()
19
+ expect(target.__beforeLoads).toEqual ['filter1', 'filter4']
20
+ expect(target.__afterLoads).toEqual ['filter2', 'filter5']
21
+ expect(target.__afterUnloads).toEqual ['filter3', 'filter6']
22
+ target = new SubFiltersA()
23
+ expect(target.__beforeLoads).toEqual ['filter1']
24
+ expect(target.__afterLoads).toEqual ['filter2']
25
+ expect(target.__afterUnloads).toEqual ['filter3']
26
+ target = new @TestFilters()
27
+ expect(target.__beforeLoads).toBeUndefined()
28
+ expect(target.__afterLoads).toBeUndefined()
29
+ expect(target.__afterUnloads).toBeUndefined()
30
+
31
+ it "should run callbacks", ->
32
+ callback = 0.upto(2).map -> sinon.spy()
33
+ @box.constructor.beforeLoad callback[0]
34
+ @box.constructor.afterLoad callback[1]
35
+ @box.constructor.afterUnload callback[2]
36
+ @box.__runBeforeLoads 1, 2
37
+ @box.__runAfterLoads 1, 2
38
+ @box.__runAfterUnloads 1, 2
39
+ for i in 0.upto(2)
40
+ expect(callback[i].callCount).toEqual 1
41
+ expect(callback[i].alwaysCalledWithExactly 1, 2).toBeTruthy()
42
+
43
+ it "should chain beforeLoad filters", ->
44
+ callback = 0.upto(2).map -> sinon.stub()
45
+ callback[0].returns true
46
+ @box.constructor.beforeLoad(callback[i]) for i in 0.upto 2
47
+ expect(@box.__runBeforeLoads()).toBeFalsy()
48
+ expect(callback[0].callCount).toEqual 1
49
+ expect(callback[1].callCount).toEqual 1
50
+ expect(callback[2].callCount).toEqual 0
51
+
52
+ it "should chain beforeLoad filters", ->
53
+ callback = 0.upto(1).map -> sinon.stub()
54
+ callback[0].returns true
55
+ callback[1].returns true
56
+ @box.constructor.beforeLoad(callback[i]) for i in 0.upto(1)
57
+ expect(@box.__runBeforeLoads()).toBeTruthy()
58
+ expect(callback[0].callCount).toEqual 1
59
+ expect(callback[1].callCount).toEqual 1
60
+
61
+ it "should accept callback names", ->
62
+ @box.constructor.beforeLoad 'callback0'
63
+ @box.constructor.afterLoad 'callback1'
64
+ @box.constructor.afterUnload 'callback2'
65
+ for i in 0.upto(2)
66
+ @box['callback' + i] = sinon.spy()
67
+ @box.__runBeforeLoads()
68
+ @box.__runAfterLoads()
69
+ @box.__runAfterUnloads()
70
+ for i in 0.upto(2)
71
+ expect(@box['callback' + i].callCount).toEqual 1
@@ -0,0 +1,15 @@
1
+ describe "Joosy.Modules.Log", ->
2
+
3
+ beforeEach ->
4
+ class @TestLog extends Joosy.Module
5
+ @include Joosy.Modules.Log
6
+ @box = new @TestLog()
7
+
8
+ it "should log into console", ->
9
+ @box.log 'message', 'appendix'
10
+
11
+ it "should log debug messages into console", ->
12
+ Joosy.debug = true
13
+ @box.debug 'debug message'
14
+ Joosy.debug = false
15
+ @box.debug 'unseen debug message'
@@ -0,0 +1,47 @@
1
+ describe "Joosy.Module", ->
2
+
3
+ it "should track inheritance", ->
4
+ class A
5
+ class B extends A
6
+ class C extends B
7
+ class D
8
+ for a in [A, B, C, D]
9
+ for b in [A, B, C, D]
10
+ if (a == b) ||
11
+ ((a == B) && (b == A)) ||
12
+ ((a == C) && (b != D))
13
+ expect(Joosy.Module.hasAncestor.apply(null, [a, b])).toBeTruthy()
14
+ else
15
+ expect(Joosy.Module.hasAncestor.apply(null, [a, b])).toBeFalsy()
16
+
17
+ it "should include properties into prototype", ->
18
+ TestModule =
19
+ property: 'value'
20
+ class Klass extends Joosy.Module
21
+ @include TestModule
22
+ expect(Klass::property).toEqual 'value'
23
+ expect((new Klass()).property).toEqual 'value'
24
+
25
+ it "should extend object", ->
26
+ TestModule =
27
+ property: 'value'
28
+ class Klass extends Joosy.Module
29
+ @extend TestModule
30
+ expect(Klass.property).toEqual 'value'
31
+ expect((new Klass()).property).toBeUndefined()
32
+
33
+ it "should run callbacks on include and extend", ->
34
+ TestModule =
35
+ property: 'value'
36
+ included: sinon.spy()
37
+ extended: sinon.spy()
38
+ class Klass extends Joosy.Module
39
+ @include TestModule
40
+ @extend TestModule
41
+ for callback in ['included', 'extended']
42
+ expect(TestModule[callback].callCount).toEqual 1
43
+ 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', 'include', 'extend']
47
+ expect(Object.extended(Joosy.Module.prototype).keys()).toEqual []
@@ -0,0 +1,149 @@
1
+ describe "Joosy.Modules.Renderer", ->
2
+
3
+ beforeEach ->
4
+ @seedGround()
5
+
6
+ class @TestContainer extends Joosy.Module
7
+ @include Joosy.Modules.Renderer
8
+
9
+ @dummyContainer = new @TestContainer
10
+
11
+ class @TestObject extends Joosy.Module
12
+ @include Joosy.Modules.Events
13
+
14
+ constructor: (@value) ->
15
+
16
+ update: (@value) ->
17
+ @trigger 'changed'
18
+
19
+ @dummyObject = new @TestObject("initial")
20
+
21
+ Joosy.namespace 'Joosy.Helpers.Hoge', ->
22
+ @multiplier = (value) ->
23
+ "#{value * 5}"
24
+
25
+ it "should update contents, but only while it is bound to DOM", ->
26
+ @TestContainer.view (locals) ->
27
+ template = (locals) ->
28
+ "#{locals.object.value}"
29
+
30
+ @render(template, locals)
31
+
32
+ elem = $("<div></div>")
33
+ @ground.append elem
34
+
35
+ elem.html @dummyContainer.__renderer({ object: @dummyObject })
36
+ expect(elem.text()).toBe "initial"
37
+
38
+ @dummyObject.update "new"
39
+
40
+ waits 0
41
+
42
+ runs ->
43
+ expect(elem.text()).toBe "new"
44
+
45
+ waits 0
46
+
47
+ runs ->
48
+ @dummyContainer.__removeMetamorphs()
49
+ @dummyObject.update "afterwards"
50
+
51
+ waits 0
52
+
53
+ runs ->
54
+ expect(elem.text()).toBe "new"
55
+
56
+ it "should render resources and keep html up2date", ->
57
+ data = Joosy.Resource.Generic.create zombie: 'rock'
58
+
59
+ @TestContainer.view (locals) ->
60
+ template = (locals) ->
61
+ "#{locals.zombie}"
62
+
63
+ @render(template, locals)
64
+
65
+ elem = $("<div></div>")
66
+ @ground.append elem
67
+
68
+ elem.html @dummyContainer.__renderer(data)
69
+
70
+ waits 0
71
+
72
+ runs ->
73
+ expect(elem.text()).toBe "rock"
74
+
75
+ runs ->
76
+ data 'zombie', 'suck'
77
+
78
+ waits 0
79
+
80
+ runs ->
81
+ expect(elem.text()).toBe "suck"
82
+
83
+ it "should debounce morpher updates", ->
84
+ @TestContainer.view (locals) ->
85
+ template = (locals) ->
86
+ "#{locals.object.value}"
87
+
88
+ @render(template, locals)
89
+
90
+ elem = $("<div></div>")
91
+ @ground.append elem
92
+
93
+ sinon.spy window, 'Metamorph'
94
+
95
+ elem.html @dummyContainer.__renderer({ object: @dummyObject })
96
+ expect(elem.text()).toBe "initial"
97
+
98
+ updater = sinon.spy window.Metamorph.returnValues[0], 'html'
99
+
100
+ @dummyObject.update "new"
101
+
102
+ waits 0
103
+
104
+ runs ->
105
+ expect(elem.text()).toBe "new"
106
+ expect(updater.callCount).toEqual 1
107
+
108
+ runs ->
109
+ @dummyObject.update "don't make"
110
+ @dummyObject.update "me evil"
111
+
112
+ waits 0
113
+
114
+ runs ->
115
+ expect(elem.text()).toBe "me evil"
116
+ expect(updater.callCount).toEqual 2
117
+
118
+ it "should include rendering helpers in locals", ->
119
+ @TestContainer.helpers "Hoge"
120
+
121
+ @TestContainer.view (locals) ->
122
+ template = (locals) ->
123
+ "#{locals.multiplier(10)}"
124
+
125
+ @render(template, locals)
126
+
127
+ elem = $("<div></div>")
128
+ @ground.append elem
129
+
130
+ elem.html @dummyContainer.__renderer({ })
131
+
132
+ expect(elem.text()).toBe "50"
133
+
134
+ it "should include global rendering helpers in locals", ->
135
+ Joosy.Helpers.Application.globalMultiplier = (value) ->
136
+ value * 6
137
+
138
+ @TestContainer.view (locals) ->
139
+ template = (locals) ->
140
+ "#{locals.globalMultiplier(10)}"
141
+
142
+ @render(template, locals)
143
+
144
+ elem = $("<div></div>")
145
+ @ground.append elem
146
+
147
+ elem.html @dummyContainer.__renderer({ })
148
+
149
+ expect(elem.text()).toBe "60"