Module: ActiveModelSerializerPlus::Assignment
- Defined in:
- lib/active_model_serializer_plus/assignment.rb
Overview
Default implementation of the #attributes=
method for ActiveModel classes.
This is the base of the JSON and Xml modules. It supplies a standard #attributes=
method to classes that follows the basic pattern of the custom one you’d normally write and adds the ability to pick up object type information from the #attributes_types
hash to convert the hash values of contained objects into actual objects. This mostly eliminates the need to write custom #attributes=
methods for each class with code to check attribute names and initialize objects from child hashes.
The standard ActiveModel/ActiveRecord serialization/deserialization has some flaws, for instance it will serialize classes as contained hashes whether or not they have an #attributes=
method available to deserialize them. We work around that by having building Procs which can do the correct thing in many cases.
Instance Method Summary collapse
-
#attributes=(hash) ⇒ self
The default
#attributes=
method which assigns a hash to the object’s attributes.
Instance Method Details
#attributes=(hash) ⇒ self
The default #attributes=
method which assigns a hash to the object’s attributes.
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 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/active_model_serializer_plus/assignment.rb', line 148 def attributes=(hash) hash.each do |key, value| # Check #attribute_types for what type this item should be, and if we have an entry # convert it to an actual Class object we can use. attr_klass = nil attr_typename = nil attr_typename = self.attribute_types[key] if self.respond_to?(:attribute_types) && self.attribute_types.is_a?(Hash) if attr_typename.is_a?(Array) # Container container_klass = nil object_klass = nil if attr_typename.length != 2 raise ArgumentError, "Container type specification for attribute #{key.to_s} is invalid." end # Sort out the type of container. First make sure the container type name translates to 'Container'. # If the type name actually is 'Container', take the type of the container from the type of the # original value. If it's an actual type name, convert it to a Class object. Raise exceptions if # any problems are encountered. container_typename = attr_typename[0] if container_typename == 'Container' container_klass = value.class else xlated_container_typename = ActiveModelSerializerPlus.type_name_xlate(container_typename) if xlated_container_typename != 'Container' raise ArgumentError, "Container type #{container_typename} for attribute #{key.to_s} is not a container." end begin container_klass = ActiveModelSerializerPlus.to_class(container_typename) rescue ArgumentError raise ArgumentError, "Container type #{container_typename} for attribute #{key.to_s} is not a valid type." end end # Sort out the type of objects in the container. Convert the object type name to a Class object # and raise an exception if it can't be converted. object_typename = attr_typename[1] begin object_klass = ActiveModelSerializerPlus.to_class(object_typename) rescue ArgumentError raise ArgumentError, "Object type #{object_typename} for attribute #{key.to_s} is not a valid type." end container = container_klass.new if container.nil? raise ArgumentError, "Cannot create container of type #{container_klass.name}." end adder_proc = ActiveModelSerializerPlus.get_container_adder(container_klass.name) iterator_proc = ActiveModelSerializerPlus.get_container_iterator(value.class.name) if adder_proc.nil? || iterator_proc.nil? msg = '' if iterator_proc.nil? msg << "iterate through #{value.class.name}" end if adder_proc.nil? if msg.length == 0 msg << ', ' end msg << "add to #{container_klass.name}" end raise ArgumentError, "Cannot #{msg}." end # Build the new container by iterating through the value container and adding each item to # the new container, using the procs we looked up. iterator_proc.call(container, adder_proc, object_klass, value) self.send("#{key}=", container) else # Object unless attr_typename.nil? begin attr_klass = ActiveModelSerializerPlus.to_class(attr_typename) rescue ArgumentError raise ArgumentError, "Type #{attr_typename.to_s} for attribute #{key.to_s} is not a valid type." end v = ActiveModelSerializerPlus.build_object(attr_klass, value) value = v unless v.nil? end self.send("#{key}=", value) end self end end |