aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/naming.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model/naming.rb')
-rw-r--r--activemodel/lib/active_model/naming.rb259
1 files changed, 213 insertions, 46 deletions
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index f16459ede2..c0d93e5d53 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -1,34 +1,174 @@
require 'active_support/inflector'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/module/introspection'
-require 'active_support/core_ext/module/deprecation'
module ActiveModel
- class Name < String
- attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key, :i18n_key
+ class Name
+ include Comparable
+
+ attr_reader :singular, :plural, :element, :collection,
+ :singular_route_key, :route_key, :param_key, :i18n_key,
+ :name
+
alias_method :cache_key, :collection
- deprecate :partial_path => "ActiveModel::Name#partial_path is deprecated. Call #to_partial_path on model instances directly instead."
+ ##
+ # :method: ==
+ #
+ # :call-seq:
+ # ==(other)
+ #
+ # Equivalent to <tt>String#==</tt>. Returns +true+ if the class name and
+ # +other+ are equal, otherwise +false+.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BlogPost.model_name == 'BlogPost' # => true
+ # BlogPost.model_name == 'Blog Post' # => false
+
+ ##
+ # :method: ===
+ #
+ # :call-seq:
+ # ===(other)
+ #
+ # Equivalent to <tt>#==</tt>.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BlogPost.model_name === 'BlogPost' # => true
+ # BlogPost.model_name === 'Blog Post' # => false
+
+ ##
+ # :method: <=>
+ #
+ # :call-seq:
+ # ==(other)
+ #
+ # Equivalent to <tt>String#<=></tt>.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BlogPost.model_name <=> 'BlogPost' # => 0
+ # BlogPost.model_name <=> 'Blog' # => 1
+ # BlogPost.model_name <=> 'BlogPosts' # => -1
+
+ ##
+ # :method: =~
+ #
+ # :call-seq:
+ # =~(regexp)
+ #
+ # Equivalent to <tt>String#=~</tt>. Match the class name against the given
+ # regexp. Returns the position where the match starts or +nil+ if there is
+ # no match.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BlogPost.model_name =~ /Post/ # => 4
+ # BlogPost.model_name =~ /\d/ # => nil
+ ##
+ # :method: !~
+ #
+ # :call-seq:
+ # !~(regexp)
+ #
+ # Equivalent to <tt>String#!~</tt>. Match the class name against the given
+ # regexp. Returns +true+ if there is no match, otherwise +false+.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BlogPost.model_name !~ /Post/ # => false
+ # BlogPost.model_name !~ /\d/ # => true
+
+ ##
+ # :method: eql?
+ #
+ # :call-seq:
+ # eql?(other)
+ #
+ # Equivalent to <tt>String#eql?</tt>. Returns +true+ if the class name and
+ # +other+ have the same length and content, otherwise +false+.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BlogPost.model_name.eql?('BlogPost') # => true
+ # BlogPost.model_name.eql?('Blog Post') # => false
+
+ ##
+ # :method: to_s
+ #
+ # :call-seq:
+ # to_s()
+ #
+ # Returns the class name.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BlogPost.model_name.to_s # => "BlogPost"
+
+ ##
+ # :method: to_str
+ #
+ # :call-seq:
+ # to_str()
+ #
+ # Equivalent to +to_s+.
+ delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
+ :to_str, :to => :name
+
+ # Returns a new ActiveModel::Name instance. By default, the +namespace+
+ # and +name+ option will take the namespace and name of the given class
+ # respectively.
+ #
+ # module Foo
+ # class Bar
+ # end
+ # end
+ #
+ # ActiveModel::Name.new(Foo::Bar).to_s
+ # # => "Foo::Bar"
def initialize(klass, namespace = nil, name = nil)
- name ||= klass.name
- super(name)
- @unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace
-
- @klass = klass
- @singular = _singularize(self).freeze
- @plural = ActiveSupport::Inflector.pluralize(@singular).freeze
- @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
- @human = ActiveSupport::Inflector.humanize(@element).freeze
- @collection = ActiveSupport::Inflector.tableize(self).freeze
- @partial_path = "#{@collection}/#{@element}".freeze
- @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
- @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze
- @i18n_key = self.underscore.to_sym
+ @name = name || klass.name
+
+ raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
+
+ @unnamespaced = @name.sub(/^#{namespace.name}::/, '') if namespace
+ @klass = klass
+ @singular = _singularize(@name)
+ @plural = ActiveSupport::Inflector.pluralize(@singular)
+ @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
+ @human = ActiveSupport::Inflector.humanize(@element)
+ @collection = ActiveSupport::Inflector.tableize(@name)
+ @param_key = (namespace ? _singularize(@unnamespaced) : @singular)
+ @i18n_key = @name.underscore.to_sym
+
+ @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
+ @singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
+ @route_key << "_index" if @plural == @singular
end
# Transform the model name into a more humane format, using I18n. By default,
- # it will underscore then humanize the class name
+ # it will underscore then humanize the class name.
+ #
+ # class BlogPost
+ # extend ActiveModel::Naming
+ # end
#
# BlogPost.model_name.human # => "Blog post"
#
@@ -44,7 +184,7 @@ module ActiveModel
defaults << options[:default] if options[:default]
defaults << @human
- options = {:scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults}.merge(options.except(:default))
+ options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default))
I18n.translate(defaults.shift, options)
end
@@ -68,23 +208,34 @@ module ActiveModel
# BookCover.model_name # => "BookCover"
# BookCover.model_name.human # => "Book cover"
#
- # BookCover.model_name.i18n_key # => "book_cover"
- # BookModule::BookCover.model_name.i18n_key # => "book_module.book_cover"
+ # BookCover.model_name.i18n_key # => :book_cover
+ # BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
#
# Providing the functionality that ActiveModel::Naming provides in your object
- # is required to pass the Active Model Lint test. So either extending the provided
- # method below, or rolling your own is required.
+ # is required to pass the Active Model Lint test. So either extending the
+ # provided method below, or rolling your own is required.
module Naming
# Returns an ActiveModel::Name object for module. It can be
- # used to retrieve all kinds of naming-related information.
+ # used to retrieve all kinds of naming-related information
+ # (See ActiveModel::Name for more information).
+ #
+ # class Person < ActiveModel::Model
+ # end
+ #
+ # Person.model_name # => Person
+ # Person.model_name.class # => ActiveModel::Name
+ # Person.model_name.singular # => "person"
+ # Person.model_name.plural # => "people"
def model_name
@_model_name ||= begin
- namespace = self.parents.detect { |n| n.respond_to?(:_railtie) }
+ namespace = self.parents.detect do |n|
+ n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
+ end
ActiveModel::Name.new(self, namespace)
end
end
- # Returns the plural class name of a record or class. Examples:
+ # Returns the plural class name of a record or class.
#
# ActiveModel::Naming.plural(post) # => "posts"
# ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
@@ -92,7 +243,7 @@ module ActiveModel
model_name_from_record_or_class(record_or_class).plural
end
- # Returns the singular class name of a record or class. Examples:
+ # Returns the singular class name of a record or class.
#
# ActiveModel::Naming.singular(post) # => "post"
# ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
@@ -100,10 +251,10 @@ module ActiveModel
model_name_from_record_or_class(record_or_class).singular
end
- # Identifies whether the class name of a record or class is uncountable. Examples:
+ # Identifies whether the class name of a record or class is uncountable.
#
# ActiveModel::Naming.uncountable?(Sheep) # => true
- # ActiveModel::Naming.uncountable?(Post) => false
+ # ActiveModel::Naming.uncountable?(Post) # => false
def self.uncountable?(record_or_class)
plural(record_or_class) == singular(record_or_class)
end
@@ -111,11 +262,26 @@ module ActiveModel
# Returns string to use while generating route names. It differs for
# namespaced models regarding whether it's inside isolated engine.
#
- # For isolated engine:
- # ActiveModel::Naming.route_key(Blog::Post) #=> posts
+ # # For isolated engine:
+ # ActiveModel::Naming.route_key(Blog::Post) #=> post
+ #
+ # # For shared engine:
+ # ActiveModel::Naming.route_key(Blog::Post) #=> blog_post
+ def self.singular_route_key(record_or_class)
+ model_name_from_record_or_class(record_or_class).singular_route_key
+ end
+
+ # Returns string to use while generating route names. It differs for
+ # namespaced models regarding whether it's inside isolated engine.
+ #
+ # # For isolated engine:
+ # ActiveModel::Naming.route_key(Blog::Post) #=> posts
+ #
+ # # For shared engine:
+ # ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
#
- # For shared engine:
- # ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
+ # The route key also considers if the noun is uncountable and, in
+ # such cases, automatically appends _index.
def self.route_key(record_or_class)
model_name_from_record_or_class(record_or_class).route_key
end
@@ -123,23 +289,24 @@ module ActiveModel
# Returns string to use for params names. It differs for
# namespaced models regarding whether it's inside isolated engine.
#
- # For isolated engine:
- # ActiveModel::Naming.param_key(Blog::Post) #=> post
+ # # For isolated engine:
+ # ActiveModel::Naming.param_key(Blog::Post) #=> post
#
- # For shared engine:
- # ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
+ # # For shared engine:
+ # ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
def self.param_key(record_or_class)
model_name_from_record_or_class(record_or_class).param_key
end
- private
- def self.model_name_from_record_or_class(record_or_class)
- (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
- end
-
- def self.convert_to_model(object)
- object.respond_to?(:to_model) ? object.to_model : object
+ def self.model_name_from_record_or_class(record_or_class) #:nodoc:
+ if record_or_class.respond_to?(:model_name)
+ record_or_class.model_name
+ elsif record_or_class.respond_to?(:to_model)
+ record_or_class.to_model.class.model_name
+ else
+ record_or_class.class.model_name
end
+ end
+ private_class_method :model_name_from_record_or_class
end
-
end