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,25 @@
1
+ describe "Joosy.Modules.TimeManager", ->
2
+
3
+ beforeEach ->
4
+ class @TestTimeManager extends Joosy.Module
5
+ @include Joosy.Modules.TimeManager
6
+ @box = new @TestTimeManager()
7
+
8
+
9
+ it "should keep timeouts list", ->
10
+ timer = @box.setTimeout 10000, ->
11
+ expect(@box.__timeouts).toEqual [timer]
12
+ window.clearTimeout timer
13
+
14
+ it "should keep intervals list", ->
15
+ timer = @box.setInterval 10000, ->
16
+ expect(@box.__intervals).toEqual [timer]
17
+ window.clearInterval timer
18
+
19
+ it "should stop intervals and timeouts", ->
20
+ callback = sinon.spy()
21
+ runs ->
22
+ @box.setTimeout 10, callback
23
+ @box.clearTime()
24
+ waits(10)
25
+ runs -> expect(callback.callCount).toEqual(0)
@@ -0,0 +1,75 @@
1
+ describe "Joosy.Modules.WidgetsManager", ->
2
+
3
+ beforeEach ->
4
+ class @TestWidgetManager extends Joosy.Module
5
+ @include Joosy.Modules.WidgetsManager
6
+ @box = new @TestWidgetManager()
7
+ @widgetMock = Object.extended(
8
+ __load: sinon.stub()
9
+ __unload: sinon.spy()
10
+ )
11
+ @widgetMock.__load.returns @widgetMock
12
+
13
+
14
+ it "should register and unregister widget", ->
15
+ expect(@box.registerWidget @ground, @widgetMock).toBe @widgetMock
16
+ expect(@box.__activeWidgets).toEqual [@widgetMock]
17
+ expect(@widgetMock.__load.callCount).toEqual 1
18
+
19
+ expect(@box.unregisterWidget @widgetMock).toBeTruthy()
20
+ expect(@box.__activeWidgets).toEqual []
21
+ expect(@widgetMock.__unload.callCount).toEqual 1
22
+
23
+ it "should unload all widgets", ->
24
+ 0.upto(2).each => @box.registerWidget(@ground, @widgetMock)
25
+ @box.__unloadWidgets()
26
+ expect(@widgetMock.__unload.callCount).toEqual 3
27
+
28
+ it "should inherit widget declarations", ->
29
+ @box.container = @ground
30
+ @TestWidgetManager::widgets =
31
+ 'test': 'widget'
32
+ class SubWidgetManagerA extends @TestWidgetManager
33
+ widgets:
34
+ 'selector': 'widget'
35
+ class SubWidgetManagerB extends SubWidgetManagerA
36
+ widgets:
37
+ 'widgets': 'widget'
38
+ 'selector': 'overriden'
39
+ subBox = new SubWidgetManagerB()
40
+ target = subBox.__collectWidgets()
41
+ expect(target).toEqual Object.extended
42
+ 'test': 'widget'
43
+ 'widgets': 'widget'
44
+ 'selector': 'overriden'
45
+
46
+ it "should register widgets per declaration", ->
47
+ @seedGround()
48
+ @box.container = $('#application')
49
+ @box.elements = {footer: '.footer'}
50
+ @box.widgets =
51
+ '$container': Joosy.Widget
52
+ '$footer': Joosy.Widget
53
+ '.post': sinon.stub()
54
+ @box.widgets['.post'].returns @widgetMock
55
+ @box.__setupWidgets()
56
+ expect(@box.__activeWidgets.length).toEqual 5
57
+ expect(@box.widgets['.post'].callCount).toEqual 3
58
+ expect(@box.widgets['.post'].getCall(0).calledOn @box).toBeTruthy()
59
+
60
+ it "should bootstrap widget properly", ->
61
+ class TextWidget extends Joosy.Widget
62
+ @view -> 'fluffy'
63
+ constructor: (@tester) ->
64
+
65
+ @seedGround()
66
+ @box.container = $('#application')
67
+ @box.widgets =
68
+ '.post:first': TextWidget
69
+ '.widget:first': (i) -> new TextWidget i
70
+ @box.__setupWidgets()
71
+
72
+ expect(@ground.find('.post').html()).toEqual 'fluffy'
73
+ expect(@ground.find('.widget').html()).toEqual 'fluffy'
74
+ expect(@box.__activeWidgets[0].tester).toBeUndefined()
75
+ expect(@box.__activeWidgets[1].tester).toEqual 0
@@ -0,0 +1,175 @@
1
+ describe "Joosy.Page", ->
2
+
3
+ beforeEach ->
4
+ window.JST = 'app/templates/layouts/default': (->)
5
+ class window.ApplicationLayout extends Joosy.Layout
6
+ class @TestPage extends Joosy.Page
7
+
8
+
9
+ describe "not rendered page", ->
10
+
11
+ beforeEach ->
12
+ sinon.stub @TestPage.prototype, '__bootstrap'
13
+ sinon.stub @TestPage.prototype, '__bootstrapLayout'
14
+ @box = new @TestPage()
15
+ expect(@TestPage::__bootstrap.callCount).toEqual 0
16
+ expect(@TestPage::__bootstrapLayout.callCount).toEqual 1
17
+
18
+
19
+ it "should have appropriate accessors", ->
20
+ callback_names = ['fetch', 'beforePaint', 'paint', 'afterPaint', 'erase']
21
+ callback_names.each (func) =>
22
+ @TestPage[func] 'callback'
23
+ expect(@TestPage::['__' + func]).toEqual 'callback'
24
+
25
+ @TestPage.scroll '#here'
26
+ expect(@TestPage::__scrollElement).toEqual '#here'
27
+ expect(@TestPage::__scrollSpeed).toEqual 500
28
+ expect(@TestPage::__scrollMargin).toEqual 0
29
+
30
+ @TestPage.scroll '#there', speed: 1000, margin: -5
31
+ expect(@TestPage::__scrollElement).toEqual '#there'
32
+ expect(@TestPage::__scrollSpeed).toEqual 1000
33
+ expect(@TestPage::__scrollMargin).toEqual -5
34
+
35
+ @TestPage.layout 'test'
36
+ expect(@TestPage::__layoutClass).toEqual 'test'
37
+
38
+ it "should not render layout if it not changes", ->
39
+ @box.layout = new ApplicationLayout()
40
+ @box.layout.yield()
41
+ new @TestPage {}, @box
42
+ expect(@TestPage::__bootstrap.callCount).toEqual 1
43
+ expect(@TestPage::__bootstrapLayout.callCount).toEqual 1
44
+
45
+ it "should render layout if it changes", ->
46
+ class SubLayout extends Joosy.Layout
47
+ @box = new @TestPage()
48
+ new @TestPage {}, @box
49
+ expect(@TestPage::__bootstrap.callCount).toEqual 0
50
+ expect(@TestPage::__bootstrapLayout.callCount).toEqual 3
51
+
52
+ it "should stop render on beforeFilter result", ->
53
+ sinon.stub @TestPage.prototype, '__runBeforeLoads'
54
+ @TestPage::__runBeforeLoads.returns(false)
55
+ new @TestPage()
56
+ expect(@TestPage::__bootstrap.callCount).toEqual 0
57
+ expect(@TestPage::__bootstrapLayout.callCount).toEqual 1
58
+
59
+ it "should use Router", ->
60
+ target = sinon.stub Joosy.Router, 'navigate'
61
+ @box.navigate 'there'
62
+ expect(target.callCount).toEqual 1
63
+ expect(target.alwaysCalledWithExactly 'there').toBeTruthy()
64
+ Joosy.Router.navigate.restore()
65
+
66
+ it "should load itself", ->
67
+ spies = []
68
+ spies.push sinon.spy(@box, 'refreshElements')
69
+ spies.push sinon.spy(@box, '__delegateEvents')
70
+ spies.push sinon.spy(@box, '__setupWidgets')
71
+ spies.push sinon.spy(@box, '__runAfterLoads')
72
+ @box.__load()
73
+ expect(spies).toBeSequenced()
74
+
75
+ it "should unload itself", ->
76
+ spies = []
77
+ spies.push sinon.spy(@box, 'clearTime')
78
+ spies.push sinon.spy(@box, '__unloadWidgets')
79
+ spies.push sinon.spy(@box, '__removeMetamorphs')
80
+ spies.push sinon.spy(@box, '__runAfterUnloads')
81
+ @box.__unload()
82
+ expect(spies).toBeSequenced()
83
+
84
+ describe "rendered page", ->
85
+
86
+ beforeEach ->
87
+ @box.previous = new @TestPage()
88
+ @box.previous.layout = new @box.previous.__layoutClass
89
+ @box.__renderer = sinon.spy()
90
+ @box.__layoutClass.prototype.__renderer = sinon.spy()
91
+ @TestPage::__bootstrap.restore()
92
+ @TestPage::__bootstrapLayout.restore()
93
+
94
+
95
+ it "should wait stageClear and dataReceived event to start render", ->
96
+ spies = []
97
+
98
+ spies.push @box.previous.__erase = sinon.spy (stage, callback) ->
99
+ expect(stage.selector).toEqual @layout.content().selector
100
+ callback()
101
+
102
+ spies.push sinon.spy(@box.previous, '__unload')
103
+
104
+ spies.push @box.__beforePaint = sinon.spy (stage, callback) ->
105
+ expect(stage.selector).toEqual @layout.content().selector
106
+ expect(@__oneShotEvents[0][0]).toEqual ['stageClear', 'dataReceived']
107
+ callback()
108
+ expect(@__oneShotEvents[0][0]).toEqual ['dataReceived']
109
+
110
+ spies.push @box.__fetch = sinon.spy (callback) ->
111
+ expect(@__oneShotEvents[0][0]).toEqual ['dataReceived']
112
+ callback()
113
+ expect(@__oneShotEvents).toEqual []
114
+
115
+ spies.push @box.__paint = sinon.spy (stage, callback) ->
116
+ expect(stage.selector).toEqual @layout.content().selector
117
+ expect(typeof callback).toEqual 'function'
118
+ # callback() - start rendering
119
+
120
+ @box.__bootstrap()
121
+
122
+ expect(spies).toBeSequenced()
123
+
124
+ it "should render page", ->
125
+ spies = []
126
+
127
+ spies.push @box.__renderer
128
+ spies.push sinon.spy(@box, 'swapContainer')
129
+ spies.push sinon.spy(@box, '__load')
130
+
131
+ @box.__bootstrap()
132
+ expect(spies).toBeSequenced()
133
+
134
+ it "should wait stageClear and dataReceived event to start layout render", ->
135
+ spies = []
136
+
137
+ spies.push ApplicationLayout::__erase = sinon.spy (stage, page, callback) ->
138
+ expect(stage.selector).toEqual Joosy.Application.content().selector
139
+ callback()
140
+
141
+ spies.push sinon.spy(@box.previous.layout, '__unload')
142
+ spies.push sinon.spy(@box.previous, '__unload')
143
+
144
+ spies.push ApplicationLayout::__beforePaint = sinon.spy (stage, page, callback) =>
145
+ expect(stage.selector).toEqual Joosy.Application.content().selector
146
+ expect(@box.__oneShotEvents[0][0]).toEqual ['stageClear', 'dataReceived']
147
+ callback()
148
+ expect(@box.__oneShotEvents[0][0]).toEqual ['dataReceived']
149
+
150
+ spies.push @box.__fetch = sinon.spy (callback) ->
151
+ expect(@__oneShotEvents[0][0]).toEqual ['dataReceived']
152
+ callback()
153
+ expect(@__oneShotEvents).toEqual []
154
+
155
+ spies.push ApplicationLayout::__paint = sinon.spy (stage, page, callback) ->
156
+ expect(stage.selector).toEqual Joosy.Application.content().selector
157
+ expect(typeof callback).toEqual 'function'
158
+ # callback() - start rendering
159
+
160
+ @box.__bootstrapLayout()
161
+
162
+ expect(spies).toBeSequenced()
163
+
164
+ it "should render layout and page", ->
165
+ spies = []
166
+
167
+ spies.push @box.__layoutClass.prototype.__renderer
168
+ spies.push @box.__renderer
169
+ swapContainer = sinon.spy(@box, 'swapContainer')
170
+ spies.push @box.__layoutClass.prototype.__load = sinon.spy()
171
+ spies.push sinon.spy(@box, '__load')
172
+
173
+ @box.__bootstrapLayout()
174
+ expect(spies).toBeSequenced()
175
+ expect(swapContainer.callCount).toEqual 2
@@ -0,0 +1,35 @@
1
+ describe "Joosy.Resource.Generic", ->
2
+
3
+ beforeEach ->
4
+ @resource = Joosy.Resource.Generic.create @data =
5
+ foo: 'bar'
6
+ bar: 'baz'
7
+ very:
8
+ deep:
9
+ value: 'boo!'
10
+
11
+
12
+ it "should remember where it belongs", ->
13
+ resource = new Joosy.Resource.Generic foo: 'bar'
14
+ expect(resource.e).toEqual Object.extended(foo: 'bar')
15
+
16
+ it "should produce magic function", ->
17
+ expect(Object.isFunction @resource).toBeTruthy()
18
+ expect(@resource.e).toEqual Object.extended(@data)
19
+
20
+ it "should get values", ->
21
+ expect(@resource 'foo').toEqual 'bar'
22
+ expect(@resource 'very.deep').toEqual value: 'boo!'
23
+ expect(@resource 'very.deep.value').toEqual 'boo!'
24
+
25
+ it "should set values", ->
26
+ expect(@resource 'foo').toEqual 'bar'
27
+ @resource 'foo', 'baz'
28
+ expect(@resource 'foo').toEqual 'baz'
29
+
30
+ expect(@resource 'very.deep').toEqual value: 'boo!'
31
+ @resource 'very.deep', 'banana!'
32
+ expect(@resource 'very.deep').toEqual 'banana!'
33
+
34
+ @resource 'another.deep.value', 'banana strikes back'
35
+ expect(@resource 'another.deep').toEqual value: 'banana strikes back'
@@ -0,0 +1,65 @@
1
+ describe "Joosy.Resource.RESTCollection", ->
2
+
3
+ class Test extends Joosy.Resource.REST
4
+ @entity 'test'
5
+
6
+ data = '[{"id": 1, "name": "test1"}, {"id": 2, "name": "test2"}]'
7
+
8
+ checkData = (collection) ->
9
+ expect(collection.data.length).toEqual 2
10
+ expect(collection.pages[1]).toEqual collection.data
11
+ expect(collection.data[0].constructor == Test).toBeTruthy()
12
+ expect(collection.data[0].e.name).toEqual 'test1'
13
+
14
+ spoofData = (server) ->
15
+ target = server.requests.last()
16
+ expect(target.method).toEqual 'GET'
17
+ expect(target.url).toMatch /^\/tests\/\?(page=\d+&)?_=\d+/
18
+ target.respond 200, 'Content-Type': 'application/json', data
19
+
20
+ beforeEach ->
21
+ @server = sinon.fakeServer.create()
22
+ @collection = new Joosy.Resource.RESTCollection(Test)
23
+
24
+ afterEach ->
25
+ @server.restore()
26
+
27
+ it "should initialize", ->
28
+ expect(@collection.model).toEqual Test
29
+ expect(@collection.params).toEqual Object.extended()
30
+ expect(@collection.data).toEqual []
31
+ expect(@collection.pages).toEqual Object.extended()
32
+
33
+ it "should modelize", ->
34
+ result = @collection.modelize $.parseJSON(data)
35
+ expect(result[0].constructor == Test).toBeTruthy()
36
+ expect(result[0].e.name).toEqual 'test1'
37
+
38
+ it "should reset", ->
39
+ @collection.reset $.parseJSON(data)
40
+ checkData @collection
41
+
42
+ it "should fetch", ->
43
+ @collection.fetch()
44
+ spoofData @server
45
+ checkData @collection
46
+
47
+ it "should paginate", ->
48
+ @collection.fetch()
49
+ spoofData @server
50
+ checkData @collection
51
+
52
+ @collection.page 2, callback=sinon.spy()
53
+ spoofData @server
54
+ expect(callback.callCount).toEqual 1
55
+ expect(@collection.data.length).toEqual 4
56
+ expect(@collection.data[2].constructor == Test).toBeTruthy()
57
+ expect(@collection.data[2].e.name).toEqual 'test1'
58
+
59
+ # Again from cache
60
+ @collection.page 2, callback=sinon.spy()
61
+ spoofData @server
62
+ expect(callback.callCount).toEqual 1
63
+ expect(@collection.data.length).toEqual 4
64
+ expect(@collection.data[2].constructor == Test).toBeTruthy()
65
+ expect(@collection.data[2].e.name).toEqual 'test1'
@@ -0,0 +1,108 @@
1
+ describe "Joosy.Resource.REST", ->
2
+
3
+ beforeEach ->
4
+ @server = sinon.fakeServer.create()
5
+ class @Test extends Joosy.Resource.REST
6
+ @entity 'test'
7
+
8
+ afterEach ->
9
+ @server.restore()
10
+
11
+ it "should have default primary key", ->
12
+ expect(@Test::__primaryKey).toEqual 'id'
13
+
14
+ it "should have appropriate accessors", ->
15
+ @Test.entity 'tada'
16
+ expect(@Test::__entityName).toEqual 'tada'
17
+ expect(@Test.entityName()).toEqual 'tada'
18
+ @Test.source 'uri'
19
+ expect(@Test::__source).toEqual 'uri'
20
+ expect(@Test.__buildSource()).toEqual 'uri/'
21
+ @Test.primary 'uid'
22
+ expect(@Test::__primaryKey).toEqual 'uid'
23
+ @Test.beforeLoad 'function'
24
+ expect(@Test::__beforeLoad).toEqual 'function'
25
+
26
+ it "should build source url based on entity name", ->
27
+ options =
28
+ extension: 'id'
29
+ params:
30
+ test: 1
31
+ expect(@Test.__buildSource(options)).toEqual '/tests/id?test=1'
32
+
33
+ it "should have overloaded constructor", ->
34
+ resource = @Test.create 'someId'
35
+ expect(resource.id).toEqual 'someId'
36
+
37
+ data = {id: 'someId', field: 'value'}
38
+
39
+ rooted = @Test.create {test: data}
40
+ unrooted = @Test.create data
41
+
42
+ expect(rooted.e).toEqual unrooted.e
43
+ expect(rooted.id).toEqual 'someId'
44
+ expect(rooted.e.id).toEqual 'someId'
45
+ expect(rooted.e.field).toEqual 'value'
46
+
47
+ it 'should find single object', ->
48
+ @Test.beforeLoad beforeLoadCallback = sinon.spy (data) ->
49
+ expect(data.id).toEqual 1
50
+ expect(data.name).toEqual 'test1'
51
+ Object.extended(test: data)
52
+ @Test.find 1, callback = sinon.spy (target) ->
53
+ expect(target.id).toEqual 1
54
+ expect(target.e?.name).toEqual 'test1'
55
+ target = @server.requests[0]
56
+ expect(target.method).toEqual 'GET'
57
+ expect(target.url).toMatch /^\/tests\/1\?_=\d+/
58
+ target.respond 200, 'Content-Type': 'application/json',
59
+ '{"id": 1, "name": "test1"}'
60
+ expect(callback.callCount).toEqual 1
61
+ expect(beforeLoadCallback.callCount).toEqual 1
62
+
63
+ it 'should find objects collection with params', ->
64
+ callback = sinon.spy (collection) ->
65
+ i = 1
66
+ collection.data.each (target) ->
67
+ expect(target.id).toEqual i
68
+ expect(target.e?.name).toEqual 'test' + i
69
+ i += 1
70
+ @Test.find null, callback
71
+ target = @server.requests[0]
72
+ expect(target.method).toEqual 'GET'
73
+ expect(target.url).toMatch /^\/tests\/\?_=\d+/
74
+ target.respond 200, 'Content-Type': 'application/json',
75
+ '[{"id": 1, "name": "test1"}, {"id": 2, "name": "test2"}]'
76
+ expect(callback.callCount).toEqual 1
77
+
78
+ it 'should find all objects collection', ->
79
+ callback = sinon.spy (collection) ->
80
+ i = 1
81
+ collection.data.each (target) ->
82
+ expect(target.id).toEqual i
83
+ expect(target.e?.name).toEqual 'test' + i
84
+ i += 1
85
+ @Test.find callback
86
+ target = @server.requests[0]
87
+ expect(target.method).toEqual 'GET'
88
+ expect(target.url).toMatch /^\/tests\/\?_=\d+/
89
+ target.respond 200, 'Content-Type': 'application/json',
90
+ '[{"id": 1, "name": "test1"}, {"id": 2, "name": "test2"}]'
91
+ expect(callback.callCount).toEqual 1
92
+
93
+ it 'should destroy single object', ->
94
+ obj = @Test.create 1
95
+ callback = sinon.spy (target) ->
96
+ expect(target).toBe obj
97
+ obj.destroy callback
98
+ target = @server.requests[0]
99
+ expect(target.method).toEqual 'DELETE'
100
+ expect(target.url).toEqual '/tests/1'
101
+ target.respond 200
102
+ expect(callback.callCount).toEqual 1
103
+
104
+ it "should identify identifiers", ->
105
+ [0, 123, -5, '123abd', 'whatever'].each (variant) =>
106
+ expect(@Test.__isId variant).toBeTruthy()
107
+ [(->) , [], {}, null, undefined, true, false].each (variant) =>
108
+ expect(@Test.__isId variant).toBeFalsy()