activemodel 3.0.20 → 3.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +17 -73
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/lib/active_model.rb +2 -2
- data/lib/active_model/attribute_methods.rb +46 -53
- data/lib/active_model/callbacks.rb +2 -5
- data/lib/active_model/conversion.rb +0 -2
- data/lib/active_model/dirty.rb +3 -4
- data/lib/active_model/errors.rb +55 -56
- data/lib/active_model/lint.rb +2 -2
- data/lib/active_model/mass_assignment_security.rb +96 -47
- data/lib/active_model/naming.rb +55 -13
- data/lib/active_model/observer_array.rb +104 -0
- data/lib/active_model/observing.rb +53 -18
- data/lib/active_model/secure_password.rb +67 -0
- data/lib/active_model/serialization.rb +4 -11
- data/lib/active_model/serializers/json.rb +18 -18
- data/lib/active_model/serializers/xml.rb +26 -5
- data/lib/active_model/translation.rb +4 -11
- data/lib/active_model/validations.rb +23 -23
- data/lib/active_model/validations/acceptance.rb +3 -5
- data/lib/active_model/validations/callbacks.rb +5 -19
- data/lib/active_model/validations/confirmation.rb +6 -5
- data/lib/active_model/validations/exclusion.rb +27 -3
- data/lib/active_model/validations/format.rb +38 -12
- data/lib/active_model/validations/inclusion.rb +30 -23
- data/lib/active_model/validations/length.rb +3 -1
- data/lib/active_model/validations/numericality.rb +4 -2
- data/lib/active_model/validations/presence.rb +3 -2
- data/lib/active_model/validations/validates.rb +23 -9
- data/lib/active_model/validations/with.rb +14 -2
- data/lib/active_model/validator.rb +16 -18
- data/lib/active_model/version.rb +3 -3
- metadata +71 -58
- checksums.yaml +0 -7
- data/lib/active_model/deprecated_error_methods.rb +0 -33
data/lib/active_model/errors.rb
CHANGED
@@ -60,11 +60,13 @@ module ActiveModel
|
|
60
60
|
# p.validate! # => ["can not be nil"]
|
61
61
|
# p.errors.full_messages # => ["name can not be nil"]
|
62
62
|
# # etc..
|
63
|
-
class Errors
|
64
|
-
include
|
63
|
+
class Errors
|
64
|
+
include Enumerable
|
65
65
|
|
66
66
|
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank]
|
67
67
|
|
68
|
+
attr_reader :messages
|
69
|
+
|
68
70
|
# Pass in the instance of the object that is using the errors object.
|
69
71
|
#
|
70
72
|
# class Person
|
@@ -73,12 +75,29 @@ module ActiveModel
|
|
73
75
|
# end
|
74
76
|
# end
|
75
77
|
def initialize(base)
|
76
|
-
@base
|
77
|
-
|
78
|
+
@base = base
|
79
|
+
@messages = ActiveSupport::OrderedHash.new
|
80
|
+
end
|
81
|
+
|
82
|
+
# Clear the messages
|
83
|
+
def clear
|
84
|
+
messages.clear
|
85
|
+
end
|
86
|
+
|
87
|
+
# Do the error messages include an error with key +error+?
|
88
|
+
def include?(error)
|
89
|
+
messages.include? error
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get messages for +key+
|
93
|
+
def get(key)
|
94
|
+
messages[key]
|
78
95
|
end
|
79
96
|
|
80
|
-
|
81
|
-
|
97
|
+
# Set messages for +key+ to +value+
|
98
|
+
def set(key, value)
|
99
|
+
messages[key] = value
|
100
|
+
end
|
82
101
|
|
83
102
|
# When passed a symbol or a name of a method, returns an array of errors
|
84
103
|
# for the method.
|
@@ -112,7 +131,7 @@ module ActiveModel
|
|
112
131
|
# # then yield :name and "must be specified"
|
113
132
|
# end
|
114
133
|
def each
|
115
|
-
each_key do |attribute|
|
134
|
+
messages.each_key do |attribute|
|
116
135
|
self[attribute].each { |error| yield attribute, error }
|
117
136
|
end
|
118
137
|
end
|
@@ -127,6 +146,16 @@ module ActiveModel
|
|
127
146
|
values.flatten.size
|
128
147
|
end
|
129
148
|
|
149
|
+
# Returns all message values
|
150
|
+
def values
|
151
|
+
messages.values
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns all message keys
|
155
|
+
def keys
|
156
|
+
messages.keys
|
157
|
+
end
|
158
|
+
|
130
159
|
# Returns an array of error messages, with the attribute name included
|
131
160
|
#
|
132
161
|
# p.errors.add(:name, "can't be blank")
|
@@ -171,9 +200,7 @@ module ActiveModel
|
|
171
200
|
end
|
172
201
|
|
173
202
|
def to_hash
|
174
|
-
|
175
|
-
each { |k, v| (hash[k] ||= []) << v }
|
176
|
-
hash
|
203
|
+
messages.dup
|
177
204
|
end
|
178
205
|
|
179
206
|
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
|
@@ -197,13 +224,6 @@ module ActiveModel
|
|
197
224
|
|
198
225
|
# Will add an error message to each of the attributes in +attributes+ that is empty.
|
199
226
|
def add_on_empty(attributes, options = {})
|
200
|
-
if options && !options.is_a?(Hash)
|
201
|
-
options = { :message => options }
|
202
|
-
ActiveSupport::Deprecation.warn \
|
203
|
-
"ActiveModel::Errors#add_on_empty(attributes, custom_message) has been deprecated.\n" +
|
204
|
-
"Instead of passing a custom_message pass an options Hash { :message => custom_message }."
|
205
|
-
end
|
206
|
-
|
207
227
|
[attributes].flatten.each do |attribute|
|
208
228
|
value = @base.send(:read_attribute_for_validation, attribute)
|
209
229
|
is_empty = value.respond_to?(:empty?) ? value.empty? : false
|
@@ -213,13 +233,6 @@ module ActiveModel
|
|
213
233
|
|
214
234
|
# Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
|
215
235
|
def add_on_blank(attributes, options = {})
|
216
|
-
if options && !options.is_a?(Hash)
|
217
|
-
options = { :message => options }
|
218
|
-
ActiveSupport::Deprecation.warn \
|
219
|
-
"ActiveModel::Errors#add_on_blank(attributes, custom_message) has been deprecated.\n" +
|
220
|
-
"Instead of passing a custom_message pass an options Hash { :message => custom_message }."
|
221
|
-
end
|
222
|
-
|
223
236
|
[attributes].flatten.each do |attribute|
|
224
237
|
value = @base.send(:read_attribute_for_validation, attribute)
|
225
238
|
add(attribute, :blank, options) if value.blank?
|
@@ -237,26 +250,20 @@ module ActiveModel
|
|
237
250
|
# company.errors.full_messages # =>
|
238
251
|
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
|
239
252
|
def full_messages
|
240
|
-
|
241
|
-
|
242
|
-
each do |attribute, messages|
|
243
|
-
messages = Array.wrap(messages)
|
244
|
-
next if messages.empty?
|
245
|
-
|
253
|
+
map { |attribute, message|
|
246
254
|
if attribute == :base
|
247
|
-
|
255
|
+
message
|
248
256
|
else
|
249
257
|
attr_name = attribute.to_s.gsub('.', '_').humanize
|
250
258
|
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
|
251
|
-
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
|
252
259
|
|
253
|
-
|
254
|
-
|
255
|
-
|
260
|
+
I18n.t(:"errors.format", {
|
261
|
+
:default => "%{attribute} %{message}",
|
262
|
+
:attribute => attr_name,
|
263
|
+
:message => message
|
264
|
+
})
|
256
265
|
end
|
257
|
-
|
258
|
-
|
259
|
-
full_messages
|
266
|
+
}
|
260
267
|
end
|
261
268
|
|
262
269
|
# Translates an error message in its default scope
|
@@ -271,29 +278,21 @@ module ActiveModel
|
|
271
278
|
# When using inheritance in your models, it will check all the inherited
|
272
279
|
# models too, but only if the model itself hasn't been found. Say you have
|
273
280
|
# <tt>class Admin < User; end</tt> and you wanted the translation for
|
274
|
-
# the <tt>:blank</tt> error
|
281
|
+
# the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
|
275
282
|
# it looks for these translations:
|
276
283
|
#
|
277
|
-
# <
|
278
|
-
# <
|
279
|
-
# <
|
280
|
-
# <
|
281
|
-
# <
|
282
|
-
# <
|
283
|
-
# <
|
284
|
-
# <
|
285
|
-
#
|
286
|
-
# </ol>
|
284
|
+
# * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
|
285
|
+
# * <tt>activemodel.errors.models.admin.blank</tt>
|
286
|
+
# * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
|
287
|
+
# * <tt>activemodel.errors.models.user.blank</tt>
|
288
|
+
# * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
|
289
|
+
# * <tt>activemodel.errors.messages.blank</tt>
|
290
|
+
# * <tt>errors.attributes.title.blank</tt>
|
291
|
+
# * <tt>errors.messages.blank</tt>
|
292
|
+
#
|
287
293
|
def generate_message(attribute, type = :invalid, options = {})
|
288
294
|
type = options.delete(:message) if options[:message].is_a?(Symbol)
|
289
295
|
|
290
|
-
if options[:default]
|
291
|
-
ActiveSupport::Deprecation.warn \
|
292
|
-
"Giving :default as validation option to errors.add has been deprecated.\n" +
|
293
|
-
"Please use :message instead."
|
294
|
-
options[:message] = options.delete(:default)
|
295
|
-
end
|
296
|
-
|
297
296
|
defaults = @base.class.lookup_ancestors.map do |klass|
|
298
297
|
[ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
|
299
298
|
:"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
|
data/lib/active_model/lint.rb
CHANGED
@@ -23,7 +23,7 @@ module ActiveModel
|
|
23
23
|
def test_to_key
|
24
24
|
assert model.respond_to?(:to_key), "The model should respond to to_key"
|
25
25
|
def model.persisted?() false end
|
26
|
-
assert model.to_key.nil
|
26
|
+
assert model.to_key.nil?, "to_key should return nil when `persisted?` returns false"
|
27
27
|
end
|
28
28
|
|
29
29
|
# == Responds to <tt>to_param</tt>
|
@@ -40,7 +40,7 @@ module ActiveModel
|
|
40
40
|
assert model.respond_to?(:to_param), "The model should respond to to_param"
|
41
41
|
def model.to_key() [1] end
|
42
42
|
def model.persisted?() false end
|
43
|
-
assert model.to_param.nil
|
43
|
+
assert model.to_param.nil?, "to_param should return nil when `persisted?` returns false"
|
44
44
|
end
|
45
45
|
|
46
46
|
# == Responds to <tt>valid?</tt>
|
@@ -20,78 +20,94 @@ module ActiveModel
|
|
20
20
|
# For example, a logged in user may need to assign additional attributes depending
|
21
21
|
# on their role:
|
22
22
|
#
|
23
|
-
#
|
24
|
-
#
|
23
|
+
# class AccountsController < ApplicationController
|
24
|
+
# include ActiveModel::MassAssignmentSecurity
|
25
25
|
#
|
26
|
-
#
|
26
|
+
# attr_accessible :first_name, :last_name
|
27
|
+
# attr_accessible :first_name, :last_name, :plan_id, :as => :admin
|
27
28
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# ...
|
34
|
-
# @account.update_attributes(account_params)
|
35
|
-
# ...
|
36
|
-
# end
|
29
|
+
# def update
|
30
|
+
# ...
|
31
|
+
# @account.update_attributes(account_params)
|
32
|
+
# ...
|
33
|
+
# end
|
37
34
|
#
|
38
|
-
#
|
35
|
+
# protected
|
39
36
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
37
|
+
# def account_params
|
38
|
+
# scope = admin ? :admin : :default
|
39
|
+
# sanitize_for_mass_assignment(params[:account], scope)
|
40
|
+
# end
|
43
41
|
#
|
44
|
-
# def mass_assignment_authorizer
|
45
|
-
# admin ? admin_accessible_attributes : super
|
46
42
|
# end
|
47
43
|
#
|
48
|
-
# end
|
49
|
-
#
|
50
44
|
module ClassMethods
|
51
45
|
# Attributes named in this macro are protected from mass-assignment
|
52
|
-
# whenever attributes are sanitized before assignment.
|
46
|
+
# whenever attributes are sanitized before assignment. A scope for the
|
47
|
+
# attributes is optional, if no scope is provided then :default is used.
|
48
|
+
# A scope can be defined by using the :as option.
|
53
49
|
#
|
54
50
|
# Mass-assignment to these attributes will simply be ignored, to assign
|
55
51
|
# to them you can use direct writer methods. This is meant to protect
|
56
52
|
# sensitive attributes from being overwritten by malicious users
|
57
|
-
# tampering with URLs or forms.
|
58
|
-
#
|
59
|
-
# == Example
|
53
|
+
# tampering with URLs or forms. Example:
|
60
54
|
#
|
61
55
|
# class Customer
|
62
56
|
# include ActiveModel::MassAssignmentSecurity
|
63
57
|
#
|
64
58
|
# attr_accessor :name, :credit_rating
|
65
|
-
# attr_protected :credit_rating
|
66
59
|
#
|
67
|
-
#
|
68
|
-
#
|
60
|
+
# attr_protected :credit_rating, :last_login
|
61
|
+
# attr_protected :last_login, :as => :admin
|
62
|
+
#
|
63
|
+
# def assign_attributes(values, options = {})
|
64
|
+
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
|
69
65
|
# send("#{k}=", v)
|
70
66
|
# end
|
71
67
|
# end
|
72
68
|
# end
|
73
69
|
#
|
70
|
+
# When using a :default scope :
|
71
|
+
#
|
74
72
|
# customer = Customer.new
|
75
|
-
# customer.
|
73
|
+
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default)
|
76
74
|
# customer.name # => "David"
|
77
75
|
# customer.credit_rating # => nil
|
76
|
+
# customer.last_login # => nil
|
78
77
|
#
|
79
78
|
# customer.credit_rating = "Average"
|
80
79
|
# customer.credit_rating # => "Average"
|
81
80
|
#
|
81
|
+
# And using the :admin scope :
|
82
|
+
#
|
83
|
+
# customer = Customer.new
|
84
|
+
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin)
|
85
|
+
# customer.name # => "David"
|
86
|
+
# customer.credit_rating # => "Excellent"
|
87
|
+
# customer.last_login # => nil
|
88
|
+
#
|
82
89
|
# To start from an all-closed default and enable attributes as needed,
|
83
90
|
# have a look at +attr_accessible+.
|
84
91
|
#
|
85
92
|
# Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of +attr_protected+
|
86
93
|
# to sanitize attributes won't provide sufficient protection.
|
87
|
-
def attr_protected(*
|
88
|
-
|
94
|
+
def attr_protected(*args)
|
95
|
+
options = args.extract_options!
|
96
|
+
scope = options[:as] || :default
|
97
|
+
|
98
|
+
self._protected_attributes = protected_attributes_configs.dup
|
99
|
+
self._protected_attributes[scope] = self.protected_attributes(scope) + args
|
100
|
+
|
89
101
|
self._active_authorizer = self._protected_attributes
|
90
102
|
end
|
91
103
|
|
92
104
|
# Specifies a white list of model attributes that can be set via
|
93
105
|
# mass-assignment.
|
94
106
|
#
|
107
|
+
# Like +attr_protected+, a scope for the attributes is optional,
|
108
|
+
# if no scope is provided then :default is used. A scope can be defined by
|
109
|
+
# using the :as option.
|
110
|
+
#
|
95
111
|
# This is the opposite of the +attr_protected+ macro: Mass-assignment
|
96
112
|
# will only set attributes in this list, to assign to the rest of
|
97
113
|
# attributes you can use direct writer methods. This is meant to protect
|
@@ -104,57 +120,90 @@ module ActiveModel
|
|
104
120
|
# include ActiveModel::MassAssignmentSecurity
|
105
121
|
#
|
106
122
|
# attr_accessor :name, :credit_rating
|
123
|
+
#
|
107
124
|
# attr_accessible :name
|
125
|
+
# attr_accessible :name, :credit_rating, :as => :admin
|
108
126
|
#
|
109
|
-
# def
|
110
|
-
# sanitize_for_mass_assignment(values).each do |k, v|
|
127
|
+
# def assign_attributes(values, options = {})
|
128
|
+
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
|
111
129
|
# send("#{k}=", v)
|
112
130
|
# end
|
113
131
|
# end
|
114
132
|
# end
|
115
133
|
#
|
134
|
+
# When using a :default scope :
|
135
|
+
#
|
116
136
|
# customer = Customer.new
|
117
|
-
# customer.
|
137
|
+
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default)
|
118
138
|
# customer.name # => "David"
|
119
139
|
# customer.credit_rating # => nil
|
120
140
|
#
|
121
141
|
# customer.credit_rating = "Average"
|
122
142
|
# customer.credit_rating # => "Average"
|
123
143
|
#
|
144
|
+
# And using the :admin scope :
|
145
|
+
#
|
146
|
+
# customer = Customer.new
|
147
|
+
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin)
|
148
|
+
# customer.name # => "David"
|
149
|
+
# customer.credit_rating # => "Excellent"
|
150
|
+
#
|
124
151
|
# Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of +attr_accessible+
|
125
152
|
# to sanitize attributes won't provide sufficient protection.
|
126
|
-
def attr_accessible(*
|
127
|
-
|
153
|
+
def attr_accessible(*args)
|
154
|
+
options = args.extract_options!
|
155
|
+
scope = options[:as] || :default
|
156
|
+
|
157
|
+
self._accessible_attributes = accessible_attributes_configs.dup
|
158
|
+
self._accessible_attributes[scope] = self.accessible_attributes(scope) + args
|
159
|
+
|
128
160
|
self._active_authorizer = self._accessible_attributes
|
129
161
|
end
|
130
162
|
|
131
|
-
def protected_attributes
|
132
|
-
|
133
|
-
w.logger = self.logger if self.respond_to?(:logger)
|
134
|
-
end
|
163
|
+
def protected_attributes(scope = :default)
|
164
|
+
protected_attributes_configs[scope]
|
135
165
|
end
|
136
166
|
|
137
|
-
def accessible_attributes
|
138
|
-
|
167
|
+
def accessible_attributes(scope = :default)
|
168
|
+
accessible_attributes_configs[scope]
|
139
169
|
end
|
140
170
|
|
141
|
-
def
|
142
|
-
self._active_authorizer ||=
|
171
|
+
def active_authorizers
|
172
|
+
self._active_authorizer ||= protected_attributes_configs
|
143
173
|
end
|
174
|
+
alias active_authorizer active_authorizers
|
144
175
|
|
145
176
|
def attributes_protected_by_default
|
146
177
|
[]
|
147
178
|
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def protected_attributes_configs
|
183
|
+
self._protected_attributes ||= begin
|
184
|
+
default_black_list = BlackList.new(attributes_protected_by_default).tap do |w|
|
185
|
+
w.logger = self.logger if self.respond_to?(:logger)
|
186
|
+
end
|
187
|
+
Hash.new(default_black_list)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def accessible_attributes_configs
|
192
|
+
self._accessible_attributes ||= begin
|
193
|
+
default_white_list = WhiteList.new.tap { |w| w.logger = self.logger if self.respond_to?(:logger) }
|
194
|
+
Hash.new(default_white_list)
|
195
|
+
end
|
196
|
+
end
|
148
197
|
end
|
149
198
|
|
150
199
|
protected
|
151
200
|
|
152
|
-
def sanitize_for_mass_assignment(attributes)
|
153
|
-
mass_assignment_authorizer.sanitize(attributes)
|
201
|
+
def sanitize_for_mass_assignment(attributes, scope = :default)
|
202
|
+
mass_assignment_authorizer(scope).sanitize(attributes)
|
154
203
|
end
|
155
204
|
|
156
|
-
def mass_assignment_authorizer
|
157
|
-
self.class.active_authorizer
|
205
|
+
def mass_assignment_authorizer(scope = :default)
|
206
|
+
self.class.active_authorizer[scope]
|
158
207
|
end
|
159
208
|
end
|
160
209
|
end
|
data/lib/active_model/naming.rb
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
|
+
require 'active_support/core_ext/hash/except'
|
3
|
+
require 'active_support/core_ext/module/introspection'
|
2
4
|
|
3
5
|
module ActiveModel
|
4
6
|
class Name < String
|
5
|
-
attr_reader :singular, :plural, :element, :collection, :partial_path, :i18n_key
|
7
|
+
attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key, :i18n_key
|
6
8
|
alias_method :cache_key, :collection
|
7
9
|
|
8
|
-
def initialize(klass)
|
10
|
+
def initialize(klass, namespace = nil)
|
9
11
|
super(klass.name)
|
12
|
+
@unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace
|
13
|
+
|
10
14
|
@klass = klass
|
11
|
-
@singular =
|
15
|
+
@singular = _singularize(self).freeze
|
12
16
|
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
|
13
17
|
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
|
14
18
|
@human = ActiveSupport::Inflector.humanize(@element).freeze
|
15
19
|
@collection = ActiveSupport::Inflector.tableize(self).freeze
|
16
20
|
@partial_path = "#{@collection}/#{@element}".freeze
|
17
|
-
@
|
21
|
+
@param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
|
22
|
+
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze
|
23
|
+
@i18n_key = self.underscore.to_sym
|
18
24
|
end
|
19
25
|
|
20
26
|
# Transform the model name into a more humane format, using I18n. By default,
|
@@ -28,16 +34,21 @@ module ActiveModel
|
|
28
34
|
@klass.respond_to?(:i18n_scope)
|
29
35
|
|
30
36
|
defaults = @klass.lookup_ancestors.map do |klass|
|
31
|
-
|
32
|
-
|
33
|
-
end.flatten
|
37
|
+
klass.model_name.i18n_key
|
38
|
+
end
|
34
39
|
|
35
|
-
defaults << options
|
40
|
+
defaults << options[:default] if options[:default]
|
36
41
|
defaults << @human
|
37
42
|
|
38
|
-
options
|
43
|
+
options = {:scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults}.merge(options.except(:default))
|
39
44
|
I18n.translate(defaults.shift, options)
|
40
45
|
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def _singularize(string, replacement='_')
|
50
|
+
ActiveSupport::Inflector.underscore(string).tr('/', replacement)
|
51
|
+
end
|
41
52
|
end
|
42
53
|
|
43
54
|
# == Active Model Naming
|
@@ -57,13 +68,16 @@ module ActiveModel
|
|
57
68
|
# BookModule::BookCover.model_name.i18n_key # => "book_module.book_cover"
|
58
69
|
#
|
59
70
|
# Providing the functionality that ActiveModel::Naming provides in your object
|
60
|
-
# is required to pass the Active Model Lint test.
|
61
|
-
# method below, or rolling your own is required
|
71
|
+
# is required to pass the Active Model Lint test. So either extending the provided
|
72
|
+
# method below, or rolling your own is required.
|
62
73
|
module Naming
|
63
74
|
# Returns an ActiveModel::Name object for module. It can be
|
64
75
|
# used to retrieve all kinds of naming-related information.
|
65
76
|
def model_name
|
66
|
-
@_model_name ||=
|
77
|
+
@_model_name ||= begin
|
78
|
+
namespace = self.parents.detect { |n| n.respond_to?(:_railtie) }
|
79
|
+
ActiveModel::Name.new(self, namespace)
|
80
|
+
end
|
67
81
|
end
|
68
82
|
|
69
83
|
# Returns the plural class name of a record or class. Examples:
|
@@ -90,9 +104,37 @@ module ActiveModel
|
|
90
104
|
plural(record_or_class) == singular(record_or_class)
|
91
105
|
end
|
92
106
|
|
107
|
+
# Returns string to use while generating route names. It differs for
|
108
|
+
# namespaced models regarding whether it's inside isolated engine.
|
109
|
+
#
|
110
|
+
# For isolated engine:
|
111
|
+
# ActiveModel::Naming.route_key(Blog::Post) #=> posts
|
112
|
+
#
|
113
|
+
# For shared engine:
|
114
|
+
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
|
115
|
+
def self.route_key(record_or_class)
|
116
|
+
model_name_from_record_or_class(record_or_class).route_key
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns string to use for params names. It differs for
|
120
|
+
# namespaced models regarding whether it's inside isolated engine.
|
121
|
+
#
|
122
|
+
# For isolated engine:
|
123
|
+
# ActiveModel::Naming.param_key(Blog::Post) #=> post
|
124
|
+
#
|
125
|
+
# For shared engine:
|
126
|
+
# ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
|
127
|
+
def self.param_key(record_or_class)
|
128
|
+
model_name_from_record_or_class(record_or_class).param_key
|
129
|
+
end
|
130
|
+
|
93
131
|
private
|
94
132
|
def self.model_name_from_record_or_class(record_or_class)
|
95
|
-
(record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
|
133
|
+
(record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.convert_to_model(object)
|
137
|
+
object.respond_to?(:to_model) ? object.to_model : object
|
96
138
|
end
|
97
139
|
end
|
98
140
|
|