hashie 2.0.5 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +36 -0
- data/.travis.yml +13 -6
- data/CHANGELOG.md +40 -21
- data/CONTRIBUTING.md +110 -19
- data/Gemfile +9 -0
- data/LICENSE +1 -1
- data/README.md +347 -0
- data/Rakefile +4 -2
- data/hashie.gemspec +4 -7
- data/lib/hashie.rb +3 -0
- data/lib/hashie/clash.rb +19 -19
- data/lib/hashie/dash.rb +47 -39
- data/lib/hashie/extensions/coercion.rb +10 -6
- data/lib/hashie/extensions/deep_fetch.rb +29 -0
- data/lib/hashie/extensions/deep_merge.rb +15 -6
- data/lib/hashie/extensions/ignore_undeclared.rb +41 -0
- data/lib/hashie/extensions/indifferent_access.rb +37 -10
- data/lib/hashie/extensions/key_conversion.rb +3 -3
- data/lib/hashie/extensions/method_access.rb +9 -9
- data/lib/hashie/hash.rb +7 -7
- data/lib/hashie/hash_extensions.rb +5 -7
- data/lib/hashie/mash.rb +38 -31
- data/lib/hashie/rash.rb +119 -0
- data/lib/hashie/trash.rb +31 -22
- data/lib/hashie/version.rb +1 -1
- data/spec/hashie/clash_spec.rb +43 -45
- data/spec/hashie/dash_spec.rb +115 -53
- data/spec/hashie/extensions/coercion_spec.rb +42 -37
- data/spec/hashie/extensions/deep_fetch_spec.rb +70 -0
- data/spec/hashie/extensions/deep_merge_spec.rb +11 -9
- data/spec/hashie/extensions/ignore_undeclared_spec.rb +23 -0
- data/spec/hashie/extensions/indifferent_access_spec.rb +117 -64
- data/spec/hashie/extensions/key_conversion_spec.rb +28 -27
- data/spec/hashie/extensions/merge_initializer_spec.rb +13 -10
- data/spec/hashie/extensions/method_access_spec.rb +49 -40
- data/spec/hashie/hash_spec.rb +25 -13
- data/spec/hashie/mash_spec.rb +243 -187
- data/spec/hashie/rash_spec.rb +44 -0
- data/spec/hashie/trash_spec.rb +81 -43
- data/spec/hashie/version_spec.rb +7 -0
- data/spec/spec_helper.rb +0 -4
- metadata +27 -78
- data/.document +0 -5
- data/README.markdown +0 -236
- data/lib/hashie/extensions/structure.rb +0 -47
@@ -10,7 +10,7 @@ module Hashie
|
|
10
10
|
def stringify_keys!
|
11
11
|
keys.each do |k|
|
12
12
|
stringify_keys_recursively!(self[k])
|
13
|
-
self[k.to_s] =
|
13
|
+
self[k.to_s] = delete(k)
|
14
14
|
end
|
15
15
|
self
|
16
16
|
end
|
@@ -51,7 +51,7 @@ module Hashie
|
|
51
51
|
def symbolize_keys!
|
52
52
|
keys.each do |k|
|
53
53
|
symbolize_keys_recursively!(self[k])
|
54
|
-
self[k.to_sym] =
|
54
|
+
self[k.to_sym] = delete(k)
|
55
55
|
end
|
56
56
|
self
|
57
57
|
end
|
@@ -81,7 +81,7 @@ module Hashie
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
module KeyConversion
|
86
86
|
def self.included(base)
|
87
87
|
base.send :include, SymbolizeKeys
|
@@ -5,8 +5,8 @@ module Hashie
|
|
5
5
|
# to access your hash's keys. It will recognize keys
|
6
6
|
# either as strings or symbols.
|
7
7
|
#
|
8
|
-
# Note that while nil keys will be returned as nil,
|
9
|
-
# undefined keys will raise NoMethodErrors. Also note that
|
8
|
+
# Note that while nil keys will be returned as nil,
|
9
|
+
# undefined keys will raise NoMethodErrors. Also note that
|
10
10
|
# #respond_to? has been patched to appropriately recognize
|
11
11
|
# key methods.
|
12
12
|
#
|
@@ -18,9 +18,9 @@ module Hashie
|
|
18
18
|
# user = User.new
|
19
19
|
# user['first_name'] = 'Michael'
|
20
20
|
# user.first_name # => 'Michael'
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# user[:last_name] = 'Bleigh'
|
23
|
-
# user.last_name # => 'Bleigh'
|
23
|
+
# user.last_name # => 'Bleigh'
|
24
24
|
#
|
25
25
|
# user[:birthday] = nil
|
26
26
|
# user.birthday # => nil
|
@@ -31,7 +31,7 @@ module Hashie
|
|
31
31
|
return true if key?(name.to_s) || key?(name.to_sym)
|
32
32
|
super
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def method_missing(name, *args)
|
36
36
|
return self[name.to_s] if key?(name.to_s)
|
37
37
|
return self[name.to_sym] if key?(name.to_sym)
|
@@ -64,7 +64,7 @@ module Hashie
|
|
64
64
|
|
65
65
|
def method_missing(name, *args)
|
66
66
|
if args.size == 1 && name.to_s =~ /(.*)=$/
|
67
|
-
return self[convert_key(
|
67
|
+
return self[convert_key(Regexp.last_match[1])] = args.first
|
68
68
|
end
|
69
69
|
|
70
70
|
super
|
@@ -97,13 +97,13 @@ module Hashie
|
|
97
97
|
# h.hji? # => NoMethodError
|
98
98
|
module MethodQuery
|
99
99
|
def respond_to?(name, include_private = false)
|
100
|
-
return true if name.to_s =~ /(.*)\?$/ && (key?(
|
100
|
+
return true if name.to_s =~ /(.*)\?$/ && (key?(Regexp.last_match[1]) || key?(Regexp.last_match[1].to_sym))
|
101
101
|
super
|
102
102
|
end
|
103
103
|
|
104
104
|
def method_missing(name, *args)
|
105
|
-
if args.empty? && name.to_s =~ /(.*)\?$/ && (key?(
|
106
|
-
return self[
|
105
|
+
if args.empty? && name.to_s =~ /(.*)\?$/ && (key?(Regexp.last_match[1]) || key?(Regexp.last_match[1].to_sym))
|
106
|
+
return self[Regexp.last_match[1]] || self[Regexp.last_match[1].to_sym]
|
107
107
|
end
|
108
108
|
|
109
109
|
super
|
data/lib/hashie/hash.rb
CHANGED
@@ -7,19 +7,19 @@ module Hashie
|
|
7
7
|
class Hash < ::Hash
|
8
8
|
include HashExtensions
|
9
9
|
|
10
|
-
# Converts a mash back to a hash (with stringified keys)
|
11
|
-
def to_hash(options={})
|
10
|
+
# Converts a mash back to a hash (with stringified or symbolized keys)
|
11
|
+
def to_hash(options = {})
|
12
12
|
out = {}
|
13
13
|
keys.each do |k|
|
14
|
+
assignment_key = k.to_s
|
15
|
+
assignment_key = assignment_key.to_sym if options[:symbolize_keys]
|
14
16
|
if self[k].is_a?(Array)
|
15
|
-
|
16
|
-
out[k] ||= []
|
17
|
+
out[assignment_key] ||= []
|
17
18
|
self[k].each do |array_object|
|
18
|
-
out[
|
19
|
+
out[assignment_key] << (Hash === array_object ? array_object.to_hash(options) : array_object)
|
19
20
|
end
|
20
21
|
else
|
21
|
-
|
22
|
-
out[k] = Hash === self[k] ? self[k].to_hash : self[k]
|
22
|
+
out[assignment_key] = Hash === self[k] ? self[k].to_hash(options) : self[k]
|
23
23
|
end
|
24
24
|
end
|
25
25
|
out
|
@@ -11,10 +11,8 @@ module Hashie
|
|
11
11
|
# Destructively convert all of the keys of a Hash
|
12
12
|
# to their string representations.
|
13
13
|
def hashie_stringify_keys!
|
14
|
-
|
15
|
-
unless String === k
|
16
|
-
self[k.to_s] = self.delete(k)
|
17
|
-
end
|
14
|
+
keys.each do |k|
|
15
|
+
self[k.to_s] = delete(k) unless String === k
|
18
16
|
end
|
19
17
|
self
|
20
18
|
end
|
@@ -22,7 +20,7 @@ module Hashie
|
|
22
20
|
# Convert all of the keys of a Hash
|
23
21
|
# to their string representations.
|
24
22
|
def hashie_stringify_keys
|
25
|
-
|
23
|
+
dup.stringify_keys!
|
26
24
|
end
|
27
25
|
|
28
26
|
# Convert this hash into a Mash
|
@@ -38,11 +36,11 @@ module Hashie
|
|
38
36
|
end
|
39
37
|
|
40
38
|
def hashie_inspect
|
41
|
-
ret = "#<#{self.class
|
39
|
+
ret = "#<#{self.class}"
|
42
40
|
stringify_keys.keys.sort.each do |key|
|
43
41
|
ret << " #{key}=#{self[key].inspect}"
|
44
42
|
end
|
45
|
-
ret <<
|
43
|
+
ret << '>'
|
46
44
|
ret
|
47
45
|
end
|
48
46
|
end
|
data/lib/hashie/mash.rb
CHANGED
@@ -55,6 +55,7 @@ module Hashie
|
|
55
55
|
# mash.author # => <Mash>
|
56
56
|
#
|
57
57
|
class Mash < Hash
|
58
|
+
ALLOWED_SUFFIXES = %w(? ! = _)
|
58
59
|
include Hashie::PrettyInspect
|
59
60
|
alias_method :to_s, :inspect
|
60
61
|
|
@@ -67,14 +68,14 @@ module Hashie
|
|
67
68
|
default ? super(default) : super(&blk)
|
68
69
|
end
|
69
70
|
|
70
|
-
class << self;
|
71
|
+
class << self; alias_method :[], :new; end
|
71
72
|
|
72
73
|
def id #:nodoc:
|
73
|
-
self[
|
74
|
+
self['id']
|
74
75
|
end
|
75
76
|
|
76
77
|
def type #:nodoc:
|
77
|
-
self[
|
78
|
+
self['type']
|
78
79
|
end
|
79
80
|
|
80
81
|
alias_method :regular_reader, :[]
|
@@ -91,8 +92,8 @@ module Hashie
|
|
91
92
|
# Sets an attribute in the Mash. Key will be converted to
|
92
93
|
# a string before it is set, and Hashes will be converted
|
93
94
|
# into Mashes for nesting purposes.
|
94
|
-
def custom_writer(key,value) #:nodoc:
|
95
|
-
regular_writer(convert_key(key), convert_value(value))
|
95
|
+
def custom_writer(key, value, convert = true) #:nodoc:
|
96
|
+
regular_writer(convert_key(key), convert ? convert_value(value) : value)
|
96
97
|
end
|
97
98
|
|
98
99
|
alias_method :[], :custom_reader
|
@@ -128,7 +129,7 @@ module Hashie
|
|
128
129
|
alias_method :regular_dup, :dup
|
129
130
|
# Duplicates the current mash as a new mash.
|
130
131
|
def dup
|
131
|
-
self.class.new(self,
|
132
|
+
self.class.new(self, default)
|
132
133
|
end
|
133
134
|
|
134
135
|
def key?(key)
|
@@ -148,14 +149,14 @@ module Hashie
|
|
148
149
|
# Recursively merges this mash with the passed
|
149
150
|
# in hash, merging each hash in the hierarchy.
|
150
151
|
def deep_update(other_hash, &blk)
|
151
|
-
other_hash.each_pair do |k,v|
|
152
|
+
other_hash.each_pair do |k, v|
|
152
153
|
key = convert_key(k)
|
153
|
-
if regular_reader(key).is_a?(Mash)
|
154
|
+
if regular_reader(key).is_a?(Mash) && v.is_a?(::Hash)
|
154
155
|
custom_reader(key).deep_update(v, &blk)
|
155
156
|
else
|
156
157
|
value = convert_value(v, true)
|
157
|
-
value = blk.call(key, self[k], value) if blk
|
158
|
-
custom_writer(key, value)
|
158
|
+
value = convert_value(blk.call(key, self[k], value), true) if blk
|
159
|
+
custom_writer(key, value, false)
|
159
160
|
end
|
160
161
|
end
|
161
162
|
self
|
@@ -172,7 +173,7 @@ module Hashie
|
|
172
173
|
# Merges (non-recursively) the hash from the argument,
|
173
174
|
# changing the receiving hash
|
174
175
|
def shallow_update(other_hash)
|
175
|
-
other_hash.each_pair do |k,v|
|
176
|
+
other_hash.each_pair do |k, v|
|
176
177
|
regular_writer(convert_key(k), convert_value(v, true))
|
177
178
|
end
|
178
179
|
self
|
@@ -186,25 +187,31 @@ module Hashie
|
|
186
187
|
|
187
188
|
# Will return true if the Mash has had a key
|
188
189
|
# set in addition to normal respond_to? functionality.
|
189
|
-
def respond_to?(method_name, include_private=false)
|
190
|
-
return true if key?(method_name) || method_name
|
190
|
+
def respond_to?(method_name, include_private = false)
|
191
|
+
return true if key?(method_name) || prefix_method?(method_name)
|
191
192
|
super
|
192
193
|
end
|
193
194
|
|
195
|
+
def prefix_method?(method_name)
|
196
|
+
method_name = method_name.to_s
|
197
|
+
method_name.end_with?(*ALLOWED_SUFFIXES) && key?(method_name.chop)
|
198
|
+
end
|
199
|
+
|
194
200
|
def method_missing(method_name, *args, &blk)
|
195
201
|
return self.[](method_name, &blk) if key?(method_name)
|
196
|
-
|
202
|
+
suffixes_regex = ALLOWED_SUFFIXES.join
|
203
|
+
match = method_name.to_s.match(/(.*?)([#{suffixes_regex}]?)$/)
|
197
204
|
case match[2]
|
198
|
-
when
|
205
|
+
when '='
|
199
206
|
self[match[1]] = args.first
|
200
|
-
when
|
207
|
+
when '?'
|
201
208
|
!!self[match[1]]
|
202
|
-
when
|
209
|
+
when '!'
|
203
210
|
initializing_reader(match[1])
|
204
|
-
when
|
211
|
+
when '_'
|
205
212
|
underbang_reader(match[1])
|
206
213
|
else
|
207
|
-
default(method_name
|
214
|
+
default(method_name)
|
208
215
|
end
|
209
216
|
end
|
210
217
|
|
@@ -214,19 +221,19 @@ module Hashie
|
|
214
221
|
key.to_s
|
215
222
|
end
|
216
223
|
|
217
|
-
def convert_value(val, duping=false) #:nodoc:
|
224
|
+
def convert_value(val, duping = false) #:nodoc:
|
218
225
|
case val
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
226
|
+
when self.class
|
227
|
+
val.dup
|
228
|
+
when Hash
|
229
|
+
duping ? val.dup : val
|
230
|
+
when ::Hash
|
231
|
+
val = val.dup if duping
|
232
|
+
self.class.new(val)
|
233
|
+
when Array
|
234
|
+
val.map { |e| convert_value(e) }
|
235
|
+
else
|
236
|
+
val
|
230
237
|
end
|
231
238
|
end
|
232
239
|
end
|
data/lib/hashie/rash.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
module Hashie
|
2
|
+
#
|
3
|
+
# Rash is a Hash whose keys can be Regexps, or Ranges, which will
|
4
|
+
# match many input keys.
|
5
|
+
#
|
6
|
+
# A good use case for this class is routing URLs in a web framework.
|
7
|
+
# The Rash's keys match URL patterns, and the values specify actions
|
8
|
+
# which can handle the URL. When the Rash's value is proc, the proc
|
9
|
+
# will be automatically called with the regexp's matched groups as
|
10
|
+
# block arguments.
|
11
|
+
#
|
12
|
+
# Usage example:
|
13
|
+
#
|
14
|
+
# greeting = Hashie::Rash.new( /^Mr./ => "Hello sir!", /^Mrs./ => "Evening, madame." )
|
15
|
+
# greeting["Mr. Steve Austin"] #=> "Hello sir!"
|
16
|
+
# greeting["Mrs. Steve Austin"] #=> "Evening, madame."
|
17
|
+
#
|
18
|
+
# Note: The Rash is automatically optimized every 500 accesses
|
19
|
+
# (Regexps get sorted by how often they get matched).
|
20
|
+
# If this is too low or too high, you can tune it by
|
21
|
+
# setting: `rash.optimize_every = n`
|
22
|
+
#
|
23
|
+
class Rash
|
24
|
+
attr_accessor :optimize_every
|
25
|
+
|
26
|
+
def initialize(initial = {})
|
27
|
+
@hash = {}
|
28
|
+
@regexes = []
|
29
|
+
@ranges = []
|
30
|
+
@regex_counts = Hash.new(0)
|
31
|
+
@optimize_every = 500
|
32
|
+
@lookups = 0
|
33
|
+
|
34
|
+
update(initial)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update(other)
|
38
|
+
other.each do |key, value|
|
39
|
+
self[key] = value
|
40
|
+
end
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def []=(key, value)
|
46
|
+
case key
|
47
|
+
when Regexp
|
48
|
+
# key = normalize_regex(key) # this used to just do: /#{regexp}/
|
49
|
+
@regexes << key
|
50
|
+
when Range
|
51
|
+
@ranges << key
|
52
|
+
end
|
53
|
+
@hash[key] = value
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Return the first thing that matches the key.
|
58
|
+
#
|
59
|
+
def [](key)
|
60
|
+
all(key).first
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Return everything that matches the query.
|
65
|
+
#
|
66
|
+
def all(query)
|
67
|
+
return to_enum(:all, query) unless block_given?
|
68
|
+
|
69
|
+
if @hash.include? query
|
70
|
+
yield @hash[query]
|
71
|
+
return
|
72
|
+
end
|
73
|
+
|
74
|
+
case query
|
75
|
+
when String
|
76
|
+
optimize_if_necessary!
|
77
|
+
|
78
|
+
# see if any of the regexps match the string
|
79
|
+
@regexes.each do |regex|
|
80
|
+
match = regex.match(query)
|
81
|
+
if match
|
82
|
+
@regex_counts[regex] += 1
|
83
|
+
value = @hash[regex]
|
84
|
+
if value.respond_to? :call
|
85
|
+
yield value.call(match)
|
86
|
+
else
|
87
|
+
yield value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
when Integer
|
93
|
+
# see if any of the ranges match the integer
|
94
|
+
@ranges.each do |range|
|
95
|
+
yield @hash[range] if range.include? query
|
96
|
+
end
|
97
|
+
|
98
|
+
when Regexp
|
99
|
+
# Reverse operation: `rash[/regexp/]` returns all the hash's string keys which match the regexp
|
100
|
+
@hash.each do |key, val|
|
101
|
+
yield val if key.is_a?(String) && query =~ key
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def method_missing(*args, &block)
|
107
|
+
@hash.send(*args, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def optimize_if_necessary!
|
113
|
+
if (@lookups += 1) >= @optimize_every
|
114
|
+
@regexes = @regex_counts.sort_by { |regex, count| -count }.map { |regex, count| regex }
|
115
|
+
@lookups = 0
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/hashie/trash.rb
CHANGED
@@ -8,7 +8,6 @@ module Hashie
|
|
8
8
|
# such as a Java api, where the keys are named differently from how we would
|
9
9
|
# in Ruby.
|
10
10
|
class Trash < Dash
|
11
|
-
|
12
11
|
# Defines a property on the Trash. Options are as follows:
|
13
12
|
#
|
14
13
|
# * <tt>:default</tt> - Specify a default value for this property, to be
|
@@ -20,33 +19,31 @@ module Hashie
|
|
20
19
|
def self.property(property_name, options = {})
|
21
20
|
super
|
22
21
|
|
22
|
+
options[:from] = options[:from].to_sym if options[:from]
|
23
|
+
property_name = property_name.to_sym
|
24
|
+
|
23
25
|
if options[:from]
|
24
|
-
if property_name
|
25
|
-
|
26
|
+
if property_name == options[:from]
|
27
|
+
fail ArgumentError, "Property name (#{property_name}) and :from option must not be the same"
|
28
|
+
end
|
29
|
+
|
30
|
+
translations[options[:from].to_sym] = property_name.to_sym
|
31
|
+
|
32
|
+
define_method "#{options[:from]}=" do |val|
|
33
|
+
with = options[:with] || options[:transform_with]
|
34
|
+
self[property_name.to_sym] = with.respond_to?(:call) ? with.call(val) : val
|
26
35
|
end
|
27
|
-
|
28
|
-
if options[:
|
29
|
-
|
30
|
-
define_method "#{options[:from]}=" do |val|
|
31
|
-
self[property_name.to_sym] = options[:with].call(val)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
else
|
35
|
-
class_eval <<-RUBY
|
36
|
-
def #{options[:from]}=(val)
|
37
|
-
self[:#{property_name}] = val
|
38
|
-
end
|
39
|
-
RUBY
|
36
|
+
else
|
37
|
+
if options[:transform_with].respond_to? :call
|
38
|
+
transforms[property_name.to_sym] = options[:transform_with]
|
40
39
|
end
|
41
|
-
elsif options[:transform_with].respond_to? :call
|
42
|
-
transforms[property_name.to_sym] = options[:transform_with]
|
43
40
|
end
|
44
41
|
end
|
45
42
|
|
46
43
|
# Set a value on the Dash in a Hash-like way. Only works
|
47
44
|
# on pre-existing properties.
|
48
45
|
def []=(property, value)
|
49
|
-
if self.class.translations.
|
46
|
+
if self.class.translations.key? property.to_sym
|
50
47
|
send("#{property}=", value)
|
51
48
|
elsif self.class.transforms.key? property.to_sym
|
52
49
|
super property, self.class.transforms[property.to_sym].call(value)
|
@@ -55,10 +52,22 @@ module Hashie
|
|
55
52
|
end
|
56
53
|
end
|
57
54
|
|
55
|
+
def self.permitted_input_keys
|
56
|
+
@permitted_input_keys ||= properties.map { |property| inverse_translations.fetch property, property }
|
57
|
+
end
|
58
|
+
|
58
59
|
private
|
59
60
|
|
61
|
+
def self.properties
|
62
|
+
@properties ||= []
|
63
|
+
end
|
64
|
+
|
60
65
|
def self.translations
|
61
|
-
@translations ||=
|
66
|
+
@translations ||= {}
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.inverse_translations
|
70
|
+
@inverse_translations ||= Hash[translations.map(&:reverse)]
|
62
71
|
end
|
63
72
|
|
64
73
|
def self.transforms
|
@@ -69,7 +78,7 @@ module Hashie
|
|
69
78
|
#
|
70
79
|
def property_exists?(property)
|
71
80
|
unless self.class.property?(property.to_sym)
|
72
|
-
|
81
|
+
fail NoMethodError, "The property '#{property}' is not defined for this Trash."
|
73
82
|
end
|
74
83
|
true
|
75
84
|
end
|
@@ -79,7 +88,7 @@ module Hashie
|
|
79
88
|
# Deletes any keys that have a translation
|
80
89
|
def initialize_attributes(attributes)
|
81
90
|
return unless attributes
|
82
|
-
attributes_copy = attributes.dup.delete_if do |k,v|
|
91
|
+
attributes_copy = attributes.dup.delete_if do |k, v|
|
83
92
|
if self.class.translations.include?(k.to_sym)
|
84
93
|
self[k] = v
|
85
94
|
true
|