dm-checked-types 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/lib/dm-checked-types.rb +13 -0
- data/lib/dm-checked-types/checked_integer.rb +53 -0
- data/lib/dm-checked-types/migrations.rb +92 -0
- metadata +66 -0
data/Rakefile
ADDED
@@ -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
|
+
|