garland 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a00d71fb025dd71c34da87926327385c4278a2a6
4
- data.tar.gz: 09a6733560c52e47f1065e35900ad4bbfd4a15c0
3
+ metadata.gz: 89056f6fadd0309a8e4829b17bdd788cd0643bc9
4
+ data.tar.gz: 33567da54c9bae23ffa87d26bb95ed3fc95795b0
5
5
  SHA512:
6
- metadata.gz: 04640cac5d39141ef5030df7d1b2b3b1e56c3b2b4e4ee31550ac9056675cdd55da7451e0332bc4a8b4e16e8998af9855b4c098ffdcf935afb9d17507c6af8a58
7
- data.tar.gz: ad3318e2a4b883b3cfe199870b30522c2e471088271eee39b58e9cc021c73d4f979dd164e41ae36493ec62fe7581c8e0f74ef0789c84010c28455dc8aa760c4d
6
+ metadata.gz: 17f6eec112cd6888fb8e2eac3bf654facaaf1195658739ee39ad1f2ccc2d834dc491e78b42dc92bc3516a8b62e9d655f27f70a7469318e8ce01c5cc44ceea812
7
+ data.tar.gz: 811a1222362482f420e5fc4db1b915a2a15f60da6a9b84ce39c66d2398c4db02b144167ca69577888d0fd5bd37d7986bf5c575abdca2022059eea54aea5195ce
@@ -1,267 +1,10 @@
1
1
  module GarlandRails
2
2
  DIFF = true
3
3
  SNAPSHOT = false
4
-
5
- module Extend
6
- def self.included(base)
7
- base.extend(self)
8
- end
9
-
10
- def has_many(name, scope = nil, options = {}, &extension)
11
- name_superclass = name.to_s.classify.constantize.superclass
12
- if name_superclass == GarlandRails::Base
13
- # if there are no scope and there are some options,
14
- # scope will contain options, and we need to swap them
15
- if scope.class == Hash
16
- options = scope
17
- scope = nil
18
- end
19
-
20
- belongs_to_type = self.name
21
- scope = -> { where(belongs_to_type: belongs_to_type) }
22
- options = options.merge(foreign_key: "belongs_to_id")
23
- end
24
-
25
- super(name, scope, options, &extension)
26
- end
27
- end
28
-
29
- class Base < Garland
30
- self.table_name = "garlands"
31
-
32
- validates :entity, presence: true
33
- validates_inclusion_of :entity_type, in: [DIFF, SNAPSHOT]
34
-
35
- # for given parent object, described by "belongs_to_id" and "belongs_to_type",
36
- # there are only one record of each type which is head or tail
37
- validates_uniqueness_of :type, scope: [
38
- :belongs_to_id,
39
- :belongs_to_type,
40
- :next_id,
41
- ], conditions: -> { where("next_id IS NULL OR previous_id IS NULL") }
42
-
43
- # everything seems to work without `polymorphic: true`,
44
- # but we will set it just for accordance to docs
45
- def self.belongs_to(name, scope = nil, options = {}, &extension)
46
- if scope.class == Hash
47
- options = scope
48
- scope = nil
49
- end
50
- options = options.merge(foreign_key: "belongs_to_id")
51
-
52
- super(name, scope, options, &extension)
53
- end
54
-
55
- def self.push(args)
56
- return nil unless args.class == Hash
57
-
58
- if args[:hash]
59
- hash = args[:hash]
60
- return nil unless hash.class == Hash
61
-
62
- belongs_to = args[:belongs_to]
63
- if belongs_to
64
- belongs_to_params = self._split_belongs_to(belongs_to)
65
- belongs_to_id, belongs_to_type =
66
- belongs_to_params.values_at(:belongs_to_id, :belongs_to_type)
67
- end
68
- else
69
- hash = args
70
- belongs_to = nil
71
- belongs_to_id = nil
72
- belongs_to_type = nil
73
- end
74
-
75
- head = self.head(belongs_to)
76
- if head
77
- diff = self.insert_diff(hash, belongs_to)
78
- else
79
- diff = self.init(hash, belongs_to)
80
- end
81
-
82
- diff
83
- end
84
-
85
- def self.thread(belongs_to = nil)
86
- if belongs_to
87
- return self.where(
88
- "belongs_to_id = ? AND belongs_to_type = ?",
89
- belongs_to.id, table_type(belongs_to)
90
- )
91
- else
92
- return self.where("belongs_to_id is null AND belongs_to_type is null")
93
- end
94
- end
95
-
96
- def self.tail(belongs_to = nil)
97
- self.thread(belongs_to).find_by(previous_id: nil)
98
- end
99
-
100
- def self.head(belongs_to = nil)
101
- self.thread(belongs_to).find_by(next_id: nil)
102
- end
103
-
104
- def self.last_diff(belongs_to = nil)
105
- head = self.head(belongs_to)
106
- return nil unless head
107
-
108
- head.previous
109
- end
110
-
111
- def next
112
- Garland.find(self.next_id) if self.next_id
113
- end
114
-
115
- def previous
116
- Garland.find(self.previous_id) if self.previous_id
117
- end
118
-
119
- def safe_eval_entity
120
- return nil unless self.entity =~ /\[.*\]/ || self.entity =~ /{.*}/
121
-
122
- eval(self.entity)
123
- end
124
-
125
- def self.table_type(record)
126
- record.class.name
127
- end
128
-
129
- def self.any?(belongs_to = nil)
130
- self.thread(belongs_to).any?
131
- end
132
-
133
- def self.init(hash, belongs_to = nil)
134
- common_props = self._split_belongs_to(belongs_to)
135
-
136
- tail_props = common_props.merge(entity: {}.to_s, entity_type: SNAPSHOT)
137
- brand_new_tail = self.new(tail_props)
138
-
139
- diff = HashDiffSym.diff({}, hash)
140
- first_diff_props = common_props.merge(entity: diff.to_s, entity_type: DIFF)
141
- first_diff = self.new(first_diff_props)
142
-
143
- head_props = common_props.merge(entity: hash.to_s, entity_type: SNAPSHOT)
144
- brand_new_head = self.new(head_props)
145
-
146
- self.transaction do
147
- ActiveRecord::Base.connection.create_savepoint("savepoint_before_init")
148
-
149
- # first id: tail ({})
150
- # second id: head (latest snapshot)
151
- # third+: diffs
152
- unless brand_new_tail.save
153
- Rails.logger.error("Unable to create new tail with props '#{tail_props}'")
154
- ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
155
- return nil
156
- end
157
-
158
- # belongs_to validations were in `brand_new_tail.save`
159
- # here and below validations may be skipped as long as we check for continuity later
160
- brand_new_head.save(validate: false)
161
- first_diff.save(validate: false)
162
- brand_new_tail.update_attribute(:next_id, first_diff.id)
163
- first_diff.update_attribute(:previous_id, brand_new_tail.id)
164
- first_diff.update_attribute(:next_id, brand_new_head.id)
165
- brand_new_head.update_attribute(:previous_id, first_diff.id)
166
-
167
- unless self.continuous?(belongs_to)
168
- Rails.logger.error("Initialized garland is not continuous")
169
- ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_init")
170
- ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
171
- return nil
172
- end
173
- end
174
-
175
- first_diff
176
- end
177
-
178
- def self.insert_diff(hash, belongs_to = nil)
179
- head = self.head(belongs_to)
180
- last_diff = self.find_by(id: head.previous_id)
181
- common_props = self._split_belongs_to(belongs_to)
182
-
183
- diff = HashDiffSym.diff(head.safe_eval_entity, hash)
184
- return unless diff.any?
185
-
186
- new_diff_props = common_props.merge(
187
- entity: diff.to_s,
188
- entity_type: DIFF,
189
- previous_id: last_diff.id,
190
- next_id: head.id,
191
- )
192
- new_diff = self.new(new_diff_props)
193
-
194
- self.transaction do
195
- ActiveRecord::Base.connection.create_savepoint("savepoint_before_insert_diff")
196
-
197
- # insert_diff should not use skipping valudatuons methods
198
- # because we don't want to check for continuity on every push
199
- unless new_diff.save
200
- Rails.logger.error("Unable to create new_diff with props '#{new_diff_props}'")
201
- ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
202
- return nil
203
- end
204
-
205
- last_diff.next_id = new_diff.id
206
- unless last_diff.save
207
- Rails.logger.error("Unable to save last_diff with 'next_id' = '#{new_diff.id}'")
208
- ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff")
209
- ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
210
- return nil
211
- end
212
-
213
- head.previous_id = new_diff.id
214
- unless head.save
215
- Rails.logger.error("Unable to save head with 'previous_id' = '#{new_diff.id}'")
216
- ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff")
217
- ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
218
- return nil
219
- end
220
-
221
- head.entity = hash.to_s
222
- unless head.save
223
- Rails.logger.error("Unable to save head with 'entity' = '#{hash}'")
224
- ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff")
225
- ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
226
- return nil
227
- end
228
- end
229
-
230
- new_diff
231
- end
232
-
233
- def self.continuous?(belongs_to = nil)
234
- tail = self.tail(belongs_to)
235
- head = self.head(belongs_to)
236
- return false unless tail && head
237
-
238
- current_bulb = tail
239
- current_hash = tail.safe_eval_entity
240
- items_counted = 1
241
- while current_bulb.next_id do
242
- items_counted += 1
243
- current_bulb = current_bulb.next
244
- if current_bulb.entity_type == DIFF
245
- current_hash = HashDiffSym.patch!(current_hash, current_bulb.safe_eval_entity)
246
- else
247
- break
248
- end
249
- end
250
-
251
- items_counted == self.thread(belongs_to).size && current_hash == head.safe_eval_entity
252
- end
253
-
254
- private
255
- def self._split_belongs_to(belongs_to)
256
- if belongs_to
257
- belongs_to_id = belongs_to.id
258
- belongs_to_type = table_type(belongs_to)
259
- else
260
- belongs_to_id = nil
261
- belongs_to_type = nil
262
- end
263
-
264
- { belongs_to_id: belongs_to_id, belongs_to_type: belongs_to_type }
265
- end
266
- end
267
4
  end
5
+
6
+ require "garland_rails/extend.rb"
7
+ require "garland_rails/base.rb"
8
+ require "garland_rails/utils.rb"
9
+ require "garland_rails/snapshot.rb"
10
+ require "garland_rails/push.rb"
@@ -0,0 +1,34 @@
1
+ module GarlandRails
2
+ class Base < Garland
3
+ self.table_name = "garlands"
4
+
5
+ validates :entity, presence: true
6
+ validates_inclusion_of :entity_type, in: [DIFF, SNAPSHOT]
7
+
8
+ # for given parent object, described by "belongs_to_id" and "belongs_to_type",
9
+ # there are only one record of each type which is head or tail
10
+ validates_uniqueness_of :type, scope: [
11
+ :belongs_to_id,
12
+ :belongs_to_type,
13
+ :next_id,
14
+ ], conditions: -> { where("next_id IS NULL OR previous_id IS NULL") }
15
+
16
+ # Minitest raises "No visible difference in the *#inspect output" error
17
+ # while comparing Garland objects without this.
18
+ def ==(other)
19
+ self.attributes == other.attributes
20
+ end
21
+
22
+ # everything seems to work without `polymorphic: true`,
23
+ # but we will set it just for accordance to docs
24
+ def self.belongs_to(name, scope = nil, options = {}, &extension)
25
+ if scope.class == Hash
26
+ options = scope
27
+ scope = nil
28
+ end
29
+ options = options.merge(foreign_key: "belongs_to_id")
30
+
31
+ super(name, scope, options, &extension)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ module GarlandRails
2
+ module Extend
3
+ def self.included(base)
4
+ base.extend(self)
5
+ end
6
+
7
+ def has_many(name, scope = nil, options = {}, &extension)
8
+ name_superclass = name.to_s.classify.constantize.superclass
9
+ if name_superclass == GarlandRails::Base
10
+ # if there are no scope and there are some options,
11
+ # scope will contain options, and we need to swap them
12
+ if scope.class == Hash
13
+ options = scope
14
+ scope = nil
15
+ end
16
+
17
+ belongs_to_type = self.name
18
+ scope = -> { where(belongs_to_type: belongs_to_type) }
19
+ options = options.merge(foreign_key: "belongs_to_id")
20
+ end
21
+
22
+ super(name, scope, options, &extension)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,133 @@
1
+ module GarlandRails
2
+ class Base < Garland
3
+ def self.push(args)
4
+ return nil unless args.class == Hash
5
+
6
+ if args[:hash]
7
+ hash = args[:hash]
8
+ return nil unless hash.class == Hash
9
+
10
+ belongs_to = args[:belongs_to]
11
+ if belongs_to
12
+ belongs_to_params = self._split_belongs_to(belongs_to)
13
+ belongs_to_id, belongs_to_type =
14
+ belongs_to_params.values_at(:belongs_to_id, :belongs_to_type)
15
+ end
16
+ else
17
+ hash = args
18
+ belongs_to = nil
19
+ belongs_to_id = nil
20
+ belongs_to_type = nil
21
+ end
22
+
23
+ head = self.head(belongs_to)
24
+ if head
25
+ diff = self.insert_diff(hash, belongs_to)
26
+ else
27
+ diff = self.init(hash, belongs_to)
28
+ end
29
+
30
+ diff
31
+ end
32
+
33
+ def self.init(hash, belongs_to = nil)
34
+ common_props = self._split_belongs_to(belongs_to)
35
+
36
+ tail_props = common_props.merge(entity: {}.to_s, entity_type: SNAPSHOT)
37
+ brand_new_tail = self.new(tail_props)
38
+
39
+ diff = HashDiffSym.diff({}, hash)
40
+ first_diff_props = common_props.merge(entity: diff.to_s, entity_type: DIFF)
41
+ first_diff = self.new(first_diff_props)
42
+
43
+ head_props = common_props.merge(entity: hash.to_s, entity_type: SNAPSHOT)
44
+ brand_new_head = self.new(head_props)
45
+
46
+ self.transaction do
47
+ ActiveRecord::Base.connection.create_savepoint("savepoint_before_init")
48
+
49
+ # first id: tail ({})
50
+ # second id: head (latest snapshot)
51
+ # third+: diffs
52
+ unless brand_new_tail.save
53
+ Rails.logger.error("Unable to create new tail with props '#{tail_props}'")
54
+ ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
55
+ return nil
56
+ end
57
+
58
+ # belongs_to validations were in `brand_new_tail.save`
59
+ # here and below validations may be skipped as long as we check for continuity later
60
+ brand_new_head.save(validate: false)
61
+ first_diff.save(validate: false)
62
+ brand_new_tail.update_attribute(:next_id, first_diff.id)
63
+ first_diff.update_attribute(:previous_id, brand_new_tail.id)
64
+ first_diff.update_attribute(:next_id, brand_new_head.id)
65
+ brand_new_head.update_attribute(:previous_id, first_diff.id)
66
+
67
+ unless self.continuous?(belongs_to)
68
+ Rails.logger.error("Initialized garland is not continuous")
69
+ ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_init")
70
+ ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
71
+ return nil
72
+ end
73
+ end
74
+
75
+ first_diff
76
+ end
77
+
78
+ def self.insert_diff(hash, belongs_to = nil)
79
+ head = self.head(belongs_to)
80
+ last_diff = self.find_by(id: head.previous_id)
81
+ common_props = self._split_belongs_to(belongs_to)
82
+
83
+ diff = HashDiffSym.diff(head.safe_eval_entity, hash)
84
+ return unless diff.any?
85
+
86
+ new_diff_props = common_props.merge(
87
+ entity: diff.to_s,
88
+ entity_type: DIFF,
89
+ previous_id: last_diff.id,
90
+ next_id: head.id,
91
+ )
92
+ new_diff = self.new(new_diff_props)
93
+
94
+ self.transaction do
95
+ ActiveRecord::Base.connection.create_savepoint("savepoint_before_insert_diff")
96
+
97
+ # insert_diff should not use skipping valudatuons methods
98
+ # because we don't want to check for continuity on every push
99
+ unless new_diff.save
100
+ Rails.logger.error("Unable to create new_diff with props '#{new_diff_props}'")
101
+ ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
102
+ return nil
103
+ end
104
+
105
+ last_diff.next_id = new_diff.id
106
+ unless last_diff.save
107
+ Rails.logger.error("Unable to save last_diff with 'next_id' = '#{new_diff.id}'")
108
+ ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff")
109
+ ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
110
+ return nil
111
+ end
112
+
113
+ head.previous_id = new_diff.id
114
+ unless head.save
115
+ Rails.logger.error("Unable to save head with 'previous_id' = '#{new_diff.id}'")
116
+ ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff")
117
+ ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
118
+ return nil
119
+ end
120
+
121
+ head.entity = hash.to_s
122
+ unless head.save
123
+ Rails.logger.error("Unable to save head with 'entity' = '#{hash}'")
124
+ ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff")
125
+ ActiveRecord::Base.connection.release_savepoint("savepoint_before_init")
126
+ return nil
127
+ end
128
+ end
129
+
130
+ new_diff
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,53 @@
1
+ module GarlandRails
2
+ class Base < Garland
3
+ def snapshot(patch_direction = :backward)
4
+ case patch_direction
5
+ when :forward
6
+ approach_nearest_snapshot_method = :previous
7
+ back_method = :next
8
+ patch_method = :patch!
9
+ when :backward
10
+ approach_nearest_snapshot_method = :next
11
+ back_method = :previous
12
+ patch_method = :unpatch!
13
+ end
14
+
15
+ if self.entity_type == SNAPSHOT
16
+ snapshot = self.safe_eval_entity
17
+ else
18
+ nearest_snapshot = self
19
+ while nearest_snapshot.entity_type != SNAPSHOT do
20
+ nearest_snapshot = nearest_snapshot.send(approach_nearest_snapshot_method)
21
+ end
22
+
23
+ snapshot = nearest_snapshot.safe_eval_entity
24
+ current_bulb = nearest_snapshot.send(back_method)
25
+ loop do
26
+ break if current_bulb == self
27
+ HashDiffSym.send(patch_method, snapshot, current_bulb.safe_eval_entity)
28
+ current_bulb = current_bulb.send(back_method)
29
+ end
30
+
31
+ # this comes from asymmetry of Garland model
32
+ #
33
+ # entity => producing snapshot:
34
+ #
35
+ # tail => {}
36
+ # diff1 => hash1
37
+ # diff2 => hash2
38
+ # diff3 => hash3
39
+ # head => hash3
40
+ if patch_direction == :forward
41
+ HashDiffSym.send(patch_method, snapshot, current_bulb.safe_eval_entity)
42
+ end
43
+ end
44
+
45
+ Garland.new(
46
+ entity_type: SNAPSHOT,
47
+ entity: snapshot.to_s,
48
+ created_at: self.created_at,
49
+ type: self.type,
50
+ )
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,86 @@
1
+ module GarlandRails
2
+ class Base < Garland
3
+ # REVIEW: shouldn't thread(), tail() and head() have the same args as push()?
4
+ def self.thread(belongs_to = nil)
5
+ if belongs_to
6
+ return self.where(
7
+ "belongs_to_id = ? AND belongs_to_type = ?",
8
+ belongs_to.id, table_type(belongs_to)
9
+ )
10
+ else
11
+ return self.where("belongs_to_id is null AND belongs_to_type is null")
12
+ end
13
+ end
14
+
15
+ def self.tail(belongs_to = nil)
16
+ self.thread(belongs_to).find_by(previous_id: nil)
17
+ end
18
+
19
+ def self.head(belongs_to = nil)
20
+ self.thread(belongs_to).find_by(next_id: nil)
21
+ end
22
+
23
+ def self.last_diff(belongs_to = nil)
24
+ head = self.head(belongs_to)
25
+ return nil unless head
26
+
27
+ head.previous
28
+ end
29
+
30
+ def self.continuous?(belongs_to = nil)
31
+ tail = self.tail(belongs_to)
32
+ head = self.head(belongs_to)
33
+ return false unless tail && head
34
+
35
+ current_bulb = tail
36
+ current_hash = tail.safe_eval_entity
37
+ items_counted = 1
38
+ while current_bulb.next_id do
39
+ items_counted += 1
40
+ current_bulb = current_bulb.next
41
+ if current_bulb.entity_type == DIFF
42
+ current_hash = HashDiffSym.patch!(current_hash, current_bulb.safe_eval_entity)
43
+ else
44
+ break
45
+ end
46
+ end
47
+
48
+ items_counted == self.thread(belongs_to).size && current_hash == head.safe_eval_entity
49
+ end
50
+
51
+ def self.table_type(record)
52
+ record.class.name
53
+ end
54
+
55
+ def self.any?(belongs_to = nil)
56
+ self.thread(belongs_to).any?
57
+ end
58
+
59
+ def next
60
+ Garland.find(self.next_id) if self.next_id
61
+ end
62
+
63
+ def previous
64
+ Garland.find(self.previous_id) if self.previous_id
65
+ end
66
+
67
+ def safe_eval_entity
68
+ return nil unless self.entity =~ /\[.*\]/ || self.entity =~ /{.*}/
69
+
70
+ eval(self.entity)
71
+ end
72
+
73
+ private
74
+ def self._split_belongs_to(belongs_to)
75
+ if belongs_to
76
+ belongs_to_id = belongs_to.id
77
+ belongs_to_type = table_type(belongs_to)
78
+ else
79
+ belongs_to_id = nil
80
+ belongs_to_type = nil
81
+ end
82
+
83
+ { belongs_to_id: belongs_to_id, belongs_to_type: belongs_to_type }
84
+ end
85
+ end
86
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: garland
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Morozov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-24 00:00:00.000000000 Z
11
+ date: 2017-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashdiff_sym
@@ -33,6 +33,11 @@ extra_rdoc_files: []
33
33
  files:
34
34
  - lib/garland.rb
35
35
  - lib/garland_rails.rb
36
+ - lib/garland_rails/base.rb
37
+ - lib/garland_rails/extend.rb
38
+ - lib/garland_rails/push.rb
39
+ - lib/garland_rails/snapshot.rb
40
+ - lib/garland_rails/utils.rb
36
41
  - lib/generators/garland_rails/install_generator.rb
37
42
  - lib/generators/garland_rails/templates/install_migration.rb
38
43
  - lib/generators/garland_rails/templates/uninstall_migration.rb
@@ -59,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
64
  version: '0'
60
65
  requirements: []
61
66
  rubyforge_project:
62
- rubygems_version: 2.6.6
67
+ rubygems_version: 2.5.2
63
68
  signing_key:
64
69
  specification_version: 4
65
70
  summary: HashDiff Rails storage