dm-checked-types 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "dm-checked-types"
5
+ gemspec.summary = "CHECK constraints in your DB"
6
+ gemspec.description = "Property types that add CHECK constraints to your DB and validations to your model. Data integrity FTW."
7
+ gemspec.email = "contact@rhnh.net"
8
+ gemspec.homepage = "http://github.com/xaviershay/dm-checked-types"
9
+ gemspec.authors = ["Xavier Shay"]
10
+ end
11
+ rescue LoadError
12
+ puts "Jeweler not available. Install it with: gem install jeweler"
13
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,13 @@
1
+ require 'dm-checked-types/checked_integer'
2
+ require 'dm-checked-types/migrations'
3
+
4
+ module DataMapper
5
+ module Migrations
6
+ constants.each do |const_name|
7
+ if CheckedTypes.const_defined?(const_name)
8
+ mod = const_get(const_name)
9
+ mod.send(:include, CheckedTypes.const_get(const_name))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ class CheckedInteger < DataMapper::Type
2
+ primitive Integer
3
+
4
+ def self.inherited(target)
5
+ target.instance_variable_set('@primitive', self.primitive)
6
+ end
7
+
8
+ def self.range
9
+ @range || {}
10
+ end
11
+
12
+ def self.range=(value)
13
+ @range = value
14
+ end
15
+
16
+ def self.new(range)
17
+ type = generated_types[range] || Class.new(CheckedInteger)
18
+ type.range = range
19
+ generated_types[range] = type
20
+ type
21
+ end
22
+
23
+ def self.generated_types
24
+ @generated_types ||= {}
25
+ end
26
+
27
+ def self.[](range = {})
28
+ new(range)
29
+ end
30
+
31
+ def self.bind(property)
32
+ if defined?(::DataMapper::Validate)
33
+ model = property.model
34
+
35
+ unless model.skip_auto_validation_for?(property)
36
+ if property.type.ancestors.include?(CheckedInteger)
37
+ range = self.range
38
+ model.class_eval do
39
+ lower_bound = range[:gte]
40
+ lower_bound ||= range[:gt] + 1 if range[:gt]
41
+ lower_bound ||= -n
42
+
43
+ upper_bound = range[:lt] - 1 if range[:lt]
44
+ upper_bound ||= range[:lte]
45
+ upper_bound ||= n
46
+
47
+ validates_within property.name, :set => (lower_bound..upper_bound)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,92 @@
1
+ module CheckedTypes
2
+ module DataObjectsAdapter
3
+ def create_custom_type_statements(repository_name, model)
4
+ model.properties.select {|x| x.type.ancestors.include?(CheckedInteger) }.each do |property|
5
+ table_name = model.storage_name(repository_name)
6
+ property.type.range.each do |key, value|
7
+ comparator = {
8
+ :gt => '>',
9
+ :gte => '>=',
10
+ :lt => '<',
11
+ :lte => '<='
12
+ }[key] || raise(ArgumentError.new("Unsupported comparator: #{key}"))
13
+
14
+ sql = <<-EOS.compress_lines
15
+ ALTER TABLE #{quote_name(table_name)}
16
+ ADD CONSTRAINT #{quote_name(check_constraint_name(table_name, property.name, key))}
17
+ CHECK (#{quote_name(property.field)} #{comparator} #{value})
18
+ EOS
19
+ execute(sql)
20
+ end
21
+ end
22
+ end
23
+
24
+ def destroy_custom_type_statements(repository_name, model)
25
+ model.properties.select {|x| x.type.ancestors.include?(CheckedInteger) }.each do |property|
26
+ table_name = model.storage_name(repository_name)
27
+
28
+ property.type.range.each do |key, value|
29
+ constraint_name = check_constraint_name(table_name, property.name, key)
30
+
31
+ next unless constraint_exists?(model.storage_name, constraint_name)
32
+
33
+ sql = <<-EOS.compress_lines
34
+ ALTER TABLE #{quote_name(model.storage_name(repository_name))}
35
+ DROP CONSTRAINT #{quote_name(constraint_name)}
36
+ EOS
37
+ execute(sql)
38
+ end
39
+ end.compact
40
+ end
41
+
42
+ private
43
+
44
+ def check_constraint_name(table_name, relationship_name, comparator_name)
45
+ "#{table_name}_#{relationship_name}_#{comparator_name}"
46
+ end
47
+
48
+ def quote_constraint_name(foreign_key)
49
+ quote_table_name(foreign_key)
50
+ end
51
+ end
52
+
53
+ module SingletonMethods
54
+ def self.included(base)
55
+ # TODO: figure out how to make this work without AMC
56
+ base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
57
+ alias_method :auto_migrate_down_without_custom_types!, :auto_migrate_down!
58
+ alias_method :auto_migrate_down!, :auto_migrate_down_with_custom_types!
59
+
60
+ alias_method :auto_migrate_up_without_custom_types!, :auto_migrate_up!
61
+ alias_method :auto_migrate_up!, :auto_migrate_up_with_custom_types!
62
+ RUBY
63
+ end
64
+
65
+ def auto_migrate_down_with_custom_types!(repository_name = nil)
66
+ repository_execute(:auto_migrate_down_with_custom_types!, repository_name)
67
+ auto_migrate_down_without_custom_types!(repository_name)
68
+ end
69
+
70
+ def auto_migrate_up_with_custom_types!(repository_name = nil)
71
+ auto_migrate_up_without_custom_types!(repository_name)
72
+ repository_execute(:auto_migrate_up_with_custom_types!, repository_name)
73
+ end
74
+ end
75
+
76
+ module Model
77
+ def auto_migrate_down_with_custom_types!(repository_name = self.repository_name)
78
+ return unless storage_exists?(repository_name)
79
+ return if self.respond_to?(:is_remixable?) && self.is_remixable?
80
+
81
+ adapter = DataMapper.repository(repository_name).adapter
82
+ adapter.destroy_custom_type_statements(repository_name, self)
83
+ end
84
+
85
+ def auto_migrate_up_with_custom_types!(repository_name = self.repository_name)
86
+ return if self.respond_to?(:is_remixable?) && self.is_remixable?
87
+
88
+ adapter = DataMapper.repository(repository_name).adapter
89
+ adapter.create_custom_type_statements(repository_name, self)
90
+ end
91
+ end
92
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-checked-types
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Xavier Shay
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-06 00:00:00 +13:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Property types that add CHECK constraints to your DB and validations to your model. Data integrity FTW.
22
+ email: contact@rhnh.net
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - Rakefile
31
+ - VERSION
32
+ - lib/dm-checked-types.rb
33
+ - lib/dm-checked-types/checked_integer.rb
34
+ - lib/dm-checked-types/migrations.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/xaviershay/dm-checked-types
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.6
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: CHECK constraints in your DB
65
+ test_files: []
66
+