granite-form 0.1.0 → 0.1.1
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/.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
|