symbolize 4.5.0 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +3 -1
- data/lib/symbolize.rb +0 -1
- data/lib/symbolize/active_record.rb +178 -170
- data/lib/symbolize/mongoid.rb +80 -68
- data/lib/symbolize/version.rb +1 -1
- data/spec/db/001_create_testing_structure.rb +3 -3
- data/spec/spec_helper.rb +11 -14
- data/spec/symbolize/active_record_spec.rb +120 -121
- data/spec/symbolize/mongoid_spec.rb +125 -129
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8c8248c8a57a1c3e3248a339ec6214fd6fe93ef
|
4
|
+
data.tar.gz: 5a2a2faba8acc56b88545a583507a714ea03f495
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6cbd772314ae69fc054ea373487d515f3f3fb6a7eea1144ceda2382c463c0797114a5df969ee7e7e79c48f3f050003b651c5f5a453c7aa096b0dddbb12508c7
|
7
|
+
data.tar.gz: 7dea8dbec2121d3357c269bcfad8926db2b3309a5d40a670911a9bdb972fd86ad9e9bad1bc862dede94acffc156c4a62a04fc263eb285e60d87c5d12847c6e3e
|
data/README.md
CHANGED
data/Rakefile
CHANGED
data/lib/symbolize.rb
CHANGED
@@ -1,211 +1,219 @@
|
|
1
1
|
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
2
3
|
|
3
4
|
module Symbolize
|
4
|
-
|
5
|
-
|
6
|
-
module Symbolize::ActiveRecord
|
7
|
-
extend ActiveSupport::Concern
|
5
|
+
module ActiveRecord
|
6
|
+
extend ActiveSupport::Concern
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
included do
|
9
|
+
# Returns an array of all the attributes that have been specified for symbolization
|
10
|
+
class_attribute :symbolized_attributes, :instance_reader => false
|
11
|
+
self.symbolized_attributes = []
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
unless enum.nil?
|
14
|
+
# Symbolize ActiveRecord attributes. Add
|
15
|
+
# symbolize :attr_name
|
16
|
+
# to your model class, to make an attribute return symbols instead of
|
17
|
+
# string values. Setting such an attribute will accept symbols as well
|
18
|
+
# as strings. In the database, the symbolized attribute should have
|
19
|
+
# the column-type :string.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
# class User < ActiveRecord::Base
|
23
|
+
# symbolize :gender, :in => [:female, :male]
|
24
|
+
# symbolize :so, :in => {
|
25
|
+
# :linux => "Linux",
|
26
|
+
# :mac => "Mac OS X"
|
27
|
+
# }
|
28
|
+
# symbolize :gui, , :in => [:gnome, :kde, :xfce], :allow_blank => true
|
29
|
+
# symbolize :browser, :in => [:firefox, :opera], :i18n => false
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# It will automattically lookup for i18n:
|
33
|
+
#
|
34
|
+
# activerecord:
|
35
|
+
# symbolizes:
|
36
|
+
# user:
|
37
|
+
# gender:
|
38
|
+
# female: Girl
|
39
|
+
# male: Boy
|
40
|
+
#
|
41
|
+
# You can skip i18n lookup with :i18n => false
|
42
|
+
# symbolize :gender, :in => [:female, :male], :i18n => false
|
43
|
+
#
|
44
|
+
# Its possible to use boolean fields also.
|
45
|
+
# symbolize :switch, :in => [true, false]
|
46
|
+
#
|
47
|
+
# ...
|
48
|
+
# switch:
|
49
|
+
# "true": On
|
50
|
+
# "false": Off
|
51
|
+
# "nil": Unknown
|
52
|
+
#
|
53
|
+
module ClassMethods
|
54
|
+
# Specifies that values of the given attributes should be returned
|
55
|
+
# as symbols. The table column should be created of type string.
|
56
|
+
|
57
|
+
def symbolize(*attr_names)
|
58
|
+
configuration = attr_names.extract_options!
|
59
|
+
configuration.assert_valid_keys(:in, :within, :i18n, :scopes, :methods, :capitalize, :validate, :default, :allow_blank, :allow_nil)
|
60
|
+
|
61
|
+
enum = configuration[:in] || configuration[:within]
|
62
|
+
i18n = configuration[:i18n]
|
63
|
+
i18n = enum && !enum.is_a?(Hash) if i18n.nil?
|
64
|
+
scopes = configuration[:scopes]
|
65
|
+
methods = configuration[:methods]
|
66
|
+
capitalize = configuration[:capitalize]
|
67
|
+
validation = configuration[:validate] != false
|
68
|
+
default_option = configuration[:default]
|
71
69
|
|
72
70
|
attr_names.each do |attr_name|
|
73
|
-
|
74
|
-
|
75
|
-
if enum
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
71
|
+
attr_name_str = attr_name.to_s
|
72
|
+
|
73
|
+
if enum
|
74
|
+
enum_hash = \
|
75
|
+
if enum.is_a?(Hash)
|
76
|
+
enum
|
77
|
+
else
|
78
|
+
enum.map do |val|
|
79
|
+
[
|
80
|
+
val.respond_to?(:to_sym) ? val.to_sym : val,
|
81
|
+
capitalize ? val.to_s.capitalize : val.to_s,
|
82
|
+
]
|
83
|
+
end.to_h
|
82
84
|
end
|
83
|
-
end
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
class_eval "def self.#{attr_name}_enum; self.get_#{const}; end"
|
86
|
+
values_name = attr_name_str + '_values'
|
87
|
+
values_const_name = values_name.upcase
|
88
|
+
|
89
|
+
# Get the values of :in
|
90
|
+
const_set values_const_name, enum_hash unless const_defined? values_const_name
|
91
|
+
|
92
|
+
[
|
93
|
+
'get_' + values_name,
|
94
|
+
attr_name_str + '_enum',
|
95
|
+
].each do |enum_method_name|
|
96
96
|
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
define_singleton_method(enum_method_name) do
|
98
|
+
if i18n
|
99
|
+
enum_hash.each_key.map do |symbol|
|
100
|
+
[i18n_translation_for(attr_name_str, symbol), symbol]
|
101
|
+
end
|
102
|
+
else
|
103
|
+
enum_hash.map(&:reverse)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
100
107
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
108
|
+
if methods
|
109
|
+
enum_hash.each_key do |key|
|
110
|
+
# It's a good idea to test for name collisions here and raise exceptions.
|
111
|
+
# However, the existing software with this kind of errors will start crashing,
|
112
|
+
# so I'd postpone this improvement until the next major version
|
113
|
+
# this way it will not affect those people who use ~> in their Gemfiles
|
105
114
|
|
106
|
-
|
115
|
+
# raise ArgumentError, "re-defined #{key}? method of #{self.name} class due to 'symbolize'" if method_defined?("#{key}?")
|
107
116
|
|
108
|
-
|
109
|
-
|
117
|
+
define_method("#{key}?") do
|
118
|
+
send(attr_name_str) == key.to_sym
|
119
|
+
end
|
110
120
|
end
|
111
121
|
end
|
112
|
-
end
|
113
122
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
scope name
|
123
|
+
if scopes
|
124
|
+
if scopes == :shallow
|
125
|
+
enum_hash.each_key do |name|
|
126
|
+
next unless name.respond_to?(:to_sym)
|
127
|
+
|
128
|
+
scope name, -> { where(attr_name_str => name) }
|
120
129
|
# Figure out if this as another option, or default...
|
121
|
-
#
|
130
|
+
# scope "not_#{name}", -> { where.not(attr_name_str => name)
|
122
131
|
end
|
132
|
+
else
|
133
|
+
scope attr_name_str, ->(val) { where(attr_name_str => val) }
|
123
134
|
end
|
124
|
-
else
|
125
|
-
scope attr_name, ->(enum) { where(attr_name => enum) }
|
126
135
|
end
|
136
|
+
|
137
|
+
if validation
|
138
|
+
validates(*attr_names, configuration.slice(:allow_nil, :allow_blank).merge(:inclusion => { :in => enum_hash.keys }))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
define_method(attr_name_str) { read_and_symbolize_attribute(attr_name_str) || default_option }
|
143
|
+
define_method(attr_name_str + '=') { |value| write_symbolized_attribute(attr_name_str, value) }
|
144
|
+
|
145
|
+
if default_option
|
146
|
+
before_save { self[attr_name_str] ||= default_option }
|
147
|
+
else
|
148
|
+
define_method(attr_name_str) { read_and_symbolize_attribute(attr_name_str) }
|
127
149
|
end
|
128
150
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
151
|
+
define_method(attr_name_str + '_text') do
|
152
|
+
if i18n
|
153
|
+
read_i18n_attribute(attr_name_str)
|
154
|
+
else
|
155
|
+
attr_value = send(attr_name_str)
|
156
|
+
if enum
|
157
|
+
enum_hash[attr_value]
|
158
|
+
else
|
159
|
+
attr_value.to_s
|
160
|
+
end
|
161
|
+
end
|
135
162
|
end
|
136
163
|
end
|
137
|
-
end
|
138
164
|
|
139
|
-
|
165
|
+
# merge new symbolized attribute and create a new array to ensure that each class in inheritance hierarchy
|
166
|
+
# has its own array of symbolized attributes
|
167
|
+
self.symbolized_attributes += attr_names.map(&:to_s)
|
168
|
+
end
|
140
169
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
class_eval("def #{attr_name}= (value); write_symbolized_attribute('#{attr_name}', value); end")
|
170
|
+
# Hook used by Rails to do extra stuff to attributes when they are initialized.
|
171
|
+
def initialize_attributes(*args)
|
172
|
+
super.tap do |attributes|
|
173
|
+
# Make sure any default values read from the database are symbolized
|
174
|
+
symbolized_attributes.each do |attr_name|
|
175
|
+
attributes[attr_name] = symbolize_attribute(attributes[attr_name])
|
176
|
+
end
|
149
177
|
end
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
178
|
+
end
|
179
|
+
|
180
|
+
# String becomes symbol, booleans string and nil nil.
|
181
|
+
def symbolize_attribute(value)
|
182
|
+
case value
|
183
|
+
when String
|
184
|
+
value.presence.try(:to_sym)
|
185
|
+
when Symbol, TrueClass, FalseClass, Numeric
|
186
|
+
value
|
154
187
|
else
|
155
|
-
|
188
|
+
nil
|
156
189
|
end
|
157
190
|
end
|
158
191
|
|
159
|
-
|
160
|
-
|
161
|
-
self.symbolized_attributes += attr_names.map(&:to_s)
|
162
|
-
end
|
163
|
-
|
164
|
-
# Hook used by Rails to do extra stuff to attributes when they are initialized.
|
165
|
-
def initialize_attributes *args
|
166
|
-
super.tap do |attributes|
|
167
|
-
# Make sure any default values read from the database are symbolized
|
168
|
-
symbolized_attributes.each do |attr_name|
|
169
|
-
attributes[attr_name] = symbolize_attribute(attributes[attr_name])
|
170
|
-
end
|
192
|
+
def i18n_translation_for(attr_name, attr_value)
|
193
|
+
I18n.translate("activerecord.symbolizes.#{model_name.to_s.underscore}.#{attr_name}.#{attr_value}")
|
171
194
|
end
|
172
195
|
end
|
173
196
|
|
174
197
|
# String becomes symbol, booleans string and nil nil.
|
175
|
-
def symbolize_attribute
|
176
|
-
|
177
|
-
when String
|
178
|
-
value.presence.try(:to_sym)
|
179
|
-
when Symbol, TrueClass, FalseClass, Numeric
|
180
|
-
value
|
181
|
-
else
|
182
|
-
nil
|
183
|
-
end
|
198
|
+
def symbolize_attribute(value)
|
199
|
+
self.class.symbolize_attribute(value)
|
184
200
|
end
|
185
|
-
end
|
186
|
-
|
187
|
-
# String becomes symbol, booleans string and nil nil.
|
188
|
-
def symbolize_attribute value
|
189
|
-
self.class.symbolize_attribute value
|
190
|
-
end
|
191
|
-
|
192
|
-
# Return an attribute's value as a symbol or nil
|
193
|
-
def read_and_symbolize_attribute attr_name
|
194
|
-
symbolize_attribute self[attr_name]
|
195
|
-
end
|
196
201
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
t.is_a?(Hash) ? nil : t
|
202
|
-
end
|
202
|
+
# Return an attribute's value as a symbol or nil
|
203
|
+
def read_and_symbolize_attribute(attr_name)
|
204
|
+
symbolize_attribute(read_attribute(attr_name))
|
205
|
+
end
|
203
206
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
207
|
+
# Return an attribute's i18n
|
208
|
+
def read_i18n_attribute(attr_name)
|
209
|
+
unless (t = self.class.i18n_translation_for(attr_name, read_attribute(attr_name))).is_a?(Hash)
|
210
|
+
t
|
211
|
+
end
|
212
|
+
end
|
208
213
|
|
209
|
-
|
214
|
+
# Write a symbolized value. Watch out for booleans.
|
215
|
+
def write_symbolized_attribute(attr_name, value)
|
216
|
+
write_attribute(attr_name, symbolize_attribute(value))
|
217
|
+
end
|
210
218
|
end
|
211
219
|
end
|
data/lib/symbolize/mongoid.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
2
3
|
|
3
4
|
module Mongoid
|
4
5
|
module Symbolize
|
@@ -49,106 +50,117 @@ module Mongoid
|
|
49
50
|
# Specifies that values of the given attributes should be returned
|
50
51
|
# as symbols. The table column should be created of type string.
|
51
52
|
|
52
|
-
def symbolize
|
53
|
-
configuration =
|
54
|
-
configuration.
|
53
|
+
def symbolize(*attr_names)
|
54
|
+
configuration = attr_names.extract_options!
|
55
|
+
configuration.assert_valid_keys(:in, :within, :i18n, :scopes, :methods, :capitalize, :validate, :default, :allow_blank, :allow_nil, :type)
|
55
56
|
|
56
|
-
enum
|
57
|
-
i18n
|
58
|
-
i18n
|
59
|
-
scopes
|
60
|
-
methods
|
61
|
-
capitalize
|
62
|
-
validation
|
63
|
-
|
64
|
-
default_opt = configuration.delete :default
|
65
|
-
enum = [true, false] if [Boolean, ::Boolean].include?(field_type)
|
57
|
+
enum = configuration[:in] || configuration[:within]
|
58
|
+
i18n = configuration[:i18n]
|
59
|
+
i18n = enum && !enum.is_a?(Hash) if i18n.nil?
|
60
|
+
scopes = configuration[:scopes]
|
61
|
+
methods = configuration[:methods]
|
62
|
+
capitalize = configuration[:capitalize]
|
63
|
+
validation = configuration[:validate] != false
|
64
|
+
default_option = configuration[:default]
|
66
65
|
|
67
|
-
|
66
|
+
field_type = configuration[:type] || Symbol
|
67
|
+
enum = [true, false] if [Boolean, ::Boolean].include?(field_type)
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
#
|
73
|
-
# Builds Mongoid 'field :name, type: type, :default'
|
74
|
-
#
|
75
|
-
const = "#{attr_name}_values"
|
76
|
-
mongo_opts = ", :type => #{field_type || 'Symbol'}"
|
77
|
-
mongo_opts += ", :default => :#{default_opt}" if default_opt
|
78
|
-
class_eval("field :#{attr_name} #{mongo_opts}")
|
69
|
+
attr_names.each do |attr_name|
|
70
|
+
attr_name_str = attr_name.to_s
|
79
71
|
|
72
|
+
if enum
|
73
|
+
enum_hash = \
|
80
74
|
if enum.is_a?(Hash)
|
81
|
-
|
75
|
+
enum
|
82
76
|
else
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
values[key] = capitalize ? val.to_s.capitalize : val.to_s
|
77
|
+
enum.each_with_object({}) do |val, hsh|
|
78
|
+
hsh.store(val.respond_to?(:to_sym) ? val.to_sym : val,
|
79
|
+
capitalize ? val.to_s.capitalize : val.to_s)
|
87
80
|
end
|
88
81
|
end
|
89
82
|
|
83
|
+
#
|
84
|
+
# Builds Mongoid 'field :name, type: type, :default'
|
85
|
+
#
|
86
|
+
{ :type => field_type }.tap do |field_opts|
|
87
|
+
field_opts.merge!(:default => default_option) if default_option
|
88
|
+
field attr_name, field_opts
|
89
|
+
end
|
90
|
+
|
91
|
+
values_name = attr_name_str + '_values'
|
92
|
+
values_const_name = values_name.upcase
|
93
|
+
|
90
94
|
# Get the values of :in
|
91
|
-
const_set
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
const_set values_const_name, enum_hash unless const_defined? values_const_name
|
96
|
+
|
97
|
+
[
|
98
|
+
'get_' + values_name, attr_name_str + '_enum'
|
99
|
+
].each do |enum_method_name|
|
100
|
+
|
101
|
+
define_singleton_method(enum_method_name) do
|
102
|
+
if i18n
|
103
|
+
enum_hash.each_key.map do |symbol|
|
104
|
+
[i18n_translation_for(attr_name_str, symbol), symbol]
|
105
|
+
end
|
106
|
+
else
|
107
|
+
enum_hash.map(&:reverse)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
101
111
|
|
102
112
|
if methods
|
103
|
-
|
104
|
-
define_method("#{
|
105
|
-
|
113
|
+
enum_hash.each_key do |key|
|
114
|
+
define_method("#{key}?") do
|
115
|
+
send(attr_name_str) == key.to_sym
|
106
116
|
end
|
107
117
|
end
|
108
118
|
end
|
109
119
|
|
110
120
|
if scopes
|
111
121
|
if scopes == :shallow
|
112
|
-
|
113
|
-
next unless
|
114
|
-
scope
|
122
|
+
enum_hash.each_key do |name|
|
123
|
+
next unless name.respond_to?(:to_sym)
|
124
|
+
scope name, -> { where(attr_name_str => name) }
|
115
125
|
end
|
116
126
|
else # scoped scopes
|
117
|
-
scope
|
127
|
+
scope attr_name_str, ->(val) { where(attr_name_str => val) }
|
118
128
|
end
|
119
129
|
end
|
120
130
|
|
121
131
|
if validation
|
122
|
-
|
123
|
-
",:inclusion => { :in => #{values.keys.inspect} }"
|
124
|
-
v += ',:allow_nil => true' if configuration[:allow_nil]
|
125
|
-
v += ',:allow_blank => true' if configuration[:allow_blank]
|
126
|
-
class_eval v
|
132
|
+
validates(*attr_names, configuration.slice(:allow_nil, :allow_blank).merge(:inclusion => { :in => enum_hash.keys }))
|
127
133
|
end
|
128
|
-
|
129
134
|
end
|
130
|
-
end
|
131
135
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
136
|
+
#
|
137
|
+
# Creates <attribute>_text helper, human text for attribute.
|
138
|
+
#
|
139
|
+
define_method(attr_name_str + '_text') do
|
140
|
+
if i18n
|
141
|
+
read_i18n_attribute(attr_name_str)
|
142
|
+
else
|
143
|
+
attr_value = send(attr_name_str)
|
144
|
+
if enum
|
145
|
+
enum_hash[attr_value]
|
146
|
+
else
|
147
|
+
attr_value.to_s
|
148
|
+
end
|
141
149
|
end
|
142
|
-
elsif enum
|
143
|
-
class_eval("def #{attr_name}_text; #{attr_name.to_s.upcase}_VALUES[#{attr_name}]; end")
|
144
|
-
else
|
145
|
-
class_eval("def #{attr_name}_text; #{attr_name}.to_s; end")
|
146
150
|
end
|
147
|
-
end
|
148
151
|
|
152
|
+
def i18n_translation_for(attr_name, attr_value)
|
153
|
+
I18n.translate("mongoid.symbolizes.#{model_name.to_s.underscore}.#{attr_name}.#{attr_value}")
|
154
|
+
end
|
155
|
+
end
|
149
156
|
end
|
150
|
-
|
151
157
|
end # ClassMethods
|
158
|
+
|
159
|
+
# Return an attribute's i18n
|
160
|
+
def read_i18n_attribute(attr_name)
|
161
|
+
t = self.class.i18n_translation_for(attr_name, read_attribute(attr_name))
|
162
|
+
t.is_a?(Hash) ? nil : t
|
163
|
+
end
|
152
164
|
end # Symbolize
|
153
165
|
end # Mongoid
|
154
166
|
|