reform 1.2.6 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/CHANGES.md +14 -0
- data/Gemfile +3 -2
- data/README.md +225 -283
- data/Rakefile +27 -0
- data/TODO.md +12 -0
- data/database.sqlite3 +0 -0
- data/gemfiles/Gemfile.rails-3.0 +1 -0
- data/gemfiles/Gemfile.rails-3.1 +1 -0
- data/gemfiles/Gemfile.rails-3.2 +1 -0
- data/gemfiles/Gemfile.rails-4.0 +1 -0
- data/lib/reform.rb +0 -1
- data/lib/reform/contract.rb +64 -170
- data/lib/reform/contract/validate.rb +10 -13
- data/lib/reform/form.rb +74 -19
- data/lib/reform/form/active_model.rb +19 -14
- data/lib/reform/form/coercion.rb +1 -13
- data/lib/reform/form/composition.rb +2 -24
- data/lib/reform/form/multi_parameter_attributes.rb +43 -62
- data/lib/reform/form/populator.rb +85 -0
- data/lib/reform/form/prepopulate.rb +13 -43
- data/lib/reform/form/validate.rb +29 -90
- data/lib/reform/form/validation/unique_validator.rb +13 -0
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +7 -7
- data/test/active_model_test.rb +43 -0
- data/test/changed_test.rb +23 -51
- data/test/coercion_test.rb +1 -7
- data/test/composition_test.rb +128 -34
- data/test/contract_test.rb +27 -86
- data/test/feature_test.rb +43 -6
- data/test/fields_test.rb +2 -12
- data/test/form_builder_test.rb +28 -25
- data/test/form_option_test.rb +19 -0
- data/test/from_test.rb +0 -75
- data/test/inherit_test.rb +178 -117
- data/test/model_reflections_test.rb +1 -1
- data/test/populate_test.rb +226 -0
- data/test/prepopulator_test.rb +112 -0
- data/test/readable_test.rb +2 -4
- data/test/save_test.rb +56 -112
- data/test/setup_test.rb +48 -0
- data/test/skip_if_test.rb +5 -2
- data/test/skip_setter_and_getter_test.rb +54 -0
- data/test/test_helper.rb +3 -1
- data/test/uniqueness_test.rb +41 -0
- data/test/validate_test.rb +325 -289
- data/test/virtual_test.rb +1 -3
- data/test/writeable_test.rb +3 -4
- metadata +35 -39
- data/lib/reform/composition.rb +0 -63
- data/lib/reform/contract/setup.rb +0 -50
- data/lib/reform/form/changed.rb +0 -9
- data/lib/reform/form/sync.rb +0 -116
- data/lib/reform/representer.rb +0 -84
- data/test/empty_test.rb +0 -58
- data/test/form_composition_test.rb +0 -145
- data/test/nested_form_test.rb +0 -197
- data/test/prepopulate_test.rb +0 -85
- data/test/sync_option_test.rb +0 -83
- data/test/sync_test.rb +0 -56
data/test/virtual_test.rb
CHANGED
@@ -2,8 +2,6 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class VirtualTest < MiniTest::Spec
|
4
4
|
class CreditCardForm < Reform::Form
|
5
|
-
reform_2_0!
|
6
|
-
|
7
5
|
property :credit_card_number, virtual: true # no read, no write, it's virtual.
|
8
6
|
end
|
9
7
|
|
@@ -12,7 +10,7 @@ class VirtualTest < MiniTest::Spec
|
|
12
10
|
it {
|
13
11
|
form.validate("credit_card_number" => "123")
|
14
12
|
|
15
|
-
form.credit_card_number.must_equal "123"
|
13
|
+
form.credit_card_number.must_equal "123" # this is still readable in the UI.
|
16
14
|
|
17
15
|
form.sync
|
18
16
|
|
data/test/writeable_test.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class WriteableTest < MiniTest::Spec
|
3
|
+
class WriteableTest < MiniTest::Spec
|
4
4
|
Location = Struct.new(:country)
|
5
5
|
|
6
6
|
class LocationForm < Reform::Form
|
7
|
-
reform_2_0!
|
8
|
-
|
9
7
|
property :country, writeable: false
|
10
8
|
end
|
11
9
|
|
12
10
|
let (:loc) { Location.new("Australia") }
|
13
11
|
let (:form) { LocationForm.new(loc) }
|
14
12
|
|
15
|
-
it { form.country.must_equal "Australia" }
|
16
13
|
it do
|
14
|
+
form.country.must_equal "Australia"
|
15
|
+
|
17
16
|
form.validate("country" => "Germany") # this usually won't change when submitting.
|
18
17
|
form.country.must_equal "Germany"
|
19
18
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
@@ -9,36 +9,36 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-06-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: representable
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 2.
|
20
|
+
version: 2.2.2
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 2.
|
27
|
+
version: 2.2.2
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: disposable
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 0.
|
34
|
+
version: 0.1.2
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 0.
|
41
|
+
version: 0.1.2
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: uber
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,16 +71,16 @@ dependencies:
|
|
71
71
|
name: bundler
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - "
|
74
|
+
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version: '
|
76
|
+
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - "
|
81
|
+
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
83
|
+
version: '0'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: rake
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,16 +99,16 @@ dependencies:
|
|
99
99
|
name: minitest
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- -
|
102
|
+
- - ">="
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version:
|
104
|
+
version: '0'
|
105
105
|
type: :development
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- -
|
109
|
+
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version:
|
111
|
+
version: '0'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: activerecord
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,7 +179,7 @@ dependencies:
|
|
179
179
|
- - ">="
|
180
180
|
- !ruby/object:Gem::Version
|
181
181
|
version: '0'
|
182
|
-
description:
|
182
|
+
description: Form object decoupled from models.
|
183
183
|
email:
|
184
184
|
- apotonick@gmail.com
|
185
185
|
- heinleng@gmail.com
|
@@ -202,29 +202,26 @@ files:
|
|
202
202
|
- gemfiles/Gemfile.rails-4.0
|
203
203
|
- lib/reform.rb
|
204
204
|
- lib/reform/active_record.rb
|
205
|
-
- lib/reform/composition.rb
|
206
205
|
- lib/reform/contract.rb
|
207
206
|
- lib/reform/contract/errors.rb
|
208
|
-
- lib/reform/contract/setup.rb
|
209
207
|
- lib/reform/contract/validate.rb
|
210
208
|
- lib/reform/form.rb
|
211
209
|
- lib/reform/form/active_model.rb
|
212
210
|
- lib/reform/form/active_model/model_validations.rb
|
213
211
|
- lib/reform/form/active_record.rb
|
214
|
-
- lib/reform/form/changed.rb
|
215
212
|
- lib/reform/form/coercion.rb
|
216
213
|
- lib/reform/form/composition.rb
|
217
214
|
- lib/reform/form/json.rb
|
218
215
|
- lib/reform/form/model_reflections.rb
|
219
216
|
- lib/reform/form/module.rb
|
220
217
|
- lib/reform/form/multi_parameter_attributes.rb
|
218
|
+
- lib/reform/form/populator.rb
|
221
219
|
- lib/reform/form/prepopulate.rb
|
222
220
|
- lib/reform/form/save.rb
|
223
221
|
- lib/reform/form/scalar.rb
|
224
|
-
- lib/reform/form/sync.rb
|
225
222
|
- lib/reform/form/validate.rb
|
223
|
+
- lib/reform/form/validation/unique_validator.rb
|
226
224
|
- lib/reform/rails.rb
|
227
|
-
- lib/reform/representer.rb
|
228
225
|
- lib/reform/schema.rb
|
229
226
|
- lib/reform/twin.rb
|
230
227
|
- lib/reform/version.rb
|
@@ -262,19 +259,18 @@ files:
|
|
262
259
|
- test/dummy/db/test.sqlite3
|
263
260
|
- test/dummy/log/production.log
|
264
261
|
- test/dummy/log/server.log
|
265
|
-
- test/empty_test.rb
|
266
262
|
- test/errors_test.rb
|
267
263
|
- test/feature_test.rb
|
268
264
|
- test/fields_test.rb
|
269
265
|
- test/form_builder_test.rb
|
270
|
-
- test/
|
266
|
+
- test/form_option_test.rb
|
271
267
|
- test/form_test.rb
|
272
268
|
- test/from_test.rb
|
273
269
|
- test/inherit_test.rb
|
274
270
|
- test/model_reflections_test.rb
|
275
271
|
- test/model_validations_test.rb
|
276
|
-
- test/
|
277
|
-
- test/
|
272
|
+
- test/populate_test.rb
|
273
|
+
- test/prepopulator_test.rb
|
278
274
|
- test/rails/integration_test.rb
|
279
275
|
- test/read_only_test.rb
|
280
276
|
- test/readable_test.rb
|
@@ -283,12 +279,13 @@ files:
|
|
283
279
|
- test/representer_test.rb
|
284
280
|
- test/save_test.rb
|
285
281
|
- test/scalar_test.rb
|
282
|
+
- test/setup_test.rb
|
286
283
|
- test/skip_if_test.rb
|
284
|
+
- test/skip_setter_and_getter_test.rb
|
287
285
|
- test/skip_unchanged_test.rb
|
288
|
-
- test/sync_option_test.rb
|
289
|
-
- test/sync_test.rb
|
290
286
|
- test/test_helper.rb
|
291
287
|
- test/twin_test.rb
|
288
|
+
- test/uniqueness_test.rb
|
292
289
|
- test/validate_test.rb
|
293
290
|
- test/virtual_test.rb
|
294
291
|
- test/writeable_test.rb
|
@@ -307,16 +304,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
307
304
|
version: '0'
|
308
305
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
309
306
|
requirements:
|
310
|
-
- - "
|
307
|
+
- - ">"
|
311
308
|
- !ruby/object:Gem::Version
|
312
|
-
version:
|
309
|
+
version: 1.3.1
|
313
310
|
requirements: []
|
314
311
|
rubyforge_project:
|
315
|
-
rubygems_version: 2.
|
312
|
+
rubygems_version: 2.4.8
|
316
313
|
signing_key:
|
317
314
|
specification_version: 4
|
318
|
-
summary:
|
319
|
-
presentation, workflows and security.
|
315
|
+
summary: Form object decoupled from models with validation, population and presentation.
|
320
316
|
test_files:
|
321
317
|
- test/active_model_test.rb
|
322
318
|
- test/active_record_test.rb
|
@@ -351,19 +347,18 @@ test_files:
|
|
351
347
|
- test/dummy/db/test.sqlite3
|
352
348
|
- test/dummy/log/production.log
|
353
349
|
- test/dummy/log/server.log
|
354
|
-
- test/empty_test.rb
|
355
350
|
- test/errors_test.rb
|
356
351
|
- test/feature_test.rb
|
357
352
|
- test/fields_test.rb
|
358
353
|
- test/form_builder_test.rb
|
359
|
-
- test/
|
354
|
+
- test/form_option_test.rb
|
360
355
|
- test/form_test.rb
|
361
356
|
- test/from_test.rb
|
362
357
|
- test/inherit_test.rb
|
363
358
|
- test/model_reflections_test.rb
|
364
359
|
- test/model_validations_test.rb
|
365
|
-
- test/
|
366
|
-
- test/
|
360
|
+
- test/populate_test.rb
|
361
|
+
- test/prepopulator_test.rb
|
367
362
|
- test/rails/integration_test.rb
|
368
363
|
- test/read_only_test.rb
|
369
364
|
- test/readable_test.rb
|
@@ -372,12 +367,13 @@ test_files:
|
|
372
367
|
- test/representer_test.rb
|
373
368
|
- test/save_test.rb
|
374
369
|
- test/scalar_test.rb
|
370
|
+
- test/setup_test.rb
|
375
371
|
- test/skip_if_test.rb
|
372
|
+
- test/skip_setter_and_getter_test.rb
|
376
373
|
- test/skip_unchanged_test.rb
|
377
|
-
- test/sync_option_test.rb
|
378
|
-
- test/sync_test.rb
|
379
374
|
- test/test_helper.rb
|
380
375
|
- test/twin_test.rb
|
376
|
+
- test/uniqueness_test.rb
|
381
377
|
- test/validate_test.rb
|
382
378
|
- test/virtual_test.rb
|
383
379
|
- test/writeable_test.rb
|
data/lib/reform/composition.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require 'disposable/composition'
|
2
|
-
|
3
|
-
# TODO: replace that with lazy Twin and Composition from Disposable.
|
4
|
-
module Reform
|
5
|
-
class Expose
|
6
|
-
include Disposable::Composition
|
7
|
-
|
8
|
-
# DISCUSS: this might be moved to Disposable::Twin::Expose.
|
9
|
-
class << self
|
10
|
-
# Builder for a concrete Composition class with configurations from the form's representer.
|
11
|
-
def from(representer)
|
12
|
-
options = {}
|
13
|
-
|
14
|
-
representer.representable_attrs.each do |definition|
|
15
|
-
process_definition!(options, definition)
|
16
|
-
end
|
17
|
-
|
18
|
-
Class.new(self).tap do |composition| # for 1.8 compat. you're welcome.
|
19
|
-
composition.map(options)
|
20
|
-
# puts composition@map.inspect
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
def process_definition!(options, definition)
|
26
|
-
options[:model] ||= []
|
27
|
-
options[:model] << [definition[:private_name], definition.name].compact
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Keeps composition of models and knows how to transform a plain hash into a nested hash.
|
33
|
-
class Composition < Expose
|
34
|
-
|
35
|
-
# DISCUSS: this might be moved to Disposable::Twin::Composition.
|
36
|
-
class << self
|
37
|
-
# Builder for a concrete Composition class with configurations from the form's representer.
|
38
|
-
def process_definition!(options, definition)
|
39
|
-
options[definition[:on]] ||= []
|
40
|
-
options[definition[:on]] << [definition[:private_name], definition.name].compact
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def save
|
45
|
-
each.collect { |model| model.save }.all?
|
46
|
-
end
|
47
|
-
|
48
|
-
def nested_hash_for(attrs)
|
49
|
-
{}.tap do |hsh|
|
50
|
-
attrs.each do |name, val|
|
51
|
-
#obj = self.class.model_for_property(name)
|
52
|
-
config = self.class.instance_variable_get(:@map)[name.to_sym]
|
53
|
-
|
54
|
-
model = config[:model]
|
55
|
-
method = config[:method]
|
56
|
-
|
57
|
-
hsh[model] ||= {}
|
58
|
-
hsh[model][method] = val
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module Reform
|
2
|
-
class Contract
|
3
|
-
module Setup
|
4
|
-
def initialize(model)
|
5
|
-
@model = model # we need this for #save.
|
6
|
-
@fields = setup_fields # delegate all methods to Fields instance.
|
7
|
-
end
|
8
|
-
|
9
|
-
# Setup#to_hash will create a nested hash of property values from the model.
|
10
|
-
# Nested properties will be recursively wrapped in a form instance.
|
11
|
-
def setup_representer
|
12
|
-
self.class.representer(:setup) do |dfn| # only nested forms.
|
13
|
-
dfn.merge!(
|
14
|
-
:representable => false, # don't call #to_hash, only prepare.
|
15
|
-
:prepare => lambda { |model, args| args.binding[:form].new(model) } # wrap nested properties in form.
|
16
|
-
)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def setup_fields
|
21
|
-
representer = setup_representer.new(aliased_model)
|
22
|
-
options = setup_options(Reform::Representer::Options[]) # handles :empty.
|
23
|
-
|
24
|
-
# populate the internal @fields set with data from the model.
|
25
|
-
create_fields(mapper.fields, representer.to_hash(options))
|
26
|
-
end
|
27
|
-
|
28
|
-
def create_fields(field_names, fields)
|
29
|
-
Fields.new(field_names, fields)
|
30
|
-
end
|
31
|
-
|
32
|
-
module SetupOptions
|
33
|
-
def setup_options(options)
|
34
|
-
options
|
35
|
-
end
|
36
|
-
end
|
37
|
-
include SetupOptions
|
38
|
-
|
39
|
-
|
40
|
-
module Readable
|
41
|
-
def setup_options(options)
|
42
|
-
empty_fields = mapper.representable_attrs.find_all { |d| d[:_readable] == false }.collect { |d| d.name.to_sym }
|
43
|
-
|
44
|
-
options.exclude!(empty_fields)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
include Readable
|
48
|
-
end
|
49
|
-
end # Setup
|
50
|
-
end
|
data/lib/reform/form/changed.rb
DELETED
data/lib/reform/form/sync.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
# #sync!
|
2
|
-
# 1. assign scalars to model (respecting virtual, excluded attributes)
|
3
|
-
# 2. call sync! on nested
|
4
|
-
module Reform::Form::Sync
|
5
|
-
def sync_models(options={})
|
6
|
-
sync!(options)
|
7
|
-
end
|
8
|
-
alias_method :sync, :sync_models
|
9
|
-
|
10
|
-
# reading from fields allows using readers in form for presentation
|
11
|
-
# and writers still pass to fields in #validate????
|
12
|
-
def sync!(options) # semi-public.
|
13
|
-
options = Reform::Representer::Options[options.merge(:form => self)] # options local for this form, only.
|
14
|
-
|
15
|
-
input = sync_hash(options)
|
16
|
-
# if aliased_model was a proper Twin, we could do changed? stuff there.
|
17
|
-
|
18
|
-
options.delete(:exclude) # TODO: can we use 2 options?
|
19
|
-
|
20
|
-
dynamic_sync_representer.new(aliased_model).from_hash(input, options) # sync properties to Song.
|
21
|
-
|
22
|
-
model
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
# Transforms form input into what actually gets written to model.
|
28
|
-
# output: {title: "Mint Car", hit: <Form>}
|
29
|
-
def input_representer
|
30
|
-
self.class.representer(:input, :all => true) do |dfn|
|
31
|
-
if dfn[:form]
|
32
|
-
dfn.merge!(
|
33
|
-
:representable => false,
|
34
|
-
:prepare => lambda { |obj, *| obj },
|
35
|
-
)
|
36
|
-
else
|
37
|
-
dfn.merge!(:render_nil => true) # do sync nil values back to the model for scalars.
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Writes input to model.
|
43
|
-
def sync_representer
|
44
|
-
self.class.representer(:sync, :all => true) do |dfn|
|
45
|
-
if dfn[:form]
|
46
|
-
dfn.merge!(
|
47
|
-
:instance => lambda { |fragment, *| fragment }, # use model's nested property for syncing.
|
48
|
-
# FIXME: do we allow options for #sync for nested forms?
|
49
|
-
:deserialize => lambda { |object, *| model = object.sync!({}) } # sync! returns the synced model.
|
50
|
-
# representable's :setter will do collection=([..]) or property=(..) for us on the model.
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# This representer inherits from sync_representer and add functionality on top of that.
|
57
|
-
# It allows running custom dynamic blocks for properties when syncing.
|
58
|
-
def dynamic_sync_representer
|
59
|
-
self.class.representer(:dynamic_sync, superclass: sync_representer, :all => true) do |dfn|
|
60
|
-
next unless setter = dfn[:sync]
|
61
|
-
|
62
|
-
setter_proc = lambda do |value, options|
|
63
|
-
if options.binding[:sync] == true # sync: true will call the runtime lambda from the options hash.
|
64
|
-
options.user_options[options.binding.name.to_sym].call(value, options)
|
65
|
-
next
|
66
|
-
end
|
67
|
-
|
68
|
-
# evaluate the :sync block in form context (should we do that everywhere?).
|
69
|
-
options.user_options[:form].instance_exec(value, options, &setter)
|
70
|
-
end
|
71
|
-
|
72
|
-
dfn.merge!(:setter => setter_proc)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
# API: semi-public.
|
78
|
-
module SyncHash
|
79
|
-
# This hash goes into the Writer that writes properties back to the model. It only contains "writeable" attributes.
|
80
|
-
def sync_hash(options)
|
81
|
-
input_representer.new(fields).to_hash(options)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
include SyncHash
|
85
|
-
|
86
|
-
|
87
|
-
# Excludes :virtual and readonly properties from #sync in this form.
|
88
|
-
module Writeable
|
89
|
-
def sync_hash(options)
|
90
|
-
readonly_fields = mapper.fields { |dfn| dfn[:_writeable] == false }
|
91
|
-
|
92
|
-
options.exclude!(readonly_fields.map(&:to_sym))
|
93
|
-
|
94
|
-
super
|
95
|
-
end
|
96
|
-
end
|
97
|
-
include Writeable
|
98
|
-
|
99
|
-
|
100
|
-
# This will skip unchanged properties in #sync. To use this for all nested form do as follows.
|
101
|
-
#
|
102
|
-
# class SongForm < Reform::Form
|
103
|
-
# feature Synd::SkipUnchanged
|
104
|
-
module SkipUnchanged
|
105
|
-
def sync_hash(options)
|
106
|
-
# DISCUSS: we currently don't track if nested forms have changed (only their attributes). that's why i include them all here, which
|
107
|
-
# is additional sync work/slightly wrong. solution: allow forms to form.changed? not sure how to do that with collections.
|
108
|
-
scalars = mapper.fields { |dfn| !dfn[:form] }
|
109
|
-
unchanged = scalars - changed.keys
|
110
|
-
|
111
|
-
# exclude unchanged scalars, nested forms and changed scalars still go in here!
|
112
|
-
options.exclude!(unchanged.map(&:to_sym))
|
113
|
-
super
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|