Module: HasCustomFields
- Extended by:
- ActiveSupport::Concern
- Defined in:
- app/models/concerns/has_custom_fields.rb
Defined Under Namespace
Modules: Helpers Classes: NotPreloadedError, PreloadedProxy
Constant Summary collapse
- CUSTOM_FIELDS_MAX_ITEMS =
100
- CUSTOM_FIELDS_MAX_VALUE_LENGTH =
10_000_000
Instance Method Summary collapse
- #clear_custom_fields ⇒ Object
-
#create_singular(name, value, field_type = nil) ⇒ Object
We support unique indexes on certain fields.
- #custom_field_preloaded?(name) ⇒ Boolean
- #custom_fields ⇒ Object
- #custom_fields=(data) ⇒ Object
- #custom_fields_clean? ⇒ Boolean
- #custom_fields_preloaded? ⇒ Boolean
- #on_custom_fields_change ⇒ Object
- #reload(options = nil) ⇒ Object
- #save_custom_fields(force = false) ⇒ Object
- #set_preloaded_custom_fields(custom_fields) ⇒ Object
-
#upsert_custom_fields(fields) ⇒ Object
‘upsert_custom_fields` will only insert/update existing fields, and will not delete anything.
Instance Method Details
#clear_custom_fields ⇒ Object
185 186 187 188 |
# File 'app/models/concerns/has_custom_fields.rb', line 185 def clear_custom_fields @custom_fields = nil @custom_fields_orig = nil end |
#create_singular(name, value, field_type = nil) ⇒ Object
We support unique indexes on certain fields. In the event two concurrent processes attempt to update the same custom field we should catch the error and perform an update instead.
309 310 311 312 313 314 315 316 317 318 319 |
# File 'app/models/concerns/has_custom_fields.rb', line 309 def create_singular(name, value, field_type = nil) write_value = value.is_a?(Hash) || field_type == :json ? value.to_json : value write_value = "t" if write_value.is_a?(TrueClass) write_value = "f" if write_value.is_a?(FalseClass) row_count = DB.exec(<<~SQL, name: name, value: write_value, id: id, now: Time.zone.now) INSERT INTO #{_custom_fields.table_name} (#{custom_fields_fk}, name, value, created_at, updated_at) VALUES (:id, :name, :value, :now, :now) ON CONFLICT DO NOTHING SQL _custom_fields.where(name: name).update_all(value: write_value) if row_count == 0 end |
#custom_field_preloaded?(name) ⇒ Boolean
181 182 183 |
# File 'app/models/concerns/has_custom_fields.rb', line 181 def custom_field_preloaded?(name) @preloaded_custom_fields && @preloaded_custom_fields.key?(name) end |
#custom_fields ⇒ Object
218 219 220 221 222 223 224 |
# File 'app/models/concerns/has_custom_fields.rb', line 218 def custom_fields if @preloaded_custom_fields return @preloaded_proxy ||= PreloadedProxy.new(@preloaded_custom_fields, self.class.to_s) end @custom_fields ||= refresh_custom_fields_from_db.dup end |
#custom_fields=(data) ⇒ Object
226 227 228 |
# File 'app/models/concerns/has_custom_fields.rb', line 226 def custom_fields=(data) custom_fields.replace(data) end |
#custom_fields_clean? ⇒ Boolean
230 231 232 233 |
# File 'app/models/concerns/has_custom_fields.rb', line 230 def custom_fields_clean? # Check whether the cached version has been changed on this model !@custom_fields || @custom_fields_orig == @custom_fields end |
#custom_fields_preloaded? ⇒ Boolean
177 178 179 |
# File 'app/models/concerns/has_custom_fields.rb', line 177 def custom_fields_preloaded? !!@preloaded_custom_fields end |
#on_custom_fields_change ⇒ Object
172 173 174 175 |
# File 'app/models/concerns/has_custom_fields.rb', line 172 def on_custom_fields_change # Callback when custom fields have changed # Override in model end |
#reload(options = nil) ⇒ Object
167 168 169 170 |
# File 'app/models/concerns/has_custom_fields.rb', line 167 def reload( = nil) clear_custom_fields super end |
#save_custom_fields(force = false) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'app/models/concerns/has_custom_fields.rb', line 250 def save_custom_fields(force = false) if force || !custom_fields_clean? dup = @custom_fields.dup.with_indifferent_access array_fields = {} ActiveRecord::Base.transaction do _custom_fields.reload.each do |f| if dup[f.name].is_a?(Array) # we need to collect Arrays fully before we can compare them if !array_fields.has_key?(f.name) array_fields[f.name] = [f] else array_fields[f.name] << f end elsif dup[f.name].is_a?(Hash) if dup[f.name].to_json != f.value f.destroy! else dup.delete(f.name) end else t = {} self.class.append_custom_field(t, f.name, f.value) if dup.has_key?(f.name) && dup[f.name] == t[f.name] dup.delete(f.name) else f.destroy! end end end # let's iterate through our arrays and compare them array_fields.each do |field_name, fields| if fields.length == dup[field_name].length && fields.map(&:value) == dup[field_name] dup.delete(field_name) else fields.each(&:destroy!) end end dup.each do |k, v| field_type = self.class.get_custom_field_type(k) if v.is_a?(Array) && field_type != :json v.each { |subv| _custom_fields.create!(name: k, value: subv) } else create_singular(k, v, field_type) end end end on_custom_fields_change refresh_custom_fields_from_db end end |
#set_preloaded_custom_fields(custom_fields) ⇒ Object
209 210 211 212 213 214 215 216 |
# File 'app/models/concerns/has_custom_fields.rb', line 209 def set_preloaded_custom_fields(custom_fields) @preloaded_custom_fields = custom_fields # we have to clear this otherwise the fields are cached inside the # already existing proxy and no new ones are added, so when we check # for custom_fields[KEY] an error is likely to occur @preloaded_proxy = nil end |
#upsert_custom_fields(fields) ⇒ Object
‘upsert_custom_fields` will only insert/update existing fields, and will not delete anything. It is safer under concurrency and is recommended when you just want to attach fields to things without maintaining a specific set of fields.
239 240 241 242 243 244 245 246 247 248 |
# File 'app/models/concerns/has_custom_fields.rb', line 239 def upsert_custom_fields(fields) fields.each do |k, v| row_count = _custom_fields.where(name: k).update_all(value: v) _custom_fields.create!(name: k, value: v) if row_count == 0 custom_fields[k.to_s] = v # We normalize custom_fields as strings end on_custom_fields_change end |