MBO / attrtastic

Attrtastic, in its assumtion, should be similar in usage to formtastic and ease of displaying AR record informations (attributes). It should help scafforld show/index pages.
http://github.com/MBO/attrtastic
MIT License
50 stars 8 forks source link

Better support for ActiveRecord associations #16

Open itspriddle opened 12 years ago

itspriddle commented 12 years ago

I've been working on adding better support to attrtastic for attributes that are ActiveRecord associations. Currently, attrtastic just calls .to_s on them, and you end up with something like #<Post:0x007f893f477a78>.

Here are the relevant pieces of an app to illustrate what I'm talking about.

Given the following models/associations:

# app/models/author.rb
# Attributes: :id, :name
class Author < ActiveRecord::Base
  has_many :posts
end

# app/models/post.rb
# Attributes: :id, :name
class Post < ActiveRecord::Base
  belongs_to :author
end

# Created as:
author = Author.create! name: "Josh"
post   = Post.create! name: "First Post", author_id: author.id

and given this ERB:

# app/views/posts/show.html.erb
<%= semantic_attributes_for @post do |attr| %>
  <%= attr.attributes do %>
    <%= attr.attribute :name %>
    <%= attr.attribute :author %>
  <% end %>
<% end %>

My changes would generate this HTML:

<div class="attrtastic post">
  <div class="attributes"><ol>
    <li class="attribute"><span class="label">Name</span><span class="value">First Post</span></li>
    <li class="attribute"><span class="label">Author</span><span class="value">Josh</span></li>
</ol></div></div>

Here's the code that provides this behavior:

Attrtastic.default_options.merge!(
  # If an attribute is an ActiveRecord itself, check for any of these columns
  # to print it's value. Otherwise, fallback to .to_s
  active_record_name_columns: [:full_name, :name, :title]
)

module Attrtastic
  class SemanticAttributesBuilder
    def format_attribute_value_custom(value)
      opts = Attrtastic.default_options.fetch(:active_record_name_columns, [:name])
      case value
      when ActiveRecord::Base
        if meth = opts.find { |m| value.respond_to?(m) }
          value.send(meth).to_s
        else
          "#{value.class.name.humanize} ##{value.id}"
        end
      else
        format_attribute_value_default value
      end
    end
    alias_method :format_attribute_value_default, :format_attribute_value
    alias_method :format_attribute_value, :format_attribute_value_custom
  end
end

If that's something you think might be useful for other users, I'd be happy to clean it up and submit a proper patch.

MBO commented 12 years ago

I don't think it's good idea.

First: I don't want to depend on ActiveRecord (Attrtastic doesn't depend on ActiveRecord, you can pass POROs),

Second: if you don't provide sane to_s for your objects, then you have similar problem with link_to helper and other ways where you output your associated object (you have to pass method name instead of defaulting on to_s).

You can archive the same with either:

  1. :format option, when you provide proper formatter for value, i.e:

    def ar_formatter(object)
     method_name = %w( full_name name ).find {|m| object.respond_to?(:m) }
     object.send(method_name || "to_s")
    end
  2. :value option, where you can provide symbol used as method name to retrieve value from object

Of course you can combine both of these, Attrtastic first applies :value if it's present to retrieve actual value of attribute and then it is formatted according to :format option (it can be false to no format at all an only call to_s, or is formatted with some defaults)