effective_resources 2.1.3 → 2.2.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
  SHA256:
3
- metadata.gz: 91015a3de5ffe3fd8727b1076558a95eedd3601ce768cf7c24befa42635c6a1b
4
- data.tar.gz: 655794c38fa9b32c7fd40692469fe255152820e497d727a69336429d8f7e3acc
3
+ metadata.gz: 570e952fca070a0e231d90c8f959a80055d6a32e0b1ec51e93ae9c88d90fd44a
4
+ data.tar.gz: c7b247bfb4f12f6368405ac21f0a8591dc5e000d3f4a02c2daf728489d171128
5
5
  SHA512:
6
- metadata.gz: 427908e33318358ca2a00ea9d578585cd18ae62dd952016e129878ebc778303084c8af06ba5dbb2d919440f9d5a0733788895cd8d77a38224915e84381d6755b
7
- data.tar.gz: 53fa67e820609b4349e1623975aee917954d865cf85dba56e27b70000a953b1394d17ab098f684a900cf60274ea25ec71e56c952f5f146822fe04c9bc39299ad
6
+ metadata.gz: eb7ee9c3d1be22a10ad2b2d2b24e21a4af3bb084f8bdf07b484f67922ea68553bb31410562029e6b762076eec1bd6bf9eb510734c3c2c35f4c31d768479443a9
7
+ data.tar.gz: 1e905d78f87ddac0bfb423ad153c1066f9925739bf7f454296b9554c6c4ef6ff4a147b125f98b1a0cddeab61bfaa7e2f710d0121e7376ca7f03b62e2e7b9a09c
@@ -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
@@ -40,7 +40,13 @@ module ActsAsSlugged
40
40
  return super unless args.length == 1
41
41
  return super if block_given?
42
42
 
43
- where(slug: args.first).first || raise(::ActiveRecord::RecordNotFound.new("Couldn't find #{name} with 'slug'=#{args.first}"))
43
+ reloading = instance_variable_get(:@_effective_reloading)
44
+ reloading ||= self.class.instance_variable_get(:@_effective_reloading)
45
+ reloading ||= klass.instance_variable_get(:@_effective_reloading) if respond_to?(:klass)
46
+
47
+ return find_by_id(args.first) if reloading
48
+
49
+ find_by_slug(args.first) || raise(::ActiveRecord::RecordNotFound.new("Couldn't find #{name} with 'slug'=#{args.first}"))
44
50
  end
45
51
 
46
52
  def find_by_slug_or_id(*args)
@@ -68,4 +74,16 @@ module ActsAsSlugged
68
74
  slug_was || slug
69
75
  end
70
76
 
77
+ def to_global_id(**params)
78
+ params[:tenant] = Tenant.current if defined?(Tenant)
79
+ GlobalID.new(URI::GID.build(app: Rails.application.config.global_id.app, model_name: model_name, model_id: to_param, params: params))
80
+ end
81
+
82
+ def reload(options = nil)
83
+ self.class.instance_variable_set(:@_effective_reloading, true)
84
+ retval = super
85
+ self.class.instance_variable_set(:@_effective_reloading, nil)
86
+ retval
87
+ end
88
+
71
89
  end
@@ -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,274 @@ 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
+ raise("expected to find #{relation.klass.name} #{name} reflection") unless reflection
91
+ raise("expected association operation") unless [:eq, :matches, :does_not_match, :sql].include?(operation)
92
+
93
+ # Parse values
94
+ value_ids = value.kind_of?(Array) ? value : (value.to_s.split(/,|\s|\|/) - [nil, '', ' '])
95
+ value_sql = Arel.sql(value) if value.kind_of?(String)
96
+
97
+ # Foreign id and type
98
+ foreign_id = reflection.foreign_key
99
+ foreign_type = reflection.foreign_key.to_s.chomp('_id') + '_type'
100
+
101
+ # belongs_to polymorphic
102
+ retval = if as == :belongs_to_polymorphic
103
+ (type, id) = value.to_s.split('_')
104
+
105
+ if type.present? && id.present? # This was from a polymorphic select
106
+ case operation
107
+ when :eq
108
+ relation.where(foreign_type => type, foreign_id => id)
109
+ when :matches
110
+ relation.where(foreign_type => type, foreign_id => id)
111
+ when :does_not_match
112
+ relation.where.not(foreign_type => type, foreign_id => id)
113
+ when :sql
114
+ if (relation.where(value_sql).present? rescue :invalid) != :invalid
115
+ relation.where(value_sql)
116
+ else
117
+ relation
118
+ end
119
+ end
102
120
  else # Maybe from a string field
103
- collection = relation.none
121
+ associated = relation.none
104
122
 
105
- relation.unscoped.distinct("#{name}_type").pluck("#{name}_type").each do |klass_name|
123
+ relation.unscoped.distinct(foreign_type).pluck(foreign_type).each do |klass_name|
106
124
  next if klass_name.nil?
107
125
 
108
126
  resource = Effective::Resource.new(klass_name)
109
127
  next unless resource.klass.present?
110
128
 
111
- collection = collection.or(relation.where("#{name}_id": resource.search_any(term, fuzzy: fuzzy), "#{name}_type": klass_name))
129
+ associated = associated.or(relation.where(foreign_id => resource.search_any(value), foreign_type => klass_name))
112
130
  end
113
131
 
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
132
+ case operation
133
+ when :eq
134
+ relation.where(id: associated.select(:id))
135
+ when :matches
136
+ relation.where(id: associated.select(:id))
137
+ when :does_not_match
138
+ relation.where.not(id: associated.select(:id))
139
+ when :sql
140
+ if (relation.where(value_sql).present? rescue :invalid) != :invalid
141
+ relation.where(value_sql)
142
+ else
143
+ relation
144
+ end
140
145
  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)
146
+ end
147
+
148
+ # belongs_to non-polymorphic
149
+ elsif as == :belongs_to
150
+ foreign_collection = reflection.klass.all
151
+ foreign_collection = reflection.klass.where(foreign_type => relation.klass.name) if reflection.klass.new.respond_to?(foreign_type)
152
+
153
+ case operation
154
+ when :eq
155
+ associated = foreign_collection.where(id: value_ids)
156
+ relation.where(foreign_id => associated.select(:id))
157
+ when :matches
158
+ associated = Resource.new(foreign_collection).search_any(value)
159
+ relation.where(foreign_id => associated.select(:id))
160
+ when :does_not_match
161
+ associated = Resource.new(foreign_collection).search_any(value)
162
+ relation.where.not(foreign_id => associated.select(:id))
163
+ when :sql
164
+ if (foreign_collection.where(value_sql).present? rescue :invalid) != :invalid
165
+ associated = foreign_collection.where(value_sql)
166
+ relation.where(foreign_id => associated.select(:id))
151
167
  else
152
- relation.where("#{sql_column} >= ? AND #{sql_column} < ?", term, term+1.0)
168
+ relation
153
169
  end
154
- else
155
- relation.where("#{sql_column} = ?", term)
156
170
  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)
171
+
172
+ # has_and_belongs_to_many
173
+ elsif as == :has_and_belongs_to_many
174
+ foreign_collection = reflection.source_reflection.klass.all
175
+
176
+ habtm = foreign_collection.klass.reflect_on_all_associations.find { |ass| ass.macro == :has_and_belongs_to_many && ass.join_table == reflection.join_table }
177
+ raise("expected a matching HABTM reflection") unless habtm
178
+
179
+ case operation
180
+ when :eq
181
+ associated = foreign_collection.where(id: value_ids)
182
+ relation.where(id: associated.joins(habtm.name).select(foreign_id))
183
+ when :matches
184
+ associated = Resource.new(foreign_collection).search_any(value)
185
+ relation.where(id: associated.joins(habtm.name).select(foreign_id))
186
+ when :does_not_match
187
+ associated = Resource.new(foreign_collection).search_any(value)
188
+ relation.where.not(id: associated.joins(habtm.name).select(foreign_id))
189
+ when :sql
190
+ if (foreign_collection.where(value_sql).present? rescue :invalid) != :invalid
191
+ associated = foreign_collection.where(value_sql)
192
+ relation.where(id: associated.joins(habtm.name).select(foreign_id))
161
193
  else
162
- relation.where("#{sql_column} >= ? AND #{sql_column} < ?", term, term+60)
194
+ relation
163
195
  end
196
+ end
197
+
198
+ # has_many through
199
+ elsif reflection.options[:through].present?
200
+ reflected_klass = if reflection.source_reflection.options[:polymorphic]
201
+ reflection.klass
164
202
  else
165
- relation.where("#{sql_column} = ?", term)
203
+ reflection.source_reflection.klass
166
204
  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}%")
205
+
206
+ reflected_id = if reflection.source_reflection.macro == :belongs_to
207
+ reflection.source_reflection.foreign_key # to do check this
176
208
  else
177
- relation.where("#{sql_column} = ?", term)
209
+ reflection.source_reflection.klass.primary_key # group_id
178
210
  end
179
- when :uuid
180
- if fuzzy
181
- relation.where("#{sql_column}::text #{ilike} ?", "%#{term}%")
211
+
212
+ foreign_id = if reflection.through_reflection.macro == :belongs_to
213
+ reflection.through_reflection.klass.primary_key # to do check this
182
214
  else
183
- relation.where("#{sql_column}::text = ?", term)
215
+ reflection.through_reflection.foreign_key # user_id
216
+ end
217
+
218
+ # Build the through collection
219
+ through = reflection.through_reflection.klass.all # group mates
220
+
221
+ if reflection.source_reflection.options[:polymorphic]
222
+ through = through.where(reflection.source_reflection.foreign_type => reflected_klass.name)
223
+ end
224
+
225
+ # Search the associated class
226
+ case operation
227
+ when :eq
228
+ associated = through.where(reflected_id => value_ids)
229
+ relation.where(id: associated.select(foreign_id))
230
+ when :matches
231
+ reflected = Resource.new(reflected_klass).search_any(value)
232
+ associated = through.where(reflected_id => reflected)
233
+ relation.where(id: associated.select(foreign_id))
234
+ when :does_not_match
235
+ reflected = Resource.new(reflected_klass).search_any(value)
236
+ associated = through.where(reflected_id => reflected)
237
+ relation.where.not(id: associated.select(foreign_id))
238
+ when :sql
239
+ if (reflected_klass.where(value_sql).present? rescue :invalid) != :invalid
240
+ reflected = reflected_klass.where(value_sql)
241
+ associated = through.where(reflected_id => reflected)
242
+ relation.where(id: associated.select(foreign_id))
243
+ else
244
+ relation
245
+ end
246
+ end
247
+
248
+ # has_many and has_one
249
+ elsif (as == :has_many || as == :has_one)
250
+ foreign_collection = reflection.klass.all
251
+ foreign_collection = reflection.klass.where(foreign_type => relation.klass.name) if reflection.klass.new.respond_to?(foreign_type)
252
+
253
+ case operation
254
+ when :eq
255
+ associated = foreign_collection.where(id: value_ids)
256
+ relation.where(id: associated.select(foreign_id))
257
+ when :matches
258
+ associated = Resource.new(foreign_collection).search_any(value)
259
+ relation.where(id: associated.select(foreign_id))
260
+ when :does_not_match
261
+ associated = Resource.new(foreign_collection).search_any(value)
262
+ relation.where.not(id: associated.select(foreign_id))
263
+ when :sql
264
+ if (foreign_collection.where(value_sql).present? rescue :invalid) != :invalid
265
+ associated = foreign_collection.where(value_sql)
266
+ relation.where(id: associated.select(foreign_id))
267
+ else
268
+ relation
269
+ end
184
270
  end
185
- else
186
- raise "unsupported sql type #{sql_type}"
271
+ end
272
+
273
+ retval || raise("unable to search associated #{as} #{operation} #{name} for #{value}")
274
+ end
275
+
276
+ def search_attribute(name, value, as:, operation:)
277
+ raise 'expected relation to be present' unless relation
278
+
279
+ attribute = relation.arel_table[name]
280
+
281
+ # Normalize the term.
282
+ # If you pass an email attribute it can return nil so we return the full value
283
+ term = Attribute.new(as).parse(value, name: name) || value
284
+
285
+ searched = case as
286
+ when :date, :datetime
287
+ if value.kind_of?(String)
288
+ end_at = (
289
+ case (value.to_s.scan(/(\d+)/).flatten).length
290
+ when 1 ; term.end_of_year # Year
291
+ when 2 ; term.end_of_month # Year-Month
292
+ when 3 ; term.end_of_day # Year-Month-Day
293
+ when 4 ; term.end_of_hour # Year-Month-Day Hour
294
+ when 5 ; term.end_of_minute # Year-Month-Day Hour-Minute
295
+ when 6 ; term + 1.second # Year-Month-Day Hour-Minute-Second
296
+ else term
297
+ end
298
+ )
299
+
300
+ relation.where(attribute.gteq(term)).where(attribute.lteq(end_at))
301
+ end
302
+
303
+ when :effective_obfuscation
304
+ term = Attribute.new(as, klass: (associated(name).try(:klass) || klass)).parse(value, name: name)
305
+ relation.where(attribute.eq((value == term ? 0 : term)))
306
+
307
+ when :effective_addresses
308
+ association = associated(name)
309
+ associated = Resource.new(association).search_any(value)
310
+ relation.where(id: associated.where(addressable_type: klass.name).select(:addressable_id))
311
+
312
+ when :effective_roles
313
+ relation.with_role(term)
314
+
315
+ when :time
316
+ timed = relation.where("EXTRACT(hour from #{sql_column}) = ?", term.utc.hour)
317
+ timed = timed.where("EXTRACT(minute from #{sql_column}) = ?", term.utc.min) if term.min > 0
318
+ timed
319
+ end
320
+
321
+ return searched if searched
322
+
323
+ # Simple operation search
324
+ case operation
325
+ when :eq then relation.where(attribute.eq(term))
326
+ when :not_eq then relation.where(attribute.not_eq(term))
327
+ when :matches then relation.where(attribute.matches("%#{term}%"))
328
+ when :does_not_match then relation.where(attribute.does_not_match("%#{term}%"))
329
+ when :starts_with then relation.where(attribute.matches("#{term}%"))
330
+ when :ends_with then relation.where(attribute.matches("%#{term}"))
331
+ when :gt then relation.where(attribute.gt(term))
332
+ when :gteq then relation.where(attribute.gteq(term))
333
+ when :lt then relation.where(attribute.lt(term))
334
+ when :lteq then relation.where(attribute.lteq(term))
335
+ else raise("Unexpected operation: #{operation}")
187
336
  end
188
337
  end
189
338
 
@@ -195,11 +344,6 @@ module Effective
195
344
  return relation.where(klass.primary_key => value)
196
345
  end
197
346
 
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
347
  # If the user specifies columns. Filter out invalid ones for this klass
204
348
  if columns.present?
205
349
  columns = Array(columns).map(&:to_s) - [nil, '']
@@ -238,81 +382,6 @@ module Effective
238
382
 
239
383
  private
240
384
 
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
385
  def order_by_associated_conditions(association, sort: nil, direction: :asc, limit: nil)
317
386
  resource = Effective::Resource.new(association)
318
387
 
@@ -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.3'.freeze
2
+ VERSION = '2.2.0'.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.3
4
+ version: 2.2.0
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-28 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