Skip to content

Instantly share code, notes, and snippets.

@FooBarWidget
Created September 22, 2008 15:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FooBarWidget/12012 to your computer and use it in GitHub Desktop.
Save FooBarWidget/12012 to your computer and use it in GitHub Desktop.
require 'rubygems'
require 'test/unit'
require 'active_record'
module DefaultValuePlugin
class NormalValueContainer
def initialize(value)
@value = value
end
def evaluate
return @value
end
end
class BlockValueContainer
def initialize(model_object, block)
@model_object = self
@block = block
end
def evaluate
return @block.call(@model_object)
end
end
module ClassMethods
def default_value_for(attribute, value = nil, &block)
if !method_defined?(:initialize_with_defaults)
include(InstanceMethods)
alias_method_chain :initialize, :defaults
class_inheritable_accessor :_default_attribute_values
self._default_attribute_values = {}
end
if block_given?
container = BlockValueContainer.new(self, block)
else
container = NormalValueContainer.new(value)
end
_default_attribute_values[attribute.to_s] = container
end
end
module InstanceMethods
def initialize_with_defaults(attrs = nil)
initialize_without_defaults(attrs) do
self.class._default_attribute_values.each_pair do |attribute, container|
if attrs.nil? || !attrs.keys.map(&:to_s).include?(attribute)
__send__("#{attribute}=", container.evaluate)
end
end
yield(self) if block_given?
end
end
end
end
ActiveRecord::Base.extend(DefaultValuePlugin::ClassMethods)
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'test.sqlite3'
)
ActiveRecord::Base.connection.create_table(:numbers, :force => true) do |t|
t.string :type
t.integer :number
t.integer :count, :null => false, :default => 1
end
ActiveRecord::Base.connection.execute("INSERT INTO numbers (number) VALUES (9876)")
class MyTest < Test::Unit::TestCase
def define_model_class(name = "TestClass", parent_class_name = "ActiveRecord::Base", &block)
Object.send(:remove_const, name) rescue nil
eval("class #{name} < #{parent_class_name}; end", TOPLEVEL_BINDING)
klass = eval(name, TOPLEVEL_BINDING)
klass.class_eval(&block) if block_given?
end
def test_default_value_can_be_passed_as_argument
define_model_class do
set_table_name 'numbers'
default_value_for(:number, 1234)
end
object = TestClass.new
assert_equal 1234, object.number
end
def test_default_value_can_be_passed_as_block
define_model_class do
set_table_name 'numbers'
default_value_for(:number) { 1234 }
end
object = TestClass.new
assert_equal 1234, object.number
end
def test_overwrites_db_default
define_model_class do
set_table_name 'numbers'
default_value_for :count, 1234
end
object = TestClass.new
assert_equal 1234, object.count
end
def test_doesnt_overwrite_values_provided_by_mass_assignment
define_model_class do
set_table_name 'numbers'
default_value_for :number, 1234
end
object = TestClass.new(:number => 1, :count => 2)
assert_equal 1, object.number
end
def test_doesnt_overwrite_values_provided_by_constructor_block
define_model_class do
set_table_name 'numbers'
default_value_for :number, 1234
end
object = TestClass.new do |x|
x.number = 1
x.count = 2
end
assert_equal 1, object.number
end
def test_doesnt_overwrite_explicitly_provided_nil_values_in_mass_assignment
define_model_class do
set_table_name 'numbers'
default_value_for :number, 1234
end
object = TestClass.new(:number => nil)
assert_nil object.number
end
def test_default_values_are_inherited
define_model_class("TestSuperClass") do
set_table_name 'numbers'
default_value_for :number, 1234
end
define_model_class("TestClass", "TestSuperClass")
object = TestClass.new
assert_equal 1234, object.number
end
def test_doesnt_set_default_on_saved_records
define_model_class do
set_table_name 'numbers'
default_value_for :number, 1234
end
assert_equal 9876, TestClass.find(:first).number
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment