     end
   end
 
+  defp prepare_from_spec(changeset, spec) do
+    component_id = get_field(changeset, :component_id)
+
+    {assoc, component_specialization} = case spec do
+      %{"spec_type" => "CPU"} ->
+        cpu =
+          spec
+          |> Map.take(["clock", "cores"])
+          |> Map.put("cpu_id", component_id)
+          |> CPU.create_changeset()
+        {:cpu, cpu}
+      %{"spec_type" => "HDD"} ->
+        hdd =
+          spec
+          |> Map.take(["size"])
+          |> Map.put("hdd_id", component_id)
+          |> HDD.create_changeset()
+        {:hdd, hdd}
+      %{"spec_type" => "NIC"} ->
+        nic =
+          spec
+          |> Map.take(["downlink", "uplink"])
+          |> Map.put("nic_id", component_id)
+          |> NIC.create_changeset()
+        {:nic, nic}
+      %{"spec_type" => "RAM"} ->
+        ram =
+          spec
+          |> Map.take(["size"])
+          |> Map.put("ram_id", component_id)
+          |> RAM.create_changeset()
+        {:ram, ram}
+      _ ->
+        {nil, %{valid?: false, errors: []}}
+    end
+
+    if component_specialization.valid? do
+      prepare_changes(changeset, fn changeset ->
+        put_assoc(changeset, assoc, component_specialization)
+      end)
+    else
+      add_error(changeset, :component_spec, "invalid spec", component_specialization.errors)
+    end
+  end
+
   defmodule Query do
 
     alias Helix.Hardware.Model.Component

  use Ecto.Schema

  alias HELL.PK
  alias Helix.Hardware.Model.ComponentSpec
  alias Helix.Hardware.Model.MotherboardSlot


      foreign_key: :link_component_id,
      references: :component_id

    # Specializations (included just to allow preparing them)
    has_one :cpu, CPU,
      foreign_key: :cpu_id,
      references: :component_id
    has_one :hdd, HDD,
      foreign_key: :hdd_id,
      references: :component_id
    has_one :nic, NIC,
      foreign_key: :nic_id,
      references: :component_id
    has_one :ram, RAM,
      foreign_key: :ram_id,
      references: :component_id

    timestamps()
  end

  @spec create_from_spec(ComponentSpec.t) :: Ecto.Changeset.t
  def create_from_spec(cs = %ComponentSpec{}) do
    params = %{component_type: cs.component_type, spec_id: cs.spec_id}

    params
    |> create_changeset()
    |> prepare_from_spec(cs.spec)
  end

  @spec create_changeset(creation_params) :: Ecto.Changeset.t
