case_form 0.0.3

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 (88) hide show
  1. data/CHANGELOG.rdoc +1 -0
  2. data/MIT-LICENSE.rdoc +20 -0
  3. data/Manifest +86 -0
  4. data/README.rdoc +0 -0
  5. data/Rakefile +30 -0
  6. data/TODO.rdoc +7 -0
  7. data/case_form.gemspec +31 -0
  8. data/lib/case_form.rb +88 -0
  9. data/lib/case_form/associations.rb +50 -0
  10. data/lib/case_form/buttons.rb +175 -0
  11. data/lib/case_form/core_ext/form_helper.rb +54 -0
  12. data/lib/case_form/core_ext/layout_helper.rb +16 -0
  13. data/lib/case_form/core_ext/sentence_error.rb +38 -0
  14. data/lib/case_form/element.rb +40 -0
  15. data/lib/case_form/element/base.rb +95 -0
  16. data/lib/case_form/element/button.rb +64 -0
  17. data/lib/case_form/element/error.rb +54 -0
  18. data/lib/case_form/element/errors/complex_error.rb +107 -0
  19. data/lib/case_form/element/errors/simple_error.rb +76 -0
  20. data/lib/case_form/element/fieldset.rb +35 -0
  21. data/lib/case_form/element/hint.rb +54 -0
  22. data/lib/case_form/element/input.rb +106 -0
  23. data/lib/case_form/element/inputs/collection/checkbox_input.rb +36 -0
  24. data/lib/case_form/element/inputs/collection/radio_input.rb +27 -0
  25. data/lib/case_form/element/inputs/collection/select_input.rb +22 -0
  26. data/lib/case_form/element/inputs/collection_input.rb +89 -0
  27. data/lib/case_form/element/inputs/datetime/date_input.rb +45 -0
  28. data/lib/case_form/element/inputs/datetime/date_time_input.rb +50 -0
  29. data/lib/case_form/element/inputs/datetime/time_input.rb +34 -0
  30. data/lib/case_form/element/inputs/datetime/time_zone_input.rb +24 -0
  31. data/lib/case_form/element/inputs/file_input.rb +13 -0
  32. data/lib/case_form/element/inputs/hidden_input.rb +17 -0
  33. data/lib/case_form/element/inputs/number_input.rb +42 -0
  34. data/lib/case_form/element/inputs/search_input.rb +32 -0
  35. data/lib/case_form/element/inputs/string_input.rb +18 -0
  36. data/lib/case_form/element/inputs/text_input.rb +19 -0
  37. data/lib/case_form/element/label.rb +52 -0
  38. data/lib/case_form/element/nested_model.rb +105 -0
  39. data/lib/case_form/element/nested_models/handle.rb +18 -0
  40. data/lib/case_form/element/nested_models/handles/destructor_handle.rb +47 -0
  41. data/lib/case_form/element/nested_models/handles/generator_handle.rb +55 -0
  42. data/lib/case_form/element_ext/associationable.rb +54 -0
  43. data/lib/case_form/element_ext/columnable.rb +21 -0
  44. data/lib/case_form/element_ext/naming.rb +17 -0
  45. data/lib/case_form/element_ext/validationable.rb +13 -0
  46. data/lib/case_form/errors.rb +189 -0
  47. data/lib/case_form/form_builder.rb +11 -0
  48. data/lib/case_form/inputs.rb +1095 -0
  49. data/lib/case_form/labels.rb +102 -0
  50. data/lib/case_form/version.rb +6 -0
  51. data/lib/generators/case_form/install_generator.rb +33 -0
  52. data/lib/generators/case_form/templates/case_form.rb +63 -0
  53. data/lib/generators/case_form/templates/javascripts/jquery.case_form.js +10 -0
  54. data/lib/generators/case_form/templates/javascripts/prototype.case_form.js +0 -0
  55. data/lib/generators/case_form/templates/locales/en.yml +28 -0
  56. data/lib/generators/case_form/templates/locales/pl.yml +28 -0
  57. data/lib/generators/case_form/templates/stylesheets/stylesheet.css +93 -0
  58. data/lib/generators/case_form/templates/stylesheets/stylesheet_changes.css +1 -0
  59. data/lib/generators/case_form/uninstall_generator.rb +30 -0
  60. data/rails/init.rb +1 -0
  61. data/test/element/button_test.rb +85 -0
  62. data/test/element/errors/complex_error_test.rb +140 -0
  63. data/test/element/errors/simple_error_test.rb +92 -0
  64. data/test/element/fieldset_test.rb +28 -0
  65. data/test/element/hint_test.rb +81 -0
  66. data/test/element/input_test.rb +197 -0
  67. data/test/element/inputs/collection/checkbox_input_test.rb +176 -0
  68. data/test/element/inputs/collection/radio_input_test.rb +156 -0
  69. data/test/element/inputs/collection/select_input_test.rb +152 -0
  70. data/test/element/inputs/datetime/date_input_test.rb +160 -0
  71. data/test/element/inputs/datetime/datetime_input_test.rb +227 -0
  72. data/test/element/inputs/datetime/time_input_test.rb +72 -0
  73. data/test/element/inputs/datetime/time_zone_input_test.rb +42 -0
  74. data/test/element/inputs/file_input_test.rb +13 -0
  75. data/test/element/inputs/hidden_input_test.rb +13 -0
  76. data/test/element/inputs/number_input_test.rb +50 -0
  77. data/test/element/inputs/search_input_test.rb +13 -0
  78. data/test/element/inputs/string_input_test.rb +33 -0
  79. data/test/element/inputs/text_input_test.rb +13 -0
  80. data/test/element/label_test.rb +62 -0
  81. data/test/element/nested_model_test.rb +163 -0
  82. data/test/element/nested_models/handles/destructor_handle_test.rb +35 -0
  83. data/test/element/nested_models/handles/generator_handle_test.rb +27 -0
  84. data/test/form_builder_test.rb +25 -0
  85. data/test/form_helper_test.rb +15 -0
  86. data/test/lib/models.rb +268 -0
  87. data/test/test_helper.rb +74 -0
  88. metadata +235 -0
@@ -0,0 +1,76 @@
1
+ module CaseForm
2
+ module Element
3
+ class SimpleError < Error
4
+ include ElementExt::Associationable
5
+
6
+ attr_accessor :method
7
+
8
+ # Initialize error object
9
+ #
10
+ def initialize(builder, method, options={})
11
+ @method = method
12
+ super(builder, options)
13
+ end
14
+
15
+ # Generate simple errors for method with HTML options
16
+ #
17
+ def generate
18
+ template.content_tag error_tag, error_messages, html_options
19
+ end
20
+
21
+ private
22
+ # Distribute default options for simple error.
23
+ #
24
+ def default_options
25
+ options[:class] ||= [:error]
26
+ options[:id] ||= "#{sanitized_object_name}_#{method}_error"
27
+ end
28
+
29
+ # Return all errors on method. If method is association than it takes also errors on association.
30
+ # == Example:
31
+ #
32
+ # # Get errors both on :country and :country_id attribute
33
+ #
34
+ # class User
35
+ # belongs_to :country
36
+ # validates_presence_of :country_id
37
+ # validates_inclusion_of :country, :in => Country.priority
38
+ # end
39
+ #
40
+ def method_errors
41
+ association ? errors[method] + errors[association_method] : errors[method]
42
+ end
43
+
44
+ # Error messages as list.
45
+ #
46
+ # == Example:
47
+ #
48
+ # # object.errors[:name] = ['can't be blank', 'should be uniq']
49
+ #
50
+ # <ul>
51
+ # <li>can't be blank</li>
52
+ # <li>should be uniq</li>
53
+ # </ul>
54
+ #
55
+ def translated_list
56
+ template.content_tag :ul, (method_errors.collect { |msg| template.content_tag :li, msg }).join.html_safe
57
+ end
58
+
59
+ # Error messages as sentence.
60
+ #
61
+ # == Example:
62
+ #
63
+ # # object.errors[:name] = ['can't be blank', 'should be uniq']
64
+ #
65
+ # <div>
66
+ # can't be blank and should be uniq
67
+ # </div>
68
+ #
69
+ def translated_sentence
70
+ method_errors.to_sentence(:words_connector => error_connector,
71
+ :last_word_connector => last_error_connector,
72
+ :two_words_connector => last_error_connector)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class Fieldset < Base
5
+ self.allowed_options << [:text]
6
+
7
+ # Generate fieldset with defined arguments or block.
8
+ # Optional add legend tag if determined in options.
9
+ #
10
+ def generate(*args, &block)
11
+ contents, legend = [], text
12
+ contents << template.content_tag(:legend, legend) if legend
13
+ contents << if block_given?
14
+ template.capture(&block)
15
+ else
16
+ args
17
+ end
18
+ template.content_tag(:fieldset, contents.join.html_safe, html_options)
19
+ end
20
+
21
+ private
22
+ # Distribute default options for fieldset.
23
+ #
24
+ def default_options
25
+ options[:class] ||= [:fieldset]
26
+ end
27
+
28
+ # Fieldset legend text.
29
+ #
30
+ def text
31
+ options.delete(:text)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,54 @@
1
+ module CaseForm
2
+ module Element
3
+ class Hint < Base
4
+ self.allowed_options << [:text, :tag]
5
+
6
+ attr_accessor :method
7
+
8
+ # Initialize hint's object
9
+ #
10
+ def initialize(builder, method, options={})
11
+ @method = method
12
+ super(builder, options)
13
+ end
14
+
15
+ # Generate hint with defined text and HTML options
16
+ #
17
+ def generate
18
+ template.content_tag(hint_tag, text, html_options)
19
+ end
20
+
21
+ private
22
+ # Distribute default options for hint.
23
+ #
24
+ def default_options
25
+ options[:class] ||= [:hint]
26
+ options[:id] ||= "#{sanitized_object_name}_#{method.to_s.underscore}_hint"
27
+ end
28
+
29
+ # Generate hint's text. By default it use :text option, entered string or translated with I18n method.
30
+ #
31
+ def text
32
+ if options[:text]
33
+ options.delete(:text)
34
+ elsif method.is_a?(String)
35
+ method
36
+ else
37
+ translated_text
38
+ end
39
+ end
40
+
41
+ # Translate hint's text with I18n.
42
+ #
43
+ def translated_text
44
+ I18n.t(:"#{object_name}.#{method}", :scope => :"case_form.hints")
45
+ end
46
+
47
+ # Default hint's tag
48
+ #
49
+ def hint_tag
50
+ options.delete(:tag) || CaseForm.hint_tag
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,106 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class Input < Base
5
+ include ElementExt::Validationable
6
+ include ElementExt::Columnable
7
+
8
+ self.allowed_options << [:autofocus, :required, :label, :error, :hint]
9
+
10
+ attr_accessor :method
11
+
12
+ def initialize(builder, method, options={})
13
+ @method = method
14
+ super(builder, options)
15
+ end
16
+
17
+ def generate
18
+ template.content_tag wrapper_tag, contents, wrapper_options
19
+ end
20
+
21
+ private
22
+ def default_options
23
+ options[:class] ||= [:input]
24
+ options[:required] ||= required?
25
+ end
26
+
27
+ def wrapper_options
28
+ wrapper_options = super
29
+ wrapper_options[:class] << :element
30
+ wrapper_options
31
+ end
32
+
33
+ def input_elements
34
+ CaseForm.input_elements
35
+ end
36
+
37
+ def contents
38
+ contents = []
39
+ input_elements.each do |element|
40
+ if options[element] == false
41
+ options.delete(element)
42
+ next
43
+ else
44
+ contents << send(element)
45
+ end
46
+ end
47
+ contents.join.html_safe
48
+ end
49
+
50
+ def label
51
+ Element::Label.new(builder, method, label_options.merge(:required => required?)).generate
52
+ end
53
+
54
+ def label_options
55
+ label_options = options.delete(:label) || {}
56
+ label_options.is_a?(String) ? { :text => label_options } : label_options
57
+ end
58
+
59
+ def input
60
+ raise(NotImplementedInput)
61
+ end
62
+
63
+ def input_options
64
+ options.except(:label, :error, :hint)
65
+ end
66
+
67
+ def error
68
+ Element::SimpleError.new(builder, method, error_options).generate if error?
69
+ end
70
+
71
+ def error?
72
+ object.errors[method].any? || object.errors[specific_method].any?
73
+ end
74
+
75
+ def error_options
76
+ options.delete(:error) || {}
77
+ end
78
+
79
+ def hint
80
+ Element::Hint.new(builder, method, hint_options).generate if hint?
81
+ end
82
+
83
+ def hint?
84
+ options[:hint].present? || !I18n.t(:"#{object_name}.#{method}", :scope => :"case_form.hints", :default => '').blank?
85
+ end
86
+
87
+ def hint_options
88
+ hint_options = options.delete(:hint) || {}
89
+ hint_options.is_a?(String) ? { :text => hint_options } : hint_options
90
+ end
91
+
92
+ def input_size
93
+ validations = object.class.validators_on(method).select { |v| v.kind == :length }
94
+ if validations.any?
95
+ validations.first.options[:maximum]
96
+ else
97
+ CaseForm.input_size
98
+ end
99
+ end
100
+
101
+ def input_type
102
+ options[:as]
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class CheckboxInput < CollectionInput
5
+ self.allowed_options << [:unchecked_value, :allow_multiple]
6
+
7
+ private
8
+ def default_options
9
+ super
10
+ options[:unchecked_value] ||= ''
11
+ options[:checked] = checked_values
12
+ options[:allow_multiple] ||= multiple_collection?
13
+ options[:multiple] = true if options[:allow_multiple] == true
14
+ end
15
+
16
+ def input
17
+ collection.map do |element|
18
+ label, value = element.first, element.last
19
+ checkbox_id = "#{sanitized_object_name}_#{specific_method}_#{value}"
20
+ label_options = { :text => label,
21
+ :required => false,
22
+ :id => "#{checkbox_id}_label",
23
+ :class => :inline_label,
24
+ :for => checkbox_id }
25
+
26
+ builder.check_box(specific_method, html_options.merge(html_options_for_value(value).merge!(:id => checkbox_id)), value, options[:unchecked_value]) <<
27
+ builder.label(specific_method, label_options)
28
+ end.join.html_safe
29
+ end
30
+
31
+ def boolean_collection
32
+ [super.first]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class RadioInput < CollectionInput
5
+ private
6
+ def default_options
7
+ super
8
+ options[:checked] = checked_values
9
+ end
10
+
11
+ def input
12
+ collection.map do |element|
13
+ label, value = element.first, element.last
14
+ radio_id = "#{sanitized_object_name}_#{specific_method}_#{value}"
15
+ label_options = { :text => label,
16
+ :required => false,
17
+ :id => "#{radio_id}_label",
18
+ :class => :inline_label,
19
+ :for => radio_id }
20
+
21
+ builder.radio_button(specific_method, value, html_options.merge(html_options_for_value(value)).merge!(:id => radio_id)) <<
22
+ builder.label(specific_method, label_options)
23
+ end.join.html_safe
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class SelectInput < CollectionInput
5
+ self.allowed_options << [:prompt, :blank, :allow_multiple, :size]
6
+
7
+ private
8
+ def default_options
9
+ super
10
+ options[:selected] = checked_values
11
+ options[:allow_multiple] ||= multiple_collection?
12
+ options[:multiple] = true if options[:allow_multiple] == true
13
+ options[:blank] ||= true
14
+ options[:include_blank] = options.delete(:blank)
15
+ end
16
+
17
+ def input
18
+ template.select(object_name, specific_method, collection, options, html_options)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,89 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class CollectionInput < Input
5
+ include ElementExt::Associationable
6
+
7
+ self.allowed_options << [:disabled, :checked, :selected, :readonly,
8
+ :collection, :label_method, :value_method]
9
+
10
+ private
11
+ def default_options
12
+ super
13
+ options[:collection] ||= default_collection
14
+ end
15
+
16
+ def html_options
17
+ super.except(:disabled, :selected, :checked)
18
+ end
19
+
20
+ def default_collection
21
+ association ? extract_from_association_array(association_class.all) : boolean_collection
22
+ end
23
+
24
+ def boolean_collection
25
+ [[I18n.t(:"case_form.yes", :default => "Yes"), true],
26
+ [I18n.t(:"case_form.no", :default => "No"), false]]
27
+ end
28
+
29
+ def collection_is_association_class?
30
+ association && options[:collection] == association.klass
31
+ end
32
+
33
+ def collection
34
+ @collection ||= if collection_is_association_class? #:nodoc only class defined, example: :collection => User
35
+ association_class.all
36
+ else
37
+ options[:collection].to_a
38
+ end
39
+ extract_collection(@collection)
40
+ end
41
+
42
+ def extract_collection(collection)
43
+ return case collection.first
44
+ when String, Integer, Symbol
45
+ Array.new(collection).collect! { |c| [c, c] }
46
+ when association_class
47
+ extract_from_association_array(collection)
48
+ else
49
+ collection
50
+ end
51
+ end
52
+
53
+ def extract_from_association_array(array)
54
+ if sample = array.first
55
+ label_method ||= options[:label_method] || collection_methods(:label, sample)
56
+ value_method ||= options[:value_method] || collection_methods(:value, sample)
57
+ array.map { |o| [o.send(label_method), o.send(value_method)] }
58
+ else
59
+ [[I18n.t(:"case_form.no_items", :default => "No #{sample.class.to_s.pluralize}"), '']]
60
+ end
61
+ end
62
+
63
+ def collection_methods(type, sample)
64
+ CaseForm.send(:"collection_#{type}_methods").find { |m| sample.respond_to?(m.to_sym) }
65
+ end
66
+
67
+ def multiple_collection?
68
+ association ? (association_type?(:belongs_to) ? false : true) : false
69
+ end
70
+
71
+ def html_options_for_value(value)
72
+ new_options = {}
73
+ [:checked, :selected, :disabled].each do |option|
74
+ next unless options[option]
75
+ new_options[option] = [options[option]].flatten.include?(value) ? true : false
76
+ end
77
+ new_options
78
+ end
79
+
80
+ def object_input_ids
81
+ @input_ids ||= (association ? object.send(association_method) : [])
82
+ end
83
+
84
+ def checked_values
85
+ [options.delete(:checked), options.delete(:selected), object_input_ids].flatten.uniq
86
+ end
87
+ end
88
+ end
89
+ end