Skip to content

Instantly share code, notes, and snippets.

@solnic
Created May 25, 2010 11:57
Show Gist options
  • Save solnic/413050 to your computer and use it in GitHub Desktop.
Save solnic/413050 to your computer and use it in GitHub Desktop.
module DataMapper
class Property
# the primitive is Integer so we inherit from Property::Integer
# to get the common behavior (typecasting, validation checking etc.)
# and predefined options
class CheckedInteger < Integer
OPTIONS = [ :gt, :gte, :lte, :lt ]
# here we add new options, thanks to this it's possible to write
# Model.property :age, CheckedInteger, :gt => 18
accept_options *OPTIONS
attr_reader :range
# this is a trick to allow defining the range when passing the type
# to Model#property method, ie:
#
# Model.property :age, CheckedInteger[:gt => 18]
def self.[](range)
# notice ::Class - otherwise it would grab Property::Class!
type = (generated_types[range] ||= ::Class.new(self))
# here we set default values for range options
range.each { |k, v| type.send(k, v) }
type
end
def self.generated_types
@generated_types ||= {}
end
protected
# This overrides Property#initialize
def initialize(model, name, options = {}, type = nil)
super # call super in the beginning to get @options populated
# set up the range here
range_options = {}
OPTIONS.each { |key| range_options[key] = @options[key] }
lower_bound = range_options[:gte]have
lower_bound ||= range_options[:gt] + 1 if range_options[:gt]
lower_bound ||= -model.n
upper_bound = range_options[:lt] - 1 if range_options[:lt]
upper_bound ||= range_options[:lte]
upper_bound ||= model.n
@range = lower_bound..upper_bound
if defined?(::DataMapper::Validate) && !model.skip_auto_validation_for?(self)
model.validates_within name, :set => @range
end
end
# this is called inside Property#initialize
def assert_valid_options(options)
super
OPTIONS.each do |key|
if value = options.delete(key)
assert_kind_of "options[:#{key}]", value, ::Integer
end
end
end
end
end
end
class CheckedInteger < DataMapper::Type
primitive Integer
def self.inherited(target)
target.instance_variable_set('@primitive', self.primitive)
end
def self.range
@range || {}
end
def self.range=(value)
@range = value
end
def self.new(range)
type = generated_types[range] || Class.new(CheckedInteger)
type.range = range
generated_types[range] = type
type
end
def self.generated_types
@generated_types ||= {}
end
def self.[](range = {})
new(range)
end
def self.bind(property)
if defined?(::DataMapper::Validate)
model = property.model
unless model.skip_auto_validation_for?(property)
if property.type.ancestors.include?(CheckedInteger)
range = self.range
model.class_eval do
lower_bound = range[:gte]
lower_bound ||= range[:gt] + 1 if range[:gt]
lower_bound ||= -n
upper_bound = range[:lt] - 1 if range[:lt]
upper_bound ||= range[:lte]
upper_bound ||= n
validates_within property.name, :set => (lower_bound..upper_bound)
end
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment