temporal_tables 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +4 -0
  3. data/.gitignore +19 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +73 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +139 -0
  10. data/Rakefile +9 -0
  11. data/config.ru +7 -0
  12. data/gemfiles/Gemfile.5.2.mysql +16 -0
  13. data/gemfiles/Gemfile.5.2.mysql.lock +155 -0
  14. data/gemfiles/Gemfile.5.2.pg +16 -0
  15. data/gemfiles/Gemfile.5.2.pg.lock +155 -0
  16. data/lib/temporal_tables.rb +63 -0
  17. data/lib/temporal_tables/connection_adapters/mysql_adapter.rb +56 -0
  18. data/lib/temporal_tables/connection_adapters/postgresql_adapter.rb +81 -0
  19. data/lib/temporal_tables/history_hook.rb +50 -0
  20. data/lib/temporal_tables/relation_extensions.rb +125 -0
  21. data/lib/temporal_tables/scope_extensions.rb +13 -0
  22. data/lib/temporal_tables/temporal_adapter.rb +159 -0
  23. data/lib/temporal_tables/temporal_class.rb +91 -0
  24. data/lib/temporal_tables/version.rb +3 -0
  25. data/lib/temporal_tables/whodunnit.rb +19 -0
  26. data/spec/basic_history_spec.rb +91 -0
  27. data/spec/extensions/combustion.rb +9 -0
  28. data/spec/internal/app/models/coven.rb +3 -0
  29. data/spec/internal/app/models/person.rb +4 -0
  30. data/spec/internal/app/models/wart.rb +7 -0
  31. data/spec/internal/config/database.sample.yml +12 -0
  32. data/spec/internal/config/routes.rb +3 -0
  33. data/spec/internal/db/schema.rb +16 -0
  34. data/spec/internal/log/.gitignore +1 -0
  35. data/spec/internal/public/favicon.ico +0 -0
  36. data/spec/spec_helper.rb +17 -0
  37. data/spec/support/database.rb +9 -0
  38. data/temporal_tables.gemspec +24 -0
  39. metadata +149 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d62499062a9e064ab78f44d40ad96dcd82936731662b8fc6c1920f5dfb5bb1f5
4
+ data.tar.gz: 14ce63e0d1dfa16260132c0204671b2694823455623958177a46ce9d33e2ddc3
5
+ SHA512:
6
+ metadata.gz: 217fa0285c8a7ad77f438e4236d50ccd35647cf1d3cd3dec127479b27e6a03f9597560943f11f8cbc28911e05a31c7788eeddfadfb7a766788fcb094ef2677f3
7
+ data.tar.gz: 4dc597b2b6ee301b7bbba1852cc466b4c4ea67760e975dff38fefbf99804d2ac1fa960157357bff7d0ea2493acfedeb226e3b79f5d31086cb02b126064c0f7af
data/.editorconfig ADDED
@@ -0,0 +1,4 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_size = 2
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .DS_Store
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ spec/internal/config/database.yml
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.3
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 2.4.3
3
+
4
+ gemfile:
5
+ - gemfiles/Gemfile.5.2.pg
6
+ - gemfiles/Gemfile.5.2.mysql
data/CHANGELOG.md ADDED
@@ -0,0 +1,73 @@
1
+ # CHANGELOG
2
+
3
+ ## 0.4.0 (2016-12-13)
4
+
5
+ * added multi-environment spec framework
6
+ * bug fix: mysql2 gem should now be functional
7
+
8
+ ## 0.3.1 (2015-10-08)
9
+
10
+ * bug fix: accepts reserved keywords for postgres column names, like "default"
11
+ * bug fix: correctly handles models nested within modules
12
+
13
+ ## 0.3.0 (2014-07-04)
14
+
15
+ * Updated for Rails 4
16
+
17
+ ## 0.2.7 (2013-07-10)
18
+
19
+ * bug fix: fix problems with STI instantiation
20
+
21
+ ## 0.2.6 (2013-05-23)
22
+
23
+ * bug fix: explicitly set sti_name on history classes of STI subclasses
24
+
25
+ ## 0.2.5 (2013-04-24)
26
+
27
+ * bug fix: rename_column wasn't linked in correctly
28
+
29
+ ## 0.2.4 (2013-03-07)
30
+
31
+ * bug fix: another fix for removing indexes
32
+
33
+ # 0.2.3 (2013-02-25)
34
+
35
+ * bug fix: history classes did not have the correct primary key set, which could provide unexpected results in certain use cases
36
+ * added a history method to ActiveRecord::Base objects to return a sorted list of all historical versions of that object.
37
+ * added prev and next methods to history objects to aid in traversing the historical versions of an object.
38
+ * added orig_obj method to history objects to return their non-historical objects
39
+
40
+ ## 0.2.2 (2013-02-25)
41
+
42
+ * bug fix: add_index with specified name caused an index collision
43
+
44
+ ## 0.2.1 (2013-02-21)
45
+
46
+ * bug fix: removing indexes didn't work
47
+
48
+ ## 0.2.0 (2013-02-21)
49
+
50
+ * added support for indexes
51
+
52
+ ## 0.1.0 (2013-01-10)
53
+
54
+ * added history querying
55
+
56
+ ## 0.0.6 (2012-11-29)
57
+
58
+ * optimized the history table index
59
+
60
+ ## 0.0.5 (2012-11-29)
61
+
62
+ * added indexes to history tables
63
+
64
+ ## 0.0.4 (2012-10-19)
65
+
66
+ * added updated_by support
67
+ * added some configuration options
68
+
69
+ ## 0.0.2 (2012-10-12)
70
+
71
+ * added support for rename_table
72
+ * added add_temporal_table(table_name) method
73
+ * added remove_temporal_table(table_name) method
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in temporal_tables.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Brent Kroeker
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # TemporalTables
2
+
3
+ Easily recall what your data looked like at any point in the past! TemporalTables sets up and maintains history tables to track all temporal changes to to your data.
4
+
5
+ Currently tested on Ruby 2.4, Rails 5.2, Postgres, MySQL
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+ ``` ruby
11
+ gem 'temporal_tables'
12
+ ```
13
+
14
+ And then execute:
15
+ ``` bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ``` bash
21
+ $ gem install temporal_tables
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Schema
27
+
28
+ In your rails migration, specify that you want a table to have its history tracked:
29
+ ``` ruby
30
+ create_table :people, :temporal => true do |t|
31
+ ...
32
+ end
33
+ ```
34
+
35
+ This will create a history table called "people_h" which will maintain a history of all changes to any records in people with the use of triggers on any create/update/delete operation.
36
+
37
+ Any subsequent schema changes to people will be reflected automatically in people_h.
38
+
39
+ ``` ruby
40
+ # Nothing extra required -- people_h will automatically get an "name" column too!
41
+ add_column :people, :name, :string
42
+ ```
43
+
44
+ To track the history of a pre-existing table, just call `add_temporal_table`:
45
+ ``` ruby
46
+ add_temporal_table :people
47
+ ```
48
+
49
+ ### Querying
50
+
51
+ For the below queries, we'll assume the following schema:
52
+ ``` ruby
53
+ class Person < ActiveRecord::Base
54
+ attr_accessible :name
55
+ belongs_to :coven
56
+ has_many :warts
57
+
58
+ def label
59
+ "#{name} from #{coven.name}"
60
+ end
61
+ end
62
+
63
+ class Coven < ActiveRecord::Base
64
+ attr_accessible :name
65
+ has_many :members, class_name: "Person"
66
+ end
67
+
68
+ class Wart < ActiveRecord::Base
69
+ attr_accessible :location
70
+ belongs_to :person
71
+
72
+ scope :very_hairy, -> { where(arel_table[:num_hairs].gteq(3)) }
73
+ end
74
+ ```
75
+
76
+ You can query the history tables by calling `history` on the class.
77
+ ``` ruby
78
+ Person #=> Person(id: :integer, name: :string)
79
+ Person.history #=> PersonHistory(history_id: :integer, id: :integer, name: :string, eff_from: :datetime, eff_to: :datetime)
80
+ ```
81
+
82
+ You can easily get a history of all changes to a records.
83
+ ``` ruby
84
+ Person.history.where(id: 1).map { |p| "#{p.eff_from}: #{p.name}")
85
+ # => [
86
+ # "1974-01-14: Emily",
87
+ # "2003-11-03: Grunthilda"
88
+ # ]
89
+ ```
90
+
91
+ You can query for records as they were at any point in the past by calling `at`.
92
+ ``` ruby
93
+ Person.history.at(2.years.ago).where(id: 1).first.name #=> "Grunthilda"
94
+ ```
95
+
96
+ Associations work too.
97
+ ``` ruby
98
+ grunthilda = Person.history.at(20.years.ago).find_by_name("Grunthilda")
99
+ grunthilda.warts.count #=> 2
100
+
101
+ grunthilda = Person.history.at(1.year.ago).find_by_name("Grunthilda")
102
+ grunthilda.warts.count #=> 13
103
+
104
+ grunthilda.warts.first.class.name #=> "WartHistory"
105
+ ```
106
+
107
+ And scopes also!
108
+ ``` ruby
109
+ grunthilda = Person.history.at(1.year.ago).find_by_name("Grunthilda")
110
+ grunthilda.warts.count #=> 13
111
+ grunthilda.warts.very_hairy.count #=> 7
112
+ ```
113
+
114
+ Instance methods are inherited.
115
+ ``` ruby
116
+ grunthilda.label #=> "Grunthilda from Delta Gamma Gamma"
117
+ grunthilda.class.name #=> "PersonHistory"
118
+ grunthilda.class.superclass.name #=> "Person"
119
+ ```
120
+
121
+ ## Config
122
+
123
+ Create temporal tables for all tables by default
124
+ ``` ruby
125
+ TemporalTables.create_by_default = true
126
+ ```
127
+
128
+ Don't create temporal tables for these tables. Defaults to schema_migrations and sessions tables.
129
+ ``` ruby
130
+ TemporalTables.skip_temporal_table_for :table_one, :table_two
131
+ ```
132
+
133
+ Add an updated_by field to all temporal tables to track who made any changes. Defaults to a :string field. The block is called when records are saved to determine the value to place within the updated_by field.
134
+ ``` ruby
135
+ TemporalTables.add_updated_by_field(:integer) { User.current_user.try(:id) }
136
+ ```
137
+
138
+ ## Copyright
139
+ See [LICENSE](https://github.com/bkroeker/temporal_tables/blob/master/LICENSE.txt) for more details.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'gemika/tasks'
5
+ rescue LoadError
6
+ puts 'Run `gem install gemika` for additional tasks'
7
+ end
8
+
9
+ task :default => 'matrix:spec'
data/config.ru ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize! :all
7
+ run Combustion::Application
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Runtime dependencies
4
+ gem 'rails', '~> 5.2.0'
5
+ gem 'mysql2', '~> 0.4.4'
6
+
7
+ # Development dependencies
8
+ gem 'rspec', '~> 3.4'
9
+ gem 'rake'
10
+ gem 'byebug'
11
+ gem 'database_cleaner'
12
+ gem 'combustion'
13
+ gem 'gemika'
14
+
15
+ # Gem under test
16
+ gem 'temporal_tables', :path => '..'
@@ -0,0 +1,155 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ temporal_tables (0.6.0)
5
+ rails (~> 5.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (5.2.0)
11
+ actionpack (= 5.2.0)
12
+ nio4r (~> 2.0)
13
+ websocket-driver (>= 0.6.1)
14
+ actionmailer (5.2.0)
15
+ actionpack (= 5.2.0)
16
+ actionview (= 5.2.0)
17
+ activejob (= 5.2.0)
18
+ mail (~> 2.5, >= 2.5.4)
19
+ rails-dom-testing (~> 2.0)
20
+ actionpack (5.2.0)
21
+ actionview (= 5.2.0)
22
+ activesupport (= 5.2.0)
23
+ rack (~> 2.0)
24
+ rack-test (>= 0.6.3)
25
+ rails-dom-testing (~> 2.0)
26
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
27
+ actionview (5.2.0)
28
+ activesupport (= 5.2.0)
29
+ builder (~> 3.1)
30
+ erubi (~> 1.4)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
33
+ activejob (5.2.0)
34
+ activesupport (= 5.2.0)
35
+ globalid (>= 0.3.6)
36
+ activemodel (5.2.0)
37
+ activesupport (= 5.2.0)
38
+ activerecord (5.2.0)
39
+ activemodel (= 5.2.0)
40
+ activesupport (= 5.2.0)
41
+ arel (>= 9.0)
42
+ activestorage (5.2.0)
43
+ actionpack (= 5.2.0)
44
+ activerecord (= 5.2.0)
45
+ marcel (~> 0.3.1)
46
+ activesupport (5.2.0)
47
+ concurrent-ruby (~> 1.0, >= 1.0.2)
48
+ i18n (>= 0.7, < 2)
49
+ minitest (~> 5.1)
50
+ tzinfo (~> 1.1)
51
+ arel (9.0.0)
52
+ builder (3.2.3)
53
+ byebug (10.0.2)
54
+ combustion (0.9.1)
55
+ activesupport (>= 3.0.0)
56
+ railties (>= 3.0.0)
57
+ thor (>= 0.14.6)
58
+ concurrent-ruby (1.0.5)
59
+ crass (1.0.4)
60
+ database_cleaner (1.7.0)
61
+ diff-lcs (1.3)
62
+ erubi (1.7.1)
63
+ gemika (0.3.2)
64
+ globalid (0.4.1)
65
+ activesupport (>= 4.2.0)
66
+ i18n (1.0.1)
67
+ concurrent-ruby (~> 1.0)
68
+ loofah (2.2.2)
69
+ crass (~> 1.0.2)
70
+ nokogiri (>= 1.5.9)
71
+ mail (2.7.0)
72
+ mini_mime (>= 0.1.1)
73
+ marcel (0.3.2)
74
+ mimemagic (~> 0.3.2)
75
+ method_source (0.9.0)
76
+ mimemagic (0.3.2)
77
+ mini_mime (1.0.0)
78
+ mini_portile2 (2.3.0)
79
+ minitest (5.11.3)
80
+ mysql2 (0.4.10)
81
+ nio4r (2.3.1)
82
+ nokogiri (1.8.4)
83
+ mini_portile2 (~> 2.3.0)
84
+ rack (2.0.5)
85
+ rack-test (1.0.0)
86
+ rack (>= 1.0, < 3)
87
+ rails (5.2.0)
88
+ actioncable (= 5.2.0)
89
+ actionmailer (= 5.2.0)
90
+ actionpack (= 5.2.0)
91
+ actionview (= 5.2.0)
92
+ activejob (= 5.2.0)
93
+ activemodel (= 5.2.0)
94
+ activerecord (= 5.2.0)
95
+ activestorage (= 5.2.0)
96
+ activesupport (= 5.2.0)
97
+ bundler (>= 1.3.0)
98
+ railties (= 5.2.0)
99
+ sprockets-rails (>= 2.0.0)
100
+ rails-dom-testing (2.0.3)
101
+ activesupport (>= 4.2.0)
102
+ nokogiri (>= 1.6)
103
+ rails-html-sanitizer (1.0.4)
104
+ loofah (~> 2.2, >= 2.2.2)
105
+ railties (5.2.0)
106
+ actionpack (= 5.2.0)
107
+ activesupport (= 5.2.0)
108
+ method_source
109
+ rake (>= 0.8.7)
110
+ thor (>= 0.18.1, < 2.0)
111
+ rake (12.3.1)
112
+ rspec (3.7.0)
113
+ rspec-core (~> 3.7.0)
114
+ rspec-expectations (~> 3.7.0)
115
+ rspec-mocks (~> 3.7.0)
116
+ rspec-core (3.7.1)
117
+ rspec-support (~> 3.7.0)
118
+ rspec-expectations (3.7.0)
119
+ diff-lcs (>= 1.2.0, < 2.0)
120
+ rspec-support (~> 3.7.0)
121
+ rspec-mocks (3.7.0)
122
+ diff-lcs (>= 1.2.0, < 2.0)
123
+ rspec-support (~> 3.7.0)
124
+ rspec-support (3.7.1)
125
+ sprockets (3.7.2)
126
+ concurrent-ruby (~> 1.0)
127
+ rack (> 1, < 3)
128
+ sprockets-rails (3.2.1)
129
+ actionpack (>= 4.0)
130
+ activesupport (>= 4.0)
131
+ sprockets (>= 3.0.0)
132
+ thor (0.20.0)
133
+ thread_safe (0.3.6)
134
+ tzinfo (1.2.5)
135
+ thread_safe (~> 0.1)
136
+ websocket-driver (0.7.0)
137
+ websocket-extensions (>= 0.1.0)
138
+ websocket-extensions (0.1.3)
139
+
140
+ PLATFORMS
141
+ ruby
142
+
143
+ DEPENDENCIES
144
+ byebug
145
+ combustion
146
+ database_cleaner
147
+ gemika
148
+ mysql2 (~> 0.4.4)
149
+ rails (~> 5.2.0)
150
+ rake
151
+ rspec (~> 3.4)
152
+ temporal_tables!
153
+
154
+ BUNDLED WITH
155
+ 1.16.1