phlexi-form 0.2.0 → 0.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +4 -9
  3. data/README.md +115 -316
  4. data/TODO +4 -0
  5. data/config.ru +0 -3
  6. data/gemfiles/default.gemfile.lock +22 -2
  7. data/gemfiles/rails_7.gemfile +8 -0
  8. data/gemfiles/rails_7.gemfile.lock +282 -0
  9. data/lib/phlexi/form/base.rb +52 -35
  10. data/lib/phlexi/form/components/base.rb +12 -6
  11. data/lib/phlexi/form/components/checkbox.rb +5 -0
  12. data/lib/phlexi/form/components/collection_checkboxes.rb +28 -14
  13. data/lib/phlexi/form/components/collection_radio_buttons.rb +19 -13
  14. data/lib/phlexi/form/components/concerns/handles_array_input.rb +21 -0
  15. data/lib/phlexi/form/components/concerns/handles_input.rb +53 -0
  16. data/lib/phlexi/form/components/concerns/has_options.rb +6 -2
  17. data/lib/phlexi/form/components/concerns/submits_form.rb +39 -0
  18. data/lib/phlexi/form/components/file_input.rb +32 -0
  19. data/lib/phlexi/form/components/input.rb +39 -33
  20. data/lib/phlexi/form/components/input_array.rb +45 -0
  21. data/lib/phlexi/form/components/label.rb +2 -1
  22. data/lib/phlexi/form/components/radio_button.rb +11 -1
  23. data/lib/phlexi/form/components/select.rb +21 -5
  24. data/lib/phlexi/form/components/submit_button.rb +41 -0
  25. data/lib/phlexi/form/field_options/associations.rb +21 -0
  26. data/lib/phlexi/form/field_options/autofocus.rb +1 -1
  27. data/lib/phlexi/form/field_options/collection.rb +26 -9
  28. data/lib/phlexi/form/field_options/errors.rb +10 -0
  29. data/lib/phlexi/form/field_options/{type.rb → inferred_types.rb} +12 -12
  30. data/lib/phlexi/form/field_options/multiple.rb +2 -0
  31. data/lib/phlexi/form/field_options/themes.rb +207 -0
  32. data/lib/phlexi/form/option_mapper.rb +2 -2
  33. data/lib/phlexi/form/structure/dom.rb +19 -14
  34. data/lib/phlexi/form/structure/field_builder.rb +145 -108
  35. data/lib/phlexi/form/structure/field_collection.rb +14 -5
  36. data/lib/phlexi/form/structure/namespace.rb +31 -19
  37. data/lib/phlexi/form/structure/namespace_collection.rb +20 -20
  38. data/lib/phlexi/form/structure/node.rb +1 -1
  39. data/lib/phlexi/form/version.rb +1 -1
  40. data/lib/phlexi/form.rb +4 -1
  41. metadata +30 -6
  42. data/CODE_OF_CONDUCT.md +0 -84
@@ -15,10 +15,10 @@ module Phlexi
15
15
 
16
16
  attr_reader :builder_klass, :object
17
17
 
18
- def initialize(key, parent:, object: nil, builder_klass: Field)
18
+ def initialize(key, parent:, builder_klass:, object: nil)
19
19
  super(key, parent: parent)
20
- @object = object
21
20
  @builder_klass = builder_klass
21
+ @object = object
22
22
  @children = {}
23
23
  yield self if block_given?
24
24
  end
@@ -29,6 +29,10 @@ module Phlexi
29
29
  end
30
30
  end
31
31
 
32
+ def submit_button(key = nil, **attributes, &)
33
+ field(key || SecureRandom.hex).submit_button_tag(**attributes, &)
34
+ end
35
+
32
36
  # Creates a `Namespace` child instance with the parent set to the current instance, adds to
33
37
  # the `@children` Hash to ensure duplicate child namespaces aren't created, then calls the
34
38
  # method on the `@object` to get the child object to pass into that namespace.
@@ -44,7 +48,7 @@ module Phlexi
44
48
  # end
45
49
  # ```
46
50
  def nest_one(key, object: nil, &)
47
- object ||= object_for(key: key)
51
+ object ||= object_value_for(key: key)
48
52
  create_child(key, self.class, object:, builder_klass:, &)
49
53
  end
50
54
 
@@ -65,17 +69,20 @@ module Phlexi
65
69
  # The object within the block is a `Namespace` object that maps each object within the enumerable
66
70
  # to another `Namespace` or `Field`.
67
71
  def nest_many(key, collection: nil, &)
68
- collection ||= Array(object_for(key: key))
72
+ collection ||= Array(object_value_for(key: key))
69
73
  create_child(key, NamespaceCollection, collection:, &)
70
74
  end
71
75
 
72
- # Creates a Hash of Hashes and Arrays that represent the fields and collections of the Superform.
73
- # This can be used to safely update ActiveRecord objects without the need for Strong Parameters.
74
- # You will want to make sure that all the fields displayed in the form are ones that you're OK updating
75
- # from the generated hash.
76
- def serialize
77
- each_with_object({}) do |child, hash|
78
- hash[child.key] = child.serialize
76
+ def extract_input(params)
77
+ if params.is_a?(Array)
78
+ each_with_object({}) do |child, hash|
79
+ hash.merge! child.extract_input(params[0])
80
+ end
81
+ else
82
+ input = each_with_object({}) do |child, hash|
83
+ hash.merge! child.extract_input(params[key])
84
+ end
85
+ {key => input}
79
86
  end
80
87
  end
81
88
 
@@ -85,17 +92,22 @@ module Phlexi
85
92
  @children.values.each(&)
86
93
  end
87
94
 
88
- # Assigns a hash to the current namespace and children namespace.
89
- def assign(hash)
90
- each do |child|
91
- child.assign hash[child.key]
95
+ def dom_id
96
+ @dom_id ||= begin
97
+ id = if object.nil?
98
+ nil
99
+ elsif object.class.respond_to?(:primary_key)
100
+ object.public_send(object.class.primary_key) || :new
101
+ elsif object.respond_to?(:id)
102
+ object.id || :new
103
+ end
104
+ [key, id].compact.join("_").underscore
92
105
  end
93
- self
94
106
  end
95
107
 
96
108
  # Creates a root Namespace, which is essentially a form.
97
- def self.root(*, **, &)
98
- new(*, parent: nil, **, &)
109
+ def self.root(*, builder_klass:, **, &)
110
+ new(*, parent: nil, builder_klass:, **, &)
99
111
  end
100
112
 
101
113
  protected
@@ -106,7 +118,7 @@ module Phlexi
106
118
  #
107
119
  # This method could be overwritten if the mapping between the `@object` and `key` name is not
108
120
  # a method call. For example, a `Hash` would be accessed via `user[:email]` instead of `user.send(:email)`
109
- def object_for(key:)
121
+ def object_value_for(key:)
110
122
  @object.send(key) if @object.respond_to? key
111
123
  end
112
124
 
@@ -6,36 +6,36 @@ module Phlexi
6
6
  class NamespaceCollection < Node
7
7
  include Enumerable
8
8
 
9
- def initialize(key, parent:, collection: nil, &)
9
+ def initialize(key, parent:, collection: nil, &block)
10
+ raise ArgumentError, "block is required" unless block.present?
11
+
10
12
  super(key, parent: parent)
11
13
 
12
- @namespaces = enumerate(collection)
13
- each(&) if block_given?
14
+ @collection = collection
15
+ @block = block
16
+ each(&block)
14
17
  end
15
18
 
16
- def serialize
17
- map(&:serialize)
18
- end
19
+ def extract_input(params)
20
+ namespace = build_namespace(0)
21
+ @block.call(namespace)
19
22
 
20
- def assign(array)
21
- # The problem with zip-ing the array is if I need to add new
22
- # elements to it and wrap it in the namespace.
23
- zip(array) do |namespace, hash|
24
- namespace.assign hash
25
- end
23
+ inputs = params[key].map { |param| namespace.extract_input([param]) }
24
+ {key => inputs}
26
25
  end
27
26
 
27
+ private
28
+
28
29
  def each(&)
29
- @namespaces.each(&)
30
+ namespaces.each(&)
30
31
  end
31
32
 
32
- private
33
-
34
- def enumerate(enumerator)
35
- Enumerator.new do |y|
36
- enumerator.each.with_index do |object, key|
37
- y << build_namespace(key, object: object)
38
- end
33
+ # Builds and memoizes namespaces for the collection.
34
+ #
35
+ # @return [Array<Hash>] An array of namespace hashes.
36
+ def namespaces
37
+ @namespaces ||= @collection.map.with_index do |object, key|
38
+ build_namespace(key, object: object)
39
39
  end
40
40
  end
41
41
 
@@ -9,7 +9,7 @@ module Phlexi
9
9
  attr_reader :key, :parent
10
10
 
11
11
  def initialize(key, parent:)
12
- @key = key
12
+ @key = key.to_s.to_sym
13
13
  @parent = parent
14
14
  end
15
15
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Phlexi
4
4
  module Form
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0.rc1"
6
6
  end
7
7
  end
data/lib/phlexi/form.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "zeitwerk"
4
4
  require "phlex"
5
+ require "active_support/core_ext/object/blank"
5
6
 
6
7
  module Phlexi
7
8
  module Form
@@ -17,7 +18,9 @@ module Phlexi
17
18
  loader.setup
18
19
  end
19
20
 
20
- BaseComponent = (defined?(::ApplicationComponent) ? ::ApplicationComponent : Phlex::HTML)
21
+ COMPONENT_BASE = (defined?(::ApplicationComponent) ? ::ApplicationComponent : Phlex::HTML)
22
+
23
+ NIL_VALUE = :__i_phlexi_form_nil_value_i__
21
24
 
22
25
  class Error < StandardError; end
23
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlexi-form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-31 00:00:00.000000000 Z
11
+ date: 2024-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.10'
19
+ version: '1.11'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.10'
26
+ version: '1.11'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: phlex-testing-capybara
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  description: A better way to customize and build forms for your Rails application
154
168
  email:
155
169
  - sfroelich01@gmail.com
@@ -161,13 +175,15 @@ files:
161
175
  - ".ruby-version"
162
176
  - Appraisals
163
177
  - CHANGELOG.md
164
- - CODE_OF_CONDUCT.md
165
178
  - LICENSE.txt
166
179
  - README.md
167
180
  - Rakefile
181
+ - TODO
168
182
  - config.ru
169
183
  - gemfiles/default.gemfile
170
184
  - gemfiles/default.gemfile.lock
185
+ - gemfiles/rails_7.gemfile
186
+ - gemfiles/rails_7.gemfile.lock
171
187
  - lib/generators/superform/install/USAGE
172
188
  - lib/generators/superform/install/install_generator.rb
173
189
  - lib/generators/superform/install/templates/application_form.rb
@@ -178,21 +194,29 @@ files:
178
194
  - lib/phlexi/form/components/checkbox.rb
179
195
  - lib/phlexi/form/components/collection_checkboxes.rb
180
196
  - lib/phlexi/form/components/collection_radio_buttons.rb
197
+ - lib/phlexi/form/components/concerns/handles_array_input.rb
198
+ - lib/phlexi/form/components/concerns/handles_input.rb
181
199
  - lib/phlexi/form/components/concerns/has_options.rb
200
+ - lib/phlexi/form/components/concerns/submits_form.rb
182
201
  - lib/phlexi/form/components/error.rb
202
+ - lib/phlexi/form/components/file_input.rb
183
203
  - lib/phlexi/form/components/full_error.rb
184
204
  - lib/phlexi/form/components/hint.rb
185
205
  - lib/phlexi/form/components/input.rb
206
+ - lib/phlexi/form/components/input_array.rb
186
207
  - lib/phlexi/form/components/label.rb
187
208
  - lib/phlexi/form/components/radio_button.rb
188
209
  - lib/phlexi/form/components/select.rb
210
+ - lib/phlexi/form/components/submit_button.rb
189
211
  - lib/phlexi/form/components/textarea.rb
190
212
  - lib/phlexi/form/components/wrapper.rb
213
+ - lib/phlexi/form/field_options/associations.rb
191
214
  - lib/phlexi/form/field_options/autofocus.rb
192
215
  - lib/phlexi/form/field_options/collection.rb
193
216
  - lib/phlexi/form/field_options/disabled.rb
194
217
  - lib/phlexi/form/field_options/errors.rb
195
218
  - lib/phlexi/form/field_options/hints.rb
219
+ - lib/phlexi/form/field_options/inferred_types.rb
196
220
  - lib/phlexi/form/field_options/labels.rb
197
221
  - lib/phlexi/form/field_options/length.rb
198
222
  - lib/phlexi/form/field_options/limit.rb
@@ -202,7 +226,7 @@ files:
202
226
  - lib/phlexi/form/field_options/placeholder.rb
203
227
  - lib/phlexi/form/field_options/readonly.rb
204
228
  - lib/phlexi/form/field_options/required.rb
205
- - lib/phlexi/form/field_options/type.rb
229
+ - lib/phlexi/form/field_options/themes.rb
206
230
  - lib/phlexi/form/field_options/validators.rb
207
231
  - lib/phlexi/form/option_mapper.rb
208
232
  - lib/phlexi/form/structure/dom.rb
data/CODE_OF_CONDUCT.md DELETED
@@ -1,84 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
-
7
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
-
9
- ## Our Standards
10
-
11
- Examples of behavior that contributes to a positive environment for our community include:
12
-
13
- * Demonstrating empathy and kindness toward other people
14
- * Being respectful of differing opinions, viewpoints, and experiences
15
- * Giving and gracefully accepting constructive feedback
16
- * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
- * Focusing on what is best not just for us as individuals, but for the overall community
18
-
19
- Examples of unacceptable behavior include:
20
-
21
- * The use of sexualized language or imagery, and sexual attention or
22
- advances of any kind
23
- * Trolling, insulting or derogatory comments, and personal or political attacks
24
- * Public or private harassment
25
- * Publishing others' private information, such as a physical or email
26
- address, without their explicit permission
27
- * Other conduct which could reasonably be considered inappropriate in a
28
- professional setting
29
-
30
- ## Enforcement Responsibilities
31
-
32
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
-
34
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
-
36
- ## Scope
37
-
38
- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
-
40
- ## Enforcement
41
-
42
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at bradgessler@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
43
-
44
- All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
-
46
- ## Enforcement Guidelines
47
-
48
- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
-
50
- ### 1. Correction
51
-
52
- **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
-
54
- **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
-
56
- ### 2. Warning
57
-
58
- **Community Impact**: A violation through a single incident or series of actions.
59
-
60
- **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
-
62
- ### 3. Temporary Ban
63
-
64
- **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
-
66
- **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
-
68
- ### 4. Permanent Ban
69
-
70
- **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
-
72
- **Consequence**: A permanent ban from any sort of public interaction within the community.
73
-
74
- ## Attribution
75
-
76
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
- available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
-
79
- Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
-
81
- [homepage]: https://www.contributor-covenant.org
82
-
83
- For answers to common questions about this code of conduct, see the FAQ at
84
- https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.