symbolize 3.3.0pre → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,52 +1,34 @@
1
- = Symbolize attribute values in ActiveRecord (e.g. for nicer enums)
2
-
3
- This plugin introduces an easy way to use symbols for values of ActiveRecord
4
- attributes. Symbolized attributes return a ruby symbol (or nil) as their value
5
- and can be set using symbols.
6
-
7
-
8
- == About
9
-
10
- Since ActiveRecord does not natively support database column types of ENUM or
11
- SET, you'll usually use a string attribute and restrict it to certain values
12
- with validations. Using this plugin, the values of such pseudo-enums are
13
- symbols, which look more ruby-style than strings.
14
-
15
- Simply add "symbolize :attr_name" to your model class, and the specified
16
- attribute will return symbol values and can be set using smbols (setting
17
- string values will still work, which is important when using forms).
18
-
19
- An attribute to symbolize should be a string (varchar) column in the database.
20
-
21
- Blog: http://zargony.com/
22
- Github: http://github.com/zargony/activerecord_symbolize
1
+ = Symbolize attribute values
23
2
 
3
+ This plugin introduces an easy way to use symbols for values of attributes. Symbolized attributes return a ruby symbol (or nil) as their value
4
+ and can be set using :symbols or "strings".
24
5
 
25
6
  == Install
26
7
 
27
8
  === Gem
28
9
 
29
- gem install symbolize
30
- config.gem "symbolize", :source => 'http://gemcutter.org'
10
+ gem install symbolize
31
11
 
32
12
 
33
- Rails 3+ Gemfile
13
+ === Mongoid
34
14
 
35
- gem "symbolize"
15
+ gem "symbolize", :require => "symbolize/mongoid"
36
16
 
37
17
 
38
- === Plugin:
18
+ === ActiveRecord
19
+
20
+ gem "symbolize", :require => "symbolize/active_record"
39
21
 
40
- ./script/plugin install git://github.com/nofxx/symbolize.git
41
22
 
42
- or in Rails3+
43
23
 
44
- rails plugin install ..
24
+ == About
45
25
 
26
+ Simply add "symbolize :attr_name" to your model class, and the specified
27
+ attribute will return symbol values and can be set using smbols (setting
28
+ string values will still work, which is important when using forms).
46
29
 
47
- == Rails 3 (beta)
30
+ On SQL-based DBs, an attribute to symbolize should be a string (varchar) column in the database.
48
31
 
49
- Specs pass with rails 3, but a scope :public == fail.
50
32
 
51
33
 
52
34
  == Usage
@@ -54,7 +36,14 @@ Specs pass with rails 3, but a scope :public == fail.
54
36
  Add "symbolize :attr_name" to your model class. You may also want to add
55
37
  validates_inclusion_of to restrict the possible values (just like an enum).
56
38
 
39
+ # ActiveRecord
57
40
  class User < ActiveRecord::Base
41
+
42
+ # Mongoid
43
+ class User
44
+ include Mongoid::Document
45
+ include Mongoid::Symbolize
46
+
58
47
  symbolize :gender, :in => [:female, :male], :scopes => true
59
48
  symbolize :so, :in => {
60
49
  :linux => "Linux",
@@ -174,6 +163,15 @@ As the name suggest, the symbol you choose as default will be set in new objects
174
163
  u = User.new(:name => 'ET', :gender => :unknown)
175
164
  u.save # => validation fails
176
165
 
166
+ == Rails Form Example
167
+
168
+ class Coffee
169
+ symbolize :genetic, :in => [:arabica, :robusta, :blend]
170
+ end
171
+
172
+ form_for(@coffee) do |f|
173
+ = f.label :genetic
174
+ = f.select :genetic, Coffee.get_genetic_values
177
175
 
178
176
  == Model Helpers
179
177
 
@@ -181,9 +179,6 @@ As the name suggest, the symbol you choose as default will be set in new objects
181
179
  You may call `Class.get_attr_values` anywhere to get a nice array.
182
180
  Works nice with dropdowns. Examples:
183
181
 
184
- class Coffee
185
- symbolize :genetic, :in => [:arabica, :robusta, :blend]
186
- end
187
182
 
188
183
  Somewhere on a view:
189
184
 
@@ -221,6 +216,27 @@ Somewhere on a view:
221
216
  </form>
222
217
 
223
218
 
219
+
220
+ === Plugin:
221
+
222
+ Try at your own risk.
223
+
224
+ rails plugin install git://github.com/nofxx/symbolize.git
225
+
226
+
227
+ == Rails 3.1 (beta)
228
+
229
+ Specs pass with rails 3, but a scope :public == fail.
230
+
231
+
232
+ == Specs
233
+
234
+ Run the adapter independently:
235
+
236
+ $ rspec spec/symbolize/mongoid_spec.rb
237
+ $ rspec spec/symbolize/active_record_spec.rb
238
+
239
+
224
240
  == Notes
225
241
 
226
242
  This fork:
data/Rakefile CHANGED
@@ -6,8 +6,8 @@ begin
6
6
  require 'jeweler'
7
7
  Jeweler::Tasks.new do |gem|
8
8
  gem.name = "symbolize"
9
- gem.summary = "ActiveRecord enums with i18n"
10
- gem.description = "ActiveRecord enums with i18n"
9
+ gem.summary = "Object enums with i18n in AR or Mongoid"
10
+ gem.description = "ActiveRecord/Mongoid enums with i18n"
11
11
  gem.email = "x@nofxx.com"
12
12
  gem.homepage = "http://github.com/nofxx/symbolize"
13
13
  gem.authors = ["Marcos Piccinini"]
@@ -39,7 +39,7 @@ end
39
39
  # rdoc.rdoc_files.include('README')
40
40
  # rdoc.rdoc_files.include('lib/**/*.rb')
41
41
  # end
42
- require 'rake/rdoctask'
42
+ require 'rdoc/task'
43
43
  Rake::RDocTask.new do |rdoc|
44
44
  if File.exist?('VERSION.yml')
45
45
  config = YAML.load(File.read('VERSION.yml'))
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0
1
+ 4.0.1
@@ -0,0 +1,194 @@
1
+ module Symbolize
2
+ def self.included base
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ # Symbolize ActiveRecord attributes. Add
7
+ # symbolize :attr_name
8
+ # to your model class, to make an attribute return symbols instead of
9
+ # string values. Setting such an attribute will accept symbols as well
10
+ # as strings. In the database, the symbolized attribute should have
11
+ # the column-type :string.
12
+ #
13
+ # Example:
14
+ # class User < ActiveRecord::Base
15
+ # symbolize :gender, :in => [:female, :male]
16
+ # symbolize :so, :in => {
17
+ # :linux => "Linux",
18
+ # :mac => "Mac OS X"
19
+ # }
20
+ # symbolize :gui, , :in => [:gnome, :kde, :xfce], :allow_blank => true
21
+ # symbolize :browser, :in => [:firefox, :opera], :i18n => false
22
+ # end
23
+ #
24
+ # It will automattically lookup for i18n:
25
+ #
26
+ # activerecord:
27
+ # attributes:
28
+ # user:
29
+ # enums:
30
+ # gender:
31
+ # female: Girl
32
+ # male: Boy
33
+ #
34
+ # You can skip i18n lookup with :i18n => false
35
+ # symbolize :gender, :in => [:female, :male], :i18n => false
36
+ #
37
+ # Its possible to use boolean fields also.
38
+ # symbolize :switch, :in => [true, false]
39
+ #
40
+ # ...
41
+ # switch:
42
+ # "true": On
43
+ # "false": Off
44
+ # "nil": Unknown
45
+ #
46
+ module ClassMethods
47
+ # Specifies that values of the given attributes should be returned
48
+ # as symbols. The table column should be created of type string.
49
+
50
+ def symbolize *attr_names
51
+ configuration = {}
52
+ configuration.update(attr_names.extract_options!)
53
+
54
+ enum = configuration[:in] || configuration[:within]
55
+ i18n = configuration.delete(:i18n).nil? && !enum.instance_of?(Hash) && enum ? true : configuration[:i18n]
56
+ scopes = configuration.delete :scopes
57
+ methods = configuration.delete :methods
58
+ capitalize = configuration.delete :capitalize
59
+ validation = configuration.delete(:validation) != false
60
+ default_option = configuration.delete :default
61
+
62
+ unless enum.nil?
63
+ # Little monkeypatching, <1.8 Hashes aren't ordered.
64
+ hsh = RUBY_VERSION > '1.9' || !defined?("ActiveSupport") ? Hash : ActiveSupport::OrderedHash
65
+
66
+ attr_names.each do |attr_name|
67
+ attr_name = attr_name.to_s
68
+ const = "#{attr_name}_values"
69
+ if enum.is_a?(Hash)
70
+ values = enum
71
+ else
72
+ values = hsh.new
73
+ enum.map do |val|
74
+ key = val.respond_to?(:to_sym) ? val.to_sym : val
75
+ values[key] = capitalize ? val.to_s.capitalize : val.to_s
76
+ end
77
+ end
78
+
79
+ # Get the values of :in
80
+ const_set const.upcase, values unless const_defined? const.upcase
81
+ ev = if i18n
82
+ # This one is a dropdown helper
83
+ code = "#{const.upcase}.map { |k,v| [I18n.translate(\"activerecord.attributes.\#{ActiveSupport::Inflector.underscore(self)}.enums.#{attr_name}.\#{k}\"), k] }" #.to_sym rescue nila
84
+ "def self.get_#{const}; #{code}; end;"
85
+ else
86
+ "def self.get_#{const}; #{const.upcase}.map(&:reverse); end"
87
+ end
88
+ class_eval(ev)
89
+
90
+ if methods
91
+ values.each do |value|
92
+ define_method("#{value[0]}?") do
93
+ self.send(attr_name) == value[0]
94
+ end
95
+ end
96
+ end
97
+
98
+ if scopes
99
+ scope_comm = lambda { |*args| ActiveRecord::VERSION::MAJOR >= 3 ? scope(*args) : named_scope(*args)}
100
+ values.each do |value|
101
+ if value[0].respond_to?(:to_sym)
102
+ scope_comm.call value[0].to_sym, :conditions => { attr_name => value[0].to_sym }
103
+ elsif ActiveRecord::VERSION::STRING <= "3.0"
104
+ if value[0] == true || value[0] == false
105
+ scope_comm.call "with_#{attr_name}".to_sym, :conditions => { attr_name => '1' }
106
+ scope_comm.call "without_#{attr_name}".to_sym, :conditions => { attr_name => '0' }
107
+
108
+ scope_comm.call attr_name.to_sym, :conditions => { attr_name => '1' }
109
+ scope_comm.call "not_#{attr_name}".to_sym, :conditions => { attr_name => '0' }
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ if validation
117
+ class_eval "validates_inclusion_of :#{attr_names.join(', :')}, #{configuration.inspect}"
118
+ end
119
+ end
120
+
121
+ attr_names.each do |attr_name|
122
+
123
+ if default_option
124
+ class_eval("def #{attr_name}; read_and_symbolize_attribute('#{attr_name}') || :#{default_option}; end")
125
+ class_eval("def #{attr_name}= (value); write_symbolized_attribute('#{attr_name}', value); end")
126
+ class_eval("def set_default_for_attr_#{attr_name}; self[:#{attr_name}] ||= :#{default_option}; end")
127
+ class_eval("before_save :set_default_for_attr_#{attr_name}")
128
+ else
129
+ class_eval("def #{attr_name}; read_and_symbolize_attribute('#{attr_name}'); end")
130
+ class_eval("def #{attr_name}= (value); write_symbolized_attribute('#{attr_name}', value); end")
131
+ end
132
+ if i18n
133
+ class_eval("def #{attr_name}_text; read_i18n_attribute('#{attr_name}'); end")
134
+ elsif enum
135
+ class_eval("def #{attr_name}_text; #{attr_name.to_s.upcase}_VALUES[#{attr_name}]; end")
136
+ else
137
+ class_eval("def #{attr_name}_text; #{attr_name}.to_s; end")
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ # String becomes symbol, booleans string and nil nil.
144
+ def symbolize_attribute attr
145
+ case attr
146
+ when String then attr.empty? ? nil : attr.to_sym
147
+ when Symbol, TrueClass, FalseClass, Numeric then attr
148
+ else nil
149
+ end
150
+ end
151
+
152
+ # Return an attribute's value as a symbol or nil
153
+ def read_and_symbolize_attribute attr_name
154
+ symbolize_attribute self[attr_name]
155
+ end
156
+
157
+ # Return an attribute's i18n
158
+ def read_i18n_attribute attr_name
159
+ attr = read_attribute(attr_name)
160
+ return nil if attr.nil?
161
+ I18n.translate("activerecord.attributes.#{ActiveSupport::Inflector.underscore(self.class)}.enums.#{attr_name}.#{attr}") #.to_sym rescue nila
162
+ end
163
+
164
+ # Write a symbolized value. Watch out for booleans.
165
+ def write_symbolized_attribute attr_name, value
166
+ val = { "true" => true, "false" => false }[value]
167
+ val = symbolize_attribute(value) if val.nil?
168
+
169
+ self[attr_name] = val #.to_s # rails 3.1 fix
170
+ end
171
+ end
172
+
173
+ # The Symbol class is extended by method quoted_id which returns a string.
174
+ # The idea behind this is, that symbols are converted to plain strings
175
+ # when being quoted by ActiveRecord::ConnectionAdapters::Quoting#quote.
176
+ # This makes it possible to work with symbolized attibutes in sql conditions.
177
+ # E.g. validates_uniqueness_of could not use :scope with a symbolized
178
+ # attribute, because AR quotes it to YAML:
179
+ # "... AND status = '--- :active\n'"
180
+ # Having support for quoted_id in Symbol, makes AR quoting symbols correctly:
181
+ # "... AND status = 'active'"
182
+ # NOTE: Normally quoted_id should be implemented as a singleton method
183
+ # only used on symbols returned by read_and_symbolize_attribute,
184
+ # but unfortunately this is not possible since Symbol is an immediate
185
+ # value and therefore does not support singleton methods.
186
+ # class Symbol
187
+ # def quoted_id
188
+ # # A symbol can contain almost every character (even a backslash or an
189
+ # # apostrophe), so make sure to properly quote the string value here.
190
+ # "'#{ActiveRecord::Base.connection.quote_string(self.to_s)}'"
191
+ # end
192
+ # end
193
+
194
+ ActiveRecord::Base.send(:include, Symbolize) if ActiveRecord::VERSION::MAJOR >= 3
@@ -0,0 +1,172 @@
1
+ module Mongoid
2
+ module Symbolize
3
+ def self.included base
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ # Symbolize Mongoid attributes. Add:
8
+ # symbolize :attr_name
9
+ # to your model class, to make an attribute return symbols instead of
10
+ # string values. Setting such an attribute will accept symbols as well
11
+ # as strings.
12
+ #
13
+ # There's no need for 'field :attr_name', symbolize will do it.
14
+ #
15
+ # Example:
16
+ # class User
17
+ # include Mongoid::Document
18
+ # symbolize :gender, :in => [:female, :male]
19
+ # symbolize :so, :in => {
20
+ # :linux => "Linux",
21
+ # :mac => "Mac OS X"
22
+ # }
23
+ # symbolize :gui, , :in => [:gnome, :kde, :xfce], :allow_blank => true
24
+ # symbolize :browser, :in => [:firefox, :opera], :i18n => false
25
+ # end
26
+ #
27
+ # It will automattically lookup for i18n:
28
+ #
29
+ # models:
30
+ # attributes:
31
+ # user:
32
+ # enums:
33
+ # gender:
34
+ # female: Girl
35
+ # male: Boy
36
+ #
37
+ # You can skip i18n lookup with :i18n => false
38
+ # symbolize :gender, :in => [:female, :male], :i18n => false
39
+ #
40
+ # Its possible to use boolean fields also.
41
+ # symbolize :switch, :in => [true, false]
42
+ #
43
+ # ...
44
+ # switch:
45
+ # "true": On
46
+ # "false": Off
47
+ # "nil": Unknown
48
+ #
49
+ module ClassMethods
50
+ # Specifies that values of the given attributes should be returned
51
+ # as symbols. The table column should be created of type string.
52
+
53
+ def symbolize *attr_names
54
+ configuration = {}
55
+ configuration.update(attr_names.extract_options!)
56
+
57
+ enum = configuration[:in] || configuration[:within]
58
+ i18n = configuration.delete(:i18n).nil? && !enum.instance_of?(Hash) && enum ? true : configuration[:i18n]
59
+ scopes = configuration.delete :scopes
60
+ methods = configuration.delete :methods
61
+ capitalize = configuration.delete :capitalize
62
+ validation = configuration.delete(:validation) != false
63
+ default_opt = configuration.delete :default
64
+
65
+ unless enum.nil?
66
+ # Little monkeypatching, <1.8 Hashes aren't ordered.
67
+ # hsh = RUBY_VERSION > '1.9' || !defined?("ActiveSupport") ? Hash : ActiveSupport::OrderedHash
68
+
69
+ attr_names.each do |attr_name|
70
+ attr_name = attr_name.to_s
71
+
72
+ # Builds Mongoid 'field :name, type: type, :default
73
+ type = ", type: Symbol"
74
+ default = ", default: :#{default_opt}" if default_opt
75
+ class_eval("field :#{attr_name} #{type} #{default}")
76
+
77
+ const = "#{attr_name}_values"
78
+ if enum.is_a?(Hash)
79
+ values = enum
80
+ else
81
+ values = {}
82
+ enum.map do |val|
83
+ key = val.respond_to?(:to_sym) ? val.to_sym : val
84
+ values[key] = capitalize ? val.to_s.capitalize : val.to_s
85
+ end
86
+ end
87
+
88
+ # Get the values of :in
89
+ const_set const.upcase, values unless const_defined? const.upcase
90
+ ev = if i18n
91
+ # This one is a dropdown helper
92
+ code = "#{const.upcase}.map { |k,v| [I18n.t(\"mongoid.attributes.\#{ActiveSupport::Inflector.underscore(self)}.enums.#{attr_name}.\#{k}\"), k] }" #.to_sym rescue nila
93
+ "def self.get_#{const}; #{code}; end;"
94
+ else
95
+ "def self.get_#{const}; #{const.upcase}.map(&:reverse); end"
96
+ end
97
+ class_eval(ev)
98
+
99
+ if methods
100
+ values.each do |value|
101
+ define_method("#{value[0]}?") do
102
+ self.send(attr_name) == value[0]
103
+ end
104
+ end
105
+ end
106
+
107
+ if scopes
108
+ scope_comm = lambda { |*args| scope(*args)}
109
+ values.each do |value|
110
+ if value[0].respond_to?(:to_sym)
111
+ scope_comm.call value[0].to_sym, :conditions => { attr_name => value[0].to_sym }
112
+ end
113
+ end
114
+ end
115
+
116
+ if validation
117
+ class_eval "validates_inclusion_of :#{attr_names.join(', :')}, #{configuration.inspect}"
118
+ end
119
+ end
120
+ end
121
+
122
+ attr_names.each do |attr_name|
123
+
124
+ # if default_option
125
+ # class_eval("def #{attr_name}; read_and_symbolize_attribute('#{attr_name}') || :#{default_option}; end")
126
+ # class_eval("def #{attr_name}= (value); write_symbolized_attribute('#{attr_name}', value); end")
127
+ # class_eval("def set_default_for_attr_#{attr_name}; self[:#{attr_name}] ||= :#{default_option}; end")
128
+ # class_eval("before_save :set_default_for_attr_#{attr_name}")
129
+ # else
130
+ # class_eval("def #{attr_name}; read_and_symbolize_attribute('#{attr_name}'); end")
131
+ # class_eval("def #{attr_name}= (value); write_symbolized_attribute('#{attr_name}', value); end")
132
+ # end
133
+ if i18n
134
+ class_eval("def #{attr_name}_text; read_i18n_attribute('#{attr_name}'); end")
135
+ elsif enum
136
+ class_eval("def #{attr_name}_text; #{attr_name.to_s.upcase}_VALUES[#{attr_name}]; end")
137
+ else
138
+ class_eval("def #{attr_name}_text; #{attr_name}.to_s; end")
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ # String becomes symbol, booleans string and nil nil.
145
+ # def symbolize_attribute attr
146
+ # case attr
147
+ # when String then attr.empty? ? nil : attr.to_sym
148
+ # when Symbol, TrueClass, FalseClass, Numeric then attr
149
+ # else nil
150
+ # end
151
+ # end
152
+
153
+ # # Return an attribute's value as a symbol or nil
154
+ # def read_and_symbolize_attribute attr_name
155
+ # symbolize_attribute self[attr_name]
156
+ # end
157
+
158
+ # Return an attribute's i18n
159
+ def read_i18n_attribute attr_name
160
+ return nil unless attr = read_attribute(attr_name)
161
+ I18n.translate("activerecord.attributes.#{ActiveSupport::Inflector.underscore(self.class)}.enums.#{attr_name}.#{attr}") #.to_sym rescue nila
162
+ end
163
+
164
+ # # Write a symbolized value. Watch out for booleans.
165
+ # def write_symbolized_attribute attr_name, value
166
+ # val = { "true" => true, "false" => false }[value]
167
+ # val = symbolize_attribute(value) if val.nil?
168
+
169
+ # self[attr_name] = val #.to_s # rails 3.1 fix
170
+ # end
171
+ end
172
+ end
@@ -1,14 +1,15 @@
1
1
  # Rails 3 initialization
2
2
  module Symbolize
3
- if defined? Rails::Railtie
4
- require 'rails'
5
- class Railtie < Rails::Railtie
6
- initializer 'symbolize.insert_into_active_record' do
7
- ActiveSupport.on_load :active_record do
8
- ActiveRecord::Base.extend(Symbolize::ClassMethods)
9
- end
10
- end
11
- end
12
- end
3
+ # if defined? Rails::Railtie
4
+ # require 'rails'
5
+ # class Railtie < Rails::Railtie
6
+ # initializer 'symbolize.insert_into_active_record' do
7
+ # ActiveSupport.on_load :active_record do
8
+ # ActiveRecord::Base.extend(Symbolize::ClassMethods)
9
+ # end
10
+ # end
11
+ # end
12
+ # end
13
13
  end
14
14
 
15
+