formalist 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -3
  3. data/Gemfile.lock +36 -38
  4. data/README.md +8 -4
  5. data/Rakefile +0 -7
  6. data/lib/formalist/element/attributes.rb +54 -0
  7. data/lib/formalist/element/class_interface.rb +133 -0
  8. data/lib/formalist/element/definition.rb +55 -0
  9. data/lib/formalist/element/permitted_children.rb +46 -0
  10. data/lib/formalist/element.rb +51 -0
  11. data/lib/formalist/elements/attr.rb +74 -0
  12. data/lib/formalist/elements/compound_field.rb +49 -0
  13. data/lib/formalist/elements/field.rb +73 -0
  14. data/lib/formalist/elements/group.rb +50 -0
  15. data/lib/formalist/elements/many.rb +125 -0
  16. data/lib/formalist/elements/section.rb +58 -0
  17. data/lib/formalist/elements/standard/check_box.rb +20 -0
  18. data/lib/formalist/elements/standard/date_field.rb +12 -0
  19. data/lib/formalist/elements/standard/date_time_field.rb +12 -0
  20. data/lib/formalist/elements/standard/hidden_field.rb +11 -0
  21. data/lib/formalist/elements/standard/multi_selection_field.rb +16 -0
  22. data/lib/formalist/elements/standard/number_field.rb +17 -0
  23. data/lib/formalist/elements/standard/radio_buttons.rb +13 -0
  24. data/lib/formalist/elements/standard/select_box.rb +13 -0
  25. data/lib/formalist/elements/standard/selection_field.rb +16 -0
  26. data/lib/formalist/elements/standard/text_area.rb +15 -0
  27. data/lib/formalist/elements/standard/text_field.rb +14 -0
  28. data/lib/formalist/elements/standard.rb +11 -0
  29. data/lib/formalist/elements.rb +20 -0
  30. data/lib/formalist/form/definition_context.rb +58 -4
  31. data/lib/formalist/form/result.rb +5 -27
  32. data/lib/formalist/form.rb +15 -35
  33. data/lib/formalist/types.rb +30 -0
  34. data/lib/formalist/version.rb +1 -1
  35. data/lib/formalist.rb +0 -20
  36. data/spec/examples.txt +8 -7
  37. data/spec/integration/dependency_injection_spec.rb +54 -0
  38. data/spec/integration/form_spec.rb +86 -13
  39. data/spec/spec_helper.rb +12 -5
  40. data/spec/support/constants.rb +11 -0
  41. data/spec/unit/elements/standard/check_box_spec.rb +33 -0
  42. metadata +36 -63
  43. data/lib/formalist/definition_compiler.rb +0 -61
  44. data/lib/formalist/display_adapters/default.rb +0 -9
  45. data/lib/formalist/display_adapters/radio.rb +0 -19
  46. data/lib/formalist/display_adapters/select.rb +0 -19
  47. data/lib/formalist/display_adapters/textarea.rb +0 -14
  48. data/lib/formalist/display_adapters.rb +0 -16
  49. data/lib/formalist/form/definition/attr.rb +0 -20
  50. data/lib/formalist/form/definition/component.rb +0 -31
  51. data/lib/formalist/form/definition/field.rb +0 -29
  52. data/lib/formalist/form/definition/group.rb +0 -31
  53. data/lib/formalist/form/definition/many.rb +0 -41
  54. data/lib/formalist/form/definition/section.rb +0 -23
  55. data/lib/formalist/form/definition.rb +0 -37
  56. data/lib/formalist/form/result/attr.rb +0 -82
  57. data/lib/formalist/form/result/component.rb +0 -51
  58. data/lib/formalist/form/result/field.rb +0 -77
  59. data/lib/formalist/form/result/group.rb +0 -51
  60. data/lib/formalist/form/result/many.rb +0 -123
  61. data/lib/formalist/form/result/section.rb +0 -54
  62. data/lib/formalist/form/validated_result.rb +0 -35
  63. data/lib/formalist/output_compiler.rb +0 -43
  64. data/lib/formalist/validation/collection_rules_compiler.rb +0 -77
  65. data/lib/formalist/validation/predicate_list_compiler.rb +0 -73
  66. data/lib/formalist/validation/value_rules_compiler.rb +0 -96
  67. data/spec/integration/display_adapters_spec.rb +0 -55
  68. data/spec/integration/validation_spec.rb +0 -86
  69. data/spec/unit/output_compiler_spec.rb +0 -70
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 213025004cb07e1b9962e338b59d4170e2f20c9d
4
- data.tar.gz: f17a38dd7de60b6672ec18027b988119b73be9f3
3
+ metadata.gz: 96b43ff385384015c3c6c1cac64a8e9cccaa753c
4
+ data.tar.gz: 42acab631e93a3c52c73de8a5822c96622e7b2af
5
5
  SHA512:
6
- metadata.gz: dafc943fc98d96e5a671b81ebbe6be0eebc20a9ab3f9558bae54ae4c8535cd542cb1e56b304dc6c89494b3f4d410694063192e993a51c79748d3aea2fa9b6681
7
- data.tar.gz: b0bf744c37494e443e294f25d0a8cc4f549955149ffa205e44be3ccee791d0b53fc60ca5bfd3471ce6ed80c1dac277c5dcac36fa606eeccf82526e2e5d314707
6
+ metadata.gz: 1cdeb2f130a14bceb9a8af2a71fcf22c463c1fc1554b42fc98ccc20bb826804853004553a3cee82114500ba49139d4413adb0ab3855cae19f73c2fa2a06e4594
7
+ data.tar.gz: b87cd081725b0fe4556858d33b8648f0dead749122d74f2e5371eb720958d5610deb104e0de09a6d1341d4ec7825a01907aad807072ac2747d2536c852505b87
data/Gemfile CHANGED
@@ -1,11 +1,15 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- # Gem dependencies are specified in formalist.gemspec
4
3
  gemspec
5
4
 
6
- # Lock to a version of dry-v that'll offer the AST/error message structures we expect
7
- gem "dry-validation", git: "https://github.com/dryrb/dry-validation", ref: "6447302f3b53766b29f29230831890a5cc3822e0"
5
+ group :test do
6
+ gem "codeclimate-test-reporter", require: nil
7
+ gem "dry-auto_inject"
8
+ gem "dry-validation", git: "https://github.com/dryrb/dry-validation", branch: "master"
9
+ gem "dry-types", git: "https://github.com/dryrb/dry-types", branch: "master"
10
+ end
8
11
 
9
12
  group :tools do
10
13
  gem "pry"
14
+ gem "byebug", platform: :mri
11
15
  end
data/Gemfile.lock CHANGED
@@ -1,63 +1,67 @@
1
+ GIT
2
+ remote: https://github.com/dryrb/dry-types
3
+ revision: 9d72307cfaa33afa8b597646cd77a06de8b6bb88
4
+ branch: master
5
+ specs:
6
+ dry-types (0.7.1)
7
+ concurrent-ruby (~> 1.0)
8
+ dry-configurable (~> 0.1)
9
+ dry-container (~> 0.3)
10
+ dry-equalizer (~> 0.2)
11
+ dry-logic (~> 0.2, >= 0.2.0)
12
+ inflecto (~> 0.0.0, >= 0.0.2)
13
+ kleisli (~> 0.2)
14
+
1
15
  GIT
2
16
  remote: https://github.com/dryrb/dry-validation
3
- revision: 6447302f3b53766b29f29230831890a5cc3822e0
4
- ref: 6447302f3b53766b29f29230831890a5cc3822e0
17
+ revision: 6167d6f778faefe13916d3d8eee8bdb4197398d3
18
+ branch: master
5
19
  specs:
6
- dry-validation (0.6.0)
20
+ dry-validation (0.7.4)
21
+ concurrent-ruby (~> 1.0)
7
22
  dry-configurable (~> 0.1, >= 0.1.3)
8
23
  dry-container (~> 0.2, >= 0.2.8)
9
- dry-data (~> 0.5, >= 0.5.0)
10
24
  dry-equalizer (~> 0.2)
11
- dry-logic (~> 0.1, >= 0.1.4)
25
+ dry-logic (~> 0.2, >= 0.2.2)
26
+ dry-types (~> 0.6, >= 0.6.0)
12
27
 
13
28
  PATH
14
29
  remote: .
15
30
  specs:
16
- formalist (0.2.2)
31
+ formalist (0.2.3)
17
32
  dry-configurable
18
33
  dry-container
19
- dry-validation (~> 0.6.0)
34
+ dry-types
20
35
  inflecto
21
36
 
22
37
  GEM
23
38
  remote: https://rubygems.org/
24
39
  specs:
25
- ast (2.1.0)
26
- astrolabe (1.3.1)
27
- parser (~> 2.2)
28
- byebug (8.2.1)
29
- coderay (1.1.0)
40
+ byebug (8.2.3)
41
+ codeclimate-test-reporter (0.5.0)
42
+ simplecov (>= 0.7.1, < 1.0.0)
43
+ coderay (1.1.1)
44
+ concurrent-ruby (1.0.1)
30
45
  diff-lcs (1.2.5)
31
46
  docile (1.1.5)
32
- dry-configurable (0.1.3)
33
- thread_safe
34
- dry-container (0.2.8)
47
+ dry-auto_inject (0.2.0)
48
+ dry-configurable (0.1.4)
49
+ concurrent-ruby (~> 1.0)
50
+ dry-container (0.3.1)
51
+ concurrent-ruby (~> 1.0)
35
52
  dry-configurable (~> 0.1, >= 0.1.3)
36
- thread_safe
37
- dry-data (0.5.1)
38
- dry-configurable (~> 0.1)
39
- dry-container (~> 0.2)
40
- dry-equalizer (~> 0.2)
41
- dry-logic (~> 0.1)
42
- inflecto (~> 0.0.0, >= 0.0.2)
43
- kleisli (~> 0.2)
44
- thread_safe (~> 0.3)
45
53
  dry-equalizer (0.2.0)
46
- dry-logic (0.1.4)
54
+ dry-logic (0.2.2)
47
55
  dry-container (~> 0.2, >= 0.2.6)
48
56
  dry-equalizer (~> 0.2)
49
57
  inflecto (0.0.2)
50
58
  json (1.8.3)
51
59
  kleisli (0.2.7)
52
60
  method_source (0.8.2)
53
- parser (2.2.3.0)
54
- ast (>= 1.1, < 3.0)
55
- powerpack (0.1.1)
56
61
  pry (0.10.3)
57
62
  coderay (~> 1.1.0)
58
63
  method_source (~> 0.8.1)
59
64
  slop (~> 3.4)
60
- rainbow (2.0.0)
61
65
  rake (10.4.2)
62
66
  rspec (3.3.0)
63
67
  rspec-core (~> 3.3.0)
@@ -72,20 +76,12 @@ GEM
72
76
  diff-lcs (>= 1.2.0, < 2.0)
73
77
  rspec-support (~> 3.3.0)
74
78
  rspec-support (3.3.0)
75
- rubocop (0.34.2)
76
- astrolabe (~> 1.3)
77
- parser (>= 2.2.2.5, < 3.0)
78
- powerpack (~> 0.1)
79
- rainbow (>= 1.99.1, < 3.0)
80
- ruby-progressbar (~> 1.4)
81
- ruby-progressbar (1.7.5)
82
79
  simplecov (0.10.0)
83
80
  docile (~> 1.1.0)
84
81
  json (~> 1.8)
85
82
  simplecov-html (~> 0.10.0)
86
83
  simplecov-html (0.10.0)
87
84
  slop (3.6.0)
88
- thread_safe (0.3.5)
89
85
  yard (0.8.7.6)
90
86
 
91
87
  PLATFORMS
@@ -94,12 +90,14 @@ PLATFORMS
94
90
  DEPENDENCIES
95
91
  bundler (~> 1.10)
96
92
  byebug
93
+ codeclimate-test-reporter
94
+ dry-auto_inject
95
+ dry-types!
97
96
  dry-validation!
98
97
  formalist!
99
98
  pry
100
99
  rake (~> 10.4.2)
101
100
  rspec (~> 3.3.0)
102
- rubocop (~> 0.34.2)
103
101
  simplecov (~> 0.10.0)
104
102
  yard
105
103
 
data/README.md CHANGED
@@ -1,20 +1,24 @@
1
+ [gem]: https://rubygems.org/gems/formalist
1
2
  [travis]: https://travis-ci.org/icelab/formalist
3
+ [code_climate]: https://codeclimate.com/github/icelab/formalist
4
+ [inch]: http://inch-ci.org/github/icelab/formalist
2
5
 
3
6
  # Formalist
4
7
 
8
+ [![Gem Version](https://img.shields.io/gem/v/formalist.svg)][gem]
5
9
  [![Build Status](https://travis-ci.org/icelab/formalist.svg?branch=master)][travis]
10
+ [![Code Climate](https://img.shields.io/codeclimate/github/icelab/formalist.svg)][code_climate]
11
+ [![Test Coverage](https://img.shields.io/codeclimate/coverage/github/icelab/formalist.svg)][code_climate]
12
+ [![API Documentation Coverage](http://inch-ci.org/github/icelab/formalist.svg)][inch]
6
13
 
7
14
  ## Installation
8
15
 
9
- Add these lines to your application’s `Gemfile`:
16
+ Add this line to your application’s `Gemfile`:
10
17
 
11
18
  ```ruby
12
- gem "dry-validation", git: "https://github.com/dryrb/dry-validation", ref: "6447302f3b53766b29f29230831890a5cc3822e0"
13
19
  gem "formalist"
14
20
  ```
15
21
 
16
- The dry-validation dependency is a temporary lock to a version that offers the AST/error message structures we expect. You should be able to remove this after future releases of formalist and dry-validation.
17
-
18
22
  Run `bundle` to install the gems.
19
23
 
20
24
  ## Contributing
data/Rakefile CHANGED
@@ -3,11 +3,4 @@ require "bundler/gem_tasks"
3
3
  require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new
5
5
 
6
- require "rubocop/rake_task"
7
- RuboCop::RakeTask.new
8
-
9
6
  task default: :spec
10
- # task default: :ci
11
-
12
- desc "Run the test suite"
13
- task ci: %w(rubocop spec)
@@ -0,0 +1,54 @@
1
+ module Formalist
2
+ class Element
3
+ class Attributes
4
+ # Returns the attributes hash.
5
+ attr_reader :attrs
6
+
7
+ # Creates an attributes object from the supplied hash.
8
+ #
9
+ # @param attrs [Hash] hash of form element attributes
10
+ def initialize(attrs = {})
11
+ @attrs = attrs
12
+ end
13
+
14
+ # Returns the attributes as an abstract syntax tree.
15
+ #
16
+ # @return [Array] the abstract syntax tree
17
+ def to_ast
18
+ deep_to_ast(deep_simplify(attrs))
19
+ end
20
+
21
+ private
22
+
23
+ def deep_to_ast(value)
24
+ case value
25
+ when Hash
26
+ [:object, [value.map { |k,v| [k.to_sym, deep_to_ast(v)] }].reject(&:empty?).flatten(1)]
27
+ when Array
28
+ [:array, value.map { |v| deep_to_ast(v) }]
29
+ when String, Numeric, TrueClass, FalseClass, NilClass
30
+ [:value, [value]]
31
+ else
32
+ [:value, [value.to_s]]
33
+ end
34
+ end
35
+
36
+ def deep_simplify(value)
37
+ case value
38
+ when Hash
39
+ value.each_with_object({}) { |(k,v), output| output[k] = deep_simplify(v) }
40
+ when Array
41
+ value.map { |v| deep_simplify(v) }
42
+ when String, Numeric, TrueClass, FalseClass, NilClass
43
+ value
44
+ else
45
+ if value.respond_to?(:to_h)
46
+ deep_simplify(value.to_h)
47
+ else
48
+ value.to_s
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,133 @@
1
+ require "inflecto"
2
+ require "formalist/element/permitted_children"
3
+
4
+ module Formalist
5
+ class Element
6
+ # Class-level API for form elements.
7
+ module ClassInterface
8
+ # Returns the element's type, which is a symbolized, camlized
9
+ # representation of the element's class name.
10
+ #
11
+ # This is a critical hook for customising form rendering when using
12
+ # custom form elements, since the type in this case will be based on the
13
+ # name of form element's sublass.
14
+ #
15
+ # @example Basic element
16
+ # Formalist::Elements::Field.type # => :field
17
+ #
18
+ # @example Custom element
19
+ # class MyField < Formalist::Elements::Field
20
+ # end
21
+ #
22
+ # MyField.type # => :my_field
23
+ #
24
+ # @!scope class
25
+ # @return [Symbol] the element type.
26
+ def type
27
+ Inflecto.underscore(Inflecto.demodulize(name)).to_sym
28
+ end
29
+
30
+ # Define a form element attribute.
31
+ #
32
+ # Form element attributes can be set when the element is defined, and
33
+ # can be further populated by the form element object itself, when the
34
+ # form is being built, using both the user input and any dependencies
35
+ # passed to the element via the form.
36
+ #
37
+ # Attributes are the way to ensure the form renderer has all the
38
+ # information it needs to render the element appropriately. Attributes
39
+ # are type-checked in order to ensure they're being passed appropriate
40
+ # values. Attributes can hold any type of value as long as it can be
41
+ # reduced to an abstract syntax tree representation by
42
+ # `Form::Element::Attributes#to_ast`.
43
+ #
44
+ # @see Formalist::Element::Attributes#to_ast
45
+ #
46
+ # @!scope class
47
+ # @param name [Symbol] attribute name
48
+ # @param type [Dry::Data::Type, #call] value type coercer/checker
49
+ # @param default default value (applied when the attribute is not explicitly populated)
50
+ # @return void
51
+ def attribute(name, type, default: nil)
52
+ attributes(name => {type: type, default: default})
53
+ end
54
+
55
+ # Returns the attributes schema for the form element.
56
+ #
57
+ # Each item in the schema includes a type definition and a default value
58
+ # (`nil` if none specified).
59
+ #
60
+ # @example
61
+ # Formalist::Elements::Field.attributes_schema
62
+ # # => {
63
+ # :name => {:type => #<Dry::Data::Type>, :default => "Default name"},
64
+ # :email => {:type => #<Dry::Data::Type>, :default => "default email"}
65
+ # }
66
+ #
67
+ # @!scope class
68
+ # @return [Hash<Symbol, Hash>] the attributes schema
69
+ def attributes_schema
70
+ super_schema = superclass.respond_to?(:attributes_schema) ? superclass.attributes_schema : {}
71
+ super_schema.merge(@attributes_schema || {})
72
+ end
73
+
74
+ # Sets or fetches the policy for the element's permitted child form elements.
75
+ #
76
+ # @overload permitted_children(policy)
77
+ # Set a policy for whether the element permits child form elements.
78
+ #
79
+ # Specify `:all` to allow all children, or `:none` to permit no
80
+ # children.
81
+ #
82
+ # @example Permitting all children
83
+ # permitted_children :all
84
+ #
85
+ # @example Permitting no children
86
+ # permitted_children :none
87
+ #
88
+ # @return void
89
+ #
90
+ # @overload permitted_children(element_type, ...)
91
+ # Permit the element to contain only the specified element types as children.
92
+ #
93
+ # @example
94
+ # permit_children :section, :field
95
+ #
96
+ # @param element_type [Symbol] the name of a child element type to permit
97
+ # @param ... [Symbol] more child element types to permit
98
+ # @return void
99
+ #
100
+ # @overload permitted_children
101
+ # Returns the permitted child element types for the element.
102
+ #
103
+ # If no `permitted_children` policy was previously specified, then it
104
+ # allows all children by default.
105
+ #
106
+ # @return [#permitted?] permissions object.
107
+ #
108
+ # @see Formalist::Element::PermittedChildren
109
+ #
110
+ # @!scope class
111
+ def permitted_children(*args)
112
+ return @permitted_children ||= PermittedChildren.all if args.empty?
113
+
114
+ @permitted_children = if %i[all none].include?(args.first)
115
+ PermittedChildren.send(args.first)
116
+ else
117
+ PermittedChildren[args]
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ # @!scope class
124
+ # @api private
125
+ def attributes(new_schema)
126
+ prev_schema = @attributes_schema || {}
127
+ @attributes_schema = prev_schema.merge(new_schema)
128
+
129
+ self
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,55 @@
1
+ require "formalist/types"
2
+
3
+ module Formalist
4
+ class Element
5
+ class Definition
6
+ Deferred = Struct.new(:name)
7
+
8
+ attr_reader :type
9
+ attr_reader :args
10
+ attr_reader :attributes
11
+ attr_reader :children
12
+
13
+ def initialize(type, *args, attributes, children)
14
+ @type = type
15
+ @args = args
16
+ @attributes = attributes
17
+ @children = children
18
+ end
19
+
20
+ def ==(other)
21
+ # Require a matching type.
22
+ return false if type != other.type
23
+
24
+ # If there are no primary args, it means that the element has no real
25
+ # "identifier" that requires uniqueness, so it's safe to say they don't
26
+ # match here.
27
+ return false if args.empty?
28
+
29
+ # Otherwise, use the primary args as a marker of a definitions
30
+ # uniqueness. With the current set of base form elements, the primary
31
+ # args only ever contains a name, so this is effectively a uniqueness
32
+ # check on the element's name.
33
+ args == other.args
34
+ end
35
+
36
+ def resolve(scope)
37
+ resolved_args = args.map { |arg|
38
+ arg.is_a?(Deferred) ? scope.send(arg.name) : arg
39
+ }
40
+
41
+ resolved_attributes = attributes.each_with_object({}) { |(key, val), hsh|
42
+ hsh[key] = val.is_a?(Deferred) ? scope.send(val.name) : val
43
+ }
44
+
45
+ resolved_children = children.map { |c| c.resolve(scope) }
46
+
47
+ self.class.new(type, *resolved_args, resolved_attributes, resolved_children)
48
+ end
49
+
50
+ def call(input, messages)
51
+ type.new(*args, attributes, children, input, messages)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,46 @@
1
+ require "inflecto"
2
+
3
+ module Formalist
4
+ class Element
5
+ class PermittedChildren
6
+ All = Class.new do
7
+ def permitted?(*)
8
+ true
9
+ end
10
+ end.new
11
+
12
+ None = Class.new do
13
+ def permitted?(*)
14
+ false
15
+ end
16
+ end.new
17
+
18
+ class Some
19
+ attr_reader :permitted_children
20
+
21
+ def initialize(children)
22
+ @permitted_children = children
23
+ end
24
+
25
+ def permitted?(child)
26
+ permitted_children.any? { |permitted_child|
27
+ permitted_child_class = Elements.const_get(Inflecto.camelize(permitted_child))
28
+ child.ancestors.include?(permitted_child_class)
29
+ }
30
+ end
31
+ end
32
+
33
+ def self.all
34
+ All
35
+ end
36
+
37
+ def self.none
38
+ None
39
+ end
40
+
41
+ def self.[](children)
42
+ Some.new(children)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,51 @@
1
+ require "formalist/element/attributes"
2
+ require "formalist/element/class_interface"
3
+ require "formalist/types"
4
+
5
+ module Formalist
6
+ class Element
7
+ extend ClassInterface
8
+
9
+ # @api private
10
+ attr_reader :attributes, :children, :input, :errors
11
+
12
+ # @api private
13
+ def initialize(*args, attributes, children, input, errors)
14
+ # Set supplied attributes or their defaults
15
+ full_attributes = self.class.attributes_schema.each_with_object({}) { |(name, defn), memo|
16
+ value = attributes[name] || defn[:default]
17
+ memo[name] = value unless value.nil?
18
+ }
19
+
20
+ # Then run them through the schema
21
+ @attributes = Types::Hash.schema(self.class.attributes_schema.map { |name, defn| [name, defn[:type]] }.to_h).(full_attributes)
22
+
23
+ @children = []
24
+ @input = input
25
+ @errors = errors
26
+ end
27
+
28
+ # Returns the element's type, which is a symbolized, camlized
29
+ # representation of the element's class name.
30
+ #
31
+ # This is a critical hook for customising form rendering when using custom
32
+ # form elements, since the type in this case will be based on the name of
33
+ # form element's sublass.
34
+ #
35
+ # @example Basic element
36
+ # field.type # => :field
37
+ #
38
+ # @example Custom element
39
+ # my_field.type # => :my_field
40
+ #
41
+ # @return [Symbol] the element type.
42
+ def type
43
+ self.class.type
44
+ end
45
+
46
+ # @abstract
47
+ def to_ast
48
+ raise NotImplementedError
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,74 @@
1
+ require "formalist/element"
2
+ require "formalist/types"
3
+
4
+ module Formalist
5
+ class Elements
6
+ class Attr < Element
7
+ permitted_children :all
8
+
9
+ # @api private
10
+ attr_reader :name
11
+
12
+ attribute :label, Types::String
13
+
14
+ # @api private
15
+ def initialize(*args, attributes, children, input, errors)
16
+ super
17
+
18
+ @name = Types::ElementName.(args.first)
19
+ @input = input.fetch(@name, {})
20
+ @errors = errors[@name]
21
+ @children = build_children(children)
22
+ end
23
+
24
+ # Converts the attribute into an abstract syntax tree.
25
+ #
26
+ # It takes the following format:
27
+ #
28
+ # ```
29
+ # [:attr, [params]]
30
+ # ```
31
+ #
32
+ # With the following parameters:
33
+ #
34
+ # 1. Attribute name
35
+ # 2. Custom element type (or `:attr` otherwise)
36
+ # 3. Error messages
37
+ # 4. Form element attributes
38
+ # 5. Child form elements
39
+ #
40
+ # @see Formalist::Element::Attributes#to_ast "Form element attributes" structure
41
+ #
42
+ # @example "metadata" attr
43
+ # attr.to_ast
44
+ # # => [:attr, [
45
+ # :metadata,
46
+ # :attr,
47
+ # ["metadata is missing"],
48
+ # [:object, []],
49
+ # [...child elements...]
50
+ # ]]
51
+ #
52
+ # @return [Array] the attribute as an abstract syntax tree.
53
+ def to_ast
54
+ local_errors = errors.is_a?(Array) ? errors : []
55
+
56
+ [:attr, [
57
+ name,
58
+ type,
59
+ local_errors,
60
+ Element::Attributes.new(attributes).to_ast,
61
+ children.map(&:to_ast),
62
+ ]]
63
+ end
64
+
65
+ private
66
+
67
+ def build_children(definitions)
68
+ child_errors = errors.is_a?(Hash) ? errors : {}
69
+
70
+ definitions.map { |definition| definition.(input, child_errors) }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,49 @@
1
+ require "formalist/element"
2
+ require "formalist/types"
3
+
4
+ module Formalist
5
+ class Elements
6
+ class CompoundField < Element
7
+ permitted_children :field
8
+
9
+ def initialize(*args, attributes, children, input, errors)
10
+ super
11
+ @children = children.map { |definition| definition.(input, errors) }
12
+ end
13
+
14
+ # Converts the compound field into an abstract syntax tree.
15
+ #
16
+ # It takes the following format:
17
+ #
18
+ # ```
19
+ # [:compound_field, [params]]
20
+ # ```
21
+ #
22
+ # With the following parameters:
23
+ #
24
+ # 1. Custom element type (or `:compound_field` otherwise)
25
+ # 2. Form element attributes
26
+ # 3. Child form elements
27
+ #
28
+ # @see Formalist::Element::Attributes#to_ast "Form element attributes" structure
29
+ #
30
+ # @example
31
+ # compound_field.to_ast
32
+ # # => [:compound_field, [
33
+ # :content,
34
+ # :compound_field,
35
+ # [:object, []],
36
+ # [...child elements...],
37
+ # ]]
38
+ #
39
+ # @return [Array] the compound field as an abstract syntax tree.
40
+ def to_ast
41
+ [:compound_field, [
42
+ type,
43
+ Element::Attributes.new(attributes).to_ast,
44
+ children.map(&:to_ast),
45
+ ]]
46
+ end
47
+ end
48
+ end
49
+ end