Module: Labimotion::ElementHelpers
- Extended by:
- Grape::API::Helpers
- Defined in:
- lib/labimotion/helpers/element_helpers.rb
Overview
ElementHelpers
Instance Method Summary collapse
- #ai_fill_analysis_attachments(analysis) ⇒ Object
-
#ai_fill_analysis_text(element, container_id) ⇒ Object
The container id must be one of the element's own analysis containers.
-
#ai_fill_attachment_text(element, attachment_id) ⇒ Object
The attachment must be one of the element's own attachments OR an attachment of one of the element's analysis DATASET containers.
-
#ai_fill_dataset_containers(element) ⇒ Object
Dataset containers under all of the element's analysis containers.
-
#ai_fill_element_attachment_ids(element) ⇒ Object
Ids of attachments this element may expose to the AI fill: its own attachments plus the attachments of its analysis dataset containers.
-
#ai_fill_element_data(params, current_user) ⇒ Object
AI auto-fill of a generic element instance's DATA VALUES from a document (an existing attachment, an analysis, or freshly uploaded files).
- #ai_fill_extract_attachment(att) ⇒ Object
-
#ai_fill_source_text(element, params) ⇒ Object
Resolve the requested source into plain text, enforcing ownership first.
- #ai_fill_upload_text(files) ⇒ Object
- #attach_thumbnail(_attachments) ⇒ Object
-
#create_ai_element_klass(params, current_user) ⇒ Object
Create a new (inactive) element klass whose properties template is generated by an LLM from a user-provided name/label plus optional description, reference links and uploaded files.
- #create_element(current_user, params) ⇒ Object
- #create_element_klass(current_user, params) ⇒ Object
- #create_repo_klass(params, current_user, origin) ⇒ Object
- #element_revisions(params) ⇒ Object
- #klass_list(is_generic_only) ⇒ Object
- #list_serialized_elements(params, current_user) ⇒ Object
- #list_user_elements(scope, params) ⇒ Object
- #split_elements(ui_state, current_user) ⇒ Object
- #update_element_by_id(current_user, params) ⇒ Object
- #update_element_klass(current_user, params) ⇒ Object
- #upload_generics_files(current_user, params) ⇒ Object
- #validate_klass(attributes) ⇒ Object
Instance Method Details
#ai_fill_analysis_attachments(analysis) ⇒ Object
217 218 219 220 221 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 217 def (analysis) analysis.children.where(container_type: 'dataset').flat_map do |dataset| Attachment.where(attachable_type: 'Container', attachable_id: dataset.id).to_a end end |
#ai_fill_analysis_text(element, container_id) ⇒ Object
The container id must be one of the element's own analysis containers. Then
concatenate the extracted text of every attachment on that analysis's dataset
children, each prefixed "###
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 172 def ai_fill_analysis_text(element, container_id) error!('404 Not Found', 404) if container_id.nil? analysis = element.analyses.detect { |c| c.id == container_id.to_i } error!('404 Not Found', 404) if analysis.nil? parts = [] context = [analysis.name.presence, analysis.description.presence].compact.join(' — ') parts << "Analysis: #{context}" if context.present? (analysis).first(Labimotion::AiTemplate::MAX_FILES).each do |att| content = (att) next if content.to_s.strip.empty? parts << "### #{att.filename}\n#{content}" end parts.join("\n\n") end |
#ai_fill_attachment_text(element, attachment_id) ⇒ Object
The attachment must be one of the element's own attachments OR an attachment of one of the element's analysis DATASET containers. Only then is it read.
160 161 162 163 164 165 166 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 160 def (element, ) error!('404 Not Found', 404) if .nil? error!('404 Not Found', 404) unless (element).include?(.to_i) att = Attachment.find() Labimotion::FileExtractor.extract_bytes(att.filename, att.read_file) end |
#ai_fill_dataset_containers(element) ⇒ Object
Dataset containers under all of the element's analysis containers.
213 214 215 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 213 def ai_fill_dataset_containers(element) element.analyses.flat_map { |analysis| analysis.children.where(container_type: 'dataset').to_a } end |
#ai_fill_element_attachment_ids(element) ⇒ Object
Ids of attachments this element may expose to the AI fill: its own attachments plus the attachments of its analysis dataset containers.
205 206 207 208 209 210 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 205 def (element) own = element..pluck(:id) dataset_ids = ai_fill_dataset_containers(element).map(&:id) dataset_att = dataset_ids.empty? ? [] : Attachment.where(attachable_type: 'Container', attachable_id: dataset_ids).pluck(:id) (own + dataset_att).uniq end |
#ai_fill_element_data(params, current_user) ⇒ Object
AI auto-fill of a generic element instance's DATA VALUES from a document (an existing attachment, an analysis, or freshly uploaded files). Reads the source text, asks the LLM to extract values for the element's OWN template fields, validates them, and returns them for the client to merge into the working copy for human review. Nothing is persisted here.
Authorization: the caller must be able to READ the element, and the source must BELONG to that element (see the ownership checks below) — this endpoint reads files by id, so it must never read an Attachment/Container the user does not already have access to through this element.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 127 def ai_fill_element_data(params, current_user) element = Labimotion::Element.find(params[:element_id]) error!('401 Unauthorized', 401) unless ElementPolicy.new(current_user, element).read? text = ai_fill_source_text(element, params) overrides = ai_user_overrides(current_user) ai = Labimotion::AiTemplate.fill( properties: element.properties, context_text: text, **overrides ) { status: 'success', values: ai['values'], summary: ai['summary'] } rescue StandardError => e Labimotion.log_exception(e, current_user) { status: 'error', message: e. } end |
#ai_fill_extract_attachment(att) ⇒ Object
223 224 225 226 227 228 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 223 def (att) Labimotion::FileExtractor.extract_bytes(att.filename, att.read_file) rescue StandardError => e Labimotion.log_exception(e) nil end |
#ai_fill_source_text(element, params) ⇒ Object
Resolve the requested source into plain text, enforcing ownership first.
145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 145 def ai_fill_source_text(element, params) case params[:source] when 'attachment' (element, params[:attachment_id]) when 'analysis' ai_fill_analysis_text(element, params[:container_id]) when 'upload' ai_fill_upload_text(params[:files]) else error!('400 Bad Request', 400) end end |
#ai_fill_upload_text(files) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 190 def ai_fill_upload_text(files) parts = Array(files).first(Labimotion::AiTemplate::MAX_FILES).map do |file| name = file[:filename] || file['filename'] next if name.blank? content = Labimotion::FileExtractor.extract(name, file[:content_base64] || file['content_base64']) next if content.to_s.strip.empty? "### #{name}\n#{content}" end.compact parts.join("\n\n") end |
#attach_thumbnail(_attachments) ⇒ Object
552 553 554 555 556 557 558 559 560 561 562 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 552 def attach_thumbnail() = &.map do || _att = Entities::AttachmentEntity.represent(, serializable: true) _att[:thumbnail] = .thumb ? Base64.encode64(.read_thumbnail) : nil _att end rescue StandardError => e Labimotion.log_exception(e) end |
#create_ai_element_klass(params, current_user) ⇒ Object
Create a new (inactive) element klass whose properties template is generated by an LLM from a user-provided name/label plus optional description, reference links and uploaded files. The admin reviews/edits the generated template in the designer and activates it (human-in-the-loop).
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 56 def create_ai_element_klass(params, current_user) name = params[:name] label = params[:label] if Labimotion::ElementKlass.find_by(name: name).present? return { status: 'error', message: "An element named [#{name}] already exists." } end if Array(params[:files]).size > Labimotion::AiTemplate::MAX_FILES return { status: 'error', message: "Too many files (max #{Labimotion::AiTemplate::MAX_FILES})." } end overrides = ai_user_overrides(current_user) ai = Labimotion::AiTemplate.generate( kind: 'element', subject: "#{params[:label]} (#{params[:name]})", desc: params[:desc], cols: params[:cols], references: params[:references], files: params[:files], **overrides ) uuid = SecureRandom.uuid # property-base schema requires pkg, uuid, klass, layers, version, identifier. properties_template = { 'uuid' => uuid, 'klass' => 'ElementKlass', 'pkg' => Labimotion::Utils.pkg(nil), 'version' => '1.0.0', 'identifier' => uuid, 'layers' => ai['layers'], 'select_options' => ai['select_options'], 'metadata' => ai['metadata'] } attributes = { 'name' => name, 'label' => label, 'klass_prefix' => params[:klass_prefix], 'icon_name' => params[:icon_name], 'desc' => params[:desc].presence || ai['label'].presence, 'is_active' => false, 'uuid' => uuid, 'released_at' => DateTime.now, 'properties_template' => properties_template, 'properties_release' => properties_template, 'created_by' => current_user.id } new_klass = Labimotion::ElementKlass.create!(attributes) new_klass.reload new_klass.create_klasses_revision(current_user) klass_names_file = Labimotion::KLASSES_JSON # Rails.root.join('app/packs/klasses.json') klasses = Labimotion::ElementKlass.where(is_active: true)&.pluck(:name) || [] File.write(klass_names_file, klasses) { status: 'success', message: "The AI element template [#{label}] has been created as inactive. Review and activate it in the designer." } rescue StandardError => e Labimotion.log_exception(e, current_user) { status: 'error', message: e. } end |
#create_element(current_user, params) ⇒ Object
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 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 251 def create_element(current_user, params) klass = params[:element_klass] || {} uuid = SecureRandom.uuid params[:properties]['uuid'] = uuid params[:properties]['klass_uuid'] = klass[:uuid] params[:properties]['pkg'] = Labimotion::Utils.pkg(params[:properties]['pkg']) params[:properties]['klass'] = 'Element' params[:properties]['identifier'] = klass[:identifier] properties = params[:properties] properties.delete('flow') unless properties['flow'].nil? properties.delete('flowObject') unless properties['flowObject'].nil? properties.delete('select_options') unless properties['select_options'].nil? attributes = { name: params[:name], element_klass_id: klass[:id], uuid: uuid, klass_uuid: klass[:uuid], properties: properties, properties_release: params[:properties_release], metadata: params[:metadata] || {}, created_by: current_user.id } element = Labimotion::Element.new(attributes) all_coll = Collection.get_all_collection_for_user(current_user.id) element.collections << all_coll if params[:collection_id] && params[:collection_id] != all_coll.id collection = current_user.collections.find(params[:collection_id]) element.collections << collection end element.save! element.properties = update_sample_association(params[:properties], current_user, element) # element.properties = update_vocabularies(_properties, current_user, element) element.container = update_datamodel(params[:container], current_user) element.save! update_element_labels(element, params[:user_labels], current_user.id) element.save_segments(segments: params[:segments], current_user_id: current_user.id) element.save! element rescue StandardError => e Labimotion.log_exception(e, current_user) raise e end |
#create_element_klass(current_user, params) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 20 def create_element_klass(current_user, params) uuid = SecureRandom.uuid template = { uuid: uuid, layers: {}, select_options: {} } attributes = declared(params, include_missing: false) attributes[:properties_template] = template if attributes[:properties_template].nil? attributes[:properties_template]['uuid'] = uuid attributes[:properties_template]['pkg'] = Labimotion::Utils.pkg(attributes[:properties_template]['pkg']) attributes[:properties_template]['klass'] = 'ElementKlass' attributes[:is_active] = false attributes[:uuid] = uuid attributes[:released_at] = DateTime.now attributes[:properties_release] = attributes[:properties_template] attributes[:created_by] = current_user.id # The owner row shares the create's transaction: a klass committed without it is # owner-less, which falls open to the legacy designer-wide gate. new_klass = Labimotion::ElementKlass.transaction do Labimotion::ElementKlass.create!(attributes).tap do |klz| Labimotion::KlassShare.seed_owner!(klz, current_user.id) end end new_klass.reload new_klass.create_klasses_revision(current_user) klass_names_file = Labimotion::KLASSES_JSON # Rails.root.join('app/packs/klasses.json') klasses = Labimotion::ElementKlass.where(is_active: true)&.pluck(:name) || [] File.write(klass_names_file, klasses) klasses rescue StandardError => e Labimotion.log_exception(e, current_user) raise e end |
#create_repo_klass(params, current_user, origin) ⇒ Object
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 535 def create_repo_klass(params, current_user, origin) response = Labimotion::TemplateHub.fetch_identifier('ElementKlass', params[:identifier], origin) attributes = response.slice('name', 'label', 'desc', 'icon_name', 'uuid', 'klass_prefix', 'is_generic', 'identifier', 'properties_release', 'version', 'metadata') attributes['properties_release']['identifier'] = attributes['identifier'] attributes['properties_template'] = attributes['properties_release'] attributes['place'] = ((Labimotion::ElementKlass.all.length * 10) || 0) + 10 attributes['is_active'] = false attributes['updated_by'] = current_user.id attributes['sync_by'] = current_user.id attributes['sync_time'] = DateTime.now validate_klass(attributes) rescue StandardError => e Labimotion.log_exception(e, current_user) # { error: e.message } raise e end |
#element_revisions(params) ⇒ Object
385 386 387 388 389 390 391 392 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 385 def element_revisions(params) klass = Labimotion::Element.find(params[:id]) list = klass.elements_revisions unless klass.nil? list&.order(created_at: :desc)&.limit(params[:limit]) rescue StandardError => e Labimotion.log_exception(e, current_user) raise e end |
#klass_list(is_generic_only) ⇒ Object
12 13 14 15 16 17 18 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 12 def klass_list(is_generic_only) if is_generic_only == true Labimotion::ElementKlass.where(is_active: true, is_generic: true).order('place') || [] else Labimotion::ElementKlass.where(is_active: true).order('place') || [] end end |
#list_serialized_elements(params, current_user) ⇒ Object
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 434 def list_serialized_elements(params, current_user) scope = Labimotion::Element.none if params[:collection_id] begin collection = Collection.accessible_for(current_user).find(params[:collection_id]) scope = collection.elements .joins(:element_klass) .where(element_klasses: { name: params[:el_type] }) .includes(:tag) rescue ActiveRecord::RecordNotFound Labimotion::Element.none end else # All collection of current_user scope = Labimotion::Element.for_user(current_user.id) end ## TO DO: refactor labimotion from = params[:from_date] to = params[:to_date] by_created_at = params[:filter_created_at] || false if params[:sort_column]&.include?('.') layer, field = params[:sort_column].split('.') element_klass = Labimotion::ElementKlass.find_by(name: params[:el_type]) allowed_fields = element_klass&.properties_release&.dig(Labimotion::Prop::LAYERS, layer, Labimotion::Prop::FIELDS)&.pluck('field') || [] if field.in?(allowed_fields) query = ActiveRecord::Base.sanitize_sql( [ "LEFT JOIN LATERAL( SELECT field->'value' AS value FROM jsonb_array_elements(properties->'layers'->:layer->'fields') a(field) WHERE field->>'field' = :field ) a ON true", { layer: layer, field: field }, ], ) scope = scope.joins(query).order('value ASC NULLS FIRST') else scope = scope.order(updated_at: :desc) end else scope = scope.order(updated_at: :desc) end scope = scope.elements_created_time_from(Time.at(from)) if from && by_created_at scope = scope.elements_created_time_to(Time.at(to) + 1.day) if to && by_created_at scope = scope.elements_updated_time_from(Time.at(from)) if from && !by_created_at scope = scope.elements_updated_time_to(Time.at(to) + 1.day) if to && !by_created_at scope = scope.by_user_label(params[:user_label]) if params[:user_label] scope rescue StandardError => e Labimotion.log_exception(e, current_user) raise e end |
#list_user_elements(scope, params) ⇒ Object
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 394 def list_user_elements(scope, params) from = params[:from_date] to = params[:to_date] by_created_at = params[:filter_created_at] || false if params[:sort_column]&.include?('.') layer, field = params[:sort_column].split('.') element_klass = Labimotion::ElementKlass.find_by(name: params[:el_type]) allowed_fields = element_klass&.properties_release&.dig(Labimotion::Prop::LAYERS, layer, Labimotion::Prop::FIELDS)&.pluck('field') || [] if field.in?(allowed_fields) query = ActiveRecord::Base.sanitize_sql( [ "LEFT JOIN LATERAL( SELECT field->'value' AS value FROM jsonb_array_elements(properties->'layers'->:layer->'fields') a(field) WHERE field->>'field' = :field ) a ON true", { layer: layer, field: field }, ], ) scope = scope.joins(query).order('value ASC NULLS FIRST') else scope = scope.order(updated_at: :desc) end else scope = scope.order(updated_at: :desc) end scope = scope.created_time_from(Time.at(from)) if from && by_created_at scope = scope.created_time_to(Time.at(to) + 1.day) if to && by_created_at scope = scope.updated_time_from(Time.at(from)) if from && !by_created_at scope = scope.updated_time_to(Time.at(to) + 1.day) if to && !by_created_at scope rescue StandardError => e Labimotion.log_exception(e, current_user) raise e end |
#split_elements(ui_state, current_user) ⇒ Object
336 337 338 339 340 341 342 343 344 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 336 def split_elements(ui_state, current_user) col_id = ui_state[:currentCollectionId] || 0 element_ids = Labimotion::Element.for_user(current_user.id).for_ui_state_with_collection(ui_state[:element], Labimotion::CollectionsElement, col_id) klass_id = Labimotion::ElementKlass.find_by(name: ui_state[:element][:name])&.id Labimotion::Element.where(id: element_ids, element_klass_id: klass_id).each do |element| element.split(current_user, col_id) end {} end |
#update_element_by_id(current_user, params) ⇒ Object
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 296 def update_element_by_id(current_user, params) element = Labimotion::Element.find(params[:id]) update_datamodel(params[:container], current_user) properties = update_sample_association(params[:properties], current_user, element) params.delete(:container) params.delete(:properties) update_element_labels(element, params[:user_labels], current_user.id) params.delete(:user_labels) attributes = declared(params.except(:segments), include_missing: false) properties['pkg'] = Labimotion::Utils.pkg(properties['pkg']) = params[:metadata] || element. || {} if element.klass_uuid != properties['klass_uuid'] || element.properties != properties || element.name != params[:name] || element. != properties['klass'] = 'Element' uuid = SecureRandom.uuid properties['uuid'] = uuid properties.delete('flow') unless properties['flow'].nil? properties.delete('flowObject') unless properties['flowObject'].nil? properties.delete('select_options') unless properties['select_options'].nil? attributes['properties'] = properties attributes['properties']['uuid'] = uuid attributes['uuid'] = uuid attributes['klass_uuid'] = properties['klass_uuid'] attributes['metadata'] = attributes['updated_at'] = Time.current element.update_columns(attributes) end # element.save_segments(segments: params[:segments], current_user_id: current_user.id) element.reload element.save_segments(segments: params[:segments], current_user_id: current_user.id) element.reload # element.properties = update_vocabularies(element.properties, current_user, element) ## element.user_for_revision = current_user element.save! element rescue StandardError => e Labimotion.log_exception(e, current_user) raise e end |
#update_element_klass(current_user, params) ⇒ Object
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 230 def update_element_klass(current_user, params) place = params[:place] || 100 begin place = place.to_i if place.present? && place.to_i == place.to_f rescue StandardError place = 100 end klass = Labimotion::ElementKlass.find(params[:id]) (klass, :write) klass.label = params[:label] if params[:label].present? klass.klass_prefix = params[:klass_prefix] if params[:klass_prefix].present? klass.icon_name = params[:icon_name] if params[:icon_name].present? klass.desc = params[:desc] if params[:desc].present? klass.place = place klass.save! klass rescue StandardError => e Labimotion.log_exception(e, current_user) raise e end |
#upload_generics_files(current_user, params) ⇒ Object
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 346 def upload_generics_files(current_user, params) attach_ary = [] att_ary = create_uploads( Labimotion::Prop::ELEMENT, params[:att_id], params[:elfiles], params[:elInfo], current_user.id, ) if params[:elfiles].present? && params[:elInfo].present? (attach_ary << att_ary).flatten! unless att_ary&.empty? att_ary = create_uploads( Labimotion::Prop::SEGMENT, params[:att_id], params[:sefiles], params[:seInfo], current_user.id ) if params[:sefiles].present? && params[:seInfo].present? (attach_ary << att_ary).flatten! unless att_ary&.empty? if params[:attfiles].present? || params[:delfiles].present? then att_ary = ( params[:attfiles], params[:delfiles], "Labimotion::#{params[:att_type]}", params[:att_id], params[:attfilesIdentifier], current_user.id ) end (attach_ary << att_ary).flatten! unless att_ary&.empty? true rescue StandardError => e Labimotion.log_exception(e, current_user) false end |
#validate_klass(attributes) ⇒ Object
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 |
# File 'lib/labimotion/helpers/element_helpers.rb', line 492 def validate_klass(attributes) # Guarded on presence like the segment path: a blank identifier must mean "create", # never `find_by(identifier: nil)` — that would match an arbitrary legacy row whose # identifier is NULL and upgrade an unrelated template. element_klass = Labimotion::ElementKlass.find_by(identifier: attributes['identifier']) if attributes['identifier'].present? if element_klass.present? if element_klass['uuid'] == attributes['uuid'] && element_klass['version'] == attributes['version'] return { status: 'success', message: "This element: #{attributes['name']} has the latest version!" } else # Upgrading an existing template bumps its version and cuts a revision — publishing, # which is what :release means, so it is owner-only. The gate has to live here, not # in after_validation: which branch applies is unknown until this identifier lookup # has run. error! throws rather than raises, so it escapes this method's rescue. (element_klass, :release) # And it must not reassign authorship. `upload_klass` merges # `created_by: current_user.id` into the attributes unconditionally # (generic_element_api.rb), so without this the importer silently becomes the author # of a template somebody else wrote — and that column is what the Owner column shows # and what per-template ownership is seeded from. element_klass.update!(attributes.except('created_by', :created_by)) element_klass.create_klasses_revision(current_user) return { status: 'success', message: "This element: [#{attributes['name']}] has been upgraded to the version: #{attributes['version']}!" } end else exist_klass = Labimotion::ElementKlass.find_by(name: attributes['name']) if exist_klass.present? return { status: 'error', message: "The name [#{attributes['name']}] is already in use." } else attributes['created_by'] = current_user.id element_klass = Labimotion::ElementKlass.transaction do Labimotion::ElementKlass.create!(attributes).tap do |klz| Labimotion::KlassShare.seed_owner!(klz, current_user.id) end end element_klass.create_klasses_revision(current_user) return { status: 'success', message: "The element: #{attributes['name']} has been created using version: #{attributes['version']}!" } end end rescue StandardError => e Labimotion.log_exception(e, current_user) return { status: 'error', message: e. } end |