paper_trail_scrapbook 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8a8392755eb1e658bea68423fffc6707e8aa465
4
- data.tar.gz: 755f0f904aa0173175f279ec8832bc959833f0a6
3
+ metadata.gz: beb12cf083c8ab24992a92031c733746855ee546
4
+ data.tar.gz: d086bc0ab29a2c5b35aae138318edfaae355fa84
5
5
  SHA512:
6
- metadata.gz: 9ee0eaa31e43a7f427b4f2f1f224a19207cdc24e6ecc67bb84bea064c95b92fc05644c3779ddc533d38291674f18a3f35b7e1c0d4825d0c8c30557be745e07a3
7
- data.tar.gz: a9d56422cad7eb4da6437077761b032a874c3940288ff3d82f3ac0b81317b7d4dc108b93d627f990378a2fd09092b0c5550ca85b98b62278b8808ba7035e566e
6
+ metadata.gz: 3a3eefe97a1dcca4d1218708959f7eaffb24d0858d37a7b957a6e51e42f74567fb8b9bb487831245a3df3240451370d0edc9d14db33611d8c4c2dcea1ab2ca1a
7
+ data.tar.gz: 828fe0985c42cb90538dd2d9e48416b171996352948f1cc8f55310a2e49a87b8994e08f3abe89d4eb88161d25770b8cb751212b24b4f7217198310f480db323a
data/.rubocop.yml CHANGED
@@ -18,3 +18,13 @@ Security/YAMLLoad:
18
18
  # Limit line length
19
19
  Metrics/LineLength:
20
20
  Max: 150
21
+
22
+ Metrics/BlockLength:
23
+ Exclude:
24
+ - 'spec/**/*'
25
+
26
+ Metrics/ModuleLength:
27
+ Exclude:
28
+ - 'spec/**/*'
29
+
30
+
data/Changelog.md CHANGED
@@ -15,6 +15,21 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
15
15
 
16
16
  ### Fixed
17
17
 
18
+ ## 0.1.7 (2018-01-19) Functional Release
19
+
20
+ ### Breaking Changes
21
+
22
+ - None
23
+
24
+ ### Added
25
+
26
+ - Added #invalid_whodunnit to support whodunnit that are declared but not found
27
+ - Added UserJournal to capture all changes made by a user over time
28
+
29
+ ### Fixed
30
+
31
+ - None
32
+
18
33
  ## 0.1.4 (2017-11-08) Minor Functional Release
19
34
 
20
35
  ### Breaking Changes
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec # DRY! No need to repeat the gem deps here
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paper_trail_scrapbook (0.1.6)
4
+ paper_trail_scrapbook (0.1.7)
5
5
  activerecord (>= 4.0, < 5.2)
6
6
  adamantium
7
7
  concord
8
8
  paper_trail (>= 5.2)
9
9
 
10
10
  GEM
11
- remote: http://rubygems.org/
11
+ remote: https://rubygems.org/
12
12
  specs:
13
13
  actionpack (5.1.2)
14
14
  actionview (= 5.1.2)
@@ -60,16 +60,16 @@ GEM
60
60
  memoizable (0.4.2)
61
61
  thread_safe (~> 0.3, >= 0.3.1)
62
62
  method_source (0.8.2)
63
- mini_portile2 (2.2.0)
63
+ mini_portile2 (2.3.0)
64
64
  minitest (5.10.2)
65
- nokogiri (1.8.0)
66
- mini_portile2 (~> 2.2.0)
67
- paper_trail (7.0.3)
68
- activerecord (>= 4.0, < 5.2)
65
+ nokogiri (1.8.1)
66
+ mini_portile2 (~> 2.3.0)
67
+ paper_trail (8.1.2)
68
+ activerecord (>= 4.2, < 5.2)
69
69
  request_store (~> 1.1)
70
- parallel (1.11.2)
71
- parser (2.4.0.0)
72
- ast (~> 2.2)
70
+ parallel (1.12.1)
71
+ parser (2.4.0.2)
72
+ ast (~> 2.3)
73
73
  powerpack (0.1.1)
74
74
  rack (2.0.3)
75
75
  rack-test (0.6.3)
@@ -85,37 +85,37 @@ GEM
85
85
  method_source
86
86
  rake (>= 0.8.7)
87
87
  thor (>= 0.18.1, < 2.0)
88
- rainbow (2.2.2)
89
- rake
90
- rake (12.0.0)
91
- request_store (1.3.2)
92
- rspec-core (3.6.0)
93
- rspec-support (~> 3.6.0)
94
- rspec-expectations (3.6.0)
88
+ rainbow (3.0.0)
89
+ rake (12.3.0)
90
+ request_store (1.4.0)
91
+ rack (>= 1.4)
92
+ rspec-core (3.7.1)
93
+ rspec-support (~> 3.7.0)
94
+ rspec-expectations (3.7.0)
95
95
  diff-lcs (>= 1.2.0, < 2.0)
96
- rspec-support (~> 3.6.0)
97
- rspec-mocks (3.6.0)
96
+ rspec-support (~> 3.7.0)
97
+ rspec-mocks (3.7.0)
98
98
  diff-lcs (>= 1.2.0, < 2.0)
99
- rspec-support (~> 3.6.0)
100
- rspec-rails (3.6.0)
99
+ rspec-support (~> 3.7.0)
100
+ rspec-rails (3.7.2)
101
101
  actionpack (>= 3.0)
102
102
  activesupport (>= 3.0)
103
103
  railties (>= 3.0)
104
- rspec-core (~> 3.6.0)
105
- rspec-expectations (~> 3.6.0)
106
- rspec-mocks (~> 3.6.0)
107
- rspec-support (~> 3.6.0)
108
- rspec-support (3.6.0)
109
- rubocop (0.49.1)
104
+ rspec-core (~> 3.7.0)
105
+ rspec-expectations (~> 3.7.0)
106
+ rspec-mocks (~> 3.7.0)
107
+ rspec-support (~> 3.7.0)
108
+ rspec-support (3.7.0)
109
+ rubocop (0.52.1)
110
110
  parallel (~> 1.10)
111
- parser (>= 2.3.3.1, < 3.0)
111
+ parser (>= 2.4.0.2, < 3.0)
112
112
  powerpack (~> 0.1)
113
- rainbow (>= 1.99.1, < 3.0)
113
+ rainbow (>= 2.2.2, < 4.0)
114
114
  ruby-progressbar (~> 1.7)
115
115
  unicode-display_width (~> 1.0, >= 1.0.1)
116
- rubocop-rspec (0.18.1)
117
- rubocop (~> 0.18)
118
- ruby-progressbar (1.8.1)
116
+ rubocop-rspec (1.22.1)
117
+ rubocop (>= 0.52.1)
118
+ ruby-progressbar (1.9.0)
119
119
  simplecov (0.14.1)
120
120
  docile (~> 1.1.0)
121
121
  json (>= 1.8, < 3)
@@ -139,7 +139,7 @@ DEPENDENCIES
139
139
  paper_trail_scrapbook!
140
140
  railties (>= 4.0, < 5.2)
141
141
  rake (~> 12.0)
142
- rspec-rails (~> 3.6)
142
+ rspec-rails (~> 3.7)
143
143
  rubocop
144
144
  rubocop-rspec
145
145
  simplecov
@@ -147,4 +147,4 @@ DEPENDENCIES
147
147
  timecop (~> 0.8.0)
148
148
 
149
149
  BUNDLED WITH
150
- 1.15.4
150
+ 1.16.1
@@ -6,8 +6,11 @@ require 'pathname'
6
6
  require 'paper_trail_scrapbook/config'
7
7
  require 'paper_trail_scrapbook/chapter'
8
8
  require 'paper_trail_scrapbook/changes'
9
+ require 'paper_trail_scrapbook/journal_entry'
9
10
  require 'paper_trail_scrapbook/life_history'
11
+ require 'paper_trail_scrapbook/user_journal'
10
12
  require 'paper_trail_scrapbook/version'
13
+ require 'paper_trail_scrapbook/version_helpers'
11
14
 
12
15
  # Library namespace
13
16
  #
@@ -36,7 +36,7 @@ module PaperTrailScrapbook
36
36
  old, new = v
37
37
  return if old.nil? && (new.nil? || new.eql?(''))
38
38
 
39
- "#{BULLET} #{k.gsub('_',' ')}: #{detailed_analysis(k, new, old)}"
39
+ "#{BULLET} #{k.tr('_', ' ')}: #{detailed_analysis(k, new, old)}"
40
40
  end
41
41
 
42
42
  def detailed_analysis(k, new, old)
@@ -62,7 +62,7 @@ module PaperTrailScrapbook
62
62
 
63
63
  begin
64
64
  assoc[key].find(value).to_s.to_s + "[#{value}]"
65
- rescue
65
+ rescue StandardError
66
66
  "*not found*[#{value}]"
67
67
  end
68
68
  end
@@ -72,7 +72,7 @@ module PaperTrailScrapbook
72
72
  return direct_class if direct_class && !direct_class.is_a?(String)
73
73
 
74
74
  Object.const_get((direct_class || name.to_s).classify)
75
- rescue
75
+ rescue StandardError
76
76
  Object.const_set(name.to_s.classify, Class.new)
77
77
  end
78
78
 
@@ -1,3 +1,5 @@
1
+ require_relative 'version_helpers'
2
+
1
3
  module PaperTrailScrapbook
2
4
  # Class Chapter provides single version history analysis
3
5
  #
@@ -6,8 +8,7 @@ module PaperTrailScrapbook
6
8
  class Chapter
7
9
  include Concord.new(:version)
8
10
  include Adamantium::Flat
9
-
10
- delegate :event, to: :version
11
+ include PaperTrailScrapbook::VersionHelpers
11
12
 
12
13
  # Single version historical analysis
13
14
  #
@@ -25,51 +26,5 @@ module PaperTrailScrapbook
25
26
  def preface
26
27
  "On #{whenn}, #{who} #{kind} the following #{model} info:".squeeze(' ')
27
28
  end
28
-
29
- def model
30
- version.item_type
31
- end
32
-
33
- def create?
34
- event.eql?('create')
35
- end
36
-
37
- def changes
38
- Changes.new(version).change_log
39
- end
40
-
41
- def who
42
- author = version.version_author
43
- if author
44
- if whodunnit_class
45
- if whodunnit_class.method_defined?(:to_whodunnit)
46
- whodunnit_class.find(author).to_whodunnit
47
- else
48
- whodunnit_class.find(author).to_s
49
- end
50
- else
51
- author
52
- end
53
- else
54
- config.unknown_whodunnit
55
- end
56
- end
57
-
58
- def whodunnit_class
59
- config.whodunnit_class
60
- end
61
-
62
- def config
63
- PaperTrailScrapbook.config
64
- end
65
-
66
- def whenn
67
- version.created_at.strftime(config.time_format)
68
- end
69
-
70
- def kind
71
- config.events[event] ||
72
- raise(ArgumentError, "incorrect event:#{event}")
73
- end
74
29
  end
75
30
  end
@@ -19,6 +19,7 @@ module PaperTrailScrapbook
19
19
  :scrub_columns,
20
20
  :drop_id_suffix,
21
21
  :unknown_whodunnit,
22
+ :invalid_whodunnit,
22
23
  :filter_non_changes
23
24
 
24
25
  def initialize
@@ -27,6 +28,7 @@ module PaperTrailScrapbook
27
28
  @events = DEFAULT_EVENTS
28
29
  @scrub_columns = SCRUB_COLUMNS
29
30
  @unknown_whodunnit = UNKNOWN_WHODUNNIT
31
+ @invalid_whodunnit = proc { |w| "*missing (#{w})*" }
30
32
  @drop_id_suffix = true
31
33
  @filter_non_changes = true
32
34
  end
@@ -0,0 +1,32 @@
1
+ require_relative 'version_helpers'
2
+
3
+ module PaperTrailScrapbook
4
+ # Class JournalEntry provides single version history analysis
5
+ #
6
+ # @author Jason Dinsmore <jason@dinjas.com>
7
+ #
8
+ class JournalEntry
9
+ include Concord.new(:version)
10
+ include Adamantium::Flat
11
+ include PaperTrailScrapbook::VersionHelpers
12
+
13
+ delegate :event, to: :version
14
+
15
+ # Single version historical analysis
16
+ #
17
+ # @return [String] Human readable description of changes
18
+ #
19
+ def story
20
+ updates = changes
21
+ return unless create? || updates.present? || !config.filter_non_changes
22
+
23
+ "#{preface}\n#{updates}"
24
+ end
25
+
26
+ private
27
+
28
+ def preface
29
+ "On #{whenn}, #{kind} #{model}[#{model_id}]:".squeeze(' ')
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ module PaperTrailScrapbook
2
+ # Class UserJournal provides history for a user, optionally scoped by
3
+ # object class and/or date range
4
+ #
5
+ # @author Jason Dinsmore <jason@dinjas.com>
6
+ #
7
+ class UserJournal
8
+ include Adamantium::Flat
9
+
10
+ def initialize(user, opts = {})
11
+ @user = user
12
+ @user_id = String(user.id)
13
+ @options = opts
14
+ @starts = options[:start] || Time.at(0).in_time_zone
15
+ @ends = options[:end] || Time.current.in_time_zone
16
+
17
+ @versions = PaperTrail::Version.where(query_params)
18
+ end
19
+
20
+ # Retries textual historical analysis of versions
21
+ #
22
+ # @return [String] analyzed versions
23
+ #
24
+ def story
25
+ s = versions.map do |v|
26
+ JournalEntry.new(v).story
27
+ end.compact.join("\n\n")
28
+
29
+ "#{preface}#{s.presence || 'No history'}"
30
+ end
31
+
32
+ private
33
+
34
+ def preface
35
+ "Between #{when_range}, #{user} made the following #{what} changes:\n\n"
36
+ .squeeze(' ')
37
+ end
38
+
39
+ def query_params
40
+ params = { whodunnit: user_id }
41
+ return params if options.empty?
42
+
43
+ params.merge(item_type: what,
44
+ created_at: starts..ends)
45
+ .delete_if { |_, v| v.presence.nil? }
46
+ end
47
+
48
+ def time_format
49
+ PaperTrailScrapbook.config.time_format
50
+ end
51
+
52
+ def what
53
+ String(options.fetch(:klass, nil)).presence
54
+ end
55
+
56
+ def when_range
57
+ "#{starts.strftime(time_format)} and #{ends.strftime(time_format)}"
58
+ end
59
+
60
+ attr_reader :user, :user_id, :versions, :options, :starts, :ends
61
+ end
62
+ end
@@ -1,4 +1,4 @@
1
1
  module PaperTrailScrapbook
2
2
  # Current version
3
- VERSION = '0.1.6'.freeze
3
+ VERSION = '0.1.7'.freeze
4
4
  end
@@ -0,0 +1,60 @@
1
+ module PaperTrailScrapbook
2
+ # Module VersionHelpers provides methods for extracting common information
3
+ # from a version or PaperTrailScrapbook config
4
+ module VersionHelpers
5
+ delegate :event, to: :version
6
+
7
+ def model
8
+ version.item_type
9
+ end
10
+
11
+ def model_id
12
+ version.item_id
13
+ end
14
+
15
+ def create?
16
+ event.eql?('create')
17
+ end
18
+
19
+ def changes
20
+ Changes.new(version).change_log
21
+ end
22
+
23
+ def config
24
+ PaperTrailScrapbook.config
25
+ end
26
+
27
+ def whenn
28
+ version.created_at.strftime(config.time_format)
29
+ end
30
+
31
+ def kind
32
+ config.events[event] ||
33
+ raise(ArgumentError, "incorrect event:#{event}")
34
+ end
35
+
36
+ def who
37
+ author = version.version_author
38
+ return config.unknown_whodunnit unless author
39
+ return author unless whodunnit_class
40
+
41
+ whodunnit_instance(author)
42
+ end
43
+
44
+ def whodunnit_class
45
+ config.whodunnit_class
46
+ end
47
+
48
+ def whodunnit_instance(author)
49
+ instance = begin
50
+ whodunnit_class.find(author)
51
+ rescue StandardError
52
+ config.invalid_whodunnit.call(author)
53
+ end
54
+
55
+ return instance.to_whodunnit if instance.respond_to?(:to_whodunnit)
56
+
57
+ instance.to_s
58
+ end
59
+ end
60
+ end
@@ -21,18 +21,18 @@ Gem::Specification.new do |gem|
21
21
  gem.add_dependency 'concord'
22
22
  gem.add_dependency 'paper_trail', '>= 5.2'
23
23
 
24
- gem.add_development_dependency 'rake', '~> 12.0'
25
24
  gem.add_development_dependency 'ffaker', '~> 2.5'
25
+ gem.add_development_dependency 'rake', '~> 12.0'
26
26
 
27
27
  # Why `railties`? Possibly used by `spec/dummy_app` boot up?
28
28
  gem.add_development_dependency 'railties', ['>= 4.0', '< 5.2']
29
29
 
30
- gem.add_development_dependency 'rspec-rails', '~> 3.6'
30
+ gem.add_development_dependency 'codeclimate-test-reporter', '~> 1.0.7'
31
31
  gem.add_development_dependency 'database_cleaner', '~> 1.2'
32
+ gem.add_development_dependency 'rspec-rails', '~> 3.7'
32
33
  gem.add_development_dependency 'rubocop'
33
34
  gem.add_development_dependency 'rubocop-rspec'
34
- gem.add_development_dependency 'timecop', '~> 0.8.0'
35
- gem.add_development_dependency 'sqlite3', '~> 1.3.13'
36
35
  gem.add_development_dependency 'simplecov'
37
- gem.add_development_dependency 'codeclimate-test-reporter', '~> 1.0.7'
36
+ gem.add_development_dependency 'sqlite3', '~> 1.3.13'
37
+ gem.add_development_dependency 'timecop', '~> 0.8.0'
38
38
  end
Binary file
Binary file
@@ -1,5 +1,3 @@
1
1
  class Fruit < ActiveRecord::Base
2
- if ENV['DB'] == 'postgres' || JsonVersion.table_exists?
3
- has_paper_trail class_name: 'JsonVersion'
4
- end
2
+ has_paper_trail class_name: 'JsonVersion' if ENV['DB'] == 'postgres' || JsonVersion.table_exists?
5
3
  end
@@ -26,12 +26,8 @@ module Dummy
26
26
  # in rails 5. Oh, how fickle are the gods.
27
27
  if ActiveRecord.respond_to?(:gem_version)
28
28
  v = ActiveRecord.gem_version
29
- if v >= Gem::Version.new('4.2') && v < Gem::Version.new('5.0.0.beta1')
30
- config.active_record.raise_in_transactional_callbacks = true
31
- end
32
- if v >= Gem::Version.new('5.0.0.beta1')
33
- config.active_record.time_zone_aware_types = [:datetime]
34
- end
29
+ config.active_record.raise_in_transactional_callbacks = true if v >= Gem::Version.new('4.2') && v < Gem::Version.new('5.0.0.beta1')
30
+ config.active_record.time_zone_aware_types = [:datetime] if v >= Gem::Version.new('5.0.0.beta1')
35
31
  end
36
32
  end
37
33
  end
@@ -4,13 +4,11 @@ module PaperTrailScrapbook
4
4
  ::RSpec.describe LifeHistory do
5
5
  before do
6
6
  PaperTrailScrapbook.config.whodunnit_class = Person
7
-
8
- p = Person.new(name: 'The Tim Man')
9
- p.save!
10
-
11
- PaperTrail.whodunnit = p.id
7
+ PaperTrail.whodunnit = person.id
12
8
  end
13
9
 
10
+ let(:person) { Person.create(name: 'The Tim Man') }
11
+
14
12
  let(:book) do
15
13
  b = Book.new(title: 'How the Grinch stole Xmas')
16
14
  b.save!
@@ -57,7 +55,35 @@ module PaperTrailScrapbook
57
55
  target.book = nil
58
56
  target.save!
59
57
 
60
- expect(subject).to match(/How the Grinch stole Xmas\[1\] was \*removed\*/)
58
+ expect(subject)
59
+ .to match(/How the Grinch stole Xmas\[1\] was \*removed\*/)
60
+ end
61
+
62
+ context 'it handles missing whodunnit record' do
63
+ it 'provides a whole story with missing whodunnit record' do
64
+ target
65
+ pid = person.id
66
+ person.destroy
67
+
68
+ expect(subject)
69
+ .to match(/\*missing \(#{pid}\)\* created the following/)
70
+ end
71
+ end
72
+
73
+ context 'with overridden invalid whodunnit handler' do
74
+ it 'allows for a custom invalid_whodunnit handler' do
75
+ config = PaperTrailScrapbook.config
76
+ handler = config.invalid_whodunnit
77
+ config.invalid_whodunnit = proc { |w| "*WHO (#{w})*" }
78
+
79
+ target
80
+ pid = person.id
81
+ person.destroy
82
+
83
+ expect(subject).to match(/\*WHO \(#{pid}\)\* created the following/)
84
+
85
+ config.invalid_whodunnit = handler
86
+ end
61
87
  end
62
88
  end
63
89
  end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ module PaperTrailScrapbook
4
+ RSpec.describe UserJournal do
5
+ before do
6
+ PaperTrailScrapbook.config.whodunnit_class = Person
7
+ PaperTrail.whodunnit = person.id
8
+
9
+ a_ship
10
+ end
11
+
12
+ let(:name) { 'The Tim Man' }
13
+ let(:title) { 'How the Grinch stole Xmas' }
14
+ let(:a_name) { 'Dr. Seuss' }
15
+ let(:changes) { 'made the following changes:' }
16
+ let(:b_changes) { 'made the following Book changes:' }
17
+
18
+ let(:book) { Book.create!(title: title) }
19
+ let(:author) { Person.create!(name: a_name) }
20
+ let(:a_ship) { Authorship.create!(book: book, author: author) }
21
+ let(:person) do
22
+ who = PaperTrail.whodunnit
23
+ PaperTrail.whodunnit = nil
24
+ p = Person.new(name: name)
25
+ p.save!
26
+ PaperTrail.whodunnit = who
27
+ p
28
+ end
29
+ let(:format) { PaperTrailScrapbook.config.time_format }
30
+ let(:object) { described_class.new(person, {}) }
31
+ let(:subject) { object.story }
32
+ let(:f_starts) { starts.strftime(format).squeeze(' ') }
33
+ let(:f_ends) { ends.strftime(format).squeeze(' ') }
34
+
35
+ describe '#story' do
36
+ context 'with a provided user' do
37
+ it 'provides a whole story' do
38
+ expect(subject)
39
+ .to match(/Between .* and .*, #{name} #{changes}/)
40
+ expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
41
+ expect(subject).to match(/ • title: #{title}/)
42
+ expect(subject).to match(/On .*, created Person\[#{author.id}\]:/)
43
+ expect(subject).to match(/ • name: #{a_name}/)
44
+ expect(subject).to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
45
+ expect(subject).to match(/ • book: #{title}\[#{book.id}\]/)
46
+ expect(subject).to match(/ • author: Dr. Seuss\[#{author.id}\]/)
47
+ end
48
+
49
+ it 'provides a whole story missing to_s content' do
50
+ book.title = nil
51
+ book.save!
52
+
53
+ expect(subject).to match(/On .*, updated Book\[#{book.id}\]:/)
54
+ expect(subject).to match(/ • title: #{title} was \*removed\*/)
55
+ end
56
+ end
57
+
58
+ context 'with missing references' do
59
+ it 'provides a whole story missing reference' do
60
+ book.destroy
61
+
62
+ expect(subject).to match(/book: \*not found\*\[/)
63
+ end
64
+
65
+ it 'provides a whole story missing reference' do
66
+ a_ship.book = nil
67
+ a_ship.save!
68
+
69
+ expect(subject).to match(/#{title}\[1\] was \*removed\*/)
70
+ end
71
+ end
72
+
73
+ context 'with a provided class' do
74
+ let(:object) { described_class.new(person, klass: Book) }
75
+
76
+ it 'provides a story for provided class' do
77
+ expect(subject)
78
+ .to match(/Between .* and .*, #{name} #{b_changes}/)
79
+ expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
80
+ expect(subject).to match(/ • title: #{title}/)
81
+ expect(subject).not_to match(/On .*, created Person\[#{author.id}\]:/)
82
+ expect(subject).not_to match(/ • name: #{a_name}/)
83
+ expect(subject)
84
+ .not_to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
85
+ end
86
+ end
87
+
88
+ context 'with start time and class but no end time' do
89
+ let(:object) { described_class.new(person, klass: Book, start: starts) }
90
+ let(:starts) { Time.current.advance(minutes: -5) }
91
+
92
+ it 'provides a story with start time' do
93
+ expect(subject)
94
+ .to match(/Between #{f_starts} and .*, #{name} #{b_changes}/)
95
+ expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
96
+ expect(subject).to match(/ • title: #{title}/)
97
+ expect(subject).not_to match(/On .*, created Person\[#{author.id}\]:/)
98
+ expect(subject).not_to match(/ • name: #{a_name}/)
99
+ expect(subject)
100
+ .not_to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
101
+ end
102
+ end
103
+
104
+ context 'with end time but no start time and no class' do
105
+ let(:ends) { Time.current.advance(minutes: 5) }
106
+ let(:object) { described_class.new(person, end: ends) }
107
+
108
+ it 'provides a story with end time in the future' do
109
+ expect(subject)
110
+ .to match(/Between .* and #{f_ends}, #{name} #{changes}/)
111
+ expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
112
+ expect(subject).to match(/ • title: #{title}/)
113
+ expect(subject).to match(/On .*, created Person\[#{author.id}\]:/)
114
+ expect(subject).to match(/ • name: #{a_name}/)
115
+ expect(subject).to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
116
+ end
117
+
118
+ describe 'with end time in past' do
119
+ let(:ends) { Time.current.advance(minutes: -5) }
120
+
121
+ it 'provides a story with end time in the past' do
122
+ expect(subject).not_to match(/#{name} created the following Person/)
123
+ expect(subject).not_to match(/#{name} created the following Book/)
124
+ expect(subject).not_to match(/#{title}/)
125
+ expect(subject).not_to match(/#{name} created the following Author/)
126
+ end
127
+ end
128
+ end
129
+
130
+ context 'with start and end times but no class' do
131
+ let(:starts) { Time.current.advance(minutes: -4) }
132
+ let(:ends) { starts.advance(hours: 1) }
133
+ let(:object) { described_class.new(person, start: starts, end: ends) }
134
+
135
+ it 'provides a story with start and end times' do
136
+ expect(subject)
137
+ .to match(/Between .* and .*, #{name} #{changes}/)
138
+ expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
139
+ expect(subject).to match(/ • title: #{title}/)
140
+ expect(subject).to match(/On .*, created Person\[#{author.id}\]:/)
141
+ expect(subject).to match(/ • name: #{a_name}/)
142
+ expect(subject).to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
143
+ expect(subject).to match(/ • book: #{title}\[#{book.id}\]/)
144
+ end
145
+ end
146
+
147
+ context 'with start time, end time, and a class' do
148
+ let(:starts) { Time.current.advance(minutes: -4) }
149
+ let(:ends) { starts.advance(hours: 1) }
150
+ let(:object) do
151
+ described_class.new(person, klass: Book, start: starts, end: ends)
152
+ end
153
+
154
+ describe 'when time range covers current time' do
155
+ it 'provides a story for provided class with start and end times' do
156
+ expect(subject)
157
+ .to match(/Between #{f_starts} and #{f_ends}, #{name} #{b_changes}/)
158
+ expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
159
+ expect(subject).to match(/ • title: #{title}/)
160
+ expect(subject)
161
+ .not_to match(/On .*, created Person\[#{author.id}\]:/)
162
+ expect(subject).not_to match(/ • name: #{a_name}/)
163
+ expect(subject)
164
+ .not_to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
165
+ end
166
+ end
167
+
168
+ describe 'when time range is completely in the future' do
169
+ let(:starts) { Time.current.advance(minutes: 4) }
170
+
171
+ it 'provides a story' do
172
+ expect(subject).to eql("Between #{f_starts} and #{f_ends}, #{name}"\
173
+ " #{b_changes}\n\nNo history".squeeze(' '))
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ module PaperTrailScrapbook
5
+ RSpec.describe VersionHelpers do
6
+ let(:person) { Person.create!(name: 'The Tim Man') }
7
+ let(:book) { Book.create!(title: 'How the Grinch stole Xmas') }
8
+ let(:version) do
9
+ OpenStruct.new(event: 'create',
10
+ item_type: 'Book',
11
+ item_id: book.id,
12
+ created_at: Time.current,
13
+ version_author: person.id)
14
+ end
15
+ let(:config) { PaperTrailScrapbook.config }
16
+ let(:subject) { JournalEntry.new(version) }
17
+
18
+ before do
19
+ PaperTrailScrapbook.config.whodunnit_class = Person
20
+ PaperTrail.whodunnit = person.id
21
+ end
22
+
23
+ describe '#model' do
24
+ it 'returns expected result' do
25
+ expect(subject.model).to eql('Book')
26
+ end
27
+ end
28
+
29
+ describe '#model_id' do
30
+ it 'returns expected result' do
31
+ expect(subject.model_id).to equal(book.id)
32
+ end
33
+ end
34
+
35
+ describe '#create?' do
36
+ it 'returns true if create event' do
37
+ expect(subject.create?).to be true
38
+ end
39
+
40
+ it 'returns false if not create event' do
41
+ version.event = 'update'
42
+
43
+ expect(subject.create?).to be false
44
+ end
45
+ end
46
+
47
+ describe '#changes' do
48
+ it 'returns changes' do
49
+ expect(subject.changes).to eql('')
50
+ end
51
+ end
52
+
53
+ describe '#config' do
54
+ it 'returns instance of Config' do
55
+ expect(subject.config).to be_an_instance_of(PaperTrailScrapbook::Config)
56
+ end
57
+ end
58
+
59
+ describe '#whenn' do
60
+ it 'returns version created_at' do
61
+ expect(subject.whenn)
62
+ .to eql(version.created_at.strftime(config.time_format))
63
+ end
64
+ end
65
+
66
+ describe '#kind' do
67
+ it 'returns expected value' do
68
+ expect(subject.kind).to eql('created')
69
+ end
70
+ end
71
+
72
+ describe '#who' do
73
+ it 'returns expected value' do
74
+ expect(subject.who).to eql(person.name)
75
+ end
76
+ end
77
+
78
+ describe '#whodunnit_class' do
79
+ it 'returns expected value' do
80
+ expect(subject.whodunnit_class).to eql(Person)
81
+ end
82
+ end
83
+ end
84
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paper_trail_scrapbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timothy Chambers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-12 00:00:00.000000000 Z
11
+ date: 2018-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -73,33 +73,33 @@ dependencies:
73
73
  - !ruby/object:Gem::Version
74
74
  version: '5.2'
75
75
  - !ruby/object:Gem::Dependency
76
- name: rake
76
+ name: ffaker
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '12.0'
81
+ version: '2.5'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '12.0'
88
+ version: '2.5'
89
89
  - !ruby/object:Gem::Dependency
90
- name: ffaker
90
+ name: rake
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '2.5'
95
+ version: '12.0'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '2.5'
102
+ version: '12.0'
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: railties
105
105
  requirement: !ruby/object:Gem::Requirement
@@ -121,19 +121,19 @@ dependencies:
121
121
  - !ruby/object:Gem::Version
122
122
  version: '5.2'
123
123
  - !ruby/object:Gem::Dependency
124
- name: rspec-rails
124
+ name: codeclimate-test-reporter
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: '3.6'
129
+ version: 1.0.7
130
130
  type: :development
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: '3.6'
136
+ version: 1.0.7
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: database_cleaner
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -148,6 +148,20 @@ dependencies:
148
148
  - - "~>"
149
149
  - !ruby/object:Gem::Version
150
150
  version: '1.2'
151
+ - !ruby/object:Gem::Dependency
152
+ name: rspec-rails
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '3.7'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '3.7'
151
165
  - !ruby/object:Gem::Dependency
152
166
  name: rubocop
153
167
  requirement: !ruby/object:Gem::Requirement
@@ -177,19 +191,19 @@ dependencies:
177
191
  - !ruby/object:Gem::Version
178
192
  version: '0'
179
193
  - !ruby/object:Gem::Dependency
180
- name: timecop
194
+ name: simplecov
181
195
  requirement: !ruby/object:Gem::Requirement
182
196
  requirements:
183
- - - "~>"
197
+ - - ">="
184
198
  - !ruby/object:Gem::Version
185
- version: 0.8.0
199
+ version: '0'
186
200
  type: :development
187
201
  prerelease: false
188
202
  version_requirements: !ruby/object:Gem::Requirement
189
203
  requirements:
190
- - - "~>"
204
+ - - ">="
191
205
  - !ruby/object:Gem::Version
192
- version: 0.8.0
206
+ version: '0'
193
207
  - !ruby/object:Gem::Dependency
194
208
  name: sqlite3
195
209
  requirement: !ruby/object:Gem::Requirement
@@ -205,33 +219,19 @@ dependencies:
205
219
  - !ruby/object:Gem::Version
206
220
  version: 1.3.13
207
221
  - !ruby/object:Gem::Dependency
208
- name: simplecov
209
- requirement: !ruby/object:Gem::Requirement
210
- requirements:
211
- - - ">="
212
- - !ruby/object:Gem::Version
213
- version: '0'
214
- type: :development
215
- prerelease: false
216
- version_requirements: !ruby/object:Gem::Requirement
217
- requirements:
218
- - - ">="
219
- - !ruby/object:Gem::Version
220
- version: '0'
221
- - !ruby/object:Gem::Dependency
222
- name: codeclimate-test-reporter
222
+ name: timecop
223
223
  requirement: !ruby/object:Gem::Requirement
224
224
  requirements:
225
225
  - - "~>"
226
226
  - !ruby/object:Gem::Version
227
- version: 1.0.7
227
+ version: 0.8.0
228
228
  type: :development
229
229
  prerelease: false
230
230
  version_requirements: !ruby/object:Gem::Requirement
231
231
  requirements:
232
232
  - - "~>"
233
233
  - !ruby/object:Gem::Version
234
- version: 1.0.7
234
+ version: 0.8.0
235
235
  description: Human Readable Change Log for Paper Trail'd data
236
236
  email: tim@possibilogy.com
237
237
  executables: []
@@ -255,10 +255,15 @@ files:
255
255
  - lib/paper_trail_scrapbook/changes.rb
256
256
  - lib/paper_trail_scrapbook/chapter.rb
257
257
  - lib/paper_trail_scrapbook/config.rb
258
+ - lib/paper_trail_scrapbook/journal_entry.rb
258
259
  - lib/paper_trail_scrapbook/life_history.rb
260
+ - lib/paper_trail_scrapbook/user_journal.rb
259
261
  - lib/paper_trail_scrapbook/version.rb
262
+ - lib/paper_trail_scrapbook/version_helpers.rb
260
263
  - paper_trail_scrapbook.gemspec
261
264
  - pkg/paper_trail_scrapbook-0.0.2.gem
265
+ - pkg/paper_trail_scrapbook-0.1.6.gem
266
+ - pkg/paper_trail_scrapbook-0.1.7.gem
262
267
  - spec/dummy_app/Rakefile
263
268
  - spec/dummy_app/app/models/animal.rb
264
269
  - spec/dummy_app/app/models/article.rb
@@ -336,6 +341,8 @@ files:
336
341
  - spec/paper_trail_scrapbook/chapter_spec.rb
337
342
  - spec/paper_trail_scrapbook/config_spec.rb
338
343
  - spec/paper_trail_scrapbook/life_history_spec.rb
344
+ - spec/paper_trail_scrapbook/user_journal_spec.rb
345
+ - spec/paper_trail_scrapbook/version_helpers_spec.rb
339
346
  - spec/paper_trail_scrapbook/version_spec.rb
340
347
  - spec/paper_trail_scrapbook_spec.rb
341
348
  - spec/spec_helper.rb