<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
        <title>Ruby - Blogs at Near Infinity</title>


        <link>http://www.nearinfinity.com/blogs/</link>
        <description>Employee Blogs</description>
        <language>en</language>
        <copyright>Copyright 2012</copyright>
        <lastBuildDate>Mon, 22 Aug 2011 09:42:18 -0500</lastBuildDate>
        <generator>http://www.sixapart.com/movabletype/</generator>
        <docs>http://www.rssboard.org/rss-specification</docs>
        
        <item>
            <title>Rails 3 Performance Monitoring with System Metrics</title>
            <description><![CDATA[<p>System Metrics is a new Rails 3 Engine providing a clean web interface to the performance metrics instrumented with <code>ActiveSupport::Notifications</code>. It will collect and display the notifications baked into Rails and any additional custom ones you add using <code>ActiveSupport::Notification#instrument</code>.</p>

<p>System Metrics is not intended to be a replacement for performance monitoring solutions such as New Relic. However, it is especially handy for quickly identifying performance problems in a development environment. It's also a great alternative for private networks disconnected from the Internet.</p>

<p>You can find more information about System Metrics on the <a href="http://nearinfinity.github.com/system-metrics">System Metrics site</a>. Please kick the tires and let us know what you think.</p>

<p><img src="/blogs/2011/08/22/system-metrics/smdetails.png" alt="System Metrics Detail View" title="" /></p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/jeff_kunkle/rails_3_performance_monitoring.html</link>
            <guid>http://www.nearinfinity.com/blogs/jeff_kunkle/rails_3_performance_monitoring.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
            <pubDate>Mon, 22 Aug 2011 09:42:18 -0500</pubDate>
        </item>
        
        <item>
            <title>Lucene Thrift and Ruby</title>
            <description><![CDATA[ This post is going to demonstrate thrift usage by searching a Lucene index from Ruby.
<h3>Thrift In a Nutshell</h3>
Essentially thrift is a serialization and RPC framework that allows you to communicate between programs that are not necessarily written in the same language.  Thrift is used by defining data types and services in a .thrift file.  You then run the .thrift file against the thrift compiler which generates the stub code needed for clients and servers.  Currently thrift will generate code for C++, C#, Erlang, Haskell, Java, Objective C/Cocoa, OCaml, Perl, PHP, Python, Ruby, and Squeak.  For a more detailed description of thrift along with instructions on how to install thrift if needed,  consult the <a href="http://wiki.apache.org/thrift/" target="new">thrift wiki</a>

<h3>Generating the Lucene Index</h3>
Our first step is to generate a small, simple lucene index.  To build our index, 50,000 fake person records were downloaded from the <a href="http://www.fakenamegenerator.com/order.php" target="new">Fake Name Generator</a> in a comma delimited file.  Each person record will contain a  first name, last name, address and email address.  Our indexing code will be very simple and will not be using any of lucene's advanced features.
<pre class="prettyprint">
public class IndexBuilder {

    public static void main(String[] args) throws Exception {
        String namesFile = "names.csv";
        Document doc = new Document();
        Field[] fields = new Field[]{new Field("firstName", "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS),
                new Field("lastName", "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS),
                new Field("address", "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS),
                new Field("email", "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS)};
        addFieldsToDocument(doc, fields);

        BufferedReader reader = new BufferedReader(new FileReader(namesFile));

        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("blog-index")),new IndexWriterConfig(Version.LUCENE_31, new StandardAnalyzer(Version.LUCENE_31)));

        String line;
        while ((line = reader.readLine()) != null) {
            String[] personData = getPersonData(line);
            setFieldData(personData, fields);
            indexWriter.addDocument(doc);
        }
        indexWriter.optimize();
        indexWriter.close();
    }

    private static String[] getPersonData(String line) {
        return line.split(",");
    }

    private static void setFieldData(String[] data, Field[] fields) {
        int index = 0;
        for (Field field : fields) {
            field.setValue(data[index++]);
        }
    }

    private static void addFieldsToDocument(Document doc, Field[] fields) {
        for (Field field : fields) {
            doc.add(field);
        }
    }
}
</pre> 
<h3>Creating the .thrift File </h3>
The next step will be to define what objects and services we want in our .thrift file, which will be called lucene_search.thrift.  The lucene_search.thrift file is intentionally very basic. For more details on the structure of .thrift files consult the <a href="http://wiki.apache.org/thrift/Tutorial" target="new">thrift wiki tutorial</a>
<pre class="prettyprint">
//all generated java code will have the following for package name
namespace java bbejeck.thrift.gen

//this is the person object 
struct Person {
  1: string firstName,
  2: string lastName,
  3: string address,
  4: string email
}

//exception used to send meaningful error messages back to user
exception LuceneSearchException {
  1: string message
}

//service definition used by client and server
service LuceneSearch { 
    list&lt;Person&gt; search(1: string query) throws (1:LuceneSearchException error) 
}
</pre>
As you can see from the example above, the .thrift file format is completely language agnostic. Next we need to generate our java and ruby code.   The following were run from the command line:
<ul>
<li>$ thrift --gen java lucene_search.thrift</li>
<li> $ thrift --gen rb lucene_search.thrift</li>
</ul>
The generated code ends up in two directories named gen-java/ and gen-rb/ respectively. The files generated for java are LuceneSearch.java, LuceneSearchException.java and Person.java.  The generated ruby files are lucene_search.rb, lucene_search_types.rb and lucene_search_constants.rb.   In our next step, we are going to use generated java code to write our thrift server.
<h3>Thrift Server - Java</h3>
Thrift generates all the stub code you need for a server to expose your service or program.  The only code we will need to write is a class that implements the generated Iface interface (defined in the LuceneSearch class), which contains the search method defined in our .thrift file.  
<pre class="prettyprint">
public class LuceneThriftServer {
    private static final int PORT = 9090;
    private static int numberThreads = 5;

    public static void main(String[] args) throws Exception {
        TServerSocket serverSocket = new TServerSocket(PORT, 100000);
        LuceneSearch.Processor searchProcessor = new LuceneSearch.Processor(new SearchHandler(args[0]));
        if (args.length > 1) {
            numberThreads = Integer.parseInt(args[1]);
        }
        TThreadPoolServer.Args serverArgs = new TThreadPoolServer.Args(serverSocket);
        serverArgs.maxWorkerThreads(numberThreads);
        TServer thriftServer = new TThreadPoolServer(serverArgs.processor(searchProcessor).protocolFactory(new TBinaryProtocol.Factory()));
        thriftServer.serve();
    }
</pre> 
<h3>Iface Implementation </h3>
The SearchHandler class actually does the work of searching the lucene index.  One tradeoff made here is that any exception while searching is caught and re-thrown as a LuceneSearchException.  While it's usually not a great idea to just re-throw an exception, in this case it makes sense to do so.  Since the LuceneSearchException is defined in the lucene_search.thrift file, the generated client code will handle that exception.  So instead of receiving a generic thrift exception when an error occurs, the client should receive a more meaningful error message. 
<pre class="prettyprint">
public class SearchHandler implements LuceneSearch.Iface {
    private IndexSearcher searcher;
    private QueryParser queryParser;
    private static final int MAX_RESULTS = 1000;

    public SearchHandler(String indexPath) {
        try {
            searcher = new IndexSearcher(FSDirectory.open(new File(indexPath)), true);
            queryParser = new QueryParser(Version.LUCENE_31, null, new StandardAnalyzer(Version.LUCENE_31));
            queryParser.setAllowLeadingWildcard(true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public List&lt;Person&gt; search(String query) throws LuceneSearchException {
        List&lt;Person&gt; results = new ArrayList&lt;Person&gt;();
        try {
            Query q = queryParser.parse(query);
            TopDocs topDocs = searcher.search(q, MAX_RESULTS);
            for (ScoreDoc sd : topDocs.scoreDocs) {
                Document document = searcher.doc(sd.doc);
                results.add(getPersonFromDocument(document));
            }
        } catch (Exception e) {
            throw new LuceneSearchException(e.getMessage());
        }
        return results;
    }

    private Person getPersonFromDocument(Document document) {
        Person p = new Person();
        p.firstName = document.get("firstName");
        p.lastName = document.get("lastName");
        p.address = document.get("address");
        p.email = document.get("email");

        return p;
    }
}
</pre>
The next step in our process is to write the client.
<h3>Thrift Client - Ruby</h3>
Writing the thrift ruby client is even easier than writing the server code.  If you have not already done so, install the thrift gem by running "gem install thrift" to get  the required thrift library code.  All the code you need for your client is already generated by thrift.  At this point we are only doing what is needed to get the client to communicate with the server.
<pre class="prettyprint">
module ThriftConnection

  class LuceneClient

    def initialize(host='localhost', port=9090)
      socket = Thrift::Socket.new(host, port)
      @transport = Thrift::BufferedTransport.new(socket)
      protocol_factory = ::Thrift::BinaryProtocolFactory.new
      protocol = protocol_factory.get_protocol(@transport)
      @transport.open
      @client = LuceneSearch::Client.new(protocol)
    end

    def search(query)
      @client.search(query)
    end

    def close
      @transport.close
    end

  end
end
</pre>
<h3>Running With Scissors</h3>
This section has the odd title "Running With Scissors", because like actual running with scissors, what we are about to do may not be a great idea.
In all the thrift generated code there is a warning at the top "DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING", obviously I don't, but I'm not going to let that stop me at this point (sometimes you just have to see if you can get something to work!)  What we've done is implement method_missing in the generated Person class (found in lucene_search_types.rb) so we can specify searches ala ActiveRecord style.  What we are going to do to accomplish this is use a regular expression to pull out what fields to search for and use the arguments passed in as the search values.  The regular expression here is fairly simple and only aims to handle simple searches.
<pre class="prettyprint">
#added to translate from symbol to expected search format
  SEARCH_KEYS_MAPPING = {:first_name => 'firstName',
                                            :last_name => 'lastName',
                                            :email => 'email',
                                            :address => 'address'}


  def self.method_missing(method_name, *args)
    lucene_client = ThriftConnection::LuceneClient.new
    query = ""
        #handles find_by_first_name etc
    if method_name.to_s =~ /^find_by_([a-z]+_?[a-z]*)$/
      query = "#{SEARCH_KEYS_MAPPING[$1.to_sym]}:#{args[0]}"
        #handles find_by_first_name_or_last_name, find_by_first_name_and_email 
    elsif method_name.to_s =~/^find_by_([a-z]+_[a-z]+)_([a-z]+)_([a-z]+_?[a-z]*)$/
      query ="#{SEARCH_KEYS_MAPPING[$1.to_sym]}:#{args[0]} #{$2.upcase} #{SEARCH_KEYS_MAPPING[$3.to_sym]}:#{args[1]}"
    else
       raise ArgumentError.new("search method pattern #{method_name} not recognized")
    end

    results = lucene_client.search(query)
    lucene_client.close
    results
  end
</pre>
As we'll see in the next section, this actually worked, but I still view this more as a useful experiment.  First of all this was placed in generated code, so any time you make changes you would have to manually get the method_missing definition back into the Person class.  Secondly, Lucene search syntax is really not all that hard to learn.  
<h3>Testing</h3>
All of what we have done so far would not be worth much if we could not verify our work with some testing.  Here is the unit test to verify that we are indeed able to search a Lucene index from Ruby.  To get some names to search on I simply ran <pre class="prettyprint">
head names.csv</pre> and then used some of the information in various combinations to get counts of what searches should return.  For example to get an idea of what searching for a first name of Elizabeth or last name of Krause would return I ran<pre class="prettyprint">cat names.csv | grep -iE 'elizabeth|krause' | wc -l </pre> which returned a count of 289.  So, first making sure that our thrift server was running in the background, here is the unit test that was run to verify our Ruby client searching against a Lucene index.
<pre class="prettyprint">
class SearchTest < Test::Unit::TestCase

  def setup
    @lucene_client = ThriftConnection::LuceneClient.new
  end


  def teardown
    @lucene_client.close
  end

  def test_search_client_first_name
    persons = @lucene_client.search("firstName:Tia")
    assert_equal(5, persons.length)

    persons.each do |person|
      assert_equal("Tia", person.firstName)
    end
  end

  def test_search_person_class_first_name
    persons = Person.find_by_first_name("Tia")
    assert_equal(5, persons.length)

    persons.each do |person|
      assert_equal("Tia", person.firstName)
    end
  end

  def test_search_client_first_name_email_domain
    persons = @lucene_client.search("+firstName:Elizabeth +email:*pookmail.com")
    assert_equal(59, persons.length)
  end

  def test_search_person_class_first_name_email_domain
    persons = Person.find_by_first_name_and_email("elizabeth", "*pookmail.com")
    assert_equal(59, persons.length)
  end

  def test_search_client_first_name_and_last_name
    persons = @lucene_client.search("+firstName:Elizabeth +lastName:Krause")
    assert_equal(1, persons.length)
    person = persons[0]

    assert_equal("Elizabeth", person.firstName)
    assert_equal("Krause", person.lastName)
  end

  def test_search_person_class_first_name_and_last_name
    persons = Person.find_by_first_name_and_last_name("elizabeth", "krause")
    assert_equal(1, persons.length)
    person = persons[0]

    assert_equal("Elizabeth", person.firstName)
    assert_equal("Krause", person.lastName)
  end

  def test_search_person_class_first_name_or_last_name
    persons = Person.find_by_first_name_or_last_name("elizabeth", "krause")
    assert_equal(289, persons.length)
  end

  def test_invalid_search
    assert_raises ArgumentError do
      Person.find_person_by_name("tia")
    end
  end

end
</pre>
<h3>Conclusion</h3>
Thrift is a compelling alternative for RPC or message passing where one might otherwise be using either REST, Java RMI or middleware (JMS, AMQP).  There is a great comparison of how thrift performs against other forms of RPC in this  <a href="http://jnb.ociweb.com/jnb/jnbJun2009.html" target="new">thrift tutorial from OCI</a> found near the end of the article.  It is hoped the reader was able to learn something useful.  Thanks for your time
<h3>Resources</h3>
Full source for the blog including the generated code can be found <a href="https://github.com/bbejeck/LuceneThriftRuby" target="new">on github</a>. If you are interested in running the test you can download <a href="https://github.com/downloads/bbejeck/LuceneThriftRuby/lucene-thrift-example.tar.gz" target="new">lucene-thrift-example.tar.gz</a> extract the tar file and execute the runSearchTest.sh script.  You do not need to have thrift installed to run the test.
<ul>
<li>For more information on thrift the <a href="http://wiki.apache.org/thrift/" target="new"> thrift wiki</a> is a great start</li>
<li>More information on Lucene can be found <a href="http://lucene.apache.org/java/docs/index.html" target="new">here</a></li>
</ul>




]]></description>
            <link>http://www.nearinfinity.com/blogs/bill_bejeck/lucene_thrift_and_ruby.html</link>
            <guid>http://www.nearinfinity.com/blogs/bill_bejeck/lucene_thrift_and_ruby.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Java</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Lucene</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Java Thrift Lucene Ruby</category>
            
            <pubDate>Wed, 18 May 2011 23:38:30 -0500</pubDate>
        </item>
        
        <item>
            <title>ActiveRecord: Nested Attributes </title>
            <description><![CDATA[<p>My project recently started developing a web app using Rails 3 and Extjs, a pure JavaScript frontend, i.e. no ERB, haml, etc.  We have several multi-page forms where we use a JavaScript model to hold the form data and upon save serialize the JSON back to the server. If successful, the updated data is rendered as JSON and sent back to the client.  
</p>
<p>
The model has a simple one-to-many relationship (parent/children)
</p>

<pre class="prettyprint">{parent = { id: 1, name: 'Jim', children: 
[{id: 2, name: 'Larry',parent_id:1},
{id: 3, name:'Curly',parent_id:1},
{id:4, name: 'Moe',parent_id:1}] } 
</pre>

<pre class="prettyprint">class Parent &lt; ActiveRecord::Base
  attr_accessible :name
  has_many :children 
end

class Child &lt; ActiveRecord::Base
  belongs_to :parent
end
</pre>

<p>
My expectation was that when I save the parent data, via update_attributes, both the parent and child data save together in a single transaction. 
</p>

<pre class="prettyprint">class ParentsController&lt; ApplicationController
  ...
  def update
    parent = Parent.find(params[:id])
    parent.update_attributes(params)
    ...
  end
end
</pre>

<p>
Being new to developing Rails apps in the "real world" I had not dealt with this, what I thought to be, common scenario.  I found that out of the box the parent data saves, but not the child data.  I first explored the book "Agile Web Development with Rails", but didn't find what I needed.  Next I checked out the Rails Guides, in particular the section on <a href="http://guides.rubyonrails.org/association_basics.html#options-for-has_many">associations</a> The first setting I found was the :autosave setting:
</p>

<blockquote><em>
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for 
destruction whenever you save the parent object.
</em></blockquote>

<p>
Seemed like a good fit.
</p>

<pre class="prettyprint">has_many :children, :autosave =&gt; true
</pre>

<p>
However, the children still did not save when calling update_attributes (I'm still not sure exactly what this is used for)
</p>

<p>
Next stop, Google. Some searching brought me to the ActiveRecord "accepts_nested_attributes_for" method. It turns out this is exactly what I needed.  So I added "accepts_nested_attributes_for :children" to Parent
</p>

<pre class="prettyprint">class Parent &lt; ActiveRecord::Base
  attr_accessible :name
  has_many :children
  accepts_nested_attributes_for :children
end
</pre>

<p>
However, just adding "accepts_nested_attributes_for :children" wasn't enough.  It turns out that with the accepts_nested_attributes_for method, the Parent class receives a method called "children_attributes=(attributes)".  This allows for the parent's children attributes to be updated.  This also means I have to update my JSON so that the "children" property is called "children_attributes".  
</p>


<pre class="prettyprint">parent = { id: 1, name: 'Jim', children_attributes: 
[{id: 2, name: 'Larry',parent_id:1},
{id: 3, name:'Curly',parent_id:1},
{id:4, name: 'Moe',parent_id:1}] } 
</pre>
<p>
Since I am using "attr_accessible" I also have to include "children_attributes" in the list
</p>

<pre class="prettyprint">class Parent &lt; ActiveRecord::Base
  attr_accessible :name, :children_attributes
  has_many :children
  accepts_nested_attributes_for :children
end
</pre>

<p>
Now calling "parent.update_attributes" saves both the parent and its children, within a single transaction.  Rails will handle inserts/updates/deletes appropriately.  If the child has an id, the child is updated.  If the child is missing an id, a new child is added.  If the child has the "_destroy" attribute set, the child is removed.
</p>

<p>
The last todo was to serialize the JSON back to the client upon success. When trying this:
</p>

<pre class="prettyprint">render :json =&gt; parent.to_json(:include =&gt; :children)
</pre>

<p>my JSON included "children", not "children_attributes":</p>
<pre class="prettyprint">{parent = { id: 1, name: 'Jim', children: 
[{id: 2, name: 'Larry',parent_id:1},
{id: 3, name:'Curly',parent_id:1},
{id:4, name: 'Moe',parent_id:1}] } 
</pre>

<p>For the save to work correctly, the JSON needs "children_attributes", (which is a bit of a disconnect between relationship and nested attribute naming conventions) I was not able to figure out how to get Rails and "to_json" to use "children_attributes".  For my solution I did not ":include =&gt; :children" in my call to "to_json" and I overrode "as_json".  In my "as_json" I manually add the "children attributes" to the parent JSON.</p>

<pre class="prettyprint">  def as_json(options={})
    json = super(options)
    json[:children_attributes]=[]
    
    self.children.each do |child|
      json[:children_attributes] &lt;&lt; {:id =&gt; child.id, :name =&gt; child.name}
    end
    json
  end
</pre>

<p>Looking back on getting the save to work, it seems rather simple.  However, several things made this a little harder than it should have been.  The first being the documented coverage.  Surprisingly, this was not covered in the book "AWDR" or Rails Guides. It is spelled out quite well in the API docs, however, but you need to know about the NestedAttributes module  The ":autosave" setting misled me a bit.  I will need to go back and do a bit more research on this.  Lastly, the naming convention for nested attributes threw me off and the fact that I had to "rig" as_json. Perhaps there is a way to handle this by "convention".</p> 

<p>
For further reading check out <a href="http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes">Ryan's blog</a> or <a href="http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html">the Nested Attributes API docs.</a> 
</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/jim_clark/activerecord_nested_attributes.html</link>
            <guid>http://www.nearinfinity.com/blogs/jim_clark/activerecord_nested_attributes.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">rails activerecord nested attributes</category>
            
            <pubDate>Mon, 02 May 2011 23:25:38 -0500</pubDate>
        </item>
        
        <item>
            <title>Blueprint for a Server Status Capistrano Script</title>
            <description><![CDATA[
<p>
Capistrano is great to work with. It's simple, powerful and flexible. For the last couple of weeks I've been building a series of Capistrano tasks that check the status and configuration of our web servers. I'm pretty happy with the results, so I though I would share the basic structure of what I've come up with. I'm just going to provide a couple of tasks here, along with all of the support methods, to provide a framework, but the idea would be to expand it with whatever commands would be run to verify the health of a server.
</p>
<p>
There were several requirements that I had to keep in mind when I was designing this
</p>
<ol>
<li>The cap tasks should use the environment configuration files that we already have.</li>
<li>Because there are a large number of tasks we should be able to run them individually, or with a single command we should be able to run the entire suite.</li>
<li>The cap script should produce well formatted, easy to read output, so that it's clear what's broken and where.</li>
</ol>


<h3><br /></h3><h3>Control Support Methods</h3>
<p>
While building the validation tasks I found that I was having to do the same basic operations fairly often:
</p>
<ol>
<li>Issue a command on a remote host.</li>
<li>Parse the output of that command.</li>
<li>Issue more commands based on the output.</li>
</ol>

<p>
Or sometimes I just wanted to run a series of commands on one host, verifying that it was correct before moving on to the next. It's possible to make Capistrano work this way, but it's not well documented. By default, if you give Capistrano multiple commands to run on multiple hosts, the it will run the commands in host order. However what I really wanted is for Capistrano to run the commands in command order. 
</p>
<p>
For instance: given that we have host-A, host-B, host-C, and I want to run commands doA, doB, and doC by default Capistrano will run:
</p>
<pre>    HostA &gt; doA
    HostB &gt; doA
    HostC &gt; doA
    HostA &gt; doB
    HostB &gt; doB
    HostC &gt; doB
    HostA &gt; doC
    HostB &gt; doC
    HostC &gt; doC

</pre>
<p>But what I needed it to do was:</p>
<pre>    HostA &gt; doA
    HostA &gt; doB
    HostA &gt; doC
    HostB &gt; doA
    HostB &gt; doB
    HostB &gt; doC
    HostC &gt; doA
    HostC &gt; doB
    HostC &gt; doC

</pre>
<p>
By using the roles that we already have configured for our deploy tasks with Capistrano we were able to create a few support methods that issue commands in the order that we want.
</p>
<br />
<h4>each_host</h4>
<p>
The each_host method is used to iterate through our configured hosts. The method prints out the hostname (which is important for the look of the script's output) sets the current host, then yields the block that was passed into it.
</p>
<pre class="prettyprint">  def each_host
    roles[:web].each do |host|
      print_hostname host
      set(:current_host, host.host)
      yield host
    end
  end

</pre>
<br />
<h4>run_serial</h4>
<p>
The run_serial method runs the given command, but only on the current host, yielding the output of the command and the name of the output stream (:out or :err). The ssh channel we don't have much use for.
</p>
<pre class="prettyprint">  def run_serial(command)
    run command, :hosts =&gt; fetch(:current_host) do |chan, stream, data|
      yield stream, data
    end
  end

</pre>
<br />
<h4>run_primary</h4>
<p>
We also have a primary server set in our configuration. We can create a few more support methods that take advantage of the primary host, for issuing command on one host only.
</p>
<pre class="prettyprint">  def run_primary(command)
    run command, :hosts =&gt; fetch(:primary) do |chan, stream, data|
      set_current_host channel
      yield stream, data
    end
  end

</pre>
<br />
<h4>currently_primary?</h4>
<p>
Having a method that tells us if we're currently on the primary host is helpful as well.
</p>
<pre class="prettyprint">  def currently_primary?
    fetch(:current_host) == fetch(:primary).host
  end

</pre>
<br />
<h4>set_current_host</h4>
<p>
The current host can also be set from the ssh channel. This method is called from the run_primary method, but this needs to be called when we call the default run method.
</p>
<pre class="prettyprint">  def set_current_host(channel)
    host = channel.properties[:host]
    print_hostname host
    set(:current_host, host)
  end
</pre>
<p>
Now we can write tasks that execute in in command order, execute tasks just on the primary host, and as we're preforming checks we print out the current host, so we're able to keep track of where we are. <br /></p><p><br /></p><p><br /></p>

<h3>Printing Support Methods</h3>
<p>
Because printing the script output out to the command line is an important part of the script I'm going to go over the printing support next. When running the server checks I wanted the output to look like rspec or cucumber. As each check is performed I wanted to see a colorful pass or fail message with the values that I was checking against to be highlighted. Also once the checks were finished I wanted to see a list of everything that failed, grouped by host.
</p>
<br />
<h4>print_hostname</h4>
<p>
This is the print_hostname method that was mentioned above. Everytime we move to a different host this method prints out the hostname.
</p>
<pre class="prettyprint">  def print_hostname(name)
    puts "    Host: #{grey(name)} &gt;"
  end

</pre>
<br />
<h4>step</h4>
<p>
Also at the beginning of every task I wanted to print the task name and the number of the task.
</p>
<pre class="prettyprint">  def step(name)
    step = fetch(:step)
    puts "\n[#{step}] #{name}"
    set(:step, step+1)
  end

</pre>
<br />
<h4>failure</h4>
<p>
The failure method is called when a check fails. It prints the failure message, and stores it under the current hostname so that it can be printed again after all the checks have been done. If the expected or actual parameters are included it prints those along with the message, otherwise just the message is printed.
</p>
<pre class="prettyprint">  def failure(message, expected=nil, actual=nil)
    host = fetch(:current_host)
    complete = (expected || actual) ?
      "#{message} Expected: #{grey(expected)} Actual: #{grey(actual)}" :
      message
    errors = fetch(:errors)
    unless errors[host]
      errors[host] = []
    end
    errors[host] &lt;&lt; message
    log_item(complete, :fail)
  end

</pre>
<br />
<h4>pass</h4>
<p>
The pass method is called when a check passes.
</p>
<pre class="prettyprint">  def pass(message)
    log_item(message, :pass)
  end

</pre>
<br />
<h4>log_item</h4>
<p>
The log_item method is called by the pass and failure methods. The method includes a warn and empty status which can also be used by the tasks when printing their status.
</p>
<pre class="prettyprint">  def log_item(message, flag=nil)
    status = case flag
      when :pass : "      [#{green("PASS")}] "
      when :fail : "      [#{red("FAIL")}] "
      when :warn : "      [#{orange("WARN")}] "
      else "      "
    end
    puts "#{status}#{message}"
  end

</pre>
<br />
<h4>colorize</h4>
<p>
The methods below handle the coloring of the text as it's printed to the command line. This was actually pretty fun figuring out. Every script should have fancy colorized output. (and now every script of mine will, muaha ha ha)
</p>
<pre class="prettyprint">  def red(text)
    colorize(text, 31)
  end

  def orange(text)
    colorize(text, 33)
  end

  def green(text)
    colorize(text, 32)
  end

  def grey(text)
    colorize(text, 37)
  end

  def colorize(text, color_code)
    "\e[#{color_code}m#{text}\e[0m"
  end

</pre>



<h3><br /></h3><h3>Start and Finish Tasks</h3>
<p>
Now that all that's taken care of we can start writing some actual Capistrano tasks to take advantage of our fine grained control and fancy printing.
</p>
<br />These server check tasks all share common startup and finish tasks that need to be run before and after any one of the tasks are run, or when the entire suite is run. This is defined using Capistrano's :start and :finish callbacks, but should only be run for the check tasks.

<pre class="prettyprint">  TASK_LIST = [
    "check:all",
    "check:environment_variables",
    "check:middleware_versions",
    "check:apache_configuration"]

  on :start, 'check:setup', :only =&gt; TASK_LIST
  on :finish, 'check:print_errors', :only =&gt; TASK_LIST

</pre><h4>setup</h4>

  
<p>
The setup task handles whatever specific setup needs to be done, but at the very least the step and errors variables need to be defined. They're used by the printing methods. Also, all of the tasks below are inside of the check namespace.
</p>
<pre class="prettyprint">  namespace :check do

    task :setup do
      set :step, 1
      set :errors, {}
    end

</pre>
<br />
<h4>print_errors</h4>
<p>
The print errors method is run after all the checks have been run to print a convenient list of errors grouped by host. This keeps errors from being lost in the output.
</p>
<pre class="prettyprint">  task :print_errors do
    errors = fetch(:errors)
    if (errors.size() &gt; 0)
      print "\n ==== #{red 'Errors'} ===="
      errors.each do |host,list|
        print_hostname host
        list.each {|e| puts "      #{e}"}
      end
    end
  end

</pre>



<h3><br /></h3><h3>Validation Tasks</h3>
<p>
The meat of the script are in the validation tasks. Out current script has fifteen different tasks that do everything from checking environment variables, to verifying directory and file permissions, to checking network and database statuses. If it can be automated, we'll find a way to get it in. Rather then include concrete examples though I'm just going to include some skeleton methods to show the method structure to illustrate how the support methods are used together in the Capistrano tasks.
</p>
<br />
<h4>Simple Tasks</h4>
<p>
This task executes one command on each server and validates the output from a list of expected results. This form is used by our script to check environment variables, commands on the sudo list, and programs in the cron, all of which can be read and verified with one command. I'm just including the validateVariable, and getVariable methods here to show the pass and failure methods.
</p>
<pre class="prettyprint">  VARIABLES = [
    { :key =&gt; 'KEY', :value =&gt; /Expected Value/ },
    { :key =&gt; 'KEY', :value =&gt; /Expected Value/ },
    { :key =&gt; 'KEY', :value =&gt; /Expected Value/ }]

  task :simple do
    step "A Simple Check"
    run "env" do |channel, stream, data|
      set_current_host channel
      VARIABLES.each do |map|
        validateVariable(data, map)
      end
    end
  end

  def validateVariable(data, map)
    value = getVariable(data, map[:key])
    (value.match map[:value]) ?
      pass("Env #{grey(map[:key])} is set to #{grey(value)}") :
      failure("Env #{grey(map[:key])} is incorrect", map[:value].inspect, value)
  end

  def getVariable(data, key)
    result = data.match /^#{key}=(.*)/
    unless result
      failure "Env #{grey(key)} is not set."
      return nil
    end
    result[1]
  end

</pre>
<br />
<h4>Tasks that run multiple commands on the same host</h4>
<p>
Most of the tasks in our validation script run multiple commands on the same host, using the each_host and run_serial methods. This allows us to read files and act on the values in those files. It's alse used because the script output looks better organized when running tasks in host order. The task below itterates through a list of standard directories, then a list of configured directories. For each directory a run_serial command is executed to see if the directory exists, then once the lists have been gone through it's done again on the next host.
</p>
<pre class="prettyprint">  task :serial_example do
    step "Run multiple commands on a host"
    each_host do
      DIRECTORIES.each do |dir|
        verify_directory dir
      end

      [:deploy_to, :transfer_path, :cache_path].each do |key|
        dir = fetch(key)
        if (dir)
          verify_directory dir
        else
          falure "No directory configured for #{grey(key)}"
        end
      end

    end
  end

  def verify_directory(path)
    command = "[ -d #{path}] &amp;&amp; echo 'true' || echo 'false'"
    run_serial command do |stream, data|
      if (data.strip == 'true')
        pass "Verified that #{grey(path)} exists."
      else
        failure "Directory at #{grey(path)} does not exist."
      end
    end
  end

</pre>
<br />
<h4>Tasks using different server combinations</h4>
<p>
Here's a command that runs on the primary host, reads a file, then acts across multiple hosts. We do something like this to verify that the Apache configuration is correct. We know the httpd.conf is the same across hosts, but we want to verify that the files and directories that are in the configuration are actually on the host in question.
</p>
<pre class="prettyprint">  task :primary_example do
    step "Verifying Certificates"
    path = fetch(:path_to_httpd_conf)
      certs = {}
      run_primary "grep SSLC[eA] #{path}" do |stream,data|

        if (stream == :err)
          failure "No http configuration file at #{grey(path)}"
          break
        end

        keys = [
          'SSLCertificateFile',
          'SSLCertificateKeyFile',
          'SSLCACertificatePath',
          'SSLCARevocationPath']

        keys.each do |key|
          certs[key] = read_http_conf_value(data,key)
        end

        pass "Read Certificate Paths from HTTP Configuration"
      end

      each_host do
        verify_file certs['SSLCertificateFile']
        verify_file certs['SSLCertificateKeyFile']
        verify_directory certs['SSLCACertificatePath']
        verify_directory certs['SSLCARevocationPath']
      end
    end

</pre>
<br />
<p>
And finally we need a task that runs the whole suite of validation tasks. The all task does nothing itself, it just runs all of the other tasks.
</p>
<pre class="prettyprint">  desc "Run all of the validation tasks"
  after "check:all",
    "check:environment_variables",
    "check:middleware_versions",
    "check:apache_configuration"

  task :all do
  end

</pre>
<p>
Then to execute the cap task you would type is:
</p>
<pre>cap -q production check:all
</pre>
<p>
And watch as all the beautiful passes and fails scroll by, though hopefully more of the former.
</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/john_cato/blueprint_for_a_server_status.html</link>
            <guid>http://www.nearinfinity.com/blogs/john_cato/blueprint_for_a_server_status.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
            <pubDate>Thu, 28 Apr 2011 18:40:02 -0500</pubDate>
        </item>
        
        <item>
            <title>Introducing RJava</title>
            <description><![CDATA[<p>You've no doubt heard about JRuby, which lets you run Ruby code on the JVM. This is nice, but wouldn't it be nicer if you could write Java code on a Ruby VM? This would let you take advantage of the power of Ruby 1.9's new YARV (Yet Another Ruby VM) interpreter while letting you write code in a statically-typed language. Without further ado, I'd like to introduce <strong>RJava</strong>, which does just that!</p>

<p>RJava lets you write code in Java and run it on a Ruby VM! And you still get the full benefit of the Java compiler to ensure your code is 100% correct. Of course with Java you also get checked exceptions and proper interfaces and abstract classes to ensure compliance with your design. You no longer need to worry about whether an object responds to a random message, because the Java compiler will enforce that it does.</p>

<p>You get all this and more but on the power and flexibility of a Ruby VM. And because Java does not support closures, you are ensured that everything is properly designed since you'll be able to define interfaces and then implement anonymous inner classes just like you're used to doing! Even when JDK 8 arrives sometime in the future with lambdas, you can rest assured that they will be statically typed.</p>

<p>As a first example, let's see how you could filter a collection in RJava to find only the even numbers from one to ten. In Ruby you'd probably write something like this:</p>

<pre class="prettyprint">
evens = (1..10).find_all { |n| n % 2 == 0 }
</pre>

<p>With RJava, you'd write this:</p>

<pre class="prettyprint">
List&lt;Integer&gt; evens = new ArrayList&lt;Integer&gt;();
for (int i = 1; i &lt;= 10; i++) {
  if (i % 2 == 0) {
    evens.add(i);
  }
}
</pre>

<p>This example shows the benefits of declaring variables with specific types, how you can use interfaces (e.g. List in the example) when declaring variables, and shows how you also get the benefits of Java generics to ensure your collections are always type-safe. Without any doubt you know that "evens" is a List containing Integers and that "i" is an int, so you can sleep soundly knowing your code is correct. You can also see Java's powerful "for" loop at work here, to easily traverse from 1 to 10, inclusive. Finally, you saw how to effectively use Java's braces to organize code to clearly show blocks, and semi-colons ensure you always know where lines terminate.</p>

<p>I've just released <a href="https://github.com/sleberknight/rjava" onclick="alert('April Fools!'); return false;">RJava</a> on GitHub, so go check it out. Please download RJava today and give it a try and let me know what you think!</p>]]></description>
            <link>http://www.nearinfinity.com/blogs/scott_leberknight/introducing_rjava.html</link>
            <guid>http://www.nearinfinity.com/blogs/scott_leberknight/introducing_rjava.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">JRuby</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Java</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">java</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">ruby</category>
            
            <pubDate>Fri, 01 Apr 2011 00:00:00 -0500</pubDate>
        </item>
        
        <item>
            <title>Simple Steps For Generating Word XML</title>
            <description><![CDATA[<br />This is a follow-up to my last post about Exporting an ExtJs GridPanel to Word XML.&nbsp; In that post I showed how to get all the configuration and state properties from an ExtJs GridPanel.&nbsp; This post will demonstrate how to generate a basic table within a Word XML document using Ruby to produce XML<br /><br /><b>Section 1: Building a sample</b><br /><br />The first step is to open Word, Open Office, or anything that will generate Open XML.&nbsp; If you're not an Open XML expert, you can create a sample document of what you want the final product to look like.&nbsp; Once you've got it formatted, save it in XML format.&nbsp; I suggest labeling everything so it's easy to find in the XML later.&nbsp; For example, if you're creating a table it might look like this:<br /><br />Header 1&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; | Header 2&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | Header 3<br />Row 1, Col 1 | Row 1, Col 2 | Row 1, Col 3<br />Row 2, Col 1 | Row 2, Col 2 | Row 2, Col 3<br /><br />There are three columns with names Header 1, Header 2, and Header 3.&nbsp; The data resides below these headers in Row 1 and Row 2.<br /><br /><b>Section 2: Building templates</b><br /><br />The next step is to start building templates.&nbsp; If you're just building an XML document with a simple table, you can use three simple templates: document, table, and row.<br /><br />The "document" template can contain everything outside the of the content of &lt;w:body&gt; tags which will be generated by the Ruby code in Section 3.&nbsp; This document template defines the fonts, styles, and document properties used by the sample generated in section 1.<br /><br /><i>document-template.xml</i> - Note that the "{table}" string will be replaced with the generated table template.<br /><br />
<pre class="prettyprint">&lt;?mso-application progid="Word.Document"?&gt;<br />&lt;w:wordDocument xml:space="preserve" w:embeddedObjPresent="no"&gt;<br />&lt;o:DocumentProperties&gt;...&lt;/o:DocumentProperties&gt;<br />&lt;o:CustomDocumentProperties&gt;...&lt;/o:CustomDocumentProperties&gt;<br />&lt;w:fonts&gt;...&lt;/w:fonts&gt;<br />&lt;w:lists&gt;...&lt;/w:lists&gt;<br />&lt;w:styles&gt;...&lt;/w:styles&gt;<br />&lt;w:docPr&gt;...&lt;/w:docPr&gt;<br />&lt;w:body&gt;{table}&lt;/w:body&gt;<br />&lt;/w:wordDocument&gt;<br /></pre><br /><i>table-template.xml</i> - Note that the "{rows}" string will be replaced with the generated rows template.&nbsp; If you're familiar with html tables, you should be able to recognize some patterns here.<br /><br />&lt;w:tbl&gt; defines a table.&nbsp; &lt;w:tr&gt; defines a row.&nbsp; &lt;w:tc&gt; defines a cell.&nbsp; Everything in between specifies the formatting.<br /><pre class="prettyprint">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;w:tbl&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tblPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tblStyle w:val="Table1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tblW w:w="9973.2467" w:type="dxa"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tblInd w:w="0" w:type="auto"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:jc w:val="left"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tblPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tblGrid&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:gridCol w:w="3324.3676"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:gridCol w:w="3324.3676"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:gridCol w:w="3324.3676"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tblGrid&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:trPr/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tc&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcW w:type="dxa" w:w="3324.3676"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcMar/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:top w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:bottom w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:left w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:right w:val="none" w:sz="0" w:color="auto"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pStyle w:val="Table_20_Contents"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:t&gt;Header 1&lt;/w:t&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tc&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tc&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcW w:type="dxa" w:w="3324.3676"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcMar/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:top w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:bottom w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:left w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:right w:val="none" w:sz="0" w:color="auto"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pStyle w:val="Table_20_Contents"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:t&gt;Header 2&lt;/w:t&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tc&gt;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; #another w:tc defined here for Header 3...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tr&gt;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {rows}<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/w:tblPr&gt;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pStyle w:val="Standard"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:sectPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:type w:val="next-page"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pgSz w:w="12241.5302" w:h="15841.9803" w:orient="portrait"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pgMar w:top="1133.9978" w:bottom="1133.9978" w:left="1133.9978" w:gutter="0" w:right="1133.9978"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pgBorders w:offset-from="text"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:sectPr&gt;<br /></pre><br /><i>row-template.xml</i> - Note that the "{column_#_value}" strings will be replaced with the corresponding values for the row being generated.<br /><br /><pre class="prettyprint">            &lt;w:tr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:trPr/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tc&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcW w:type="dxa" w:w="3324.3676"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcMar/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:top w:val="none" w:sz="0" w:color="auto"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:bottom w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:left w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:right w:val="none" w:sz="0" w:color="auto"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pStyle w:val="Table_20_Contents"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:t&gt;{column_1_value}&lt;/w:t&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tc&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tc&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcW w:type="dxa" w:w="3324.3676"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcMar/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:top w:val="none" w:sz="0" w:color="auto"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:bottom w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:left w:val="single" w:sz="0" w:color="000000"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:right w:val="none" w:sz="0" w:color="auto"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcBorders&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tcPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:pStyle w:val="Table_20_Contents"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:pPr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;w:t&gt;{column_2_value}&lt;/w:t&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:r&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:p&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tc&gt;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; #another w:tc defined with {column_3_value}...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/w:tbl&gt;</pre><br /><b>Section 3:&nbsp; Injecting data into the templates and creating an XML file</b><br /><br />The code snippet below queries for some data. It loops over the "table_data" returned and builds rows based on the row-template.xml defined in section 2.&nbsp; The rows are concatenated and injected into the table template, which in turn is injected into the document template.&nbsp; Obviously some of of these steps are unnecessary (the table template could be a part of the document template, thus skipping a substitution), but I wanted to show that you can make templates as granular as you like.&nbsp; In my own solution, I have templates for cells, header rows, and much more.&nbsp; I used the configuration and state data of the ExtJs GridPanel (described in a previous blog post) and looped over column names and values to generate a table from scratch.&nbsp; However, most of the work in this blog post is already done for you.<br /><br />Depending on the size of your dataset, you may want to write periodically to the file.&nbsp; Holding many iterations of row-template.xml in memory can be costly.<br /><pre class="prettyprint"><br />#create a new empty xml document<br />document = File.new(some_filename, 'a')<br /><br />#query for data<br />table_data = SomeObject.find(:all, :conditions =&gt; 'some conditions...')<br /><br />document_template = File.open('path/to/document-template.xml').read<br /><br />table_template = File.open('path/to/table-template.xml').read<br />rows = []<br />table_data.each do |row_data|<br />&nbsp;&nbsp;&nbsp; row_template = File.open('path/to/row-template.xml').read<br />&nbsp;&nbsp;&nbsp; row_template.gsub!('{column_1_value}', row_data.col_1) #substitute in whatever returns the data you need to put in the Col 1 column.<br />&nbsp;&nbsp;&nbsp; row_template.gsub!('{column_2_value}', row_data.col_2) #substitute in whatever returns the data you need to put in the Col 2 column.<br />&nbsp;&nbsp;&nbsp; row_template.gsub!('{column_3_value}', row_data.col_3) #substitute in whatever returns the data you need to put in the Col 3 column.<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; #add the row to the master array of rows<br />&nbsp;&nbsp;&nbsp; rows &lt;&lt; row_template<br />end<br /><br />#substitute the dynamically generated rows into the table template<br />table_template.gsub!('{rows}', rows.join(''))<br /><br />#substitute the dynamically generated table into the document template<br />document_template.gsub!('{table}', table)<br /><br />#write everything to the new xml document.&nbsp; As I said before you'll probably want to write to the xml document more frequently than <br />#this example to avoid Out-Of-Memory errors.<br />document.puts document_template<br />document.close</pre><br /><br />As you can see, this is a very basic example.&nbsp; However, the same principals can be applied to much more complex and dynamic situations such as a exporting a fully user-configurable ExtJs GridPanel to Word XML.&nbsp; This process can also be applied to generating Excel XML files.<br />]]></description>
            <link>http://www.nearinfinity.com/blogs/michael_bevels/simple_steps_for_generating_wo.html</link>
            <guid>http://www.nearinfinity.com/blogs/michael_bevels/simple_steps_for_generating_wo.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
            <pubDate>Wed, 09 Mar 2011 22:21:41 -0500</pubDate>
        </item>
        
        <item>
            <title>Exporting an ExtJs GridPanel to Word XML</title>
            <description><![CDATA[This blog post will lay out some of steps for exporting an ExtJs GridPanel to Word XML.&nbsp; This is useful if you want to save or print what's currently displayed in the grid.&nbsp; I am writing a subsequent post with steps on how to actually generate a Word XML file, but for now I'll focus on defining a grid, getting the grid's state, sending the grid's state to a controller, and prompting the user to save the generated Word XML document.<br /><br />Technologies used:<br />- Ruby on Rails<br />- mod_xsendfile for Apache2/Apache2.2<br />- ExtJs<br />- javascript<br /><br />At a high level, the following happens:<br />1. Gather and send the GridPanel state/configuration to the controller.<br />2. The controller runs a query and generates Word XML based on the query results.<br />3. The Word XML file is presented to the user via an "Open/Save As" dialog using XSendFile.<br /><br /><font style="font-size: 1.25em;"><b>Section 1: Getting the current state/configuration of the GridPanel</b></font><br /><br />If you're familiar with ExtJs GridPanel's you know they can be configured by each user viewing the panel.&nbsp; Columns can be added, removed, grouped, shortened, lengthened and repositioned.&nbsp; To capture the GridPanel's current state and pass it to the exporter, I wrote the following javascript.&nbsp; It calculates and returns an array containing the column order, which column data indexes belong to which titles, the grouped field (if any), the sorted field, the sorted direction, and the widths of each column.&nbsp; You will see where this function is used when I define the GridPanel in the next section.<br /><br /><br />
<pre class="prettyprint">var prepareExport = function(gridStore, gridColumnModel) {<br />&nbsp;&nbsp;&nbsp; var sort = null;<br />&nbsp;&nbsp;&nbsp; var direction = null;<br />&nbsp;&nbsp;&nbsp; if (gridStore.getSortState()) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; sort = gridStore.getSortState().field();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; direction = gridStore.getSortState().direction;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; var columnOrder = []; //ordered list of columns reflecting the GridPanel's state<br />&nbsp;&nbsp;&nbsp; var columnsToTitles = {}; //map of ext field mappings to column titles<br />&nbsp;&nbsp;&nbsp; var columnsToWidths = {}; //width of each column<br />&nbsp;&nbsp;&nbsp; var totalWidth = 0;<br />&nbsp;&nbsp;&nbsp; var groupByField = gridStore.groupField; //the store's current grouping field ('false' if not grouped)<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; var storeReaderMapping = gridStore.fields.items;<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; //check to see if the groupField has a corresponding mapping<br />&nbsp;&nbsp;&nbsp; for (var i = 0; i &lt; gridReaderMapping.length; i++) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (gridReaderMapping[i].name == gridStore.groupField &amp;&amp; gridReaderMapping[i].mapping != null) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; groupByField = gridReaderMapping[i].mapping;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; //get the total width of the displayed columns (this will be used later when generating the Word XML)<br />&nbsp;&nbsp;&nbsp; gridColumnModel.getColumnsBy(function(c) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (!c.hidden) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; totalWidth = totalWidth + c.width;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; //iterate over the grid's column model.&nbsp; For displayed columns, get the ext field names (mappings) and column titles<br />&nbsp;&nbsp;&nbsp; gridColumnModel.getColumnsBy(function(c) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (!c.hidden) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; for (var i = 0; i &lt; gridReaderMapping.length; i++) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (gridReaderMapping[i].name == c.dataIndex &amp;&amp; gridReaderMapping[i].mapping == null) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; columnOrder.push(gridReaderMapping[i].name);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; var key = gridReaderMapping[i].name;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; columnsToTitles[key] = c.header;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; columnsToWidths[key] = c.width/totalWidth;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else if (gridReaderMapping[i].name == c.dataIndex &amp;&amp; gridReaderMapping[i].mapping != null) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; //if the name and mapping are different (i.e. a mapping exists), we need to push the mapping and not the name<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; columnOrder.push(gridReaderMapping[i].mapping);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; var key = gridReaderMapping[i].mapping;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; columnsToTitles[key] = c.header;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; columnsToWidths[key] = c.width/totalWidth;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; return [columnOrder, columnsToTitles, groupByField, sort, direction, columnsToWidths];<br />};</pre><br /><br /><br /><font style="font-size: 1.25em;"><b>Section 2: The GridPanel</b></font><br /><br />Here's a basic ExtJs GridPanel.&nbsp; Note the "exportButton" is defined in Section 3.<br /><br /><pre class="prettyprint">var gridReaderMapping = [<br />&nbsp;&nbsp;&nbsp; {name: 'id'},<br />&nbsp;&nbsp;&nbsp; {name: 'col_one'},<br />&nbsp;&nbsp;&nbsp; {name: 'col_two', mapping: 'some_mapping'},<br />&nbsp;&nbsp;&nbsp; {name: 'col_three'}<br />];<br /><br />var gridColumnModel = new Ext.grid.ColumnModel({<br />&nbsp;&nbsp;&nbsp; defaults: {sortable: true},<br />&nbsp;&nbsp;&nbsp; columns: [<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {header: 'ID', dataIndex: 'id'},<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {header: 'Column One', dataIndex: 'col_one'},<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {header: 'Column Two', dataIndex: 'col_two'},<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {header: 'Column Three', dataIndex: 'col_three'}<br />&nbsp;&nbsp;&nbsp; ]<br />});<br /><br />var store = new Ext.data.GroupingStore({<br />&nbsp;&nbsp;&nbsp; proxy: new Ext.dataHttpProxy({<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; url: 'give/me/my/data'<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; method: 'GET'<br />&nbsp;&nbsp;&nbsp; }),<br />&nbsp;&nbsp;&nbsp; reader: new Ext.data.JsonReader({<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; root: 'data',<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; totalProperty: 'total',<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; messageProperty: 'message'<br />&nbsp;&nbsp;&nbsp; }, gridReaderMapping)<br />});<br /><br />var grid = new Ext.grid.GridPanel({<br />&nbsp;&nbsp;&nbsp; id: 'gridpanel',<br />&nbsp;&nbsp;&nbsp; ds: gridStore,<br />&nbsp;&nbsp;&nbsp; cm: gridColumnModel,<br />&nbsp;&nbsp;&nbsp; sm: new Ext.grid.RowSelectionModel({<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; singleSelect: true<br />&nbsp;&nbsp;&nbsp; }),<br />&nbsp;&nbsp;&nbsp; tbar: [exportButton],<br />&nbsp;&nbsp;&nbsp; view: etc, etc, etc, etc...<br />});</pre><br /><br /><font style="font-size: 1.25em;"><b>Section 3: The Export Button</b></font><br /><br />When the export button is clicked it gathers all the necessary data from the GridPanel (columns, widths, etc) and passes that data along with query parameters to the controller.&nbsp; The controller runs a query and generates the Word XML file.&nbsp; Section 4 describes what happens when the Ajax request returns the data successfully.<br /><br />As I said at the beginning of this post, I will follow on with another post about the actual Word XML generation.&nbsp; The Word XML generation isn't too scary.&nbsp; If you're impatient, here are a few tips on Word XML generation.<br />&nbsp;&nbsp;&nbsp; 1. You can start by creating a small Word XML file with a single table using MS Word or Open Office.&nbsp; <br />&nbsp;&nbsp;&nbsp; 2. Open the XML file with the XML viewer/editor of your choice.&nbsp; Your table will be buried somewhere between &lt;w:body&gt; and &lt;/w:body&gt;.&nbsp; All the other sections of the XML can remain the same.&nbsp; <br />&nbsp;&nbsp;&nbsp; 3. You can create a series of templates for Word XML tables and rows based on the patterns you see in your Word XML sample.<br />&nbsp;&nbsp;&nbsp; 4. In your Word XML generator, substitute values into these templates to build tables.<br />&nbsp;&nbsp;&nbsp; 5. Insert the generated templates into a "master" template. <br /><br /><pre class="prettyprint">var exportButton = new Ext.Button({<br />&nbsp;&nbsp;&nbsp; renderTo: this.wrap,<br />&nbsp;&nbsp;&nbsp; listeners: {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; click: function (node, event) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; var exportConfig = prepareExport(grid.getStore(), grid.getColumnModel());<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Ext.Ajax.request({<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; url: 'url/to/query/for/data'<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; params: {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'columnsToTitles': Ext.util.JSON.encode(exportConfig[1]),<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'columnsToWidths': Ext.util.JSON.encode(exportConfig[5]),<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'columnOrder[]': exportConfig[0],<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'groupByExport': exportConfig[2],<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'groupBy': gridStore.groupField,<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'groupDir': 'ASC',<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'sort': exportConfig[3],<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'dir': exportConfig[4],<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'export': true<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'query': build_some_query_params_to_pass_to_the_controller<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; },<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; success: function(response, opts) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; window.location.href = 'path/to/exporter_controller/export_word_XML'<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; },<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; failure: function(response, opts) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; //output error message<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; },<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; scope: this<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }); <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />});</pre><br /><br /><font style="font-size: 1.25em;"><b>Section 4:&nbsp; Downloading the Word XML</b></font><br /><br />Using XSendFile to prompt the user with an "Open/Save As" dialog box.<br /><br /><pre class="prettyprint">class ExportController &lt; ApplicationController<br />&nbsp;&nbsp;&nbsp; def export_word_XML<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; filename = "#{X_SENDFILEPATH}/word_doc.xml"<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (File.exist?(filename))<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; send_file filename, :x_sendfile =&gt; true<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; render :nothing =&gt; true<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp; end<br />end</pre><br />Stay tuned for the upcoming Word XML <b><i>generation</i></b> blog post!<br />]]></description>
            <link>http://www.nearinfinity.com/blogs/michael_bevels/exporting_an_extjs_gridpanel_t.html</link>
            <guid>http://www.nearinfinity.com/blogs/michael_bevels/exporting_an_extjs_gridpanel_t.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">JavaScript</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Web Development</category>
            
            
            <pubDate>Sun, 06 Mar 2011 16:43:27 -0500</pubDate>
        </item>
        
        <item>
            <title>New Class &quot;Rails in Production&quot; Coming March 11</title>
            <description><![CDATA[<p>Near Infinity is proud to host a new class, "Rails in Production: How to Deploy, Secure, Tune, and Monitor Your Production Rails Application", taught by CodeSherpas' Dave Bock. This class will be held on March 11th at the NIC Training Center.<br /></p>You've build the killer rails app. Now what? This class will go over 
detailed examples from choosing a hosting provider, through provisioning
 and securing your servers, to deploying your app, as well as monitoring
 and tuning performance once in production. In this class, we will 
publish a real website from scratch, with consideration for real world 
concerns, like safeguarding passwords, turning on and off application 
monitoring, multiple machine deployments, and general system 
administration tasks.<br /><br />This class is for anyone responsible for deploying live Rails applications. You should be 
comfortable enough with Rails to generate an app, deal with models and 
migrations, understand layouts and partials. You'll also need the 
ability to navigate around a command line.<br /><br />The instructor, David Bock, has been deploying high-availability, heavily trafficked 
Rails apps for four years and has amassed some great experience in 
dealing with different configurations, traffic patterns and optimization
 techniques. <br /><br />To learn more and to register please visit CodeSherpas' <a href="http://codesherpas.com/training" target="_blank">Rails in Production</a> web site.<br /><br />Be sure to check out our <a href="../../trainingcenter/coursecatalog/upcoming/">Upcoming NIC-U Training Courses</a> page for future courses coming up this spring.]]></description>
            <link>http://www.nearinfinity.com/blogs/gray_herter/new_class_rails_in_production.html</link>
            <guid>http://www.nearinfinity.com/blogs/gray_herter/new_class_rails_in_production.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">rails</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">ruby</category>
            
            <pubDate>Fri, 11 Feb 2011 09:24:24 -0500</pubDate>
        </item>
        
        <item>
            <title>Ruby Default Arguments and nil</title>
            <description><![CDATA[<p>Ruby's default arguments are a really handy language feature that I recently discovered aren't as handy as I once thought. I was trying to track down an error I was getting in the following snippet of code in my ActiveRecord model object:</p>

<pre class="prettyprint">
  1 def as_json(options={})
  2   options[:methods] ||= []
  3   options[:methods] << :full_name
  4   super(options)
  5 end
</pre>

<p>When this method was executed in order to convert the object into a JSON representation, I received this error (with line numbers adjusted for this example):</p>

<pre class="prettyprint">
  NoMethodError: undefined method '[]' for nil:NilClass
    from MyModel:2:in 'as_json'
</pre>

<p>I stared at this for a while until it dawned on me that explicitly passing 'nil' to as_json might not result in options being set to the default empty hash, and indeed it does not. I suppose it's technically correct, since nil is an instance of NilClass in Ruby and not some special value as it is in many other languages. It's just not what I was expecting and not what I want. For all the books, blog posts, and tutorials I've read, you'd think I would have picked up on this somewhere along the way.</p>

<p>Curiously though, can anyone think of a valid use case for not assigning the default value to a nil argument? I'm sure there are some, but they're not coming to mind.</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/jeff_kunkle/ruby_default_arguments_and_nil.html</link>
            <guid>http://www.nearinfinity.com/blogs/jeff_kunkle/ruby_default_arguments_and_nil.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
            <pubDate>Mon, 07 Feb 2011 09:00:00 -0500</pubDate>
        </item>
        
        <item>
            <title>Ruby, Perl and MySql 5.5 on Snow Leopard</title>
            <description><![CDATA[<h3>The Problem</h3>

<p>You downloaded and installed the latest 64-bit DMG from <a href="http://dev.mysql.com/downloads/mysql/">MySQL</a> and now you&#8217;re getting &#8220;could not load driver&#8221; errors in DBI (Ruby or Perl) and Rails.</p>

<h3>The Cause</h3>

<p>In Ruby, the &#8216;mysql&#8217; and &#8216;mysql-2&#8217; gems were compiled against older version of the MySql library. Ditto DBD:Mysql module in Perl.</p>

<h3>The Solution</h3>

<p><strong>Update 6/6/2011</strong>: Google lead me to <a href="http://www.blog.bridgeutopiaweb.com/post/how-to-fix-mysql-load-issues-on-mac-os-x/">a better solution</a>:</p>

<pre>export DYLD_LIBRARY_PATH=/usr/local/mysql/lib/</pre>

<p>That should work for everything! But in case it doesn&#8217;t, this will fix Rails:</p>
<pre class="prettyprint">
sudo install_name_tool -change libmysqlclient.16.dylib \ 
/usr/local/mysql/lib/libmysqlclient.16.dylib \
/usr/local/lib/ruby/gems/1.9.1/gems/mysql2-0.2.6/lib/mysql2/mysql2.bundle
</pre>

<p>And this will fix Ruby&#8217;s DBI:</p>

<pre class="prettyprint">
sudo install_name_tool -change libmysqlclient.16.dylib  \ 
/usr/local/mysql/lib/libmysqlclient.16.dylib \
/usr/local/lib/ruby/gems/1.9.1/gems/mysql-2.8.1/lib/mysql_api.bundle
</pre>

<p><strong>Note:</strong> you&#8217;ll need to substitute both the appropriate path to the gems, and the appropriate version of the MySQL library.</p>

<p>Fixing Perl&#8217;s DBI is a little trickier. You need to download the MySql DBD source and run the install_name_tool on the bundle before you install. Something like:</p>

<pre class="prettyprint">
sudo install_name_tool -change libmysqlclient.16.dylib \ 
/usr/local/mysql/lib/libmysqlclient.16.dylib \
/path-to-my-sql-dbd-source-folder-with-the-bundle-file/mysql.bundle
</pre>
]]></description>
            <link>http://www.nearinfinity.com/blogs/frank_showalter/ruby_perl_and_mysql_55_on_snow.html</link>
            <guid>http://www.nearinfinity.com/blogs/frank_showalter/ruby_perl_and_mysql_55_on_snow.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Database</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Mac OS X</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">mysql</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">osx</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">rails</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">ruby</category>
            
            <pubDate>Thu, 20 Jan 2011 09:25:46 -0500</pubDate>
        </item>
        
        <item>
            <title>Environment Specific Default Attachment Options for Paperclip</title>
            <description><![CDATA[<p><a href="https://github.com/thoughtbot/paperclip">Paperclip</a> is a popular Ruby file attachment solution for ActiveRecord. Following some basic setup, you can specify an attachment to an ActiveRecord model by calling the has_attached_file class method as shown in the example below.</p>

<pre class="prettyprint">
class Photo < ActiveRecord::Base
  has_attached_file :image,
    :styles => { :medium => '350x350>', :thumb => '100x100#' },
    :url    => ':class/:id/:style.:extension',
    :path   => ':rails_root/assets/:class/:id_partition/:style.:extension'
end
</pre>

<p>I love how easy it is to specify an attachment, but I don't like having to specify the :url and :path on all attachments for two reasons:</p>

<ol>
<li>It's not <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>. With the <a href="https://github.com/thoughtbot/paperclip/wiki/Interpolations">Paperclip interpolations</a> I'm using, the :url and :path are never going to change within an environment no matter how many attachments I have across all my models.</li>
<li>Between environments I do need to change the path. In development I keep the attachments in :rails_root/assets but in production I want them somewhere else.</li>
</ol>

<p>Fortunately, changing the Paperclip attachment defaults per environment is really easy. In your environment config files, do something like the following.</p>

<pre class="prettyprint">
# development.rb (or use environment.rb to setup defaults for all)
Paperclip::Attachment.default_options[:url] = ':class/:id/:style.:extension'
Paperclip::Attachment.default_options[:path] = ':rails_root/assets/:class/:id_partition/:style.:extension'

# production.rb
Paperclip::Attachment.default_options[:url] = ':class/:id/:style.:extension'
Paperclip::Attachment.default_options[:path] = '/usr/local/assets/:class/:id_partition/:style.:extension'
</pre>

<p>Adding those defaults in your environment configuration files means you can keep your models DRY and free of any ugly code used to change options depending on the environment. Isn't this nicer?</p>

<pre class="prettyprint">
class Photo < ActiveRecord::Base
  has_attached_file :image, 
    :styles => { :medium => '350x350>', :thumb => '100x100#' }
end
</pre>
]]></description>
            <link>http://www.nearinfinity.com/blogs/jeff_kunkle/environment_specific_default_a.html</link>
            <guid>http://www.nearinfinity.com/blogs/jeff_kunkle/environment_specific_default_a.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Web Development</category>
            
            
            <pubDate>Tue, 07 Dec 2010 09:09:34 -0500</pubDate>
        </item>
        
        <item>
            <title>Conferences are Not For Learning</title>
            <description><![CDATA[<p>Near Infinity has the <a href="http://www.nearinfinity.com/home/currentopenings.html" title="Awesome Training Program">most generous training program</a> I've heard of among our competitors, and certainly rivals the best in the industry. Colleagues occasionally ask me for opinions about a conference or training course they're considering. My stock answer for such inquiries has always been to "attend a good training class for depth of understanding on a narrow topic and pick a conference for more broad but shallower knowledge of many areas." I think the training course advice is still accurate, but I was wrong about conferences.</p>

<p>The best conferences aren't about learning at all. They're about inspiration. I finally realized this at RailsConf last week, despite having attended dozens of conferences during the past ten years. Surprisingly, I found most of the topics rather boring, most of the presenters unpolished, and most of the presentations poorly constructed and minimally rehearsed. The things I enjoyed most were the keynotes from Dave Thomas and David Heinemeier Hansson, a performance-related presentation by Aaron Patterson, an entrepreneurial story by Tom Preston-Werner, and a reflection talk by Keavy McMinn on her journey from artist to programmer. Why did I enjoy them? Because they caused me to look at things differently, forced me to think critically about my choices, and motivated me to act. Only one of these was technical in nature by the way, just one! </p>

<p>A good conference should inspire you to do something. No talk is long enough to teach you anything immediately useful. The best you can hope for is exposure to lots of stuff you might want to look into after the conference. Of course you could get the same level of exposure by reading the conference agenda, Googling each topic, and reading through some documentation on each homepage. What you can't get is the inspiration from a great speaker, motivational story, or engaged community. </p>

<p>So the next time you contemplate attending a conference, do your best to judge the inspiration potential. After all, if you're not going to do anything different after the conference, why bother going?</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/jeff_kunkle/conferences_are_not_for_learni.html</link>
            <guid>http://www.nearinfinity.com/blogs/jeff_kunkle/conferences_are_not_for_learni.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
            <pubDate>Mon, 15 Nov 2010 09:45:50 -0500</pubDate>
        </item>
        
        <item>
            <title>Advanced Git with Scott Chacon Comes to NIC-U December 4th</title>
            <description><![CDATA[Our friends at Jumpstart Lab will be holding an <a href="http://jumpstartlab.com/trainings/3-git-reston-va">Advanced Git</a> class December 4th, taught by GitHub's <a href="http://scottchacon.com/">Scott Chacon</a>, at the new NIC-U Training Center.<br /><div id=":42">
<br />
The Git source control system is quickly taking over the development world, replacing SVN and CVS. It's strengths lie in fast, distributed control of source code for teams from one to thousands. The real power of Git comes when you move beyond simply "doing what you used to do in SVN, just with Git." It's more than just commits and clones. In this advanced session you'll explore topics like:<br />
<br /><ul><li>
 Managing and merging multiple branches</li><li>
 Workflow best practices (rebasing, merging, etc)</li><li>
 Finding issues with bisect and blame</li><li>
 Hooks</li><li>
 Submodules</li><li>
 Customizing</li></ul>
<br />
This class will be taught by Scott Chacon, chief Git evangelist at <a href="http://github.com/">GitHub</a>, creator of <a href="http://gitcasts.com/">GitCasts</a>, and author of <a href="http://progit.org/book/">Pro Git (Apress) </a>and <a href="http://peepcode.com/products/git-internals-pdf">Git Internals (Peepcode)</a>. Don't miss this chance to learn Git from a true expert.<br />
</div> <br />The training provider, <a href="http://jumpstartlab.com/">Jumpstart Lab</a><a href="http://www.jumpstartlab.com/"></a>, is a DC-based training company with a long history of bringing interesting technology courses to our area. We are happy to have convinced them to hold this excellent course in our facility.<br />
<br />
Be sure to check out our <a href="https://www.nearinfinity.com/trainingcenter/coursecatalog/upcoming/">Upcoming NIC-U Training Courses</a> page for upcoming courses.<br />]]></description>
            <link>http://www.nearinfinity.com/blogs/gray_herter/advanced_git_with_scott_chacon.html</link>
            <guid>http://www.nearinfinity.com/blogs/gray_herter/advanced_git_with_scott_chacon.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Agile Development</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Git</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Git</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">GitHub</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Rails</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Ruby</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">training</category>
            
            <pubDate>Mon, 01 Nov 2010 08:40:00 -0500</pubDate>
        </item>
        
        <item>
            <title>Rack Lightning Talk</title>
            <description><![CDATA[<p>I gave a short lightning talk on <a href="http://rack.rubyforge.org/">Rack</a> tonight at the <a href="http://novarug.org/">NovaRUG</a>. It's on slideshare <a href="http://www.slideshare.net/scottleber/rack-5521616">here</a>. Rack is really cool  because it makes creating modular functionality really easy. For example, if you want to have exceptions mailed to you you can use the Rack::MailExceptions middleware, or if you want responses compressed you can add one line of code to a Rails app to use Rack::Deflater. Cool.</p>]]></description>
            <link>http://www.nearinfinity.com/blogs/scott_leberknight/rack_lightning_talk.html</link>
            <guid>http://www.nearinfinity.com/blogs/scott_leberknight/rack_lightning_talk.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">middleware</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">rack</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">ruby</category>
            
            <pubDate>Thu, 21 Oct 2010 20:26:05 -0500</pubDate>
        </item>
        
        <item>
            <title>Configuring WEBrick to use SSL in Rails 3</title>
            <description><![CDATA[<p>The other day my project took on the task of upgrading our (fairly new) rails application from Rails 2.3.5 to Rails 3.  Everything was going great until we got to the part of actually trying to run our application under WEBrick. </p>

<p>Our application has some services that it connects to that require secure connections so in order to comply and still develop we had modified the WEBrick configuration under 2.3.5 to make it run under SSL. This turned out to be very easy to accomplish with 2.3.5, we just cracked open the script/server file and added the appropriate SSL options that WEBrick already knows about.</p>

<p>Rails 3 proved to be a little bit more difficult to modify.  This is due to the fact that the script folder only contains a rails script now and all of the old scripts are now contained deep in the rails build.  I didn&#8217;t really want to crack open rails and start modifying (especially not when it is only for development), so I was looking for another option.  I spent a few days searching the internet with no luck.  Today I came into work and after 6+ hours and some pair programming a solution was figured out.  You can find the solution that we came up with below, and if there is a more elegant solution out there I would love to hear about it.</p>

<p>All you have to do to get WEBrick running in SSL is modify the script/rails file and add the following lines (above the APP_PATH variable declaration):</p>

<pre><code>require 'rubygems'
require 'rails/commands/server'
require 'rack'
require 'webrick'
require 'webrick/https'

module Rails
    class Server &lt; ::Rack::Server
        def default_options
            super.merge({
                :Port =&gt; 3000,
                :environment =&gt; (ENV['RAILS_ENV'] || "development").dup,
                :daemonize =&gt; false,
                :debugger =&gt; false,
                :pid =&gt; File.expand_path("tmp/pids/server.pid"),
                :config =&gt; File.expand_path("config.ru"),
                :SSLEnable =&gt; true,
                :SSLVerifyClient =&gt; OpenSSL::SSL::VERIFY_NONE,
                :SSLPrivateKey =&gt; OpenSSL::PKey::RSA.new(
                       File.open("path/to/key").read),
                :SSLCertificate =&gt; OpenSSL::X509::Certificate.new(
                       File.open("/path/to/crt").read),
                :SSLCertName =&gt; [["CN", WEBrick::Utils::getservername]]
            })
        end
    end
end
</code></pre>
]]></description>
            <link>http://www.nearinfinity.com/blogs/chris_rohr/configuring_webrick_to_use_ssl.html</link>
            <guid>http://www.nearinfinity.com/blogs/chris_rohr/configuring_webrick_to_use_ssl.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">rails3</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">ssl</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">webrick</category>
            
            <pubDate>Tue, 07 Sep 2010 14:26:14 -0500</pubDate>
        </item>
        
    </channel>
</rss>

