acts_as_versioner 1.0

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: 4d2358c69672d8feac037526ab9513951a5ae197
4
+ data.tar.gz: 848639453794e3025c584b834ebf073aca5f88c2
5
+ SHA512:
6
+ metadata.gz: ec883fcad954d7ac02bb1b1a223386f980a425a28f2cb20a0241d996337b30465771c2b58fa5deb9c475781e65c2626a0b3493bcea3e0f044caf3c1eb16e89bf
7
+ data.tar.gz: 4cb7cc81cbbab950970caac6657e1dcf455b7b6be99e240ab90843d751abd271ebe54281a29d85c70927f5b4188873717faf20db1eff737379cd3a5065017fcc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in access_checker.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ ActsAsVersioner
2
+ =================
3
+
4
+ This gem is versioning changes of a database table into a underlying table with the same name rules but a "_versions" appendix. Just add
5
+ "acts_as_versioner" in class definition of a model you want versioning to happen. Then create a new table by using the method "create_versioned_table". If you change tables, make sure the changes take also place in the versioned table, you can use the method "adapt_versioned_table" for that (User.adapt_versioned_table).
6
+
7
+ table name: "name of the original table in singluar demodulized form" + "_versions"
8
+ table format:
9
+ - Primary Key: id integer not null auto_increment
10
+ - "action":
11
+ * 0 stands for created
12
+ * 1 stands for updated
13
+ * 2 stands for destroyed
14
+ - From here there are the same columns like in the original table. It must be pointed out that the column "id" of the original table is in the format of the original table in singluar demodulized form" + "_id"
15
+ - Timestamps are automatically added
16
+ - Editor id's are added through Userstamp
17
+
18
+ This gem expands a model class, marked with keyword "acts_as_versioner", with following methods:
19
+
20
+ def get_current_version
21
+ - Returns the current version, here e.g. Entry (table 'entries' and 'entry_versions')
22
+ -> #<Entry::EntryVersion id: 5, entry_id: 2, action: 1, ..., created_by: [User.id], updated_by: [User.id], created_at: [DateTime], updated_at: [DateTime]>
23
+
24
+ def get_versions
25
+ - Returns all versions of a model, here e.g. Entry (table 'entries' and 'entry_versions')
26
+ -> [#<Entry::EntryVersion id: 1, entry_id: 1, action: 0,...>, #<Entry::EntryVersion id: 2, entry_id: 1, action: 1, ...>]
27
+
28
+ Userstamp
29
+ =========
30
+
31
+ This module expects a current_user to be present (devise, authlogic etc). In order to be able to access to current_user in models and modules, necessary methods in application_controller and user.rb have to be available. If there is no current_user Userstamp will set 0 as version editor...
32
+
33
+ app/controllers/application_controller
34
+ ---
35
+
36
+ around_action :setcurrentuser, :except => [:sign_in]
37
+
38
+ ....
39
+
40
+ protected
41
+
42
+ def setcurrentuser
43
+ User.current_user = current_user.nil? ? nil : User.find(current_user.id)
44
+ yield
45
+ ensure
46
+ User.current_user = nil
47
+ end
48
+
49
+ app/models/user.rb
50
+ ---
51
+
52
+ def self.current_user
53
+ Thread.current[:current_user]
54
+ end
55
+
56
+ def self.current_user=(usr)
57
+ Thread.current[:current_user] = usr
58
+ end
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.test_files = FileList['test/test_acts_as_versioner.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ desc "Run tests"
10
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0
@@ -0,0 +1,33 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "acts_as_versioner"
3
+ s.version = "1.0"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Markus Hediger"]
7
+ s.date = "2018-01-01"
8
+ s.description = "Versioning of ar tables"
9
+ s.email = "m.hed@gmx.ch"
10
+ s.extra_rdoc_files = [
11
+ "README.md"
12
+ ]
13
+ s.files = [
14
+ "Gemfile",
15
+ "README.md",
16
+ "Rakefile",
17
+ "VERSION",
18
+ "lib/acts_as_versioner/acts_as_versioner.rb",
19
+ "lib/acts_as_versioner/userstamp.rb",
20
+ "lib/acts_as_versioner.rb",
21
+ "acts_as_versioner.gemspec"
22
+ ]
23
+ s.homepage = "http://github.com/kusihed/acts_as_versioner"
24
+ s.licenses = ["MIT"]
25
+ s.require_paths = ["lib"]
26
+ s.summary = "Versioning of ar tables"
27
+
28
+ s.add_development_dependency "rails", "~> 5.0"
29
+ s.add_development_dependency "bundler", '~> 0'
30
+ s.add_development_dependency "rake", '~> 0'
31
+ s.add_development_dependency "sqlite3", '~> 0'
32
+ end
33
+
@@ -0,0 +1,325 @@
1
+ # This module serves to versioning of data sets. If a new data set is created, updated or destroyed, the old data set gets saved into a second table.
2
+ # The second table has the same name like the original table but is expanded with "Version".
3
+ # => E.g. User -> UserVersions
4
+
5
+ # ActsAsVersioner
6
+
7
+ module ActiveRecord
8
+ module Acts
9
+ module Versioner
10
+
11
+ def self.included(mod)
12
+ mod.extend(ClassMethods)
13
+ end
14
+
15
+ module ClassMethods
16
+ def acts_as_versioner(options = {}, &extension)
17
+ include ActiveRecord::Acts::Versioner::InstanceMethods
18
+
19
+ # don't allow multiple calls
20
+ return if self.included_modules.include?(ActiveRecord::Acts::Versioner::ActMethods)
21
+
22
+ send :include, ActiveRecord::Acts::Versioner::ActMethods
23
+
24
+ cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name
25
+
26
+ send :attr_accessor
27
+
28
+ self.versioned_class_name = options[:class_name] || "#{base_class}Version"
29
+ self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}#{ActiveRecord::Acts::Versioner::configurator[:default_versioned_class_name]}#{table_name_suffix}"
30
+ self.versioned_foreign_key = options[:versioned_foreign_key] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_id" # quick 'n' dirty fix
31
+
32
+ if block_given?
33
+ extension_module_name = "#{versioned_class_name}Extension"
34
+ silence_warnings do
35
+ self.const_set(extension_module_name, Module.new(&extension))
36
+ end
37
+ options[:extend] = self.const_get(extension_module_name)
38
+ end
39
+
40
+ class_eval do
41
+ include options[:extend] if options[:extend].is_a?(Module)
42
+
43
+ before_save :b_s
44
+ before_destroy :b_d
45
+ after_save :a_s
46
+ after_destroy :a_d
47
+ end
48
+
49
+ # create the dynamic versioned model
50
+ const_set(versioned_class_name, Class.new(ApplicationRecord)).class_eval do
51
+ def self.reloadable? ; false ; end
52
+ end
53
+
54
+ versioned_class.table_name = "#{versioned_table_name}"
55
+ versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym, :class_name => "#{self.to_s}::#{versioned_class_name}", :foreign_key => versioned_foreign_key
56
+ versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module)
57
+ end
58
+ end
59
+
60
+ module InstanceMethods
61
+ attr_accessor :acts_as_versioner_model
62
+ attr_accessor :acts_as_versioner_mode
63
+
64
+ # Returns the current version.
65
+ def get_current_version
66
+ instance_eval(self.versioned_class_name).where([self.versioned_foreign_key + ' = ?', self.id]).order("#{ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_at]} desc, id desc").first
67
+ end
68
+
69
+ # Returns all versions of a model.
70
+ def get_versions
71
+ instance_eval(self.versioned_class_name).where([self.versioned_foreign_key + ' = ?', self.id]).order("#{ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_at]} asc, id asc").all
72
+ end
73
+
74
+ # This methods returns all versions of associated tables (table that belong to the existing model).
75
+ def get_versions_children
76
+ associations = Hash.new # result hash
77
+ stack = Array.new # Stack of the same algorithm.
78
+
79
+ # Initiate algorithm with the used model
80
+ versions = instance_eval(self.versioned_class_name).where([self.versioned_foreign_key + ' = ?', self.id]).order("#{ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_at]} asc, #{ActiveRecord::Acts::Versioner::configurator[:default_versioned_created_at]} asc").all
81
+ associations[self.versioned_class_name] = versions # Caching itself in the result hash
82
+ stack.push self.class => versions # Setting itself onto the stack
83
+
84
+ # Main loop of the algorith
85
+ while class_struct = stack.pop
86
+ class_name = nil
87
+ data_set = nil
88
+
89
+ class_struct.each do |key_class_name, value_data_set|
90
+ class_name = key_class_name
91
+ data_set = value_data_set
92
+ end
93
+
94
+ # Read all assocations
95
+ reflection_assoc = Array.new
96
+ reflection_assoc.concat(class_name.reflect_on_all_associations(:has_one))
97
+ reflection_assoc.concat(class_name.reflect_on_all_associations(:has_many))
98
+ reflection_assoc.compact!
99
+
100
+ # Iterate through all associations
101
+ reflection_assoc.each do |association|
102
+ association_klass = association.klass
103
+ # Is there a versioning table? If yes, go back to the beginning of the iteration..
104
+ if association_klass.to_s.include?("version") then next end
105
+ child_associations_has_one = association_klass.reflect_on_all_associations(:has_one)
106
+ child_associations_has_many = association_klass.reflect_on_all_associations(:has_many)
107
+
108
+ # Does the associated table have further associated tables and did they already be iterated through?
109
+ if (child_associations_has_one.empty? || child_associations_has_many.empty?) && associations[association_klass.versioned_class_name] != nil then next end
110
+
111
+ new_data_set = Array.new
112
+ # Check if the table has been visited already. If yes, complete the data sets -> Does only happen if we have a table without associations.
113
+ if associations[association_klass.versioned_class_name] != nil then new_data_set = associations[association_klass.versioned_class_name] end
114
+
115
+ foreign_ids = []
116
+ data_set.each { |data|
117
+ foreign_ids << instance_eval("data." + class_name.to_s.tableize.singularize.downcase + "_id.to_s")
118
+ }
119
+
120
+ unless foreign_ids.blank?
121
+ tmp_new_data_set = association_klass.versioned_class.where(["#{class_name.to_s.tableize.singularize.downcase}_id IN (?)", foreign_ids]).order("#{ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_at]} asc, #{ActiveRecord::Acts::Versioner::configurator[:default_versioned_created_at]} asc").all
122
+ unless tmp_new_data_set.blank? then new_data_set.concat(tmp_new_data_set) end
123
+ end
124
+
125
+ # Cache the found data sets into the result hash
126
+ associations[association_klass.versioned_class_name] = new_data_set
127
+ # Additionally found data sets get saved on the stack for the next iteration
128
+ stack.push association_klass => new_data_set
129
+ end
130
+ end
131
+
132
+ # Remove all double entries
133
+ associations.each do |class_name_to_s, versionArray|
134
+ versionArray.uniq!
135
+ end
136
+
137
+ return associations
138
+ end
139
+
140
+ private
141
+
142
+ # This method overrides the default method "before_save" of the ActiveRecord class.
143
+ # It is invoked before the actual saving takes place and serves to preparing the versioning.
144
+ def b_s
145
+ prepare_versioning
146
+ end
147
+
148
+ # This method overrides the default method "before_destroy" of the ActiveRecord class.
149
+ # It is invoked before the actual destroying takes place and serves to preparing the versioning.
150
+ def b_d
151
+ prepare_versioning 2
152
+ end
153
+
154
+ # This method overrides the default method "after_save" of the ActiveRecord class.
155
+ # It is invoked after the actual saving has token place and serves to execute the versioning.
156
+ def a_s
157
+ do_versioning
158
+ end
159
+
160
+ # This method overrides the default method "after_destroy" of the ActiveRecord class.
161
+ # It is invoked after the actual destroying has token place and serves to execute the versioning.
162
+ def a_d
163
+ do_versioning
164
+ end
165
+
166
+ # This method is preparing the versioning. It copies the current object and saves it into a variable.
167
+ # For the number it is assumed to be between 0 and 2 depending on the mode (0 = insert, 1 = update, 2 = delete).
168
+ def prepare_versioning(mode = 0)
169
+ @acts_as_versioner_mode = mode # mode : 0 = insert, 1 = update, 2 = delete
170
+ @acts_as_versioner_model = self.dup
171
+ @acts_as_versioner_model.updated_at = Time.now
172
+
173
+ if mode == 0 && self.id != nil then @acts_as_versioner_mode = 1 end
174
+ end
175
+
176
+ # In this method the versioning is happening. It expects a copy of the current object in the variable @acts_as_versioner_mode.
177
+ # It will be invoked after the method "prepare_versioning"
178
+ def do_versioning
179
+ attributes = Hash.new
180
+ # Save variables and the values in a hash
181
+ @acts_as_versioner_model.attributes.each do |attribute, value|
182
+ attributes[attribute] = value unless attribute == "id" # ID has to be excluded because MassAssignment warning...
183
+ end
184
+
185
+ @acts_as_versioner_model = nil
186
+
187
+ attributes[self.versioned_foreign_key] = self.id
188
+ attributes[:action] = @acts_as_versioner_mode
189
+
190
+ modelversion = instance_eval(self.versioned_class_name).new(attributes)
191
+ modelversion.save(:validate => false)
192
+ end
193
+ end
194
+
195
+ module ActMethods
196
+ def self.included(base) # :nodoc:
197
+ base.extend ClassMethods
198
+ end
199
+
200
+ private
201
+
202
+ def empty_callback() end #:nodoc:
203
+
204
+ module ClassMethods
205
+
206
+ # Returns an array of columns that are versioned. See non_versioned_columns
207
+ def versioned_columns
208
+ self.columns.select { |c| c.name }
209
+ end
210
+
211
+ # Returns an instance of the dynamic versioned model
212
+ def versioned_class
213
+ const_get versioned_class_name
214
+ end
215
+
216
+ # Rake migration task to create the versioned table
217
+ def create_versioned_table(create_table_options = {})
218
+ versioned_table_name = self.to_s.underscore + ActiveRecord::Acts::Versioner::configurator[:default_versioned_class_name]
219
+ puts table_name
220
+ # create version column in main table if it does not exist
221
+ add_column_to_table(table_name, ActiveRecord::Acts::Versioner::configurator[:default_versioned_created_at], :datetime)
222
+ add_column_to_table(table_name, ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_at], :datetime)
223
+ add_column_to_table(table_name, ActiveRecord::Acts::Versioner::configurator[:default_versioned_created_by], :integer)
224
+ add_column_to_table(table_name, ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_by], :integer)
225
+
226
+
227
+ # create versions table
228
+ self.connection.create_table(versioned_table_name, create_table_options) do |t|
229
+ t.column versioned_foreign_key, :integer
230
+ t.column :action, :integer, :null => false, :default => 0
231
+ end
232
+
233
+ # clone the original table in order to create the versions table
234
+ puts versioned_table_name
235
+ not_versioned = %w{id}
236
+ self.versioned_columns.each do |col|
237
+ unless not_versioned.include?(col.name)
238
+ self.connection.add_column versioned_table_name, col.name, col.type,
239
+ :limit => col.limit,
240
+ :default => col.default
241
+ end
242
+ end
243
+ end
244
+
245
+ def add_column_to_table(table, column, type)
246
+ tabelle = self.connection.execute("show columns from #{table} like '#{column}'")
247
+
248
+ do_add = true
249
+ for res in tabelle
250
+ do_add = false if column.to_s == res.first.to_s
251
+ end
252
+ if do_add
253
+ self.connection.add_column table, column, type
254
+ end
255
+ end
256
+
257
+ # Rake migration task to drop the versioned table
258
+ def drop_versioned_table
259
+ self.connection.drop_table versioned_table_name
260
+ end
261
+
262
+ # If a column is added call this method to adapt the versioned table
263
+ def adapt_versioned_table
264
+ not_versioned = ["id", "action", versioned_foreign_key.to_s]
265
+ versioned_columns = []
266
+ self.connection.execute("show columns from #{versioned_table_name}").each { |col|
267
+ versioned_columns << [col[0], col[1]] unless not_versioned.include?(col[0])
268
+ }
269
+
270
+ missing = []
271
+ changed = []
272
+
273
+ reset_columns = []
274
+ self.connection.execute("show columns from #{table_name}").each { |col|
275
+ reset_columns << [col[0], col[1]] unless not_versioned.include?(col[0])
276
+ }
277
+
278
+ reset_columns.each do |rc|
279
+ found = versioned_columns.detect{ |wc| wc.first == rc.first }
280
+ unless found.blank?
281
+ changed << rc if rc.last.to_s != found.last.to_s
282
+ versioned_columns.delete_if { |k| k.first == rc.first }
283
+ else
284
+ missing << rc
285
+ end
286
+ end
287
+
288
+ # Add new column
289
+ missing.each do |m|
290
+ self.connection.add_column versioned_table_name, m.first, m.last
291
+ end
292
+
293
+ # Change column
294
+ changed.each do |c|
295
+ self.connection.change_column versioned_table_name, c.first, c.last
296
+ end
297
+
298
+ # Remove column
299
+ versioned_columns.each do |vc|
300
+ self.connection.remove_column versioned_table_name, vc.first
301
+ end
302
+ end
303
+
304
+ # You can resurrect a destroyed entry by its versioned foreign key
305
+ def resurrect(id)
306
+ destroyed_version = self.versioned_class.where(self.versioned_foreign_key => id).last
307
+ if destroyed_version && destroyed_version.action == 2
308
+ model = self.new
309
+ self.columns.map{|c| c.name}.each do |c|
310
+ model[c] = destroyed_version[c] unless c == "id"
311
+ model[c] = id if c == "id"
312
+ model[c] = Time.now if c == "updated_at"
313
+ end
314
+ model.save
315
+ return model if model.errors.blank?
316
+ end
317
+ end
318
+
319
+ end
320
+ end
321
+
322
+ end
323
+ end
324
+ end
325
+
@@ -0,0 +1,22 @@
1
+ module Userstamp
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # It's important to use before_save here because of the hierarchy in callbacks (see acts_as_versioner)
6
+ before_save :set_stamps
7
+ end
8
+
9
+ def set_stamps
10
+ if defined?(User)
11
+ stamper = 0 # System
12
+ stamper = User.current_user.id if User.current_user
13
+ if self.id.blank?
14
+ self[ActiveRecord::Acts::Versioner::configurator[:default_versioned_created_by]] = stamper if self.has_attribute? ActiveRecord::Acts::Versioner::configurator[:default_versioned_created_by]
15
+ self[ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_by]] = stamper if self.has_attribute? ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_by]
16
+ else
17
+ self[ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_by]] = stamper if self.has_attribute? ActiveRecord::Acts::Versioner::configurator[:default_versioned_updated_by]
18
+ end
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/acts_as_versioner/acts_as_versioner.rb'
2
+ require File.dirname(__FILE__) + '/acts_as_versioner/userstamp.rb'
3
+ module ActiveRecord::Acts::Versioner
4
+ @@configurator = {
5
+ :default_versioned_class_name => '_versions',
6
+ :default_versioned_created_at => 'created_at',
7
+ :default_versioned_updated_at => 'updated_at',
8
+ :default_versioned_created_by => 'created_by',
9
+ :default_versioned_updated_by => 'updated_by'
10
+ }
11
+ mattr_reader :configurator
12
+ end
13
+
14
+ class ActiveRecord::Base
15
+ include Userstamp
16
+ end
17
+
18
+ ActiveRecord::Base.class_eval do
19
+ include ActiveRecord::Acts::Versioner
20
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_versioner
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Markus Hediger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Versioning of ar tables
70
+ email: m.hed@gmx.ch
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files:
74
+ - README.md
75
+ files:
76
+ - Gemfile
77
+ - README.md
78
+ - Rakefile
79
+ - VERSION
80
+ - acts_as_versioner.gemspec
81
+ - lib/acts_as_versioner.rb
82
+ - lib/acts_as_versioner/acts_as_versioner.rb
83
+ - lib/acts_as_versioner/userstamp.rb
84
+ homepage: http://github.com/kusihed/acts_as_versioner
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.6.13
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Versioning of ar tables
108
+ test_files: []