trailblazer icon indicating copy to clipboard operation
trailblazer copied to clipboard

How to handle nested collections of looked up objects?

Open francois opened this issue 10 years ago • 1 comments

Look at this form:

capture d ecran 2015-08-13 a 09 45 45

I have 6 different types of objects we can look up shows, brands, etc. Each one is represented by an ID. The parent object I'm creating is a TailoredAudience.

I wrote the following test:

RSpec.describe TailoredAudience::Create do

  let(:valid_params) do
    {tailored_audience: {
      user_account_id: 4,
      name: "Morning People",
      shows: [show_id: "7168f630-daec-11e0-9b8e-40402761cfca"]}
    }
  end

  it "creates a new tailored audience" do
    model = TailoredAudience::Create[valid_params].model
    expect(model).to be_persisted
  end

end

The ::Create operation is implemented very simply:

require "tailored_audience"

class TailoredAudience < Sequel::Model(RDB[:tailored_audiences])
  class Create < Trailblazer::Operation
    include CRUD
    model TailoredAudience, :create

    contract do
      property :name, validates: { presence: true, length: { minimum: 1 } }
      property :user_account_id, validates: { presence: true }

      collection :shows do
        property :show_id, validates: { presence: true }
      end

      # similarly for brands, show categories, etc.
    end

    def process(params)
      validate(params[:tailored_audience]) do |f|
        f.save
      end
    end
  end
end

This fails with the following backtrace:

  1) TailoredAudience::Create creates a new tailored audience
     Failure/Error: model = TailoredAudience::Create[valid_params].model
     RuntimeError:
       [Reform] Your :populator did not return a Reform::Form instance for `shows`.
     # /home/francois/.bundler/ruby/1.9.1/reform-7657fd317754/lib/reform/form/populator.rb:40:in `handle_fail'
     # /home/francois/.bundler/ruby/1.9.1/reform-7657fd317754/lib/reform/form/populator.rb:26:in `call'
     # /var/lib/gems/1.9.1/gems/uber-0.0.13/lib/uber/options.rb:85:in `callable!'
     # /var/lib/gems/1.9.1/gems/uber-0.0.13/lib/uber/options.rb:70:in `evaluate_for'
     # /var/lib/gems/1.9.1/gems/uber-0.0.13/lib/uber/options.rb:60:in `evaluate'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/binding.rb:130:in `evaluate_option'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:76:in `instance_for'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:62:in `create_object'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:21:in `call'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:98:in `deserialize!'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:90:in `block in call'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:86:in `each'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:86:in `each_with_index'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/deserializer.rb:86:in `call'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/populator.rb:49:in `deserialize'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/populator.rb:21:in `call'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/binding.rb:88:in `read_fragment'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/binding.rb:67:in `block in uncompile_fragment'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/binding.rb:123:in `evaluate_option'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/binding.rb:66:in `uncompile_fragment'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/mapper.rb:77:in `uncompile_fragment'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/mapper.rb:38:in `deserialize_property'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/mapper.rb:18:in `block in deserialize'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/mapper.rb:17:in `each'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/mapper.rb:17:in `deserialize'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable.rb:41:in `update_properties_from'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/hash/allow_symbols.rb:10:in `update_properties_from'
     # /home/francois/.bundler/ruby/1.9.1/representable-dbf0cb4fa346/lib/representable/hash.rb:28:in `from_hash'
     # /home/francois/.bundler/ruby/1.9.1/reform-7657fd317754/lib/reform/form/validate.rb:55:in `deserialize'
     # /home/francois/.bundler/ruby/1.9.1/reform-7657fd317754/lib/reform/form/validate.rb:25:in `validate'
     # /home/francois/.bundler/ruby/1.9.1/trailblazer-6646e5f5e227/lib/trailblazer/operation.rb:126:in `validate_contract'
     # /home/francois/.bundler/ruby/1.9.1/trailblazer-6646e5f5e227/lib/trailblazer/operation.rb:116:in `validate'
     # /home/francois/.bundler/ruby/1.9.1/trailblazer-6646e5f5e227/lib/trailblazer/operation/crud.rb:41:in `validate'
     # ./app/concepts/tailored_audience/crud.rb:38:in `process'
     # /home/francois/.bundler/ruby/1.9.1/trailblazer-6646e5f5e227/lib/trailblazer/operation.rb:69:in `run'
     # /home/francois/.bundler/ruby/1.9.1/trailblazer-6646e5f5e227/lib/trailblazer/operation.rb:39:in `call'
     # ./spec/concepts/tailored_audience_crud_spec.rb:14:in `block (2 levels) in <top (required)>'

I found the Populator section on the Reform README, and I tried applying it, but the syntax as shown doesn't work anymore. The lambda receives 4 parameters, amongst other things.

How do I transform a list of IDs into a list of Show instances, and allow my form to save?

francois avatar Aug 13 '15 14:08 francois

Hi Francois, this is described in the first section of the Authentication chapter, and here: http://trailblazerb.org/gems/reform/populators.html#populating-by-id

apotonick avatar Aug 14 '15 07:08 apotonick