paper_trail 4.2.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +28 -9
  3. data/.github/ISSUE_TEMPLATE.md +13 -0
  4. data/.gitignore +2 -1
  5. data/.rubocop.yml +100 -0
  6. data/.rubocop_todo.yml +14 -0
  7. data/.travis.yml +8 -9
  8. data/Appraisals +41 -0
  9. data/CHANGELOG.md +49 -9
  10. data/Gemfile +1 -1
  11. data/README.md +130 -109
  12. data/Rakefile +19 -19
  13. data/doc/bug_report_template.rb +20 -14
  14. data/gemfiles/ar3.gemfile +10 -53
  15. data/gemfiles/ar4.gemfile +7 -0
  16. data/gemfiles/ar5.gemfile +13 -0
  17. data/lib/generators/paper_trail/install_generator.rb +26 -18
  18. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +4 -2
  19. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +2 -0
  20. data/lib/generators/paper_trail/templates/create_version_associations.rb +9 -4
  21. data/lib/generators/paper_trail/templates/create_versions.rb +39 -5
  22. data/lib/paper_trail.rb +169 -146
  23. data/lib/paper_trail/attributes_serialization.rb +89 -17
  24. data/lib/paper_trail/cleaner.rb +15 -9
  25. data/lib/paper_trail/config.rb +28 -11
  26. data/lib/paper_trail/frameworks/active_record.rb +4 -0
  27. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +5 -1
  28. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +6 -2
  29. data/lib/paper_trail/frameworks/cucumber.rb +1 -0
  30. data/lib/paper_trail/frameworks/rails.rb +2 -7
  31. data/lib/paper_trail/frameworks/rails/controller.rb +29 -9
  32. data/lib/paper_trail/frameworks/rails/engine.rb +7 -1
  33. data/lib/paper_trail/frameworks/rspec.rb +5 -5
  34. data/lib/paper_trail/frameworks/rspec/helpers.rb +3 -1
  35. data/lib/paper_trail/frameworks/sinatra.rb +6 -4
  36. data/lib/paper_trail/has_paper_trail.rb +199 -106
  37. data/lib/paper_trail/record_history.rb +1 -3
  38. data/lib/paper_trail/reifier.rb +297 -118
  39. data/lib/paper_trail/serializers/json.rb +3 -3
  40. data/lib/paper_trail/serializers/yaml.rb +27 -8
  41. data/lib/paper_trail/version_association_concern.rb +3 -1
  42. data/lib/paper_trail/version_concern.rb +75 -35
  43. data/lib/paper_trail/version_number.rb +6 -9
  44. data/paper_trail.gemspec +44 -51
  45. data/spec/generators/install_generator_spec.rb +24 -25
  46. data/spec/generators/paper_trail/templates/create_versions_spec.rb +51 -0
  47. data/spec/models/animal_spec.rb +12 -12
  48. data/spec/models/boolit_spec.rb +8 -8
  49. data/spec/models/callback_modifier_spec.rb +47 -47
  50. data/spec/models/car_spec.rb +13 -0
  51. data/spec/models/fluxor_spec.rb +3 -3
  52. data/spec/models/gadget_spec.rb +19 -19
  53. data/spec/models/joined_version_spec.rb +3 -3
  54. data/spec/models/json_version_spec.rb +23 -24
  55. data/spec/models/kitchen/banana_spec.rb +3 -3
  56. data/spec/models/not_on_update_spec.rb +7 -4
  57. data/spec/models/post_with_status_spec.rb +13 -3
  58. data/spec/models/skipper_spec.rb +10 -10
  59. data/spec/models/thing_spec.rb +4 -4
  60. data/spec/models/truck_spec.rb +5 -0
  61. data/spec/models/vehicle_spec.rb +5 -0
  62. data/spec/models/version_spec.rb +103 -59
  63. data/spec/models/widget_spec.rb +82 -52
  64. data/spec/modules/paper_trail_spec.rb +2 -2
  65. data/spec/modules/version_concern_spec.rb +11 -12
  66. data/spec/modules/version_number_spec.rb +2 -4
  67. data/spec/paper_trail/config_spec.rb +10 -29
  68. data/spec/paper_trail_spec.rb +16 -14
  69. data/spec/rails_helper.rb +10 -9
  70. data/spec/requests/articles_spec.rb +11 -7
  71. data/spec/spec_helper.rb +41 -22
  72. data/spec/support/alt_db_init.rb +8 -13
  73. data/test/custom_json_serializer.rb +3 -3
  74. data/test/dummy/Rakefile +2 -2
  75. data/test/dummy/app/controllers/application_controller.rb +21 -8
  76. data/test/dummy/app/controllers/articles_controller.rb +11 -8
  77. data/test/dummy/app/controllers/widgets_controller.rb +13 -12
  78. data/test/dummy/app/models/animal.rb +1 -1
  79. data/test/dummy/app/models/article.rb +19 -11
  80. data/test/dummy/app/models/authorship.rb +1 -1
  81. data/test/dummy/app/models/bar_habtm.rb +4 -0
  82. data/test/dummy/app/models/book.rb +4 -4
  83. data/test/dummy/app/models/boolit.rb +1 -1
  84. data/test/dummy/app/models/callback_modifier.rb +6 -6
  85. data/test/dummy/app/models/car.rb +3 -0
  86. data/test/dummy/app/models/chapter.rb +4 -4
  87. data/test/dummy/app/models/customer.rb +1 -1
  88. data/test/dummy/app/models/document.rb +2 -2
  89. data/test/dummy/app/models/editor.rb +1 -1
  90. data/test/dummy/app/models/foo_habtm.rb +4 -0
  91. data/test/dummy/app/models/fruit.rb +2 -2
  92. data/test/dummy/app/models/gadget.rb +1 -1
  93. data/test/dummy/app/models/kitchen/banana.rb +1 -1
  94. data/test/dummy/app/models/legacy_widget.rb +2 -2
  95. data/test/dummy/app/models/line_item.rb +1 -1
  96. data/test/dummy/app/models/not_on_update.rb +1 -1
  97. data/test/dummy/app/models/person.rb +6 -6
  98. data/test/dummy/app/models/post.rb +1 -1
  99. data/test/dummy/app/models/post_with_status.rb +1 -1
  100. data/test/dummy/app/models/quotation.rb +1 -1
  101. data/test/dummy/app/models/section.rb +1 -1
  102. data/test/dummy/app/models/skipper.rb +2 -2
  103. data/test/dummy/app/models/song.rb +13 -4
  104. data/test/dummy/app/models/thing.rb +2 -2
  105. data/test/dummy/app/models/translation.rb +2 -2
  106. data/test/dummy/app/models/truck.rb +4 -0
  107. data/test/dummy/app/models/vehicle.rb +4 -0
  108. data/test/dummy/app/models/whatchamajigger.rb +1 -1
  109. data/test/dummy/app/models/widget.rb +7 -6
  110. data/test/dummy/app/versions/joined_version.rb +4 -3
  111. data/test/dummy/app/versions/json_version.rb +1 -1
  112. data/test/dummy/app/versions/kitchen/banana_version.rb +1 -1
  113. data/test/dummy/app/versions/post_version.rb +2 -2
  114. data/test/dummy/config.ru +1 -1
  115. data/test/dummy/config/application.rb +20 -9
  116. data/test/dummy/config/boot.rb +5 -5
  117. data/test/dummy/config/environment.rb +1 -1
  118. data/test/dummy/config/environments/development.rb +4 -3
  119. data/test/dummy/config/environments/production.rb +3 -2
  120. data/test/dummy/config/environments/test.rb +15 -5
  121. data/test/dummy/config/initializers/backtrace_silencers.rb +4 -2
  122. data/test/dummy/config/initializers/paper_trail.rb +1 -2
  123. data/test/dummy/config/initializers/secret_token.rb +3 -1
  124. data/test/dummy/config/initializers/session_store.rb +1 -1
  125. data/test/dummy/config/routes.rb +2 -2
  126. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +120 -74
  127. data/test/dummy/db/schema.rb +29 -6
  128. data/test/dummy/script/rails +6 -4
  129. data/test/functional/controller_test.rb +34 -35
  130. data/test/functional/enabled_for_controller_test.rb +6 -7
  131. data/test/functional/modular_sinatra_test.rb +43 -38
  132. data/test/functional/sinatra_test.rb +49 -40
  133. data/test/functional/thread_safety_test.rb +4 -6
  134. data/test/paper_trail_test.rb +15 -14
  135. data/test/test_helper.rb +68 -44
  136. data/test/time_travel_helper.rb +1 -15
  137. data/test/unit/associations_test.rb +517 -251
  138. data/test/unit/cleaner_test.rb +66 -60
  139. data/test/unit/inheritance_column_test.rb +17 -17
  140. data/test/unit/model_test.rb +611 -504
  141. data/test/unit/protected_attrs_test.rb +16 -12
  142. data/test/unit/serializer_test.rb +44 -43
  143. data/test/unit/serializers/json_test.rb +17 -18
  144. data/test/unit/serializers/mixin_json_test.rb +15 -14
  145. data/test/unit/serializers/mixin_yaml_test.rb +20 -16
  146. data/test/unit/serializers/yaml_test.rb +12 -13
  147. data/test/unit/timestamp_test.rb +10 -12
  148. data/test/unit/version_test.rb +7 -7
  149. metadata +92 -40
@@ -1,5 +1,14 @@
1
1
  module PaperTrail
2
+ # "Serialization" here refers to the preparation of data for insertion into a
3
+ # database, particularly the `object` and `object_changes` columns in the
4
+ # `versions` table.
5
+ #
6
+ # Likewise, "deserialization" refers to any processing of data after they
7
+ # have been read from the database, for example preparing the result of
8
+ # `VersionConcern#changeset`.
2
9
  module AttributesSerialization
10
+ # An attribute which needs no processing. It is part of our backport (shim)
11
+ # of rails 4.2's attribute API. See `type_for_attribute` below.
3
12
  class NoOpAttribute
4
13
  def type_cast_for_database(value)
5
14
  value
@@ -11,6 +20,9 @@ module PaperTrail
11
20
  end
12
21
  NO_OP_ATTRIBUTE = NoOpAttribute.new
13
22
 
23
+ # An attribute which requires manual (de)serialization to/from what we get
24
+ # from the database. It is part of our backport (shim) of rails 4.2's
25
+ # attribute API. See `type_for_attribute` below.
14
26
  class SerializedAttribute
15
27
  def initialize(coder)
16
28
  @coder = coder.respond_to?(:dump) ? coder : PaperTrail.serializer
@@ -25,16 +37,72 @@ module PaperTrail
25
37
  end
26
38
  end
27
39
 
28
- SERIALIZE, DESERIALIZE =
29
- if ::ActiveRecord::VERSION::MAJOR >= 5
30
- [:serialize, :deserialize]
31
- else
32
- [:type_cast_for_database, :type_cast_from_database]
40
+ # The `AbstractSerializer` (de)serializes model attribute values. For
41
+ # example, the string "1.99" serializes into the integer `1` when assigned
42
+ # to an attribute of type `ActiveRecord::Type::Integer`.
43
+ #
44
+ # This implementation depends on the `type_for_attribute` method, which was
45
+ # introduced in rails 4.2. In older versions of rails, we shim this method
46
+ # below.
47
+ #
48
+ # At runtime, the `AbstractSerializer` has only one child class, the
49
+ # `CastedAttributeSerializer`, whose implementation varies depending on the
50
+ # version of ActiveRecord.
51
+ class AbstractSerializer
52
+ def initialize(klass)
53
+ @klass = klass
33
54
  end
34
55
 
35
- if ::ActiveRecord::VERSION::STRING < '4.2'
36
- # Backport Rails 4.2 and later's `type_for_attribute` to build
37
- # on a common interface.
56
+ private
57
+
58
+ def apply_serialization(method, attr, val)
59
+ @klass.type_for_attribute(attr).send(method, val)
60
+ end
61
+ end
62
+
63
+ if ::ActiveRecord::VERSION::MAJOR >= 5
64
+ # This implementation uses AR 5's `serialize` and `deserialize`.
65
+ class CastedAttributeSerializer < AbstractSerializer
66
+ def serialize(attr, val)
67
+ apply_serialization(:serialize, attr, val)
68
+ end
69
+
70
+ def deserialize(attr, val)
71
+ apply_serialization(:deserialize, attr, val)
72
+ end
73
+ end
74
+ else
75
+ # This implementation uses AR 4.2's `type_cast_for_database`. For
76
+ # versions of AR < 4.2 we provide an implementation of
77
+ # `type_cast_for_database` in our shim attribute type classes,
78
+ # `NoOpAttribute` and `SerializedAttribute`.
79
+ class CastedAttributeSerializer < AbstractSerializer
80
+ def serialize(attr, val)
81
+ val = defined_enums[attr][val] if defined_enums[attr]
82
+ apply_serialization(:type_cast_for_database, attr, val)
83
+ end
84
+
85
+ def deserialize(attr, val)
86
+ val = apply_serialization(:type_cast_from_database, attr, val)
87
+
88
+ if defined_enums[attr]
89
+ defined_enums[attr].key(val)
90
+ else
91
+ val
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def defined_enums
98
+ @defined_enums ||= (@klass.respond_to?(:defined_enums) ? @klass.defined_enums : {})
99
+ end
100
+ end
101
+ end
102
+
103
+ # Backport Rails 4.2 and later's `type_for_attribute` so we can build
104
+ # on a common interface.
105
+ if ::ActiveRecord::VERSION::STRING < "4.2"
38
106
  def type_for_attribute(attr_name)
39
107
  serialized_attribute_types[attr_name.to_s] || NO_OP_ATTRIBUTE
40
108
  end
@@ -49,40 +117,44 @@ module PaperTrail
49
117
 
50
118
  # Used for `Version#object` attribute.
51
119
  def serialize_attributes_for_paper_trail!(attributes)
52
- alter_attributes_for_paper_trail!(SERIALIZE, attributes)
120
+ alter_attributes_for_paper_trail!(:serialize, attributes)
53
121
  end
54
122
 
55
123
  def unserialize_attributes_for_paper_trail!(attributes)
56
- alter_attributes_for_paper_trail!(DESERIALIZE, attributes)
124
+ alter_attributes_for_paper_trail!(:deserialize, attributes)
57
125
  end
58
126
 
59
- def alter_attributes_for_paper_trail!(serializer, attributes)
127
+ def alter_attributes_for_paper_trail!(serialization_method, attributes)
60
128
  # Don't serialize before values before inserting into columns of type
61
129
  # `JSON` on `PostgreSQL` databases.
62
130
  return attributes if paper_trail_version_class.object_col_is_json?
63
131
 
132
+ serializer = CastedAttributeSerializer.new(self)
64
133
  attributes.each do |key, value|
65
- attributes[key] = type_for_attribute(key).send(serializer, value)
134
+ attributes[key] = serializer.send(serialization_method, key, value)
66
135
  end
67
136
  end
68
137
 
69
138
  # Used for Version#object_changes attribute.
70
139
  def serialize_attribute_changes_for_paper_trail!(changes)
71
- alter_attribute_changes_for_paper_trail!(SERIALIZE, changes)
140
+ alter_attribute_changes_for_paper_trail!(:serialize, changes)
72
141
  end
73
142
 
74
143
  def unserialize_attribute_changes_for_paper_trail!(changes)
75
- alter_attribute_changes_for_paper_trail!(DESERIALIZE, changes)
144
+ alter_attribute_changes_for_paper_trail!(:deserialize, changes)
76
145
  end
77
146
 
78
- def alter_attribute_changes_for_paper_trail!(serializer, changes)
147
+ def alter_attribute_changes_for_paper_trail!(serialization_method, changes)
79
148
  # Don't serialize before values before inserting into columns of type
80
149
  # `JSON` on `PostgreSQL` databases.
81
150
  return changes if paper_trail_version_class.object_changes_col_is_json?
82
151
 
152
+ serializer = CastedAttributeSerializer.new(self)
83
153
  changes.clone.each do |key, change|
84
- type = type_for_attribute(key)
85
- changes[key] = Array(change).map { |value| type.send(serializer, value) }
154
+ # `change` is an Array with two elements, representing before and after.
155
+ changes[key] = Array(change).map do |value|
156
+ serializer.send(serialization_method, key, value)
157
+ end
86
158
  end
87
159
  end
88
160
  end
@@ -1,4 +1,5 @@
1
1
  module PaperTrail
2
+ # Utilities for deleting version records.
2
3
  module Cleaner
3
4
  # Destroys all but the most recent version(s) for items on a given date
4
5
  # (or on all dates). Useful for deleting drafts.
@@ -6,7 +7,8 @@ module PaperTrail
6
7
  # Options:
7
8
  #
8
9
  # - :keeping - An `integer` indicating the number of versions to be kept for
9
- # each item per date. Defaults to `1`.
10
+ # each item per date. Defaults to `1`. The most recent matching versions
11
+ # are kept.
10
12
  # - :date - Should either be a `Date` object specifying which date to
11
13
  # destroy versions for or `:all`, which will specify that all dates
12
14
  # should be cleaned. Defaults to `:all`.
@@ -14,13 +16,13 @@ module PaperTrail
14
16
  # causes all items to be cleaned. Defaults to `nil`.
15
17
  #
16
18
  def clean_versions!(options = {})
17
- options = {:keeping => 1, :date => :all}.merge(options)
18
- gather_versions(options[:item_id], options[:date]).each do |item_id, versions|
19
- group_versions_by_date(versions).each do |date, _versions|
19
+ options = { keeping: 1, date: :all }.merge(options)
20
+ gather_versions(options[:item_id], options[:date]).each do |_item_id, item_versions|
21
+ group_versions_by_date(item_versions).each do |_date, date_versions|
20
22
  # Remove the number of versions we wish to keep from the collection
21
23
  # of versions prior to destruction.
22
- _versions.pop(options[:keeping])
23
- _versions.map(&:destroy)
24
+ date_versions.pop(options[:keeping])
25
+ date_versions.map(&:destroy)
24
26
  end
25
27
  end
26
28
  end
@@ -30,10 +32,14 @@ module PaperTrail
30
32
  # Returns a hash of versions grouped by the `item_id` attribute formatted
31
33
  # like this: {:item_id => PaperTrail::Version}. If `item_id` or `date` is
32
34
  # set, versions will be narrowed to those pointing at items with those ids
33
- # that were created on specified date.
35
+ # that were created on specified date. Versions are returned in
36
+ # chronological order.
34
37
  def gather_versions(item_id = nil, date = :all)
35
- raise ArgumentError.new("`date` argument must receive a Timestamp or `:all`") unless date == :all || date.respond_to?(:to_date)
36
- versions = item_id ? PaperTrail::Version.where(:item_id => item_id) : PaperTrail::Version
38
+ unless date == :all || date.respond_to?(:to_date)
39
+ raise ArgumentError, "Expected date to be a Timestamp or :all"
40
+ end
41
+ versions = item_id ? PaperTrail::Version.where(item_id: item_id) : PaperTrail::Version
42
+ versions = versions.order(PaperTrail::Version.timestamp_sort_order)
37
43
  versions = versions.between(date.to_date, date.to_date + 1.day) unless date == :all
38
44
 
39
45
  # If `versions` has not been converted to an ActiveRecord::Relation yet,
@@ -1,15 +1,22 @@
1
- require 'singleton'
2
- require 'paper_trail/serializers/yaml'
1
+ require "singleton"
2
+ require "paper_trail/serializers/yaml"
3
3
 
4
4
  module PaperTrail
5
+ # Global configuration affecting all threads. Some thread-specific
6
+ # configuration can be found in `paper_trail.rb`, others in `controller.rb`.
5
7
  class Config
6
8
  include Singleton
7
9
  attr_accessor :timestamp_field, :serializer, :version_limit
8
10
  attr_writer :track_associations
9
11
 
10
12
  def initialize
13
+ # Variables which affect all threads, whose access is synchronized.
14
+ @mutex = Mutex.new
15
+ @enabled = true
16
+
17
+ # Variables which affect all threads, whose access is *not* synchronized.
11
18
  @timestamp_field = :created_at
12
- @serializer = PaperTrail::Serializers::YAML
19
+ @serializer = PaperTrail::Serializers::YAML
13
20
  end
14
21
 
15
22
  def serialized_attributes
@@ -27,21 +34,31 @@ module PaperTrail
27
34
  )
28
35
  end
29
36
 
30
- def track_associations
31
- @track_associations.nil? ?
32
- PaperTrail::VersionAssociation.table_exists? :
37
+ # Previously, we checked `PaperTrail::VersionAssociation.table_exists?`
38
+ # here, but that proved to be problematic in situations when the database
39
+ # connection had not been established, or when the database does not exist
40
+ # yet (as with `rake db:create`).
41
+ def track_associations?
42
+ if @track_associations.nil?
43
+ ActiveSupport::Deprecation.warn <<-EOS.strip_heredoc.gsub(/\s+/, " ")
44
+ PaperTrail.track_associations has not been set. As of PaperTrail 5, it
45
+ defaults to false. Tracking associations is an experimental feature so
46
+ we recommend setting PaperTrail.config.track_associations = false in
47
+ your config/initializers/paper_trail.rb
48
+ EOS
49
+ false
50
+ else
33
51
  @track_associations
52
+ end
34
53
  end
35
- alias_method :track_associations?, :track_associations
36
54
 
37
55
  # Indicates whether PaperTrail is on or off. Default: true.
38
56
  def enabled
39
- value = PaperTrail.paper_trail_store.fetch(:paper_trail_enabled, true)
40
- value.nil? ? true : value
57
+ @mutex.synchronize { !!@enabled }
41
58
  end
42
59
 
43
- def enabled= enable
44
- PaperTrail.paper_trail_store[:paper_trail_enabled] = enable
60
+ def enabled=(enable)
61
+ @mutex.synchronize { @enabled = enable }
45
62
  end
46
63
  end
47
64
  end
@@ -2,3 +2,7 @@
2
2
  # since otherwise the model(s) will get loaded in via the `Rails::Engine`.
3
3
  require "paper_trail/frameworks/active_record/models/paper_trail/version_association"
4
4
  require "paper_trail/frameworks/active_record/models/paper_trail/version"
5
+
6
+ ActiveSupport.on_load(:active_record) do
7
+ include PaperTrail::Model
8
+ end
@@ -1,6 +1,10 @@
1
- require 'paper_trail/version_concern'
1
+ require "paper_trail/version_concern"
2
2
 
3
3
  module PaperTrail
4
+ # This is the default ActiveRecord model provided by PaperTrail. Most simple
5
+ # applications will only use this and its partner, `VersionAssociation`, but
6
+ # it is possible to sub-class, extend, or even do without this model entirely.
7
+ # See the readme for details.
4
8
  class Version < ::ActiveRecord::Base
5
9
  include PaperTrail::VersionConcern
6
10
  end
@@ -1,7 +1,11 @@
1
- require 'paper_trail/version_association_concern'
1
+ require "paper_trail/version_association_concern"
2
2
 
3
3
  module PaperTrail
4
+ # This is the default ActiveRecord model provided by PaperTrail. Most simple
5
+ # applications will only use this and its partner, `Version`, but it is
6
+ # possible to sub-class, extend, or even do without this model entirely.
7
+ # See the readme for details.
4
8
  class VersionAssociation < ::ActiveRecord::Base
5
9
  include PaperTrail::VersionAssociationConcern
6
10
  end
7
- end
11
+ end
@@ -8,6 +8,7 @@ end
8
8
 
9
9
  module PaperTrail
10
10
  module Cucumber
11
+ # Helper method for enabling PT in Cucumber features.
11
12
  module Extensions
12
13
  # :call-seq:
13
14
  # with_versioning
@@ -1,7 +1,2 @@
1
- require 'paper_trail/frameworks/rails/controller'
2
- require 'paper_trail/frameworks/rails/engine'
3
-
4
- module PaperTrail
5
- module Rails
6
- end
7
- end
1
+ require "paper_trail/frameworks/rails/controller"
2
+ require "paper_trail/frameworks/rails/engine"
@@ -1,23 +1,26 @@
1
1
  module PaperTrail
2
2
  module Rails
3
+ # Extensions to rails controllers. Provides convenient ways to pass certain
4
+ # information to the model layer, with `controller_info` and `whodunnit`.
5
+ # Also includes a convenient on/off switch, `enabled_for_controller`.
3
6
  module Controller
4
-
5
7
  def self.included(base)
6
8
  before = [
7
9
  :set_paper_trail_enabled_for_controller,
8
- :set_paper_trail_whodunnit,
9
10
  :set_paper_trail_controller_info
10
11
  ]
11
- after = []
12
+ after = [
13
+ :warn_about_not_setting_whodunnit
14
+ ]
12
15
 
13
16
  if base.respond_to? :before_action
14
17
  # Rails 4+
15
- before.map {|sym| base.before_action sym }
16
- after.map {|sym| base.after_action sym }
18
+ before.map { |sym| base.before_action sym }
19
+ after.map { |sym| base.after_action sym }
17
20
  else
18
21
  # Rails 3.
19
- before.map {|sym| base.before_filter sym }
20
- after.map {|sym| base.after_filter sym }
22
+ before.map { |sym| base.before_filter sym }
23
+ after.map { |sym| base.after_filter sym }
21
24
  end
22
25
  end
23
26
 
@@ -76,6 +79,7 @@ module PaperTrail
76
79
 
77
80
  # Tells PaperTrail who is responsible for any changes that occur.
78
81
  def set_paper_trail_whodunnit
82
+ @set_paper_trail_whodunnit_called = true
79
83
  ::PaperTrail.whodunnit = user_for_paper_trail if ::PaperTrail.enabled_for_controller?
80
84
  end
81
85
 
@@ -85,10 +89,26 @@ module PaperTrail
85
89
  ::PaperTrail.controller_info = info_for_paper_trail if ::PaperTrail.enabled_for_controller?
86
90
  end
87
91
 
92
+ def warn_about_not_setting_whodunnit
93
+ enabled = ::PaperTrail.enabled_for_controller?
94
+ user_present = user_for_paper_trail.present?
95
+ whodunnit_blank = ::PaperTrail.whodunnit.blank?
96
+ if enabled && user_present && whodunnit_blank && !@set_paper_trail_whodunnit_called
97
+ warn <<-EOS.strip_heredoc
98
+ user_for_paper_trail is present, but whodunnit has not been set.
99
+ PaperTrail no longer adds the set_paper_trail_whodunnit
100
+ before_filter for you. Please add this before_filter to your
101
+ ApplicationController to continue recording whodunnit. See the
102
+ PaperTrail readme for an example.
103
+ EOS
104
+ end
105
+ end
88
106
  end
89
107
  end
108
+ end
90
109
 
91
- if defined?(::ActionController)
92
- ::ActiveSupport.on_load(:action_controller) { include PaperTrail::Rails::Controller }
110
+ if defined?(::ActionController)
111
+ ::ActiveSupport.on_load(:action_controller) do
112
+ include ::PaperTrail::Rails::Controller
93
113
  end
94
114
  end
@@ -1,7 +1,13 @@
1
1
  module PaperTrail
2
2
  module Rails
3
+ # See http://guides.rubyonrails.org/engines.html
3
4
  class Engine < ::Rails::Engine
4
- paths['app/models'] << 'lib/paper_trail/frameworks/active_record/models'
5
+ paths["app/models"] << "lib/paper_trail/frameworks/active_record/models"
6
+ config.paper_trail = ActiveSupport::OrderedOptions.new
7
+ initializer "paper_trail.initialisation" do |app|
8
+ ActiveRecord::Base.send :include, PaperTrail::Model
9
+ PaperTrail.enabled = app.config.paper_trail.fetch(:enabled, true)
10
+ end
5
11
  end
6
12
  end
7
13
  end
@@ -1,6 +1,6 @@
1
- require 'rspec/core'
2
- require 'rspec/matchers'
3
- require 'paper_trail/frameworks/rspec/helpers'
1
+ require "rspec/core"
2
+ require "rspec/matchers"
3
+ require "paper_trail/frameworks/rspec/helpers"
4
4
 
5
5
  RSpec.configure do |config|
6
6
  config.include ::PaperTrail::RSpec::Helpers::InstanceMethods
@@ -13,14 +13,14 @@ RSpec.configure do |config|
13
13
  ::PaperTrail.controller_info = {} if defined?(::Rails) && defined?(::RSpec::Rails)
14
14
  end
15
15
 
16
- config.before(:each, :versioning => true) do
16
+ config.before(:each, versioning: true) do
17
17
  ::PaperTrail.enabled = true
18
18
  end
19
19
  end
20
20
 
21
21
  RSpec::Matchers.define :be_versioned do
22
22
  # check to see if the model has `has_paper_trail` declared on it
23
- match { |actual| actual.kind_of?(::PaperTrail::Model::InstanceMethods) }
23
+ match { |actual| actual.is_a?(::PaperTrail::Model::InstanceMethods) }
24
24
  end
25
25
 
26
26
  RSpec::Matchers.define :have_a_version_with do |attributes|
@@ -1,6 +1,7 @@
1
1
  module PaperTrail
2
2
  module RSpec
3
3
  module Helpers
4
+ # Included in the RSpec configuration in `frameworks/rspec.rb`
4
5
  module InstanceMethods
5
6
  # enable versioning for specific blocks (at instance-level)
6
7
  def with_versioning
@@ -12,10 +13,11 @@ module PaperTrail
12
13
  end
13
14
  end
14
15
 
16
+ # Extended by the RSpec configuration in `frameworks/rspec.rb`
15
17
  module ClassMethods
16
18
  # enable versioning for specific blocks (at class-level)
17
19
  def with_versioning(&block)
18
- context 'with versioning', :versioning => true do
20
+ context "with versioning", versioning: true do
19
21
  class_exec(&block)
20
22
  end
21
23
  end