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 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
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in changes_are_logged.gemspec
4
+ gemspec
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 ? "&bull;&nbsp;" : "- ")
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,3 @@
1
+ module ChangesAreLogged
2
+ VERSION = "0.0.2"
3
+ 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
@@ -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