paper_trail 4.2.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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