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
data/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "keywords": [
5
5
  "joosy"
6
6
  ],
7
- "version": "1.2.0-alpha.38",
7
+ "version": "1.2.0-alpha.41",
8
8
  "author": "Boris Staal <boris@staal.io>",
9
9
  "homepage": "http://joosy.ws/",
10
10
  "repository": {
@@ -18,16 +18,16 @@
18
18
  "node": ">=0.4.0"
19
19
  },
20
20
  "dependencies": {
21
+ "bower": "~1.0.0",
21
22
  "sugar": "~1.3.8",
22
23
  "coffee-script": "~1.6.3",
23
24
  "command-router": "0.0.5",
24
25
  "resolve": "~0.4.0",
25
26
  "ejs": "~0.8.4",
26
- "prompt": "~0.2.9",
27
+ "commander": "~2.0.0",
27
28
  "commander": "~1.2.0",
28
29
  "colors": "~0.6.0-1",
29
30
  "grunt": "~0.4.1",
30
- "bower": "~0.10.0",
31
31
  "mincer": "~0.4.6",
32
32
  "connect": "~2.7.11",
33
33
  "haml-coffee": "~1.11.1",
@@ -0,0 +1,33 @@
1
+ #
2
+ # DOM builders
3
+ #
4
+ beforeEach ->
5
+ @$body = $('body')
6
+ @$ground = $('<div/>').attr('id', 'ground')
7
+
8
+ @$body.append @$ground
9
+
10
+ @$ground.seed = @seedGround = =>
11
+ @$ground.html """
12
+ <div id="application" class="application">
13
+ <div id="header" class="header" />
14
+ <div id="wrapper" class="wrapper">
15
+ <div id="content" class="content">
16
+ <div id="post1" class="post" />
17
+ <div id="post2" class="post" />
18
+ <div id="post3" class="post" />
19
+ </div>
20
+ <div id="sidebar" class="sidebar">
21
+ <div id="widget1" class="widget" />
22
+ <div id="widget2" class="widget" />
23
+ </div>
24
+ </div>
25
+ <div id="footer" class="footer" />
26
+ </div>
27
+ """
28
+
29
+ #
30
+ # DOM cleanup
31
+ #
32
+ afterEach ->
33
+ @$ground.remove()
@@ -0,0 +1,65 @@
1
+ beforeEach ->
2
+ @addMatchers
3
+
4
+ #
5
+ # Checks whether listed array of callbacks was
6
+ # called in exact order one by one
7
+ #
8
+ # @example
9
+ # [ (->), (->), (->) ].toBesequenced()
10
+ #
11
+ toBeSequenced: ->
12
+ # Are we working with array?
13
+ if !Object.isArray(@actual) || @actual.length == 0
14
+ @message = -> 'Not array or empty array given'
15
+ return false
16
+
17
+ # Was every spy called just once?
18
+ for spy, i in @actual
19
+ unless spy.callCount == 1
20
+ @message = -> "Spy ##{i} was called #{spy.callCount} times instead of just one"
21
+ return false
22
+
23
+ # Were they called in a proper order?
24
+ if @actual.length > 1
25
+ for spy, i in @actual.from(1)
26
+ unless spy.calledAfter @actual[i]
27
+ @message = -> "Spy ##{i+1} wasn't called after spy ##{i}"
28
+ return false
29
+
30
+ return true
31
+
32
+ #
33
+ # Checks the exact equality of tag including attributes and content
34
+ # with the posibility to check attributes values by regexp
35
+ #
36
+ # @example
37
+ # tag = "<div class='foo' id='bar'>foo</div>"
38
+ # tag.toBeTag 'div', 'foo', class: 'foo', id: /\S+/
39
+ #
40
+ toBeTag: (tagName, content, attrs) ->
41
+ @message = =>
42
+ "Expected #{@actual} to be a tag #{tagName} with attributes #{JSON.stringify attrs} and content '#{content}'"
43
+
44
+ tag = $ @actual
45
+
46
+ # Is it alone?
47
+ flag = tag.length == 1
48
+
49
+ # Tag name matches?
50
+ flag &&= tag[0].nodeName == tagName.toUpperCase()
51
+
52
+ # Content matches?
53
+ flag &&= tag.html() == content if content != false
54
+
55
+ # Same number of attributes?
56
+ flag &&= tag[0].attributes.length == Object.keys(attrs).length
57
+
58
+ # Attributes match?
59
+ for name, val of attrs
60
+ if val.constructor == RegExp
61
+ flag &&= tag.attr(name).match(val)
62
+ else
63
+ flag &&= tag.attr(name) == val
64
+
65
+ flag
@@ -1,27 +1,27 @@
1
1
  describe "Joosy.Application", ->
2
2
 
3
3
  beforeEach ->
4
- sinon.stub(Joosy.Router, "__setupRoutes")
5
- @seedGround()
4
+ sinon.stub Joosy.Router, "__setupRoutes"
5
+ @$ground.seed()
6
6
 
7
7
  afterEach ->
8
8
  Joosy.Router.__setupRoutes.restore()
9
9
 
10
- it "should initialize", ->
11
- Joosy.Application.initialize 'app', '#application', router: {foo: 'bar'}
10
+ it "initializes", ->
11
+ Joosy.Application.initialize 'app', '#application', foo: {bar: 'baz'}
12
12
  expect(Joosy.Application.page).toBeUndefined()
13
13
  expect(Joosy.Application.selector).toEqual '#application'
14
14
  expect(Joosy.Router.__setupRoutes.callCount).toEqual 1
15
15
  expect(Joosy.Application.name).toEqual 'app'
16
- expect(Joosy.Application.config.router.foo).toEqual 'bar'
17
-
18
- it "should set container", ->
16
+ expect(Joosy.Application.config.foo.bar).toEqual 'baz'
19
17
  expect(Joosy.Application.content()).toEqual $('#application')
20
18
 
21
- it "should properly initialize and save pages", ->
19
+ it "manages pages", ->
22
20
  spy = sinon.spy()
21
+
23
22
  class Page1
24
23
  constructor: spy
24
+
25
25
  class Page2
26
26
  constructor: spy
27
27
 
@@ -1,10 +1,14 @@
1
1
  describe "Joosy.Helpers.View", ->
2
2
 
3
+ # Shortcut
3
4
  h = Joosy.Helpers.Application
4
5
 
5
- it "renders tag with string inner", ->
6
- expect(h.tag 'div', {id: 'ololo'}, 'test').toEqual '<div id="ololo">test</div>'
6
+ it "renders tag with string content", ->
7
+ tag = h.tag 'div', {id: 'id'}, 'content'
8
+ expect(tag).toEqual '<div id="id">content</div>'
7
9
 
8
- it "renders tag with lambda inner", ->
9
- data = h.tag 'div', {id: 'ololo'}, -> h.tag 'div', {id: 'ololo'}, 'test'
10
- expect(data).toEqual '<div id="ololo"><div id="ololo">test</div></div>'
10
+ it "renders tag with lambda content", ->
11
+ tag = h.tag 'div', {id: 'id'}, ->
12
+ h.tag 'div', {id: 'id2'}, 'content'
13
+
14
+ expect(tag).toEqual '<div id="id"><div id="id2">content</div></div>'
@@ -1,17 +1,50 @@
1
1
  describe "Joosy.Helpers.Widgets", ->
2
2
 
3
- h = Joosy.Helpers.Application
4
-
5
3
  beforeEach ->
6
- h.onRefresh = sinon.spy()
4
+ @$ground.seed()
5
+
6
+ # Widget we are going to include using helper
7
+ class @Widget extends Joosy.Widget
8
+ @view -> "test"
9
+
10
+ # Least possible configuration capable of using helper
11
+ class @Renderer extends Joosy.Module
12
+ @include Joosy.Modules.Renderer
13
+ @include Joosy.Modules.TimeManager
14
+ @include Joosy.Modules.WidgetsManager
15
+
16
+ @renderer = new @Renderer
17
+ @widget = new @Widget
18
+ @template = (context) => context.widget 'div', @widget
19
+
20
+ it "renders widget tag", ->
21
+ runs ->
22
+ @$ground.find('#header').html @renderer.render(@template)
7
23
 
8
- afterEach ->
9
- delete h.onRefresh
24
+ expect(@$ground.find('#header').html()).toBeTag 'div', '',
25
+ id: /__joosy\d+/
10
26
 
11
27
  it "renders widget tag", ->
12
- expect(h.widget 'div', (->)).toBeTag 'div', '', id: /\S{36}/
13
- expect(h.onRefresh.callCount).toEqual 1
28
+ runs ->
29
+ @template = (context) => context.widget 'div', {class: 'test'}, @widget
30
+
31
+ @$ground.find('#header').html @renderer.render(@template)
32
+
33
+ expect(@$ground.find('#header').html()).toBeTag 'div', '',
34
+ id: /__joosy\d+/
35
+ class: 'test'
36
+
37
+ it "bootstraps widget", ->
38
+ runs ->
39
+ @$ground.find('#header').html @renderer.render(@template)
40
+
41
+ # At the first step only the container will be injected into HTML
42
+ expect(@$ground.find('#header').html()).toBeTag 'div', '',
43
+ id: /__joosy\d+/
44
+
45
+ waits 0
14
46
 
15
- it "renders widget tag with given classes", ->
16
- expect(h.widget 'div.class1.class2', (->)).toBeTag 'div', '', id: /\S{36}/, class: 'class1 class2'
17
- expect(h.onRefresh.callCount).toEqual 1
47
+ runs ->
48
+ # But at the next asynchronous tick, widget will be emerged
49
+ expect(@$ground.find('#header').html()).toBeTag 'div', 'test',
50
+ id: /__joosy\d+/
@@ -1,41 +1,60 @@
1
1
  describe "Joosy", ->
2
2
 
3
- it "should properly initialize", ->
3
+ it "initializes", ->
4
4
  expect(Joosy.Application.config.debug).toBeFalsy()
5
5
  expect(Joosy.Modules).toBeDefined()
6
6
  expect(Joosy.Resources).toBeDefined()
7
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')
8
+ it "generates proper UUIDs", ->
9
+ uuids = []
10
+ 2.times -> uuids.push Joosy.uuid()
11
+ expect(uuids.unique().length).toEqual(2)
12
+ 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}/
13
+
14
+ it "generates proper UIDs", ->
15
+ uids = []
16
+ 5.times -> uids.push Joosy.uid()
17
+ expect(uids.unique().length).toEqual(5)
18
+
19
+ it "builds proper URLs", ->
20
+ expect(Joosy.buildUrl 'http://www.org').toEqual('http://www.org')
21
+ expect(Joosy.buildUrl 'http://www.org#hash').toEqual('http://www.org#hash')
22
+ expect(Joosy.buildUrl 'http://www.org', {foo: 'bar'}).toEqual('http://www.org?foo=bar')
23
+ expect(Joosy.buildUrl 'http://www.org?bar=baz', {foo: 'bar'}).toEqual('http://www.org?bar=baz&foo=bar')
14
24
 
15
- it "should imprint namespace paths in Joosy.Module descendants", ->
16
- Joosy.namespace 'Irish', ->
17
- class @Pub extends Joosy.Module
25
+ describe "namespacer", ->
26
+ it "declares", ->
27
+ Joosy.namespace 'Namespaces.Test1'
28
+ Joosy.namespace 'Namespaces.Test2', ->
29
+ @bingo = 'bongo'
18
30
 
19
- Joosy.namespace 'British', ->
20
- class @Pub extends Joosy.Module
31
+ expect(window.Namespaces.Test1).toBeDefined()
32
+ expect(window.Namespaces.Test2.bingo).toEqual('bongo')
21
33
 
22
- Joosy.namespace 'Keltic', ->
23
- class @Pub extends Irish.Pub
34
+ it "imprints path", ->
35
+ Joosy.namespace 'Irish', ->
36
+ class @Pub extends Joosy.Module
24
37
 
25
- expect(Irish.Pub.__namespace__).toEqual ["Irish"]
26
- expect(British.Pub.__namespace__).toEqual ["British"]
27
- expect(Keltic.Pub.__namespace__).toEqual ["Keltic"]
38
+ Joosy.namespace 'British', ->
39
+ class @Pub extends Joosy.Module
28
40
 
29
- Joosy.namespace 'Deeply.Nested', ->
30
- class @Klass extends Joosy.Module
41
+ Joosy.namespace 'Keltic', ->
42
+ class @Pub extends Irish.Pub
31
43
 
32
- expect(Deeply.Nested.Klass.__namespace__).toEqual ["Deeply", "Nested"]
44
+ expect(Irish.Pub.__namespace__).toEqual ["Irish"]
45
+ expect(British.Pub.__namespace__).toEqual ["British"]
46
+ expect(Keltic.Pub.__namespace__).toEqual ["Keltic"]
33
47
 
34
- class @Flat extends Joosy.Module
48
+ Joosy.namespace 'Deeply.Nested', ->
49
+ class @Klass extends Joosy.Module
35
50
 
36
- expect(@Flat.__namespace__).toEqual []
51
+ expect(Deeply.Nested.Klass.__namespace__).toEqual ["Deeply", "Nested"]
37
52
 
38
- it "should set up helpers", ->
53
+ class @Flat extends Joosy.Module
54
+
55
+ expect(@Flat.__namespace__).toEqual []
56
+
57
+ it "declares helpers", ->
39
58
  Joosy.helpers 'Hoge', ->
40
59
  @fuga = ->
41
60
  "piyo"
@@ -43,30 +62,3 @@ describe "Joosy", ->
43
62
  expect(window.Joosy.Helpers).toBeDefined()
44
63
  expect(window.Joosy.Helpers.Hoge).toBeDefined()
45
64
  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)
@@ -1,50 +1,46 @@
1
1
  describe "Joosy.Layout", ->
2
2
 
3
3
  beforeEach ->
4
- class @TestLayout extends Joosy.Layout
5
- @box = new @TestLayout()
4
+ class @Layout extends Joosy.Layout
5
+ @layout = new @Layout
6
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'
7
+ it "has appropriate accessors", ->
8
+ callbackNames = ['beforePaint', 'paint', 'erase']
9
+ callbackNames.each (callbackName) =>
10
+ @Layout[callbackName] 'callback'
11
+ expect(@Layout::['__' + callbackName]).toEqual 'callback'
12
12
 
13
- it "should have default view", ->
14
- @box = new @TestLayout()
15
- expect(@box.__renderer instanceof Function).toBeTruthy()
13
+ it "generates uid", ->
14
+ @layout.page()
15
+ expect(@layout.uid).toBeDefined()
16
16
 
17
- it "should use Router", ->
17
+ it "uses uid as selector", ->
18
+ @layout.page()
19
+ expect(@layout.content().selector).toEqual '#' + @layout.uid
20
+
21
+ it "has default view", ->
22
+ expect(@layout.__renderDefault instanceof Function).toBeTruthy()
23
+
24
+ it "integrates with Router", ->
18
25
  target = sinon.stub Joosy.Router, 'navigate'
19
- @box.navigate 'there'
26
+ @layout.navigate 'there'
20
27
  expect(target.callCount).toEqual 1
21
28
  expect(target.alwaysCalledWithExactly 'there').toBeTruthy()
22
29
  Joosy.Router.navigate.restore()
23
30
 
24
- it "should load itself", ->
31
+ it "loads", ->
25
32
  spies = []
26
- spies.push sinon.spy(@box, '__assignElements')
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)
33
+ spies.push sinon.spy(@layout, '__assignElements')
34
+ spies.push sinon.spy(@layout, '__delegateEvents')
35
+ spies.push sinon.spy(@layout, '__setupWidgets')
36
+ spies.push sinon.spy(@layout, '__runAfterLoads')
37
+ @layout.__load(@$ground)
31
38
  expect(spies).toBeSequenced()
32
39
 
33
- it "should unload itself", ->
40
+ it "unloads", ->
34
41
  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()
42
+ spies.push sinon.spy(@layout, '__clearTime')
43
+ spies.push sinon.spy(@layout, '__unloadWidgets')
44
+ spies.push sinon.spy(@layout, '__runAfterUnloads')
45
+ @layout.__unload()
39
46
  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