Formatting Rabl for Ember or Mapping Ember to Rabl? - ruby-on-rails

My rails app used to produce JSON that looked like this:
{"paintings":
[
{"id":1,"name":"a"},
{"id":2,"name":"b"}
]
}
I added rabl json formatting, and now the json looks like this:
[
{"id":1,"name":"a"},
{"id":2,"name":"b"}
]
Ember is telling me
Uncaught Error: assertion failed: Your server returned a hash with the key 0 but you have no mapping for it
How can I make Ember understand this? Or how should I make rabl understandable to ember?

Found a solution. My index.json.rabl looked like this:
collection #paintings
extends 'paintings/show'
Now it looks like this:
collection #paintings => :paintings
extends 'paintings/show'

You can extend DS.RESTSerializer and change extract and extractMany. The following is merely a copy and paste from the serializer I use in .NET, for the same scenario:
window.App = Ember.Application.create();
var adapter = DS.RESTAdapter.create();
var serializer = Ember.get( adapter, 'serializer' );
serializer.reopen({
extractMany: function (loader, json, type, records) {
var root = this.rootForType(type);
root = this.pluralize(root);
var objects;
if (json instanceof Array) {
objects = json;
}
else {
this.sideload(loader, type, json, root);
this.extractMeta(loader, type, json);
objects = json[root];
}
if (objects) {
var references = [];
if (records) { records = records.toArray(); }
for (var i = 0; i < objects.length; i++) {
if (records) { loader.updateId(records[i], objects[i]); }
var reference = this.extractRecordRepresentation(loader, type, objects[i]);
references.push(reference);
}
loader.populateArray(references);
}
},
extract: function (loader, json, type, record) {
if (record) loader.updateId(record, json);
this.extractRecordRepresentation(loader, type, json);
}
});
And before you set your store, you must configure your model to sideload properly:
serializer.configure( 'App.Painting', {
sideloadAs: 'paintings'
} );
App.Store = DS.Store.extend({
adapter: adapter,
revision: 12
});
Now you should be able to load rootless JSON payload into your app.
(see fiddle)

Related

Returning recursive result

I currently have this in a simple MVC Api controller:
var rootFolder = Umbraco.TypedMedia(200);
return rootFolder.Children().Select(s => new MediaItem
{
Name = s.Name,
Children = s.Children.Select(e => new MediaItem
{
Name = e.Name
})
});
It works, but only return level 1 and 2.
I tried using
return rootFolder.Descendants(), which returns all results from all levels - but "flattened out", so I cannot see the structure in the output.
The output is used in a simple app, navigating a tree structure.
Any ideas, as to how I can make it recursive?
Using Descendants, the output is returned like this
[
{
"Name":"dok1"
},
{
"Name":"dok2"
},
{
"Name":"dok21"
}
]
But it should be
[
{
"Name":"dok1"
},
{
"Name":"dok2"
"Children": [
{
"Name":"dok21"
}
]
}
Not sure you really need recursion here -- the solution below (or something similar) should suffice
// Dictionary between level/depth(int) and the files on that level/depth
var fileDictionary = new Dictionary<int, List<MediaItem>>();
var children = rootFolder.Children();
var depth = 1;
while (children.Any())
{
var tempList = new List<MediaItem>();
children.ForEach(child => {
tempList.Add(child);
});
fileDictionary.Add(depth, tempList);
children = children.Children();
depth++;
}
Then, you can do something like:
foreach (var key in fileDictionary.Keys)
{
// Access the key by key.Key (key would be "depth")
// Access the values by fileDictionary[key] (values would be list of MediaItem)
}
Why not just create a recursive function like so?
IEnumerable<MediaItem> ConvertToMediaItems(IEnumerable<IPublishedContent> items)
{
return items?.Select(i => new MediaItem
{
Name = i.Name,
Children = ConvertToMediaItems(i.Children)
}) ?? Enumerable.Empty<MediaItem>();
}
Then the usage would be
var rootFolder = Umbraco.TypedMedia(200);
return ConvertToMediaItems(rootFolder.Children());
You can also make the function a local function if it's only needed in one place.

Zapier Scripting: Catch error and rewrite response

The app I'm integrating returns a 400 bad request when the search doesn't find a match which Zapier sees as an error.
How can I catch the error and return a [] to tell Zapier there is no match?
Here's the existing post_search code of got:
match_person_email_post_search: function(bundle) {
console.log(bundle.response.content);
var res = JSON.parse(bundle.response.content);
var data;
if(res.person!==null)
{
data = [ res.person ];
}
else
{ data = []; }
return data;
},
I should probably mention I'm a rookie at this scripting!
Thanks!
As long as the search API result returns something, you should be able to capture that using Zapier scripting. Assuming your Zapier search key is contact_data, here is how the function would look:
var Zap = {
search_contact_post_search: function(bundle) {
return bundle.response.content;
}
};
So you can check (within that function) if the data is empty or containing something other than search data, and just return an empty array:
var Zap = {
search_contact_post_search: function(bundle) {
var response_content = JSON.parse(bundle.response.content);
if (!response_content.some_field) {
return [];
}
}
};

Why am I getting this error in a basic Rails+Ember app?

I am trying to do a simple CRUD app using Ember + Rails and I'm getting the following error when trying to go to the /workouts route.
Error while loading route: TypeError {} ember.js?body=1:415
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this), proto = m.proto;
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
Ember.assert("`actions` must be provided at extend time, not at create time, when Ember.ActionHandler is used (i.e. views, controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this)));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
this.init.apply(this, arguments);
m.proto = proto;
finishChains(this);
sendEvent(this, "init");
} has no method 'find'
My code is located here: https://github.com/ecl1pse/ember-workouts
What am I doing wrong?
Edit: Upon further investigation I believe the culprit is
EmberWorkouts.WorkoutsRoute = Ember.Route.extend(
model: -> EmberWorkouts.Workout.find()
This doesn't actually return anything. How do I debug from there?
If I replace that with this
EmberWorkouts.WorkoutsRoute = Ember.Route.extend
model: -> [{title: 'hi'}, {title: 'damn'}]
The view actually renders content.
How do I get the model to collect from Rails properly?
Ember Data's interface has changed a little with the current release:
You can clear out the store.js file entirely. Ember Data will automatically set up a data store for you using the REST Adapter (unless you tell it otherwise).
Use model: -> #store.find('workout') instead.
I tested this with your app and it works.
If you haven't read through the Ember Data Guide in the last week or two (it's changed a lot), I would spend a few minutes on it.
The fix for this error (as of ember-data 1.0.0.beta.6) for me was to make sure that the JSON returned from the server included an "id" field for each model, BUT not to explicitly declare the id when setting up the Ember DS.Model.
jbuilder template:
json.scans do
json.array! #scans do |scan|
json.id scan.id # This prop has to be there
json.name scan.name
end
end
Ember model:
EmberApp.Scan = DS.Model.extend(
// Don't include the id prop here
name: DS.attr("string")
)

Back and forward buttons not rendering templates in ember rails app

I always have this issue in ember apps that are built on a rails backend. I have a groups.hbs template which lists out a bunch of groups, when you click on a group it loads the group.hbs template next to the groups template and changes the url to /groups/:group_id.
However, when I click the back and forward buttons, or try to manually load a url with a specific :group_id the group template fails to render and the console throws a giant
Uncaught TypeError: Object function () {
...
error.
group.js.coffee
App.Group = Ember.Object.extend()
App.Group.reopenClass
all: ->
App.ajax(
url: App.apiUrl('/groups')
).then (data) ->
console.log data
groups = []
for group in data.response
groups.addObject(App.Group.create(group))
console.log(groups)
groups
router.js.coffee
Mdm.Router.map ->
#resource 'groups', ->
#resource 'group', {path: "/:group_id"}
Mdm.Router.reopen
location: 'history'
I've never experienced this issue when building standalone ember apps. Any idea what would cause this?
EDIT
I should add that I am pulling my data from an api via XHR requests.
EDIT 2
I just explicitly created the GroupRoute and had it load all of the groups, this code is identical to the GroupsRoute. The template is still not rendering, but I no longer get that error.
GroupRoute
App.GroupRoute = Ember.Route.extend(model: ->
App.Group.all()
)
And GroupsRoute:
App.GroupsRoute = Ember.Route.extend(model: ->
App.Group.all()
)
EDIT 3
Here's the whole error if it helps anyone.
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this);
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
delete m.proto;
finishChains(this);
this.init.apply(this, arguments);
} has no method 'find'
EDIT
So I think I figured out the problem, when you click the back button or enter a manual url it wasn't finding the obkect based on id. So I added a find() method to the Group model. Not it looks like this:
Mdm.Group = Ember.Object.extend()
Mdm.Group.reopenClass
all: ->
Mdm.ajax(
url: Mdm.apiUrl('/groups')
).then (data) ->
console.log data
groups = []
for group in data.response
groups.addObject(Mdm.Group.create(group))
console.log(groups)
groups
find: (group_id) ->
Mdm.ajax(
url: Mdm.apiUrl("/groups/#{group_id}")
).then (data) ->
renderTemplate: (data)
And my GroupRoute looks like this:
Mdm.GroupRoute = Ember.Route.extend
model: (params) ->
console.log 'oh hai'
Mdm.Group.find(params.group_id)
Now in the console when I click the back button it is loading the data but its not associating the group template with the group_id. What is the best practice way to tell ember to do this?
I'm not a rails developer but try doing something like this for the simple model/route setup you show above
App.Group = Ember.Object.extend().reopenClass
groups: []
find: ->
$.ajax
url: "/api/groups/"
type: "GET"
cache: false
dataType: "json"
beforeSend: =>
#groups.clear()
success: (results) =>
[#groups.addObject(App.Group.create(result)) for result in results]
error: =>
alert "error: failed to load the available groups"
#groups
App.Router.map ->
#resource "groups", path: "/", ->
#route "group", path: "/:group_id"

I need to filter the yui datatable which has inline cell editing. and the data is coming from the groovy controller in key:value pair form. as JSON

hi i am also facing the data error problem. i wanted to filter the data coming from json result from the groovy controller in key:value pair. even if i chage the TYPE_JSON to TYPE_JSARRAY,i am getting NoRecordas found in data table but the json result has the data.
please can u correct me.
Thanks in advance!!
dobMenuButton.subscribe("selectedMenuItemChange",function(e) {
var value =e.newValue.value;
if(YAHOO.lang.isValue(value)) {
myDataTable.getDataSource().sendRequest(null, {
success:function(request, response, payload) {
this.initializeTable();
var rs = response.results;
var filtered = [];
for(var i = 0; i < rs.length; i++) {
if(((rs[i].dateOfBirth).format("MM/dd/yyyy")) == value) {
filtered[filtered.length] = rs[i];
}
}
this.getRecordSet().reset();
MCMPagination.paginatorvar.setTotalRecords(filtered.length,true);
this.getRecordSet().setRecords(filtered, 0);
this.render();
},
scope:myDataTable,
argument:myDataTable.getState()
});
}
});

Resources