action_form 0.4.0 → 0.5.0
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 +4 -4
- data/README.md +307 -0
- data/lib/action_form/base.rb +9 -4
- data/lib/action_form/composition.rb +42 -0
- data/lib/action_form/element.rb +4 -1
- data/lib/action_form/subform.rb +4 -1
- data/lib/action_form/subforms_collection.rb +1 -0
- data/lib/action_form/version.rb +1 -1
- data/lib/action_form.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0211bd706504ef28c1045cc72f33a2408b554ca0a2b3e922436491a1865a16b9
|
|
4
|
+
data.tar.gz: 87c0249ed1b139d6fea7d26fb9e9d2e220cafa3a045b129530e94b9afb8ef545
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 817ea2221b6780ed40c769fd37416fae7e2051256b7084b4d4cb612c60fac7288a4dad38316a9af0eba543bf712e07afce8185f33e1d3768e7172cfd81de8103
|
|
7
|
+
data.tar.gz: 32a63869d603e1117ac6bacaf4cd526c7127ba23a703343f537507e31e5739ef9bdce5d6701529413f23f0cd9e8794c3d01b5a4d2954070051eecdcd10a7d881
|
data/README.md
CHANGED
|
@@ -621,6 +621,313 @@ expect(params).to be_invalid
|
|
|
621
621
|
|
|
622
622
|
This feature provides a powerful way to customize and extend form definitions while maintaining the declarative nature of ActionForm.
|
|
623
623
|
|
|
624
|
+
### Composition and Owner Delegation
|
|
625
|
+
|
|
626
|
+
ActionForm includes a composition system that allows form components (elements, subforms, and `many` collections) to access methods from their owner (typically the parent form or a custom host object). This promotes code reuse and reduces redundancy by allowing shared logic to be defined in a central location and accessed by multiple form components.
|
|
627
|
+
|
|
628
|
+
#### **How Composition Works**
|
|
629
|
+
|
|
630
|
+
When you create a form, all nested elements and subforms automatically have access to their owner through the `owner` accessor. Methods called on elements or subforms that are not defined locally are automatically delegated up the ownership chain, allowing you to access methods from the parent form or a custom host object.
|
|
631
|
+
|
|
632
|
+
#### **Automatic Ownership Chain**
|
|
633
|
+
|
|
634
|
+
Ownership is automatically established when forms are built:
|
|
635
|
+
|
|
636
|
+
```ruby
|
|
637
|
+
class ProductForm < ActionForm::Base
|
|
638
|
+
element :name do
|
|
639
|
+
input(type: :text)
|
|
640
|
+
output(type: :string)
|
|
641
|
+
|
|
642
|
+
# This method can call methods on the owner (ProductForm)
|
|
643
|
+
def render?
|
|
644
|
+
name_render? # Delegates to owner.name_render?
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
many :variants, default: [{}] do
|
|
649
|
+
subform do
|
|
650
|
+
element :name do
|
|
651
|
+
input(type: :text)
|
|
652
|
+
output(type: :string)
|
|
653
|
+
|
|
654
|
+
def render?
|
|
655
|
+
variants_name_render? # Delegates to owner.variants_name_render?
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def render?
|
|
660
|
+
variants_subform_render? # Delegates to owner.variants_subform_render?
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
subform :manufacturer, default: {} do
|
|
666
|
+
element :name do
|
|
667
|
+
input(type: :text)
|
|
668
|
+
output(type: :string)
|
|
669
|
+
|
|
670
|
+
def render?
|
|
671
|
+
manufacturer_name_render? # Delegates to owner.manufacturer_name_render?
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
# Methods accessible by nested components
|
|
677
|
+
def name_render?
|
|
678
|
+
true
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
def variants_name_render?
|
|
682
|
+
true
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
def variants_subform_render?
|
|
686
|
+
true
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def manufacturer_name_render?
|
|
690
|
+
true
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
#### **Custom Host Object**
|
|
696
|
+
|
|
697
|
+
You can pass a custom host object when initializing a form, allowing you to separate form logic from business logic:
|
|
698
|
+
|
|
699
|
+
```ruby
|
|
700
|
+
class HostObject
|
|
701
|
+
def name_render?
|
|
702
|
+
current_user.admin? || form_context == :edit
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
def variants_subform_render?
|
|
706
|
+
feature_enabled?(:variants)
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def variants_name_render?
|
|
710
|
+
true
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def variants_price_render?
|
|
714
|
+
pricing_enabled?
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def manufacturer_name_render?
|
|
718
|
+
manufacturer_feature_enabled?
|
|
719
|
+
end
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
class ProductForm < ActionForm::Base
|
|
723
|
+
element :name do
|
|
724
|
+
input(type: :text)
|
|
725
|
+
output(type: :string)
|
|
726
|
+
|
|
727
|
+
def render?
|
|
728
|
+
name_render? # Calls HostObject#name_render?
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
many :variants, default: [{}] do
|
|
733
|
+
subform do
|
|
734
|
+
element :name do
|
|
735
|
+
input(type: :text)
|
|
736
|
+
output(type: :string)
|
|
737
|
+
|
|
738
|
+
def render?
|
|
739
|
+
variants_name_render? # Calls HostObject#variants_name_render?
|
|
740
|
+
end
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
element :price do
|
|
744
|
+
input(type: :number)
|
|
745
|
+
output(type: :float)
|
|
746
|
+
|
|
747
|
+
def render?
|
|
748
|
+
variants_price_render? # Calls HostObject#variants_price_render?
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
def render?
|
|
753
|
+
variants_subform_render? # Calls HostObject#variants_subform_render?
|
|
754
|
+
end
|
|
755
|
+
end
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
subform :manufacturer, default: {} do
|
|
759
|
+
element :name do
|
|
760
|
+
input(type: :text)
|
|
761
|
+
output(type: :string)
|
|
762
|
+
|
|
763
|
+
def render?
|
|
764
|
+
manufacturer_name_render? # Calls HostObject#manufacturer_name_render?
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
# Use the form with a custom host object
|
|
771
|
+
host = HostObject.new
|
|
772
|
+
product = Product.new(name: "Product 1")
|
|
773
|
+
form = ProductForm.new(object: product, owner: host)
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
#### **Ownership Chain Traversal**
|
|
777
|
+
|
|
778
|
+
The composition system supports multi-level ownership chains. When a method is called on an element or subform, it searches up the ownership chain until it finds the method:
|
|
779
|
+
|
|
780
|
+
```ruby
|
|
781
|
+
class GrandparentForm < ActionForm::Base
|
|
782
|
+
def shared_helper
|
|
783
|
+
"grandparent"
|
|
784
|
+
end
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
class ParentForm < ActionForm::Base
|
|
788
|
+
def shared_helper
|
|
789
|
+
"parent"
|
|
790
|
+
end
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
class ChildForm < ActionForm::Base
|
|
794
|
+
element :field do
|
|
795
|
+
input(type: :text)
|
|
796
|
+
|
|
797
|
+
def render?
|
|
798
|
+
shared_helper # Will find ParentForm#shared_helper first
|
|
799
|
+
end
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
# If ChildForm has ParentForm as owner, and ParentForm has GrandparentForm as owner:
|
|
804
|
+
# The method lookup order is: ChildForm -> ParentForm -> GrandparentForm
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
#### **Accessing Owner Directly**
|
|
808
|
+
|
|
809
|
+
You can access the owner directly using the `owner` accessor:
|
|
810
|
+
|
|
811
|
+
```ruby
|
|
812
|
+
class ProductForm < ActionForm::Base
|
|
813
|
+
element :discount_code do
|
|
814
|
+
input(type: :text)
|
|
815
|
+
output(type: :string)
|
|
816
|
+
|
|
817
|
+
def disabled?
|
|
818
|
+
# Access owner's methods directly
|
|
819
|
+
owner.current_user && !owner.current_user.admin?
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def placeholder
|
|
823
|
+
owner.discount_placeholder_text
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
def current_user
|
|
828
|
+
@current_user ||= User.find(session[:user_id])
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
def discount_placeholder_text
|
|
832
|
+
"Enter discount code"
|
|
833
|
+
end
|
|
834
|
+
end
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
#### **Ownership Hierarchy**
|
|
838
|
+
|
|
839
|
+
The ownership hierarchy is automatically established as follows:
|
|
840
|
+
|
|
841
|
+
- **Top-level form**: Can have a custom `owner` passed during initialization
|
|
842
|
+
- **Elements**: Owner is the form or subform that contains them
|
|
843
|
+
- **Subforms**: Owner is the form that contains them
|
|
844
|
+
- **SubformsCollection (many)**: Owner is the form that contains them
|
|
845
|
+
- **Nested elements in subforms**: Owner is the subform that contains them
|
|
846
|
+
- **Nested subforms**: Owner is the parent form or subform
|
|
847
|
+
|
|
848
|
+
#### **Practical Use Cases**
|
|
849
|
+
|
|
850
|
+
**Conditional Rendering Based on Context:**
|
|
851
|
+
```ruby
|
|
852
|
+
class OrderForm < ActionForm::Base
|
|
853
|
+
element :admin_notes do
|
|
854
|
+
input(type: :textarea)
|
|
855
|
+
output(type: :string)
|
|
856
|
+
|
|
857
|
+
def render?
|
|
858
|
+
owner.current_user&.admin?
|
|
859
|
+
end
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
def current_user
|
|
863
|
+
@current_user
|
|
864
|
+
end
|
|
865
|
+
end
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
**Shared Validation Logic:**
|
|
869
|
+
```ruby
|
|
870
|
+
class RegistrationForm < ActionForm::Base
|
|
871
|
+
element :email do
|
|
872
|
+
input(type: :email)
|
|
873
|
+
output(type: :string)
|
|
874
|
+
|
|
875
|
+
def disabled?
|
|
876
|
+
owner.email_locked?
|
|
877
|
+
end
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
element :email_confirmation do
|
|
881
|
+
input(type: :email)
|
|
882
|
+
output(type: :string)
|
|
883
|
+
|
|
884
|
+
def disabled?
|
|
885
|
+
owner.email_locked?
|
|
886
|
+
end
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
def email_locked?
|
|
890
|
+
@user.persisted? && @user.email_verified?
|
|
891
|
+
end
|
|
892
|
+
end
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
**Feature Flags:**
|
|
896
|
+
```ruby
|
|
897
|
+
class SettingsForm < ActionForm::Base
|
|
898
|
+
many :advanced_settings do
|
|
899
|
+
subform do
|
|
900
|
+
element :feature_flag do
|
|
901
|
+
input(type: :checkbox)
|
|
902
|
+
output(type: :bool)
|
|
903
|
+
|
|
904
|
+
def render?
|
|
905
|
+
owner.feature_enabled?(:advanced_settings)
|
|
906
|
+
end
|
|
907
|
+
end
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
def render?
|
|
911
|
+
owner.feature_enabled?(:advanced_settings)
|
|
912
|
+
end
|
|
913
|
+
end
|
|
914
|
+
|
|
915
|
+
def feature_enabled?(feature)
|
|
916
|
+
FeatureFlags.enabled?(feature, current_user)
|
|
917
|
+
end
|
|
918
|
+
end
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
#### **Benefits**
|
|
922
|
+
|
|
923
|
+
- **Code Reuse**: Share common logic across multiple form components
|
|
924
|
+
- **Separation of Concerns**: Keep business logic in host objects, form logic in forms
|
|
925
|
+
- **Flexibility**: Support conditional rendering and behavior based on context
|
|
926
|
+
- **Maintainability**: Centralize shared logic instead of duplicating it
|
|
927
|
+
- **Testability**: Test host objects separately from form definitions
|
|
928
|
+
|
|
929
|
+
The composition system provides a powerful mechanism for creating flexible, maintainable forms that can adapt to different contexts and requirements.
|
|
930
|
+
|
|
624
931
|
### Tagging system
|
|
625
932
|
|
|
626
933
|
ActionForm includes a flexible tagging system that allows you to add custom metadata to form elements and control rendering behavior. Tags serve multiple purposes:
|
data/lib/action_form/base.rb
CHANGED
|
@@ -7,6 +7,7 @@ module ActionForm
|
|
|
7
7
|
include ActionForm::SchemaDSL
|
|
8
8
|
include ActionForm::ElementsDSL
|
|
9
9
|
include ActionForm::Rendering
|
|
10
|
+
include ActionForm::Composition
|
|
10
11
|
|
|
11
12
|
attr_reader :elements_instances, :scope, :object, :html_options, :errors
|
|
12
13
|
|
|
@@ -30,13 +31,14 @@ module ActionForm
|
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
def initialize(object: nil, scope: self.class.scope, params: nil, **html_options)
|
|
34
|
+
def initialize(object: nil, scope: self.class.scope, params: nil, owner: nil, **html_options)
|
|
34
35
|
super()
|
|
35
36
|
@object = object
|
|
36
37
|
@scope ||= scope
|
|
37
38
|
@params = @scope && params.respond_to?(@scope) ? params.public_send(@scope) : params
|
|
38
39
|
@html_options = html_options
|
|
39
40
|
@elements_instances = []
|
|
41
|
+
@owner = owner
|
|
40
42
|
build_from_object
|
|
41
43
|
end
|
|
42
44
|
|
|
@@ -48,7 +50,7 @@ module ActionForm
|
|
|
48
50
|
elsif element_definition < ActionForm::Subform
|
|
49
51
|
@elements_instances << build_subform(name, element_definition)
|
|
50
52
|
elsif element_definition < ActionForm::Element
|
|
51
|
-
@elements_instances << element_definition.new(name, @params || @object, parent_name: @scope)
|
|
53
|
+
@elements_instances << element_definition.new(name, @params || @object, parent_name: @scope, owner: self)
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
end
|
|
@@ -73,6 +75,7 @@ module ActionForm
|
|
|
73
75
|
|
|
74
76
|
def build_many_subforms(name, collection_definition)
|
|
75
77
|
collection = collection_definition.new(name)
|
|
78
|
+
collection.owner = self
|
|
76
79
|
Array(subform_value(name)).each.with_index do |item, index|
|
|
77
80
|
collection << build_subform(name, collection_definition.subform_definition, value: item, index: index)
|
|
78
81
|
end
|
|
@@ -93,7 +96,8 @@ module ActionForm
|
|
|
93
96
|
|
|
94
97
|
def build_subform(name, form_definition, value: subform_value(name), index: nil)
|
|
95
98
|
html_name = subform_html_name(name, index: index)
|
|
96
|
-
form_definition.new(name: name, scope: html_name, model: value, index: index
|
|
99
|
+
form_definition.new(name: name, scope: html_name, model: value, index: index,
|
|
100
|
+
owner: self).tap do |subform|
|
|
97
101
|
subform.helpers = helpers
|
|
98
102
|
end
|
|
99
103
|
end
|
|
@@ -103,7 +107,8 @@ module ActionForm
|
|
|
103
107
|
elements_keys = form_definition.elements.keys.push(:persisted?)
|
|
104
108
|
values = form_definition.elements.values.map(&:default)
|
|
105
109
|
value = Struct.new(*elements_keys).new(*values)
|
|
106
|
-
form_definition.new(name: name, scope: html_name, model: value, template: true
|
|
110
|
+
form_definition.new(name: name, scope: html_name, model: value, template: true,
|
|
111
|
+
owner: self).tap do |subform|
|
|
107
112
|
subform.helpers = helpers
|
|
108
113
|
end
|
|
109
114
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionForm
|
|
4
|
+
# Provides host–guest association helpers for ActionForm components.
|
|
5
|
+
# When included, it exposes `host_object` and `host_association_name` accessors
|
|
6
|
+
# and delegates `host_*` method calls to the associated host via `method_missing`.
|
|
7
|
+
module Composition
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.attr_accessor :owner
|
|
10
|
+
base.include(InstanceMethods)
|
|
11
|
+
base.extend(ClassMethods)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods # rubocop:disable Style/Documentation
|
|
15
|
+
def part_of(owner_name)
|
|
16
|
+
@owner_name = owner_name
|
|
17
|
+
alias_method owner_name, :owner
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module InstanceMethods # rubocop:disable Style/Documentation
|
|
22
|
+
def method_missing(name, *attrs, **kwargs, &block)
|
|
23
|
+
if (handler = owners_chain.lazy.detect { |o| o.public_methods.include?(name) })
|
|
24
|
+
handler.public_send(name, *attrs, **kwargs, &block)
|
|
25
|
+
else
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def respond_to_missing?(method_name, _include_private = false)
|
|
31
|
+
public_methods.detect { |m| m == :owner } || super
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def owners_chain
|
|
35
|
+
obj = self
|
|
36
|
+
Enumerator.new do |y|
|
|
37
|
+
y << obj = obj.owner while obj.public_methods.include?(:owner)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/action_form/element.rb
CHANGED
|
@@ -4,10 +4,12 @@ module ActionForm
|
|
|
4
4
|
# Represents a form element with input/output configuration and HTML attributes
|
|
5
5
|
# rubocop:disable Metrics/ClassLength
|
|
6
6
|
class Element
|
|
7
|
+
include ActionForm::Composition
|
|
8
|
+
|
|
7
9
|
attr_reader :name, :input_options, :output_options, :html_name, :html_id, :select_options, :tags, :errors_messages
|
|
8
10
|
attr_accessor :helpers
|
|
9
11
|
|
|
10
|
-
def initialize(name, object, parent_name: nil)
|
|
12
|
+
def initialize(name, object, parent_name: nil, owner: nil)
|
|
11
13
|
@name = name
|
|
12
14
|
@object = object
|
|
13
15
|
@html_name = build_html_name(name, parent_name)
|
|
@@ -15,6 +17,7 @@ module ActionForm
|
|
|
15
17
|
@tags = self.class.tags_list.dup
|
|
16
18
|
@errors_messages = extract_errors_messages(object, name)
|
|
17
19
|
tags.merge!(errors: errors_messages.any?)
|
|
20
|
+
@owner = owner
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
class << self
|
data/lib/action_form/subform.rb
CHANGED
|
@@ -8,6 +8,7 @@ module ActionForm
|
|
|
8
8
|
include ActionForm::Rendering
|
|
9
9
|
include ActionForm::SchemaDSL
|
|
10
10
|
include ActionForm::ElementsDSL
|
|
11
|
+
include ActionForm::Composition
|
|
11
12
|
|
|
12
13
|
class << self
|
|
13
14
|
attr_accessor :default
|
|
@@ -23,7 +24,7 @@ module ActionForm
|
|
|
23
24
|
attr_reader :elements_instances, :tags, :name, :object
|
|
24
25
|
attr_accessor :helpers
|
|
25
26
|
|
|
26
|
-
def initialize(name:, scope: nil, model: nil, params: nil, **tags)
|
|
27
|
+
def initialize(name:, scope: nil, model: nil, params: nil, owner: nil, **tags) # rubocop:disable Metrics/ParameterLists
|
|
27
28
|
super()
|
|
28
29
|
@name = name
|
|
29
30
|
@scope = scope
|
|
@@ -31,6 +32,7 @@ module ActionForm
|
|
|
31
32
|
@params = params
|
|
32
33
|
@elements_instances = []
|
|
33
34
|
@tags = tags
|
|
35
|
+
@owner = owner
|
|
34
36
|
build_from_object
|
|
35
37
|
end
|
|
36
38
|
|
|
@@ -38,6 +40,7 @@ module ActionForm
|
|
|
38
40
|
self.class.elements.each do |element_name, element_definition|
|
|
39
41
|
@elements_instances << element_definition.new(element_name, @params || @object, parent_name: @scope)
|
|
40
42
|
@elements_instances.last.tags.merge!(subform: @name)
|
|
43
|
+
@elements_instances.last.owner = self
|
|
41
44
|
end
|
|
42
45
|
end
|
|
43
46
|
|
data/lib/action_form/version.rb
CHANGED
data/lib/action_form.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "phlex"
|
|
|
4
4
|
require "easy_params"
|
|
5
5
|
require "forwardable"
|
|
6
6
|
require_relative "action_form/version"
|
|
7
|
+
require_relative "action_form/composition"
|
|
7
8
|
require_relative "action_form/schema_dsl"
|
|
8
9
|
require_relative "action_form/elements_dsl"
|
|
9
10
|
require_relative "action_form/input"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: action_form
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrii Baran
|
|
@@ -67,6 +67,7 @@ files:
|
|
|
67
67
|
- Rakefile
|
|
68
68
|
- lib/action_form.rb
|
|
69
69
|
- lib/action_form/base.rb
|
|
70
|
+
- lib/action_form/composition.rb
|
|
70
71
|
- lib/action_form/element.rb
|
|
71
72
|
- lib/action_form/elements_dsl.rb
|
|
72
73
|
- lib/action_form/input.rb
|