mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
eaaa2d83e5
`allow_value` will now raise a CouldNotSetAttribute error if the attribute in question cannot be changed from a non-nil value to a nil value, or vice versa. In other words, these are the exact cases in which the error will occur: * If you're testing whether the attribute allows `nil`, but the attribute detects and ignores nil. (For instance, you have a model that `has_secure_password`. This will add a #password= method to your model that is defined in a such a way that you cannot clear the password by setting it to nil -- nothing happens.) * If you're testing whether the attribute allows a non-nil value, but the attribute fails to set that value. (For instance, you have an ActiveRecord model. If ActiveRecord cannot typecast the value in the context of the column, then it will do nothing, and the attribute will be effectively set to nil.) What's the reasoning behind this change? Simply put, if you are assuming that the attribute is changing but in fact it is not, then the test you're writing isn't the test that actually gets run. We feel that this is dishonest and produces an invalid test.
134 lines
3.3 KiB
Ruby
134 lines
3.3 KiB
Ruby
require_relative 'class_builder'
|
|
|
|
module UnitTests
|
|
module ModelBuilder
|
|
include ClassBuilder
|
|
|
|
def self.configure_example_group(example_group)
|
|
example_group.include(self)
|
|
|
|
example_group.after do
|
|
clear_column_caches
|
|
drop_created_tables
|
|
end
|
|
end
|
|
|
|
def create_table(table_name, options = {}, &block)
|
|
connection = ActiveRecord::Base.connection
|
|
|
|
begin
|
|
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
|
connection.create_table(table_name, options, &block)
|
|
created_tables << table_name
|
|
connection
|
|
rescue Exception => e
|
|
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
|
raise e
|
|
end
|
|
end
|
|
|
|
def define_model_class(class_name, &block)
|
|
define_class(class_name, ActiveRecord::Base, &block)
|
|
end
|
|
|
|
def define_active_model_class(class_name, options = {}, &block)
|
|
accessors = options.fetch(:accessors, [])
|
|
|
|
define_class(class_name) do
|
|
include ActiveModel::Validations
|
|
|
|
def initialize(attributes = {})
|
|
attributes.each do |name, value|
|
|
__send__("#{name}=", value)
|
|
end
|
|
end
|
|
|
|
accessors.each do |column|
|
|
attr_accessor column.to_sym
|
|
end
|
|
|
|
if block_given?
|
|
class_eval(&block)
|
|
end
|
|
end
|
|
end
|
|
|
|
def define_model(name, columns = {}, &block)
|
|
class_name = name.to_s.pluralize.classify
|
|
table_name = class_name.tableize.gsub('/', '_')
|
|
|
|
table_block = lambda do |table|
|
|
columns.each do |column_name, specification|
|
|
if specification.is_a?(Hash)
|
|
column_type = specification[:type]
|
|
column_options = specification.fetch(:options, {})
|
|
else
|
|
column_type = specification
|
|
column_options = {}
|
|
end
|
|
|
|
begin
|
|
table.__send__(column_type, column_name, column_options)
|
|
rescue NoMethodError
|
|
raise NoMethodError, "#{Tests::Database.instance.adapter_class} does not support :#{column_type} columns"
|
|
end
|
|
end
|
|
end
|
|
|
|
if columns.key?(:id) && columns[:id] == false
|
|
columns.delete(:id)
|
|
create_table(table_name, id: false, &table_block)
|
|
else
|
|
create_table(table_name, &table_block)
|
|
end
|
|
|
|
model = define_model_class(class_name).tap do |m|
|
|
if block
|
|
if block.arity == 0
|
|
m.class_eval(&block)
|
|
else
|
|
block.call(m)
|
|
end
|
|
end
|
|
|
|
m.table_name = table_name
|
|
end
|
|
|
|
defined_models << model
|
|
|
|
model
|
|
end
|
|
|
|
private
|
|
|
|
def clear_column_caches
|
|
# Rails 4.x
|
|
if ActiveRecord::Base.connection.respond_to?(:schema_cache)
|
|
ActiveRecord::Base.connection.schema_cache.clear!
|
|
# Rails 3.1 - 4.0
|
|
elsif ActiveRecord::Base.connection_pool.respond_to?(:clear_cache!)
|
|
ActiveRecord::Base.connection_pool.clear_cache!
|
|
end
|
|
|
|
defined_models.each do |model|
|
|
model.reset_column_information
|
|
end
|
|
end
|
|
|
|
def drop_created_tables
|
|
connection = ActiveRecord::Base.connection
|
|
|
|
created_tables.each do |table_name|
|
|
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
|
end
|
|
end
|
|
|
|
def created_tables
|
|
@_created_tables ||= []
|
|
end
|
|
|
|
def defined_models
|
|
@_defined_models ||= []
|
|
end
|
|
end
|
|
end
|