enum_column 0.1.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.
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.