diffit 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 51a22c289600fbd9b443344c8d478ae2ab8c170b
4
+ data.tar.gz: 8b41bc0c64837641b34c3afeee1fb61fcf928a92
5
+ SHA512:
6
+ metadata.gz: f348d9f2c135baf8f25cfec6b9331b07691a4e36b038393e1043aef05fb734ba0df718a433cbdc9d1c000e87dd1351170c353ffa4c002ef65061509e211edf11
7
+ data.tar.gz: 6d02351691c2fc6ef90bfa15567db530717b3d691935de86d3413006b35879890b02d8cbff0a69010e1ce16e08eb1363c4ff5264319ebd4edb17908ba0daeac1
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Denis Lifanov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Diffit'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,24 @@
1
+ require 'diffit/version'
2
+
3
+ module Diffit
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :Changes
7
+ autoload :Record
8
+ autoload :Tracker
9
+ autoload :Trackable
10
+
11
+ mattr_accessor :function_name
12
+ @@function_name = :diffit_function
13
+
14
+ mattr_accessor :table_name
15
+ @@table_name = :diffit_column_diffs
16
+
17
+ mattr_accessor :strategy
18
+ @@strategy = :join
19
+
20
+ def self.configure
21
+ yield self
22
+ end
23
+
24
+ end
@@ -0,0 +1,98 @@
1
+ module Diffit
2
+ class Changes
3
+
4
+ include Enumerable
5
+
6
+ attr_reader :timestamp
7
+ attr_reader :records
8
+
9
+ # Instantiates a Diffit::Changes with provided timestamp.
10
+ #
11
+ # @param timestamp [Time, DateTime, Date, Fixnum] date, time or timestamp.
12
+ # @return [Diffit::Changes] Diffit::Changes
13
+ def initialize(timestamp)
14
+ @timestamp = timestamp
15
+ @records = []
16
+ end
17
+
18
+ # Appends provided data to `self`.
19
+ #
20
+ # @param model [String] model name.
21
+ # @param data [Array(Hash)] data to append.
22
+ # @return [self] self
23
+ def append(model, data)
24
+ data.group_by { |row| row[:record_id] }.each do |record_id, changes|
25
+ @records << Record.new(model, record_id, changes.map { |c| c.slice(:column_name, :value, :changed_at)})
26
+ end
27
+ @length = nil
28
+ self
29
+ end
30
+
31
+ # Are there any changes?
32
+ #
33
+ # @return [Boolean] existence of changes.
34
+ def empty?
35
+ @records.empty?
36
+ end
37
+
38
+ # Number of changes.
39
+ #
40
+ # @return [Fixnum] number of changes
41
+ def length
42
+ @length ||= @records.inject(0) { |v,r| v += r.changes.length }
43
+ end
44
+
45
+ # Calls the given block once for each record in the collection.
46
+ #
47
+ # @return [Enumerator] if no block is given.
48
+ # @return [Array(Diffit::Record)] otherwise
49
+ def each
50
+ if block_given?
51
+ @records.each { |c| yield c }
52
+ else
53
+ @records.enum_for(:each)
54
+ end
55
+ end
56
+
57
+ # A short `String` representation of `self`.
58
+ #
59
+ # @return [String] the object converted to string.
60
+ def to_s
61
+ sprintf '#<%s:%#0x @timestamp: %s @changes: {%d}>',
62
+ self.class.to_s,
63
+ self.object_id,
64
+ @timestamp.strftime('%d/%b/%Y:%H:%M:%S %z'),
65
+ length
66
+ end
67
+
68
+ alias :to_str :to_s
69
+
70
+ # A `Hash` representation of `self`.
71
+ #
72
+ # @return [Hash] the object converted to hash.
73
+ def to_h
74
+ {timestamp: timestamp.to_i, changes: @records.map(&:to_h)}
75
+ end
76
+
77
+ alias :to_hash :to_h
78
+
79
+ # A JSON representation of `self`.
80
+ #
81
+ # @return [String] the object converted to JSON.
82
+ def to_json
83
+ to_h.to_json
84
+ end
85
+
86
+ def prepare!
87
+ @records.sort_by! { |record| record.last_changed_at }
88
+ @records.uniq! { |record| [record.model, record.record_id] }
89
+ nil
90
+ end
91
+
92
+ def cleanup!
93
+ @records.clear
94
+ @length = nil
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,27 @@
1
+ module Diffit
2
+ class Record
3
+
4
+ attr_reader :model, :record_id, :changes
5
+
6
+ def initialize(model, record_id, changes)
7
+ @model = model
8
+ @record_id = record_id
9
+ @changes = changes
10
+ end
11
+
12
+ # Timestamp of the latest change.
13
+ #
14
+ # @return [Time] latest change timestamp.
15
+ def last_changed_at
16
+ @last_changed_at ||= @changes.map { |c| c[:changed_at] }.max
17
+ end
18
+
19
+ # A `Hash` representation of `self`.
20
+ #
21
+ # @return [Hash] the object converted to hash.
22
+ def to_h
23
+ {model: model, record_id: record_id, changes: changes}
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Diffit
2
+ module Trackable
3
+
4
+ def self.included(base)
5
+ base.extend(self)
6
+ end
7
+
8
+ def changes_since(timestamp)
9
+ Diffit::Tracker.new(timestamp).append(self)
10
+ end
11
+
12
+ alias :diff_from :changes_since
13
+
14
+ def changes_since_midnight
15
+ Diffit::Tracker.new(Time.now.beginning_of_day).append(self)
16
+ end
17
+
18
+ alias :diff_from_midnight :changes_since_midnight
19
+
20
+ end
21
+ end
@@ -0,0 +1,205 @@
1
+ module Diffit
2
+ class Tracker
3
+
4
+ attr_reader :timestamp
5
+
6
+ delegate :to_h, :to_hash, :to_json, :each, :length, :size, to: :changes
7
+
8
+ # Instantiates a Diffit::Tracker with provided timestamp.
9
+ #
10
+ # @param timestamp [Time, DateTime, Date, Fixnum] date, time or timestamp.
11
+ # @return [Diffit::Tracker] Diffit::Tracker
12
+ def initialize(timestamp)
13
+ if timestamp.respond_to?(:to_datetime)
14
+ @timestamp = timestamp.to_datetime
15
+ elsif timestamp.respond_to?(:to_i)
16
+ @timestamp = Time.at(timestamp.to_i).to_datetime
17
+ else
18
+ raise ArgumentError, "#{timestamp.inspect} is not a timestamp!"
19
+ end
20
+
21
+ @tracked = []
22
+ @changes = Diffit::Changes.new(self.timestamp)
23
+ @fetched = false
24
+ end
25
+
26
+ def initialize_clone(other)
27
+ @tracked = Array.new(@tracked)
28
+ @changes = Diffit::Changes.new(self.timestamp)
29
+ end
30
+
31
+ # Appends provided objects.
32
+ #
33
+ # @param object [ActiveRecord::Relation, ActiveRecord::Base, Array(ActiveRecord::Base), Array(ActiveRecord::Relation)]
34
+ # @return [Diffit::Tracker] new instance of Diffit::Tracker.
35
+ def append(*objects)
36
+ copy = self.clone
37
+ copy.append!(*objects)
38
+ copy
39
+ end
40
+
41
+ # Appends provided objects to `self`.
42
+ #
43
+ # @param object [ActiveRecord::Relation, ActiveRecord::Base, Array(ActiveRecord::Base), Array(ActiveRecord::Relation)]
44
+ # @return [self] self
45
+ def append!(*objects)
46
+ objects.flatten!
47
+ objects.each do |object|
48
+ if accepts?(object)
49
+ @tracked << object
50
+ else
51
+ raise ArgumentError, 'Expected ActiveRecord::Base or ActiveRecord::Relation'
52
+ end
53
+ end
54
+
55
+ @changes.cleanup!
56
+ @fetched = false
57
+ self
58
+ end
59
+
60
+ # Appends all changes.
61
+ #
62
+ # @return [Diffit::Tracker] a new instance of Diffit::Tracker.
63
+ def all
64
+ copy = self.clone
65
+ copy.all!
66
+ copy
67
+ end
68
+
69
+ # Appends all changes to `self`.
70
+ #
71
+ # @return [self] self.
72
+ def all!
73
+ @changes.cleanup!
74
+ handle_all.group_by { |row| row[:table_name] }.each do |t, records|
75
+ @changes.append t.classify, records
76
+ end
77
+ self
78
+ end
79
+
80
+ def changes
81
+ return @changes if @fetched
82
+
83
+ @tracked.each do |object|
84
+ if record?(object)
85
+ @changes.append object.model_name.name, handle_one(object)
86
+ elsif relation?(object)
87
+ model = object.respond_to?(:model) ? object.model : object.class
88
+ changes = case Diffit.strategy
89
+ when :join then handle_relation_with_join(object)
90
+ when :subquery then handle_relation_with_subquery(object)
91
+ end
92
+
93
+ @changes.append model.name, changes
94
+ end
95
+ end
96
+
97
+ @fetched = true
98
+ @changes.prepare!
99
+ @changes
100
+ end
101
+
102
+ private
103
+
104
+ def accepts?(object)
105
+ record?(object) || relation?(object)
106
+ end
107
+
108
+ def relation?(object)
109
+ object.is_a?(ActiveRecord::Relation) || ActiveRecord::Base.descendants.include?(object)
110
+ end
111
+
112
+ def record?(object)
113
+ object.is_a?(ActiveRecord::Base)
114
+ end
115
+
116
+ def handle_relation_with_subquery(relation)
117
+ table = Arel::Table.new(Diffit.table_name)
118
+
119
+ sanitized = relation.except(:select, :order, :group, :having, :includes).select(:id)
120
+
121
+ query = table.
122
+ where(table[:changed_at].gteq(self.timestamp)).
123
+ where(table[:table_name].eq(relation.table_name)).
124
+ order(:table_name, :record_id)
125
+
126
+ if sanitized.where_values.present? || sanitized.joins_values.present?
127
+ query = query.where(table[:record_id].in(Arel.sql(sanitized.to_sql)))
128
+ end
129
+
130
+ query = query.project(table[:record_id], table[:column_name], table[:value], table[:changed_at])
131
+
132
+ execute_query(query)
133
+ end
134
+
135
+ def handle_relation_with_join(relation)
136
+ table = Arel::Table.new(Diffit.table_name)
137
+
138
+ sanitized = relation.except(:select, :order, :group, :having, :includes)
139
+
140
+ query = sanitized.
141
+ from(table).
142
+ where(table[:changed_at].gteq(self.timestamp)).
143
+ where(table[:table_name].eq(relation.table_name)).
144
+ select(table[:record_id], table[:column_name], table[:value], table[:changed_at])
145
+
146
+ if sanitized.where_values.present? || sanitized.joins_values.present?
147
+
148
+ join_cond = Arel::Nodes::On.new(sanitized.arel_table[:id].eq(table[:record_id]))
149
+ join_arel = Arel::Nodes::InnerJoin.new(sanitized.arel_table, join_cond)
150
+
151
+ query = query.joins(join_arel)
152
+ end
153
+
154
+ execute_query(query)
155
+ end
156
+
157
+ def handle_one(record)
158
+ table = Arel::Table.new(Diffit.table_name)
159
+
160
+ query = table.
161
+ where(table[:changed_at].gteq(self.timestamp)).
162
+ where(table[:table_name].eq(record.class.table_name)).
163
+ where(table[:record_id].eq(record.id)).
164
+ order(:table_name, :record_id).
165
+ project(table[:record_id], table[:column_name], table[:value], table[:changed_at])
166
+
167
+ execute_query(query)
168
+ end
169
+
170
+ def handle_all
171
+ table = Arel::Table.new(Diffit.table_name)
172
+
173
+ query = table.
174
+ where(table[:changed_at].gteq(self.timestamp)).
175
+ order(:table_name, :record_id).
176
+ project(table[:table_name], table[:record_id], table[:column_name], table[:value], table[:changed_at])
177
+
178
+ execute_query(query)
179
+ end
180
+
181
+ # Executes raw SQL query.
182
+ # Uses ActiveRecord::Result, performs typecasting.
183
+ #
184
+ # @param query [ActiveRecord::Relation, Arel::SelectManager]
185
+ # @return [Array(Hash)] query result
186
+ def execute_query(query)
187
+ result = ActiveRecord::Base.connection.select_all(query.to_sql)
188
+
189
+ cols = result.columns.map { |c| c.to_sym }
190
+ rows = result.cast_values
191
+
192
+ rows.map do |row|
193
+ hash, i, l = {}, 0, cols.length
194
+
195
+ while i < l
196
+ hash[cols[i]] = row[i]
197
+ i += 1
198
+ end
199
+
200
+ hash
201
+ end
202
+ end
203
+
204
+ end
205
+ end
@@ -0,0 +1,3 @@
1
+ module Diffit
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/active_record'
3
+
4
+ module Diffit
5
+ class BaseGenerator < Rails::Generators::Base
6
+
7
+ hide!
8
+
9
+ include Rails::Generators::Migration
10
+
11
+ def self.next_migration_number(dirname)
12
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
13
+ end
14
+
15
+ protected
16
+
17
+ def migration_exists?(basename)
18
+ self.class.migration_exists?('db/migrate', basename).present?
19
+ end
20
+
21
+ def create_diffit_migration(template, basename)
22
+ if migration_exists?(basename)
23
+ warning "Migration '#{basename}' already exists."
24
+ else
25
+ migration_template template, "db/migrate/#{basename}.rb"
26
+ sleep 1
27
+ end
28
+ end
29
+
30
+ def warning(message)
31
+ say_status '!', message, :red
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ require 'generators/diffit/base_generator'
2
+
3
+ module Diffit
4
+ class InitGenerator < Diffit::BaseGenerator
5
+
6
+ source_root File.dirname(__FILE__)
7
+
8
+ desc 'Creates a diffit initializer and migrations for tracking table and stored procedure.'
9
+
10
+ argument :table_name, type: :string, required: true, desc: "Name of tracking table"
11
+
12
+ def prepare
13
+ Diffit.table_name = self.table_name
14
+ Diffit.function_name = "#{self.table_name}_function"
15
+ end
16
+
17
+ def create_initializer
18
+ template 'templates/diffit.erb', 'config/initializers/diffit.rb'
19
+ end
20
+
21
+ def create_table_migration
22
+ basename = "create_#{Diffit.table_name.to_s.underscore}"
23
+ create_diffit_migration 'migrations/create_table.erb', basename
24
+ end
25
+
26
+ def create_function_migration
27
+ basename = "create_#{Diffit.table_name.to_s.underscore}_function"
28
+ create_diffit_migration 'migrations/create_function.erb', basename
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,102 @@
1
+ class Create<%= Diffit.function_name.to_s.camelize %> < ActiveRecord::Migration
2
+ def up
3
+ execute <<-SQL
4
+ CREATE OR REPLACE FUNCTION <%= Diffit.function_name %>() RETURNS TRIGGER AS $$
5
+ DECLARE
6
+
7
+ n JSON; -- NEW as JSON
8
+ o JSON; -- OLD as JSON
9
+ t TIMESTAMP; -- current timestamp
10
+ a RECORD; -- attribute
11
+
12
+ BEGIN
13
+
14
+ t := current_timestamp;
15
+ n := row_to_json(NEW);
16
+
17
+ IF (TG_OP = 'UPDATE') THEN
18
+ o := row_to_json(OLD);
19
+ END IF;
20
+
21
+ -- select attributes (without pkeys)
22
+
23
+ FOR a IN SELECT
24
+ pga.attname AS name
25
+ FROM pg_attribute pga
26
+ LEFT JOIN pg_index pgi ON pgi.indrelid = pga.attrelid AND pga.attnum = ANY(pgi.indkey)
27
+ WHERE pga.attnum > 0 AND
28
+ pgi.indisprimary IS NOT true AND
29
+ pga.attrelid = TG_TABLE_NAME::regclass AND
30
+ NOT pga.attisdropped
31
+ ORDER BY pga.attnum
32
+ LOOP
33
+
34
+ IF (TG_OP = 'UPDATE' AND (n->(a.name))::text IS DISTINCT FROM (o->(a.name))::text) OR (TG_OP = 'INSERT') THEN
35
+
36
+ -- UPSERT
37
+
38
+ UPDATE <%= Diffit.table_name %>
39
+ SET
40
+ value = n->(a.name),
41
+ changed_at = t
42
+ WHERE
43
+ table_name = TG_TABLE_NAME AND
44
+ record_id = NEW.id AND
45
+ column_name = a.name;
46
+
47
+ IF NOT FOUND THEN
48
+
49
+ BEGIN
50
+
51
+ INSERT INTO <%= Diffit.table_name %>
52
+ (table_name, record_id, column_name, value, changed_at)
53
+ VALUES
54
+ (TG_TABLE_NAME, NEW.id, a.name, n->(a.name), t);
55
+
56
+ EXCEPTION
57
+ WHEN unique_violation THEN
58
+
59
+ -- handle unique violation (constraint: table_name, record_id, column_name)
60
+ -- TODO: notice
61
+ -- CHEKME
62
+
63
+ UPDATE <%= Diffit.table_name %>
64
+ SET
65
+ value = n->(a.name),
66
+ changed_at = t
67
+ WHERE
68
+ table_name = TG_TABLE_NAME AND
69
+ record_id = NEW.id AND
70
+ column_name = a.name;
71
+
72
+ WHEN OTHERS THEN
73
+
74
+ -- TODO: notice
75
+
76
+ UPDATE <%= Diffit.table_name %>
77
+ SET
78
+ value = n->(a.name),
79
+ changed_at = t
80
+ WHERE
81
+ table_name = TG_TABLE_NAME AND
82
+ record_id = NEW.id AND
83
+ column_name = a.name;
84
+
85
+ END;
86
+ END IF;
87
+ END IF;
88
+ END LOOP;
89
+
90
+ RETURN NEW;
91
+
92
+ END;
93
+ $$ LANGUAGE plpgsql;
94
+ SQL
95
+ end
96
+
97
+ def down
98
+ execute <<-SQL
99
+ DROP FUNCTION IF EXISTS <%= Diffit.function_name %>();
100
+ SQL
101
+ end
102
+ end
@@ -0,0 +1,24 @@
1
+ class Create<%= Diffit.table_name.to_s.camelize %> < ActiveRecord::Migration
2
+ def up
3
+ create_table :<%= Diffit.table_name %> do |t|
4
+ t.string :table_name
5
+ t.integer :record_id
6
+ t.string :column_name
7
+ t.json :value
8
+ t.timestamp :changed_at
9
+ end
10
+
11
+ add_index :<%= Diffit.table_name %>, :table_name
12
+ add_index :<%= Diffit.table_name %>, :record_id
13
+ add_index :<%= Diffit.table_name %>, :changed_at
14
+
15
+ execute <<-SQL
16
+ ALTER TABLE <%= Diffit.table_name %>
17
+ ADD CONSTRAINT record_identifiers UNIQUE (table_name, record_id, column_name);
18
+ SQL
19
+ end
20
+
21
+ def down
22
+ drop_table :<%= Diffit.table_name %>
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ Diffit.configure do |config|
2
+
3
+ # A name of the tracking table.
4
+ config.table_name = :<%= Diffit.table_name %>
5
+
6
+ # A name of the stored procedure.
7
+ config.function_name = :<%= Diffit.function_name %>
8
+
9
+ # A strategy to handle relations. Should be one of: [:join, :subquery].
10
+ config.strategy = :join
11
+
12
+ end
@@ -0,0 +1,32 @@
1
+ class Create<%= Diffit.function_name.to_s.camelize %>TriggersOn<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def up
3
+
4
+ execute <<-SQL
5
+ CREATE TRIGGER <%= Diffit.function_name %>_on_<%= table_name %>_insert_trigger
6
+ AFTER INSERT ON <%= table_name %>
7
+ FOR EACH ROW
8
+ EXECUTE PROCEDURE <%= Diffit.function_name %>();
9
+ SQL
10
+
11
+ execute <<-SQL
12
+ CREATE TRIGGER <%= Diffit.function_name %>_on_<%= table_name %>_update_trigger
13
+ AFTER UPDATE ON <%= table_name %>
14
+ FOR EACH ROW
15
+ WHEN (OLD.* IS DISTINCT FROM NEW.*)
16
+ EXECUTE PROCEDURE <%= Diffit.function_name %>();
17
+ SQL
18
+
19
+ end
20
+
21
+ def down
22
+
23
+ execute <<-SQL
24
+ DROP TRIGGER <%= Diffit.function_name %>_on_<%= table_name %>_insert_trigger ON <%= table_name %>;
25
+ SQL
26
+
27
+ execute <<-SQL
28
+ DROP TRIGGER <%= Diffit.function_name %>_on_<%= table_name %>_update_trigger ON <%= table_name %>;
29
+ SQL
30
+
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ require 'generators/diffit/base_generator'
2
+
3
+ module Diffit
4
+ class TriggersGenerator < Diffit::BaseGenerator
5
+
6
+ source_root File.dirname(__FILE__)
7
+
8
+ argument :table_name, type: :string, required: true, desc: "ModelName or table_name"
9
+
10
+ desc 'Creates diffit triggers using a ModelName or a table_name provided.'
11
+
12
+ def create_triggers_migration
13
+ detect_table_name!
14
+ basename = "create_#{Diffit.function_name.to_s.underscore}_triggers_on_#{table_name}"
15
+ create_diffit_migration 'migrations/create_triggers.erb', basename
16
+ end
17
+
18
+ protected
19
+
20
+ def detect_table_name!
21
+ return if table_name == table_name.tableize
22
+
23
+ begin
24
+ klass = table_name.classify.constantize
25
+ self.table_name = klass.table_name if klass.respond_to?(:table_name)
26
+ rescue NameError
27
+ end
28
+
29
+ nil
30
+ end
31
+
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: diffit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Denis Lifanov
8
+ - Alexey Gaziev
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-08-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '4.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '4.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: pg
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '0.18'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '0.18'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec-rails
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.2'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.2'
56
+ - !ruby/object:Gem::Dependency
57
+ name: ammeter
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.1'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.1'
70
+ - !ruby/object:Gem::Dependency
71
+ name: simplecov
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.10'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.10'
84
+ - !ruby/object:Gem::Dependency
85
+ name: database_cleaner
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1.4'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.4'
98
+ - !ruby/object:Gem::Dependency
99
+ name: yard
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 0.8.7
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 0.8.7
112
+ - !ruby/object:Gem::Dependency
113
+ name: redcarpet
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '3.2'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '3.2'
126
+ - !ruby/object:Gem::Dependency
127
+ name: github-markup
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '1.3'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '1.3'
140
+ description: Track changes in your tables using PostgreSQL triggers..
141
+ email:
142
+ - inadsence@gmail.com
143
+ - alex.gaziev@gmail.com
144
+ executables: []
145
+ extensions: []
146
+ extra_rdoc_files: []
147
+ files:
148
+ - MIT-LICENSE
149
+ - Rakefile
150
+ - lib/diffit.rb
151
+ - lib/diffit/changes.rb
152
+ - lib/diffit/record.rb
153
+ - lib/diffit/trackable.rb
154
+ - lib/diffit/tracker.rb
155
+ - lib/diffit/version.rb
156
+ - lib/generators/diffit/base_generator.rb
157
+ - lib/generators/diffit/init/init_generator.rb
158
+ - lib/generators/diffit/init/migrations/create_function.erb
159
+ - lib/generators/diffit/init/migrations/create_table.erb
160
+ - lib/generators/diffit/init/templates/diffit.erb
161
+ - lib/generators/diffit/triggers/migrations/create_triggers.erb
162
+ - lib/generators/diffit/triggers/triggers_generator.rb
163
+ homepage: https://github.com/gazay/diffit
164
+ licenses:
165
+ - MIT
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 2.4.5
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: A simple solution to track changes in your tables.
187
+ test_files: []