granite-form 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -2
  3. data/.github/workflows/{ci.yml → ruby.yml} +22 -4
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +3 -3
  6. data/Appraisals +1 -2
  7. data/CHANGELOG.md +15 -0
  8. data/README.md +6 -15
  9. data/docker-compose.yml +14 -0
  10. data/gemfiles/rails.5.0.gemfile +0 -1
  11. data/gemfiles/rails.5.1.gemfile +0 -1
  12. data/gemfiles/rails.5.2.gemfile +0 -1
  13. data/granite-form.gemspec +15 -15
  14. data/lib/granite/form/active_record/associations.rb +1 -1
  15. data/lib/granite/form/base.rb +1 -2
  16. data/lib/granite/form/errors.rb +0 -15
  17. data/lib/granite/form/model/associations/base.rb +0 -4
  18. data/lib/granite/form/model/associations/collection/embedded.rb +2 -1
  19. data/lib/granite/form/model/associations/collection/proxy.rb +1 -1
  20. data/lib/granite/form/model/associations/embeds_any.rb +7 -0
  21. data/lib/granite/form/model/associations/embeds_many.rb +9 -58
  22. data/lib/granite/form/model/associations/embeds_one.rb +7 -36
  23. data/lib/granite/form/model/associations/nested_attributes.rb +7 -7
  24. data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +0 -4
  25. data/lib/granite/form/model/associations/persistence_adapters/base.rb +0 -4
  26. data/lib/granite/form/model/associations/references_many.rb +0 -32
  27. data/lib/granite/form/model/associations/references_one.rb +0 -28
  28. data/lib/granite/form/model/associations/reflections/embeds_any.rb +1 -1
  29. data/lib/granite/form/model/associations/reflections/embeds_many.rb +1 -1
  30. data/lib/granite/form/model/associations/reflections/embeds_one.rb +11 -1
  31. data/lib/granite/form/model/associations/reflections/references_any.rb +0 -4
  32. data/lib/granite/form/model/associations/reflections/singular.rb +0 -22
  33. data/lib/granite/form/model/associations.rb +0 -6
  34. data/lib/granite/form/model/attributes/attribute.rb +3 -21
  35. data/lib/granite/form/model/attributes/base.rb +5 -23
  36. data/lib/granite/form/model/attributes/reference_many.rb +1 -1
  37. data/lib/granite/form/model/attributes/reference_one.rb +1 -1
  38. data/lib/granite/form/model/attributes/reflections/attribute.rb +2 -8
  39. data/lib/granite/form/model/attributes/reflections/base/build_type_definition.rb +38 -0
  40. data/lib/granite/form/model/attributes/reflections/base.rb +20 -17
  41. data/lib/granite/form/model/attributes/reflections/collection/build_type_definition.rb +19 -0
  42. data/lib/granite/form/model/attributes/reflections/dictionary/build_type_definition.rb +19 -0
  43. data/lib/granite/form/model/attributes/reflections/dictionary.rb +0 -3
  44. data/lib/granite/form/model/attributes/reflections/reference_one.rb +0 -6
  45. data/lib/granite/form/model/attributes/reflections/represents/build_type_definition.rb +73 -0
  46. data/lib/granite/form/model/attributes/reflections/represents.rb +10 -2
  47. data/lib/granite/form/model/attributes/represents.rb +22 -37
  48. data/lib/granite/form/model/attributes.rb +10 -2
  49. data/lib/granite/form/model/persistence.rb +1 -19
  50. data/lib/granite/form/model/representation.rb +1 -0
  51. data/lib/granite/form/model/validations.rb +6 -0
  52. data/lib/granite/form/model.rb +1 -3
  53. data/lib/granite/form/types/active_support/time_zone.rb +2 -0
  54. data/lib/granite/form/types/array.rb +2 -0
  55. data/lib/granite/form/types/big_decimal.rb +2 -0
  56. data/lib/granite/form/types/boolean.rb +2 -0
  57. data/lib/granite/form/types/collection.rb +11 -0
  58. data/lib/granite/form/types/date.rb +2 -0
  59. data/lib/granite/form/types/date_time.rb +2 -0
  60. data/lib/granite/form/types/dictionary.rb +23 -0
  61. data/lib/granite/form/types/float.rb +2 -0
  62. data/lib/granite/form/types/has_subtype.rb +18 -0
  63. data/lib/granite/form/types/hash_with_action_controller_parameters.rb +2 -0
  64. data/lib/granite/form/types/integer.rb +2 -0
  65. data/lib/granite/form/types/object.rb +28 -0
  66. data/lib/granite/form/types/string.rb +2 -0
  67. data/lib/granite/form/types/time.rb +2 -0
  68. data/lib/granite/form/types/uuid.rb +2 -0
  69. data/lib/granite/form/types.rb +3 -0
  70. data/lib/granite/form/util.rb +55 -0
  71. data/lib/granite/form/version.rb +1 -1
  72. data/lib/granite/form.rb +1 -0
  73. data/spec/granite/form/active_record/associations_spec.rb +16 -18
  74. data/spec/granite/form/model/associations/embeds_many_spec.rb +29 -305
  75. data/spec/granite/form/model/associations/embeds_one_spec.rb +27 -212
  76. data/spec/granite/form/model/associations/nested_attributes_spec.rb +0 -95
  77. data/spec/granite/form/model/associations/references_many_spec.rb +5 -326
  78. data/spec/granite/form/model/associations/references_one_spec.rb +7 -279
  79. data/spec/granite/form/model/associations/reflections/embeds_any_spec.rb +1 -2
  80. data/spec/granite/form/model/associations/reflections/embeds_many_spec.rb +18 -26
  81. data/spec/granite/form/model/associations/reflections/embeds_one_spec.rb +16 -23
  82. data/spec/granite/form/model/associations/reflections/references_many_spec.rb +1 -1
  83. data/spec/granite/form/model/associations/reflections/references_one_spec.rb +1 -22
  84. data/spec/granite/form/model/associations/validations_spec.rb +0 -3
  85. data/spec/granite/form/model/associations_spec.rb +3 -24
  86. data/spec/granite/form/model/attributes/attribute_spec.rb +0 -29
  87. data/spec/granite/form/model/attributes/reflections/attribute_spec.rb +0 -9
  88. data/spec/granite/form/model/attributes/reflections/base/build_type_definition_spec.rb +27 -0
  89. data/spec/granite/form/model/attributes/reflections/base_spec.rb +16 -10
  90. data/spec/granite/form/model/attributes/reflections/collection/build_type_definition_spec.rb +24 -0
  91. data/spec/granite/form/model/attributes/reflections/dictionary/build_type_definition_spec.rb +24 -0
  92. data/spec/granite/form/model/attributes/reflections/dictionary_spec.rb +0 -6
  93. data/spec/granite/form/model/attributes/reflections/represents/build_type_definition_spec.rb +129 -0
  94. data/spec/granite/form/model/attributes/reflections/represents_spec.rb +43 -20
  95. data/spec/granite/form/model/attributes/represents_spec.rb +78 -55
  96. data/spec/granite/form/model/attributes_spec.rb +84 -23
  97. data/spec/granite/form/model/dirty_spec.rb +1 -7
  98. data/spec/granite/form/model/persistence_spec.rb +0 -2
  99. data/spec/granite/form/model/representation_spec.rb +4 -7
  100. data/spec/granite/form/model/validations/associated_spec.rb +2 -4
  101. data/spec/granite/form/model/validations/nested_spec.rb +2 -4
  102. data/spec/granite/form/model/validations_spec.rb +28 -1
  103. data/spec/granite/form/types/collection_spec.rb +22 -0
  104. data/spec/granite/form/types/dictionary_spec.rb +32 -0
  105. data/spec/granite/form/types/has_subtype_spec.rb +20 -0
  106. data/spec/granite/form/types/object_spec.rb +50 -4
  107. data/spec/granite/form/util_spec.rb +108 -0
  108. data/spec/spec_helper.rb +0 -15
  109. data/spec/support/active_record.rb +23 -0
  110. data/spec/support/shared/nested_attribute_examples.rb +3 -21
  111. metadata +56 -51
  112. data/.github/workflows/main.yml +0 -29
  113. data/gemfiles/rails.4.2.gemfile +0 -15
  114. data/lib/granite/form/model/attributes/collection.rb +0 -19
  115. data/lib/granite/form/model/attributes/dictionary.rb +0 -28
  116. data/lib/granite/form/model/attributes/localized.rb +0 -44
  117. data/lib/granite/form/model/attributes/reflections/localized.rb +0 -45
  118. data/lib/granite/form/model/callbacks.rb +0 -72
  119. data/lib/granite/form/model/lifecycle.rb +0 -309
  120. data/lib/granite/form/model/localization.rb +0 -26
  121. data/spec/granite/form/model/attributes/collection_spec.rb +0 -72
  122. data/spec/granite/form/model/attributes/dictionary_spec.rb +0 -100
  123. data/spec/granite/form/model/attributes/localized_spec.rb +0 -103
  124. data/spec/granite/form/model/attributes/reflections/localized_spec.rb +0 -37
  125. data/spec/granite/form/model/callbacks_spec.rb +0 -337
  126. data/spec/granite/form/model/lifecycle_spec.rb +0 -356
@@ -1,15 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activesupport", "~> 4.2.0"
6
- gem "activemodel", "~> 4.2.0"
7
- gem "activerecord", "~> 4.2.0"
8
- gem "sqlite3", "~> 1.3.6"
9
-
10
- group :test do
11
- gem "guard"
12
- gem "guard-rspec"
13
- end
14
-
15
- gemspec path: "../"
@@ -1,19 +0,0 @@
1
- module Granite
2
- module Form
3
- module Model
4
- module Attributes
5
- class Collection < Attribute
6
- def read
7
- @value ||= normalize(read_before_type_cast.map do |value|
8
- enumerize(type_definition.ensure_type(value))
9
- end)
10
- end
11
-
12
- def read_before_type_cast
13
- @value_before_type_cast ||= Array.wrap(@value_cache).map { |value| defaultize(value) }
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,28 +0,0 @@
1
- module Granite
2
- module Form
3
- module Model
4
- module Attributes
5
- class Dictionary < Attribute
6
- delegate :keys, to: :reflection
7
-
8
- def read
9
- @value ||= begin
10
- hash = read_before_type_cast
11
- hash = hash.stringify_keys.slice(*keys) if keys.present?
12
-
13
- normalize(Hash[hash.map do |key, value|
14
- [key, enumerize(type_definition.ensure_type(value))]
15
- end].with_indifferent_access).with_indifferent_access
16
- end
17
- end
18
-
19
- def read_before_type_cast
20
- @value_before_type_cast ||= Hash[(@value_cache.presence || {}).map do |key, value|
21
- [key, defaultize(value)]
22
- end].with_indifferent_access
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,44 +0,0 @@
1
- module Granite
2
- module Form
3
- module Model
4
- module Attributes
5
- class Localized < Attribute
6
- def read
7
- @value ||= Hash[read_before_type_cast.map do |locale, value|
8
- [locale.to_s, normalize(enumerize(type_definition.ensure_type(value)))]
9
- end]
10
- end
11
-
12
- def read_before_type_cast
13
- @value_before_type_cast ||= Hash[(@value_cache.presence || {}).map do |locale, value|
14
- [locale.to_s, defaultize(value)]
15
- end]
16
- end
17
-
18
- def write_locale(value, locale)
19
- pollute do
20
- write(read.merge(locale.to_s => value))
21
- end
22
- end
23
-
24
- def read_locale(locale)
25
- read[owner.class.fallbacks(locale).detect do |fallback|
26
- read[fallback.to_s]
27
- end.to_s]
28
- end
29
-
30
- def read_locale_before_type_cast(locale)
31
- read_before_type_cast[owner.class.fallbacks(locale).detect do |fallback|
32
- read_before_type_cast[fallback.to_s]
33
- end.to_s]
34
- end
35
-
36
- def locale_query(locale)
37
- value = read_locale(locale)
38
- !(value.respond_to?(:zero?) ? value.zero? : value.blank?)
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,45 +0,0 @@
1
- module Granite
2
- module Form
3
- module Model
4
- module Attributes
5
- module Reflections
6
- class Localized < Attribute
7
- def self.build(target, generated_methods, name, *args, &block)
8
- attribute = super(target, generated_methods, name, *args, &block)
9
- generate_methods name, generated_methods
10
- attribute
11
- end
12
-
13
- def self.generate_methods(name, target)
14
- target.class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
- def #{name}_translations
16
- attribute('#{name}').read
17
- end
18
-
19
- def #{name}_translations= value
20
- attribute('#{name}').write(value)
21
- end
22
-
23
- def #{name}
24
- attribute('#{name}').read_locale(self.class.locale)
25
- end
26
-
27
- def #{name}= value
28
- attribute('#{name}').write_locale(value, self.class.locale)
29
- end
30
-
31
- def #{name}?
32
- attribute('#{name}').locale_query(self.class.locale)
33
- end
34
-
35
- def #{name}_before_type_cast
36
- attribute('#{name}').read_locale_before_type_cast(self.class.locale)
37
- end
38
- RUBY
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,72 +0,0 @@
1
- module Granite
2
- module Form
3
- module Model
4
- # == Callbacks for Granite::Form::Model lifecycle
5
- #
6
- # Provides ActiveModel callbacks support for lifecycle
7
- # actions.
8
- #
9
- # class Book
10
- # include Granite::Form::Model
11
- #
12
- # attribute :id, Integer
13
- # attribute :title, String
14
- #
15
- # define_save do
16
- # REDIS.set(id, attributes.to_json)
17
- # end
18
- #
19
- # define_destroy do
20
- # REDIS.del(instance.id)
21
- # end
22
- #
23
- # after_initialize :setup_id
24
- # before_save :do_something
25
- # around_update do |&block|
26
- # ...
27
- # block.call
28
- # ...
29
- # end
30
- # after_destroy { ... }
31
- # end
32
- #
33
- module Callbacks
34
- extend ActiveSupport::Concern
35
-
36
- included do
37
- extend ActiveModel::Callbacks
38
-
39
- include ActiveModel::Validations::Callbacks
40
- include Lifecycle
41
- prepend PrependMethods
42
-
43
- define_model_callbacks :initialize, only: :after
44
- define_model_callbacks :save, :create, :update, :destroy
45
- end
46
-
47
- module PrependMethods
48
- def initialize(*_)
49
- super
50
- run_callbacks :initialize
51
- end
52
-
53
- def save_object(&block)
54
- run_callbacks(:save) { super(&block) }
55
- end
56
-
57
- def create_object(&block)
58
- run_callbacks(:create) { super(&block) }
59
- end
60
-
61
- def update_object(&block)
62
- run_callbacks(:update) { super(&block) }
63
- end
64
-
65
- def destroy_object(&block)
66
- run_callbacks(:destroy) { super(&block) }
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
@@ -1,309 +0,0 @@
1
- module Granite
2
- module Form
3
- module Model
4
- # == Lifecycle methods for Granite::Form::Model
5
- #
6
- # Provides methods +save+ and +destroy+ and its bang variants.
7
- # Also, patches +create+ and +update_attributes+ methods by adding
8
- # save at the end.
9
- #
10
- # You can define save or destroy performers with <tt>define_<action></tt>
11
- # methods. Create and update performers might be defined instead of
12
- # save performer:
13
- #
14
- # class Book
15
- # include Granite::Form::Model
16
- # include Granite::Form::Model::Lifecycle
17
- #
18
- # attribute :id, Integer
19
- # attribute :title, String
20
- #
21
- # define_save do # executes in the instance scope
22
- # REDIS.set(id, attributes.to_json)
23
- # end
24
- #
25
- # define_destroy do
26
- # REDIS.del(id)
27
- # end
28
- # end
29
- #
30
- # class Author
31
- # include Granite::Form::Model
32
- # include Granite::Form::Model::Lifecycle
33
- #
34
- # attribute :id, Integer
35
- # attribute :name, String
36
- #
37
- # define_create do # will be called on create only
38
- # REDIS.sadd('author_ids', id)
39
- # REDIS.set(id, attributes.to_json)
40
- # end
41
- #
42
- # define_update do # will be called on update only
43
- # REDIS.set(id, attributes.to_json)
44
- # end
45
- # end
46
- #
47
- # In case of undefined performer Granite::Form::UnsavableObject
48
- # or Granite::Form::UndestroyableObject will be raised respectively.
49
- #
50
- # If performers was not defined in model, they cat be passed as
51
- # blocks to `save`, `update` and `destroy` methods:
52
- #
53
- # authos.save { REDIS.set(id, attributes.to_json) }
54
- # authos.update { REDIS.set(id, attributes.to_json) }
55
- # authos.destroy { REDIS.del(id) }
56
- #
57
- # Save and destroy processes acts almost the save way as
58
- # ActiveRecord's (with +persisted?+ and +destroyed?+ methods
59
- # affecting).
60
- #
61
- module Lifecycle
62
- extend ActiveSupport::Concern
63
-
64
- included do
65
- include Persistence
66
-
67
- class_attribute(*%i[save create update destroy].map { |action| "_#{action}_performer" })
68
- private(*%i[save create update destroy].map { |action| "_#{action}_performer=" })
69
- end
70
-
71
- module ClassMethods
72
- # <tt>define_<action></tt> methods define performers for lifecycle
73
- # actions. Every action block must return boolean result, which
74
- # would mean the action success. If action performed unsuccessfully
75
- # Granite::Form::ObjectNotSaved or Granite::Form::ObjectNotDestroyed will
76
- # be raised respectively in case of bang methods using.
77
- #
78
- # class Author
79
- # define_create { true }
80
- # end
81
- #
82
- # Author.new.save # => true
83
- # Author.new.save! # => true
84
- #
85
- # class Author
86
- # define_create { false }
87
- # end
88
- #
89
- # Author.new.save # => false
90
- # Author.new.save! # => Granite::Form::ObjectNotSaved
91
- #
92
- # Also performers blocks are executed in the instance context, but
93
- # instance also passed as argument
94
- #
95
- # define_update do |instance|
96
- # instance.attributes.to_json
97
- # end
98
- #
99
- # +define_create+ and +define_update+ performers has higher priority
100
- # than +define_save+.
101
- #
102
- # class Author
103
- # define_update { ... }
104
- # define_save { ... }
105
- # end
106
- #
107
- # author = Author.create # using define_save performer
108
- # author.update_attributes(...) # using define_update performer
109
- #
110
- %i[save create update destroy].each do |action|
111
- define_method "define_#{action}" do |&block|
112
- send("_#{action}_performer=", block)
113
- end
114
- end
115
-
116
- # Initializes new instance with attributes passed and calls +save+
117
- # on it. Returns instance in any case.
118
- #
119
- def create(*args)
120
- new(*args).tap(&:save)
121
- end
122
-
123
- # Initializes new instance with attributes passed and calls +save!+
124
- # on it. Returns instance in case of success and raises Granite::Form::ValidationError
125
- # or Granite::Form::ObjectNotSaved in case of validation or saving fail respectively.
126
- #
127
- def create!(*args)
128
- new(*args).tap(&:save!)
129
- end
130
- end
131
-
132
- # <tt>define_<action></tt> on instance level works the same
133
- # way as class <tt>define_<action></tt> methods, but defines
134
- # performers for instance only
135
- #
136
- # user.define_save do
137
- # REDIS.set(id, attributes.to_json)
138
- # end
139
- # user.save! # => will use instance-level performer
140
- #
141
- %i[save create update destroy].each do |action|
142
- define_method "define_#{action}" do |&block|
143
- send("_#{action}_performer=", block)
144
- end
145
- end
146
-
147
- # Assigns passed attributes and calls +save+
148
- # Returns true or false in case of successful or unsuccessful
149
- # saving respectively.
150
- #
151
- # author.update(name: 'Donald')
152
- #
153
- # If update performer is not defined with `define_update`
154
- # or `define_save`, it raises Granite::Form::UnsavableObject.
155
- # Also save performer block might be passed instead of in-class
156
- # performer definition:
157
- #
158
- # author.update(name: 'Donald') { REDIS.set(id, attributes.to_json) }
159
- #
160
- def update(attributes, &block)
161
- assign_attributes(attributes) && save(&block)
162
- end
163
-
164
- alias_method :update_attributes, :update
165
-
166
- # Assigns passed attributes and calls +save!+
167
- # Returns true in case of success and raises Granite::Form::ValidationError
168
- # or Granite::Form::ObjectNotSaved in case of validation or
169
- # saving fail respectively.
170
- #
171
- # author.update!(name: 'Donald')
172
- #
173
- # If update performer is not defined with `define_update`
174
- # or `define_save`, it raises Granite::Form::UnsavableObject.
175
- # Also save performer block might be passed instead of in-class
176
- # performer definition:
177
- #
178
- # author.update!(name: 'Donald') { REDIS.set(id, attributes.to_json) }
179
- #
180
- def update!(attributes, &block)
181
- assign_attributes(attributes) && save!(&block)
182
- end
183
-
184
- alias_method :update_attributes!, :update!
185
-
186
- # # Saves object by calling save performer defined with +define_save+,
187
- # +define_create+ or +define_update+ methods.
188
- # Returns true or false in case of successful
189
- # or unsuccessful saving respectively. Changes +persisted?+ to true
190
- #
191
- # author.save
192
- #
193
- # If save performer is not defined with `define_update` or
194
- # `define_create` or `define_save`, it raises Granite::Form::UnsavableObject.
195
- # Also save performer block might be passed instead of in-class
196
- # performer definition:
197
- #
198
- # author.save { REDIS.set(id, attributes.to_json) }
199
- #
200
- def save(_options = {}, &block)
201
- raise Granite::Form::UnsavableObject unless block || savable?
202
- valid? && save_object(&block)
203
- end
204
-
205
- # Saves object by calling save performer defined with +define_save+,
206
- # +define_create+ or +define_update+ methods.
207
- # Returns true in case of success and raises Granite::Form::ValidationError
208
- # or Granite::Form::ObjectNotSaved in case of validation or
209
- # saving fail respectively. Changes +persisted?+ to true
210
- #
211
- # author.save!
212
- #
213
- # If save performer is not defined with `define_update` or
214
- # `define_create` or `define_save`, it raises Granite::Form::UnsavableObject.
215
- # Also save performer block might be passed instead of in-class
216
- # performer definition:
217
- #
218
- # author.save! { REDIS.set(id, attributes.to_json) }
219
- #
220
- def save!(_options = {}, &block)
221
- raise Granite::Form::UnsavableObject unless block || savable?
222
- validate!
223
- save_object(&block) or raise Granite::Form::ObjectNotSaved
224
- end
225
-
226
- # Destroys object by calling the destroy performer.
227
- # Returns instance in any case. Changes +persisted?+
228
- # to false and +destroyed?+ to true in case of success.
229
- #
230
- # author.destroy
231
- #
232
- # If destroy performer is not defined with `define_destroy`,
233
- # it raises Granite::Form::UndestroyableObject.
234
- # Also destroy performer block might be passed instead of in-class
235
- # performer definition:
236
- #
237
- # author.destroy { REDIS.del(id) }
238
- #
239
- def destroy(&block)
240
- raise Granite::Form::UndestroyableObject unless block || destroyable?
241
- destroy_object(&block)
242
- self
243
- end
244
-
245
- # Destroys object by calling the destroy performer.
246
- # In case of success returns instance and changes +persisted?+
247
- # to false and +destroyed?+ to true.
248
- # Raises Granite::Form::ObjectNotDestroyed in case of fail.
249
- #
250
- # author.destroy!
251
- #
252
- # If destroy performer is not defined with `define_destroy`,
253
- # it raises Granite::Form::UndestroyableObject.
254
- # Also destroy performer block might be passed instead of in-class
255
- # performer definition:
256
- #
257
- # author.destroy! { REDIS.del(id) }
258
- #
259
- def destroy!(&block)
260
- raise Granite::Form::UndestroyableObject unless block || destroyable?
261
- destroy_object(&block) or raise Granite::Form::ObjectNotDestroyed
262
- self
263
- end
264
-
265
- private
266
-
267
- def savable?
268
- !!((persisted? ? _update_performer : _create_performer) || _save_performer)
269
- end
270
-
271
- def save_object(&block)
272
- apply_association_changes! if respond_to?(:apply_association_changes!)
273
- result = persisted? ? update_object(&block) : create_object(&block)
274
- mark_persisted! if result
275
- result
276
- end
277
-
278
- def create_object(&block)
279
- performer = block || _create_performer || _save_performer
280
- !!performer_exec(&performer)
281
- end
282
-
283
- def update_object(&block)
284
- performer = block || _update_performer || _save_performer
285
- !!performer_exec(&performer)
286
- end
287
-
288
- def destroyable?
289
- !!_destroy_performer
290
- end
291
-
292
- def destroy_object(&block)
293
- performer = block || _destroy_performer
294
- result = !!performer_exec(&performer)
295
- mark_destroyed! if result
296
- result
297
- end
298
-
299
- def performer_exec(&block)
300
- if block.arity == 1
301
- yield(self)
302
- else
303
- instance_exec(&block)
304
- end
305
- end
306
- end
307
- end
308
- end
309
- end
@@ -1,26 +0,0 @@
1
- require 'granite/form/model/attributes/reflections/localized'
2
- require 'granite/form/model/attributes/localized'
3
-
4
- module Granite
5
- module Form
6
- module Model
7
- module Localization
8
- extend ActiveSupport::Concern
9
-
10
- module ClassMethods
11
- def localized(*args, &block)
12
- add_attribute(Granite::Form::Model::Attributes::Reflections::Localized, *args, &block)
13
- end
14
-
15
- def fallbacks(locale)
16
- ::I18n.respond_to?(:fallbacks) ? ::I18n.fallbacks[locale] : [locale]
17
- end
18
-
19
- def locale
20
- I18n.locale
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,72 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Granite::Form::Model::Attributes::Collection do
4
- before { stub_model(:dummy) }
5
-
6
- def attribute(*args)
7
- options = args.extract_options!
8
- Dummy.add_attribute(Granite::Form::Model::Attributes::Reflections::Collection, :field, options)
9
- Dummy.new.attribute(:field)
10
- end
11
-
12
- describe '#read' do
13
- let(:field) { attribute(type: String, normalizer: ->(v) { v.uniq }, default: :world, enum: %w[hello 42]) }
14
-
15
- specify { expect(field.tap { |r| r.write(nil) }.read).to eq([]) }
16
- specify { expect(field.tap { |r| r.write([nil]) }.read).to eq([nil]) }
17
- specify { expect(field.tap { |r| r.write('hello') }.read).to eq(['hello']) }
18
- specify { expect(field.tap { |r| r.write([42]) }.read).to eq(['42']) }
19
- specify { expect(field.tap { |r| r.write([43]) }.read).to eq([nil]) }
20
- specify { expect(field.tap { |r| r.write([43, 44]) }.read).to eq([nil]) }
21
- specify { expect(field.tap { |r| r.write(['']) }.read).to eq([nil]) }
22
- specify { expect(field.tap { |r| r.write(['hello', 42]) }.read).to eq(%w[hello 42]) }
23
- specify { expect(field.tap { |r| r.write(['hello', false]) }.read).to eq(['hello', nil]) }
24
-
25
- context do
26
- let(:field) { attribute(type: String, normalizer: ->(v) { v.uniq }, default: :world) }
27
-
28
- specify { expect(field.tap { |r| r.write(nil) }.read).to eq([]) }
29
- specify { expect(field.tap { |r| r.write([nil, nil]) }.read).to eq(['world']) }
30
- specify { expect(field.tap { |r| r.write('hello') }.read).to eq(['hello']) }
31
- specify { expect(field.tap { |r| r.write([42]) }.read).to eq(['42']) }
32
- specify { expect(field.tap { |r| r.write(['']) }.read).to eq(['']) }
33
- end
34
- end
35
-
36
- describe '#read_before_type_cast' do
37
- let(:field) { attribute(type: String, default: :world, enum: %w[hello 42]) }
38
-
39
- specify { expect(field.tap { |r| r.write(nil) }.read_before_type_cast).to eq([]) }
40
- specify { expect(field.tap { |r| r.write([nil]) }.read_before_type_cast).to eq([:world]) }
41
- specify { expect(field.tap { |r| r.write('hello') }.read_before_type_cast).to eq(['hello']) }
42
- specify { expect(field.tap { |r| r.write([42]) }.read_before_type_cast).to eq([42]) }
43
- specify { expect(field.tap { |r| r.write([43]) }.read_before_type_cast).to eq([43]) }
44
- specify { expect(field.tap { |r| r.write([43, 44]) }.read_before_type_cast).to eq([43, 44]) }
45
- specify { expect(field.tap { |r| r.write(['']) }.read_before_type_cast).to eq(['']) }
46
- specify { expect(field.tap { |r| r.write(['hello', 42]) }.read_before_type_cast).to eq(['hello', 42]) }
47
- specify { expect(field.tap { |r| r.write(['hello', false]) }.read_before_type_cast).to eq(['hello', false]) }
48
-
49
- context do
50
- let(:field) { attribute(type: String, default: :world) }
51
-
52
- specify { expect(field.tap { |r| r.write(nil) }.read_before_type_cast).to eq([]) }
53
- specify { expect(field.tap { |r| r.write([nil, nil]) }.read_before_type_cast).to eq(%i[world world]) }
54
- specify { expect(field.tap { |r| r.write('hello') }.read_before_type_cast).to eq(['hello']) }
55
- specify { expect(field.tap { |r| r.write([42]) }.read_before_type_cast).to eq([42]) }
56
- specify { expect(field.tap { |r| r.write(['']) }.read_before_type_cast).to eq(['']) }
57
- end
58
- end
59
-
60
- context 'integration' do
61
- before do
62
- stub_model(:post) do
63
- collection :tags, String
64
- end
65
- end
66
-
67
- specify { expect(Post.new(tags: ['hello', 42]).tags).to eq(%w[hello 42]) }
68
- specify { expect(Post.new(tags: ['hello', 42]).tags_before_type_cast).to eq(['hello', 42]) }
69
- specify { expect(Post.new.tags?).to eq(false) }
70
- specify { expect(Post.new(tags: ['hello', 42]).tags?).to eq(true) }
71
- end
72
- end