├── .document ├── .gitignore ├── .rspec ├── .rvmrc ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── VERSION ├── lib ├── mv-postgresql.rb └── mv │ └── postgresql │ ├── active_record │ └── connection_adapters │ │ └── postgresql_adapter_decorator.rb │ ├── constraint │ ├── builder │ │ ├── check.rb │ │ └── trigger.rb │ └── check.rb │ ├── loader.rb │ ├── railtie.rb │ ├── route │ └── check.rb │ └── validation │ ├── absence.rb │ ├── active_model_presenter │ └── format.rb │ ├── builder │ ├── exclusion.rb │ ├── format.rb │ ├── inclusion.rb │ └── trigger │ │ ├── absence.rb │ │ ├── custom.rb │ │ ├── exclusion.rb │ │ ├── format.rb │ │ ├── inclusion.rb │ │ ├── length.rb │ │ ├── presence.rb │ │ ├── trigger_column.rb │ │ └── uniqueness.rb │ ├── check_support.rb │ ├── custom.rb │ ├── exclusion.rb │ ├── format.rb │ ├── inclusion.rb │ ├── length.rb │ └── presence.rb ├── mv-postgresql.gemspec └── spec ├── mv └── postgresql │ ├── active_record │ └── connection_adapters │ │ └── postgresql_adapter_decorator_spec.rb │ ├── constraint │ ├── builder │ │ ├── check_spec.rb │ │ ├── factory_spec.rb │ │ └── trigger_spec.rb │ ├── check_spec.rb │ └── factory_spec.rb │ ├── integration │ ├── add_validations_spec.rb │ ├── constraints │ │ ├── check │ │ │ ├── absence_spec.rb │ │ │ ├── custom_spec.rb │ │ │ ├── exclusion_spec.rb │ │ │ ├── format_spec.rb │ │ │ ├── inclusion_spec.rb │ │ │ ├── length_spec.rb │ │ │ └── presence_spec.rb │ │ ├── index │ │ │ └── uniqueness_spec.rb │ │ └── trigger │ │ │ ├── absence_spec.rb │ │ │ ├── custom_spec.rb │ │ │ ├── exclusion_spec.rb │ │ │ ├── format_spec.rb │ │ │ ├── inclusion_spec.rb │ │ │ ├── length_spec.rb │ │ │ ├── presence_spec.rb │ │ │ └── uniqueness_spec.rb │ ├── delete_validations_spec.rb │ └── update_validations_spec.rb │ ├── router_spec.rb │ └── validation │ ├── absence_spec.rb │ ├── active_model_presenter │ ├── factory_spec.rb │ └── format_spec.rb │ ├── builder │ ├── exclusion_spec.rb │ ├── format_spec.rb │ ├── inclusion_spec.rb │ └── trigger │ │ ├── absence_spec.rb │ │ ├── custom_spec.rb │ │ ├── exclusion_spec.rb │ │ ├── format_spec.rb │ │ ├── inclusion_spec.rb │ │ ├── length_spec.rb │ │ ├── presence_spec.rb │ │ └── uniqueness_spec.rb │ ├── custom_spec.rb │ ├── exclusion_spec.rb │ ├── factory_spec.rb │ ├── format_spec.rb │ ├── inclusion_spec.rb │ ├── length_spec.rb │ └── presence_spec.rb └── spec_helper.rb /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # rcov generated 2 | coverage 3 | 4 | # rdoc generated 5 | rdoc 6 | 7 | # yard generated 8 | doc 9 | .yardoc 10 | 11 | # bundler 12 | .bundle 13 | 14 | # jeweler generated 15 | pkg 16 | 17 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 18 | # 19 | # * Create a file at ~/.gitignore 20 | # * Include files you want ignored 21 | # * Run: git config --global core.excludesfile ~/.gitignore 22 | # 23 | # After doing this, these files will be ignored in all your git projects, 24 | # saving you from having to 'pollute' every project you touch with them 25 | # 26 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) 27 | # 28 | # For MacOS: 29 | # 30 | #.DS_Store 31 | # 32 | # For TextMate 33 | #*.tmproj 34 | #tmtags 35 | # 36 | # For emacs: 37 | #*~ 38 | #\#* 39 | #.\#* 40 | # 41 | # For vim: 42 | *.swp 43 | /vendor 44 | .ruby-version 45 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm ruby-2.0.0@mv --create 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - "2.2.5" 4 | script: bundle exec rspec spec 5 | before_script: 6 | - psql -c 'create database validation_migration_test_db;' -U postgres 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem 'railties', '~> 5.0' 4 | gem 'pg', '~> 0.18' 5 | gem "mv-core", '~> 3.0' 6 | 7 | group :development do 8 | gem "jeweler", '~> 2.0' 9 | gem "rspec", '~> 3.1' 10 | gem 'rspec-its', '~> 1.1' 11 | gem 'guard-rspec', '~> 4.5', require: false 12 | gem 'shoulda', '~> 3.5' 13 | end 14 | 15 | group :test do 16 | gem 'pry-byebug' 17 | gem 'coveralls', require: false 18 | gem 'rb-fsevent' 19 | gem 'terminal-notifier-guard' 20 | end 21 | 22 | 23 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | actionpack (5.0.1) 5 | actionview (= 5.0.1) 6 | activesupport (= 5.0.1) 7 | rack (~> 2.0) 8 | rack-test (~> 0.6.3) 9 | rails-dom-testing (~> 2.0) 10 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 11 | actionview (5.0.1) 12 | activesupport (= 5.0.1) 13 | builder (~> 3.1) 14 | erubis (~> 2.7.0) 15 | rails-dom-testing (~> 2.0) 16 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 17 | activemodel (5.0.1) 18 | activesupport (= 5.0.1) 19 | activerecord (5.0.1) 20 | activemodel (= 5.0.1) 21 | activesupport (= 5.0.1) 22 | arel (~> 7.0) 23 | activesupport (5.0.1) 24 | concurrent-ruby (~> 1.0, >= 1.0.2) 25 | i18n (~> 0.7) 26 | minitest (~> 5.1) 27 | tzinfo (~> 1.1) 28 | addressable (2.5.0) 29 | public_suffix (~> 2.0, >= 2.0.2) 30 | arel (7.1.4) 31 | builder (3.2.2) 32 | byebug (9.0.6) 33 | coderay (1.1.1) 34 | concurrent-ruby (1.0.4) 35 | coveralls (0.8.17) 36 | json (>= 1.8, < 3) 37 | simplecov (~> 0.12.0) 38 | term-ansicolor (~> 1.3) 39 | thor (~> 0.19.1) 40 | tins (~> 1.6) 41 | descendants_tracker (0.0.4) 42 | thread_safe (~> 0.3, >= 0.3.1) 43 | diff-lcs (1.2.5) 44 | docile (1.1.5) 45 | erubis (2.7.0) 46 | faraday (0.9.2) 47 | multipart-post (>= 1.2, < 3) 48 | ffi (1.9.14) 49 | formatador (0.2.5) 50 | git (1.3.0) 51 | github_api (0.11.3) 52 | addressable (~> 2.3) 53 | descendants_tracker (~> 0.0.1) 54 | faraday (~> 0.8, < 0.10) 55 | hashie (>= 1.2) 56 | multi_json (>= 1.7.5, < 2.0) 57 | nokogiri (~> 1.6.0) 58 | oauth2 59 | guard (2.14.0) 60 | formatador (>= 0.2.4) 61 | listen (>= 2.7, < 4.0) 62 | lumberjack (~> 1.0) 63 | nenv (~> 0.1) 64 | notiffany (~> 0.0) 65 | pry (>= 0.9.12) 66 | shellany (~> 0.0) 67 | thor (>= 0.18.1) 68 | guard-compat (1.2.1) 69 | guard-rspec (4.7.3) 70 | guard (~> 2.1) 71 | guard-compat (~> 1.1) 72 | rspec (>= 2.99.0, < 4.0) 73 | hashie (3.4.6) 74 | highline (1.7.8) 75 | i18n (0.7.0) 76 | jeweler (2.3.2) 77 | builder 78 | bundler (>= 1.0) 79 | git (>= 1.2.5) 80 | github_api (~> 0.11.0) 81 | highline (>= 1.6.15) 82 | nokogiri (>= 1.5.10) 83 | psych (~> 2.2) 84 | rake 85 | rdoc 86 | semver2 87 | json (2.0.2) 88 | jwt (1.5.6) 89 | listen (3.1.5) 90 | rb-fsevent (~> 0.9, >= 0.9.4) 91 | rb-inotify (~> 0.9, >= 0.9.7) 92 | ruby_dep (~> 1.2) 93 | loofah (2.0.3) 94 | nokogiri (>= 1.5.9) 95 | lumberjack (1.0.10) 96 | method_source (0.8.2) 97 | mini_portile2 (2.1.0) 98 | minitest (5.10.1) 99 | multi_json (1.12.1) 100 | multi_xml (0.6.0) 101 | multipart-post (2.0.0) 102 | mv-core (3.0.0) 103 | activerecord (~> 5.0) 104 | i18n (~> 0.7) 105 | railties (~> 5.0) 106 | nenv (0.3.0) 107 | nokogiri (1.6.8.1) 108 | mini_portile2 (~> 2.1.0) 109 | notiffany (0.1.1) 110 | nenv (~> 0.1) 111 | shellany (~> 0.0) 112 | oauth2 (1.3.0) 113 | faraday (>= 0.8, < 0.11) 114 | jwt (~> 1.0) 115 | multi_json (~> 1.3) 116 | multi_xml (~> 0.5) 117 | rack (>= 1.2, < 3) 118 | pg (0.19.0) 119 | pry (0.10.4) 120 | coderay (~> 1.1.0) 121 | method_source (~> 0.8.1) 122 | slop (~> 3.4) 123 | pry-byebug (3.4.2) 124 | byebug (~> 9.0) 125 | pry (~> 0.10) 126 | psych (2.2.2) 127 | public_suffix (2.0.4) 128 | rack (2.0.1) 129 | rack-test (0.6.3) 130 | rack (>= 1.0) 131 | rails-dom-testing (2.0.2) 132 | activesupport (>= 4.2.0, < 6.0) 133 | nokogiri (~> 1.6) 134 | rails-html-sanitizer (1.0.3) 135 | loofah (~> 2.0) 136 | railties (5.0.1) 137 | actionpack (= 5.0.1) 138 | activesupport (= 5.0.1) 139 | method_source 140 | rake (>= 0.8.7) 141 | thor (>= 0.18.1, < 2.0) 142 | rake (12.0.0) 143 | rb-fsevent (0.9.8) 144 | rb-inotify (0.9.7) 145 | ffi (>= 0.5.0) 146 | rdoc (5.0.0) 147 | rspec (3.5.0) 148 | rspec-core (~> 3.5.0) 149 | rspec-expectations (~> 3.5.0) 150 | rspec-mocks (~> 3.5.0) 151 | rspec-core (3.5.4) 152 | rspec-support (~> 3.5.0) 153 | rspec-expectations (3.5.0) 154 | diff-lcs (>= 1.2.0, < 2.0) 155 | rspec-support (~> 3.5.0) 156 | rspec-its (1.2.0) 157 | rspec-core (>= 3.0.0) 158 | rspec-expectations (>= 3.0.0) 159 | rspec-mocks (3.5.0) 160 | diff-lcs (>= 1.2.0, < 2.0) 161 | rspec-support (~> 3.5.0) 162 | rspec-support (3.5.0) 163 | ruby_dep (1.5.0) 164 | semver2 (3.4.2) 165 | shellany (0.0.1) 166 | shoulda (3.5.0) 167 | shoulda-context (~> 1.0, >= 1.0.1) 168 | shoulda-matchers (>= 1.4.1, < 3.0) 169 | shoulda-context (1.2.2) 170 | shoulda-matchers (2.8.0) 171 | activesupport (>= 3.0.0) 172 | simplecov (0.12.0) 173 | docile (~> 1.1.0) 174 | json (>= 1.8, < 3) 175 | simplecov-html (~> 0.10.0) 176 | simplecov-html (0.10.0) 177 | slop (3.6.0) 178 | term-ansicolor (1.4.0) 179 | tins (~> 1.0) 180 | terminal-notifier-guard (1.7.0) 181 | thor (0.19.4) 182 | thread_safe (0.3.5) 183 | tins (1.13.0) 184 | tzinfo (1.2.2) 185 | thread_safe (~> 0.1) 186 | 187 | PLATFORMS 188 | ruby 189 | 190 | DEPENDENCIES 191 | coveralls 192 | guard-rspec (~> 4.5) 193 | jeweler (~> 2.0) 194 | mv-core (~> 3.0) 195 | pg (~> 0.18) 196 | pry-byebug 197 | railties (~> 5.0) 198 | rb-fsevent 199 | rspec (~> 3.1) 200 | rspec-its (~> 1.1) 201 | shoulda (~> 3.5) 202 | terminal-notifier-guard 203 | 204 | BUNDLED WITH 205 | 1.17.1 206 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rspec, cmd: 'bundle exec rspec', all_after_pass: true, failed_mode: :focus do 2 | watch(%r{^spec/mv/.+_spec\.rb$}) 3 | watch(%r{^lib/mv/(.+)\.rb$}) { |m| "spec/mv/#{m[1]}_spec.rb" } 4 | watch('spec/spec_helper.rb') { "spec" } 5 | end -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Valeriy Prokopchuk 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. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/vprokopchuk256/mv-postgresql.svg?branch=master)](https://travis-ci.org/vprokopchuk256/mv-postgresql) 2 | [![Coverage Status](https://coveralls.io/repos/vprokopchuk256/mv-postgresql/badge.png?branch=master)](https://coveralls.io/r/vprokopchuk256/mv-postgresql?branch=master) 3 | [![Gem Version](https://badge.fury.io/rb/mv-postgresql.svg)](http://badge.fury.io/rb/mv-postgresql) 4 | 5 | # `Migration Validators` project. PostgreSQL driver. 6 | 7 | Define validations directly in DB as PostgreSQL constraints and integrate them into your model transparently. See [mv-core](https://github.com/vprokopchuk256/mv-core) for details. There you will be able to review high level project information. Below you can see details of the migration validations that are supported by PostgreSQL driver. 8 | 9 | #Table Of Contents 10 | * [Validations](#validations) 11 | * [uniqueness](#uniqueness) 12 | * [length](#length) 13 | * [inclusion](#inclusion) 14 | * [exclusion](#exclusion) 15 | * [presence](#presence) 16 | * [absence](#absence) 17 | * [format](#format) 18 | * [custom](#custom) 19 | * [Version History](#version history) 20 | * [Contributing](#contributing) 21 | 22 | # Validations 23 | 24 | ### uniqueness 25 | 26 | Examples: 27 | 28 | validate uniqueness of the column `column_name`: 29 | 30 | ```ruby 31 | def up 32 | validates :table_name, :column_name, uniqueness: true 33 | end 34 | 35 | def down 36 | validates :table_name, :column_name, uniqueness: false 37 | end 38 | ``` 39 | 40 | define validation as trigger with specified failure message: 41 | 42 | ```ruby 43 | def up 44 | validates :table_name, :column_name, 45 | uniqueness: { message: 'Error message', as: :trigger } 46 | end 47 | 48 | def down 49 | validates :table_name, :column_name, uniqueness: false 50 | end 51 | ``` 52 | 53 | define validation as unique index: 54 | 55 | ```ruby 56 | def up 57 | validates :table_name, :column_name, uniqueness: { as: :index } 58 | end 59 | 60 | def down 61 | validates :table_name, :column_name, uniqueness: false 62 | end 63 | ``` 64 | 65 | all above are available in a create and change table blocks: 66 | 67 | ```ruby 68 | def change 69 | create_table :table_name do |t| 70 | t.string :column_name, validates: { uniqueness: true } 71 | end 72 | end 73 | ``` 74 | 75 | ```ruby 76 | def up 77 | change :table_name do |t| 78 | t.change :column_name, :string, validates: { uniqueness: false } 79 | end 80 | end 81 | 82 | def down 83 | change :table_name do |t| 84 | t.change :column_name, :string, validates: { uniqueness: false } 85 | end 86 | end 87 | ``` 88 | 89 | simplifications (version >= 2.1 is required): 90 | 91 | ```ruby 92 | def change 93 | create_table :table_name do |t| 94 | t.string :column_name, uniqueness: true 95 | end 96 | end 97 | ``` 98 | 99 | Options: 100 | 101 | * `:message` - text of the error message that will be shown if constraint violated. Ignored unless `:as == :trigger` 102 | * `:index_name` - name of the index that will be created for validator. Ignored unless `:as == :index` 103 | * `:on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value `:save` 104 | * `:create_tigger_name` - name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 105 | * `:update_tigger_name` - name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 106 | * `:allow_nil` - ignore validation for nil values. Ignored unless `:as == :trigger`. Default value: `false` 107 | * `:allow_blank` - ignore validation for blank values. Ignored unless `:as == :trigger`. Default value: `false` 108 | * `:as` - defines the way how constraint will be implemented. Possible values: `[:index, :trigger]`. Default value: `:index` 109 | 110 | ### length 111 | 112 | Examples: 113 | 114 | ```ruby 115 | def up 116 | validates :table_name, :column_name, 117 | length: { in: 5..8, message: 'Wrong length message'} 118 | end 119 | 120 | def down 121 | validates :table_name, :column_name, length: false 122 | end 123 | ``` 124 | 125 | allow `NULL`: 126 | 127 | ```ruby 128 | def up 129 | validates :table_name, :column_name, 130 | length: { is: 3, allow_nil: true} 131 | end 132 | 133 | def down 134 | validates :table_name, :column_name, length: false 135 | end 136 | ``` 137 | 138 | allow blank values: 139 | 140 | ```ruby 141 | def up 142 | validates :table_name, :column_name, 143 | length: { maximum: 3, 144 | allow_blank: true, 145 | too_long: 'Value is longer than 3 symbols' } 146 | end 147 | 148 | def down 149 | validates :table_name, :column_name, length: false 150 | end 151 | ``` 152 | 153 | define constraint in trigger: 154 | 155 | ```ruby 156 | def up 157 | validates :table_name, :column_name, 158 | length: { maximum: 3, 159 | as: :trigger, 160 | too_long: 'Value is longer than 3 symbols' } 161 | end 162 | 163 | def down 164 | validates :table_name, :column_name, length: false 165 | end 166 | ``` 167 | 168 | all above are available in a create and change table blocks: 169 | 170 | ```ruby 171 | def change 172 | create_table :table_name do |t| 173 | t.string :column_name, validates: { length: { is: 3, allow_nil: true} } 174 | end 175 | end 176 | ``` 177 | 178 | ```ruby 179 | def up 180 | change :table_name do |t| 181 | t.change :column_name, :string, validates: { length: { is: 3 } } 182 | end 183 | end 184 | 185 | def down 186 | change :table_name do |t| 187 | t.change :column_name, :string, validates: { length: false } 188 | end 189 | end 190 | ``` 191 | 192 | simplifications (version >= 2.1 is required): 193 | 194 | ```ruby 195 | def change 196 | create_table :table_name do |t| 197 | t.string :string_3, length: 3 198 | t.string :string_from_1_to_3, length: 1..3, 199 | t.string :string_1_or_3, length: [1, 3] 200 | t.string :string_4, validates: { length: 4 } 201 | t.string :string_4_in_trigger: length: { is: 4, as: :trigger } 202 | end 203 | end 204 | ``` 205 | 206 | Options: 207 | 208 | * `in` - range or array that length of the value should be contained in. 209 | * `within` - synonym of `:in` 210 | * `is` - exact length of the value 211 | * `maximum` - maximum allowed length 212 | * `minimum` - minimum allowed length 213 | * `message` - message that should be shown if validation failed and specific message is not defined. Ignored unless `:as == :trigger` 214 | * `too_long` - message that will be shown if value longer than allowed. Ignored unless maximum value is defined 215 | * `too_short` - message that will be shown if value shorter than allowed. Ignored unless minimum value is defined 216 | * `on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value: `:save` 217 | * `create_tigger_name` - Name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 218 | * `update_tigger_name` - Name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 219 | * `allow_nil` - ignore validation for nil values. Default value: `false` 220 | * `allow_blank` - ignore validation for blank values. Default value: `false` 221 | * `as` - defines the way how constraint will be implemented. Possible values: `[:trigger, :check]` Default value: `:check` 222 | 223 | ### inclusion 224 | 225 | Examples: 226 | 227 | valid values array: 228 | 229 | ```ruby 230 | def up 231 | validates :table_name, :column_name, inclusion: { in: [1, 2, 3] } 232 | end 233 | 234 | def down 235 | validates :table_name, :column_name, inclusion: false 236 | end 237 | ``` 238 | 239 | with failure message specified: 240 | 241 | ```ruby 242 | def up 243 | validates :table_name, :column_name, 244 | inclusion: { in: [1, 2, 3], 245 | message: "Column value should be equal to 1 or 2 or 3" } 246 | end 247 | 248 | def down 249 | validates :table_name, :column_name, inclusion: false 250 | end 251 | ``` 252 | 253 | make it as check constraint: 254 | 255 | ```ruby 256 | def up 257 | validates :table_name, :column_name, 258 | inclusion: { in: [1, 2, 3], as: :check } 259 | end 260 | 261 | def down 262 | validates :table_name, :column_name, inclusion: false 263 | end 264 | ``` 265 | 266 | make it in trigger: 267 | 268 | ```ruby 269 | def up 270 | validates :table_name, :column_name, 271 | inclusion: { in: 1..3, 272 | on: :create, 273 | as: :trigger } 274 | end 275 | 276 | def down 277 | validates :table_name, :column_name, inclusion: false 278 | end 279 | ``` 280 | 281 | all above are available in a create and change table blocks: 282 | 283 | ```ruby 284 | def change 285 | create_table :table_name do |t| 286 | t.integer :column_name, validates: { inclusion: { in: 1..3 } } 287 | end 288 | end 289 | ``` 290 | 291 | ```ruby 292 | def up 293 | change :table_name do |t| 294 | t.change :column_name, :integer, validates: { inclusion: { in: 1..3 } } 295 | end 296 | end 297 | 298 | def down 299 | change :table_name do |t| 300 | t.change :column_name, :integer, validates: { inclusion: false } 301 | end 302 | end 303 | ``` 304 | 305 | simplifications (version >= 2.1 is required): 306 | 307 | ```ruby 308 | def change 309 | create_table :table_name do |t| 310 | t.string :str_or_str_1, inclusion: ['str', 'str1'] 311 | t.string :from_str_to_str_1, inclusion: 'str'..'str1' 312 | t.string :str_or_str_1_in_trigger, inclusion: { in: ['str', 'str1'], 313 | as: :trigger} 314 | end 315 | end 316 | ``` 317 | 318 | Options: 319 | 320 | * `in` - range or array that column value should be contained in. 321 | * `message` - message that should be shown if validation failed. Ignored unless `:as == :trigger` 322 | * `on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value: `:save` 323 | * `create_tigger_name` - Name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 324 | * `update_tigger_name` - Name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 325 | * `allow_nil` - ignore validation for nil values. Default value: `false` 326 | * `allow_blank` - ignore validation for blank values. Default value: `false` 327 | * `as` - defines the way how constraint will be implemented. Possible values: `[:trigger, :check]` Default value: `:check` 328 | 329 | 330 | ### exclusion 331 | 332 | Examples: 333 | 334 | exclude 1, 2, and 3: 335 | 336 | within `create_table` statement: 337 | 338 | ```ruby 339 | def change 340 | create_table :tabld_name do |t| 341 | t.integer :column_name, validates: { exclusion: { in: [1, 2, 3] } } 342 | end 343 | end 344 | ``` 345 | 346 | or as standalone statements: 347 | 348 | ```ruby 349 | def up 350 | validates :table_name, :column_name, exclusion: { in: [1, 2, 3] } 351 | end 352 | 353 | def down 354 | validates :table_name, :column_name, exclusion: false 355 | end 356 | ``` 357 | 358 | the same with failure message: 359 | 360 | ```ruby 361 | def up 362 | validates :table_name, :column_name, 363 | exclusion: { 364 | in: [1, 2, 3], 365 | message: "Column 'column_name' should not be equal to 1 or 2 or 3" } 366 | end 367 | 368 | def down 369 | validates :table_name, :column_name, exclusion: false 370 | end 371 | ``` 372 | 373 | as check constraint: 374 | 375 | ```ruby 376 | def up 377 | validates :table_name, :column_name, 378 | exclusion: { in: [1, 2, 3], as: :check } 379 | end 380 | 381 | def down 382 | validates :table_name, :column_name, exclusion: false 383 | end 384 | ``` 385 | 386 | as trigger: 387 | 388 | ```ruby 389 | def up 390 | validates :table_name, :column_name, 391 | exclusion: { in: 1..3, 392 | on: :create, 393 | as: :trigger } 394 | end 395 | 396 | def down 397 | validates :table_name, :column_name, exclusion: false 398 | end 399 | ``` 400 | 401 | all above are available in a create and change table blocks: 402 | 403 | ```ruby 404 | def change 405 | create_table :table_name do |t| 406 | t.integer :column_name, validates: { exclusion: { in: 1..3 } } 407 | end 408 | end 409 | ``` 410 | 411 | ```ruby 412 | def up 413 | change :table_name do |t| 414 | t.change :column_name, :integer, validates: { exclusion: { in: 1..3 } } 415 | end 416 | end 417 | 418 | def down 419 | change :table_name do |t| 420 | t.change :column_name, :integer, validates: { exclusion: false } 421 | end 422 | end 423 | ``` 424 | 425 | simplifications (version >= 2.1 is required): 426 | 427 | ```ruby 428 | def change 429 | create_table :table_name do |t| 430 | t.string :neither_str_nor_str_1, exclusion: ['str', 'str1'] 431 | t.string :from_str_to_str_1, exclusion: 'str'..'str1' 432 | t.string :str_or_str_1_in_trigger, exclusion: { in: ['str', 'str1'], 433 | as: :trigger} 434 | end 435 | end 436 | ``` 437 | 438 | Options: 439 | 440 | * `:in` - range or array that column value should NOT be contained in. 441 | * `:message` - message that should be shown if validation failed. Ignored unless `:as == :trigger` 442 | * `:on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value: `:save` 443 | * `:create_tigger_name` - Name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 444 | * `:update_tigger_name` - Name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 445 | * `:allow_nil` - ignore validation for `nil` values. Default value: `false` 446 | * `:allow_blank` - ignore validation for blank values. Default value: `false` 447 | * `:as` - defines the way how constraint will be implemented. Possible values: `[:trigger, :check]` Default value: `:check` 448 | 449 | ### presence 450 | 451 | Examples: 452 | 453 | ```ruby 454 | def up 455 | validates :table_name, :column_name, presence: true 456 | end 457 | 458 | def down 459 | validates :table_name, :column_name, presence: false 460 | end 461 | ``` 462 | 463 | with failure message: 464 | 465 | ```ruby 466 | def up 467 | validates :table_name, :column_name, 468 | presence: { message: 'value should not be empty' } 469 | end 470 | 471 | def down 472 | validates :table_name, :column_name, presence: false 473 | end 474 | ``` 475 | 476 | implemented as trigger: 477 | 478 | ```ruby 479 | def up 480 | validates :table_name, :column_name, 481 | presence: { message: 'value should not be empty', 482 | as: :trigger } 483 | end 484 | 485 | def down 486 | validates :table_name, :column_name, presence: false 487 | end 488 | ``` 489 | 490 | check when record is inserted only: 491 | 492 | ```ruby 493 | def up 494 | validates :table_name, :column_name, 495 | presence: { message: 'value should not be empty', 496 | as: :trigger, 497 | on: :create } 498 | end 499 | 500 | def down 501 | validates :table_name, :column_name, presence: false 502 | end 503 | ``` 504 | 505 | all above are available in a create and change table blocks: 506 | 507 | ```ruby 508 | def change 509 | create_table :table_name do |t| 510 | t.string :column_name, validates: { presence: true } 511 | end 512 | end 513 | ``` 514 | 515 | ```ruby 516 | def up 517 | change :table_name do |t| 518 | t.change :column_name, :string, validates: { presence: true } 519 | end 520 | end 521 | 522 | def down 523 | change :table_name do |t| 524 | t.change :column_name, :string, validates: { presence: false } 525 | end 526 | end 527 | ``` 528 | 529 | simplifications (version >= 2.1 is required): 530 | 531 | ```ruby 532 | create_table :table_name do |t| 533 | t.string :presence_in_check, presence: true 534 | t.string :presence_in_trigger, presence: { as: :trigger, on: :create } 535 | end 536 | ``` 537 | 538 | Options: 539 | 540 | * `message` - message that should be shown if validation failed. Ignored unless `:as == :trigger` 541 | * `on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value: `:save` 542 | * `create_tigger_name` - Name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 543 | * `update_tigger_name` - Name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 544 | * `allow_nil` - ignore validation for `nil` values. Default value: `false` 545 | * `allow_blank` - ignore validation for blank values. Default value: `false` 546 | * `as` - defines the way how constraint will be implemented. Possible values: `[:trigger, :check]` Default value: `:check` 547 | 548 | ### absence 549 | 550 | Examples: 551 | 552 | ```ruby 553 | def up 554 | validates :table_name, :column_name, absence: true 555 | end 556 | 557 | def down 558 | validates :table_name, :column_name, absence: false 559 | end 560 | ``` 561 | 562 | with failure message: 563 | 564 | ```ruby 565 | def up 566 | validates :table_name, :column_name, 567 | absence: { message: 'value should be empty' } 568 | end 569 | 570 | def down 571 | validates :table_name, :column_name, absence: false 572 | end 573 | ``` 574 | 575 | implemented as trigger: 576 | 577 | ```ruby 578 | def up 579 | validates :table_name, :column_name, 580 | absence: { message: 'value should be empty', 581 | as: :trigger } 582 | end 583 | 584 | def down 585 | validates :table_name, :column_name, absence: false 586 | end 587 | ``` 588 | 589 | check when record is inserted only: 590 | 591 | ```ruby 592 | def up 593 | validates :table_name, :column_name, 594 | absence: { message: 'value should be empty', 595 | as: :trigger, 596 | on: :create } 597 | end 598 | 599 | def down 600 | validates :table_name, :column_name, absence: false 601 | end 602 | ``` 603 | 604 | all above are available in a create and change table blocks: 605 | 606 | ```ruby 607 | def change 608 | create_table :table_name do |t| 609 | t.string :column_name, validates: { absence: true } 610 | end 611 | end 612 | ``` 613 | 614 | ```ruby 615 | def up 616 | change :table_name do |t| 617 | t.change :column_name, :string, validates: { absence: true } 618 | end 619 | end 620 | 621 | def down 622 | change :table_name do |t| 623 | t.change :column_name, :string, validates: { absence: false } 624 | end 625 | end 626 | ``` 627 | 628 | simplifications (version >= 2.1 is required): 629 | 630 | ```ruby 631 | def change 632 | create_table :table_name do |t| 633 | t.string :absence_in_check, absence: true 634 | t.string :absence_in_trigger, absence: { as: :trigger, on: :create } 635 | end 636 | end 637 | ``` 638 | 639 | Options: 640 | 641 | * `message` - message that should be shown if validation failed. Ignored unless `:as == :trigger` 642 | * `on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value: `:save` 643 | * `create_tigger_name` - Name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 644 | * `update_tigger_name` - Name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 645 | * `allow_nil` - ignore validation for `nil` values. Default value: `false` 646 | * `allow_blank` - ignore validation for blank values. Default value: `false` 647 | * `as` - defines the way how constraint will be implemented. Possible values: `[:trigger, :check]` Default value: `:check` 648 | 649 | ### format 650 | 651 | Examples: 652 | 653 | allows only values that contains 'word' inside: 654 | 655 | ```ruby 656 | def up 657 | validates :table_name, :column_name, format: { with: /word/ } 658 | end 659 | 660 | def down 661 | validates :table_name, :column_name, format: false 662 | end 663 | ``` 664 | 665 | with failure message: 666 | 667 | ```ruby 668 | def up 669 | validates :table_name, :column_name, 670 | format: { with: /word/, 671 | message: 'Column_name value should contain start word' } 672 | end 673 | 674 | def down 675 | validates :table_name, :column_name, format: false 676 | end 677 | ``` 678 | 679 | implemented as trigger: 680 | 681 | ```ruby 682 | def up 683 | validates :table_name, :column_name, 684 | format: { with: /word/, 685 | message: 'Column_name value should contain start word', 686 | as: :trigger } 687 | end 688 | 689 | def down 690 | validates :table_name, :column_name, format: false 691 | end 692 | ``` 693 | 694 | all above are available in a create and change table blocks: 695 | 696 | ```ruby 697 | def change 698 | create_table :table_name do |t| 699 | t.string :column_name, validates { format: { with: /word/ } } 700 | end 701 | end 702 | ``` 703 | 704 | ```ruby 705 | def up 706 | change :table_name do |t| 707 | t.change :column_name, :string, validates: { format: { with: /word/ } } 708 | end 709 | end 710 | 711 | def down 712 | change :table_name do |t| 713 | t.change :column_name, :string, validates: { format: false } 714 | end 715 | end 716 | ``` 717 | 718 | simplifications (version >= 2.1 is required): 719 | 720 | ```ruby 721 | def change 722 | create_table :table_name do |t| 723 | t.string :contains_word, format: /word/ 724 | t.string :contains_word_in_trigger, format: { with: /word/, 725 | as: :trigger } 726 | end 727 | end 728 | ``` 729 | 730 | Options: 731 | 732 | * `with` - regular expression that column value should be matched to 733 | * `message` - message that should be shown if validation failed. Ignored unless `:as == :trigger` 734 | * `on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value: `:save` 735 | * `create_tigger_name` - Name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 736 | * `update_tigger_name` - Name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 737 | * `allow_nil` - ignore validation for `nil` values. Default value: `false` 738 | * `allow_blank` - ignore validation for blank values. Default value: `false` 739 | * `as` - defines the way how constraint will be implemented. Possible values: `[:trigger, :check]` Default value: `:check` 740 | 741 | ### custom 742 | 743 | (version >= 2.1 is required) 744 | 745 | Examples: 746 | 747 | allows only values that contains 'word' inside: 748 | 749 | ```ruby 750 | def up 751 | validates :table_name, :column_name, 752 | custom: { statement: "TRIM({column_name}) ~ 'word'" } 753 | end 754 | 755 | def down 756 | validates :table_name, :column_name, custom: false 757 | end 758 | ``` 759 | 760 | with failure message: 761 | 762 | ```ruby 763 | def up 764 | validates :table_name, :column_name, 765 | custom: { statement: "TRIM({column_name}) ~ 'word'", 766 | message: 'Column_name value should contain start word' } 767 | end 768 | 769 | def down 770 | validates :table_name, :column_name, custom: false 771 | end 772 | ``` 773 | 774 | implemented as trigger: 775 | 776 | ```ruby 777 | def up 778 | validates :table_name, :column_name, 779 | custom: { statement: "TRIM({column_name}) ~ 'word'", 780 | message: 'Column_name value should contain start word', 781 | as: :trigger } 782 | end 783 | 784 | def down 785 | validates :table_name, :column_name, custom: false 786 | end 787 | ``` 788 | 789 | all above are available in a create and change table blocks: 790 | 791 | ```ruby 792 | def change 793 | create_table :table_name do |t| 794 | t.string :column_name, 795 | validates: { custom: { statement: "TRIM({column_name}) ~ 'word'"} } 796 | end 797 | end 798 | ``` 799 | 800 | ```ruby 801 | def up 802 | change :table_name do |t| 803 | t.change :column_name, :string, 804 | validates: { custom: { statement: "TRIM({column_name}) ~ 'word'"} } 805 | end 806 | end 807 | 808 | def down 809 | change :table_name do |t| 810 | t.change :column_name, :string, validates: { custom: false } 811 | end 812 | end 813 | ``` 814 | 815 | simplifications (version >= 2.1 is required): 816 | 817 | ```ruby 818 | def change 819 | create_table :table_name do |t| 820 | t.string :contains_word, custom: "TRIM({contains_word}) ~ 'word'" 821 | t.string :contains_word_synonym, 822 | validates: "TRIM({contains_word_synonym}) ~ 'word'" 823 | t.string :contains_word_in_trigger, 824 | custom: { statement: "TRIM({contains_word_in_trigger}) ~ 'word'", 825 | as: :trigger } 826 | end 827 | end 828 | ``` 829 | 830 | Options: 831 | 832 | * `statement` - db expression that column value should be matched to 833 | * `message` - message that should be shown if validation failed. Ignored unless `:as == :trigger` 834 | * `on` - validation event. Possible values `[:save, :update, :create]`. Ignored unless `:as == :trigger`. Default value: `:save` 835 | * `create_tigger_name` - Name of the 'before insert' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :create]` 836 | * `update_tigger_name` - Name of the 'before update' trigger that will be created if `:as == :trigger` && `:on` in `[:save, :update]` 837 | * `allow_nil` - ignore validation for `nil` values. Default value: `false` 838 | * `allow_blank` - ignore validation for blank values. Default value: `false` 839 | * `as` - defines the way how constraint will be implemented. Possible values: `[:trigger, :check]` Default value: `:check` 840 | 841 | ## Version History 842 | 843 | **(2.0.0)** (17 Jan, 2015) 844 | 845 | * Completely rewritten. Migrated to Ruby 2.0 and RoR 4 846 | 847 | **(2.1.0)** (22 Jan, 2015) 848 | 849 | * Custom validation 850 | 851 | **(2.2.0)** (28 Jan, 2015) 852 | 853 | * Integration with ActiveRecord 854 | 855 | **(2.2.1)** (29 May, 2015) 856 | 857 | * Made it possible to use several mv-* drivers in the same project 858 | 859 | **(2.2.2)** (20 Jul, 2015) 860 | 861 | * Fix issue with invalid parameters number in `add_column` and `change_column` methods 862 | 863 | **(2.2.3)** (23 Nov, 2015) 864 | 865 | * Do not camel case column name in error message 866 | 867 | **(2.2.4)** (9 Feb, 2016) 868 | 869 | * Support booleans in inclusion validator 870 | 871 | **(2.2.5)** (23 Feb, 2016) 872 | 873 | * Suppress exception while running db:schema:load 874 | 875 | **(2.2.7)** (12 Sep, 2016) 876 | 877 | * Escape single quotes in the custom validation statement body 878 | 879 | **(2.2.8)** (4 Dec, 2018) 880 | 881 | * Bump required pg version 882 | 883 | ## Contributing 884 | 885 | * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet 886 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it 887 | * Fork the project 888 | * Start a feature/bugfix branch 889 | * Commit and push until you are happy with your contribution 890 | * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. 891 | * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. 892 | 893 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | begin 4 | Bundler.setup(:default, :development, :test) 5 | rescue Bundler::BundlerError => e 6 | $stderr.puts e.message 7 | $stderr.puts "Run `bundle install` to install missing gems" 8 | exit e.status_code 9 | end 10 | require 'rake' 11 | 12 | require 'jeweler' 13 | Jeweler::Tasks.new do |gem| 14 | gem.name = "mv-postgresql" 15 | gem.homepage = "http://github.com/vprokopchuk256/mv-postgresql" 16 | gem.license = "MIT" 17 | gem.summary = "Migration Validators project postgresql driver" 18 | gem.summary = "Postgresql constraints in migrations similiar to ActiveRecord validations" 19 | gem.description = "Postgresql constraints in migrations similiar to ActiveRecord validations" 20 | gem.email = "vprokopchuk@gmail.com" 21 | gem.authors = ["Valeriy Prokopchuk"] 22 | gem.files = Dir.glob('lib/**/*.rb') 23 | gem.required_ruby_version = '>= 2.2.5' 24 | end 25 | Jeweler::RubygemsDotOrgTasks.new 26 | 27 | require 'rspec/core' 28 | require 'rspec/core/rake_task' 29 | RSpec::Core::RakeTask.new(:spec) do |spec| 30 | spec.pattern = FileList['spec/**/*_spec.rb'] 31 | end 32 | 33 | RSpec::Core::RakeTask.new(:rcov) do |spec| 34 | spec.pattern = 'spec/**/*_spec.rb' 35 | spec.rcov = true 36 | end 37 | 38 | task :default => :spec 39 | 40 | require 'rdoc/task' 41 | Rake::RDocTask.new do |rdoc| 42 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 43 | 44 | rdoc.rdoc_dir = 'rdoc' 45 | rdoc.title = "mv-postgresql #{version}" 46 | rdoc.rdoc_files.include('README*') 47 | rdoc.rdoc_files.include('lib/**/*.rb') 48 | end 49 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 3.0.0 -------------------------------------------------------------------------------- /lib/mv-postgresql.rb: -------------------------------------------------------------------------------- 1 | require 'mv-core' 2 | require 'mv/postgresql/railtie' 3 | -------------------------------------------------------------------------------- /lib/mv/postgresql/active_record/connection_adapters/postgresql_adapter_decorator.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module ActiveRecord 4 | module ConnectionAdapters 5 | module PostgresqlAdapterDecorator 6 | include Mv::Core::ActiveRecord::ConnectionAdapters::AbstractAdapterDecorator 7 | end 8 | end 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/constraint/builder/check.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Constraint 4 | module Builder 5 | class Check < Mv::Core::Constraint::Builder::Base 6 | def create 7 | validation_builders.group_by(&:table_name).each do |table_name, validations| 8 | db.execute(drop_check_statement(table_name)) 9 | db.execute(create_check_statement(table_name)) 10 | end 11 | end 12 | 13 | def update new_constraint_builder 14 | delete 15 | new_constraint_builder.create 16 | end 17 | 18 | def delete 19 | validation_builders.group_by(&:table_name).each do |table_name, validations| 20 | if db.data_source_exists?(table_name) 21 | db.execute(drop_check_statement(table_name)) 22 | end 23 | end 24 | end 25 | 26 | private 27 | 28 | def check_body(table_name) 29 | validation_builders.select{|b| b.table_name == table_name }.collect(&:conditions).flatten.collect do |condition| 30 | "(#{condition[:statement]})" 31 | end.join(" AND ") 32 | end 33 | 34 | def create_check_statement(table_name) 35 | "ALTER TABLE #{table_name} ADD CONSTRAINT #{name} CHECK (#{check_body(table_name)});" 36 | end 37 | 38 | def drop_check_statement(table_name) 39 | "ALTER TABLE #{table_name} DROP CONSTRAINT IF EXISTS #{name};" 40 | end 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/mv/postgresql/constraint/builder/trigger.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Constraint 4 | module Builder 5 | class Trigger < Mv::Core::Constraint::Builder::Trigger 6 | def create 7 | validation_builders.group_by(&:table_name).each do |table_name, validations| 8 | db.execute(drop_trigger_statement(table_name)) 9 | db.execute(drop_function_statement()) 10 | db.execute(create_function_statement(table_name)) 11 | db.execute(create_trigger_statement(table_name)) 12 | end 13 | end 14 | 15 | def delete 16 | validation_builders.group_by(&:table_name).each do |table_name, validations| 17 | if db.data_source_exists?(table_name) 18 | db.execute(drop_trigger_statement(table_name)) 19 | db.execute(drop_function_statement()) 20 | end 21 | end 22 | end 23 | 24 | def update new_constraint_builder 25 | delete 26 | new_constraint_builder.create 27 | end 28 | 29 | private 30 | 31 | def func_name 32 | "#{name}_func" 33 | end 34 | 35 | def drop_trigger_statement table_name 36 | "DROP TRIGGER IF EXISTS #{name} ON #{table_name};" 37 | end 38 | 39 | def create_trigger_statement table_name 40 | "CREATE TRIGGER #{name} BEFORE #{update? ? 'UPDATE' : 'INSERT'} ON #{table_name} 41 | FOR EACH ROW EXECUTE PROCEDURE #{func_name}();".squish 42 | end 43 | 44 | def function_body(table_name) 45 | validation_builders.select{|b| b.table_name == table_name }.collect(&:conditions).flatten.collect do |condition| 46 | "IF NOT(#{condition[:statement]}) THEN 47 | RAISE EXCEPTION '#{condition[:message]}'; 48 | END IF".squish 49 | end.join("; \n") 50 | end 51 | 52 | def drop_function_statement 53 | "DROP FUNCTION IF EXISTS #{func_name}();" 54 | end 55 | 56 | def create_function_statement table_name 57 | "CREATE FUNCTION #{func_name}() RETURNS TRIGGER AS $#{func_name}$ 58 | BEGIN 59 | #{function_body(table_name)}; 60 | 61 | RETURN NEW; 62 | END; 63 | $#{func_name}$ LANGUAGE plpgsql;" 64 | end 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/mv/postgresql/constraint/check.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Constraint 4 | class Check < Mv::Core::Constraint::Base 5 | def initialize description 6 | super 7 | end 8 | end 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/loader.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/route/check' 2 | 3 | require 'mv/postgresql/constraint/check' 4 | 5 | require 'mv/postgresql/constraint/builder/trigger' 6 | require 'mv/postgresql/constraint/builder/check' 7 | 8 | require 'mv/postgresql/validation/exclusion' 9 | require 'mv/postgresql/validation/format' 10 | require 'mv/postgresql/validation/inclusion' 11 | require 'mv/postgresql/validation/length' 12 | require 'mv/postgresql/validation/presence' 13 | require 'mv/postgresql/validation/absence' 14 | require 'mv/postgresql/validation/custom' 15 | 16 | require 'mv/postgresql/validation/builder/trigger/exclusion' 17 | require 'mv/postgresql/validation/builder/trigger/inclusion' 18 | require 'mv/postgresql/validation/builder/trigger/length' 19 | require 'mv/postgresql/validation/builder/trigger/format' 20 | require 'mv/postgresql/validation/builder/trigger/presence' 21 | require 'mv/postgresql/validation/builder/trigger/absence' 22 | require 'mv/postgresql/validation/builder/trigger/uniqueness' 23 | require 'mv/postgresql/validation/builder/trigger/custom' 24 | 25 | require 'mv/postgresql/validation/active_model_presenter/format' 26 | 27 | require 'mv/postgresql/active_record/connection_adapters/postgresql_adapter_decorator' 28 | 29 | #router 30 | Mv::Core::Router.define_route(:check, Mv::Postgresql::Route::Check) 31 | 32 | #constraints 33 | Mv::Core::Constraint::Factory.register_constraint(:check, Mv::Postgresql::Constraint::Check) 34 | 35 | #constraint builders 36 | Mv::Core::Constraint::Builder::Factory.register_builders( 37 | Mv::Core::Constraint::Trigger => Mv::Postgresql::Constraint::Builder::Trigger, 38 | Mv::Postgresql::Constraint::Check => Mv::Postgresql::Constraint::Builder::Check 39 | ) 40 | 41 | #validations 42 | Mv::Core::Validation::Factory.register_validations( 43 | :exclusion => Mv::Postgresql::Validation::Exclusion, 44 | :format => Mv::Postgresql::Validation::Format, 45 | :inclusion => Mv::Postgresql::Validation::Inclusion, 46 | :length => Mv::Postgresql::Validation::Length, 47 | :presence => Mv::Postgresql::Validation::Presence, 48 | :absence => Mv::Postgresql::Validation::Absence, 49 | :custom => Mv::Postgresql::Validation::Custom 50 | ) 51 | 52 | #validation builders in trigger 53 | Mv::Postgresql::Constraint::Builder::Trigger.validation_builders_factory.register_builders( 54 | Mv::Postgresql::Validation::Exclusion => Mv::Postgresql::Validation::Builder::Trigger::Exclusion, 55 | Mv::Postgresql::Validation::Inclusion => Mv::Postgresql::Validation::Builder::Trigger::Inclusion, 56 | Mv::Postgresql::Validation::Length => Mv::Postgresql::Validation::Builder::Trigger::Length, 57 | Mv::Postgresql::Validation::Format => Mv::Postgresql::Validation::Builder::Trigger::Format, 58 | Mv::Postgresql::Validation::Presence => Mv::Postgresql::Validation::Builder::Trigger::Presence, 59 | Mv::Postgresql::Validation::Absence => Mv::Postgresql::Validation::Builder::Trigger::Absence, 60 | Mv::Core::Validation::Uniqueness => Mv::Postgresql::Validation::Builder::Trigger::Uniqueness, 61 | Mv::Postgresql::Validation::Custom => Mv::Postgresql::Validation::Builder::Trigger::Custom, 62 | ) 63 | 64 | #validation builders in check 65 | Mv::Postgresql::Constraint::Builder::Check.validation_builders_factory.register_builders( 66 | Mv::Postgresql::Validation::Exclusion => Mv::Postgresql::Validation::Builder::Exclusion, 67 | Mv::Postgresql::Validation::Inclusion => Mv::Postgresql::Validation::Builder::Inclusion, 68 | Mv::Postgresql::Validation::Length => Mv::Core::Validation::Builder::Length, 69 | Mv::Postgresql::Validation::Format => Mv::Postgresql::Validation::Builder::Format, 70 | Mv::Postgresql::Validation::Presence => Mv::Core::Validation::Builder::Presence, 71 | Mv::Postgresql::Validation::Absence => Mv::Core::Validation::Builder::Absence, 72 | Mv::Postgresql::Validation::Custom => Mv::Core::Validation::Builder::Custom 73 | ) 74 | 75 | #validation active model presenters 76 | Mv::Core::Validation::ActiveModelPresenter::Factory.register_presenters( 77 | Mv::Postgresql::Validation::Exclusion => Mv::Core::Validation::ActiveModelPresenter::Exclusion, 78 | Mv::Postgresql::Validation::Inclusion => Mv::Core::Validation::ActiveModelPresenter::Inclusion, 79 | Mv::Postgresql::Validation::Length => Mv::Core::Validation::ActiveModelPresenter::Length, 80 | Mv::Postgresql::Validation::Presence => Mv::Core::Validation::ActiveModelPresenter::Presence, 81 | Mv::Postgresql::Validation::Absence => Mv::Core::Validation::ActiveModelPresenter::Absence, 82 | Mv::Postgresql::Validation::Format => Mv::Postgresql::Validation::ActiveModelPresenter::Format 83 | ) 84 | 85 | ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send( 86 | :prepend, 87 | Mv::Postgresql::ActiveRecord::ConnectionAdapters::PostgresqlAdapterDecorator 88 | ) 89 | -------------------------------------------------------------------------------- /lib/mv/postgresql/railtie.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | class Railtie < ::Rails::Railtie 4 | initializer 'mv-postgresql.initialization', after: 'active_record.initialize_database' do 5 | if defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) && 6 | ::ActiveRecord::Base.connection.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) 7 | require 'mv/postgresql/loader.rb' 8 | end 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/mv/postgresql/route/check.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Route 4 | class Check 5 | attr_reader :validation 6 | 7 | def initialize(validation) 8 | @validation = validation 9 | end 10 | 11 | def route 12 | [Mv::Core::Constraint::Description.new(validation.check_name, :check)] 13 | end 14 | end 15 | end 16 | end 17 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/absence.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/check_support' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | class Absence < Mv::Core::Validation::Absence 7 | include CheckSupport 8 | end 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/active_model_presenter/format.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Validation 4 | module ActiveModelPresenter 5 | class Format < Mv::Core::Validation::ActiveModelPresenter::Base 6 | def option_names 7 | super + [:with] 8 | end 9 | 10 | def validation_name 11 | :format 12 | end 13 | end 14 | end 15 | end 16 | end 17 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/exclusion.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Validation 4 | module Builder 5 | class Exclusion < Mv::Core::Validation::Builder::Exclusion 6 | protected 7 | 8 | def db_value value 9 | return "'#{value.strftime('%Y-%m-%d %H:%M:%S')}'" if value.is_a?(DateTime) 10 | return "'#{value.strftime('%Y-%m-%d %H:%M:%S')}'" if value.is_a?(Time) 11 | return "'#{value.strftime('%Y-%m-%d')}'" if value.is_a?(Date) 12 | super 13 | end 14 | end 15 | end 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/format.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Validation 4 | module Builder 5 | class Format < Mv::Core::Validation::Builder::Base 6 | delegate :with, to: :validation 7 | 8 | def conditions 9 | [{ 10 | statement: apply_allow_nil_and_blank(apply_with(column_reference)), 11 | message: message 12 | }] 13 | end 14 | 15 | protected 16 | 17 | def db_value value 18 | return "'#{value.source}'" if value.is_a?(Regexp) 19 | return "'#{value.to_s}'" if value.is_a?(String) 20 | raise Mv::Core::Error.new(table_name: table_name, 21 | column_name: column_name, 22 | validation_type: :inclusion, 23 | options: { in: value }, 24 | error: "#{value.class} is not supported as :with value") 25 | end 26 | 27 | def apply_with stmt 28 | "#{stmt} ~ #{db_value(with)}" 29 | end 30 | end 31 | end 32 | end 33 | end 34 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/inclusion.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Validation 4 | module Builder 5 | class Inclusion < Mv::Core::Validation::Builder::Inclusion 6 | protected 7 | 8 | def db_value value 9 | return "'#{value.strftime('%Y-%m-%d %H:%M:%S')}'" if value.is_a?(DateTime) 10 | return "'#{value.strftime('%Y-%m-%d %H:%M:%S')}'" if value.is_a?(Time) 11 | return "'#{value.strftime('%Y-%m-%d')}'" if value.is_a?(Date) 12 | return value if value.is_a?(TrueClass) || value.is_a?(FalseClass) 13 | super 14 | end 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/absence.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | module Builder 7 | module Trigger 8 | class Absence < Mv::Core::Validation::Builder::Absence 9 | include TriggerColumn 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/custom.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | module Builder 7 | module Trigger 8 | class Custom < Mv::Core::Validation::Builder::Custom 9 | include TriggerColumn 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/exclusion.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/exclusion' 2 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 3 | 4 | module Mv 5 | module Postgresql 6 | module Validation 7 | module Builder 8 | module Trigger 9 | class Exclusion < Mv::Postgresql::Validation::Builder::Exclusion 10 | include TriggerColumn 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/format.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/format' 2 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 3 | 4 | module Mv 5 | module Postgresql 6 | module Validation 7 | module Builder 8 | module Trigger 9 | class Format < Mv::Postgresql::Validation::Builder::Format 10 | include TriggerColumn 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/inclusion.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/inclusion' 2 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 3 | 4 | module Mv 5 | module Postgresql 6 | module Validation 7 | module Builder 8 | module Trigger 9 | class Inclusion < Mv::Postgresql::Validation::Builder::Inclusion 10 | include TriggerColumn 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/length.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | module Builder 7 | module Trigger 8 | class Length < Mv::Core::Validation::Builder::Length 9 | include TriggerColumn 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/presence.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | module Builder 7 | module Trigger 8 | class Presence < Mv::Core::Validation::Builder::Presence 9 | include TriggerColumn 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/trigger_column.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Validation 4 | module Builder 5 | module Trigger 6 | module TriggerColumn 7 | protected 8 | 9 | def column_reference 10 | "NEW.#{super}" 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end 17 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/builder/trigger/uniqueness.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/builder/trigger/trigger_column' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | module Builder 7 | module Trigger 8 | class Uniqueness < Mv::Core::Validation::Builder::Uniqueness 9 | include TriggerColumn 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/check_support.rb: -------------------------------------------------------------------------------- 1 | module Mv 2 | module Postgresql 3 | module Validation 4 | module CheckSupport 5 | attr_reader :check_name 6 | 7 | def self.included mod 8 | mod.validates :check_name, absence: { message: 'allowed when :as == :trigger' }, unless: :check? 9 | end 10 | 11 | def initialize(table_name, column_name, opts) 12 | super 13 | 14 | @check_name = options.with_indifferent_access[:check_name] || default_check_name 15 | end 16 | 17 | def to_a 18 | super + [check_name.to_s] 19 | end 20 | 21 | protected 22 | 23 | def available_as 24 | super + [:check] 25 | end 26 | 27 | def default_as 28 | :check 29 | end 30 | 31 | def default_check_name 32 | "chk_mv_#{table_name}_#{column_name}" if check? 33 | end 34 | 35 | private 36 | 37 | def check? 38 | as.try(:to_sym) == :check 39 | end 40 | 41 | end 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/custom.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/check_support' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | class Custom < Mv::Core::Validation::Custom 7 | include CheckSupport 8 | end 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/exclusion.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/check_support' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | class Exclusion < Mv::Core::Validation::Exclusion 7 | include CheckSupport 8 | end 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/format.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/check_support' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | class Format < Mv::Core::Validation::Base 7 | include CheckSupport 8 | 9 | attr_reader :with 10 | 11 | validates :with, presence: true 12 | 13 | def initialize(table_name, column_name, opts) 14 | opts = { with: opts } unless opts.is_a?(Hash) 15 | 16 | super(table_name, column_name, opts) 17 | 18 | @with = opts.with_indifferent_access[:with] 19 | end 20 | 21 | def to_a 22 | super + [with.to_s] 23 | end 24 | end 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/inclusion.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/check_support' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | class Inclusion < Mv::Core::Validation::Inclusion 7 | include CheckSupport 8 | end 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/length.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/check_support' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | class Length < Mv::Core::Validation::Length 7 | include CheckSupport 8 | end 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/mv/postgresql/validation/presence.rb: -------------------------------------------------------------------------------- 1 | require 'mv/postgresql/validation/check_support' 2 | 3 | module Mv 4 | module Postgresql 5 | module Validation 6 | class Presence < Mv::Core::Validation::Presence 7 | include CheckSupport 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /mv-postgresql.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | # stub: mv-postgresql 3.0.0 ruby lib 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "mv-postgresql" 9 | s.version = "3.0.0" 10 | 11 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 12 | s.require_paths = ["lib"] 13 | s.authors = ["Valeriy Prokopchuk"] 14 | s.date = "2016-12-31" 15 | s.description = "Postgresql constraints in migrations similiar to ActiveRecord validations" 16 | s.email = "vprokopchuk@gmail.com" 17 | s.extra_rdoc_files = [ 18 | "LICENSE.txt", 19 | "README.md" 20 | ] 21 | s.files = [ 22 | "lib/mv-postgresql.rb", 23 | "lib/mv/postgresql/active_record/connection_adapters/postgresql_adapter_decorator.rb", 24 | "lib/mv/postgresql/constraint/builder/check.rb", 25 | "lib/mv/postgresql/constraint/builder/trigger.rb", 26 | "lib/mv/postgresql/constraint/check.rb", 27 | "lib/mv/postgresql/loader.rb", 28 | "lib/mv/postgresql/railtie.rb", 29 | "lib/mv/postgresql/route/check.rb", 30 | "lib/mv/postgresql/validation/absence.rb", 31 | "lib/mv/postgresql/validation/active_model_presenter/format.rb", 32 | "lib/mv/postgresql/validation/builder/exclusion.rb", 33 | "lib/mv/postgresql/validation/builder/format.rb", 34 | "lib/mv/postgresql/validation/builder/inclusion.rb", 35 | "lib/mv/postgresql/validation/builder/trigger/absence.rb", 36 | "lib/mv/postgresql/validation/builder/trigger/custom.rb", 37 | "lib/mv/postgresql/validation/builder/trigger/exclusion.rb", 38 | "lib/mv/postgresql/validation/builder/trigger/format.rb", 39 | "lib/mv/postgresql/validation/builder/trigger/inclusion.rb", 40 | "lib/mv/postgresql/validation/builder/trigger/length.rb", 41 | "lib/mv/postgresql/validation/builder/trigger/presence.rb", 42 | "lib/mv/postgresql/validation/builder/trigger/trigger_column.rb", 43 | "lib/mv/postgresql/validation/builder/trigger/uniqueness.rb", 44 | "lib/mv/postgresql/validation/check_support.rb", 45 | "lib/mv/postgresql/validation/custom.rb", 46 | "lib/mv/postgresql/validation/exclusion.rb", 47 | "lib/mv/postgresql/validation/format.rb", 48 | "lib/mv/postgresql/validation/inclusion.rb", 49 | "lib/mv/postgresql/validation/length.rb", 50 | "lib/mv/postgresql/validation/presence.rb" 51 | ] 52 | s.homepage = "http://github.com/vprokopchuk256/mv-postgresql" 53 | s.licenses = ["MIT"] 54 | s.required_ruby_version = Gem::Requirement.new(">= 2.2.5") 55 | s.rubygems_version = "2.4.5.1" 56 | s.summary = "Postgresql constraints in migrations similiar to ActiveRecord validations" 57 | 58 | if s.respond_to? :specification_version then 59 | s.specification_version = 4 60 | 61 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 62 | s.add_runtime_dependency(%q, ["~> 5.0"]) 63 | s.add_runtime_dependency(%q, ["~> 0.17"]) 64 | s.add_runtime_dependency(%q, ["~> 3.0"]) 65 | s.add_development_dependency(%q, ["~> 2.0"]) 66 | s.add_development_dependency(%q, ["~> 3.1"]) 67 | s.add_development_dependency(%q, ["~> 1.1"]) 68 | s.add_development_dependency(%q, ["~> 4.5"]) 69 | s.add_development_dependency(%q, ["~> 3.5"]) 70 | else 71 | s.add_dependency(%q, ["~> 5.0"]) 72 | s.add_dependency(%q, ["~> 0.17"]) 73 | s.add_dependency(%q, ["~> 3.0"]) 74 | s.add_dependency(%q, ["~> 2.0"]) 75 | s.add_dependency(%q, ["~> 3.1"]) 76 | s.add_dependency(%q, ["~> 1.1"]) 77 | s.add_dependency(%q, ["~> 4.5"]) 78 | s.add_dependency(%q, ["~> 3.5"]) 79 | end 80 | else 81 | s.add_dependency(%q, ["~> 5.0"]) 82 | s.add_dependency(%q, ["~> 0.17"]) 83 | s.add_dependency(%q, ["~> 3.0"]) 84 | s.add_dependency(%q, ["~> 2.0"]) 85 | s.add_dependency(%q, ["~> 3.1"]) 86 | s.add_dependency(%q, ["~> 1.1"]) 87 | s.add_dependency(%q, ["~> 4.5"]) 88 | s.add_dependency(%q, ["~> 3.5"]) 89 | end 90 | end 91 | 92 | -------------------------------------------------------------------------------- /spec/mv/postgresql/active_record/connection_adapters/postgresql_adapter_decorator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/active_record/connection_adapters/postgresql_adapter_decorator' 4 | 5 | describe Mv::Postgresql::ActiveRecord::ConnectionAdapters::PostgresqlAdapterDecorator do 6 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/constraint/builder/check_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/constraint/builder/check' 4 | 5 | describe Mv::Postgresql::Constraint::Builder::Check do 6 | before do 7 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 8 | Mv::Core::Db::MigrationValidator.delete_all 9 | ActiveRecord::Base.connection.drop_table(:table_name) if ActiveRecord::Base.connection.data_source_exists?(:table_name) 10 | ActiveRecord::Base.connection.create_table(:table_name) do |t| 11 | t.string :column_name 12 | end 13 | end 14 | 15 | describe "#validation_builders" do 16 | let(:check_description) { Mv::Core::Constraint::Description.new(:chk_table_name_column_name, :check) } 17 | let(:check_constraint) { Mv::Core::Constraint::Trigger.new(check_description) } 18 | 19 | subject(:check_builder) { described_class.new(check_constraint) } 20 | 21 | subject { check_builder.validation_builders } 22 | 23 | before do 24 | check_constraint.validations << validation 25 | end 26 | 27 | describe "when exlusion validation provided" do 28 | let(:validation) { 29 | Mv::Postgresql::Validation::Exclusion.new(:table_name, :column_name, in: [1, 3], as: :check) 30 | } 31 | 32 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Exclusion) } 33 | end 34 | 35 | describe "when inclusion validation provided" do 36 | let(:validation) { 37 | Mv::Postgresql::Validation::Inclusion.new(:table_name, :column_name, in: [1, 3], as: :check) 38 | } 39 | 40 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Inclusion) } 41 | end 42 | 43 | describe "when length validation provided" do 44 | let(:validation) { 45 | Mv::Postgresql::Validation::Length.new(:table_name, :column_name, in: [1, 3], as: :check) 46 | } 47 | 48 | its(:first) { is_expected.to be_a_kind_of(Mv::Core::Validation::Builder::Length) } 49 | end 50 | 51 | describe "when format validation provided" do 52 | let(:validation) { 53 | Mv::Postgresql::Validation::Format.new(:table_name, :column_name, with: /exp/, as: :check) 54 | } 55 | 56 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Format) } 57 | end 58 | 59 | describe "when presence validation provided" do 60 | let(:validation) { 61 | Mv::Postgresql::Validation::Presence.new(:table_name, :column_name, as: :check) 62 | } 63 | 64 | its(:first) { is_expected.to be_a_kind_of(Mv::Core::Validation::Builder::Presence) } 65 | end 66 | 67 | describe "when absence validation provided" do 68 | let(:validation) { 69 | Mv::Postgresql::Validation::Absence.new(:table_name, :column_name, as: :check) 70 | } 71 | 72 | its(:first) { is_expected.to be_a_kind_of(Mv::Core::Validation::Builder::Absence) } 73 | end 74 | 75 | describe "when custom validation provided" do 76 | let(:validation) { 77 | Mv::Postgresql::Validation::Custom.new(:table_name, 78 | :column_name, 79 | as: :trigger, 80 | update_trigger_name: :trg_mv_table_name) 81 | } 82 | 83 | its(:first) { is_expected.to be_a_kind_of(Mv::Core::Validation::Builder::Custom) } 84 | end 85 | end 86 | 87 | describe "SQL methods" do 88 | def checks name 89 | ActiveRecord::Base.connection.select_values("select conname from pg_constraint where contype='c' and conname='#{name}'") 90 | end 91 | 92 | let(:test_validation_builder_klass) do 93 | Class.new(Mv::Core::Validation::Builder::Presence) do 94 | def conditions 95 | [{ statement: '1 = 1', message: 'some error message' }] 96 | end 97 | end 98 | end 99 | 100 | before do 101 | Mv::Postgresql::Constraint::Builder::Check.validation_builders_factory.register_builder( 102 | Mv::Postgresql::Validation::Presence, 103 | test_validation_builder_klass 104 | ) 105 | end 106 | 107 | after do 108 | Mv::Postgresql::Constraint::Builder::Check.validation_builders_factory.register_builder( 109 | Mv::Postgresql::Validation::Presence, 110 | Mv::Core::Validation::Builder::Presence 111 | ) 112 | end 113 | 114 | let(:validation) { 115 | Mv::Postgresql::Validation::Presence.new(:table_name, :column_name, as: :check) 116 | } 117 | 118 | 119 | let(:check_description) { Mv::Core::Constraint::Description.new(:chk_table_name_column_name, :check) } 120 | 121 | let(:check) { Mv::Postgresql::Constraint::Check.new(check_description)} 122 | 123 | before do 124 | check.validations << validation 125 | ActiveRecord::Base.connection.execute('ALTER TABLE table_name DROP CONSTRAINT IF EXISTS chk_table_name_column_name;') 126 | end 127 | 128 | let(:check_builder) { Mv::Postgresql::Constraint::Builder::Check.new(check)} 129 | 130 | describe "#create" do 131 | subject { check_builder.create } 132 | 133 | describe "when check constraint not yet exist" do 134 | it "creates new check constraint" do 135 | expect { subject }.to change{ checks('chk_table_name_column_name').length }.from(0).to(1) 136 | end 137 | end 138 | 139 | describe "when check constraint already exist" do 140 | before do 141 | ActiveRecord::Base.connection.execute('ALTER TABLE table_name ADD CONSTRAINT chk_table_name_column_name CHECK (1 = 1);') 142 | end 143 | 144 | it "does not raise an error" do 145 | expect { subject }.not_to raise_error 146 | end 147 | end 148 | 149 | describe "when several validations provided" do 150 | before do 151 | check.validations << validation 152 | end 153 | 154 | it "does not raise an error" do 155 | expect{ subject }.not_to raise_error 156 | end 157 | end 158 | end 159 | 160 | describe "#update" do 161 | subject { check_builder.update(check_builder) } 162 | 163 | describe "when check constraint not yet exist" do 164 | it "creates new check constraint" do 165 | expect { subject }.to change{ checks('chk_table_name_column_name').length }.from(0).to(1) 166 | end 167 | end 168 | 169 | describe "when check constraint already exist" do 170 | before do 171 | ActiveRecord::Base.connection.execute('ALTER TABLE table_name ADD CONSTRAINT chk_table_name_column_name CHECK (1 = 1);') 172 | end 173 | 174 | it "does not raise an error" do 175 | expect { subject }.not_to raise_error 176 | end 177 | end 178 | end 179 | 180 | describe "#delete" do 181 | subject { check_builder.delete } 182 | 183 | describe "when check constraint not yet exist" do 184 | it "does not raise an error" do 185 | expect { subject }.not_to raise_error 186 | end 187 | end 188 | 189 | describe "when table does not exist" do 190 | before { ActiveRecord::Base.connection.drop_table(:table_name) if ActiveRecord::Base.connection.data_source_exists?(:table_name)} 191 | 192 | it "does not raise an error" do 193 | expect { subject }.not_to raise_error 194 | end 195 | end 196 | 197 | describe "when check constraint already exist" do 198 | before do 199 | ActiveRecord::Base.connection.execute('ALTER TABLE table_name ADD CONSTRAINT chk_table_name_column_name CHECK (1 = 1);') 200 | end 201 | 202 | it "deletes check constraint" do 203 | expect { subject }.to change{ checks('chk_table_name_column_name').length }.from(1).to(0) 204 | end 205 | end 206 | end 207 | end 208 | end 209 | -------------------------------------------------------------------------------- /spec/mv/postgresql/constraint/builder/factory_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/constraint/builder/trigger' 4 | require 'mv/postgresql/constraint/builder/check' 5 | 6 | describe Mv::Core::Constraint::Builder::Factory do 7 | describe "#create_builder" do 8 | subject { described_class.create_builder(constraint) } 9 | 10 | describe "for trigger constraint" do 11 | let(:trigger_description) { Mv::Core::Constraint::Description.new(:trg_table_name_upd, :trigger, event: :update) } 12 | let(:constraint) { Mv::Core::Constraint::Trigger.new(trigger_description)} 13 | 14 | it { is_expected.to be_a_kind_of(Mv::Postgresql::Constraint::Builder::Trigger) } 15 | end 16 | 17 | describe "for check constraint" do 18 | let(:check_description) { Mv::Core::Constraint::Description.new(:trg_table_name_upd, :check) } 19 | let(:constraint) { Mv::Postgresql::Constraint::Check.new(check_description)} 20 | 21 | it { is_expected.to be_a_kind_of(Mv::Postgresql::Constraint::Builder::Check) } 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/constraint/builder/trigger_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/constraint/builder/trigger' 4 | 5 | describe Mv::Postgresql::Constraint::Builder::Trigger do 6 | before do 7 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 8 | Mv::Core::Db::MigrationValidator.delete_all 9 | ActiveRecord::Base.connection.drop_table(:table_name) if ActiveRecord::Base.connection.data_source_exists?(:table_name) 10 | ActiveRecord::Base.connection.create_table(:table_name) do |t| 11 | t.string :column_name 12 | end 13 | end 14 | 15 | describe "#validation_builders" do 16 | let(:trigger_description) { Mv::Core::Constraint::Description.new(:trg_mv_table_name, :trigger) } 17 | let(:trigger_constraint) { Mv::Core::Constraint::Trigger.new(trigger_description) } 18 | 19 | subject(:trigger_builder) { described_class.new(trigger_constraint) } 20 | 21 | subject { trigger_builder.validation_builders } 22 | 23 | before do 24 | trigger_constraint.validations << validation 25 | end 26 | 27 | describe "when exlusion validation provided" do 28 | let(:validation) { 29 | Mv::Postgresql::Validation::Exclusion.new(:table_name, 30 | :column_name, 31 | in: [1, 3], 32 | as: :trigger, 33 | update_trigger_name: :trg_mv_table_name) 34 | } 35 | 36 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Exclusion) } 37 | end 38 | 39 | describe "when inclusion validation provided" do 40 | let(:validation) { 41 | Mv::Postgresql::Validation::Inclusion.new(:table_name, 42 | :column_name, 43 | in: [1, 3], 44 | as: :trigger, 45 | update_trigger_name: :trg_mv_table_name) 46 | } 47 | 48 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Inclusion) } 49 | end 50 | 51 | describe "when length validation provided" do 52 | let(:validation) { 53 | Mv::Postgresql::Validation::Length.new(:table_name, 54 | :column_name, 55 | in: [1, 3], 56 | as: :trigger, 57 | update_trigger_name: :trg_mv_table_name) 58 | } 59 | 60 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Length) } 61 | end 62 | 63 | describe "when format validation provided" do 64 | let(:validation) { 65 | Mv::Postgresql::Validation::Format.new(:table_name, 66 | :column_name, 67 | with: /exp/, 68 | as: :trigger, 69 | update_trigger_name: :trg_mv_table_name) 70 | } 71 | 72 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Format) } 73 | end 74 | 75 | describe "when presence validation provided" do 76 | let(:validation) { 77 | Mv::Postgresql::Validation::Presence.new(:table_name, 78 | :column_name, 79 | as: :trigger, 80 | update_trigger_name: :trg_mv_table_name) 81 | } 82 | 83 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Presence) } 84 | end 85 | 86 | describe "when absence validation provided" do 87 | let(:validation) { 88 | Mv::Postgresql::Validation::Absence.new(:table_name, 89 | :column_name, 90 | as: :trigger, 91 | update_trigger_name: :trg_mv_table_name) 92 | } 93 | 94 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Absence) } 95 | end 96 | 97 | describe "when uniqueness validation provided" do 98 | let(:validation) { 99 | Mv::Core::Validation::Uniqueness.new(:table_name, 100 | :column_name, 101 | as: :trigger, 102 | update_trigger_name: :trg_mv_table_name) 103 | } 104 | 105 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Uniqueness) } 106 | end 107 | 108 | describe "when custom validation provided" do 109 | let(:validation) { 110 | Mv::Postgresql::Validation::Custom.new(:table_name, 111 | :column_name, 112 | as: :trigger, 113 | update_trigger_name: :trg_mv_table_name) 114 | } 115 | 116 | its(:first) { is_expected.to be_a_kind_of(Mv::Postgresql::Validation::Builder::Trigger::Custom) } 117 | end 118 | end 119 | 120 | describe "SQL methods" do 121 | def triggers name 122 | ActiveRecord::Base.connection.select_values("select tgname from pg_trigger where tgname = '#{name}'") 123 | end 124 | 125 | def procs name 126 | ActiveRecord::Base.connection.select_values("select proname from pg_proc where proname = '#{name}'") 127 | end 128 | 129 | before do 130 | Mv::Postgresql::Constraint::Builder::Trigger.validation_builders_factory.register_builder( 131 | Mv::Postgresql::Validation::Presence, 132 | test_validation_builder_klass 133 | ) 134 | end 135 | 136 | after do 137 | Mv::Postgresql::Constraint::Builder::Trigger.validation_builders_factory.register_builder( 138 | Mv::Postgresql::Validation::Presence, 139 | Mv::Postgresql::Validation::Builder::Trigger::Presence 140 | ) 141 | end 142 | 143 | let(:validation) { 144 | Mv::Postgresql::Validation::Presence.new(:table_name, 145 | :column_name, 146 | as: :trigger, 147 | update_trigger_name: :trg_mv_table_name_upd, 148 | create_trigger_name: :trg_mv_table_name_ins) 149 | } 150 | 151 | let(:test_validation_builder_klass) do 152 | Class.new(Mv::Postgresql::Validation::Builder::Trigger::Presence) do 153 | def conditions 154 | [{ statement: '1 = 1', message: 'some error message' }] 155 | end 156 | end 157 | end 158 | 159 | let(:create_trigger_description) { Mv::Core::Constraint::Description.new(:trg_mv_table_name_ins, :trigger, event: :create) } 160 | let(:update_trigger_description) { Mv::Core::Constraint::Description.new(:trg_mv_table_name_upd, :trigger, event: :update) } 161 | 162 | let(:create_trigger) { Mv::Core::Constraint::Trigger.new(create_trigger_description)} 163 | let(:update_trigger) { Mv::Core::Constraint::Trigger.new(update_trigger_description)} 164 | 165 | before do 166 | create_trigger.validations << validation 167 | update_trigger.validations << validation 168 | ActiveRecord::Base.connection.execute('DROP TRIGGER IF EXISTS trg_mv_table_name_ins ON table_name;') 169 | ActiveRecord::Base.connection.execute('DROP FUNCTION IF EXISTS trg_mv_table_name_ins_func();') 170 | end 171 | 172 | let(:create_trigger_builder) { Mv::Postgresql::Constraint::Builder::Trigger.new(create_trigger)} 173 | let(:update_trigger_builder) { Mv::Postgresql::Constraint::Builder::Trigger.new(update_trigger)} 174 | 175 | describe "#create" do 176 | subject { create_trigger_builder.create } 177 | 178 | describe "when both trigger and trigger function do not exist" do 179 | it "creates new trigger" do 180 | expect { subject }.to change{ triggers('trg_mv_table_name_ins').length }.from(0).to(1) 181 | end 182 | 183 | it "create new trigger function" do 184 | expect { subject }.to change{ procs('trg_mv_table_name_ins_func').length }.from(0).to(1) 185 | end 186 | end 187 | 188 | describe "when function exists but trigger does not" do 189 | before do 190 | ActiveRecord::Base.connection.execute( 191 | "CREATE FUNCTION trg_mv_table_name_ins_func() RETURNS TRIGGER AS $trg_mv_table_name_ins_func$ 192 | BEGIN 193 | IF NOT(1 = 1) THEN 194 | RAISE EXCEPTION 'some error exception'; 195 | END IF; 196 | 197 | RETURN NEW; 198 | END; 199 | $trg_mv_table_name_ins_func$ LANGUAGE plpgsql;" 200 | ) 201 | end 202 | 203 | it "creates new trigger" do 204 | expect { subject }.to change{ triggers('trg_mv_table_name_ins').length }.from(0).to(1) 205 | end 206 | end 207 | 208 | describe "when several validations provided" do 209 | before do 210 | create_trigger.validations << validation 211 | end 212 | 213 | it "does not raise an error" do 214 | expect{ subject }.not_to raise_error 215 | end 216 | end 217 | end 218 | 219 | describe "#update" do 220 | subject { create_trigger_builder.update(create_trigger_builder) } 221 | 222 | describe "when both trigger and trigger function exist" do 223 | before do 224 | ActiveRecord::Base.connection.execute( 225 | "CREATE FUNCTION trg_mv_table_name_ins_func() RETURNS TRIGGER AS $trg_mv_table_name_ins_func$ 226 | BEGIN 227 | IF NOT(1 = 1) THEN 228 | RAISE EXCEPTION 'some error exception'; 229 | END IF; 230 | 231 | RETURN NEW; 232 | END; 233 | $trg_mv_table_name_ins_func$ LANGUAGE plpgsql;" 234 | ) 235 | ActiveRecord::Base.connection.execute( 236 | "CREATE TRIGGER trg_mv_table_name_ins 237 | BEFORE INSERT ON table_name 238 | FOR EACH ROW EXECUTE PROCEDURE trg_mv_table_name_ins_func();" 239 | ) 240 | end 241 | 242 | it "does not raise an error" do 243 | expect { subject }.not_to raise_error 244 | end 245 | end 246 | 247 | describe "when function exists but trigger does not" do 248 | before do 249 | ActiveRecord::Base.connection.execute( 250 | "CREATE FUNCTION trg_mv_table_name_ins_func() RETURNS TRIGGER AS $trg_mv_table_name_ins_func$ 251 | BEGIN 252 | IF NOT(1 = 1) THEN 253 | RAISE EXCEPTION 'some error exception'; 254 | END IF; 255 | 256 | RETURN NEW; 257 | END; 258 | $trg_mv_table_name_ins_func$ LANGUAGE plpgsql;" 259 | ) 260 | end 261 | 262 | it "creates new trigger" do 263 | expect { subject }.to change{ triggers('trg_mv_table_name_ins').length }.from(0).to(1) 264 | end 265 | end 266 | end 267 | 268 | describe "#delete" do 269 | subject { create_trigger_builder.delete } 270 | 271 | describe "when both trigger and trigger function exist" do 272 | before do 273 | ActiveRecord::Base.connection.execute( 274 | "CREATE FUNCTION trg_mv_table_name_ins_func() RETURNS TRIGGER AS $trg_mv_table_name_ins_func$ 275 | BEGIN 276 | IF NOT(1 = 1) THEN 277 | RAISE EXCEPTION 'some error exception'; 278 | END IF; 279 | 280 | RETURN NEW; 281 | END; 282 | $trg_mv_table_name_ins_func$ LANGUAGE plpgsql;" 283 | ) 284 | ActiveRecord::Base.connection.execute( 285 | "CREATE TRIGGER trg_mv_table_name_ins 286 | BEFORE INSERT ON table_name 287 | FOR EACH ROW EXECUTE PROCEDURE trg_mv_table_name_ins_func();" 288 | ) 289 | end 290 | 291 | it "deletes trigger" do 292 | expect { subject }.to change{ triggers('trg_mv_table_name_ins').length }.from(1).to(0) 293 | end 294 | 295 | it "deletes trigger function" do 296 | expect { subject }.to change{ procs('trg_mv_table_name_ins_func').length }.from(1).to(0) 297 | end 298 | end 299 | 300 | describe "when table does not exist" do 301 | before { ActiveRecord::Base.connection.drop_table(:table_name) if ActiveRecord::Base.connection.data_source_exists?(:table_name)} 302 | 303 | it "does not raise an error" do 304 | expect { subject }.not_to raise_error 305 | end 306 | end 307 | 308 | describe "when function exists but trigger does not" do 309 | before do 310 | ActiveRecord::Base.connection.execute( 311 | "CREATE FUNCTION trg_mv_table_name_ins_func() RETURNS TRIGGER AS $trg_mv_table_name_ins_func$ 312 | BEGIN 313 | IF NOT(1 = 1) THEN 314 | RAISE EXCEPTION 'some error exception'; 315 | END IF; 316 | 317 | RETURN NEW; 318 | END; 319 | $trg_mv_table_name_ins_func$ LANGUAGE plpgsql;" 320 | ) 321 | end 322 | 323 | it "deletes trigger function" do 324 | expect { subject }.to change{ procs('trg_mv_table_name_ins_func').length }.from(1).to(0) 325 | end 326 | end 327 | end 328 | end 329 | end 330 | -------------------------------------------------------------------------------- /spec/mv/postgresql/constraint/check_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/constraint/check' 4 | 5 | describe Mv::Postgresql::Constraint::Check do 6 | before do 7 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 8 | end 9 | 10 | let(:check_description) { Mv::Core::Constraint::Description.new(:chk_mv_table_name, :check)} 11 | 12 | subject(:check) { described_class.new(check_description) } 13 | 14 | describe "#initialize" do 15 | its(:description) { is_expected.to eq(check_description) } 16 | its(:validations) { is_expected.to eq([]) } 17 | end 18 | 19 | describe "#constraint interface" do 20 | it { is_expected.to respond_to(:create) } 21 | it { is_expected.to respond_to(:delete) } 22 | end 23 | 24 | describe "#<=>" do 25 | let(:inclusion) { Mv::Core::Validation::Inclusion.new(:table_name, :column_name, in: [1, 2], as: :check) } 26 | let(:exclusion) { Mv::Core::Validation::Exclusion.new(:table_name, :column_name, in: [0, 3], as: :check) } 27 | 28 | before do 29 | check.validations << inclusion 30 | check.validations << exclusion 31 | end 32 | 33 | it { is_expected.to eq(check) } 34 | 35 | describe "when description is different" do 36 | let(:other_check_description) { Mv::Core::Constraint::Description.new(:chk_mv_table_name_1, :check) } 37 | let(:other_check) { described_class.new(other_check_description) } 38 | 39 | before do 40 | other_check.validations << inclusion 41 | other_check.validations << exclusion 42 | end 43 | 44 | it { is_expected.not_to eq(other_check) } 45 | end 46 | 47 | describe "when validations list contains different elements" do 48 | let(:other_check) { described_class.new(check_description) } 49 | 50 | before do 51 | other_check.validations << inclusion 52 | end 53 | 54 | it { is_expected.not_to eq(other_check) } 55 | end 56 | 57 | describe "when validations list sorted differently" do 58 | let(:other_check) { described_class.new(check_description) } 59 | 60 | before do 61 | other_check.validations << exclusion 62 | other_check.validations << inclusion 63 | end 64 | 65 | it { is_expected.to eq(other_check) } 66 | end 67 | 68 | describe "when description and validations list are the same" do 69 | let(:other_check) { described_class.new(check_description) } 70 | 71 | before do 72 | other_check.validations << inclusion 73 | other_check.validations << exclusion 74 | end 75 | 76 | it { is_expected.to eq(other_check) } 77 | end 78 | end 79 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/constraint/factory_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Mv::Core::Constraint::Factory do 4 | before do 5 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 6 | end 7 | 8 | describe "#create_constraint" do 9 | subject { described_class.create_constraint(description) } 10 | 11 | describe "by default" do 12 | describe "index" do 13 | let(:description) { 14 | Mv::Core::Constraint::Description.new(:index_name, :index) 15 | } 16 | 17 | it { is_expected.to be_instance_of(Mv::Core::Constraint::Index) } 18 | end 19 | 20 | describe "check" do 21 | let(:description) { 22 | Mv::Core::Constraint::Description.new(:check_name, :check) 23 | } 24 | 25 | it { is_expected.to be_instance_of(Mv::Postgresql::Constraint::Check) } 26 | end 27 | 28 | describe "trigger" do 29 | let(:description) { 30 | Mv::Core::Constraint::Description.new(:trigger_name, :trigger, event: :create) 31 | } 32 | 33 | it { is_expected.to be_instance_of(Mv::Core::Constraint::Trigger) } 34 | end 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/add_validations_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Add validation scenarios' do 4 | before do 5 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 6 | Mv::Core::Db::MigrationValidator.delete_all 7 | ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:prepend, Mv::Postgresql::ActiveRecord::ConnectionAdapters::PostgresqlAdapterDecorator) 8 | 9 | Mv::Core::Migration::Base.with_suppressed_validations do 10 | ActiveRecord::Base.connection.drop_table(:table_name) if ActiveRecord::Base.connection.data_source_exists?(:table_name) 11 | end 12 | end 13 | 14 | subject(:migrate) { migration.migrate(:up) } 15 | 16 | describe 'change_table' do 17 | describe "create column" do 18 | before do 19 | ActiveRecord::Base.connection.create_table :table_name do |t| 20 | t.string :column_name 21 | end 22 | end 23 | 24 | let(:migration) do 25 | Class.new(::ActiveRecord::Migration[5.0]) do 26 | def change 27 | change_table :table_name, id: false do |t| 28 | t.string :column_name_1, validates: { length: { is: 5, as: :trigger, on: :create} } 29 | end 30 | end 31 | end.new('TestMigration', '20141118164617') 32 | end 33 | 34 | it "adds new validator" do 35 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(1) 36 | end 37 | 38 | it "creates new trigger constraint" do 39 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:create).once 40 | subject 41 | end 42 | end 43 | 44 | describe "update column" do 45 | before do 46 | ActiveRecord::Base.connection.create_table :table_name do |t| 47 | t.string :column_name 48 | end 49 | end 50 | 51 | let(:migration) do 52 | Class.new(::ActiveRecord::Migration[5.0]) do 53 | def change 54 | change_table :table_name, id: false do |t| 55 | t.change :column_name, :string, validates: { length: { is: 5, as: :trigger, on: :create} } 56 | end 57 | end 58 | end.new('TestMigration', '20141118164617') 59 | end 60 | 61 | it "adds new validator" do 62 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(1) 63 | end 64 | 65 | it "creates new trigger constraint" do 66 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:create).once 67 | subject 68 | end 69 | end 70 | end 71 | 72 | describe 'standalone' do 73 | describe "create column" do 74 | before do 75 | ActiveRecord::Base.connection.create_table :table_name do |t| 76 | t.string :column_name 77 | end 78 | end 79 | 80 | let(:migration) do 81 | Class.new(::ActiveRecord::Migration[5.0]) do 82 | def change 83 | add_column :table_name, :column_name_1, :string, validates: { length: { is: 5, as: :trigger, on: :create} } 84 | end 85 | end.new('TestMigration', '20141118164617') 86 | end 87 | 88 | 89 | it "adds new validator" do 90 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(1) 91 | end 92 | 93 | it "creates new trigger constraint" do 94 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:create).once 95 | subject 96 | end 97 | end 98 | 99 | describe "update column" do 100 | before do 101 | ActiveRecord::Base.connection.create_table :table_name do |t| 102 | t.string :column_name 103 | end 104 | end 105 | 106 | let(:migration) do 107 | Class.new(::ActiveRecord::Migration[5.0]) do 108 | def change 109 | change_column :table_name, :column_name, :string, validates: { length: { is: 5, as: :trigger, on: :create} } 110 | end 111 | end.new('TestMigration', '20141118164617') 112 | end 113 | 114 | it "adds new validator" do 115 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(1) 116 | end 117 | 118 | it "creates new trigger constraint" do 119 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:create).once 120 | subject 121 | end 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/check/absence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | AbsenceCheckTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "absence validation in check constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :absence, validates: { absence: { allow_nil: true, as: :check } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | end 23 | 24 | after { Mv::Core::Db::MigrationValidator.delete_all } 25 | 26 | subject(:insert) { AbsenceCheckTestTableName.create! opts } 27 | 28 | describe "with all nulls" do 29 | let(:opts) { {} } 30 | 31 | it "doesn't raise an error" do 32 | expect{ subject }.not_to raise_error 33 | end 34 | end 35 | 36 | describe "with all valid values" do 37 | let(:opts) { { 38 | absence: ' ', 39 | } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with invalid value" do 47 | let(:opts) { { 48 | absence: '1', 49 | } } 50 | 51 | it "raises an error with valid message" do 52 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/check/custom_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | CustomCheckTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "custom validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.integer :custom, validates: { custom: { statement: '{custom} > 1', allow_nil: true, as: :check } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | end 23 | 24 | after { Mv::Core::Db::MigrationValidator.delete_all } 25 | 26 | subject(:insert) { CustomCheckTestTableName.create! opts } 27 | 28 | describe "with all nulls" do 29 | let(:opts) { {} } 30 | 31 | it "doesn't raise an error" do 32 | expect{ subject }.not_to raise_error 33 | end 34 | end 35 | 36 | describe "with all valid values" do 37 | let(:opts) { { 38 | custom: 2, 39 | } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with valid value" do 47 | let(:opts) { { 48 | custom: 1, 49 | } } 50 | 51 | it "raises an error with valid message" do 52 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/check/exclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | ExclusionCheckTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "exclusion validation in check constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | ##string 19 | ###array 20 | t.string :string_array, validates: { exclusion: { in: ['a', 'b'], allow_nil: true, as: :check}} 21 | ###range 22 | t.string :string_range, validates: { exclusion: { in: 'a'..'b', allow_nil: true, as: :check}} 23 | 24 | ##integer 25 | ###array 26 | t.integer :integer_array, validates: { exclusion: { in: [1, 3], allow_nil: true, as: :check}} 27 | ###range 28 | t.integer :integer_range, validates: { exclusion: { in: 1..3, allow_nil: true, as: :check}} 29 | 30 | ##datetime 31 | ###array 32 | t.datetime :datetime_array, validates: { exclusion: { in: [DateTime.new(2011, 1, 1, 1, 1, 1, '+2'), DateTime.new(2012, 2, 2, 2, 2, 2, '+2')], allow_nil: true, as: :check}} 33 | ###range 34 | t.datetime :datetime_range, validates: { exclusion: { in: DateTime.new(2011, 1, 1, 1, 1, 1, '+2')..DateTime.new(2012, 2, 2, 2, 2, 2, '+2'), allow_nil: true, as: :check}} 35 | 36 | ##time 37 | ###array 38 | t.time :time_array, validates: { exclusion: { in: [Time.new(2011, 1, 1, 1, 1, 1, '+02:00'), Time.new(2012, 2, 2, 2, 2, 2, '+02:00')], allow_nil: true, as: :check}} 39 | ###range 40 | t.time :time_range, validates: { exclusion: { in: Time.new(2011, 1, 1, 1, 1, 1, '+02:00')..Time.new(2012, 2, 2, 2, 2, 2, '+02:00'), allow_nil: true, as: :check}} 41 | 42 | ##date 43 | ###array 44 | t.date :date_array, validates: { exclusion: { in: [Date.new(2011, 1, 1), Date.new(2012, 2, 2)], allow_nil: true, as: :check}} 45 | ###range 46 | t.date :date_range, validates: { exclusion: { in: Date.new(2011, 1, 1)..Date.new(2012, 2, 2), allow_nil: true, as: :check}} 47 | 48 | ##float 49 | ###array 50 | t.float :float_array, validates: { exclusion: { in: [1.1, 2.2], allow_nil: true, as: :check}} 51 | ###range 52 | t.float :float_range, validates: { exclusion: { in: 1.1..2.2, allow_nil: true, as: :check}} 53 | end 54 | end 55 | end.new('TestMigration', '20141118164617').migrate(:up) 56 | end 57 | 58 | after { Mv::Core::Db::MigrationValidator.delete_all } 59 | 60 | subject(:insert) { ExclusionCheckTestTableName.create! opts } 61 | 62 | describe "with all nulls" do 63 | let(:opts) { {} } 64 | 65 | it "doesn't raise an error" do 66 | expect{ subject }.not_to raise_error 67 | end 68 | end 69 | 70 | describe "with all valid values" do 71 | let(:opts) { { 72 | string_array: 'c', 73 | string_range: 'c', 74 | integer_array: 4, 75 | integer_range: 4, 76 | datetime_array: DateTime.new(2010, 1, 1, 1, 1, 1), 77 | datetime_range: DateTime.new(2010, 1, 1, 1, 1, 1), 78 | date_array: Date.new(2010, 1, 1), 79 | date_range: Date.new(2010, 1, 2), 80 | float_array: 1.0, 81 | float_range: 1.0 82 | } } 83 | 84 | it "doesn't raise an error" do 85 | expect{ subject }.not_to raise_error 86 | end 87 | end 88 | 89 | describe "with invalid" do 90 | describe "float" do 91 | describe "array" do 92 | let(:opts) { { float_array: 1.1 } } 93 | 94 | it "raises an error with valid message" do 95 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 96 | end 97 | end 98 | 99 | describe "range" do 100 | let(:opts) { { float_range: 1.2 } } 101 | 102 | it "raises an error with valid message" do 103 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 104 | end 105 | end 106 | end 107 | describe "date" do 108 | describe "array" do 109 | let(:opts) { { date_array: DateTime.new(2011, 1, 1) } } 110 | 111 | it "raises an error with valid message" do 112 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 113 | end 114 | end 115 | 116 | describe "range" do 117 | let(:opts) { { date_range: DateTime.new(2011, 1, 2) } } 118 | 119 | it "raises an error with valid message" do 120 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 121 | end 122 | end 123 | end 124 | describe "datetime" do 125 | describe "array" do 126 | let(:opts) { { datetime_array: DateTime.new(2011, 1, 1, 1, 1, 1) } } 127 | 128 | it "raises an error with valid message" do 129 | # expect{ subject }.to raise_error 130 | end 131 | end 132 | 133 | describe "range" do 134 | let(:opts) { { datetime_range: DateTime.new(2011, 1, 1, 1, 1, 2) } } 135 | 136 | it "raises an error with valid message" do 137 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 138 | end 139 | end 140 | end 141 | describe "integer" do 142 | describe "array" do 143 | let(:opts) { { integer_array: 1 } } 144 | 145 | it "raises an error with valid message" do 146 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 147 | end 148 | end 149 | 150 | describe "range" do 151 | let(:opts) { { integer_range: 2 } } 152 | 153 | it "raises an error with valid message" do 154 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 155 | end 156 | end 157 | end 158 | describe "string" do 159 | describe "array" do 160 | let(:opts) { { string_array: 'a' } } 161 | 162 | it "raises an error with valid message" do 163 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 164 | end 165 | end 166 | 167 | describe "range" do 168 | let(:opts) { { string_range: 'a' } } 169 | 170 | it "raises an error with valid message" do 171 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 172 | end 173 | end 174 | end 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/check/format_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | FormatCheckTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "format validation in check constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :format_string, validates: { format: { with: 'value', allow_nil: true, as: :check} } 19 | t.string :format_regexp, validates: { format: { with: /value/, allow_nil: true, as: :check} } 20 | end 21 | end 22 | end.new('TestMigration', '20141118164617').migrate(:up) 23 | end 24 | 25 | after { Mv::Core::Db::MigrationValidator.delete_all } 26 | 27 | subject(:insert) { FormatCheckTestTableName.create! opts } 28 | 29 | describe "with all nulls" do 30 | let(:opts) { {} } 31 | 32 | it "doesn't raise an error" do 33 | expect{ subject }.not_to raise_error 34 | end 35 | end 36 | 37 | describe "with all valid values" do 38 | let(:opts) { { 39 | format_string: 'some value', 40 | format_regexp: 'some value' 41 | } } 42 | 43 | it "doesn't raise an error" do 44 | expect{ subject }.not_to raise_error 45 | end 46 | end 47 | 48 | describe "with invalid" do 49 | describe "string" do 50 | let(:opts) { { format_string: 'some string' } } 51 | 52 | it "raises an error with valid message" do 53 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 54 | end 55 | end 56 | 57 | describe "regexp" do 58 | let(:opts) { { format_regexp: 'some string' } } 59 | 60 | it "raises an error with valid message" do 61 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/check/inclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | InclusionCheckTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "inclusion validation in check constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | ##string 19 | ###array 20 | t.string :string_array, validates: { inclusion: { in: ['a', 'b'], allow_nil: true, as: :check } } 21 | ###range 22 | t.string :string_range, validates: { inclusion: { in: 'a'..'b', allow_nil: true, as: :check } } 23 | 24 | ##integer 25 | ###array 26 | t.integer :integer_array, validates: { inclusion: { in: [1, 3], allow_nil: true, as: :check } } 27 | ###range 28 | t.integer :integer_range, validates: { inclusion: { in: 1..3, allow_nil: true, as: :check } } 29 | 30 | ##datetime 31 | ###array 32 | t.datetime :datetime_array, validates: { inclusion: { in: [DateTime.new(2011, 1, 1, 1, 1, 1, '+2'), DateTime.new(2012, 2, 2, 2, 2, 2, '+2')], allow_nil: true, as: :check } } 33 | ###range 34 | t.datetime :datetime_range, validates: { inclusion: { in: DateTime.new(2011, 1, 1, 1, 1, 1, '+2')..DateTime.new(2012, 2, 2, 2, 2, 2, '+2'), allow_nil: true, as: :check } } 35 | 36 | ##time 37 | ###array 38 | t.time :time_array, validates: { inclusion: { in: [Time.new(2011, 1, 1, 1, 1, 1, '+02:00'), Time.new(2012, 2, 2, 2, 2, 2, '+02:00')], allow_nil: true, as: :check } } 39 | ###range 40 | t.time :time_range, validates: { inclusion: { in: Time.new(2011, 1, 1, 1, 1, 1, '+02:00')..Time.new(2012, 2, 2, 2, 2, 2, '+02:00'), allow_nil: true, as: :check } } 41 | 42 | ##date 43 | ###array 44 | t.date :date_array, validates: { inclusion: { in: [Date.new(2011, 1, 1), Date.new(2012, 2, 2)], allow_nil: true, as: :check } } 45 | ###range 46 | t.date :date_range, validates: { inclusion: { in: Date.new(2011, 1, 1)..Date.new(2012, 2, 2), allow_nil: true, as: :check } } 47 | 48 | ##float 49 | ###array 50 | t.float :float_array, validates: { inclusion: { in: [1.1, 2.2], allow_nil: true, as: :check } } 51 | ###range 52 | t.float :float_range, validates: { inclusion: { in: 1.1..2.2, allow_nil: true, as: :check } } 53 | end 54 | end 55 | end.new('TestMigration', '20141118164617').migrate(:up) 56 | end 57 | 58 | after { Mv::Core::Db::MigrationValidator.delete_all } 59 | 60 | subject(:insert) { InclusionCheckTestTableName.create! opts } 61 | 62 | describe "with all nulls" do 63 | let(:opts) { {} } 64 | 65 | it "doesn't raise an error" do 66 | expect{ subject }.not_to raise_error 67 | end 68 | end 69 | 70 | describe "with all valid values" do 71 | let(:opts) { { 72 | string_array: 'a', 73 | string_range: 'a', 74 | integer_array: 1, 75 | integer_range: 1, 76 | datetime_array: DateTime.new(2011, 1, 1, 1, 1, 1), 77 | datetime_range: DateTime.new(2011, 1, 1, 1, 1, 2), 78 | date_array: Date.new(2011, 1, 1), 79 | date_range: Date.new(2011, 1, 2), 80 | float_array: 1.1, 81 | float_range: 1.2 82 | } } 83 | 84 | it "doesn't raise an error" do 85 | # expect{ subject }.not_to raise_error 86 | end 87 | 88 | end 89 | 90 | describe "with invalid" do 91 | describe "float" do 92 | describe "array" do 93 | let(:opts) { { float_array: 1.0 } } 94 | 95 | it "raises an error with valid message" do 96 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 97 | end 98 | end 99 | 100 | describe "range" do 101 | let(:opts) { { float_range: 1.0 } } 102 | 103 | it "raises an error with valid message" do 104 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 105 | end 106 | end 107 | end 108 | describe "date" do 109 | describe "array" do 110 | let(:opts) { { date_array: DateTime.new(2010, 1, 1) } } 111 | 112 | it "raises an error with valid message" do 113 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 114 | end 115 | end 116 | 117 | describe "range" do 118 | let(:opts) { { date_range: DateTime.new(2010, 1, 1) } } 119 | 120 | it "raises an error with valid message" do 121 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 122 | end 123 | end 124 | end 125 | describe "datetime" do 126 | describe "array" do 127 | let(:opts) { { datetime_array: DateTime.new(2010, 1, 1, 1, 1, 1) } } 128 | 129 | it "raises an error with valid message" do 130 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 131 | end 132 | end 133 | 134 | describe "range" do 135 | let(:opts) { { datetime_range: DateTime.new(2010, 1, 1, 1, 1, 1) } } 136 | 137 | it "raises an error with valid message" do 138 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 139 | end 140 | end 141 | end 142 | describe "integer" do 143 | describe "array" do 144 | let(:opts) { { integer_array: 4 } } 145 | 146 | it "raises an error with valid message" do 147 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 148 | end 149 | end 150 | 151 | describe "range" do 152 | let(:opts) { { integer_range: 4 } } 153 | 154 | it "raises an error with valid message" do 155 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 156 | end 157 | end 158 | end 159 | describe "string" do 160 | describe "array" do 161 | let(:opts) { { string_array: 'c' } } 162 | 163 | it "raises an error with valid message" do 164 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 165 | end 166 | end 167 | 168 | describe "range" do 169 | let(:opts) { { string_range: 'c' } } 170 | 171 | it "raises an error with valid message" do 172 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 173 | end 174 | end 175 | end 176 | end 177 | end 178 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/check/length_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | LengthCheckTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "length validation in check constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :length_is, validates: { length: { is: 5, allow_nil: true, as: :check } } 19 | t.string :length_in_array, validates: { length: { in: [1, 5], allow_nil: true, as: :check } } 20 | t.string :length_in_range, validates: { length: { in: 1..5, allow_nil: true, as: :check } } 21 | t.string :length_within_array, validates: { length: { within: [1, 5], allow_nil: true, as: :check } } 22 | t.string :length_within_range, validates: { length: { within: 1..5, allow_nil: true, as: :check } } 23 | t.string :length_minimum, validates: { length: { minimum: 5, allow_nil: true, as: :check } } 24 | t.string :length_maximum, validates: { length: { maximum: 5, allow_nil: true, as: :check } } 25 | end 26 | end 27 | end.new('TestMigration', '20141118164617').migrate(:up) 28 | end 29 | 30 | after { Mv::Core::Db::MigrationValidator.delete_all } 31 | 32 | subject(:insert) { LengthCheckTestTableName.create! opts } 33 | 34 | describe "with all nulls" do 35 | let(:opts) { {} } 36 | 37 | it "doesn't raise an error" do 38 | expect{ subject }.not_to raise_error 39 | end 40 | end 41 | 42 | describe "with all valid values" do 43 | let(:opts) { { 44 | length_is: '12345', 45 | length_in_array: '1', 46 | length_in_range: '1234', 47 | length_within_array: '1', 48 | length_within_range: '1234', 49 | length_minimum: '123456', 50 | length_maximum: '1234' 51 | } } 52 | 53 | it "doesn't raise an error" do 54 | expect{ subject }.not_to raise_error 55 | end 56 | end 57 | 58 | describe "with invalid" do 59 | describe ":is" do 60 | let(:opts) { { length_is: '123456' } } 61 | 62 | it "raises an error with valid message" do 63 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 64 | end 65 | end 66 | 67 | describe ":in" do 68 | describe "array" do 69 | let(:opts) { { length_in_array: '1234' } } 70 | 71 | it "raises an error with valid message" do 72 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 73 | end 74 | end 75 | 76 | describe "range" do 77 | let(:opts) { { length_in_range: '123456' } } 78 | 79 | it "raises an error with valid message" do 80 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 81 | end 82 | end 83 | end 84 | 85 | describe ":within" do 86 | describe "array" do 87 | let(:opts) { { length_within_array: '1234' } } 88 | 89 | it "raises an error with valid message" do 90 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 91 | end 92 | end 93 | 94 | describe "range" do 95 | let(:opts) { { length_within_range: '123456' } } 96 | 97 | it "raises an error with valid message" do 98 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 99 | end 100 | end 101 | end 102 | 103 | describe ":minimum" do 104 | let(:opts) { { length_minimum: '1234' } } 105 | 106 | it "raises an error with valid message" do 107 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 108 | end 109 | end 110 | 111 | describe ":maximum" do 112 | let(:opts) { { length_maximum: '123456' } } 113 | 114 | it "raises an error with valid message" do 115 | expect{ subject }.to raise_error(ActiveRecord::StatementInvalid, /CheckViolation/) 116 | end 117 | end 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/check/presence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | PresenceCheckTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "presence validation in check constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :presence, validates: { presence: { allow_nil: true, as: :check } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | end 23 | 24 | after { Mv::Core::Db::MigrationValidator.delete_all } 25 | 26 | subject(:insert) { PresenceCheckTestTableName.create! opts } 27 | 28 | describe "with all nulls" do 29 | let(:opts) { {} } 30 | 31 | it "doesn't raise an error" do 32 | expect{ subject }.not_to raise_error 33 | end 34 | end 35 | 36 | describe "with all valid values" do 37 | let(:opts) { { 38 | presence: 'some value', 39 | } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with invalid value" do 47 | let(:opts) { { 48 | presence: ' ', 49 | } } 50 | 51 | it "raises an error with valid message" do 52 | expect{ subject }.to raise_error(StandardError) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/index/uniqueness_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | UniquenessIndexTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "uniqueness validation in index constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :uniqueness, validates: { uniqueness: { as: :index } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | 23 | UniquenessIndexTestTableName.create!(uniqueness: 'value') 24 | end 25 | 26 | after { Mv::Core::Db::MigrationValidator.delete_all } 27 | 28 | subject(:insert) { UniquenessIndexTestTableName.create! opts } 29 | 30 | describe "with all nulls" do 31 | let(:opts) { {} } 32 | 33 | it "doesn't raise an error" do 34 | expect{ subject }.not_to raise_error 35 | end 36 | end 37 | 38 | describe "with all valid values" do 39 | let(:opts) { { uniqueness: 'some value' } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with not unique value" do 47 | let(:opts) { { uniqueness: 'value' } } 48 | 49 | it "raises an error with valid message" do 50 | expect{ subject }.to raise_error(StandardError) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/absence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | AbsenceTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "absence validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :absence, validates: { absence: { allow_nil: true, as: :trigger, message: 'absence_error' } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | end 23 | 24 | after { Mv::Core::Db::MigrationValidator.delete_all } 25 | 26 | subject(:insert) { AbsenceTestTableName.create! opts } 27 | 28 | describe "with all nulls" do 29 | let(:opts) { {} } 30 | 31 | it "doesn't raise an error" do 32 | expect{ subject }.not_to raise_error 33 | end 34 | end 35 | 36 | describe "with all valid values" do 37 | let(:opts) { { 38 | absence: ' ', 39 | } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with valid value" do 47 | let(:opts) { { 48 | absence: '1', 49 | } } 50 | 51 | it "raises an error with valid message" do 52 | expect{ subject }.to raise_error.with_message(/absence_error/) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/custom_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | CustomTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "custom validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.integer :custom, validates: { custom: { statement: '{custom} > 1', allow_nil: true, as: :trigger, message: 'custom_error' } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | end 23 | 24 | after { Mv::Core::Db::MigrationValidator.delete_all } 25 | 26 | subject(:insert) { CustomTestTableName.create! opts } 27 | 28 | describe "with all nulls" do 29 | let(:opts) { {} } 30 | 31 | it "doesn't raise an error" do 32 | expect{ subject }.not_to raise_error 33 | end 34 | end 35 | 36 | describe "with all valid values" do 37 | let(:opts) { { 38 | custom: 2, 39 | } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with valid value" do 47 | let(:opts) { { 48 | custom: 1, 49 | } } 50 | 51 | it "raises an error with valid message" do 52 | expect{ subject }.to raise_error.with_message(/custom_error/) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/exclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | ExclusionTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "exclusion validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | ##string 19 | ###array 20 | t.string :string_array, validates: { exclusion: { in: ['a', 'b'], allow_nil: true, as: :trigger, message: 'string_array_error' }} 21 | ###range 22 | t.string :string_range, validates: { exclusion: { in: 'a'..'b', allow_nil: true, as: :trigger, message: 'string_range_error' }} 23 | 24 | ##integer 25 | ###array 26 | t.integer :integer_array, validates: { exclusion: { in: [1, 3], allow_nil: true, as: :trigger, message: 'integer_array_error' }} 27 | ###range 28 | t.integer :integer_range, validates: { exclusion: { in: 1..3, allow_nil: true, as: :trigger, message: 'integer_range_error' }} 29 | 30 | ##datetime 31 | ###array 32 | t.datetime :datetime_array, validates: { exclusion: { in: [DateTime.new(2011, 1, 1, 1, 1, 1, '+2'), DateTime.new(2012, 2, 2, 2, 2, 2, '+2')], allow_nil: true, as: :trigger, message: 'datetime_array_error' }} 33 | ###range 34 | t.datetime :datetime_range, validates: { exclusion: { in: DateTime.new(2011, 1, 1, 1, 1, 1, '+2')..DateTime.new(2012, 2, 2, 2, 2, 2, '+2'), allow_nil: true, as: :trigger, message: 'datetime_range_error' }} 35 | 36 | ##time 37 | ###array 38 | t.time :time_array, validates: { exclusion: { in: [Time.new(2011, 1, 1, 1, 1, 1, '+02:00'), Time.new(2012, 2, 2, 2, 2, 2, '+02:00')], allow_nil: true, as: :trigger, message: 'time_array_error' }} 39 | ###range 40 | t.time :time_range, validates: { exclusion: { in: Time.new(2011, 1, 1, 1, 1, 1, '+02:00')..Time.new(2012, 2, 2, 2, 2, 2, '+02:00'), allow_nil: true, as: :trigger, message: 'time_range_error' }} 41 | 42 | ##date 43 | ###array 44 | t.date :date_array, validates: { exclusion: { in: [Date.new(2011, 1, 1), Date.new(2012, 2, 2)], allow_nil: true, as: :trigger, message: 'date_array_error' }} 45 | ###range 46 | t.date :date_range, validates: { exclusion: { in: Date.new(2011, 1, 1)..Date.new(2012, 2, 2), allow_nil: true, as: :trigger, message: 'date_range_error' }} 47 | 48 | ##float 49 | ###array 50 | t.float :float_array, validates: { exclusion: { in: [1.1, 2.2], allow_nil: true, as: :trigger, message: 'float_array_error' }} 51 | ###range 52 | t.float :float_range, validates: { exclusion: { in: 1.1..2.2, allow_nil: true, as: :trigger, message: 'float_range_error' }} 53 | end 54 | end 55 | end.new('TestMigration', '20141118164617').migrate(:up) 56 | end 57 | 58 | after { Mv::Core::Db::MigrationValidator.delete_all } 59 | 60 | subject(:insert) { ExclusionTestTableName.create! opts } 61 | 62 | describe "with all nulls" do 63 | let(:opts) { {} } 64 | 65 | it "doesn't raise an error" do 66 | expect{ subject }.not_to raise_error 67 | end 68 | end 69 | 70 | describe "with all valid values" do 71 | let(:opts) { { 72 | string_array: 'c', 73 | string_range: 'c', 74 | integer_array: 4, 75 | integer_range: 4, 76 | datetime_array: DateTime.new(2010, 1, 1, 1, 1, 1), 77 | datetime_range: DateTime.new(2010, 1, 1, 1, 1, 1), 78 | date_array: Date.new(2010, 1, 1), 79 | date_range: Date.new(2010, 1, 2), 80 | float_array: 1.0, 81 | float_range: 1.0 82 | } } 83 | 84 | it "doesn't raise an error" do 85 | expect{ subject }.not_to raise_error 86 | end 87 | end 88 | 89 | describe "with invalid" do 90 | describe "float" do 91 | describe "array" do 92 | let(:opts) { { float_array: 1.1 } } 93 | 94 | it "raises an error with valid message" do 95 | expect{ subject }.to raise_error.with_message(/float_array_error/) 96 | end 97 | end 98 | 99 | describe "range" do 100 | let(:opts) { { float_range: 1.2 } } 101 | 102 | it "raises an error with valid message" do 103 | expect{ subject }.to raise_error.with_message(/float_range_error/) 104 | end 105 | end 106 | end 107 | describe "date" do 108 | describe "array" do 109 | let(:opts) { { date_array: DateTime.new(2011, 1, 1) } } 110 | 111 | it "raises an error with valid message" do 112 | expect{ subject }.to raise_error.with_message(/date_array_error/) 113 | end 114 | end 115 | 116 | describe "range" do 117 | let(:opts) { { date_range: DateTime.new(2011, 1, 2) } } 118 | 119 | it "raises an error with valid message" do 120 | expect{ subject }.to raise_error.with_message(/date_range_error/) 121 | end 122 | end 123 | end 124 | describe "datetime" do 125 | describe "array" do 126 | let(:opts) { { datetime_array: DateTime.new(2011, 1, 1, 1, 1, 1) } } 127 | 128 | it "raises an error with valid message" do 129 | # expect{ subject }.to raise_error.with_message(/datetime_array_error/) 130 | end 131 | end 132 | 133 | describe "range" do 134 | let(:opts) { { datetime_range: DateTime.new(2011, 1, 1, 1, 1, 2) } } 135 | 136 | it "raises an error with valid message" do 137 | expect{ subject }.to raise_error.with_message(/datetime_range_error/) 138 | end 139 | end 140 | end 141 | describe "integer" do 142 | describe "array" do 143 | let(:opts) { { integer_array: 1 } } 144 | 145 | it "raises an error with valid message" do 146 | expect{ subject }.to raise_error.with_message(/integer_array_error/) 147 | end 148 | end 149 | 150 | describe "range" do 151 | let(:opts) { { integer_range: 2 } } 152 | 153 | it "raises an error with valid message" do 154 | expect{ subject }.to raise_error.with_message(/integer_range_error/) 155 | end 156 | end 157 | end 158 | describe "string" do 159 | describe "array" do 160 | let(:opts) { { string_array: 'a' } } 161 | 162 | it "raises an error with valid message" do 163 | expect{ subject }.to raise_error.with_message(/string_array_error/) 164 | end 165 | end 166 | 167 | describe "range" do 168 | let(:opts) { { string_range: 'a' } } 169 | 170 | it "raises an error with valid message" do 171 | expect{ subject }.to raise_error.with_message(/string_range_error/) 172 | end 173 | end 174 | end 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/format_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | FormatTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "format validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :format_string, validates: { format: { with: 'value', allow_nil: true, as: :trigger, message: 'format_string_error' } } 19 | t.string :format_regexp, validates: { format: { with: /value/, allow_nil: true, as: :trigger, message: 'format_regexp_error' } } 20 | end 21 | end 22 | end.new('TestMigration', '20141118164617').migrate(:up) 23 | end 24 | 25 | after { Mv::Core::Db::MigrationValidator.delete_all } 26 | 27 | subject(:insert) { FormatTestTableName.create! opts } 28 | 29 | describe "with all nulls" do 30 | let(:opts) { {} } 31 | 32 | it "doesn't raise an error" do 33 | expect{ subject }.not_to raise_error 34 | end 35 | end 36 | 37 | describe "with all valid values" do 38 | let(:opts) { { 39 | format_string: 'some value', 40 | format_regexp: 'some value' 41 | } } 42 | 43 | it "doesn't raise an error" do 44 | expect{ subject }.not_to raise_error 45 | end 46 | end 47 | 48 | describe "with invalid" do 49 | describe "string" do 50 | let(:opts) { { format_string: 'some string' } } 51 | 52 | it "raises an error with valid message" do 53 | expect{ subject }.to raise_error.with_message(/format_string_error/) 54 | end 55 | end 56 | 57 | describe "regexp" do 58 | let(:opts) { { format_regexp: 'some string' } } 59 | 60 | it "raises an error with valid message" do 61 | expect{ subject }.to raise_error.with_message(/format_regexp_error/) 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/inclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | InclusionTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "inclusion validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | ##string 19 | ###array 20 | t.string :string_array, validates: { inclusion: { in: ['a', 'b'], allow_nil: true, as: :trigger, message: 'string_array_error' }} 21 | ###range 22 | t.string :string_range, validates: { inclusion: { in: 'a'..'b', allow_nil: true, as: :trigger, message: 'string_range_error' }} 23 | 24 | ##integer 25 | ###array 26 | t.integer :integer_array, validates: { inclusion: { in: [1, 3], allow_nil: true, as: :trigger, message: 'integer_array_error' }} 27 | ###range 28 | t.integer :integer_range, validates: { inclusion: { in: 1..3, allow_nil: true, as: :trigger, message: 'integer_range_error' }} 29 | 30 | ##datetime 31 | ###array 32 | t.datetime :datetime_array, validates: { inclusion: { in: [DateTime.new(2011, 1, 1, 1, 1, 1, '+2'), DateTime.new(2012, 2, 2, 2, 2, 2, '+2')], allow_nil: true, as: :trigger, message: 'datetime_array_error' }} 33 | ###range 34 | t.datetime :datetime_range, validates: { inclusion: { in: DateTime.new(2011, 1, 1, 1, 1, 1, '+2')..DateTime.new(2012, 2, 2, 2, 2, 2, '+2'), allow_nil: true, as: :trigger, message: 'datetime_range_error' }} 35 | 36 | ##time 37 | ###array 38 | t.time :time_array, validates: { inclusion: { in: [Time.new(2011, 1, 1, 1, 1, 1, '+02:00'), Time.new(2012, 2, 2, 2, 2, 2, '+02:00')], allow_nil: true, as: :trigger, message: 'time_array_error' }} 39 | ###range 40 | t.time :time_range, validates: { inclusion: { in: Time.new(2011, 1, 1, 1, 1, 1, '+02:00')..Time.new(2012, 2, 2, 2, 2, 2, '+02:00'), allow_nil: true, as: :trigger, message: 'time_range_error' }} 41 | 42 | ##date 43 | ###array 44 | t.date :date_array, validates: { inclusion: { in: [Date.new(2011, 1, 1), Date.new(2012, 2, 2)], allow_nil: true, as: :trigger, message: 'date_array_error' }} 45 | ###range 46 | t.date :date_range, validates: { inclusion: { in: Date.new(2011, 1, 1)..Date.new(2012, 2, 2), allow_nil: true, as: :trigger, message: 'date_range_error' }} 47 | 48 | ##float 49 | ###array 50 | t.float :float_array, validates: { inclusion: { in: [1.1, 2.2], allow_nil: true, as: :trigger, message: 'float_array_error' }} 51 | ###range 52 | t.float :float_range, validates: { inclusion: { in: 1.1..2.2, allow_nil: true, as: :trigger, message: 'float_range_error' }} 53 | end 54 | end 55 | end.new('TestMigration', '20141118164617').migrate(:up) 56 | end 57 | 58 | after { Mv::Core::Db::MigrationValidator.delete_all } 59 | 60 | subject(:insert) { InclusionTestTableName.create! opts } 61 | 62 | describe "with all nulls" do 63 | let(:opts) { {} } 64 | 65 | it "doesn't raise an error" do 66 | expect{ subject }.not_to raise_error 67 | end 68 | end 69 | 70 | describe "with all valid values" do 71 | let(:opts) { { 72 | string_array: 'a', 73 | string_range: 'a', 74 | integer_array: 1, 75 | integer_range: 1, 76 | datetime_array: DateTime.new(2011, 1, 1, 1, 1, 1), 77 | datetime_range: DateTime.new(2011, 1, 1, 1, 1, 2), 78 | date_array: Date.new(2011, 1, 1), 79 | date_range: Date.new(2011, 1, 2), 80 | float_array: 1.1, 81 | float_range: 1.2 82 | } } 83 | 84 | it "doesn't raise an error" do 85 | # expect{ subject }.not_to raise_error 86 | end 87 | 88 | end 89 | 90 | describe "with invalid" do 91 | describe "float" do 92 | describe "array" do 93 | let(:opts) { { float_array: 1.0 } } 94 | 95 | it "raises an error with valid message" do 96 | expect{ subject }.to raise_error.with_message(/float_array_error/) 97 | end 98 | end 99 | 100 | describe "range" do 101 | let(:opts) { { float_range: 1.0 } } 102 | 103 | it "raises an error with valid message" do 104 | expect{ subject }.to raise_error.with_message(/float_range_error/) 105 | end 106 | end 107 | end 108 | describe "date" do 109 | describe "array" do 110 | let(:opts) { { date_array: DateTime.new(2010, 1, 1) } } 111 | 112 | it "raises an error with valid message" do 113 | expect{ subject }.to raise_error.with_message(/date_array_error/) 114 | end 115 | end 116 | 117 | describe "range" do 118 | let(:opts) { { date_range: DateTime.new(2010, 1, 1) } } 119 | 120 | it "raises an error with valid message" do 121 | expect{ subject }.to raise_error.with_message(/date_range_error/) 122 | end 123 | end 124 | end 125 | describe "datetime" do 126 | describe "array" do 127 | let(:opts) { { datetime_array: DateTime.new(2010, 1, 1, 1, 1, 1) } } 128 | 129 | it "raises an error with valid message" do 130 | expect{ subject }.to raise_error.with_message(/datetime_array_error/) 131 | end 132 | end 133 | 134 | describe "range" do 135 | let(:opts) { { datetime_range: DateTime.new(2010, 1, 1, 1, 1, 1) } } 136 | 137 | it "raises an error with valid message" do 138 | expect{ subject }.to raise_error.with_message(/datetime_range_error/) 139 | end 140 | end 141 | end 142 | describe "integer" do 143 | describe "array" do 144 | let(:opts) { { integer_array: 4 } } 145 | 146 | it "raises an error with valid message" do 147 | expect{ subject }.to raise_error.with_message(/integer_array_error/) 148 | end 149 | end 150 | 151 | describe "range" do 152 | let(:opts) { { integer_range: 4 } } 153 | 154 | it "raises an error with valid message" do 155 | expect{ subject }.to raise_error.with_message(/integer_range_error/) 156 | end 157 | end 158 | end 159 | describe "string" do 160 | describe "array" do 161 | let(:opts) { { string_array: 'c' } } 162 | 163 | it "raises an error with valid message" do 164 | expect{ subject }.to raise_error.with_message(/string_array_error/) 165 | end 166 | end 167 | 168 | describe "range" do 169 | let(:opts) { { string_range: 'c' } } 170 | 171 | it "raises an error with valid message" do 172 | expect{ subject }.to raise_error.with_message(/string_range_error/) 173 | end 174 | end 175 | end 176 | end 177 | end 178 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/length_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | 4 | LengthTestTableName = Class.new(ActiveRecord::Base) do 5 | self.table_name = :table_name 6 | end 7 | 8 | describe "length validation in trigger constraint begaviour" do 9 | let(:db) { ActiveRecord::Base.connection } 10 | 11 | before do 12 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 13 | 14 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 15 | 16 | Class.new(::ActiveRecord::Migration[5.0]) do 17 | def change 18 | create_table :table_name, id: false do |t| 19 | t.string :length_is, validates: { length: { is: 5, allow_nil: true, as: :trigger, message: 'length_is_error' } } 20 | t.string :length_in_array, validates: { length: { in: [1, 5], allow_nil: true, as: :trigger, message: 'length_in_array_error' } } 21 | t.string :length_in_range, validates: { length: { in: 1..5, allow_nil: true, as: :trigger, message: 'length_in_range_error' } } 22 | t.string :length_within_array, validates: { length: { within: [1, 5], allow_nil: true, as: :trigger, message: 'length_within_array_error' } } 23 | t.string :length_within_range, validates: { length: { within: 1..5, allow_nil: true, as: :trigger, message: 'length_within_range_error' } } 24 | t.string :length_minimum, validates: { length: { minimum: 5, allow_nil: true, as: :trigger, too_short: 'length_minimum_error' } } 25 | t.string :length_maximum, validates: { length: { maximum: 5, allow_nil: true, as: :trigger, too_long: 'length_maximum_error' } } 26 | end 27 | end 28 | end.new('TestMigration', '20141118164617').migrate(:up) 29 | end 30 | 31 | after { Mv::Core::Db::MigrationValidator.delete_all } 32 | 33 | subject(:insert) { LengthTestTableName.create! opts } 34 | 35 | describe "with all nulls" do 36 | let(:opts) { {} } 37 | 38 | it "doesn't raise an error" do 39 | expect{ subject }.not_to raise_error 40 | end 41 | end 42 | 43 | describe "with all valid values" do 44 | let(:opts) { { 45 | length_is: '12345', 46 | length_in_array: '1', 47 | length_in_range: '1234', 48 | length_within_array: '1', 49 | length_within_range: '1234', 50 | length_minimum: '123456', 51 | length_maximum: '1234' 52 | } } 53 | 54 | it "doesn't raise an error" do 55 | expect{ subject }.not_to raise_error 56 | end 57 | end 58 | 59 | describe "with invalid" do 60 | describe ":is" do 61 | let(:opts) { { length_is: '123456' } } 62 | 63 | it "raises an error with valid message" do 64 | expect{ subject }.to raise_error.with_message(/length_is_error/) 65 | end 66 | end 67 | 68 | describe ":in" do 69 | describe "array" do 70 | let(:opts) { { length_in_array: '1234' } } 71 | 72 | it "raises an error with valid message" do 73 | expect{ subject }.to raise_error.with_message(/length_in_array_error/) 74 | end 75 | end 76 | 77 | describe "range" do 78 | let(:opts) { { length_in_range: '123456' } } 79 | 80 | it "raises an error with valid message" do 81 | expect{ subject }.to raise_error.with_message(/length_in_range_error/) 82 | end 83 | end 84 | end 85 | 86 | describe ":within" do 87 | describe "array" do 88 | let(:opts) { { length_within_array: '1234' } } 89 | 90 | it "raises an error with valid message" do 91 | expect{ subject }.to raise_error.with_message(/length_within_array_error/) 92 | end 93 | end 94 | 95 | describe "range" do 96 | let(:opts) { { length_within_range: '123456' } } 97 | 98 | it "raises an error with valid message" do 99 | expect{ subject }.to raise_error.with_message(/length_within_range_error/) 100 | end 101 | end 102 | end 103 | 104 | describe ":minimum" do 105 | let(:opts) { { length_minimum: '1234' } } 106 | 107 | it "raises an error with valid message" do 108 | expect{ subject }.to raise_error.with_message(/length_minimum_error/) 109 | end 110 | end 111 | 112 | describe ":maximum" do 113 | let(:opts) { { length_maximum: '123456' } } 114 | 115 | it "raises an error with valid message" do 116 | expect{ subject }.to raise_error.with_message(/length_maximum_error/) 117 | end 118 | end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/presence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | PresenceTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "presence validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :presence, validates: { presence: { allow_nil: true, as: :trigger, message: 'presence_error' } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | end 23 | 24 | after { Mv::Core::Db::MigrationValidator.delete_all } 25 | 26 | subject(:insert) { PresenceTestTableName.create! opts } 27 | 28 | describe "with all nulls" do 29 | let(:opts) { {} } 30 | 31 | it "doesn't raise an error" do 32 | expect{ subject }.not_to raise_error 33 | end 34 | end 35 | 36 | describe "with all valid values" do 37 | let(:opts) { { 38 | presence: 'some value', 39 | } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with valid value" do 47 | let(:opts) { { 48 | presence: ' ', 49 | } } 50 | 51 | it "raises an error with valid message" do 52 | expect{ subject }.to raise_error.with_message(/presence_error/) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/constraints/trigger/uniqueness_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | UniquenessTestTableName = Class.new(ActiveRecord::Base) do 4 | self.table_name = :table_name 5 | end 6 | 7 | describe "uniqueness validation in trigger constraint begaviour" do 8 | let(:db) { ActiveRecord::Base.connection } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | 13 | db.drop_table(:table_name) if db.data_source_exists?(:table_name) 14 | 15 | Class.new(::ActiveRecord::Migration[5.0]) do 16 | def change 17 | create_table :table_name, id: false do |t| 18 | t.string :uniqueness, validates: { uniqueness: { allow_nil: true, as: :trigger, message: 'uniqueness_error' } } 19 | end 20 | end 21 | end.new('TestMigration', '20141118164617').migrate(:up) 22 | 23 | UniquenessTestTableName.create!(uniqueness: 'value') 24 | end 25 | 26 | after { Mv::Core::Db::MigrationValidator.delete_all } 27 | 28 | subject(:insert) { UniquenessTestTableName.create! opts } 29 | 30 | describe "with all nulls" do 31 | let(:opts) { {} } 32 | 33 | it "doesn't raise an error" do 34 | expect{ subject }.not_to raise_error 35 | end 36 | end 37 | 38 | describe "with all valid values" do 39 | let(:opts) { { uniqueness: 'some value' } } 40 | 41 | it "doesn't raise an error" do 42 | expect{ subject }.not_to raise_error 43 | end 44 | end 45 | 46 | describe "with valid value" do 47 | let(:opts) { { uniqueness: 'value' } } 48 | 49 | it "raises an error with valid message" do 50 | expect{ subject }.to raise_error.with_message(/uniqueness_error/) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/delete_validations_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Delete validation scenarios' do 4 | before do 5 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 6 | Mv::Core::Db::MigrationValidator.delete_all 7 | ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:prepend, Mv::Postgresql::ActiveRecord::ConnectionAdapters::PostgresqlAdapterDecorator) 8 | 9 | Mv::Core::Migration::Base.with_suppressed_validations do 10 | ActiveRecord::Base.connection.drop_table(:table_name) if ActiveRecord::Base.connection.data_source_exists?(:table_name) 11 | end 12 | end 13 | 14 | describe "change_table" do 15 | describe "udpate column" do 16 | before do 17 | Class.new(::ActiveRecord::Migration[5.0]) do 18 | def change 19 | create_table :table_name, id: false do |t| 20 | t.string :column_name, validates: { length: { is: 5, as: :trigger, on: :create} } 21 | end 22 | end 23 | end.new('TestMigration', '20141118164617').migrate(:up) 24 | end 25 | 26 | subject do 27 | Class.new(::ActiveRecord::Migration[5.0]) do 28 | def change 29 | change_table :table_name, id: false do |t| 30 | t.change :column_name, :string 31 | end 32 | end 33 | end.new('TestMigration', '20141118164617').migrate(:up) 34 | end 35 | 36 | it "removes migration validator" do 37 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(-1) 38 | end 39 | 40 | it "creates new trigger constraint" do 41 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:delete).once 42 | subject 43 | end 44 | end 45 | 46 | describe "remove column" do 47 | before do 48 | Class.new(::ActiveRecord::Migration[5.0]) do 49 | def change 50 | create_table :table_name, id: false do |t| 51 | t.string :column_name_1 52 | t.string :column_name, validates: { length: { is: 5, as: :trigger, on: :create} } 53 | end 54 | end 55 | end.new('TestMigration', '20141118164617').migrate(:up) 56 | end 57 | 58 | subject do 59 | Class.new(::ActiveRecord::Migration[5.0]) do 60 | def change 61 | change_table :table_name, id: false do |t| 62 | t.remove :column_name 63 | end 64 | end 65 | end.new('TestMigration', '20141118164617').migrate(:up) 66 | end 67 | 68 | it "removes migration validator" do 69 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(-1) 70 | end 71 | 72 | it "creates new trigger constraint" do 73 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:delete).once 74 | subject 75 | end 76 | end 77 | end 78 | 79 | describe "standalone" do 80 | describe "remove column" do 81 | before do 82 | Class.new(::ActiveRecord::Migration[5.0]) do 83 | def change 84 | create_table :table_name, id: false do |t| 85 | t.string :column_name_1 86 | t.string :column_name, validates: { length: { is: 5, as: :trigger, on: :create} } 87 | end 88 | end 89 | end.new('TestMigration', '20141118164617').migrate(:up) 90 | end 91 | 92 | subject do 93 | Class.new(::ActiveRecord::Migration[5.0]) do 94 | def change 95 | remove_column :table_name, :column_name 96 | end 97 | end.new('TestMigration', '20141118164617').migrate(:up) 98 | end 99 | 100 | it "removes migration validator" do 101 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(-1) 102 | end 103 | 104 | it "creates new trigger constraint" do 105 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:delete).once 106 | subject 107 | end 108 | end 109 | 110 | describe "update column" do 111 | before do 112 | Class.new(::ActiveRecord::Migration[5.0]) do 113 | def change 114 | create_table :table_name, id: false do |t| 115 | t.string :column_name, validates: { length: { is: 5, as: :trigger, on: :create} } 116 | end 117 | end 118 | end.new('TestMigration', '20141118164617').migrate(:up) 119 | end 120 | 121 | subject do 122 | Class.new(::ActiveRecord::Migration[5.0]) do 123 | def change 124 | change_column :table_name, :column_name, :string, {} 125 | end 126 | end.new('TestMigration', '20141118164617').migrate(:up) 127 | end 128 | 129 | it "removes migration validator" do 130 | expect{ subject }.to change(Mv::Core::Db::MigrationValidator, :count).by(-1) 131 | end 132 | 133 | it "creates new trigger constraint" do 134 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:delete).once 135 | subject 136 | end 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /spec/mv/postgresql/integration/update_validations_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Update validation scenarios' do 4 | before do 5 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 6 | Mv::Core::Db::MigrationValidator.delete_all 7 | ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:prepend, Mv::Postgresql::ActiveRecord::ConnectionAdapters::PostgresqlAdapterDecorator) 8 | 9 | Mv::Core::Migration::Base.with_suppressed_validations do 10 | ActiveRecord::Base.connection.drop_table(:table_name) if ActiveRecord::Base.connection.data_source_exists?(:table_name) 11 | end 12 | end 13 | 14 | describe 'update column in change_table block' do 15 | before do 16 | Class.new(::ActiveRecord::Migration[5.0]) do 17 | def change 18 | create_table :table_name, id: false do |t| 19 | t.string :column_name, validates: { uniqueness: { as: :trigger, on: :create } } 20 | end 21 | end 22 | end.new('TestMigration', '20141118164617').migrate(:up) 23 | end 24 | 25 | subject do 26 | Class.new(::ActiveRecord::Migration[5.0]) do 27 | def change 28 | change_table :table_name, id: false do |t| 29 | t.change :column_name, :string, validates: { uniqueness: { as: :index } } 30 | end 31 | end 32 | end.new('TestMigration', '20141118164617').migrate(:up) 33 | end 34 | 35 | it "deletes trigger constraint" do 36 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:delete).once 37 | subject 38 | end 39 | 40 | it "creates index constraint" do 41 | expect_any_instance_of(Mv::Core::Constraint::Builder::Index).to receive(:create).once 42 | subject 43 | end 44 | 45 | it "does NOT change migration validators number" do 46 | expect{ subject }.not_to change(Mv::Core::Db::MigrationValidator, :count) 47 | end 48 | 49 | it "updates migration validator" do 50 | expect{ subject }.to change{Mv::Core::Db::MigrationValidator.first.options}.from(as: :trigger, on: :create) 51 | .to(as: :index) 52 | end 53 | end 54 | 55 | describe 'standalone update column statement' do 56 | before do 57 | Class.new(::ActiveRecord::Migration[5.0]) do 58 | def change 59 | create_table :table_name, id: false do |t| 60 | t.string :column_name, validates: { uniqueness: { as: :trigger, on: :create } } 61 | end 62 | end 63 | end.new('TestMigration', '20141118164617').migrate(:up) 64 | end 65 | 66 | subject do 67 | Class.new(::ActiveRecord::Migration[5.0]) do 68 | def change 69 | change_column :table_name, :column_name, :string, validates: { uniqueness: { as: :index } } 70 | end 71 | end.new('TestMigration', '20141118164617').migrate(:up) 72 | end 73 | 74 | it "deletes trigger constraint" do 75 | expect_any_instance_of(Mv::Postgresql::Constraint::Builder::Trigger).to receive(:delete).once 76 | subject 77 | end 78 | 79 | it "creates index constraint" do 80 | expect_any_instance_of(Mv::Core::Constraint::Builder::Index).to receive(:create).once 81 | subject 82 | end 83 | 84 | it "does NOT change migration validators number" do 85 | expect{ subject }.not_to change(Mv::Core::Db::MigrationValidator, :count) 86 | end 87 | 88 | it "updates migration validator" do 89 | expect{ subject }.to change{Mv::Core::Db::MigrationValidator.first.options}.from(as: :trigger, on: :create) 90 | .to(as: :index) 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/mv/postgresql/router_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | Description = Mv::Core::Constraint::Description 4 | 5 | describe Mv::Core::Router do 6 | let(:migration_validator) { create(:migration_validator) } 7 | 8 | subject(:router) { described_class } 9 | 10 | before do 11 | Mv::Core::Services::CreateMigrationValidatorsTable.new.execute 12 | end 13 | 14 | describe "#route" do 15 | let(:presence) { 16 | Mv::Postgresql::Validation::Presence.new(:table_name, :column_name, options) 17 | } 18 | 19 | subject { described_class.route(presence) } 20 | 21 | describe "when :as == :check" do 22 | let(:options) { { as: :check } } 23 | 24 | it { is_expected.to eq([Description.new(presence.check_name, :check)]) } 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/absence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/absence' 4 | 5 | describe Mv::Postgresql::Validation::Absence do 6 | def instance opts = {} 7 | described_class.new(:table_name, :column_name, opts) 8 | end 9 | 10 | subject { instance } 11 | 12 | describe "default values" do 13 | its(:as) { is_expected.to eq(:check)} 14 | its(:check_name) { is_expected.to eq("chk_mv_table_name_column_name") } 15 | 16 | describe ":create_trigger_name" do 17 | describe "when :as == :check" do 18 | subject { instance(create_trigger_name: nil, as: :check) } 19 | 20 | its(:create_trigger_name) { is_expected.to be_nil } 21 | end 22 | end 23 | 24 | describe ":update_trigger_name" do 25 | describe "when :as == :check" do 26 | subject { instance(update_trigger_name: nil, as: :check) } 27 | 28 | its(:update_trigger_name) { is_expected.to be_nil } 29 | end 30 | end 31 | end 32 | 33 | describe "#<==>" do 34 | it { is_expected.to eq(instance) } 35 | it { is_expected.not_to eq(instance('check_name' => 'check_name_1')) } 36 | end 37 | 38 | describe "validation" do 39 | it { is_expected.to be_valid } 40 | 41 | describe ":check_name" do 42 | describe "when :as == :trigger" do 43 | subject { instance(update_trigger_name: nil, 44 | create_trigger_name: nil, 45 | check_name: :check_name, 46 | as: :trigger) } 47 | 48 | it { is_expected.to be_invalid } 49 | end 50 | end 51 | 52 | describe ":on" do 53 | describe "when :as == :check" do 54 | subject { instance(on: :create, as: :check, create_trigger_name: nil, update_trigger_name: nil) } 55 | 56 | it { is_expected.to be_invalid } 57 | end 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/active_model_presenter/factory_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Mv::Core::Validation::ActiveModelPresenter::Factory do 4 | subject(:factory) { described_class } 5 | 6 | describe "#create_presenter" do 7 | subject { factory.create_presenter(validation) } 8 | 9 | describe "exclusion" do 10 | let(:validation) { Mv::Postgresql::Validation::Exclusion.new(:table_name, :column_name, in: [1, 2]) } 11 | 12 | it { is_expected.to be_an_instance_of(Mv::Core::Validation::ActiveModelPresenter::Exclusion) } 13 | end 14 | 15 | describe "inclusion" do 16 | let(:validation) { Mv::Postgresql::Validation::Inclusion.new(:table_name, :column_name, in: [1, 2]) } 17 | 18 | it { is_expected.to be_an_instance_of(Mv::Core::Validation::ActiveModelPresenter::Inclusion) } 19 | end 20 | 21 | describe "length" do 22 | let(:validation) { Mv::Postgresql::Validation::Length.new(:table_name, :column_name, in: [1, 2]) } 23 | 24 | it { is_expected.to be_an_instance_of(Mv::Core::Validation::ActiveModelPresenter::Length) } 25 | end 26 | 27 | describe "presence" do 28 | let(:validation) { Mv::Postgresql::Validation::Presence.new(:table_name, :column_name, {}) } 29 | 30 | it { is_expected.to be_an_instance_of(Mv::Core::Validation::ActiveModelPresenter::Presence) } 31 | end 32 | 33 | describe "absence" do 34 | let(:validation) { Mv::Postgresql::Validation::Absence.new(:table_name, :column_name, {}) } 35 | 36 | it { is_expected.to be_an_instance_of(Mv::Core::Validation::ActiveModelPresenter::Absence) } 37 | end 38 | 39 | describe "uniqueness" do 40 | let(:validation) { Mv::Core::Validation::Uniqueness.new(:table_name, :column_name, {}) } 41 | 42 | it { is_expected.to be_an_instance_of(Mv::Core::Validation::ActiveModelPresenter::Uniqueness) } 43 | end 44 | 45 | describe "format" do 46 | let(:validation) { Mv::Postgresql::Validation::Format.new(:table_name, :column_name, {}) } 47 | 48 | it { is_expected.to be_an_instance_of(Mv::Postgresql::Validation::ActiveModelPresenter::Format) } 49 | end 50 | end 51 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/active_model_presenter/format_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/active_model_presenter/format' 4 | 5 | describe Mv::Postgresql::Validation::ActiveModelPresenter::Format do 6 | subject(:validation) { 7 | Mv::Postgresql::Validation::Format.new(:table_name, 8 | :column_name, 9 | opts) 10 | } 11 | 12 | subject(:acive_model_presenter) { described_class.new(validation) } 13 | 14 | describe "#initialize" do 15 | let(:opts) { true } 16 | 17 | its(:validation) { is_expected.to eq(validation) } 18 | its(:column_name) { is_expected.to eq(:column_name) } 19 | end 20 | 21 | describe "#options" do 22 | subject { acive_model_presenter.options } 23 | 24 | describe "when empty properties has specified" do 25 | let(:opts) { { with: /txt/ } } 26 | 27 | it { is_expected.to eq(format: { with: /txt/}) } 28 | end 29 | 30 | describe "when properties has specified" do 31 | let(:opts) { { 32 | with: /txt/, 33 | on: :create, 34 | as: :trigger, 35 | allow_blank: true, 36 | allow_nil: true, 37 | message: 'some error message' 38 | } } 39 | 40 | it { is_expected.to eq( 41 | format: { with: /txt/, 42 | on: :create, 43 | allow_blank: true, 44 | allow_nil: true, 45 | message: 'some error message' } 46 | )} 47 | end 48 | 49 | end 50 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/exclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/exclusion' 4 | require 'mv/postgresql/validation/builder/exclusion' 5 | 6 | describe Mv::Postgresql::Validation::Builder::Exclusion do 7 | def exclusion(opts = {}) 8 | Mv::Postgresql::Validation::Exclusion.new(:table_name, 9 | :column_name, 10 | { in: [1, 5], message: 'is excluded' }.merge(opts)) 11 | end 12 | 13 | describe "#conditions" do 14 | subject { described_class.new(exclusion(opts)).conditions } 15 | 16 | describe "when dates array passed" do 17 | let(:opts) { { in: [Date.new(2001, 1, 1), Date.new(2002, 2, 2)] } } 18 | 19 | it { is_expected.to eq([{ 20 | statement: "column_name IS NOT NULL AND column_name NOT IN ('2001-01-01', '2002-02-02')", 21 | message: 'column_name is excluded' 22 | }]) } 23 | end 24 | 25 | describe "when date times array passed" do 26 | let(:opts) { { in: [DateTime.new(2001, 1, 1, 1, 1, 1), DateTime.new(2002, 2, 2, 2, 2, 2)] } } 27 | 28 | it { is_expected.to eq([{ 29 | statement: "column_name IS NOT NULL AND column_name NOT IN ('2001-01-01 01:01:01', '2002-02-02 02:02:02')", 30 | message: 'column_name is excluded' 31 | }]) } 32 | end 33 | 34 | describe "when date times array passed" do 35 | let(:opts) { { in: [Time.new(2001, 1, 1, 1, 1, 1), Time.new(2002, 2, 2, 2, 2, 2)] } } 36 | 37 | it { is_expected.to eq([{ 38 | statement: "column_name IS NOT NULL AND column_name NOT IN ('2001-01-01 01:01:01', '2002-02-02 02:02:02')", 39 | message: 'column_name is excluded' 40 | }]) } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/format_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/format' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Format do 6 | def format(opts = {}) 7 | Mv::Postgresql::Validation::Format.new(:table_name, 8 | :column_name, 9 | { with: /exp/, message: 'is not valid' }.merge(opts)) 10 | end 11 | 12 | describe "#initalize" do 13 | subject { described_class.new(format) } 14 | 15 | its(:validation) { is_expected.to eq(format) } 16 | its(:with) { is_expected.to eq(format.with) } 17 | its(:allow_nil) { is_expected.to eq(format.allow_nil) } 18 | its(:allow_blank) { is_expected.to eq(format.allow_blank) } 19 | its(:column_name) { is_expected.to eq(format.column_name) } 20 | its(:message) { is_expected.to eq(format.full_message) } 21 | end 22 | 23 | describe "#conditions" do 24 | subject { described_class.new(format(opts)).conditions } 25 | 26 | describe "when regex passed" do 27 | let(:opts) { { with: /exp/ } } 28 | 29 | it { is_expected.to eq([{ 30 | statement: "column_name IS NOT NULL AND column_name ~ 'exp'", 31 | message: 'column_name is not valid' 32 | }]) } 33 | end 34 | 35 | describe "when string passed" do 36 | let(:opts) { { with: 'exp' } } 37 | 38 | it { is_expected.to eq([{ 39 | statement: "column_name IS NOT NULL AND column_name ~ 'exp'", 40 | message: 'column_name is not valid' 41 | }]) } 42 | end 43 | 44 | describe "when not supported type passed" do 45 | let(:opts) { { with: 1 } } 46 | 47 | it 'raises an error' do 48 | expect{ subject }.to raise_error(Mv::Core::Error) 49 | end 50 | end 51 | 52 | describe "when nil is allowed" do 53 | let(:opts) { { with: /exp/, allow_nil: true } } 54 | 55 | it { is_expected.to eq([{ 56 | statement: "column_name ~ 'exp' OR column_name IS NULL", 57 | message: 'column_name is not valid' 58 | }]) } 59 | end 60 | 61 | describe "when blank is allowed" do 62 | let(:opts) { { with: /exp/, allow_blank: true } } 63 | 64 | it { is_expected.to eq([{ 65 | statement: "column_name ~ 'exp' OR column_name IS NULL OR LENGTH(TRIM(column_name)) = 0", 66 | message: 'column_name is not valid' 67 | }]) } 68 | end 69 | 70 | describe "when blank and nill are both allowed" do 71 | let(:opts) { { with: /exp/, allow_blank: true, allow_nil: true } } 72 | 73 | it { is_expected.to eq([{ 74 | statement: "column_name ~ 'exp' OR column_name IS NULL OR LENGTH(TRIM(column_name)) = 0", 75 | message: 'column_name is not valid' 76 | }]) } 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/inclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/inclusion' 4 | require 'mv/postgresql/validation/builder/inclusion' 5 | 6 | describe Mv::Postgresql::Validation::Builder::Inclusion do 7 | def inclusion(opts = {}) 8 | Mv::Postgresql::Validation::Inclusion.new(:table_name, 9 | :column_name, 10 | { in: [1, 5], message: 'is included' }.merge(opts)) 11 | end 12 | 13 | describe "#conditions" do 14 | subject { described_class.new(inclusion(opts)).conditions } 15 | 16 | describe "when dates array passed" do 17 | let(:opts) { { in: [Date.new(2001, 1, 1), Date.new(2002, 2, 2)] } } 18 | 19 | it { is_expected.to eq([{ 20 | statement: "column_name IS NOT NULL AND column_name IN ('2001-01-01', '2002-02-02')", 21 | message: 'column_name is included' 22 | }]) } 23 | end 24 | 25 | describe "when date times array passed" do 26 | let(:opts) { { in: [DateTime.new(2001, 1, 1, 1, 1, 1), DateTime.new(2002, 2, 2, 2, 2, 2)] } } 27 | 28 | it { is_expected.to eq([{ 29 | statement: "column_name IS NOT NULL AND column_name IN ('2001-01-01 01:01:01', '2002-02-02 02:02:02')", 30 | message: 'column_name is included' 31 | }]) } 32 | end 33 | 34 | describe "when date times array passed" do 35 | let(:opts) { { in: [Time.new(2001, 1, 1, 1, 1, 1), Time.new(2002, 2, 2, 2, 2, 2)] } } 36 | 37 | it { is_expected.to eq([{ 38 | statement: "column_name IS NOT NULL AND column_name IN ('2001-01-01 01:01:01', '2002-02-02 02:02:02')", 39 | message: 'column_name is included' 40 | }]) } 41 | 42 | describe "when boolean array passed" do 43 | let(:opts) { { in: [true, false] } } 44 | 45 | it { is_expected.to eq([{ 46 | statement: "column_name IS NOT NULL AND column_name IN (true, false)", 47 | message: 'column_name is included' 48 | }]) } 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/absence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/absence' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Absence do 6 | def absence(opts = {}) 7 | Mv::Postgresql::Validation::Absence.new(:table_name, 8 | :column_name, 9 | { message: 'should not be present' }.merge(opts)) 10 | end 11 | 12 | describe "#conditions" do 13 | subject { described_class.new(absence(opts)).conditions } 14 | 15 | describe "by default" do 16 | let(:opts) { {} } 17 | 18 | it { is_expected.to eq([{ 19 | statement: "NEW.column_name IS NULL OR LENGTH(TRIM(NEW.column_name)) = 0", 20 | message: 'column_name should not be present' 21 | }]) } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/custom_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/custom' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Custom do 6 | def custom(opts = {}) 7 | Mv::Postgresql::Validation::Custom.new(:table_name, 8 | :column_name, 9 | { message: 'is not valid as expected' }.merge(opts)) 10 | end 11 | 12 | describe "#conditions" do 13 | subject { described_class.new(custom(opts)).conditions } 14 | 15 | describe "by default" do 16 | let(:opts) { { statement: "{column_name} > 0" } } 17 | 18 | it { is_expected.to eq([{ 19 | statement: "NEW.column_name IS NOT NULL AND (NEW.column_name > 0)", 20 | message: 'column_name is not valid as expected' 21 | }]) } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/exclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/exclusion' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Exclusion do 6 | def exclusion(opts = {}) 7 | Mv::Postgresql::Validation::Exclusion.new(:table_name, 8 | :column_name, 9 | { in: [1, 5], message: 'is excluded' }.merge(opts)) 10 | end 11 | 12 | describe "#conditions" do 13 | subject { described_class.new(exclusion(opts)).conditions } 14 | 15 | describe "when dates array passed" do 16 | let(:opts) { { in: [Date.new(2001, 1, 1), Date.new(2002, 2, 2)] } } 17 | 18 | it { is_expected.to eq([{ 19 | statement: "NEW.column_name IS NOT NULL AND NEW.column_name NOT IN ('2001-01-01', '2002-02-02')", 20 | message: 'column_name is excluded' 21 | }]) } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/format_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/format' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Format do 6 | def format(opts = {}) 7 | Mv::Postgresql::Validation::Format.new(:table_name, 8 | :column_name, 9 | { with: /exp/, message: 'is not valid' }.merge(opts)) 10 | end 11 | 12 | describe "#conditions" do 13 | subject { described_class.new(format(opts)).conditions } 14 | 15 | describe "when regex passed" do 16 | let(:opts) { { with: /exp/ } } 17 | 18 | it { is_expected.to eq([{ 19 | statement: "NEW.column_name IS NOT NULL AND NEW.column_name ~ 'exp'", 20 | message: 'column_name is not valid' 21 | }]) } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/inclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/inclusion' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Inclusion do 6 | def inclusion(opts = {}) 7 | Mv::Postgresql::Validation::Inclusion.new(:table_name, 8 | :column_name, 9 | { in: [1, 5], message: 'is included' }.merge(opts)) 10 | end 11 | 12 | describe "#conditions" do 13 | subject { described_class.new(inclusion(opts)).conditions } 14 | 15 | describe "when dates array passed" do 16 | let(:opts) { { in: [Date.new(2001, 1, 1), Date.new(2002, 2, 2)] } } 17 | 18 | it { is_expected.to eq([{ 19 | statement: "NEW.column_name IS NOT NULL AND NEW.column_name IN ('2001-01-01', '2002-02-02')", 20 | message: 'column_name is included' 21 | }]) } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/length_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/length' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Length do 6 | def length(opts = {}) 7 | Mv::Postgresql::Validation::Length.new(:table_name, 8 | :column_name, 9 | { with: /exp/, message: 'has invalid length' }.merge(opts)) 10 | end 11 | 12 | describe "#conditions" do 13 | subject { described_class.new(length(opts)).conditions } 14 | 15 | describe "when :in is defined" do 16 | describe "as array" do 17 | let(:opts) { { in: [1, 3] } } 18 | 19 | it { is_expected.to eq([{ 20 | statement: 'NEW.column_name IS NOT NULL AND LENGTH(NEW.column_name) IN (1, 3)', 21 | message: 'column_name has invalid length' 22 | }]) } 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/presence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/presence' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Presence do 6 | def presence(opts = {}) 7 | Mv::Postgresql::Validation::Presence.new(:table_name, 8 | :column_name, 9 | { message: 'must be present' }.merge(opts)) 10 | end 11 | 12 | describe "#conditions" do 13 | subject { described_class.new(presence(opts)).conditions } 14 | 15 | describe "by default" do 16 | let(:opts) { {} } 17 | 18 | it { is_expected.to eq([{ 19 | statement: "NEW.column_name IS NOT NULL AND LENGTH(TRIM(NEW.column_name)) > 0", 20 | message: 'column_name must be present' 21 | }]) } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/builder/trigger/uniqueness_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/builder/trigger/uniqueness' 4 | 5 | describe Mv::Postgresql::Validation::Builder::Trigger::Uniqueness do 6 | def uniqueness(opts = {}) 7 | Mv::Core::Validation::Uniqueness.new(:table_name, 8 | :column_name, 9 | { message: 'must be unique' }.merge(opts)) 10 | end 11 | 12 | 13 | describe "#conditions" do 14 | subject { described_class.new(uniqueness(opts)).conditions } 15 | 16 | describe "by default" do 17 | let(:opts) { {} } 18 | 19 | it { is_expected.to eq([{ 20 | statement: "NEW.column_name IS NOT NULL AND NOT EXISTS(SELECT column_name 21 | FROM table_name 22 | WHERE NEW.column_name = column_name)".squish, 23 | message: 'column_name must be unique' 24 | }])} 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/custom_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/custom' 4 | 5 | describe Mv::Postgresql::Validation::Custom do 6 | def instance opts = {} 7 | described_class.new(:table_name, :column_name, { statement: 'column_name > 0'}.merge(opts)) 8 | end 9 | 10 | subject { instance } 11 | 12 | describe "default values" do 13 | its(:as) { is_expected.to eq(:check)} 14 | its(:check_name) { is_expected.to eq("chk_mv_table_name_column_name") } 15 | 16 | describe ":create_trigger_name" do 17 | describe "when :as == :check" do 18 | subject { instance(create_trigger_name: nil, as: :check) } 19 | 20 | its(:create_trigger_name) { is_expected.to be_nil } 21 | end 22 | end 23 | 24 | describe ":update_trigger_name" do 25 | describe "when :as == :check" do 26 | subject { instance(update_trigger_name: nil, as: :check) } 27 | 28 | its(:update_trigger_name) { is_expected.to be_nil } 29 | end 30 | end 31 | end 32 | 33 | describe "#<==>" do 34 | it { is_expected.to eq(instance) } 35 | it { is_expected.not_to eq(instance('check_name' => 'check_name_1')) } 36 | end 37 | 38 | describe "validation" do 39 | it { is_expected.to be_valid } 40 | 41 | describe ":check_name" do 42 | describe "when :as == :trigger" do 43 | subject { instance(update_trigger_name: nil, 44 | create_trigger_name: nil, 45 | check_name: :check_name, 46 | as: :trigger) } 47 | 48 | it { is_expected.to be_invalid } 49 | end 50 | end 51 | 52 | describe ":on" do 53 | describe "when :as == :check" do 54 | subject { instance(on: :create, as: :check, create_trigger_name: nil, update_trigger_name: nil) } 55 | 56 | it { is_expected.to be_invalid } 57 | end 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/exclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/exclusion' 4 | 5 | describe Mv::Postgresql::Validation::Exclusion do 6 | def instance opts = {} 7 | described_class.new(:table_name, :column_name, { in: [1, 2] }.merge(opts)) 8 | end 9 | 10 | subject { instance } 11 | 12 | describe "default values" do 13 | its(:as) { is_expected.to eq(:check)} 14 | its(:check_name) { is_expected.to eq("chk_mv_table_name_column_name") } 15 | 16 | describe ":create_trigger_name" do 17 | describe "when :as == :check" do 18 | subject { instance(create_trigger_name: nil, as: :check) } 19 | 20 | its(:create_trigger_name) { is_expected.to be_nil } 21 | end 22 | end 23 | 24 | describe ":update_trigger_name" do 25 | describe "when :as == :check" do 26 | subject { instance(update_trigger_name: nil, as: :check) } 27 | 28 | its(:update_trigger_name) { is_expected.to be_nil } 29 | end 30 | end 31 | end 32 | 33 | describe "#<==>" do 34 | it { is_expected.to eq(instance) } 35 | it { is_expected.not_to eq(instance('check_name' => 'check_name_1')) } 36 | end 37 | 38 | describe "validation" do 39 | it { is_expected.to be_valid } 40 | 41 | describe ":check_name" do 42 | describe "when :as == :trigger" do 43 | subject { instance(update_trigger_name: nil, 44 | create_trigger_name: nil, 45 | check_name: :check_name, 46 | as: :trigger) } 47 | 48 | it { is_expected.to be_invalid } 49 | end 50 | end 51 | 52 | describe ":on" do 53 | describe "when :as == :check" do 54 | subject { instance(on: :create, as: :check, create_trigger_name: nil, update_trigger_name: nil) } 55 | 56 | it { is_expected.to be_invalid } 57 | end 58 | end 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/factory_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/core/validation/factory' 4 | 5 | describe Mv::Core::Validation::Factory do 6 | subject(:factory) { described_class } 7 | 8 | describe "exclusion" do 9 | subject { factory.create_validation(:table_name, 10 | :column_name, 11 | :exclusion, 12 | { as: :check })} 13 | 14 | it { is_expected.to be_kind_of(Mv::Postgresql::Validation::Exclusion) } 15 | end 16 | 17 | describe "uniqueness" do 18 | subject { factory.create_validation(:table_name, 19 | :column_name, 20 | :uniqueness, 21 | { as: :check })} 22 | 23 | it { is_expected.to be_kind_of(Mv::Core::Validation::Uniqueness) } 24 | end 25 | 26 | describe "format" do 27 | subject { factory.create_validation(:table_name, 28 | :column_name, 29 | :format, 30 | { as: :check })} 31 | 32 | it { is_expected.to be_kind_of(Mv::Postgresql::Validation::Format) } 33 | end 34 | 35 | describe "inclusion" do 36 | subject { factory.create_validation(:table_name, 37 | :column_name, 38 | :inclusion, 39 | { as: :check })} 40 | 41 | it { is_expected.to be_kind_of(Mv::Postgresql::Validation::Inclusion) } 42 | end 43 | 44 | describe "length" do 45 | subject { factory.create_validation(:table_name, 46 | :column_name, 47 | :length, 48 | { as: :check })} 49 | 50 | it { is_expected.to be_kind_of(Mv::Postgresql::Validation::Length) } 51 | end 52 | 53 | describe "presence" do 54 | subject { factory.create_validation(:table_name, 55 | :column_name, 56 | :presence, 57 | { as: :check })} 58 | 59 | it { is_expected.to be_kind_of(Mv::Postgresql::Validation::Presence) } 60 | end 61 | 62 | describe "absence" do 63 | subject { factory.create_validation(:table_name, 64 | :column_name, 65 | :absence, 66 | { as: :check })} 67 | 68 | it { is_expected.to be_kind_of(Mv::Postgresql::Validation::Absence) } 69 | end 70 | 71 | describe "custom" do 72 | subject { factory.create_validation(:table_name, 73 | :column_name, 74 | :custom, 75 | { as: :check })} 76 | 77 | it { is_expected.to be_kind_of(Mv::Postgresql::Validation::Custom) } 78 | end 79 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/format_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/format' 4 | 5 | describe Mv::Postgresql::Validation::Format do 6 | def instance opts = {} 7 | table_name = opts.with_indifferent_access.delete(:table_name) || :table_name 8 | column_name = opts.with_indifferent_access.delete(:column_name) || :column_name 9 | described_class.new(table_name, column_name, 10 | { with: :with, 11 | message: :message, 12 | on: :save, 13 | create_trigger_name: :create_trigger_name, 14 | update_trigger_name: :update_trigger_name, 15 | allow_nil: true, 16 | allow_blank: true, 17 | as: :trigger}.with_indifferent_access.merge(opts) ) 18 | end 19 | 20 | subject { instance } 21 | 22 | describe "#initialize" do 23 | describe "by default" do 24 | its(:table_name) { is_expected.to eq(:table_name) } 25 | its(:column_name) { is_expected.to eq(:column_name) } 26 | its(:with) { is_expected.to eq(:with) } 27 | its(:message) { is_expected.to eq(:message) } 28 | its(:on) { is_expected.to eq(:save) } 29 | its(:create_trigger_name) { is_expected.to eq(:create_trigger_name) } 30 | its(:update_trigger_name) { is_expected.to eq(:update_trigger_name) } 31 | its(:allow_nil) { is_expected.to be_truthy } 32 | its(:allow_blank) { is_expected.to be_truthy } 33 | its(:as) { is_expected.to eq(:trigger) } 34 | 35 | describe ":check_name" do 36 | subject { instance(check_name: :check_name, as: :check) } 37 | 38 | its(:check_name) { is_expected.to eq(:check_name) } 39 | end 40 | end 41 | 42 | describe "when simplification provided" do 43 | subject { described_class.new(:table_name, :column_name, /aaa/)} 44 | 45 | its(:with) { is_expected.to eq(/aaa/) } 46 | end 47 | end 48 | 49 | describe "#<==>" do 50 | it { is_expected.to eq(instance) } 51 | it { is_expected.to eq(instance({'table_name' => 'table_name', 52 | 'column_name' => 'column_name', 53 | 'with' => 'with', 54 | 'message' => 'message', 55 | 'on' => 'save', 56 | 'create_trigger_name' => 'create_trigger_name', 57 | 'update_trigger_name' => 'update_trigger_name', 58 | 'allow_nil' => true, 59 | 'allow_blank' => true, 60 | 'as' => 'trigger' } )) } 61 | 62 | it { is_expected.not_to eq(instance(table_name: 'table_name_1')) } 63 | it { is_expected.not_to eq(instance(column_name: 'column_name_1')) } 64 | it { is_expected.not_to eq(instance(message: 'some_other_message')) } 65 | it { is_expected.not_to eq(instance(on: 'create')) } 66 | it { is_expected.not_to eq(instance(create_trigger_name: 'some_other_create_trigger_name')) } 67 | it { is_expected.not_to eq(instance(update_trigger_name: 'some_other_update_trigger_name')) } 68 | it { is_expected.not_to eq(instance(allow_nil: false)) } 69 | it { is_expected.not_to eq(instance(allow_blank: false)) } 70 | it { is_expected.not_to eq(instance(as: 'check')) } 71 | 72 | it { is_expected.not_to eq(instance(with: 'with_1')) } 73 | end 74 | 75 | describe "default values" do 76 | describe ":allow_nil" do 77 | subject { instance(allow_nil: nil) } 78 | 79 | its(:allow_nil) { is_expected.to be_falsey } 80 | end 81 | 82 | describe ":allow_blank" do 83 | subject { instance(allow_blank: nil) } 84 | 85 | its(:allow_blank) { is_expected.to be_falsey } 86 | end 87 | 88 | describe ":message" do 89 | subject { instance(message: nil) } 90 | 91 | its(:message) { is_expected.to eq('Format violated on the table table_name column column_name') } 92 | end 93 | 94 | describe ":on" do 95 | describe "when :as == :trigger" do 96 | subject { instance(on: nil, as: :trigger) } 97 | 98 | its(:on) { is_expected.to eq(:save) } 99 | end 100 | 101 | describe "when :as == :check" do 102 | subject { instance(on: nil, as: :check) } 103 | 104 | its(:on) { is_expected.to be_nil } 105 | end 106 | end 107 | 108 | describe ":as" do 109 | subject { instance(as: nil) } 110 | 111 | its(:as) { is_expected.to eq(:check) } 112 | end 113 | 114 | describe ":create_trigger_name" do 115 | describe "when :as == :trigger" do 116 | subject { instance(create_trigger_name: nil, as: :trigger) } 117 | 118 | its(:create_trigger_name) { is_expected.to eq('trg_mv_table_name_ins') } 119 | end 120 | 121 | describe "when :as == :check" do 122 | subject { instance(create_trigger_name: nil, as: :check) } 123 | 124 | its(:create_trigger_name) { is_expected.to be_nil } 125 | end 126 | 127 | describe "when :on == :update" do 128 | subject { instance(create_trigger_name: nil, on: :update) } 129 | 130 | its(:create_trigger_name) { is_expected.to be_nil } 131 | end 132 | end 133 | 134 | describe ":update_trigger_name" do 135 | describe "when :as == :trigger" do 136 | subject { instance(update_trigger_name: nil, as: :trigger) } 137 | 138 | its(:update_trigger_name) { is_expected.to eq('trg_mv_table_name_upd') } 139 | end 140 | 141 | describe "when :as == :check" do 142 | subject { instance(update_trigger_name: nil, as: :check) } 143 | 144 | its(:update_trigger_name) { is_expected.to be_nil } 145 | end 146 | 147 | describe "when :on == :create" do 148 | subject { instance(update_trigger_name: nil, on: :create) } 149 | 150 | its(:update_trigger_name) { is_expected.to be_nil } 151 | end 152 | end 153 | 154 | describe ":check_name" do 155 | describe "when :as == :trigger" do 156 | subject { instance(update_trigger_name: nil, as: :trigger, check_name: nil) } 157 | 158 | its(:check_name) { is_expected.to be_nil } 159 | end 160 | 161 | describe "when :as == :check" do 162 | subject { instance(update_trigger_name: nil, as: :check, check_name: nil) } 163 | 164 | its(:check_name) { is_expected.to eq('chk_mv_table_name_column_name')} 165 | end 166 | end 167 | end 168 | 169 | describe "validation" do 170 | it { is_expected.to be_valid } 171 | 172 | describe ":check_name" do 173 | describe "when :as == :check" do 174 | subject { instance(update_trigger_name: nil, 175 | create_trigger_name: nil, 176 | check_name: :check_name, 177 | on: nil, 178 | as: :check) } 179 | 180 | it { is_expected.to be_valid } 181 | end 182 | 183 | describe "when :as == :trigger" do 184 | subject { instance(update_trigger_name: nil, 185 | create_trigger_name: nil, 186 | check_name: :check_name, 187 | as: :trigger) } 188 | 189 | it { is_expected.to be_invalid } 190 | end 191 | end 192 | 193 | describe ":create_trigger_name" do 194 | describe "when :as == :check" do 195 | subject { instance(create_trigger_name: :trigger_name, update_trigger_name: nil, as: :check) } 196 | 197 | it { is_expected.to be_invalid } 198 | end 199 | 200 | describe "when :on == :update" do 201 | subject { instance(create_trigger_name: :trigger_name, update_trigger_name: nil, on: :update) } 202 | 203 | it { is_expected.to be_invalid } 204 | end 205 | end 206 | 207 | describe ":update_trigger_name" do 208 | describe "when :as == :check" do 209 | subject { instance(update_trigger_name: :trigger_name, create_trigger_name: nil, as: :check) } 210 | 211 | it { is_expected.to be_invalid } 212 | end 213 | 214 | describe "when :on == :create" do 215 | subject { instance(update_trigger_name: :trigger_name, create_trigger_name: nil, on: :create) } 216 | 217 | it { is_expected.to be_invalid } 218 | end 219 | end 220 | 221 | describe ":with" do 222 | describe "when it is not defined" do 223 | subject { instance(with: nil) } 224 | 225 | it { is_expected.to be_invalid } 226 | end 227 | end 228 | 229 | describe ":on" do 230 | describe "when :on == :save" do 231 | subject { instance(on: :save) } 232 | 233 | it { is_expected.to be_valid } 234 | end 235 | 236 | describe "when :on == :update" do 237 | subject { instance(on: :update, create_trigger_name: nil) } 238 | 239 | it { is_expected.to be_valid } 240 | end 241 | 242 | describe "when :on == :create" do 243 | subject { instance(on: :create, update_trigger_name: nil) } 244 | 245 | it { is_expected.to be_valid } 246 | end 247 | 248 | describe "when :on == :invalid_event" do 249 | subject { instance(on: :invalid_event) } 250 | 251 | it { is_expected.to be_invalid } 252 | end 253 | 254 | describe "when :as == :check" do 255 | subject { instance(on: :create, as: :check, create_trigger_name: nil, update_trigger_name: nil) } 256 | 257 | it { is_expected.to be_invalid } 258 | end 259 | end 260 | 261 | describe ":allow_nil" do 262 | [true, false].each do |value| 263 | describe "when :allow_nil == #{value}" do 264 | subject { instance(allow_nil: value) } 265 | 266 | it { is_expected.to be_valid } 267 | end 268 | end 269 | 270 | describe "when :allow_nil == :non_boolean_value" do 271 | subject { instance(allow_nil: :non_boolean_value) } 272 | 273 | it { is_expected.to be_invalid } 274 | end 275 | end 276 | 277 | describe ":allow_blank" do 278 | [true, false].each do |value| 279 | describe "when :allow_blank == #{value}" do 280 | subject { instance(allow_blank: value) } 281 | 282 | it { is_expected.to be_valid } 283 | end 284 | end 285 | 286 | describe "when :allow_blank == :non_boolean_value" do 287 | subject { instance(allow_blank: :non_boolean_value) } 288 | 289 | it { is_expected.to be_invalid } 290 | end 291 | end 292 | 293 | describe ":as" do 294 | describe "when :as == :check" do 295 | subject { instance(as: :check, create_trigger_name: nil, update_trigger_name: nil, on: nil) } 296 | 297 | it { is_expected.to be_valid } 298 | end 299 | 300 | describe "when :as == :invalid_constraint_type" do 301 | subject { instance(as: :invalid_constraint_type, create_trigger_name: nil, update_trigger_name: nil) } 302 | 303 | it { is_expected.to be_invalid } 304 | end 305 | end 306 | end 307 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/inclusion_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/inclusion' 4 | 5 | describe Mv::Postgresql::Validation::Inclusion do 6 | def instance opts = {} 7 | described_class.new(:table_name, :column_name, { in: [1, 2] }.merge(opts)) 8 | end 9 | 10 | subject { instance } 11 | 12 | describe "default values" do 13 | its(:as) { is_expected.to eq(:check)} 14 | its(:check_name) { is_expected.to eq("chk_mv_table_name_column_name") } 15 | 16 | describe ":create_trigger_name" do 17 | describe "when :as == :check" do 18 | subject { instance(create_trigger_name: nil, as: :check) } 19 | 20 | its(:create_trigger_name) { is_expected.to be_nil } 21 | end 22 | end 23 | 24 | describe ":update_trigger_name" do 25 | describe "when :as == :check" do 26 | subject { instance(update_trigger_name: nil, as: :check) } 27 | 28 | its(:update_trigger_name) { is_expected.to be_nil } 29 | end 30 | end 31 | end 32 | 33 | describe "#<==>" do 34 | it { is_expected.to eq(instance) } 35 | it { is_expected.not_to eq(instance('check_name' => 'check_name_1')) } 36 | end 37 | 38 | describe "validation" do 39 | it { is_expected.to be_valid } 40 | 41 | describe ":check_name" do 42 | describe "when :as == :trigger" do 43 | subject { instance(update_trigger_name: nil, 44 | create_trigger_name: nil, 45 | check_name: :check_name, 46 | as: :trigger) } 47 | 48 | it { is_expected.to be_invalid } 49 | end 50 | end 51 | 52 | describe ":on" do 53 | describe "when :as == :check" do 54 | subject { instance(on: :create, as: :check, create_trigger_name: nil, update_trigger_name: nil) } 55 | 56 | it { is_expected.to be_invalid } 57 | end 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/length_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/length' 4 | 5 | describe Mv::Postgresql::Validation::Length do 6 | def instance opts = {} 7 | described_class.new(:table_name, :column_name, { in: [1, 2] }.merge(opts)) 8 | end 9 | 10 | subject { instance } 11 | 12 | describe "default values" do 13 | its(:as) { is_expected.to eq(:check)} 14 | its(:check_name) { is_expected.to eq("chk_mv_table_name_column_name") } 15 | 16 | describe ":create_trigger_name" do 17 | describe "when :as == :check" do 18 | subject { instance(create_trigger_name: nil, as: :check) } 19 | 20 | its(:create_trigger_name) { is_expected.to be_nil } 21 | end 22 | end 23 | 24 | describe ":update_trigger_name" do 25 | describe "when :as == :check" do 26 | subject { instance(update_trigger_name: nil, as: :check) } 27 | 28 | its(:update_trigger_name) { is_expected.to be_nil } 29 | end 30 | end 31 | end 32 | 33 | describe "#<==>" do 34 | it { is_expected.to eq(instance) } 35 | it { is_expected.not_to eq(instance('check_name' => 'check_name_1')) } 36 | end 37 | 38 | describe "validation" do 39 | it { is_expected.to be_valid } 40 | 41 | describe ":check_name" do 42 | describe "when :as == :trigger" do 43 | subject { instance(update_trigger_name: nil, 44 | create_trigger_name: nil, 45 | check_name: :check_name, 46 | as: :trigger) } 47 | 48 | it { is_expected.to be_invalid } 49 | end 50 | end 51 | 52 | describe ":on" do 53 | describe "when :as == :check" do 54 | subject { instance(on: :create, as: :check, create_trigger_name: nil, update_trigger_name: nil) } 55 | 56 | it { is_expected.to be_invalid } 57 | end 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /spec/mv/postgresql/validation/presence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'mv/postgresql/validation/presence' 4 | 5 | describe Mv::Postgresql::Validation::Presence do 6 | def instance opts = {} 7 | described_class.new(:table_name, :column_name, opts) 8 | end 9 | 10 | subject { instance } 11 | 12 | describe "default values" do 13 | its(:as) { is_expected.to eq(:check)} 14 | its(:check_name) { is_expected.to eq("chk_mv_table_name_column_name") } 15 | 16 | describe ":create_trigger_name" do 17 | describe "when :as == :check" do 18 | subject { instance(create_trigger_name: nil, as: :check) } 19 | 20 | its(:create_trigger_name) { is_expected.to be_nil } 21 | end 22 | end 23 | 24 | describe ":update_trigger_name" do 25 | describe "when :as == :check" do 26 | subject { instance(update_trigger_name: nil, as: :check) } 27 | 28 | its(:update_trigger_name) { is_expected.to be_nil } 29 | end 30 | end 31 | end 32 | 33 | describe "#<==>" do 34 | it { is_expected.to eq(instance) } 35 | it { is_expected.not_to eq(instance('check_name' => 'check_name_1')) } 36 | end 37 | 38 | describe "validation" do 39 | it { is_expected.to be_valid } 40 | 41 | describe ":check_name" do 42 | describe "when :as == :trigger" do 43 | subject { instance(update_trigger_name: nil, 44 | create_trigger_name: nil, 45 | check_name: :check_name, 46 | as: :trigger) } 47 | 48 | it { is_expected.to be_invalid } 49 | end 50 | end 51 | 52 | describe ":on" do 53 | describe "when :as == :check" do 54 | subject { instance(on: :create, as: :check, create_trigger_name: nil, update_trigger_name: nil) } 55 | 56 | it { is_expected.to be_invalid } 57 | end 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | require 'rspec' 4 | require 'rspec/its' 5 | require 'shoulda' 6 | require 'mv-postgresql' 7 | require 'active_record/connection_adapters/postgresql_adapter' 8 | require 'mv/postgresql/loader' 9 | 10 | # Requires supporting files with custom matchers and macros, etc, 11 | # in ./support/ and its subdirectories. 12 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} 13 | 14 | require 'coveralls' 15 | Coveralls.wear! 16 | 17 | ActiveRecord::Migration.verbose = false 18 | 19 | RSpec.configure do |config| 20 | config.before :each do 21 | ActiveRecord::Base.remove_connection if ::ActiveRecord::Base.connected? 22 | ActiveRecord::Base.establish_connection(adapter: "postgresql", 23 | database: "validation_migration_test_db", 24 | username: "postgres") 25 | end 26 | end 27 | --------------------------------------------------------------------------------