Posted by admin
Tue, 24 Apr 2007 02:58:00 GMT
We needed a convenient way to store a series of values in a single field. A bitfield would not do because it would only allow a value of 1 or 0 for each field, and we wanted to be able to store at least 3 values for each field.
So I found Gabriel Gironda’s acts_as_bitfield plugin and made a few tweaks. ActsAsBytefield is the result. It allows storage of 256 values in each field, or 255 discrete values ranging from 0-255 (unsigned char or byte) for each value in a MySQL varchar(255) field.
NOTE: Gabriel’s site has been down for some time. My own repository has also been down but is now back up. Sorry for the inconvenience.
Values greater than 255, or less than 0 wrap around. For example, setting a bytefield column to -1 will actually set it to 255, and setting it to -2 will actually set it to 254, etc.
Installation
./script/plugin install acts_as_bytefield
OR
./script/plugin install https://svn.cbciweb.com/svn/plugins/acts_as_bytefield
Documentation (RDoc)
Testing
The tests require rspec
Usage
- Create a string column in your table – varchar(255)
- Add this directive to your model:
acts_as_bytefield :bytefield_column_name, :fields => [:field_name_one, :field_name_two]
You will then be able to use the model in the following manner, for example:
class SomeModel < ActiveRecord::Base
acts_as_bytefield :bfield, :fields => [:test, :production]
end
obj = SomeModel.new(:test => 1)
obj.test
obj.test?
obj.production?
obj.test = 0
obj.production = 65
obj.save
obj.test?
obj.production?
obj.bfield
Posted in Ruby | Tags activerecord, bytefield, plugins, rubyonrails, sourcecode | 3 comments | no trackbacks
Posted by admin
Mon, 16 Apr 2007 23:09:00 GMT
Managing a distributed Rails application without Capistrano is painful at best. If you have ever had to do it you know what I am talking about.
Capistrano makes things much easier. Case in point is updating your gems across multiple servers. Here is the Capistrano recipe that we use to keep gems updated:
desc "Update ruby gems"
task :update_gems, :roles => [:web, :admin, :app] do
cmd = "/usr/local/bin/gem update --include-dependencies"
sudo cmd do |channel, stream, data|
puts "#{channel[:host]}: #{data}"
if data =~ /([12])\.[^\(]+\(ruby\)/
channel.send_data "#{$1}\n"
end
end
end
Now, you’ll probably need to customize it to your own needs. For instance, change the roles to suit your own setup. Also, you may want to modify the path to your ‘gem’ executable, and configure ’sudo’ to allow your user to run that command without a password. You’ll have to do that on each server as follows:
sudo visudo
Then add a line like this:
myuser ALL=(ALL) NOPASSWD: /usr/local/bin/gem update --include-dependencies
If you don’t have access to ‘visudo’ ask your administrator.
Also, Win32 users will need to change the regular expression from this:
if data =~ /([12])\.[^\(]+\(ruby\)/
to this:
if data =~ /([12])\.[^\(]+\(mswin32\)/
… so Capistrano will choose the correct version of the gem to use.
Basically this recipe will run the ’gem update --include-dependencies’ command and analyze the output choosing the correct latest version of gem to install.
Tags capistrano, distributed, gem, rails, recipe, ruby, sourcecode | no comments | 1 trackback
Posted by admin
Sat, 24 Mar 2007 03:45:00 GMT
As promised here is the second article about validating credit cards with Ruby.
This edition wraps a module and class around the code in preparation for future enhancement.
Feel free to change whatever you want. If you do please send me an update and at least give me credit for the original.
To use it just do something like this in your controller:
if params[:cc]
begin
cc = Payment::CreditCard.new(params[:cc])
cc.valid?
@user.update_attribute(:verified_at, Time.now)
rescue Exception => e
logger.debug e.inspect
flash[:notice] = "Your profile has been updated. However, #{e}."
return
end
end
This assumes you have a form like this somewhere in your view:
<p><label>Name On Card</label><%= text_field :cc, :name, :class => "text-n" %></p>
<p><label>Card Type</label><%= select :cc, :card_type, FundingSource.get_card_types %></p>
<p><label>Card Number</label><%= text_field :cc, :number, {:size => 16, :maxlength => 16, :class => "text-n"} %><br />
<label></label><small>(15-16 digits)</small></p>
<p><label>Expiration Date</label><%= date_select :cc, :expiration,
:start_year => Time.now.year,
:end_year => Time.now.year+10,
:use_month_numbers => true,
:discard_day => true,
:include_blank => true,
:order => [:month, :year] %></p>
<p><label>Security Code</label><%= text_field :cc, :security_code, :size => 4, :class => "text-s" %><br />
<label></label><small>(3 Digit Code on back of credit card)</small></p>
Obviously, this code comes with no warranty of any kind and could hurt your application, your data, your home, your feelings, etc. Don’t sue me. Other than that, use it as you see fit. Just please give me some credit.
module Payment
class CreditCard
CARD_TYPE = {
:master_card => 0,
:visa => 1,
:american_express => 2,
:diners_club => 3,
:discover => 4
}
def initialize(attributes = {})
if (@name = attributes[:name]).nil? or @name.empty?
raise "Cardholder Name is required"
return
end
@card_type = attributes[:card_type]
@card_type = convert_cc_type(@card_type)
if @card_type.nil? or !CARD_TYPE.has_value?(@card_type)
raise "A valid Card Type is required"
return
end
if (@number = attributes[:number]).nil? or @number.to_i.nil? or
@number.length < 15
raise "A valid Card Number is required"
return
end
if ((@security_code = attributes[:security_code]).nil? or
@security_code.to_i.nil? or
(@security_code.length != 3 and
@card_type != CARD_TYPE[:american_express]) or
(@security_code.length != 4 and
@card_type == CARD_TYPE[:american_express]))
raise "A valid Security Code Number is required"
return
end
if ((@card_expiration_month = attributes['expiration(2i)']).nil? or
@card_expiration_month.to_i.nil? or
@card_expiration_month.to_i < 1 or
@card_expiration_month.to_i > 12)
raise "A valid Card expiration month is required"
return
end
if ((@card_expiration_year = attributes['expiration(1i)']).nil? or
@card_expiration_year.to_i.nil?)
raise "A valid Card expiration year is required"
return
end
@card_expiration_date = Time.gm("#{@card_expiration_year}".to_i,
@card_expiration_month).next_month
if (@card_expiration_date <= Time.now)
raise "Card is expired."
return
end
end
def valid?
valid_format = false
pass_check = false
case @card_type
when CARD_TYPE[:master_card]
valid_format = @number[/^5[1-5][0-9]{14}$/] == @number
when CARD_TYPE[:visa]
valid_format = @number[/^4[0-9]{12}$|^4[0-9]{15}$/] == @number
when CARD_TYPE[:american_express]
valid_format = @number[/^3[4|7][0-9]{13}$/] == @number
when CARD_TYPE[:diners_club]
valid_format = @number[/^30[0-5][0-9]{11}$|^3[6|8][0-9]{12}$/] == @number
when CARD_TYPE[:discover]
valid_format = @number[/^6011[0-9]{12}$/] == @number
end
raise "credit card number is invalid." if valid_format == false
reverse_card_num = @number.reverse
sum = 0
reverse_card_num.scan(/./).each_with_index do |digit, index|
digit = digit.to_i
digit *= 2 if index % 2 != 0
if digit.to_s.length == 2
first_num = digit.to_s[0..0]
second_num = digit.to_s[1..1]
digit = first_num.to_i + second_num.to_i
end
sum += digit
end
pass_check = sum % 10 == 0 ? true : false
raise "credit card is invalid." if pass_check == false
true
end
def masked_number(char = 'X', count = 4)
len = @number.to_s.length
card_number = char * (len - count)
card_number << @number[-count..-1]
end
private
def convert_cc_type(raw_type)
card_type = nil
if raw_type.is_a?(String)
card_type = CARD_TYPE[raw_type.downcase.gsub(' ', '_').to_sym]
else
card_type = raw_type
end
end
end
end
Tags algorithms, cards, credit, ruby, rubyonrails, sourcecode, validation | no comments | 1 trackback
Posted by admin
Wed, 21 Mar 2007 03:50:00 GMT
Recently, while working on one of our client’s projects, I found myself needing to validate credit card numbers. Of course the most secure way to do it is to use your merchant services (i.e., Verisign PayFlowPro, etc.). However most often those services cost anywhere from $15/month and 3 cents per transaction and up.
For most purposes the business wants to simply prevent its customers from fat-fingering their credit card numbers when typing it in. But there are several pieces of information that can be validated for any given credit card like: expiration date, billing address, security code, cardholder’s name, etc.
For our purposes we simply wanted to protect customers from their own fat fingers. The Luhn algorithm does nicely for that purpose, and for the most part, keeps honest people honest.
Here it is using Ruby:
def validate_credit_card(number)
reverse_card_num = @number.reverse
sum = 0
reverse_card_num.scan(/./).each_with_index do |digit, index|
digit = digit.to_i
digit *= 2 if index % 2 != 0
if digit.to_s.length == 2
first_num = digit.to_s[0..0]
second_num = digit.to_s[1..1]
digit = first_num.to_i + second_num.to_i
end
sum += digit
end
pass = sum % 10 == 0 ? true : false
end
To use it just pass in your 15-16 digit credit card number and it will return a boolean for pass or fail.
When I have some more time I’ll post some additional validation code that CBCI currently uses for credit cards.
Tags algorithms, credit cards, ruby, rubyonrails, sourcecode, validation | 5 comments | no trackbacks