rails_validation_api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 00afcf5c271e4cb83d53f8f5cb0b7a92c1564222d3b505862e74eecfde47d0e9
4
+ data.tar.gz: 8d964b0829f1e0402f561e6d4e20bb0382ce2d573ae10d8857567ab636700c2e
5
+ SHA512:
6
+ metadata.gz: cf86fa4a1ee9a5931346d3d7effda41c07a69ed9886107e9d4d9316d4dbb8fa7bf20fc16cd5639d204858b637bb86d49f3a21f039214204c733359b3f689cee6
7
+ data.tar.gz: 2a5f164d626457756e0f927f663d7a52ca1b771b4b69ada411bbc9bee402f95d0832f92b6a34e74c4c9aace7d843d346dad86f0911f358ce1b14884abbb03494
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "active_support/core_ext/string/inflections"
4
+ require "fileutils"
5
+ require "debug"
6
+
7
+ COMMAND = ARGV.shift
8
+ NAME = ARGV.shift&.downcase
9
+
10
+ def usage
11
+ puts <<~TEXT
12
+ Usage:
13
+ rails_validation_api install
14
+ rails_validation_api generate [validator_name]
15
+ rails_validation_api destroy [validator_name]
16
+ Example:
17
+ rails_validation_api generate purchaseorder
18
+ rails_validation_api destroy purchaseorder
19
+ TEXT
20
+ end
21
+
22
+ def check_inflection(name)
23
+ require_relative "../config/initializers/inflections"
24
+ if ActiveSupport::Inflector.inflections.acronyms.key?("api")
25
+ 'API'
26
+ else
27
+ 'Api'
28
+ end
29
+ end
30
+
31
+ def generate_parameter_validator(name)
32
+ api = check_inflection(name)
33
+ class_name = "#{name.camelize}Validator"
34
+ path = "app/validators/api/validate_parameters/#{name}_validator.rb"
35
+
36
+ FileUtils.mkdir_p(File.dirname(path))
37
+
38
+ if File.exist?(path)
39
+ puts "⚠️ Validator already exists at #{path}"
40
+ else
41
+ File.write(path, <<~RUBY)
42
+ # frozen_string_literal: true
43
+
44
+ class #{api}::ValidateParameters::#{class_name}
45
+ FIELDS_VALIDATES = {
46
+ # account_id_validate:
47
+ # {
48
+ # field: :account_id, type: Integer, opts: [
49
+ # { required: true, message: "Account id is required" }
50
+ # ]
51
+ # }
52
+ }.freeze
53
+
54
+ def index
55
+ [
56
+ # FIELDS_VALIDATES[:account_id_validate]
57
+ ]
58
+ end
59
+ end
60
+ RUBY
61
+
62
+ puts "✅ Created #{path}"
63
+ end
64
+ end
65
+
66
+ def destroy_validator(name)
67
+ params_path = "app/validators/api/validate_parameters/#{name}_validator.rb"
68
+ path = "app/validators/api/#{name}_validator.rb"
69
+
70
+ if File.exist?(path)
71
+ File.delete(path)
72
+ puts "🗑️ Deleted #{path}"
73
+ else
74
+ puts "⚠️ File not found: #{path}"
75
+ end
76
+ if File.exist?(params_path)
77
+ File.delete(params_path)
78
+ puts "🗑️ Deleted #{params_path}"
79
+ else
80
+ puts "⚠️ File not found: #{params_path}"
81
+ end
82
+ end
83
+
84
+ def generate_validator(name)
85
+ api = check_inflection(name)
86
+ class_name = "#{name.camelize}Validator"
87
+ path = "app/validators/api/#{name}_validator.rb"
88
+
89
+ FileUtils.mkdir_p(File.dirname(path))
90
+
91
+ if File.exist?(path)
92
+ puts "⚠️ Validator already exists at #{path}"
93
+ else
94
+ File.write(path, <<~RUBY)
95
+ # frozen_string_literal: true
96
+
97
+ class #{api}::#{class_name}
98
+ def initialize(model_name, account)
99
+ @model_name = model_name
100
+ @account = account
101
+ end
102
+
103
+ def index(opts)
104
+ end
105
+ end
106
+ RUBY
107
+
108
+ puts "✅ Created #{path}"
109
+ end
110
+ end
111
+
112
+ case COMMAND
113
+ when "install"
114
+ puts "🚀 Installing rails_validation_api..."
115
+ generate_validator("application")
116
+ when "generate"
117
+ if NAME.nil?
118
+ puts "❌ Please provide a name. Example: rails_validation_api generate purchaseorder"
119
+ else
120
+ generate_parameter_validator(NAME)
121
+ generate_validator(NAME)
122
+ end
123
+ when "destroy"
124
+ if NAME.nil?
125
+ puts "❌ Please provide a name. Example: rails_validation_api generate purchaseorder"
126
+ else
127
+ destroy_validator(NAME)
128
+ end
129
+ else
130
+ usage
131
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+ module AutoLoadRailsValidationApi
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :set_validation_context
7
+ before_action :auto_load_method
8
+ end
9
+
10
+ def auto_load_method
11
+ call_validator
12
+ end
13
+
14
+ private
15
+
16
+ def set_validation_context
17
+ @params = params
18
+ @controller = controller_name
19
+ @action = action_name
20
+ @model_name = controller_name.classify
21
+ @account = Current.account rescue nil
22
+ end
23
+
24
+ def set_validator
25
+ klass = Module.const_get(validator_name) rescue nil
26
+ param_klass = Module.const_get(param_object_validator_name) rescue nil
27
+
28
+ @validator = klass.new(@model_name, @account) if klass.present?
29
+ @param_validator = param_klass.new if param_klass.present?
30
+ end
31
+
32
+ def validator_name
33
+ "API::#{@model_name}Validator"
34
+ end
35
+
36
+ def param_object_validator_name
37
+ "API::ValidateParameters::#{@model_name}Validator"
38
+ end
39
+
40
+ def call_validator(action: nil, opts: {})
41
+ set_validator
42
+ action = (action || action_name).to_sym
43
+ opts = opts.blank? ? params : opts
44
+ rules = @param_validator.send(action) rescue nil
45
+ if rules.present?
46
+ validator = RailsValidationApi::Validator.new(params, rules)
47
+ validator.validate
48
+ end
49
+ get_params = @validator.method(action).arity rescue nil
50
+
51
+ return get_params if get_params.nil?
52
+
53
+ if get_params == 0
54
+ begin
55
+ @validator.send(action)
56
+ rescue NoMethodError => ex
57
+ nil
58
+ rescue Exception => ex
59
+ raise RailsValidationApi::Error.new(ex.status, ex.message)
60
+ end
61
+ else
62
+ begin
63
+ @validator.send(action, opts)
64
+ rescue NoMethodError => ex
65
+ nil
66
+ rescue => ex
67
+ status_code = ex.is_a?(ActiveRecord::RecordNotFound) ? :not_found : ex.status
68
+ raise RailsValidationApi::Error.new(status_code, ex.message)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,11 @@
1
+ module Error
2
+ class CustomError < StandardError
3
+ attr_reader :status, :details
4
+
5
+ def initialize(status = :bad_request, message = nil, details: nil)
6
+ @status = status
7
+ @details = details
8
+ super(message || "Something went wrong")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsValidationApi
4
+ class DSL
5
+ attr_reader :rules
6
+
7
+ def initialize
8
+ @rules = {}
9
+ end
10
+
11
+ def param!(name, type, **opts, &block)
12
+ key = :"#{name}_validate"
13
+ rule = { field: name, type: type, opts: [opts].compact }
14
+
15
+ if block_given?
16
+ nested = DSL.new
17
+ nested.instance_eval(&block)
18
+ rule[:items] = nested.rules.values
19
+ end
20
+
21
+ @rules[key] = rule
22
+ end
23
+
24
+ # def rules
25
+ # @rules
26
+ # end
27
+ end
28
+ end
@@ -0,0 +1,89 @@
1
+ require "date"
2
+ require "time"
3
+ require "bigdecimal"
4
+ require "active_support/all"
5
+
6
+ module RailsValidationApi
7
+ class Validator
8
+ include RailsParam
9
+
10
+ attr_accessor :params
11
+ attr_reader :errors
12
+
13
+ def initialize(params, rules)
14
+ self.params = params.is_a?(ActionController::Parameters) ? params.to_unsafe_h : params
15
+ @rules = rules
16
+ @errors = []
17
+ end
18
+
19
+ def validate
20
+ return false if @rules.nil? || @rules.empty?
21
+
22
+ @rules.each do |rule_name, rule|
23
+ validate_field(rule_name)
24
+ end
25
+ if @errors.any?
26
+ @errors.each do |error|
27
+ raise RailsValidationApi::Error.new(error[:field], :unprocessable_entity, error[:message])
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def validate_field(rule)
35
+ return unless rule.is_a?(Hash)
36
+
37
+ field = rule[:field]
38
+ type = rule[:type]
39
+ opts = rule[:opts] || []
40
+ items = rule[:items]
41
+
42
+ return unless field && type
43
+
44
+ # Validate the main field
45
+ opts.each do |opt|
46
+ next unless opt.is_a?(Hash)
47
+
48
+ begin
49
+ param! field, type, **opt
50
+ rescue RailsParam::Param::InvalidParameterError => e
51
+ message = (e.message).include?(type.to_s) ? e.message : opt[:message]
52
+ @errors << { field: field, message: message }
53
+ end
54
+ end
55
+
56
+ # Validate nested items if present (for Hash/Array fields)
57
+ if items && params[field].is_a?(Hash)
58
+ validate_nested_items(field, items, params[field])
59
+ end
60
+ end
61
+
62
+ def validate_nested_items(parent_field, items, nested_params)
63
+ return unless items.is_a?(Array)
64
+
65
+ items.each do |item_rule|
66
+ next unless item_rule.is_a?(Hash)
67
+
68
+ item_field = item_rule[:field]
69
+ item_type = item_rule[:type]
70
+ item_opts = item_rule[:opts] || []
71
+
72
+ next unless item_field && item_type
73
+
74
+ # Create a temporary validator for nested validation
75
+ temp_validator = self.class.new(nested_params, {})
76
+ item_opts.each do |opt|
77
+ next unless opt.is_a?(Hash)
78
+
79
+ begin
80
+ temp_validator.param! item_field, item_type, **opt
81
+ rescue RailsParam::Param::InvalidParameterError => e
82
+ message = (e.message).include?(item_type.to_s) ? e.message : opt[:message]
83
+ @errors << { field: "#{parent_field}.#{item_field}", message: message }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module RailsValidationApi
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "active_model"
5
+ require "active_support"
6
+ require "rails_param"
7
+ require "active_support/core_ext/hash"
8
+ require_relative "rails_validation_api/dsl"
9
+ require_relative "rails_validation_api/validator"
10
+ require "active_support/concern"
11
+ require "active_support/core_ext/string/inflections"
12
+ require "rails_param"
13
+ require "date"
14
+ require "time"
15
+ require "bigdecimal"
16
+ require "active_support/all"
17
+
18
+ module RailsValidationApi
19
+ class Error < StandardError
20
+ attr_reader :field, :status, :additional_info
21
+ def initialize(field = :base, status = :unprocessable_entity, message = nil, additional_info: nil)
22
+ @field = field
23
+ @status = status
24
+ @additional_info = additional_info
25
+ super(message || "Something went wrong")
26
+ end
27
+ end
28
+
29
+ def self.build(&block)
30
+ dsl = DSL.new
31
+ dsl.instance_eval(&block)
32
+ dsl.rules
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_validation_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Linh Nguyen Quang
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-07-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails_param
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activemodel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: debug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Provides a DSL to validate and permit params like Rails strong parameters
70
+ email:
71
+ - linhnq@gmail.com
72
+ executables:
73
+ - rails_validation_api
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - exe/rails_validation_api
78
+ - lib/auto_load_rails_validation_api.rb
79
+ - lib/error/custom_error.rb
80
+ - lib/rails_validation_api.rb
81
+ - lib/rails_validation_api/dsl.rb
82
+ - lib/rails_validation_api/validator.rb
83
+ - lib/rails_validation_api/version.rb
84
+ homepage: https://github.com/your_username/rails_validation_api
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.4.6
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Strong parameter-style validation for Rails or plain Ruby apps
107
+ test_files: []