simple_form 2.0.0 → 3.5.1
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.
Potentially problematic release.
This version of simple_form might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +97 -198
- data/MIT-LICENSE +1 -1
- data/README.md +572 -296
- data/lib/generators/simple_form/install_generator.rb +17 -7
- data/lib/generators/simple_form/templates/README +3 -4
- data/lib/generators/simple_form/templates/_form.html.erb +1 -0
- data/lib/generators/simple_form/templates/_form.html.haml +1 -0
- data/lib/generators/simple_form/templates/config/initializers/{simple_form.rb.tt → simple_form.rb} +57 -63
- data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +155 -0
- data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +111 -0
- data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +14 -7
- data/lib/simple_form/action_view_extensions/builder.rb +5 -305
- data/lib/simple_form/action_view_extensions/form_helper.rb +18 -20
- data/lib/simple_form/components/errors.rb +30 -3
- data/lib/simple_form/components/hints.rb +10 -3
- data/lib/simple_form/components/html5.rb +17 -3
- data/lib/simple_form/components/label_input.rb +21 -2
- data/lib/simple_form/components/labels.rb +16 -11
- data/lib/simple_form/components/maxlength.rb +19 -12
- data/lib/simple_form/components/min_max.rb +4 -2
- data/lib/simple_form/components/minlength.rb +48 -0
- data/lib/simple_form/components/pattern.rb +5 -4
- data/lib/simple_form/components/placeholders.rb +3 -2
- data/lib/simple_form/components/readonly.rb +3 -2
- data/lib/simple_form/components.rb +15 -11
- data/lib/simple_form/error_notification.rb +4 -3
- data/lib/simple_form/form_builder.rb +283 -105
- data/lib/simple_form/helpers/autofocus.rb +1 -0
- data/lib/simple_form/helpers/disabled.rb +1 -0
- data/lib/simple_form/helpers/readonly.rb +1 -0
- data/lib/simple_form/helpers/required.rb +1 -0
- data/lib/simple_form/helpers/validators.rb +4 -3
- data/lib/simple_form/helpers.rb +7 -6
- data/lib/simple_form/i18n_cache.rb +1 -0
- data/lib/simple_form/inputs/base.rb +76 -23
- data/lib/simple_form/inputs/block_input.rb +3 -2
- data/lib/simple_form/inputs/boolean_input.rb +55 -16
- data/lib/simple_form/inputs/collection_check_boxes_input.rb +2 -1
- data/lib/simple_form/inputs/collection_input.rb +41 -18
- data/lib/simple_form/inputs/collection_radio_buttons_input.rb +11 -19
- data/lib/simple_form/inputs/collection_select_input.rb +5 -2
- data/lib/simple_form/inputs/date_time_input.rb +23 -12
- data/lib/simple_form/inputs/file_input.rb +5 -2
- data/lib/simple_form/inputs/grouped_collection_select_input.rb +16 -3
- data/lib/simple_form/inputs/hidden_input.rb +5 -2
- data/lib/simple_form/inputs/numeric_input.rb +4 -8
- data/lib/simple_form/inputs/password_input.rb +6 -4
- data/lib/simple_form/inputs/priority_input.rb +5 -2
- data/lib/simple_form/inputs/range_input.rb +2 -1
- data/lib/simple_form/inputs/string_input.rb +6 -4
- data/lib/simple_form/inputs/text_input.rb +6 -3
- data/lib/simple_form/inputs.rb +20 -17
- data/lib/simple_form/map_type.rb +1 -0
- data/lib/simple_form/railtie.rb +15 -0
- data/lib/simple_form/tags.rb +69 -0
- data/lib/simple_form/version.rb +2 -1
- data/lib/simple_form/wrappers/builder.rb +12 -35
- data/lib/simple_form/wrappers/leaf.rb +29 -0
- data/lib/simple_form/wrappers/many.rb +12 -7
- data/lib/simple_form/wrappers/root.rb +7 -4
- data/lib/simple_form/wrappers/single.rb +12 -3
- data/lib/simple_form/wrappers.rb +3 -1
- data/lib/simple_form.rb +118 -63
- data/test/action_view_extensions/builder_test.rb +230 -164
- data/test/action_view_extensions/form_helper_test.rb +107 -39
- data/test/components/label_test.rb +105 -87
- data/test/form_builder/association_test.rb +131 -62
- data/test/form_builder/button_test.rb +15 -14
- data/test/form_builder/error_notification_test.rb +11 -10
- data/test/form_builder/error_test.rb +188 -34
- data/test/form_builder/general_test.rb +247 -102
- data/test/form_builder/hint_test.rb +59 -32
- data/test/form_builder/input_field_test.rb +138 -25
- data/test/form_builder/label_test.rb +84 -13
- data/test/form_builder/wrapper_test.rb +236 -33
- data/test/generators/simple_form_generator_test.rb +15 -4
- data/test/inputs/boolean_input_test.rb +147 -13
- data/test/inputs/collection_check_boxes_input_test.rb +166 -71
- data/test/inputs/collection_radio_buttons_input_test.rb +229 -113
- data/test/inputs/collection_select_input_test.rb +222 -85
- data/test/inputs/datetime_input_test.rb +134 -47
- data/test/inputs/disabled_test.rb +62 -21
- data/test/inputs/discovery_test.rb +70 -10
- data/test/inputs/file_input_test.rb +4 -3
- data/test/inputs/general_test.rb +90 -26
- data/test/inputs/grouped_collection_select_input_test.rb +88 -23
- data/test/inputs/hidden_input_test.rb +7 -5
- data/test/inputs/numeric_input_test.rb +56 -46
- data/test/inputs/priority_input_test.rb +31 -16
- data/test/inputs/readonly_test.rb +68 -27
- data/test/inputs/required_test.rb +63 -18
- data/test/inputs/string_input_test.rb +76 -51
- data/test/inputs/text_input_test.rb +21 -8
- data/test/simple_form_test.rb +9 -0
- data/test/support/discovery_inputs.rb +39 -2
- data/test/support/misc_helpers.rb +176 -20
- data/test/support/mock_controller.rb +13 -7
- data/test/support/models.rb +187 -71
- data/test/test_helper.rb +38 -39
- metadata +53 -39
- data/lib/simple_form/core_ext/hash.rb +0 -16
- data/test/support/mock_response.rb +0 -14
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module MiscHelpers
|
|
2
3
|
def store_translations(locale, translations, &block)
|
|
3
4
|
I18n.backend.store_translations locale, translations
|
|
@@ -8,7 +9,7 @@ module MiscHelpers
|
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def assert_no_select(selector, value = nil)
|
|
11
|
-
assert_select(selector, :
|
|
12
|
+
assert_select(selector, text: value, count: 0)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def swap(object, new_values)
|
|
@@ -24,46 +25,201 @@ module MiscHelpers
|
|
|
24
25
|
end
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
def stub_any_instance(klass, method, value)
|
|
29
|
+
klass.class_eval do
|
|
30
|
+
alias_method :"new_#{method}", method
|
|
31
|
+
|
|
32
|
+
define_method(method) do
|
|
33
|
+
if value.respond_to?(:call)
|
|
34
|
+
value.call
|
|
35
|
+
else
|
|
36
|
+
value
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
yield
|
|
42
|
+
ensure
|
|
43
|
+
klass.class_eval do
|
|
44
|
+
undef_method method
|
|
45
|
+
alias_method method, :"new_#{method}"
|
|
46
|
+
undef_method :"new_#{method}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def swap_wrapper(name = :default, wrapper = custom_wrapper)
|
|
51
|
+
old = SimpleForm.wrappers[name.to_s]
|
|
52
|
+
SimpleForm.wrappers[name.to_s] = wrapper
|
|
30
53
|
yield
|
|
31
54
|
ensure
|
|
32
|
-
SimpleForm.wrappers[name] = old
|
|
55
|
+
SimpleForm.wrappers[name.to_s] = old
|
|
33
56
|
end
|
|
34
57
|
|
|
35
58
|
def custom_wrapper
|
|
36
|
-
SimpleForm.build :
|
|
59
|
+
SimpleForm.build tag: :section, class: "custom_wrapper", pattern: false do |b|
|
|
37
60
|
b.use :pattern
|
|
38
|
-
b.wrapper :another, :
|
|
61
|
+
b.wrapper :another, class: "another_wrapper" do |ba|
|
|
39
62
|
ba.use :label
|
|
40
63
|
ba.use :input
|
|
41
64
|
end
|
|
42
|
-
b.wrapper :error_wrapper, :
|
|
43
|
-
be.use :error, :
|
|
65
|
+
b.wrapper :error_wrapper, tag: :div, class: "error_wrapper" do |be|
|
|
66
|
+
be.use :error, wrap_with: { tag: :span, class: "omg_error" }
|
|
67
|
+
end
|
|
68
|
+
b.use :hint, wrap_with: { class: "omg_hint" }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def custom_wrapper_with_wrapped_optional_component
|
|
73
|
+
SimpleForm.build tag: :section, class: "custom_wrapper" do |b|
|
|
74
|
+
b.wrapper tag: :div, class: 'no_output_wrapper' do |ba|
|
|
75
|
+
ba.optional :hint, wrap_with: { tag: :p, class: 'omg_hint' }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def custom_wrapper_with_unless_blank
|
|
81
|
+
SimpleForm.build tag: :section, class: "custom_wrapper" do |b|
|
|
82
|
+
b.wrapper tag: :div, class: 'no_output_wrapper', unless_blank: true do |ba|
|
|
83
|
+
ba.optional :hint, wrap_with: { tag: :p, class: 'omg_hint' }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def custom_wrapper_with_input_class
|
|
89
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
90
|
+
b.use :label
|
|
91
|
+
b.use :input, class: 'inline-class'
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def custom_wrapper_with_input_data_modal
|
|
96
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
97
|
+
b.use :label
|
|
98
|
+
b.use :input, data: { modal: 'data-modal', wrapper: 'data-wrapper' }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def custom_wrapper_with_input_aria_modal
|
|
103
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
104
|
+
b.use :label
|
|
105
|
+
b.use :input, aria: { modal: 'aria-modal', wrapper: 'aria-wrapper' }
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def custom_wrapper_with_label_class
|
|
110
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
111
|
+
b.use :label, class: 'inline-class'
|
|
112
|
+
b.use :input
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def custom_wrapper_with_input_attributes
|
|
117
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
118
|
+
b.use :input, data: { modal: true }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def custom_wrapper_with_label_input_class
|
|
123
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
124
|
+
b.use :label_input, class: 'inline-class'
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def custom_wrapper_with_wrapped_input
|
|
129
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
130
|
+
b.wrapper tag: :div, class: 'elem' do |component|
|
|
131
|
+
component.use :label
|
|
132
|
+
component.use :input, wrap_with: { tag: :div, class: 'input' }
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def custom_wrapper_with_wrapped_label
|
|
138
|
+
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
|
|
139
|
+
b.wrapper tag: :div, class: 'elem' do |component|
|
|
140
|
+
component.use :label, wrap_with: { tag: :div, class: 'label' }
|
|
141
|
+
component.use :input
|
|
44
142
|
end
|
|
45
|
-
b.use :hint, :wrap_with => { :class => "omg_hint" }
|
|
46
143
|
end
|
|
47
144
|
end
|
|
48
145
|
|
|
49
146
|
def custom_wrapper_without_top_level
|
|
50
|
-
SimpleForm.build :
|
|
147
|
+
SimpleForm.build tag: false, class: 'custom_wrapper_without_top_level' do |b|
|
|
148
|
+
b.use :label_input
|
|
149
|
+
b.use :hint, wrap_with: { tag: :span, class: :hint }
|
|
150
|
+
b.use :error, wrap_with: { tag: :span, class: :error }
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def custom_wrapper_without_class
|
|
155
|
+
SimpleForm.build tag: :div, wrapper_html: { id: 'custom_wrapper_without_class' } do |b|
|
|
156
|
+
b.use :label_input
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def custom_wrapper_with_label_html_option
|
|
161
|
+
SimpleForm.build tag: :div, class: "custom_wrapper", label_html: { class: 'extra-label-class' } do |b|
|
|
162
|
+
b.use :label_input
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def custom_wrapper_with_wrapped_label_input
|
|
167
|
+
SimpleForm.build tag: :section, class: "custom_wrapper", pattern: false do |b|
|
|
168
|
+
b.use :label_input, wrap_with: { tag: :div, class: :field }
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def custom_wrapper_with_additional_attributes
|
|
173
|
+
SimpleForm.build tag: :div, class: 'custom_wrapper', html: { data: { wrapper: :test }, title: 'some title' } do |b|
|
|
174
|
+
b.use :label_input
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def custom_wrapper_with_full_error
|
|
179
|
+
SimpleForm.build tag: :div, class: 'custom_wrapper' do |b|
|
|
180
|
+
b.use :full_error, wrap_with: { tag: :span, class: :error }
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def custom_wrapper_with_label_text
|
|
185
|
+
SimpleForm.build label_text: proc { |label, required| "**#{label}**" } do |b|
|
|
51
186
|
b.use :label_input
|
|
52
|
-
|
|
53
|
-
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def custom_wrapper_with_custom_label_component
|
|
191
|
+
SimpleForm.build tag: :span, class: 'custom_wrapper' do |b|
|
|
192
|
+
b.use :label_text
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def custom_wrapper_with_html5_components
|
|
197
|
+
SimpleForm.build tag: :span, class: 'custom_wrapper' do |b|
|
|
198
|
+
b.use :label_text
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def custom_wrapper_with_required_input
|
|
203
|
+
SimpleForm.build tag: :span, class: 'custom_wrapper' do |b|
|
|
204
|
+
b.use :html5
|
|
205
|
+
b.use :input, required: true
|
|
54
206
|
end
|
|
55
207
|
end
|
|
56
208
|
|
|
57
209
|
def custom_form_for(object, *args, &block)
|
|
58
|
-
simple_form_for(object, *
|
|
210
|
+
simple_form_for(object, *args, { builder: CustomFormBuilder }, &block)
|
|
59
211
|
end
|
|
60
212
|
|
|
61
213
|
def custom_mapping_form_for(object, *args, &block)
|
|
62
|
-
simple_form_for(object, *
|
|
214
|
+
simple_form_for(object, *args, { builder: CustomMapTypeFormBuilder }, &block)
|
|
63
215
|
end
|
|
64
216
|
|
|
65
217
|
def with_concat_form_for(*args, &block)
|
|
66
|
-
concat simple_form_for(*args, &block)
|
|
218
|
+
concat simple_form_for(*args, &(block || proc {}))
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def with_concat_fields_for(*args, &block)
|
|
222
|
+
concat simple_fields_for(*args, &block)
|
|
67
223
|
end
|
|
68
224
|
|
|
69
225
|
def with_concat_custom_form_for(*args, &block)
|
|
@@ -80,19 +236,19 @@ module MiscHelpers
|
|
|
80
236
|
end
|
|
81
237
|
end
|
|
82
238
|
|
|
83
|
-
def with_input_for(object, attribute_name, type, options={})
|
|
239
|
+
def with_input_for(object, attribute_name, type, options = {})
|
|
84
240
|
with_concat_form_for(object) do |f|
|
|
85
|
-
f.input(attribute_name, options.merge(:
|
|
241
|
+
f.input(attribute_name, options.merge(as: type))
|
|
86
242
|
end
|
|
87
243
|
end
|
|
88
244
|
end
|
|
89
245
|
|
|
90
246
|
class CustomFormBuilder < SimpleForm::FormBuilder
|
|
91
247
|
def input(attribute_name, *args, &block)
|
|
92
|
-
super(attribute_name, *
|
|
248
|
+
super(attribute_name, *args, { input_html: { class: 'custom' } }, &block)
|
|
93
249
|
end
|
|
94
250
|
end
|
|
95
251
|
|
|
96
252
|
class CustomMapTypeFormBuilder < SimpleForm::FormBuilder
|
|
97
|
-
map_type :custom_type, :
|
|
253
|
+
map_type :custom_type, to: SimpleForm::Inputs::StringInput
|
|
98
254
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
class MockController
|
|
2
3
|
attr_writer :action_name
|
|
3
4
|
|
|
@@ -9,16 +10,21 @@ class MockController
|
|
|
9
10
|
defined?(@action_name) ? @action_name : "edit"
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def url_for(*
|
|
13
|
+
def url_for(*)
|
|
13
14
|
"http://example.com"
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def
|
|
17
|
-
|
|
17
|
+
def url_options
|
|
18
|
+
{}
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
def
|
|
23
|
-
|
|
21
|
+
def polymorphic_mappings(*); {}; end
|
|
22
|
+
|
|
23
|
+
def hash_for_user_path(*); end
|
|
24
|
+
|
|
25
|
+
def hash_for_validating_user_path(*); end
|
|
26
|
+
|
|
27
|
+
def hash_for_other_validating_user_path(*); end
|
|
28
|
+
|
|
29
|
+
def hash_for_users_path(*); end
|
|
24
30
|
end
|
data/test/support/models.rb
CHANGED
|
@@ -1,23 +1,61 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
Association = Struct.new(:klass, :name, :macro, :scope, :options)
|
|
2
3
|
|
|
3
|
-
Column = Struct.new(:name, :type, :limit)
|
|
4
|
-
|
|
4
|
+
Column = Struct.new(:name, :type, :limit) do
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Relation = Struct.new(:records) do
|
|
8
|
+
delegate :each, to: :records
|
|
9
|
+
|
|
10
|
+
def where(conditions = nil)
|
|
11
|
+
self.class.new conditions ? [records.first] : records
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def order(conditions = nil)
|
|
15
|
+
self.class.new conditions ? records.last : records
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
alias_method :to_a, :records
|
|
19
|
+
alias_method :to_ary, :records
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Decorator = Struct.new(:object) do
|
|
23
|
+
def to_model
|
|
24
|
+
object
|
|
25
|
+
end
|
|
26
|
+
end
|
|
5
27
|
|
|
6
|
-
|
|
28
|
+
Picture = Struct.new(:id, :name) do
|
|
7
29
|
extend ActiveModel::Naming
|
|
8
30
|
include ActiveModel::Conversion
|
|
9
31
|
|
|
10
|
-
def self.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
32
|
+
def self.where(conditions = nil)
|
|
33
|
+
if conditions.is_a?(Hash) && conditions[:name]
|
|
34
|
+
all.to_a.last
|
|
35
|
+
else
|
|
36
|
+
all
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.all
|
|
41
|
+
Relation.new((1..3).map { |i| new(i, "#{name} #{i}") })
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Company = Struct.new(:id, :name) do
|
|
46
|
+
extend ActiveModel::Naming
|
|
47
|
+
include ActiveModel::Conversion
|
|
48
|
+
|
|
49
|
+
class << self
|
|
50
|
+
delegate :order, :where, to: :_relation
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self._relation
|
|
16
54
|
all
|
|
17
55
|
end
|
|
18
56
|
|
|
19
|
-
def self.
|
|
20
|
-
(
|
|
57
|
+
def self.all
|
|
58
|
+
Relation.new((1..3).map { |i| new(i, "#{name} #{i}") })
|
|
21
59
|
end
|
|
22
60
|
|
|
23
61
|
def persisted?
|
|
@@ -25,15 +63,23 @@ class Company < Struct.new(:id, :name)
|
|
|
25
63
|
end
|
|
26
64
|
end
|
|
27
65
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
66
|
+
Friend = Struct.new(:id, :name) do
|
|
67
|
+
extend ActiveModel::Naming
|
|
68
|
+
include ActiveModel::Conversion
|
|
69
|
+
|
|
70
|
+
def self.all
|
|
71
|
+
(1..3).map { |i| new(i, "#{name} #{i}") }
|
|
31
72
|
end
|
|
32
|
-
end
|
|
33
73
|
|
|
34
|
-
|
|
74
|
+
def persisted?
|
|
75
|
+
true
|
|
76
|
+
end
|
|
35
77
|
end
|
|
36
78
|
|
|
79
|
+
class Tag < Company; end
|
|
80
|
+
|
|
81
|
+
TagGroup = Struct.new(:id, :name, :tags)
|
|
82
|
+
|
|
37
83
|
class User
|
|
38
84
|
extend ActiveModel::Naming
|
|
39
85
|
include ActiveModel::Conversion
|
|
@@ -42,9 +88,22 @@ class User
|
|
|
42
88
|
:description, :created_at, :updated_at, :credit_limit, :password, :url,
|
|
43
89
|
:delivery_time, :born_at, :special_company_id, :country, :tags, :tag_ids,
|
|
44
90
|
:avatar, :home_picture, :email, :status, :residence_country, :phone_number,
|
|
45
|
-
:post_count, :lock_version, :amount, :attempts, :action, :credit_card, :gender
|
|
91
|
+
:post_count, :lock_version, :amount, :attempts, :action, :credit_card, :gender,
|
|
92
|
+
:extra_special_company_id, :pictures, :picture_ids, :special_pictures,
|
|
93
|
+
:special_picture_ids, :uuid, :friends, :friend_ids, :special_tags, :special_tag_ids
|
|
94
|
+
|
|
95
|
+
def self.build(extra_attributes = {})
|
|
96
|
+
attributes = {
|
|
97
|
+
id: 1,
|
|
98
|
+
name: 'New in SimpleForm!',
|
|
99
|
+
description: 'Hello!',
|
|
100
|
+
created_at: Time.now
|
|
101
|
+
}.merge! extra_attributes
|
|
102
|
+
|
|
103
|
+
new attributes
|
|
104
|
+
end
|
|
46
105
|
|
|
47
|
-
def initialize(options={})
|
|
106
|
+
def initialize(options = {})
|
|
48
107
|
@new_record = false
|
|
49
108
|
options.each do |key, value|
|
|
50
109
|
send("#{key}=", value)
|
|
@@ -82,11 +141,58 @@ class User
|
|
|
82
141
|
when :attempts then :integer
|
|
83
142
|
when :action then :string
|
|
84
143
|
when :credit_card then :string
|
|
144
|
+
when :uuid then :uuid
|
|
85
145
|
end
|
|
86
146
|
Column.new(attribute, column_type, limit)
|
|
87
147
|
end
|
|
88
148
|
|
|
89
|
-
|
|
149
|
+
begin
|
|
150
|
+
require 'active_model/type'
|
|
151
|
+
begin
|
|
152
|
+
ActiveModel::Type.lookup(:text)
|
|
153
|
+
rescue ArgumentError # :text is no longer an ActiveModel::Type
|
|
154
|
+
# But we don't want our tests to depend on ActiveRecord
|
|
155
|
+
class ::ActiveModel::Type::Text < ActiveModel::Type::String
|
|
156
|
+
def type; :text; end
|
|
157
|
+
end
|
|
158
|
+
ActiveModel::Type.register(:text, ActiveModel::Type::Text)
|
|
159
|
+
end
|
|
160
|
+
def type_for_attribute(attribute)
|
|
161
|
+
column_type, limit = case attribute
|
|
162
|
+
when 'name', 'status', 'password' then [:string, 100]
|
|
163
|
+
when 'description' then [:text, 200]
|
|
164
|
+
when 'age' then :integer
|
|
165
|
+
when 'credit_limit' then [:decimal, 15]
|
|
166
|
+
when 'active' then :boolean
|
|
167
|
+
when 'born_at' then :date
|
|
168
|
+
when 'delivery_time' then :time
|
|
169
|
+
when 'created_at' then :datetime
|
|
170
|
+
when 'updated_at' then :datetime
|
|
171
|
+
when 'lock_version' then :integer
|
|
172
|
+
when 'home_picture' then :string
|
|
173
|
+
when 'amount' then :integer
|
|
174
|
+
when 'attempts' then :integer
|
|
175
|
+
when 'action' then :string
|
|
176
|
+
when 'credit_card' then :string
|
|
177
|
+
when 'uuid' then :string
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
ActiveModel::Type.lookup(column_type, limit: limit)
|
|
181
|
+
end
|
|
182
|
+
rescue LoadError
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def has_attribute?(attribute)
|
|
186
|
+
case attribute.to_sym
|
|
187
|
+
when :name, :status, :password, :description, :age,
|
|
188
|
+
:credit_limit, :active, :born_at, :delivery_time,
|
|
189
|
+
:created_at, :updated_at, :lock_version, :home_picture,
|
|
190
|
+
:amount, :attempts, :action, :credit_card, :uuid then true
|
|
191
|
+
else false
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def self.human_attribute_name(attribute, options = {})
|
|
90
196
|
case attribute
|
|
91
197
|
when 'name'
|
|
92
198
|
'Super User Name!'
|
|
@@ -95,68 +201,80 @@ class User
|
|
|
95
201
|
when 'company'
|
|
96
202
|
'Company Human Name!'
|
|
97
203
|
else
|
|
98
|
-
attribute.humanize
|
|
204
|
+
attribute.to_s.humanize
|
|
99
205
|
end
|
|
100
206
|
end
|
|
101
207
|
|
|
102
208
|
def self.reflect_on_association(association)
|
|
103
209
|
case association
|
|
104
210
|
when :company
|
|
105
|
-
Association.new(Company, association, :belongs_to, {})
|
|
211
|
+
Association.new(Company, association, :belongs_to, nil, {})
|
|
106
212
|
when :tags
|
|
107
|
-
Association.new(Tag, association, :has_many, {})
|
|
213
|
+
Association.new(Tag, association, :has_many, nil, {})
|
|
214
|
+
when :special_tags
|
|
215
|
+
Association.new(Tag, association, :has_many, ->(user) { where(id: user.id) }, {})
|
|
108
216
|
when :first_company
|
|
109
|
-
Association.new(Company, association, :has_one, {})
|
|
217
|
+
Association.new(Company, association, :has_one, nil, {})
|
|
110
218
|
when :special_company
|
|
111
|
-
Association.new(Company, association, :belongs_to,
|
|
219
|
+
Association.new(Company, association, :belongs_to, nil, conditions: { id: 1 })
|
|
220
|
+
when :extra_special_company
|
|
221
|
+
Association.new(Company, association, :belongs_to, nil, conditions: proc { { id: self.id } })
|
|
222
|
+
when :pictures
|
|
223
|
+
Association.new(Picture, association, :has_many, nil, {})
|
|
224
|
+
when :special_pictures
|
|
225
|
+
Association.new(Picture, association, :has_many, proc { where(name: self.name) }, {})
|
|
226
|
+
when :friends
|
|
227
|
+
Association.new(Friend, association, :has_many, nil, {})
|
|
112
228
|
end
|
|
113
229
|
end
|
|
114
230
|
|
|
115
231
|
def errors
|
|
116
232
|
@errors ||= begin
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
233
|
+
errors = ActiveModel::Errors.new(self)
|
|
234
|
+
errors.add(:name, "cannot be blank")
|
|
235
|
+
errors.add(:description, 'must be longer than 15 characters')
|
|
236
|
+
errors.add(:age, 'is not a number')
|
|
237
|
+
errors.add(:age, 'must be greater than 18')
|
|
238
|
+
errors.add(:company, 'company must be present')
|
|
239
|
+
errors.add(:company_id, 'must be valid')
|
|
240
|
+
errors
|
|
125
241
|
end
|
|
126
242
|
end
|
|
127
243
|
|
|
128
244
|
def self.readonly_attributes
|
|
129
|
-
[
|
|
245
|
+
["credit_card"]
|
|
130
246
|
end
|
|
131
247
|
end
|
|
132
248
|
|
|
133
249
|
class ValidatingUser < User
|
|
134
250
|
include ActiveModel::Validations
|
|
135
|
-
validates :name, :
|
|
136
|
-
validates :company, :
|
|
137
|
-
validates :age, :
|
|
138
|
-
validates :amount, :
|
|
251
|
+
validates :name, presence: true
|
|
252
|
+
validates :company, presence: true
|
|
253
|
+
validates :age, presence: true, if: proc { |user| user.name }
|
|
254
|
+
validates :amount, presence: true, unless: proc { |user| user.age }
|
|
139
255
|
|
|
140
|
-
validates :action, :
|
|
141
|
-
validates :credit_limit, :
|
|
142
|
-
validates :phone_number, :
|
|
256
|
+
validates :action, presence: true, on: :create
|
|
257
|
+
validates :credit_limit, presence: true, on: :save
|
|
258
|
+
validates :phone_number, presence: true, on: :update
|
|
143
259
|
|
|
144
260
|
validates_numericality_of :age,
|
|
145
|
-
:
|
|
146
|
-
:
|
|
147
|
-
:
|
|
261
|
+
greater_than_or_equal_to: 18,
|
|
262
|
+
less_than_or_equal_to: 99,
|
|
263
|
+
only_integer: true
|
|
148
264
|
validates_numericality_of :amount,
|
|
149
|
-
:
|
|
150
|
-
:
|
|
151
|
-
:
|
|
265
|
+
greater_than: :min_amount,
|
|
266
|
+
less_than: :max_amount,
|
|
267
|
+
only_integer: true
|
|
152
268
|
validates_numericality_of :attempts,
|
|
153
|
-
:
|
|
154
|
-
:
|
|
155
|
-
:
|
|
156
|
-
validates_length_of :name, :
|
|
157
|
-
validates_length_of :description, :
|
|
158
|
-
|
|
159
|
-
|
|
269
|
+
greater_than_or_equal_to: :min_attempts,
|
|
270
|
+
less_than_or_equal_to: :max_attempts,
|
|
271
|
+
only_integer: true
|
|
272
|
+
validates_length_of :name, maximum: 25, minimum: 5
|
|
273
|
+
validates_length_of :description, in: 15..50
|
|
274
|
+
if ActionPack::VERSION::STRING < '5'
|
|
275
|
+
validates_length_of :action, maximum: 10, tokenizer: ->(str) { str.scan(/\w+/) }
|
|
276
|
+
end
|
|
277
|
+
validates_length_of :home_picture, is: 12
|
|
160
278
|
|
|
161
279
|
def min_amount
|
|
162
280
|
10
|
|
@@ -178,26 +296,21 @@ end
|
|
|
178
296
|
class OtherValidatingUser < User
|
|
179
297
|
include ActiveModel::Validations
|
|
180
298
|
validates_numericality_of :age,
|
|
181
|
-
:
|
|
182
|
-
:
|
|
183
|
-
:
|
|
299
|
+
greater_than: 17,
|
|
300
|
+
less_than: 100,
|
|
301
|
+
only_integer: true
|
|
184
302
|
validates_numericality_of :amount,
|
|
185
|
-
:
|
|
186
|
-
:
|
|
187
|
-
:
|
|
303
|
+
greater_than: proc { |user| user.age },
|
|
304
|
+
less_than: proc { |user| user.age + 100 },
|
|
305
|
+
only_integer: true
|
|
188
306
|
validates_numericality_of :attempts,
|
|
189
|
-
:
|
|
190
|
-
:
|
|
191
|
-
:
|
|
192
|
-
|
|
193
|
-
validates_format_of :country, :with => /\w+/
|
|
307
|
+
greater_than_or_equal_to: proc { |user| user.age },
|
|
308
|
+
less_than_or_equal_to: proc { |user| user.age + 100 },
|
|
309
|
+
only_integer: true
|
|
194
310
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
else
|
|
199
|
-
validates_format_of :name, :with => /\w+/
|
|
200
|
-
end
|
|
311
|
+
validates_format_of :country, with: /\w+/
|
|
312
|
+
validates_format_of :name, with: proc { /\w+/ }
|
|
313
|
+
validates_format_of :description, without: /\d+/
|
|
201
314
|
end
|
|
202
315
|
|
|
203
316
|
class HashBackedAuthor < Hash
|
|
@@ -210,3 +323,6 @@ class HashBackedAuthor < Hash
|
|
|
210
323
|
'hash backed author'
|
|
211
324
|
end
|
|
212
325
|
end
|
|
326
|
+
|
|
327
|
+
class UserNumber1And2 < User
|
|
328
|
+
end
|