phlexi-form 0.7.2 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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