granite-form 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +2 -0
- data/CHANGELOG.md +4 -68
- data/README.md +53 -51
- data/lib/granite/form/model/representation.rb +1 -1
- data/lib/granite/form/version.rb +1 -1
- data/spec/lib/granite/form/model/representation_spec.rb +14 -2
- data/spec/spec_helper.rb +1 -2
- data/spec/support/translations.rb +6 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df27378689a1bdcdc95593c32c65046ecaab452cc3303839979ace6f1a12ef0f
|
4
|
+
data.tar.gz: 41b0ee8c8b22f1cf992e7b46c91528b4596ad6b362930c5299b6fde58a9bd2b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aef5479a5c58afc85c8e68635fbd8382039c69b8235beaeda74d5caf320540c2fd8aac330f70f4c031abfd37d6a214c3acab0086c9c0a1d3843aedd7aab5a1db
|
7
|
+
data.tar.gz: 5fcc7f498c8685167096ec9d835c6cc80efec7915b17c32aa27f8e912776d2ef25e215ed24c076e7c8e4cb32790eba060d8a7aac827ff1c2cd8d93819b7ae7e2
|
data/.github/CODEOWNERS
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,73 +1,9 @@
|
|
1
1
|
# master
|
2
2
|
|
3
|
-
|
3
|
+
## v0.1.1
|
4
4
|
|
5
|
-
|
5
|
+
- Fixed represented error message copying when represented model uses symbols for `message`.
|
6
6
|
|
7
|
-
|
7
|
+
## v0.1.0
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
# Version 1.1.6
|
12
|
-
|
13
|
-
* Fix Ruby 2.6 deprecations (#72)
|
14
|
-
|
15
|
-
# Version 1.1.5
|
16
|
-
|
17
|
-
* Rails 6 support (#70, #71)
|
18
|
-
|
19
|
-
# Version 1.1.4
|
20
|
-
|
21
|
-
## Changes
|
22
|
-
|
23
|
-
* `came_from_default?` attribute method (#66)
|
24
|
-
|
25
|
-
# Version 1.1.3
|
26
|
-
|
27
|
-
## Changes
|
28
|
-
|
29
|
-
* `came_from_user?` attribute method (#65)
|
30
|
-
|
31
|
-
# Version 1.1.2
|
32
|
-
|
33
|
-
## Changes
|
34
|
-
|
35
|
-
* `ActiveData.base_concern` config option in addition to `ActiveData.base_class` (#64)
|
36
|
-
|
37
|
-
# Version 1.1.1
|
38
|
-
|
39
|
-
## Changes
|
40
|
-
|
41
|
-
* `ActiveData.base_class` config option (#63)
|
42
|
-
|
43
|
-
* ActiveModel 5.2 compatibility (#62)
|
44
|
-
|
45
|
-
# Version 1.1.0
|
46
|
-
|
47
|
-
## Incompatible changes
|
48
|
-
|
49
|
-
* Represented attributes are not provided by default, to add them, `include ActiveData::Model::Representation` (#46)
|
50
|
-
|
51
|
-
* `include ActiveData::Model::Associations::Validations` is not included by default anymore, to get `validate_ancestry!`, `valid_ancestry?` and `invalid_ancestry?` methods back you need to include this module manually
|
52
|
-
|
53
|
-
## Changes
|
54
|
-
|
55
|
-
* Introduce persistence adapters for associations (#24, #51)
|
56
|
-
|
57
|
-
* `ActionController::Parameters` support (#43)
|
58
|
-
|
59
|
-
* Nested attributes simple method overriding (#41)
|
60
|
-
|
61
|
-
* Persistence for `references` associations (#28, #32)
|
62
|
-
|
63
|
-
* Support `update_only` option on collection nested attributes (#30)
|
64
|
-
|
65
|
-
* `embedder` accessor for embedded associations
|
66
|
-
|
67
|
-
* Dynamic scopes for `references` associations (#27)
|
68
|
-
|
69
|
-
## Bugfixes
|
70
|
-
|
71
|
-
* Fixed multiple validations on represented attributes and associations (#44)
|
72
|
-
|
73
|
-
* Proper boolean attributes defaults (#31, #33)
|
9
|
+
- Forked from ActiveData, see https://github.com/pyromaniac/active_data/blob/v1.2.0/CHANGELOG.md for changes before this
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
# Granite::Form
|
5
5
|
|
6
|
-
Granite::Form is
|
6
|
+
`Granite::Form` is an `ActiveModel`-based front-end for your data. It is useful in the following cases:
|
7
7
|
|
8
8
|
* When you need a form objects pattern.
|
9
9
|
|
@@ -43,7 +43,7 @@ class ProfileController < ApplicationController
|
|
43
43
|
end
|
44
44
|
```
|
45
45
|
|
46
|
-
* When you need to work with data
|
46
|
+
* When you need to work with data storage à la `ActiveRecord`.
|
47
47
|
|
48
48
|
```ruby
|
49
49
|
class Flight
|
@@ -75,7 +75,7 @@ class Flight
|
|
75
75
|
end
|
76
76
|
```
|
77
77
|
|
78
|
-
* When you need to
|
78
|
+
* When you need to embed objects in `ActiveRecord` models.
|
79
79
|
|
80
80
|
```ruby
|
81
81
|
class Answer
|
@@ -94,25 +94,25 @@ class Quiz < ActiveRecord::Base
|
|
94
94
|
validates :answers, associated: true
|
95
95
|
end
|
96
96
|
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
quiz = Quiz.new
|
98
|
+
quiz.answers.build(question_id: 42, content: 'blabla')
|
99
|
+
quiz.save
|
100
100
|
```
|
101
101
|
|
102
102
|
## Why?
|
103
103
|
|
104
|
-
Granite::Form is an ActiveModel-based library that provides the following
|
104
|
+
`Granite::Form` is an `ActiveModel-based library that provides the following functionalities:
|
105
105
|
|
106
106
|
* Standard form objects building toolkit: attributes with typecasting, validations, etc.
|
107
107
|
* High-level universal ORM/ODM library using any data source (DB, http, redis, text files).
|
108
|
-
* Embedding objects into
|
108
|
+
* Embedding objects into ActiveRecord entities. Quite useful with PG JSON capabilities.
|
109
109
|
|
110
110
|
Key features:
|
111
111
|
|
112
|
-
* Complete
|
112
|
+
* Complete object lifecycle support: saving, updating, destroying.
|
113
113
|
* Embedded and referenced associations.
|
114
|
-
* Backend-agnostic named scopes
|
115
|
-
* Callbacks, validations and dirty attributes
|
114
|
+
* Backend-agnostic named scopes.
|
115
|
+
* Callbacks, validations and dirty attributes.
|
116
116
|
|
117
117
|
## Installation
|
118
118
|
|
@@ -130,11 +130,11 @@ Or install it yourself as:
|
|
130
130
|
|
131
131
|
## Usage
|
132
132
|
|
133
|
-
Granite::Form has modular architecture, so it is required to include modules to obtain additional features. By default Granite::Form supports attributes definition and validations.
|
133
|
+
`Granite::Form` has modular architecture, so it is required to include modules to obtain additional features. By default `Granite::Form` supports attributes definition and validations.
|
134
134
|
|
135
135
|
### Attributes
|
136
136
|
|
137
|
-
Granite::Form provides several types of attributes and typecasts each attribute to its defined type upon initialization.
|
137
|
+
`Granite::Form` provides several types of attributes and typecasts each attribute to its defined type upon initialization.
|
138
138
|
|
139
139
|
```ruby
|
140
140
|
class Book
|
@@ -151,16 +151,16 @@ end
|
|
151
151
|
attribute :full_name, String, default: 'John Talbot'
|
152
152
|
```
|
153
153
|
|
154
|
-
|
154
|
+
If type for an attribute is not set, it defaults to `Object`. It is therefore recommended to specify the type for every attribute explicitly.
|
155
155
|
|
156
|
-
|
156
|
+
The type is necessary for attribute typecasting. Here is the list of pre-defined basic typecasters:
|
157
157
|
|
158
158
|
```irb
|
159
159
|
[1] pry(main)> Granite::Form._typecasters.keys
|
160
160
|
=> ["Object", "String", "Array", "Hash", "Date", "DateTime", "Time", "ActiveSupport::TimeZone", "BigDecimal", "Float", "Integer", "Boolean", "Granite::Form::UUID"]
|
161
161
|
```
|
162
162
|
|
163
|
-
In addition, you can provide any class type when defining
|
163
|
+
In addition, you can provide any class type when defining an attribute, but in that case you will be able to only assign instances of that specific class or `nil`:
|
164
164
|
|
165
165
|
```ruby
|
166
166
|
attribute :template, MyCustomTemplateType
|
@@ -168,7 +168,7 @@ attribute :template, MyCustomTemplateType
|
|
168
168
|
|
169
169
|
##### Defaults
|
170
170
|
|
171
|
-
It is possible to provide default values for attributes and they will act in the same way as
|
171
|
+
It is possible to provide default values for attributes and they will act in the same way as `ActiveRecord` or `Mongoid` default values:
|
172
172
|
|
173
173
|
```ruby
|
174
174
|
attribute :check, Boolean, default: false # Simply false by default
|
@@ -179,7 +179,7 @@ attribute :today_wday, Integer, default: ->(instance) { instance.today.wday } #
|
|
179
179
|
|
180
180
|
##### Enums
|
181
181
|
|
182
|
-
Enums restrict the scope of possible values for attribute. If assigned value is not included in provided list
|
182
|
+
Enums restrict the scope of possible values for an attribute. If the assigned value is not included in the provided list, the attribute value is set to `nil`:
|
183
183
|
|
184
184
|
```ruby
|
185
185
|
attribute :direction, String, enum: %w[north south east west]
|
@@ -187,7 +187,7 @@ attribute :direction, String, enum: %w[north south east west]
|
|
187
187
|
|
188
188
|
##### Normalizers
|
189
189
|
|
190
|
-
Normalizers are applied last, modifying typecast value. It is possible to provide a list of normalizers
|
190
|
+
Normalizers are applied last, modifying a typecast value. It is possible to provide a list of normalizers. They will be applied in the provided order. It is possible to pre-define normalizers to DRY code:
|
191
191
|
|
192
192
|
```ruby
|
193
193
|
Granite::Form.normalizer(:trim) do |value, options, _attribute|
|
@@ -201,13 +201,13 @@ attribute :title, String, normalizers: [->(value) { value.strip }, trim: {length
|
|
201
201
|
|
202
202
|
```ruby
|
203
203
|
attribute :name, String, readonly: true # Readonly forever
|
204
|
-
attribute :name, String, readonly: ->{ true } # Conditionally readonly
|
204
|
+
attribute :name, String, readonly: -> { true } # Conditionally readonly
|
205
205
|
attribute :name, String, readonly: ->(instance) { instance.subject.present? } # Explicit instance
|
206
206
|
```
|
207
207
|
|
208
208
|
#### Collection
|
209
209
|
|
210
|
-
|
210
|
+
A collection is simply an array of equally-typed values:
|
211
211
|
|
212
212
|
```ruby
|
213
213
|
class Panda
|
@@ -217,7 +217,7 @@ class Panda
|
|
217
217
|
end
|
218
218
|
```
|
219
219
|
|
220
|
-
|
220
|
+
A collection typecasts each value to the specified type. Also, it normalizes any given value to an array.
|
221
221
|
|
222
222
|
```irb
|
223
223
|
[1] pry(main)> Panda.new
|
@@ -228,11 +228,11 @@ Collection typecasts each value to specified type and also no matter what are yo
|
|
228
228
|
=> #<Panda ids: [42, 33]>
|
229
229
|
```
|
230
230
|
|
231
|
-
Default and enum modifiers are applied
|
231
|
+
Default and enum modifiers are applied on each value, normalizers are applied on the array.
|
232
232
|
|
233
233
|
#### Dictionary
|
234
234
|
|
235
|
-
|
235
|
+
A dictionary field is a hash of specified type values with string keys:
|
236
236
|
|
237
237
|
```ruby
|
238
238
|
class Foo
|
@@ -249,11 +249,11 @@ end
|
|
249
249
|
=> #<Foo ordering: {"name"=>"desc"}>
|
250
250
|
```
|
251
251
|
|
252
|
-
|
252
|
+
The keys list might be restricted with the `:keys` option. Default and enum modifiers are applied on each value, normalizers are applied on the hash.
|
253
253
|
|
254
254
|
#### Localized
|
255
255
|
|
256
|
-
|
256
|
+
`localized` is similar to how `Globalize 3` attributes work.
|
257
257
|
|
258
258
|
```ruby
|
259
259
|
localized :title, String
|
@@ -261,11 +261,11 @@ localized :title, String
|
|
261
261
|
|
262
262
|
#### Represents
|
263
263
|
|
264
|
-
|
265
|
-
It will automatically set passed value to the represented object **before validation**.
|
266
|
-
You can use any ActiveRecord
|
267
|
-
|
268
|
-
If
|
264
|
+
`represents` provides an easy way to expose model attributes through an interface.
|
265
|
+
It will automatically set the passed value to the represented object **before validation**.
|
266
|
+
You can use any `ActiveRecord`, `ActiveModel` or `Granite::Form` object as a target of representation.
|
267
|
+
The type of an attribute will be taken from it.
|
268
|
+
If no type is defined, it will be `Object` by default. You can set the type explicitly by passing the `type: TypeClass` option.
|
269
269
|
Represents will also add automatic validation of the target object.
|
270
270
|
|
271
271
|
```ruby
|
@@ -295,7 +295,7 @@ person.name
|
|
295
295
|
|
296
296
|
### Associations
|
297
297
|
|
298
|
-
Granite::Form provides a set of associations. There are two types
|
298
|
+
`Granite::Form` provides a set of associations. There are two types: referenced and embedded. The closest example of referenced association is `AcitveRecord`'s `belongs_to`. For embedded ones - Mongoid's embedded. Also these associations support `accepts_nested_attributes` calls.
|
299
299
|
|
300
300
|
#### EmbedsOne
|
301
301
|
|
@@ -312,11 +312,11 @@ embeds_one :profile do
|
|
312
312
|
end
|
313
313
|
```
|
314
314
|
|
315
|
-
|
315
|
+
Оptions:
|
316
316
|
|
317
317
|
* `:class_name` - association class name
|
318
|
-
* `:validate` - true or false
|
319
|
-
* `:default` - default value for association: attributes hash or instance of defined class
|
318
|
+
* `:validate` - `true` or `false`
|
319
|
+
* `:default` - default value for the association: an attributes hash or an instance of the defined class
|
320
320
|
|
321
321
|
#### EmbedsMany
|
322
322
|
|
@@ -324,7 +324,7 @@ Possible options:
|
|
324
324
|
embeds_many :tags
|
325
325
|
```
|
326
326
|
|
327
|
-
Defines collection of embedded objects. Might be defined inline:
|
327
|
+
Defines a collection of embedded objects. Might be defined inline:
|
328
328
|
|
329
329
|
```ruby
|
330
330
|
embeds_many :tags do
|
@@ -332,9 +332,11 @@ embeds_many :tags do
|
|
332
332
|
end
|
333
333
|
```
|
334
334
|
|
335
|
+
Оptions:
|
336
|
+
|
335
337
|
* `:class_name` - association class name
|
336
|
-
* `:validate` -
|
337
|
-
* `:default` - default value for association: attributes hash
|
338
|
+
* `:validate` - `true` or `false`
|
339
|
+
* `:default` - default value for the association: an attributes hash or an instance of the defined class
|
338
340
|
|
339
341
|
#### ReferencesOne
|
340
342
|
|
@@ -342,22 +344,22 @@ end
|
|
342
344
|
references_one :user
|
343
345
|
```
|
344
346
|
|
345
|
-
|
347
|
+
Provides several methods to the object: `#user`, `#user=`, `#user_id` and `#user_id=`, similarly to an ActiveRecord association.
|
346
348
|
|
347
|
-
|
349
|
+
Оptions:
|
348
350
|
|
349
351
|
* `:class_name` - association class name
|
350
|
-
* `:primary_key` - associated object primary key (`:id` by default):
|
352
|
+
* `:primary_key` - the associated object's primary key name (`:id` by default):
|
351
353
|
|
352
354
|
```ruby
|
353
355
|
references_one :user, primary_key: :name
|
354
356
|
```
|
355
357
|
|
356
|
-
|
358
|
+
Creates the following methods: `#user`, `#user=`, `#user_name` and `#user_name=`.
|
357
359
|
|
358
360
|
* `:reference_key` - redefines `#user_id` and `#user_id=` method names completely.
|
359
|
-
* `:validate` - true or false
|
360
|
-
* `:default` - default value for association: reference or object itself
|
361
|
+
* `:validate` - `true` or `false`
|
362
|
+
* `:default` - default value for the association: reference or the object itself
|
361
363
|
|
362
364
|
#### ReferencesMany
|
363
365
|
|
@@ -365,18 +367,18 @@ Possible options:
|
|
365
367
|
references_many :users
|
366
368
|
```
|
367
369
|
|
368
|
-
|
370
|
+
Provides several methods to the object: `#users`, `#users=`, `#user_ids` and `#user_ids=`, similarly to an ActiveRecord association.
|
369
371
|
|
370
|
-
|
372
|
+
Options:
|
371
373
|
|
372
374
|
* `:class_name` - association class name
|
373
|
-
* `:primary_key` - associated object primary key (`:id` by default):
|
375
|
+
* `:primary_key` - the associated object's primary key name (`:id` by default):
|
374
376
|
|
375
377
|
```ruby
|
376
378
|
references_many :users, primary_key: :name
|
377
379
|
```
|
378
380
|
|
379
|
-
|
381
|
+
Creates the following methods: `#users`, `#users=`, `#user_names` and `#user_names=`.
|
380
382
|
|
381
383
|
* `:reference_key` - redefines `#user_ids` and `#user_ids=` method names completely.
|
382
384
|
* `:validate` - true or false
|
@@ -396,15 +398,15 @@ class Mongoid::Document
|
|
396
398
|
end
|
397
399
|
end
|
398
400
|
```
|
399
|
-
|
401
|
+
where
|
400
402
|
`ClassName` - name of model class or one of ancestors
|
401
403
|
`data_source` - name of data source class
|
402
404
|
`primary_key` - key to search data
|
403
405
|
`scope_proc` - additional proc for filtering
|
404
406
|
|
405
|
-
All
|
407
|
+
All requirements for the adapter interfaces are described in `Granite::Form::Model::Associations::PersistenceAdapters::Base`.
|
406
408
|
|
407
|
-
|
409
|
+
The adapter for `ActiveRecord` is `Granite::Form::Model::Associations::PersistenceAdapters::ActiveRecord`. All `ActiveRecord` models use `PersistenceAdapters::ActiveRecord` by default.
|
408
410
|
|
409
411
|
### Primary
|
410
412
|
|
@@ -79,7 +79,7 @@ module Granite
|
|
79
79
|
errors.where(from).each do |error|
|
80
80
|
options = error.options
|
81
81
|
# If we generate message for built-in validation, we don't want to later escape it in our monkey-patch
|
82
|
-
options = options.merge(message: error.message.html_safe)
|
82
|
+
options = options.merge(message: error.message.html_safe)
|
83
83
|
|
84
84
|
errors.add(to, error.type, **options)
|
85
85
|
end
|
data/lib/granite/form/version.rb
CHANGED
@@ -126,13 +126,25 @@ describe Granite::Form::Model::Representation do
|
|
126
126
|
|
127
127
|
specify do
|
128
128
|
expect { post.validate }.to change { post.errors.messages }
|
129
|
-
.to(
|
129
|
+
.to('author.user.email': ['is invalid'], name: ["can't be blank"])
|
130
130
|
end
|
131
131
|
|
132
132
|
if ActiveModel.version >= Gem::Version.new('6.1.0')
|
133
133
|
specify do
|
134
134
|
expect { post.validate }.to change { post.errors.details }
|
135
|
-
.to(
|
135
|
+
.to('author.user.email': [{error: 'is invalid'}], name: [{error: :blank}])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'when using symbol in error message of represented model' do
|
140
|
+
before do
|
141
|
+
Author.validates :name, inclusion: {in: ['Author'], message: :invalid_name, allow_blank: true}
|
142
|
+
post.author.name = 'Not Author'
|
143
|
+
end
|
144
|
+
|
145
|
+
specify do
|
146
|
+
expect { post.validate }.to change { post.errors.messages }
|
147
|
+
.to('author.user.email': ['is invalid'], name: ['must be Author'])
|
136
148
|
end
|
137
149
|
end
|
138
150
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,8 +8,7 @@ require 'rack/test'
|
|
8
8
|
require 'action_controller/metal/strong_parameters'
|
9
9
|
require 'database_cleaner'
|
10
10
|
|
11
|
-
|
12
|
-
require 'support/muffle_helper'
|
11
|
+
Dir[File.join(__dir__, 'support', '**', '*.rb')].sort.each { |f| require f }
|
13
12
|
|
14
13
|
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
15
14
|
ActiveRecord::Base.logger = Logger.new('/dev/null')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: granite-form
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pyromaniac
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -200,6 +200,7 @@ extensions: []
|
|
200
200
|
extra_rdoc_files: []
|
201
201
|
files:
|
202
202
|
- ".codeclimate.yml"
|
203
|
+
- ".github/CODEOWNERS"
|
203
204
|
- ".github/workflows/ci.yml"
|
204
205
|
- ".github/workflows/main.yml"
|
205
206
|
- ".gitignore"
|
@@ -331,6 +332,7 @@ files:
|
|
331
332
|
- spec/spec_helper.rb
|
332
333
|
- spec/support/model_helpers.rb
|
333
334
|
- spec/support/muffle_helper.rb
|
335
|
+
- spec/support/translations.rb
|
334
336
|
homepage: ''
|
335
337
|
licenses: []
|
336
338
|
metadata: {}
|
@@ -401,3 +403,4 @@ test_files:
|
|
401
403
|
- spec/spec_helper.rb
|
402
404
|
- spec/support/model_helpers.rb
|
403
405
|
- spec/support/muffle_helper.rb
|
406
|
+
- spec/support/translations.rb
|