Substruct 1.0.a4
Substruct, the Ruby on Rails e-commerce and content management software just tagged and bagged the last release before we go 1.0.
The version number is seriously misleading as the project has been active for over 2 years. Oh well…
Major changes
- Update to Rails 2.1.0
- Lots of bug fixes and updates from our community
Get the good stuff
- See all of the latest changes
- Download the latest version from SVN, or a tar / gzipped archive
- Discuss Substruct with us at Google Groups
Enjoy…
Substruct v1.0a3
Substruct v1.0.a3 was just tagged and bagged.
Lots of bugfixes and a mostly working test harness. Next step, fully working tests :)
Go to the home page to download, or get your fix directly from here.
Find out what ports your mongrels are using
I like mongrel. I deploy all of our Rails apps using it.
But…I got sick of opening and reading each of my mongrel_cluster.yml config files when adding a new mongrel to the server.
I cooked up this quick script to list all Rails apps on a production box – and their mongrel ports. It assumes all of your apps live in /var/www/(app_name)/current. You might have to do some modifications if you store your Rails apps in a different place.
Maybe you’ll find it useful as well.
list_mongrel_ports.rb
#!/usr/bin/env ruby
#
# Loops through all rails apps and compiles port information
# for mongrels.
#
# Great for figuring out what ports are in use when deploying a new app.
#
# Written by seth @ subimage llc - http://sublog.subimage.com
#
require 'fileutils'
APP_DIR = '/var/www/'
def get_mongrel_config_info(app)
config_f = File.join(APP_DIR, app, 'current', 'config', 'mongrel_cluster.yml')
if File.exists?(config_f)
starting_port, servers = ''
File.open(config_f, 'r') do |f|
while line = f.gets
if line.include?('port:')
starting_port = line.gsub(/port:|"| /, '')
elsif line.include?('servers:')
servers = line.gsub(/servers:| /, '')
end
end
end
servers ||= 1
ports = []
servers.to_i.times do |i|
ports << starting_port.to_i+i
end
return ports.join(', ')
else
return nil
end
end
Dir.open(APP_DIR).each do |app|
info = get_mongrel_config_info(app)
puts "#{app}\n ports: #{info}" unless info.nil?
endSample output
-> ./list_mongrel_ports.rb
cashboard
ports: 8500, 8501, 8502, 8503, 8504
getcashboard
ports: 8010, 8011
cbinfo
ports: 8700
cbforum
ports: 8705Hrm…looks like I could do a little reorganization of my ports :)
Automated testing is the shit
Up until a week ago I never really understood the draw of automated testing.
I come from the school of manual test scripts that you run through before every new release.
Write down things you should do. Walk through the app, click on stuff, make sure it works.
Slow, error prone, and time consuming – but it’s what I knew. That all changed last week when I dusted off my copy of Extreme Programming Installed and burned through the remainder I had failed to read previously.
I finally jumped into unit testing in Ruby on Rails with Mocha and Autotest. I’m steadily catching up on tests for Cashboard and it’s amazing the things you find when you start automated testing.
Mocha
Mocha is a library useful for “mocking” or “stubbing” code that you want to verify executes, but not really run. Useful for network calls, sending emails, and all those other things you want to make sure get called by your test code, but don’t actually run.
Autotest
Autotest is part of the ZenTest suite. You run it from the root of your project and it monitors your code for any changes made.
I keep it running in a terminal next to TextMate and watch it spring into action and run my tests immediately after I save them. Major time saver.
For extra nerd points you can even get Autotest to display notifications with the dude from DOOM, letting you know if your tests passed or failed.
Nice that DOOM guy is now boosting my productivity, instead of draining it in years past. (How many hours did I waste blasting monsters in that thing?)
Security in testing
I’ve already cornered a few long-standing bugs via test code, and I’m on my way to full coverage for all units.
It feels good, and secure. If you’re programming in RoR and aren’t testing today…make the jump. Dig in, learn it. It’s worth the headache and trouble you’ll save in the long run.
Side note
Does anyone else buy massive amounts of books from Amazon, read a few chapters, then let them sit? I’ve got to stop doing that and finish off the ones I purchase.
Substruct v0.97
...Just got done prepping a fairly major release of Substruct, the first and only open-source RoR e-commerce platform.
This release updates Substruct to Rails 2 / Engines 2, replaces file_column with attachment_fu, AND is the initial release that will be distributed in archive form. Sorry no PayPal IPN support yet – within the next few days hopefully…
Shouts out to Luke Ludwig for his contribution of the attachment_fu patch.
From now on, all new people to Substruct are recommended to download the latest tar/gzipped archive avaliable here.
New install instructions have been placed on the site…and are much easier than before.
Now the project even includes a rake task that checks if you have the correct gems installed.
I have only tested here on my OS X box, so hopefully things will go smooth on Windows as well.
Version 0.97
---------------------------------------
Added:
1. Added nice installer rake task
2. Upgrade to Rails 2.0.2 / Engines v2
3. Images and uploads switched from using file_column and rmagick to
using attachment_fu and mini_magick.
Upgrade steps:
1. Make sure you have the mini_magick and mime-types gems installed.
a. sudo gem install mini_magick
b. sudo gem install mime-types
* Note: mime-types gem is only needed to upgrade, but is not
needed after the upgrade is complete.
2. Back up your database just in case.
* mysql example: mysqldump -u USERNAME -p DATABASE_NAME > backup.sql
3. script/generate plugin_migration
4. rake db:migrate
* All of your uploads and images will be copied to a new location.
Since it is a copy it is non-destructive, leaving the originals
in their place.
5. Check out the new environment.rb file in the
vendor/plugins/substruct/config directory.
Prince.rb update
Michael Day from YesLogic emailed me the other day to alert me to a possible problem with my original HTML/CSS to PDF in Rails code.
We’ve recently received a bug report on our forum of Ruby leaving zombie Prince processes running on UNIX systems:http://princexml.com/bb/viewtopic.php?t=1149
It looks as if this issue affects Seth’s module and probably the Princely plugin which appears to be based upon it.
I believe the solution for this is to add a call to pdf.close_read as mentioned at the end of that thread.
Cheers,
Michael
— Print XML with Prince! http://www.princexml.com
Below I’ve pasted the newest code, and here’s a link to the fixed prince.rb library.
Enjoy.
Prince.rb
# Prince XML Ruby interface.
# http://www.princexml.com
#
# Library by Subimage Interactive - http://www.subimage.com
#
#
# USAGE
# -----------------------------------------------------------------------------
# prince = Prince.new()
# html_string = render_to_string(:template => 'some_document')
# send_data(
# prince.pdf_from_string(html_string),
# :filename => 'some_document.pdf'
# :type => 'application/pdf'
# )
#
class Prince
attr_accessor :exe_path, :style_sheets, :log_file
# Initialize method
#
def initialize()
# Finds where the application lives, so we can call it.
@exe_path = `which prince`.chomp
@style_sheets = ''
@log_file = "#{RAILS_ROOT}/log/prince.log"
end
# Sets stylesheets...
# Can pass in multiple paths for css files.
#
def add_style_sheets(*sheets)
for sheet in sheets do
@style_sheets << " -s #{sheet} "
end
end
# Returns fully formed executable path with any command line switches
# we've set based on our variables.
#
def exe_path
# Add any standard cmd line arguments we need to pass
@exe_path << " --input=html --server --log=#{@log_file} "
@exe_path << @style_sheets
return @exe_path
end
# Makes a pdf from a passed in string.
#
# Returns PDF as a stream, so we can use send_data to shoot
# it down the pipe using Rails.
#
def pdf_from_string(string)
path = self.exe_path()
# Don't spew errors to the standard out...and set up to take IO
# as input and output
path << ' --silent - -o -'
# Show the command used...
#logger.info "\n\nPRINCE XML PDF COMMAND"
#logger.info path
#logger.info ''
# Actually call the prince command, and pass the entire data stream back.
pdf = IO.popen(path, "w+")
pdf.puts(string)
pdf.close_write
output = pdf.gets(nil)
pdf.close_read
return output
end
end
Rails Rumble - Have you voted yet?
If you haven’t done so already, go check out and vote for your favorite Rails Rumble entry of 2007.
Some amazing applications have been created in the time span of only two days.
Of course, we’ll be giving away prizes to the winners, along with many other great sponsors.
Cashboard is an official sponsor of Rails Rumble 2007
I’m proud to announce that Cashboard is an official sponsor of the Rails Rumble 2007.
The Rails Rumble is a competition to test your Ruby on Rails hacking skills, build some cool new web apps, and make some friends.
The concept is simple: you get 48 hours to design, develop, and deploy a web application from scratch. After those 48 hours are up, you’ll be judged by the community through a peer-ranking system in a variety of categories (see Rules for more information). After about a week of controlled mayhem, judging wraps up and the dust settles. Winners are then declared, and awarded some cool prizes. We’ll be announcing what those prizes are shortly.
We will be giving away 6 year-long Maple subscriptions (a $1080 value each) to Cashboard as prizes, one for each category winner.
HTML / CSS to PDF using Ruby on Rails
Ever tried to save a web page and send it to someone? What about printing a web page? Both pretty much suck.
A lot of people are talking about printing with HTML and CSS these days but what they don’t tell you is the following:
With CSS you can’t…- Determine where page breaks happen.
- Set page size or type (landscape or portrait).
- Print background colors…some browsers don’t even print images.
- Set page footers.
Wow, that’s lame.
Ever tried sending someone a web page? Not a link, but the actual web page. It doesn’t work well at all.
A little history
I wanted to be able to print or send estimates and invoices from our Ruby on Rails application, Cashboard. A good majority of our users felt the same way.
Just sending a link to someone to view a HTML document online doesn’t cut it. What if you want to snail mail that document? Shouldn’t it look the same on the page as it does on your screen?
When I used to create invoices manually I’d always save PDF files for my clients and email them.
PDF handled printing well, was portable, and my clients couldn’t edit the file.
We needed PDF for Cashboard.
The Prince of PDFs
I spent a good week just researching all of the PDF libraries and programs out there.
The ones I found for Ruby wanted me to re-create my documents in some PDF-specific layout language. That wasn’t going to cut it. See, in Cashboard we already had these documents designed in HTML. On top of that, we allow users to customize the colors and upload their logo for invoices. Duplicating the layout wasn’t an option.
After exhausting those possibilities I started looking into HTML to PDF programs. I found a few, but none that were any good at converting HTML and CSS to PDF.
This all changed when I ran across Prince XML. Not only is Prince great at converting HTML and CSS to PDF, it even passed the Acid2 test. They had a ton of demos available on their web site, and provide their program free for evaluation purposes.
Making it happen
Prince is a command line program, available for whatever platform you’re probably running.
It can take a variety of inputs including files on disk, or even HTTP urls. We run a pretty tight ship on Cashboard, and everything is password protected.
Luckilly Prince also can take input from standard in, and can even pass its output back to standard out. This means if you don’t want to mess with saving files and dealing with cleaning them up you don’t have to.
prince.rb, pdf_helper.rb
I cooked up a very simple Ruby library to call Prince and a helper module to include on my Rails controllers. I store them both in the lib folder of my Rails application.
Here’s the full helper, slightly modified for simplicity:
# We use this chunk of controller code all over to generate PDF files.
#
# To stay DRY we placed it here instead of repeating it all over the place.
#
module PdfHelper
require 'prince'
private
# Makes a pdf, returns it as data...
def make_pdf(template_path, pdf_name, landscape=false)
prince = Prince.new()
# Sets style sheets on PDF renderer.
prince.add_style_sheets(
"#{RAILS_ROOT}/public/stylesheets/application.css",
"#{RAILS_ROOT}/public/stylesheets/print.css",
"#{RAILS_ROOT}/public/stylesheets/prince.css"
)
prince.add_style_sheets("#{RAILS_ROOT}/public/stylesheets/prince_landscape.css") if landscape
# Render the estimate to a big html string.
# Set RAILS_ASSET_ID to blank string or rails appends some time after
# to prevent file caching, fucking up local - disk requests.
ENV["RAILS_ASSET_ID"] = ''
html_string = render_to_string(:template => template_path, :layout => 'document')
# Make all paths relative, on disk paths...
html_string.gsub!("src=\"", "src=\"#{RAILS_ROOT}/public")
# Send the generated PDF file from our html string.
return prince.pdf_from_string(html_string)
end
# Makes and sends a pdf to the browser
#
def make_and_send_pdf(template_path, pdf_name, landscape=false)
send_data(
make_pdf(template_path, pdf_name, landscape),
:filename => pdf_name,
:type => 'application/pdf'
)
end
endThis simple module has two methods. Both take an ERB template path as an argument, then a pdf file name.
Make_pdf renders the template to a string, then does some modifications to make all requests within local. I’m passing in all CSS files locally as well, so I don’t have to deal with authentication.
I’ve created some special CSS files for printing, and even can pass in if I’d like the page to be laid out in a portrait or landscape format.
When it’s done it returns the PDF file as data. Nothing is rendered to disk. This method is useful for not only sending the PDF file to the client (as in make_and_send_pdf), but when generating PDF files for email attachments.
Creating PDF files from the controller
Both of these files make creating a PDF file dead easy.
Here’s a slimmed down version of Cashboard’s estimate controller.
class Provider::EstimatesController < Provider::BaseController
include PdfHelper
# Sends pdf of an estimate out...
#
def pdf
# @estimate is set with a before_filter and isn't relevant for this how-to ;)
make_and_send_pdf('/client/estimates/show', @estimate.pdf_name)
end
endDoes it get any easier than that? Check out an example of a PDF file generated with Prince, straight from Cashboard. (Right click and save to your disk…)
Download
Here again are the libraries I’ve created for using Prince XML with Ruby on Rails:They’re free to use, download em and check em out.
Cashboard presentation at May's Silicon Valley Rails Meetup
![]()
I’ll be giving a presentation about Cashboard at the upcoming Silicon Valley Ruby on Rails meetup. If you want some of the behind-the-scenes insight on the product it’ll be worth attending.
For times and directions click here.
Hope to meet some of you there!
Go to page:
1 2
(Showing 1 - 10 of 14 articles total.)

