simple_form_with_client_validation 0.0.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.
Files changed (99) hide show
  1. data/CHANGELOG.md +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +783 -0
  4. data/lib/generators/simple_form_with_client_validation/USAGE +3 -0
  5. data/lib/generators/simple_form_with_client_validation/install_generator.rb +32 -0
  6. data/lib/generators/simple_form_with_client_validation/templates/README +12 -0
  7. data/lib/generators/simple_form_with_client_validation/templates/_form.html.erb +13 -0
  8. data/lib/generators/simple_form_with_client_validation/templates/_form.html.haml +10 -0
  9. data/lib/generators/simple_form_with_client_validation/templates/_form.html.slim +10 -0
  10. data/lib/generators/simple_form_with_client_validation/templates/config/initializers/simple_form.rb.tt +179 -0
  11. data/lib/generators/simple_form_with_client_validation/templates/config/locales/simple_form.en.yml +26 -0
  12. data/lib/simple_form_with_client_validation/action_view_extensions/builder.rb +331 -0
  13. data/lib/simple_form_with_client_validation/action_view_extensions/form_helper.rb +74 -0
  14. data/lib/simple_form_with_client_validation/components/errors.rb +35 -0
  15. data/lib/simple_form_with_client_validation/components/hints.rb +18 -0
  16. data/lib/simple_form_with_client_validation/components/html5.rb +26 -0
  17. data/lib/simple_form_with_client_validation/components/label_input.rb +15 -0
  18. data/lib/simple_form_with_client_validation/components/labels.rb +79 -0
  19. data/lib/simple_form_with_client_validation/components/maxlength.rb +41 -0
  20. data/lib/simple_form_with_client_validation/components/min_max.rb +50 -0
  21. data/lib/simple_form_with_client_validation/components/minlength.rb +41 -0
  22. data/lib/simple_form_with_client_validation/components/pattern.rb +34 -0
  23. data/lib/simple_form_with_client_validation/components/placeholders.rb +16 -0
  24. data/lib/simple_form_with_client_validation/components/readonly.rb +22 -0
  25. data/lib/simple_form_with_client_validation/components.rb +21 -0
  26. data/lib/simple_form_with_client_validation/core_ext/hash.rb +16 -0
  27. data/lib/simple_form_with_client_validation/error_notification.rb +48 -0
  28. data/lib/simple_form_with_client_validation/form_builder.rb +484 -0
  29. data/lib/simple_form_with_client_validation/helpers/autofocus.rb +11 -0
  30. data/lib/simple_form_with_client_validation/helpers/disabled.rb +15 -0
  31. data/lib/simple_form_with_client_validation/helpers/readonly.rb +15 -0
  32. data/lib/simple_form_with_client_validation/helpers/required.rb +35 -0
  33. data/lib/simple_form_with_client_validation/helpers/validators.rb +44 -0
  34. data/lib/simple_form_with_client_validation/helpers.rb +12 -0
  35. data/lib/simple_form_with_client_validation/i18n_cache.rb +22 -0
  36. data/lib/simple_form_with_client_validation/inputs/base.rb +162 -0
  37. data/lib/simple_form_with_client_validation/inputs/block_input.rb +14 -0
  38. data/lib/simple_form_with_client_validation/inputs/boolean_input.rb +64 -0
  39. data/lib/simple_form_with_client_validation/inputs/collection_check_boxes_input.rb +21 -0
  40. data/lib/simple_form_with_client_validation/inputs/collection_input.rb +101 -0
  41. data/lib/simple_form_with_client_validation/inputs/collection_radio_buttons_input.rb +63 -0
  42. data/lib/simple_form_with_client_validation/inputs/collection_select_input.rb +14 -0
  43. data/lib/simple_form_with_client_validation/inputs/date_time_input.rb +28 -0
  44. data/lib/simple_form_with_client_validation/inputs/file_input.rb +9 -0
  45. data/lib/simple_form_with_client_validation/inputs/grouped_collection_select_input.rb +41 -0
  46. data/lib/simple_form_with_client_validation/inputs/hidden_input.rb +17 -0
  47. data/lib/simple_form_with_client_validation/inputs/numeric_input.rb +24 -0
  48. data/lib/simple_form_with_client_validation/inputs/password_input.rb +12 -0
  49. data/lib/simple_form_with_client_validation/inputs/priority_input.rb +24 -0
  50. data/lib/simple_form_with_client_validation/inputs/range_input.rb +14 -0
  51. data/lib/simple_form_with_client_validation/inputs/string_input.rb +23 -0
  52. data/lib/simple_form_with_client_validation/inputs/text_input.rb +11 -0
  53. data/lib/simple_form_with_client_validation/inputs.rb +21 -0
  54. data/lib/simple_form_with_client_validation/map_type.rb +16 -0
  55. data/lib/simple_form_with_client_validation/version.rb +3 -0
  56. data/lib/simple_form_with_client_validation/wrappers/builder.rb +115 -0
  57. data/lib/simple_form_with_client_validation/wrappers/many.rb +78 -0
  58. data/lib/simple_form_with_client_validation/wrappers/root.rb +34 -0
  59. data/lib/simple_form_with_client_validation/wrappers/single.rb +18 -0
  60. data/lib/simple_form_with_client_validation/wrappers.rb +8 -0
  61. data/lib/simple_form_with_client_validation.rb +218 -0
  62. data/test/action_view_extensions/builder_test.rb +577 -0
  63. data/test/action_view_extensions/form_helper_test.rb +104 -0
  64. data/test/components/label_test.rb +310 -0
  65. data/test/form_builder/association_test.rb +177 -0
  66. data/test/form_builder/button_test.rb +47 -0
  67. data/test/form_builder/error_notification_test.rb +79 -0
  68. data/test/form_builder/error_test.rb +121 -0
  69. data/test/form_builder/general_test.rb +356 -0
  70. data/test/form_builder/hint_test.rb +139 -0
  71. data/test/form_builder/input_field_test.rb +63 -0
  72. data/test/form_builder/label_test.rb +71 -0
  73. data/test/form_builder/wrapper_test.rb +149 -0
  74. data/test/generators/simple_form_generator_test.rb +32 -0
  75. data/test/inputs/boolean_input_test.rb +108 -0
  76. data/test/inputs/collection_check_boxes_input_test.rb +224 -0
  77. data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
  78. data/test/inputs/collection_select_input_test.rb +241 -0
  79. data/test/inputs/datetime_input_test.rb +99 -0
  80. data/test/inputs/disabled_test.rb +38 -0
  81. data/test/inputs/discovery_test.rb +61 -0
  82. data/test/inputs/file_input_test.rb +16 -0
  83. data/test/inputs/general_test.rb +69 -0
  84. data/test/inputs/grouped_collection_select_input_test.rb +118 -0
  85. data/test/inputs/hidden_input_test.rb +30 -0
  86. data/test/inputs/numeric_input_test.rb +173 -0
  87. data/test/inputs/priority_input_test.rb +43 -0
  88. data/test/inputs/readonly_test.rb +61 -0
  89. data/test/inputs/required_test.rb +113 -0
  90. data/test/inputs/string_input_test.rb +140 -0
  91. data/test/inputs/text_input_test.rb +29 -0
  92. data/test/simple_form_test.rb +9 -0
  93. data/test/support/discovery_inputs.rb +21 -0
  94. data/test/support/misc_helpers.rb +102 -0
  95. data/test/support/mock_controller.rb +24 -0
  96. data/test/support/mock_response.rb +14 -0
  97. data/test/support/models.rb +210 -0
  98. data/test/test_helper.rb +95 -0
  99. metadata +227 -0
@@ -0,0 +1,63 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class CollectionRadioButtonsInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+
7
+ @builder.send("collection_#{input_type}",
8
+ attribute_name, collection, value_method, label_method,
9
+ input_options, input_html_options, &collection_block_for_nested_boolean_style
10
+ )
11
+ end
12
+
13
+ def input_options
14
+ options = super
15
+ apply_default_collection_options!(options)
16
+ apply_nested_boolean_collection_options!(options) if nested_boolean_style?
17
+ options
18
+ end
19
+
20
+ protected
21
+
22
+ def apply_default_collection_options!(options)
23
+ options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleFormWithClientValidation.item_wrapper_tag)
24
+ options[:item_wrapper_class] = [
25
+ item_wrapper_class, options[:item_wrapper_class], SimpleFormWithClientValidation.item_wrapper_class
26
+ ].compact.presence
27
+
28
+ options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleFormWithClientValidation.collection_wrapper_tag)
29
+ options[:collection_wrapper_class] = [
30
+ options[:collection_wrapper_class], SimpleFormWithClientValidation.collection_wrapper_class
31
+ ].compact.presence
32
+ end
33
+
34
+ # Force item wrapper to be a label when using nested boolean, to support
35
+ # configuring classes through :item_wrapper_class, and to maintain
36
+ # compatibility with :inline style and default :item_wrapper_tag.
37
+ def apply_nested_boolean_collection_options!(options)
38
+ options[:item_wrapper_tag] = :label
39
+ end
40
+
41
+ def collection_block_for_nested_boolean_style
42
+ return unless nested_boolean_style?
43
+
44
+ proc { |builder| build_nested_boolean_style_item_tag(builder) }
45
+ end
46
+
47
+ def build_nested_boolean_style_item_tag(collection_builder)
48
+ collection_builder.radio_button + collection_builder.text
49
+ end
50
+
51
+ def item_wrapper_class
52
+ "radio"
53
+ end
54
+
55
+ # Do not attempt to generate label[for] attributes by default, unless an
56
+ # explicit html option is given. This avoids generating labels pointing to
57
+ # non existent fields.
58
+ def generate_label_for_attribute?
59
+ false
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class CollectionSelectInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+
7
+ @builder.collection_select(
8
+ attribute_name, collection, value_method, label_method,
9
+ input_options, input_html_options
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class DateTimeInput < Base
4
+ def input
5
+ @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
6
+ end
7
+
8
+ def has_required?
9
+ false
10
+ end
11
+
12
+ private
13
+
14
+ def label_target
15
+ position = case input_type
16
+ when :date, :datetime
17
+ date_order = input_options[:order] || I18n.t('date.order')
18
+ date_order.first
19
+ else
20
+ :hour
21
+ end
22
+
23
+ position = ActionView::Helpers::DateTimeSelector::POSITION[position]
24
+ "#{attribute_name}_#{position}i"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class FileInput < Base
4
+ def input
5
+ @builder.file_field(attribute_name, input_html_options)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class GroupedCollectionSelectInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+ @builder.grouped_collection_select(attribute_name, grouped_collection,
7
+ group_method, group_label_method, value_method, label_method,
8
+ input_options, input_html_options)
9
+ end
10
+
11
+ private
12
+
13
+ def grouped_collection
14
+ @grouped_collection ||= begin
15
+ grouped_collection = options.delete(:collection)
16
+ grouped_collection.respond_to?(:call) ? grouped_collection.call : grouped_collection.to_a
17
+ end
18
+ end
19
+
20
+ # Sample collection
21
+ def collection
22
+ @collection ||= grouped_collection.first.try(:send, group_method) || []
23
+ end
24
+
25
+ def group_method
26
+ @group_method ||= options.delete(:group_method)
27
+ end
28
+
29
+ def group_label_method
30
+ label = options.delete(:group_label_method)
31
+
32
+ unless label
33
+ common_method_for = detect_common_display_methods(detect_collection_classes(grouped_collection))
34
+ label = common_method_for[:label]
35
+ end
36
+
37
+ label
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,17 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class HiddenInput < Base
4
+ disable :label, :errors, :hint, :required
5
+
6
+ def input
7
+ @builder.hidden_field(attribute_name, input_html_options)
8
+ end
9
+
10
+ private
11
+
12
+ def required_class
13
+ nil
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class NumericInput < Base
4
+ enable :placeholder, :min_max
5
+
6
+ def input
7
+ add_size!
8
+ input_html_classes.unshift("numeric")
9
+ if html5?
10
+ input_html_options[:type] ||= "number"
11
+ input_html_options[:step] ||= integer? ? 1 : "any"
12
+ end
13
+ @builder.text_field(attribute_name, input_html_options)
14
+ end
15
+
16
+ private
17
+
18
+ # Rails adds the size attr by default, if the :size key does not exist.
19
+ def add_size!
20
+ input_html_options[:size] ||= nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class PasswordInput < Base
4
+ enable :placeholder, :maxlength
5
+
6
+ def input
7
+ add_size!
8
+ @builder.password_field(attribute_name, input_html_options)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class PriorityInput < CollectionSelectInput
4
+ def input
5
+ @builder.send(:"#{input_type}_select", attribute_name, input_priority,
6
+ input_options, input_html_options)
7
+ end
8
+
9
+ def input_priority
10
+ options[:priority] || SimpleFormWithClientValidation.send(:"#{input_type}_priority")
11
+ end
12
+
13
+ protected
14
+
15
+ def has_required?
16
+ false
17
+ end
18
+
19
+ def skip_include_blank?
20
+ super || input_priority.present?
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class RangeInput < NumericInput
4
+ def input
5
+ if html5?
6
+ input_html_options[:type] ||= "range"
7
+ input_html_options[:step] ||= 1
8
+ end
9
+
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class StringInput < Base
4
+ enable :placeholder, :maxlength, :pattern
5
+
6
+ def input
7
+ unless string?
8
+ input_html_classes.unshift("string")
9
+ input_html_options[:type] ||= input_type if html5?
10
+ end
11
+
12
+ add_size!
13
+ @builder.text_field(attribute_name, input_html_options)
14
+ end
15
+
16
+ private
17
+
18
+ def string?
19
+ input_type == :string
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ class TextInput < Base
4
+ enable :placeholder, :maxlength
5
+
6
+ def input
7
+ @builder.text_area(attribute_name, input_html_options)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ module SimpleFormWithClientValidation
2
+ module Inputs
3
+ autoload :Base, 'simple_form_with_client_validation/inputs/base'
4
+ autoload :BlockInput, 'simple_form_with_client_validation/inputs/block_input'
5
+ autoload :BooleanInput, 'simple_form_with_client_validation/inputs/boolean_input'
6
+ autoload :CollectionCheckBoxesInput, 'simple_form_with_client_validation/inputs/collection_check_boxes_input'
7
+ autoload :CollectionInput, 'simple_form_with_client_validation/inputs/collection_input'
8
+ autoload :CollectionRadioButtonsInput, 'simple_form_with_client_validation/inputs/collection_radio_buttons_input'
9
+ autoload :CollectionSelectInput, 'simple_form_with_client_validation/inputs/collection_select_input'
10
+ autoload :DateTimeInput, 'simple_form_with_client_validation/inputs/date_time_input'
11
+ autoload :FileInput, 'simple_form_with_client_validation/inputs/file_input'
12
+ autoload :GroupedCollectionSelectInput, 'simple_form_with_client_validation/inputs/grouped_collection_select_input'
13
+ autoload :HiddenInput, 'simple_form_with_client_validation/inputs/hidden_input'
14
+ autoload :NumericInput, 'simple_form_with_client_validation/inputs/numeric_input'
15
+ autoload :PasswordInput, 'simple_form_with_client_validation/inputs/password_input'
16
+ autoload :PriorityInput, 'simple_form_with_client_validation/inputs/priority_input'
17
+ autoload :RangeInput, 'simple_form_with_client_validation/inputs/range_input'
18
+ autoload :StringInput, 'simple_form_with_client_validation/inputs/string_input'
19
+ autoload :TextInput, 'simple_form_with_client_validation/inputs/text_input'
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
3
+ module SimpleFormWithClientValidation
4
+ module MapType
5
+ def self.extended(base)
6
+ base.class_attribute :mappings
7
+ base.mappings = {}
8
+ end
9
+
10
+ def map_type(*types)
11
+ map_to = types.extract_options![:to]
12
+ raise ArgumentError, "You need to give :to as option to map_type" unless map_to
13
+ self.mappings = mappings.merge types.each_with_object({}) { |t, m| m[t] = map_to }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleFormWithClientValidation
2
+ VERSION = "0.0.0".freeze
3
+ end
@@ -0,0 +1,115 @@
1
+ module SimpleFormWithClientValidation
2
+ module Wrappers
3
+ # Provides the builder syntax for components. The builder provides
4
+ # three methods `use`, `optional` and `wrapper` and they allow the following invocations:
5
+ #
6
+ # config.wrappers do |b|
7
+ # # Use a single component
8
+ # b.use :html5
9
+ #
10
+ # # Use the component, but do not automatically lookup. It will only be triggered when
11
+ # # :placeholder is explicitly set.
12
+ # b.optional :placeholder
13
+ #
14
+ # # Use a component with specific wrapper options
15
+ # b.use :error, :wrap_with => { :tag => "span", :class => "error" }
16
+ #
17
+ # # Use a set of components by wrapping them in a tag+class.
18
+ # b.wrapper :tag => "div", :class => "another" do |ba|
19
+ # ba.use :label
20
+ # ba.use :input
21
+ # end
22
+ #
23
+ # # Use a set of components by wrapping them in a tag+class.
24
+ # # This wrapper is identified by :label_input, which means it can
25
+ # # be turned off on demand with `f.input :name, :label_input => false`
26
+ # b.wrapper :label_input, :tag => "div", :class => "another" do |ba|
27
+ # ba.use :label
28
+ # ba.use :input
29
+ # end
30
+ # end
31
+ #
32
+ # The builder also accepts default options at the root level. This is usually
33
+ # used if you want a component to be disabled by default:
34
+ #
35
+ # config.wrappers :hint => false do |b|
36
+ # b.use :hint
37
+ # b.use :label_input
38
+ # end
39
+ #
40
+ # In the example above, hint defaults to false, which means it won't automatically
41
+ # do the lookup anymore. It will only be triggered when :hint is explicitly set.
42
+ class Builder
43
+ def initialize(options)
44
+ @options = options
45
+ @components = []
46
+ end
47
+
48
+ #this triggers the use of this option
49
+ def use(name, options=nil, &block)
50
+ if block_given?
51
+ ActiveSupport::Deprecation.warn "Passing a block to use is deprecated. " \
52
+ "Please use wrapper instead of use."
53
+ return wrapper(name, options, &block)
54
+ end
55
+
56
+ if options && options.keys != [:wrap_with]
57
+ ActiveSupport::Deprecation.warn "Passing :tag, :class and others to use is deprecated. " \
58
+ "Please invoke b.use #{name.inspect}, :wrap_with => #{options.inspect} instead."
59
+ options = { :wrap_with => options }
60
+ end
61
+
62
+ if options && wrapper = options[:wrap_with]
63
+ @components << Single.new(name, wrapper)
64
+ else
65
+ @components << name
66
+ end
67
+ end
68
+
69
+ def optional(name, options=nil, &block)
70
+ if block_given?
71
+ ActiveSupport::Deprecation.warn "Passing a block to optional is deprecated. " \
72
+ "Please use wrapper instead of optional."
73
+ return wrapper(name, options, &block)
74
+ end
75
+
76
+ if options && options.keys != [:wrap_with]
77
+ ActiveSupport::Deprecation.warn "Passing :tag, :class and others to optional is deprecated. " \
78
+ "Please invoke b.optional #{name.inspect}, :wrap_with => #{options.inspect} instead."
79
+ options = { :wrap_with => options }
80
+ end
81
+
82
+ @options[name] = false
83
+ use(name, options, &block)
84
+ end
85
+
86
+ def wrapper(name, options=nil)
87
+ if block_given?
88
+ #setting name to nil if only a hash passed in
89
+ name, options = nil, name if name.is_a?(Hash)
90
+
91
+ #create a builder based on current options of parent
92
+ builder = self.class.new(@options)
93
+
94
+
95
+ options ||= {}
96
+ #use div if nothing else supplied
97
+ options[:tag] = :div if options[:tag].nil?
98
+
99
+ #now run the block setup stuff (use, optional, and wrappers)
100
+ yield builder
101
+
102
+ #turn this builder into a Many object (wrapper for multiple @components) and
103
+ #add to parent @components
104
+ @components << Many.new(name, builder.to_a, options)
105
+ else
106
+ raise ArgumentError, "A block is required as argument to wrapper"
107
+ end
108
+ end
109
+
110
+ def to_a
111
+ @components
112
+ end
113
+ end
114
+ end
115
+ end