paper_trail 8.1.2 → 9.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.
- checksums.yaml +4 -4
- data/lib/generators/paper_trail/install_generator.rb +2 -0
- data/lib/paper_trail.rb +130 -78
- data/lib/paper_trail/attribute_serializers/attribute_serializer_factory.rb +3 -1
- data/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +2 -0
- data/lib/paper_trail/attribute_serializers/object_attribute.rb +2 -0
- data/lib/paper_trail/attribute_serializers/object_changes_attribute.rb +2 -0
- data/lib/paper_trail/cleaner.rb +2 -0
- data/lib/paper_trail/config.rb +33 -8
- data/lib/paper_trail/frameworks/active_record.rb +2 -0
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +2 -0
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +2 -0
- data/lib/paper_trail/frameworks/cucumber.rb +5 -3
- data/lib/paper_trail/frameworks/rails.rb +2 -0
- data/lib/paper_trail/frameworks/rails/controller.rb +28 -16
- data/lib/paper_trail/frameworks/rails/engine.rb +2 -0
- data/lib/paper_trail/frameworks/rspec.rb +5 -3
- data/lib/paper_trail/frameworks/rspec/helpers.rb +2 -0
- data/lib/paper_trail/has_paper_trail.rb +2 -1
- data/lib/paper_trail/model_config.rb +76 -14
- data/lib/paper_trail/queries/versions/where_object.rb +2 -0
- data/lib/paper_trail/queries/versions/where_object_changes.rb +3 -1
- data/lib/paper_trail/record_history.rb +2 -0
- data/lib/paper_trail/record_trail.rb +188 -48
- data/lib/paper_trail/reifier.rb +4 -2
- data/lib/paper_trail/reifiers/belongs_to.rb +2 -0
- data/lib/paper_trail/reifiers/has_and_belongs_to_many.rb +2 -0
- data/lib/paper_trail/reifiers/has_many.rb +2 -0
- data/lib/paper_trail/reifiers/has_many_through.rb +2 -0
- data/lib/paper_trail/reifiers/has_one.rb +52 -4
- data/lib/paper_trail/request.rb +183 -0
- data/lib/paper_trail/serializers/json.rb +2 -2
- data/lib/paper_trail/serializers/yaml.rb +10 -14
- data/lib/paper_trail/type_serializers/postgres_array_serializer.rb +2 -0
- data/lib/paper_trail/version_association_concern.rb +1 -1
- data/lib/paper_trail/version_concern.rb +2 -6
- data/lib/paper_trail/version_number.rb +5 -3
- metadata +8 -21
data/lib/paper_trail/reifier.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "paper_trail/attribute_serializers/object_attribute"
|
2
4
|
require "paper_trail/reifiers/belongs_to"
|
3
5
|
require "paper_trail/reifiers/has_and_belongs_to_many"
|
@@ -54,7 +56,7 @@ module PaperTrail
|
|
54
56
|
# @api private
|
55
57
|
def each_enabled_association(associations)
|
56
58
|
associations.each do |assoc|
|
57
|
-
next unless assoc.klass
|
59
|
+
next unless ::PaperTrail.request.enabled_for_model?(assoc.klass)
|
58
60
|
yield assoc
|
59
61
|
end
|
60
62
|
end
|
@@ -192,7 +194,7 @@ module PaperTrail
|
|
192
194
|
# @api private
|
193
195
|
def reify_habtm_associations(transaction_id, model, options = {})
|
194
196
|
model.class.reflect_on_all_associations(:has_and_belongs_to_many).each do |assoc|
|
195
|
-
pt_enabled = assoc.klass
|
197
|
+
pt_enabled = ::PaperTrail.request.enabled_for_model?(assoc.klass)
|
196
198
|
next unless model.class.paper_trail_save_join_tables.include?(assoc.name) || pt_enabled
|
197
199
|
Reifiers::HasAndBelongsToMany.reify(pt_enabled, assoc, model, options, transaction_id)
|
198
200
|
end
|
@@ -1,12 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PaperTrail
|
2
4
|
module Reifiers
|
3
5
|
# Reify a single `has_one` association of `model`.
|
4
6
|
# @api private
|
5
7
|
module HasOne
|
8
|
+
# A more helpful error message, instead of the AssociationTypeMismatch
|
9
|
+
# you would get if, eg. we were to try to assign a Bicycle to the :car
|
10
|
+
# association (before, if there were multiple records we would just take
|
11
|
+
# the first and hope for the best).
|
12
|
+
# @api private
|
13
|
+
class FoundMoreThanOne < RuntimeError
|
14
|
+
MESSAGE_FMT = <<~STR
|
15
|
+
Unable to reify has_one association. Expected to find one %s,
|
16
|
+
but found %d.
|
17
|
+
|
18
|
+
This is a known issue, and a good example of why association tracking
|
19
|
+
is an experimental feature that should not be used in production.
|
20
|
+
|
21
|
+
That said, this is a rare error. In spec/models/person_spec.rb we
|
22
|
+
reproduce it by having two STI models with the same foreign_key (Car
|
23
|
+
and Bicycle are both Vehicles and the FK for both is owner_id)
|
24
|
+
|
25
|
+
If you'd like to help fix this error, please read
|
26
|
+
https://github.com/airblade/paper_trail/issues/594
|
27
|
+
and see spec/models/person_spec.rb
|
28
|
+
STR
|
29
|
+
|
30
|
+
def initialize(base_class_name, num_records_found)
|
31
|
+
@base_class_name = base_class_name.to_s
|
32
|
+
@num_records_found = num_records_found.to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
def message
|
36
|
+
format(MESSAGE_FMT, @base_class_name, @num_records_found)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
6
40
|
class << self
|
7
41
|
# @api private
|
8
42
|
def reify(assoc, model, options, transaction_id)
|
9
|
-
version =
|
43
|
+
version = load_version(assoc, model, transaction_id, options[:version_at])
|
10
44
|
return unless version
|
11
45
|
if version.event == "create"
|
12
46
|
create_event(assoc, model, options)
|
@@ -31,15 +65,29 @@ module PaperTrail
|
|
31
65
|
# Given a has-one association `assoc` on `model`, return the version
|
32
66
|
# record from the point in time identified by `transaction_id` or `version_at`.
|
33
67
|
# @api private
|
34
|
-
def
|
68
|
+
def load_version(assoc, model, transaction_id, version_at)
|
69
|
+
base_class_name = assoc.klass.base_class.name
|
70
|
+
versions = load_versions(assoc, model, transaction_id, version_at, base_class_name)
|
71
|
+
case versions.length
|
72
|
+
when 0
|
73
|
+
nil
|
74
|
+
when 1
|
75
|
+
versions.first
|
76
|
+
else
|
77
|
+
raise FoundMoreThanOne.new(base_class_name, versions.length)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @api private
|
82
|
+
def load_versions(assoc, model, transaction_id, version_at, base_class_name)
|
35
83
|
version_table_name = model.class.paper_trail.version_class.table_name
|
36
84
|
model.class.paper_trail.version_class.joins(:version_associations).
|
37
85
|
where("version_associations.foreign_key_name = ?", assoc.foreign_key).
|
38
86
|
where("version_associations.foreign_key_id = ?", model.id).
|
39
|
-
where("#{version_table_name}.item_type = ?",
|
87
|
+
where("#{version_table_name}.item_type = ?", base_class_name).
|
40
88
|
where("created_at >= ? OR transaction_id = ?", version_at, transaction_id).
|
41
89
|
order("#{version_table_name}.id ASC").
|
42
|
-
|
90
|
+
load
|
43
91
|
end
|
44
92
|
|
45
93
|
# @api private
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "request_store"
|
4
|
+
|
5
|
+
module PaperTrail
|
6
|
+
# Manages variables that affect the current HTTP request, such as `whodunnit`.
|
7
|
+
#
|
8
|
+
# Please do not use `PaperTrail::Request` directly, use `PaperTrail.request`.
|
9
|
+
# Currently, `Request` is a `Module`, but in the future it is quite possible
|
10
|
+
# we may make it a `Class`. If we make such a choice, we will not provide any
|
11
|
+
# warning and will not treat it as a breaking change. You've been warned :)
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
module Request
|
15
|
+
class InvalidOption < RuntimeError
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# @api private
|
20
|
+
def clear_transaction_id
|
21
|
+
self.transaction_id = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sets any data from the controller that you want PaperTrail to store.
|
25
|
+
# See also `PaperTrail::Rails::Controller#info_for_paper_trail`.
|
26
|
+
#
|
27
|
+
# PaperTrail.request.controller_info = { ip: request_user_ip }
|
28
|
+
# PaperTrail.request.controller_info # => { ip: '127.0.0.1' }
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def controller_info=(value)
|
32
|
+
store[:controller_info] = value
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the data from the controller that you want PaperTrail to store.
|
36
|
+
# See also `PaperTrail::Rails::Controller#info_for_paper_trail`.
|
37
|
+
#
|
38
|
+
# PaperTrail.request.controller_info = { ip: request_user_ip }
|
39
|
+
# PaperTrail.request.controller_info # => { ip: '127.0.0.1' }
|
40
|
+
#
|
41
|
+
# @api public
|
42
|
+
def controller_info
|
43
|
+
store[:controller_info]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Switches PaperTrail off for the given model.
|
47
|
+
# @api public
|
48
|
+
def disable_model(model_class)
|
49
|
+
enabled_for_model(model_class, false)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Switches PaperTrail on for the given model.
|
53
|
+
# @api public
|
54
|
+
def enable_model(model_class)
|
55
|
+
enabled_for_model(model_class, true)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sets whether PaperTrail is enabled or disabled for the current request.
|
59
|
+
# @api public
|
60
|
+
def enabled=(value)
|
61
|
+
store[:enabled] = value
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns `true` if PaperTrail is enabled for the request, `false` otherwise.
|
65
|
+
# See `PaperTrail::Rails::Controller#paper_trail_enabled_for_controller`.
|
66
|
+
# @api public
|
67
|
+
def enabled?
|
68
|
+
!!store[:enabled]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets whether PaperTrail is enabled or disabled for this model in the
|
72
|
+
# current request.
|
73
|
+
# @api public
|
74
|
+
def enabled_for_model(model, value)
|
75
|
+
store[:"enabled_for_#{model}"] = value
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns `true` if PaperTrail is enabled for this model in the current
|
79
|
+
# request, `false` otherwise.
|
80
|
+
# @api public
|
81
|
+
def enabled_for_model?(model)
|
82
|
+
model.include?(::PaperTrail::Model::InstanceMethods) &&
|
83
|
+
!!store.fetch(:"enabled_for_#{model}", true)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
def merge(options)
|
88
|
+
options.to_h.each do |k, v|
|
89
|
+
store[k] = v
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# @api private
|
94
|
+
def set(options)
|
95
|
+
store.clear
|
96
|
+
merge(options)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns a deep copy of the internal hash from our RequestStore. Keys are
|
100
|
+
# all symbols. Values are mostly primitives, but whodunnit can be a Proc.
|
101
|
+
# We cannot use Marshal.dump here because it doesn't support Proc. It is
|
102
|
+
# unclear exactly how `deep_dup` handles a Proc, but it doesn't complain.
|
103
|
+
# @api private
|
104
|
+
def to_h
|
105
|
+
store.deep_dup
|
106
|
+
end
|
107
|
+
|
108
|
+
# @api private
|
109
|
+
def transaction_id
|
110
|
+
store[:transaction_id]
|
111
|
+
end
|
112
|
+
|
113
|
+
# @api private
|
114
|
+
def transaction_id=(id)
|
115
|
+
store[:transaction_id] = id
|
116
|
+
end
|
117
|
+
|
118
|
+
# Temporarily set `options` and execute a block.
|
119
|
+
# @api private
|
120
|
+
def with(options)
|
121
|
+
return unless block_given?
|
122
|
+
validate_public_options(options)
|
123
|
+
before = to_h
|
124
|
+
merge(options)
|
125
|
+
yield
|
126
|
+
ensure
|
127
|
+
set(before)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Sets who is responsible for any changes that occur during request. You
|
131
|
+
# would normally use this in a migration or on the console, when working
|
132
|
+
# with models directly.
|
133
|
+
#
|
134
|
+
# `value` is usually a string, the name of a person, but you can set
|
135
|
+
# anything that responds to `to_s`. You can also set a Proc, which will
|
136
|
+
# not be evaluated until `whodunnit` is called later, usually right before
|
137
|
+
# inserting a `Version` record.
|
138
|
+
#
|
139
|
+
# @api public
|
140
|
+
def whodunnit=(value)
|
141
|
+
store[:whodunnit] = value
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns who is reponsible for any changes that occur during request.
|
145
|
+
#
|
146
|
+
# @api public
|
147
|
+
def whodunnit
|
148
|
+
who = store[:whodunnit]
|
149
|
+
who.respond_to?(:call) ? who.call : who
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# Returns a Hash, initializing with default values if necessary.
|
155
|
+
# @api private
|
156
|
+
def store
|
157
|
+
RequestStore.store[:paper_trail] ||= {
|
158
|
+
enabled: true
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
# Provide a helpful error message if someone has a typo in one of their
|
163
|
+
# option keys. We don't validate option values here. That's traditionally
|
164
|
+
# been handled with casting (`to_s`, `!!`) in the accessor method.
|
165
|
+
# @api private
|
166
|
+
def validate_public_options(options)
|
167
|
+
options.each do |k, _v|
|
168
|
+
case k
|
169
|
+
when :controller_info,
|
170
|
+
/enabled_for_/,
|
171
|
+
:enabled,
|
172
|
+
:whodunnit
|
173
|
+
next
|
174
|
+
when :transaction_id
|
175
|
+
raise InvalidOption, "Cannot set private option: #{k}"
|
176
|
+
else
|
177
|
+
raise InvalidOption, "Invalid option: #{k}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PaperTrail
|
4
4
|
module Serializers
|
@@ -34,7 +34,7 @@ module PaperTrail
|
|
34
34
|
|
35
35
|
def where_object_changes_condition(*)
|
36
36
|
raise <<-STR.squish.freeze
|
37
|
-
where_object_changes no longer supports reading
|
37
|
+
where_object_changes no longer supports reading JSON from a text
|
38
38
|
column. The old implementation was inaccurate, returning more records
|
39
39
|
than you wanted. This feature was deprecated in 7.1.0 and removed in
|
40
40
|
8.0.0. The json and jsonb datatypes are still supported. See the
|
@@ -1,16 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "yaml"
|
2
4
|
|
3
5
|
module PaperTrail
|
4
6
|
module Serializers
|
5
7
|
# The default serializer for, e.g. `versions.object`.
|
6
8
|
module YAML
|
7
|
-
E_WHERE_OBJ_CHANGES = <<-STR.squish.freeze
|
8
|
-
where_object_changes has a known issue. When reading YAML from a text
|
9
|
-
column, it may return more records than expected. Instead of a warning,
|
10
|
-
this method may raise an error in the future. Please join the discussion
|
11
|
-
at https://github.com/airblade/paper_trail/pull/997
|
12
|
-
STR
|
13
|
-
|
14
9
|
extend self # makes all instance methods become module methods as well
|
15
10
|
|
16
11
|
def load(string)
|
@@ -29,13 +24,14 @@ module PaperTrail
|
|
29
24
|
|
30
25
|
# Returns a SQL LIKE condition to be used to match the given field and
|
31
26
|
# value in the serialized `object_changes`.
|
32
|
-
def where_object_changes_condition(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
def where_object_changes_condition(*)
|
28
|
+
raise <<-STR.squish.freeze
|
29
|
+
where_object_changes no longer supports reading YAML from a text
|
30
|
+
column. The old implementation was inaccurate, returning more records
|
31
|
+
than you wanted. This feature was deprecated in 8.1.0 and removed in
|
32
|
+
9.0.0. The json and jsonb datatypes are still supported. See
|
33
|
+
discussion at https://github.com/airblade/paper_trail/pull/997
|
34
|
+
STR
|
39
35
|
end
|
40
36
|
end
|
41
37
|
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "paper_trail/attribute_serializers/object_changes_attribute"
|
3
4
|
require "paper_trail/queries/versions/where_object"
|
4
5
|
require "paper_trail/queries/versions/where_object_changes"
|
@@ -230,11 +231,6 @@ module PaperTrail
|
|
230
231
|
@paper_trail_originator ||= previous.try(:whodunnit)
|
231
232
|
end
|
232
233
|
|
233
|
-
def originator
|
234
|
-
::ActiveSupport::Deprecation.warn "Use paper_trail_originator instead of originator."
|
235
|
-
paper_trail_originator
|
236
|
-
end
|
237
|
-
|
238
234
|
# Returns who changed the item from the state it had in this version. This
|
239
235
|
# is an alias for `whodunnit`.
|
240
236
|
def terminator
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PaperTrail
|
2
4
|
# The version number of the paper_trail gem. Not to be confused with
|
3
5
|
# `PaperTrail::Version`. Ruby constants are case-sensitive, apparently,
|
@@ -5,9 +7,9 @@ module PaperTrail
|
|
5
7
|
# because of this confusion, but it's not worth the breaking change.
|
6
8
|
# People are encouraged to use `PaperTrail.gem_version` instead.
|
7
9
|
module VERSION
|
8
|
-
MAJOR =
|
9
|
-
MINOR =
|
10
|
-
TINY =
|
10
|
+
MAJOR = 9
|
11
|
+
MINOR = 0
|
12
|
+
TINY = 0
|
11
13
|
PRE = nil
|
12
14
|
|
13
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".").freeze
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paper_trail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 9.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Stewart
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2018-03-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '4.2'
|
22
22
|
- - "<"
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: '5.
|
24
|
+
version: '5.3'
|
25
25
|
type: :runtime
|
26
26
|
prerelease: false
|
27
27
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
version: '4.2'
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '5.
|
34
|
+
version: '5.3'
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: request_store
|
37
37
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,20 +74,6 @@ dependencies:
|
|
74
74
|
- - "~>"
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '9.1'
|
77
|
-
- !ruby/object:Gem::Dependency
|
78
|
-
name: database_cleaner
|
79
|
-
requirement: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - "~>"
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '1.6'
|
84
|
-
type: :development
|
85
|
-
prerelease: false
|
86
|
-
version_requirements: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - "~>"
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '1.6'
|
91
77
|
- !ruby/object:Gem::Dependency
|
92
78
|
name: ffaker
|
93
79
|
requirement: !ruby/object:Gem::Requirement
|
@@ -252,7 +238,7 @@ description: |
|
|
252
238
|
Track changes to your models, for auditing or versioning. See how a model looked
|
253
239
|
at any stage in its lifecycle, revert it to any version, or restore it after it
|
254
240
|
has been destroyed.
|
255
|
-
email:
|
241
|
+
email: jared@jaredbeck.com
|
256
242
|
executables: []
|
257
243
|
extensions: []
|
258
244
|
extra_rdoc_files: []
|
@@ -292,6 +278,7 @@ files:
|
|
292
278
|
- lib/paper_trail/reifiers/has_many.rb
|
293
279
|
- lib/paper_trail/reifiers/has_many_through.rb
|
294
280
|
- lib/paper_trail/reifiers/has_one.rb
|
281
|
+
- lib/paper_trail/request.rb
|
295
282
|
- lib/paper_trail/serializers/json.rb
|
296
283
|
- lib/paper_trail/serializers/yaml.rb
|
297
284
|
- lib/paper_trail/type_serializers/postgres_array_serializer.rb
|
@@ -310,7 +297,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
310
297
|
requirements:
|
311
298
|
- - ">="
|
312
299
|
- !ruby/object:Gem::Version
|
313
|
-
version: 2.
|
300
|
+
version: 2.3.0
|
314
301
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
315
302
|
requirements:
|
316
303
|
- - ">="
|
@@ -318,7 +305,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
318
305
|
version: 1.3.6
|
319
306
|
requirements: []
|
320
307
|
rubyforge_project:
|
321
|
-
rubygems_version: 2.7.
|
308
|
+
rubygems_version: 2.7.6
|
322
309
|
signing_key:
|
323
310
|
specification_version: 4
|
324
311
|
summary: Track changes to your models.
|