At XAOP we have been using our ActiveDCTM library for several years now to connect to Documentum systems from Ruby code. In this article we want to help you get on your way to using it yourself as successfully as we do.

Installation

As ActiveDCTM is a Ruby library, you will first have to install Ruby. You can find the details on how to do this for the most common platforms on the Ruby web site. We have only used ActiveDCTM with Ruby 1.8.x, so it is best to go with 1.8.6 if possible. Make sure you also install Rubygems as ActiveDCTM is currently distributed as a gem.

Next, download the activedctm and dctmruby gems. We have prebuilt gems for Linux, Windows and Solaris. If you would like to build from source, then you can find the source code for dctmruby on Rubyforge. In addition to installing these gems, you need a copy of the DMCL library. On Linux this library is called libdmcl40.so and you may need to set the LD_LIBRARY_PATH environment variable for it to be found. On Windows this library is called dmcl40.dll and should be placed in a location that is in your PATH. These libraries should be included in your Documentum installation.

You can test if your installation was successful by opening a command prompt and starting an irb session with the irb command:

irb

You will be presented with a prompt. At that prompt, type the following two lines:

require 'rubygems'
require 'activedctm'

If this succeeds with error, then you have successfully installed Ruby and ActiveDCTM, and you are ready to go.

Interactive shell

ActiveDCTM provides an interactive shell to connect to a given Documentum repository. This interactive shell is based on irb so you can evaluate Ruby expressions, define classes and methods, etc. As an extra this shell connects to Documentum using the parameters you specify on the command line and adds some convenient access to the Documentum session.

To connect to a repository, you have to run the dctm-connect.rb command and specify the docbroker and port, the name of the repository, your user name and password:

dctm-connect.rb -h <host:port> -d <repository> -u <user name> -p <password>

The port is optional and defaults to the default Docbroker port (1489).

If the connection is successful, you should be presented with a prompt. You can now type any Ruby expressions such as 1+1, but you can also access documents on the Documentum repository.

Types are classes

The types in the repository become classes in Ruby. It is Ruby convention to write class names in camelcase, just as in Java. This means that you reference a type such as dm_document as DmDocument:

s0@xaop:001:0> DmDocument
s0@xaop::DmDocument

The prompt shows that we have a session s0 with the xaop repository. When you type DmDocument, the class with that name is returned as an object. As you can see, the class prints itself with a prefix that includes the session and repository. The returned class is actually session specific because when you connect to different repositories, they may have different class definitions for the same name, including built-in classes that have changes over versions of Documentum. At the moment, you need not worry about this as we are connected to a single repository.

With the class being just another Ruby object, we can call methods on it:

s0@xaop:002:0> DmDocument.class
Class

s0@xaop:003:0> DmDocument.attributes
{:r_modify_date=>xaop::DmDocument#r_modify_date(Time), :object_name=>xaop::DmDocument#object_name(String255), :r_object_id=>xaop::DmDocument#r_object_id(ID), :authors=>xaop::DmDocument#authors[](String48), ...}

The attributes method returns a hash that maps attribute names to their types. The attribute names are symbols; they are a bit like an immutable string that is more efficient in storage and in comparisons such as in a hash.

Read access

Using the classes for the Documentum types, you can access pretty much all information in your Documentum repository. A few examples that show the capabilities follow:

s0@xaop:004:0> DmDocument.count
94

s0@xaop:005:0> d = DmDocument.first
#<s0@xaop::DmEsignTemplate:0901046b8000014e Default Signature Page Template>

s0@xaop:006:0> d.object_name
"Default Signature Page Template"

s0@xaop:007:0> d.r_creation_date
Sat May 22 11:05:28 +0200 2004

s0@xaop:008:0> d.authors
#<CompositeValue:0xb703e910 []>

s0@xaop:009:0> d.authors.to_a
[]

s0@xaop:010:0> d.i_folder_id
#<CompositeValue:0xb703a310 [#<s0@xaop::DmFolder:0b01046b8000014d /Integration/Esignature/Templates>]>

s0@xaop:011:0> d.i_folder_id.to_a
[#<s0@xaop::DmFolder:0b01046b8000014d /Integration/Esignature/Templates>]

s0@xaop:012:0> f = d.i_folder_id.to_a.first
#<s0@xaop::DmFolder:0b01046b8000014d /Integration/Esignature/Templates>

s0@xaop:013:0> f.class
s0@xaop::DmFolder

s0@xaop:014:0> d.contents.page(0).primary_content.get_file
"/tmp/dmcl/0001046b/01c1a8c0.hst/80001de7/Default Signature Page Template.doc"

Attributes such as dates and times are converted to the according Ruby types, repeating values are handled transparently by the CompositeValue class which tries to be Array-compatible, and attributes of type ID automatically are automatically replaced by the objects that they reference.

ActiveDCTM also has support for executing DQL queries. The DmDocument.count above actually is an example of this already. Here are a few more simple examples:

s0@xaop:015:0> DmDocument.count_by_dql("object_name LIKE 'a%'")
0

s0@xaop:016:0> DmDocument.count_by_dql("object_name LIKE '%Template%'")
5

s0@xaop:017:0> DmDocument.find_all_by_dql("object_name LIKE '%Template%'")
[#<s0@xaop::DmEsignTemplate:0901046b8000014e Default Signature Page Template>, #<s0@xaop::DmDocument:0901046b80000155 Blank Excel 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015b Blank Word 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015d Blank PowerPoint 97 / 2000 Template>, #<s0@xaop::DmMenuSystem:0901046b80000188 MenuSystemTemplate.ini>]

s0@xaop:018:0> DmDocument.find_all_by_dql("object_name LIKE '%Template%' AND FOLDER('/Templates', DESCEND)")
[#<s0@xaop::DmDocument:0901046b80000155 Blank Excel 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015b Blank Word 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015d Blank PowerPoint 97 / 2000 Template>, #<s0@xaop::DmMenuSystem:0901046b80000188 MenuSystemTemplate.ini>]

ActiveDCTM also supports the idea of scopes. This allows treating classes and scopes uniformly as sets of objects that you can query against:

s0@xaop:019:0> DmDocument.with_object_name_like("%Template%").count
5

s0@xaop:020:0> DmDocument.with_object_name_like("%Template%").all
[#<s0@xaop::DmEsignTemplate:0901046b8000014e Default Signature Page Template>, #<s0@xaop::DmDocument:0901046b80000155 Blank Excel 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015b Blank Word 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015d Blank PowerPoint 97 / 2000 Template>, #<s0@xaop::DmMenuSystem:0901046b80000188 MenuSystemTemplate.ini>]

s0@xaop:021:0> DmDocument.with_object_name_like("%Template%").in_folder("/Templates", :descend).all
[#<s0@xaop::DmDocument:0901046b80000155 Blank Excel 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015b Blank Word 97 / 2000 Template>, #<s0@xaop::DmDocument:0901046b8000015d Blank PowerPoint 97 / 2000 Template>, #<s0@xaop::DmMenuSystem:0901046b80000188 MenuSystemTemplate.ini>]

Creating documents

Creating documents is very easy:

s0@xaop:022:0> d = DmDocument.new
#<s0@xaop::DmDocument:0901046b80001d56 >

s0@xaop:023:0> d.object_name = "Test document"
"Test document"

s0@xaop:024:0> d.save
#<s0@xaop::DmDocument:0901046b80001d56 Test document>

s0@xaop:025:0> d.i_folder_id
#<CompositeValue:0xb7b4ea44 [#<s0@xaop::DmCabinet:0c01046b80000104 /Administrator>]>

This can also be done in a single method call:

s0@xaop:026:0> d = DmDocument.create(:object_name => "Test document")
#<s0@xaop::DmDocument:0901046b80001d57 Test document>

As you can see above, the document is by default created in your home cabinet. You can link it to a different folder like so:

s0@xaop:026 d.unlink("/Administrator")
true

s0@xaop:027:0> d.link("/Temp")
true

s0@xaop:028:0> d.link("/Templates")
true

s0@xaop:029:0> d.save
#<s0@xaop::DmDocument:0901046b80001d57 Test document>

s0@xaop:030:0> d.i_folder_id
#<CompositeValue:0xb7127ef8 [#<s0@xaop::DmCabinet:0c01046b80000106 /Temp>, #<s0@xaop::DmCabinet:0c01046b80000112 /Templates>]>

Adding content is a breeze:

s0@xaop:031:0> d.contents.set_page("Content_Server_53_object_ref.pdf", "pdf")
#<ActiveDCTM::ContentPage:0xb70c9d08 @primary_format="pdf", @renditions={}, @page=0, @document=#<s0@xaop::DmDocument:0901046b80001d57 Test document>, @primary_content=nil>

s0@xaop:032:0> d.save
#<s0@xaop::DmDocument:0901046b80001d57 Test document>

AtctiveDCTM supports the full model of multiple pages, primary content and renditions, including page modifiers.

Versioning documents

Creating a new version of a document can be done by the checkout-checkin mechanism as follows:

s0@xaop:033:0> d.checkout
#<s0@xaop::DmDocument:0901046b80001d57 Test document>

s0@xaop:034:0> d.authors << "Santa"
#<CompositeValue:0xb70c2f08 ["Santa"]>

s0@xaop:035:0> d1 = d.checkin
#<s0@xaop::DmDocument:0901046b80001d59 Test document>

s0@xaop:036:0> d1.r_version_label
#<CompositeValue:0xb70bc680 ["1.1", "CURRENT"]>

s0@xaop:037:0> d.r_version_label
#<CompositeValue:0xb70b9e58 ["1.0"]>

s0@xaop:038:0> d.versions
{"CURRENT"=>#<s0@xaop::DmDocument:0901046b80001d59 Test document>, "1.0"=>#<s0@xaop::DmDocument:0901046b80001d57 Test document>, "1.1"=>#<s0@xaop::DmDocument:0901046b80001d59 Test document>}

s0@xaop:039:0> d1.checkout
#<s0@xaop::DmDocument:0901046b80001d59 Test document>

s0@xaop:040:0> d1.authors << "Rudolph"
#<CompositeValue:0xb7089b18 ["Santa", "Rudolph"]>

s0@xaop:041:0> d2 = d1.checkin("2.0")
#<s0@xaop::DmDocument:0901046b80001d5d Test document>

s0@xaop:042:0> d.versions
{"CURRENT"=>#<s0@xaop::DmDocument:0901046b80001d59 Test document>, "1.0"=>#<s0@xaop::DmDocument:0901046b80001d57 Test document>, "2.0"=>#<s0@xaop::DmDocument:0901046b80001d5d Test document>, "1.1"=>#<s0@xaop::DmDocument:0901046b80001d59 Test document>}

Using ActiveDCTM in your own programs

You can also use ActiveDCTM in non-interactive Ruby scripts. An example of this is given below:

require 'rubygems'
require 'activedctm'

ActiveDCTM.connect("primary_host" => "<host>", "primary_port" => <port>, "repository" => "<repository>", "login" => "<login>", "password" => "<password>") do |session|

p session::DmDocument.with_object_name_like("%Template%").count

end

As before, the port is optional.

The main difference with dctm-connect.rb is that the session is now explicit and you need to access the classes from there using the :: scope operator. Actually, the session in dctm-connect.rb is the current object that can be accessed as self:

s0@xaop:001:0> self
s0@xaop

s0@xaop:002:0> self::DmDocument
s0@xaop::DmDocument

Further reading

We are still working on comprehensive documentation for ActiveDCTM. The main obstacle for us is that we generate classes on the fly and none of the popular documentation systems seem to support this very well. Visit the XAOP Labs for more information.

blog comments powered by Disqus

Entries per category

  1. 9 pages are tagged with documentum
  2. 12 pages are tagged with events
  3. 14 pages are tagged with rails
  4. 32 pages are tagged with ruby
  5. 13 pages are tagged with sharepoint

Recent Comments

Popular Threads