Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 320 lines (289 sloc) 9.872 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,
eebb9dd @patricksrobertson Convert ActiveModel to 1.9 hash syntax.
patricksrobertson authored
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
eebb9dd @patricksrobertson Convert ActiveModel to 1.9 hash syntax.
patricksrobertson 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 #
33cc907 @shunsukeaida Name#model_name doesn't return a String object
shunsukeaida authored
207 # BookCover.model_name.name # => "BookCover"
755af49 @fxn edit pass to apply API guideline wrt the use of "# =>" in example code
fxn authored
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
10adc2e @yuki24 Delegate #model_name method to self.class
yuki24 authored
217 def self.extended(base) #:nodoc:
218 base.class_eval do
fa03fa7 @sgrif Silence warning emitted in tests
sgrif authored
219 remove_possible_method(:model_name)
10adc2e @yuki24 Delegate #model_name method to self.class
yuki24 authored
220 delegate :model_name, to: :class
221 end
222 end
223
1c4d28b @josh Move model naming into ActiveModel
josh authored
224 # Returns an ActiveModel::Name object for module. It can be
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
225 # used to retrieve all kinds of naming-related information
226 # (See ActiveModel::Name for more information).
b8e4f71 @frodsan add example to ActiveModel::Naming#model_name [ci skip]
frodsan authored
227 #
fd3f3c5 @akshay-vishnoi [ci skip] Include ActiveModel::Model in a class instead of inheriting
akshay-vishnoi authored
228 # class Person
229 # include ActiveModel::Model
b8e4f71 @frodsan add example to ActiveModel::Naming#model_name [ci skip]
frodsan authored
230 # end
231 #
fd3f3c5 @akshay-vishnoi [ci skip] Include ActiveModel::Model in a class instead of inheriting
akshay-vishnoi authored
232 # Person.model_name.name # => "Person"
b8e4f71 @frodsan add example to ActiveModel::Naming#model_name [ci skip]
frodsan authored
233 # Person.model_name.class # => ActiveModel::Name
234 # Person.model_name.singular # => "person"
235 # Person.model_name.plural # => "people"
1c4d28b @josh Move model naming into ActiveModel
josh authored
236 def model_name
9c57bd8 @thedarkone Fix Namind#model_name.
thedarkone authored
237 @_model_name ||= begin
fd86a1b @josevalim Rely on a public contract between railties instead of accessing railtie ...
josevalim authored
238 namespace = self.parents.detect do |n|
239 n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
240 end
9c57bd8 @thedarkone Fix Namind#model_name.
thedarkone authored
241 ActiveModel::Name.new(self, namespace)
242 end
1c4d28b @josh Move model naming into ActiveModel
josh authored
243 end
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
244
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
245 # Returns the plural class name of a record or class.
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
246 #
247 # ActiveModel::Naming.plural(post) # => "posts"
248 # ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
249 def self.plural(record_or_class)
250 model_name_from_record_or_class(record_or_class).plural
251 end
252
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
253 # Returns the singular class name of a record or class.
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
254 #
255 # ActiveModel::Naming.singular(post) # => "post"
256 # ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
257 def self.singular(record_or_class)
258 model_name_from_record_or_class(record_or_class).singular
259 end
260
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
261 # 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
262 #
263 # ActiveModel::Naming.uncountable?(Sheep) # => true
87615f2 @frodsan update ActiveModel::Naming documentation
frodsan authored
264 # ActiveModel::Naming.uncountable?(Post) # => false
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
265 def self.uncountable?(record_or_class)
266 plural(record_or_class) == singular(record_or_class)
267 end
268
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
269 # Returns string to use while generating route names. It differs for
270 # namespaced models regarding whether it's inside isolated engine.
271 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
272 # # For isolated engine:
efff6c1 @sikachu Change syntax format for example returned values
sikachu authored
273 # ActiveModel::Naming.singular_route_key(Blog::Post) # => "post"
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
274 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
275 # # For shared engine:
efff6c1 @sikachu Change syntax format for example returned values
sikachu authored
276 # ActiveModel::Naming.singular_route_key(Blog::Post) # => "blog_post"
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
277 def self.singular_route_key(record_or_class)
278 model_name_from_record_or_class(record_or_class).singular_route_key
279 end
280
281 # Returns string to use while generating route names. It differs for
282 # namespaced models regarding whether it's inside isolated engine.
283 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
284 # # For isolated engine:
efff6c1 @sikachu Change syntax format for example returned values
sikachu authored
285 # ActiveModel::Naming.route_key(Blog::Post) # => "posts"
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
286 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
287 # # For shared engine:
efff6c1 @sikachu Change syntax format for example returned values
sikachu authored
288 # ActiveModel::Naming.route_key(Blog::Post) # => "blog_posts"
7280787 @josevalim Improve cache on route_key lookup.
josevalim authored
289 #
290 # The route key also considers if the noun is uncountable and, in
291 # such cases, automatically appends _index.
6e5aed0 @drogus Prepared ActiveModel::Naming to handle cases for namespaced isolated eng...
drogus authored
292 def self.route_key(record_or_class)
293 model_name_from_record_or_class(record_or_class).route_key
294 end
295
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
296 # Returns string to use for params names. It differs for
297 # namespaced models regarding whether it's inside isolated engine.
298 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
299 # # For isolated engine:
efff6c1 @sikachu Change syntax format for example returned values
sikachu authored
300 # ActiveModel::Naming.param_key(Blog::Post) # => "post"
bf1ac82 @drogus Add some documantation on new route_key and param_key in ActiveModel::Na...
drogus authored
301 #
36ebafd @frodsan fix AM::Naming examples [ci skip]
frodsan authored
302 # # For shared engine:
efff6c1 @sikachu Change syntax format for example returned values
sikachu authored
303 # ActiveModel::Naming.param_key(Blog::Post) # => "blog_post"
6e5aed0 @drogus Prepared ActiveModel::Naming to handle cases for namespaced isolated eng...
drogus authored
304 def self.param_key(record_or_class)
305 model_name_from_record_or_class(record_or_class).param_key
306 end
307
ce06b8a @carlosantoniodasilva Refactor model name logic and make it a real private class method
carlosantoniodasilva authored
308 def self.model_name_from_record_or_class(record_or_class) #:nodoc:
309 if record_or_class.respond_to?(:model_name)
310 record_or_class.model_name
311 elsif record_or_class.respond_to?(:to_model)
312 record_or_class.to_model.class.model_name
313 else
920d9ee @Bertg Naming helpers should first check if passed object responds to model_nam...
Bertg authored
314 record_or_class.class.model_name
6807b08 @drogus Moved a few methods from RecordIdentifier to ActiveModel::Naming
drogus authored
315 end
ce06b8a @carlosantoniodasilva Refactor model name logic and make it a real private class method
carlosantoniodasilva authored
316 end
317 private_class_method :model_name_from_record_or_class
566d717 @jeremy Move Class::ModelName to Active Support module core_ext
jeremy authored
318 end
319 end
Something went wrong with that request. Please try again.