ende 0.1.14 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/component.json +1 -1
  3. data/lib/assets/javascripts/aura/extensions/devise.js.coffee +5 -2
  4. data/lib/assets/javascripts/aura/extensions/models.js.coffee.erb +4 -3
  5. data/lib/assets/javascripts/value_objects/phone.js.coffee +8 -2
  6. data/lib/assets/javascripts/widgets/viewer/main.js.coffee +18 -21
  7. data/lib/ende/version.rb +1 -1
  8. data/lib/ende.rb +2 -2
  9. data/vendor/assets/javascripts/ende/build.js +485 -4879
  10. data/vendor/components/component-querystring/component.json +18 -0
  11. data/vendor/components/component-querystring/index.js +49 -0
  12. data/vendor/components/component-trim/component.json +13 -0
  13. data/vendor/components/component-trim/index.js +17 -0
  14. data/vendor/components/indefinido-indemma/.gitignore +14 -0
  15. data/vendor/components/indefinido-indemma/.ruby-gemset +1 -0
  16. data/vendor/components/indefinido-indemma/.ruby-version +1 -0
  17. data/vendor/components/indefinido-indemma/Gemfile +13 -0
  18. data/vendor/components/indefinido-indemma/Guardfile +39 -0
  19. data/vendor/components/indefinido-indemma/History.md +0 -0
  20. data/vendor/components/indefinido-indemma/Readme.md +443 -0
  21. data/vendor/components/indefinido-indemma/build/development.js +331 -0
  22. data/vendor/components/indefinido-indemma/build/release.js +21693 -0
  23. data/vendor/components/indefinido-indemma/build/test.js +331 -0
  24. data/vendor/components/indefinido-indemma/component.json +8 -9
  25. data/vendor/components/indefinido-indemma/components/chaijs-assertion-error/component.json +18 -0
  26. data/vendor/components/indefinido-indemma/components/chaijs-assertion-error/index.js +110 -0
  27. data/vendor/components/indefinido-indemma/components/chaijs-chai/component.json +47 -0
  28. data/vendor/components/indefinido-indemma/components/chaijs-chai/index.js +1 -0
  29. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/assertion.js +130 -0
  30. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/core/assertions.js +1270 -0
  31. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/interface/assert.js +1080 -0
  32. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/interface/expect.js +12 -0
  33. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/interface/should.js +76 -0
  34. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/addChainableMethod.js +94 -0
  35. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/addMethod.js +37 -0
  36. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/addProperty.js +40 -0
  37. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/eql.js +129 -0
  38. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/flag.js +32 -0
  39. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/getActual.js +19 -0
  40. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/getEnumerableProperties.js +25 -0
  41. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/getMessage.js +49 -0
  42. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/getName.js +20 -0
  43. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/getPathValue.js +102 -0
  44. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/getProperties.js +35 -0
  45. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/index.js +108 -0
  46. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/inspect.js +320 -0
  47. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/objDisplay.js +48 -0
  48. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/overwriteMethod.js +51 -0
  49. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/overwriteProperty.js +54 -0
  50. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/test.js +26 -0
  51. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/transferFlags.js +44 -0
  52. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai/utils/type.js +45 -0
  53. data/vendor/components/indefinido-indemma/components/chaijs-chai/lib/chai.js +80 -0
  54. data/vendor/components/indefinido-indemma/components/component-bind/component.json +14 -0
  55. data/vendor/components/indefinido-indemma/components/component-bind/index.js +24 -0
  56. data/vendor/components/indefinido-indemma/components/component-jquery/component.json +14 -0
  57. data/vendor/components/indefinido-indemma/components/component-jquery/index.js +9601 -0
  58. data/vendor/components/indefinido-indemma/components/component-type/component.json +18 -0
  59. data/vendor/components/indefinido-indemma/components/component-type/index.js +32 -0
  60. data/vendor/components/indefinido-indemma/components/indefinido-advisable/component.json +21 -0
  61. data/vendor/components/indefinido-indemma/components/indefinido-advisable/index.js +1 -0
  62. data/vendor/components/indefinido-indemma/components/indefinido-advisable/lib/advisable.js +60 -0
  63. data/vendor/components/indefinido-indemma/components/indefinido-observable/component.json +25 -0
  64. data/vendor/components/indefinido-indemma/components/indefinido-observable/components/cjohansen-sinon/sinon.js +4290 -0
  65. data/vendor/components/indefinido-indemma/components/indefinido-observable/index.js +1 -0
  66. data/vendor/components/indefinido-indemma/components/indefinido-observable/lib/adapters/rivets.js +26 -0
  67. data/vendor/components/indefinido-indemma/components/indefinido-observable/lib/observable.js +323 -0
  68. data/vendor/components/indefinido-indemma/components/indefinido-observable/vendor/shims/accessors-legacy.js +92 -0
  69. data/vendor/components/indefinido-indemma/components/indefinido-observable/vendor/shims/accessors.js +173 -0
  70. data/vendor/components/indefinido-indemma/components/indefinido-observable/vendor/shims/array.indexOf.js +8 -0
  71. data/vendor/components/indefinido-indemma/components/indefinido-observable/vendor/shims/object.create.js +77 -0
  72. data/vendor/components/indefinido-indemma/components/kapit-observe-utils/component.json +13 -0
  73. data/vendor/components/indefinido-indemma/components/pluma-assimilate/component.json +25 -0
  74. data/vendor/components/indefinido-indemma/components/pluma-assimilate/dist/assimilate.js +87 -0
  75. data/vendor/components/indefinido-indemma/karma.conf.js +86 -0
  76. data/vendor/components/indefinido-indemma/lib/record/associable.js +6 -6
  77. data/vendor/components/indefinido-indemma/lib/record/errors.js +1 -0
  78. data/vendor/components/indefinido-indemma/lib/record/resource.js +14 -3
  79. data/vendor/components/indefinido-indemma/lib/record/rest.js +7 -4
  80. data/vendor/components/indefinido-indemma/lib/record/restfulable.js +45 -15
  81. data/vendor/components/indefinido-indemma/lib/record/scopable.js +13 -3
  82. data/vendor/components/indefinido-indemma/lib/record/validatable.js +20 -9
  83. data/vendor/components/indefinido-indemma/lib/record/validations/confirmation.js +1 -1
  84. data/vendor/components/indefinido-indemma/lib/record/validations/remote.js +6 -5
  85. data/vendor/components/indefinido-indemma/lib/record/validations/type.js +29 -0
  86. data/vendor/components/indefinido-indemma/lib/record.js +1 -11
  87. data/vendor/components/indefinido-indemma/spec/record/associable_spec.js +76 -0
  88. data/vendor/components/indefinido-indemma/spec/record/resource_spec.js +90 -0
  89. data/vendor/components/indefinido-indemma/spec/record/rest_spec.js +32 -0
  90. data/vendor/components/indefinido-indemma/spec/record/restfulable_spec.js +232 -0
  91. data/vendor/components/indefinido-indemma/spec/record/scopable_spec.js +201 -0
  92. data/vendor/components/indefinido-indemma/spec/record/translationable.js +28 -0
  93. data/vendor/components/indefinido-indemma/spec/record/validatable_spec.js +111 -0
  94. data/vendor/components/indefinido-indemma/spec/record/validations/associated_spec.js +43 -0
  95. data/vendor/components/indefinido-indemma/spec/record/validations/confirmation_spec.js +36 -0
  96. data/vendor/components/indefinido-indemma/spec/record/validations/cpf_spec.js +35 -0
  97. data/vendor/components/indefinido-indemma/spec/record/validations/presence_spec.js +28 -0
  98. data/vendor/components/indefinido-indemma/spec/record/validations/remote_spec.js +86 -0
  99. data/vendor/components/indefinido-indemma/spec/record/validations/type_spec.js +48 -0
  100. data/vendor/components/indefinido-indemma/spec/record_spec.js +37 -0
  101. data/vendor/components/indefinido-indemma/spec/spec_helper.js +11 -0
  102. data/vendor/components/indefinido-indemma/spec/support/value_objects/phone.js +45 -0
  103. data/vendor/components/indefinido-indemma/src/lib/extensions/rivets.coffee +17 -0
  104. data/vendor/components/indefinido-indemma/src/lib/record/associable.coffee +173 -0
  105. data/vendor/components/indefinido-indemma/src/lib/record/errors.coffee +20 -0
  106. data/vendor/components/indefinido-indemma/src/lib/record/maid.coffee +16 -0
  107. data/vendor/components/indefinido-indemma/src/lib/record/resource.coffee +103 -0
  108. data/vendor/components/indefinido-indemma/src/lib/record/rest.coffee +28 -0
  109. data/vendor/components/indefinido-indemma/src/lib/record/restfulable.coffee +314 -0
  110. data/vendor/components/indefinido-indemma/src/lib/record/scopable.coffee +266 -0
  111. data/vendor/components/indefinido-indemma/src/lib/record/translationable.coffee +18 -0
  112. data/vendor/components/indefinido-indemma/src/lib/record/validatable.coffee +209 -0
  113. data/vendor/components/indefinido-indemma/src/lib/record/validations/associated.coffee +32 -0
  114. data/vendor/components/indefinido-indemma/src/lib/record/validations/confirmation.coffee +19 -0
  115. data/vendor/components/indefinido-indemma/src/lib/record/validations/cpf.coffee +58 -0
  116. data/vendor/components/indefinido-indemma/src/lib/record/validations/presence.coffee +19 -0
  117. data/vendor/components/indefinido-indemma/src/lib/record/validations/remote.coffee +65 -0
  118. data/vendor/components/indefinido-indemma/src/lib/record/validations/type.coffee +32 -0
  119. data/vendor/components/indefinido-indemma/src/lib/record.coffee +123 -0
  120. data/vendor/components/indefinido-indemma/src/spec/record/associable_spec.coffee +63 -0
  121. data/vendor/components/indefinido-indemma/src/spec/record/resource_spec.coffee +64 -0
  122. data/vendor/components/indefinido-indemma/src/spec/record/rest_spec.coffee +22 -0
  123. data/vendor/components/indefinido-indemma/src/spec/record/restfulable_spec.coffee +164 -0
  124. data/vendor/components/indefinido-indemma/src/spec/record/scopable_spec.coffee +181 -0
  125. data/vendor/components/indefinido-indemma/src/spec/record/translationable.coffee +19 -0
  126. data/vendor/components/indefinido-indemma/src/spec/record/validatable_spec.coffee +100 -0
  127. data/vendor/components/indefinido-indemma/src/spec/record/validations/associated_spec.coffee +35 -0
  128. data/vendor/components/indefinido-indemma/src/spec/record/validations/confirmation_spec.coffee +25 -0
  129. data/vendor/components/indefinido-indemma/src/spec/record/validations/cpf_spec.coffee +28 -0
  130. data/vendor/components/indefinido-indemma/src/spec/record/validations/presence_spec.coffee +24 -0
  131. data/vendor/components/indefinido-indemma/src/spec/record/validations/remote_spec.coffee +72 -0
  132. data/vendor/components/indefinido-indemma/src/spec/record/validations/type_spec.coffee +33 -0
  133. data/vendor/components/indefinido-indemma/src/spec/record_spec.coffee +23 -0
  134. data/vendor/components/indefinido-indemma/src/spec/spec_helper.coffee +9 -0
  135. data/vendor/components/indefinido-indemma/src/spec/support/value_objects/phone.coffee +30 -0
  136. data/vendor/components/indefinido-indemma/vendor/owl/pluralize.js +190 -0
  137. data/vendor/components/indefinido-observable/lib/observable.js +3 -0
  138. metadata +119 -2
@@ -0,0 +1,314 @@
1
+ merge = require('assimilate').withStrategy 'deep'
2
+ type = require 'type'
3
+ observable = require('observable').mixin
4
+ $ = require 'jquery' # TODO remove jquery dependency and use simple promises implementation
5
+ rest = require './rest.js'
6
+
7
+ util =
8
+ model:
9
+ map: (models) ->
10
+ @ model for model in models
11
+
12
+
13
+ restful =
14
+ model:
15
+ # returns an array of promises
16
+ create: (params..., callback) ->
17
+ throw new TypeError("No arguments provided for #{@resource}.create") unless arguments.length
18
+ unless typeof callback == 'function'
19
+ params.push callback
20
+ callback = undefined
21
+
22
+ params.unshift {} unless params.length
23
+
24
+ savings = []
25
+ for attributes in params
26
+ # TODO accept dirty as attribute on record creation
27
+ record = @ attributes
28
+ record.dirty = true
29
+ savings.push record.save callback
30
+
31
+ $.when savings...
32
+
33
+ # returns a promise
34
+ # TODO move to scopable
35
+ all: (conditions = {}, callback) ->
36
+ if typeof conditions == 'function'
37
+ callback = conditions
38
+ conditions = {}
39
+
40
+ # TODO Consider parent resources
41
+ # if @parent and not @parent._id
42
+ # return callback.call @model, []
43
+
44
+
45
+ $.when(rest.get.call @, conditions)
46
+ .then(util.model.map )
47
+ .done callback
48
+
49
+ first: (conditions = {}, callback) ->
50
+ if typeof conditions == 'function'
51
+ callback = conditions
52
+ conditions = {}
53
+
54
+ namespaced = conditions[@resource] || {}
55
+ namespaced.limit = 1
56
+ namespaced.order = 'desc'
57
+
58
+ # TODO should fail when server returns more then one record
59
+ @all conditions, callback
60
+
61
+ get: (action, data) ->
62
+ # TODO better way to override route
63
+ old_route = @route
64
+ @route = "/#{model.pluralize @resource.name}/#{action}"
65
+ resource = data.resource
66
+ data = data.json() if data and data.json
67
+
68
+ if resource?
69
+ payload = data
70
+ data = {}
71
+ data[resource] = payload
72
+
73
+ promise = rest.get.call @, data
74
+
75
+ route = old_route
76
+
77
+ promise
78
+
79
+ put: rest.put
80
+
81
+ record:
82
+ reload: ->
83
+ promise = rest.get.call @
84
+ promise.done @assign_attributes
85
+ promise.fail @failed
86
+
87
+ # Bind one time save callbacks
88
+ promise.done argument for argument in arguments when type(argument) is 'function'
89
+
90
+ promise
91
+
92
+ assign_attributes: (attributes) ->
93
+
94
+ # TODO only set associations on nested attributes!
95
+ # First assign has_many associations
96
+ # TODO implement setter on has_many association and move this code there
97
+ for association_name in model[@resource.toString()].has_many
98
+ associations_attributes = attributes[association_name]
99
+ delete attributes[association_name] # Remove loaded json data
100
+
101
+ # Clear current stored cache on this association
102
+ # TODO implement setter on this association and let user to set
103
+ # it to an empty array
104
+ association = @[association_name]
105
+
106
+ unless association?
107
+ message = "Association '#{association_name}' not found. \n"
108
+ message += "For record with resource #{@resource}. \n"
109
+ message += "Probably defined on server side but not on client side.\n"
110
+ message += "Skipping association assignment!"
111
+ console.warn message
112
+ continue
113
+
114
+ # TODO implement association.clear
115
+ Array.prototype.splice.call association, 0 if association.length
116
+
117
+ # continue if no associations_attributes were found by the server
118
+ continue unless associations_attributes? and associations_attributes.length
119
+
120
+ singular_resource = model.singularize association_name
121
+
122
+ # Normalize json data for building on association
123
+ for association_attributes in associations_attributes
124
+
125
+ # TODO only nest specified nested attributes on model definition
126
+ # TODO create special deserialization method no plural association
127
+ # TODO check if we need to nest attributes in other association tipes
128
+ for association_name in model[singular_resource].has_many
129
+ association_attributes["#{association_name}_attributes"] = association_attributes[association_name]
130
+ delete association_attributes[association_name]
131
+
132
+ # Load new associations_attributes on this association
133
+ association.add associations_attributes...
134
+
135
+
136
+ # Nested attributes
137
+ # TODO implement setter on has_one association and move this code there
138
+ for association_name in model[@resource.toString()].has_one
139
+ association_attributes = attributes[association_name]
140
+ delete attributes[association_name]
141
+
142
+ @[association_name] = @["build_#{association_name}"] association_attributes if association_attributes
143
+
144
+
145
+ # Assign remaining attributes
146
+ @[attribute] = attributes[attribute] for attribute of attributes
147
+
148
+ destroy: (doned, failed, data) ->
149
+ throw new Error 'Can\'t delete record without id!' unless @id? or @_id?
150
+
151
+ promise = rest.delete.call @, data
152
+ promise.done @destroyed
153
+ promise.fail @failed
154
+
155
+ # Bind one time save callbacks
156
+ promise.done doned
157
+ promise.fail failed
158
+
159
+ promise
160
+
161
+ saving: false
162
+ salvation: null
163
+ save: (doned, failed, data) ->
164
+ return @salvation if @saving
165
+
166
+ # TODO better lock generation
167
+ @lock = JSON.stringify @json()
168
+
169
+ # TODO remove jquery dependency
170
+ # TODO think with wich value makes more sense to resolve the
171
+ # absence of need to save the model
172
+ salvation = $.Deferred().resolveWith @, null unless @dirty
173
+ salvation ||= rest[if @_id then 'put' else 'post'].call @, data
174
+ @salvation = salvation
175
+ @saving = true
176
+
177
+ salvation.done @saved
178
+ salvation.fail @failed
179
+ salvation.always -> @saving = false
180
+
181
+ # Bind one time save callbacks
182
+ salvation.done doned
183
+ salvation.fail failed
184
+
185
+ salvation
186
+
187
+ saved: (data) ->
188
+
189
+ # TODO better lock generation
190
+ if @lock == JSON.stringify(@json())
191
+ @dirty = false
192
+ delete @lock
193
+ # Delayed optimistic lock
194
+ else
195
+ return @save()
196
+
197
+ @assign_attributes data if data?
198
+
199
+ throw "Not supported after_save callback: " + callback for callback in @after_save if @after_save
200
+
201
+ # Parse error json if any
202
+ failed: (xhr, error, status) ->
203
+ payload = xhr.responseJSON
204
+ try payload ||= JSON.parse(xhr.responseText) catch e
205
+ payload ||= xhr.responseText
206
+
207
+ # When client fail
208
+ switch xhr.status
209
+ # move to validatable
210
+ when 422
211
+
212
+ definition = model[@resource]
213
+
214
+ for attribute_name, messages of payload.errors
215
+
216
+ # Only add errors to existing attributes
217
+ unless @hasOwnProperty(attribute_name) or definition.hasOwnProperty(attribute_name)
218
+ message = "Server returned an validation error message for a attribute that is not defined in your model.\n"
219
+ message += "The attribute was '#{attribute_name}', the model resource was '#{@resource}'.\n"
220
+ message += "The model definition keys were '#{JSON.stringify Object.keys definition }'.\n"
221
+ message += "Please remove server validation, or update your model definition."
222
+ throw new TypeError message
223
+
224
+ for message in messages
225
+ @errors.add attribute_name, 'server', server_message: message
226
+
227
+ # Unknown fail
228
+ else
229
+ message = "Fail in #{@resource}.save:\n"
230
+ message += "Record: #{@}\n"
231
+ message += "Status: #{status} (#{payload.status || xhr.status})\n"
232
+ message += "Error : #{payload.error || payload.message || payload}"
233
+
234
+
235
+ toString: ->
236
+ serialized = {}
237
+ serialized[@resource] = @json()
238
+ JSON.stringify serialized
239
+
240
+ json: (methods = {}) ->
241
+ json = {}
242
+
243
+ for name, value of @ when type(value) isnt 'function'
244
+ continue unless value? # Bypass null, and undefined values
245
+
246
+ if type(value) == 'object'
247
+
248
+ if value.toJSON?
249
+
250
+ json[name] = value.toJSON(methods[name])
251
+
252
+ else
253
+
254
+ # TODO move nested attributes to model definition
255
+ # TODO and implement toJSON there
256
+ for attribute in @nested_attributes when attribute == name
257
+ json["#{name}_attributes"] = value.json(methods[name])
258
+
259
+ else
260
+
261
+ json[name] = value
262
+
263
+ observable.unobserve json
264
+
265
+ # TODO Store reserved words in a array
266
+ # TODO Use _.omit function
267
+ # TODO Use object.defineProperty to not need to delete this properties
268
+ # Remove model reserved words
269
+ delete json.dirty
270
+ delete json.resource
271
+ delete json.route
272
+ delete json.initial_route # TODO implement better initial_route and remove attribute from here
273
+ delete json.after_initialize
274
+ delete json.parent_resource
275
+ delete json.nested_attributes
276
+ delete json.saving
277
+ delete json.salvation
278
+ delete json.element
279
+ delete json.default
280
+ delete json.lock
281
+ delete json.validated
282
+ delete json.validation
283
+
284
+ json
285
+
286
+ # TODO put deprecation warning on json method
287
+ # TODO rename json method to toJSON
288
+ restful.toJSON = restful.json
289
+
290
+
291
+ # Extend indemma
292
+ model = window.model # TODO better way to get parent
293
+ record = window.record # TODO better way to get parent
294
+
295
+ model.restfulable = true
296
+
297
+ record.mix (recordable) ->
298
+ merge recordable, restful.record
299
+
300
+
301
+ model.mix (modelable ) ->
302
+ merge modelable , restful.model
303
+
304
+
305
+ model.associable && model.associable.mix (singular_association, plural_association) ->
306
+
307
+ # TODO move route setting to plural_association.after_mix
308
+ plural_association.get = ->
309
+ @route ||= "#{@parent.route}/#{@parent._id}/#{model.pluralize @resource.name}" if @parent?
310
+ rest.get.apply @, arguments
311
+
312
+ plural_association.post = ->
313
+ @route ||= "#{@parent.route}/#{@parent._id}/#{model.pluralize @resource.name}" if @parent?
314
+ rest.post.apply @, arguments
@@ -0,0 +1,266 @@
1
+ require './restfulable'
2
+ require './resource'
3
+
4
+ stampit = require '../../vendor/stampit'
5
+ extend = require 'assimilate'
6
+ merge = extend.withStrategy 'deep'
7
+ $ = require 'jquery'
8
+ rest = require './rest'
9
+
10
+
11
+ scopable =
12
+ builder: stampit().enclose ->
13
+
14
+ # Builds a given scope
15
+ # {param} name Scope name also, the name of method used to invoke it
16
+ # {param} type Default value for scope, or base class to derive default for default type
17
+
18
+ stampit.mixIn (name, type) ->
19
+ if $.type(type) == 'function'
20
+ @["$#{name}"] = type() || new type
21
+ type = $.type @["$#{name}"]
22
+ else
23
+ @["$#{name}"] = defaults[type] || type
24
+
25
+ type = $.type type unless $.type(type) == 'string'
26
+ builder = builders[type]
27
+
28
+ throw "Unknown scope type #{type} for model with resource #{model.resource}" unless builder?
29
+
30
+ @scope.declared.push name
31
+ @[name] = builder name: name
32
+ ,
33
+ data: {}
34
+ then: []
35
+ fail: []
36
+ declared: []
37
+ fetch: (data, done, fail) ->
38
+ scope = extend {}, @scope.data
39
+
40
+ if scope.noned?
41
+ deferred = $.Deferred()
42
+ deferred.resolveWith @, [[]]
43
+ else
44
+ deferred = rest.get.call(@, extend scope, data)
45
+
46
+ deferred
47
+ .done(@scope.then.concat done)
48
+ .fail([@scope.fail, fail])
49
+
50
+ @scope.clear()
51
+
52
+ deferred
53
+
54
+ clear: ->
55
+ @data = {}
56
+ @callbacks = []
57
+
58
+ # Shared scope stuff
59
+ base: stampit().state
60
+ name: 'unamed_scope'
61
+
62
+ record:
63
+ # Parse error json if any
64
+ failed: (xhr, error, status) ->
65
+ payload = xhr.responseJSON
66
+ try payload ||= JSON.parse(xhr.responseText) catch e
67
+ payload ||= xhr.responseText
68
+
69
+ # When client fail
70
+ switch xhr.status
71
+ when 422
72
+ @valid = false
73
+ return @errors = payload.errors
74
+ # Unknown fail
75
+ else
76
+ message = "Fail in #{@resource}.save:\n"
77
+ message += "Record: #{@}\n"
78
+ message += "Status: #{status} (#{payload.status || xhr.status})\n"
79
+ message += "Error : #{payload.error || payload.message || payload}"
80
+
81
+ console.error message
82
+ model:
83
+ # TODO implement getter for none property!
84
+ none: ->
85
+ @scope.data.noned = true
86
+ @
87
+
88
+ fetch: (data, done, fail) ->
89
+ @scope.fetch.call @, data, done, fail
90
+
91
+ # TODO optmize this iterations or add support for stampit on associable and merge factories
92
+ # @ = record instance
93
+ forward_scopes_to_associations: ->
94
+ factory = model[@resource]
95
+
96
+ for association_name in factory.has_many
97
+ associated_resource = model.singularize association_name
98
+ associated_factory = model[associated_resource]
99
+
100
+ # TODO change this warn message into a exception when
101
+ # associations are renamable
102
+ unless model[associated_resource]
103
+ console.warn("Associated factory not found for associated resource: #{associated_resource}")
104
+ continue
105
+
106
+ association = @[association_name]
107
+ association.scope = scopable.builder association
108
+
109
+
110
+ for scope in associated_factory.scope.declared
111
+ association.scope scope, associated_factory["$#{scope}"]
112
+
113
+ for associated_resource in factory.has_one
114
+ # TODO change this warn message into a exception when
115
+ # associations are renamable
116
+ unless model[associated_resource]
117
+ console.warn("Associated factory not found for associated resource: #{associated_resource}")
118
+ continue
119
+
120
+ for scope in model[associated_resource].scope.declared
121
+ @[associated_resource][scope] = factory[scope]
122
+
123
+ # TODO improve associable inner workings to stampit objects
124
+ if factory.belongs_to.length
125
+ generate_forwarder = (associated_resource) ->
126
+ associated_factory = model[associated_resource]
127
+
128
+ # TODO change this warn message into a exception when
129
+ # associations are renamable
130
+ return console.warn("Associated factory not found for associated resource: #{associated_resource}") unless associated_factory
131
+
132
+ declared_scopes = associated_factory.scope.declared
133
+
134
+ ->
135
+ for scope in declared_scopes
136
+ @[associated_resource][scope] = associated_factory[scope]
137
+
138
+ for associated_resource in factory.belongs_to
139
+ forwarder = generate_forwarder associated_resource
140
+ @after "build_#{associated_resource}", forwarder
141
+
142
+ true
143
+ # @ = model instance
144
+ after_mix: ->
145
+ @scope = scopable.builder @
146
+
147
+ for property, type of @
148
+ if property.charAt(0) == '$'
149
+ name = property.substring 1
150
+ @scope name, type
151
+
152
+ builders =
153
+ # Builds a boolean scope builder
154
+ boolean: stampit().enclose ->
155
+ base = scopable.base @
156
+
157
+ stampit.mixIn (value, callbacks...) ->
158
+ callbacks.length and @scope.then = @scope.then.concat callbacks
159
+ @scope.data[base.name] ||= value ? @["$#{base.name}"]
160
+ @
161
+
162
+ # Builds a array scope builder
163
+ array: stampit().enclose ->
164
+ base = scopable.base @
165
+
166
+ stampit.mixIn (values...) ->
167
+ @scope.data[base.name] ||= values ? @["$#{base.name}"]
168
+ @
169
+
170
+ defaults =
171
+ boolean: true
172
+ array: []
173
+
174
+ # Extend indemma
175
+ model = window.model # TODO better way to get parent
176
+ record = window.record # TODO better way to get parent
177
+
178
+ model.scopable = true
179
+
180
+ # TODO use stampit to extend record and model
181
+ #record.mix (recordable) ->
182
+ # merge recordable, scopable.record
183
+
184
+ model.mix (modelable) ->
185
+ merge modelable, scopable.model
186
+
187
+ modelable.after_mix.push scopable.after_mix
188
+
189
+
190
+ # TODO create a deferred to better mix models
191
+ if model.associable
192
+ model.mix (modelable) ->
193
+ modelable.record.after_initialize.push ->
194
+ scopable.model.forward_scopes_to_associations.call @
195
+
196
+ model.associable.mix (singular_association, plural_association) ->
197
+
198
+ # reload (done callbacks...)
199
+ plural_association.all = plural_association.reload = (data, done, fail) ->
200
+ # TODO move route discovery to plural_association.after_mix
201
+ @route ||= "#{@parent.route}/#{@parent._id}/#{model.pluralize @resource}" if @parent?
202
+ promises = []
203
+
204
+ # TODO better calculate fetch settings
205
+ # dirty = 0
206
+ # if more than 5 records are dirty, we reftech all
207
+ # if dirty < 5
208
+ # for record in @
209
+ # promises.push record.reload()
210
+ # else we reload everthing!
211
+ # else
212
+ #
213
+ # for record in @
214
+ # dirty++ if record.dirty
215
+
216
+ if typeof data == 'function'
217
+ done = data
218
+ data = undefined
219
+
220
+ promises.push @scope.fetch.call @, data, null, scopable.record.failed
221
+
222
+ reload = $.when.apply jQuery, promises
223
+
224
+ # Update association with data sent from the server
225
+ # TODO implement setter on this association and let user to set
226
+ reload.done (records, status) ->
227
+
228
+ # Clear current stored cache on this association
229
+ # it to an empty array
230
+ Array.prototype.splice.call @, 0
231
+
232
+ # return if no records were found by the server
233
+ return unless records.length
234
+
235
+ singular_resource = model.singularize @resource
236
+
237
+ # Normalize json data for building on association
238
+ for record in records
239
+
240
+ # TODO only nest specified nested attributes on model definition
241
+ # TODO create special deserialization method no plural association
242
+ # TODO check if we need to nest attributes in other association tipes
243
+ for association_name in model[singular_resource].has_many
244
+ record["#{association_name}_attributes"] = record[association_name]
245
+ delete record[association_name]
246
+
247
+ # Load new records on this association
248
+ @add.apply @, records
249
+
250
+ # Override the response records object with added to association records
251
+ records.splice 0
252
+ records.push.apply records, @
253
+
254
+
255
+ reload.done done
256
+ reload.fail fail
257
+
258
+ reload
259
+
260
+ plural_association.each = (callback) ->
261
+ @route ||= "#{@parent.route}/#{@parent._id}/#{model.pluralize @resource}" if @parent?
262
+
263
+ # TODO cache models
264
+ @get().done (records) =>
265
+ for record in @
266
+ callback record
@@ -0,0 +1,18 @@
1
+ root = exports ? window
2
+
3
+ # TODO better way to get core model definition
4
+ # model = require ...
5
+
6
+ # TODO implement method
7
+ # model[resource].validators_on 'field' # Get all validators related to this field
8
+
9
+ extend = require('assimilate')
10
+
11
+ extensions =
12
+ model:
13
+ human_attribute_name: (attribute_name) ->
14
+ @translation?.attributes?[attribute_name] or attribute_name
15
+
16
+ model.mix (modelable) ->
17
+ extend modelable, extensions.model
18
+