Trigger action in Rails controller on model callback - ruby-on-rails-4

I am trying to wrap my head around Live Streaming with Server-Sent Events in Rails. I have a Rake task listening for file changes which adds records to the database. Once added I would like to send a SSE to the frontend.
But, the model can't send events to the frontend, the controller is responsible for that. How do I tell my controller a new record was added to the database?
My (broken) solution so far: use an EventBus with an after_save callback in the model that announces the changes and asks the controller to listen for these messages:
require 'reloader/sse'
class SseController < ApplicationController
include ActionController::Live
def index
response.headers['Content-Type'] = 'text/event-stream'
sse = Reloader::SSE.new(response.stream)
EventBus.subscribe(:added) do |payload|
sse.write({ /* payload */ })
end
rescue IOError
ensure
sse.close
end
end
I think my request ends before the event is received meaning it will never end up in de subscribe block. Is this the right approach, if so, what am I missing?

SOLN 1: you can use rest_client gem to send a request to your controller in your after_save callback from model.
SOLN 2: Why dont you call a model method in your controller which creates the database records and then handles further action based on whether record was created or not

Related

How to get PayPal client-side info to Django?

I am using PayPal standard IPN payment solution in client side in my Django web app.
<body>
<!-- Set up a container element for the button -->
<div id="paypal-button-container"></div>
<!-- Include the PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=test&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: '88.44'
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
// Successful capture! For demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
});
}
}).render('#paypal-button-container');
</script>
</body>
everything works fine and I can access all the data through the details variable in the js code.
Now, i need to insert the details into django db, no api, simple model.
Tried many things, none worked.
I prefer not to use django-paypal because it doesn't have smart buttons (as far as i saw) and there is only option for "buy now button" and no credit / debit card.
how can it be done? or is there smart buttons for django-paypal package?
Thanks for the help!
How to get PayPal client-side info to Django?
Don't.
An integration that creates and captures payments with client-side JS functions is for very simple use cases. It should never be used if you need to do anything automated with the result, such as writing transaction results to a database.
Instead, API-based integrations exist for precisely this use case. Use the v2/checkout/orders API and make two routes (url paths) on your server, one for 'Create Order' and one for 'Capture Order'. You could use the Checkout-PHP-SDK for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call. Both of these routes should return/output only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as reserving product or sending an email) immediately before forwarding return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options, add a body parameter to the fetch with a value that is a JSON string or object)

Change the edit url, dynamically using the Datatable Editor

I'm looking on how to update the ajax configuration dynamically using data from a resource when updating a record. Django REST expects the id at the end of the url and the request method must be type PUT
I've spent some time figuring out how to update the ajax request made by the Datatable Editor plugin. I'm using Django Rest as the backend. This might be useful for some people looking for a similar answer.
Technically you can update the ajax options if the editor object before it sends the request by using the preSubmit Event.
editor.on('preSubmit', (e, request,) =>{
let _url = new URL(window.location.origin + "/" + editor.ajax().url)
if(request.action == 'edit'){
editor.ajax().url = `${_url.protocol}//${_url.host}/api/v1/some-endpoint/${Object.keys(request.data)[0]}/${_url.search}`;
editor.ajax().type = 'PUT'
}
editor.ajax().data = request.data[Object.keys(request.data)]
})
This will update the ajax configuration of the edit request right before it get sent. Django Rest expects a PUT request and the id of the record to be added at the end of the URL. As you can see we grab the id from the data object (Its the first key of the request.data object), and we can also change the type of request to PUT.

Ember.js - Updating a model via Websocket Stream

I want to keep my market model updated via the websocket stream.
I have a platform model that has many markets.
When the user first requests the model, it is retrieved from the backend database. I then want to update with the websocket data.
How do I update different values in the model? I can't figure out how to filter the hasmany relationship by market name then set the values. Maybe there's an easier way to go about it that I'm not seeing.
It's actually pretty simple - just make sure you have these things setup:
you'll want your websocket to send json data to ember, using the same format of json (json:ap for example)
when you establish your websocket connection on the ember side of things, you'll want an event handler for handling received messages.
that event handler will use store.pushPayload to add/update the model in the store (which means your websocket code needs access to the store).
an example:
// some controller.js
import Controller from '#ember/controller';
import { action } from 'ember-decorators/object';
import myAwesomeWebSocketStuff from 'lib/websocket';
export default class extends Controller {
init() {
const socket = myAwesomeWebSocketStuff(this.store);
this.set('socket', socket');
}
willDestroy() {
this.get('socket').disconnect();
}
}
and then in lib/websocket.js
import SomeWebSocketLibrary from 'some-library';
export default function(store) {
const socket = new SomeWebSocketLibrary(url);
socket.connect();
socket.on('receive', data => store.pushPayload(data));
return socket;
}

How to handle race-condition in ember-data when live-polling filtered array and saving records

I have an Ember Route that polls for new records every 5 seconds.
Here is the Route's model function:
model: ->
#store.filter "event", "status": "created", (instance) =>
instance.get("status") == "created"
Here is the Route's polling mechanism:
setupController: (controller, model) ->
#_super(controller, model)
#startPolling() unless #get('polling')
startPolling: ->
#set('polling', true)
Ember.run.later #, ->
#poll()
, 5000
poll: ->
return unless #get('polling')
#model().then =>
#poller = Ember.run.later #, ->
#poll()
, 5000
This functionality works fine except in one scenario.
A user may alter the state attribute of an Event model.
For example, a user can choose to hide an Event instance, which calls the following code:
changeStatusAndDisplayFlash: (event, status) ->
event.set('status', status)
event.save().then (event) =>
#queueFlashMessage(event)
The problem is that if the Events poller is currently making a request and the individual record save promise returns before the poller's request completes, the record will have its state reverted when the poller's request finally does complete.
This is a classic race-condition, however, I am unsure of how to handle this in ember-data.
In short, I'm trying to figure out how to:
Honor the state of a model based on the most recently triggered request rather than the most recently completed request.
So if we have the following requests (in order of when they were initiated):
Original model request which returns the filtered collection.
Polling request that updates the filtered collection (with new models and/or updated attributes to existing models).
Save request that updates the attributes of a single model in the collection.
And the order of completion is such:
1, 3, 2
I would like the outcome of request #3 to be the final result, however, as it stands, the last request to complete (which is currently #2) is the one that sets the outcome.
Does anyone know of a strategy for achieving this in ember/ember-data?
Thank you for your time.

Ember not saving records when using DS.RESTAdapter

I'm trying to create a new object via the DS.RESTAdapter , but I don't see any activity on the backend (via console or in the db). There's no errors output in the console (using chrome). Reading data via index and show works fine. In my route.rb I have
resource #posts, only: [:index, :show, :new, :create]
After I fill out a form and click submit this line gets called (trying static data to start with)
this.get('store').createRecord('post', {name: 'asdf'});
In the ember inspector in chrome I see under "Data" the new records being created without the ID. I notice the new method in my backend post controller isn't called so is there another step I have to do for the adapter to attempt a REST PUT?
Thanks!
createRecord only creates the record in the store.
To persist that record you need to call save() on it.
// Puts record into store
var newPost = this.get('store').createRecord('post', {name: 'asdf'});
// Persists it
newPost.save();
See: http://emberjs.com/guides/models/persisting-records/