Many to Many in Hobo
Start out with a new hobo app
hobo g resource book title:string
hobo g resource author name:string
hobo g model book_author
Add has_many assotications to book.rb, author.rb and book_author.rb
Add to book.rb
has_many :book_authors, :dependent => :destroy
has_many :authors, :through => :book_authors, :accessible => :true
children :authors
Add to author.rb
has_many :book_authors, :dependent => :destroy
has_many :books, :through => :book_authors, :accessible => :true
children :books
Add to book_author.rb
belongs_to :book
belongs_to :author
create and run the migration
hobo g migration
Run the app
At this point you can add authors to books and books to authors but the book or author must already exist. There is no direct way to add a new author to a book or new book to an author you need to first create the book/author and then make the association separately. Wouldn't it be nice to be able to add a new author to a book while editing the book? You can do this very easily with Hobo and auto_actions_for int he control in a one to many association but that doesn't work totally automaticly for many to many associations.
First we are going to use Hobo's auto_actions_for in the controllers to create the routes for us.
Add to authors_controller.rb
auto_actions_for :books, [:create, :new]
Add to books_controller.rb
auto_actions_for :authors, [:create, :new]
If you go to a book or author you won't see any changes to the UI. If this were a one to many association we would have a New link below the collection of books or authors but there isn't one so lets add it.
First lets confirm we have the appropriate routs created for us
run a rake routes from the command prompt and you should see these in the list of all the routes
create_author_for_book POST /books/:book_id/authors(.:format) {:controller=>"authors", :action=>"create_for_book"}
new_author_for_book GET /books/:book_id/authors/new(.:format) {:controller=>"authors", :action=>"new_for_book"}
create_book_for_author POST /authors/:author_id/books(.:format) {:controller=>"books", :action=>"create_for_author"}
new_book_for_author GET /authors/:author_id/books/new(.:format) {:controller=>"books", :action=>"new_for_author"}
Ok we have the routes now to add them.
Create a books and authors subdir under views.
Create show.dryml in the books dir
<show-page><after-collection-section:>
<a:authors action="new" if="&can_create?(BookAuthor)">
<ht key="author.actions.new" count="1">
New Author
</ht>
</a:authors>
</after-collection-section:>
</show-page>
And a show.dryml is authors dir
<show-page>
<after-collection-section:>
<a:books action="new" if="&can_create?(BookAuthor)">
<ht key="book.actions.new" count="1">
New Book
</ht>
</a:books>
</after-collection-section:>
</show-page>
Now if you view a book or an author there is a link to add a new book or author but they don't quite work yet so on we march.
We need to modify the forms that are auto generated for entering books and authors. Open up your views/taglibs/applicatoin.dryml and add the following.
<extend tag="form" for="Author">
<old-form replace>
<field-list: fields="name, books" />
<after-field-list:> <%= hidden_field(:book, :id) %></after-field-list:>
<actions:>
<submit label="#{ht 'author.actions.save', :default=>['Save']}" param/>
<or-cancel with="&@book" param="cancel"/>
</actions:>
</old-form>
</extend>
<extend tag="form" for="Book">
<old-form replace>
<field-list: fields="title, authors" />
<after-field-list:> <%= hidden_field(:author, :id) %></after-field-list:>
<actions:>
<submit label="#{ht 'book.actions.save', :default=>['Save']}" param/>
<or-cancel with="&@author" param="cancel"/>
</actions:>
</old-form>
</extend>
now add this to your books_controller.rb
def new_for_author
@author = Author.find_by_id(params[:author_id])
hobo_new
end
def create
@author = Author.find_by_id(params[:author][:id])
hobo_create do
@this.authors=[@author]
redirect_to @author if valid?
end
end
and this to your authors_controller.rb
def new_for_book
@book = Book.find_by_id(params[:book_id])
hobo_new
end
def create
@book = Book.find_by_id(params[:book][:id])
hobo_create do
@this.books=[@book]
redirect_to @book if valid?
end
end
You can now add new books to authors and new authors to books.
Bob
undefined method `form__for_author' for class `#<Class:0x565eff0>'undefined method `editable_by?' for #<ActiveRecord::Relation:0x562f560> ( http://pastebin.com/ykrQj0sH )
Any idea on why I can't extend tags in application.dryml or on how to avoid that editable_by? error? Could this be related to the 1.4 alpha?
Thanks in advance
Tom
Hmmm, that's a very strange error. If this is just a demo app,
please push it to github so I can take a look at it.
>
>
> Adding the extend tags to forms.dryml (bad idea, I know) seems to make it
> work but rendering the index view of authors causes
>
> undefined method `editable_by?' for #<ActiveRecord::Relation:0x562f560> (
> http://pastebin.com/ykrQj0sH )
>
> Any idea on why I can't extend tags in application.dryml or on how to avoid
> that editable_by? error? Could this be related to the 1.4 alpha?
Quite likely. Older versions of 1.4 have had problems like that.
They should be gone now -- please update your Hobo,
Bryan