changes_are_logged 0.0.2
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 +7 -0
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +49 -0
- data/Rakefile +1 -0
- data/changes_are_logged.gemspec +25 -0
- data/lib/changes_are_logged.rb +62 -0
- data/lib/changes_are_logged/change_log.rb +63 -0
- data/lib/changes_are_logged/version.rb +3 -0
- data/spec/change_log_spec.rb +63 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/database_helpers.rb +39 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0c0a48f3d22566d6b3ce0e11581f26e349927a31
|
4
|
+
data.tar.gz: 9f236ae8f678bdc46e2796b203d2de2485c6c466
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e8c63536e1e473aaa7edab32adfcc79c5ba25ff23a0851c1414c9445e8cbe29f57eee6bc265c4faf0675b92f70918ef7b7cda777865fd2da4bfac3761a7afb41
|
7
|
+
data.tar.gz: 18e39e2afe1ccca46ff6f4c39ef76471a99d68bb23f1efd92675ad11c04fd1cf04a559ad5a41be67ae193b21b1de1b319be6664e56e1f9bcc7d0e30980a1eaca
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
Changes Are Logged
|
2
|
+
==================
|
3
|
+
|
4
|
+
This is a simple way to record when a ActiveRecord model is modified.
|
5
|
+
|
6
|
+
USAGE
|
7
|
+
=====
|
8
|
+
|
9
|
+
Hook changes_are_logged into your ActiveRecord model:
|
10
|
+
|
11
|
+
class Game < ActiveRecord::Base
|
12
|
+
include ChangesAreLogged
|
13
|
+
after_initialize :automatically_log_changes
|
14
|
+
end
|
15
|
+
|
16
|
+
Then anytime that object is modified, a new entry in the change_logs table will be added:
|
17
|
+
<pre><code>
|
18
|
+
> game.change_logs
|
19
|
+
=> []
|
20
|
+
> game.update_attribute(:name, 'Wombats Rule')
|
21
|
+
=> true
|
22
|
+
> game.change_logs
|
23
|
+
=> [#<ChangeLog id: 442, target_id: 65, target_type: "Game", changes_logged: {"name"=>["Old Name", "Wombats Rule"]}, comments: nil, user_id: 68, created_at: "2011-11-16 00:01:04">]
|
24
|
+
>
|
25
|
+
</code></pre>
|
26
|
+
|
27
|
+
ASSUMPTIONS
|
28
|
+
===========
|
29
|
+
|
30
|
+
The method 'current_user' is defined by your app, and returns the User that is currently logged in.
|
31
|
+
|
32
|
+
TODO
|
33
|
+
====
|
34
|
+
|
35
|
+
Add migration for creation of the change_logs table. For now, here is the schema:
|
36
|
+
<pre><code>
|
37
|
+
mysql> desc change_logs;
|
38
|
+
+----------------+--------------+------+-----+---------+----------------+
|
39
|
+
| Field | Type | Null | Key | Default | Extra |
|
40
|
+
+----------------+--------------+------+-----+---------+----------------+
|
41
|
+
| id | int(11) | NO | PRI | NULL | auto_increment |
|
42
|
+
| target_id | int(11) | YES | | NULL | |
|
43
|
+
| target_type | varchar(255) | YES | | NULL | |
|
44
|
+
| changes_logged | text | YES | | NULL | |
|
45
|
+
| comments | text | YES | | NULL | |
|
46
|
+
| user_id | int(11) | YES | | NULL | |
|
47
|
+
| created_at | datetime | YES | | NULL | |
|
48
|
+
+----------------+--------------+------+-----+---------+----------------+
|
49
|
+
</code></pre>
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "changes_are_logged/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "changes_are_logged"
|
7
|
+
s.version = ChangesAreLogged::VERSION
|
8
|
+
s.authors = ["nyu", "jdb", "cef", "ejk"]
|
9
|
+
s.email = ""
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Log changes for record keeping}
|
12
|
+
s.description = %q{change_log}
|
13
|
+
|
14
|
+
s.rubyforge_project = "changes_are_logged"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here instead of Gemfile:
|
22
|
+
s.add_development_dependency 'rspec-rails', '2.7.0'
|
23
|
+
s.add_development_dependency 'activerecord', '3.0.11'
|
24
|
+
s.add_development_dependency 'sqlite3', '1.3.4'
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "changes_are_logged/version"
|
2
|
+
require 'changes_are_logged/change_log'
|
3
|
+
|
4
|
+
module ChangesAreLogged
|
5
|
+
module InstanceMethods
|
6
|
+
def log_it
|
7
|
+
return unless @log_changes
|
8
|
+
|
9
|
+
# FIXME too complected with lumos_rails
|
10
|
+
unless @modifying_user_id
|
11
|
+
@modifying_user_id = if defined?(StaffUser) && staff_user = StaffUser.current
|
12
|
+
@modifying_user_is_staff = true
|
13
|
+
staff_user.id
|
14
|
+
end
|
15
|
+
@modifying_user_id ||= if defined?(current_user) && current_user && current_user.respond_to?(:id)
|
16
|
+
current_user.id
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if self.new_record?
|
21
|
+
@change_comments = "new record" if @change_comments.blank?
|
22
|
+
@changes_logged = {}
|
23
|
+
save_change_log
|
24
|
+
elsif self.changed? || !@change_comments.blank?
|
25
|
+
@changes_logged = self.changes
|
26
|
+
@changes_logged.delete("updated_at")
|
27
|
+
save_change_log
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def save_change_log
|
32
|
+
self.change_logs << ChangeLog.new(
|
33
|
+
:changes_logged => @changes_logged,
|
34
|
+
:user_id => @modifying_user_id,
|
35
|
+
:comments => @change_comments,
|
36
|
+
:user_is_staff => @modifying_user_is_staff
|
37
|
+
)
|
38
|
+
@change_comments = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# this modifies dirty.rb behavior. previously #changes returned the change in the accessor method
|
42
|
+
# now, #changes will return raw changes made to actual database attributes
|
43
|
+
def attribute_change(attr)
|
44
|
+
[changed_attributes[attr], __send__(:read_attribute, attr)] if attribute_changed?(attr)
|
45
|
+
end
|
46
|
+
|
47
|
+
def automatically_log_changes
|
48
|
+
@log_changes = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.included(klass)
|
53
|
+
klass.class_eval do
|
54
|
+
include InstanceMethods
|
55
|
+
attr_accessor :modifying_user_id
|
56
|
+
attr_accessor :change_comments
|
57
|
+
attr_accessor :log_changes
|
58
|
+
before_save :log_it
|
59
|
+
has_many :change_logs, :as => :target
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class ChangeLog < ActiveRecord::Base
|
2
|
+
|
3
|
+
IGNORED_KEYS = ["updated_at"]
|
4
|
+
|
5
|
+
belongs_to :target, :polymorphic => true
|
6
|
+
serialize :changes_logged
|
7
|
+
|
8
|
+
scope :for_target_id, lambda {|target_id| {:conditions => {:target_id => target_id}}}
|
9
|
+
scope :for_target_type, lambda {|target_type| {:conditions => {:target_type => target_type}}}
|
10
|
+
|
11
|
+
validates :target, :presence => true
|
12
|
+
|
13
|
+
def pretty_changes(html = false)
|
14
|
+
change_string = ""
|
15
|
+
if has_changes?
|
16
|
+
changes_logged.each do |k,v|
|
17
|
+
unless IGNORED_KEYS.include?(k)
|
18
|
+
from = self.class.pretty_value(v[0])
|
19
|
+
to = self.class.pretty_value(v[1])
|
20
|
+
|
21
|
+
change_string << (html ? "• " : "- ")
|
22
|
+
change_string << (html ? "<i>#{k}</i>" : k)
|
23
|
+
change_string << " CHANGED FROM #{from} TO #{to}"
|
24
|
+
change_string << (html ? "<br/>" : "\n")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
change_string.html_safe
|
28
|
+
else
|
29
|
+
"No changes!"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# FIXME too complected with lumos_rails
|
34
|
+
def user
|
35
|
+
return nil unless user_id
|
36
|
+
user_is_staff ? StaffUser.find(user_id) : User.find(user_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_changes?
|
40
|
+
changes_logged && changes_logged.any?
|
41
|
+
end
|
42
|
+
|
43
|
+
def pretty_change_hashes
|
44
|
+
changes_logged.map do |k, (from, to)|
|
45
|
+
{
|
46
|
+
:key => k,
|
47
|
+
:from => self.class.pretty_value(from),
|
48
|
+
:to => self.class.pretty_value(to)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.pretty_value(v)
|
54
|
+
if v.nil?
|
55
|
+
"(nil)"
|
56
|
+
elsif v == ""
|
57
|
+
"(empty)"
|
58
|
+
else
|
59
|
+
v.pretty_inspect
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ChangeLog' do
|
4
|
+
context 'Making changes to a game' do
|
5
|
+
before do
|
6
|
+
@game = Game.create
|
7
|
+
@original_change_logs_size = @game.change_logs.size
|
8
|
+
@game.update_attribute(:name, 'shazam!')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should add an entry to the change_logs' do
|
12
|
+
@game.reload.change_logs.size.should == @original_change_logs_size + 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'Making changes to a game with change comments' do
|
17
|
+
before do
|
18
|
+
@game = Game.create
|
19
|
+
@original_change_logs_size = @game.change_logs.size
|
20
|
+
@original_name = @game.name
|
21
|
+
@original_slug = @game.url_slug
|
22
|
+
@game.name = 'shazam!'
|
23
|
+
@game.url_slug = 'shazam'
|
24
|
+
@comment = 'switching to cooler name'
|
25
|
+
@game.change_comments = @comment
|
26
|
+
@game.save!
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should record the change comments in the change_logs' do
|
30
|
+
@game.reload.change_logs.size.should == @original_change_logs_size + 1
|
31
|
+
@game.change_logs.last.changes_logged.should == {"name" => [@original_name, "shazam!"], "url_slug"=>[@original_slug, "shazam"]}
|
32
|
+
@game.change_logs.last.comments.should == @comment
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'Saving a game with no changes and a blank comment' do
|
37
|
+
before do
|
38
|
+
@game = Game.create
|
39
|
+
@original_change_logs_size = @game.change_logs.size
|
40
|
+
@game.change_comments = ""
|
41
|
+
@game.save!
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should not log anything' do
|
45
|
+
@game.reload.change_logs.size.should == @original_change_logs_size
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'Saving a game with no changes but a comment with content' do
|
50
|
+
before do
|
51
|
+
@game = Game.create
|
52
|
+
@original_change_logs_size = @game.change_logs.size
|
53
|
+
@game.change_comments = "commenting on a non-change"
|
54
|
+
@game.save!
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should log the comment and indicate no changes' do
|
58
|
+
@game.reload.change_logs.size.should == @original_change_logs_size + 1
|
59
|
+
@game.change_logs.last.changes_logged.should == {}
|
60
|
+
@game.change_logs.last.comments.should == "commenting on a non-change"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
require 'changes_are_logged'
|
6
|
+
|
7
|
+
require 'support/database_helpers' #test_data'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.include ChangesAreLogged::Spec::DatabaseHelpers
|
11
|
+
|
12
|
+
config.before do
|
13
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
14
|
+
setup_db
|
15
|
+
end
|
16
|
+
|
17
|
+
config.after do
|
18
|
+
teardown_db
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ChangesAreLogged
|
2
|
+
module Spec
|
3
|
+
module DatabaseHelpers
|
4
|
+
|
5
|
+
def setup_db
|
6
|
+
ActiveRecord::Schema.define(:version => 1) do
|
7
|
+
create_table :change_logs do |t|
|
8
|
+
t.column :target_id, :integer
|
9
|
+
t.column :target_type, :string
|
10
|
+
t.column :changes_logged, :text
|
11
|
+
t.column :comments, :text
|
12
|
+
t.column :user_id, :integer
|
13
|
+
t.column :user_is_staff, :boolean
|
14
|
+
t.datetime :created_at
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :games do |t|
|
18
|
+
t.column :name, :string
|
19
|
+
t.column :url_slug, :string
|
20
|
+
t.column :created_at, :datetime
|
21
|
+
t.column :updated_at, :datetime
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown_db
|
27
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
28
|
+
ActiveRecord::Base.connection.drop_table(table)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ::Game < ActiveRecord::Base
|
33
|
+
include ChangesAreLogged
|
34
|
+
after_initialize :automatically_log_changes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: changes_are_logged
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- nyu
|
8
|
+
- jdb
|
9
|
+
- cef
|
10
|
+
- ejk
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2016-12-20 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec-rails
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - '='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.7.0
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - '='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.7.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - '='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 3.0.11
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - '='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 3.0.11
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: sqlite3
|
46
|
+
requirement: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - '='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 1.3.4
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - '='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.3.4
|
58
|
+
description: change_log
|
59
|
+
email: ''
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- Gemfile
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- changes_are_logged.gemspec
|
69
|
+
- lib/changes_are_logged.rb
|
70
|
+
- lib/changes_are_logged/change_log.rb
|
71
|
+
- lib/changes_are_logged/version.rb
|
72
|
+
- spec/change_log_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- spec/support/database_helpers.rb
|
75
|
+
homepage: ''
|
76
|
+
licenses: []
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project: changes_are_logged
|
94
|
+
rubygems_version: 2.2.3
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: Log changes for record keeping
|
98
|
+
test_files:
|
99
|
+
- spec/change_log_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
- spec/support/database_helpers.rb
|