Merge branch 'master' into rails4

Conflicts:
	.travis.yml
	Gemfile
This commit is contained in:
Ben Atkins 2013-03-15 12:43:12 -04:00
commit c2dbb1c154
14 changed files with 170 additions and 67 deletions

View File

@ -1,5 +1,8 @@
## 2.7.1 (Unreleased)
## 2.7.1
- [#206](https://github.com/airblade/paper_trail/issues/206) - Fixed Ruby 1.8.7 compatibility for tracking `object_changes`.
- [#200](https://github.com/airblade/paper_trail/issues/200) - Fixed `next_version` method so that it returns the live model
when called on latest reified version of a model prior to the live model.
- [#197](https://github.com/airblade/paper_trail/issues/197) - PaperTrail now falls back on using YAML for serialization of
serialized model attributes for storage in the `object` and `object_changes` columns in the `Version` table. This fixes
compatibility for `Rails 3.0.x` for projects that employ the `serialize` declaration on a model.

View File

@ -1,5 +1,2 @@
source :rubygems
gem 'rails', github: 'rails/rails'
source 'https://rubygems.org'
gemspec

View File

@ -746,7 +746,7 @@ end
By default, PaperTrail stores your changes as a YAML dump. You can override this with the serializer config option:
```ruby
>> PaperTrail.config.serializer = MyCustomSerializer
>> PaperTrail.serializer = MyCustomSerializer
```
A valid serializer is a `module` (or `class`) that defines a `load` and `dump` method. These serializers are included in the gem for your convenience:

View File

@ -85,7 +85,7 @@ module PaperTrail
def serialize_attributes_for_paper_trail(attributes)
serialized_attributes.each do |key, coder|
if attributes.key?(key)
coder = PaperTrail::Serializers::Yaml unless coder.respond_to?(:dump) # Rails 3.0.x's default serializers don't have a `dump` method
coder = PaperTrail::Serializers::Yaml unless coder.respond_to?(:dump) # Fall back to YAML if `coder` has no `dump` method
attributes[key] = coder.dump(attributes[key])
end
end
@ -104,7 +104,7 @@ module PaperTrail
def serialize_attribute_changes(changes)
serialized_attributes.each do |key, coder|
if changes.key?(key)
coder = PaperTrail::Serializers::Yaml unless coder.respond_to?(:dump) # Rails 3.0.x's default serializers don't have a `dump` method
coder = PaperTrail::Serializers::Yaml unless coder.respond_to?(:dump) # Fall back to YAML if `coder` has no `dump` method
old_value, new_value = changes[key]
changes[key] = [coder.dump(old_value),
coder.dump(new_value)]
@ -155,15 +155,17 @@ module PaperTrail
# Returns the object (not a Version) as it was most recently.
def previous_version
preceding_version = source_version ? source_version.previous : send(self.class.versions_association_name).last
preceding_version.try :reify
preceding_version.reify if preceding_version
end
# Returns the object (not a Version) as it became next.
# NOTE: if self (the item) was not reified from a version, i.e. it is the
# "live" item, we return nil. Perhaps we should return self instead?
def next_version
# NOTE: if self (the item) was not reified from a version, i.e. it is the
# "live" item, we return nil. Perhaps we should return self instead?
subsequent_version = source_version ? source_version.next : nil
subsequent_version.reify if subsequent_version
subsequent_version = source_version.next
subsequent_version ? subsequent_version.reify : self.class.find(self.id)
rescue
nil
end
# Executes the given method or block without creating a new version.
@ -215,8 +217,8 @@ module PaperTrail
end
def changes_for_paper_trail
self.changes.keep_if do |key, value|
notably_changed.include?(key)
self.changes.delete_if do |key, value|
!notably_changed.include?(key)
end.tap do |changes|
self.class.serialize_attribute_changes(changes) # Use serialized value for attributes when necessary
end

View File

@ -1,3 +1,3 @@
module PaperTrail
VERSION = '2.7.0'
VERSION = '2.7.1'
end

View File

@ -15,10 +15,11 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ['lib']
s.add_dependency 'railties', '~> 4.0.0.beta'
s.add_dependency 'activerecord', '~> 4.0.0.beta'
s.add_dependency 'railties', '>= 4.0.0.beta', '< 5.0'
s.add_dependency 'activerecord', '>= 4.0.0.beta', '< 5.0'
s.add_development_dependency 'rake'
s.add_development_dependency 'sqlite3'
s.add_development_dependency 'ffaker', '>= 1.15'
s.add_development_dependency 'protected_attributes', '~> 1.0'
end

View File

@ -0,0 +1,3 @@
class ProtectedWidget < Widget
attr_accessible :name, :a_text
end

View File

@ -4,8 +4,9 @@ require "active_model/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_view/railtie"
require "protected_attributes" # Rails 4 requirement for using `attr_protected` and `attr_accessible`
Bundler.require
Bundler.require(:default, Rails.env) if defined?(Bundler)
require 'paper_trail'
module Dummy
@ -32,13 +33,30 @@ module Dummy
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# JavaScript files you want as :defaults (application.js is always included).
# config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Enforce whitelist mode for mass assignment.
# This will create an empty whitelist of attributes available for mass-assignment for all models
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
config.active_record.whitelist_attributes = false
# Enable the asset pipeline
config.assets.enabled = false
# Version of your assets, change this if you want to expire all your assets
# config.assets.version = '1.0'
end
end

View File

@ -2,25 +2,34 @@ Dummy::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the webserver when you make code changes.
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_view.debug_rjs = true
config.action_controller.perform_caching = false
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false
# config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger
config.active_support.deprecation = :log
# Only use best-standards-support built into browsers
config.action_dispatch.best_standards_support = :builtin
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL)
config.active_record.auto_explain_threshold_in_seconds = 0.5
# Do not compress assets
config.assets.compress = false
# Expands the lines which load the assets
config.assets.debug = true
end

View File

@ -1,7 +1,6 @@
Dummy::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# The production environment is meant for finished, "live" apps.
# Code is not reloaded between requests
config.cache_classes = true
@ -9,31 +8,46 @@ Dummy::Application.configure do
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = false
# Compress JavaScripts and CSS
config.assets.compress = true
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
# Defaults to nil and saved in location specified by config.assets.prefix
# config.assets.manifest = YOUR_PATH
# Specifies the header that your server uses for sending files
config.action_dispatch.x_sendfile_header = "X-Sendfile"
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# For nginx:
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
# If you have no front-end server that supports something like X-Sendfile,
# just comment this out and Rails will serve the files
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# See everything in the log (default is :info)
# config.log_level = :debug
# Prepend all log lines with the following tags
# config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups
# config.logger = SyslogLogger.new
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production
# config.cache_store = :mem_cache_store
# Disable Rails's static asset server
# In production, Apache or nginx will already do this
config.serve_static_assets = false
# Enable serving of images, stylesheets, and javascripts from an asset server
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
@ -46,4 +60,8 @@ Dummy::Application.configure do
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL)
# config.active_record.auto_explain_threshold_in_seconds = 0.5
end

View File

@ -2,13 +2,14 @@ Dummy::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
# Show full error reports and disable caching
config.consider_all_requests_local = true
@ -18,17 +19,15 @@ Dummy::Application.configure do
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test if config.respond_to?(:action_mailer)
# config.action_mailer.delivery_method = :test
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr

View File

@ -628,6 +628,15 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
should 'return the current object for version_at after latest update' do
assert_equal 'Digit', @widget.version_at(1.day.from_now).name
end
context 'passing in a string representation of a timestamp' do
should 'still return a widget when appropriate' do
# need to add 1 second onto the timestamps before casting to a string, since casting a Time to a string drops the microseconds
assert_equal 'Widget', @widget.version_at((@created + 1.second).to_s).name
assert_equal 'Fidget', @widget.version_at((@first_update + 1.second).to_s).name
assert_equal 'Digit', @widget.version_at((@second_update + 1.second).to_s).name
end
end
end
describe '.versions_between' do
@ -795,10 +804,10 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
end
should 'have a previous version' do
assert_equal @widget.versions.last.reify, @widget.previous_version
assert_equal @widget.versions.last.reify.name, @widget.previous_version.name
end
should 'have a next version' do
should 'not have a next version' do
assert_nil @widget.next_version
end
end
@ -806,21 +815,20 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
describe 'A reified item' do
setup do
widget = Widget.create :name => 'Bob'
%w( Tom Dick Jane ).each { |name| widget.update_attributes :name => name }
@versions = widget.versions
@second_widget = @versions[1].reify # first widget is null
@last_widget = @versions.last.reify
@widget = Widget.create :name => 'Bob'
%w(Tom Dick Jane).each { |name| @widget.update_attributes :name => name }
@second_widget = @widget.versions[1].reify # first widget is `nil`
@last_widget = @widget.versions.last.reify
end
should 'have a previous version' do
assert_nil @second_widget.previous_version
assert_equal @versions[-2].reify, @last_widget.previous_version
assert_nil @second_widget.previous_version # `create` events return `nil` for `reify`
assert_equal @widget.versions[-2].reify.name, @last_widget.previous_version.name
end
should 'have a next version' do
assert_equal @versions[2].reify, @second_widget.next_version
assert_nil @last_widget.next_version
assert_equal @widget.versions[2].reify.name, @second_widget.next_version.name
assert_equal @last_widget.next_version.name, @widget.name
end
end
@ -1106,7 +1114,11 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
assert_equal 2, @doc.paper_trail_versions.length
end
should 'respond to previous_version as normal' do
should 'respond to `next_version` as normal' do
assert_equal @doc.paper_trail_versions.last.reify.next_version.name, @doc.name
end
should 'respond to `previous_version` as normal' do
@doc.update_attributes :name => 'Doc 2'
assert_equal 3, @doc.paper_trail_versions.length
assert_equal 'Doc 1', @doc.previous_version.name

View File

@ -0,0 +1,34 @@
require 'test_helper'
class ProtectedAttrsTest < ActiveSupport::TestCase
describe 'A model with `attr_accessible` created' do
setup do
@widget = ProtectedWidget.create! :name => 'Henry'
@initial_attributes = @widget.attributes
end
should { assert !ProtectedWidget.accessible_attributes.empty? }
should 'be `nil` in its previous version' do
assert_nil @widget.previous_version
end
describe 'which is then updated' do
setup do
@widget.assign_attributes(:name => 'Jeff', :a_text => 'Short statement')
@widget.an_integer = 42
@widget.save!
end
should 'not be `nil` in its previous version' do
assert_not_nil @widget.previous_version
end
should 'the previous version should contain right attributes' do
assert_equal @widget.previous_version.attributes, @initial_attributes
end
end
end
end

View File

@ -19,10 +19,13 @@ class SerializerTest < ActiveSupport::TestCase
assert_nil @fluxor.versions[0].reify
assert_equal 'Some text.', @fluxor.versions[1].reify.name
# Check values are stored as YAML.
assert_equal YAML.dump(@original_fluxor_attributes), @fluxor.versions[1].object
assert_equal @original_fluxor_attributes, YAML.load(@fluxor.versions[1].object)
# This test can't consistently pass in Ruby1.8 because hashes do no preserve order, which means the order of the
# attributes in the YAML can't be ensured.
if RUBY_VERSION.to_f >= 1.9
assert_equal YAML.dump(@original_fluxor_attributes), @fluxor.versions[1].object
end
end
end
@ -52,8 +55,12 @@ class SerializerTest < ActiveSupport::TestCase
assert_equal 'Some text.', @fluxor.versions[1].reify.name
# Check values are stored as JSON.
assert_equal ActiveSupport::JSON.encode(@original_fluxor_attributes), @fluxor.versions[1].object
assert_equal @original_fluxor_attributes, ActiveSupport::JSON.decode(@fluxor.versions[1].object)
# This test can't consistently pass in Ruby1.8 because hashes do no preserve order, which means the order of the
# attributes in the JSON can't be ensured.
if RUBY_VERSION.to_f >= 1.9
assert_equal ActiveSupport::JSON.encode(@original_fluxor_attributes), @fluxor.versions[1].object
end
end
should 'store object_changes' do