simple_enum 1.3.2 → 1.4.0

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.
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'