Friday, 17 March 2017

Applied Rails: Bulleted Text With Prawn

I use the prawn gem to generate pdf documents in my Rails application. It has a drawback that it does not have built-in support for displaying bulleted text. So I wrote a simple function that took a string parameter and printed it with an indent and a leading asterisk.
def bullet_item string
    indent 15, 0 do
        text "* " + string, :align => :justify
    end
end
So you get output like:

But this solution had an issue if the sentences are long. The second line starts at same indent as the first line, that is, it gets aligned to the star. Users are familiar with Office documents in which the first line and second line of bulleted text start at the same indentation.

See the following output:

To fix the problem, I split the string into two, as follows:
def bullet_item_2 string
    if string.length > 110
        sub_str = string[85..string.length-1]
        space_index = sub_str.index(' ')
        str1 = string[0..85+space_index]
        str2 = string[86+space_index..string.length-1]
        indent 10, 0 do
            text "* " + str1, :align => :justify
            indent(9) {text str2, :align => :justify}
        end
    else
        indent 10, 0 do
            text "* " + string, :align => :justify
        end
    end
end
In here, I split the string into two, if it is more than 110 characters long. I first get the index of the space character after the 85th character and use that position to split. I then print both the strings at the same indentation.

The output looks like:

But the problem did not go away. I was told that some of the sentences could be really long, perhaps would span three or four lines. Here is such text, taken from Wikipedia, that I will use to illustrate:
A Welding Procedure Specification (WPS) is the formal written document describing welding procedures, which provides direction to the welder or welding operators for making sound and quality production welds as per the code requirements . The purpose of the document is to guide welders to the accepted procedures so that repeatable and trusted welding techniques are used. A WPS is developed for each material alloy and for each welding type used. Specific codes and/or engineering societies are often the driving force behind the development of a company's WPS. A WPS is supported by a Procedure Qualification Record (PQR or WPQR). A PQR is a record of a test weld performed and tested (more rigorously) to ensure that the procedure will produce a good weld. Individual welders are certified with a qualification test documented in a Welder Qualification Test Record (WQTR) that shows they have the understanding and demonstrated ability to work within the specified WPS.
Back to the keyboard again I went, and came up with another solution, as given below:
def extractFirstPart(str)
    return str if str.length < 110
    substr = str[85..str.length-1]
    space_index = substr.index(' ')

    if space_index != nil
        return str[0..85+space_index]
    else
        return str
    end
end

def bullet_item_3 string
    counter = 1
    myStr = string.clone
    while myStr.length > 0 do
        str = extractFirstPart myStr
        if counter == 1
            indent 10, 0 do
                text "* " + str, :align => :justify
            end
        else
            indent(19) {text str, :align => :justify}
        end
        myStr.slice! str
        counter += 1
    end
end
This time, I extracted sub-strings in a loop and printed them with indentation. The problem of any length string was solved, but not the text-alignment issue. This latest solution made the problem even worse, as lines were cut at the right end at different positions.

This is how it looked like:
Finally, I realized that an aligned and justified bulleted text is nothing but a table without borders. We can have the asterisk in the first column and the text in the second column. Since the prawn table functions render text properly in the cells, this might work.
def bullet_text_with_borderless_table data
    bullet_data = []
    data.each {|d| bullet_data << ["*", d]}
    t = make_table bullet_data, {:cell_style => { :borders => [], :align => :justify, :padding => [0,10,0,0]}, header: true}
    indent(20) {t.draw}
end
And it worked..! Sometimes in programming life, dirty hacks are required to get clean output. Here is how my text gets displayed now:
Here is --> the file that has all three solutions. The output pdf is --> here.

Friday, 3 March 2017

Applied Rails : Customizable Text

I got a requirement in my Rails application in which the user would add a set of "Terms & Conditions" in the Offer screen. These are typically standard, but the user should be able to customize the individual clauses. It sounded oxymoronic, but remembering Scott Meyers' words, "pretty this ain’t, but sometimes a programmer’s just gotta do what a programmer’s gotta do," I set out in right earnest to figure out what can be done.

Wednesday, 15 February 2017

Applied Rails : Variable Rows For Child Records

I got a requirement in my Rails application to give the user the ability to input a variable number of records with multiple fields. The fields were of text type as well as select comboboxes. These rows had to be saved to the database as child table rows.

Wednesday, 11 January 2017

Welcome 2017

First blog post at the beginning of the year, as was last year's, is a quantified compilation of my personal activities outside of my day job.

Sunday, 6 November 2016

Book Review : Everyone Has A Story

The novel 'Everyone Has a Story' by Savi Sharma is structured on the classic three acts of a romantic story: lady meets man, man goes away from lady and lady gets man back. Lady's goal is to become a writer.
It was a breezy read for me and appealed to me as a record of the good-heartedness in people.

Friday, 21 October 2016

Steps And Commands For RoR Application Deployment On Linode Server

I deployed my Ruby-on-Rails application on a cloud server.

About a couple of years ago, I got into this practice of hosting my applications on the cloud. At that time AWS did not offer Ubuntu machines. I love Ubuntu so I did not go to AWS. I also have this feeling that access and deployment procedures on the biggies (AWS and Azure) are slightly more cumbersome than the not-so-biggies. Amongst these, Site5 is the cheapest but I don't get root access and was very disappointed with their lack of support. For $20 a month, I got a 4 GB RAM server on Linode compared to 2 GB RAM on Digital Ocean. Hence I went with Linode.

Friday, 8 April 2016

Practical Rails : Adding a bootstrap theme

When you are trying to get your webapp prototype or MVP up and running, you wouldn't wait for your UI group to deliver their entire UI assets. {wrap}bootstrap is a good place to get bootstrap themes for less than $20. And who knows, the theme you select maybe good enough to go to production.

In this post, I present the steps to plug a bootstrap theme into your Rails app.