paper_trail 4.0.0.beta2 → 4.0.0.rc1

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +9 -7
  4. data/CHANGELOG.md +38 -2
  5. data/README.md +30 -12
  6. data/Rakefile +1 -1
  7. data/gemfiles/3.0.gemfile +3 -1
  8. data/lib/generators/paper_trail/install_generator.rb +4 -1
  9. data/lib/paper_trail/cleaner.rb +1 -1
  10. data/lib/paper_trail/config.rb +6 -0
  11. data/lib/paper_trail/frameworks/sinatra.rb +1 -0
  12. data/lib/paper_trail/has_paper_trail.rb +80 -67
  13. data/lib/paper_trail/serializers/yaml.rb +2 -1
  14. data/lib/paper_trail/version_concern.rb +64 -19
  15. data/lib/paper_trail/version_number.rb +1 -1
  16. data/lib/paper_trail.rb +7 -3
  17. data/paper_trail.gemspec +2 -1
  18. data/spec/models/animal_spec.rb +19 -0
  19. data/spec/models/boolit_spec.rb +48 -0
  20. data/spec/models/json_version_spec.rb +80 -0
  21. data/spec/models/thing_spec.rb +11 -0
  22. data/spec/models/version_spec.rb +153 -78
  23. data/spec/models/widget_spec.rb +27 -6
  24. data/spec/modules/paper_trail_spec.rb +27 -0
  25. data/spec/requests/articles_spec.rb +0 -2
  26. data/test/dummy/app/models/animal.rb +2 -0
  27. data/test/dummy/app/models/boolit.rb +4 -0
  28. data/test/dummy/app/models/fruit.rb +5 -0
  29. data/test/dummy/app/models/song.rb +20 -0
  30. data/test/dummy/app/models/thing.rb +3 -0
  31. data/test/dummy/app/models/whatchamajigger.rb +4 -0
  32. data/test/dummy/app/models/widget.rb +1 -0
  33. data/test/dummy/app/versions/json_version.rb +3 -0
  34. data/test/dummy/config/initializers/paper_trail.rb +3 -0
  35. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +44 -3
  36. data/test/dummy/db/schema.rb +13 -4
  37. data/test/functional/controller_test.rb +4 -2
  38. data/test/unit/model_test.rb +76 -12
  39. data/test/unit/serializer_test.rb +3 -3
  40. metadata +40 -6
@@ -6,11 +6,20 @@ module PaperTrail
6
6
 
7
7
  included do
8
8
  belongs_to :item, :polymorphic => true
9
- has_many :version_associations, :dependent => :destroy
9
+
10
+ # Since the test suite has test coverage for this, we want to declare the
11
+ # association when the test suite is running. This makes it pass
12
+ # when DB is not initialized prior to test runs such as when we run on
13
+ # Travis CI (there won't be a db in `test/dummy/db/`)
14
+ if PaperTrail.config.track_associations?
15
+ has_many :version_associations, :dependent => :destroy
16
+ end
10
17
 
11
18
  validates_presence_of :event
12
19
 
13
- attr_accessible :item_type, :item_id, :event, :whodunnit, :object, :object_changes, :transaction_id, :created_at if PaperTrail.active_record_protected_attributes?
20
+ if PaperTrail.active_record_protected_attributes?
21
+ attr_accessible :item_type, :item_id, :event, :whodunnit, :object, :object_changes, :transaction_id, :created_at
22
+ end
14
23
 
15
24
  after_create :enforce_version_limit!
16
25
 
@@ -77,12 +86,22 @@ module PaperTrail
77
86
  # identically-named method in the serializer being used.
78
87
  def where_object(args = {})
79
88
  raise ArgumentError, 'expected to receive a Hash' unless args.is_a?(Hash)
80
- arel_field = arel_table[:object]
81
89
 
82
- where_conditions = args.map do |field, value|
83
- PaperTrail.serializer.where_object_condition(arel_field, field, value)
84
- end.reduce do |condition1, condition2|
85
- condition1.and(condition2)
90
+ if columns_hash['object'].type == :jsonb
91
+ where_conditions = "object @> '#{args.to_json}'::jsonb"
92
+ elsif columns_hash['object'].type == :json
93
+ where_conditions = args.map do |field, value|
94
+ "object->>'#{field}' = '#{value}'"
95
+ end
96
+ where_conditions = where_conditions.join(" AND ")
97
+ else
98
+ arel_field = arel_table[:object]
99
+
100
+ where_conditions = args.map do |field, value|
101
+ PaperTrail.serializer.where_object_condition(arel_field, field, value)
102
+ end.reduce do |condition1, condition2|
103
+ condition1.and(condition2)
104
+ end
86
105
  end
87
106
 
88
107
  where(where_conditions)
@@ -90,12 +109,23 @@ module PaperTrail
90
109
 
91
110
  def where_object_changes(args = {})
92
111
  raise ArgumentError, 'expected to receive a Hash' unless args.is_a?(Hash)
93
- arel_field = arel_table[:object_changes]
94
112
 
95
- where_conditions = args.map do |field, value|
96
- PaperTrail.serializer.where_object_changes_condition(arel_field, field, value)
97
- end.reduce do |condition1, condition2|
98
- condition1.and(condition2)
113
+ if columns_hash['object_changes'].type == :jsonb
114
+ args.each { |field, value| args[field] = [value] }
115
+ where_conditions = "object_changes @> '#{args.to_json}'::jsonb"
116
+ elsif columns_hash['object'].type == :json
117
+ where_conditions = args.map do |field, value|
118
+ "((object_changes->>'#{field}' ILIKE '[#{value.to_json},%') OR (object_changes->>'#{field}' ILIKE '[%,#{value.to_json}]%'))"
119
+ end
120
+ where_conditions = where_conditions.join(" AND ")
121
+ else
122
+ arel_field = arel_table[:object_changes]
123
+
124
+ where_conditions = args.map do |field, value|
125
+ PaperTrail.serializer.where_object_changes_condition(arel_field, field, value)
126
+ end.reduce do |condition1, condition2|
127
+ condition1.and(condition2)
128
+ end
99
129
  end
100
130
 
101
131
  where(where_conditions)
@@ -109,12 +139,12 @@ module PaperTrail
109
139
 
110
140
  # Returns whether the `object` column is using the `json` type supported by PostgreSQL
111
141
  def object_col_is_json?
112
- @object_col_is_json ||= columns_hash['object'].type == :json
142
+ [:json, :jsonb].include?(columns_hash['object'].type)
113
143
  end
114
144
 
115
145
  # Returns whether the `object_changes` column is using the `json` type supported by PostgreSQL
116
146
  def object_changes_col_is_json?
117
- @object_changes_col_is_json ||= columns_hash['object_changes'].try(:type) == :json
147
+ [:json, :jsonb].include?(columns_hash['object_changes'].try(:type))
118
148
  end
119
149
  end
120
150
 
@@ -158,7 +188,7 @@ module PaperTrail
158
188
  # `item_type` will be the base class, not the actual subclass.
159
189
  # If `type` is present but empty, the class is the base class.
160
190
 
161
- if item && options[:dup] != true
191
+ if options[:dup] != true && item
162
192
  model = item
163
193
  # Look for attributes that exist in the model and not in this version. These attributes should be set to nil.
164
194
  (model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
@@ -166,17 +196,27 @@ module PaperTrail
166
196
  inheritance_column_name = item_type.constantize.inheritance_column
167
197
  class_name = attrs[inheritance_column_name].blank? ? item_type : attrs[inheritance_column_name]
168
198
  klass = class_name.constantize
169
- model = klass.new
199
+ # the `dup` option always returns a new object, otherwise we should attempt
200
+ # to look for the item outside of default scope(s)
201
+ if options[:dup] || (_item = klass.unscoped.find_by_id(item_id)).nil?
202
+ model = klass.new
203
+ else
204
+ model = _item
205
+ # Look for attributes that exist in the model and not in this version. These attributes should be set to nil.
206
+ (model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
207
+ end
170
208
  end
171
209
 
172
210
  if PaperTrail.serialized_attributes?
173
- model.class.unserialize_attributes_for_paper_trail attrs
211
+ model.class.unserialize_attributes_for_paper_trail! attrs
174
212
  end
175
213
 
176
214
  # Set all the attributes in this version on the model
177
215
  attrs.each do |k, v|
178
216
  if model.has_attribute?(k)
179
217
  model[k.to_sym] = v
218
+ elsif model.respond_to?("#{k}=")
219
+ model.send("#{k}=", v)
180
220
  else
181
221
  logger.warn "Attribute #{k} does not exist on #{item_type} (Version id: #{id})."
182
222
  end
@@ -204,7 +244,7 @@ module PaperTrail
204
244
  _changes = self.class.object_changes_col_is_json? ? object_changes : PaperTrail.serializer.load(object_changes)
205
245
  @changeset ||= HashWithIndifferentAccess.new(_changes).tap do |changes|
206
246
  if PaperTrail.serialized_attributes?
207
- item_type.constantize.unserialize_attribute_changes(changes)
247
+ item_type.constantize.unserialize_attribute_changes_for_paper_trail!(changes)
208
248
  end
209
249
  end
210
250
  rescue
@@ -212,8 +252,13 @@ module PaperTrail
212
252
  end
213
253
 
214
254
  # Returns who put the item into the state stored in this version.
255
+ def paper_trail_originator
256
+ @paper_trail_originator ||= previous.whodunnit rescue nil
257
+ end
258
+
215
259
  def originator
216
- @originator ||= previous.whodunnit rescue nil
260
+ warn "DEPRECATED: use `paper_trail_originator` instead of `originator`. Support for `originator` will be removed in PaperTrail 4.0"
261
+ self.paper_trail_originator
217
262
  end
218
263
 
219
264
  # Returns who changed the item from the state it had in this version.
@@ -3,7 +3,7 @@ module PaperTrail
3
3
  MAJOR = 4
4
4
  MINOR = 0
5
5
  TINY = 0
6
- PRE = 'beta2'
6
+ PRE = 'rc1'
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
 
data/lib/paper_trail.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'request_store'
2
+
1
3
  # Require core library
2
4
  Dir[File.join(File.dirname(__FILE__), 'paper_trail', '*.rb')].each do |file|
3
5
  require File.join('paper_trail', File.basename(file, '.rb'))
@@ -118,16 +120,18 @@ module PaperTrail
118
120
  # Thread-safe hash to hold PaperTrail's data.
119
121
  # Initializing with needed default values.
120
122
  def self.paper_trail_store
121
- Thread.current[:paper_trail] ||= { :request_enabled_for_controller => true }
123
+ RequestStore.store[:paper_trail] ||= { :request_enabled_for_controller => true }
122
124
  end
123
125
 
124
126
  # Returns PaperTrail's configuration object.
125
127
  def self.config
126
128
  @@config ||= PaperTrail::Config.instance
129
+ yield @@config if block_given?
130
+ @@config
127
131
  end
128
132
 
129
- def self.configure
130
- yield config
133
+ class << self
134
+ alias_method :configure, :config
131
135
  end
132
136
  end
133
137
 
data/paper_trail.gemspec CHANGED
@@ -21,11 +21,12 @@ Gem::Specification.new do |s|
21
21
 
22
22
  s.add_dependency 'activerecord', ['>= 3.0', '< 6.0']
23
23
  s.add_dependency 'activesupport', ['>= 3.0', '< 6.0']
24
+ s.add_dependency 'request_store', '~> 1.1.0'
24
25
 
25
26
  s.add_development_dependency 'rake', '~> 10.1.1'
26
27
  s.add_development_dependency 'shoulda', '~> 3.5'
27
28
  # s.add_development_dependency 'shoulda-matchers', '~> 1.5' # needed for ActiveRecord < 4
28
- s.add_development_dependency 'ffaker', '>= 1.15'
29
+ s.add_development_dependency 'ffaker', '<= 1.31.0'
29
30
  s.add_development_dependency 'railties', ['>= 3.0', '< 5.0']
30
31
  s.add_development_dependency 'sinatra', '~> 1.0'
31
32
  s.add_development_dependency 'rack-test', '>= 0.6'
@@ -0,0 +1,19 @@
1
+ require 'rails_helper'
2
+
3
+ describe Animal, :type => :model do
4
+ it { is_expected.to be_versioned }
5
+
6
+ describe "STI", :versioning => true do
7
+ it { expect(Animal.inheritance_column).to eq('species') }
8
+
9
+ describe "updates to the `inheritance_column`" do
10
+ subject { Cat.create!(:name => 'Leo') }
11
+
12
+ it "should be allowed" do
13
+ subject.update_attributes(:name => 'Spike', :species => 'Dog')
14
+ dog = Animal.find(subject.id)
15
+ expect(dog).to be_instance_of(Dog)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,48 @@
1
+ require 'rails_helper'
2
+ require Rails.root.join('..', 'custom_json_serializer')
3
+
4
+ describe Boolit, :type => :model do
5
+ it { is_expected.to be_versioned }
6
+
7
+ it "has a default scope" do
8
+ expect(subject.default_scopes).to_not be_empty
9
+ end
10
+
11
+ describe "Versioning", :versioning => true do
12
+ subject { Boolit.create! }
13
+ before { subject.update_attributes!(:name => Faker::Name.name) }
14
+
15
+ it "should have versions" do
16
+ expect(subject.versions.size).to eq(2)
17
+ end
18
+
19
+ it "should be able to be reified and persisted" do
20
+ expect { subject.versions.last.reify.save! }.to_not raise_error
21
+ end
22
+
23
+ context "Instance falls out of default scope" do
24
+ before { subject.update_attributes!(:scoped => false) }
25
+
26
+ it "is NOT scoped" do
27
+ expect(Boolit.first).to be_nil
28
+ end
29
+
30
+ it "should still be able to be reified and persisted" do
31
+ expect { subject.previous_version.save! }.to_not raise_error
32
+ end
33
+
34
+ context "with `nil` attributes on the live instance" do
35
+ before do
36
+ PaperTrail.serializer = CustomJsonSerializer
37
+ subject.update_attributes!(:name => nil)
38
+ subject.update_attributes!(:name => Faker::Name.name)
39
+ end
40
+ after { PaperTrail.serializer = PaperTrail::Serializers::YAML }
41
+
42
+ it "should not overwrite that attribute during the reification process" do
43
+ expect(subject.previous_version.name).to be_nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,80 @@
1
+ if JsonVersion.table_exists?
2
+
3
+ require 'rails_helper'
4
+
5
+ describe JsonVersion, :type => :model do
6
+ it "should include the `VersionConcern` module to get base functionality" do
7
+ expect(JsonVersion).to include(PaperTrail::VersionConcern)
8
+ end
9
+
10
+ describe "Methods" do
11
+ describe "Class" do
12
+
13
+ describe '#where_object' do
14
+ it { expect(JsonVersion).to respond_to(:where_object) }
15
+
16
+ context "invalid arguments" do
17
+ it "should raise an error" do
18
+ expect { JsonVersion.where_object(:foo) }.to raise_error(ArgumentError)
19
+ expect { JsonVersion.where_object([]) }.to raise_error(ArgumentError)
20
+ end
21
+ end
22
+
23
+ context "valid arguments", :versioning => true do
24
+ let(:fruit_names) { %w(apple orange lemon banana lime coconut strawberry blueberry) }
25
+ let(:fruit) { Fruit.new }
26
+ let(:name) { 'pomegranate' }
27
+ let(:color) { Faker::Color.name }
28
+
29
+ before do
30
+ fruit.update_attributes!(:name => name)
31
+ fruit.update_attributes!(:name => fruit_names.sample, :color => color)
32
+ fruit.update_attributes!(:name => fruit_names.sample, :color => Faker::Color.name)
33
+ end
34
+
35
+ it "should be able to locate versions according to their `object` contents" do
36
+ expect(JsonVersion.where_object(:name => name)).to eq([fruit.versions[1]])
37
+ expect(JsonVersion.where_object(:color => color)).to eq([fruit.versions[2]])
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '#where_object_changes' do
43
+ it { expect(JsonVersion).to respond_to(:where_object_changes) }
44
+
45
+ context "invalid arguments" do
46
+ it "should raise an error" do
47
+ expect { JsonVersion.where_object_changes(:foo) }.to raise_error(ArgumentError)
48
+ expect { JsonVersion.where_object_changes([]) }.to raise_error(ArgumentError)
49
+ end
50
+ end
51
+
52
+ context "valid arguments", :versioning => true do
53
+ let(:fruit_names) { %w(apple orange lemon banana lime strawberry blueberry) }
54
+ let(:tropical_fruit_names) { %w(coconut pineapple kiwi mango melon) }
55
+ let(:fruit) { Fruit.new }
56
+ let(:name) { 'pomegranate' }
57
+ let(:color) { Faker::Color.name }
58
+
59
+ before do
60
+ fruit.update_attributes!(:name => name)
61
+ fruit.update_attributes!(:name => tropical_fruit_names.sample, :color => color)
62
+ fruit.update_attributes!(:name => fruit_names.sample, :color => Faker::Color.name)
63
+ end
64
+
65
+ it "should be able to locate versions according to their `object_changes` contents" do
66
+ expect(fruit.versions.where_object_changes(:name => name)).to eq(fruit.versions[0..1])
67
+ expect(fruit.versions.where_object_changes(:color => color)).to eq(fruit.versions[1..2])
68
+ end
69
+
70
+ it "should be able to handle queries for multiple attributes" do
71
+ expect(fruit.versions.where_object_changes(:color => color, :name => name)).to eq([fruit.versions[1]])
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails_helper'
2
+
3
+ describe Thing, :type => :model do
4
+ it { is_expected.to be_versioned }
5
+
6
+ describe "should not store object_changes", :versioning => true do
7
+ let(:thing) { Thing.create(:name =>"pencil") }
8
+
9
+ it { expect(thing.versions.last.object_changes).to be_nil }
10
+ end
11
+ end
@@ -45,13 +45,60 @@ describe PaperTrail::Version, :type => :model do
45
45
  describe "Instance" do
46
46
  subject { PaperTrail::Version.new(attributes) rescue PaperTrail::Version.new }
47
47
 
48
+ describe '#paper_trail_originator' do
49
+ it { is_expected.to respond_to(:paper_trail_originator) }
50
+
51
+ context "No previous versions" do
52
+ specify { expect(subject.previous).to be_nil }
53
+
54
+ it "should return nil" do
55
+ expect(subject.paper_trail_originator).to be_nil
56
+ end
57
+ end
58
+
59
+ context "Has previous version", :versioning => true do
60
+ let(:name) { Faker::Name.name }
61
+ let(:widget) { Widget.create!(name: Faker::Name.name) }
62
+ before do
63
+ widget.versions.first.update_attributes!(:whodunnit => name)
64
+ widget.update_attributes!(name: Faker::Name.first_name)
65
+ end
66
+ subject { widget.versions.last }
67
+
68
+ specify { expect(subject.previous).to be_instance_of(PaperTrail::Version) }
69
+
70
+ it "should return nil" do
71
+ expect(subject.paper_trail_originator).to eq(name)
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#originator" do
77
+ it { is_expected.to respond_to(:originator) }
78
+ let(:warning_msg) do
79
+ "DEPRECATED: use `paper_trail_originator` instead of `originator`." +
80
+ " Support for `originator` will be removed in PaperTrail 4.0"
81
+ end
82
+
83
+ it 'should set the invoke `paper_trail_originator`' do
84
+ is_expected.to receive(:warn)
85
+ is_expected.to receive(:paper_trail_originator)
86
+ subject.originator
87
+ end
88
+
89
+ it 'should display a deprecation warning' do
90
+ is_expected.to receive(:warn).with(warning_msg)
91
+ subject.originator
92
+ end
93
+ end
94
+
48
95
  describe '#terminator' do
49
96
  it { is_expected.to respond_to(:terminator) }
50
97
 
51
98
  let(:attributes) { {:whodunnit => Faker::Name.first_name} }
52
99
 
53
100
  it "is an alias for the `whodunnit` attribute" do
54
- expect(subject.whodunnit).to eq(attributes[:whodunnit])
101
+ expect(subject.terminator).to eq(attributes[:whodunnit])
55
102
  end
56
103
  end
57
104
 
@@ -65,100 +112,128 @@ describe PaperTrail::Version, :type => :model do
65
112
  end
66
113
 
67
114
  describe "Class" do
68
- describe '#where_object' do
69
- it { expect(PaperTrail::Version).to respond_to(:where_object) }
70
-
71
- context "invalid arguments" do
72
- it "should raise an error" do
73
- expect { PaperTrail::Version.where_object(:foo) }.to raise_error(ArgumentError)
74
- expect { PaperTrail::Version.where_object([]) }.to raise_error(ArgumentError)
75
- end
76
- end
77
-
78
- context "valid arguments", :versioning => true do
79
- let(:widget) { Widget.new }
80
- let(:name) { Faker::Name.first_name }
81
- let(:int) { rand(10) + 1 }
115
+ column_overrides = [false]
116
+ if ENV['DB'] == 'postgres' && ::ActiveRecord::VERSION::MAJOR >= 4
117
+ column_overrides << 'json'
118
+ # 'jsonb' column types are only supported for ActiveRecord 4.2+
119
+ column_overrides << 'jsonb' if ::ActiveRecord::VERSION::STRING >= '4.2'
120
+ end
82
121
 
122
+ column_overrides.shuffle.each do |override|
123
+ context "with a #{override || 'text'} column" do
83
124
  before do
84
- widget.update_attributes!(:name => name, :an_integer => int)
85
- widget.update_attributes!(:name => 'foobar', :an_integer => 100)
86
- widget.update_attributes!(:name => Faker::Name.last_name, :an_integer => 15)
87
- end
88
-
89
- context "`serializer == YAML`" do
90
- specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML }
91
-
92
- it "should be able to locate versions according to their `object` contents" do
93
- expect(PaperTrail::Version.where_object(:name => name)).to eq([widget.versions[1]])
94
- expect(PaperTrail::Version.where_object(:an_integer => 100)).to eq([widget.versions[2]])
125
+ if override
126
+ ActiveRecord::Base.connection.execute("SAVEPOINT pgtest;")
127
+ %w[object object_changes].each do |column|
128
+ ActiveRecord::Base.connection.execute("ALTER TABLE versions DROP COLUMN #{column};")
129
+ ActiveRecord::Base.connection.execute("ALTER TABLE versions ADD COLUMN #{column} #{override};")
130
+ end
131
+ PaperTrail::Version.reset_column_information
95
132
  end
96
133
  end
97
-
98
- context "`serializer == JSON`" do
99
- before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON }
100
- specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON }
101
-
102
- it "should be able to locate versions according to their `object` contents" do
103
- expect(PaperTrail::Version.where_object(:name => name)).to eq([widget.versions[1]])
104
- expect(PaperTrail::Version.where_object(:an_integer => 100)).to eq([widget.versions[2]])
134
+ after do
135
+ if override
136
+ ActiveRecord::Base.connection.execute("ROLLBACK TO SAVEPOINT pgtest;")
137
+ PaperTrail::Version.reset_column_information
105
138
  end
106
-
107
- after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML }
108
139
  end
109
- end
110
- end
111
-
112
- describe '#where_object_changes' do
113
- it { expect(PaperTrail::Version).to respond_to(:where_object_changes) }
114
-
115
- context "invalid arguments" do
116
- it "should raise an error" do
117
- expect { PaperTrail::Version.where_object_changes(:foo) }.to raise_error(ArgumentError)
118
- expect { PaperTrail::Version.where_object_changes([]) }.to raise_error(ArgumentError)
119
- end
120
- end
121
140
 
122
- context "valid arguments", :versioning => true do
123
- let(:widget) { Widget.new }
124
- let(:name) { Faker::Name.first_name }
125
- let(:int) { rand(5) + 2 }
141
+ describe '#where_object' do
142
+ it { expect(PaperTrail::Version).to respond_to(:where_object) }
126
143
 
127
- before do
128
- widget.update_attributes!(:name => name, :an_integer => 0)
129
- widget.update_attributes!(:name => 'foobar', :an_integer => 77)
130
- widget.update_attributes!(:name => Faker::Name.last_name, :an_integer => int)
131
- end
132
-
133
- context "`serializer == YAML`" do
134
- specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML }
135
-
136
- it "should be able to locate versions according to their `object_changes` contents" do
137
- expect(widget.versions.where_object_changes(:name => name)).to eq(widget.versions[0..1])
138
- expect(widget.versions.where_object_changes(:an_integer => 77)).to eq(widget.versions[1..2])
139
- expect(widget.versions.where_object_changes(:an_integer => int)).to eq([widget.versions.last])
144
+ context "invalid arguments" do
145
+ it "should raise an error" do
146
+ expect { PaperTrail::Version.where_object(:foo) }.to raise_error(ArgumentError)
147
+ expect { PaperTrail::Version.where_object([]) }.to raise_error(ArgumentError)
148
+ end
140
149
  end
141
150
 
142
- it "should be able to handle queries for multiple attributes" do
143
- expect(widget.versions.where_object_changes(:an_integer => 77, :name => 'foobar')).to eq(widget.versions[1..2])
151
+ context "valid arguments", :versioning => true do
152
+ let(:widget) { Widget.new }
153
+ let(:name) { Faker::Name.first_name }
154
+ let(:int) { rand(10) + 1 }
155
+
156
+ before do
157
+ widget.update_attributes!(:name => name, :an_integer => int)
158
+ widget.update_attributes!(:name => 'foobar', :an_integer => 100)
159
+ widget.update_attributes!(:name => Faker::Name.last_name, :an_integer => 15)
160
+ end
161
+
162
+ context "`serializer == YAML`" do
163
+ specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML }
164
+
165
+ it "should be able to locate versions according to their `object` contents" do
166
+ expect(PaperTrail::Version.where_object(:name => name)).to eq([widget.versions[1]])
167
+ expect(PaperTrail::Version.where_object(:an_integer => 100)).to eq([widget.versions[2]])
168
+ end
169
+ end
170
+
171
+ context "`serializer == JSON`" do
172
+ before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON }
173
+ specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON }
174
+
175
+ it "should be able to locate versions according to their `object` contents" do
176
+ expect(PaperTrail::Version.where_object(:name => name)).to eq([widget.versions[1]])
177
+ expect(PaperTrail::Version.where_object(:an_integer => 100)).to eq([widget.versions[2]])
178
+ end
179
+
180
+ after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML }
181
+ end
144
182
  end
145
183
  end
146
184
 
147
- context "`serializer == JSON`" do
148
- before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON }
149
- specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON }
185
+ describe '#where_object_changes' do
186
+ it { expect(PaperTrail::Version).to respond_to(:where_object_changes) }
150
187
 
151
- it "should be able to locate versions according to their `object_changes` contents" do
152
- expect(widget.versions.where_object_changes(:name => name)).to eq(widget.versions[0..1])
153
- expect(widget.versions.where_object_changes(:an_integer => 77)).to eq(widget.versions[1..2])
154
- expect(widget.versions.where_object_changes(:an_integer => int)).to eq([widget.versions.last])
188
+ context "invalid arguments" do
189
+ it "should raise an error" do
190
+ expect { PaperTrail::Version.where_object_changes(:foo) }.to raise_error(ArgumentError)
191
+ expect { PaperTrail::Version.where_object_changes([]) }.to raise_error(ArgumentError)
192
+ end
155
193
  end
156
194
 
157
- it "should be able to handle queries for multiple attributes" do
158
- expect(widget.versions.where_object_changes(:an_integer => 77, :name => 'foobar')).to eq(widget.versions[1..2])
195
+ context "valid arguments", :versioning => true do
196
+ let(:widget) { Widget.new }
197
+ let(:name) { Faker::Name.first_name }
198
+ let(:int) { rand(5) + 2 }
199
+
200
+ before do
201
+ widget.update_attributes!(:name => name, :an_integer => 0)
202
+ widget.update_attributes!(:name => 'foobar', :an_integer => 77)
203
+ widget.update_attributes!(:name => Faker::Name.last_name, :an_integer => int)
204
+ end
205
+
206
+ context "`serializer == YAML`" do
207
+ specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML }
208
+
209
+ it "should be able to locate versions according to their `object_changes` contents" do
210
+ expect(widget.versions.where_object_changes(:name => name)).to eq(widget.versions[0..1])
211
+ expect(widget.versions.where_object_changes(:an_integer => 77)).to eq(widget.versions[1..2])
212
+ expect(widget.versions.where_object_changes(:an_integer => int)).to eq([widget.versions.last])
213
+ end
214
+
215
+ it "should be able to handle queries for multiple attributes" do
216
+ expect(widget.versions.where_object_changes(:an_integer => 77, :name => 'foobar')).to eq(widget.versions[1..2])
217
+ end
218
+ end
219
+
220
+ context "`serializer == JSON`" do
221
+ before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON }
222
+ specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON }
223
+
224
+ it "should be able to locate versions according to their `object_changes` contents" do
225
+ expect(widget.versions.where_object_changes(:name => name)).to eq(widget.versions[0..1])
226
+ expect(widget.versions.where_object_changes(:an_integer => 77)).to eq(widget.versions[1..2])
227
+ expect(widget.versions.where_object_changes(:an_integer => int)).to eq([widget.versions.last])
228
+ end
229
+
230
+ it "should be able to handle queries for multiple attributes" do
231
+ expect(widget.versions.where_object_changes(:an_integer => 77, :name => 'foobar')).to eq(widget.versions[1..2])
232
+ end
233
+
234
+ after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML }
235
+ end
159
236
  end
160
-
161
- after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML }
162
237
  end
163
238
  end
164
239
  end