paper_trail 4.0.0.beta2 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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