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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0134c8126c7ce5ea25422550c9ed232b668ce2d0e3b892bbc1535340bf2bcfe5
|
4
|
+
data.tar.gz: a1a90179815dec9d0964d6aa03612565afc1cafe157cb29eebeba225fcf0996d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb484a9edbfb4fe0f215717dbd4cab04b41ec6eaf33334c663ac588df393bdae26d13cd7d7c973d25e3c193a5f21871314142122679c91b44c3fee7765cd5af7
|
7
|
+
data.tar.gz: f845993ba3a0cc7c0756b610634cae6a1c6b3fb0e4cee3f45d985ffc045cbe10460637b198fdcbd35cdbc1f1ff71df105edddc55ac3aaaca18f832223a40b15e
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,20 @@
|
|
1
1
|
inherit_gem:
|
2
2
|
rubocop-gp:
|
3
|
-
- ./config/default.yml
|
3
|
+
- ./config/default.yml
|
4
|
+
|
5
|
+
Gp/OptArgParameters:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
Style/SingleLineMethods:
|
9
|
+
Exclude:
|
10
|
+
- lib/active_dry_form/builder.rb
|
11
|
+
|
12
|
+
Style/StringLiterals:
|
13
|
+
EnforcedStyle: single_quotes
|
14
|
+
|
15
|
+
Rails/Delegate:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
AllCops:
|
19
|
+
NewCops: enable
|
20
|
+
TargetRubyVersion: 2.7
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.1.2
|
data/Gemfile
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
source
|
3
|
+
source 'https://rubygems.org'
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in active_dry_form.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
-
gem
|
8
|
+
gem 'dry-schema', github: 'dry-rb/dry-schema'
|
9
9
|
|
10
|
-
gem
|
10
|
+
gem 'rake', '~> 13.0'
|
11
|
+
gem 'rspec', '~> 3.0'
|
12
|
+
|
13
|
+
gem 'sqlite3', '~> 1.4'
|
11
14
|
|
12
15
|
gem 'rubocop-gp', github: 'corp-gp/rubocop-gp', require: false
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/corp-gp/rubocop-gp.git
|
3
|
+
revision: e5bd900ce4b10077cf8f4ae88a5a6e32e7861b65
|
4
|
+
specs:
|
5
|
+
rubocop-gp (0.0.4)
|
6
|
+
rubocop
|
7
|
+
rubocop-capybara
|
8
|
+
rubocop-factory_bot
|
9
|
+
rubocop-performance
|
10
|
+
rubocop-rails
|
11
|
+
rubocop-rspec
|
12
|
+
rubocop-rspec_rails
|
13
|
+
|
14
|
+
GIT
|
15
|
+
remote: https://github.com/dry-rb/dry-schema.git
|
16
|
+
revision: ced2c60aef958eb7f9b2fa0057540249f1ec4e20
|
17
|
+
specs:
|
18
|
+
dry-schema (1.13.4)
|
19
|
+
concurrent-ruby (~> 1.0)
|
20
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
21
|
+
dry-core (~> 1.0, < 2)
|
22
|
+
dry-initializer (~> 3.0)
|
23
|
+
dry-logic (>= 1.4, < 2)
|
24
|
+
dry-types (>= 1.7, < 2)
|
25
|
+
zeitwerk (~> 2.6)
|
26
|
+
|
27
|
+
PATH
|
28
|
+
remote: .
|
29
|
+
specs:
|
30
|
+
active_dry_form (1.0.0)
|
31
|
+
actionpack
|
32
|
+
activerecord
|
33
|
+
dry-configurable
|
34
|
+
dry-monads
|
35
|
+
dry-validation
|
36
|
+
|
37
|
+
GEM
|
38
|
+
remote: https://rubygems.org/
|
39
|
+
specs:
|
40
|
+
actionpack (7.1.3.4)
|
41
|
+
actionview (= 7.1.3.4)
|
42
|
+
activesupport (= 7.1.3.4)
|
43
|
+
nokogiri (>= 1.8.5)
|
44
|
+
racc
|
45
|
+
rack (>= 2.2.4)
|
46
|
+
rack-session (>= 1.0.1)
|
47
|
+
rack-test (>= 0.6.3)
|
48
|
+
rails-dom-testing (~> 2.2)
|
49
|
+
rails-html-sanitizer (~> 1.6)
|
50
|
+
actionview (7.1.3.4)
|
51
|
+
activesupport (= 7.1.3.4)
|
52
|
+
builder (~> 3.1)
|
53
|
+
erubi (~> 1.11)
|
54
|
+
rails-dom-testing (~> 2.2)
|
55
|
+
rails-html-sanitizer (~> 1.6)
|
56
|
+
activemodel (7.1.3.4)
|
57
|
+
activesupport (= 7.1.3.4)
|
58
|
+
activerecord (7.1.3.4)
|
59
|
+
activemodel (= 7.1.3.4)
|
60
|
+
activesupport (= 7.1.3.4)
|
61
|
+
timeout (>= 0.4.0)
|
62
|
+
activesupport (7.1.3.4)
|
63
|
+
base64
|
64
|
+
bigdecimal
|
65
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
66
|
+
connection_pool (>= 2.2.5)
|
67
|
+
drb
|
68
|
+
i18n (>= 1.6, < 2)
|
69
|
+
minitest (>= 5.1)
|
70
|
+
mutex_m
|
71
|
+
tzinfo (~> 2.0)
|
72
|
+
ast (2.4.2)
|
73
|
+
base64 (0.2.0)
|
74
|
+
bigdecimal (3.1.8)
|
75
|
+
builder (3.3.0)
|
76
|
+
concurrent-ruby (1.3.3)
|
77
|
+
connection_pool (2.4.1)
|
78
|
+
crass (1.0.6)
|
79
|
+
diff-lcs (1.5.1)
|
80
|
+
drb (2.2.1)
|
81
|
+
dry-configurable (1.2.0)
|
82
|
+
dry-core (~> 1.0, < 2)
|
83
|
+
zeitwerk (~> 2.6)
|
84
|
+
dry-core (1.0.1)
|
85
|
+
concurrent-ruby (~> 1.0)
|
86
|
+
zeitwerk (~> 2.6)
|
87
|
+
dry-inflector (1.1.0)
|
88
|
+
dry-initializer (3.1.1)
|
89
|
+
dry-logic (1.5.0)
|
90
|
+
concurrent-ruby (~> 1.0)
|
91
|
+
dry-core (~> 1.0, < 2)
|
92
|
+
zeitwerk (~> 2.6)
|
93
|
+
dry-monads (1.6.0)
|
94
|
+
concurrent-ruby (~> 1.0)
|
95
|
+
dry-core (~> 1.0, < 2)
|
96
|
+
zeitwerk (~> 2.6)
|
97
|
+
dry-types (1.7.2)
|
98
|
+
bigdecimal (~> 3.0)
|
99
|
+
concurrent-ruby (~> 1.0)
|
100
|
+
dry-core (~> 1.0)
|
101
|
+
dry-inflector (~> 1.0)
|
102
|
+
dry-logic (~> 1.4)
|
103
|
+
zeitwerk (~> 2.6)
|
104
|
+
dry-validation (1.10.0)
|
105
|
+
concurrent-ruby (~> 1.0)
|
106
|
+
dry-core (~> 1.0, < 2)
|
107
|
+
dry-initializer (~> 3.0)
|
108
|
+
dry-schema (>= 1.12, < 2)
|
109
|
+
zeitwerk (~> 2.6)
|
110
|
+
erubi (1.13.0)
|
111
|
+
i18n (1.14.5)
|
112
|
+
concurrent-ruby (~> 1.0)
|
113
|
+
json (2.7.2)
|
114
|
+
language_server-protocol (3.17.0.3)
|
115
|
+
loofah (2.22.0)
|
116
|
+
crass (~> 1.0.2)
|
117
|
+
nokogiri (>= 1.12.0)
|
118
|
+
minitest (5.24.1)
|
119
|
+
mutex_m (0.2.0)
|
120
|
+
nokogiri (1.16.6-arm64-darwin)
|
121
|
+
racc (~> 1.4)
|
122
|
+
nokogiri (1.16.6-x86_64-linux)
|
123
|
+
racc (~> 1.4)
|
124
|
+
parallel (1.25.1)
|
125
|
+
parser (3.3.3.0)
|
126
|
+
ast (~> 2.4.1)
|
127
|
+
racc
|
128
|
+
racc (1.8.0)
|
129
|
+
rack (3.1.6)
|
130
|
+
rack-session (2.0.0)
|
131
|
+
rack (>= 3.0.0)
|
132
|
+
rack-test (2.1.0)
|
133
|
+
rack (>= 1.3)
|
134
|
+
rails-dom-testing (2.2.0)
|
135
|
+
activesupport (>= 5.0.0)
|
136
|
+
minitest
|
137
|
+
nokogiri (>= 1.6)
|
138
|
+
rails-html-sanitizer (1.6.0)
|
139
|
+
loofah (~> 2.21)
|
140
|
+
nokogiri (~> 1.14)
|
141
|
+
rainbow (3.1.1)
|
142
|
+
rake (13.2.1)
|
143
|
+
regexp_parser (2.9.2)
|
144
|
+
rexml (3.3.1)
|
145
|
+
strscan
|
146
|
+
rspec (3.13.0)
|
147
|
+
rspec-core (~> 3.13.0)
|
148
|
+
rspec-expectations (~> 3.13.0)
|
149
|
+
rspec-mocks (~> 3.13.0)
|
150
|
+
rspec-core (3.13.0)
|
151
|
+
rspec-support (~> 3.13.0)
|
152
|
+
rspec-expectations (3.13.1)
|
153
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
154
|
+
rspec-support (~> 3.13.0)
|
155
|
+
rspec-mocks (3.13.1)
|
156
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
157
|
+
rspec-support (~> 3.13.0)
|
158
|
+
rspec-support (3.13.1)
|
159
|
+
rubocop (1.64.1)
|
160
|
+
json (~> 2.3)
|
161
|
+
language_server-protocol (>= 3.17.0)
|
162
|
+
parallel (~> 1.10)
|
163
|
+
parser (>= 3.3.0.2)
|
164
|
+
rainbow (>= 2.2.2, < 4.0)
|
165
|
+
regexp_parser (>= 1.8, < 3.0)
|
166
|
+
rexml (>= 3.2.5, < 4.0)
|
167
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
168
|
+
ruby-progressbar (~> 1.7)
|
169
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
170
|
+
rubocop-ast (1.31.3)
|
171
|
+
parser (>= 3.3.1.0)
|
172
|
+
rubocop-capybara (2.21.0)
|
173
|
+
rubocop (~> 1.41)
|
174
|
+
rubocop-factory_bot (2.26.1)
|
175
|
+
rubocop (~> 1.61)
|
176
|
+
rubocop-performance (1.21.1)
|
177
|
+
rubocop (>= 1.48.1, < 2.0)
|
178
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
179
|
+
rubocop-rails (2.25.1)
|
180
|
+
activesupport (>= 4.2.0)
|
181
|
+
rack (>= 1.1)
|
182
|
+
rubocop (>= 1.33.0, < 2.0)
|
183
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
184
|
+
rubocop-rspec (3.0.2)
|
185
|
+
rubocop (~> 1.61)
|
186
|
+
rubocop-rspec_rails (2.30.0)
|
187
|
+
rubocop (~> 1.61)
|
188
|
+
rubocop-rspec (~> 3, >= 3.0.1)
|
189
|
+
ruby-progressbar (1.13.0)
|
190
|
+
sqlite3 (1.7.3-arm64-darwin)
|
191
|
+
sqlite3 (1.7.3-x86_64-linux)
|
192
|
+
strscan (3.1.0)
|
193
|
+
timeout (0.4.1)
|
194
|
+
tzinfo (2.0.6)
|
195
|
+
concurrent-ruby (~> 1.0)
|
196
|
+
unicode-display_width (2.5.0)
|
197
|
+
zeitwerk (2.6.16)
|
198
|
+
|
199
|
+
PLATFORMS
|
200
|
+
arm64-darwin-21
|
201
|
+
x86_64-linux
|
202
|
+
|
203
|
+
DEPENDENCIES
|
204
|
+
active_dry_form!
|
205
|
+
dry-schema!
|
206
|
+
rake (~> 13.0)
|
207
|
+
rspec (~> 3.0)
|
208
|
+
rubocop-gp!
|
209
|
+
sqlite3 (~> 1.4)
|
210
|
+
|
211
|
+
BUNDLED WITH
|
212
|
+
2.4.6
|
data/README.md
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
# ActiveDryForm
|
2
|
-
|
3
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/active_dry_form`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
6
|
-
|
7
2
|
## Installation
|
8
3
|
|
9
4
|
Add this line to your application's Gemfile:
|
@@ -11,18 +6,280 @@ Add this line to your application's Gemfile:
|
|
11
6
|
```ruby
|
12
7
|
gem 'active_dry_form'
|
13
8
|
```
|
9
|
+
---
|
10
|
+
|
11
|
+
### Under the hood ActiveDryForm uses [dry-validation](https://dry-rb.org/gems/dry-validation), [dry-monads](https://dry-rb.org/gems/dry-monads)
|
12
|
+
|
13
|
+
#### Initialize form
|
14
|
+
```ruby
|
15
|
+
ProductForm.new(params: { title: 'n', price: '120' })
|
16
|
+
ProductForm.new(params: { product: { title: 'n', price: '120' } })
|
17
|
+
ProductForm.new(record: Product.find(params[:id]), params:)
|
18
|
+
```
|
19
|
+
|
20
|
+
#### Attribute accessors
|
21
|
+
```ruby
|
22
|
+
form.title # => 'n'
|
23
|
+
form.price # => '120'
|
24
|
+
form.price = '119' # => '119'
|
25
|
+
form[:price] # => '119'
|
26
|
+
form[:price] = '121' # => '121'
|
27
|
+
```
|
28
|
+
|
29
|
+
#### Methods
|
30
|
+
```ruby
|
31
|
+
form.validate # => checks field validity
|
32
|
+
form.validator # => #<Dry::Validation::Result{:title=>"n", :price=>120, errors={:title=>["minimum length 2"]}...>
|
33
|
+
form.valid? # => false
|
34
|
+
form.persisted? # => true
|
35
|
+
form.errors # => {:title=>["minimum length 2"]}
|
36
|
+
form.base_errors = []
|
37
|
+
form.errors_full_messages # => ['Cannot be less than 2 words']
|
38
|
+
form.record # => #<Product:0x00007f05c27106c8 id: 1, title: 'name', price: 100, description: 'product'>
|
39
|
+
# form.data - is hash, casted to types, after validate
|
40
|
+
form.data # => {:title=>"n", :price=>120}
|
41
|
+
form.data[:price] # => 120
|
42
|
+
form.update # Failure(:invalid_form)
|
43
|
+
```
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
Methods `form.update` and `form.create` return [Result monad](https://dry-rb.org/gems/dry-monads/1.3/result/)
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# app/forms/product_form.rb
|
51
|
+
|
52
|
+
class ProductForm < Form
|
53
|
+
fields :product do
|
54
|
+
params do
|
55
|
+
required(:title).filled(:string, min_size?: 2)
|
56
|
+
required(:price).filled(:integer)
|
57
|
+
optional(:description).maybe(:string)
|
58
|
+
optional(:upload_attachments).maybe(:array)
|
59
|
+
end
|
60
|
+
|
61
|
+
rule(:description) do
|
62
|
+
key.failure('Cannot be less than 2 words') if value.split.size < 2
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
action def update
|
67
|
+
# Here you can implement any save logic
|
68
|
+
|
69
|
+
record.update!(data)
|
70
|
+
Success(record)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
In your controller
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class ProductsController < ApplicationController
|
80
|
+
include Dry::Monads[:result]
|
81
|
+
|
82
|
+
def new
|
83
|
+
@form = ProductForm.new
|
84
|
+
end
|
85
|
+
|
86
|
+
def create # without monads
|
87
|
+
@form = ProductForm.new(params:)
|
88
|
+
@form.validate
|
89
|
+
|
90
|
+
if @form.valid?
|
91
|
+
Product.create!(@form)
|
92
|
+
|
93
|
+
redirect_to products_path
|
94
|
+
else
|
95
|
+
render :new
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def edit
|
100
|
+
@form = ProductForm.new(record:)
|
101
|
+
end
|
102
|
+
|
103
|
+
def update # with monads
|
104
|
+
@form = ProductForm.new(record:, params:)
|
105
|
+
|
106
|
+
case @form.update
|
107
|
+
in Success(product)
|
108
|
+
redirect_to product
|
109
|
+
else
|
110
|
+
render :edit
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private def record = Product.find(params[:id])
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
in your view (slim for example)
|
119
|
+
|
120
|
+
```slim
|
121
|
+
/ app/views/products/new.slim
|
122
|
+
|
123
|
+
- active_dry_form_for @form do |f|
|
124
|
+
= f.input :title
|
125
|
+
= f.input :price
|
126
|
+
= f.input_select :status, Product::STATUSES.values
|
127
|
+
= f.input_file :upload_attachments, multiple: true, label: false
|
128
|
+
= f.button 'Submit'
|
129
|
+
```
|
130
|
+
|
131
|
+
### Fill attributes init values
|
132
|
+
|
133
|
+
In your controller
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
def new
|
137
|
+
@form = ProductForm.new(params: { title: 'name', price: 120 })
|
138
|
+
@form.attributes = { title: 'new name', price: 100 }
|
139
|
+
@form.description = 'product description'
|
140
|
+
end
|
141
|
+
```
|
14
142
|
|
15
|
-
|
143
|
+
or like this
|
16
144
|
|
17
|
-
|
145
|
+
```ruby
|
146
|
+
def new
|
147
|
+
@form = ProductForm.new
|
148
|
+
@form.create_default(params[:title])
|
18
149
|
|
19
|
-
|
150
|
+
# class ProductForm < Form
|
151
|
+
# ...
|
152
|
+
# def create_default(title)
|
153
|
+
# self.title = title
|
154
|
+
# end
|
155
|
+
# end
|
156
|
+
end
|
157
|
+
```
|
20
158
|
|
21
|
-
|
159
|
+
### Inputs
|
22
160
|
|
23
|
-
|
161
|
+
`Inputs` under the hood uses [standard rails builder form methods](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-text_area), and you can use it on form.
|
24
162
|
|
25
|
-
|
163
|
+
[input](https://github.com/corp-gp/active_dry_form/blob/master/lib/active_dry_form/builder.rb#L97) method automatically determines tag type:
|
164
|
+
|
165
|
+
- by [dry type predicates](https://dry-rb.org/gems/dry-schema/main/basics/built-in-predicates/) - date, time, integer, number, string, boolean
|
166
|
+
- by field name - password, email, telephone, url
|
167
|
+
|
168
|
+
```slim
|
169
|
+
- active_dry_form_for @form, html: { 'data-controller': 'product'} do |f|
|
170
|
+
= f.input :title, 'data-product-target': 'title', readonly: true,
|
171
|
+
= f.show_error(:title)
|
172
|
+
= f.input_select :category_id, Category.pluck(:name, :id),
|
173
|
+
{ include_blank: true },
|
174
|
+
{ label: false, multiple: true, style: 'max-width:unset;'}
|
175
|
+
= f.input_check_box :is_discount
|
176
|
+
= f.input_check_box_inline :is_sale
|
177
|
+
= f.input_text :shipper_name
|
178
|
+
= f.input_text_area :description
|
179
|
+
= f.input_hidden :admin_id
|
180
|
+
= f.input_file :upload_attachments, multiple: true, label: false
|
181
|
+
= f.input_date :date
|
182
|
+
= f.input_datetime :date_time
|
183
|
+
= f.input_integer :category_id
|
184
|
+
= f.input_number :number
|
185
|
+
= f.input_password :password
|
186
|
+
= f.input_email :email
|
187
|
+
= f.input_url :url
|
188
|
+
= f.input_telephone :telephone
|
189
|
+
|
190
|
+
// standard rails builder form methods
|
191
|
+
= f.label :name
|
192
|
+
= f.search_field :name
|
193
|
+
|
194
|
+
= f.button 'Submit'
|
195
|
+
```
|
196
|
+
|
197
|
+
### Create custom `input`
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
# lib/acitve_dry_form/builder.rb
|
201
|
+
|
202
|
+
module ActiveDryForm
|
203
|
+
class Builder
|
204
|
+
|
205
|
+
def input_date_native(field, options = {})
|
206
|
+
wrap_input(__method__, field, options) { |opts| date_field(field, opts) }
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
### The form can be nested
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
class NestedDryForm < Form
|
217
|
+
|
218
|
+
class BookmarkForm < Form
|
219
|
+
fields(:bookmark) do
|
220
|
+
params do
|
221
|
+
required(:url).filled(:string)
|
222
|
+
optional(:id).maybe(:integer)
|
223
|
+
optional(:name).maybe(:string)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
fields :product do
|
229
|
+
params do
|
230
|
+
required(:title).filled(:string, min_size?: 2)
|
231
|
+
required(:price).filled(:integer)
|
232
|
+
optional(:description).maybe(:string)
|
233
|
+
optional(:upload_attachments).maybe(:array)
|
234
|
+
optional(:bookmarks).array(Dry.Types.Constructor(BookmarkForm) { |params| BookmarkForm.new(params: params) })
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
action def update
|
239
|
+
bookmarks_data = data.delete(:bookmarks)
|
240
|
+
|
241
|
+
record.attributes = data
|
242
|
+
record.bookmarks_attributes = bookmarks_data if bookmarks_data
|
243
|
+
record.save!
|
244
|
+
|
245
|
+
Success(record)
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
As you noticed in the above example, we use the construction `Dry.Types.Constructor(BookmarkForm) { |params| BookmarkForm.new(params: params) }`,
|
252
|
+
what it is `dry types` you can find out [here](https://dry-rb.org/gems/dry-types)
|
253
|
+
|
254
|
+
|
255
|
+
### Internationalization
|
256
|
+
By default, uses `i18n_key` from `fields(:user)`, but you can set a custom key using `fields(:profile, i18n_key: :user)`.
|
257
|
+
|
258
|
+
Using standard rails i18n path:
|
259
|
+
|
260
|
+
[helpers.label](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label)
|
261
|
+
|
262
|
+
```yml
|
263
|
+
ru:
|
264
|
+
helpers:
|
265
|
+
label:
|
266
|
+
user:
|
267
|
+
id: Идентификатор
|
268
|
+
name: Имя
|
269
|
+
```
|
270
|
+
|
271
|
+
or [translations for your model attribute names](https://api.rubyonrails.org/classes/ActiveModel/Translation.html#method-i-human_attribute_name)
|
272
|
+
|
273
|
+
```yml
|
274
|
+
ru:
|
275
|
+
activerecord:
|
276
|
+
attributes:
|
277
|
+
user:
|
278
|
+
id: Идентификатор
|
279
|
+
name: Имя
|
280
|
+
```
|
281
|
+
|
282
|
+
---
|
26
283
|
|
27
284
|
## Development
|
28
285
|
|
@@ -30,10 +287,14 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
30
287
|
|
31
288
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
289
|
|
290
|
+
---
|
291
|
+
|
33
292
|
## Contributing
|
34
293
|
|
35
294
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_dry_form.
|
36
295
|
|
296
|
+
---
|
297
|
+
|
37
298
|
## License
|
38
299
|
|
39
300
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
5
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
7
7
|
|
8
|
-
require
|
8
|
+
require 'rubocop/rake_task'
|
9
9
|
|
10
10
|
RuboCop::RakeTask.new
|
11
11
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
ru:
|
2
|
+
dry_validation:
|
3
|
+
or: "или"
|
4
|
+
|
5
|
+
errors:
|
6
|
+
not_format?: 'не соответствует формату'
|
7
|
+
array?: 'должно быть массивом'
|
8
|
+
empty?: 'должно быть пустым'
|
9
|
+
excludes?: 'не должно содержать %{value}'
|
10
|
+
excluded_from?:
|
11
|
+
arg:
|
12
|
+
default: 'не должно быть одним из: %{list}'
|
13
|
+
range: 'не должно быть одним из: %{list_left} - %{list_right}'
|
14
|
+
exclusion?: 'не должно быть одним из: %{list}'
|
15
|
+
eql?: 'должно быть равным %{left}'
|
16
|
+
not_eql?: 'не должно быть равным %{left}'
|
17
|
+
filled?: 'должно быть заполнено'
|
18
|
+
format?: 'имеет недопустимый формат'
|
19
|
+
number?: 'должно быть числом'
|
20
|
+
odd?: 'должно быть нечётным'
|
21
|
+
even?: 'должно быть чётным'
|
22
|
+
gt?: 'должно быть больше %{num}'
|
23
|
+
gteq?: 'должно быть больше или равным %{num}'
|
24
|
+
hash?: 'должно быть хешем'
|
25
|
+
included_in?:
|
26
|
+
arg:
|
27
|
+
default: 'должно быть выбрано из списка'
|
28
|
+
range: 'должно быть одним из: %{list_left} - %{list_right}'
|
29
|
+
inclusion?: 'должно быть одним из: %{list}'
|
30
|
+
includes?: 'должно содержать %{value}'
|
31
|
+
bool?: 'должно быть булевым'
|
32
|
+
true?: 'должно быть истинным'
|
33
|
+
false?: 'должно быть false'
|
34
|
+
int?: 'должно быть целым числом'
|
35
|
+
float?: 'должно быть числом с плавающей точкой одинарной точности'
|
36
|
+
decimal?: 'должно быть числом с плавающей точкой расширенной точности'
|
37
|
+
date?: 'должно быть датой'
|
38
|
+
date_time?: 'должно быть датой и временем'
|
39
|
+
time?: 'должно быть временем'
|
40
|
+
key?: 'отсутствует'
|
41
|
+
attr?: 'отсутствует'
|
42
|
+
lt?: 'должно быть меньше %{num}'
|
43
|
+
lteq?: 'должно быть меньше или равным %{num}'
|
44
|
+
max_size?: 'максимальная длинна %{num}'
|
45
|
+
min_size?: 'минимальная длинна %{num}'
|
46
|
+
none?: 'не может быть определён'
|
47
|
+
str?: 'должно быть строкой'
|
48
|
+
type?: 'должно иметь тип %{type}'
|
49
|
+
size?:
|
50
|
+
arg:
|
51
|
+
default: 'размер должно быть %{size}'
|
52
|
+
range: 'размер должно быть от %{size_left} до %{size_right}'
|
53
|
+
value:
|
54
|
+
string:
|
55
|
+
arg:
|
56
|
+
default: 'длина должна быть %{size}'
|
57
|
+
range: 'длина должна быть от %{size_left} до %{size_right}'
|
58
|
+
valid?: 'неверное значение'
|
59
|
+
|
60
|
+
rules:
|
61
|
+
name:
|
62
|
+
ivan_cant_be_maria: 'Иван не может стать Марией'
|