0

I have the scenario where an author has and belongs to many books, vice versa. Following the instructions for setting up associations in a one-to-many relationship works fine but when a many-to-many relationship introduced I get this error message whenever I try to create or update my book model.

undefined method `author' for #<Book:0x007fb91ae56a70>

As far as setting up how authors are chosen for a book I'm using the code provided by the token-input railscast here with a few alterations.

class Author < ActiveRecord::Base
    has_many :authorships
    has_many :books, through: :authorships

    def self.tokens(query)
        authors = where("name like ?", "%#{query}%")
        if authors.empty?
            [{id: "<<<#{query}>>>", name: "Add New Author: \"#{query}\""}]
        else
            authors
        end
    end

    def self.ids_from_tokens(tokens)
       tokens.gsub!(/<<<(.+?)>>>/) {create!(name: $1).id}
       tokens.split(',')
    end
end

class Book < ActiveRecord::Base
    attr_reader :author_tokens

    include PublicActivity::Model
    tracked owner: :author

    has_many :authorships
    has_many :authors, through: :authorships

    def author_tokens=(ids)
        self.author_ids = Author.ids_from_tokens(ids)
    end
end

Form View

<%= form_for(@book) do |f| %>
  ...

  <div class="field">
    <%= f.text_field :author_tokens, label: 'Author', input_html: {"data-pre" => @book.authors.to_json} %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119
  • @MohammadAbuShady Any idea how this would be done based off the one-to-many discussed prior? – Carl Edwards Apr 02 '15 at 17:42
  • when u changed it to `has_many :authors` you lost the method `author` and got the method `authors` instead – Mohammad AbuShady Apr 02 '15 at 20:07
  • @MohammadAbuShady Makes perfect sense. The solution provided by Piotrek shows how to select one author in a proc but the goal would be to get them all. Is this possible to do in this particular scenario? – Carl Edwards Apr 02 '15 at 21:17
  • i'm afraid that's a gem specific question, i'll check the gem's read me and tell you if i find something – Mohammad AbuShady Apr 03 '15 at 04:19

2 Answers2

0

There is no author relationship in your Book model.

What

tracked owner: :author

does is basically calling method author on your Book instance. You should try :authors

But!

That won't solve your problem because owner can only be one. So you can do something like:

tracked owner: proc {|_, book| book.authors.first }

to set the owner to the first author the book has.

Piotrek Okoński
  • 600
  • 8
  • 17
  • Testing this out it does exactly as it reads but the problem occurs when two or more authors are added to the book. In the activities view it will only record that the first author of that book had an activity. – Carl Edwards Apr 02 '15 at 21:15
  • If you want to create an activity for every author you need to do so manualy via `create_activity` called on the Book instance. – Piotrek Okoński Apr 08 '15 at 07:58
  • How would that logic look? Would it be done via the books controller or in some sort of callback in the model? – Carl Edwards Apr 08 '15 at 13:49
-2
class Author < ActiveRecord::Base
  has_many :author_books, inverse_of: :author, dependent: :destroy
  accepts_nested_attributes_for :author_books
  has_many :books, through: :author_books
end

class Book < ActiveRecord::Base
  has_many :author_books, inverse_of: :book, dependent: :destroy
  accepts_nested_attributes_for :author_books
  has_many :authors, through: :author_books
end

class AuthorBook < ActiveRecord::Base
  validates_presence_of :book, :author
end

============= view ==============

<%= form_for @book do |f| %>
  <%= f.text_field :title %>
  <%= f.fields_for :author_books do |f2| %>
    <%# will look through all author_books in the form builder.. %>
    <%= f2.fields_for :author do |f3| %>
      <%= f3.text_field :name %>
    <% end %>
  <% end %>
<% end %>
ilan berci
  • 3,883
  • 1
  • 16
  • 21