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

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods inherited from ApplicationRecord

transaction_with_retry

Instance Attribute Details

#contained_object_idInteger

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

#contained_object_typeString

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

#container_idObject

Returns the value of attribute container_id


55
56
57
# File 'app/models/container_item.rb', line 55

def container_id
  @container_id
end

#disposition_xInteger

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

#disposition_yInteger

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

#disposition_zInteger

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

#global_entityGlobalID

TODO: this is silly, type should be the same

Returns:

  • (GlobalID)

    ! not a string


105
106
107
# File 'app/models/container_item.rb', line 105

def global_entity
  @global_entity
end

#localizationString

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

#parent_idInteger

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

#project_idIntegers

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/container_item.rb', line 39

class ContainerItem < ApplicationRecord  # @return class
  #   this method calls Module#module_parent
  # TODO: This method can be placed elsewhere inside this class (or even removed if not used)
  #       when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

  def self.parent
    self.module_parent
  end

  has_closure_tree

  include Housekeeping
  include Shared::IsData

  attr_accessor :global_entity

  attr_accessor :container_id

  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
  validate :parent_is_provided_if_object_is_not_container

  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')}

  # before_save :set_container, unless: Proc.new {|n| n.container_id.nil? || errors.any? }
 
  # @params object [Container]
  def container=(object)
    if object.metamorphosize.kind_of?(Container)
      if self.parent
        self.parent.contained_object = object
      else
        # This self required?!
        self.parent = ContainerItem.new(contained_object: object)
      end

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

  # @param value [a Container#id]
  def container_id=(value)
    @container_id = value
    set_container
  end

  # @return [Container, nil]
  #   the immediate container for this ContainerItem
  def container
    parent.try(:contained_object)
  end

  # TODO: this is silly, type should be the same
  # @return [GlobalID]
  #   ! not a string
  def global_entity
    contained_object.to_global_id if contained_object.present?
  end

  # @params entity [String, a global id]
  def global_entity=(entity)
    self.contained_object = GlobalID::Locator.locate(entity)
  end

  protected

  def set_container
    c = Container.find(container_id)

    # Already in some container
    if parent && parent.persisted? 
      self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)    # Not in container

    else
      # In same container as something else
      if d = c.container_item
        self.parent = d      # In a new container

      else
        self.parent = ContainerItem.create!(contained_object: c) 
      end
    end


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

  def object_fits_in_container
    if parent
      %w{x y z}.each do |coord|
        c = 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? && container_id.blank? && container.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 parent_is_provided_if_object_is_not_container
    if !(contained_object_type =~ /Container/) && !parent 
      errors.add(:parent, "must be set if contained object is not a 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

Class Method Details

.parentObject

TODO: This method can be placed elsewhere inside this class (or even removed if not used)

when https://github.com/ClosureTree/closure_tree/issues/346 is fixed.

Returns:

  • class this method calls Module#module_parent


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

def self.parent
  self.module_parent
end

Instance Method Details

#contained_object_is_container_when_parent_id_is_blankObject (protected)

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


160
161
162
163
164
# File 'app/models/container_item.rb', line 160

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

#contained_object_is_uniqueObject (protected)


179
180
181
182
183
# File 'app/models/container_item.rb', line 179

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

#containerContainer?

Returns the immediate container for this ContainerItem.

Returns:

  • (Container, nil)

    the immediate container for this ContainerItem


98
99
100
# File 'app/models/container_item.rb', line 98

def container
  parent.try(:contained_object)
end

#container=(object) ⇒ Object


76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/models/container_item.rb', line 76

def container=(object)
  if object.metamorphosize.kind_of?(Container)
    if self.parent
      self.parent.contained_object = object
    else
      # This self required?!
      self.parent = ContainerItem.new(contained_object: object)
    end

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

#object_fits_in_containerObject (protected)


138
139
140
141
142
143
144
145
# File 'app/models/container_item.rb', line 138

def object_fits_in_container
  if parent
    %w{x y z}.each do |coord|
      c = 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

#parent_contained_object_is_containerObject (protected)

parent_id links an object to a container through container_item


167
168
169
170
171
# File 'app/models/container_item.rb', line 167

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

#parent_is_provided_if_object_is_not_containerObject (protected)


173
174
175
176
177
# File 'app/models/container_item.rb', line 173

def parent_is_provided_if_object_is_not_container
  if !(contained_object_type =~ /Container/) && !parent 
    errors.add(:parent, "must be set if contained object is not a container")
  end
end

#position_is_not_replicatedObject (protected)


147
148
149
150
151
152
153
154
155
156
157
# File 'app/models/container_item.rb', line 147

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

#set_containerObject (protected)


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'app/models/container_item.rb', line 116

def set_container
  c = Container.find(container_id)

  # Already in some container
  if parent && parent.persisted? 
    self.parent.update_columns(contained_object_type: 'Container', contained_object_id: c.id)  # Not in container

  else
    # In same container as something else
    if d = c.container_item
      self.parent = d    # In a new container

    else
      self.parent = ContainerItem.create!(contained_object: c) 
    end
  end


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