enum_column 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +6 -0
  2. data/LICENSE +20 -0
  3. data/Rakefile +57 -0
  4. data/VERSION +1 -0
  5. data/enum_column.gemspec +108 -0
  6. data/lib/enum/active_record_helper.rb +107 -0
  7. data/lib/enum/enum_adapter.rb +76 -0
  8. data/lib/enum/mysql_adapter.rb +33 -0
  9. data/lib/enum/postgresql_adapter.rb +64 -0
  10. data/lib/enum/quoting.rb +17 -0
  11. data/lib/enum/schema_definitions.rb +12 -0
  12. data/lib/enum/schema_statements.rb +32 -0
  13. data/lib/enum/sqlite3_adapter.rb +52 -0
  14. data/lib/enum/validations.rb +40 -0
  15. data/lib/enum_column.rb +9 -0
  16. data/readme.html +18 -0
  17. data/test/db/schema.rb +28 -0
  18. data/test/enum_mysql_test.rb +220 -0
  19. data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
  20. data/test/fixtures/db_definitions/mysql.sql +244 -0
  21. data/test/fixtures/enumeration.rb +20 -0
  22. data/test/mock_app/.gitignore +3 -0
  23. data/test/mock_app/README +243 -0
  24. data/test/mock_app/Rakefile +10 -0
  25. data/test/mock_app/app/controllers/application_controller.rb +10 -0
  26. data/test/mock_app/app/controllers/contents_controller.rb +85 -0
  27. data/test/mock_app/app/models/content.rb +2 -0
  28. data/test/mock_app/config/boot.rb +110 -0
  29. data/test/mock_app/config/database.yml +5 -0
  30. data/test/mock_app/config/environment.rb +41 -0
  31. data/test/mock_app/config/environments/development.rb +17 -0
  32. data/test/mock_app/config/environments/production.rb +28 -0
  33. data/test/mock_app/config/environments/test.rb +28 -0
  34. data/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  35. data/test/mock_app/config/initializers/inflections.rb +10 -0
  36. data/test/mock_app/config/initializers/mime_types.rb +5 -0
  37. data/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  38. data/test/mock_app/config/initializers/session_store.rb +15 -0
  39. data/test/mock_app/config/locales/en.yml +5 -0
  40. data/test/mock_app/config/routes.rb +45 -0
  41. data/test/mock_app/config/sitemap.rb +13 -0
  42. data/test/mock_app/db/migrate/20090826121911_create_contents.rb +12 -0
  43. data/test/mock_app/db/schema.rb +20 -0
  44. data/test/mock_app/db/test.sqlite3 +0 -0
  45. data/test/mock_app/public/index.html +275 -0
  46. data/test/mock_app/script/console +3 -0
  47. data/test/test_helper.rb +21 -0
  48. metadata +130 -0
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Stephan Kaag
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "enum_column"
8
+ gem.summary = %Q{Ruby on Rails Enumerated Column Constraints}
9
+ gem.description = %Q{Ruby on Rails Enumerated Column Constraints}
10
+ gem.email = "stephan.kaag@gmail.com"
11
+ gem.homepage = "http://github.com/stephankaag/enum_column"
12
+ gem.authors = ["Stephan Kaag"]
13
+ gem.add_development_dependency "thoughtbot-shoulda"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ if File.exist?('VERSION')
48
+ version = File.read('VERSION')
49
+ else
50
+ version = ""
51
+ end
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "enum_column #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,108 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{enum_column}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Stephan Kaag"]
12
+ s.date = %q{2009-10-14}
13
+ s.description = %q{Ruby on Rails Enumerated Column Constraints}
14
+ s.email = %q{stephan.kaag@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "LICENSE",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "enum_column.gemspec",
24
+ "lib/enum/active_record_helper.rb",
25
+ "lib/enum/enum_adapter.rb",
26
+ "lib/enum/mysql_adapter.rb",
27
+ "lib/enum/postgresql_adapter.rb",
28
+ "lib/enum/quoting.rb",
29
+ "lib/enum/schema_definitions.rb",
30
+ "lib/enum/schema_statements.rb",
31
+ "lib/enum/sqlite3_adapter.rb",
32
+ "lib/enum/validations.rb",
33
+ "lib/enum_column.rb",
34
+ "readme.html",
35
+ "test/db/schema.rb",
36
+ "test/enum_mysql_test.rb",
37
+ "test/fixtures/db_definitions/mysql.drop.sql",
38
+ "test/fixtures/db_definitions/mysql.sql",
39
+ "test/fixtures/enumeration.rb",
40
+ "test/mock_app/.gitignore",
41
+ "test/mock_app/README",
42
+ "test/mock_app/Rakefile",
43
+ "test/mock_app/app/controllers/application_controller.rb",
44
+ "test/mock_app/app/controllers/contents_controller.rb",
45
+ "test/mock_app/app/models/content.rb",
46
+ "test/mock_app/config/boot.rb",
47
+ "test/mock_app/config/database.yml",
48
+ "test/mock_app/config/environment.rb",
49
+ "test/mock_app/config/environments/development.rb",
50
+ "test/mock_app/config/environments/production.rb",
51
+ "test/mock_app/config/environments/test.rb",
52
+ "test/mock_app/config/initializers/backtrace_silencers.rb",
53
+ "test/mock_app/config/initializers/inflections.rb",
54
+ "test/mock_app/config/initializers/mime_types.rb",
55
+ "test/mock_app/config/initializers/new_rails_defaults.rb",
56
+ "test/mock_app/config/initializers/session_store.rb",
57
+ "test/mock_app/config/locales/en.yml",
58
+ "test/mock_app/config/routes.rb",
59
+ "test/mock_app/config/sitemap.rb",
60
+ "test/mock_app/db/migrate/20090826121911_create_contents.rb",
61
+ "test/mock_app/db/schema.rb",
62
+ "test/mock_app/db/test.sqlite3",
63
+ "test/mock_app/public/index.html",
64
+ "test/mock_app/script/console",
65
+ "test/test_helper.rb"
66
+ ]
67
+ s.homepage = %q{http://github.com/stephankaag/enum_column}
68
+ s.rdoc_options = ["--charset=UTF-8"]
69
+ s.require_paths = ["lib"]
70
+ s.rubygems_version = %q{1.3.5}
71
+ s.summary = %q{Ruby on Rails Enumerated Column Constraints}
72
+ s.test_files = [
73
+ "test/db/schema.rb",
74
+ "test/enum_mysql_test.rb",
75
+ "test/fixtures/enumeration.rb",
76
+ "test/mock_app/app/controllers/application_controller.rb",
77
+ "test/mock_app/app/controllers/contents_controller.rb",
78
+ "test/mock_app/app/models/content.rb",
79
+ "test/mock_app/config/boot.rb",
80
+ "test/mock_app/config/environment.rb",
81
+ "test/mock_app/config/environments/development.rb",
82
+ "test/mock_app/config/environments/production.rb",
83
+ "test/mock_app/config/environments/test.rb",
84
+ "test/mock_app/config/initializers/backtrace_silencers.rb",
85
+ "test/mock_app/config/initializers/inflections.rb",
86
+ "test/mock_app/config/initializers/mime_types.rb",
87
+ "test/mock_app/config/initializers/new_rails_defaults.rb",
88
+ "test/mock_app/config/initializers/session_store.rb",
89
+ "test/mock_app/config/routes.rb",
90
+ "test/mock_app/config/sitemap.rb",
91
+ "test/mock_app/db/migrate/20090826121911_create_contents.rb",
92
+ "test/mock_app/db/schema.rb",
93
+ "test/test_helper.rb"
94
+ ]
95
+
96
+ if s.respond_to? :specification_version then
97
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
98
+ s.specification_version = 3
99
+
100
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
101
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
102
+ else
103
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
104
+ end
105
+ else
106
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
107
+ end
108
+ end
@@ -0,0 +1,107 @@
1
+
2
+ module ActionView
3
+ module Helpers
4
+ module FormHelper
5
+ # helper to create a select drop down list for the enumerated values. This
6
+ # is the default input tag.
7
+ def enum_select(object_name, method, options = {})
8
+ if RAILS_GEM_VERSION < '2.2.0'
9
+ tag = InstanceTag.new(object_name, method, self, nil, options.delete(:object))
10
+ else
11
+ tag = InstanceTag.new(object_name, method, self, options.delete(:object))
12
+ end
13
+ tag.to_enum_select_tag(options)
14
+ end
15
+
16
+ # Creates a set of radio buttons for all the enumerated values.
17
+ def enum_radio(object_name, method, options = {})
18
+ if RAILS_GEM_VERSION < '2.2.0'
19
+ tag = InstanceTag.new(object_name, method, self, nil, options.delete(:object))
20
+ else
21
+ tag = InstanceTag.new(object_name, method, self, options.delete(:object))
22
+ end
23
+ tag.to_enum_radio_tag(options)
24
+ end
25
+ end
26
+
27
+ class FormBuilder
28
+ def enum_select(method, options = { })
29
+ @template.enum_select(@object_name, method, options)
30
+ end
31
+
32
+ def enum_radio(method, options = { })
33
+ @template.enum_radio(@object_name, method, options)
34
+ end
35
+ end
36
+
37
+ class InstanceTag #:nodoc:
38
+ alias __to_tag_enum to_tag
39
+
40
+ # Add the enumeration tag support. Defaults using the select tag to
41
+ # display the options.
42
+ def to_tag(options = {})
43
+ if column_type == :enum
44
+ to_enum_select_tag(options)
45
+ else
46
+ __to_tag_enum(options)
47
+ end
48
+ end
49
+
50
+ # Create a select tag and one option for each of the
51
+ # enumeration values.
52
+ def to_enum_select_tag(options = {})
53
+ # Remove when we no longer support 1.1.
54
+ begin
55
+ v = value(object)
56
+ rescue ArgumentError
57
+ v = value
58
+ end
59
+ add_default_name_and_id(options)
60
+ tag_text = "<select"
61
+ tag_text << tag_options(options)
62
+ tag_text << ">"
63
+ values = enum_values
64
+ raise ArgumentError, "No values for enum select tag" unless values
65
+ if options[:include_blank]
66
+ tag_text << "<option value=\"\"></option>\n"
67
+ end
68
+ values.each do |enum|
69
+ tag_text << "<option value=\"#{enum}\""
70
+ tag_text << ' selected="selected"' if v and v == enum
71
+ tag_text << ">#{enum}</option>"
72
+ end
73
+ tag_text << "</select>"
74
+ end
75
+
76
+ # Creates a set of radio buttons and labels.
77
+ def to_enum_radio_tag(options = {})
78
+ # Remove when we no longer support 1.1.
79
+ begin
80
+ v = value(object)
81
+ rescue ArgumentError
82
+ v = value
83
+ end
84
+ add_default_name_and_id(options)
85
+ values = enum_values
86
+ raise ArgumentError, "No values for enum select tag" unless values
87
+ tag_text = ''
88
+ template = options.dup
89
+ template.delete('checked')
90
+ values.each do |enum|
91
+ opts = template.dup
92
+ opts['checked'] = 'checked' if v and v == enum
93
+ opts['id'] = "#{opts['id']}_#{enum}"
94
+ tag_text << "<label>#{enum}: "
95
+ tag_text << to_radio_button_tag(enum, opts)
96
+ tag_text << "</label>"
97
+ end
98
+ tag_text
99
+ end
100
+
101
+ # Gets the list of values for the column.
102
+ def enum_values
103
+ object.send("column_for_attribute", @method_name).values
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,76 @@
1
+
2
+ # This module provides all the column helper methods to deal with the
3
+ # values and adds the common type management code for the adapters.
4
+ module ActiveRecordEnumerations
5
+ module Column
6
+ # Add the values accessor to the column class.
7
+ def self.included(klass)
8
+ klass.module_eval <<-EOE
9
+ def values; @limit; end
10
+ EOE
11
+ end
12
+
13
+ # Add the type to the native database types. This will most
14
+ # likely need to be modified in the adapter as well.
15
+ def native_database_types
16
+ types = super
17
+ types[:enum] = { :name => "enum" }
18
+ types
19
+ end
20
+
21
+ # The new constructor with a values argument.
22
+ def initialize(name, default, sql_type = nil, null = true, values = nil)
23
+ super(name, default, sql_type, null)
24
+ @limit = values if type == :enum
25
+ end
26
+
27
+ # The class for enum is Symbol.
28
+ def klass
29
+ if type == :enum
30
+ Symbol
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ # Convert to a symbol.
37
+ def type_cast(value)
38
+ return nil if value.nil?
39
+ if type == :enum
40
+ ActiveRecordEnumerations::Column.value_to_symbol(value)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ # Code to convert to a symbol.
47
+ def type_cast_code(var_name)
48
+ if type == :enum
49
+ "ActiveRecordEnumerations::Column.value_to_symbol(#{var_name})"
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ # The enum simple type.
56
+ def simplified_type(field_type)
57
+ if field_type =~ /enum/i
58
+ :enum
59
+ else
60
+ super
61
+ end
62
+ end
63
+
64
+ # Safely convert the value to a symbol.
65
+ def self.value_to_symbol(value)
66
+ case value
67
+ when Symbol
68
+ value
69
+ when String
70
+ value.empty? ? nil : value.intern
71
+ else
72
+ nil
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+
2
+ module ActiveRecord
3
+ module ConnectionAdapters
4
+ class MysqlAdapter
5
+ alias __native_database_types_enum native_database_types
6
+
7
+ def native_database_types #:nodoc
8
+ types = __native_database_types_enum
9
+ types[:enum] = { :name => "enum" }
10
+ types
11
+ end
12
+
13
+ def columns(table_name, name = nil)#:nodoc:
14
+ sql = "SHOW FIELDS FROM #{table_name}"
15
+ columns = []
16
+ execute(sql, name).each { |field| columns << MysqlColumnWithEnum.new(field[0], field[4], field[1], field[2] == "YES") }
17
+ columns
18
+ end
19
+ end
20
+
21
+ class MysqlColumnWithEnum < MysqlColumn
22
+ include ActiveRecordEnumerations::Column
23
+
24
+ def initialize(name, default, sql_type = nil, null = true)
25
+ if sql_type =~ /^enum/i
26
+ values = sql_type.sub(/^enum\('([^)]+)'\)/i, '\1').split("','").map { |v| v.intern }
27
+ default = default.intern if default and !default.empty?
28
+ end
29
+ super(name, default, sql_type, null, values)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter
4
+ alias __native_database_types_enum native_database_types
5
+
6
+ def native_database_types #:nodoc
7
+ types = __native_database_types_enum
8
+ types[:enum] = { :name => "character varying(32)" }
9
+ types
10
+ end
11
+
12
+ def columns(table_name, name = nil)#:nodoc:
13
+ column_definitions(table_name).collect do |name, type, default, notnull, consrc|
14
+ values = nil
15
+ if consrc and consrc =~ /ANY \(\(ARRAY(\[[^]]+\])/o and type == native_database_types[:enum][:name]
16
+ values = eval $1.gsub(/::character varying/, '')
17
+ type = 'enum'
18
+ end
19
+
20
+ # typmod now unused as limit, precision, scale all handled by superclass
21
+ PostgreSQLColumnWithEnum.new(name, default_value(default),
22
+ translate_field_type(type), notnull == "f",
23
+ values)
24
+ end
25
+ end
26
+
27
+ # Add constraints to the list of columns. This will only pick up check constraints.
28
+ # We'll filter the constraint for the column type and ANY ((ARRAY[ type.
29
+ def column_definitions(table_name)
30
+ query <<-end_sql
31
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull, c.consrc
32
+ FROM pg_attribute a LEFT JOIN pg_attrdef d
33
+ ON a.attrelid = d.adrelid AND a.attnum = d.adnum
34
+ LEFT JOIN pg_constraint c ON a.attrelid = c.conrelid AND
35
+ c.contype = 'c' AND c.conkey[1] = a.attnum
36
+ WHERE a.attrelid = '#{table_name}'::regclass
37
+ AND a.attnum > 0 AND NOT a.attisdropped
38
+ ORDER BY a.attnum
39
+ end_sql
40
+ end
41
+
42
+ def add_column_options!(sql, options)
43
+ unless sql =~ /\(32\)\('[^']+'/
44
+ super(sql, options)
45
+ else
46
+ sql.gsub!(/("[^"]+")([^3]+32\))(.+)/, '\1\2 CHECK(\1 in \3)')
47
+ super(sql, options)
48
+ end
49
+ end
50
+ end
51
+
52
+ class PostgreSQLColumnWithEnum < Column
53
+ include ActiveRecordEnumerations::Column
54
+
55
+ def initialize(name, default, sql_type = nil, null = true, values = nil)
56
+ if values
57
+ values = values.map { |v| v.intern }
58
+ default = default.intern if default and !default.empty?
59
+ end
60
+ super(name, default, sql_type, null, values)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module Quoting
4
+ alias __quote_enum quote
5
+
6
+ # Quote a symbol as a normal string. This will support quoting of
7
+ # enumerated values.
8
+ def quote(value, column = nil)
9
+ if !value.is_a? Symbol
10
+ __quote_enum(value, column)
11
+ else
12
+ ActiveRecord::Base.send(:quote_bound_value, value.to_s)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+
2
+ module ActiveRecord
3
+ module ConnectionAdapters
4
+ class TableDefinition
5
+ def enum(*args)
6
+ options = args.extract_options!
7
+ column_names = args
8
+ column_names.each { |name| column(name, 'enum', options) }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module SchemaStatements
4
+ alias __type_to_sql_enum type_to_sql
5
+
6
+ # Add enumeration support for schema statement creation. This
7
+ # will have to be adapted for every adapter if the type requires
8
+ # anything by a list of allowed values. The overrides the standard
9
+ # type_to_sql method and chains back to the default. This could
10
+ # be done on a per adapter basis, but is generalized here.
11
+ #
12
+ # will generate enum('a', 'b', 'c') for :limit => [:a, :b, :c]
13
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
14
+ if type == :enum
15
+ native = native_database_types[type]
16
+ column_type_sql = native[:name] || 'enum'
17
+
18
+ column_type_sql << "(#{limit.map { |v| quote(v) }.join(',')})"
19
+ column_type_sql
20
+ else
21
+ # Edge rails fallback for Rails 1.1.6. We can remove the
22
+ # rescue once everyone has upgraded to 1.2.
23
+ begin
24
+ __type_to_sql_enum(type, limit, precision, scale)
25
+ rescue ArgumentError
26
+ __type_to_sql_enum(type, limit)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class SQLite3Adapter
4
+ alias __native_database_types_enum native_database_types
5
+
6
+ def native_database_types #:nodoc
7
+ types = __native_database_types_enum
8
+ types[:enum] = { :name => "varchar(32)" }
9
+ types
10
+ end
11
+
12
+ def columns(table_name, name = nil)#:nodoc:
13
+ constraints = { }
14
+ @connection.execute "SELECT sql FROM sqlite_master WHERE name = '#{table_name}'" do |row|
15
+ sql = row[0]
16
+ sql.scan(/, \"(\w+)\" varchar\(32\) CHECK\(\"\w+\" in \(([^\)]+)\)/i) do |column, constraint|
17
+ constraints[column] = constraint
18
+ end
19
+ end
20
+ table_structure(table_name).map do |field|
21
+ name = field['name']
22
+ type = field['type']
23
+ if (const = constraints[name])
24
+ type = "enum(#{const.strip})"
25
+ end
26
+ SQLite3ColumnWithEnum.new(name, field['dflt_value'], type, field['notnull'] == "0")
27
+ end
28
+ end
29
+
30
+ def add_column_options!(sql, options)
31
+ unless sql =~ /\(32\)\('[^']+'/
32
+ super(sql, options)
33
+ else
34
+ sql.gsub!(/("[^"]+")([^3]+32\))(.+)/, '\1\2 CHECK(\1 in \3)')
35
+ super(sql, options)
36
+ end
37
+ end
38
+ end
39
+
40
+ class SQLite3ColumnWithEnum < SQLiteColumn
41
+ include ActiveRecordEnumerations::Column
42
+
43
+ def initialize(name, default, sql_type = nil, null = true)
44
+ if sql_type =~ /^enum/i
45
+ values = sql_type.sub(/^enum\('([^)]+)'\)/i, '\1').split("','").map { |v| v.intern }
46
+ default = default.intern if default and !default.empty?
47
+ end
48
+ super(name, default, sql_type, null, values)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,40 @@
1
+
2
+ module ActiveRecord
3
+ module Validations
4
+ module ClassMethods
5
+ # Automatically validates the column against the schema definition
6
+ # for nullability, format, and enumerations. Handles integers, floats,
7
+ # enumerations, and string limits.
8
+ #
9
+ # Usage: validates_columns :severity, :name
10
+ def validates_columns(*column_names)
11
+ begin
12
+ cols = columns_hash
13
+ column_names.each do |name|
14
+ col = cols[name.to_s]
15
+ raise ArgumentError, "Cannot find column #{name}" unless col
16
+
17
+ # test for nullability
18
+ validates_presence_of(name) if !col.null
19
+
20
+ # Test various known types.
21
+ case col.type
22
+ when :enum
23
+ validates_inclusion_of name, :in => col.values, :allow_nil => true
24
+
25
+ when :integer, :float
26
+ validates_numericality_of name, :allow_nil => true
27
+
28
+ when :string
29
+ if col.limit
30
+ validates_length_of name, :maximum => col.limit, :allow_nil => true
31
+ end
32
+ end
33
+ end
34
+ rescue ActiveRecord::StatementInvalid=>e
35
+ raise e unless e.message.include?("42S02") # swallow the exception if its for a missing table
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ require 'enum/enum_adapter'
2
+ require 'enum/mysql_adapter' if defined? ActiveRecord::ConnectionAdapters::MysqlAdapter
3
+ require 'enum/postgresql_adapter' if defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
4
+ require 'enum/sqlite3_adapter' if defined? ActiveRecord::ConnectionAdapters::SQLite3Adapter
5
+ require 'enum/schema_statements'
6
+ require 'enum/schema_definitions'
7
+ require 'enum/quoting'
8
+ require 'enum/validations'
9
+ require 'enum/active_record_helper'
data/readme.html ADDED
@@ -0,0 +1,18 @@
1
+ = enum_column
2
+
3
+ Gemified fork of http://github.com/adamsalter/enum_column-plugin
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 Stephan Kaag. See LICENSE for details.