temporal_tables 0.6.1

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.
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