Welcome to Adaptation
Adaptation is a framework that tries to facilitate data interchange between applications. Applications send and receive xml messages through a message oriented middleware (mom) using a publish/subscribe pattern:
Example:
Application A publishes messages on topic "messages from A", and all
applications interested on reading messages from A subscribe to that topic.
Adaptation focuses on facilitate building adaptors. adaptors are programs that are executed for an application subscribed to a topic, when a message is received on that topic.
Adaptation is highly inspired by Ruby on Rails web framework, so adaptors are built in a similar way that web apps are built using the RoR framework.
When building an adaptor, logic will be stored into Adaptation::Adaptor objects, and mapping of xml data messages will be performed by Adaptation::Message objects.
Adaptation can use ActiveRecord based models for data interaction with databases.
Installation
Adaptation is available as a ruby gem, so the easiest way should be:
> gem install adaptation
Usage
-
At the command prompt, start a new adaptation adaptor using the adaptation command and your adaptor name:
> adaptation myadaptor
This will generate a an adaptor file tree under folder myadaptor.
-
If no message oriented middleware (mom) has been already set, you can start one typing:
> mom
This will start a mom in localhost (default), listening on port 8080 (default).
-
Subscribe your adaptor to the mom, so it will be executed when a message is received on a topic your adaptor is interested in:
> ruby script/subscribe
By default this will try to subscribe to a mom listening on localhost:8080, using port 8081 to subscribe (subscribing means starting a new server that listens for message publication notifications). These values can be changed editing config/mom.yml. In mom.yml you can also specify wich topics your adaptor is interested in.
-
Right now you should have a mom listening for messages on localhost:8080, and an adaptor subscribed to that mom and listening on localhost:8081, and interested in all topics available.
This environment can be tested by executing the following from myadaptor folder:
> ruby script/publish NEWS '<helloworld/>'
The previous command should publish de xml <helloworld/> message into topic NEWS from the mom. This message should be displayed in the subscriber terminal when delivered by the mom. Nothing would be executed, because a Helloworld message to map this xml message and a HelloworldAdaptor[link:../rdoc/classes/Adaptation/Adaptor.html] to process it don’t exist yet. Since these classes aren’t implemented, Adaptation will pass the message as an Adaptation::Message object to the default ApplicationAdaptor adaptor, but its process method is still empty, so nothing will happen.
To see something happening the process method in the default ApplicationAdaptor could be implemented, editing file myadaptor/app/adaptors/application.rb:
class ApplicationAdaptor < Adaptation::Adaptor def process logger.info "Received message #{}" end end
Now, if the previous <helloword/> message is published, that should be visible in log/development.log.
The other way this can be done is by creating Helloworld Adaptation::Message class to map the xml data:
> ruby script/generate message helloworld exists app/messages/ exists test/unit/ exists test/fixtures/ create app/messages/helloworld.rb create test/unit/helloworld_test.rb create test/fixtures/helloworld.xml
The file we care about right now is app/messages/helloworld.rb:
class Helloworld < Adaptation::Message end
We can leave it like this by now, and proceed to generate the HelloworldAdaptor Adaptation::Adaptor class:
> ruby script/generate adaptor helloworld exists app/adaptors/ exists test/functional/ create app/adaptors/helloworld_adaptor.rb create test/functional/helloworld_adaptor_test.rb
and to edit app/adaptors/helloworld_adaptor to make something happen when a message is received:
class HelloworldAdaptor < ApplicationAdaptor def process helloworld logger.info "Received message: #{helloworld.to_xml.to_s}" end end
We can notice that helloworld variable is an instance of Helloworld class (because Adaptation has been able to map it to a subclass of Adaptation::Message object), and that the HelloworldAdaptor inherits from ApplicationAdaptor, so functionality repeated in different Adaptors[link:../rdoc/classes/Adaptation/Adaptor.html] can be placed in ApplicationAdaptor.
Moms
By default, Adaptation will try to use druby to execute the built-in Ruby mom. This mom is suitable for development, but not for production. For a production environment a more stable solution like Xmlblaster should be chosen.
Different message brokers can be configured in config/mom.yml, and example configuration for supported moms are present in the same file when an adaptor is generated with the adaptation command.
When we want to publish/subscribe to a mom different than the default druby, we can do so by adding the MOM=mom_type option:
> ruby script/subscribe MOM=xmlblaster
> ruby script/publish MOM=xmlblaster topic message
Description of an adaptor file tree:
app
Holds all the code that's specific to this particular adaptor.
app/adaptors
Holds adaptors that should be named like messagename_adaptor.rb for
automated mapping. All adaptors should descend from Adaptation::Adaptor.
app/messages
Holds messages that should be named like messagename.rb.
Messages descend from Adaptation::Message.
app/models
Holds models that should be named like post.rb.
Most models will descend from ActiveRecord::Base.
config
Configuration files for the Adaptation environment, the mom, the adapted application,
the database, and other dependencies.
db
Contains database related scripts.
doc
This directory is where your adaptor documentation will be stored.
lib
Application specific libraries. Basically, any kind of custom code that doesn't
belong under adaptors, models, or messages.
public
The directory available for calling the adaptor (contains dispatch.rb).
script
Helper scripts for automation and generation.
test
Unit and functional tests along with fixtures. When using the script/generate scripts, template
test files will be generated for you and placed in this directory.
Debugging
Adaptation includes a console from wich we can access Adaptation::Message and ActiveRecord classes defined in the adaptor we are working on.
To open the console type:
> ruby script/console [environment]
From the command prompt that should appear, we can instantiate ActiveRecord objects defined in app/models and Adaptation::Message objects defined in app/messages. With the previous example, where an Adaptation::Message subclass called Helloworld was defined in app/messages, we could do:
> ruby script/console test
$ >> Loading test environment (Adaptation X.X.X)
$ hw = HelloWorld.new("<helloworld>hello</helloworld>")
$ hw.content
$ >> "hello"
$ hw.to_xml
$ >> "<helloworld>hello</helloworld>"
We could edit app/messages/hellworld.rb to add a validation:
class Helloworld < Adaptation::Message
validates_presence_of :atr
end
and check if it works with the console:
> ruby script/console
$ >> Loading test environment (Adaptation X.X.X)
$ hw = HelloWorld.new("<helloworld>hello</helloworld>")
$ hw.valid?
$ >> false
$ hw = HelloWorld.new("<helloworld atr="something">hello</helloworld>")
$ hw.valid?
$ >> true
If we define ActiveRecord::Base subclasses in app/models we can do the same mapping database rows instead of xml messages, if config/database.yml is properly configured to access the database. If we had a users table in our database, we could generate a model to access it with ActiveRecord:
> ruby script/generate model user
exists app/models/
exists test/fixtures/
create app/models/user.rb
and use it from the console:
> ruby script/console
$ >> Loading test environment (Adaptation X.X.X)
$ u = User.find(:first)
Testing
As it can be seen when generating adaptors and messages, adaptation automatically generates empty functional tests for adaptors and empty unit tests for messages.
Tests can be run typing:
> ruby test.rb
Tests can use fixtures stored in test/fixtures folder. There are two kind of fixtures:
-
database fixtures: the same database fixtures a rails application uses to fill its tables in tests. These fixtures are usually stored in *.yml files, and are used to reset database contents before each test, so it is important to configure the test database properly in config/database.yml. More about ActiveRecord fixtures here.
-
xml fixtures: these fixtures are plain xml strored in *.xml, and are used to build Adaptation::Message objects and probably process them with our adaptor in the tests. These fixtures can also be created dynamically with Erb templetes. This is an example of a xml fixture built with Erb:
<20_people> <% 20.times do |c| %> <person num="<%= c %>"/> <% end %> </20_people>
For the HellworldAdaptor in our example, we should find a functional test in test/functional/helloworld_adaptor_test.rb, and for the Helloworld message a unit test in test/unit/helloworld_test.rb.
Tests usually perfrom assertions. There are a couple of methods that may be useful when testing adaptors, href="../../../../rdoc/classes/ActiveSupport/TestCase.html#M000044"> and href="../../../../rdoc/classes/ActiveSupport/TestCase.html#M000043">:
-
get_message_from_fixture: Returns an Adaptation::Message object from a fixture, without processing it (or an instance of the corresponding subclass, if it’s defined).
-
message: Builds a message from a xml fixture file and processes it the same way messages from the mom are processed by Adaptation, but using the test environment. Messages published with publish will be published to a mocked MOM (and can be checked with assert_message_published).