├── .github └── workflows │ └── main.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── lib ├── spicy_validation.rb └── spicy_validation │ ├── monkey │ └── new_hash_syntax.rb │ ├── railtie.rb │ ├── renderer.rb │ ├── schema.rb │ ├── tasks │ └── validation.rake │ ├── validation.rb │ └── version.rb ├── spec ├── fake_app.rb ├── spec_helper.rb ├── spicy_validation │ ├── monkey │ │ └── new_hash_syntax_spec.rb │ ├── renderer_spec.rb │ ├── schema_spec.rb │ └── validation_spec.rb ├── spicy_validation_spec.rb └── support │ └── shared_contexts.rb └── spicy_validation.gemspec /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Ruby 2 | 3 | on: [push,pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Set up Database 10 | run: | 11 | sudo systemctl start mysql.service 12 | mysql -uroot -h127.0.0.1 -proot -e 'CREATE DATABASE IF NOT EXISTS sample;' 13 | - uses: actions/checkout@v2 14 | - name: Set up Ruby 15 | uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: 2.5.5 18 | - name: Run the default task 19 | run: | 20 | gem install bundler -v 2.2.11 21 | bundle install 22 | bundle exec rake 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | Gemfile.lock 10 | 11 | # rspec failure tracking 12 | .rspec_status 13 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.4 3 | 4 | Style/StringLiterals: 5 | Enabled: true 6 | EnforcedStyle: double_quotes 7 | 8 | Style/StringLiteralsInInterpolation: 9 | Enabled: true 10 | EnforcedStyle: double_quotes 11 | 12 | Layout/LineLength: 13 | Max: 120 14 | 15 | Style/Documentation: 16 | Enabled: false 17 | 18 | Style/SoleNestedConditional: 19 | AllowModifier: true 20 | 21 | Metrics/AbcSize: 22 | Max: 22 23 | 24 | CyclomaticComplexity: 25 | Max: 9 26 | 27 | Metrics/BlockLength: 28 | Exclude: 29 | - "spec/**/*" 30 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | # Specify your gem's dependencies in spicy_validation.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Naoto Ono 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpicyValidation 2 | 3 | Generate validation methods automatically from database schema. 4 | 5 | **[important notice]** Your model file will be overwritten! 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'spicy_validation' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle install 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install spicy_validation 22 | 23 | ## Usage 24 | 25 | 1. Run `validation:generate` task 26 | 2. Type a number that you would like to generate validation 27 | ```shell 28 | % rails validation:generate 29 | [warning] If you generate validation, model files will be overwritten. 30 | {:"0"=>"samples", :"1"=>"users"} 31 | Type a number you wanna generate validation > ex) 0 32 | ``` 33 | 34 | ## Example 35 | 36 | ```sql 37 | +------------+--------------+------+-----+---------+----------------+ 38 | | Field | Type | Null | Key | Default | Extra | 39 | +------------+--------------+------+-----+---------+----------------+ 40 | | id | bigint(20) | NO | PRI | NULL | auto_increment | 41 | | name | varchar(255) | NO | | NULL | | 42 | | message | varchar(255) | YES | | NULL | | 43 | | age | int(11) | NO | | NULL | | 44 | | score | int(11) | YES | | NULL | | 45 | | premium | tinyint(1) | YES | | NULL | | 46 | | created_at | datetime(6) | NO | | NULL | | 47 | | updated_at | datetime(6) | NO | | NULL | | 48 | +------------+--------------+------+-----+---------+----------------+ 49 | ``` 50 | 51 | ```ruby 52 | # app/models/user.rb 53 | class User < ApplicationRecord 54 | validates :name, presence: true 55 | validates :age, presence: true, numericality: true 56 | validates :score, numericality: true, allow_nil: true 57 | end 58 | ``` 59 | 60 | ## License 61 | 62 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 63 | 64 | ## Acknowledgement 65 | 66 | This repository based on https://github.com/sinsoku/pretty_validation. See the file headers for detail informations. 67 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rspec/core/rake_task" 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | require "rubocop/rake_task" 9 | 10 | RuboCop::RakeTask.new 11 | 12 | task default: %i[spec rubocop] 13 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "spicy_validation" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require "irb" 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/spicy_validation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails" 4 | require_relative "spicy_validation/version" 5 | require_relative "spicy_validation/railtie" 6 | require_relative "spicy_validation/renderer" 7 | require_relative "spicy_validation/schema" 8 | require_relative "spicy_validation/validation" 9 | require_relative "spicy_validation/monkey/new_hash_syntax" 10 | -------------------------------------------------------------------------------- /lib/spicy_validation/monkey/new_hash_syntax.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | module NewHashSyntax 30 | refine Hash do 31 | def format_hash 32 | map { |k, v| "#{k}: #{v.inspect}" }.join(", ") 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/spicy_validation/railtie.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | module SpicyValidation 30 | class Railtie < Rails::Railtie 31 | rake_tasks do 32 | load "spicy_validation/tasks/validation.rake" 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/spicy_validation/renderer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | require_relative "validation" 30 | require_relative "schema" 31 | 32 | module SpicyValidation 33 | class Renderer 34 | attr_reader :table_name 35 | 36 | def initialize(table_name:) 37 | @table_name = table_name 38 | end 39 | 40 | def self.generate(dry_run: false) 41 | table = choose_table_name 42 | object_table = new(table_name: table) 43 | return if object_table.validations.empty? 44 | 45 | object_table.write! if generate?(dry_run: dry_run) 46 | end 47 | 48 | def write! 49 | File.write(model_path, content) 50 | end 51 | 52 | def self.generate?(dry_run:) 53 | !dry_run 54 | end 55 | 56 | def self.choose_table_name 57 | puts "\e[33m[warning] If you generate validation, model file will be overwritten.\e[0m" 58 | hash_tables = Schema.table_names.map.with_index { |table, index| [index.to_s.to_sym, table] }.to_h 59 | p hash_tables 60 | while true 61 | print "Type a number you wanna generate validation > " 62 | num = $stdin.gets.chomp.to_sym 63 | break if hash_tables.key?(num) 64 | 65 | p "Type a number correctly!" 66 | end 67 | 68 | hash_tables[num] 69 | end 70 | 71 | def validations 72 | normal_validations + unique_validations 73 | end 74 | 75 | def content 76 | <<~MODEL 77 | class #{model_name} < ApplicationRecord 78 | #{validations.join("\n ")} 79 | end 80 | MODEL 81 | end 82 | 83 | def model_name 84 | table_name.classify 85 | end 86 | 87 | private 88 | 89 | def normal_validations 90 | Validation.normal_validations(table_name: table_name) 91 | end 92 | 93 | def unique_validations 94 | Validation.unique_validations(table_name: table_name) 95 | end 96 | 97 | def model_path 98 | File.join(Rails.root.glob("app/models/**/#{model_name.underscore}.rb")) 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /lib/spicy_validation/schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | module SpicyValidation 30 | class Schema 31 | def self.table_names 32 | abstract_tables = [ActiveRecord::SchemaMigration.table_name, ActiveRecord::InternalMetadata.table_name] 33 | ActiveRecord::Base 34 | .connection.tables 35 | .delete_if { |t| abstract_tables.include?(t) } 36 | end 37 | 38 | def self.columns(table_name:) 39 | ActiveRecord::Base.connection.columns(table_name) 40 | end 41 | 42 | def self.indexes(table_name:) 43 | ActiveRecord::Base.connection.indexes(table_name) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/spicy_validation/tasks/validation.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | namespace :validation do 30 | desc "Generate validations from database schema" 31 | task generate: :environment do 32 | require "spicy_validation/renderer" 33 | dry_run = %w[true 1 on].include? ENV["DRY_RUN"] 34 | SpicyValidation::Renderer.generate(dry_run: dry_run) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/spicy_validation/validation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | require_relative "monkey/new_hash_syntax" 30 | module SpicyValidation 31 | class Validation 32 | using NewHashSyntax 33 | attr_reader :method_name, :column_name, :options 34 | 35 | def initialize(method_name:, column_name:, options:) 36 | @method_name = method_name 37 | @column_name = column_name 38 | @options = options 39 | end 40 | 41 | def self.normal_validations(table_name:) 42 | columns = Schema.columns(table_name: table_name) 43 | columns = columns.reject { |x| x.name.in? %w[id created_at updated_at] } 44 | columns.map do |column| 45 | options = {} 46 | options[:presence] = true unless column.null 47 | options[:numericality] = true if column.type == :integer 48 | options[:allow_nil] = true if column.null && (column.type == :integer) 49 | Validation.new(method_name: "validates", column_name: column.name.to_sym, options: options) if options.present? 50 | end.compact 51 | end 52 | 53 | def self.unique_validations(table_name:) 54 | Schema.indexes(table_name: table_name).map do |index| 55 | column_name = index.columns[0] 56 | scope = index.columns[1..-1].map(&:to_sym) 57 | options = if scope.size > 1 58 | { scope: scope } 59 | elsif scope.size == 1 60 | { scope: scope[0] } 61 | end 62 | Validation.new(method_name: "validates_uniqueness_of", column_name: column_name.to_sym, options: options) 63 | end 64 | end 65 | 66 | def to_s 67 | if options.blank? 68 | "#{method_name} #{column_name.inspect}" 69 | else 70 | "#{method_name} #{column_name.inspect}, #{options.format_hash}" 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/spicy_validation/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SpicyValidation 4 | VERSION = "0.1.1" 5 | end 6 | -------------------------------------------------------------------------------- /spec/fake_app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | ActiveRecord::Base.establish_connection( 30 | adapter: "mysql2", 31 | username: "root", 32 | password: "root", 33 | database: "sample" 34 | ) 35 | class CreateAllTables < ActiveRecord::Migration[6.0] 36 | def self.up 37 | create_table ActiveRecord::SchemaMigration.table_name do |t| 38 | t.string :version, null: false 39 | end 40 | 41 | create_table :users 42 | end 43 | end 44 | CreateAllTables.up unless ActiveRecord::Base.connection.table_exists?(:users) 45 | ActiveRecord::Migration.verbose = false 46 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | require "spicy_validation" 30 | RSpec.configure do |config| 31 | # Enable flags like --only-failures and --next-failure 32 | config.example_status_persistence_file_path = ".rspec_status" 33 | 34 | # Disable RSpec exposing methods globally on `Module` and `main` 35 | config.disable_monkey_patching! 36 | 37 | config.expect_with :rspec do |c| 38 | c.syntax = :expect 39 | end 40 | end 41 | require "active_record" 42 | 43 | require_relative "fake_app" 44 | require_relative "support/shared_contexts" 45 | -------------------------------------------------------------------------------- /spec/spicy_validation/monkey/new_hash_syntax_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | require "spec_helper" 30 | 31 | using NewHashSyntax 32 | 33 | RSpec.describe NewHashSyntax do 34 | describe "#format" do 35 | context "{a: 1}.format" do 36 | subject { { a: 1 }.format_hash } 37 | it { is_expected.to eq "a: 1" } 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/spicy_validation/renderer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | require "spec_helper" 30 | RSpec.describe SpicyValidation::Renderer do 31 | describe "content" do 32 | subject { described_class.new(table_name: "users").content } 33 | include_context "add_column", :name, :string, null: false, default: "" 34 | include_context "add_column", :age, :integer 35 | include_context "add_column", :score, :integer, null: false, default: 0 36 | include_context "add_column", :premium, :boolean 37 | include_context "add_index", :name, unique: true 38 | include_context "add_index", %i[name age], unique: true 39 | include_context "add_index", %i[name age premium], unique: true 40 | let(:expected_result) do 41 | <<~MODEL 42 | class User < ApplicationRecord 43 | validates :name, presence: true 44 | validates :age, numericality: true, allow_nil: true 45 | validates :score, presence: true, numericality: true 46 | validates_uniqueness_of :name 47 | validates_uniqueness_of :name, scope: :age 48 | validates_uniqueness_of :name, scope: [:age, :premium] 49 | end 50 | MODEL 51 | end 52 | it { is_expected.to eq expected_result } 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/spicy_validation/schema_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | require "spec_helper" 30 | RSpec.describe SpicyValidation::Schema do 31 | describe "table_names" do 32 | subject { described_class.table_names } 33 | it { is_expected.to contain_exactly "users" } 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/spicy_validation/validation_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | require "spec_helper" 30 | RSpec.describe SpicyValidation::Validation do 31 | describe "normal_validations" do 32 | subject { described_class.normal_validations(table_name: "users") } 33 | context "column is {null: false}" do 34 | include_context "add_column", :name, :string, null: false, default: "" 35 | it { 36 | is_expected.to include have_attributes(method_name: "validates", column_name: :name, 37 | options: { presence: true }) 38 | } 39 | end 40 | context "column type is nullable integer" do 41 | include_context "add_column", :age, :integer 42 | it { 43 | is_expected.to include have_attributes(method_name: "validates", column_name: :age, 44 | options: { numericality: true, allow_nil: true }) 45 | } 46 | end 47 | context "column type is not null integer" do 48 | include_context "add_column", :score, :integer, null: false, default: 0 49 | it { 50 | is_expected.to include have_attributes(method_name: "validates", column_name: :score, 51 | options: { numericality: true, presence: true }) 52 | } 53 | end 54 | end 55 | describe "unique_validations" do 56 | subject { described_class.unique_validations(table_name: "users") } 57 | include_context "add_column", :name, :string, null: false, default: "" 58 | include_context "add_column", :age, :integer 59 | include_context "add_column", :premium, :boolean 60 | context "add index a column" do 61 | include_context "add_index", :name, unique: true 62 | it { is_expected.to include have_attributes(method_name: "validates_uniqueness_of", column_name: :name) } 63 | end 64 | context "add index to two columns" do 65 | include_context "add_index", %i[name age], unique: true 66 | it { 67 | is_expected.to include have_attributes(method_name: "validates_uniqueness_of", column_name: :name, 68 | options: { scope: :age }) 69 | } 70 | end 71 | context "add index to three columns" do 72 | include_context "add_index", %i[name age premium], unique: true 73 | it { 74 | is_expected.to include have_attributes(method_name: "validates_uniqueness_of", column_name: :name, 75 | options: { scope: %i[age premium] }) 76 | } 77 | end 78 | end 79 | describe "to_s" do 80 | subject { described_class.new(method_name: method_name, column_name: column_name, options: options).to_s } 81 | context "options is blank" do 82 | let(:method_name) { "validates_uniqueness_of" } 83 | let(:column_name) { :name } 84 | let(:options) { nil } 85 | it { is_expected.to eq "validates_uniqueness_of :name" } 86 | end 87 | context "options is single" do 88 | let(:method_name) { "validates" } 89 | let(:column_name) { :name } 90 | let(:options) { { presence: true } } 91 | it { is_expected.to eq "validates :name, presence: true" } 92 | end 93 | context "options are multiple" do 94 | let(:method_name) { "validates" } 95 | let(:column_name) { :age } 96 | let(:options) { { presence: true, numericality: true } } 97 | it { is_expected.to eq "validates :age, presence: true, numericality: true" } 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /spec/spicy_validation_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe SpicyValidation do 4 | it "has a version number" do 5 | expect(SpicyValidation::VERSION).not_to be nil 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/support/shared_contexts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # The following codes based on https://github.com/sinsoku/pretty_validation 5 | # 6 | # The MIT License (MIT) 7 | # 8 | # Copyright (c) 2015 sinsoku 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy 11 | # of this software and associated documentation files (the "Software"), to deal 12 | # in the Software without restriction, including without limitation the rights 13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | # copies of the Software, and to permit persons to whom the Software is 15 | # furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in 18 | # all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | # THE SOFTWARE. 27 | # 28 | 29 | RSpec.shared_context "add_column" do |column, type, options = {}| 30 | kclass = Class.new(ActiveRecord::Migration[6.0]) do 31 | class_eval <<-MIGRATION, __FILE__, __LINE__ + 1 32 | def self.up 33 | add_column :users, #{column.inspect}, #{type.inspect}, #{options.inspect} 34 | end 35 | def self.down 36 | remove_column :users, #{column.inspect} 37 | end 38 | MIGRATION 39 | end 40 | 41 | before { kclass.up } 42 | after { kclass.down } 43 | end 44 | RSpec.shared_context "add_index" do |column, options = {}| 45 | kclass = Class.new(ActiveRecord::Migration[6.0]) do 46 | class_eval <<-MIGRATION, __FILE__, __LINE__ + 1 47 | def self.up 48 | add_index :users, #{column.inspect}, #{options.inspect} 49 | end 50 | def self.down 51 | remove_index :users, #{column.inspect} 52 | end 53 | MIGRATION 54 | end 55 | 56 | before { kclass.up } 57 | after { kclass.down } 58 | end 59 | -------------------------------------------------------------------------------- /spicy_validation.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/spicy_validation/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "spicy_validation" 7 | spec.version = SpicyValidation::VERSION 8 | spec.authors = ["Naoto Ono"] 9 | spec.email = ["nono19@students.desu.edu"] 10 | 11 | spec.summary = "SpicyValidation generate validation from database schema." 12 | spec.description = "SpicyValidation overwrite model file that user would like to generate validation." 13 | spec.homepage = "https://github.com/ono-max/spicy_validation" 14 | spec.license = "MIT" 15 | spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0") 16 | 17 | spec.metadata["homepage_uri"] = spec.homepage 18 | spec.metadata["source_code_uri"] = "https://github.com/ono-max/spicy_validation" 19 | 20 | # Specify which files should be added to the gem when it is released. 21 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 22 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 23 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) } 24 | end 25 | spec.bindir = "exe" 26 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 27 | spec.require_paths = ["lib"] 28 | 29 | spec.add_dependency "rails" 30 | 31 | spec.add_development_dependency "bundler" 32 | spec.add_development_dependency "mysql2" 33 | spec.add_development_dependency "rake" 34 | spec.add_development_dependency "rspec" 35 | spec.add_development_dependency "rubocop" 36 | end 37 | --------------------------------------------------------------------------------