Rails 5.1.: cannot get JSON object from string - ruby-on-rails

I'm a bit stuck with JSON parsing as I'm new to coding and this is my first time dealing with JSON :) I kind of figured out how to save JSON, but can't read it back from DB.
I have this Controller:
class Common::DatatablesStatesController < ApplicationController
require 'json'
def update
datatables_state.update(datatable_states_params)
render json: datatables_state
end
def show
table_state = current_user.datatables_states.where(name: params[:id]).select(:state)
state = JSON.parse(table_state)
render json: { state: state["state"] }
end
private
def datatable_states_params
params['common_datatable_state']['state'] = params['common_datatable_state']['state'].to_json
params.require(:common_datatable_state).permit(:user_id, :name, :state)
end
def datatables_state
#datatables_state ||= current_user.datatables_states.where(name: params[:id]).first_or_create
end
end
In terminal I see my Update action is working, however I cannot make Show action to work as it throws out this error:
TypeError (no implicit conversion of Common::DatatableState::ActiveRecord_AssociationRelation into String):
app/controllers/common/datatables_states_controller.rb:12:in `show'
app/controllers/application_controller.rb:41:in `set_current_user'
In my Pry I can do this:
[3] pry(main)> user.datatables_states.where(name: 'campaigns_index').select(:state)
Common::DatatableState Load (0.5ms) SELECT "common_datatable_states"."state" FROM "common_datatable_states" WHERE "common_datatable_states"."user_id" = $1 AND "common_datatable_states"."name" = $2 [["user_id", 2], ["name", "campaigns_index"]]
=> [#<Common::DatatableState:0x000000028e3f30
id: nil,
state:
"{\"time\":\"1494910215056\",\"start\":\"0\",\"length\":\"10\",\"order\":{\"0\":[\"0\",\"asc\"]},\"search\":{\"search\":\"\",\"smart\":\"true\",\"regex\":\"false\",\"caseInsensitive\":\"true\"},\"columns\":{\"0\":{\"visible\":\"true\",\"search\":{\"search\":\"\",\"smart\":\"true\",\"regex\":\"false\",\"caseInsensitive\":\"true\"}},\"1\":{\"visible\":\"true\",\"search\":{\"search\":\"\",\"smart\":\"true\",\"regex\":\"false\",\"caseInsensitive\":\"true\"}},\"2\":{\"visible\":\"true\",\"search\":{\"search\":\"\",\"smart\":\"true\",\"regex\":\"false\",\"caseInsensitive\":\"true\"}}}}">]
How do I get single JSON object from string I have in state field (JSON column, Postgres) from my DB, please? Thank you!
Update
This piece of code from application_controller.rb helped to access current_user outside Controllers:
#38 around_action :set_current_user
#39 def set_current_user
#40 Current.user = current_user
#41 yield
ensure
Current.user = nil
end
Update 2
After I updated show to this:
def show
table_state = current_user.datatables_states.where(name: params[:id]).select(:state).first
state = JSON.parse(table_state.state)
render json: { state: state }
end
I still don't see it loading my state drom DB with stateLoadCallback from JS below:
jQuery(document).ready(function() {
var user_id = $("#data-table").attr('data-user-id');
var contname = $("#data-table").attr('data-controller-name');
$('#data-table').DataTable(
{
"processing": true,
"serverSide": true,
"ajax": $('#data-table').data('source'),
stateSave: true,
"stateSaveCallback": function (settings, data) {
// Send an Ajax request to the server with the state object
$.ajax( {
"url": "/common/datatables_states/"+contname+".json",
"data": {"common_datatable_state":{"user_id": user_id, "name": contname, "state": data}} ,
"dataType": "json",
"type": "PATCH",
"success": function () {}
} );
},
stateLoadCallback: function (settings, callback) {
$.ajax( {
url: '/common/datatables_states/'+contname+'.json',
async: false,
dataType: 'json',
type: 'GET',
success: function (json) {
callback( json );
}
} );
}
}
);
});

The problem is that table_state is an ActiveRecord::Relation of Common::DatatableState as you point in your example. You are trying to parse the relation which is not a string.
What about this:
table_state = current_user.datatables_states.where(name: params[:id]).select(:state).first
state = JSON.parse(table_state.state)
render json: { state: state }

Related

Tying in the PayPal smart buttons with a Ruby on Rails form_for and controller/method?

I have the smart buttons "working" in sandbox but I can't think of any way to attach the smart buttons success to the order form which creates the order. With Stripe Elements, it's pretty plug and play because it's on the page and a part of the form itself, but with PayPal with the redirects, I can't seem to think of a way.
Does this require javascript or can I do this without it, aside from what's already there?
Form:
<%= form_for(#order, url: listing_orders_path([#listing, #listing_video]), html: {id: "payment_form-4"} ) do |form| %>
<%= form.label :name, "Your Name", class: "form-label" %>
<%= form.text_field :name, class: "form-control", required: true, placeholder: "John" %>
#stripe code here (not important)
<%= form.submit %>
<div id="paypal-button-container"></div>
<!-- Include the PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=sb&currency=USD"></script>
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: <%= #listing.listing_video.price %>
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name + '!');
});
}
}).render('#paypal-button-container');
</script>
Create Method in Controller:
require 'paypal-checkout-sdk'
client_id = Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_id)
client_secret = Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_secret)
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
#amount_paypal = (#listing.listing_video.price || #listing.listing_tweet.price)
request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
request.request_body(
{
intent: 'AUTHORIZE',
purchase_units: [
{
amount: {
currency_code: 'USD',
value: "#{#amount_paypal}"
}
}
]
}
)
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
puts order
#order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers['debug_id']
end
How can I tie in the PayPal smart buttons with the form so once the payment is completed, it creates an order if successful?
UPDATE:::::::
Created a PaypalPayments controller and model:
controller:
def create
#paypal_payment = PaypalPayment.new
#listing = Listing.find_by(params[:listing_id])
require 'paypal-checkout-sdk'
client_id = "#{Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_id)}"
client_secret = "#{Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_secret)}"
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
#amount_paypal = #listing.listing_video.price
request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
#paypal_payment = request.request_body({
intent: "AUTHORIZE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "#{#amount_paypal}"
}
}
]
})
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
puts order
# #order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
# if #paypal_payment.create
# render json: {success: true}
# else
# render json: {success: false}
# end
end
Javascript in view:
paypal.Buttons({
createOrder: function() {
return fetch('/paypal_payments', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.orderID;
});
},
onApprove: function(data) {
return fetch('/orders', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID
})
}).then(function(res) {
return res.json();
}).then(function(details) {
alert('Authorization created for ' + details.payer_given_name);
});
},
}).render('#paypal-button-container');
With this, the paypal box appears but then goes away right after it loads with this in the CMD:
#<OpenStruct id="1Pxxxxxxx394U", links=[#<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1P0xxxxxxx394U", rel="self", method="GET">, #<OpenStruct href="https://www.sandbox.paypal.com/checkoutnow?token=1P07xxxxxxx94U", rel="approve", method="GET">, #<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1Pxxxxxxx4U", rel="update", method="PATCH">, #<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1P07xxxxxxx394U/authorize", rel="authorize", method="POST">], status="CREATED">
No template found for PaypalPaymentsController#create, rendering head :no_content
Completed 204 No Content in 2335ms (ActiveRecord: 15.8ms)
I have not used smart buttons. However, you should not have "a ton more code" in a create action. If you are following MVC and rails conventions. It would seem that you need a seperate controller action to handle the payment authorization separately from the create action. But if you can get to this point in your javascript, here is example of how you would send the data from paypal javascript back to your controller, this will need some work but hopefully it points you in the right direction:
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name + '!');
// here is where you should send info to your controller action via ajax.
$.ajax({
type: "POST",
url: "/orders",
data: data,
success: function(data) {
alert(data); // or whatever you wanna do here
return false;
},
error: function(data) {
alert(data); // or something else
return false;
}
});
});
}
This is most likely far too late, but ill add what worked for me.
You need to return the response ID to the PayPal script as a json object. All you need to do is update your create function like so :
...
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
render json: { orderID: order.id }
# #order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
...

param is missing or the value is empty for: quotes

I have a POST request from a javascript file below:
this.submitQuoteButton = $("<button />")
.text("Download PDF")
.addClass("submitQuoteButton button-success pure-button")
.click(function() {
$.ajax({
type: "POST",
url: "../quotes/create",
data: {
name : "John ",
email: "john#john.com",
json: "data",
uid: "uid",
},
dataType:'text',
success: function(data,status,xhr){
console.log(status);
alert("SUCCESS!");
},
error: function(xhr,status,error){
console.log(status,error);
alert("ERROR!");
}
});
})
This POST calls to my quotes_controller create method where I have this
def create
#quote = Quote.new(quote_params)
if #quote.save
redirect_to root_url
else
redirect_to blog_path
end
end
private
def quote_params
params.require(:quotes).permit(:uid, :name, :email, :json)
end
The aim is to get the data passed in the POST request and save it to my database with the create method. Am I doing this right? I am getting a:
param is missing or the value is empty for: quotes
Does this mean there is a problem with my database set up or the create method?
quote_params require the quotes key in your params. So your ajax call data should look like this:
data: {
quotes: {
name : "John ",
email: "john#john.com",
json: "data",
uid: "uid",
}
}

Angularjs: post data to rails server by service

I want send data from angularjs to rails server. For this, I have an angularjs service that I use GET,POST,DELETE,UPDATE method. I can use GET method, but for other method I cannot use, beacause I have to sent parameter to server, but I cannot do this.
record.js:
var app = angular.module('app');
app.controller('RecordCtrl',['$scope','Session','Records', function($scope, Session, Records){
$scope.records = Records.index();
}]);
recordService.js:
'use strict';
var app = angular.module('recordService', ['ngResource']);
//angular.module('recordService', ['ngResource'])
app.factory('Records', function($resource) {
return $resource('/api/record.json', {}, {
index: { method: 'GET', isArray: true},
create: { method: 'POST' }
});
})
.factory('Secure', function($resource){
return $resource('/api/record/:record_id.json', {}, {
show: { method: 'GET' },
update: { method: 'PUT' },
destroy: { method: 'DELETE' }
});
});
and I get data in rails server by below code:
class Api::V1::RecordController < Api::V1::BaseController
def index
respond_with(Record.all)
end
def show
#data = Record.find(params[:id]).to_json()
respond_with(#data)
end
def update
#data = Record.find(params[:id])
respond_to do |format|
if #data.update_attributes(record_params)
format.json { head :no_content }
else
format.json { render json: #data.errors, status: :unprocessable_entity }
end
end
end
def create
#data = Record.create(record_params)
#data.save
respond_with(#data)
end
def destroy
#data = Record.find(params[:id])
#data.destroy
respond_to do |format|
format.json { head :ok }
end
end
private
def record_params
params.require(:record).permit(:name)
end
end
I don't know how can I send method from angularjs controller to rails server. I try below code, but I don't successful:
Records.create(function() {
//"name" is the name of record column.
return {name: test3};
});
but I get below error in rails server:
Started POST "/api/record.json" for 127.0.0.1 at 2014-08-30 17:55:27 +0430
Processing by Api::V1::RecordController#create as JSON
How can I fix this problem? How can I send parameter to rails server?
I want send delete method to rails server. I know I have to send record.id to server, I use below type:
//type 1
var maskhare = { record_id: 4};
Secure.destroy(function(){
return maskhare.json;
});
//type 2
Secure.destroy(4);
but I get below error in server:
Started DELETE "/api/record" for 127.0.0.1 at 2014-08-30 19:01:21 +0430
ActionController::RoutingError (No route matches [DELETE] "/api/record"):
I fix correct url in recordService.js, but I don't know why request is send to before url again. Where is the problem?
It looks like you are successfully making a request, the last line there says that a POST request was made and went to the right controller and action.
The problem is strong parameters. You need to add name to the filtered parameters list.
private
def record_params
params.require(:record).permit(:secure, :name)
end
Also rails expects the parameters in the following format: { record: {name: 'something"} }
To fix your second problem
I would try to follow this recipe
Replace your code with this:
app.factory("Secure", function($resource) {
return $resource("/api/record/:id", { id: "#id" },
{
'show': { method: 'GET', isArray: false },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
}
);
});
and then
Secure.destroy({id: 4});
Keep in mind that if you add respond_to :json in your controller then you can omit the .json in the URLs. Like so:
class Api::V1::RecordController < Api::V1::BaseController
respond_to :json
...
end

Ruby on rails Ajax call showing 404 error

I am making and ajax call to hit the controller but it is showing the 404 error:
My controller method is like:
def get_user_time
if(params[:user])
#user_time_checks = UserTimeCheck.where(:user_id => params[:user])
end
end
And my route for this is like:
post "user_time_checks/get_user_time"
And my ajax script is like:
function get_user_time(id) {
var user_id = id;
if(user_id != ''){
$.ajax({
url:"get_user_time?user="+user_id,
type:"POST",
success: function(time){
console.log(time);
},error: function(xhr,response){
console.log("Error code is "+xhr.status+" and the error is "+response);
}
});
}
}
Try this:
$.ajax({
url:"user_time_checks/get_user_time",
type:"POST",
data: {
user: user_id
},
success: function(time){
console.log(time);
},error: function(xhr,response){
console.log("Error code is "+xhr.status+" and the error is "+response);
}
});
Also make sure you really need to do POST method and that rails route does not require specific paramater like :user_id. Basically check the output from
rake routes | grep get_user_time
Your route should be:
post "user_time_checks/get_user_time" => "user_time_checks#get_user_time"
Also, since the purpose of the request is to get some data, you should make it a GET request instead. So:
function get_user_time(id) {
var user_id = id;
if(user_id != ''){
$.get("get_user_time",
{user: user_id})
.success(function(time) {
console.log(time);
})
.error(function(xhr,response){
console.log("Error code is "+xhr.status+" and the error is "+response);
});
}
}
Lastly, maybe you should tell the controller to be able to repond_to json:
def get_user_time
if(params[:user])
#user_time_checks = UserTimeCheck.where(:user_id => params[:user])
respond_to do |format|
format.html # The .html response
format.json { render :json => #user_time_checks }
end
end
end

Sencha Touch RestProxy with Rails example needed

I've got the following model
Ext.regModel("Entries", {
fields: [
{name: "id", type: "int"},
{name: "title", type: "string"},
{name: "bought", type: "boolean"},
],
proxy: {
type: 'rest',
url: '/lists',
format: 'json',
reader: {
type: 'json'
}
}
});
Then I've got a list, which is populated from this model
...
store: new Ext.data.Store({
model: "Entries",
autoLoad: true,
remoteFilter: true
}),
...
The list is populated correctly. But when I try to perform the following
listeners: {
itemswipe: function (record, index, item, e) {
var el = Ext.get(item);
el.toggleCls("crossedOut");
var store = record.getStore();
var rec = store.getAt(index);
if (el.hasCls('crossedOut')) {
rec.set('bought', true);
rec.save({
success: function() {
console.log("Saved!");
}
});
} else {
console.log('not crossed out');
rec.set('bought', false);
rec.save({
success: function() {
console.log("Saved!");
}
});
}
}
}
when swipe event is fired, I've got the following error
Uncaught TypeError: Cannot read property 'data' of undefined gsencha-touch.js:6
(anonymous function) sencha-touch.js:6
Ext.data.Connection.Ext.extend.onCompletesencha-touch.js:6
Ext.data.Connection.Ext.extend.onStateChangesencha-touch.js:6
(anonymous function)
I understand, that there is some problem with the payload I return, but I can't find an example of the correct one, and all my guesses do not work.
In backend I return the following
format.json {render :json => {:data => #list, :success=> true, :id => #list.id}}
I'm using the ExtJS 4 preview, but it should work the same with Sencha Touch. Your problem might be related to the nesting of the returned json. Here's what works for me.
In the Rails controller:
def index
respond_with #entries = Entry.all do |format|
format.json { render :json => {:success => true, :data => #entries, :total => #entries.count} }
end
end
def show
respond_with #entry = Entry.find(params[:id]) do |format|
format.json { render :json => {:success => true, :data => [#entry]} }
end
end
def create
respond_with #entry = Entry.create(params[:records][0]) do |format|
format.json { render :json => {:success => true, :data => [#entry]} }
end
end
def update
#entry = Entry.find(params[:id])
#entry.update_attributes(params[:records][0])
respond_with #entry do |format|
format.json { render :json => {:success => true, :data => [#entry]} }
end
end
ExtJS model:
Ext.regModel("Entries", {
fields: [
{name: "id", type: "int"},
{name: "title", type: "string"},
{name: "bought", type: "boolean"},
],
proxy: {
type: 'rest',
url: '/lists',
format: 'json',
reader: {
type: 'json',
root: 'data',
record: 'entry'
}
}
});
Two differences with what you've done:
1/ The record option of the reader tells ExtJS to look for nested records in the json. It tells it to look for:
data: [
{
entry: {
id: 1,
title: "Title 1",
bought: true
}
}
]
instead of:
data: [
{
id: 1,
title: "Title 1",
bought: true
}
]
An alternative to setting the record property on the reader would be to disable nested json in Rails, by dumping this into your application config:
config.active_record.include_root_in_json = true
2/ When returning a single record, the json output should be the same as a collection, except you only have one record.
The Sencha Touch and ExtJS 4 docs can be a bit sparse sometimes, I found dissecting the examples is the best way to learn.
HTH
I ran into a similar problem when submitting a single record instead of a store. Setting a writer object with the name of your record as rootProperty solves the issue.
writer: {
type : 'json',
rootProperty : 'your_record'
}

Resources