Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 312 lines (282 sloc) 9.682 kb
75a960c Don't act destructively on ActiveModel::Name#human options hash. [#5366 ...
John Firebaugh authored
1 require 'active_support/core_ext/hash/except'
973b908 @snusnu Added missing AS require to active_model/naming.rb
snusnu authored
2 require 'active_support/core_ext/module/introspection'
f28bd95 @jeremy Fix dependencies revealed by testing in isolation
jeremy authored
3
1c4d28b @josh Move model naming into ActiveModel
josh authored
4 module ActiveModel
72cbccb ActiveModel::Name does not inherit from string
Lukasz Sarnacki authored
5 class Name
6 include Comparable
7
bf8d757 @nertzy Remove ActiveModel::Naming#partial_path
nertzy authored
8 attr_reader :singular, :plural, :element, :collection,
72cbccb ActiveModel::Name does not inherit from string
Lukasz Sarnacki authored
9 :singular_route_key, :route_key, :param_key, :i18n_key,
10 :name
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
11
99cf77b @jeremy Add #element and #collection to ModelName
jeremy authored
12 alias_method :cache_key, :collection
566d717 @jeremy Move Class::ModelName to Active Support module core_ext
jeremy authored
13
809d4f8 @frodsan update ActiveModel::Name documentation [ci skip]
frodsan authored
14 ##
15 # :method: ==
16 #
17 # :call-seq:
18 # ==(other)
19 #
20 # Equivalent to <tt>String#==</tt>. Returns +true+ if the class name and
21 # +other+ are equal, otherwise +false+.
22 #
23 # class BlogPost
24 # extend ActiveModel::Naming
25 # end
26 #
27 # BlogPost.model_name == 'BlogPost' # => true
28 # BlogPost.model_name == 'Blog Post' # => false
29
30 ##
31 # :method: ===
32 #
33 # :call-seq:
34 # ===(other)
35 #
36 # Equivalent to <tt>#==</tt>.
37 #
38 # class BlogPost
39 # extend ActiveModel::Naming
40 # end
41 #
42 # BlogPost.model_name === 'BlogPost' # => true
43 # BlogPost.model_name === 'Blog Post' # => false
44
45 ##
46 # :method: <=>
47 #
48 # :call-seq:
49 # ==(other)
50 #
51 # Equivalent to <tt>String#<=></tt>.
52 #
53 # class BlogPost
54 # extend ActiveModel::Naming
55 # end
56 #
57 # BlogPost.model_name <=> 'BlogPost' # => 0
b7edbc7 @senny cleanup, remove broken whitespace
senny authored
58 # BlogPost.model_name <=> 'Blog' # => 1
59 # BlogPost.model_name <=> 'BlogPosts' # => -1
809d4f8 @frodsan update ActiveModel::Name documentation [ci skip]
frodsan authored
60
61 ##
62 # :method: =~
63 #
64 # :call-seq:
65 # =~(regexp)
66 #
67 # Equivalent to <tt>String#=~</tt>. Match the class name against the given
68 # regexp. Returns the position where the match starts or +nil+ if there is
69 # no match.
70 #
71 # class BlogPost
72 # extend ActiveModel::Naming
73 # end
74 #
75 # BlogPost.model_name =~ /Post/ # => 4
76 # BlogPost.model_name =~ /\d/ # => nil
77
78 ##
79 # :method: !~
80 #
81 # :call-seq:
82 # !~(regexp)
83 #
84 # Equivalent to <tt>String#!~</tt>. Match the class name against the given
85 # regexp. Returns +true+ if there is no match, otherwise +false+.
86 #
87 # class BlogPost
88 # extend ActiveModel::Naming
89 # end
90 #
91 # BlogPost.model_name !~ /Post/ # => false
92 # BlogPost.model_name !~ /\d/ # => true
93
94 ##
95 # :method: eql?
96 #
97 # :call-seq:
98 # eql?(other)
99 #
100 # Equivalent to <tt>String#eql?</tt>. Returns +true+ if the class name and
101 # +other+ have the same length and content, otherwise +false+.
102 #
103 # class BlogPost
104 # extend ActiveModel::Naming
105 # end
106 #
107 # BlogPost.model_name.eql?('BlogPost') # => true
108 # BlogPost.model_name.eql?('Blog Post') # => false
109
110 ##
111 # :method: to_s
112 #
113 # :call-seq:
114 # to_s()
115 #
116 # Returns the class name.
117 #
118 # class BlogPost
119 # extend ActiveModel::Naming
120 # end
121 #
122 # BlogPost.model_name.to_s # => "BlogPost"
123
124 ##
125 # :method: to_str
126 #
127 # :call-seq:
128 # to_str()
129 #
130 # Equivalent to +to_s+.
72cbccb ActiveModel::Name does not inherit from string
Lukasz Sarnacki authored
131 delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
132 :to_str, :to => :name
dc39af0 @qoobaa make ActiveModel::Name fail gracefully with anonymous classes
qoobaa authored
133
809d4f8 @frodsan update ActiveModel::Name documentation [ci skip]
frodsan authored
134 # Returns a new ActiveModel::Name instance. By default, the +namespace+
135 # and +name+ option will take the namespace and name of the given class
136 # respectively.
137 #
138 # module Foo
139 # class Bar
140 # end
141 # end
142 #
143 # ActiveModel::Name.new(Foo::Bar).to_s
144 # # => "Foo::Bar"
72cbccb ActiveModel::Name does not inherit from string
Lukasz Sarnacki authored
145 def initialize(klass, namespace = nil, name = nil)
146 @name = name || klass.name
dc39af0 @qoobaa make ActiveModel::Name fail gracefully with anonymous classes
qoobaa authored
147
72cbccb ActiveModel::Name does not inherit from string
Lukasz Sarnacki authored
148 raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
6e5aed0 @drogus Prepared ActiveModel::Naming to handle cases for namespaced isolated eng...
drogus authored
149
72cbccb ActiveModel::Name does not inherit from string
Lukasz Sarnacki authored
150 @unnamespaced = @name.sub(/^#{namespace.name}::/, '') if namespace
fd86a1b @josevalim Rely on a public contract between railties instead of accessing railtie ...
josevalim authored
151 @klass = klass
65f4d80 @tenderlove no need to freeze things all the time
tenderlove authored
152 @singular = _singularize(@name)
153 @plural = ActiveSupport::Inflector.pluralize(@singular)
154 @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
155 @human = ActiveSupport::Inflector.humanize(@element)
156 @collection = ActiveSupport::Inflector.tableize(@name)
157 @param_key = (namespace ? _singularize(@unnamespaced) : @singular)
72cbccb ActiveModel::Name does not inherit from string
Lukasz Sarnacki authored
158 @i18n_key = @name.underscore.to_sym
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
159
160 @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
65f4d80 @tenderlove no need to freeze things all the time
tenderlove authored
161 @singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
162 @route_key << "_index" if @plural == @singular
566d717 @jeremy Move Class::ModelName to Active Support module core_ext
jeremy authored
163 end
e31077c @josevalim Small clean up in Naming and TTranslation tests.
josevalim authored
164
165 # Transform the model name into a more humane format, using I18n. By default,
809d4f8 @frodsan update ActiveModel::Name documentation [ci skip]
frodsan authored
166 # it will underscore then humanize the class name.
167 #
168 # class BlogPost
169 # extend ActiveModel::Naming
170 # end
755af49 @fxn edit pass to apply API guideline wrt the use of "# =>" in example code
fxn authored
171 #
172 # BlogPost.model_name.human # => "Blog post"
173 #
e31077c @josevalim Small clean up in Naming and TTranslation tests.
josevalim authored
174 # Specify +options+ with additional translating options.
175 def human(options={})
176 return @human unless @klass.respond_to?(:lookup_ancestors) &&
177 @klass.respond_to?(:i18n_scope)
178
179 defaults = @klass.lookup_ancestors.map do |klass|
410a428 @josevalim Get rid of the alternate namespace lookup.
josevalim authored
180 klass.model_name.i18n_key
181 end
e31077c @josevalim Small clean up in Naming and TTranslation tests.
josevalim authored
182
75a960c Don't act destructively on ActiveModel::Name#human options hash. [#5366 ...
John Firebaugh authored
183 defaults << options[:default] if options[:default]
e31077c @josevalim Small clean up in Naming and TTranslation tests.
josevalim authored
184 defaults << @human
185
f48d83b @carlosantoniodasilva Refactor human attribute name
carlosantoniodasilva authored
186 options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default))
e31077c @josevalim Small clean up in Naming and TTranslation tests.
josevalim authored
187 I18n.translate(defaults.shift, options)
188 end
6e5aed0 @drogus Prepared ActiveModel::Naming to handle cases for namespaced isolated eng...
drogus authored
189
190 private
4f90b28 @spastorino Bring back AMo#i18n_key method
spastorino authored
191
192 def _singularize(string, replacement='_')
193 ActiveSupport::Inflector.underscore(string).tr('/', replacement)
194 end
566d717 @jeremy Move Class::ModelName to Active Support module core_ext
jeremy authored
195 end
2572af1 @rizwanreza Revised and added headings.
rizwanreza authored
196
c9a88a2 @frodsan minor edits in AM documentation [ci skip]
frodsan authored
197 # == Active \Model \Naming
de12b5e @rizwanreza Revised naming.rb and lint.rb
rizwanreza authored
198 #
199 # Creates a +model_name+ method on your object.
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
200 #
fbc7c2b @mikel Adding ActiveModel::Naming documentation
mikel authored
201 # To implement, just extend ActiveModel::Naming in your object:
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
202 #
fbc7c2b @mikel Adding ActiveModel::Naming documentation
mikel authored
203 # class BookCover
9acd686 @snusnu Adds #key and #to_param to the AMo interface
snusnu authored
204 # extend ActiveModel::Naming
fbc7c2b @mikel Adding ActiveModel::Naming documentation
mikel authored
205 # end
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
206 #
755af49 @fxn edit pass to apply API guideline wrt the use of "# =>" in example code
fxn authored
207 # BookCover.model_name # => "BookCover"
208 # BookCover.model_name.human # => "Book cover"
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -exec ...
spastorino authored
209 #
d834755 @marcandre ActiveModel::Name#i18n_key: Fix doc and add tests
marcandre authored
210 # BookCover.model_name.i18n_key # => :book_cover
211 # BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
4f90b28 @spastorino Bring back AMo#i18n_key method
spastorino authored
212 #
fbc7c2b @mikel Adding ActiveModel::Naming documentation
mikel authored
213 # Providing the functionality that ActiveModel::Naming provides in your object
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
214 # is required to pass the Active Model Lint test. So either extending the
215 # provided method below, or rolling your own is required.
1c4d28b @josh Move model naming into ActiveModel
josh authored
216 module Naming
217 # Returns an ActiveModel::Name object for module. It can be
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
218 # used to retrieve all kinds of naming-related information
219 # (See ActiveModel::Name for more information).
b8e4f71 @frodsan add example to ActiveModel::Naming#model_name [ci skip]
frodsan authored
220 #
221 # class Person < ActiveModel::Model
222 # end
223 #
224 # Person.model_name # => Person
225 # Person.model_name.class # => ActiveModel::Name
226 # Person.model_name.singular # => "person"
227 # Person.model_name.plural # => "people"
1c4d28b @josh Move model naming into ActiveModel
josh authored
228 def model_name
9c57bd8 @thedarkone Fix Namind#model_name.
thedarkone authored
229 @_model_name ||= begin
fd86a1b @josevalim Rely on a public contract between railties instead of accessing railtie ...
josevalim authored
230 namespace = self.parents.detect do |n|
231 n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
232 end
9c57bd8 @thedarkone Fix Namind#model_name.
thedarkone authored
233 ActiveModel::Name.new(self, namespace)
234 end
1c4d28b @josh Move model naming into ActiveModel
josh authored
235 end
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
236
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
237 # Returns the plural class name of a record or class.
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
238 #
239 # ActiveModel::Naming.plural(post) # => "posts"
240 # ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
241 def self.plural(record_or_class)
242 model_name_from_record_or_class(record_or_class).plural
243 end
244
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
245 # Returns the singular class name of a record or class.
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
246 #
247 # ActiveModel::Naming.singular(post) # => "post"
248 # ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
249 def self.singular(record_or_class)
250 model_name_from_record_or_class(record_or_class).singular
251 end
252
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
253 # Identifies whether the class name of a record or class is uncountable.
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
254 #
255 # ActiveModel::Naming.uncountable?(Sheep) # => true
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
256 # ActiveModel::Naming.uncountable?(Post) # => false
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
257 def self.uncountable?(record_or_class)
258 plural(record_or_class) == singular(record_or_class)
259 end
260
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
261 # Returns string to use while generating route names. It differs for
262 # namespaced models regarding whether it's inside isolated engine.
263 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
264 # # For isolated engine:
ba291a6 Trivial documentation fix for ActiveModel::Naming comment.
Dennis Taylor authored
265 # ActiveModel::Naming.singular_route_key(Blog::Post) #=> post
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
266 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
267 # # For shared engine:
ba291a6 Trivial documentation fix for ActiveModel::Naming comment.
Dennis Taylor authored
268 # ActiveModel::Naming.singular_route_key(Blog::Post) #=> blog_post
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
269 def self.singular_route_key(record_or_class)
270 model_name_from_record_or_class(record_or_class).singular_route_key
271 end
272
273 # Returns string to use while generating route names. It differs for
274 # namespaced models regarding whether it's inside isolated engine.
275 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
276 # # For isolated engine:
277 # ActiveModel::Naming.route_key(Blog::Post) #=> posts
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
278 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
279 # # For shared engine:
280 # ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
281 #
282 # The route key also considers if the noun is uncountable and, in
283 # such cases, automatically appends _index.
6e5aed0 @drogus Prepared ActiveModel::Naming to handle cases for namespaced isolated eng...
drogus authored
284 def self.route_key(record_or_class)
285 model_name_from_record_or_class(record_or_class).route_key
286 end
287
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
288 # Returns string to use for params names. It differs for
289 # namespaced models regarding whether it's inside isolated engine.
290 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
291 # # For isolated engine:
292 # ActiveModel::Naming.param_key(Blog::Post) #=> post
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
293 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
294 # # For shared engine:
295 # ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
6e5aed0 @drogus Prepared ActiveModel::Naming to handle cases for namespaced isolated eng...
drogus authored
296 def self.param_key(record_or_class)
297 model_name_from_record_or_class(record_or_class).param_key
298 end
299
ce06b8a @carlosantoniodasilva Refactor model name logic and make it a real private class method
carlosantoniodasilva authored
300 def self.model_name_from_record_or_class(record_or_class) #:nodoc:
301 if record_or_class.respond_to?(:model_name)
302 record_or_class.model_name
303 elsif record_or_class.respond_to?(:to_model)
304 record_or_class.to_model.class.model_name
305 else
920d9ee @Bertg Naming helpers should first check if passed object responds to model_nam...
Bertg authored
306 record_or_class.class.model_name
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
307 end
ce06b8a @carlosantoniodasilva Refactor model name logic and make it a real private class method
carlosantoniodasilva authored
308 end
309 private_class_method :model_name_from_record_or_class
566d717 @jeremy Move Class::ModelName to Active Support module core_ext
jeremy authored
310 end
311 end
Something went wrong with that request. Please try again.