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.
@@ -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
+