Created
March 23, 2010 15:02
-
-
Save pond/341262 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
######################################################################## | |
# File:: without_table.rb | |
# (C):: http://stackoverflow.com/questions/315850/rails-model-without-database/318919#318919 | |
# | |
# Purpose:: Define a class which lets us use ActiveRecord validations | |
# without needing a database representation of the model. | |
# ---------------------------------------------------------------------- | |
# 23-Mar-2010 (ADH): Created. | |
######################################################################## | |
class WithoutTable < ActiveRecord::Base | |
self.abstract_class = true | |
def self.columns | |
@columns ||= []; | |
end | |
def self.column( name, sql_type = nil, default = nil, null = true ) | |
columns << ActiveRecord::ConnectionAdapters::Column.new( | |
name.to_s, | |
default, | |
sql_type.to_s, | |
null | |
) | |
end | |
end | |
######################################################################## | |
# File:: payment_card.rb | |
# (C):: Hipposoft 2010 | |
# | |
# Purpose:: Support a payment (credit/debit) card entry form with | |
# ActiveRecord-like validations but without needing a database | |
# table underneath. | |
# ---------------------------------------------------------------------- | |
# 23-Mar-2010 (ADH): Created. | |
######################################################################## | |
class PaymentCard < WithoutTable | |
# =========================================================================== | |
# CHARACTERISTICS | |
# =========================================================================== | |
# This model has no database representation - see "models/without_table.rb". | |
self.abstract_class = true # Keeps AR quiet but does not prevent instantiation | |
# Card details | |
column :card_name, :string | |
column :card_type, :string | |
column :card_number, :string | |
column :card_cvv, :string | |
column :card_to, :datetime | |
column :card_from, :datetime | |
column :card_issue, :string | |
validates_presence_of :card_name, | |
:card_type, | |
:card_number | |
validate :not_expired, | |
:valid_number, | |
:valid_type, | |
:valid_switch_or_solo_attributes, | |
:valid_cvv_if_needed | |
def not_expired | |
errors.add( :card_to, :has_expired ) if card.expired? | |
end | |
def valid_number | |
# Don't add both "cannot be blank" and "is invalid" errors to the same field | |
errors.add( :card_number, :is_invalid ) unless card_number.blank? || ActiveMerchant::Billing::CreditCard.valid_number?( card_number ) | |
end | |
def valid_type | |
# Views should use a menu of type options, but this is here in case someone | |
# tries to try and hack the site with a custom form request | |
errors.add( :card_type, :is_invalid ) unless card_type.blank? || ActiveMerchant::Billing::CreditCard.card_companies.keys.include?( card_type ) | |
end | |
def valid_switch_or_solo_attributes | |
if ( %w{ switch solo }.include?( card_type ) ) | |
errors.add( :card_issue, :is_invalid ) unless card.valid_issue_number( card_number ) # Very odd API! | |
end | |
end | |
def valid_cvv_if_needed | |
if ( ActiveMerchant::Billing::CreditCard.requires_verification_value? ) | |
errors.add_on_blank :card_cvv | |
end | |
end | |
# User billing address details | |
column :address_1, :string | |
column :address_2, :string | |
column :address_3, :string | |
column :city, :string | |
column :state, :string | |
column :country, :string | |
column :postcode, :string | |
validates_presence_of :address_1, | |
:city, | |
:country | |
# =========================================================================== | |
# PERMISSIONS | |
# =========================================================================== | |
# Permissions are equivalent to those for the User model. | |
# | |
def self.can_modify?( param1, param2 ) | |
#TODO | |
raise "PaymentCard permissions TBD" | |
end | |
# =========================================================================== | |
# GENERAL | |
# =========================================================================== | |
# Read-only accessor style method which defines a credit card with Active | |
# Merchant and caches the result internally. Only read the card once you | |
# have filled in the various "card_..." column values (see above). | |
# | |
def card | |
names = card_name.try( :split, ' ' ) || [] | |
first_name = names.shift | |
last_name = names.join( ' ' ) | |
@card ||= ActiveMerchant::Billing::CreditCard.new( | |
:first_name => first_name, | |
:last_name => last_name, | |
:number => card_number, | |
:type => card_type, | |
:month => card_to.try( :month ).to_s, | |
:year => card_to.try( :year ).to_s, | |
:verification_value => card_cvv, | |
:start_month => card_from.try( :month ).to_s, | |
:start_year => card_from.try( :year ).to_s, | |
:issue_number => card_issue | |
) | |
end | |
end | |
# Then, in e.g. "en.yml": | |
en: | |
activerecord: | |
errors: | |
models: | |
payment_card: | |
has_expired: "has expired" | |
is_invalid: "is invalid" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment