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

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 (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)}"