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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb26aa530c266a54d01fe3723d4ee44a95bedbefb140a5358403ebcdf78ced5c
4
- data.tar.gz: 1c63d7e31c87a934a6c858c24c396f0547e16268997dcd2c2e8dbeac16ba98d5
3
+ metadata.gz: df27378689a1bdcdc95593c32c65046ecaab452cc3303839979ace6f1a12ef0f
4
+ data.tar.gz: 41b0ee8c8b22f1cf992e7b46c91528b4596ad6b362930c5299b6fde58a9bd2b8
5
5
  SHA512:
6
- metadata.gz: 2b28feea4881f8c4f6635f1e626345b4ea534306b8596deb8a61f2a38ce428950eb957bd9b1c936699fbe049a22e1c41d36dce4ebd695c769b3fee39c3ecf716
7
- data.tar.gz: e0fdbd61872e007edaf0cf4ffed388b382f14e53e2637a04979eae1f4e2f1b67aa0bad71569b9ab4b2ce8cd2363820e380661f402edec70584c480962aa7c0e1
6
+ metadata.gz: aef5479a5c58afc85c8e68635fbd8382039c69b8235beaeda74d5caf320540c2fd8aac330f70f4c031abfd37d6a214c3acab0086c9c0a1d3843aedd7aab5a1db
7
+ data.tar.gz: 5fcc7f498c8685167096ec9d835c6cc80efec7915b17c32aa27f8e912776d2ef25e215ed24c076e7c8e4cb32790eba060d8a7aac827ff1c2cd8d93819b7ae7e2
@@ -0,0 +1,2 @@
1
+ .github/workflows/ci.yml @toptal/coresmiths-team
2
+ .github/workflows/main.yml @toptal/coresmiths-team
data/CHANGELOG.md CHANGED
@@ -1,73 +1,9 @@
1
1
  # master
2
2
 
3
- # Version 1.2.0
3
+ ## v0.1.1
4
4
 
5
- * Rails 6.1 and 7 support (#80). Thanks to @ojab and @rewritten
5
+ - Fixed represented error message copying when represented model uses symbols for `message`.
6
6
 
7
- # Version 1.1.7
7
+ ## v0.1.0
8
8
 
9
- * Add typecasting from `ActionController::Parameters` to `Hash` (#73)
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 a ActiveModel-based front-end for your data. You might need to use it in the following cases:
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-storage in ActiveRecord style with
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 implement embedded objects for ActiveRecord models
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
- q = Quiz.new
98
- q.answers.build(question_id: 42, content: 'blabla')
99
- q.save
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 abilities:
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 your ActiveRecord entities. Quite useful with PG JSON capabilities.
108
+ * Embedding objects into ActiveRecord entities. Quite useful with PG JSON capabilities.
109
109
 
110
110
  Key features:
111
111
 
112
- * Complete objects lifecycle support: saving, updating, destroying.
112
+ * Complete object lifecycle support: saving, updating, destroying.
113
113
  * Embedded and referenced associations.
114
- * Backend-agnostic named scopes functionality.
115
- * Callbacks, validations and dirty attributes inside.
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
- By default, if type for attribute is not set, it is defined with `Object` type, so it would be a great idea to specify type for every attribute explicitly.
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
- Type is necessary for attribute typecasting. Here is the list of pre-defined basic typecasters:
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 the attribute, but in that case you will be able to only assign instances of that specific class or value nil:
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 AR or Mongoid default values:
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 - then it turns to nil:
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, they will be applied in the order. It is possible to pre-define normalizers to DRY code:
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
- Collection is simply an array of equally-typed values:
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
- Collection typecasts each value to specified type and also no matter what are you going to pass - it will be an array.
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 to every value, normalizer will be applied to the whole array.
231
+ Default and enum modifiers are applied on each value, normalizers are applied on the array.
232
232
 
233
233
  #### Dictionary
234
234
 
235
- Dictionary field is a hash of specified type values with string keys:
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
- Keys list might be restricted with `:keys` option, defaults and enums are applied to every value, normalizers are applied to the whole hash.
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
- Localized is similar to how Globalize 3 attributes work.
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
- Represents provides an easy way to expose model attributes through an interface.
265
- It will automatically set 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
- A type of an attribute will be taken from it.
268
- If there is no type, it will be `Object` by default. You can set the type explicitly by passing the `type: TypeClass` option.
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 of them: referenced and embedded. The closest example of referenced association is AR `belongs_to` and as for embedded ones - Mongoid's embedded. Also these associations support `accepts_nested_attributes` call.
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
- Possible options:
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` - true or false
337
- * `:default` - default value for association: attributes hash collection or instances of defined class
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
- This will provide several methods to the object: `#user`, `#user=`, `#user_id` and `#user_id=`, just as would occur with an ActiveRecord association.
347
+ Provides several methods to the object: `#user`, `#user=`, `#user_id` and `#user_id=`, similarly to an ActiveRecord association.
346
348
 
347
- Possible options:
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
- This will create the following methods: `#user`, `#user=`, `#user_name` and `#user_name=`
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
- This will provide several methods to the object: `#users`, `#users=`, `#user_ids` and `#user_ids=` just as an ActiveRecord relation does.
370
+ Provides several methods to the object: `#users`, `#users=`, `#user_ids` and `#user_ids=`, similarly to an ActiveRecord association.
369
371
 
370
- Possible options:
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
- This will create the following methods: `#users`, `#users=`, `#user_names` and `#user_names=`
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
- Where
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 required interface for adapters described in `Granite::Form::Model::Associations::PersistenceAdapters::Base`.
407
+ All requirements for the adapter interfaces are described in `Granite::Form::Model::Associations::PersistenceAdapters::Base`.
406
408
 
407
- Adapter for ActiveRecord is `Granite::Form::Model::Associations::PersistenceAdapters::ActiveRecord`. So, all AR models will use `PersistenceAdapters::ActiveRecord` by default.
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) unless options.key?(:message)
82
+ options = options.merge(message: error.message.html_safe)
83
83
 
84
84
  errors.add(to, error.type, **options)
85
85
  end
@@ -1,5 +1,5 @@
1
1
  module Granite
2
2
  module Form
3
- VERSION = '0.1.0'.freeze
3
+ VERSION = '0.1.1'.freeze
4
4
  end
5
5
  end
@@ -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(hash_including('author.user.email': ['is invalid'], name: ["can't be blank"]))
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(hash_including('author.user.email': [{error: 'is invalid'}], name: [{error: :blank}]))
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
- require 'support/model_helpers'
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')
@@ -0,0 +1,6 @@
1
+ I18n.backend.store_translations(:en, YAML.safe_load(<<-YAML))
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ invalid_name: "must be Author"
6
+ YAML
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.0
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-21 00:00:00.000000000 Z
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