ehoch_simple_form 2.0.2.dev
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +257 -0
- data/MIT-LICENSE +20 -0
- data/README.md +797 -0
- data/lib/generators/simple_form/USAGE +3 -0
- data/lib/generators/simple_form/install_generator.rb +32 -0
- data/lib/generators/simple_form/templates/README +12 -0
- data/lib/generators/simple_form/templates/_form.html.erb +13 -0
- data/lib/generators/simple_form/templates/_form.html.haml +10 -0
- data/lib/generators/simple_form/templates/_form.html.slim +10 -0
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb.tt +181 -0
- data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +26 -0
- data/lib/simple_form.rb +215 -0
- data/lib/simple_form/action_view_extensions/builder.rb +338 -0
- data/lib/simple_form/action_view_extensions/form_helper.rb +74 -0
- data/lib/simple_form/components.rb +20 -0
- data/lib/simple_form/components/errors.rb +35 -0
- data/lib/simple_form/components/hints.rb +18 -0
- data/lib/simple_form/components/html5.rb +26 -0
- data/lib/simple_form/components/label_input.rb +15 -0
- data/lib/simple_form/components/labels.rb +79 -0
- data/lib/simple_form/components/maxlength.rb +41 -0
- data/lib/simple_form/components/min_max.rb +50 -0
- data/lib/simple_form/components/pattern.rb +34 -0
- data/lib/simple_form/components/placeholders.rb +16 -0
- data/lib/simple_form/components/readonly.rb +22 -0
- data/lib/simple_form/core_ext/hash.rb +16 -0
- data/lib/simple_form/error_notification.rb +48 -0
- data/lib/simple_form/form_builder.rb +472 -0
- data/lib/simple_form/helpers.rb +12 -0
- data/lib/simple_form/helpers/autofocus.rb +11 -0
- data/lib/simple_form/helpers/disabled.rb +15 -0
- data/lib/simple_form/helpers/readonly.rb +15 -0
- data/lib/simple_form/helpers/required.rb +35 -0
- data/lib/simple_form/helpers/validators.rb +44 -0
- data/lib/simple_form/i18n_cache.rb +22 -0
- data/lib/simple_form/inputs.rb +21 -0
- data/lib/simple_form/inputs/base.rb +162 -0
- data/lib/simple_form/inputs/block_input.rb +14 -0
- data/lib/simple_form/inputs/boolean_input.rb +64 -0
- data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
- data/lib/simple_form/inputs/collection_input.rb +101 -0
- data/lib/simple_form/inputs/collection_radio_buttons_input.rb +63 -0
- data/lib/simple_form/inputs/collection_select_input.rb +14 -0
- data/lib/simple_form/inputs/date_time_input.rb +28 -0
- data/lib/simple_form/inputs/file_input.rb +9 -0
- data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
- data/lib/simple_form/inputs/hidden_input.rb +17 -0
- data/lib/simple_form/inputs/numeric_input.rb +24 -0
- data/lib/simple_form/inputs/password_input.rb +12 -0
- data/lib/simple_form/inputs/priority_input.rb +24 -0
- data/lib/simple_form/inputs/range_input.rb +14 -0
- data/lib/simple_form/inputs/string_input.rb +23 -0
- data/lib/simple_form/inputs/text_input.rb +11 -0
- data/lib/simple_form/map_type.rb +16 -0
- data/lib/simple_form/version.rb +3 -0
- data/lib/simple_form/wrappers.rb +8 -0
- data/lib/simple_form/wrappers/builder.rb +103 -0
- data/lib/simple_form/wrappers/many.rb +69 -0
- data/lib/simple_form/wrappers/root.rb +34 -0
- data/lib/simple_form/wrappers/single.rb +18 -0
- data/test/action_view_extensions/builder_test.rb +577 -0
- data/test/action_view_extensions/form_helper_test.rb +104 -0
- data/test/components/label_test.rb +310 -0
- data/test/form_builder/association_test.rb +177 -0
- data/test/form_builder/button_test.rb +47 -0
- data/test/form_builder/error_notification_test.rb +79 -0
- data/test/form_builder/error_test.rb +121 -0
- data/test/form_builder/general_test.rb +356 -0
- data/test/form_builder/hint_test.rb +139 -0
- data/test/form_builder/input_field_test.rb +63 -0
- data/test/form_builder/label_test.rb +71 -0
- data/test/form_builder/wrapper_test.rb +149 -0
- data/test/generators/simple_form_generator_test.rb +32 -0
- data/test/inputs/boolean_input_test.rb +108 -0
- data/test/inputs/collection_check_boxes_input_test.rb +224 -0
- data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
- data/test/inputs/collection_select_input_test.rb +241 -0
- data/test/inputs/datetime_input_test.rb +99 -0
- data/test/inputs/disabled_test.rb +38 -0
- data/test/inputs/discovery_test.rb +61 -0
- data/test/inputs/file_input_test.rb +16 -0
- data/test/inputs/general_test.rb +69 -0
- data/test/inputs/grouped_collection_select_input_test.rb +118 -0
- data/test/inputs/hidden_input_test.rb +30 -0
- data/test/inputs/numeric_input_test.rb +173 -0
- data/test/inputs/priority_input_test.rb +43 -0
- data/test/inputs/readonly_test.rb +61 -0
- data/test/inputs/required_test.rb +113 -0
- data/test/inputs/string_input_test.rb +140 -0
- data/test/inputs/text_input_test.rb +24 -0
- data/test/simple_form_test.rb +9 -0
- data/test/support/discovery_inputs.rb +21 -0
- data/test/support/misc_helpers.rb +102 -0
- data/test/support/mock_controller.rb +24 -0
- data/test/support/models.rb +210 -0
- data/test/test_helper.rb +90 -0
- 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,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
|