phlexi-form 0.7.2 → 0.8.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: 5efa476cb7e4856462046b6658c996843537b102f807e5c7c08a2d8e88520d11
4
- data.tar.gz: 1938daa8358013cee51abd0b4d50188c99459c6b714470002e4ec26a77e0b224
3
+ metadata.gz: 5cdc4a47adf374d0b808624816ac63ea57fd62241a2be9051188bb2af878fa5f
4
+ data.tar.gz: 7702abb1fdeed8416dffac18d7d2d9b1bcf27a4ff5dd4df1352970c9fc24f988
5
5
  SHA512:
6
- metadata.gz: a0b9918b54df47959563f801982e0cbfae8b2a22f21ea2900cb64be952c891b7df7702361255c09063b45a858ed4fc9445911bc73d1766edf3f91ed91000417a
7
- data.tar.gz: 73de9965cf5106cefe30081ba2d2a822d725ec203cb26d4d1fd2f2be53b336fd56aa227c2935b85b6d405ddf9a4dca35096f010d98d4724285d9dbfd9c2d8df8
6
+ metadata.gz: 9597ee8e767850282a7c7d1a9b96153cbdb9ebe939cc026e19f9aa99e2600edf99fb095b92302bf6caf41b35ba391496e5cdbaf7a256c16dd320019f63c260d3
7
+ data.tar.gz: c70bdee290744b95cb72f0841e9e33b909019b39c0727691510b17b539573ac29884e32a28445e60a00085aaf0efc99b2b6ca5ef81e5b9df4dd4dc159ad7d663
@@ -276,26 +276,6 @@ module Phlexi
276
276
  theme_key = attributes.delete(:theme) || theme_key
277
277
  mix({class: themed(theme_key, self)}, attributes)
278
278
  end
279
-
280
- def determine_initial_value(value)
281
- return value unless value == NIL_VALUE
282
-
283
- determine_value_from_association || super
284
- end
285
-
286
- def determine_value_from_association
287
- return unless association_reflection.present?
288
-
289
- value = object.public_send(key)
290
- case association_reflection.macro
291
- when :has_many, :has_and_belongs_to_many
292
- value&.map { |v| v.public_send(association_reflection.klass.primary_key) }
293
- when :belongs_to, :has_one
294
- value&.public_send(association_reflection.klass.primary_key)
295
- else
296
- raise ArgumentError, "Unsupported association type: #{association_reflection.macro}"
297
- end
298
- end
299
279
  end
300
280
  end
301
281
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Form
5
+ module Components
6
+ class AssociationBase < Select
7
+ protected
8
+
9
+ delegate :association_reflection, to: :field
10
+
11
+ def selected?(option)
12
+ case association_reflection.macro
13
+ when :belongs_to, :has_one
14
+ singular_field_value.to_s == option.to_s
15
+ when :has_many, :has_and_belongs_to_many
16
+ collection_field_value.any? { |item| item.to_s == option.to_s }
17
+ end
18
+ end
19
+
20
+ def singular_field_value
21
+ @singular_field_value ||= field.value&.public_send(association_reflection.klass.primary_key)
22
+ end
23
+
24
+ def collection_field_value
25
+ @collection_field_value ||= field.value&.map { |v| v.public_send(association_reflection.klass.primary_key) }
26
+ end
27
+
28
+ def build_attributes
29
+ build_association_attributes
30
+ super
31
+ end
32
+
33
+ def build_association_attributes
34
+ raise NotImplementedError, "#{self.class}#build_association_attributes"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -3,17 +3,10 @@
3
3
  module Phlexi
4
4
  module Form
5
5
  module Components
6
- class BelongsTo < Select
6
+ class BelongsTo < AssociationBase
7
7
  protected
8
8
 
9
- delegate :association_reflection, to: :field
10
-
11
- def build_attributes
12
- build_belongs_to_attributes
13
- super
14
- end
15
-
16
- def build_belongs_to_attributes
9
+ def build_association_attributes
17
10
  attributes.fetch(:input_param) do
18
11
  attributes[:input_param] = if association_reflection.respond_to?(:options) && association_reflection.options[:foreign_key]
19
12
  association_reflection.options[:foreign_key]
@@ -9,34 +9,49 @@ module Phlexi
9
9
 
10
10
  def build_attributes
11
11
  super
12
- @choice_collection = attributes.delete(:choices) || field.choices
13
12
  @label_method = attributes.delete(:label_method)
14
13
  @value_method = attributes.delete(:value_method)
14
+ @group_method = attributes.delete(:group_method)
15
+ @group_label_method = attributes.delete(:group_label_method)
15
16
  end
16
17
 
17
18
  def choices
18
- @choices ||= ChoicesMapper.new(@choice_collection, label_method: @label_method, value_method: @value_method)
19
- end
20
-
21
- def selected?(option)
22
- if attributes[:multiple]
23
- field_value.any? { |item| item.to_s == option.to_s }
24
- else
25
- field_value.to_s == option.to_s
19
+ @choices ||= begin
20
+ collection = attributes.delete(:choices) || field.choices
21
+ build_choice_mapper(collection)
26
22
  end
27
23
  end
28
24
 
29
- def field_value
25
+ def selected?(option)
30
26
  if attributes[:multiple]
31
- Array(field.value)
27
+ Array(field.value).any? { |item| item.to_s == option.to_s }
32
28
  else
33
- field.value
29
+ field.value.to_s == option.to_s
34
30
  end
35
31
  end
36
32
 
37
33
  def normalize_simple_input(input_value)
34
+ # ensure that choice is in the list
38
35
  ([super] & choices.values)[0]
39
36
  end
37
+
38
+ def build_choice_mapper(collection)
39
+ if @group_method
40
+ GroupedChoicesMapper.new(
41
+ collection,
42
+ group_method: @group_method,
43
+ group_label_method: @group_label_method,
44
+ label_method: @label_method,
45
+ value_method: @value_method
46
+ )
47
+ else
48
+ SimpleChoicesMapper.new(
49
+ collection,
50
+ label_method: @label_method,
51
+ value_method: @value_method
52
+ )
53
+ end
54
+ end
40
55
  end
41
56
  end
42
57
  end
@@ -3,17 +3,10 @@
3
3
  module Phlexi
4
4
  module Form
5
5
  module Components
6
- class HasMany < Select
6
+ class HasMany < AssociationBase
7
7
  protected
8
8
 
9
- delegate :association_reflection, to: :field
10
-
11
- def build_attributes
12
- build_has_many_attributes
13
- super
14
- end
15
-
16
- def build_has_many_attributes
9
+ def build_association_attributes
17
10
  attributes.fetch(:input_param) {
18
11
  attributes[:input_param] = :"#{association_reflection.name.to_s.singularize}_ids"
19
12
  }
@@ -19,8 +19,18 @@ module Phlexi
19
19
  protected
20
20
 
21
21
  def options
22
- choices.each do |value, label|
23
- option(selected: selected?(value), value: value) { label }
22
+ if choices.is_a?(GroupedChoicesMapper)
23
+ choices.each do |group_label, group_choices|
24
+ optgroup(label: group_label) do
25
+ group_choices.each do |value, label|
26
+ option(selected: selected?(value), value: value) { label }
27
+ end
28
+ end
29
+ end
30
+ else
31
+ choices.each do |value, label|
32
+ option(selected: selected?(value), value: value) { label }
33
+ end
24
34
  end
25
35
  end
26
36
 
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Form
5
+ class GroupedChoicesMapper
6
+ include Enumerable
7
+
8
+ def initialize(collection, group_method:, group_label_method: :to_s, label_method: nil, value_method: nil)
9
+ @collection = collection
10
+ @group_method = group_method
11
+ @group_label_method = group_label_method
12
+ @label_method = label_method
13
+ @value_method = value_method
14
+ end
15
+
16
+ def each(&)
17
+ grouped_choices.each(&)
18
+ end
19
+
20
+ def grouped_choices
21
+ @grouped_choices ||= materialize_grouped_choices(@collection)
22
+ end
23
+
24
+ def materialize_grouped_choices(collection)
25
+ case collection
26
+ in Hash => hash
27
+ hash.transform_values { |items| to_choices_mapper(items) }
28
+ in Array => arr
29
+ array_to_grouped_hash(arr)
30
+ in Proc => proc
31
+ materialize_grouped_choices(proc.call)
32
+ else
33
+ record_collection_to_hash
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def array_to_grouped_hash(array)
40
+ sample = array.first
41
+ if group_method == :last && sample.is_a?(Array) && sample.size == 2
42
+ array.each_with_object({}) do |(label, items), hash|
43
+ hash[label.to_s] = to_choices_mapper(items)
44
+ end
45
+ else
46
+ record_collection_to_hash
47
+ end
48
+ end
49
+
50
+ def record_collection_to_hash
51
+ @collection.group_by do |item|
52
+ group = item.public_send(@group_method)
53
+ group.public_send(@group_label_method).to_s
54
+ end.transform_values { |items| to_choices_mapper(items) }
55
+ end
56
+
57
+ def to_choices_mapper(items)
58
+ SimpleChoicesMapper.new(items, label_method: @label_method, value_method: @value_method)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -2,31 +2,31 @@
2
2
 
3
3
  module Phlexi
4
4
  module Form
5
- # ChoicesMapper is responsible for converting a collection of objects into a hash of options
5
+ # SimpleChoicesMapper is responsible for converting a collection of objects into a hash of options
6
6
  # suitable for form controls, such as `select > options`.
7
7
  # Both values and labels are converted to strings.
8
8
  #
9
9
  # @example Basic usage
10
10
  # collection = [["First", 1], ["Second", 2]]
11
- # mapper = ChoicesMapper.new(collection)
11
+ # mapper = SimpleChoicesMapper.new(collection)
12
12
  # mapper.each { |value, label| puts "#{value}: #{label}" }
13
13
  #
14
14
  # @example Using with ActiveRecord objects
15
15
  # users = User.all
16
- # mapper = ChoicesMapper.new(users)
16
+ # mapper = SimpleChoicesMapper.new(users)
17
17
  # mapper.each { |id, name| puts "#{id}: #{name}" }
18
18
  #
19
19
  # @example Array access with different value types
20
- # mapper = ChoicesMapper.new([["Integer", 1], ["String", "2"], ["Symbol", :three]])
20
+ # mapper = SimpleChoicesMapper.new([["Integer", 1], ["String", "2"], ["Symbol", :three]])
21
21
  # puts mapper["1"] # Output: "Integer"
22
22
  # puts mapper["2"] # Output: "String"
23
23
  # puts mapper["three"] # Output: "Symbol"
24
24
  #
25
25
  # @note This class is thread-safe as it doesn't maintain mutable state.
26
- class ChoicesMapper
26
+ class SimpleChoicesMapper
27
27
  include Enumerable
28
28
 
29
- # Initializes a new ChoicesMapper instance.
29
+ # Initializes a new SimpleChoicesMapper instance.
30
30
  #
31
31
  # @param collection [#call, #to_a] The collection to be mapped.
32
32
  # @param label_method [Symbol, nil] The method to call on each object to get the label.
@@ -93,9 +93,9 @@ module Phlexi
93
93
  else
94
94
  array_to_hash(Array(collection))
95
95
  end
96
- rescue ArgumentError
97
- # Rails.logger.warn("Unhandled inclusion collection type: #{e}")TODO
98
- {}
96
+ # rescue ArgumentError
97
+ # # Rails.logger.warn("Unhandled inclusion collection type: #{e}")TODO
98
+ # {}
99
99
  end
100
100
 
101
101
  # Converts an array to a hash using detected or specified methods.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Phlexi
4
4
  module Form
5
- VERSION = "0.7.2"
5
+ VERSION = "0.8.1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlexi-form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich
@@ -205,7 +205,7 @@ files:
205
205
  - lib/phlexi/form.rb
206
206
  - lib/phlexi/form/base.rb
207
207
  - lib/phlexi/form/builder.rb
208
- - lib/phlexi/form/choices_mapper.rb
208
+ - lib/phlexi/form/components/association_base.rb
209
209
  - lib/phlexi/form/components/base.rb
210
210
  - lib/phlexi/form/components/belongs_to.rb
211
211
  - lib/phlexi/form/components/checkbox.rb
@@ -231,6 +231,7 @@ files:
231
231
  - lib/phlexi/form/components/submit_button.rb
232
232
  - lib/phlexi/form/components/textarea.rb
233
233
  - lib/phlexi/form/components/wrapper.rb
234
+ - lib/phlexi/form/grouped_choices_mapper.rb
234
235
  - lib/phlexi/form/html.rb
235
236
  - lib/phlexi/form/options/autofocus.rb
236
237
  - lib/phlexi/form/options/choices.rb
@@ -247,6 +248,7 @@ files:
247
248
  - lib/phlexi/form/options/required.rb
248
249
  - lib/phlexi/form/options/step.rb
249
250
  - lib/phlexi/form/options/validators.rb
251
+ - lib/phlexi/form/simple_choices_mapper.rb
250
252
  - lib/phlexi/form/structure/field_collection.rb
251
253
  - lib/phlexi/form/structure/manages_fields.rb
252
254
  - lib/phlexi/form/structure/namespace.rb