Best way to reformat json in Rails? - ruby-on-rails

An app I am building receives some information in JSON, however the data is very poorly organized. I would like to rebuild the JSON output. The JSON I'm receiving is completely flat, and certain things should be nested. To illustrate what I mean:
I'm getting something like this:
{[
{fullname: 'Joe', session: 'A', time: '5:00', room: 'Ballroom'},
{fullname: 'Abe', session: 'B', time: '5:00', room: 'Bathroom'},
{fullname: 'Mike', session: 'C', time: '6:00', room: 'Bathroom'},
]}
I want something like this:
{
rooms: [
{
name: 'Ballroom',
sessions: [
{
title: 'A',
speakers: [{name: 'Joe'}]
}
]
},
{
name: 'Bathroom',
sessions: [
{
title: 'B',
speakers: [{name: 'Abe'}]
},
{
title: 'C',
speakers : [{name: 'Mike'}]
}
]
}
]
}
Are there any gems that are well equipped for doing something like this? Is there a specific part of the application this manipulation should be done in to follow MVC?
I should note that all this app does is receive this JSON and then makes API calls to another application to create/update information in that app's DB to reflect what's in the JSON.

Use:
JSON.pretty_generate your_hash
For example:
require 'json'
my_json = { :array => [1, 2, 3, { :sample => "hash"} ], :foo => "bar" }
puts JSON.pretty_generate(my_json)
refer to: How can I "pretty" format my JSON output in Ruby on Rails?

You can re-format using jbuilder or rabl gems. Usage and examples are pretty strait forward in their readme

Related

Angular 4 & Rails 5 Post Request JSON Formatting

I'm developing an angular app using a rails backend. I'm having problems formatting the parameters hash so rails can use it. The data is a many to many relationship, and the form contains nested attributes. In Rails, my models utilize the accepts_nested_attributes_for helper. I know exactly what format rails expects, but when I make a POST request, there is one minor detail that's off. I'm going to list below two param hashes. One is what Angular produces, and the other is what Rails expects.
What's off about the Angular request is rails expects a deeper layer of nesting in the expense_expense_categories attributes. I've never understood why rails requires it. What angular produces looks logical to me. My question is.. What do I need to do to format the parameters in Angular? Looking at what I have so far, am I doing this in a way that satisfies Angular best practices?
Angular:
{
"expense": {
"date": "2017/4/13",
"check_number": "132",
"debit": "0",
"notes": "har",
"amount": "24",
"payee_id": "334"
},
"expense_expense_categories_attributes": [{
"expense_category_id": "59",
"amount": 12
},
{
"expense_category_id": "62",
"amount": 11
}
]
}
What Rails expects:
{
"expense": {
"date": "2017/12/12",
"check_number": "122",
"debit": "0",
"notes": "har",
"amount": "24",
"payee_id": "334",
"expense_expense_categories_attributes": {
"210212312": {
"expense_category_id": "72",
"amount": "12"
},
"432323432": {
"expense_category_id": "73",
"amount": "12"
}
}
}
}
My code in angular is as follows.
onSubmit() method in component:
onSubmit() {
this.expenseService.addExpense(this.expenseForm.value)
.subscribe(
() => {
this.errorMessage = '';
},
error => {
this.errorMessage = <any>error;
}
);
this.expenseForm.reset();
}
addExpense in my service file:
addExpense(expense: Expense): Observable<any> {
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.post('http://localhost:3000/expenses', expense, options)
.map(
(res: Response) => {
const expenseNew: Expense = res.json();
this.expenses.push(expenseNew);
this.expensesChanged.next(this.expenses.slice());
})
.catch(this.handleError);
}
my main form:
private initForm() {
let expense_expense_categories_attributes = new FormArray([]);
this.expenseForm = this.fb.group({
id: '',
date: '',
amount: '',
check_number: '',
debit: '',
payee_id: '',
notes: '',
expense_expense_categories_attributes: expense_expense_categories_attributes
});
}
My FormArray for nested attributes:
onAddExpenseCategories() {
(<FormArray>this.expenseForm.get('expense_expense_categories_attributes')).push(
new FormGroup({
'expense_category_id': new FormControl(null, Validators.required),
'amount': new FormControl(null, [
Validators.required
])
})
);
}
UPDATE: I was able to get it working, but I had to use a god awful regex to manipulate the request to what I wanted. It was an extremely ugly option so I still need to find a better option. Is there a better way to format JSON Objects and replace the contents? I'm not sure the correct way to do it. Need help.
You need to add the expense_expense_categories to the wrap_parameters like this:
wrap_parameters :expense, include: [:expense_expense_categories_attributes]
Additional attributes must be explicitly added to wrap_parameters as it only wraps attributes of the model itself by default.

Ruby on Rails form with array of nested hashes

I'm trying to build a rails form to submit an array of nested hashes.
In controller i want to get params in structure like:
[
{
id: 'section1',
rows:
[
{ id: '1', seats: '1-20' },
{ id: '2', seats: '1-25' }
]
},
{
id: 'section2',
rows:
[
{ id: '1', seats: '1-50' },
...
]
},
...
]
It's not a problem to dynamically add additional sections and rows fields with js, but i cant figure out how this form should look like to work properly.

How to pass a Hash to Grape API method?

I'm having problems with the Grape gem and the parameters validation.
The idea behind this is to create a complex entity using nested attributes through an API service.
I have a method to create a trip, trip have many destinations and i want to pass that destinations using a hash (using the accepts_nested_attributes_for helper).
I have this grape restriction over the parameter:
requires :destinations, type: Hash
And I'm trying to send something like this:
{ destinations => [
{ destination: { name => 'dest1'} },
{ destination: { name => 'dest2'} },
{ destination: { name => 'dest3'} }
]}
In order to build something like the structure below inside the method and get the trip created:
{ trip: {
name: 'Trip1', destinations_attributes: [
{ name: 'dest1' },
{ name: 'dest2' },
{ name: 'dest3' }
]
}}
I'm using POSTMAN chrome extension to call the API method.
Here's a screen capture:
If someone can help me i would be very grateful.
By the looks of what you are trying to send, you need to change the Grape restriction, because destinations is an Array, not a Hash:
requires :destinations, type: Array
You don't need the "destination" hash when sending the request:
{ destinations => [
{ name => 'dest1', other_attribute: 'value', etc... },
{ name => 'dest2', other_attribute: 'value', etc... },
{ name => 'dest3', other_attribute: 'value', etc... }
]}
This creates an Array of hashes.
In order to send this through POSTMAN, you'll need to modify that destinations param your sending and add multiple lines in POSTMAN. Something like:
destinations[][name] 'dest1'
destinations[][other_attribute] 'value1'
destinations[][name] 'dest2'
destinations[][other_attribute] 'value2'
destinations[][name] 'dest3'
destinations[][other_attribute] 'value3'
Hope this answers your questions. Let me know if this is what you were looking for.

"The response from a findAll must be an Array, not undefined" - potentially incorrect serialisation?

Just getting started with Ember, and I'm following this tutorial.
I've got the Ember app set up, the Route and the Model, except when I hit the Route, I get a large number of warnings printed to the console, followed by the error in the title.
My Model looks like:
App = DS.Model.extend
firstName: DS.attr('string')
lastName: DS.attr('string')
email: DS.attr('string')
phone: DS.attr('string')
status: DS.attr('string', defaultValue: 'new')
notes: DS.attr('string')
And my Route:
App.LeadsRoute = Ember.Route.extend
model: -> #store.find 'lead'
My Store.js:
App.ApplicationStore = DS.Store.extend({
})
# Override the default adapter with the `DS.ActiveModelAdapter` which
# is built to work nicely with the ActiveModel::Serializers gem.
App.ApplicationAdapter = DS.ActiveModelAdapter.extend({
})
The JSON my Rails app is returning looks like this.
The warnings are:
WARNING: Encountered "0" in payload, but no model was found for model name "0" (resolved model name using DS.ActiveModelSerializer.typeForRoot("0"))
(I get one warning for each Lead in the Database)
And the error is:
Error while processing route: leads Assertion Failed: The response from a findAll must be an Array, not undefined Error: Assertion Failed: The response from a findAll must be an Array, not undefined
Which seems to be a pretty common one, if StackOverflow search is anything to go by :)
Help greatly appreciated!
Thanks,
Nik
Ok, so I figured this one out... for list responses, ember-data/ActiveModelAdapter is expecting responses formatted like this:
{ "leads" : [ { id: 123, name: "Test", ... }, { id: 456, name: "Test 2", ... } ] }
Whereas I was sending
[ { "lead": { id: 123, name: "Test", ... } }, { "lead": { id: 456, name: "Test 2", ... }} ] }

Correct JSON structure

I need to return a leaderboard data in pages via JSON, which is the correct structure, is it this
{
pages: [
{
[
{user: John,
rating:11},
{user: Bob,
rating: 20},
{user: Andy,
rating: 30},
...
]
},
{
[
{user: Sally,
rating: 110},
{user: Peter,
rating: 115},
{user: Jim,
rating: 350},
...
]
},
...
]
}
Or is this (correct JSON)
{
"pages": [
[
{
"user": "John",
"rating": 11
},
{
"user": "Bob",
"rating": 20
},
{
"user": "Andy",
"rating": 30
}
],
[
{
"user": "Sally",
"rating": 110
},
{
"user": "Peter",
"rating": 115
},
{
"user": "Jim",
"rating": 350
}
]
]
}
UPDATE:
Thanks for all the prompt answers, and yes I did construct the JSON by hand which is obviously not a good idea as some of you have pointed out. The 2nd option is the proper JSON and I have updated it with the correct JSON structure for anyone else that might be reading this in the future.
The latter is correct, but you need to enclose all your strings with double quotes. You also used a period instead of a comma after the first closing square bracket.
You may wish to use JSONLint to validate your JSON.
Your first example doesn’t make much sense: pages is an array whose elements are objects but there are no key-value pairs. Your second example makes more sense: pages is an array where which element is in turn another array containing a list of objects.
Note that neither of your examples is valid JSON. As explained in the previous paragraph, your first example has objects with no key-value pairs. Furthermore, in both examples the strings aren’t quoted. In JSON, every string must be quoted, be it a key or a value.
You might want to check out this JSON validator :) http://www.jsonlint.com/
For starters, the first option is not valid JSON. The array:
{
pages: [
{
[ <== HERE
...would require a name. E.g.
{
pages: [
{
"SomeName": [
Also, assuming John, Bob, Andy etc, are strings, then they should be:
[
{user: "John",
rating:11},
{user: "Bob",
rating: 20},
{user: "Andy",
rating: 30},
...
]
Some would also argue that your dictionary member names should be enquoted.

Resources