kindred-rails 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9f0898e7d7fdb38c9b2eb82b57567856fc5b2529
4
+ data.tar.gz: bfeba610f6bdfbf1bf31c1a96161b43dd45bce16
5
+ SHA512:
6
+ metadata.gz: a2094425ecf6f9b495246a4890f631d81810a51aedcb077c202bbea7d5b9f095aa654377f2f368f7d21c66c76891b94274387d9ed7823800ebbe7a97ee445a61
7
+ data.tar.gz: 8ee63227844b8e0894a4751f31fa3e435fe6aa651dbe18926af8e04f99fab0e38aa5a10e8063720734557bdc521cf8a379a153128a3e9048f59f6def669f3d36
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kindred.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Mike Piccolo
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,310 @@
1
+ kindred
2
+ ============
3
+ | Project | Gem Release |
4
+ |------------------------ | ----------------- |
5
+ | Gem name | kindred |
6
+ | License | [MIT](LICENSE.txt) |
7
+ | Version | [![Gem Version](https://badge.fury.io/rb/kindred.png)](http://badge.fury.io/rb/kindred) |
8
+ | Continuous Integration | [![Build Status](https://travis-ci.org/mfpiccolo/kindred.png?branch=master)](https://travis-ci.org/mfpiccolo/kindred)
9
+ | Test Coverage | [![Coverage Status](https://coveralls.io/repos/mfpiccolo/kindred/badge.png?branch=master)](https://coveralls.io/r/mfpiccolo/kindred?branch=coveralls)
10
+ | Grade | [![Code Climate](https://codeclimate.com/github/mfpiccolo/kindred/badges/gpa.svg)](https://codeclimate.com/github/mfpiccolo/kindred)
11
+ | Dependencies | [![Dependency Status](https://gemnasium.com/mfpiccolo/kindred.png)](https://gemnasium.com/mfpiccolo/kindred)
12
+ | Homepage | [http://mfpiccolo.github.io/kindred][homepage] |
13
+ | Documentation | [http://rdoc.info/github/mfpiccolo/kindred/frames][documentation] |
14
+ | Issues | [https://github.com/mfpiccolo/kindred/issues][issues] |
15
+
16
+ ## Description
17
+ Kindred is an open source project that intends to optimize programmers happiness and productivity for client-heavy rails applications. Kindred aims to allow developers to create robust client side applications with minimal code while maintaining best practices and conventions.
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem "kindred"
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install kindred
34
+
35
+ ## Getting Started with Rails
36
+
37
+ Add `gem "kindred"` to gemfile
38
+
39
+ Add `//= require kindred` to application.js manifest
40
+
41
+ ## Demo
42
+
43
+ If you would like to see kindred in action check out [kindred-demo](https://kindred-demo.herokuapp.com/dashboard).
44
+
45
+ ## Features
46
+ ###Templates
47
+ In a rails view you can pass html to your javascript by using the `#template` helper method.
48
+ The `#template` method takes the following keyword arguments:
49
+
50
+ `model:` String of the javascript model name
51
+
52
+ `collection:` Collection of json serializable ruby objects
53
+
54
+ `target:` String of the data-target attribute wrapper
55
+
56
+ `&block` In the block pass html that will be used for that model
57
+
58
+ ```HTML
59
+ <div data-target="line-item">
60
+ <%= template(collection: @line_items, target: "line-item", model: "line_item") do %>
61
+ <tr>
62
+ <td><%= k_text_field_tag(:line_item, :description) %></td>
63
+ <td><%= k_text_field_tag(:line_item, :qty) %></td>
64
+ <td><%= k_text_field_tag(:line_item, :price_cents) %></td>
65
+ <td><%= k_check_box_tag(:line_item, :complete) %></td>
66
+ </tr>
67
+ <% end %>
68
+ </div>
69
+ ```
70
+ Templates will be available in your javascript by accessing the `App.Template` class. The `#template_info` property is an array of objects that contain both the collection and the template namespaced under the model that you passed.
71
+
72
+ If you passed a line_item you could access it with:
73
+
74
+ `li_info = App.Template.template_info["line_item"]`
75
+
76
+ You could then get access to the collection or the template by using those properties.
77
+
78
+ `li_info.template` would return the html you passed through
79
+
80
+ `li_info.collection` would return the json collection
81
+
82
+ ###Controllers
83
+ In kindred, javascript controllers are a client side augmentation of your ruby controllers. They are just client side code that gets run on page load.
84
+
85
+ ```coffeescript
86
+ class this.InvoicesController
87
+ @edit: ->
88
+ console.log "Run this on invoice edit view"
89
+ ```
90
+
91
+ To ensure that this code is run on the client side call the js method from your view or controller:
92
+
93
+ ```ruby
94
+ class InvoicesController < ApplicationController
95
+ def edit
96
+ @line_items = @invoice.line_items
97
+
98
+ respond_to do |format|
99
+ format.html { js }
100
+ end
101
+ end
102
+ end
103
+ ```
104
+
105
+ ###Models
106
+
107
+ A kindred model is an object that helps the interaction between the page and the rails api.
108
+
109
+ Here is an example model:
110
+
111
+ ```coffeescript
112
+ class App.LineItem extends App.Base
113
+
114
+ @route = "/line_items"
115
+ @set_class_name("LineItem")
116
+ ```
117
+
118
+ ####Instance Functions
119
+
120
+ #####Page functions:
121
+
122
+ `append_to_page()` This function will put the values from the model instance that is called on the page. If the element found is an input it will add it as a value. If it is not an input it will insert the value into the tag.
123
+
124
+ `dirty_from_page()` This boolean function will check all the inputs that belong to the model instance that it is being called on and check if the value has changed since it was set on the page. Returns true or false.
125
+
126
+ `assign_attributes_from_page()` This will grab all the inputs that belong to a model instance and assign them to the attributes property as a javascript object.
127
+
128
+ `remove_errors_from_page()` This function will remove all errors from the page belonging to the model instance.
129
+
130
+ #####Base functions:
131
+
132
+ `set(attr_name, val)` Assigns the value to the model instance attributes object using the attribute name as a key.
133
+
134
+ ```coffeescript
135
+ li = new App.LineItem()
136
+ li.set("foo", "bar")
137
+ li.attributes # => Object {uuid: "some-uuid", foo: "bar"}
138
+ ```
139
+
140
+ `get(attr_name)` Retrieves the value from the model instance attributes object using the attribute name.
141
+
142
+ ```coffeescript
143
+ li = new App.LineItem({foo: "bar"})
144
+ li.get("foo") # => "bar"
145
+ ```
146
+
147
+ `save()` ajax post request to the route specified in the model with the data from the model instance attributes object to either post or patch depending on the presence of the id.
148
+
149
+ ```coffeescript
150
+ li = new App.LineItem({foo: "bar"})
151
+ li.save() # => sends request to POST or PATCH depending on presince of id
152
+ ```
153
+
154
+ ```
155
+ # Server log
156
+ Started POST "/line_items.json" for 127.0.0.1 at 2014-12-14 02:35:32 -0800
157
+ Processing by LineItemsController#create as JSON
158
+ Parameters: {"line_item"=>{"uuid"=>"354f1fb8-a80a-449d-2320-e316bb02390c", "foo"=>"bar"}}
159
+ ```
160
+
161
+ `destroy()` ajax delete request to the route specified in the model.
162
+
163
+ ```coffeescript
164
+ li = new App.LineItem({id: 1})
165
+ li.destroy() # => Removes element from the page and sends delete request if id present
166
+ ```
167
+
168
+ ```
169
+ # Server log
170
+ Started DELETE "/line_items/1.json" for 127.0.0.1 at 2014-12-14 02:41:39 -0800
171
+ Processing by LineItemsController#destroy as JSON
172
+ Parameters: {"id"=>"1"}
173
+ ```
174
+
175
+ `assign_attributes(attrs)` Adds the attrs to the attributes object for the model instance.
176
+
177
+ ```coffeescript
178
+ li = new App.LineItem({foo: "bar"})
179
+ li.assign_attributes({baz: qux, quux: "corge"})
180
+ li.attributes # => Object {uuid: "some-uuid", foo: "bar", baz: "qux", quux: "corge"}
181
+ ```
182
+
183
+ #####Base Overridable Hooks:
184
+ These hooks have defalut functionality but you can override them in the model to do custom behavior.
185
+
186
+ `after_save`
187
+
188
+ `after_save_error`
189
+
190
+ `after_destroy`
191
+
192
+ `after_destroy_error`
193
+
194
+ Here is an example where you are removing relevant errors from the page after deleting a line item.
195
+
196
+ ```coffeescript
197
+ class App.LineItem extends App.Base
198
+
199
+ @route = "/line_items"
200
+ @set_class_name("LineItem")
201
+
202
+ # kindred override hook
203
+ after_destroy: (data, textStatus, xhr) ->
204
+ $("[data-error][data-k-uuid='" + @uuid + "']").parent().parent().remove()
205
+ ```
206
+
207
+ ####Class Functions
208
+
209
+ `set_template()` On page load, use this function to set the template for the model.
210
+ (i.e. `App.LineItem.set_template App.Template.template_info["line_item"]`)
211
+
212
+ `set_class_name()` Sets the class name for the model as well as dash_name and snake_name.
213
+
214
+ `collection_from_page()` Retrieves a collection of model objects from the page.
215
+
216
+ `save_all(opts)` Collects all the objects of this class from the page ajax posts the json to the save_all action.
217
+
218
+ ###Listeners
219
+ Listeners in kindred should be namespaces and set in classes.
220
+
221
+ Below is an example of a listener that will both send a delete request to the server and remove the element from the page.
222
+
223
+ ```coffeescript
224
+ # app/assets/javascripts/listeners/invoice_listeners.coffee
225
+ class App.InvoiceListeners extends App.Listener
226
+
227
+ @set: ->
228
+ $("#line-item-table").on "click.Listeners.LineItem.delete", ".delete", (evt) ->
229
+ li = new App.LineItem({id: $(@).data("id"), uuid: $(@).data("k-uuid")})
230
+ li.destroy()
231
+ $(@).parent().parent().remove()
232
+ ```
233
+
234
+ A bonus for namespacing the listener is that you can see all the listeners that kindred has registerd using the `App.Listeners` class.
235
+
236
+ `App.Listeners` will return an object which contains all the registered listeners and information about each listener.
237
+
238
+ ### Error Logging
239
+
240
+ All jquery element not found errors are logged to `App.Logger`
241
+
242
+ `App.Logger.errors` will return an array of errors with information including stack traces.
243
+
244
+ ### Naming and Directory Structure
245
+ Although this is really up to the individual developer, Kindred should really be set up with a similar naming and directory stucture as rails.
246
+
247
+ If you are adding code that is controller and action specific, then add a directory called controllers in your `app/assets/javascripts` directory. If your controllers are namespaced then namespace them just like you do in your rails controllers. Here is an example of a namespaced coffee class:
248
+
249
+ ```coffeescript
250
+ # app/assets/javascripts/controllers/admin/special/orders_controller.coffee
251
+ @Admin ||= {};
252
+ @Admin.Special ||= {};
253
+
254
+ class @Admin.Special.OrdersController
255
+
256
+ @index: (args) ->
257
+ alert("Do some js stuff here...")
258
+ ```
259
+
260
+ Put models in `app/assets/javascripts/models`
261
+
262
+ ```coffeescript
263
+ # app/assets/javascripts/models/some_model.coffee
264
+ class App.SomeModel
265
+
266
+ @route = "/some_models"
267
+ @set_class_name("SomeModel")
268
+ ```
269
+
270
+ Make note of the ||=. This is to make sure that you don't overwrite the js object if it already exists.
271
+
272
+ Use this same naming and directory structure for all your js. If you are creating service objects then put them in `app/assets/javascripts/services`
273
+
274
+ Remember to add your paths to the manifest so sprockets can load them:
275
+
276
+ ```
277
+ //= require_tree ./controllers
278
+ //= require_tree ./services
279
+ ```
280
+
281
+ Or require them explicitly:
282
+
283
+ `//= require controllers/admin/special/orders_controller`
284
+
285
+ ## Donating
286
+ Support this project and [others by mfpiccolo][gittip-mfpiccolo] via [gittip][gittip-mfpiccolo].
287
+
288
+ [gittip-mfpiccolo]: https://www.gittip.com/mfpiccolo/
289
+
290
+ ## Copyright
291
+
292
+ Copyright (c) 2014 Mike Piccolo
293
+
294
+ See [LICENSE.txt](LICENSE.txt) for details.
295
+
296
+ ## Contributing
297
+
298
+ 1. Fork it ( http://github.com/mfpiccolo/kindred/fork )
299
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
300
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
301
+ 4. Push to the branch (`git push origin my-new-feature`)
302
+ 5. Create new Pull Request
303
+
304
+ [![githalytics.com alpha](https://cruel-carlota.pagodabox.com/e1a155a07163d56ca0c4f246c7aa8766 "githalytics.com")](http://githalytics.com/mfpiccolo/kindred)
305
+
306
+ [license]: https://github.com/mfpiccolo/kindred/MIT-LICENSE
307
+ [homepage]: http://mfpiccolo.github.io/kindred
308
+ [documentation]: http://rdoc.info/github/mfpiccolo/kindred/frames
309
+ [issues]: https://github.com/mfpiccolo/kindred/issues
310
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1 @@
1
+ window.App = {}
@@ -0,0 +1,13 @@
1
+ //= require app
2
+ //= require ./utilities/binder
3
+ //= require ./utilities/uuid
4
+ //= require ./utilities/virtual_class
5
+ //= require ./utilities/logger
6
+ //= require ./utilities/listener
7
+ //= require ./utilities/template
8
+ //= require ./utilities/stack_trace
9
+ //= require ./models/setup
10
+ //= require ./models/active_page
11
+ //= require ./models/base
12
+
13
+
@@ -0,0 +1,97 @@
1
+ class App.ActivePage
2
+
3
+ @collection_from_page: ->
4
+ indices = $("[data-kindred-model]").find("[data-k-uuid][data-class='#{@snake_name}']")
5
+ uuids = []
6
+
7
+ indices.map (i, tag) ->
8
+ uuids.push($(tag).data("k-uuid"))
9
+
10
+ # map args for JS array seem to differ from jQuery array above
11
+ collection_attrs = uuids.map (uuid, i) =>
12
+ new @({uuid: uuid}).assign_attributes_from_page().attributes
13
+
14
+ append_to_page: ->
15
+ $template = $(@template)
16
+ $.each @attributes, (key, value) =>
17
+ input = $template.find("input[data-attr='" + key + "']")
18
+ if input.length
19
+ if input.is(':checkbox')
20
+ input.prop('checked', value)
21
+ else
22
+ input.val(value)
23
+
24
+ select = $template.find("select[data-attr='" + key + "']")
25
+ if select.length
26
+ select.val(value)
27
+
28
+ span = $template.find("span[data-attr='" + key + "']")
29
+ if span.length
30
+ span.html(value)
31
+
32
+ @_append_data_model_to_page()
33
+
34
+ $("[data-target][data-target-uuid='" + @target_uuid + "']").append($template)
35
+
36
+ error_tag = $("[data-error][data-k-uuid='" + @uuid + "']")
37
+ error_tag.hide()
38
+
39
+ update_vals_on_page: ->
40
+ $.each @attributes, (attr, val) =>
41
+ $("[data-k-uuid='" + @uuid + "'][data-attr='" + attr + "']").val(val)
42
+
43
+ dirty_from_page: ->
44
+ dirty = []
45
+ $.each $("input[data-k-uuid='" + @uuid + "'], select[data-k-uuid='" + @uuid + "']"), (i, input) =>
46
+ $input = $(input)
47
+
48
+ dirty_object = {}
49
+ attr = $input.data("attr")
50
+
51
+ if @_input_dirty($input)
52
+ dirty.push(dirty_object[attr] = [$input.data("val").toString(), $input.val().toString()])
53
+
54
+ if dirty.length
55
+ true
56
+ else
57
+ false
58
+
59
+ assign_attributes_from_page: ->
60
+ $("input[data-k-uuid='" + @uuid + "']").each (i, input) =>
61
+ $input = $(input)
62
+
63
+ if $input.is(':checkbox')
64
+ @set $input.data("attr"), $input.prop('checked')
65
+ else
66
+ @set $input.data("attr"), $input.val()
67
+
68
+ model_data = $("[data-kindred-model]").find("[data-k-uuid='" + @uuid + "']")
69
+ if !isNaN(parseFloat(model_data.data("id"))) && isFinite(model_data.data("id"))
70
+ @id = model_data.data("id")
71
+
72
+ $("select[data-k-uuid='" + @uuid + "']").each (i, select) =>
73
+ @set $(select).data("attr"), $(select).val()
74
+
75
+ @
76
+
77
+ remove_errors_from_page: ->
78
+ $("[data-error][data-k-uuid='" + @uuid + "']").each (i, elem) =>
79
+ $(elem).remove()
80
+
81
+ _update_data_vals_on_page: ->
82
+ model_data = $("[data-kindred-model]").find("[data-k-uuid='" + @uuid + "']")
83
+ model_data.data("id", @id)
84
+ $.each @attributes, (attr, val) =>
85
+ $("[data-k-uuid='" + @uuid + "'][data-attr='" + attr + "']").data("val", val)
86
+
87
+ _append_data_model_to_page: ->
88
+ model_div = "<div data-k-uuid=" + @uuid + " data-id=" + @id + " data-class=" + @snake_name + "></div>"
89
+ $("[data-kindred-model]").append(model_div)
90
+
91
+ _input_dirty: (input) ->
92
+ if input.is("select") && input.data("val").length == 0
93
+ false
94
+ else if input.is(":checkbox")
95
+ !(input.data("val").toString() == input.prop("checked").toString())
96
+ else
97
+ !(input.data("val").toString() == input.val().toString())
@@ -0,0 +1,162 @@
1
+ class App.Base extends App.VirtualClass App.ActivePage, App.Setup
2
+
3
+ @set_template: (template) ->
4
+ @template = template
5
+
6
+ @save_all: (opts) ->
7
+ data = {}
8
+ # TODO fix the naive inflection
9
+ collection = @collection_from_page(@snake_name)
10
+ added_attrs = []
11
+
12
+ $.each collection, (i, attrs) ->
13
+ added_attrs.push($.extend attrs, opts.add_data_to_each)
14
+
15
+ data[@snake_name + "s"] = added_attrs
16
+
17
+ # TODO REMOVE THIS AFTER WEBKIT BUG FIX. https://github.com/thoughtbot/capybara-webkit/issues/553
18
+ # This conditonal is for testing but there is no easy fix at the moment.
19
+ # Put passes through data. Patch dosn't.
20
+ if (userAgent = window?.navigator?.userAgent).match /capybara-webkit/ || userAgent.match /PhantomJS/
21
+ path = @route + "/save_all.json"
22
+ method = 'PUT'
23
+ else
24
+ path = @route + "/save_all.json"
25
+ method = 'PATCH'
26
+
27
+ $.ajax
28
+ type: method
29
+ url: App.BaseUrl + "/" + path
30
+ data: data
31
+
32
+ success: (data, textStatus, xhr) =>
33
+ @after_save_all(data, textStatus, xhr)
34
+ error: (xhr) =>
35
+ @after_save_all_error(xhr)
36
+
37
+ # The attribute setter publish changes using the DataBinder PubSub
38
+ set: (attr_name, val) ->
39
+ @attributes[attr_name] = val
40
+ # @binder.trigger @id + ":change", [
41
+ # attr_name
42
+ # val
43
+ # @
44
+ # ]
45
+
46
+ get: (attr_name) ->
47
+ @attributes[attr_name]
48
+
49
+ remove: (attr_name) ->
50
+ delete @attributes[attr_name]
51
+
52
+ save: ->
53
+ if !isNaN(parseFloat(@id)) && isFinite(@id)
54
+
55
+ # TODO REMOVE THIS AFTER WEBKIT BUG FIX. https://github.com/thoughtbot/capybara-webkit/issues/553
56
+ # This conditonal is for testing but there is no easy fix at the moment.
57
+ # Put passes through data. Patch dosn't.
58
+ if (userAgent = window?.navigator?.userAgent).match /capybara-webkit/ || userAgent.match /PhantomJS/
59
+ path = @route + "/" + @id + ".json"
60
+ method = 'PUT'
61
+ else
62
+ path = @route + "/" + @id + ".json"
63
+ method = 'PATCH'
64
+ else
65
+ path = @route + ".json"
66
+ method = "POST"
67
+
68
+ params = {}
69
+ params[@snake_name] = @attributes
70
+
71
+ response = $.ajax
72
+ type: method
73
+ url: App.BaseUrl + "/" + path
74
+ dataType: "json"
75
+ data: params
76
+ global: false
77
+ async: false
78
+ success: (data, textStatus, xhr) =>
79
+ @after_save(data, textStatus, xhr)
80
+ error: (xhr) =>
81
+ @after_save_error(xhr)
82
+
83
+ destroy: ->
84
+ @route ||= @snake_name + "s"
85
+ path = @route + "/" + @id + ".json"
86
+ method = "DELETE"
87
+
88
+ if !isNaN(parseFloat(@id)) && isFinite(@id)
89
+ $.ajax
90
+ type: method
91
+ url: App.BaseUrl + "/" + path
92
+ dataType: "json"
93
+ global: false
94
+ async: false
95
+ success: (data, textStatus, xhr) =>
96
+ @after_destroy(data, textStatus, xhr)
97
+ error: (xhr) =>
98
+ @after_destroy_error(xhr)
99
+ else
100
+ @after_destroy()
101
+
102
+ assign_attributes: (attrs) ->
103
+ $.each attrs, (attr, val) =>
104
+ if attr == "id" && !isNaN(parseFloat(val)) && isFinite(val)
105
+ @id = val
106
+ @set(attr, val)
107
+
108
+ #overridable hook
109
+ after_save: (data, textStatus, xhr) ->
110
+ @assign_attributes(data)
111
+ @_clear_errors()
112
+ @_update_data_vals_on_page()
113
+ @_setup_interpolated_vars()
114
+
115
+ #overridable hook
116
+ after_save_error: (xhr) ->
117
+ errors = JSON.parse(xhr.responseText)
118
+ @_handle_errors(errors)
119
+
120
+ #overridable hook
121
+ after_destroy: (data, textStatus, xhr) ->
122
+ @remove_errors_from_page()
123
+
124
+ #overridable hook
125
+ after_destroy_error: (xhr) ->
126
+
127
+ #overridable hook
128
+ @after_save_all: (data, textStatus, xhr) ->
129
+ $(data).each (i, response_object) =>
130
+ attrs = response_object[@snake_name]
131
+ model = new App[@class_name]({uuid: attrs["uuid"]})
132
+ model.assign_attributes(attrs)
133
+ model._clear_errors()
134
+ model._update_data_vals_on_page()
135
+
136
+ #overridable hook
137
+ @after_save_all_error: (xhr) ->
138
+ data = JSON.parse(xhr.responseText)
139
+ $(data).each (i, response_object) =>
140
+ unless $.isEmptyObject(response_object["errors"])
141
+ uuid = response_object[@snake_name].uuid
142
+ model = new App[@class_name](response_object[@snake_name])
143
+ model.assign_attributes_from_page()
144
+ model._handle_errors(response_object["errors"])
145
+
146
+ _handle_errors: (errors_obj, uuid) ->
147
+ hideable_error_inputs = $(Object.keys(@attributes)).not(Object.keys(errors_obj)).get()
148
+ $.each hideable_error_inputs, (i, attr) =>
149
+ $("[data-error][data-attr='" + attr + "'][data-k-uuid='" + @uuid + "']").hide()
150
+
151
+ $.each errors_obj, (attr, messages) =>
152
+ error_tag = $("[data-error][data-attr='" + attr + "'][data-k-uuid='" + @uuid + "']")
153
+ error_tag.html("")
154
+
155
+ $.each messages, (i, message) ->
156
+ error_tag.append("<span>" + message + "</span><br>")
157
+
158
+ error_tag.show()
159
+
160
+ _clear_errors: () ->
161
+ $.each @attributes, (attr, val) =>
162
+ $("[data-error][data-attr='" + attr + "'][data-k-uuid='" + @uuid + "']").hide()