Jump to content

David Schwartz

Members
  • Content Count

    1237
  • Joined

  • Last visited

  • Days Won

    25

Everything posted by David Schwartz

  1. David Schwartz

    Error when installing JCL from GetIt

    I get these periodically. I just close Delphi and reopen it.
  2. David Schwartz

    Detect user location from ip address

    Both my T-Mobile and AT&T LTE data accounts terminate at servers in the Los Angeles area. I'm in Phoenix, but could be anywhere in a very large radius (hundreds of miles). When I watch TV or surf the net using these connections, I get stuff for the LA area b/c they think that's where I am. No, that's just where the IPs terminate. It's effectively working somewhat like a VPN. If you're using WiFi and moving around, then you can use the location of nearby WiFi routers to provide fairly accurate location info as long as they're connected to an ISP that uses static IPs. But with DHCP, location data could be off by a mile or two. Or it'll be the location of the hub where they're line is connected.
  3. Does anybody know if there's an ActiveMQ + OpenWire library for Delphi anywhere? This is what I'm referring to: https://activemq.apache.org/openwire-version-2-specification Mitov has published an OpenWire project but I don't think it's related.
  4. David Schwartz

    ActiveMQ + OpenWire for Delphi?

    As I said, I don't think they're related. Boian's stuff seems to be mainly UI stuff, although he's got a fairly significant investment in under-the-hood stuff. It's just not very well documented. (All the docs I can find are mainly about making components for the UI part.) ActiveMQ and its relatives all seem to be designed for machine-to-machine solutions, not so much stuff on the same machine; while Boian's stuff seems to be mainly for things running asynchronously as parts of the same EXE. I'm curious about something appropriate for bridging both domains.
  5. I've read some books and even played around with building some REST-based services, but there's something I'm missing. Hopefully someone here can shed some light on this for me. Over the years I've worked on tons of Delphi applications that are classic Client/Server designs with both data-aware controls and more decoupled data management via Data Modules. This is just how Delphi evolved, and all of these old "legacy" apps are build this way. Left to my own devices, I tend to take a different approach, which is that I add Load/Save methods into objects. There's a Load/Save data from/to the back-end, and Load/Save data between an object (or list) and the UI elements it's using. The back-end part is sometimes an INI file, as well as typical DB servers that use SQL or whatever. This is what I'd call a "semi-ORM" approach in that my client code doesn't really know anything about the data storage mechanism, just about objects. The object classes themselves usually have two parts: an object and a collection (list) of them; so I can Load/Save a single object, as well as a whole bunch of them in a list at the same time. In this case, there's usually a pretty close correlation between the on-screen UI elements and fields in the back-end data-store (whatever it is). When it comes to REST-type service interfaces, I've seen different approaches. There's usually a "one vs. many" way to Load/Save data, and this lets you treat the back-end as sort of an extension of the objects. But I've also seen designs that are dominated by lots of small Peek/Poke interfaces that I guess originated with javascript programmers who wanted to get/set values on individual UI elements asynchronous to the rest of the screen. Both approaches ultimately get mapped to a DB made up of tables of records. Some people love to flatten the damn things out and some leave them highly structured. I guess where I'm confused is this: if I'm building an app from scratch, what's the best way to approach designing the interfaces (or API?) for the back-end services (eg., REST structure)? Most examples I've seen take an existing application that has several DB tables and they simply move them to the server and slap some REST calls in front of them to replace queries to get/set fields and lists of records. I've never really seen a discussion about how you'd approach this if you're really starting from scratch, other than really trivial examples. My thinking is that I prefer to think of the back-end as a "persistent object store" and treat it as a transparent means of adding persistence to individual objects and lists of them. But that's just me. I mean, the fewer "oil-slicks" you need to translate the objects used on the front-end into whatever structure is needed to store them on the back-end, the more efficient and faster everything will flow, right? Cripes, look at how most SOAP interfaces end up working! If you get a bug in one of those "oil-slick" routines, it can be a bear to track down. (I've seen this where someone used StrToInt instead of StrToIntDef and failed to trap exceptions, and it would periodically return invalid but perfectly in-range results that took days to find and fix!) I've been playing around with TMS XData and Aurelius and it makes this approach seem really simple and very intuitive. But this seems to be the exception in terms of what I've found in "real live apps" in the wild. Are there any books or articles that discuss what I'm pointing at here? Of course, feel free to add your thoughts as well, as I'm really curious how people think about this aspect of designing remote services.
  6. David Schwartz

    Dynamic creation of SQL queries

    There are several products that already do this. TMS Query Studio is one I'm somewhat familiar with: https://www.tmssoftware.com/site/qs.asp
  7. David Schwartz

    Dynamic BPL loading (CData)

    You said: if AClass <> nil then begin with TComponentClass(AClass).Create(Application) as TFDPhysCDataExcelDriverLink do begin create(self); but I'm curious why you're calling create(self) when the context is already operating on a newly created instance? You seem to be creating an instance of something, then casting it into a (probably much larger) object and then recreating it. something := TFDPhysCDataExcelDriverLink( TComponentClass(AClass).Create(Application) ).Create(self); This does not look kosher to me.
  8. The thing to realize is that forms are objects -- instances of class TForm or a descendant. People don't seem to think of forms as regular objects, but they are. They also have restrictions due to potential multitasking conflicts with the VCL since it's not "re-entrant" code (how I learned it). That is, it's not "thread-safe". It has its own data. A DataModule is the same. If you had defined any normal class with private data members in it, you'd expose them via properties and thereby hide the private data through encapsulation. People don't do that with forms for whatever reason; rather, they reach inside of form objects and fiddle with what should be PRIVATE data members directly. But because of how the VCL streams stuff, the components on the form cannot actually be made private, which leads to people cheating and then justifying it as if that makes it OK. Then they get their knickers in a wad when things don't work properly. Well DUH! If you treat a form as if it was any other kind of object, then it would work. That is, if you expected it to be used by multiple clients, you'd maintain separate data elements for each client and they would not be aware of that since you'd be accessing everything through properties. Your questions do not seem to indicate that's what you're doing because you wouldn't be aware of the Name values given to anything on the Forms or DMs via the IDE. You wouldn't even care. Most likely, you'd set up properties to provide access to the data on the form or DM and have a Factory pattern to give you instances of it. The Factory would either ensure that each one had isolated private data elements or ensure that no conflicts existed when accessing things that aren't thread-safe. You said you think your question has been answered, but I think you were asking the wrong question from the outset. Database components like TDatabase, TDataset, and TTable are not stateless. You cannot generally share them among different forms. TQuery components can be treated as stateless if they're simple lookup tables and their queries obtain the entire result set; but if not, you're going to end up with quite a mess. The client forms should not know about how things are implemented on the forms and DMs they're sharing. Your original problem statement is expressed in a way that assumes this is not the case -- that the clients DO know what's living inside of the shared objects. THAT is a fundamental flaw in the approach you laid out, and your questions arise due to this flawed model. Change your approach and the questions will disappear along with the conflicts you're experiencing. Forms are normal objects. If you treat them like normal objects, they'll work like normal objects. You cannot do the typical kind of "cheating" that most Delphi devs do with forms and expect these encapsulation problems to be solved by some kind of magic. Here is the crux of the issue: the RTL can only stream PUBLIC data members. That doesn't give you a license to TREAT them as if they're "safe" just because they're PUBLIC! They need to be regarded as if they're PRIVATE. Perhaps I'm completely off-base in terms of what you've arrived at in your thinking, but I can't tell that from what you've said here.
  9. I'm not quite following this, and I'm curious what the crux of the problem you're trying to address seems to be. AFAIK, the underlying logic uses RTTI stuff, which depends in part on strings that need to match things on the form or DM. But once everything has been created and initialized, the symbolic references are typically no needed. (They can slow things down.) It's fairly common in some apps to only have a few forms and DMs get auto-created at startup, and create the rest dynamically. When this is done, you typically comment out the global form var at the bottom of the Interface section of the forms that are created dynamically. That way it's entirely possible to create as many instances of the form as desired. Assuming the form itself creates the associated DM, there's not going to be any problem. You WILL have a problem if you try to share a single DM among multiple forms, but that does not seem to be what you're doing. So what exactly is your concern? (I have one based on what's said above, but I want to see if it's the same as what you're thinking.)
  10. I'd use two buffers. The other task would write into whichever one you have not flagged as busy, or the other one that what was just filled if neither are busy.
  11. David Schwartz

    Overload methods or use unique names?

    You have a TTask object and you are defining different ways of adding it to a TTaskScheduler object and when to start it running. Initially, I thought the examples in the first post are global-scope methods, but there's no queue argument, so I'm going to guess they're members of the TTaskScheduler class. So you have a Task Scheduler object where you want to add tasks with different initial start attributes. The normal (default) case when adding a task to a scheduler is that it's blocked; you'd add the task, set some properties, then start it running. It's an initialization sequence. var t : TTaskID; . . . begin . . . t := q.Add( task1 ); task1.prop1 := xxx; task1.prop2 := yyy; q.Resume( t ); In a situation where the task is fully initialized or can run with default settings, then there's usually a way to add it and start it running in a single call. t := q.AddAndRun( task1 ); // equivalent to this: q.Resume( q.Add( task1 ) ); Both Add cases need to return a handle of sorts that identifies the task in the queue, referred to as TTaskID here. It's an index related to the queue because that's what you're adding it to. (It could also be a TTaskSchduler reference or possibly something else.) Why? Because using a TTask reference forces you to search the queue to find the task in question. It takes O(n/2) time which will vary with the size of the queue, rather than O(1) which is constant. The second option where you're delaying the start to a specified time is like adding a task normally that starts out blocked, but you set its starting time instead of calling Resume. So it's really a variation on Resume, not Add. This is what I'd do: type TTaskID = integer; TTaskScheduler = class . . . public function Add( aTask : TTask ) : TTaskID; // add task to the queue and leave it blocked function AddAndRun( aTask : TTask ) : TTaskID; // add a task to the queue and start it running immediately function Suspend( aTaskID : TTaskID ) : TTaskID; // stops the task function Resume( aTaskID : TTaskID ) : TTaskID; // starts running immediately function ResumeAt( aTaskID : TTaskID; aExecDateTime: TDateTime ) : TTaskID; // resumes the task at the appointed time function ResumeAfter( aTaskID: TTaskID; aMsecDelay : Cardinal ) : TTaskID; // resumes the task after some delay . . . end; So in a way, the question is irrelevant in this case when you get the design right (IMHO) and name things properly. Sorry, but this doesn't really address your question ... and yes, I do run into issues from time to time with too many overloaded methods, but the problem usually resolves itself when I look more closely at the design of the class and what the actual use cases are. Note that you could get fancier with this, allowing expressions using a fluent notation like so: q.Add( task1 ).Resume(); // in place of AddAndRun q.Add( task1 ).Resume().At( dawn_tomorrow ); q.Resume( t ).After( Refresh_delay );
  12. David Schwartz

    DX10.3.2 Installer weirdness

    I flushed out everything from a VM and tried installing Rio 10.3.2, but the installer kept acting as if a previous installation was there. So I went in and manually removed a ton of entries from the Registry that contained "Embarcadero". That fixed the problem. I installed from the ISO image because my internet is going over an LTE connection, and I see no need to DL several GBs of files that are already on the disk image. For the install, I let it install EVERYTHING. It got to the end and said separate installers would run for the other things, but nothing ran after that. Delphi started, but there was only Win32 platform installed. One thing I do change is the installation folder: I change the name from "Studio" back to "RAD_Studio" the way it used to be (plus an underscore). Old habits die hard, I guess. Re-running the installer showed the same options and all of the options were selected. I ran it and it accomplished ... absolutely nothing. So I fired up the ESD installation EXE and it gave me options to install everything else. There was a note that it's accessible from the IDE, so I closed the ESD EXE and tried it from the Delphi IDE. I guess it just runs the ESD program. I went ahead and ran the ESD installer and it pulled down around 700 MB of stuff from the internet and installed it. The Good News is that it actually ran without reinstalling the whole 7GB or so of previously installed code the way I've seen it do previously! Oddly, it never asked about the installation folder or gave me an option to change it. I figured it just used the registry settings. When it was done, I discovered it ignored the registry settings and dumped everything into a newly created "Studio" folder (not "RAD_Studio" where everything else was), and dutifully added a bunch of new stuff to the search path environment variable. If you have the ISO image loaded, I don't understand why the ESD installer can't use it for its files. I mean, if you're not connected to the internet, you're forced to use the ISO installer, but it seems to have problems installing different platform options for several releases now, that can only be fixed by using the ESD installer. For whatever reason, they both copy the .7z files to a hidden folder somewhere to facilitate reinstalls and updates, so what's the problem here? Also, if you have the ISO image available, why does the installer even need to copy the files to disk? You have to pay for the overhead of the copy in addition to the install, which just slows everything down, so why force a needless copy? I really wish someone at Embt would budget a month to have someone streamline the installation process so everything can be run offline from the ISO media via either installer without wasting the time and space needed to either DL files or copy them to local HD; provide a better way to install and uninstall chunks of Delphi without all of this redoing stuff; and make the uninstall process work faster. (I found that if I run a full uninstall, it takes around 40 minutes, but if I delete everything in the Embarcadero/<vers> folder first, it completes the uninstall in 5 minutes, although it may leave crap scattered throughout the Registry -- not totally clear about this, tho.)
  13. David Schwartz

    DX10.3.2 Installer weirdness

    Another thing I'd love to see is a better way of preserving the state of installed components as well as the toolbar at the top. I realize these are two unrelated and very different things, but at least a checklist of previously installed components and libs, along with their installed versions and dates, would be really handy. I run the migration tool and saved the output on several upgrades, then ran it on the new version, and it has never changed the toolbar layout at the top. I've never gotten that to work, although I've had people tell me it works for them. Can't stuff like this be made amenable to support with external DevOps tools? Everything is locked inside of the installer and nobody outside of the factory can do anything to fix or improve it at all.
  14. David Schwartz

    Duplicate resource, but strange...

    I've seen this show up in projects randomly then not stop, and have never figured out what causes it. But after reading this, I do recall something I saw years ago about how the position of the $R line can cause it. I think in our case, it may have been because of some merging of edited source files and it got put in the wrong place. Some people seem to think that its position is innocuous, but apparently it's not. This is a great reminder. Thanks!
  15. In case anybody is interested, I've been trying to scrape a Google search result page, and it's nearly impossible without a huge amount of work. The HTML page you get back from their server looks like <HTML><HEAD>...</HEAD></HTML> There's no BODY. It's generated inside the DOM by javascript. The javascript takes a bunch of regular HTML and CSS and appears to use a lookup function do global search-and-replace of nearly every human-readable word and phrase with random character strings that are generated dynamically for each page. So there are hardly any "landmarks" you can use to find anything. It's not like you can build a symbol table inferred from one page on subsequent pages, since the mappings are different from page to page. It also employs deeply nested DIVs and lots of needless SPANs to make it difficult to extract content. Finally, while it's clear from viewing the page that there are 10 entries in the main part of the page, it manages to hide some in ways that I couldn't figure out where they are. I searched for strings that can be seen in the browser view, but they aren't found anywhere in body of the rendered HTML. I'm guessing they're hiding inside of some javascript functions having been obfuscated to the point where they can't be read without executing the functions to extract them. I suspect it won't be long before it will be nearly impossible to extract anything meaningful from HTML pages, as whatever we see displayed in a browser will be generated entirely on-the-fly from a variety of dynamic and static methods buried inside of deeply nested javascript functions, some of which could be rendered even after the page loads. Some of it even looks to be a variety of self-modifying (or self-generated) code. This has some pretty crazy implications for both security as well as making it impossible to sniff code that could signal the presence of malware injections. I've attached an unwound pretty-printed sample of a search page I was able to extract for your reading pleasure. Fun stuff, eh? sample_SERP.html
  16. David Schwartz

    Parsing Google Search Results redux

    Sorry, I read that wrong. I though the ones in red were the free ones. I was talking about parsing the Google SERPs, not using the API. From some research I've done, the API does return "search results", but it's a subset of what you see when you run a query, and may be subject to variations based on data you may or may not have, like LAT+LON location info (ie, what does "near me" mean? an IP-based location can be far away from where you really are). I haven't actually played with the API yet, just basing this on numerous comments and complaints I found around the internet. Like the map often found at the top of the page with the top-3 locations on it ... that's apparently "derived", along with all of the sponsored ads and other stuff. How exactly they come up with those three selections is anybody's guess. Some people care about that, some don't. (That's something I was specifically looking to identify.) IOW, there's both "primary" and "derived" data displayed on SERPs you get back from Google. From what I could determine, the API only gives you the "primary" data, which is fine for most needs. And the API can get rather expensive in some circumstances.
  17. David Schwartz

    How to get Linux installed?

    I'm running Delphi inside of VirtualBox hosted on a Mac. You're saying that PAServer needs to be running in order for Delphi to do anything Linux-related? Where's any of this documented? I haven't seen anything about it other than how to RUN stuff through PAServer.
  18. David Schwartz

    Parsing Google Search Results redux

    Google Search is not part of the free package. And I'd bet it doesn't work very well. (It may parse some stuff, but after looking over the efforts G has expended to embed obfuscated javascript that's generated dynamically, intended to hide random sections of results in different ways, it would need to be pretty damn smart to reverse engineer a search page and break it up into meaningful chunks that come close to showing what's actually visible to the user.)
  19. David Schwartz

    How to get Linux installed?

    This is the problem I keep running into: I *NEED* to be able to build for LINUX targets! I get the same thing for Android, b/c the installer never ran the two SDK installers when it finished with the main install.
  20. David Schwartz

    How to get Linux installed?

    FMXLinux does not show up in my GetIt list. But IIRC it wasn't added until 10.3.2 This is 10.2.3. I thought it's possible to target Linux for either VCL or FMX because there's no UI support offered for it. It's for services only. TMSSoftware's Business Suite supports it, for instance. However, I do see 64-bit Linux as an option in the Tools | Options | Library pane.
  21. David Schwartz

    How to get Linux installed?

    Enterprise Edition Delphi 10.2.3 installer from ISO It even installed the Android platform this time, although I'm not sure it ran the external setups for the SDK. It's running on Windows 7 SP1 (V6.1, Build 7601, 64-bit Edition)
  22. This is focused on Perl, but it mentions Delphi as well. https://thehftguy.com/2019/10/07/perl-is-dying-quick-could-be-extinct-by-2023/
  23. David Schwartz

    Any update on the v10.3.3 release?

    I use a VM. Installing a new Delphi update is just as much of a PITA on a VM as on a host machine.
  24. David Schwartz

    Interesting article about dying languages

    Isn't there a survey that reports on the number of job ads for specific languages? I'd think that would be more indicative. Even more interesting would be things like what version of Java or C# is being requested. Most Delphi jobs are for legacy stuff and are almost universally D2010 or earlier, so that's sort of meaningless for us. But there's a HUGE amount of Java that's still Java 5 and 6, although you often don't know until you ask.
  25. David Schwartz

    Interesting article about dying languages

    Very cool!
×