dm-checked-types 0.0.1
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/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
|
+
|