Thursday, June 25, 2009

Creating a calendar using Newspeak and Hopscotch

For this post I'm going to show the code for a little calendar UI fragment created using the Newspeak programming language and the Hopscotch framework.

Calendar


Here's how the calendar looks:

Hopscotch calendar experiment

(As you can see, I'm focusing on the functionality for the moment).

The code



As described in "Hopscotch: Towards User Interface Composition" this framework promotes the separation between data (the subject) and the UI elements (presenter) . For this calendar fragment the data part will be the given date and the UI part will be a series of UI elements that represent the days of a month.

Here's an overview of the definition for the HCalendar class:


class HCalendar usingLib: platform = NewspeakObject (
|
...
|
)
(

class CalendarSubject for: date = Subject (
|
...
|
)
(
...
)

class CalendarPresenter = Presenter (
|
...
|
)
(
...
))




The subject



As mentioned above the subject only holds a given date. Some operations are added to make it easy to manipulate it.


class CalendarSubject for: date = Subject (
|
private date = date.
|
)
('as yet unclassified'
changeDayTo: newDay <Number> = (
date:: Date year: year
month: (month name)
day: newDay.
)

createPresenter = (
^CalendarPresenter new subject: self.
)

day ^ <Number> = (
^date dayOfMonth.
)

month ^ <Month>= (
^date month.
)

moveToNextMonth = (
date:: date addMonths: 1.
)

moveToPreviousMonth = (
date:: date addMonths: -1.
)

year ^ <Number> = (
^date year.
))

The presenter


The presenter class is more interesting. It takes the date from the subject and tries to create a representation of the month using Hopscotch fragments. The following snippet shows an overview of the presenter class.


class CalendarPresenter = Presenter (
"Calendar presenter, shows the days of the a month."
|

protected weeksRow
protected monthHolder
|
)
('as yet unclassified'


definition = (
monthHolder::
holder: [
row: {
link: '<' action: [ subject moveToPreviousMonth.
refreshHolders. ].
blank: 1.
column: { header .
weeks. }.
blank: 1.
link: '>' action: [ subject moveToNextMonth.
refreshHolders. ].

}.].
^monthHolder.
)

fragmentForDaysNotInCurrentMonth = (
^label: ' '.
)

header = (
|headerRow|
headerRow:: row: {
filler.
label:: subject month name, ' ' , subject year asString.
filler.
}.
^headerRow.
)


refreshHolders= (
monthHolder refresh.
)

weekDayFragmentFor: dayNumber = (
|dayNumberText|
dayNumberText:: dayNumber asString.

^link: dayNumberText
action: [ subject changeDayTo: dayNumber.
highlightSelectedDay.
].
)

highlightSelectedDay = (
...
)


weeks = (
...
)

addFirstWeekTo: result withDaysFromPreviousMonth: previousMonthDays = (
...
)

addLastWeekTo: result weeksToShow: weeksToShow lastDayAdded: lastDayAdded daysInNextMonthToShow: nextMonthDays= (
...
)

addMonthWeeksTo: result weeksToShow: weeksToShow lastDayAdded: lastday= (
...
)

columnSeparator = (
...
)

createColumnsFromWeekArray: weekArray = (
...
)

)



The weeks method is where most of the work of creating the calendar is done. For space reasons I'm not including it here, see the link at the end of the post for the complete code.

Reusing the calendar



Now that we have the definition of the calendar presenter and subject we can reuse it to create more interesting fragments. For example the following definition could be used to create a date range picker.

class HDateRange usingLib: platform = (
"Date range selector."
|
...
|
)
(

class DateRangePresenter = Presenter (
"Presenter for date range."
|
dateRangeTextHolder
|
)
(
calendar: dateSubject = (
|aCalendar|
aCalendar:: dateSubject createPresenter.
aCalendar onChange: [ dateRangeTextHolder refresh ].
^aCalendar.
)

definition = (
dateRangeTextHolder::
holder: [label: subject initialDate selectedDate asString, ' - ',
subject finalDate selectedDate asString].
^heading: dateRangeTextHolder
details: [
row: {
calendar: subject initialDate .
blank:49.
calendar: subject finalDate.
}]
)

)

class DateRangeSubject from: initial to: final= Subject (
"Data for the date range."
|
private initialDate = (HCalendar usingLib: platform) CalendarSubject for: initial.
private finalDate = (HCalendar usingLib: platform) CalendarSubject for: final.
|
)
(
createPresenter = (
^ (DateRangePresenter new) subject: self.
)))




Code for this post can be found here.

Sunday, June 21, 2009

Using libcurl with Newspeak FFI (continued)

The previous post presented a small low level interface to libcurl using the Newspeak programming language. In this post I'm going to show the HttpServiceClient class, which was created to give a simple interface to the LibCurlHelper class.

The definition for class looks like this:


Newsqueak2
'LangexplrExperiments'

class HttpServiceClient usingLib: platform withCurlPath: curlLibraryPath = (
"This class is used to access services provided by the HTTP protocol"
|
LibCurlHelper = platform LibCurlHelper.
ByteString = platform ByteString.
platform = platform.
Transcript = platform Transcript .
private curlLibraryPath = curlLibraryPath .
|
)
(

class HttpRequestResult curlErrorCode: curlErrorCode httpResponse: httpResponse data: data= (
...
)
(
...
)

createNewCurlInstance = (
...
)

get: url <String> ^ <HttpRequestResult> = (
...
)

get: url <String> withHeaders: headers <Array> ^ <HttpRequestResult> = (
...
)

private isHttpsUrl: url <String> ^ <Boolean> = (
...
)

postForm: formData <Dictionary> to: url <String> ^ <HttpRequestResult> = (
...
)

) : (
...
)


The get:, get: withHeaders: and postForm: to: methods provide the functionally to do very simple GET and POST requests.

The HttpRequestResult encapsulates the result of calling these methods which has the result of calling libcurl, the HTTP response code and the text of the requested data if successful.

The code for the GET methods looks like this:

get: url <String> ^ <HttpRequestResult> = (
| curl data tmpBuffer bufferLength response|
^ get: url withHeaders: {}.
)


get: url <String> withHeaders: headers <Array> ^ <HttpRequestResult> = (
| curl data tmpBuffer bufferLength curlCallResult response|
data:: ''.
curl:: createNewCurlInstance.
curl writeCallback:
[:args :result|
bufferLength:: ((args datasize) * (args nmemb)).
tmpBuffer:: ByteString new: bufferLength.
args data copyInto: tmpBuffer
from: 1 to: bufferLength
in: (args data) startingAt: 1.
data:: data,tmpBuffer.
result returnInteger: bufferLength.
].

headers size > 0 ifTrue: [curl headers: headers].

(isHttpsUrl: url)
ifTrue: [curl noSslVerification.].
curl url: url.

curlCallResult:: curl performOperation.

response:: curl responseCode.
curl cleanup.
^HttpRequestResult
curlErrorCode: curlCallResult
httpResponse: response
data: data.
)


The code for the POST operation looks like this:


postForm: formData <Dictionary> to: url = (
| curl data tmpBuffer bufferLength curlFormData response curlCallResult|
data:: ''.
curl:: createNewCurlInstance.

curl post: formData.
curl writeCallback:
[:args :result|
bufferLength:: ((args datasize) * (args nmemb)).
tmpBuffer:: ByteString new: bufferLength.
args data copyInto: tmpBuffer
from: 1 to: bufferLength
in: (args data) startingAt: 1.
data:: data,tmpBuffer.
result returnInteger: bufferLength.
].

(isHttpsUrl: url)
ifTrue: [curl noSslVerification.].

curl url: url.
curlCallResult: curl performOperation.

response:: curl responseCode.
curl cleanup.
^HttpRequestResult
curlErrorCode: curlCallResult
httpResponse: response
data: data.
)


Code for this post can be found here.

Saturday, June 13, 2009

Using libcurl with Newspeak FFI

In this post I'm going to show a little example of using libcurl from the Newspeak programming language .

Newspeak FFI



Newspeak provides a nice mechanism to call C code. This mechanism is described in
Newspeak Foreign Function Interface User Guide document. The AlienDemo example provided with the Newspeak prototype has some nice small examples of the FFI.

The experiment presented in this post was created using the Newspeak prototype from February 2009. Due to some limitations of this release, this code only works with the Windows version of the prototype.

libcurl



libcurl is a C library that provides client access to several networking protocols with a common interface. For this post I'm going to implement a wrapper for very small subset of the functionality provided by libcurl in order to perform simple HTTP/HTTPS GET and POST requests .

The simple.c example shows how to do a simple GET request.


int main(void)
{
CURL *curl;
CURLcode res;

curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return 0;
}


The LibCurlHelper class



A class named LibCurlHelper will be used to encapsulate calls to libcurl. As you will notice the interface of this class is pretty low level. For future posts I'll try to create a better interface using more Newspeak features.


class LibCurlHelper usingLib: platform = (
"This class wraps an implementation of the libcurl library"
|
Transcript = platform Transcript.
Alien = platform Alien.
UnsafeAlien = platform UnsafeAlien.
Callback = platform Callback.
CurlWriteCallback = platform CurlWriteCallbackNs1.
CurlDebugCallback = platform CurlDebugCallback.
ByteString = platform ByteString.
OrderedCollection = platform OrderedCollection.
...
public libcurlPath = ''.
public errorBuffer = ''.
protected CURL_OPT_URL = 10002.
protected CURLOPT_WRITEFUNCTION = 20011.
...

internalDebugCallback = nil.
internalWriteCallback = nil.
formPostData = nil.
private curlInstance = nil.
private aliensToRelease = nil.


CURLFORM_NOTHING = 0 .
CURLFORM_COPYNAME = 1 .
CURLFORM_PTRNAME = 2.
...


libcurl uses a lot of constaints prefixed with "CURL" this class contains definitions for some of them.

Initialization



The initializeCurl method calls the curl_easy_init (as in the simple.c example shown above) and stores the returned pointer in a slot called curlInstance which will be used in further calls.


initializeCurl = (
|curl|
ensureLibrariesLoaded .
(Alien lookup: 'curl_easy_init' inLibrary: curlLibName )
primFFICallResult: (curl:: Alien new: 4).
curlInstance: curl.
)


The ensureLibrariesLoaded and methods.


curlLibName = (
^libcurlPath, 'libcurl.dll'
)
ensureLibrariesLoaded = (
Alien ensureLoaded: libcurlPath, 'libidn-11.dll'.
Alien ensureLoaded: libcurlPath, 'libeay32.dll'.
Alien ensureLoaded: libcurlPath, 'libssl32.dll'.
Alien ensureLoaded: curlLibName.
)


Setting the URL



In order to set the URL for the request we need to call the curl_easy_setopt function with the CURL_OPT_URL with the URL string.

The code looks like this:


url: url <String> = (
|result|
(Alien lookup: 'curl_easy_setopt' inLibrary: curlLibName )
primFFICallResult: (result:: Alien new:4)
withArguments: { curlInstance.
CURL_OPT_URL.
(addAlienToRelease: (url asAlien)) pointer. }.
^result.
)


The addAlienToRelease: method was added to in order to keep track of resources allocated in the C heap that need to be manually released when not needed. The asAlien method of the String class creates a resource of this kind.

The implementation of this method looks like this:


addAlienToRelease: anAlien = (
aliensToRelease isNil ifTrue: [ aliensToRelease:: OrderedCollection new. ].
aliensToRelease add: anAlien.
^anAlien.
)


Setting the write callback



Callback functions are used by libcurl to process the data coming from the network. The Newspeak FFI provides a nice way to add this kind of callbacks.


writeCallback: callback <Block>= (
|result|
internalWriteCallback:: Callback
block: callback
argsClass: CurlWriteCallback.

(Alien lookup: 'curl_easy_setopt' inLibrary: curlLibName )
primFFICallResult: (result:: Alien new: 4)
withArguments: { curlInstance.
CURLOPT_WRITEFUNCTION.
internalWriteCallback thunk. }.

^result.
)



The writeCallback: method sets the block in callback as the libcurl write callback. In order to do this it creates an instance of the Callback class with the block and the arguments class. An instance of this class is used to create a function pointer which is passed to the curl_easy_setopt function.

The "arguments class" is defined using the NS1 Newspeak syntax as follows:


Newsqueak1
'LangexplrExperiments'
CurlWriteCallbackNs1 = Alien (
"Class used to represent arguments of the LibCurl write function."
'as yet unclassified'
data = (
^Alien forPointer: (self unsignedLongAt: 1)
)
datasize = (
^(self unsignedLongAt: 5)
)
nmemb = (
^(self unsignedLongAt: 9)
)
writerData = (
^Alien forPointer: (self unsignedLongAt: 13)
)
) : (
'as yet unclassified'
dataSize = (
^16
))



An instance of this class is used to represent the arguments of a callback call. An example of the use of this function is presented below.

Performing the request



The curl_easy_perform function is used to start the operation. The following code shows the call to this function:


performOperation = (
|r|
(Alien lookup: 'curl_easy_perform' inLibrary: curlLibName )
primFFICallResult: (r:: Alien new: 4)
withArguments: { curlInstance. }.
^r signedLongAt: 1.
)


Cleanup



Finally the following method is used to release the resources allocated by libcurl.


cleanup = (
(Alien lookup: 'curl_easy_cleanup' inLibrary: curlLibName )
primFFICallResult: nil
withArguments: { curlInstance } .

aliensToRelease do: [:anAlien | anAlien free ].
)


Example of using the library



As mentioned above, the LibCurlHelper class provides a low level interface to libcurl, something needs to be created to encapsulate this functionality.

The following method shows a method that preforms a simple GET request and returns the downloaded data as a string.


class HttpServiceClient usingLib: platform withCurlPath: curlLibraryPath = (
"This class is used to access services provided by the HTTP protocol"
|
LibCurlHelper = platform LibCurlHelper.
ByteString = platform ByteString.
platform = platform.
Transcript = platform Transcript .
private curlLibraryPath = curlLibraryPath .
|
)

(
simpleGet: url ^ = (
| curl data tmpBuffer bufferLength response|
curl:: (LibCurlHelper usingLib: platform).
curl libcurlPath: curlLibraryPath .
curl initializeCurl.

data:: ''.
curl:: createNewCurlInstance.

curl writeCallback:
[:args :result|
bufferLength:: ((args datasize) * (args nmemb)).
tmpBuffer:: ByteString new: bufferLength.
args data copyInto: tmpBuffer
from: 1 to: bufferLength
in: (args data) startingAt: 1.
data:: data,tmpBuffer.
result returnInteger: bufferLength.
].
curl url: url.
curl performOperation.
curl cleanup.
^data
)
)


Notice that here the callback function modifies a local variable every time the data arrives. Also notice that args is an instance of CurlWriteCallbackNs1.

Final words


The experiment of using libcurl from Newspeak was a nice way to learn about its foreign function interface. Having access to libcurl access to useful things such as HTTPS requests.

There's already a nice Squeak wrapper for libcurl called CurlPlugin .

Code for this post can be found here.