1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionview/lib/action_view/record_identifier.rb
claudiob 943ebcb5f1 Better docs for AV::RecordIdentifier
This commit intends to clarify the scope of ActionView::RecordIdentifier
methods `dom_id` and `dom_class`.

Most of the current documentation comes from da257eb8 (7 years ago) when
the decoupling of ActionView, ActiveRecord and ActiveModel was not a concern.

Since then, steps have been taken to reach such decoupling, especially
8ca17926 which duplicated ActionController::ModelNaming into
ActionView::ModelNaming explaining that:

> These are just a simple helpers for decoupling Active Model, so it does not
> make sense to extract it to Active Support, but the point is to decouple also
> Action View and Action Pack

As of today, ActionView::RecordIdentifier only includes `dom_id` and `dom_class`
so it makes sense to explicitly document those two methods, and leaving the
details of helpers like `div_for` in the corresponding files.

Moreover, I think it's important to mention in the documentation that
ActionView::RecordIdentifier **does not strictly depend on the ActiveRecord API**: any class `Post` implementing `post.to_key` and `post.model_name.param_key` will work.

[ci skip]
2014-12-23 02:01:53 +01:00

108 lines
3.9 KiB
Ruby

require 'active_support/core_ext/module'
require 'action_view/model_naming'
module ActionView
# RecordIdentifier encapsulates methods used by various ActionView helpers
# to associate records with DOM elements.
#
# Consider for example the following code that displays the body of a post:
#
# <%= div_for(post) do %>
# <%= post.body %>
# <% end %>
#
# When +post+ is a new, unsaved ActiveRecord::Base intance, the resulting HTML
# is:
#
# <div id="new_post" class="post">
# </div>
#
# When +post+ is a persisted ActiveRecord::Base instance, the resulting HTML
# is:
#
# <div id="post_42" class="post">
# What a wonderful world!
# </div>
#
# In both cases, the +id+ and +class+ of the wrapping DOM element are
# automatically generated, following naming conventions encapsulated by the
# RecordIdentifier methods #dom_id and #dom_class:
#
# dom_id(Post.new) # => "new_post"
# dom_class(Post.new) # => "post"
# dom_id(Post.find 42) # => "post_42"
# dom_class(Post.find 42) # => "post"
#
# Note that these methods do not strictly require +Post+ to be a subclass of
# ActiveRecord::Base.
# Any +Post+ class will do as long as its instances respond to +post.to_key+
# and +post.model_name.param_key+; for instance:
#
# class Post
# attr_accessor :to_key
#
# def model_name
# OpenStruct.new param_key: 'post'
# end
#
# def self.find(id)
# new.tap{|post| post.to_key = [id]}
# end
# end
module RecordIdentifier
extend self
extend ModelNaming
include ModelNaming
JOIN = '_'.freeze
NEW = 'new'.freeze
# The DOM class convention is to use the singular form of an object or class.
#
# dom_class(post) # => "post"
# dom_class(Person) # => "person"
#
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
#
# dom_class(post, :edit) # => "edit_post"
# dom_class(Person, :edit) # => "edit_person"
def dom_class(record_or_class, prefix = nil)
singular = model_name_from_record_or_class(record_or_class).param_key
prefix ? "#{prefix}#{JOIN}#{singular}" : singular
end
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
# If no id is found, prefix with "new_" instead.
#
# dom_id(Post.find(45)) # => "post_45"
# dom_id(Post.new) # => "new_post"
#
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
#
# dom_id(Post.find(45), :edit) # => "edit_post_45"
# dom_id(Post.new, :custom) # => "custom_post"
def dom_id(record, prefix = nil)
if record_id = record_key_for_dom_id(record)
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
else
dom_class(record, prefix || NEW)
end
end
protected
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
# This can be overwritten to customize the default generated string representation if desired.
# If you need to read back a key from a dom_id in order to query for the underlying database record,
# you should write a helper like 'person_record_from_dom_id' that will extract the key either based
# on the default implementation (which just joins all key attributes with '_') or on your own
# overwritten version of the method. By default, this implementation passes the key string through a
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
# make sure yourself that your dom ids are valid, in case you overwrite this method.
def record_key_for_dom_id(record)
key = convert_to_model(record).to_key
key ? key.join('_') : key
end
end
end