Read file as record reads only the first record

With read from Standard Additions, I have not been able to read any record except the first. Here is an example of a script in which I have tried to read a record, after having written two.


property full_path : (path to desktop as text) & "record_data"

set the_record_1 to {first_name:"Anthony", age:37, date_of_birth:date "Monday, December 4, 1967 12:00:00 AM"}
set the_record_2 to {first_name:"Cleopatra", age:38, date_of_birth:date "Friday, December 5, 1975 12:00:00 AM"}

repeat with a_record_item in {the_record_1, the_record_1}
	write_record(a_record_item)
end repeat

on write_record(a_record_item)
	set the_record to contents of a_record_item
	set file_ID to open for access file full_path with write permission
	write the_record to file_ID starting at eof as record
	try
		close access file_ID
	end try	
end write_record

set the_record to read file full_path from 0 as record


The result always returns {first_name:“Anthony”, age:37, date_of_birth:date “Monday, December 4, 1967 12:00:00 AM”}. It never returns the second or the Cleopatra record. I am looking for a method to read both records.

Hi akim. Welcome to MacScripter.

Your script does in fact write the same record to the file twice (that is, twice more each time it’s run). And since you always read the file from the beginning, you’re always going to get the same result anyway.

The thing to do after writing the records is to open the file for access again (write permission not needed) and read from it twice without any explicit file-byte pointers. The script will read the first record and then read the second record from where it got to in the file.

Delete the file you’ve been playing with before trying this:


property full_path : (path to desktop as text) & "record_data"

on write_record(a_record_item)
	set the_record to contents of a_record_item
	set file_ID to open for access file full_path with write permission
	try
		write the_record to file_ID starting at eof as record
	end try
	close access file_ID
end write_record

set the_record_1 to {first_name:"Anthony", age:37, date_of_birth:date "Monday, December 4, 1967 12:00:00 AM"}
set the_record_2 to {first_name:"Cleopatra", age:38, date_of_birth:date "Friday, December 5, 1975 12:00:00 AM"}

repeat with a_record_item in {the_record_1, the_record_2} -- not {the_record_1, the_record_1}
	write_record(a_record_item)
end repeat

-- Read the records back serially from the file.
set file_ID to (open for access file full_path)
try
	set record_1 to (read file_ID as record) -- Reading starts at the beginning immediately after the access is opened.
	set record_2 to (read file_ID as record) -- Reading carries on from where we've reached.
end try
close access file_ID

{record_1, record_2}

Another possible approach, depending on your needs, would be to put all your records into a list and write that to the file. You could then read it back ‘as list’ and get a list of records.

Edit: A lapse in my own coding corrected. More comments added.

Nigel,
Thanks for the clarification and read now works.

I would like to use this set of records as a database. For example, I want to find every record whose first_name is “Cleopatra”. I have composed the following applescript using your example and then setting up two repeat functions. The first gathers all of the records into a list , and exits on an error code, as I could find no other way to exit. The second parses through each item of that list to find a certain piece of data and then exits.


property full_path : (path to desktop as text) & "record_data"

on write_record(a_record_item)
	set the_record to contents of a_record_item
	set file_ID to open for access file full_path with write permission
	try
		write the_record to file_ID starting at eof as record
	end try
	close access file_ID
end write_record

set the_record_1 to {first_name:"Anthony", age:37, date_of_birth:date "Monday, December 4, 1967 12:00:00 AM"}
set the_record_2 to {first_name:"Cleopatra", age:38, date_of_birth:date "Friday, December 5, 1975 12:00:00 AM"}

repeat with a_record_item in {the_record_1, the_record_2} 
	write_record(a_record_item)
end repeat

(*  Two repeat functions follow. The first  gathers all of the records into a list *)

set Record_List to {}
set file_ID to (open for access file full_path)
repeat
	try
		set end of Record_List to read file_ID as record
	on error --  (* exits on an error code, as I could find no other way to exit *)
		exit repeat
	end try
end repeat
close access file_ID

(*The second repeat function parses through each item of that list to find a certain piece of data and then exits. *)
repeat with a_record in Record_List
	set record_reference to (contents of a_record)
	if first_name of record_reference is "Cleopatra" then exit repeat
end repeat
-- found record that contains Cleopatra
{record_reference}




Do you know of a a simpler method? Thanks for your help.

Hi akim,

I don’t have your answer, but how many records do you plan to have?

gl,
kel

Using Database Events instead would be simpler, and a lot faster.

Kel, I was attempting to build a database reference as a lookup of values for 30 records, initially, with the intention of expanding to 60 records.

Hi akim,

60 is not too bad, but as Shane said, database events are easier and quicker if you plan to expand. But wait, what is database events?

gl,
kel

Thanks for the advice and great help.

It’s a small faceless app in /System/Library/CoreServices/, and is basically a scripting interface to SQLite. You can download some samples on this page: www.macosautomation.com/applescript/resources.html

That’s a neat little app.

Hello.

Database events is neat, and it is probably easier to use than applescript records in the long run. So I provide a link for this fine article by Benjamin S. Waldie to read along with the examples you’ll find on the page Shane posted a link for. If you are solely interested in accessing your records from AppleScript, then this is the solution I would go for, I think. :slight_smile:

Having said that, it isn’t that hard, to make an indexed collection of non-related records in AppleScript.
That is if you use Nigel’s recommendation, and reads them in as a list. Getting hold of a copy of quick sort, and binary search, you could also easily create an index file, that you could use to retrieve the records somewhat faster with. The clue then, is to add the record number to the record, and store the record number with the index field in the index. You’d have to update both the index, and the index numbers when you delete a record. and you’d have to rebuild the index every time you change the value of one of the indexed fields. Which is basically just to change the value of the field, and resort it.

Other approaches, could be to use Excel, or Numbers for what I know, as it may be convenient to have the data there for easy editing. And then of course there is FileMaker, but I don’t recommend using filemaker if you are going to access your data just from AppleScript, as you tend to get a lot of databases open, as it is so fast and fun to make one, and the design of it forces you to activate the database for queries and the like, through a layout. If you really want a database, and a database is what you are really after, then I whole heartedly recommend Filemaker. You’ll have to learn a little bit to take advantage of it though.

I agree with McUser but of course the choice of the database totally depends on your needs.

There is one complex and probably overkilling method for simple AppleScript but I think it’s worth mentioning it. Especially if multiple workstations needs the same data and different scripts (or even in different programming languages) needs the same data too. When you’re using webservices you’re really going pro. I’ve developed XML-RPC server(s) through time and the latest is more an RPC server than an XML-RPC (it supports json and soap as well, it’s easy to extend with another parser). What I like about it is that scripts in Bash or AppleScript but also application written in Objective-C and command line utilities written in C or an webapplication written in JavaScript can all use the same XML-RPC server. What the most basic XML-RPC servers do is sitting on top of an MySQL database and with methods and parameters you can give your command (procedure) to the server and it will come back with a response. Another great advantage over using do shell scripts is that you don’t need to coerce data. AppleScript supports almost all XML-RPC data types as integer(i4), number (double), string, date, list(array) and record (struct), it doesn’t support base64 object.

Hello.

I wonder if you have a link that you recommend on this. It is not that Filemaker can’t provide such a solution, but you are pretty much tied up to FileMaker when you use it, (unless you have an SQL database in the bottom) and versatility is often good, and worth the extrawork, with regards to access from different scripting/programming languages. Javascript seems quite suitable for writing web-apps. :slight_smile:

You also have the low-level, and cumbersome approach, that you can make an applescript web app, by making it respond to a protocol, through a html interface that consist’s of a web form (Nik’s Blog) that way you should also be able to make a dasboard widget, as a userinterface to an AppleScript controlled database. But it is cumbersome, long before you get to the dashboard widget I think. There is also a project over at macosxautomation, that covers using an Applescript.app, that are installed with its own protocol, to be triggered by bookmarks in Safari. It’s name is webpagehelper.app.

Hi McUsr, well I’ve written my XML-RPC from scratch myself using the book “Programming Web Services With XML-RPC”, here an online pdf. Then I had already some years of experience in programming PHP, the new features __callStatic in PHP 5.3 made it even more dynamic.

I’m sorry that I can’t share the XML-RPC server with you because I would violate too many licenses because some of my companions think it’s architecture is unique and worth money to give it to use to someone else, not my call. The missing feature in all RPC servers is, is that the methods itself are too complex.


tell application "http://api.opensubtitles.org/xml-rpc"
	call xmlrpc {method name:"ServerInfo"}
end tell

opensubtitles.org is one of the greatest example of the flexibility of XML-RPC. Which home theater PC doesn’t have an application that downloads automatically the right subtitles to your downloaded movie. If you’re on android, iOS, TV of even behind your PC, they all use the XML-RPC services of opensubtitle.org. You can even create an AppleScript that downloads the subtitles in the right language to your movies (you need an account to search for subtitles).

Hello.

Thanks for the link, it seems like great stuff. Thanks. :slight_smile: I’ll have a look at it.

Still pondering a little about AppleScript, thinking that a quick and dirty system for keeping records indexed, may come in handy for the utterly lazy of us. Also thinking, that maybe Apua? could provide for a record entry/edit system, that would be viable with AppleScript. (For those solutions you really would benefit for in the moment, but has to complete as fast as possible.)

I still think Excel, or Numbers should be the greatest alternatives for say 60 records, and that you have to live with it, and create the kluge to keep your window in background and all that. That is, if you have Excel or Numbers, if you don’t, then I recommend buying iWork, which is a great value for money. I know for sure, that if you have set up your records as row of data in a datarange in Excel, then you get a data entry form for free, and I think you can search through your data from the same form, maybe Numbers has some of the same functionality.

But, we are really talking 60 records here, a spreadsheet isn’t a database system!

Of course, all records could be stored as tabulated data in a text file, so that it is possible to edit/and enter data from there as well. (Comma Separated ascii with returns) and then read in paragraphs of text stuffing them into the record, adding recordnumbers, and creating an index along the way.

That is probably the easisest way to get a database up and running, that is to be used with applescript.

as Shane said, the fastest, easiest and most powerful way to be used with AppleScript is Database Events

Well, as for the retrieval part I fully agree, and performing queries, I agree, but for editing, and adding records, I think there are better alternatives.

Programatically editing records, is of course ok, when you edit a bulk of records, but it represents quite the overhead, if you are doing individual changes.

A quick google reveals that there are several graphical user interfaces for sqllite, and I can’t find anything that excludes the use of a Database Events made SQLlite file with it. But Database Events probably lacks some versions behind in file format. :slight_smile:

Edit

I really wouldn’t use Database Events for anything but flat files, that is I at least don’t want to handle a relational database through Database Events. That may soon be too much work, and too many queries (slow). :slight_smile:

The topic is just to read a few records.

Therefore a full-fledged solution is overkill :wink:

You could use my version of hash table :wink: