joosy 1.2.0.beta.4 → 1.2.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.codoopts +1 -1
  3. data/Gruntfile.coffee +3 -3
  4. data/README.md +4 -0
  5. data/bower.json +1 -1
  6. data/build/joosy.js +2 -2
  7. data/build/joosy/form.js +1 -1
  8. data/build/joosy/resources.js +1 -1
  9. data/package.json +2 -2
  10. data/source/joosy/application.coffee +2 -2
  11. data/source/joosy/form.coffee +4 -4
  12. data/source/joosy/helpers/form.coffee +12 -3
  13. data/source/joosy/helpers/index.coffee +0 -1
  14. data/source/joosy/helpers/view.coffee +16 -3
  15. data/source/joosy/layout.coffee +0 -4
  16. data/source/joosy/module.coffee +16 -1
  17. data/source/joosy/modules/dom.coffee +106 -101
  18. data/source/joosy/modules/events.coffee +44 -10
  19. data/source/joosy/modules/filters.coffee +64 -60
  20. data/source/joosy/modules/page.coffee +3 -0
  21. data/source/joosy/modules/page/scrolling.coffee +46 -29
  22. data/source/joosy/modules/page/title.coffee +14 -0
  23. data/source/joosy/modules/renderer.coffee +219 -190
  24. data/source/joosy/modules/resources.coffee +3 -0
  25. data/source/joosy/modules/resources/cacher.coffee +81 -10
  26. data/source/joosy/modules/resources/function.coffee +26 -29
  27. data/source/joosy/modules/resources/identity_map.coffee +64 -42
  28. data/source/joosy/modules/resources/model.coffee +127 -73
  29. data/source/joosy/modules/time_manager.coffee +2 -0
  30. data/source/joosy/page.coffee +3 -6
  31. data/source/joosy/resources/array.coffee +87 -2
  32. data/source/joosy/resources/hash.coffee +53 -1
  33. data/source/joosy/resources/rest.coffee +59 -3
  34. data/source/joosy/resources/scalar.coffee +47 -1
  35. data/source/joosy/router.coffee +63 -21
  36. data/source/joosy/templaters/jst.coffee +3 -0
  37. data/source/joosy/widget.coffee +17 -11
  38. data/spec/joosy/core/helpers/view_spec.coffee +14 -0
  39. data/spec/joosy/core/modules/dom_spec.coffee +1 -1
  40. data/spec/joosy/core/modules/filters_spec.coffee +2 -2
  41. data/spec/joosy/core/modules/module_spec.coffee +1 -1
  42. data/spec/joosy/core/modules/renderer_spec.coffee +19 -1
  43. data/spec/joosy/core/router_spec.coffee +80 -45
  44. data/spec/joosy/core/widget_spec.coffee +9 -0
  45. data/spec/joosy/resources/modules/cacher_spec.coffee +3 -3
  46. data/spec/joosy/resources/modules/function_spec.coffee +2 -2
  47. data/spec/joosy/resources/modules/identity_map_spec.coffee +2 -2
  48. data/spec/joosy/resources/modules/model_spec.coffee +1 -1
  49. metadata +2 -5
  50. data/source/joosy/helpers/routes.coffee +0 -17
  51. data/source/joosy/modules/widgets_manager.coffee +0 -90
  52. data/spec/joosy/core/helpers/routes_spec.coffee +0 -15
@@ -1 +1,4 @@
1
+ #
2
+ # The namespace for resources modules
3
+ #
1
4
  Joosy.Modules.Resources = {}
@@ -1,13 +1,73 @@
1
1
  #= require ../resources
2
2
 
3
+ #
4
+ # Cacher allows you to implement the delayed refresh of a resource.
5
+ #
6
+ # During the first fetch it stores the current value to localStorage
7
+ # and on the second call it returns the cached value. At the same moment
8
+ # it starts the new fetch and triggers 'changed' event as soon as
9
+ # the new value arrives.
10
+ #
11
+ # When used in conjuction with `@renderDynamic` it allows you to render
12
+ # template with cached value immediately. And the new arrived value will
13
+ # automatically be injected into DOM.
14
+ #
15
+ # @example
16
+ # class Test extends Joosy.Resources.Hash
17
+ # @concern Joosy.Modules.Resources.Cacher
18
+ #
19
+ # @cache 'test' # key to use at localStorage
20
+ # @fetcher (callback) ->
21
+ # $.get '...', (data) -> callback data
22
+ #
23
+ # Test.cached (instance) ->
24
+ # # ...
25
+ #
3
26
  # @mixin
4
27
  Joosy.Modules.Resources.Cacher =
5
28
 
6
- included: ->
7
- @cache = (cacheKey) -> @::__cacheKey = cacheKey
8
- @fetcher = (fetcher) -> @::__fetcher = fetcher
29
+ ClassMethods:
30
+ #
31
+ # Defines the key that will be used to reference
32
+ # the cached value at localStorage. Unless specified
33
+ # localStorage will not be used.
34
+ #
35
+ # @param [String] cacheKey
36
+ # @example
37
+ # class Test extends Joosy.Resources.Hash
38
+ # @concern Joosy.Modules.Resources.Cacher
39
+ # @cache 'test'
40
+ #
41
+ cache: (cacheKey) -> @::__cacheKey = cacheKey
9
42
 
10
- @cached = (callback, cacheKey=false, fetcher=false) ->
43
+ #
44
+ # Defines the asynchronous routine that will be used
45
+ # to load the actual value of resource when `cached` is called.
46
+ #
47
+ # @param [Function<Function>] fetcher
48
+ # @example
49
+ # class Test extends Joosy.Resources.Hash
50
+ # @concern Joosy.Modules.Resources.Cacher
51
+ # @fetcher (callback) ->
52
+ # $.get '...', (data) -> callback data
53
+ #
54
+ fetcher: (fetcher) -> @::__fetcher = fetcher
55
+
56
+ #
57
+ # The main resolver of current value. If the cached
58
+ # value is available it is returned immediately. Otherwise
59
+ # the actual value is loaded.
60
+ #
61
+ # During the first fetch it stores the current value to localStorage
62
+ # and on the second call it returns the cached value. At the same moment
63
+ # it starts the new fetch and triggers 'changed' event as soon as
64
+ # the new value arrives.
65
+ #
66
+ # @param [Function<Cacher>] callback Action to perform with resulting instance of resource
67
+ # @param [String] cacheKey The override for {Joosy.Modules.Resources.Cacher.cache}
68
+ # @param [Function<Function>] fetcher The override for {Joosy.Modules.Resources.Cacher.fetcher}
69
+ #
70
+ cached: (callback, cacheKey=false, fetcher=false) ->
11
71
  if typeof(cacheKey) == 'function'
12
72
  fetcher = cacheKey
13
73
  cacheKey = undefined
@@ -23,16 +83,27 @@ Joosy.Modules.Resources.Cacher =
23
83
  @fetch (results) =>
24
84
  instance = @build results...
25
85
  callback? instance
26
-
27
- @fetch = (callback) ->
86
+ #
87
+ # Low-level fetcher that gets the actual value and stores it to
88
+ # localStorage (if required)
89
+ #
90
+ # @param [Function<Mixed>] callback Action to perform with raw fetched value
91
+ #
92
+ fetch: (callback) ->
28
93
  @::__fetcher (results...) =>
29
94
  localStorage[@::__cacheKey] = JSON.stringify(results) if @::__cacheKey && localStorage
30
95
  callback results
31
96
 
32
- refresh: (callback) ->
33
- @constructor.fetch (results) =>
34
- @load results...
35
- callback? @
97
+ InstanceMethods:
98
+ #
99
+ # Refreshes the value in both – the cache and the resource itself
100
+ #
101
+ # @param [Function<Cacher>] callback Action to perform with resulting instance of resource
102
+ #
103
+ refresh: (callback) ->
104
+ @constructor.fetch (results) =>
105
+ @load results...
106
+ callback? @
36
107
 
37
108
  # AMD wrapper
38
109
  if define?.amd?
@@ -1,41 +1,38 @@
1
1
  #
2
2
  # Module allowing to emulate Fn-based instances in JS
3
3
  #
4
- # @mixin
5
- #
6
4
  # @example
7
- # class Foo extends Joosy.Module
8
- #
9
- # @extend Joosy.Module.Function
5
+ # class Foo extends Joosy.Modules.Resources.Hash
6
+ # @concern Joosy.Module.Function
10
7
  #
11
- # constructor: (value)
12
- # @value = value
8
+ # foo = Foo.build foo: 'bar'
9
+ # typeof(foo) # 'function'
10
+ # foo('foo') # 'bar'
13
11
  #
14
- # __call: -> @value
15
- #
16
- # foo = Foo.build 'test'
17
- # typeof(foo) # 'function'
18
- # foo() # 'test'
12
+ # @mixin
19
13
  #
20
14
  Joosy.Modules.Resources.Function =
21
15
 
22
- extended: ->
23
- if @build
24
- @aliasStaticMethodChain 'build', 'function'
25
- else
26
- @build = @buildWithFunction
27
- @buildWithoutFunction = ->
28
- new @ arguments...
16
+ ClassMethods:
17
+ # @nodoc
18
+ extended: ->
19
+ if @build
20
+ @aliasStaticMethodChain 'build', 'function'
21
+ else
22
+ @build = @buildWithFunction
23
+ @buildWithoutFunction = ->
24
+ new @ arguments...
29
25
 
30
- buildWithFunction: ->
31
- shim = -> shim.__call.apply shim, arguments
32
- proto = @buildWithoutFunction arguments...
26
+ # @nodoc
27
+ buildWithFunction: ->
28
+ shim = -> shim.__call.apply shim, arguments
29
+ proto = @buildWithoutFunction arguments...
33
30
 
34
- if shim.__proto__
35
- shim.__proto__ = proto
36
- else
37
- for key, value of proto
38
- shim[key] = value
31
+ if shim.__proto__
32
+ shim.__proto__ = proto
33
+ else
34
+ for key, value of proto
35
+ shim[key] = value
39
36
 
40
- shim.constructor = proto.constructor
41
- shim
37
+ shim.constructor = proto.constructor
38
+ shim
@@ -1,47 +1,69 @@
1
1
  #= require ../resources
2
2
 
3
+ #
4
+ # The basic implementation of Identity Map allowing you to automatically
5
+ # make all the instances of the same model to be a single JS Object.
6
+ #
7
+ # @example
8
+ # class Model extends Joosy.Resources.Hash
9
+ # @concern Joosy.Modules.Resources.Model
10
+ # @concern Joosy.Modules.Resources.IdentityMap
11
+ #
12
+ # foo = Test.build id: 1
13
+ # bar = Test.build id: 1 # bar === foo
14
+ #
3
15
  # @mixin
16
+ #
4
17
  Joosy.Modules.Resources.IdentityMap =
5
18
 
6
- extended: ->
7
- @::__identityHolder = @
8
- @aliasStaticMethodChain 'build', 'identityMap'
9
-
10
- #
11
- # Clears the identity map cache. Recomended to be called during layout switch to
12
- # ensure correct garbage collection.
13
- #
14
- identityReset: ->
15
- @::__identityHolder.identity = {}
16
-
17
- identityPath: (data) ->
18
- [
19
- @::__entityName, # entity name as a first-level entry to make inheritance safe
20
- "s#{@__source || ''}", # save identity from overlaping on `@at` calls
21
- data[@::__primaryKey] # direct identifier as a main distinguisher
22
- ]
23
-
24
- #
25
- # Wraps instance of resource inside shim-function allowing to track
26
- # data changes. See class example
27
- #
28
- # @return [Joosy.Resources.REST]
29
- #
30
- buildWithIdentityMap: (data={}) ->
31
- elements = @identityPath(data)
32
-
33
- if elements.filter((element) -> !element?).length == 0
34
- location = @::__identityHolder.identity ?= {}
35
- destination = elements.pop()
36
- location = location[element] ?= {} for element in elements
37
-
38
- # Data can be circulary referenced so we have to
39
- # init identity cell as a first step...
40
- preload = {}
41
- preload[@::__primaryKey] = data[@::__primaryKey]
42
- location[destination] ?= @buildWithoutIdentityMap preload
43
-
44
- # ...and load data as a second
45
- location[destination].load data
46
- else
47
- @buildWithoutIdentityMap data
19
+ ClassMethods:
20
+ # @nodoc
21
+ extended: ->
22
+ @::__identityHolder = @
23
+ @aliasStaticMethodChain 'build', 'identityMap'
24
+
25
+ #
26
+ # Clears the identity map cache. Recomended to be called during layout switch to
27
+ # ensure correct garbage collection.
28
+ #
29
+ identityReset: ->
30
+ @::__identityHolder.identity = {}
31
+
32
+ #
33
+ # Defines the array of values identifying a unique object within a set
34
+ #
35
+ # @param [Mixed] data The data given upon object creation
36
+ # @return [Array<Mixed>] Uniqueness path
37
+ #
38
+ identityPath: (data) ->
39
+ [
40
+ @::__entityName, # entity name as a first-level entry to make inheritance safe
41
+ "s#{@__source || ''}", # save identity from overlaping on `@at` calls
42
+ data[@::__primaryKey] # direct identifier as a main distinguisher
43
+ ]
44
+
45
+ #
46
+ # Wraps instance of resource inside shim-function allowing to track
47
+ # data changes. See class example
48
+ #
49
+ # @private
50
+ # @return [Mixed]
51
+ #
52
+ buildWithIdentityMap: (data={}) ->
53
+ elements = @identityPath(data)
54
+
55
+ if elements.filter((element) -> !element?).length == 0
56
+ location = @::__identityHolder.identity ?= {}
57
+ destination = elements.pop()
58
+ location = location[element] ?= {} for element in elements
59
+
60
+ # Data can be circulary referenced so we have to
61
+ # init identity cell as a first step...
62
+ preload = {}
63
+ preload[@::__primaryKey] = data[@::__primaryKey]
64
+ location[destination] ?= @buildWithoutIdentityMap preload
65
+
66
+ # ...and load data as a second
67
+ location[destination].load data
68
+ else
69
+ @buildWithoutIdentityMap data
@@ -1,18 +1,23 @@
1
1
  #= require ../resources
2
2
 
3
+ #
4
+ # The collection of methods extending basic implemenetation of resources
5
+ # adding typical model features and making it aware of primary key and collection.
6
+ #
7
+ # @example
8
+ # class Test extends Joosy.Resources.Hash
9
+ # @concern Joosy.Modules.Resources.Model
10
+ #
3
11
  # @mixin
4
12
  Joosy.Modules.Resources.Model =
5
13
 
6
- included: ->
14
+ ClassMethods:
7
15
  #
8
16
  # Sets the field containing primary key.
9
17
  #
10
- # @note It has no direct use inside the REST resource itself and can be omited.
11
- # But it usually should not since we have plans on adding some kind of Identity Map to Joosy.
12
- #
13
18
  # @param [String] primary Name of the field
14
19
  #
15
- @primaryKey = (primaryKey) ->
20
+ primaryKey: (primaryKey) ->
16
21
  @::__primaryKey = primaryKey
17
22
 
18
23
  #
@@ -20,7 +25,7 @@ Joosy.Modules.Resources.Model =
20
25
  #
21
26
  # @param [Class] klass Class to use as a collection wrapper
22
27
  #
23
- @collection = (klass) ->
28
+ collection: (klass) ->
24
29
  @::__collection = klass
25
30
 
26
31
  #
@@ -29,7 +34,7 @@ Joosy.Modules.Resources.Model =
29
34
  #
30
35
  # @param [String] name Singular name of resource
31
36
  #
32
- @entity = (name) ->
37
+ entity: (name) ->
33
38
  @::__entityName = name
34
39
 
35
40
  #
@@ -39,22 +44,26 @@ Joosy.Modules.Resources.Model =
39
44
  # to handle inline changes with triggers and all that resources stuff
40
45
  #
41
46
  # @example Basic usage
42
- # class Zombie extends Joosy.Resources.REST
47
+ # class Model extends Joosy.Resources.Hash
48
+ # @concern Joosy.Modules.Resources.Model
49
+ #
50
+ # class Zombie extends Model
43
51
  # @entity 'zombie'
44
- # class Puppy extends Joosy.Resources.REST
52
+ #
53
+ # class Puppy extends Model
45
54
  # @entity 'puppy'
46
55
  # @map 'zombies'
47
56
  #
48
57
  # p = Puppy.build {zombies: [{foo: 'bar'}]}
49
58
  #
50
- # p('zombies') # Direct access: [{foo: 'bar'}]
59
+ # p.get('zombies') # Direct access: [{foo: 'bar'}]
51
60
  # p.zombies # Wrapped Collection of Zombie instances
52
- # p.zombies.at(0)('foo') # bar
61
+ # p.zombies[0].get('foo') # bar
53
62
  #
54
63
  # @param [String] name Pluralized name of property to define
55
64
  # @param [Class] klass Resource class to instantiate
56
65
  #
57
- @map = (name, klass=false) ->
66
+ map: (name, klass=false) ->
58
67
  unless klass
59
68
 
60
69
  klass = window[inflection.camelize inflection.singularize(name)]
@@ -77,10 +86,24 @@ Joosy.Modules.Resources.Model =
77
86
  #
78
87
  # @param [DOMElement] form Form to grab
79
88
  #
80
- @grab = (form) ->
89
+ grab: (form) ->
81
90
  @build({}).grab form
82
91
 
83
- @attrAccessor = ->
92
+ #
93
+ # Registers dynamic accessors for given fields
94
+ #
95
+ # @example
96
+ # class Test extends Joosy.Resources.Hash
97
+ # @concern Joosy.Modules.Resources.Model
98
+ #
99
+ # @attrAccessor 'field1', 'field2'
100
+ #
101
+ # test = Test.build(field1: 'test', field2: 'test')
102
+ #
103
+ # test.field1() # 'test'
104
+ # test.field2('new value') # 'new value'
105
+ #
106
+ attrAccessor: ->
84
107
  for attribute in arguments
85
108
  do (attribute) =>
86
109
  @::[attribute] = (value) ->
@@ -89,63 +112,94 @@ Joosy.Modules.Resources.Model =
89
112
  else
90
113
  @get attribute
91
114
 
115
+ InstanceMethods:
116
+ #
117
+ # Default primary key field 'id'
118
+ #
119
+ __primaryKey: 'id'
120
+
121
+ #
122
+ # Default collection: Joosy.Resources.Array
123
+ #
124
+ __collection: Joosy.Resources.Array
125
+
126
+ #
127
+ # Getter for the primary key field
128
+ #
129
+ # @see Joosy.Modules.Resources.Model.primaryKey
130
+ #
131
+ id: ->
132
+ @data?[@__primaryKey]
133
+
134
+ #
135
+ # Returns the list of known fields for the resource
136
+ #
137
+ # @return [Array<String>]
138
+ #
139
+ # @example
140
+ # class Test extends Joosy.Resources.Hash
141
+ # @concern Joosy.Modules.Resources.Model
142
+ #
143
+ # test = Test.build field1: 'test', field2: 'test'
144
+ #
145
+ # test.knownAttributes() # ['field1', 'field2']
146
+ #
147
+ knownAttributes: ->
148
+ Object.keys @data
149
+
150
+ #
151
+ # Set the resource data manually.
152
+ #
153
+ # Unlike the basic implementation of load, this one
154
+ # merges newer and older fieldsets that allows you to
155
+ # receive different parts of model data from different endpoints.
156
+ #
157
+ # @param [Object] data Data to store
158
+ # @param [Boolean] clear Whether previous data should be overwriten (not merged)
159
+ #
160
+ # @return [Object] Returns self
161
+ #
162
+ load: (data, clear=false) ->
163
+ @data = {} if clear
164
+ @__fillData data
165
+ @
166
+
167
+ #
168
+ # Defines how exactly prepared data should be saved
169
+ #
170
+ # @param [Object] data Raw data to store
171
+ # @private
172
+ #
173
+ __fillData: (data, notify=true) ->
174
+ @raw = data
175
+ @data = {} unless @hasOwnProperty 'data'
176
+
177
+ Joosy.Module.merge @data, @__applyBeforeLoads(data)
178
+ @trigger 'changed' if notify
179
+ null
180
+
181
+ #
182
+ # Updates the Resource with a data from given form
183
+ #
184
+ # @param [DOMElement] form Form to grab
185
+ #
186
+ # @example
187
+ # class Test extends Joosy.Resources.Hash
188
+ # @concern Joosy.Modules.Resources.Model
189
+ #
190
+ # test = Test.build().grab($ 'form')
191
+ #
192
+ grab: (form) ->
193
+ data = {}
194
+ for field in $(form).serializeArray()
195
+ unless data[field.name]
196
+ data[field.name] = field.value
197
+ else
198
+ data[field.name] = [data[field.name]] unless data[field.name] instanceof Array
199
+ data[field.name].push field.value
200
+
201
+ @load data
92
202
 
93
- #
94
- # Default primary key field 'id'
95
- #
96
- __primaryKey: 'id'
97
-
98
- #
99
- # Default collection: Joosy.Resources.Array
100
- #
101
- __collection: Joosy.Resources.Array
102
-
103
- id: ->
104
- @data?[@__primaryKey]
105
-
106
- knownAttributes: ->
107
- Object.keys @data
108
-
109
- #
110
- # Set the resource data manually
111
- #
112
- # @param [Object] data Data to store
113
- #
114
- # @return [Object] Returns self
115
- #
116
- load: (data, clear=false) ->
117
- @data = {} if clear
118
- @__fillData data
119
- @
120
-
121
- #
122
- # Defines how exactly prepared data should be saved
123
- #
124
- # @param [Object] data Raw data to store
125
- #
126
- __fillData: (data, notify=true) ->
127
- @raw = data
128
- @data = {} unless @hasOwnProperty 'data'
129
-
130
- Joosy.Module.merge @data, @__applyBeforeLoads(data)
131
- @trigger 'changed' if notify
132
- null
133
-
134
- #
135
- # Updates the Resource with a data from given form
136
- #
137
- # @param [DOMElement] form Form to grab
138
- #
139
- grab: (form) ->
140
- data = {}
141
- for field in $(form).serializeArray()
142
- unless data[field.name]
143
- data[field.name] = field.value
144
- else
145
- data[field.name] = [data[field.name]] unless data[field.name] instanceof Array
146
- data[field.name].push field.value
147
-
148
- @load data
149
-
150
- toString: ->
151
- "<Resource #{@__entityName}> #{JSON.stringify(@data)}"
203
+ # @nodoc
204
+ toString: ->
205
+ "<Resource #{@__entityName}> #{JSON.stringify(@data)}"