changes_are_logged 0.0.2

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