Cannot save a post to a database - ruby-on-rails

I have an Event app, where I am using a calendar. I have created 2 external routes and controller action to #save and #load data and I have 2 templates for them. One is save.json.erb other one is load.json.erb. For now, I have hardcoded the load data from load.json.erb, looks like;
{"2015-05-06": {"available":"1",
"bind":0,
"info":"",
"notes":"",
"price":"20",
"promo":"",
"status":"available"
},
"2015-05-07": {"available":"1",
"bind":0,
"info":"",
"notes":"",
"price":"20",
"promo":"",
"status":"available"
}}
So, it shows the hardcoded data on the calendar.
My problem is, when I press submit, it does not give an error but it does not save it to database. I could not figure out the strong params either. I am using permit! for now to see it saves to database. Maybe my Event Model and its columns are wrong. Because I could not figure it out what it posts (integer, string etc..)
Here is my events_controller
....
def load
end
def save
#THIS IS WRONG I SHOULD PUT SMTH ELSE
#event = Event.new
#event.save
end
def event_params
params.require(:event).permit!
end
when I select data from calendar press submit it sends this;
HEADER:
event_id:0
event:{"2015-05-06":{"available":"1","bind":0,"info":"","notes":"","price":"20","promo":"","status":"available"},"2015-05-07":{"available":"1","bind":0,"info":"","notes":"","price":"20","promo":"","status":"available"},"2015-05-27":{"available":"1","bind":0,"info":"demo","notes":"demodemodemo","price":"15","promo":"2","status":"available"}}
Also from the log; Is this data JSON?, or I have to use JSON.parse somewhere?
Started POST "/events/save" for ::1 at 2015-05-02 18:39:42 +0300
Processing by EventsController#save as */* #HERE IT SENDS THE PARAMETERS, BUT I COULD NOT GET THEM
Parameters: {"event_id"=>"0", "event"=>"{\"2015-05-06\":{\"available\":\"1\",\"bind\":0,\"info\":\"\",\"notes\":\"\",\"price\":\"20\",\"promo\":\"\",\"status\":\"available\"},\"2015-05-07\":{\"available\":\"1\",\"bind\":0,\"info\":\"\",\"notes\":\"\",\"price\":\"20\",\"promo\":\"\",\"status\":\"available\"},\"2015-05-27\":{\"available\":\"1\",\"bind\":0,\"info\":\"demo\",\"notes\":\"demodemodemo\",\"price\":\"15\",\"promo\":\"2\",\"status\":\"available\"}}"}
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "events" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2015-05-02 15:39:42.823906"], ["updated_at", "2015-05-02 15:39:42.823906"]]
(2.4ms) commit transaction
Rendered events/save.json.erb (0.1ms)
Completed 200 OK in 21ms (Views: 12.9ms | ActiveRecord: 3.0ms)
When I try;
def save
#event = Event.new(event_params)
#event.save
end
def event_params
params.require(:event).permit!
end
It gives an error of ;
NoMethodError in EventsController#save
undefined method `permit!' for #<String:0x007fcbb99d24c0>
So I think it is posted as a string instead of a hash.
For me, there is something wrong with the data it sends to database or the database can not configured for these datas. Because of #save action has #events = Event.new all the time it creates new event in the database and saves it.So I have to get the POSTed data but I could not get those parameters. I think I have to take the POSTed data and with for loops I have to take those data and save it to database. Could not find a way. Hope someone helps
Here is the full HEADER:
Cache-Control:no-cache
Connection:Keep-Alive
Content-Length:0
Content-Type:application/json; charset=utf-8
Date:Sat, 02 May 2015 18:42:54 GMT
Server:WEBrick/1.3.1 (Ruby/2.2.1/2015-02-26)
Set-Cookie:_demo_session=Snh1Nm5CNWJzTHNITXp5SkRDejNmZzRXRkdoVm5SeEkwUW82WmE5QkFndThkeUdyTEdBK2s3ZVR1aEUxQXpGclpqR202WTIzSGkzNWtIQUdWZXFFeUtmSVpBbnZZNmJnN090YUxXS2c5eFFPVzZOaXJjTGJndGRsS0xBcUhaU0VlMFAvRVdtT05RMHpLcUYrSy9wSElnPT0tLUI2SWFiOHZXRlpqMmpteVloV3Zsb2c9PQ%3D%3D--3533fcf1e966b5c1e766dcc099caa6df4b5c284f; path=/; HttpOnly
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Request-Id:7863cf16-0162-4760-8719-d155202eca7a
X-Runtime:0.030455
X-Xss-Protection:1; mode=block
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Content-Length:3126
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Cookie:_Paint_session=NXN5NVUvemhXcHF2UnF1ZlBXTHhtZVVWODlyVWFoL0tEWU9vWCtOYVp6ck9EUVF6OUs0WTJYV21yWW5VeVhqdndST2JUYTdCdnNnamNzZDJjQXFLNE1CYzlQT3dtcVJiRTJLcmlwaXR3aFdKZUZBVXN4VitWamMzaWdZUUtTQU1mcWhIRFg3SnJwQ2FBK1hmclZ3WmtiODRuTUpVRUVLKzYzQTNuUGxCTURYMHhKN3Z6eUZtbURLS2MybVlNejNoTmo5MVZteUQzcG8wTnB6WUFlR2pRK2UwSHFnUTVua0hUM0VzWitLUTdya1BXVVZ1eTVKUmNCR3pQaEh2djlpcFo3dFU1WmhZYlhSWS9wYTBVSHRTYXc9PS0tbDJHT3A3NVJ1Tm5OczVIREM2emNLQT09--f2703914b69129404c69d1d9dbb446fde16c6442; _Resim_session=cXQ5cEE5ZjFPNU1Jby9ZY0lKWis2TVY1NVlaN2dVZGtSaDdzc3B1QU9saS9yNXNUUHE5N2d1Ym1CZjcxYlNNZjAwbnhPNm9oeEN0aUpNUXZYMXM1Q2ZHWHJuZ3lhdjhxRmxteVR3ZXFvNHdxMFJZcXp6NWZMbk1qWVJyZG5GVFdBWVVtRkp5MHZsZWVNTzZ1aGs4ejAxUzFoZWE2VHVlWCtuWFBpclZnQU5GR2dzeUFZZFBWeEZaSCsxWUhHNmRkQVhwYk9uWW5oVjd4VVA5L2xvRzR3amt2RmE4VTdjUzRHRHQvRC80Rys0V0ZWQ2FCTW01cWtNQ2E2SHUwYWFFQ080VVJkUEduUWxpUjNaUkZldWZsOWc9PS0tcnM2M2UyVCttTEJBbW13SVJQN3RNQT09--3233372784be95cd2d6e2630f7e5e22c17eeff17; _Carrierwave_session=cE4vek43aGlxZ3BrSFNXWU1JaDZQekcxdlVVSnRzNFRMbGNXRGxjOGhNZ0hGNHRaSFpzODQ1YU95WXVnb1VKUlM3Y0xBVFBjYW1paG5FSTh5dVVzYm1LWXNHVXd2RGJnNktaZ0YxY1JtM25Rb1hLMkhUWm5XTElZS1JUejAxSmFvcnE3VzVXTVFoYmdPbTczOE02Wjk4N2hsaTJDRFdLY2pRc1NxYVRNVFBHdkFHbmEvb1U4aEN4T2FOVDZWZGlUMnpJMU0yNkdHNW1acVpYN3NzRkxwQkN2TzRzS0ZBNlMwMGtIMGJ5bWN2QVp5Q3cxQUNIVnExajFDSldsZWhJek1zUjJxVzJyRHRsUDBjQnJsdlB1RFE9PS0tSjViSGdocSs5blRLdTNnUk1GWk9qZz09--068ded1e78c2d70739361e662b995b5470a059c4; _raddit_session=Vy9USHZBa1FodGxON1BMOWFZOHBIazk3eWVSOXh0OW9yUjRmSjJ5Tmg1OXpsMnI2Tm5ZTGQwOHdEMVJnUWhFNG5jRVZEcVhLL3ZCSVdsM1g5S2FTMjYvZWo4U1ovT0xNdkZVT2dJS0VWTkRMcXdRQ0lzTlFRWC9EZ1ZxbDRtYmtLQ1dkZzM3THltSEFvdzJTN010MHVnYytJMzE1R2F0cUVkQlh2bERBRUM4c0NVNnFsTFdmbVB1cURJeDVHUkhKOWZUOG5pUlhFZkxOSkJ3c01uU2paTW50WXRrRW8zRmJtTTBrUm5GWlJvbz0tLUh4ckZqdnA3WGQzRjdmNnAzMXdMcmc9PQ%3D%3D--622d3f9adb9afb871e118d0715921aaf986f82d2; _Cal_session=cjRlSlc0Q2FITHJJTmlTbU5TZjk5aFNFaTczTHRQZW9TR0h1MG4xYVovZG9xaUxyNittWENjbXdib3FLc1J6cXh3dHA5YitaVkZYbFVtaFI2bGxUKzJyM1kvRzJlRFV4b0JibzJ1TTdQS0ZkdVE2eVJJSlZQVXY0Q0thS1NBRzhGUzlaQ1ZRaHppYzJES2V2eXRmVWtBPT0tLWEzVWFIcWl0WnJBRVdnanJ0em1Renc9PQ%3D%3D--03a65bd6c5338108d2d3db62295843f8b3e3eaaa; _fullcalendar_and_rails_example_session=M1h0eFJieVY1ek16OWRtR1JRUGc1eCtsdFZnTDdqWkd3NndWQTFuUzFKdHJpOHlRYSsweHZ6UGZubTV1eEdJZm14NVZxbE1IUHZzRG1id2NVeENERzFlaFF3NzFNWHk0RlltbS8yNmc2aEtnYjJDcGZmY3pZWG0vU0RsU29ST005NDVpdVpYSWtNTURkM3M2RmVvZU8vUStwdUxFNk1TaVJTRDY1Z2FKcEd4QkxZMlpGeUlsUGl5UlNCVWhPNk5qR082T25lZlZ4NkIxY012MjRJaVVTKzFqdUZFMUVjc1VxVnA0UXFwSVFEbW1acE9KMWw1N0xzQWJlYUd6R200Q1VCdkJjUWpTbElrbFhlZ2ZBdnY2WnB1NHdHU2tBQTlQandzekR5SmJkbnVhU08vNWpLNjFuY1UyVUxuZWt2ZWotLTY3TU9jZklCcVR6ZXZQRy9UUCtHTmc9PQ%3D%3D--9bcb70f5196a063e7915b11622953f7935d9166c; _takvim_session=WVJmQVpEbEJUOE9KeVdLK3ZyODRpejI0dzA2aWJPd2dqOGRCaysxOU5VT3lhVkNnNzNIZytvYlpqL2FRZGhTWXZFcWxLc3JYVmoyUVRibGFuelpCczl4eFVjaFVLdkZiRURpUC84cysyUldFbC9VT295SURnMkpDQnFtdldLdUhHeDhRL3NGOG90WS9mWUI1WitocndBPT0tLVp6Mk1VS2VaT0hmMGtwcnhNcnpYMkE9PQ%3D%3D--a215e9c8f635c5c638ccdcd55105c3bdc452760e; SQLiteManager_currentLangue=2; request_method=GET; _Last_session=VnErWEFwazE3dWFBdm94WUVhMXdYWFhKVnViUmtQWUFja0xENjNDTmYxWVAxbjVyVDZrK3pxRlVjUnp0MGQ3R240UWZSK2lNcld5bDVKM2ZYb3JwMTJ1QXpPa0p1cFJOYXFXUllIakVrdnBzVHh2V09YQlZGTHNjTWtNLzFDOXNTdUhYZGNXei80enRwclZkdzBCUzZFcUtEQVFqeDFock15anMrVnVIUHRRPS0tci9lSEMvNFBtVWJ5eW1BSTQ0VUpadz09--46ddca06b8e604506dcfff84e7cc8111c7a1b679; _demo_session=TzE2bi93R2E3ZXVNSHdkZ0MvVnRCTm5BeG1VQ0luMVQrWFJ5a1FhbTc4amNCSGFNazJYTVhVYlRxbEhGejlnREowTTJDVFByYkoveHRpeEx6SlZvRnpaQ3R3UEdWaVRlK25UK29SQmx5Nmplbk9Gb1NNZ0pYdm9PQ3FLeTFKZ25CdkF4USt0clpGN1dMUkxrekxTM3lRPT0tLTZoQ1F4UHg3VExsa3JaMWRxQ1IvTFE9PQ%3D%3D--a430ecd15a69103694b06ec430875e21b66f5e62
Host:localhost:3000
Origin:http://localhost:3000
Referer:http://localhost:3000/events
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36
X-CSRF-Token:GT/gFRykJMorJu4yPLmLwwGz1EZIwlVSp8XWeO1SOvIVYITeJLAPa1YcYkJOswrkXqMUUFb/jk/DP8JtvLhQYQ==
X-Requested-With:XMLHttpRequest
Form Data
view source
view URL encoded
event_id:0
event:{"2015-05-06":{"available":"1","bind":0,"info":"","notes":"","price":"20","promo":"","status":"available"},"2015-05-07":{"available":"1","bind":0,"info":"","notes":"","price":"20","promo":"","status":"available"},"2015-05-13":{"available":"1","bind":0,"info":"","notes":"","price":"35","promo":"","status":"available"},"2015-05-14":{"available":"1","bind":0,"info":"","notes":"","price":"35","promo":"","status":"available"},"2015-05-15":{"available":"1","bind":0,"info":"","notes":"","price":"35","promo":"","status":"available"},"2015-05-16":{"available":"1","bind":0,"info":"","notes":"","price":"35","promo":"","status":"available"},"2015-05-17":{"available":"1","bind":0,"info":"","notes":"","price":"35","promo":"","status":"available"},"2015-05-20":{"available":"","bind":0,"info":"","notes":"","price":"","promo":"","status":"booked"},"2015-05-21":{"available":"","bind":0,"info":"","notes":"","price":"","promo":"","status":"booked"},"2015-05-22":{"available":"","bind":0,"info":"","notes":"","price":"","promo":"","status":"booked"},"2015-05-23":{"available":"","bind":0,"info":"","notes":"","price":"","promo":"","status":"booked"},"2015-05-24":{"available":"","bind":0,"info":"","notes":"","price":"","promo":"","status":"booked"},"2015-05-28":{"available":"1","bind":0,"info":"","notes":"","price":"25","promo":"","status":"available"},"2015-05-29":{"available":"1","bind":0,"info":"","notes":"","price":"25","promo":"","status":"available"},"2015-05-30":{"available":"1","bind":0,"info":"","notes":"","price":"25","promo":"","status":"available"},"2015-05-31":{"available":"1","bind":0,"info":"","notes":"","price":"25","promo":"","status":"available"}}
and Here is my database schema;
ActiveRecord::Schema.define(version: 20150502145455) do
create_table "events", force: :cascade do |t|
t.string "title"
t.text "description"
t.datetime "start_time"
t.datetime "end_time"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "status"
t.float "price"
t.float "promo"
t.integer "available"
t.text "info"
t.text "notes"
t.boolean "bind"
t.string "time"
t.string "p"
t.string "event_id"
end
end
EDIT 1:
Here is the Jquery request;
<script>
$(document).ready(function() {
$('#backend').DOPBackendBookingCalendarPRO({
'DataURL': '/events/load', #TO LOAD DATA
'SaveURL': '/events/save' #TO SAVE DATA
});
});
</script>
I have shared load.js.erb file as it is harcoded right now. But save.js.erb file is empty. When I created the url, rails asked me to define template. But #create action does not have a template,right?.
This calendar was written for php but I am trying to use it on RoR. The guy who wrote it, I do not know why, they defined 2 post urls one to retrieve data one to send data. Normally, one of them should be get, right?. That is why I created 2 post urls in routes.rb.Anyway. If you can help, I can send the calendar project I do not know how, but you see their backend.js file. Maybe I should change something there. Thank you
Here at the top of the backend.js file, I have ;
Schedule = {},
And there is this function in backend..js file to load data
parseCalendarData:function(){
$.post(DataURL, {event_id:ID}, function(data){
console.log(typeof data); //THIS RETURNS OBJECT
if(typeof data === 'object') {
Schedule = data;
}
else {
data = $.trim(data).replace(/\\/gi, '');
if (data != ''){
Schedule = JSON.parse(data);
}
}
methods.initCalendar();
});
},
Before save data, here is the code to set submitted data, probably the problem is there;
setData:function(){// Set submited data.
var y, m, d, noDays, key,
startDate, sYear, sMonth, sDay,
endDate, eYear, eMonth, eDay,
fromMonth, toMonth, fromDay, toDay,
availableValue = $('#DOPBCP_available').val(),
bindValue = 0,
infoValue = $('#DOPBCP_info').val().replace(/\n/gi, '<br />'),
notesValue = $('#DOPBCP_notes').val().replace(/\n/gi, '<br />'),
priceValue = $('#DOPBCP_price').val() != undefined ? $('#DOPBCP_price').val():'',
promoValue = $('#DOPBCP_promo').val() != undefined ? $('#DOPBCP_promo').val():'',
statusValue = $('#DOPBCP_status').val();
startDate = dayStartSelection < dayEndSelection ? dayStartSelection.split('_')[1]:dayEndSelection.split('_')[1];
endDate = dayStartSelection < dayEndSelection ? dayEndSelection.split('_')[1]:dayStartSelection.split('_')[1];
sYear = parseInt(startDate.split('-')[0], 10);
sMonth = parseInt(startDate.split('-')[1], 10);
sDay = parseInt(startDate.split('-')[2], 10);
eYear = parseInt(endDate.split('-')[0], 10);
eMonth = parseInt(endDate.split('-')[1], 10);
eDay = parseInt(endDate.split('-')[2], 10);
if (Schedule[methods.previousDay(startDate)] != undefined){
if (Schedule[methods.previousDay(startDate)]['bind'] == 1){
Schedule[methods.previousDay(startDate)]['bind'] = 0;
}
else if (Schedule[methods.previousDay(startDate)]['bind'] == 2){
Schedule[methods.previousDay(startDate)]['bind'] = 3;
}
}
if (Schedule[methods.nextDay(endDate)] != undefined){
if (Schedule[methods.nextDay(endDate)]['bind'] == 2){
Schedule[methods.nextDay(endDate)]['bind'] = 1;
}
else if (Schedule[methods.nextDay(endDate)]['bind'] == 3){
Schedule[methods.nextDay(endDate)]['bind'] = 0;
}
}
for (y=sYear; y<=eYear; y++){
fromMonth = 1;
if (y == sYear){
fromMonth = sMonth;
}
toMonth = 12;
if (y == eYear){
toMonth = eMonth;
}
for (m=fromMonth; m<=toMonth; m++){
noDays = new Date(y, m, 0).getDate();
fromDay = 1;
if (y == sYear && m == sMonth){
fromDay = sDay;
}
toDay = noDays;
if (y == eYear && m == eMonth){
toDay = eDay;
}
for (d=fromDay; d<=toDay; d++){
key = y+'-'+prototypes.timeLongItem(m)+'-'+prototypes.timeLongItem(d);
if ($('#DOPBCP_group').is(':checked')){
if (key == startDate){
bindValue = 1;
}
else if (key == endDate){
bindValue = 3;
}
else{
bindValue = 2;
}
}
//HERE IS THE PROBLEM I GUESS
Schedule[key] = {"available": availableValue,
"bind": bindValue,
"info": infoValue,
"notes": notesValue,
"price": priceValue,
"promo": promoValue,
"status": statusValue};
}
}
}
methods.saveData();
},
Lastly the save function in js file;
saveData:function(){// Save data.
var today = new Date(),
dd = prototypes.timeLongItem(today.getDate()),
mm = prototypes.timeLongItem(today.getMonth()+1),
yyyy = today.getFullYear();
for (var day in Schedule){
if (day < yyyy+'-'+mm+'-'+dd){
delete Schedule[day];
}
}
methods.hideForm();
methods.generateCalendar(StartYear, dayStartSelectionCurrMonth);
$.post(SaveURL, {event_id:ID, event:JSON.stringify(Schedule)}, function(data){});
},
PS: I have tried this in the events controller;
def save
respond_to do |format|
#event = Event.new(event_params)
#event.save
format.html
format.json { render :json => #event, :content_type => 'application/json' }
end
end
But content type did not change.
EDIT 2:
Sorry for giving you so much trouble but I am really trying to learn. I have changed $.post to $.ajaxand I changed the controller as you suggested. Now it try to post array and I am planing to iterate over the hash to get datas and save it. As you said I can then loop over params[:event] as it contains all the data, right?.Because of the multiple data it gives an nil error, that is why I would like to loop over them(I changed the #create as you said);
NoMethodError in EventsController#create
undefined method `new' for nil:NilClass
I think this is because Schedule; (key is the selected date)
Schedule[key] = {"date": key,
"available": availableValue,
"bind": bindValue,
"info": infoValue,
"notes": notesValue,
"price": priceValue,
"promo": promoValue,
"status": statusValue};
}
This is what comes from the Network tab;
Connection:Keep-Alive
Content-Length:94953
Content-Type:text/plain; charset=utf-8
Date:Sun, 03 May 2015 15:26:09 GMT
Server:WEBrick/1.3.1 (Ruby/2.2.1/2015-02-26)
X-Request-Id:1d59e6cd-726f-4eb0-afdf-92205c8a0974
X-Runtime:0.390420
Request Headers
view source
Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip, deflate
Accept-Language:tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Content-Length:1687
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Cookie:_Paint_session=NXN5NVUvemhXcHF2UnF1ZlBXTHhtZVVWODlyVWFoL0tEWU9vWCtOYVp6ck9EUVF6OUs0WTJYV21yWW5VeVhqdndST2JUYTdCdnNnamNzZDJjQXFLNE1CYzlQT3dtcVJiRTJLcmlwaXR3aFdKZUZBVXN4VitWamMzaWdZUUtTQU1mcWhIRFg3SnJwQ2FBK1hmclZ3WmtiODRuTUpVRUVLKzYzQTNuUGxCTURYMHhKN3Z6eUZtbURLS2MybVlNejNoTmo5MVZteUQzcG8wTnB6WUFlR2pRK2UwSHFnUTVua0hUM0VzWitLUTdya1BXVVZ1eTVKUmNCR3pQaEh2djlpcFo3dFU1WmhZYlhSWS9wYTBVSHRTYXc9PS0tbDJHT3A3NVJ1Tm5OczVIREM2emNLQT09--f2703914b69129404c69d1d9dbb446fde16c6442; _Resim_session=cXQ5cEE5ZjFPNU1Jby9ZY0lKWis2TVY1NVlaN2dVZGtSaDdzc3B1QU9saS9yNXNUUHE5N2d1Ym1CZjcxYlNNZjAwbnhPNm9oeEN0aUpNUXZYMXM1Q2ZHWHJuZ3lhdjhxRmxteVR3ZXFvNHdxMFJZcXp6NWZMbk1qWVJyZG5GVFdBWVVtRkp5MHZsZWVNTzZ1aGs4ejAxUzFoZWE2VHVlWCtuWFBpclZnQU5GR2dzeUFZZFBWeEZaSCsxWUhHNmRkQVhwYk9uWW5oVjd4VVA5L2xvRzR3amt2RmE4VTdjUzRHRHQvRC80Rys0V0ZWQ2FCTW01cWtNQ2E2SHUwYWFFQ080VVJkUEduUWxpUjNaUkZldWZsOWc9PS0tcnM2M2UyVCttTEJBbW13SVJQN3RNQT09--3233372784be95cd2d6e2630f7e5e22c17eeff17; _Carrierwave_session=cE4vek43aGlxZ3BrSFNXWU1JaDZQekcxdlVVSnRzNFRMbGNXRGxjOGhNZ0hGNHRaSFpzODQ1YU95WXVnb1VKUlM3Y0xBVFBjYW1paG5FSTh5dVVzYm1LWXNHVXd2RGJnNktaZ0YxY1JtM25Rb1hLMkhUWm5XTElZS1JUejAxSmFvcnE3VzVXTVFoYmdPbTczOE02Wjk4N2hsaTJDRFdLY2pRc1NxYVRNVFBHdkFHbmEvb1U4aEN4T2FOVDZWZGlUMnpJMU0yNkdHNW1acVpYN3NzRkxwQkN2TzRzS0ZBNlMwMGtIMGJ5bWN2QVp5Q3cxQUNIVnExajFDSldsZWhJek1zUjJxVzJyRHRsUDBjQnJsdlB1RFE9PS0tSjViSGdocSs5blRLdTNnUk1GWk9qZz09--068ded1e78c2d70739361e662b995b5470a059c4; _raddit_session=Vy9USHZBa1FodGxON1BMOWFZOHBIazk3eWVSOXh0OW9yUjRmSjJ5Tmg1OXpsMnI2Tm5ZTGQwOHdEMVJnUWhFNG5jRVZEcVhLL3ZCSVdsM1g5S2FTMjYvZWo4U1ovT0xNdkZVT2dJS0VWTkRMcXdRQ0lzTlFRWC9EZ1ZxbDRtYmtLQ1dkZzM3THltSEFvdzJTN010MHVnYytJMzE1R2F0cUVkQlh2bERBRUM4c0NVNnFsTFdmbVB1cURJeDVHUkhKOWZUOG5pUlhFZkxOSkJ3c01uU2paTW50WXRrRW8zRmJtTTBrUm5GWlJvbz0tLUh4ckZqdnA3WGQzRjdmNnAzMXdMcmc9PQ%3D%3D--622d3f9adb9afb871e118d0715921aaf986f82d2; _Cal_session=cjRlSlc0Q2FITHJJTmlTbU5TZjk5aFNFaTczTHRQZW9TR0h1MG4xYVovZG9xaUxyNittWENjbXdib3FLc1J6cXh3dHA5YitaVkZYbFVtaFI2bGxUKzJyM1kvRzJlRFV4b0JibzJ1TTdQS0ZkdVE2eVJJSlZQVXY0Q0thS1NBRzhGUzlaQ1ZRaHppYzJES2V2eXRmVWtBPT0tLWEzVWFIcWl0WnJBRVdnanJ0em1Renc9PQ%3D%3D--03a65bd6c5338108d2d3db62295843f8b3e3eaaa; _fullcalendar_and_rails_example_session=M1h0eFJieVY1ek16OWRtR1JRUGc1eCtsdFZnTDdqWkd3NndWQTFuUzFKdHJpOHlRYSsweHZ6UGZubTV1eEdJZm14NVZxbE1IUHZzRG1id2NVeENERzFlaFF3NzFNWHk0RlltbS8yNmc2aEtnYjJDcGZmY3pZWG0vU0RsU29ST005NDVpdVpYSWtNTURkM3M2RmVvZU8vUStwdUxFNk1TaVJTRDY1Z2FKcEd4QkxZMlpGeUlsUGl5UlNCVWhPNk5qR082T25lZlZ4NkIxY012MjRJaVVTKzFqdUZFMUVjc1VxVnA0UXFwSVFEbW1acE9KMWw1N0xzQWJlYUd6R200Q1VCdkJjUWpTbElrbFhlZ2ZBdnY2WnB1NHdHU2tBQTlQandzekR5SmJkbnVhU08vNWpLNjFuY1UyVUxuZWt2ZWotLTY3TU9jZklCcVR6ZXZQRy9UUCtHTmc9PQ%3D%3D--9bcb70f5196a063e7915b11622953f7935d9166c; _takvim_session=WVJmQVpEbEJUOE9KeVdLK3ZyODRpejI0dzA2aWJPd2dqOGRCaysxOU5VT3lhVkNnNzNIZytvYlpqL2FRZGhTWXZFcWxLc3JYVmoyUVRibGFuelpCczl4eFVjaFVLdkZiRURpUC84cysyUldFbC9VT295SURnMkpDQnFtdldLdUhHeDhRL3NGOG90WS9mWUI1WitocndBPT0tLVp6Mk1VS2VaT0hmMGtwcnhNcnpYMkE9PQ%3D%3D--a215e9c8f635c5c638ccdcd55105c3bdc452760e; SQLiteManager_currentLangue=2; request_method=GET; _Last_session=VnErWEFwazE3dWFBdm94WUVhMXdYWFhKVnViUmtQWUFja0xENjNDTmYxWVAxbjVyVDZrK3pxRlVjUnp0MGQ3R240UWZSK2lNcld5bDVKM2ZYb3JwMTJ1QXpPa0p1cFJOYXFXUllIakVrdnBzVHh2V09YQlZGTHNjTWtNLzFDOXNTdUhYZGNXei80enRwclZkdzBCUzZFcUtEQVFqeDFock15anMrVnVIUHRRPS0tci9lSEMvNFBtVWJ5eW1BSTQ0VUpadz09--46ddca06b8e604506dcfff84e7cc8111c7a1b679; _demo_session=R1dIMnpjMU1BTkszczFzZTR2Zmk3cFMyUVU2Yk12eEwyZ2poR2RWUzhQWEtPbEM5UkMrQXRJb21oc1grWkFNNlpVM2VwMElvMnI1YUN2UlFVeXR2KytqYzJyN3AxeS9lM3gxVlNhLysxQzNLMklQOGszdUZBaEREMGQwZVMzc09BNFVndjRZaGQzY25KZGwrR0wyME5nPT0tLUEreS9acGZTWUhzdXdOc0hrVW5WYVE9PQ%3D%3D--9a636d832705858099e968c44c959b12980b6e99
Host:localhost:3000
Origin:http://localhost:3000
Referer:http://localhost:3000/events
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36
X-CSRF-Token:BaHKI75O/nhl2kbiQDxSvJeZHiUAlTttTPGd0b/usf4J/q7ohlrV2RjgypIyNtObyIneMx6o4HAoC4nE7gTbbQ==
X-Requested-With:XMLHttpRequest
Form Data
view source
view URL encoded
event[2015-05-06][available]:1
event[2015-05-06][bind]:0
event[2015-05-06][info]:
event[2015-05-06][notes]:
event[2015-05-06][price]:20
event[2015-05-06][promo]:
event[2015-05-06][status]:available
event[2015-05-07][available]:1
event[2015-05-07][bind]:0
event[2015-05-07][info]:
event[2015-05-07][notes]:
event[2015-05-07][price]:20
event[2015-05-07][promo]:
event[2015-05-07][status]:available
event[2015-05-20][date]:2015-05-20
event[2015-05-20][available]:1
event[2015-05-20][bind]:0
event[2015-05-20][info]:
event[2015-05-20][notes]:
event[2015-05-20][price]:
event[2015-05-20][promo]:
event[2015-05-20][status]:available
event[2015-05-21][date]:2015-05-21
event[2015-05-21][available]:1
event[2015-05-21][bind]:0
event[2015-05-21][info]:
event[2015-05-21][notes]:
event[2015-05-21][price]:
event[2015-05-21][promo]:
event[2015-05-21][status]:available
event[2015-05-22][date]:2015-05-22
event[2015-05-22][available]:1
event[2015-05-22][bind]:0
event[2015-05-22][info]:
event[2015-05-22][notes]:
event[2015-05-22][price]:
event[2015-05-22][promo]:
event[2015-05-22][status]:available
event[2015-05-23][date]:2015-05-23
event[2015-05-23][available]:1
event[2015-05-23][bind]:0
event[2015-05-23][info]:
event[2015-05-23][notes]:
event[2015-05-23][price]:
event[2015-05-23][promo]:
event[2015-05-23][status]:available
EDIT 3:
h = {"event"=>{"2015-05-06"=>{"available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"20", "promo"=>"", "status"=>"available"}, "2015-05-07"=>{"available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"20", "promo"=>"", "status"=>"available"}, "2015-05-18"=>{"date"=>"2015-05-18", "available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"30", "promo"=>"", "status"=>"available"}, "2015-05-19"=>{"date"=>"2015-05-19", "available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"30", "promo"=>"", "status"=>"available"}, "2015-05-20"=>{"date"=>"2015-05-20", "available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"30", "promo"=>"", "status"=>"available"}, "2015-05-21"=>{"date"=>"2015-05-21", "available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"30", "promo"=>"", "status"=>"available"}, "2015-05-22"=>{"date"=>"2015-05-22", "available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"30", "promo"=>"", "status"=>"available"}, "2015-05-23"=>{"date"=>"2015-05-23", "available"=>"1", "bind"=>"0", "info"=>"", "notes"=>"", "price"=>"30", "promo"=>"", "status"=>"available"}}}
h.each do |key, value|
puts key
value.each do |key2, value2|
puts key2
value2.each do |k, v|
puts k
puts v
end
end
end
Something like the code works fine to get the key and values, however I do not know if that works if I put it to controller.Then I should save it to database. I surely think there must be a easier way. But I am dependent on the calendar js code that the person wrote. Sorry again asking too much. If you can guide me I appreciate. Thank you again
In my controller #create action I am able to iterate over key and values;
Now I have to figure out how to save them
def create
h = params[:event]
h.each do |key, value|
puts key
value.each do |key2, value2|
puts key2
puts value2
end
end
end

You haven't detailed how you are actually sending the JSON request but two things are going wrong:
Your save action expects a hash with the key event with:
{
event: {
title: "foo"
# more attributes ...
}
}
But you're trying to send the parameters for several dates at the same time.
event:{
"2015-05-06": {"available":"1","bind":0,"info":"","notes":"","price":"20","promo":"","status":"available"}
#... more events
}
Also you sending the request with the wrong `Content-Type
Your request is sent with:
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Not:
Content-Type: application/json; charset=utf-8;
differences in application/json and application/x-www-form-urlencoded
Added in response to edit of the question:
Use jQuery.ajax instead of jQuery.post and it will stringify the data for you - that way jQuery will take care of browsers which don't have native JSON support.
$.ajax(SaveURL, {
method: 'POST',
data: { event: Schedule },
dataType: 'json'
});
Now that you are sending the correct request type jQuery will merge the post data into params.
But this still doesn't fix the issue that Schedule contains the attributes for multiple Events. I would start by creating a simple route which accepts the parameters for a single event. In Rails conventions we call that action create not save.
class EventsController
# ...
# POST /events
def create
#event.new(params[:event])
if (#event.save)
render json: #event, status: :created
else
# ... what do we do if validation fails?
end
end
# ...
private
def create_params
params.permit(:event).allow(:available, :bind, :info, :notes, :price, :promo, :status)
end
end
You can generate a full set of restful routes for events by adding this to your config/routes.rb:
resources :events
which generates the following routes:
Prefix Verb URI Pattern Controller#Action
events GET /events(.:format) events#index
POST /events(.:format) events#create
new_event GET /events/new(.:format) events#new
edit_event GET /events/:id/edit(.:format) events#edit
event GET /events/:id(.:format) events#show
PATCH /events/:id(.:format) events#update
PUT /events/:id(.:format) events#update
DELETE /events/:id(.:format) events#destroy
At your skill level I would not bother trying to create a controller action which creates several Events at once, and I'm not going to instruct how to do it here since it would be really lengthy.

Related

How to transform nested parameters in Rails API for PATCH requests

I'm having problems trying to implement a PATCH endpoint for a Rails API which deals with complex request objects that are structurally different from the ActiveRecord model.
As an example let's say I have the following request object:
{
"details": {
"color": {
"id": 1
}
},
"name": "Hello, world!"
...
}
However, on my model I expect a flat color_id attribute:
class CreateModel < ActiveRecord::Migration[7.0]
def change
create_table :model do |t|
t.string :name, null: false
t.integer :color_id, null: false
end
end
end
Therefore I need to transform the request params. For this I've found one approach which works pretty well in case of PUT requests, but not at all for PATCH:
ActionController::Parameters.new({
color_id: params.dig(:details, :color, :id),
name: params.dig(:name)
})
If I issue a PUT request this solution works great since PUT expects the whole object as payload, PATCH on the other hand would cause issues when passing only a subset of the properties since everything else will be set to nil due to how dig works.
Assuming I have no control over the request format, how can I transform the request params in the backend so that omitted keys will not result in nil values? Of course I could imperatively handle each property line by line, checking whether the key is present in the original params and then setting it in the new one, but is there a more elegant approach?
I've found a generic solution using mapping logic with a lookup table. For the example above:
{
"details": {
"color": {
"id": 1
}
},
"name": "Hello, world!"
...
}
I would have the following mapping variable:
MAPPING = {
[:details, :color, :id] => [:color_id]
}
Then I'm able to transform the params using this recursive algorithm:
def handle(params, keys)
output = Hash.new
params.each do |k,v|
sym_keys = (keys + [k]).map &:to_sym
target_keys = MAPPING[sym_keys]
if v.is_a? ActionController::Parameters
keys << k
output = output.deep_merge! transform(v, keys)
else
if target_keys.nil?
value = sym_keys.reverse().reduce(v) { |v, k| Hash[k, v] }
else
value = target_keys.reverse().reduce(v) { |v, k| Hash[k, v] }
end
output = output.deep_merge! value
end
end
output
end
def transform(params)
output = handle(params, [])
end

Refactoring an API request on Rails

I have a rails worker using redis/sidekiq where I send some data to an API (Active Campaign), so I normally use all the http configurations to send data. I want to have it nice and clean, so it's part of a refactor thing. My worker currently looks like this:
class UpdateLeadIdWorker
include Sidekiq::Worker
BASE_URL = Rails.application.credentials.dig(:active_campaign, :url)
private_constant :BASE_URL
API_KEY = Rails.application.credentials.dig(:active_campaign, :key)
private_constant :API_KEY
def perform(ac_id, current_user_id)
lead = Lead.where(user_id: current_user_id).last
url = URI("#{BASE_URL}/api/3/contacts/#{ac_id}") #<--- need this endpoint
https = bindable_lead_client.assign(url)
pr = post_request.assign(url)
case lead.quote_type
when 'renter'
data = { contact: { fieldValues: [{ field: '5', value: lead.lead_id }] } }
when 'home'
data = { contact: { fieldValues: [{ field: '4', value: lead.lead_id }] } }
when 'auto'
data = { contact: { fieldValues: [{ field: '3', value: lead.lead_id }] } }
else
raise 'Invalid quote type'
end
pr.body = JSON.dump(data)
response = JSON.parse(https.request(pr).read_body).symbolize_keys
if response.code == '200'
Rails.logger.info "Successfully updated contact #{ac_id} with lead id #{lead.lead_id}"
else
raise "Error creating contact: #{response.body}"
end
end
def bindable_lead_client
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http
end
def post_request
post_request_ = Net::HTTP::Put.new(url)
post_request_['Accept'] = 'application/json'
post_request_['Content-Type'] = 'application/json'
post_request_['api-token'] = API_KEY
post_request_
end
end
But whenever I run this I get:
2022-07-28T00:52:08.683Z pid=24178 tid=1s1u WARN: NameError: undefined local variable or method `url' for #<UpdateLeadIdWorker:0x00007fc713442be0 #jid="e2b9ddb6d5f4b8aecffa4d8b">
Did you mean? URI
I don't want everything stuck in one method. How could I achieve to make this cleaner?
Thanks.
Pure ruby wise, The reason you get the error is because your method definition bindable_lead_client is missing the url argument. Hence undefined variable.
So def should look something like:
def bindable_lead_client (url)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http
end
and call:
bindable_lead_client(url)
As for how to make this code better, falls under question being too subjective under StackOverflow guidelines, which encourage you to ask more specific questions.

Display image on browser, where image get from the API response

I am working on sample application, where I have to show generated QR code. I am using http://goqr.me/api/ for generating the API.
Here is my sample code
class HomeController < ApplicationController
def index
end
def submit_qr
code = params[:code]
url = "https://api.qrserver.com/v1/create-qr-code/?data=#{params[:code]}&size=220x220&margin=0"
#response = HTTParty.get(url).parsed_response
end
end
When I check the call from rails console I am getting following
irb(main):004:0> url = "https://api.qrserver.com/v1/create-qr-code/?data=test&size=220x220&margin=0"
=> "https://api.qrserver.com/v1/create-qr-code/?data=test&size=220x220&margin=0"
irb(main):005:0> response = HTTParty.get(url)
=> #<HTTParty::Response:0x8efada0 parsed_response="\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\xDC\x00\x00\x00\xDC\x01\x03\x00\x00\x00!\x94]\xC2\x00\x00\x00\x06PLTE\xFF\xFF\xFF\x00\x00\x00U\xC2\xD3~\x00\x00\x01\x02IDATX\x85\xED\xD7\xBB\x11\x840\f\x04\xD0e\b\b)\xC1\xA5P\x1A\x94\xE6R(\x81\x90\x80A\x87\xFC\x05\x06\xCE\xC7\xA5\xAC\"\x8F\x1F\xD1\x0E\xB2lH\xAC\x01\xB5\x00\x8D\xA0\x8B;\x13\x88\x8A\x16Z\xB5\ff\x01\xCC\ft\xB3\xDBAO\f\xD8jRu\x88\x0F\x8A\xD5\xB6\xB3\x12\x9Fa\xCE\x96x\x13\x1F\\\xB3\x12\x9Faj\xDEmk\xD4\xE3\xED\xB2\xB3_\x8Ci\x00l\x7F\x9F\xC7\xDD\x00 \x16\xF0P\xBA\x85\xBBz+\x86\xA4j\xF1\xD0\x88\xD8fE7U\xDA\xB0\xC4\"Z\xE8i\xB6\xA0\x1F\xE1\xFF>(\xA6\xE3\x8D\xB8U\x15\xE6\xA7\x068\xC7\xAF\xC5M\x04b\tS\x8C&6n\xA3\xB4\xBA%1\xA1\xBF\xC2\xE6\xF8|\x7F\x12\x8B\x98\xCAH(\xBD~\xB8[\e\xF1t\xC3\x90\xF8\b?M\a\xE2\x17\xB4.\xD8\xF0\xC44!\xDB\xCE7/\xD1\xE1\xEE\x89\x99\xE7g\x8C\x8F\xF8\x17~\xCD\xF6\xCD\x18\a\x80m\x89?bl^\x97\xAD\xAE\xF5\x15#\xCC\x98\a\xC0!>9M\a\xE2\x15~\x00\xA9\xB7\xB6\x8EW\xEAb\xBE\x00\x00\x00\x00IEND\xAEB`\x82", #response=#<Net::HTTPOK 200 OK readbody=true>, #headers={"date"=>["Fri, 20 Mar 2020 10:33:39 GMT"], "server"=>["Apache/2"], "access-control-allow-origin"=>["*"], "access-control-allow-methods"=>["POST, GET, OPTIONS, DELETE, PUT"], "access-control-max-age"=>["7200"], "access-control-allow-headers"=>["Accept, Accept-Language, Content-Language, Content-Type, Origin, Authorization, X-Requested-With, Client-Security-Token"], "x-powered-by"=>["PHP/5.4.16"], "content-length"=>["333"], "connection"=>["close"], "content-type"=>["image/png"]}>
So how can I show the image on browser.
response = "\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\xDC\x00\x00\x00\xDC\x01\x03\x00\x00\x00!\x94]\xC2\x00\x00\x00\x06PLTE\xFF\xFF\xFF\x00\x00\x00U\xC2\xD3~\x00\x00\x01\x02IDATX\x85\xED\xD7\xBB\x11\x840\f\x04\xD0e\b\b)\xC1\xA5P\x1A\x94\xE6R(\x81\x90\x80A\x87\xFC\x05\x06\xCE\xC7\xA5\xAC\"\x8F\x1F\xD1\x0E\xB2lH\xAC\x01\xB5\x00\x8D\xA0\x8B;\x13\x88\x8A\x16Z\xB5\ff\x01\xCC\ft\xB3\xDBAO\f\xD8jRu\x88\x0F\x8A\xD5\xB6\xB3\x12\x9Fa\xCE\x96x\x13\x1F\\\xB3\x12\x9Faj\xDEmk\xD4\xE3\xED\xB2\xB3_\x8Ci\x00l\x7F\x9F\xC7\xDD\x00 \x16\xF0P\xBA\x85\xBBz+\x86\xA4j\xF1\xD0\x88\xD8fE7U\xDA\xB0\xC4\"Z\xE8i\xB6\xA0\x1F\xE1\xFF>(\xA6\xE3\x8D\xB8U\x15\xE6\xA7\x068\xC7\xAF\xC5M\x04b\tS\x8C&6n\xA3\xB4\xBA%1\xA1\xBF\xC2\xE6\xF8|\x7F\x12\x8B\x98\xCAH(\xBD~\xB8[\e\xF1t\xC3\x90\xF8\b?M\a\xE2\x17\xB4.\xD8\xF0\xC44!\xDB\xCE7/\xD1\xE1\xEE\x89\x99\xE7g\x8C\x8F\xF8\x17~\xCD\xF6\xCD\x18\a\x80m\x89?bl^\x97\xAD\xAE\xF5\x15#\xCC\x98\a\xC0!>9M\a\xE2\x15~\x00\xA9\xB7\xB6\x8EW\xEAb\xBE\x00\x00\x00\x00IEND\xAEB`\x82"
decoded_image = Base64.strict_encode64(response)
base64_img = "data:image/png;base64," + decoded_image
Something like this

Rails 4 unpermitted parameters - JSON data [duplicate]

This question already has answers here:
strong parameters permit all attributes for nested attributes
(6 answers)
Closed 7 years ago.
I'm working on an AngularJS and Rails app that uses Google's definition API. The API returns a JSON response.
I would like to store the JSON response in my Words table. I'm issuing the API request through my Angular controller, then I'm trying to add the data to my Words table.
I'm getting an "Unpermitted parameters" error when I try to insert the JSON data. I've researched and this seems to be due to Rails 4 nested params.
I've tried adding nested brackets to my word_params method in Words controller but then get "Unpermitted parameters: dataType, numToShow, groupNumber, groupResult, sectionType, dictionary"... which is part of the API response.
Is there an easier way to permit the full JSON data without having to go through and specifically define each nested param in the controller's word_params method?
scheme.rb
create_table "words", force: :cascade do |t|
t.string "title"
t.json "full_data"
t.integer "list_id"
t.datetime "date"
end
Word controller
class WordsController < ApplicationController
respond_to :json
def create
list = List.find(params[:list_id])
word = list.words.create(word_params)
respond_with list, word
end
private
def word_params
params.require(:word).permit(:title, :date, :full_data => {})
end
end
app.js
//= require angular-rails-templates
//= require_tree .
angular.module('d-angular', ['ui.router', 'templates'])
// Set routing/configuration
// ------------------------------
.config(['$stateProvider', '$urlRouterProvider',
// Set state providers
function($stateProvider, $urlRouterProvider) {$stateProvider
// Home state
.state('home', {
url: '/home',
templateUrl: 'home.html',
controller: 'MainCtrl',
resolve: {
listPromise: ['lists', function(lists){
return lists.getAll();
}]
}
})
// Lists state
.state('lists', {
url: '/lists/{id}',
templateUrl: 'list.html',
controller: 'ListsCtrl',
resolve: {
list: ['$stateParams', 'lists', function($stateParams, lists) {
return lists.get($stateParams.id);
}]
}
})
$urlRouterProvider.otherwise('home');
}
])
// lists factory
// Factories are used to organize and share code across the app.
// ------------------------------
.factory('lists', ['$http',
function($http){
// create new obect with array of lists
var o = { lists: [] };
// get all lists
o.getAll = function() {
return $http.get('/lists.json').success(function(data){
angular.copy(data, o.lists);
});
};
// get specific list
o.get = function(id) {
return $http.get('/lists/' + id + '.json').then(function(res){
return res.data;
});
};
// create list
o.create = function(post) {
return $http.post('/lists.json', post).success(function(data){
o.lists.push(data);
});
};
// add word to list
o.addWord = function(id, word) {
return $http.post('/lists/' + id + '/words.json', word);
};
return o;
}
])
// Lists controller
// ------------------------------
.controller('ListsCtrl', ['$scope', 'lists', 'list', '$http',
// Main scope (used in views)
function($scope, lists, list, $http){
$scope.list = list; // get list by ID
// Add word function
$scope.addWord = function(){
// API URL
var api_url = "https://www.googleapis.com/scribe/v1/research?key=AIzaSyDqVYORLCUXxSv7zneerIgC2UYMnxvPeqQ&dataset=dictionary&dictionaryLanguage=en&query=";
// get data from API
$http.get(api_url + $scope.title)
// handle successful api request
.success(function (response) {
// push new word to array
lists.addWord(list.id, {
title: $scope.title,
date: new Date().toJSON().slice(0,10),
full_data: response
})
.success(function(word) {
$scope.list.words.push(word);
});
});
};
}
]);
Console response
Started POST "/lists/1/words.json" for ::1 at 2015-05-08 19:53:11 -0400
Processing by WordsController#create as JSON
Parameters: {"title"=>"fallacy", "date"=>"2015-05-08", "full_data"=>{"dataType"=>"dictionary", "numToShow"=>1, "groupNumber"=>0, "groupResult"=>{"query"=>"fallacy", "displayName"=>"<b>fal·la·cy</b>", "dataset"=>{"dataset"=>"dictionary"}, "score"=>1}, "sectionType"=>"dictionary", "dictionary"=>{"word"=>"fallacy", "dictionaryType"=>"STANDARD", "definitionData"=>[{"word"=>"fallacy", "pos"=>"Noun", "meanings"=>[{"meaning"=>"a mistaken belief, especially one based on unsound argument.", "examples"=>["the notion that the camera never lies is a fallacy"], "synonyms"=>[{"nym"=>"misconception", "nymResult"=>{"query"=>"misconception", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"misbelief", "nymResult"=>{"query"=>"misbelief", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"delusion", "nymResult"=>{"query"=>"delusion", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"mistaken impression"}, {"nym"=>"error", "nymResult"=>{"query"=>"error", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"misapprehension", "nymResult"=>{"query"=>"misapprehension", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"misinterpretation"}, {"nym"=>"misconstruction", "nymResult"=>{"query"=>"misconstruction", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"mistake", "nymResult"=>{"query"=>"mistake", "dataset"=>{"dataset"=>"dictionary"}}}], "submeanings"=>[{"meaning"=>"a failure in reasoning that renders an argument invalid."}, {"meaning"=>"faulty reasoning; misleading or unsound argument.", "examples"=>["the potential for fallacy which lies behind the notion of self-esteem"]}]}], "phoneticText"=>"ˈfaləsē", "wordForms"=>[{"word"=>"fallacy", "form"=>"noun"}, {"word"=>"fallacies", "form"=>"plural noun"}]}]}}, "list_id"=>"1", "word"=>{"title"=>"fallacy", "full_data"=>{"dataType"=>"dictionary", "numToShow"=>1, "groupNumber"=>0, "groupResult"=>{"query"=>"fallacy", "displayName"=>"<b>fal·la·cy</b>", "dataset"=>{"dataset"=>"dictionary"}, "score"=>1}, "sectionType"=>"dictionary", "dictionary"=>{"word"=>"fallacy", "dictionaryType"=>"STANDARD", "definitionData"=>[{"word"=>"fallacy", "pos"=>"Noun", "meanings"=>[{"meaning"=>"a mistaken belief, especially one based on unsound argument.", "examples"=>["the notion that the camera never lies is a fallacy"], "synonyms"=>[{"nym"=>"misconception", "nymResult"=>{"query"=>"misconception", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"misbelief", "nymResult"=>{"query"=>"misbelief", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"delusion", "nymResult"=>{"query"=>"delusion", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"mistaken impression"}, {"nym"=>"error", "nymResult"=>{"query"=>"error", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"misapprehension", "nymResult"=>{"query"=>"misapprehension", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"misinterpretation"}, {"nym"=>"misconstruction", "nymResult"=>{"query"=>"misconstruction", "dataset"=>{"dataset"=>"dictionary"}}}, {"nym"=>"mistake", "nymResult"=>{"query"=>"mistake", "dataset"=>{"dataset"=>"dictionary"}}}], "submeanings"=>[{"meaning"=>"a failure in reasoning that renders an argument invalid."}, {"meaning"=>"faulty reasoning; misleading or unsound argument.", "examples"=>["the potential for fallacy which lies behind the notion of self-esteem"]}]}], "phoneticText"=>"ˈfaləsē", "wordForms"=>[{"word"=>"fallacy", "form"=>"noun"}, {"word"=>"fallacies", "form"=>"plural noun"}]}]}}, "date"=>"2015-05-08"}}
List Load (0.1ms) SELECT "lists".* FROM "lists" WHERE "lists"."id" = $1 LIMIT 1 [["id", 1]]
Unpermitted parameters: dataType, numToShow, groupNumber, groupResult, sectionType, dictionary
(0.1ms) BEGIN
Word Exists (0.2ms) SELECT 1 AS one FROM "words" WHERE ("words"."title" = 'fallacy' AND "words"."title" = 'fallacy') LIMIT 1
SQL (0.2ms) INSERT INTO "words" ("title", "date", "full_data", "list_id") VALUES ($1, $2, $3, $4) RETURNING "id" [["title", "fallacy"], ["date", "2015-05-08 00:00:00.000000"], ["full_data", "{}"], ["list_id", 1]]
(0.7ms) COMMIT
Completed 201 Created in 8ms (Views: 0.4ms | ActiveRecord: 1.4ms)
Note that if you use permit in a key that points to a hash, it won't allow all the hash. You also need to specify which attributes inside the hash should be whitelisted.
http://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit
Rails does not really provide a straight-forward way to blindly whitelist a nested parameter hash.
You could do something hackey like:
params.require(:word).permit(:full_data).tap do |whitelisted|
whitelisted[:full_data] = params[:word][:full_data]
end

without template, need to render not return json

I'm trying to make code from a Sinatra app work in the Rails context. The Sinatra app uses ajax requests to trigger the Sinatra routes/controller actions. For example, if you trigger the new function on a javascript model
new: function() {
var _this = this;
$.ajax({
url: "/gamestart",
type: "POST",
....
It will trigger the route/controller code in the Sinatra app
post "/new" do
end
When I tried to make this work in Rails, I'm getting a 500 internal server error. In my Rails app, the new_game button triggers an ajax request to a Rails route which triggers a controller action, and that controller action uses the Rails model to get data from the database. For some reason that doesn't seem like the right way to do it in Rails, and I'm wondering if it's the reason I'm getting the server error
GET http://localhost:3000/gamestart 500 (Internal Server Error)
If possible, can you tell me where in the chain of actions outlined below that error is arising and what I might do to fix it.
1 Click on the new game button triggers 'startNewGame' method
'click #new_game': 'startNewGame',
2 The startNewGame method calls method on Game model
startNewGame: function() {
this.model.new();
},
3 The new method in the Game model makes a GET request to the url '/gamestart'. I also tried a post request. I don't know why it would need to be a post request, but neither worked. (In the original Sinatra application, the gamestart url led immediately into the function post '/gamestart' do...)
new: function() {
var _this = this;
$.ajax({
url: "/gamestart",
type: "GET", \\\ also tried POST
success: function(response) {
var json = $.parseJSON(response);
_this.set({lost: false});
_this.set({win: false});
_this.trigger("gameStartedEvent", json);
}
})
},
4 I directed the url to a controller action in Rails router file
match 'gamestart' => 'locations#gamestart', :via => :get
Note, in the original Sinatra application, the route and the controller action were combined
5 The gamestart method of the locations_controller.rb
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
6 The get_random method on the word model Word.rb, which is called from locations controller
def get_random
words = []
locations = Location.all (this pulls up the names of the locations from the db)
locations.each do |e|
words << e.name
end
words.sample
end
ERROR MESSAGE
GET http://localhost:3000/gamestart 500 (Internal Server Error) jquery.js:8215
XHR finished loading: "http://localhost:3000/gamestart". jquery.js:8215
send jquery.js:8215
jQuery.extend.ajax jquery.js:7767
window.Game.Backbone.Model.extend game.js:27
window.OptionsView.Backbone.View.extend.startNewGame optionsView.js:14
jQuery.event.dispatch jquery.js:3062
elemData.handle.eventHandle
Note, in the original Sinatra application, the route and the controller action were combined in the usual Sinatra way
post "/gamestart" do
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
UPDATE
The 500 error seemed to be triggered by a missing template. This method in locations controller wasn't rendering anything. It didn't have a view file. I therefore changed the controller to make it respond_to :json and then use respond_with at the end of the action, but that triggered a 406 error.
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
became now triggers 406 error
respond_to :json
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
plainvariable = {:word => masquerade_word}.to_json ###changed
respond_with plainvariable ###changed
end
You say that your gamestart controller method is causing a server error due to a missing template. If we look at that controller method:
def gamestart
#...
{:word => masquerade_word}.to_json
end
we see that it returns a JSON string but it neglects to render anything. You don't call any rendering or redirection methods so Rails helpfully (ha ha) assumes that you want to render the gamestart view template; but, you have no such thing so you get an error.
You should render your JSON, not return it; something more like this:
def gamestart
#...
render :json => { :word => masquerade_word }
end

Resources