Class: ContainerItem

Inherits:
ApplicationRecord show all
Includes:
Housekeeping, Shared::IsData
Defined in:
app/models/container_item.rb

Overview

A container item is something that has been “localized to” a container. We can't say that it is “in” the container, because not all containers (e.g. a pin with three specimens) contain the object. By “localized to” we mean that if you can find the container, then its contents should also be locatable.

This concept is a graph edge defining the relationship to the container.

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods included from ActiverecordUtilities

#trim_attributes

Instance Attribute Details

- (Integer) contained_object_id

Returns the id of the object that is contained (Rails polymorphic)

Returns:

  • (Integer)

    the id of the object that is contained (Rails polymorphic)



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

- (String) contained_object_type

Returns the type of the object that is contained (Rails polymorphic)

Returns:

  • (String)

    the type of the object that is contained (Rails polymorphic)



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

- (Integer) disposition_x

Returns a x coordinate for this item in its container

Returns:

  • (Integer)

    a x coordinate for this item in its container



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

- (Integer) disposition_y

Returns a y coordinate for this item in its container

Returns:

  • (Integer)

    a y coordinate for this item in its container



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

- (Integer) disposition_z

Returns a z coordinate for this item in its container

Returns:

  • (Integer)

    a z coordinate for this item in its container



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

- (Object) global_entity

Returns the value of attribute global_entity



45
46
47
# File 'app/models/container_item.rb', line 45

def global_entity
  @global_entity
end

- (String) localization

some additional modifier arbitrarily defining the position of this item, aka disposition, always relative to enclosing container

Returns:

  • (String)


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

- (Integer) parent_id

Returns id of the ContainerItem whose contained_object is a Container, i.e. the container of this ContainerItem

Returns:

  • (Integer)

    id of the ContainerItem whose contained_object is a Container, i.e. the container of this ContainerItem



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

- (Integers) project_id

the project ID

Returns:

  • (Integers)

    Integers



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord
  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  belongs_to :contained_object, polymorphic: true

  # !! this will prevent accepts_nested assignments if we add this
  validates_presence_of :contained_object

  validate :parent_contained_object_is_container
  validate :contained_object_is_container_when_parent_id_is_blank
  validate :contained_object_is_unique
  validate :object_fits_in_container
  validate :position_is_not_replicated

  scope :containers, -> { where(contained_object_type: 'Container') }
  scope :not_containers, -> { where.not(contained_object_type: 'Container') }
  scope :containing_collection_objects, -> {where(contained_object_type: 'CollectionObject')}

  def container=(object)
    if object.metamorphosize.class.to_s == 'Container'
      if parent
        parent.contained_object = object
      else
        self.parent = ContainerItem.new(contained_object: object)
      end

      parent.save! if !parent.new_record?
      self.save! unless self.new_record?
    end
  end

  # @return [container]
  #   the container for this ContainerItem
  def container
    reload_parent.try(:contained_object) || Container.none
  end

  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  def global_entity=(entity)
    contained_object = GlobalID::Locator.locate entity
  end

  protected

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = self.send("disposition_#{coord}")
        errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
      end
    end
  end

  def position_is_not_replicated
    if parent && (disposition_x || disposition_y || disposition_z)
      if ContainerItem.where.not(id: id).
          where(parent: parent,
                disposition_x: disposition_x,
                disposition_y: disposition_y,
                disposition_z: disposition_z ).count > 0
        errors.add(:base, 'position is already taken in this container')
      end
    end
  end

  # if the contained_object is a CollectionObject, it must have a parent container reference
  def contained_object_is_container_when_parent_id_is_blank
    if parent_id.blank?
      errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
    end
  end

  # parent_id links an object to a container through container_item
  def parent_contained_object_is_container
    unless parent_id.blank? && parent.nil?
      errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
    end
  end

  def contained_object_is_unique
    if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
      errors.add(:contained_object, 'is already in a container_item')
    end
  end

end

Instance Method Details

- (Object) contained_object_is_container_when_parent_id_is_blank (protected)

if the contained_object is a CollectionObject, it must have a parent container reference



113
114
115
116
117
# File 'app/models/container_item.rb', line 113

def contained_object_is_container_when_parent_id_is_blank
  if parent_id.blank?
    errors.add(:parent_id, 'can only be blank if object is a container') if contained_object_type != 'Container'
  end
end

- (Object) contained_object_is_unique (protected)



126
127
128
129
130
# File 'app/models/container_item.rb', line 126

def contained_object_is_unique
  if ContainerItem.where.not(id: id).where(project_id: project_id, contained_object_id: contained_object_id, contained_object_type: contained_object_type).count > 0
    errors.add(:contained_object, 'is already in a container_item')
  end
end

- (container) container

Returns the container for this ContainerItem

Returns:

  • (container)

    the container for this ContainerItem



77
78
79
# File 'app/models/container_item.rb', line 77

def container
  reload_parent.try(:contained_object) || Container.none
end

- (Object) container=(object)



62
63
64
65
66
67
68
69
70
71
72
73
# File 'app/models/container_item.rb', line 62

def container=(object)
  if object.metamorphosize.class.to_s == 'Container'
    if parent
      parent.contained_object = object
    else
      self.parent = ContainerItem.new(contained_object: object)
    end

    parent.save! if !parent.new_record?
    self.save! unless self.new_record?
  end
end

- (Object) object_fits_in_container (protected)



91
92
93
94
95
96
97
98
# File 'app/models/container_item.rb', line 91

def object_fits_in_container
  if parent
    %w{x y z}.each do |coord|
      c = self.send("disposition_#{coord}")
      errors.add("disposition_#{coord}".to_sym, 'is larger than the container size') if c && parent.contained_object.send("size_#{coord}") < c
    end
  end
end

- (Object) parent_contained_object_is_container (protected)

parent_id links an object to a container through container_item



120
121
122
123
124
# File 'app/models/container_item.rb', line 120

def parent_contained_object_is_container
  unless parent_id.blank? && parent.nil?
    errors.add(:parent_id, "can only be set if parent's contained object is a container") if parent.contained_object_type != 'Container'
  end
end

- (Object) position_is_not_replicated (protected)



100
101
102
103
104
105
106
107
108
109
110
# File 'app/models/container_item.rb', line 100

def position_is_not_replicated
  if parent && (disposition_x || disposition_y || disposition_z)
    if ContainerItem.where.not(id: id).
        where(parent: parent,
              disposition_x: disposition_x,
              disposition_y: disposition_y,
              disposition_z: disposition_z ).count > 0
      errors.add(:base, 'position is already taken in this container')
    end
  end
end