module Shoulda::Macros

def should_change(description, options = {}, &block)

should_change("the post title", :to => "new") { @post.title }
# Assert the value changed to "new:"

should_change("the post title", :from => "old") { @post.title }
# Assert the value changed to anything other than "old:"

should_change("the post title") { @post.title }
# Assert the value changed in some way:

Combinations of :by, :from, and :to are allowed:

should_change("the post title", :from => "old", :to => "new") { @post.title }
should_change("the number of posts", :from => 0, :to => 1) { Post.count }

may also specify :from and :to options:
difference between the before and after values of the expression. You
As shown in this example, the :by option expects a numeric

end
should_change("the number of posts", :by => 1) { Post.count }
setup { Post.create }
context "Creating a post" do

Example:

The passed description will be used when generating the test name and failure messages.

should_not_change.
assertion, but supports more than just numeric values. See also
is run. This is similar to Active Support's assert_difference
of a block that is run before and after the current setup block
Macro that creates a test asserting a change between the return value
def should_change(description, options = {}, &block)
  by, from, to = get_options!([options], :by, :from, :to)
  stmt = "change #{description}"
  stmt << " from #{from.inspect}" if from
  stmt << " to #{to.inspect}" if to
  stmt << " by #{by.inspect}" if by
  if block_given?
    code = block
  else
    warn "[DEPRECATION] should_change(expression, options) is deprecated. " <<
         "Use should_change(description, options) { code } instead."
    code = lambda { eval(description) }
  end
  before = lambda { @_before_should_change = code.bind(self).call }
  should stmt, :before => before do
    old_value = @_before_should_change
    new_value = code.bind(self).call
    assert_operator from, :===, old_value, "#{description} did not originally match #{from.inspect}" if from
    assert_not_equal old_value, new_value, "#{description} did not change" unless by == 0
    assert_operator to, :===, new_value, "#{description} was not changed to match #{to.inspect}" if to
    assert_equal old_value + by, new_value if by
  end
end

def should_change_record_count_of(class_name, amount, action) # :nodoc:

:nodoc:
def should_change_record_count_of(class_name, amount, action) # :nodoc:
  klass = class_name.to_s.camelize.constantize
  before = lambda do
    @_before_change_record_count = klass.count
  end
  human_name = class_name.to_s.humanize.downcase
  should "#{action} a #{human_name}", :before => before do
    assert_equal @_before_change_record_count + amount,
                 klass.count,
                 "Expected to #{action} a #{human_name}"
  end
end

def should_create(class_name)

end
should_create :post
setup { Post.create(post_attributes) }
context "creating a post" do

Example:

created.
Macro that creates a test asserting that a record of the given class was
def should_create(class_name)
  should_change_record_count_of(class_name, 1, 'create')
end

def should_destroy(class_name)

end
should_destroy :post
setup { Post.first.destroy }
context "destroying a post" do

Example:

destroyed.
Macro that creates a test asserting that a record of the given class was
def should_destroy(class_name)
  should_change_record_count_of(class_name, -1, 'destroy')
end

def should_not_change(description, &block)

end
should_not_change("the number of posts") { Post.count }
setup { @post.update_attributes(:title => "new") }
context "Updating a post" do

Example:

The passed description will be used when generating the test name and failure message.

is run. This is the logical opposite of should_change.
of a block that is run before and after the current setup block
Macro that creates a test asserting no change between the return value
def should_not_change(description, &block)
  if block_given?
    code = block
  else
    warn "[DEPRECATION] should_not_change(expression) is deprecated. " <<
         "Use should_not_change(description) { code } instead."
    code = lambda { eval(description) }
  end
  before = lambda { @_before_should_not_change = code.bind(self).call }
  should "not change #{description}", :before => before do
    new_value = code.bind(self).call
    assert_equal @_before_should_not_change, new_value, "#{description} changed"
  end
end