diffit 0.0.1

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