Jump to content
david_navigator

Stream in controls at run time created by REST Debugger

Recommended Posts

The delphi RESTDebugger has an option "Copy Components", that puts something like this on the clipboard.

 

object RESTClient1: TRESTClient
  BaseURL = 
    'https://api-eu1.XXXXXXXXXXXXXXX/ORD'
  Params = <>
end
object RESTRequest1: TRESTRequest
  AssignedValues = [rvConnectTimeout, rvReadTimeout]
  Client = RESTClient1
  Method = rmPOST
  Params = <
    item
      Kind = pkHTTPHEADER
      Name = 'x-api-compleat-key'
      Value = '0aXXXXXXXXXXXXXXXXXXXXXX94'
    end
    item
      Kind = pkREQUESTBODY
      Name = 'body9BE6CF603159471FB026D7FF6FC3D2DB'
      Value = 
        '{"master_id":1,"LayoutID":"d967cc4c-5b2b-4474-8cac-a71f9684ee70"' +
        ',"TransactionStatus":"SAV","Reference":1,"PoNumber":"ORD1","Date' +
        'Created":"2008-07-08","SupplierName":"TMB","CurrencyCode":"GBP",' +
        '"SupplierID":"1234","LineItems":[{"master_id":1,"Description":"G' +
        'obo Original","UnitCost":"100.00","Net":"100.00","Quantity":1,"T' +
        'ax":0,"Gross":100},{"master_id":1,"Description":"Gobo Copy","Uni' +
        'tCost":"50.00","Net":"100.00","Quantity":2,"Tax":0,"Gross":100}]' +
        '}'
      ContentType = ctAPPLICATION_JSON
    end>
  Response = RESTResponse1
end
object RESTResponse1: TRESTResponse
end

I can take this and paste it on to a form and Delphi will create all the components etc.
However I don't want to paste it in to a form at design time, I want to store it and then use the data to create a populated form at run time.
I haven't got a problem reading and writing this to an external storage format nor creating the form at run time, but how to I then use this to create the components ?
Google suggest using TReader, but unless I'm looking at the wrong example, that only seem to handle one component, not a whole load that reference each other.

 

Does any one have any code to help or can tell me what key words I need to be searching for ?

Many thanks

Share this post


Link to post
Guest

I would suggest scripting from my past work with such need.

 

In some cases, i design a from or frame and after i am happy with i use CnWizard "Convert Selected Components to Creating Code"

image.thumb.png.928d1f0cbc43cae7b95e93e161b065ab.png

 

Then adjust (tweak) that code into a script, and send it to the client that requested custom UI to fit his need.

To do that you need scripting library, my scripting library is TMS scripter that expired since 2011 (i think) along with my subscription, until this day i am still using it and satisfied with it, it does work nice.

 

 

Anyway, the point is to think about the flexibility by using scripting, because this will give you the ability to code not just create, you can add custom handling or what every the sky is the limit, some clients just love it and have their fun adjusting stuff as they fit, of course it comes with little extra work on your side but that depends on you and how far you want to go, or simply what is your need.

 

Share this post


Link to post

Any component can be created in code at runtime. Here's the code for your posted set of components:

var
  RESTClient1: TRESTClient;
  RESTRequest1: TRESTRequest;
  RESTResponse1: TRESTResponse;
begin
  RESTClient1 := TRESTClient.Create(Self);

  RESTClient1.Name := 'RESTClient1';
  RESTClient1.BaseURL := 'https://api-eu1.XXXXXXXXXXXXXXX/ORD';
  RESTClient1.Params := <>;


  RESTRequest1 := TRESTRequest.Create(Self);

  RESTRequest1.Name := 'RESTRequest1';
  RESTRequest1.AssignedValues := [rvConnectTimeout, rvReadTimeout];
  RESTRequest1.Client := RESTClient1;
  RESTRequest1.Method := rmPOST;
  with RESTRequest1.Params.Add do begin
    Kind := pkHTTPHEADER;
    Name := 'x-api-compleat-key';
    Value := '0aXXXXXXXXXXXXXXXXXXXXXX94';
  end;
  with RESTRequest1.Params.Add do begin
    Kind := pkREQUESTBODY;
    Name := 'body9BE6CF603159471FB026D7FF6FC3D2DB';
    Value := ;
    ContentType := ctAPPLICATION_JSON;
  end;
  RESTRequest1.Response := RESTResponse1;

  RESTResponse1 := TRESTResponse.Create(Self);

  RESTResponse1.Name := 'RESTResponse1';
end;

The GExperts IDE plugin has a very easy way to generate this. After it's installed, simply right+click on a component and select "Components to Code" and the code to generate the component is copied to the clipboard. Then just paste them into your code editor.  It does one component at a time so I had to combine the three RESTxxx components into the VAR section after each one but it's really as simple as that.

  • Like 1

Share this post


Link to post
11 minutes ago, corneliusdavid said:

Any component can be created in code at runtime. Here's the code for your posted set of components:

Thanks but maybe I didn't explain properly. I know how to create components etc at run time and I use the Gexpert tool frequently, but in this particular scenario I need to go from the code created by the RestDebugger to a working set of components, BUT all done at run time.

So effectively I need to do what delphi does when you click Paste on a delphi form and it streams in the code from my OP.
 

 

Share this post


Link to post

Pasting components to the form is essentially creating the components and hooking them to the form. How does this code not do exactly that? What else are you looking for?

Share this post


Link to post
21 minutes ago, corneliusdavid said:

Pasting components to the form is essentially creating the components and hooking them to the form. How does this code not do exactly that? What else are you looking for?

Because I have to start at run time with what the RestDebugger produces.

Imagine the end user presses Copy Components in the Rest Debugger, then goes to my app and clicks "Paste Components" - I need the mechanism that effectively goes from 
 

object RESTClient1: TRESTClient
  BaseURL = 
    'https://api-eu1.XXXXXXXXXXXXXXX/ORD'
  Params = <>
end

to

 RESTClient1 := TRESTClient.Create(Self);

 RESTClient1.BaseURL := 'https://api-eu1.XXXXXXXXXXXXXXX/ORD';

 

Share this post


Link to post

Or do you want the user to actually use the REST Debugger, click the Copy Components button, and go to your running application and do the paste while it's running?  So you want to convert the pasted objects to "create" statements--is that what you're after?

Share this post


Link to post

I have hacked together some code that may help here. Create a new VCL Forms Application, drop a button and a memo onto the form and use the following code:

const
  cArr: TArray<string> = [                         //
    'object RESTClient1: TRESTClient',             //
    '  BaseURL = ',                                //
    '    ''https://api-eu1.XXXXXXXXXXXXXXX/ORD''', //
    '  Params = <>',                               //
    'end',                                         //
    'object RESTRequest1: TRESTRequest',           //
    '  AssignedValues = [rvConnectTimeout, rvReadTimeout]', //
    '  Client = RESTClient1',                       //
    '  Method = rmPOST',                            //
    '  Params = <',                                 //
    '    item',                                     //
    '      Kind = pkHTTPHEADER',                    //
    '      Name = ''x-api-compleat-key''',          //
    '      Value = ''0aXXXXXXXXXXXXXXXXXXXXXX94''', //
    '    end',                                      //
    '    item',                                     //
    '      Kind = pkREQUESTBODY',                   //
    '      Name = ''body9BE6CF603159471FB026D7FF6FC3D2DB''', //
    '      Value = ', //
    '        ''{"master_id":1,"LayoutID":"d967cc4c-5b2b-4474-8cac-a71f9684ee70"'' +', //
    '        '',"TransactionStatus":"SAV","Reference":1,"PoNumber":"ORD1","Date'' +', //
    '        ''Created":"2008-07-08","SupplierName":"TMB","CurrencyCode":"GBP",'' +', //
    '        ''"SupplierID":"1234","LineItems":[{"master_id":1,"Description":"G'' +', //
    '        ''obo Original","UnitCost":"100.00","Net":"100.00","Quantity":1,"T'' +', //
    '        ''ax":0,"Gross":100},{"master_id":1,"Description":"Gobo Copy","Uni'' +', //
    '        ''tCost":"50.00","Net":"100.00","Quantity":2,"Tax":0,"Gross":100}]'' +', //
    '        ''}''',                          //
    '      ContentType = ctAPPLICATION_JSON', //
    '    end>',                               //
    '  Response = RESTResponse1',             //
    'end',                                    //
    'object RESTResponse1: TRESTResponse',    //
    'end',                                    //
    ''];

procedure TForm649.Button1Click(Sender: TObject);
var
  binary: TMemoryStream;
  cmp: TComponent;
  lst: TStringList;
  stream: TMemoryStream;
begin
  RegisterClasses([TRESTClient, TRESTRequest, TRESTResponse]);
  lst := TStringList.Create;
  try
    lst.Add('object myName: TComponent'); // name doesn't really matter
    lst.AddStrings(cArr);
    lst.Add('end');
    stream := TMemoryStream.Create;
    try
      lst.SaveToStream(stream);
      stream.Position := 0;
      binary := TMemoryStream.Create;
      try
        ObjectTextToBinary(stream, binary);
        binary.Position := 0;
        binary.ReadComponent(Self); // Self puts the code into the form itself, but any other component will do
      finally
        binary.Free;
      end;
    finally
      stream.Free;
    end;
  finally
    lst.Free;
  end;

  cmp := FindComponent('RESTRequest1');
  if cmp is TRESTRequest then begin
    Memo1.Lines.Add(TRESTRequest(cmp).Params[1].Value);
  end;

end;

 

Share this post


Link to post
11 minutes ago, corneliusdavid said:

Or do you want the user to actually use the REST Debugger, click the Copy Components button, and go to your running application and do the paste while it's running?  So you want to convert the pasted objects to "create" statements--is that what you're after?

Exactly that.

Share this post


Link to post

So what I would suggest is either try out Uwe's code above or look at the GExperts code (it's open source) to figure out how they do it. Also, since you're specifically wanting to use the three components generated by the REST Debugger, you could compare the components it creates with the values in the paste buffer and do a little parsing to set the values for those specific components.

Share this post


Link to post
Quote

I have hacked together some code that may help here. Create a new VCL Forms Application, drop a button and a memo onto the form and use the following code:

Many Thanks. That's exactly what I was after. 
 

Share this post


Link to post
8 minutes ago, corneliusdavid said:

So what I would suggest is either try out Uwe's code above

Yup, That seems to do what IO want, well it certainly gives me enough information to be able to do what I want.

Thanks for your help too though.

Share this post


Link to post

I would like to add some candy to the code above. If you add three published fields to the form which names and types matches the ones in the clipboard text (which are predictable when created with the Rest Debugger), you can access the imported components by these fields:

type
  TForm649 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
  public
  published
    RESTClient1: TRESTClient;
    RESTRequest1: TRESTRequest;
    RESTResponse1: TRESTResponse;
  end;
procedure TForm649.Button1Click(Sender: TObject);
var
  binary: TMemoryStream;
  cmp: TComponent;
  lst: TStringList;
  stream: TMemoryStream;
begin
  { clear any existing instances }
  FreeAndNil(RESTClient1);
  FreeAndNil(RESTRequest1);
  FreeAndNil(RESTResponse1);
  lst := TStringList.Create;
  try
    lst.Add('object myName: TComponent');  // this resembles the instance given as parameter to ReadComponent
    lst.AddStrings(cArr);
    lst.Add('end');
    stream := TMemoryStream.Create;
    try
      lst.SaveToStream(stream);
      stream.Position := 0;
      binary := TMemoryStream.Create;
      try
        ObjectTextToBinary(stream, binary);
        binary.Position := 0;
        binary.ReadComponent(Self); // Self puts the code into the form itself, but any other component will do
      finally
        binary.Free;
      end;
    finally
      stream.Free;
    end;
  finally
    lst.Free;
  end;

  Memo1.Lines.Add(RESTRequest1.Params[1].Value);
end;

You can even omit the RegisterClasses call then.

 

The FreeAndNil of the three fields guarantees fresh components being created. It is as well possible to keep existing instances, but that can end up having properties with non default values when these are not included in the stream. (That may even be desired in some cases.)

Edited by Uwe Raabe
  • Like 1
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×