Serialize Object like a form in as3 - actionscript-3

I am looking to transmit a post request through HTTP, the body needs to contain information from a ValueObject I've already created. Essentially I need to serialize the object (which has multiple dimensions). The problem I'm running into is that I don't want to transmit the data as JSON or XML but as a multidimensional array. It works in HTML forms like:
data:
Array
(
[a] => Array
(
[a] => Array
(
[1] => abcd
)
[b] => defg
)
[d] => ghij
)
request body (decoded):
a[a][1]=abcd&a[b]=defg&d=ghij
Is there any library out there that does this kind of serialization already? I'm using as3httpclientlib, so I don't have the convenience of the mx.rpc family. I've spent the past several hours trying to roll my own solution, but I'm pretty sure I'd be better off with a solution someone has put more time and thought into.

You can try AMF to solve your problem (you have different library according to your programming language).
The main advantage is that you can do mapping object to object (using Value Object) and preserve the data type (like arrays).
I'm using the plugin amf with Symfony and here is an example of data returned :
SabreAMF_TypedObject Object
(
[amfClassName:private] => Product
[amfData:private] => Array
(
[code] => 1970A007
[designation] => 1970 PELL A007 BLANC
[description] => Vernis extra souple extra brillant
[sommeil] =>
[name] => 1970
[base] => A Stretch Enduit
[composition] => 43% PL 57% PU
[laize] => 137-140 cm
[weight] => 588 g/ml
[dechirure] => 10 daN/9 daN
[abrasion] => 5000
[xenotest] => 4/5
[non_feu] => Non
[Color] => SabreAMF_TypedObject Object
(
[amfClassName:private] => Color
[amfData:private] => Array
(
[id] => 124
[name] => BLANC
)
)
Some explanation :
[amfClassName:private] => Product
My Object is typed. As3 side i have a Product Value Object too wich contains array properties like "code", "designation", "description",... and Color (another typed Array with Value Object Color)
So you can easily have a multidimensional structure arrays...

Ok,
so here is something more simple using URLVariables :
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
public class URLVariableExample extends Sprite {
private static const URL_SERVER:String = "http://localhost/testURL.php";
private var loader:URLLoader;
public function URLVariableExample() {
init();
}
private function init() : void {
var request:URLRequest = new URLRequest(URL_SERVER);
var urlVar:URLVariables = new URLVariables();
var a : Array = ["toto", "titi", "loulou"];
urlVar["urlTest[a][a]"] = a.toString();
urlVar["urlTest[a][b]"] = "desc";
urlVar["urlTest[d]"] = "defg";
request.data = urlVar;
request.method = URLRequestMethod.POST;
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, loaderComplete);
loader.load(request);
}
private function loaderComplete(event : Event) : void {
loader.removeEventListener(Event.COMPLETE, loaderComplete);
trace(URLLoader(event.currentTarget).data);
}
}
}
I requested a php file and just put a simple var_dump function.
Trace return :
array(2) {["d"]=>string(4) "defg"["a"]=> array(2) {["a"]=>string(16) "toto,titi,loulou["b"]=>string(4) "desc"}}

var url:String = 'http://localhost/dump.php';
var params:Object = {
test: 'ok',
nested_1: {
nested_2: {
nested_3: {
nested_4: {
hello: 'mf',
str: '~!##$%^&*()_+'
}
}
}
},
};
var request:URLRequest = new URLRequest(url);
var variables:URLVariables = new URLVariables();
parameters = fixParameters(parameters || {});
for (var key:String in parameters) {
variables[key] = parameters[key];
}
request.data = variables;
var loader:URLLoader = new URLLoader();
loader.load(request);
and here is fixParameters method
private function fixParameters(data:Object, parameters:Object = null, prefixes:Array = null):Object {
var setPrefix:Array;
var prefixKey:String;
if (!parameters) {
parameters = {};
}
if (!prefixes) {
prefixes = [];
}
for (var key:String in data) {
setPrefix = prefixes.concat([key]);
if (typeof(data[key]) == 'object') {
parameters = fixParameters(data[key], parameters, setPrefix);
} else {
prefixKey = '';
for (var i:Number = 0; i < setPrefix.length; i++) {
prefixKey += i == 0 ? setPrefix[i] : '[' + setPrefix[i] + ']';
}
parameters[prefixKey] = data[key];
}
}
return parameters;
}

Related

Pushing complete data from Spring Boot server through Web Socket to Client

I have a spring boot server and i am able to generate a streaming table on client side by sending json one after the another. The problem is if a user logs in say after 10 minutes, he is only able to access data starting from 10th minute i.e he is not able to access data from 0 to 10th minute. What i want is to push the data from 0th to 10th minute first and at the same time continue the streaming process. How can this be done? I am using jquery datatable to generate the table.
I am attaching the controller and client side html for reference
1) Controller
#Controller
public class ScheduledUpdatesOnTopic {
#Autowired
private SimpMessagingTemplate template;
int count=0;
#Scheduled(fixedDelay=500)
public void trigger() {
DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
String str[] = {"name"+count,""+Math.round(Math.random()*100),"India"+Math.round(Math.random()*100),df.format(date)};
this.template.convertAndSend("/topic/message",str);
++count;
}
}
2) Client HTML
var _self = this;
$(document).ready(function() {
var message ;
$('#tbl').DataTable( {
data: message,
"aLengthMenu" : [[25,50,75,-1],[25,50,75,"All"]],
"pageLength" :25,
columns: [
{ title: "Name" },
{ title: "Age" },
{ title: "Country" },
{ title: "Date"}
]
});
subscribeSocket();
});
function addRow(message){
var table = $('#tbl').DataTable();
if(table && message ){
table.row.add(message).draw();
}
}
function subscribeSocket(){
var socket = new SockJS('/gs-guide-websocket');
var stompClient = Stomp.over(socket);
stompClient.connect({ }, function(frame) {
stompClient.subscribe("/topic/message", function(data) {
message = JSON.parse(data.body);
_self.addRow(message);
});
});
};
If you don't save previous sent datas, you can't send them back to new customers.
On the front side, you have to subscribe to an "history" resource and make a call to get it.
Front:
function subscribeSocket() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
var firstCounterReceived = null;
stompClient.connect({}, function (frame) {
setConnected(true);
stompClient.subscribe('/topic/history', function (response) {
console.log(JSON.parse(response.body));
});
stompClient.subscribe('/topic/message', function (response) {
var message = JSON.parse(response.body);
if (firstCounterReceived == null) {
firstCounterReceived = message[0];
console.log(`Calling history endpoint with ${firstCounterReceived}`);
stompClient.send("/app/topic/history", {}, message[0]);
}
console.log(message);
});
});
}
Back:
#Controller
#EnableScheduling
public class ScheduledUpdatesOnTopic {
Map<Integer, String[]> history = new LinkedHashMap<>();
#Autowired
private SimpMessagingTemplate template;
private Integer count = 0;
#MessageMapping("/topic/history")
#SendTo("/topic/history")
public List<String[]> history(Integer to) {
return history.keySet()
.stream()
.filter(counter -> counter < to)
.map(counter -> history.get(counter))
.collect(Collectors.toList());
}
#Scheduled(fixedRate = 500)
public void sendMessage() {
DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
String[] str = {count.toString(), "name"+count,""+Math.round(Math.random()*100),"India"+Math.round(Math.random()*100),df.format(date)};
history.put(count, str);
this.template.convertAndSend("/topic/message",str);
++count;
}
}
In this sample, saved datas are stored in a map, be aware that it will consume some memory at some point.

Redux, Fetch and where to use .map

Consider this scenario:
app loads => fetches json from api => needs to modify json returned
In this case, I'm using moment to make some date modifications and do some grouping that I'll use in the UI. I looked on stack and found a similar question but didn't feel like it provided the clarity I am seeking.
Where should I use .map to create the new objects that contain the formatted & grouped dates? Should I manipulate the raw json in the api call or in the redux action before I dispatch? What is the best practice?
Is it OK to add properties and mutate the object as I am showing below,
service["mStartDate"] = mStartDate before I put the data into my store and treat it as immutable state?
First Approach - changing raw json in the api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
})
}
}
2nd Approach, make a simple api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
}
}
Change the json in the action before dispatching
export function getDataByID() {
return (dispatch, getState) => {
dispatch(dataLookupRequest())
const state = getState()
const groupedData = {}
return TicketRepository.getDataByID(userData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
return groupedData
})
.then(groupedData => {
dispatch(lookupSuccess(groupedData))
})
.catch(err => dispatch(dataLookupFailure(err.code, err.message)))
}
}
All data manipulation should be handled by your reducer. That is, the returned response data should be passed on to a reducer. This practice is common, because this way if there's a problem with your data, you will always know where to look - reducer. So neither of your approaches is "correct". Actions should just take some input and dispatch an object (no data manipulation).
When you want to manipulate data for 'view' purposes only, consider using reselect library, which makes it easier to handle "data views" that are composed of the existing data.

Detect when bind occurs in mvvmcross

Hi i need to intercept when bind occur in mvvmcross project.
I have my MvxCollectionViewCell that i bind:
public ProjectsCollectionCell (IntPtr handle)
: base (string.Empty, handle)
{
this.DelayBind(() => {
var set = this.CreateBindingSet<ProjectsCollectionCell, ViewItem>();
set.Bind (lblTitle).To (prj => prj.MnemonicId);
set.Bind (lblDescription).To (prj => prj.Description);
set.Bind(imgPhoto).For (s => s.Image).WithConversion("ImageArray").To(prj => prj.Image);
set.Apply();
if (imgPhoto.Image != null) {
this.imgPhoto.Layer.RasterizationScale = UIScreen.MainScreen.Scale;
this.imgPhoto.Layer.ShouldRasterize = true;
this.imgPhoto.Layer.BorderWidth = 10;
this.imgPhoto.Layer.BorderColor = UIColor.White.CGColor;
this.imgPhoto.Layer.CornerRadius = 8f;
this.imgPhoto.Layer.MasksToBounds = true;
this.imgPhoto.Layer.Position = new PointF(imgPhoto.Frame.Left - 80, imgPhoto.Frame.Bottom);
this.imgPhoto.Transform = CGAffineTransform.MakeRotation(-0.05f);
};
});
}
i want to intercept when the content of the 'imgPhoto' change.
Is there an event to subscribe?
Could you suggest me how to do this?
If you need to detect when the Image on your cell's DataContext changes, then one way to do this is to add the property to your cell and to bind that property to your DataContext - e.g.
private byte[] _bytes;
public byte[] Bytes
{
get { return _bytes; }
set
{
_bytes = value;
// your code here...
}
}
public ProjectsCollectionCell (IntPtr handle)
: base (string.Empty, handle)
{
this.DelayBind(() => {
var set = this.CreateBindingSet<ProjectsCollectionCell, ViewItem>();
set.Bind(_hook).For(h => h.CurrentSource);
set.Bind (lblTitle).To (prj => prj.MnemonicId);
set.Bind (lblDescription).To (prj => prj.Description);
set.Bind(this).For(s => s.Bytes).WithConversion("ImageArray").To(prj => prj.Image);
set.Apply();
// etc
});
}
As an alternative, you might also want to consider subclassing whatever type imgPhoto is and providing a new property on that object. For an example of this approach, see the AnimatingText properties in http://slodge.blogspot.co.uk/2013/07/n33-animating-data-bound-text-changes.html

Upload to s3 directly from Flash 403 issue

there are quite a few ways to upload to S3 from flash. I am trying to implement code from the following example on Amazon's site http://aws.amazon.com/code/1092?_encoding=UTF8&jiveRedirect=1
Some posts that I ran across indicated that a lot of the fields from Amazon are now Requiered and unless you fill them in you will get this dreaded 403 error.
I have tried several things and I am hoping there will be a solution soon. I used the following libs from here http://code.google.com/p/as3awss3lib/.
Here is my class that handles all the uploading
package com.myemma.s3uploader.main.controllers
{
import jp.classmethod.aws.core.AWSEvent;
import s3.flash.S3PostOptions;
import s3.flash.S3PostRequest;
import utils.PolicyGenerator;
import com.myemma.s3uploader.main.model.MainDM;
import com.myemma.s3uploader.settings.Settings;
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.external.ExternalInterface;
import flash.net.FileReference;
/**
* #author Matthew Sloan Wallace - http://mattwallace.me
*/
public class UploadFilesAction extends EventDispatcher
{
[Inject]
public var dm : MainDM;
private var service : S3PostRequest;
[Init]
public function onInit() : void
{
if (ExternalInterface.available)
ExternalInterface.addCallback( "uploadFiles", uploadFiles );
}
private function uploadFiles() : void
{
if (dm.selectedFiles)
upload();
}
private function upload() : void
{
if (dm.selectedFiles.length > 0)
{
var fileReference : FileReference = dm.selectedFiles[0];
// var s3:AWSS3 = new AWSS3(Settings.accessKey, Settings.secret_key);
// s3.saveObject("mattwallace", fileReference.name, "image/png", fileReference);
// s3.addEventListener("objectSaved", onObjectSaved);
var policy : PolicyGenerator = PolicyGenerator.getInstance( Settings.accessKey, Settings.secret_key );
var s3Options : S3PostOptions = new S3PostOptions();
s3Options.secure = false;
s3Options.acl = "private";
s3Options.contentType = "image/png";
s3Options.filename = fileReference.name;
s3Options.success_action_status = "201";
s3Options.policy = policy.policy;
s3Options.signature = policy.signature;
service = new S3PostRequest( Settings.accessKey, Settings.bucket, Settings.secret_key, s3Options );
service.addEventListener( Event.OPEN, function( event : Event ) : void
{
trace( "Uploading..." );
trace( "Upload started: " + fileReference.name );
} );
service.addEventListener( ProgressEvent.PROGRESS, function( event : ProgressEvent ) : void
{
trace( Math.floor( event.bytesLoaded / event.bytesTotal * 100 ) );
} );
service.addEventListener( IOErrorEvent.IO_ERROR, function( event : IOErrorEvent ) : void
{
trace( "Upload error!" );
trace( "An IO error occurred: " + event );
} );
service.addEventListener( SecurityErrorEvent.SECURITY_ERROR, function( event : SecurityErrorEvent ) : void
{
trace( "Upload error!" );
trace( "A security error occurred: " + event );
} );
service.addEventListener( DataEvent.UPLOAD_COMPLETE_DATA, function( event : Event ) : void
{
trace( "Upload complete!" );
trace( "Upload completed: " + event );
dm.selectedFiles.splice( 0, 1 );
} );
try
{
service.upload( fileReference );
}
catch(e : Error)
{
trace( "Upload error!" );
trace( "An error occurred: " + e );
}
}
}
}
}
I'd worked with s3 and I didn't used special libs for that (maybe except generating policy and signature). Try something like the following:
var policyURL = "..."; //something like "http://domain.s3.amazonaws.com/crossdomain.xml"
flash.security.Security.loadPolicyFile(policyURL);
fileReference.addEventListener(Event.SELECT, selectHandler);
fileReference.browse();
function selectHandler(e: Event) {
fileReference = event.target;
s3options = new flash.net.URLVariables();
s3Options.secure = false;
s3Options.acl = "private";
s3Options.contentType = "image/png";
s3Options.filename = fileReference.name;
s3Options.success_action_status = "201";
s3Options.policy = policy;
s3Options.signature = signature;
uploadURL = new flash.net.URLRequest();
uploadURL.data = s3Options;
fileReference.upload(uploadURL, "file", false);
}
I managed to get my code to work, the difficulty is to get the right policy. I'm using elctech's library to generate the s3 upload call.
import com.elctech.*;
public class Uploader{
private var _options : S3UploadOptions = new S3UploadOptions();
// ...
public function uploadPDF(tempFile:File):void{
_options.FileSize = tempFile.size.toString();
_options.FileName = getFileName(tempFile);
_options.ContentType = 'application/pdf';
_options.key = certificate['key'] + _options.FileName;
_options.policy = certificate['policy'];
_options.signature = certificate['signature'];
_options.bucket = certificate['bucket'];
_options.AWSAccessKeyId = certificate['accessKey'];
_options.acl = certificate['acl'];
_options.Expires = certificate['expiration'];
_options.Secure = 'false';
_options.successactionstatus = certificate['sas'];
var request:S3UploadRequest = new S3UploadRequest(_options);
configureUploadListeners(request);
try {
request.upload(tempFile);
} catch(e:Error) {
trace("An error occurred: " + e);
}
}
private function getFileName(file:FileReference):String {
var fileName:String = file.name.replace(/^.*(\\|\/)/gi, '').replace(/[^A-Za-z0-9\.\-]/gi, '_');
return fileName;
}
}
I get the amazon s3 certificate from our main Rails application via our API, here's what the policy generation looks like:
def certificate
bucket = ENV['S3_BUCKET']
access_key = ENV['S3_KEY']
secret = ENV['S3_SECRET']
key = "temp/"
expiration = 10.hours.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z')
max_filesize = 500.megabytes
acl = 'public-read'
sas = '201'
policy = Base64.encode64(
"{'expiration': '#{expiration}',
'conditions': [
{'bucket': '#{bucket}'},
['starts-with', '$key', '#{key}'],
{'acl': '#{acl}'},
{'Content-Type': 'application/pdf'},
{'Content-Disposition': 'attachment'},
['starts-with', '$Filename', ''],
['eq', '$success_action_status', '201']
]}
").gsub(/\n|\r/, '')
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), secret, policy)).gsub(/\n| |\r/, '')
{ :access_key => access_key, :key => key, :policy => policy, :signature => signature, :sas => sas, :bucket => bucket, :acl => acl, :expiration => expiration }
end

scala swing: draggable / resizable component trait

I'm looking for a scala trait that I can mix in to a scala.swing.Component that will allow that component to be positioned and resized using mouse input.
Ideally it would add little boxes as "handles" to indicate to the user that the component can be resized:
I feel like this is a fairly common task, and there should be some traits out there that support it.
I'm using these in my current project. You probably need to replace the Vector library with your own and add implicit defs. Or use Point/Dimension from swing. The Components need to be in a panel which allows custom positions and sizes, like NullPanel from http://dgronau.wordpress.com/2010/08/28/eine-frage-des-layouts/
trait Movable extends Component{
var dragstart:Vec2i = null
listenTo(mouse.clicks, mouse.moves)
reactions += {
case e:MouseDragged =>
if( dragstart != null )
peer.setLocation(location - dragstart + e.point)
case e:MousePressed =>
this match {
case component:Resizable =>
if( component.resizestart == null )
dragstart = e.point
case _ =>
dragstart = e.point
}
case e:MouseReleased =>
dragstart = null
}
}
trait Resizable extends Component{
var resizestart:Vec2i = null
var oldsize = Vec2i(0)
def resized(delta:Vec2i) {}
listenTo(mouse.clicks, mouse.moves)
reactions += {
case e:MouseDragged =>
if( resizestart != null ){
val delta = e.point - resizestart
peer.setSize(max(oldsize + delta, minimumSize))
oldsize += delta
resizestart += delta
resized(delta)
revalidate
}
case e:MousePressed =>
if( size.width - e.point.x < 15
&& size.height - e.point.y < 15 ){
resizestart = e.point
oldsize = size
this match {
case component:Movable =>
component.dragstart = null
case _ =>
}
}
case e:MouseReleased =>
resizestart = null
}
}
The only Component I know of that is Dragable and resizeable is the InternalFrame on the JDesktop. here is an example:
import swing._
import event._
import javax.swing.{UIManager,JComponent}
import javax.swing.KeyStroke.getKeyStroke
import java.awt.{Graphics2D,Graphics}
object InternalFrameDemo extends SimpleSwingApplication{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
val top = new MainFrame{
title = "InternalFrameDemo"
preferredSize = new Dimension(640,480)
val desktop = new javax.swing.JDesktopPane
val jc = new JComponent{
override def paint(g:Graphics){
import g._
drawLine(0,0,20,20)
drawLine(0,20,20,0)
println("yay draw")
}
setVisible(true)
}
desktop add jc
contents = Component.wrap(desktop)
menuBar = new MenuBar{
contents += new Menu("Document"){
mnemonic = Key.D
contents += new MenuItem("New"){
mnemonic = Key.N
action = new Action("new"){
def apply = createFrame
accelerator = Some(getKeyStroke("alt N"))
}
}
contents += new MenuItem("Quit"){
mnemonic = Key.Q
action = new Action("quit"){
def apply(){
quit()
}
accelerator = Some(getKeyStroke("alt Q"))
}
}
}
}
def createFrame{
val newFrame = MyInternalFrame()
newFrame setVisible true
desktop add newFrame
newFrame setSelected true
}
}
}
import javax.swing.{JDesktopPane,JInternalFrame}
import collection.mutable.ArrayBuffer
object MyInternalFrame{
var openFrameCount = 0;
val xOffset, yOffset = 30;
def apply() = {
openFrameCount += 1
val jframe = new javax.swing.JInternalFrame("Document #" + openFrameCount,true,true,true,true)
jframe.setSize(300,300)
jframe.setLocation(xOffset*openFrameCount,yOffset*openFrameCount)
jframe //Component.wrap(jframe)
}
}
but both JInternalFrame and JDesktop are not integrated in the scala swing package and need to be wrapped manually.