Dear Tom. Dear fellow Hobo addicts.
I'm sure, you noticed my need for a Hobo supporting polymorphic
associations,
nested forms and similar stuff ;-) That's because I have a serious use
case:
My employer need an application that allows freelancing authors to
write
unique content for our customers websites. Each customer usually has
his very
own requirement regarding the authored contents. Some just want simple
weblog
content with just a title and a body. Another one needs a meta
description as
well. Other customers need something completely different: A
transcription of
an audio file or an image.
Until recently I programmed one application with one (git) branch per
customer
which had it's own model to comply with the customers requirement. In
the
future the application shall host all customers at once. I designed
some
models to do so:
I have an «Item» to represent one task an author shall complete. Each
Item
belongs to several other models like User and others of course. The
Item model
includes every field that is always required like author, reviewer,
status and
others.
All fields that are individual to one customers instructions are kept
in a
model named «Complement». Every Complement has a name, a type and a
value like
so:
class Complement < ActiveRecord::Base
hobo_model
fields do
name :string
type :string
value :serialized
end
belongs_to :complementable, :accessible => true, :polymorphic =>
true
end
Did you recognize the polymorphic association? I'm sure you did. So
you can
imagine how the Item model looks:
class Item < ActiveRecord::Base
hobo_model
fields do
duplicates :integer
status enum_string(:Authoring, :ReturnedToAuthor, :Reviewing,
\
:ReturnedToReviewer, :Completed), :default =>
'Authoring'
timestamps
end
has_many :complements, :as => :complementable
belongs_to :author, :class_name => 'User'
belongs_to :reviewer, :class_name => 'User'
end
Of course these code fragments are quite incomplete.
Ok, back to «Complement». Every instance of Complement persists one
"field" of
an Item instance which actually is not such a field but an associated
model.
Complement.value stores the actual value, Complement.type tells of
which type
(think of a Hobo::Field) the value is and how that value should be
displayed
in edit forms. Last but not least that all got a name:
Complement.name.
I would like to point out, that Complements -- from a user's point of
view --
are more equal to there complemented models than e.g. comments to
there
commented models. The user shall only see items which include
complemented
fields indistinguishable from the first-class fields and edit them as
would
the item model include all these fields. In fact in my actual use case
only an
excel import routine shall create complements as required. And maybe
-- in a
distant future -- privileged users like administrators shall be able
to add or
remove complements manually.
Ok, that was much more than just the use case ;-) Time to talk about
Hobo.
My application uses Hobo as it's foundation. Lots of Hobo's feature
saved me
hours and some nerves ;-) Thank you, Tom.
Hobo's polymorphic tags and the growing support of nested forms -- I
refer to
<input-many> in fact -- look very promising; I hope to implement the
stuff
described above much easier than using plain Rails only.
After updating the Hobo to the latest refs/head/master I adopted the
show and
edit views of Item like so:
<show-page>
<content-body:>
<field-list fields="complements, ...">
<complements-view:>
<collection />
</complements-view:>
</field-list>
</content-body:>
</show-page>
<edit-page>
<form:>
<field-list: fields="complements, ..." no-edit>
<complements-view:>
<input-all><field-list no-edit /></input-all>
</complements-view:>
</field-list:>
</form:>
</edit-page>
Remember: These are excerpts from my actual code base only.
I chose <input-all> instead of <input-many> because the user shall
edit
existing complements but not add or remove one. One drawback until now
is,
that I don't get editors matching Complement.type. So there is still a
long
way do go; A lot of work to be done :-(
And updating an item fails with an
"ActiveRecord::AssociationTypeMismatch in
ItemsController#update" exception (Complement expected, got Array). I
include
the stack trace in the post scriptum below.
My goodness, your still reading this way to long post ;-) May I
challenge your
tolerance a little bit more and ask you for constructive criticism,
ideas,
hints or thoughts? I appreciate every kind of help.
Kind regards,
Gert Thiel
P.S.:
The request parameters:
{"page_path"=>"items/edit",
"_method"=>"PUT",
"authenticity_token"=>"55ceac9f99c61c0e41ac38be6ec725f8deb011d7",
"id"=>"174",
"item"=>{"complements"=>{"0"=>{"name"=>"ext_id",
"complementable_type"=>"Item",
"complementable_id"=>"174",
"id"=>"520",
"value"=>"174"},
"1"=>{"name"=>"ext_page",
"complementable_type"=>"Item",
"complementable_id"=>"174",
"id"=>"521",
"value"=>"(interaktive) DVDs"},
"2"=>{"name"=>"ext_text",
"complementable_type"=>"Item",
"complementable_id"=>"174",
"id"=>"522",
"value"=>"Dieser <b>Text</b> dient nur zu <i>Testzwecken</i> und
sollte <u>einzigartig</u> sein!"}}}}
The stack trace:
ActiveRecord::AssociationTypeMismatch (Complement expected, got
Array):
/vendor/plugins/hobo/hobo/lib/active_record/association_proxy.rb:
20:in `raise_on_type_mismatch'
/vendor/rails/activerecord/lib/active_record/associations/
association_collection.rb:310:in `replace'
/vendor/rails/activerecord/lib/active_record/associations/
association_collection.rb:310:in `each'
/vendor/rails/activerecord/lib/active_record/associations/
association_collection.rb:310:in `replace'
/vendor/rails/activerecord/lib/active_record/associations.rb:
1322:in `complements='
/vendor/rails/activerecord/lib/active_record/base.rb:2587:in
`send'
/vendor/rails/activerecord/lib/active_record/base.rb:2587:in
`attributes_without_hobo_type_conversion='
/vendor/rails/activerecord/lib/active_record/base.rb:2583:in
`each'
/vendor/rails/activerecord/lib/active_record/base.rb:2583:in
`attributes_without_hobo_type_conversion='
/vendor/plugins/hobo/hobo/lib/hobo/model.rb:430:in `send'
/vendor/plugins/hobo/hobo/lib/hobo/model.rb:430:in `attributes='
/vendor/plugins/hobo/hobo/lib/hobo/permissions.rb:144:in
`user_update_attributes'
/vendor/plugins/hobo/hobo/lib/hobo/permissions.rb:121:in
`with_acting_user'
/vendor/plugins/hobo/hobo/lib/hobo/permissions.rb:143:in
`user_update_attributes'
/vendor/plugins/hobo/hobo/lib/hobo/model_controller.rb:553:in
`hobo_update'
/vendor/plugins/hobo/hobo/lib/hobo/model_controller.rb:157:in
`update'
/vendor/rails/actionpack/lib/action_controller/base.rb:1256:in
`send'
/vendor/rails/actionpack/lib/action_controller/base.rb:1256:in
`perform_action_without_filters'
/vendor/rails/actionpack/lib/action_controller/filters.rb:617:in
`call_filters'
/vendor/rails/actionpack/lib/action_controller/filters.rb:638:in
`run_before_filters'
/vendor/plugins/hobo/hobo/lib/hobo/controller.rb:22:in `call'
/vendor/plugins/hobo/hobo/lib/hobo/controller.rb:22:in
`included_in_class'
/vendor/rails/activesupport/lib/active_support/callbacks.rb:182:in
`call'
/vendor/rails/activesupport/lib/active_support/callbacks.rb:182:in
`evaluate_method'
/vendor/rails/actionpack/lib/action_controller/filters.rb:184:in
`call'
/vendor/rails/actionpack/lib/action_controller/filters.rb:635:in
`run_before_filters'
/vendor/rails/actionpack/lib/action_controller/filters.rb:615:in
`call_filters'
/vendor/rails/actionpack/lib/action_controller/filters.rb:610:in
`perform_action_without_benchmark'
/vendor/rails/actionpack/lib/action_controller/benchmarking.rb:
68:in `perform_action_without_rescue'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/1.8/benchmark.rb:293:in `measure'
/vendor/rails/actionpack/lib/action_controller/benchmarking.rb:
68:in `perform_action_without_rescue'
/vendor/rails/actionpack/lib/action_controller/rescue.rb:136:in
`perform_action_without_caching'
/vendor/rails/actionpack/lib/action_controller/caching/
sql_cache.rb:13:in `perform_action'
/vendor/rails/activerecord/lib/active_record/connection_adapters/
abstract/query_cache.rb:34:in `cache'
/vendor/rails/activerecord/lib/active_record/query_cache.rb:8:in
`cache'
/vendor/rails/actionpack/lib/action_controller/caching/
sql_cache.rb:12:in `perform_action'
/vendor/rails/actionpack/lib/action_controller/base.rb:524:in
`send'
/vendor/rails/actionpack/lib/action_controller/base.rb:524:in
`process_without_filters'
/vendor/rails/actionpack/lib/action_controller/filters.rb:606:in
`process_without_session_management_support'
/vendor/rails/actionpack/lib/action_controller/
session_management.rb:134:in `process'
/vendor/rails/actionpack/lib/action_controller/base.rb:392:in
`process'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:
184:in `handle_request'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:
112:in `dispatch_unlocked'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:
125:in `dispatch'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:
124:in `synchronize'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:
124:in `dispatch'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:
134:in `dispatch_cgi'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:41:in
`dispatch'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/bin/../lib/mongrel/rails.rb:76:in
`process'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/bin/../lib/mongrel/rails.rb:74:in
`synchronize'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/bin/../lib/mongrel/rails.rb:74:in
`process'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in
`process_client'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in
`process_client'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel/configurator.rb:282:in
`run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel/configurator.rb:281:in
`each'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel/configurator.rb:281:in
`run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/bin/mongrel_rails:128:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel/command.rb:212:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/
ruby/gems/1.8/gems/mongrel-1.1.4/bin/mongrel_rails:281
/vendor/rails/activesupport/lib/active_support/dependencies.rb:
142:in `load_without_new_constant_marking'
/vendor/rails/activesupport/lib/active_support/dependencies.rb:
142:in `load'
/vendor/rails/activesupport/lib/active_support/dependencies.rb:
521:in `new_constants_in'
/vendor/rails/activesupport/lib/active_support/dependencies.rb:
142:in `load'
/vendor/rails/railties/lib/commands/servers/mongrel.rb:64
/Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in
`gem_original_require'
/Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `require'
/vendor/rails/activesupport/lib/active_support/dependencies.rb:
153:in `require'
/vendor/rails/activesupport/lib/active_support/dependencies.rb:
521:in `new_constants_in'
/vendor/rails/activesupport/lib/active_support/dependencies.rb:
153:in `require'
/vendor/rails/railties/lib/commands/server.rb:49
/Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in
`gem_original_require'
/Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `require'
script/server:3
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Hobo
Users" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/hobousers?hl=en
-~----------~----~----~----~------~----~------~--~---