simple_enum 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,2 +1,5 @@
1
1
  doc
2
2
  pkg
3
+ *.rbc
4
+ .*.sw?
5
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - rbx
6
+ - ree
data/Gemfile CHANGED
@@ -1,7 +1,3 @@
1
- source :rubygems
1
+ source "http://rubygems.org"
2
2
 
3
- gem "activesupport", "~> 3.0", :require => "active_support"
4
- gem "activerecord", "~> 3.0", :require => "active_record"
5
-
6
- gem "sqlite3-ruby", :require => "sqlite3", :platforms => :ruby
7
- gem "jdbc-sqlite3", :require => "jdbcsqlite3", :platforms => :jruby
3
+ gemspec
data/LICENCE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Lukas Westermann (Zurich, Switzerland)
1
+ Copyright (c) 2011 Lukas Westermann (Zurich, Switzerland)
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -1,17 +1,19 @@
1
1
  = SimpleEnum - unobtrusive enum-like fields for ActiveRecord
2
2
 
3
3
  A Rails plugin which brings easy-to-use enum-like functionality to
4
- ActiveRecord models (now compatible with rails 3, ruby 1.9 and jruby).
4
+ ActiveRecord models (now compatible with rails 3.1, ruby 1.9 and jruby).
5
+
6
+ Since version 1.4, simple_enum is no longer compatible with activerecord 2.x, use
7
+ version 1.3.2 instead: <https://github.com/lwe/simple_enum/tree/v1.3.2>.
5
8
 
6
9
  *Note*: a recent search on github for `enum` turned out, that there are many, many similar solutions.
7
- Yet, none seem to provide so many options, but I may be biased ;)
8
10
 
9
11
  == Quick start
10
12
 
11
13
  Add this to a model:
12
14
 
13
15
  class User < ActiveRecord::Base
14
- as_enum :gender, {:female => 1, :male => 0}
16
+ as_enum :gender, :female => 1, :male => 0
15
17
  end
16
18
 
17
19
  Then create the new column using migrations:
@@ -36,9 +38,9 @@ any fancy metaclass or similar.
36
38
  jane.male? # => false
37
39
  jane.gender # => :female
38
40
  jane.gender_cd # => 1
39
-
41
+
40
42
  Easily switch to another value using the bang methods.
41
-
43
+
42
44
  joe = User.new
43
45
  joe.male! # => :male
44
46
  joe.gender # => :male
@@ -68,11 +70,11 @@ useful when creating queries, displaying option elements or similar:
68
70
  class User < ActiveRecord::Base
69
71
  as_enum :gender, [:male, :female], :column => 'sex'
70
72
  end
71
-
73
+
72
74
  * To make it easier to create dropdowns with values use:
73
75
 
74
76
  <%= select(:user, :gender, User.genders.keys) %>
75
-
77
+
76
78
  * It's possible to validate the internal enum values, just like any other ActiveRecord validation:
77
79
 
78
80
  class User < ActiveRecord::Base
@@ -85,7 +87,7 @@ useful when creating queries, displaying option elements or similar:
85
87
  instead the value of <tt>@user.gender_cd</tt>.
86
88
  * If the shortcut methods (like <tt><symbol>?</tt>, <tt><symbol>!</tt> or <tt>Klass.<symbol></tt>) conflict with something in your class, it's possible to
87
89
  define a prefix:
88
-
90
+
89
91
  class User < ActiveRecord::Base
90
92
  as_enum :gender, [:male, :female], :prefix => true
91
93
  end
@@ -93,43 +95,43 @@ useful when creating queries, displaying option elements or similar:
93
95
  jane = User.new :gender => :female
94
96
  jane.gender_female? # => true
95
97
  User.gender_female # => 1, this also works on the class methods
96
-
98
+
97
99
  The <tt>:prefix</tt> option not only takes a boolean value as an argument, but instead can also be supplied a custom
98
100
  prefix (i.e. any string or symbol), so with <tt>:prefix => 'foo'</tt> all shortcut methods would look like: <tt>foo_<symbol>...</tt>
99
101
  *Note*: if the <tt>:slim => true</tt> is defined, this option has no effect whatsoever (because no shortcut methods are generated).
100
102
  * Sometimes it might be useful to disable the generation of the shortcut methods (<tt><symbol>?</tt>, <tt><symbol>!</tt> and <tt>Klass.<symbol></tt>), to do so just add the option <tt>:slim => true</tt>:
101
-
103
+
102
104
  class User < ActiveRecord::Base
103
105
  as_enum :gender, [:male, :female], :slim => true
104
106
  end
105
107
 
106
108
  jane = User.new :gender => :female
107
- jane.female? # => throws NoMethodError: undefined method `female?'
108
- User.male # => throws NoMethodError: undefined method `male'
109
-
109
+ jane.female? # => throws NoMethodError: undefined method `female?'
110
+ User.male # => throws NoMethodError: undefined method `male'
111
+
110
112
  Yet the setter and getter for <tt>gender</tt>, as well as the <tt>User.genders</tt> methods are still available, only all shortcut
111
113
  methods for each of the enumeration values are not generated.
112
-
114
+
113
115
  It's also possible to set <tt>:slim => :class</tt> which only disables the generation of any class-level shortcut method, because those
114
116
  are also available via the enhanced enumeration hash:
115
-
117
+
116
118
  class Message < ActiveRecord::Base
117
- as_enum :status, { :unread => 0, :read => 1, :archived => 99}, :slim => :class
119
+ as_enum :status, { :unread => 0, :read => 1, :archived => 99 }, :slim => :class
118
120
  end
119
-
121
+
120
122
  msg = Message.new :body => 'Hello World!', status_cd => 0
121
123
  msg.read? # => false; shortuct methods on instance are still enabled
122
124
  msg.status # => :unread
123
125
  Message.unread # => throws NoMethodError: undefined method `unread`
124
126
  Message.statuses.unread # => 0
125
127
  Message.statuses.unread(true) # => :unread
126
-
128
+
127
129
  # or useful for IN queries
128
130
  Messages.statuses(:unread, :read) # => [0, 1]
129
-
131
+
130
132
  * As a default an <tt>ArgumentError</tt> is raised if the user tries to set the field to an invalid enumeration value, to change this
131
133
  behaviour use the <tt>:whiny</tt> option:
132
-
134
+
133
135
  class User < ActiveRecord::Base
134
136
  as_enum :gender, [:male, :female], :whiny => false
135
137
  end
@@ -138,10 +140,20 @@ useful when creating queries, displaying option elements or similar:
138
140
 
139
141
  # on the gender field
140
142
  <%= select("user", "gender", User.genders_for_select) %>
141
-
143
+
142
144
  # or on the '_cd' field
143
145
  <%= select("user", "gender_cd", User.genders_for_select(:value))
144
146
 
147
+ Translations need to be stored like:
148
+
149
+ de:
150
+ activerecord:
151
+ enums:
152
+ user: # the Model, as User.class.name.underscore
153
+ genders: # pluralized version of :gender
154
+ male: männlich
155
+ female: weiblich
156
+
145
157
  * To define any option globally, like setting <tt>:whiny</tt> to +false+, or globally enable <tt>:prefix</tt>; all default options
146
158
  are stored in <tt>SimpleEnum.default_options</tt>, this hash can be easily changed in your initializers or wherever:
147
159
 
@@ -152,38 +164,38 @@ useful when creating queries, displaying option elements or similar:
152
164
 
153
165
  Searching for certain values by using the finder methods:
154
166
 
155
- User.find :all, :conditions => { :gender_cd => User.female }
156
-
167
+ User.where(:gender_cd => User.female)
168
+
157
169
  Working with database backed values, now assuming that there exists a +genders+ table:
158
170
 
159
171
  class Person < ActiveRecord::Base
160
- as_enum :gender, Gender.find(:all).map { |g| [g.name.to_sym, g.id] } # map to array of symbols
172
+ as_enum :gender, Gender.all.map { |g| [g.name.to_sym, g.id] } # map to array of symbols
161
173
  end
162
-
174
+
163
175
  Working with object backed values, the only requirement to enable this is that <em>a)</em> either a field name +name+ exists
164
176
  or <em>b)</em> a custom method to convert an object to a symbolized form named +to_enum_sym+ (for general uses overriding
165
177
  +to_enum+ is perfectly fine) exists:
166
178
 
167
179
  class Status < ActiveRecord::Base
168
180
  # this has a column named :name
169
- STATUSES = self.find(:all, :order => :name)
181
+ STATUSES = self.order(:name)
170
182
  end
171
-
183
+
172
184
  class BankTransaction < ActiveRecord::Base
173
- as_enum :status, Status.STATUSES
185
+ as_enum :status, Status::STATUSES
174
186
  end
175
-
187
+
176
188
  # what happens now? the id's of Status now serve as enumeration key and the
177
- # Status object as the value so...
189
+ # Status object as the value so...
178
190
  t = BankTransaction.new
179
191
  t.pending!
180
192
  t.status # => #<Status id: 1, name: "pending">
181
-
193
+
182
194
  # and it's also possible to access the objects/values using:
183
195
  BankTransaction.statuses(:pending) # => 1, access by symbol (not) the object!
184
196
  BankTransaction.statuses.pending # => 1
185
197
  BankTransaction.statuses.pending(true) # => #<Status id: 1, name: "pending">
186
-
198
+
187
199
  == Known issues/Open items
188
200
 
189
201
  * Maybe the <tt>:whiny</tt> option should default to <tt>false</tt>, so that generally no exceptions are thrown if a user fakes a request?
@@ -192,10 +204,11 @@ or <em>b)</em> a custom method to convert an object to a symbolized form named +
192
204
 
193
205
  == Contributors
194
206
 
195
- * dmitry - bugfixes and other improvements
196
- * tarsolya - implemented all the ruby 1.9 and rails 3 goodness!
197
- * dbalatero - rails 2.3.5 bugfix
198
- * johnthethird - feature for <tt>_for_select</tt> to return the values
207
+ * @dmitry - bugfixes and other improvements
208
+ * @tarsolya - implemented all the ruby 1.9 and rails 3 goodness!
209
+ * @dbalatero - rails 2.3.5 bugfix & validator fixes
210
+ * @johnthethird - feature for <tt>_for_select</tt> to return the values
211
+ * @sinsiliux - ruby 1.9 fixes and removed AR dependency
199
212
 
200
213
  == Licence & Copyright
201
- Copyright (c) 2009 by Lukas Westermann, Licenced under MIT Licence (see LICENCE file)
214
+ Copyright (c) 2011 by Lukas Westermann, Licenced under MIT Licence (see LICENCE file)
data/Rakefile CHANGED
@@ -1,9 +1,10 @@
1
- require 'rake'
1
+ require 'rubygems'
2
+ require 'bundler'
2
3
  require 'rake/testtask'
3
- require 'rake/rdoctask'
4
4
 
5
- # kinda ensure simple_enum is on load path
6
- $: << File.join(File.dirname(__FILE__), 'lib')
5
+ include Rake::DSL
6
+
7
+ Bundler::GemHelper.install_tasks
7
8
 
8
9
  desc 'Default: run unit tests.'
9
10
  task :default => :test
@@ -12,68 +13,6 @@ desc 'Test the simple_enum plugin.'
12
13
  Rake::TestTask.new(:test) do |t|
13
14
  t.libs << 'lib'
14
15
  t.libs << 'test'
15
- t.pattern = 'test/**/*_test.rb'
16
+ t.test_files = Dir.glob('test/**/*_test.rb')
16
17
  t.verbose = true
17
18
  end
18
-
19
- desc 'Generate documentation for the simple_enum plugin (results in doc/).'
20
- Rake::RDocTask.new(:rdoc) do |rdoc|
21
- rdoc.rdoc_dir = 'doc'
22
- rdoc.title = 'SimpleEnum'
23
- rdoc.options << '--line-numbers' << '--inline-source'
24
- rdoc.rdoc_files.include('README.rdoc')
25
- rdoc.rdoc_files.include('lib/**/*.rb')
26
- rdoc.rdoc_files.include('LICENCE');
27
- end
28
-
29
- begin
30
- require 'jeweler'
31
- Jeweler::Tasks.new do |gemspec|
32
- require 'rubygems'
33
- require 'simple_enum'
34
-
35
- gemspec.name = "simple_enum"
36
- gemspec.version = SimpleEnum::VERSION
37
- gemspec.summary = "Simple enum-like field support for ActiveRecord (including validations and i18n)"
38
- gemspec.email = "lukas.westermann@gmail.com"
39
- gemspec.homepage = "http://github.com/lwe/simple_enum"
40
- gemspec.authors = ["Lukas Westermann"] # ask & add "Dmitry Polushkin"
41
-
42
- gemspec.files.reject! { |file| file =~ /\.gemspec$/ } # kinda redundant
43
- end
44
- Jeweler::GemcutterTasks.new
45
- rescue LoadError
46
- puts "Jeweler not available. Install it with: sudo gem install jeweler"
47
- end
48
-
49
- namespace :metrics do
50
- desc 'Report code statistics for library and tests to shell.'
51
- task :stats do |t|
52
- require 'code_statistics'
53
- dirs = {
54
- 'Libraries' => 'lib',
55
- 'Unit tests' => 'test'
56
- }.map { |name,dir| [name, File.join(File.dirname(__FILE__), dir)] }
57
- CodeStatistics.new(*dirs).to_s
58
- end
59
-
60
- desc 'Report code coverage to HTML (doc/coverage) and shell (requires rcov).'
61
- task :coverage do |t|
62
- rm_f "doc/coverage"
63
- mkdir_p "doc/coverage"
64
- rcov = %(rcov -Ilib:test --exclude '\/gems\/' -o doc/coverage -T test/*_test.rb )
65
- system rcov
66
- end
67
- end
68
-
69
- desc 'Start IRB console with loaded test/test_helper.rb and sqlite db.'
70
- task :console do |t|
71
- chdir File.dirname(__FILE__)
72
- exec 'irb -Ilib/ -r test/test_helper'
73
- end
74
-
75
- desc 'Clean up generated files.'
76
- task :clean do |t|
77
- FileUtils.rm_rf "doc"
78
- FileUtils.rm_rf "pkg"
79
- end
data/lib/simple_enum.rb CHANGED
@@ -9,7 +9,6 @@
9
9
  # See the +as_enum+ documentation for more details.
10
10
 
11
11
  # because we depend on AR and i18n
12
- require 'active_record'
13
12
  require 'i18n'
14
13
 
15
14
  require 'simple_enum/enum_hash'
@@ -20,11 +19,8 @@ require 'simple_enum/validation'
20
19
  # of +SimpleEnum::ClassMethods+ for more details.
21
20
  module SimpleEnum
22
21
 
23
- # +SimpleEnum+ version string.
24
- VERSION = "1.3.2".freeze
25
-
26
22
  class << self
27
-
23
+
28
24
  # Provides configurability to SimpleEnum, allows to override some defaults which are
29
25
  # defined for all uses of +as_enum+. Most options from +as_enum+ are available, such as:
30
26
  # * <tt>:prefix</tt> - Define a prefix, which is prefixed to the shortcut methods (e.g. <tt><symbol>!</tt> and
@@ -41,15 +37,18 @@ module SimpleEnum
41
37
  @default_options ||= {
42
38
  :whiny => true,
43
39
  :upcase => false
44
- }
40
+ }
45
41
  end
46
-
42
+
47
43
  def included(base) #:nodoc:
44
+ base.send :class_attribute, :enum_definitions, :instance_write => false, :instance_reader => false
45
+ base.enum_definitions = {}
48
46
  base.send :extend, ClassMethods
49
47
  end
50
48
  end
51
-
49
+
52
50
  module ClassMethods
51
+
53
52
  # Provides ability to create simple enumerations based on hashes or arrays, backed
54
53
  # by integer columns (but not limited to integer columns).
55
54
  #
@@ -58,7 +57,7 @@ module SimpleEnum
58
57
  #
59
58
  # add_column :users, :gender_cd, :integer
60
59
  # add_column :users, :status, :integer # and a custom column...
61
- #
60
+ #
62
61
  # and then in your model:
63
62
  #
64
63
  # class User < ActiveRecord::Base
@@ -80,7 +79,7 @@ module SimpleEnum
80
79
  # john_doe.gender_cd # => 0
81
80
  #
82
81
  # And to make life a tad easier: a few shortcut methods to work with the enumeration are also created.
83
- #
82
+ #
84
83
  # john_doe.male? # => true
85
84
  # john_doe.female? # => false
86
85
  # john_doe.female! # => :female (set's gender to :female => gender_cd = 1)
@@ -127,12 +126,12 @@ module SimpleEnum
127
126
  #
128
127
  # class Address < ActiveRecord::Base
129
128
  # as_enum :canton, {:aargau => 'ag', ..., :wallis => 'vs', :zug => 'zg', :zurich => 'zh'}, :slim => true
130
- # end
129
+ # end
131
130
  #
132
131
  # home = Address.new(:canton => :zurich, :street => 'Bahnhofstrasse 1', ...)
133
132
  # home.canton # => :zurich
134
133
  # home.canton_cd # => 'zh'
135
- # home.aargau! # throws NoMethodError: undefined method `aargau!'
134
+ # home.aargau! # throws NoMethodError: undefined method `aargau!'
136
135
  # Address.aargau # throws NoMethodError: undefined method `aargau`
137
136
  #
138
137
  # This is especially useful if there are (too) many enumeration values, or these shortcut methods
@@ -155,56 +154,60 @@ module SimpleEnum
155
154
  options.assert_valid_keys(:column, :whiny, :prefix, :slim, :upcase)
156
155
 
157
156
  metaclass = (class << self; self; end)
158
-
157
+
159
158
  # convert array to hash...
160
159
  values = SimpleEnum::EnumHash.new(values)
161
160
  values_inverted = values.invert
162
-
161
+
163
162
  # store info away
164
- write_inheritable_attribute(:enum_definitions, {}) if enum_definitions.nil?
165
- enum_definitions[enum_cd] = enum_definitions[options[:column]] = { :name => enum_cd, :column => options[:column], :options => options }
166
-
167
- # generate getter
163
+ self.enum_definitions = {} if self.enum_definitions.nil?
164
+ self.enum_definitions[enum_cd] = self.enum_definitions[options[:column]] = { :name => enum_cd, :column => options[:column], :options => options }
165
+
166
+ # generate getter
168
167
  define_method("#{enum_cd}") do
169
168
  id = read_attribute options[:column]
170
169
  values_inverted[id]
171
170
  end
172
-
171
+
173
172
  # generate setter
174
173
  define_method("#{enum_cd}=") do |new_value|
175
174
  v = new_value.blank? ? nil : values[new_value.to_sym]
176
175
  raise(ArgumentError, "Invalid enumeration value: #{new_value}") if (options[:whiny] and v.nil? and !new_value.blank?)
177
176
  write_attribute options[:column], v
178
177
  end
179
-
180
- # allow access to defined values hash, e.g. in a select helper or finder method.
178
+
179
+ # allow access to defined values hash, e.g. in a select helper or finder method.
181
180
  attr_name = enum_cd.to_s.pluralize
182
181
  enum_attr = :"#{attr_name.downcase}_enum_hash"
183
- write_inheritable_attribute(enum_attr, values)
184
-
182
+
185
183
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
184
+ class_attribute #{enum_attr.inspect}, :instance_write => false, :instance_reader => false
185
+
186
186
  def self.#{attr_name}(*args)
187
- return read_inheritable_attribute(#{enum_attr.inspect}) if args.first.nil?
188
- return read_inheritable_attribute(#{enum_attr.inspect})[args.first] if args.size == 1
189
- args.inject([]) { |ary, sym| ary << read_inheritable_attribute(#{enum_attr.inspect})[sym]; ary }
187
+ return #{enum_attr} if args.first.nil?
188
+ return #{enum_attr}[args.first] if args.size == 1
189
+ args.inject([]) { |ary, sym| ary << #{enum_attr}[sym]; ary }
190
190
  end
191
-
191
+
192
192
  def self.#{attr_name}_for_select(attr = :key, &block)
193
193
  self.#{attr_name}.map do |k,v|
194
194
  [block_given? ? yield(k,v) : self.human_enum_name(#{attr_name.inspect}, k), attr == :value ? v : k]
195
195
  end
196
196
  end
197
197
  RUBY
198
-
198
+
199
+ # write values
200
+ self.send "#{enum_attr}=", values
201
+
199
202
  # only create if :slim is not defined
200
203
  if options[:slim] != true
201
204
  # create both, boolean operations and *bang* operations for each
202
205
  # enum "value"
203
206
  prefix = options[:prefix] && "#{options[:prefix] == true ? enum_cd : options[:prefix]}_"
204
-
207
+
205
208
  values.each do |k,code|
206
209
  sym = k.to_enum_sym
207
-
210
+
208
211
  define_method("#{prefix}#{sym}?") do
209
212
  code == read_attribute(options[:column])
210
213
  end
@@ -212,7 +215,7 @@ module SimpleEnum
212
215
  write_attribute options[:column], code
213
216
  sym
214
217
  end
215
-
218
+
216
219
  # allow class access to each value
217
220
  unless options[:slim] === :class
218
221
  metaclass.send(:define_method, "#{prefix}#{sym}", Proc.new { |*args| args.first ? k : code })
@@ -222,7 +225,7 @@ module SimpleEnum
222
225
  end
223
226
 
224
227
  include Validation
225
-
228
+
226
229
  def human_enum_name(enum, key, options = {})
227
230
  klasses = self.respond_to?(:descendants) ? descendants : ancestors
228
231
  defaults = ([self] + klasses).map { |klass| :"#{klass.name.underscore}.#{enum}.#{key}" }
@@ -232,18 +235,11 @@ module SimpleEnum
232
235
  options[:count] ||= 1
233
236
  I18n.translate(defaults.shift, options.merge(:default => defaults.flatten, :scope => [:activerecord, :enums]))
234
237
  end
235
-
236
- protected
237
- # Returns enum definitions as defined by each call to
238
- # +as_enum+.
239
- def enum_definitions
240
- read_inheritable_attribute(:enum_definitions)
241
- end
242
238
  end
243
239
  end
244
240
 
245
241
  # Tie stuff together and load translations if ActiveRecord is defined
246
242
  Object.send(:include, SimpleEnum::ObjectSupport)
247
-
248
- ActiveRecord::Base.send(:include, SimpleEnum)
243
+
244
+ ActiveRecord::Base.send(:include, SimpleEnum) if defined?(ActiveRecord)
249
245
  I18n.load_path << File.join(File.dirname(__FILE__), '..', 'locales', 'en.yml')
@@ -1,6 +1,5 @@
1
1
  module SimpleEnum
2
2
  module Validation
3
-
4
3
  # Validates an +as_enum+ field based on the value of it's column.
5
4
  #
6
5
  # Model:
@@ -14,7 +13,7 @@ module SimpleEnum
14
13
  #
15
14
  # Configuration options:
16
15
  # * <tt>:message</tt> - A custom error message (default: is <tt>[:activerecord, :errors, :messages, :invalid_enum]</tt>).
17
- # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
16
+ # * <tt>:on</tt> - Specifies when this validation is active (default is always, other options <tt>:create</tt>, <tt>:update</tt>).
18
17
  # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
19
18
  # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
20
19
  # method, proc or string should return or evaluate to a true or false value.
@@ -22,15 +21,18 @@ module SimpleEnum
22
21
  # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
23
22
  # method, proc or string should return or evaluate to a true or false value.
24
23
  def validates_as_enum(*attr_names)
25
- @configuration = { :on => :save }
26
- @configuration.update(attr_names.extract_options!)
24
+ configuration = attr_names.extract_options!
25
+
27
26
  attr_names.map! { |e| enum_definitions[e][:column] } # map to column name
28
- validates_each(attr_names) do |record, attr_name, value|
27
+
28
+ validates_each(attr_names, configuration) do |record, attr_name, value|
29
29
  enum_def = enum_definitions[attr_name]
30
30
  unless send(enum_def[:name].to_s.pluralize).values.include?(value)
31
- record.errors.add(enum_def[:name], :invalid_enum, :default => @configuration[:message], :value => value)
31
+ params = { :value => value}
32
+ params[:default] = configuration[:message] if configuration[:message].present?
33
+ record.errors.add(enum_def[:name], :invalid_enum, params)
32
34
  end
33
35
  end
34
36
  end
35
37
  end
36
- end
38
+ end
@@ -0,0 +1,5 @@
1
+ module SimpleEnum
2
+
3
+ # +SimpleEnum+ version string.
4
+ VERSION = "1.4.0"
5
+ end
data/locales/en.yml CHANGED
@@ -4,11 +4,3 @@ en:
4
4
  errors:
5
5
  messages:
6
6
  invalid_enum: invalid option supplied.
7
- enums:
8
- dummy:
9
- genders:
10
- female: Girl
11
- didums:
12
- foo:
13
- one: Foo
14
- other: Foos
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "simple_enum/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "simple_enum"
7
+ s.version = SimpleEnum::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = "Simple enum-like field support for active records."
10
+ s.description = ""
11
+
12
+ s.required_ruby_version = ">= 1.8.7"
13
+ s.required_rubygems_version = ">= 1.3.6"
14
+
15
+ s.authors = ["Lukas Westermann"]
16
+ s.email = ["lukas.westermann@gmail.com"]
17
+ s.homepage = "http://github.com/lwe/simple_enum"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.require_path = 'lib'
22
+
23
+ s.license = 'MIT'
24
+
25
+ s.add_dependency "activesupport", "~> 3.0"
26
+
27
+ s.add_development_dependency 'rake', '>= 0.8.7'
28
+ s.add_development_dependency 'activerecord', '~> 3.0'
29
+ s.add_development_dependency 'mongoid', '~> 2.0'
30
+
31
+ unless RUBY_PLATFORM =~ /java/
32
+ s.add_development_dependency 'sqlite3'
33
+ else
34
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter'
35
+ end
36
+ end
data/test/locales.yml ADDED
@@ -0,0 +1,17 @@
1
+ # english translation
2
+ en:
3
+ activerecord:
4
+ errors:
5
+ models:
6
+ simple_enum_test/validated_computer:
7
+ attributes:
8
+ operating_system:
9
+ invalid_enum: y u no os?
10
+ enums:
11
+ dummy:
12
+ genders:
13
+ female: Girl
14
+ didums:
15
+ foo:
16
+ one: Foo
17
+ other: Foos
data/test/models.rb CHANGED
@@ -1,12 +1,17 @@
1
1
  class Dummy < ActiveRecord::Base
2
2
  as_enum :gender, [:male, :female]
3
3
  as_enum :word, { :alpha => 'alpha', :beta => 'beta', :gamma => 'gamma'}
4
- as_enum :didum, [ :foo, :bar, :foobar ], :column => 'other'
4
+ as_enum :didum, [ :foo, :bar, :foobar ], :column => 'other'
5
5
  end
6
6
 
7
7
  class Gender < ActiveRecord::Base
8
8
  end
9
9
 
10
+ class Computer < ActiveRecord::Base
11
+ as_enum :manufacturer, [:dell, :compaq, :apple]
12
+ as_enum :operating_system, [:windows, :osx, :linux, :bsd]
13
+ end
14
+
10
15
  # Used to test STI stuff
11
16
  class SpecificDummy < Dummy
12
17
  set_table_name 'dummies'
@@ -1,3 +1,5 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
1
3
  class ObjectSupportTest < ActiveSupport::TestCase
2
4
 
3
5
  test "ensure that symbols stay symbols" do
@@ -24,4 +26,4 @@ class ObjectSupportTest < ActiveSupport::TestCase
24
26
 
25
27
  assert_same :contains_spaces, another_named.to_enum_sym
26
28
  end
27
- end
29
+ end
@@ -1,35 +1,39 @@
1
1
  require 'test_helper'
2
2
 
3
- class SimpleEnumTest < ActiveSupport::TestCase
3
+ class SimpleEnumTest < ActiveSupport::TestCase
4
4
  def setup
5
5
  reload_db
6
6
  end
7
-
7
+
8
+ test "reading public enum_definitions" do
9
+ assert_equal "gender_cd", Dummy.enum_definitions[:gender][:column]
10
+ end
11
+
8
12
  test "get the correct integer values when setting to symbol" do
9
13
  d = Dummy.new
10
14
  d.gender = :male
11
15
  assert_equal(0, d.gender_cd)
12
16
  end
13
-
17
+
14
18
  test "get the correct symbol when setting the integer value" do
15
19
  d = Dummy.new
16
20
  d.gender_cd = 1
17
21
  assert_equal(:female, d.gender)
18
22
  end
19
-
23
+
20
24
  test "verify that <symbol>? returns correct result" do
21
25
  d = Dummy.new
22
26
  d.gender = :male
23
27
  assert_equal(true, d.male?)
24
28
  assert_equal(false, d.female?)
25
29
  end
26
-
30
+
27
31
  test "get symbol when rows are fetched from db" do
28
32
  # Anna
29
33
  assert_equal(:female, Dummy.find(1).gender)
30
34
  assert_equal(:alpha, Dummy.find(1).word)
31
35
  assert_equal(:foo, Dummy.find(1).didum)
32
-
36
+
33
37
  # Bella
34
38
  assert_equal(true, Dummy.find(2).female?)
35
39
  assert_equal(true, Dummy.find(2).beta?)
@@ -38,58 +42,160 @@ class SimpleEnumTest < ActiveSupport::TestCase
38
42
  # Chris
39
43
  assert_equal(false, Dummy.find(3).female?)
40
44
  assert_equal(:gamma, Dummy.find(3).word)
41
- assert_equal(:foobar, Dummy.find(3).didum)
45
+ assert_equal(:foobar, Dummy.find(3).didum)
42
46
  end
43
-
47
+
44
48
  test "create and save new record then test symbols" do
45
49
  d = Dummy.create({ :name => 'Dummy', :gender_cd => 0 }) # :gender => male
46
50
  assert_equal(true, d.male?)
47
-
51
+
48
52
  # change :gender_cd to 1
49
53
  d.female!
50
- d.save!
54
+ d.save!
51
55
  assert_equal(true, Dummy.find(d.id).female?)
52
56
  end
53
-
54
- test "add validation and test validations" do
55
- Dummy.class_eval { validates_as_enum :gender }
56
-
57
- d = Dummy.new :gender_cd => 5 # invalid number :)
58
- assert_equal(false, d.save)
59
- d.gender_cd = 1
60
- assert_equal(true, d.save)
61
- assert_equal(:female, d.gender)
57
+
58
+ test "validation :if" do
59
+ class ValidateIfComputer < Computer
60
+ set_table_name 'computers'
61
+
62
+ validates_as_enum :manufacturer, :if => lambda { |computer|
63
+ computer.name == "Fred"
64
+ }
65
+ end
66
+
67
+ computer = ValidateIfComputer.new(:manufacturer_cd => 48328432)
68
+
69
+ computer.name = nil
70
+ assert_equal(true, computer.save)
71
+
72
+ computer.name = "Fred"
73
+ assert_equal(false, computer.save)
74
+ end
75
+
76
+ test "validation :unless" do
77
+ class ValidateUnlessComputer < Computer
78
+ set_table_name 'computers'
79
+
80
+ validates_as_enum :manufacturer, :unless => lambda { |computer|
81
+ computer.name == "Unless"
82
+ }
83
+ end
84
+
85
+ computer = ValidateUnlessComputer.new(:manufacturer_cd => 48328432)
86
+
87
+ computer.name = nil
88
+ assert_equal(false, computer.save)
89
+ assert_equal(1, computer.errors[:manufacturer].size)
90
+
91
+ computer.name = "Unless"
92
+ assert_equal(true, computer.save)
93
+ end
94
+
95
+ test "validation :on => :update" do
96
+ class ValidateOnUpdateComputer < Computer
97
+ set_table_name 'computers'
98
+
99
+ validates_as_enum :manufacturer, :on => :update
100
+ end
101
+
102
+ computer = ValidateOnUpdateComputer.new(:manufacturer_cd => nil)
103
+ assert_equal(true, computer.save)
104
+
105
+ computer.name = 'Something else'
106
+ assert_equal(false, computer.save)
107
+ assert_equal(1, computer.errors[:manufacturer].size)
108
+ end
109
+
110
+ test "validation :on => :create" do
111
+ class ValidateOnCreateComputer < Computer
112
+ set_table_name 'computers'
113
+
114
+ validates_as_enum :manufacturer, :on => :create
115
+ end
116
+
117
+ computer = ValidateOnCreateComputer.new(:manufacturer_cd => nil)
118
+ assert_equal(false, computer.save)
119
+ assert_equal(1, computer.errors[:manufacturer].size)
120
+
121
+ computer.manufacturer = :apple
122
+ assert_equal(true, computer.save)
123
+
124
+ computer.manufacturer = nil
125
+ assert_equal(true, computer.save)
126
+ end
127
+
128
+ test "validation :allow_nil" do
129
+ class ValidateAllowNilComputer < Computer
130
+ set_table_name 'computers'
131
+
132
+ validates_as_enum :manufacturer, :allow_nil => true
133
+ end
134
+
135
+ computer = ValidateAllowNilComputer.new(:manufacturer_cd => nil)
136
+ assert_equal(true, computer.save)
137
+
138
+ computer.manufacturer = :apple
139
+ assert_equal(true, computer.save)
140
+
141
+ computer.manufacturer_cd = 84321483219
142
+ assert_equal(false, computer.save)
143
+ assert_equal(1, computer.errors[:manufacturer].size)
62
144
  end
63
-
145
+
146
+ test "default error messages using translations" do
147
+ class ValidatedComputer < Computer
148
+ set_table_name 'computers'
149
+ validates_as_enum :manufacturer
150
+ validates_as_enum :operating_system
151
+ end
152
+
153
+ computer = ValidatedComputer.new
154
+ assert !computer.save, "save should return false"
155
+ assert_equal "invalid option supplied.", computer.errors[:manufacturer].first
156
+ assert_equal "y u no os?", computer.errors[:operating_system].first
157
+ end
158
+
159
+ test "allow setting custom error via :message" do
160
+ class ValidateMessageComputer < Computer
161
+ set_table_name 'computers'
162
+ validates_as_enum :manufacturer, :message => "invalid manufacturer"
163
+ end
164
+
165
+ computer = ValidateMessageComputer.new
166
+ assert !computer.valid?, "valid? should return false"
167
+ assert_equal "invalid manufacturer", computer.errors[:manufacturer].first
168
+ end
169
+
64
170
  test "raises ArgumentError if invalid symbol is passed" do
65
171
  assert_raise ArgumentError do
66
172
  Dummy.new :gender => :foo
67
173
  end
68
174
  end
69
-
175
+
70
176
  test "raises NO ArgumentError if :whiny => false is defined" do
71
177
  not_whiny = Class.new(Dummy) do
72
178
  as_enum :gender, [:male, :female], :whiny => false
73
179
  end
74
-
180
+
75
181
  d = not_whiny.new :gender => :foo
76
182
  assert_nil(d.gender)
77
183
  d.gender = ''
78
184
  assert_nil(d.gender)
79
185
  end
80
-
186
+
81
187
  test "ensure that setting to 'nil' works if :whiny => true and :whiny => false" do
82
- d = Dummy.new :gender => :male
188
+ d = Dummy.new :gender => :male
83
189
  assert_equal(:male, d.gender)
84
190
  d.gender = nil
85
191
  assert_nil(d.gender)
86
192
  d.gender = ''
87
193
  assert_nil(d.gender)
88
-
194
+
89
195
  not_whiny_again = Class.new(Dummy) do
90
196
  as_enum :gender, [:male, :female], :whiny => false
91
197
  end
92
-
198
+
93
199
  d = not_whiny_again.new :gender => :male
94
200
  assert_equal(:male, d.gender)
95
201
  d.gender = nil
data/test/test_helper.rb CHANGED
@@ -12,25 +12,23 @@ require 'active_record'
12
12
  require 'active_support/version'
13
13
  require 'active_record/version'
14
14
 
15
- # setup fake rails env
16
- ROOT = File.join(File.dirname(__FILE__), '..')
17
- RAILS_ROOT = ROOT
18
- RAILS_ENV = 'test'
19
-
20
15
  # create database connection (in memory db!)
21
16
  ActiveRecord::Base.establish_connection({
22
17
  :adapter => RUBY_PLATFORM =~ /java/ ? 'jdbcsqlite3' : 'sqlite3',
23
18
  :database => ':memory:'})
24
19
 
25
20
  # load simple_enum
26
- require File.join(ROOT, 'init')
21
+ require 'simple_enum'
27
22
 
28
23
  # load dummy class
29
- require File.join(ROOT, 'test', 'models')
24
+ require File.join(File.dirname(__FILE__), 'models')
30
25
 
31
26
  # Test environment info
32
27
  puts "Testing against: activesupport-#{ActiveSupport::VERSION::STRING}, activerecord-#{ActiveRecord::VERSION::STRING}"
33
28
 
29
+ # Add test locales
30
+ I18n.load_path << File.join(File.dirname(__FILE__), 'locales.yml')
31
+
34
32
  # Reload database
35
33
  def reload_db(options = {})
36
34
  options = { :fill => true, :genders => false }.merge(options)
@@ -40,24 +38,30 @@ def reload_db(options = {})
40
38
  t.column :word_cd, :string, :limit => 5
41
39
  t.column :other, :integer
42
40
  end
43
-
41
+
42
+ ActiveRecord::Base.connection.create_table :computers, :force => true do |t|
43
+ t.column :name, :string
44
+ t.column :operating_system_cd, :integer
45
+ t.column :manufacturer_cd, :integer
46
+ end
47
+
44
48
  # Create ref-data table and fill with records
45
49
  ActiveRecord::Base.connection.create_table :genders, :force => true do |t|
46
50
  t.column :name, :string
47
- end
48
-
51
+ end
52
+
49
53
  if options[:fill]
50
54
  # fill db with some rows
51
55
  Dummy.create({ :name => 'Anna', :gender_cd => 1, :word_cd => 'alpha', :other => 0})
52
56
  Dummy.create({ :name => 'Bella', :gender_cd => 1, :word_cd => 'beta', :other => 1})
53
57
  Dummy.create({ :name => 'Chris', :gender_cd => 0, :word_cd => 'gamma', :other => 2})
54
58
  end
55
-
56
- if options[:genders]
59
+
60
+ if options[:genders]
57
61
  male = Gender.new({ :name => 'male' })
58
62
  male.id = 0;
59
63
  male.save!
60
-
64
+
61
65
  female = Gender.new({ :name => 'female' })
62
66
  female.id = 1;
63
67
  female.save!
@@ -68,6 +72,6 @@ end
68
72
  if Object.const_defined?('IRB')
69
73
  reload_db :fill => true, :genders => true
70
74
  else # and load test classes when in test cases...
71
- require 'test/unit'
75
+ require 'test/unit'
72
76
  require 'active_support/test_case'
73
- end
77
+ end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_enum
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
5
- prerelease:
4
+ prerelease: false
6
5
  segments:
7
6
  - 1
8
- - 3
9
- - 2
10
- version: 1.3.2
7
+ - 4
8
+ - 0
9
+ version: 1.4.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Lukas Westermann
@@ -15,37 +14,102 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2011-03-08 00:00:00 +01:00
17
+ date: 2011-09-09 00:00:00 +02:00
19
18
  default_executable:
20
- dependencies: []
21
-
22
- description:
23
- email: lukas.westermann@gmail.com
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activesupport
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 3
29
+ - 0
30
+ version: "3.0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ - 8
43
+ - 7
44
+ version: 0.8.7
45
+ type: :development
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: activerecord
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 3
56
+ - 0
57
+ version: "3.0"
58
+ type: :development
59
+ version_requirements: *id003
60
+ - !ruby/object:Gem::Dependency
61
+ name: mongoid
62
+ prerelease: false
63
+ requirement: &id004 !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 2
69
+ - 0
70
+ version: "2.0"
71
+ type: :development
72
+ version_requirements: *id004
73
+ - !ruby/object:Gem::Dependency
74
+ name: sqlite3
75
+ prerelease: false
76
+ requirement: &id005 !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ type: :development
84
+ version_requirements: *id005
85
+ description: ""
86
+ email:
87
+ - lukas.westermann@gmail.com
24
88
  executables: []
25
89
 
26
90
  extensions: []
27
91
 
28
- extra_rdoc_files:
29
- - README.rdoc
92
+ extra_rdoc_files: []
93
+
30
94
  files:
31
95
  - .gitignore
96
+ - .travis.yml
32
97
  - Gemfile
33
- - Gemfile.BACKPORT
34
- - Gemfile.BACKPORT.lock
35
- - Gemfile.lock
36
98
  - LICENCE
37
99
  - README.rdoc
38
100
  - Rakefile
39
- - init.rb
40
101
  - lib/simple_enum.rb
41
102
  - lib/simple_enum/enum_hash.rb
42
103
  - lib/simple_enum/object_support.rb
43
104
  - lib/simple_enum/validation.rb
105
+ - lib/simple_enum/version.rb
44
106
  - locales/en.yml
107
+ - simple_enum.gemspec
45
108
  - test/array_conversions_test.rb
46
109
  - test/class_methods_test.rb
47
110
  - test/enum_hash_test.rb
48
111
  - test/finders_test.rb
112
+ - test/locales.yml
49
113
  - test/models.rb
50
114
  - test/object_backed_test.rb
51
115
  - test/object_support_test.rb
@@ -55,43 +119,44 @@ files:
55
119
  - test/without_shortcuts_test.rb
56
120
  has_rdoc: true
57
121
  homepage: http://github.com/lwe/simple_enum
58
- licenses: []
59
-
122
+ licenses:
123
+ - MIT
60
124
  post_install_message:
61
- rdoc_options:
62
- - --charset=UTF-8
125
+ rdoc_options: []
126
+
63
127
  require_paths:
64
128
  - lib
65
129
  required_ruby_version: !ruby/object:Gem::Requirement
66
- none: false
67
130
  requirements:
68
131
  - - ">="
69
132
  - !ruby/object:Gem::Version
70
- hash: 3
71
133
  segments:
72
- - 0
73
- version: "0"
134
+ - 1
135
+ - 8
136
+ - 7
137
+ version: 1.8.7
74
138
  required_rubygems_version: !ruby/object:Gem::Requirement
75
- none: false
76
139
  requirements:
77
140
  - - ">="
78
141
  - !ruby/object:Gem::Version
79
- hash: 3
80
142
  segments:
81
- - 0
82
- version: "0"
143
+ - 1
144
+ - 3
145
+ - 6
146
+ version: 1.3.6
83
147
  requirements: []
84
148
 
85
149
  rubyforge_project:
86
- rubygems_version: 1.4.1
150
+ rubygems_version: 1.3.6
87
151
  signing_key:
88
152
  specification_version: 3
89
- summary: Simple enum-like field support for ActiveRecord (including validations and i18n)
153
+ summary: Simple enum-like field support for active records.
90
154
  test_files:
91
155
  - test/array_conversions_test.rb
92
156
  - test/class_methods_test.rb
93
157
  - test/enum_hash_test.rb
94
158
  - test/finders_test.rb
159
+ - test/locales.yml
95
160
  - test/models.rb
96
161
  - test/object_backed_test.rb
97
162
  - test/object_support_test.rb
data/Gemfile.BACKPORT DELETED
@@ -1,7 +0,0 @@
1
- source :rubygems
2
-
3
- gem "activesupport", "2.3.5", :require => "active_support"
4
- gem "activerecord", "2.3.5", :require => "active_record"
5
-
6
- gem "sqlite3-ruby", :require => "sqlite3", :platforms => :ruby
7
- gem "jdbc-sqlite3", :require => "jdbcsqlite3", :platforms => :jruby
@@ -1,16 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- activerecord (2.3.5)
5
- activesupport (= 2.3.5)
6
- activesupport (2.3.5)
7
- sqlite3-ruby (1.3.2)
8
-
9
- PLATFORMS
10
- ruby
11
-
12
- DEPENDENCIES
13
- activerecord (= 2.3.5)
14
- activesupport (= 2.3.5)
15
- jdbc-sqlite3
16
- sqlite3-ruby
data/Gemfile.lock DELETED
@@ -1,29 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- activemodel (3.0.5)
5
- activesupport (= 3.0.5)
6
- builder (~> 2.1.2)
7
- i18n (~> 0.4)
8
- activerecord (3.0.5)
9
- activemodel (= 3.0.5)
10
- activesupport (= 3.0.5)
11
- arel (~> 2.0.2)
12
- tzinfo (~> 0.3.23)
13
- activesupport (3.0.5)
14
- arel (2.0.9)
15
- builder (2.1.2)
16
- i18n (0.5.0)
17
- sqlite3 (1.3.3)
18
- sqlite3-ruby (1.3.3)
19
- sqlite3 (>= 1.3.3)
20
- tzinfo (0.3.24)
21
-
22
- PLATFORMS
23
- ruby
24
-
25
- DEPENDENCIES
26
- activerecord (~> 3.0)
27
- activesupport (~> 3.0)
28
- jdbc-sqlite3
29
- sqlite3-ruby
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require 'simple_enum'