ehoch_simple_form 2.0.2.dev

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 (97) hide show
  1. data/CHANGELOG.md +257 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +797 -0
  4. data/lib/generators/simple_form/USAGE +3 -0
  5. data/lib/generators/simple_form/install_generator.rb +32 -0
  6. data/lib/generators/simple_form/templates/README +12 -0
  7. data/lib/generators/simple_form/templates/_form.html.erb +13 -0
  8. data/lib/generators/simple_form/templates/_form.html.haml +10 -0
  9. data/lib/generators/simple_form/templates/_form.html.slim +10 -0
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb.tt +181 -0
  11. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +26 -0
  12. data/lib/simple_form.rb +215 -0
  13. data/lib/simple_form/action_view_extensions/builder.rb +338 -0
  14. data/lib/simple_form/action_view_extensions/form_helper.rb +74 -0
  15. data/lib/simple_form/components.rb +20 -0
  16. data/lib/simple_form/components/errors.rb +35 -0
  17. data/lib/simple_form/components/hints.rb +18 -0
  18. data/lib/simple_form/components/html5.rb +26 -0
  19. data/lib/simple_form/components/label_input.rb +15 -0
  20. data/lib/simple_form/components/labels.rb +79 -0
  21. data/lib/simple_form/components/maxlength.rb +41 -0
  22. data/lib/simple_form/components/min_max.rb +50 -0
  23. data/lib/simple_form/components/pattern.rb +34 -0
  24. data/lib/simple_form/components/placeholders.rb +16 -0
  25. data/lib/simple_form/components/readonly.rb +22 -0
  26. data/lib/simple_form/core_ext/hash.rb +16 -0
  27. data/lib/simple_form/error_notification.rb +48 -0
  28. data/lib/simple_form/form_builder.rb +472 -0
  29. data/lib/simple_form/helpers.rb +12 -0
  30. data/lib/simple_form/helpers/autofocus.rb +11 -0
  31. data/lib/simple_form/helpers/disabled.rb +15 -0
  32. data/lib/simple_form/helpers/readonly.rb +15 -0
  33. data/lib/simple_form/helpers/required.rb +35 -0
  34. data/lib/simple_form/helpers/validators.rb +44 -0
  35. data/lib/simple_form/i18n_cache.rb +22 -0
  36. data/lib/simple_form/inputs.rb +21 -0
  37. data/lib/simple_form/inputs/base.rb +162 -0
  38. data/lib/simple_form/inputs/block_input.rb +14 -0
  39. data/lib/simple_form/inputs/boolean_input.rb +64 -0
  40. data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
  41. data/lib/simple_form/inputs/collection_input.rb +101 -0
  42. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +63 -0
  43. data/lib/simple_form/inputs/collection_select_input.rb +14 -0
  44. data/lib/simple_form/inputs/date_time_input.rb +28 -0
  45. data/lib/simple_form/inputs/file_input.rb +9 -0
  46. data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
  47. data/lib/simple_form/inputs/hidden_input.rb +17 -0
  48. data/lib/simple_form/inputs/numeric_input.rb +24 -0
  49. data/lib/simple_form/inputs/password_input.rb +12 -0
  50. data/lib/simple_form/inputs/priority_input.rb +24 -0
  51. data/lib/simple_form/inputs/range_input.rb +14 -0
  52. data/lib/simple_form/inputs/string_input.rb +23 -0
  53. data/lib/simple_form/inputs/text_input.rb +11 -0
  54. data/lib/simple_form/map_type.rb +16 -0
  55. data/lib/simple_form/version.rb +3 -0
  56. data/lib/simple_form/wrappers.rb +8 -0
  57. data/lib/simple_form/wrappers/builder.rb +103 -0
  58. data/lib/simple_form/wrappers/many.rb +69 -0
  59. data/lib/simple_form/wrappers/root.rb +34 -0
  60. data/lib/simple_form/wrappers/single.rb +18 -0
  61. data/test/action_view_extensions/builder_test.rb +577 -0
  62. data/test/action_view_extensions/form_helper_test.rb +104 -0
  63. data/test/components/label_test.rb +310 -0
  64. data/test/form_builder/association_test.rb +177 -0
  65. data/test/form_builder/button_test.rb +47 -0
  66. data/test/form_builder/error_notification_test.rb +79 -0
  67. data/test/form_builder/error_test.rb +121 -0
  68. data/test/form_builder/general_test.rb +356 -0
  69. data/test/form_builder/hint_test.rb +139 -0
  70. data/test/form_builder/input_field_test.rb +63 -0
  71. data/test/form_builder/label_test.rb +71 -0
  72. data/test/form_builder/wrapper_test.rb +149 -0
  73. data/test/generators/simple_form_generator_test.rb +32 -0
  74. data/test/inputs/boolean_input_test.rb +108 -0
  75. data/test/inputs/collection_check_boxes_input_test.rb +224 -0
  76. data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
  77. data/test/inputs/collection_select_input_test.rb +241 -0
  78. data/test/inputs/datetime_input_test.rb +99 -0
  79. data/test/inputs/disabled_test.rb +38 -0
  80. data/test/inputs/discovery_test.rb +61 -0
  81. data/test/inputs/file_input_test.rb +16 -0
  82. data/test/inputs/general_test.rb +69 -0
  83. data/test/inputs/grouped_collection_select_input_test.rb +118 -0
  84. data/test/inputs/hidden_input_test.rb +30 -0
  85. data/test/inputs/numeric_input_test.rb +173 -0
  86. data/test/inputs/priority_input_test.rb +43 -0
  87. data/test/inputs/readonly_test.rb +61 -0
  88. data/test/inputs/required_test.rb +113 -0
  89. data/test/inputs/string_input_test.rb +140 -0
  90. data/test/inputs/text_input_test.rb +24 -0
  91. data/test/simple_form_test.rb +9 -0
  92. data/test/support/discovery_inputs.rb +21 -0
  93. data/test/support/misc_helpers.rb +102 -0
  94. data/test/support/mock_controller.rb +24 -0
  95. data/test/support/models.rb +210 -0
  96. data/test/test_helper.rb +90 -0
  97. metadata +210 -0
@@ -0,0 +1,113 @@
1
+ require 'test_helper'
2
+
3
+ class RequiredTest < ActionView::TestCase
4
+ # REQUIRED AND PRESENCE VALIDATION
5
+ test 'builder input should obtain required from ActiveModel::Validations when it is included' do
6
+ with_form_for @validating_user, :name
7
+ assert_select 'input.required[required]#validating_user_name'
8
+ with_form_for @validating_user, :status
9
+ assert_select 'input.optional#validating_user_status'
10
+ end
11
+
12
+ test 'builder input should allow overriding required when ActiveModel::Validations is included' do
13
+ with_form_for @validating_user, :name, :required => false
14
+ assert_select 'input.optional#validating_user_name'
15
+ with_form_for @validating_user, :status, :required => true
16
+ assert_select 'input.required[required]#validating_user_status'
17
+ end
18
+
19
+ test 'builder input should be required by default when ActiveModel::Validations is not included' do
20
+ with_form_for @user, :name
21
+ assert_select 'input.required[required]#user_name'
22
+ end
23
+
24
+ test 'builder input should not be required by default when ActiveModel::Validations is not included if option is set to false' do
25
+ swap SimpleForm, :required_by_default => false do
26
+ with_form_for @user, :name
27
+ assert_select 'input.optional#user_name'
28
+ assert_no_select 'input[required]'
29
+ end
30
+ end
31
+
32
+ test 'when not using browser validations, input should not generate required html attribute' do
33
+ swap SimpleForm, :browser_validations => false do
34
+ with_input_for @user, :name, :string
35
+ assert_select 'input[type=text].required'
36
+ assert_no_select 'input[type=text][required]'
37
+ end
38
+ end
39
+
40
+ test 'builder input should allow disabling required when ActiveModel::Validations is not included' do
41
+ with_form_for @user, :name, :required => false
42
+ assert_no_select 'input.required'
43
+ assert_no_select 'input[required]'
44
+ assert_select 'input.optional#user_name'
45
+ end
46
+
47
+ test 'when not the required component the input does not have the required attribute but has the required class' do
48
+ swap_wrapper do
49
+ with_input_for @user, :name, :string
50
+ assert_select 'input[type=text].required'
51
+ assert_no_select 'input[type=text][required]'
52
+ end
53
+ end
54
+
55
+ # VALIDATORS :if :unless
56
+ test 'builder input should not be required when ActiveModel::Validations is included and if option is present' do
57
+ with_form_for @validating_user, :age
58
+ assert_no_select 'input.required'
59
+ assert_no_select 'input[required]'
60
+ assert_select 'input.optional#validating_user_age'
61
+ end
62
+
63
+ test 'builder input should not be required when ActiveModel::Validations is included and unless option is present' do
64
+ with_form_for @validating_user, :amount
65
+ assert_no_select 'input.required'
66
+ assert_no_select 'input[required]'
67
+ assert_select 'input.optional#validating_user_amount'
68
+ end
69
+
70
+ # VALIDATORS :on
71
+ test 'builder input should be required when validation is on create and is not persisted' do
72
+ @validating_user.new_record!
73
+ with_form_for @validating_user, :action
74
+ assert_select 'input.required'
75
+ assert_select 'input[required]'
76
+ assert_select 'input.required[required]#validating_user_action'
77
+ end
78
+
79
+ test 'builder input should not be required when validation is on create and is persisted' do
80
+ with_form_for @validating_user, :action
81
+ assert_no_select 'input.required'
82
+ assert_no_select 'input[required]'
83
+ assert_select 'input.optional#validating_user_action'
84
+ end
85
+
86
+ test 'builder input should be required when validation is on save' do
87
+ with_form_for @validating_user, :credit_limit
88
+ assert_select 'input.required'
89
+ assert_select 'input[required]'
90
+ assert_select 'input.required[required]#validating_user_credit_limit'
91
+
92
+ @validating_user.new_record!
93
+ with_form_for @validating_user, :credit_limit
94
+ assert_select 'input.required'
95
+ assert_select 'input[required]'
96
+ assert_select 'input.required[required]#validating_user_credit_limit'
97
+ end
98
+
99
+ test 'builder input should be required when validation is on update and is persisted' do
100
+ with_form_for @validating_user, :phone_number
101
+ assert_select 'input.required'
102
+ assert_select 'input[required]'
103
+ assert_select 'input.required[required]#validating_user_phone_number'
104
+ end
105
+
106
+ test 'builder input should not be required when validation is on update and is not persisted' do
107
+ @validating_user.new_record!
108
+ with_form_for @validating_user, :phone_number
109
+ assert_no_select 'input.required'
110
+ assert_no_select 'input[required]'
111
+ assert_select 'input.optional#validating_user_phone_number'
112
+ end
113
+ end
@@ -0,0 +1,140 @@
1
+ # encoding: UTF-8
2
+ require 'test_helper'
3
+
4
+ class StringInputTest < ActionView::TestCase
5
+ test 'input should map text field to string attribute' do
6
+ with_input_for @user, :name, :string
7
+ assert_select "input#user_name[type=text][name='user[name]'][value=New in SimpleForm!]"
8
+ end
9
+
10
+ test 'input should generate a password field for password attributes' do
11
+ with_input_for @user, :password, :password
12
+ assert_select "input#user_password.password[type=password][name='user[password]']"
13
+ end
14
+
15
+ test 'input should not use size attribute for decimal attributes' do
16
+ with_input_for @user, :credit_limit, :decimal
17
+ assert_no_select 'input.decimal[size]'
18
+ end
19
+
20
+ test 'input should get maxlength from column definition for string attributes' do
21
+ with_input_for @user, :name, :string
22
+ assert_select 'input.string[maxlength=100]'
23
+ end
24
+
25
+ test 'input should not get maxlength from column without size definition for string attributes' do
26
+ with_input_for @user, :action, :string
27
+ assert_no_select 'input.string[maxlength]'
28
+ end
29
+
30
+ test 'input should get size from column definition for string attributes respecting maximum value' do
31
+ with_input_for @user, :name, :string
32
+ assert_select 'input.string[size=50]'
33
+ end
34
+
35
+ test 'input should use default text size for password attributes' do
36
+ with_input_for @user, :password, :password
37
+ assert_select 'input.password[type=password][size=50]'
38
+ end
39
+
40
+ test 'input should get maxlength from column definition for password attributes' do
41
+ with_input_for @user, :password, :password
42
+ assert_select 'input.password[type=password][maxlength=100]'
43
+ end
44
+
45
+ test 'input should infer maxlength column definition from validation when present' do
46
+ with_input_for @validating_user, :name, :string
47
+ assert_select 'input.string[maxlength=25]'
48
+ end
49
+
50
+ test 'input should not get maxlength from validation when tokenizer present' do
51
+ with_input_for @validating_user, :action, :string
52
+ assert_no_select 'input.string[maxlength]'
53
+ end
54
+
55
+ test 'input should get maxlength from validation when :is option present' do
56
+ with_input_for @validating_user, :home_picture, :string
57
+ assert_select 'input.string[maxlength=12]'
58
+ end
59
+
60
+ test 'input should not generate placeholder by default' do
61
+ with_input_for @user, :name, :string
62
+ assert_no_select 'input[placeholder]'
63
+ end
64
+
65
+ test 'input should accept the placeholder option' do
66
+ with_input_for @user, :name, :string, :placeholder => 'Put in some text'
67
+ assert_select 'input.string[placeholder=Put in some text]'
68
+ end
69
+
70
+ test 'input should generate a password field for password attributes that accept placeholder' do
71
+ with_input_for @user, :password, :password, :placeholder => 'Password Confirmation'
72
+ assert_select 'input[type=password].password[placeholder=Password Confirmation]#user_password'
73
+ end
74
+
75
+ test 'input should not infer pattern from attributes by default' do
76
+ with_input_for @other_validating_user, :country, :string
77
+ assert_no_select 'input[pattern="\w+"]'
78
+ end
79
+
80
+ test 'input should infer pattern from attributes' do
81
+ with_input_for @other_validating_user, :country, :string, :pattern => true
82
+ assert_select 'input[pattern="\w+"]'
83
+ end
84
+
85
+ test 'input should infer pattern from attributes using proc' do
86
+ with_input_for @other_validating_user, :name, :string, :pattern => true
87
+ assert_select 'input[pattern="\w+"]'
88
+ end
89
+
90
+ test 'input should not infer pattern from attributes if root default is false' do
91
+ swap_wrapper do
92
+ with_input_for @other_validating_user, :country, :string
93
+ assert_no_select 'input[pattern="\w+"]'
94
+ end
95
+ end
96
+
97
+ test 'input should use given pattern from attributes' do
98
+ with_input_for @other_validating_user, :country, :string, :input_html => { :pattern => "\\d+" }
99
+ assert_select 'input[pattern="\d+"]'
100
+ end
101
+
102
+ test 'input should use i18n to translate placeholder text' do
103
+ store_translations(:en, :simple_form => { :placeholders => { :user => {
104
+ :name => 'Name goes here'
105
+ } } }) do
106
+ with_input_for @user, :name, :string
107
+ assert_select 'input.string[placeholder=Name goes here]'
108
+ end
109
+ end
110
+
111
+ [:email, :url, :search, :tel].each do |type|
112
+ test "input should allow type #{type}" do
113
+ with_input_for @user, :name, type
114
+ assert_select "input.string.#{type}"
115
+ assert_select "input[type=#{type}]"
116
+ end
117
+
118
+ test "input should not allow type #{type} if HTML5 compatibility is disabled" do
119
+ swap_wrapper do
120
+ with_input_for @user, :name, type
121
+ assert_select "input[type=text]"
122
+ assert_no_select "input[type=#{type}]"
123
+ end
124
+ end
125
+ end
126
+
127
+ test 'input strips extra spaces from class html attribute with default classes' do
128
+ with_input_for @user, :name, :string
129
+ assert_select "input[class='string required']"
130
+ assert_no_select "input[class='string required ']"
131
+ assert_no_select "input[class=' string required']"
132
+ end
133
+
134
+ test 'input strips extra spaces from class html attribute when giving a custom class' do
135
+ with_input_for @user, :name, :string, :input_html => { :class => "my_input" }
136
+ assert_select "input[class='string required my_input']"
137
+ assert_no_select "input[class='string required my_input ']"
138
+ assert_no_select "input[class=' string required my_input']"
139
+ end
140
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+ require 'test_helper'
3
+
4
+ class TextInputTest < ActionView::TestCase
5
+ test 'input should generate a text area for text attributes' do
6
+ with_input_for @user, :description, :text
7
+ assert_select 'textarea.text#user_description'
8
+ end
9
+
10
+ test 'input should generate a text area for text attributes that accept placeholder' do
11
+ with_input_for @user, :description, :text, :placeholder => 'Put in some text'
12
+ assert_select 'textarea.text[placeholder=Put in some text]'
13
+ end
14
+
15
+ test 'input should get maxlength from column definition for text attributes' do
16
+ with_input_for @user, :description, :text
17
+ assert_select 'textarea.text[maxlength=200]'
18
+ end
19
+
20
+ test 'input should infer maxlength column definition from validation when present for text attributes' do
21
+ with_input_for @validating_user, :description, :text
22
+ assert_select 'textarea.text[maxlength=50]'
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ class SimpleFormTest < ActiveSupport::TestCase
4
+ test 'setup block yields self' do
5
+ SimpleForm.setup do |config|
6
+ assert_equal SimpleForm, config
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ class StringInput < SimpleForm::Inputs::StringInput
2
+ def input
3
+ "<section>#{super}</section>".html_safe
4
+ end
5
+ end
6
+
7
+ class NumericInput < SimpleForm::Inputs::NumericInput
8
+ def input
9
+ "<section>#{super}</section>".html_safe
10
+ end
11
+ end
12
+
13
+ class CustomizedInput < SimpleForm::Inputs::StringInput
14
+ def input
15
+ "<section>#{super}</section>".html_safe
16
+ end
17
+
18
+ def input_method
19
+ :text_field
20
+ end
21
+ end
@@ -0,0 +1,102 @@
1
+ module MiscHelpers
2
+ def store_translations(locale, translations, &block)
3
+ I18n.backend.store_translations locale, translations
4
+ yield
5
+ ensure
6
+ I18n.reload!
7
+ I18n.backend.send :init_translations
8
+ end
9
+
10
+ def assert_no_select(selector, value = nil)
11
+ assert_select(selector, :text => value, :count => 0)
12
+ end
13
+
14
+ def swap(object, new_values)
15
+ old_values = {}
16
+ new_values.each do |key, value|
17
+ old_values[key] = object.send key
18
+ object.send :"#{key}=", value
19
+ end
20
+ yield
21
+ ensure
22
+ old_values.each do |key, value|
23
+ object.send :"#{key}=", value
24
+ end
25
+ end
26
+
27
+ def swap_wrapper(name=:default, wrapper=self.custom_wrapper)
28
+ old = SimpleForm.wrappers[name]
29
+ SimpleForm.wrappers[name] = wrapper
30
+ yield
31
+ ensure
32
+ SimpleForm.wrappers[name] = old
33
+ end
34
+
35
+ def custom_wrapper
36
+ SimpleForm.build :tag => :section, :class => "custom_wrapper", :pattern => false do |b|
37
+ b.use :pattern
38
+ b.wrapper :another, :class => "another_wrapper" do |ba|
39
+ ba.use :label
40
+ ba.use :input
41
+ end
42
+ b.wrapper :error_wrapper, :tag => :div, :class => "error_wrapper" do |be|
43
+ be.use :error, :wrap_with => { :tag => :span, :class => "omg_error" }
44
+ end
45
+ b.use :hint, :wrap_with => { :class => "omg_hint" }
46
+ end
47
+ end
48
+
49
+ def custom_wrapper_without_top_level
50
+ SimpleForm.build :tag => false, :class => 'custom_wrapper_without_top_level' do |b|
51
+ b.use :label_input
52
+ b.use :hint, :wrap_with => { :tag => :span, :class => :hint }
53
+ b.use :error, :wrap_with => { :tag => :span, :class => :error }
54
+ end
55
+ end
56
+
57
+ def custom_form_for(object, *args, &block)
58
+ simple_form_for(object, *(args << { :builder => CustomFormBuilder }), &block)
59
+ end
60
+
61
+ def custom_mapping_form_for(object, *args, &block)
62
+ simple_form_for(object, *(args << { :builder => CustomMapTypeFormBuilder }), &block)
63
+ end
64
+
65
+ def with_concat_form_for(*args, &block)
66
+ concat simple_form_for(*args, &(block || proc {}))
67
+ end
68
+
69
+ def with_concat_fields_for(*args, &block)
70
+ concat simple_fields_for(*args, &block)
71
+ end
72
+
73
+ def with_concat_custom_form_for(*args, &block)
74
+ concat custom_form_for(*args, &block)
75
+ end
76
+
77
+ def with_concat_custom_mapping_form_for(*args, &block)
78
+ concat custom_mapping_form_for(*args, &block)
79
+ end
80
+
81
+ def with_form_for(object, *args, &block)
82
+ with_concat_form_for(object) do |f|
83
+ f.input(*args, &block)
84
+ end
85
+ end
86
+
87
+ def with_input_for(object, attribute_name, type, options={})
88
+ with_concat_form_for(object) do |f|
89
+ f.input(attribute_name, options.merge(:as => type))
90
+ end
91
+ end
92
+ end
93
+
94
+ class CustomFormBuilder < SimpleForm::FormBuilder
95
+ def input(attribute_name, *args, &block)
96
+ super(attribute_name, *(args << { :input_html => { :class => 'custom' } }), &block)
97
+ end
98
+ end
99
+
100
+ class CustomMapTypeFormBuilder < SimpleForm::FormBuilder
101
+ map_type :custom_type, :to => SimpleForm::Inputs::StringInput
102
+ end
@@ -0,0 +1,24 @@
1
+ class MockController
2
+ attr_writer :action_name
3
+
4
+ def _routes
5
+ self
6
+ end
7
+
8
+ def action_name
9
+ defined?(@action_name) ? @action_name : "edit"
10
+ end
11
+
12
+ def url_for(*args)
13
+ "http://example.com"
14
+ end
15
+
16
+ def url_helpers
17
+ self
18
+ end
19
+
20
+ def hash_for_user_path(*args); end
21
+ def hash_for_validating_user_path(*args); end
22
+ def hash_for_other_validating_user_path(*args); end
23
+ def hash_for_users_path(*args); end
24
+ end
@@ -0,0 +1,210 @@
1
+ Column = Struct.new(:name, :type, :limit)
2
+ Association = Struct.new(:klass, :name, :macro, :options)
3
+
4
+ class Company < Struct.new(:id, :name)
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+
8
+ def self.all(options={})
9
+ all = (1..3).map{|i| Company.new(i, "Company #{i}")}
10
+ return [all.first] if options[:conditions].present?
11
+ return [all.last] if options[:order].present?
12
+ return all[0..1] if options[:include].present?
13
+ return all[1..2] if options[:joins].present?
14
+ all
15
+ end
16
+
17
+ def self.merge_conditions(a, b)
18
+ (a || {}).merge(b || {})
19
+ end
20
+
21
+ def persisted?
22
+ true
23
+ end
24
+ end
25
+
26
+ class Tag < Company
27
+ def self.all(options={})
28
+ (1..3).map{|i| Tag.new(i, "Tag #{i}")}
29
+ end
30
+ end
31
+
32
+ class TagGroup < Struct.new(:id, :name, :tags)
33
+ end
34
+
35
+ class User
36
+ extend ActiveModel::Naming
37
+ include ActiveModel::Conversion
38
+
39
+ attr_accessor :id, :name, :company, :company_id, :time_zone, :active, :age,
40
+ :description, :created_at, :updated_at, :credit_limit, :password, :url,
41
+ :delivery_time, :born_at, :special_company_id, :country, :tags, :tag_ids,
42
+ :avatar, :home_picture, :email, :status, :residence_country, :phone_number,
43
+ :post_count, :lock_version, :amount, :attempts, :action, :credit_card, :gender
44
+
45
+ def initialize(options={})
46
+ @new_record = false
47
+ options.each do |key, value|
48
+ send("#{key}=", value)
49
+ end if options
50
+ end
51
+
52
+ def new_record!
53
+ @new_record = true
54
+ end
55
+
56
+ def persisted?
57
+ !@new_record
58
+ end
59
+
60
+ def company_attributes=(*)
61
+ end
62
+
63
+ def tags_attributes=(*)
64
+ end
65
+
66
+ def column_for_attribute(attribute)
67
+ column_type, limit = case attribute.to_sym
68
+ when :name, :status, :password then [:string, 100]
69
+ when :description then [:text, 200]
70
+ when :age then :integer
71
+ when :credit_limit then [:decimal, 15]
72
+ when :active then :boolean
73
+ when :born_at then :date
74
+ when :delivery_time then :time
75
+ when :created_at then :datetime
76
+ when :updated_at then :timestamp
77
+ when :lock_version then :integer
78
+ when :home_picture then :string
79
+ when :amount then :integer
80
+ when :attempts then :integer
81
+ when :action then :string
82
+ when :credit_card then :string
83
+ end
84
+ Column.new(attribute, column_type, limit)
85
+ end
86
+
87
+ def self.human_attribute_name(attribute)
88
+ case attribute
89
+ when 'name'
90
+ 'Super User Name!'
91
+ when 'description'
92
+ 'User Description!'
93
+ when 'company'
94
+ 'Company Human Name!'
95
+ else
96
+ attribute.humanize
97
+ end
98
+ end
99
+
100
+ def self.reflect_on_association(association)
101
+ case association
102
+ when :company
103
+ Association.new(Company, association, :belongs_to, {})
104
+ when :tags
105
+ Association.new(Tag, association, :has_many, {})
106
+ when :first_company
107
+ Association.new(Company, association, :has_one, {})
108
+ when :special_company
109
+ Association.new(Company, association, :belongs_to, { :conditions => { :id => 1 } })
110
+ end
111
+ end
112
+
113
+ def errors
114
+ @errors ||= begin
115
+ hash = Hash.new { |h,k| h[k] = [] }
116
+ hash.merge!(
117
+ :name => ["can't be blank"],
118
+ :description => ["must be longer than 15 characters"],
119
+ :age => ["is not a number", "must be greater than 18"],
120
+ :company => ["company must be present"],
121
+ :company_id => ["must be valid"]
122
+ )
123
+ end
124
+ end
125
+
126
+ def self.readonly_attributes
127
+ [:credit_card]
128
+ end
129
+ end
130
+
131
+ class ValidatingUser < User
132
+ include ActiveModel::Validations
133
+ validates :name, :presence => true
134
+ validates :company, :presence => true
135
+ validates :age, :presence => true, :if => Proc.new { |user| user.name }
136
+ validates :amount, :presence => true, :unless => Proc.new { |user| user.age }
137
+
138
+ validates :action, :presence => true, :on => :create
139
+ validates :credit_limit, :presence => true, :on => :save
140
+ validates :phone_number, :presence => true, :on => :update
141
+
142
+ validates_numericality_of :age,
143
+ :greater_than_or_equal_to => 18,
144
+ :less_than_or_equal_to => 99,
145
+ :only_integer => true
146
+ validates_numericality_of :amount,
147
+ :greater_than => :min_amount,
148
+ :less_than => :max_amount,
149
+ :only_integer => true
150
+ validates_numericality_of :attempts,
151
+ :greater_than_or_equal_to => :min_attempts,
152
+ :less_than_or_equal_to => :max_attempts,
153
+ :only_integer => true
154
+ validates_length_of :name, :maximum => 25
155
+ validates_length_of :description, :maximum => 50
156
+ validates_length_of :action, :maximum => 10, :tokenizer => lambda { |str| str.scan(/\w+/) }
157
+ validates_length_of :home_picture, :is => 12
158
+
159
+ def min_amount
160
+ 10
161
+ end
162
+
163
+ def max_amount
164
+ 100
165
+ end
166
+
167
+ def min_attempts
168
+ 1
169
+ end
170
+
171
+ def max_attempts
172
+ 100
173
+ end
174
+ end
175
+
176
+ class OtherValidatingUser < User
177
+ include ActiveModel::Validations
178
+ validates_numericality_of :age,
179
+ :greater_than => 17,
180
+ :less_than => 100,
181
+ :only_integer => true
182
+ validates_numericality_of :amount,
183
+ :greater_than => Proc.new { |user| user.age },
184
+ :less_than => Proc.new { |user| user.age + 100},
185
+ :only_integer => true
186
+ validates_numericality_of :attempts,
187
+ :greater_than_or_equal_to => Proc.new { |user| user.age },
188
+ :less_than_or_equal_to => Proc.new { |user| user.age + 100},
189
+ :only_integer => true
190
+
191
+ validates_format_of :country, :with => /\w+/
192
+
193
+ # TODO: Remove this check when we drop Rails 3.0 support
194
+ if ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR >= 1
195
+ validates_format_of :name, :with => Proc.new { /\w+/ }
196
+ else
197
+ validates_format_of :name, :with => /\w+/
198
+ end
199
+ end
200
+
201
+ class HashBackedAuthor < Hash
202
+ extend ActiveModel::Naming
203
+ include ActiveModel::Conversion
204
+
205
+ def persisted?; false; end
206
+
207
+ def name
208
+ 'hash backed author'
209
+ end
210
+ end