effective_resources 2.1.4 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c6a9d06e7d62a4fa96c365b8e6386d4229585ed5c0963b3f55b435ed4b7cbf4
4
- data.tar.gz: 84459f1187673d432756a4e3fccf6b163f3b5f55af26a7c1c485938e892d9e95
3
+ metadata.gz: 8f9e795410769087135de28ce4b8c46a48de30d72dc9feeaa48a96966f059c43
4
+ data.tar.gz: 488c2b300e0b05e1c111093e903aae654ab7867f952b66b81dbdd2c5fce46159
5
5
  SHA512:
6
- metadata.gz: f76ef089d1b702f51948cf5ec7b03fc8fe9529428190225b9e27bddbc91c83c3e8d9b01911a2ae5d97efcae22cd8e2f77dd0d642b32b5184b459a6ccb4e3bbfa
7
- data.tar.gz: 6e13de73624c865c3312d2af94816890769599ed2aa1d89e4cde5251d545d25583a14293b04d2a152cc493c2c6c222015cba326e09dec19310771668bda2b8ab
6
+ metadata.gz: b6a2ca614bafbeef5787bcaf49a68d467c0707c3610e5b692d4d58d40295a630b15baa828c2ec71d8837c76372817db6a4ec78e4eb96b391517815d1edcfae99
7
+ data.tar.gz: 0deb208420dc1591c95cd23735ef4c4f843411d91a071cc515f492225e8c1a63f9d3415c66257357d7651aed58ca3f3f842a6508aa8eb1e1b0cd658325071879
@@ -68,6 +68,7 @@ module ActsAsPurchasableWizard
68
68
  order.billing_address = owner.billing_address if owner.try(:billing_address).present?
69
69
 
70
70
  # Important to add/remove anything
71
+ # This will update the prices, but the purchasables must be persisted
71
72
  order.save!
72
73
 
73
74
  order
@@ -73,13 +73,13 @@ module Effective
73
73
  { as: :string }
74
74
  else
75
75
  if res.klass.unscoped.respond_to?(:datatables_scope)
76
- { collection: res.klass.datatables_scope.map { |obj| [obj.to_s, obj.to_param] } }
76
+ { collection: res.klass.datatables_scope.map { |obj| [obj.to_s, obj.id] } }
77
77
  elsif res.klass.unscoped.respond_to?(:datatables_filter)
78
- { collection: res.klass.datatables_filter.map { |obj| [obj.to_s, obj.to_param] } }
78
+ { collection: res.klass.datatables_filter.map { |obj| [obj.to_s, obj.id] } }
79
79
  elsif res.klass.unscoped.respond_to?(:sorted)
80
- { collection: res.klass.sorted.map { |obj| [obj.to_s, obj.to_param] } }
80
+ { collection: res.klass.sorted.map { |obj| [obj.to_s, obj.id] } }
81
81
  else
82
- { collection: res.klass.all.map { |obj| [obj.to_s, obj.to_param] }.sort { |x, y| x[0] <=> y[0] } }
82
+ { collection: res.klass.all.map { |obj| [obj.to_s, obj.id] }.sort { |x, y| x[0] <=> y[0] } }
83
83
  end
84
84
  end
85
85
  end
@@ -65,125 +65,279 @@ module Effective
65
65
  end
66
66
  end
67
67
 
68
- def search(name, value, as: nil, fuzzy: true, sql_column: nil)
68
+ def search(name, value, as: nil, column: nil, operation: nil)
69
69
  raise 'expected relation to be present' unless relation
70
70
 
71
- sql_column ||= sql_column(name)
72
- sql_type = (as || sql_type(name))
73
- fuzzy = true unless fuzzy == false
71
+ sql_as = (as || sql_type(name))
72
+ sql_column = (column || sql_column(name))
73
+ sql_operation = (operation || sql_operation(name, as: sql_as)).to_sym
74
74
 
75
75
  if ['SUM(', 'COUNT(', 'MAX(', 'MIN(', 'AVG('].any? { |str| sql_column.to_s.include?(str) }
76
76
  return relation.having("#{sql_column} = ?", value)
77
77
  end
78
78
 
79
- association = associated(name)
80
-
81
- term = Effective::Attribute.new(sql_type, klass: (association.try(:klass) rescue nil) || klass).parse(value, name: name)
82
-
83
- # term == 'nil' rescue false is a Rails 4.1 fix, where you can't compare a TimeWithZone to 'nil'
84
- if (term == 'nil' rescue false) && ![:has_and_belongs_to_many, :has_many, :has_one, :belongs_to, :belongs_to_polymorphic, :effective_roles].include?(sql_type)
85
- return relation.where(is_null(sql_column))
79
+ case sql_as
80
+ when :belongs_to, :belongs_to_polymorphic, :has_and_belongs_to_many, :has_many, :has_one
81
+ search_associated(name, value, as: sql_as, operation: sql_operation)
82
+ else
83
+ return relation.where(is_null(sql_column)) if value.to_s == 'nil'
84
+ search_attribute(name, value, as: sql_as, operation: sql_operation)
86
85
  end
86
+ end
87
87
 
88
- case sql_type
89
- when :belongs_to
90
- if term == 'nil'
91
- relation.where(is_null(association.foreign_key))
92
- else
93
- relation.where(search_by_associated_conditions(association, term, fuzzy: fuzzy))
94
- end
95
- when :belongs_to_polymorphic
96
- (type, id) = term.split('_')
97
-
98
- if term == 'nil'
99
- relation.where(is_null("#{sql_column}_id")).where(is_null("#{sql_column}_type"))
100
- elsif type.present? && id.present? # This was from a polymorphic select
101
- relation.where("#{sql_column}_id = ?", id).where("#{sql_column}_type = ?", type)
88
+ def search_associated(name, value, as:, operation:)
89
+ reflection = associated(name)
90
+
91
+ raise("expected to find #{relation.klass.name} #{name} reflection") unless reflection
92
+ raise("unexpected search_associated operation #{operation || 'nil'}") unless [:eq, :matches, :does_not_match, :sql].include?(operation)
93
+
94
+ # Parse values
95
+ value_ids = value.kind_of?(Array) ? value : (value.to_s.split(/,|\s|\|/) - [nil, '', ' '])
96
+ value_sql = Arel.sql(value) if value.kind_of?(String)
97
+
98
+ # Foreign id and type
99
+ foreign_id = reflection.foreign_key
100
+ foreign_type = reflection.foreign_key.to_s.chomp('_id') + '_type'
101
+
102
+ # belongs_to polymorphic
103
+ retval = if as == :belongs_to_polymorphic
104
+ (type, id) = value.to_s.split('_')
105
+
106
+ if type.present? && id.present? # This was from a polymorphic select
107
+ case operation
108
+ when :eq
109
+ relation.where(foreign_type => type, foreign_id => id)
110
+ when :matches
111
+ relation.where(foreign_type => type, foreign_id => id)
112
+ when :does_not_match
113
+ relation.where.not(foreign_type => type, foreign_id => id)
114
+ when :sql
115
+ if (relation.where(value_sql).present? rescue :invalid) != :invalid
116
+ relation.where(value_sql)
117
+ else
118
+ relation
119
+ end
120
+ end
102
121
  else # Maybe from a string field
103
- collection = relation.none
122
+ associated = relation.none
104
123
 
105
- relation.unscoped.distinct("#{name}_type").pluck("#{name}_type").each do |klass_name|
124
+ relation.unscoped.distinct(foreign_type).pluck(foreign_type).each do |klass_name|
106
125
  next if klass_name.nil?
107
126
 
108
127
  resource = Effective::Resource.new(klass_name)
109
128
  next unless resource.klass.present?
110
129
 
111
- collection = collection.or(relation.where("#{name}_id": resource.search_any(term, fuzzy: fuzzy), "#{name}_type": klass_name))
130
+ associated = associated.or(relation.where(foreign_id => resource.search_any(value), foreign_type => klass_name))
112
131
  end
113
132
 
114
- collection
115
- end
116
- when :has_and_belongs_to_many, :has_many, :has_one
117
- relation.where(search_by_associated_conditions(association, term, fuzzy: fuzzy))
118
- when :effective_addresses
119
- relation.where(id: Effective::Resource.new(association).search_any(value, fuzzy: fuzzy).pluck(:addressable_id))
120
- when :effective_obfuscation
121
- # If value == term, it's an invalid deobfuscated id
122
- relation.where("#{sql_column} = ?", (value == term ? 0 : term))
123
- when :effective_roles
124
- relation.with_role(term)
125
- when :active_storage
126
- relation.send("with_attached_#{name}").references("#{name}_attachment")
127
- .where(ActiveStorage::Blob.arel_table[:filename].matches("%#{term}%"))
128
- when :boolean
129
- relation.where("#{sql_column} = ?", term)
130
- when :datetime, :date
131
- end_at = (
132
- case (value.to_s.scan(/(\d+)/).flatten).length
133
- when 1 ; term.end_of_year # Year
134
- when 2 ; term.end_of_month # Year-Month
135
- when 3 ; term.end_of_day # Year-Month-Day
136
- when 4 ; term.end_of_hour # Year-Month-Day Hour
137
- when 5 ; term.end_of_minute # Year-Month-Day Hour-Minute
138
- when 6 ; term + 1.second # Year-Month-Day Hour-Minute-Second
139
- else term
133
+ case operation
134
+ when :eq
135
+ relation.where(id: associated.select(:id))
136
+ when :matches
137
+ relation.where(id: associated.select(:id))
138
+ when :does_not_match
139
+ relation.where.not(id: associated.select(:id))
140
+ when :sql
141
+ if (relation.where(value_sql).present? rescue :invalid) != :invalid
142
+ relation.where(value_sql)
143
+ else
144
+ relation
145
+ end
140
146
  end
141
- )
142
- relation.where("#{sql_column} >= ? AND #{sql_column} <= ?", term, end_at)
143
- when :time
144
- timed = relation.where("EXTRACT(hour from #{sql_column}) = ?", term.utc.hour)
145
- timed = timed.where("EXTRACT(minute from #{sql_column}) = ?", term.utc.min) if term.min > 0
146
- timed
147
- when :decimal, :currency
148
- if fuzzy && (term.round(0) == term) && value.to_s.include?('.') == false
149
- if term < 0
150
- relation.where("#{sql_column} <= ? AND #{sql_column} > ?", term, term-1.0)
147
+ end
148
+
149
+ # belongs_to non-polymorphic
150
+ elsif as == :belongs_to
151
+ foreign_collection = reflection.klass.all
152
+ foreign_collection = reflection.klass.where(foreign_type => relation.klass.name) if reflection.klass.new.respond_to?(foreign_type)
153
+
154
+ case operation
155
+ when :eq
156
+ associated = foreign_collection.where(id: value_ids)
157
+ relation.where(foreign_id => associated.select(:id))
158
+ when :matches
159
+ associated = Resource.new(foreign_collection).search_any(value)
160
+ relation.where(foreign_id => associated.select(:id))
161
+ when :does_not_match
162
+ associated = Resource.new(foreign_collection).search_any(value)
163
+ relation.where.not(foreign_id => associated.select(:id))
164
+ when :sql
165
+ if (foreign_collection.where(value_sql).present? rescue :invalid) != :invalid
166
+ associated = foreign_collection.where(value_sql)
167
+ relation.where(foreign_id => associated.select(:id))
151
168
  else
152
- relation.where("#{sql_column} >= ? AND #{sql_column} < ?", term, term+1.0)
169
+ relation
153
170
  end
154
- else
155
- relation.where("#{sql_column} = ?", term)
156
171
  end
157
- when :duration
158
- if fuzzy && (term % 60 == 0) && value.to_s.include?('m') == false
159
- if term < 0
160
- relation.where("#{sql_column} <= ? AND #{sql_column} > ?", term, term-60)
172
+
173
+ # has_and_belongs_to_many
174
+ elsif as == :has_and_belongs_to_many
175
+ foreign_collection = reflection.source_reflection.klass.all
176
+
177
+ habtm = foreign_collection.klass.reflect_on_all_associations.find { |ass| ass.macro == :has_and_belongs_to_many && ass.join_table == reflection.join_table }
178
+ raise("expected a matching HABTM reflection") unless habtm
179
+
180
+ case operation
181
+ when :eq
182
+ associated = foreign_collection.where(id: value_ids)
183
+ relation.where(id: associated.joins(habtm.name).select(foreign_id))
184
+ when :matches
185
+ associated = Resource.new(foreign_collection).search_any(value)
186
+ relation.where(id: associated.joins(habtm.name).select(foreign_id))
187
+ when :does_not_match
188
+ associated = Resource.new(foreign_collection).search_any(value)
189
+ relation.where.not(id: associated.joins(habtm.name).select(foreign_id))
190
+ when :sql
191
+ if (foreign_collection.where(value_sql).present? rescue :invalid) != :invalid
192
+ associated = foreign_collection.where(value_sql)
193
+ relation.where(id: associated.joins(habtm.name).select(foreign_id))
161
194
  else
162
- relation.where("#{sql_column} >= ? AND #{sql_column} < ?", term, term+60)
195
+ relation
163
196
  end
197
+ end
198
+
199
+ # has_many through
200
+ elsif reflection.options[:through].present?
201
+ reflected_klass = if reflection.source_reflection.options[:polymorphic]
202
+ reflection.klass
164
203
  else
165
- relation.where("#{sql_column} = ?", term)
204
+ reflection.source_reflection.klass
166
205
  end
167
- when :integer
168
- relation.where("#{sql_column} = ?", term)
169
- when :percent
170
- relation.where("#{sql_column} = ?", term)
171
- when :price
172
- relation.where("#{sql_column} = ?", term)
173
- when :string, :text, :email
174
- if fuzzy
175
- relation.where("#{sql_column} #{ilike} ?", "%#{term}%")
206
+
207
+ reflected_id = if reflection.source_reflection.macro == :belongs_to
208
+ reflection.source_reflection.foreign_key # to do check this
176
209
  else
177
- relation.where("#{sql_column} = ?", term)
210
+ reflection.source_reflection.klass.primary_key # group_id
178
211
  end
179
- when :uuid
180
- if fuzzy
181
- relation.where("#{sql_column}::text #{ilike} ?", "%#{term}%")
212
+
213
+ foreign_id = if reflection.through_reflection.macro == :belongs_to
214
+ reflection.through_reflection.klass.primary_key # to do check this
182
215
  else
183
- relation.where("#{sql_column}::text = ?", term)
216
+ reflection.through_reflection.foreign_key # user_id
217
+ end
218
+
219
+ # Build the through collection
220
+ through = reflection.through_reflection.klass.all # group mates
221
+
222
+ if reflection.source_reflection.options[:polymorphic]
223
+ through = through.where(reflection.source_reflection.foreign_type => reflected_klass.name)
224
+ end
225
+
226
+ # Search the associated class
227
+ case operation
228
+ when :eq
229
+ associated = through.where(reflected_id => value_ids)
230
+ relation.where(id: associated.select(foreign_id))
231
+ when :matches
232
+ reflected = Resource.new(reflected_klass).search_any(value)
233
+ associated = through.where(reflected_id => reflected)
234
+ relation.where(id: associated.select(foreign_id))
235
+ when :does_not_match
236
+ reflected = Resource.new(reflected_klass).search_any(value)
237
+ associated = through.where(reflected_id => reflected)
238
+ relation.where.not(id: associated.select(foreign_id))
239
+ when :sql
240
+ if (reflected_klass.where(value_sql).present? rescue :invalid) != :invalid
241
+ reflected = reflected_klass.where(value_sql)
242
+ associated = through.where(reflected_id => reflected)
243
+ relation.where(id: associated.select(foreign_id))
244
+ else
245
+ relation
246
+ end
247
+ end
248
+
249
+ # has_many and has_one
250
+ elsif (as == :has_many || as == :has_one)
251
+ foreign_collection = reflection.klass.all
252
+ foreign_collection = reflection.klass.where(foreign_type => relation.klass.name) if reflection.klass.new.respond_to?(foreign_type)
253
+
254
+ case operation
255
+ when :eq
256
+ associated = foreign_collection.where(id: value_ids)
257
+ relation.where(id: associated.select(foreign_id))
258
+ when :matches
259
+ associated = Resource.new(foreign_collection).search_any(value)
260
+ relation.where(id: associated.select(foreign_id))
261
+ when :does_not_match
262
+ associated = Resource.new(foreign_collection).search_any(value)
263
+ relation.where.not(id: associated.select(foreign_id))
264
+ when :sql
265
+ if (foreign_collection.where(value_sql).present? rescue :invalid) != :invalid
266
+ associated = foreign_collection.where(value_sql)
267
+ relation.where(id: associated.select(foreign_id))
268
+ else
269
+ relation
270
+ end
184
271
  end
185
- else
186
- raise "unsupported sql type #{sql_type}"
272
+ end
273
+
274
+ retval || raise("unable to search associated #{as} #{operation} #{name} for #{value}")
275
+ end
276
+
277
+ def search_attribute(name, value, as:, operation:)
278
+ raise 'expected relation to be present' unless relation
279
+
280
+ attribute = relation.arel_table[name]
281
+
282
+ # Normalize the term.
283
+ # If you pass an email attribute it can return nil so we return the full value
284
+ term = Attribute.new(as).parse(value, name: name) || value
285
+
286
+ searched = case as
287
+ when :active_storage
288
+ relation.send("with_attached_#{name}").references("#{name}_attachment")
289
+ .where(ActiveStorage::Blob.arel_table[:filename].matches("%#{term}%"))
290
+
291
+ when :date, :datetime
292
+ if value.kind_of?(String)
293
+ end_at = (
294
+ case (value.to_s.scan(/(\d+)/).flatten).length
295
+ when 1 ; term.end_of_year # Year
296
+ when 2 ; term.end_of_month # Year-Month
297
+ when 3 ; term.end_of_day # Year-Month-Day
298
+ when 4 ; term.end_of_hour # Year-Month-Day Hour
299
+ when 5 ; term.end_of_minute # Year-Month-Day Hour-Minute
300
+ when 6 ; term + 1.second # Year-Month-Day Hour-Minute-Second
301
+ else term
302
+ end
303
+ )
304
+
305
+ relation.where(attribute.gteq(term)).where(attribute.lteq(end_at))
306
+ end
307
+
308
+ when :effective_obfuscation
309
+ term = Attribute.new(as, klass: (associated(name).try(:klass) || klass)).parse(value, name: name)
310
+ relation.where(attribute.eq((value == term ? 0 : term)))
311
+
312
+ when :effective_addresses
313
+ association = associated(name)
314
+ associated = Resource.new(association).search_any(value)
315
+ relation.where(id: associated.where(addressable_type: klass.name).select(:addressable_id))
316
+
317
+ when :effective_roles
318
+ relation.with_role(term)
319
+
320
+ when :time
321
+ timed = relation.where("EXTRACT(hour from #{sql_column}) = ?", term.utc.hour)
322
+ timed = timed.where("EXTRACT(minute from #{sql_column}) = ?", term.utc.min) if term.min > 0
323
+ timed
324
+ end
325
+
326
+ return searched if searched
327
+
328
+ # Simple operation search
329
+ case operation
330
+ when :eq then relation.where(attribute.eq(term))
331
+ when :not_eq then relation.where(attribute.not_eq(term))
332
+ when :matches then relation.where(attribute.matches("%#{term}%"))
333
+ when :does_not_match then relation.where(attribute.does_not_match("%#{term}%"))
334
+ when :starts_with then relation.where(attribute.matches("#{term}%"))
335
+ when :ends_with then relation.where(attribute.matches("%#{term}"))
336
+ when :gt then relation.where(attribute.gt(term))
337
+ when :gteq then relation.where(attribute.gteq(term))
338
+ when :lt then relation.where(attribute.lt(term))
339
+ when :lteq then relation.where(attribute.lteq(term))
340
+ else raise("Unexpected operation: #{operation}")
187
341
  end
188
342
  end
189
343
 
@@ -195,11 +349,6 @@ module Effective
195
349
  return relation.where(klass.primary_key => value)
196
350
  end
197
351
 
198
- # If the value is 3-something-like-this
199
- if (values = value.to_s.split('-')).length > 0 && (maybe_id = values.first).present?
200
- return relation.where(klass.primary_key => maybe_id) if (maybe_id.to_i.to_s == maybe_id)
201
- end
202
-
203
352
  # If the user specifies columns. Filter out invalid ones for this klass
204
353
  if columns.present?
205
354
  columns = Array(columns).map(&:to_s) - [nil, '']
@@ -238,81 +387,6 @@ module Effective
238
387
 
239
388
  private
240
389
 
241
- def search_by_associated_conditions(association, value, fuzzy: nil)
242
- resource = Effective::Resource.new(association)
243
-
244
- # Search the target model for its matching records / keys
245
- relation = resource.search_any(value, fuzzy: fuzzy)
246
-
247
- if association.options[:as] # polymorphic
248
- relation = relation.where(association.type => klass.name)
249
- end
250
-
251
- # key: the id, or associated_id on my table
252
- # keys: the ids themselves as per the target table
253
- if association.macro == :belongs_to && association.options[:polymorphic]
254
- key = sql_column(association.foreign_key)
255
- keys = relation.pluck((relation.klass.primary_key rescue nil))
256
- elsif association.macro == :belongs_to
257
- key = sql_column(association.foreign_key)
258
- keys = relation.pluck(association.klass.primary_key)
259
- elsif association.macro == :has_and_belongs_to_many
260
- key = sql_column(klass.primary_key)
261
- values = relation.pluck(association.source_reflection.klass.primary_key).uniq.compact
262
-
263
- keys = if value == 'nil'
264
- klass.where.not(klass.primary_key => klass.joins(association.name)).pluck(klass.primary_key)
265
- else
266
- klass.joins(association.name)
267
- .where(association.name => { association.source_reflection.klass.primary_key => values })
268
- .pluck(klass.primary_key)
269
- end
270
- elsif association.options[:through].present?
271
- scope = association.through_reflection.klass.all
272
-
273
- if association.source_reflection.options[:polymorphic]
274
- reflected_klass = association.klass
275
- scope = scope.where(association.source_reflection.foreign_type => reflected_klass.name)
276
- else
277
- reflected_klass = association.source_reflection.klass
278
- end
279
-
280
- if association.through_reflection.macro == :belongs_to
281
- key = association.through_reflection.foreign_key
282
- pluck_key = association.through_reflection.klass.primary_key
283
- else
284
- key = sql_column(klass.primary_key)
285
- pluck_key = association.through_reflection.foreign_key
286
- end
287
-
288
- if value == 'nil'
289
- keys = klass.where.not(klass.primary_key => scope.pluck(pluck_key)).pluck(klass.primary_key)
290
- else
291
- keys = scope.where(association.source_reflection.foreign_key => relation).pluck(pluck_key)
292
- end
293
-
294
- elsif association.macro == :has_many
295
- key = sql_column(klass.primary_key)
296
-
297
- keys = if value == 'nil'
298
- klass.where.not(klass.primary_key => resource.klass.pluck(association.foreign_key)).pluck(klass.primary_key)
299
- else
300
- relation.pluck(association.foreign_key)
301
- end
302
-
303
- elsif association.macro == :has_one
304
- key = sql_column(klass.primary_key)
305
-
306
- keys = if value == 'nil'
307
- klass.where.not(klass.primary_key => resource.klass.pluck(association.foreign_key)).pluck(klass.primary_key)
308
- else
309
- relation.pluck(association.foreign_key)
310
- end
311
- end
312
-
313
- "#{key} IN (#{(keys.uniq.compact.presence || [0]).join(',')})"
314
- end
315
-
316
390
  def order_by_associated_conditions(association, sort: nil, direction: :asc, limit: nil)
317
391
  resource = Effective::Resource.new(association)
318
392
 
@@ -38,6 +38,15 @@ module Effective
38
38
  name.to_s.downcase == 'desc' ? 'DESC' : 'ASC'
39
39
  end
40
40
 
41
+ def sql_operation(name, as: nil)
42
+ sql_type = (as || sql_type(name))
43
+
44
+ case sql_type
45
+ when :boolean, :decimal, :integer, :price, :date, :datetime, :percent then :eq
46
+ else :matches
47
+ end
48
+ end
49
+
41
50
  # This is for EffectiveDatatables (col as:)
42
51
  # Might be :name, or 'users.name'
43
52
  def sql_type(name)
@@ -1,3 +1,3 @@
1
1
  module EffectiveResources
2
- VERSION = '2.1.4'.freeze
2
+ VERSION = '2.2.1'.freeze
3
3
  end
@@ -172,4 +172,10 @@ module EffectiveResources
172
172
 
173
173
  end
174
174
 
175
+ def self.replace_nested_attributes(attributes)
176
+ attributes.reject { |k, values| truthy?(values[:_destroy]) }.inject({}) do |h, (key, values)|
177
+ h[key] = values.reject { |k, v| k == 'id' || k == '_destroy' }; h
178
+ end
179
+ end
180
+
175
181
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-29 00:00:00.000000000 Z
11
+ date: 2023-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails