December 1, 2010

Job Position Available

Filed under: Random Business Thoughts,Team Dynamics — Stacy @ 1:42 pm

We just posted a position to our website today for a writing position at Thunder Data.  We are seeking an intern to write articles and blog posts for ThunderTix, a ticketing software to help manage events for small venues to sell tickets. We are looking for a talented writer to create both educational articles and short blog posts. We’ll provide the topics and concepts for each writing assignment.

This is a paid position with flexible hours for a college intern or graduate interested in part-time work.  All work can be performed outside the office, so close proximity to our office is not necessary.  Our preferred candidate would be working towards a degree in either English or Journalism.  Experience in marketing or public relations is a plus, but is not required. 

If you are interested in this position or know someone who might be, please send resumes and/or a portfolio to careers_info@thunderdata.com.

Come and join our team!

October 13, 2010

Using PHP’s preg_match() to Parse Credit Card Data from Magnetic Swipe Readers

Filed under: TDS Developer's Corner — Dawn @ 12:24 pm

Magnetic swipe card readers via USB can facilitate faster credit card processing. This is particularly important for our ThunderTix clients whose box offices must move patrons past the ticket window as quickly as possible. This week, a client from one of our legacy PHP apps asked us if we could implement a swipe reader for his own administrative checkout page. I thought it would be a good time to create a tutorial for parsing the credit card string using PHP and regular expressions.

Our client wanted the ability to enter either all form fields during the checkout process by manual entry or only some of the required fields when swiping the card during a purchase. We create a form for the checkout:

<span style='required'>*</span> - Indicates required field.
<div class='fields'>First Name</div>
  <input type=text name='first_name'><span style='required'>*</span>
</div>
<div class='fields'>Last Name</div>
  <input type=text name='last_name'><span style='required'>*</span>
</div>
<div class='fields'>Expiration</div>
  <input type=text size=8 name='expiration'><span style='required'>*</span>(MMYY)
</div>
<div class='fields'>CVV Code</div>
  <input type=text size=8 name='cvv'><span style='required'>*</span>
</div>
<div class='fields'>Credit Card Number</div>
  <input type=text name='card'><span style='required'>*</span>
</div>

Now, how do we bypass the validation for the required fields? Using regular expressions, we can both assess whether the credit card post variable is from the magnetic stripe data and parse the string itself when a match exists.

The string generated from a magnetic stripe reader looks like this :

    %B4444490633714269^DOE/JANE B^12101010000000379000000?    

The data from the string is divided by the start sentinel (%B), the end sentinel (?), and field separators (^ and /). So, we need to create a regular expression to parse the pieces out. We’ll create the following regex pattern and save it to the $swipe variable.

$swipe = "/^(%B)([0-9]{16})(^)([a-zA-Z-s]*)(/)([a-zA-Z-s]*)(^)([0-9]{2})([0-9]{2})(.)*?$/";

Let’s break down our regular expression:

  /^(%B)  = Start Sentinel and format Code (alpha only)
([0-9]{16}) = Primary account number is 16 digits long, accepts only numeric characters
(^) = Field Separator
([a-zA-Z-]*) = Last Name contains only alpha characters, a space, or a dash. Note the - and sare escaped
(/) = Separator between first and last name (escaped forward slash)
([a-zA-Z-]*) = First Name accepts only alpha, spaces, or dashes
(^) = Field Separator
([0-9]{2}) = Expiration year is two digits, accepts only numeric characters
([0-9]{2}) = Expiration month is two digits, accepts only numeric characters
(.)*?$/ = The "(.)*" sequence is any other characters that follow through to the end sentinel for the mag data represented by a the Question Mark is reMagnetic Data 'end sentinenel'

So, now we’ll move onto the form’s action. We’ll use preg_match to compare our $_POST['card'] string to our regex contained in $swipe. The result is held in the $matches array. If a match exists, it is held in $matches[0], and $matches[1] contains the first parenthesized value, $matches[2] contains the next (of actual card number), and so on. We’ll assign these to the $_POST variables, so our form can be processed as if the data had been entered into the fields individually. If there is no match, standard validation processing occurs.

if($_POST['action'] == 'pay') {
  $swipe = "/^(%B)([0-9]{16})(^)([a-zA-Z-s]*)(/)([a-zA-Z-s]*)(^)([0-9]{2})([0-9]{2})(.)*?$/";
  
  if (preg_match($swipe, $_POST['x_card_num'], $matches)) {
    $_POST['card'] =  $matches[2];
    $_POST['first_name'] = $matches[6];
    $_POST['last_name'] =  $matches[4];
    $_POST['expiration'] = $matches[9]."".$matches[8];
  }  else {
    perform_validation_procedures ...
}

A couple of final notes: if using Authorize.net as your gateway, make sure you have the Address Verification Service (AVS) matching turned off. This will prevent swipes from passing and result in a declined transaction since no address is being passed. Likewise, you may also want to turn off the CVV requirement to prevent a rejection. Otherwise, you can enter the CVV number in the CVV form field prior to swiping the card. Also, make sure you properly order the expiration month and year and the first and last names, or your transaction will fail.

Happy parsing!

July 19, 2010

10 Steps to Capistrano and Plesk Subdomains Deployment

Filed under: Designer Showcase,TDS Developer's Corner — Dawn @ 12:56 pm

We’ve adopted a standard convention for deploying Rails apps that places the files in a subdomain of our clients’ domains. Since we use Plesk, there are some steps we have to take to get Capistrano deploys working. Since we deploy a lot of apps, I decided to write this guide as much for the office as for anyone who may find it useful. Let’s go through the steps:

  1. Create your subdomain in Plesk (ie. myrailsapp)
  2. Navigate to the /domain.com/subdomains/myrailsapp/conf directory
  3. Create a vhost.conf file. Again, we use a convention that creates a new directory within /myrailsapp that holds our rails code. Fittingly, we name the directory /rails. The DocumentRoot points to the public directory of our application. Enter the following into the vhost.conf file:
  4. DocumentRoot /var/www/vhosts/domain.com/subdomains/myrailsapp/rails/current/public 
    RailsEnv production
    
    
  5. If you haven’t already installed the capistrano and capistrano-ext (for multistage environments such as testing and production deploys), you’ll need to get them. The Capistrano tutorials will walk you through that process.
  6. With the gems installed, now we want to capify our code. In your root rails app directory, enter the following command. (You may have to sudo the command depending on your directory permissions.)
  7. capify .
    

    This will create two files: Capfile and deploy.rb in your /config directory.

  8. Open up your deploy.rb file and enter your configuration variables. Again, you can refer to the Capistrano tutorials for help.
  9. If you are only planning to deploy to a single environment, you’re work is done. If you plan a multi-stage environment, you’ll want to set the stages in your deploy.rb file and require the capistrano-ext gem like this:
    set :stages, %w(testing, production)
    set :default_stage, "testing"
    require "capistrano/ext/multistage"
    
    
  10. Within the /config directory, create a new /deploy directory and two new files, testing.rb and production.rb. These files will provide the deploy details for each environment.
  11. Open up your new .rb files and enter the following for each environment:
    
    #############################################################
    #   Application
    #############################################################
    
    set :application, "myrailsapp"
    set :deploy_to, "/var/www/vhosts/domain.com/subdomains/myrailsapp/rails"
    
    #############################################################
    #   Settings
    #############################################################
    
    default_run_options[:pty] = true
    ssh_options[:forward_agent] = true
    set :use_sudo, true
    set :scm_verbose, true
    set :rails_env, "testing" 
    
    #############################################################
    #   Servers
    #############################################################
    
    set :user, "username"
    set :password, "user_password"
    set :domain, "myrailsapp.domain.com"
    server domain, :app, :web
    role :db, domain, :primary => true
    
    #############################################################
    #   Passenger
    #############################################################
    
    namespace :deploy do
      desc "Create the database yaml file"
      task :after_update_code do
        db_config = <<-EOF
        production:    
          adapter: mysql
          encoding: utf8
          username: database_username
          password: database_password
          database: database
          host: localhost
        EOF
        
        put db_config, "#{release_path}/config/database.yml"
    
      end
        
      # Restart passenger on deploy
      desc "Restarting mod_rails with restart.txt"
      task :restart, :roles => :app, :except => { :no_release => true } do
        run "touch #{current_path}/tmp/restart.txt"
      end
      
      [:start, :stop].each do |t|
        desc "#{t} task is a no-op with mod_rails"
        task t, :roles => :app do ; end
      end
      
    end
    
    
    Then add the details of your repository for either subversion or git.
    
    
    #############################################################
    #   GIT
    #############################################################
    
    set :scm, :git
    set :branch, "master"
    set :repository, "git@github.com:your_github_path/MYRAILSAPP.git"
    set :deploy_via, :remote_cache
    ssh_options[:forward_agent] = true
    
    #############################################################
    #   SVN
    #############################################################
    
    set :scm, :svn
    set :branch, "trunk"
    set :scm_user, 'username'
    set :scm_password, "user_password"
    set :repository, Proc.new { "--username #{scm_user} --password '#{scm_password}' --no-auth-cache http://doman.com/path/to/your/subversion/repository/#{application}/trunk" } 
    
    set :deploy_via, :remote_cache
    ssh_options[:forward_agent] = true
    
    
  12. That’s it; you’re ready to deploy. Back at your terminal window, you’ll type the respective environment to which you’re deploy:
cap testing deploy

A final note: make sure the user specified in your deploy files has permission to create directories on the server, otherwise your /releases directories will not be created.

Feel free to ask any questions if you’re having trouble with your Plesk subdomain and Capistrano deployment. Good luck!

January 7, 2010

Rails validation error not displaying – validates_presence_of, error_message_for

Filed under: Newbie Rails Trip-ups,TDS Developer's Corner — Dawn @ 4:26 pm

I hit a wall recently as I tried to track down the reason validates_presence_of was not displaying errors. Rails validations normally go off without a hitch, so I was a bit stumped when my attempts to save blank fields were not triggering the appropriate messages. After ensuring my model and view were correct, I fired up the console to make sure my validations were working in the first place. After loading an object and attempting a save, I checked to see if the errors object returned false. Sure enough, all of the validations were enforced.


>> @contact = Contact.find_by_id(9)
=> #
>> @contact.save
=> false
>> @contact.errors.empty?
=> false
>> @contact.errors.count
=> 6
>> @contact.errors.each_full { |msg| puts msg }
   City No blanks
   Zip No blanks
   First name No blanks
   Address No blanks
   Last name No blanks
   State No blanks

After more head banging, I was ready to give up with a simple notification workaround on the update method.


if !@contact.errors.empty?
  flash[:error] = "You are missing required fields."
  redirect_to edit_contact_url(@contact.user)
else
  flash[:notice] = "Successfully saved."
  redirect_to contact_url(@contact.user)
end

That works, but it doesn’t take advantage of the Rails’ error_messages_for helper to display helpful messages and highlight problem fields. Then I realized the initial error. During redirects, the object’s data isn’t passed, so while the validations were enforced (the form data would not save upon submission), no errors were being output to the screen. My new code rendered the page along with the error messages:


if !@contact.errors.empty?
  render :action => 'edit'
else
  flash[:notice] = "Successfully saved."
  redirect_to contact_url(@contact.user)
end

October 7, 2009

Magnets in a Digital World: Credit Card Swipe Processing

Filed under: Random Business Thoughts,Team Dynamics — gary @ 2:37 pm

In a traditional store, paying by credit card involves swiping the card through a swipe machine, which reads the data stored on a credit card’s magnetic stripe.  The data includes a few key bits of information, namely:

  • The account number and expiration date of the card
  • The account holder’s name
  • Some provider-specific data for verification

For a typical online transaction, the magnetic stripe on a card is not used.  Purchases are made by entering account information manually, along with verification values not found in the magnetic stripe.  This is acceptable for most people, as few computers come with magnetic card swipes.

However, what if someone wants to use an online system to run in-person transactions?  With ThunderTix, many of our clients use our system for ticket sales on site.  Typing every bit of data for each customer is slow, and tends to make people in line cranky.  Fortunately, the magnetic stripe comes to our rescue.  The first part of a magnetic stripe’s data looks something like this:

%Bxxxxxxx^LASTNAME/FIRSTNAME ^yymmzzzzzzzzzzzzzzz?

where the

xxxxxxx

is an account number,

LASTNAME/FIRSTNAME

are the account holder’s name,

yymm

is the expiration date, and

zzzzzzzzzz

is additional numbers.  Using the standard format to detect a card swipe transaction, we can extract this information to expedite order processing:

if credit_card_number =~ /^%B(.)*?$/
card_fields = params[:cc][:number][2..-1].split('^')
card_number = card_fields[0]
name = card_fields[1].strip.split('/')
first_name = name[0]
last_name = name[1]
expiration_year = card_fields[2][0,2]
expiration_month = card_fields[2][2,2]
end

A few bits of additional data (magnetic stripes do not contain the verification values used for online transactions), and we’ve managed to cut each patron’s wait time significantly.

September 16, 2009

Marketing in Down Times

Filed under: Random Business Thoughts — Dawn @ 10:01 am

I’m always pleasantly surprised when I stumble upon the result of a company’s marketing efforts and realize how perfectly they succeeded in directing their message. My favorite, a television ad campaign for Prego, used music to drive home the old-world style of their spaghetti sauce and succeeded beautifully.

In yesterday’s NY Times section on advertising, the article focused on Tropicana orange juice. Included with the piece was an ad image containing Tropicana’s new tag line along with a plain-jane carton of “store brand” OJ. At a glance, I assumed Tropicana was positioning their orange juice as a cut above the generic store brand. It was only upon reading the article that I recognized the simple carton design was actually Tropicana’s adopted new look–replacing the old straw and orange logo. Why would they choose such a simple carton? To appeal to budget conscious consumers.

Interestingly, Tropicana’s full brand name includes the words, “Pure Premium”. Coming at a time when buyers are eschewing luxury items, Tropicana needed to downplay “premium” without sacrificing their brand association with quality . The carton is an effective messenger. Throw in the cute new screw cap that’s shaped like half an orange, and Tropicana is poised to retain its 40% market share–even as shoppers trim their budgets. That’s effective marketing!

May 29, 2009

Can Management Understand Employee Needs?

Filed under: Team Dynamics — Dawn @ 11:40 pm

During a conversation with a large national client, two of their staff members made the implication that their “boss”, the executive director, was out of touch with what was going on in the trenches, and he should not be consulted on important decisions.   Though I agree that the director may be unaware of the day-to-day minutiae of staff work, as the person who signs the check that pays me, the director’s feedback from a high level was important to us.  When I hung up the phone, I asked my office why such a common misconception existed–an employee’s belief that management lacks an understanding of their work?

Bad managers do exist, but the vast majority of my clients have exceptional leaders–including the aforementioned director.  Most are intimately aware of the roles their employees play in their success. A good manager shouldn’t have to know the details of an employee’s job; inherent in any position is a trust that sound decisions are best made within one’s area of expertise. Those organizations are profitable, successful, and forward thinking businesses indicating a healthy understanding of the roles of their staff.

As a business owner, I also allow a certain latitude within an employee’s job. Our web sales is efficiently handled without my input; ThunderTix has been cultivated under anothers purview; and our programmers would not benefit from my code reviews. WIth them, I have been pleased to see growth in our sales and profitability each year of our eight year history.  But watching over those financial markers does not translate into an all-encompassing focus on the bottom line or lack of insight into my employees’ respective roles.

Yet I and our executive director share something in common. Despite our efforts, our employees have both publicly disparaged our leadership. A former employee wrote:

Someone running a business isn’t as focused on the institution.  To generalize, their primary focus is simply on generating money.

That’s a pretty haughty generalization from someone who has not walked in the shoes of a business owner.   The fact is, most company leaders are focused on a whole lot more than generating money.   For us here at Thunder Data, a primary driver of our own success lies in client satisfaction and responsiveness, and it is the single most lauded trait we hear in feedback from clients.   And in what I’ve hoped was a demonstration of my understanding of employee needs, I’ve been proud of the fact that I’ve not been afraid to let my people test the waters with new languages, products or processes to help feed their collective hunger for staying on top of the latest trends in tech. 

One of my goals this year is to expand employee decision making while balancing with client expectations.   It requires mutual trust and respect of what each of us brings to the table. When we recognize an individual’s role, we’ll succeed individually and collectively.

May 20, 2009

Altering Course: Development Team and Client Relationships

Filed under: Team Dynamics — Dawn @ 11:53 pm

Today, we received a client request to improve a page’s load time while in the midst of making major aesthetic improvements to another site.  When I suggested we switch gears to help the client, I was met with resistance in the form of frustration that the new project was being interrupted.

This gave us opportunity to have a long overdue discussion that probably would not have taken place if we hadn’t had recent staff changes to our programming team.   The crux of our talk:  our recent past will not dictate our future.

For the last year, we have followed agile development practices that offered remarkable improvements to how we scheduled and executed work.   Though the scrum master’s philosphies dramatically differed from my own, I  allowed a fundamental shift in how we operated our business.  The biggest change lay in our responsiveness to client requests for bug fixes and improvements which were increasingly  pushed to the back burner so as not to disrupt weekly sprints or programmers who were “in the flow”.

At some point, I began to realize that the way we practiced agile centered on how we could keep the development team happy.   But they weren’t happy.  And neither were the project managers.  Worse, our clients were not getting the attention to which they were accustomed and deserved.

Despite my knowledge that our practices were not benefitting our business or clients, in lettng the programming team to dictate our business process, I was essentially allowing the tail to wag the dog.  It was during one sprint meeting when I asked a question that I got my wake-up call.  Why, I asked, are we focused on how we could improve the programmers’ jobs, but no one is interested in how to improve our jobs as the primary contacts to the customer?

The answer was telling:  ”We don’t even know what you do.”

So this morning, we talked about change to our collective mindset.  I explained that I meet with a group of professional women every six weeks.  Virtually all of them have fallen victim to the recent economic climate.  Separately, two of the women indicated that we must be doing something right:  in their minds, we seemed to stand apart as a business that is profitable and growing despite the downturn.

I told the staff that our success didn’t happen by chance.  Before this past year, we’d put our clients first.   Today, I stood firm:  from here on out, we stand second to our clients.  If they are our priority, we cannot help but succeed.   The cool thing is, they will, too.

May 11, 2009

RailsConf Retrospectives, Part 1: Testing

Filed under: TDS Developer's Corner — gary @ 1:27 pm

The Thunder Data dev team was represented at this year’s RailsConf in Las Vegas, and we learned quite a bit.  We’re starting a series of RailsConf Retrospectives to delve into some of the topics covered at the conference.

Testing

One of the most pervasive themes at RailsConf was that of testing and Test-Driven Design, principles that have been embraced in the Rails community for good reason.  When done properly, TDD yields a suite of automated tests that can be run at any time to ensure the integrity and production readiness of an application.  But what exactly is testing, and how does it yield such boons?

Red-Green-Refactor

The essence of TDD is the cycle of Red-Green-Refactor.  Start by writing a high-level test describing a bit of functionality that fails, write code until it passes, and then clean up and improve the code.  This continuous cycle results in not only cleaner, more robust code, but by its very nature produces a set of tests that describe the entire functionality and operation of the application as a whole.  This process also forces tests to be written and debugged first, so that any errors later can be properly attributed to the application code, not the tests.  At the end, you will have a test suite that both defines and monitors the functionality of the application.

Fixing It Up

What happens when it comes time to rewrite that particularly nasty controller?  With TDD, have no fear!  If an application has a good set of tests, refactoring is a snap.  Simply make whatever changes you need and rerun the test suite.  If everything comes back green, you may proceed.  If not, track down the failing tests and fix the application code that is causing failure.  Over the lifespan of an application, a comprehensive set of tests for general use and edge cases alike will allow for any future development on any particular part to proceed without a hitch.

Stay tuned for more articles as we continue our RailsConf Retrospectives series.

March 16, 2009

Fixing a Dysfunctional Team

Filed under: Team Dynamics — Dawn @ 10:00 pm

Every Monday morning our team engages in a sprint planning meeting.  Starting with a retrospective of the prior week, we discuss problems, successes, and ideas from individually posted notes on each topic  Intended to be frank discussions, we are openly enthusiastic about the good things that occur, but our challenges and frustrations are generally veiled.  The goals of a retrospective aim to improve the team’s flow, but if we aren’t honest about the challenges we’re facing, they are simply a waste of time.

During a retrospective six weeks ago, my Post-it note read:  “We are a dysfunctional team.”

For a couple of weeks, I felt frustrated and unhappy with the office atmosphere.  No one was complaining, work was getting completed, and there was no list of dissatisfying issues.  But there was a palpable sense of unhappiness among the team.  My posted note opened a long closed Pandora’s box, and so began our first real steps to re-creating the team.

We are not alone.

At SXSW, no less than four panels tossed around the subject of developer/staff relationships.  The consistency of the theme was surprising.  Two questions begged answers:  what factors lend themselves to these difficult relationships, and how do we change team dynamics?  I was on a quest to find answers and work on fixing our own team.

To start, we discussed what we collectively felt were the primary requirements of a <i>functional</i> team: trust, honesty, partnership, and commitment.  We were failing at all of them, and the negative pall cast on the office was tangible.

Each month, we’ll share the changes made and the results realized as we attempt to overcome the obstacles that prevent us from realizing our potential

Next Page »