joosy 1.2.0.alpha.73 → 1.2.0.beta.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gruntfile.coffee +56 -18
  3. data/bower.json +1 -1
  4. data/build/joosy/form.js +1 -0
  5. data/build/joosy/resources.js +1 -0
  6. data/build/joosy.js +2 -2774
  7. data/package.json +5 -4
  8. data/source/joosy/application.coffee +9 -7
  9. data/source/joosy/{extensions/resources-form/form.coffee → form.coffee} +58 -51
  10. data/source/joosy/helpers/form.coffee +241 -0
  11. data/source/joosy/helpers/index.coffee +3 -0
  12. data/source/joosy/helpers/routes.coffee +3 -1
  13. data/source/joosy/helpers/view.coffee +9 -9
  14. data/source/joosy/joosy.coffee +3 -5
  15. data/source/joosy/module.coffee +9 -4
  16. data/source/joosy/modules/dom.coffee +33 -31
  17. data/source/joosy/modules/events.coffee +24 -20
  18. data/source/joosy/modules/filters.coffee +38 -35
  19. data/source/joosy/modules/page/title.coffee +3 -3
  20. data/source/joosy/modules/renderer.coffee +23 -18
  21. data/source/joosy/modules/resources/identity_map.coffee +45 -0
  22. data/source/joosy/modules/resources/model.coffee +146 -0
  23. data/source/joosy/modules/widgets_manager.coffee +8 -8
  24. data/source/joosy/resources/array.coffee +0 -5
  25. data/source/joosy/resources/hash.coffee +8 -13
  26. data/source/joosy/resources/index.coffee +2 -0
  27. data/source/joosy/{extensions/resources → resources}/rest.coffee +48 -19
  28. data/source/joosy/resources/scalar.coffee +8 -10
  29. data/source/joosy/router.coffee +13 -12
  30. data/source/joosy/templaters/jst.coffee +3 -2
  31. data/source/joosy/widget.coffee +17 -15
  32. data/source/joosy.coffee +2 -0
  33. data/source/vendor/es5-shim.js +1316 -0
  34. data/source/vendor/inflections.js +598 -0
  35. data/source/vendor/metamorph.js +457 -0
  36. data/spec/helpers/matchers.coffee +4 -4
  37. data/spec/joosy/core/application_spec.coffee +1 -1
  38. data/spec/joosy/core/helpers/view_spec.coffee +2 -2
  39. data/spec/joosy/core/joosy_spec.coffee +8 -4
  40. data/spec/joosy/core/modules/dom_spec.coffee +7 -7
  41. data/spec/joosy/core/modules/events_spec.coffee +2 -2
  42. data/spec/joosy/core/modules/filters_spec.coffee +7 -8
  43. data/spec/joosy/core/modules/module_spec.coffee +5 -5
  44. data/spec/joosy/core/router_spec.coffee +3 -3
  45. data/spec/joosy/core/widget_spec.coffee +6 -6
  46. data/spec/joosy/environments/amd_spec.coffee +4 -2
  47. data/spec/joosy/environments/global_spec.coffee +1 -1
  48. data/spec/joosy/{extensions/form → form}/form_spec.coffee +9 -16
  49. data/spec/joosy/{extensions/form → form}/helpers/forms_spec.coffee +5 -5
  50. data/spec/joosy/{core/resources → resources}/array_spec.coffee +2 -2
  51. data/spec/joosy/{core/resources → resources}/hash_spec.coffee +0 -8
  52. data/spec/joosy/{core/modules/resources → resources/modules}/cacher_spec.coffee +0 -0
  53. data/spec/joosy/resources/modules/identity_map_spec.coffee +47 -0
  54. data/spec/joosy/{extensions/resources/base_spec.coffee → resources/modules/model_spec.coffee} +28 -48
  55. data/spec/joosy/{extensions/resources → resources}/rest_spec.coffee +29 -22
  56. data/spec/joosy/{core/resources → resources}/scalar_spec.coffee +8 -8
  57. data/templates/application/application.coffee.tt +0 -2
  58. data/templates/environment/app/haml/index.haml +2 -2
  59. data/templates/environment/package.json +1 -1
  60. metadata +23 -19
  61. data/build/joosy/extensions/resources-form.js +0 -590
  62. data/build/joosy/extensions/resources.js +0 -561
  63. data/source/joosy/extensions/resources/base.coffee +0 -282
  64. data/source/joosy/extensions/resources/index.coffee +0 -1
  65. data/source/joosy/extensions/resources-form/helpers/form.coffee +0 -104
  66. data/source/joosy/extensions/resources-form/index.coffee +0 -1
  67. data/source/metamorph.coffee +0 -410
@@ -1,282 +0,0 @@
1
- #
2
- # Basic data wrapper with triggering and entity name binding
3
- #
4
- # @example Basic usage
5
- # class R extends Joosy.Resources.Base
6
- # @entity 'r'
7
- #
8
- # @beforeLoad (data) ->
9
- # data.real = true
10
- #
11
- # r = R.build {r: {foo: {bar: 'baz'}}}
12
- #
13
- # r('foo') # {baz: 'baz'}
14
- # r('real') # true
15
- # r('foo.bar') # baz
16
- # r('foo.bar', 'fluffy') # triggers 'changed'
17
- #
18
- # @include Joosy.Modules.Log
19
- # @include Joosy.Modules.Events
20
- # @include Joosy.Modules.Filters
21
- #
22
- # @method .beforeLoad(callback)
23
- # Allows to modify data before it gets stored.
24
- # You can define several beforeLoad filters that will be chained.
25
- # @param [Function] action `(Object) -> Object` to call
26
- #
27
- #
28
- class Joosy.Resources.Base extends Joosy.Function
29
- @include Joosy.Modules.Log
30
- @include Joosy.Modules.Events
31
- @include Joosy.Modules.Filters
32
-
33
- #
34
- # Default primary key field 'id'
35
- #
36
- __primaryKey: 'id'
37
-
38
- #
39
- # Clears the identity map cache. Recomended to be called during layout switch to
40
- # ensure correct garbage collection.
41
- #
42
- @resetIdentity: ->
43
- Joosy.Resources.Base.identity = {}
44
-
45
- @registerPlainFilters 'beforeLoad'
46
-
47
- #
48
- # Sets the field containing primary key.
49
- #
50
- # @note It has no direct use inside the REST resource itself and can be omited.
51
- # But it usually should not since we have plans on adding some kind of Identity Map to Joosy.
52
- #
53
- # @param [String] primary Name of the field
54
- #
55
- @primaryKey: (primaryKey) ->
56
- @::__primaryKey = primaryKey
57
-
58
- #
59
- # Sets the entity text name:
60
- # required to do some magic like skipping the root node.
61
- #
62
- # @param [String] name Singular name of resource
63
- #
64
- @entity: (name) ->
65
- @::__entityName = name
66
-
67
- #
68
- # Dynamically creates collection of inline resources.
69
- #
70
- # Inline resources share the instance with direct data and therefore can be used
71
- # to handle inline changes with triggers and all that resources stuff
72
- #
73
- # @example Basic usage
74
- # class Zombie extends Joosy.Resources.Base
75
- # @entity 'zombie'
76
- # class Puppy extends Joosy.Resources.Base
77
- # @entity 'puppy'
78
- # @map 'zombies'
79
- #
80
- # p = Puppy.build {zombies: [{foo: 'bar'}]}
81
- #
82
- # p('zombies') # Direct access: [{foo: 'bar'}]
83
- # p.zombies # Wrapped Collection of Zombie instances
84
- # p.zombies.at(0)('foo') # bar
85
- #
86
- # @param [String] name Pluralized name of property to define
87
- # @param [Class] klass Resource class to instantiate
88
- #
89
- @map: (name, klass=false) ->
90
- unless klass
91
- klass = window[name.singularize().camelize()]
92
-
93
- if !klass
94
- throw new Error "#{Joosy.Module.__className @}> class can not be detected for '#{name}' mapping"
95
-
96
- @beforeLoad (data) ->
97
- klass = klass() unless Joosy.Module.hasAncestor(klass, Joosy.Resources.Base)
98
-
99
- @__map(data, name, klass)
100
-
101
- #
102
- # Wraps instance of resource inside shim-function allowing to track
103
- # data changes. See class example
104
- #
105
- # @return [Joosy.Resources.Base]
106
- #
107
- @build: (data={}) ->
108
- if Object.isNumber(data) || Object.isString(data)
109
- id = data
110
- data = {}
111
- data[@::__primaryKey] = id
112
-
113
- klass = @::__entityName
114
- id = data[@::__primaryKey]
115
-
116
- if klass? && id?
117
- Joosy.Resources.Base.identity ?= {}
118
- Joosy.Resources.Base.identity[klass] ?= {}
119
- Joosy.Resources.Base.identity[klass][id] ?= new @ id: id
120
-
121
- Joosy.Resources.Base.identity[klass][id].load data
122
- else
123
- new @ data
124
-
125
- #
126
- # Creates new instance of Resource using values from form
127
- #
128
- # @param [DOMElement] form Form to grab
129
- #
130
- @grab: (form) ->
131
- @build({}).grab form
132
-
133
- #
134
- # Should NOT be called directly, use {::build} instead
135
- #
136
- # @private
137
- # @param [Object] data Data to store
138
- #
139
- constructor: (data={}) ->
140
- return super ->
141
- @__fillData data, false
142
-
143
- id: ->
144
- @data?[@__primaryKey]
145
-
146
- knownAttributes: ->
147
- Object.keys @data
148
-
149
- #
150
- # Set the resource data manually
151
- #
152
- # @param [Object] data Data to store
153
- #
154
- # @return [Joosy.Resources.Base] Returns self
155
- #
156
- load: (data, clear=false) ->
157
- @data = {} if clear
158
- @__fillData data
159
- return @
160
-
161
- #
162
- # Updates the Resource with a data from given form
163
- #
164
- # @param [DOMElement] form Form to grab
165
- #
166
- grab: (form) ->
167
- data = {}
168
- for field in $(form).serializeArray()
169
- unless data[field.name]
170
- data[field.name] = field.value
171
- else
172
- data[field.name] = [data[field.name]] unless data[field.name] instanceof Array
173
- data[field.name].push field.value
174
-
175
- @load data
176
-
177
- #
178
- # Getter for wrapped data
179
- #
180
- # @param [String] path Attribute name to get. Can contain dots to get inline Objects values
181
- # @return [mixed]
182
- #
183
- __get: (path) ->
184
- target = @__callTarget path, true
185
-
186
- if !target
187
- return undefined
188
- else if target[0] instanceof Joosy.Resources.Base
189
- return target[0](target[1])
190
- else
191
- return target[0][target[1]]
192
-
193
- #
194
- # Setter for wrapped data, triggers `changed` event.
195
- #
196
- # @param [String] path Attribute name to set. Can contain dots to get inline Objects values
197
- # @param [mixed] value Value to set
198
- #
199
- __set: (path, value) ->
200
- target = @__callTarget path
201
-
202
- if target[0] instanceof Joosy.Resources.Base
203
- target[0](target[1], value)
204
- else
205
- target[0][target[1]] = value
206
-
207
- @trigger 'changed'
208
- null
209
-
210
- #
211
- # Locates the actual instance of attribute path `foo.bar` from get/set
212
- #
213
- # @param [String] path Path to the attribute (`foo.bar`)
214
- # @param [Boolean] safe Indicates whether nested hashes should not be automatically created when they don't exist
215
- # @return [Array] Instance of object containing last step of path and keyword for required field
216
- #
217
- __callTarget: (path, safe=false) ->
218
- if path.has(/\./) && !@data[path]?
219
- path = path.split '.'
220
- keyword = path.pop()
221
- target = @data
222
-
223
- for part in path
224
- return false if safe && !target[part]?
225
-
226
- target[part] ||= {}
227
- if target instanceof Joosy.Resources.Base
228
- target = target(part)
229
- else
230
- target = target[part]
231
-
232
- [target, keyword]
233
- else
234
- [@data, path]
235
-
236
- #
237
- # Wrapper for {Joosy.Resources.Base.build} magic
238
- #
239
- __call: (path, value) ->
240
- if arguments.length > 1
241
- @__set path, value
242
- else
243
- @__get path
244
-
245
- #
246
- # Defines how exactly prepared data should be saved
247
- #
248
- # @param [Object] data Raw data to store
249
- #
250
- __fillData: (data, notify=true) ->
251
- @raw = data
252
- @data = {} unless @hasOwnProperty 'data'
253
-
254
- Joosy.Module.merge @data, @__prepareData(data)
255
-
256
- @trigger 'changed' if notify
257
-
258
- null
259
-
260
- #
261
- # Prepares raw data: cuts the root node if it exists, runs before filters
262
- #
263
- # @param [Hash] data Raw data to prepare
264
- # @return [Hash]
265
- #
266
- __prepareData: (data) ->
267
- if Object.isObject(data) && Object.keys(data).length == 1 && @__entityName
268
- name = @__entityName.camelize(false)
269
- data = data[name] if data[name]
270
-
271
- @__applyBeforeLoads data
272
-
273
- __map: (data, name, klass) ->
274
- if Object.isArray data[name]
275
- entries = data[name].map (x) -> klass.build x
276
- data[name] = new Joosy.Resources.Array entries...
277
- else if Object.isObject data[name]
278
- data[name] = klass.build data[name]
279
- data
280
-
281
- toString: ->
282
- "<Resource #{@__entityName}> #{JSON.stringify(@data)}"
@@ -1 +0,0 @@
1
- #= require_tree ./
@@ -1,104 +0,0 @@
1
- #= require ../form
2
-
3
- #
4
- # Form helper
5
- #
6
- Joosy.helpers 'Application', ->
7
-
8
- description = (resource, method, extendIds, idSuffix) ->
9
- if Joosy.Module.hasAncestor resource.constructor, Joosy.Resources.Base
10
- id = resource.id()
11
- resource = resource.__entityName
12
-
13
- name: resource + "#{if method.match(/^\[.*\]$/) then method else "[#{method}]"}"
14
- id: resource + (if id && extendIds then '_'+id else '') + "_#{method.parameterize().underscore()}" + (if idSuffix then '_'+idSuffix else '')
15
-
16
- input = (type, resource, method, options={}) =>
17
- d = description(resource, method, options.extendIds, options.idSuffix)
18
- delete options.extendIds
19
- delete options.idSuffix
20
- @tag 'input', Joosy.Module.merge {type: type, name: d.name, id: d.id}, options
21
-
22
- #
23
- # @private
24
- #
25
- class Form
26
- constructor: (@context, @resource, @options) ->
27
-
28
- label: (method, options={}, content='') ->
29
- if !Object.isObject(options)
30
- content = options
31
- options = {}
32
-
33
- @context.label(@resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options), content)
34
-
35
- radioButton: (method, tagValue, options={}) -> @context.radioButton(@resource, method, tagValue, Joosy.Module.merge(extendIds: @options.extendIds, options))
36
- textArea: (method, options={}) -> @context.textArea(@resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options))
37
- checkBox: (method, options={}, checkedValue=1, uncheckedValue=0) -> @context.checkBox(@resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options), checkedValue, uncheckedValue)
38
- select: (method, options={}, htmlOptions={}) -> @context.select @resource, method, options, Joosy.Module.merge(extendIds: @options.extendIds, htmlOptions)
39
-
40
- ['text', 'file', 'hidden', 'password'].each (type) =>
41
- Form.prototype[type+'Field'] = (method, options={}) ->
42
- @context[type+'Field'] @resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options)
43
-
44
- @formFor = (resource, options={}, block) ->
45
- if Object.isFunction(options)
46
- block = options
47
- options = {}
48
-
49
- uuid = Joosy.uuid()
50
- form = @tag 'form', Joosy.Module.merge(options.html || {}, id: uuid), block?.call(this, new Form(this, resource, options))
51
-
52
- @onRefresh? -> Joosy.Form.attach '#'+uuid, Joosy.Module.merge(options, resource: resource)
53
-
54
- form
55
-
56
- @label = (resource, method, options={}, content='') ->
57
- if !Object.isObject(options)
58
- content = options
59
- options = {}
60
-
61
- d = description(resource, method, options.extendIds)
62
- delete options.extendIds
63
-
64
- @contentTag 'label', content, Joosy.Module.merge(options, for: d.id)
65
-
66
- ['text', 'file', 'hidden', 'password'].each (type) =>
67
- @[type+'Field'] = (resource, method, options={}) -> input type, resource, method, options
68
-
69
- @radioButton = (resource, method, tagValue, options={}) -> input 'radio', resource, method, Joosy.Module.merge(value: tagValue, idSuffix: tagValue, options)
70
-
71
- @checkBox = (resource, method, options={}, checkedValue=1, uncheckedValue=0) ->
72
- spy = @tag 'input', Joosy.Module.merge(name: description(resource, method).name, value: uncheckedValue, type: 'hidden')
73
- box = input 'checkbox', resource, method, Joosy.Module.merge(value: checkedValue, options)
74
-
75
- spy+box
76
-
77
- @select = (resource, method, options, htmlOptions) ->
78
- if Object.isObject options
79
- opts = []
80
- for key, val of options
81
- opts.push [val, key]
82
- else
83
- opts = options
84
- if htmlOptions.includeBlank
85
- delete htmlOptions.includeBlank
86
- opts.unshift ['', '']
87
- opts = opts.reduce (str, vals) =>
88
- params = if Object.isArray vals then ['option', vals[0], { value: vals[1] }] else ['option', vals, {}]
89
- if htmlOptions.value == (if Object.isArray(vals) then vals[1] else vals)
90
- params[2].selected = 'selected'
91
- str += @contentTag.apply @, params
92
- , ''
93
- extendIds = htmlOptions.extendIds
94
- delete htmlOptions.value
95
- delete htmlOptions.extendIds
96
- @contentTag 'select', opts, Joosy.Module.merge(description(resource, method, extendIds), htmlOptions)
97
-
98
- @textArea = (resource, method, options={}) ->
99
- value = options.value
100
- extendIds = options.extendIds
101
- delete options.value
102
- delete options.extendIds
103
-
104
- @contentTag 'textarea', value, Joosy.Module.merge(description(resource, method, extendIds), options)
@@ -1 +0,0 @@
1
- #= require_tree ./