active_dry_form 0.1.0 → 1.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 +4 -4
- data/.rubocop.yml +18 -1
- data/.tool-versions +1 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +212 -0
- data/README.md +272 -11
- data/Rakefile +3 -3
- data/config/locales/dry_validation.ru.yml +62 -0
- data/lib/active_dry_form/base_contract.rb +10 -0
- data/lib/active_dry_form/base_form.rb +285 -0
- data/lib/active_dry_form/builder.rb +83 -48
- data/lib/active_dry_form/configuration.rb +46 -0
- data/lib/active_dry_form/form.rb +16 -181
- data/lib/active_dry_form/form_helper.rb +11 -4
- data/lib/active_dry_form/input.rb +19 -24
- data/lib/active_dry_form/version.rb +3 -1
- data/lib/active_dry_form.rb +18 -7
- metadata +57 -9
- data/lib/active_dry_form/schema_compiler_patch.rb +0 -31
data/lib/active_dry_form/form.rb
CHANGED
@@ -1,129 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveDryForm
|
4
|
-
class Form
|
4
|
+
class Form < BaseForm
|
5
5
|
|
6
6
|
include Dry::Monads[:result]
|
7
|
-
Dry::Schema.load_extensions(:
|
7
|
+
Dry::Schema.load_extensions(:json_schema)
|
8
8
|
ResultError = Class.new(StandardError)
|
9
9
|
|
10
|
-
|
10
|
+
cattr_accessor :contract_klass, instance_accessor: false, default: ::ActiveDryForm::BaseContract
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
def self.fields(namespace, i18n_key: nil, &block)
|
13
|
+
const_set :CURRENT_CONTRACT, Class.new(contract_klass, &block).new
|
14
|
+
const_set :FIELDS_INFO, self::CURRENT_CONTRACT.schema.json_schema(loose: true)
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
def self.fields(namespace, &block)
|
18
|
-
const_set :NAMESPACE, ActiveModel::Name.new(nil, nil, namespace.to_s)
|
19
|
-
const_set :CURRENT_CONTRACT, Class.new(ContractBase, &block).new
|
20
|
-
const_set :FIELDS_INFO, self::CURRENT_CONTRACT.schema.info[:keys]
|
21
|
-
|
22
|
-
self::FIELDS_INFO.each do |key, value|
|
23
|
-
if value[:keys]
|
24
|
-
sub_klass = Class.new do
|
25
|
-
const_set :NAMESPACE, ActiveModel::Name.new(nil, nil, key.to_s)
|
26
|
-
const_set :FIELDS_INFO, value[:keys]
|
27
|
-
|
28
|
-
def initialize(params, record, errors)
|
29
|
-
@params = params || {}
|
30
|
-
@record = record
|
31
|
-
@errors = errors || {}
|
32
|
-
end
|
33
|
-
|
34
|
-
self::FIELDS_INFO.each_key do |sub_key|
|
35
|
-
define_method sub_key do
|
36
|
-
@params[sub_key] || @record.try(sub_key)
|
37
|
-
end
|
38
|
-
|
39
|
-
def model_name
|
40
|
-
self.class::NAMESPACE
|
41
|
-
end
|
42
|
-
|
43
|
-
def info(sub_key)
|
44
|
-
self.class::FIELDS_INFO[sub_key]
|
45
|
-
end
|
16
|
+
const_set :NAMESPACE, ActiveModel::Name.new(nil, nil, namespace.name.capitalize)
|
17
|
+
const_get(:NAMESPACE).i18n_key = i18n_key if i18n_key
|
46
18
|
|
47
|
-
|
48
|
-
def to_model
|
49
|
-
self
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.human_attribute_name(field)
|
53
|
-
I18n.t(field, scope: :"activerecord.attributes.#{self::NAMESPACE.i18n_key}")
|
54
|
-
end
|
55
|
-
|
56
|
-
def errors
|
57
|
-
@errors
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
define_method key do
|
62
|
-
sub_klass.new(@params[key], @record.try(key), errors[key])
|
63
|
-
end
|
64
|
-
else
|
65
|
-
define_method key do
|
66
|
-
@params[key] || @record.try(key)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.default(method)
|
73
|
-
alias_method :"__#{method}", method
|
74
|
-
|
75
|
-
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
76
|
-
# def create_default(...)
|
77
|
-
# @params.merge!(__create_default(...))
|
78
|
-
# end
|
79
|
-
|
80
|
-
def #{method}(...)
|
81
|
-
@params.merge!(__#{method}(...))
|
82
|
-
end
|
83
|
-
RUBY
|
19
|
+
define_methods
|
84
20
|
end
|
85
21
|
|
86
22
|
def self.action(method)
|
87
23
|
alias_method :"__#{method}", method
|
88
24
|
|
89
|
-
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
90
|
-
# def create(...)
|
91
|
-
# if validator.failure?
|
92
|
-
# @base_errors = validator.errors.filter(:base?).map(&:to_s).presence
|
93
|
-
# return Failure(:validate_invalid)
|
94
|
-
# end
|
95
|
-
#
|
96
|
-
# result = __create(...)
|
97
|
-
#
|
98
|
-
# unless result.is_a?(::Dry::Monads::Result)
|
99
|
-
# raise ResultError, 'method `create` should be returning `monad`'
|
100
|
-
# end
|
101
|
-
#
|
102
|
-
# case result
|
103
|
-
# in Failure[:failure_service, base_errors]
|
104
|
-
# @base_errors = base_errors
|
105
|
-
# else
|
106
|
-
# end
|
107
|
-
#
|
108
|
-
# result
|
109
|
-
# end
|
110
|
-
|
25
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
|
111
26
|
def #{method}(...)
|
112
|
-
|
113
|
-
|
114
|
-
return Failure(:validate_invalid)
|
115
|
-
end
|
27
|
+
validate
|
28
|
+
return Failure(:invalid_form) unless valid?
|
116
29
|
|
117
30
|
result = __#{method}(...)
|
118
31
|
|
119
|
-
unless result.is_a?(::Dry::Monads::Result)
|
120
|
-
raise ResultError, 'method `#{method}` should be returning `monad`'
|
121
|
-
end
|
122
|
-
|
123
32
|
case result
|
124
|
-
|
125
|
-
@base_errors =
|
33
|
+
when ::Dry::Monads::Result::Failure
|
34
|
+
@base_errors = Array.wrap(result.failure)
|
35
|
+
when ::Dry::Monads::Result::Success
|
126
36
|
else
|
37
|
+
raise ResultError, 'method `#{method}` must return `monad`'
|
127
38
|
end
|
128
39
|
|
129
40
|
result
|
@@ -131,81 +42,5 @@ module ActiveDryForm
|
|
131
42
|
RUBY
|
132
43
|
end
|
133
44
|
|
134
|
-
attr_reader :base_errors, :record
|
135
|
-
|
136
|
-
def initialize(record: nil, params_form: nil, params_init: nil)
|
137
|
-
raise 'in `params_form` use `request.parameters` instead of `params`' if params_form.is_a?(::ActionController::Parameters)
|
138
|
-
raise 'in `params_init` use `request.parameters` instead of `params`' if params_init.is_a?(::ActionController::Parameters)
|
139
|
-
|
140
|
-
@params =
|
141
|
-
if params_form
|
142
|
-
_deep_transform_values_in_params!(params_form[self.class::NAMESPACE.param_key].deep_transform_keys!(&:to_sym))
|
143
|
-
elsif params_init
|
144
|
-
params_init.deep_transform_keys!(&:to_sym)
|
145
|
-
else
|
146
|
-
{}
|
147
|
-
end
|
148
|
-
|
149
|
-
@record = record if record
|
150
|
-
end
|
151
|
-
|
152
|
-
def info(key)
|
153
|
-
self.class::FIELDS_INFO[key]
|
154
|
-
end
|
155
|
-
|
156
|
-
def model_name
|
157
|
-
self.class::NAMESPACE
|
158
|
-
end
|
159
|
-
|
160
|
-
# ActionView::Helpers::Tags::Translator#human_attribute_name
|
161
|
-
def to_model
|
162
|
-
self
|
163
|
-
end
|
164
|
-
|
165
|
-
def self.human_attribute_name(field)
|
166
|
-
I18n.t(field, scope: :"activerecord.attributes.#{self::NAMESPACE.i18n_key}")
|
167
|
-
end
|
168
|
-
|
169
|
-
def persisted?
|
170
|
-
@record&.persisted?
|
171
|
-
end
|
172
|
-
|
173
|
-
def to_key
|
174
|
-
key = @record&.id
|
175
|
-
[key] if key
|
176
|
-
end
|
177
|
-
|
178
|
-
# используется при генерации URL, когда record.persisted?
|
179
|
-
def to_param
|
180
|
-
@record.id.to_s
|
181
|
-
end
|
182
|
-
|
183
|
-
def validator
|
184
|
-
@validator ||= self.class::CURRENT_CONTRACT.call(@params, { form: self })
|
185
|
-
end
|
186
|
-
|
187
|
-
def errors
|
188
|
-
@errors ||= @validator ? @validator.errors.to_h : {}
|
189
|
-
end
|
190
|
-
|
191
|
-
def view_component
|
192
|
-
self.class.module_parent::Component.new(self)
|
193
|
-
end
|
194
|
-
|
195
|
-
private def _deep_transform_values_in_params!(object)
|
196
|
-
case object
|
197
|
-
when String
|
198
|
-
object.strip.presence
|
199
|
-
when Hash
|
200
|
-
object.transform_values! { |value| _deep_transform_values_in_params!(value) }
|
201
|
-
when Array
|
202
|
-
object.map! { |e| _deep_transform_values_in_params!(e) }
|
203
|
-
object.compact!
|
204
|
-
object
|
205
|
-
else
|
206
|
-
object
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
45
|
end
|
211
46
|
end
|
@@ -3,14 +3,21 @@
|
|
3
3
|
module ActiveDryForm
|
4
4
|
module FormHelper
|
5
5
|
|
6
|
-
def active_dry_form_for(name, options = {})
|
6
|
+
def active_dry_form_for(name, options = {}, &block)
|
7
7
|
options[:builder] = ActiveDryForm::Builder
|
8
|
-
options[:html]
|
9
|
-
options[:html][:class] = "active-dry-form #{options[:html][:class]}"
|
8
|
+
options[:html] = html_options(options)
|
10
9
|
|
11
10
|
form_for(name, options) do |f|
|
12
11
|
concat f.show_base_errors
|
13
|
-
|
12
|
+
instance_exec(f, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private def html_options(options)
|
17
|
+
classes = { class: ActiveDryForm.config.css_classes.form }
|
18
|
+
|
19
|
+
(options[:html] || {}).merge(ActiveDryForm.config.html_options.form, classes) do |_key, oldval, newval|
|
20
|
+
Array.wrap(newval) + Array.wrap(oldval)
|
14
21
|
end
|
15
22
|
end
|
16
23
|
|
@@ -3,37 +3,32 @@
|
|
3
3
|
module ActiveDryForm
|
4
4
|
class Input
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(builder, method_type, method, options)
|
6
|
+
def initialize(builder, builder_method, field, options)
|
9
7
|
@builder = builder
|
10
|
-
@
|
11
|
-
@
|
8
|
+
@form_object = builder.object
|
9
|
+
@template = builder.instance_variable_get(:@template)
|
12
10
|
|
13
|
-
|
14
|
-
@
|
15
|
-
@required = info[:required]
|
11
|
+
@builder_method = builder_method
|
12
|
+
@field = field
|
16
13
|
|
17
14
|
@label_opts = options[:label]
|
18
15
|
@label_text = options[:label_text]
|
19
16
|
@hint_text = options[:hint]
|
20
|
-
@
|
21
|
-
@
|
17
|
+
@required = options[:required]
|
18
|
+
@input_user_options = options.except(:label, :hint, :label_text)
|
22
19
|
end
|
23
20
|
|
24
21
|
def css_classes
|
25
22
|
[
|
26
|
-
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
('required' if @required),
|
31
|
-
('error' if error?(@method)),
|
23
|
+
ActiveDryForm.config.css_classes.input,
|
24
|
+
@builder_method,
|
25
|
+
(ActiveDryForm.config.css_classes.input_required if @required),
|
26
|
+
(ActiveDryForm.config.css_classes.input_error if error?(@field)),
|
32
27
|
].compact
|
33
28
|
end
|
34
29
|
|
35
30
|
def wrap_tag(input, label_last: nil)
|
36
|
-
@
|
31
|
+
@template.tag(:div, class: css_classes) do
|
37
32
|
[
|
38
33
|
label_last ? input : label,
|
39
34
|
label_last ? label : input,
|
@@ -44,29 +39,29 @@ module ActiveDryForm
|
|
44
39
|
end
|
45
40
|
|
46
41
|
def label
|
47
|
-
@builder.label(@
|
42
|
+
@builder.label(@field, @label_text) unless @label_opts == false
|
48
43
|
end
|
49
44
|
|
50
45
|
def hint_text
|
51
46
|
return unless @hint_text
|
52
47
|
|
53
|
-
@
|
48
|
+
@template.content_tag(:small, @hint_text, class: ActiveDryForm.config.css_classes.hint)
|
54
49
|
end
|
55
50
|
|
56
51
|
def error_text
|
57
|
-
return unless error?(@
|
52
|
+
return unless error?(@field)
|
58
53
|
|
59
54
|
obj_error_text =
|
60
|
-
case e = @
|
55
|
+
case e = @form_object.errors[@field]
|
61
56
|
when Hash then e.values
|
62
57
|
else e
|
63
58
|
end
|
64
59
|
|
65
|
-
@
|
60
|
+
@template.content_tag(:div, obj_error_text.join('<br />').html_safe, class: ActiveDryForm.config.css_classes.error)
|
66
61
|
end
|
67
62
|
|
68
|
-
def error?(
|
69
|
-
@
|
63
|
+
def error?(field)
|
64
|
+
@form_object.errors.key?(field)
|
70
65
|
end
|
71
66
|
|
72
67
|
end
|
data/lib/active_dry_form.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
require 'dry-monads'
|
4
|
+
require 'dry-validation'
|
5
|
+
require 'active_model'
|
6
|
+
require 'action_view'
|
7
|
+
require 'action_controller'
|
8
|
+
|
9
|
+
require_relative 'active_dry_form/version'
|
10
|
+
require_relative 'active_dry_form/configuration'
|
11
|
+
require_relative 'active_dry_form/builder'
|
12
|
+
require_relative 'active_dry_form/base_form'
|
13
|
+
require_relative 'active_dry_form/base_contract'
|
14
|
+
require_relative 'active_dry_form/form'
|
15
|
+
require_relative 'active_dry_form/input'
|
16
|
+
require_relative 'active_dry_form/form_helper'
|
9
17
|
|
10
18
|
module ActiveDryForm
|
19
|
+
|
11
20
|
class Error < StandardError; end
|
12
|
-
|
21
|
+
class ParamsNotAllowedError < Error; end
|
22
|
+
class DateTimeNotAllowedError < Error; end
|
23
|
+
|
13
24
|
end
|
metadata
CHANGED
@@ -1,17 +1,45 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_dry_form
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ermolaev Andrey
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: actionpack
|
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: activerecord
|
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: dry-configurable
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
16
44
|
requirements:
|
17
45
|
- - ">="
|
@@ -38,6 +66,20 @@ dependencies:
|
|
38
66
|
- - ">="
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: dry-validation
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
41
83
|
description: Form validation for Rails with Dry-Validation
|
42
84
|
email:
|
43
85
|
- andruhafirst@yandex.ru
|
@@ -48,18 +90,23 @@ files:
|
|
48
90
|
- ".editorconfig"
|
49
91
|
- ".rspec"
|
50
92
|
- ".rubocop.yml"
|
93
|
+
- ".tool-versions"
|
51
94
|
- Gemfile
|
95
|
+
- Gemfile.lock
|
52
96
|
- LICENSE.txt
|
53
97
|
- README.md
|
54
98
|
- Rakefile
|
55
99
|
- bin/console
|
56
100
|
- bin/setup
|
101
|
+
- config/locales/dry_validation.ru.yml
|
57
102
|
- lib/active_dry_form.rb
|
103
|
+
- lib/active_dry_form/base_contract.rb
|
104
|
+
- lib/active_dry_form/base_form.rb
|
58
105
|
- lib/active_dry_form/builder.rb
|
106
|
+
- lib/active_dry_form/configuration.rb
|
59
107
|
- lib/active_dry_form/form.rb
|
60
108
|
- lib/active_dry_form/form_helper.rb
|
61
109
|
- lib/active_dry_form/input.rb
|
62
|
-
- lib/active_dry_form/schema_compiler_patch.rb
|
63
110
|
- lib/active_dry_form/version.rb
|
64
111
|
homepage: https://github.com/corp-gp/active_dry_form
|
65
112
|
licenses:
|
@@ -68,7 +115,8 @@ metadata:
|
|
68
115
|
allowed_push_host: https://rubygems.org
|
69
116
|
homepage_uri: https://github.com/corp-gp/active_dry_form
|
70
117
|
source_code_uri: https://github.com/corp-gp/active_dry_form
|
71
|
-
|
118
|
+
rubygems_mfa_required: 'true'
|
119
|
+
post_install_message:
|
72
120
|
rdoc_options: []
|
73
121
|
require_paths:
|
74
122
|
- lib
|
@@ -76,15 +124,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
124
|
requirements:
|
77
125
|
- - ">="
|
78
126
|
- !ruby/object:Gem::Version
|
79
|
-
version: 2.
|
127
|
+
version: 2.7.0
|
80
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
129
|
requirements:
|
82
130
|
- - ">="
|
83
131
|
- !ruby/object:Gem::Version
|
84
132
|
version: '0'
|
85
133
|
requirements: []
|
86
|
-
rubygems_version: 3.
|
87
|
-
signing_key:
|
134
|
+
rubygems_version: 3.3.17
|
135
|
+
signing_key:
|
88
136
|
specification_version: 4
|
89
137
|
summary: Form validation for Rails with Dry-Validation
|
90
138
|
test_files: []
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require "dry/schema/extensions/info/schema_compiler"
|
2
|
-
|
3
|
-
require "dry/schema/constants"
|
4
|
-
|
5
|
-
module Dry
|
6
|
-
module Schema
|
7
|
-
module Info
|
8
|
-
class SchemaCompiler
|
9
|
-
def visit_not(_node, opts = {})
|
10
|
-
key = opts[:key]
|
11
|
-
keys[key][:nullable] = true
|
12
|
-
end
|
13
|
-
|
14
|
-
def visit_predicate(node, opts = {})
|
15
|
-
name, rest = node
|
16
|
-
|
17
|
-
key = opts[:key]
|
18
|
-
|
19
|
-
if name.equal?(:key?)
|
20
|
-
keys[rest[0][1]] = { required: opts.fetch(:required, true) }
|
21
|
-
elsif name.equal?(:array?)
|
22
|
-
keys[key][:array] = true
|
23
|
-
else
|
24
|
-
type = PREDICATE_TO_TYPE[name]
|
25
|
-
keys[key][:type] = type if type
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|