RAILS 2.1 – :through

July 17, 2008

Active Record has an interesting feature :through. It helps to create the association through the association. For example:

class Court < ActiveRecord::Base
has_many :court_users
has_many users :through=> :court_users
end

class CourtUser < ActiveRecord::Base
belongs_to :court
belongs_to :user
end

In the above example court has_many users through the court_users since the association between court and the user is exists only in the court_users table.

Before 2.1, only has_many method has :through. Now, has_one method has the option :through

Dynamic Attributes

July 15, 2008

Did you ever faced the situation where you need to store the object in the table column? Did you ever faced the situation where you need to add the attribute dynamically to the table?

Rails allows us to store the object in the table column using ‘serialize’. This is somewhat tricky to explain the use of storing the object in the database. You can easily understand when you face the scenario. Let’s think out a simple example:

create_table :users do t
t.string :login
t.string :email
t.string :address
end

Note that address is a string field.

class user < ActiveRecord::Base
serialize :address
end

We can save the address as follows:

address = Addres.new(params[:address])
user = User.first
user.address = address
user.save

You can also store hash in the table column like below:

user = User.first
user.address[:street]= “Mettu Street”
user.address[:zip]= 23456
user.save

Use this concept when we want to add attribute to the table dynamically

Rails composed_of

July 10, 2008

Initially, I had the difficulty in understanding the composed_of.
It is very simple. It is used to associate the value object to the class (Newbie’s, please note that the object is value object, not the active record object). Let’s say an example we used in our project.

We have the FilterField class and SortField class. If you notice the common thing in both the classes you will come to know both are field classes. Right?

Why don’t we have the seperate class for Field and associate it to FilterField class and SortField class. This will help us to group the field related attributes in Field class, sorting related attributes in SortField class and filter related attributes in FilterField class.

class Field
attr_reader :class_name
attr_reader :attribute_name
def initialize (class_name, attribute_name)
@class_name = class_name
@attribute_name = attribute_name
end
end

class SortField < ActiveRecord::Base
composed_of :query_field, :class_name=>’Field’,
:mapping=>[%w(class_name class_name), %w(attribute_name attribute_name)]
end

class FilterField< ActiveRecord::Base
composed_of :query_field, :class_name=>’Field’,
:mapping=>[%w(class_name class_name), %w(attribute_name attribute_name)]
end

SortField object can be created like below:

field => Field.new(:class_name=>’Court’, :attribute_name=> ‘Court_Name’)
SortField.new(:query_field => field, :sorting_order => ‘Ascending’)

Filters and Observers in Rails

July 8, 2008

Though I’m a little late to the Rails party. If you have yet to experience the goodness of these this is the place where you need to peep into.

Observers:

This is a great way to reduce the clutter that normally comes when the model class is burdened with functionality that doesn‘t pertain to the core responsibility of the class. Observer classes respond to lifecycle callbacks to implement trigger-like behavior outside the original class.

In my project I had a requirement to perform some action after creating every actor. Instead loading the model we decided to move it to the observers.

A better example where to use observers would be. Consider in a forum kind of application, If any new comment is added to the topic all the people in the group has to be sent a notification-mail about the comment.

Instead having the sending mail part in the model we can have it in a observer so that it’ll get triggered after every save action.

class CommentObserver < ActiveRecord::Observer
    def after_save(comment)
      Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)
    end
end

This Observer sends an email when a Comment#save is finished.

Observers will by default be mapped to the class with which they share a name.If you want to name your observer differently than the class you‘re interested in observing, you can use the Observer.observe class method which takes either the concrete class (Product) or a symbol for that class (:product)

class AuditObserver < ActiveRecord::Observer
    observe :account, :balance
    def after_update(record)
      AuditTrail.new(record, "UPDATED")
    end
  end

The AuditObserver will now act on both updates to Account and Balance by treating them both as records.

Storing Observers in Rails
If you‘re using Active Record within Rails, observer classes are usually stored in app/models with the naming convention of app/models/audit_observer.rb.

Configuration

In order to activate an observer, list it in the config.active_record.observers configuration setting in your config/environment.rb file.

  config.active_record.observers = :comment_observer, :signup_observer

Observers will not be invoked unless you define these in your application configuration.

Some call backs available in rails are

  • save
  • valid
  • before_validation
  • before_validation_on_create
  • validate
  • validate_on_create
  • after_validation
  • after_validation_on_create
  • before_save
  • before_create
  • create
  • after_create
  • after_save

Enjoy the magic of Rails using these observers…….

We’ll see how to use filters in rest of the post.

Filters:

Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do authentication, caching, or auditing before the intended action is performed. Or to do localization or output compression after the action has been performed. Filters have access to the request, response, and all the instance variables set by other filters in the chain or by the action (in the case of after filters).

Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without affecting the superclass. For example:

class BankController < ActionController::Base
    before_filter :login_required,:audit
    private
      def login_required
        # Check user authentication
      end
      def audit
        # record the action and parameters in an audit log
      end
end
class VaultController < BankController
    before_filter :verify_credentials
    private
      def verify_credentials
        # make sure the user is allowed into the vault
      end
end

Few calls back methods available with rails are

  • after_filter
  • append_after_filter
  • append_around_filter
  • append_before_filter
  • around_filter
  • before_filter
  • filter_chain
  • prepend_after_filter
  • prepend_around_filter
  • prepend_before_filter
  • skip_after_filter
  • skip_before_filter
  • skip_filter

Skipping a filter

How to skip a filter?

before_filter :login_required, :except => :create
before_filter :login_required, :o nly => :show

Or using methods like
Skip_before_filter :login_required

Integrating the scanner device to ROR application

July 3, 2008

We got the requirement to scan and upload the image to the web application. We have analyzed all the commercial twain solutions like DynamicWebTwain, csxImage,TwainControlX,TwainConnect, jTwain, JSane and others. Many of the commercial solutions are very costly. Finally, we picked up the DynamicWebTwain for the following reasons:

1) DynamSoft is a Microsoft Gold Certified Partner
2) Stable software
3) The customers of DynamicWebTwain include IBM, HP and LockHeed Martin
4) Compatible with IE, FireFox and Mozilla
5) Good Technical Support
6) Cheaper one
7) Supports basic image editing features including Rotate, Crop, Miror, Flip and Change Image size

We used Ruby on Rails for our web application. Since the DynamicWebTwain is the ActiveX objects, we simply embeded the ActiveX object using html object tag in the rhtml page. We called the object methods through javascript and uploaded the image to the URL. Here is the javascript code used by us to upload the image.

var CurrentPath = CurrentPathName.substring(0, CurrentPathName.lastIndexOf(“/”) + 1);
strActionPage = CurrentPath + “court_images/save_scanned_image?court_id=”+court_id;
frmScan.DynamicWebTwain1.HTTPPort = 3000;
frmScan.DynamicWebTwain1.HTTPUploadThroughPost(“aspire76″, 0, strActionPage, “imageData.pdf”);

To the ROR developers, we used attachment_fu plugin to save the image in the file system. Here is the code for save_scanned_image action (See the above URL)

params.sanitize_keys!
court_image = CourtImage.new(:uploaded_data => params[:RemoteFile],:court_id => params[:court_id] )
respond_to do format
if court_image.save
format.html{render :text=>”#{court_image.id}”}

court_image is the active_recod which has_one attachment

has_attachment :storage => :file_system,
:size => 0.kilobytes..1.megabytes,
:resize_to => ’320×200>’,
:thumbnails => { :thumb => ’100×100>’ }

Please note the below code, where we are rendering back the court image id through response. We can receive the response in javascript where we called the URL to upload the image (see above). Here is the javascript code to get the response:

var court_image_id = frmScan.DynamicWebTwain1.HTTPPostResponseString;

Hope, this will help the ROR and other web application developers to integrate the scanner device to the web application. The scanner device should be connected to the client (browser) system.

Behaviour-Driven Development – Have a definite edge in the outsourced software development

June 16, 2008

Behaviour-driven development is an “outside-in” methodology. It starts at the outside by identifying business outcomes, and then drills down into the feature set that will achieve those outcomes. Each feature is captured as a “story”, which defines the scope of the feature along with its acceptance criteria” – DAN NORTH

Behaviour-driven development is the test-first approach agile development which focus on design, documentation and behaviour. “Behaviour” makes more sense than “Test” since developers code for the behaviour rather test.

Challenges in outsourced development

Communication is the major challenge in the outsourced software development.

Developers often complaint that they are not getting the detail requirements and design, and they are doing the requirement analysis and design analysis though it is done already. They back-up their delay in delivery with this reason.

Is it true?

Let’s assume the project is extended team model where the requirement analysis and design is done at customer end, and developer needs to follow the design and do the implementation. The customer needs to send the requirements and design in detail. If that doesn’t happen, then the developer needs to analyse the requirements and design though it is done already if he needs to deliver what the customer wants.

Let’s discuss some other challenges. Customer often complaint that the code is not met the expected behaviours (requirements). This might be because the developer not understood the expected behaviours or it is not communicated to him properly. There should be proper communication between the co-ordinator and the developer. Our experience proves that all the stake holders should be aligned on the requirements and there should be a tool to support that.

Customer expects the working output in short iterations, and the world is moving towards the agile development. They provide the requirements in call/email, and the developers needs to deliver it in short iteration. The project manager needs to pick up the best agile method to deliver the output in short iteration

One common challenge in the software development is delivering the quality output and adding the value to the customer. How can we make sure the code quality is good? One way is doing the code review as per coding standards. The other way is test-first approach. It is imperative to do the test first approach to meet the customer expectations. The TDD and BDD facilitates the test-first approach.

Behaviour-Driven Development

Experience

We started exploring the BDD before 5 months and following the practice in one of our Ruby-On-Rails project. We picked up the BDD for the following reasons:

1) We are working on the extended team model. The customer do the requirement analysis and most of the design. Our people needs to do the implementation. Our people do the requirement analysis and the design analysis though it is already done, and create the documentation for the CMMI process. We wanted to come out of the repetition and improve the productivity of the developers. We initially expected the detail requirement document from the customer, but the customer didn’t find time to create the detail word document. BDD helps us to communicate the requirements through text stories, and this is also serves as documentation for the CMMI process.

2) We wanted to make sure our code is accepted by the customer by acceptence testing

3) Agile Development

We tried out TDD initially, but that didn’t work out. It facilitated the unit testing and acceptence testing but we communicated the requirements and design through basecamp.

Then we tried out BDD text stories. The customer is expected to send the plain text stories with all the scenarios. The developer’s work is nailed down here. He just code for the scenarios. We pick up one feature per iteration (Each feature is a RSPEC story). Normaly we deliver in very short iterations (2-3 days). We do SVN check-in the code and the spec. Customer run the spec for acceptence testing

We use RSPEC for BDD development since our technology is Ruby On Rails. There are some other BDD frameworks available for Java, PHP and .NET

Behaviour-Driven Development

Lifecyle

How to Integrate Crystal Reports with Ruby on Rails

June 13, 2008

This article explains how the crystal report can be integrated with Ruby on Rails application. With reference from http://wiki.rubyonrails.org/rails/pages/howtointegratejasperreports, I integrated the crystal reports with my ROR application. Thanks to the publisher of that article.

As of now, there is no API for integrating Crystal Reports with ROR. But we can achieve this with the Java API. We can run the java classes from ROR application to generate the crystal report.

Steps for Integration:

1. Placing the jar files in the ROR application

2. Creating the java classes to generate the report with the given inputs

3. Generating the XML and Schema in ROR application

4. Writing the business logic to run the java class that generates the report

5. Writing the controller code

1. Placing the jar files in the ROR application:

We have to keep the following jar files in our application.

cecore.jar
celib.jar
commons-collections-3.1.jar
commons-configuration-1.2.jar
commons-discovery.jar
commons-lang-2.1.jar
commons-logging.jar
Concurrent.jar
corbaidl.jar
CRDBJavaServerCommon.jar
CRDBXMLExternal.jar
CRDBXMLServer.jar
CrystalCharting.jar
CrystalCommon.jar
CrystalContentModels.jar
CrystalDatabaseConnectors.jar
CrystalExporters.jar
CrystalExportingBase.jar
CrystalFormulas.jar
CrystalQueryEngine.jar
CrystalReportEngine.jar
CrystalReportingCommon.jar
ebus405.jar
icu4j.jar
jrcadapter.jar
jrcerom.jar
keycodeDecoder.jar
log4j.jar
MetafileRenderer.jar
pullparser.jar
rasapp.jar
rascore.jar
ReportViewer.jar
rpoifs.jar
serialization.jar
xbean.jar
xercesImpl.jar
xml-apis.jar

I placed these jar files in <MY_RAILS_APP_ROOT>/public/jars/crystal folder. You can place these jar files wherever you want. You can also check and remove some jar files if they won’t be needed.

2. Creating the java classes to generate the report with the given inputs:

Now we have to create a java class that communicates with the API and generates the report for the given inputs. The following class gets the xml data file path, crystal report template file path and the xml schema content as command-line arguments. Please note that I am reading the xml data from the local file system and the xml schema as a command-line argument. You can use either the file system or the command-line for both XML and Schema.

XmlCrystalInterface.java:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.PrintWriter;

import com.crystaldecisions.reports.sdk.*;
import com.crystaldecisions.sdk.occa.report.data.*;
import com.crystaldecisions.sdk.occa.report.exportoptions.ExportOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.PDFExportFormatOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.ReportExportFormat;
import com.crystaldecisions.sdk.occa.report.lib.*;
import com.crystaldecisions.sdk.occa.report.reportsource.IReportSource;

/**
* Takes xml data file path, crystal report template path and xml schema string as arguments
* and generates the report for the given data with the given template design
*/
public class XmlCrystalInterface {

public static void main(String[] args) {
String reportDesign = null;
String xmlSchema = null;
String xmlFilePath = null;

// Parse the arguments
for (int indexOfArgs = 0; indexOfArgs < args.length; indexOfArgs++)
if (args[indexOfArgs].startsWith(”-f”))
reportDesign = args[indexOfArgs].substring(2);
else if (args[indexOfArgs].startsWith(”-x”))
xmlFilePath = args[indexOfArgs].substring(2);
else if (args[indexOfArgs].startsWith(”-s”))
xmlSchema = args[indexOfArgs].substring(2);

try {
// Open the report
ReportClientDocument reportClientDocument = new ReportClientDocument();
reportClientDocument.open(reportDesign, 0);

// Read the xml file
File xmlFile = new File(xmlFilePath);
InputStream inputStream = new FileInputStream(xmlFile);
byte[] xmlByteArray = new byte[inputStream.available()];
inputStream.read(xmlByteArray);
inputStream.close();

// Create the byte array for xml data
IByteArray xmlDataByteArray = new ByteArray(xmlByteArray);

// Create the xml schema byte array
byte[] sByteArray = new byte[xmlSchema.length()];
sByteArray = xmlSchema.getBytes();
IByteArray xmlScehmaByteArray = new ByteArray(sByteArray);

// Create the XmlDataSet and add the xml data and shema
IXMLDataSet xmlDataSet = new XMLDataSet();
xmlDataSet.setXMLData(xmlDataByteArray);
xmlDataSet.setXMLSchema(xmlScehmaByteArray);

// Set the data source as the xml data source
reportClientDocument.getDatabaseController().setDataSource(
xmlDataSet, “”, “”);

// Open the report in the Crystal Report Viewer

IReportSource reportSource = reportClientDocument.getReportSource();
reportClientDocument.close();

ExportOptions expOpt = new ExportOptions();
expOpt.setExportFormatType(ReportExportFormat.PDF);
PDFExportFormatOptions pdfExpOpt = new PDFExportFormatOptions();
expOpt.setFormatOptions(pdfExpOpt);

ReportStateInfoImpl repStateInfo = new ReportStateInfoImpl();
RequestContextImpl reqCont = new RequestContextImpl();
reqCont.setReportStateInfo(repStateInfo);

InputStream byteArrayInputStream = reportSource.export(expOpt, reqCont);
byte reportByteArray[] = new byte[byteArrayInputStream.available()];
byteArrayInputStream.read(reportByteArray);
byteArrayInputStream.close();

System.out.write(reportByteArray);
} catch (Exception e) {
PrintWriter out = null;
try {
// Write the exception in the log file
out = new PrintWriter(”./log/XmlCrystalInterface.log”);
out.write(e.getMessage());
} catch (FileNotFoundException filenotfoundexception) {
}
out.close();
}
}
}

Two more classes are needed to set the report export options.

RequestContextImpl.java:
class RequestContextImpl implements
com.crystaldecisions.sdk.occa.report.reportsource.IRequestContext {

private com.crystaldecisions.sdk.occa.report.reportsource.IReportStateInfo repStateInfo = null;

public com.crystaldecisions.sdk.occa.report.reportsource.ISubreportRequestContext getSubreportRequestContext() {
return null;
}

public com.crystaldecisions.sdk.occa.report.reportsource.ITotallerNodeID getTotallerNodeID() {
return null;
}

public void setSubreportRequestContext(
com.crystaldecisions.sdk.occa.report.reportsource.ISubreportRequestContext arg0) {
}

public void setTotallerNodeID(
com.crystaldecisions.sdk.occa.report.reportsource.ITotallerNodeID arg0) {
}

public com.crystaldecisions.sdk.occa.report.lib.PropertyBag getClientCapability() {
return null;
}

public com.crystaldecisions.sdk.occa.report.reportsource.IReportStateInfo getReportStateInfo() {
return this.repStateInfo;
}

public void setClientCapability(
com.crystaldecisions.sdk.occa.report.lib.PropertyBag arg0) {
}

public void setReportStateInfo(
com.crystaldecisions.sdk.occa.report.reportsource.IReportStateInfo iReportStateInfo) {
this.repStateInfo = iReportStateInfo;
}

}

ReportStateInfoImpl.java:
import com.crystaldecisions.sdk.occa.report.data.ConnectionInfos;

class ReportStateInfoImpl implements
com.crystaldecisions.sdk.occa.report.reportsource.IReportStateInfo {

private com.crystaldecisions.sdk.occa.report.data.Fields parameters = null;

public com.crystaldecisions.sdk.occa.report.data.ConnectionInfos getDatabaseOnInfos() {
return null;
}

public String getGroupSelectionFormula() {
return null;
}

public com.crystaldecisions.sdk.occa.report.data.Fields getParameterFields() {
return this.parameters;
}

public String getSelectionFormula() {
return null;
}

public String getViewTimeSelectionFormula() {
return null;
}

public void setDatabaseLogOnInfos(
com.crystaldecisions.sdk.occa.report.data.ConnectionInfos arg0) {
}

public void setGroupSelectionFormula(String arg0) {
}

public void setParameterFields(
com.crystaldecisions.sdk.occa.report.data.Fields parameters) {
this.parameters = parameters;
}

public void setSelectionFormula(String arg0) {
}

public void setViewTimeSelectionFormula(String arg0) {
}

public Object clone(boolean arg0) {
return null;
}

public void copyTo(Object arg0, boolean arg1) {
}

public ConnectionInfos getDatabaseLogOnInfos() {
return null;
}

public boolean hasContent(Object arg0) {
return false;
}
}

Create class files for these classes and place it in a folder. I placed these classes in “<MY_RAILS_APP_ROOT>/public/classes/crystal” folder. Also add a log4j.properties file along with the java class files.

log4j.properties:

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE
#log4j.rootCategory=INFO, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.Threshold=INFO
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x – %m%n

The java environment has been set up now. Now we have to integrate these with our ROR application.

3. Generating the XML and Schema in ROR application:

We can generate the xml by iterating the result objects in the view file and rendering it as a string. The following is the code which generates the xml from the resuts.

export_report.rxml:

xml.instruct!
xml.report do
@results.each do |result|
xml << result.to_xml(:dasherize=>false,:skip_instruct=>true, :root => ‘your_tag’)
end
end

For generating the xml schema, to my knowledge, there is no built-in approach in ruby. So I have generated the schema according to my XML. I have written the method in crystal_report_generator.rb(see below) to generate my XML schema. You can customize as per your application need.

4. Writing the business logic to run the java class that generates the report:

Now we have to write the business logic that runs the java class and returns the report content. The “generate_report” method accepts the xml data, xml schema and the report design template path as arguments. Class path is set for all the jar files and java class files and the “XmlCrystalInterface ” java class is run with the command-line arguments. It is just like running the java class in the command prompt. The output is read from the stream as a string.

crystal_report_generator.rb:

class CrystalReportGenerator
include Config

# Generates the crystal report with the help of XmlCrystalInterface java class. Returns the String output(PDF formatted)
# to the controller. The Controller will send the string output to the browser in the PDF format.
def self.generate_report(xml_data, xml_schema, report_design)

xml_temp_file_path = Dir.getwd+”/tmp/xml_tmp.xml”
# Create a temporary xml file for xml_data
xml_file = File.new(xml_temp_file_path, “w”)
xml_file.print(xml_data)
xml_file.close

# Set the class path for java class files and jar files
interface_classpath=Dir.getwd+”/public/classes/crystal”
case CONFIG['host']
when /mswin32/
Dir.foreach(Dir.getwd+”/public/jars/crystal”) do |file|
interface_classpath << “;#{Dir.getwd}/public/jars/crystal/”+file if (file != ‘.’ and file != ‘..’ and file.match(/.jar/))
end
else
Dir.foreach(Dir.getwd+”/public/jars/crystal”) do |file|
interface_classpath << “:#{Dir.getwd}/public/jars/crystal/”+file if (file != ‘.’ and file != ‘..’ and file.match(/.jar/))
end
end

result = “”
# Pass the arguments and run the java class to generate the report
IO.popen “java -cp \”#{interface_classpath}\” XmlCrystalInterface \”-x#{xml_temp_file_path}\” \”-f#{report_design}\” \”-s#{xml_schema}\””, “w+b” do |pipe|
# Read the result pdf content from the stream
result = pipe.read
pipe.close
end

# Delete the temporary xml file
File.delete(xml_temp_file_path) if File.exists?(xml_temp_file_path)
return result
end

# Generate the xml schema for the query results
def self.build_xml_schema(header_element, body_elements)
schema = “<?xml version=’1.0′ encoding=’utf-8′?>”
schema << “<xs:schema version=’1.0′ xmlns:xs=’http://www.w3.org/2001/XMLSchema’>”
schema << “<xs:element name=’report’>”
schema << “<xs:complexType><xs:sequence>”
schema << “<xs:element maxOccurs=’unbounded’ name=’#{header_element}’ minOccurs=’0′>”
schema << “<xs:complexType><xs:sequence>”
body_elements.each do |element|
schema << “<xs:element name=’#{element.to_s(:underscored)}’ minOccurs=’0′/>”
end
schema << “</xs:sequence></xs:complexType></xs:element>”
schema << “</xs:sequence></xs:complexType></xs:element></xs:schema>”
return schema

end

end

5. Writing the controller code:

Here I am generating the XML and XML Schema and calling the CrystalReportGenerator.generate_report method to generate the report. The result is sent as a PDF to the user.


def export_report
# Your code here
xml_data = “”
file_name = # Your code here. File name with which the report should be saved or opened
@results = # Your code here. This is an array of result objects.
body_elements = # Your code here. xml tag names for generating schema.
template_file_path = # Your code here. Path of the report design template file.
xml_data = escape_xml_data(render_to_string(:template => ‘my_controller/export_report’, :layout => false))

xml_schema = CrystalReportGenerator.build_xml_schema(’header_element’, body_elements)
send_data CrystalReportGenerator.generate_report(xml_data, xml_schema, template_file_path),
:filename => “#{file_name}.pdf”, :type => ‘application/pdf’

end

private

# Parse and sanitize the xml data
def escape_xml_data(xml_data)
xml_data = xml_data.gsub(”\n”, ”)
xml_data = xml_data.gsub(”\””, “‘”)
xml_data = xml_data.gsub(/[\>]([ ])*[\<]/, ‘><’)
return xml_data
end

This approach worked fine for me. You can customize any code or approach according to your application need.

Load balancing in ROR

April 2, 2008

Load balancing is dividing the amount of work that a computer has to do between two or more processors or computers so that more work gets done in the same amount of time and, in general, all users get served faster. Load balancing can be implemented with hardware, software, or a combination of both. Typically, load balancing is the main reason for computer server clustering.

Load Balancing in ROR

Caching In ROR

April 2, 2008

Caching, in the web application world, is the art of taking a processed web page (or part of a webpage), and storing it in a temporary location. If another user requests this same webpage, then we can serve up the cached version.Loading up a cached webpage can not only save us from having to do ANY database queries, it can even allow us to serve up websites without touching our Ruby on Rails Server.

Caching in ROR

Multiple Database Connection

April 2, 2008

Multiple database connection is very easy in ruby on rails. It will increase the scalability of any website. We did the premature optimization of connecting the application to multiple databases. The objective here is to reduce the database load if needed. All the frequently used tables are grouped in the single database.

Multiple database connection


Follow

Get every new post delivered to your Inbox.