ehoch_simple_form 2.0.2.dev

Sign up to get free protection for your applications and to get access to all the features.
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