Hobo and many to many associations recipe

39 views
Skip to first unread message

Bob Sleys

unread,
Jul 14, 2011, 4:54:05 PM7/14/11
to hobo...@googlegroups.com
Ok here are my notes on setting up a many to many association including adding child records to the parent, both directions.  Please comment and let me know what you think.  Once the dust settles I'll post it as a recipe on hobocentral.

Bob

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

Owen

unread,
Jul 14, 2011, 6:00:08 PM7/14/11
to Hobo Users
Very nice!

Bob Sleys

unread,
Jul 15, 2011, 11:10:21 AM7/15/11
to hobo...@googlegroups.com
I'd like to take this one step further but am going to need some expert help.

As it stands above when viewing a book there is a New Author link below the list of authors.  However to add an existing author to the book you need to edit the book.  On the flip side while editing to the book you can only add already existing authors to create a new author you need to either go back to the book view to create a new author and link them to the book or go to the authors view and create them and add the book to the author.

I'd like to either be able to create new authors and add existing authors to books from either the edit book screen or the show book screen.  IE be able to do both from one screen instead of splitting it up as it is now.  Anyone have any ideas on how to make that happen?

Bob

Tom Tosers

unread,
Mar 22, 2012, 4:46:46 AM3/22/12
to hobo...@googlegroups.com
I installed the 1.4 alpha version and started testing recipes to get a better understanding of Hobo.

This many to many recipe was a welcome candidate. 

In this example (applied to a new Hobo 1.4 alpha app) adding the extend tags to application.dryml causes

undefined method `form__for_author' for class `#<Class:0x565eff0>'

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?

Thanks in advance

Tom

Bryan Larsen

unread,
Mar 27, 2012, 5:44:27 PM3/27/12
to hobo...@googlegroups.com
> In this example (applied to a new Hobo 1.4 alpha app) adding the extend tags
> to application.dryml causes
>
> undefined method `form__for_author' for class `#<Class:0x565eff0>'

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

Bryan Larsen

unread,
Apr 17, 2012, 11:29:47 AM4/17/12
to Hobo Users


> > In this example (applied to a new Hobo 1.4 alpha app) adding the extend tags
> > to application.dryml causes
>
> > undefined method `form__for_author' for class `#<Class:0x565eff0>'
>


If anybody else encounters this problem, it is because a default
generated app for Hobo 1.4 creates a front_site.dryml that loads after
application.dryml. The front_site.dryml is the one that loads the
auto-generated forms, so put your extend statement at the bottom of
front_site.dryml and will be visible to all the views in the front
site. This problem can occur in earlier versions of Hobo, but
front_site.dryml is not generated in a blank app, so newcomers are
unlikely to run into it.

If you want your extension visible to both front and admin sites,
create a new file (common.dryml, perhaps), put the form extend in
there and then <include src="common"/> in both front_site.dryml and
admin_site.dryml. This is the style I generally use for all larger
Hobo apps because otherwise front_site.dryml can get very large and
unwieldy.

I've added a warning to the generated application.dryml:
https://github.com/tablatom/hobo/commit/3a1d4cf1d9eb1a45f90eb3618a5d22d1a471a3f6

Bryan
Reply all
Reply to author
Forward
0 new messages