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.

Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +97 -198
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +572 -296
  5. data/lib/generators/simple_form/install_generator.rb +17 -7
  6. data/lib/generators/simple_form/templates/README +3 -4
  7. data/lib/generators/simple_form/templates/_form.html.erb +1 -0
  8. data/lib/generators/simple_form/templates/_form.html.haml +1 -0
  9. data/lib/generators/simple_form/templates/config/initializers/{simple_form.rb.tt → simple_form.rb} +57 -63
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +155 -0
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +111 -0
  12. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +14 -7
  13. data/lib/simple_form/action_view_extensions/builder.rb +5 -305
  14. data/lib/simple_form/action_view_extensions/form_helper.rb +18 -20
  15. data/lib/simple_form/components/errors.rb +30 -3
  16. data/lib/simple_form/components/hints.rb +10 -3
  17. data/lib/simple_form/components/html5.rb +17 -3
  18. data/lib/simple_form/components/label_input.rb +21 -2
  19. data/lib/simple_form/components/labels.rb +16 -11
  20. data/lib/simple_form/components/maxlength.rb +19 -12
  21. data/lib/simple_form/components/min_max.rb +4 -2
  22. data/lib/simple_form/components/minlength.rb +48 -0
  23. data/lib/simple_form/components/pattern.rb +5 -4
  24. data/lib/simple_form/components/placeholders.rb +3 -2
  25. data/lib/simple_form/components/readonly.rb +3 -2
  26. data/lib/simple_form/components.rb +15 -11
  27. data/lib/simple_form/error_notification.rb +4 -3
  28. data/lib/simple_form/form_builder.rb +283 -105
  29. data/lib/simple_form/helpers/autofocus.rb +1 -0
  30. data/lib/simple_form/helpers/disabled.rb +1 -0
  31. data/lib/simple_form/helpers/readonly.rb +1 -0
  32. data/lib/simple_form/helpers/required.rb +1 -0
  33. data/lib/simple_form/helpers/validators.rb +4 -3
  34. data/lib/simple_form/helpers.rb +7 -6
  35. data/lib/simple_form/i18n_cache.rb +1 -0
  36. data/lib/simple_form/inputs/base.rb +76 -23
  37. data/lib/simple_form/inputs/block_input.rb +3 -2
  38. data/lib/simple_form/inputs/boolean_input.rb +55 -16
  39. data/lib/simple_form/inputs/collection_check_boxes_input.rb +2 -1
  40. data/lib/simple_form/inputs/collection_input.rb +41 -18
  41. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +11 -19
  42. data/lib/simple_form/inputs/collection_select_input.rb +5 -2
  43. data/lib/simple_form/inputs/date_time_input.rb +23 -12
  44. data/lib/simple_form/inputs/file_input.rb +5 -2
  45. data/lib/simple_form/inputs/grouped_collection_select_input.rb +16 -3
  46. data/lib/simple_form/inputs/hidden_input.rb +5 -2
  47. data/lib/simple_form/inputs/numeric_input.rb +4 -8
  48. data/lib/simple_form/inputs/password_input.rb +6 -4
  49. data/lib/simple_form/inputs/priority_input.rb +5 -2
  50. data/lib/simple_form/inputs/range_input.rb +2 -1
  51. data/lib/simple_form/inputs/string_input.rb +6 -4
  52. data/lib/simple_form/inputs/text_input.rb +6 -3
  53. data/lib/simple_form/inputs.rb +20 -17
  54. data/lib/simple_form/map_type.rb +1 -0
  55. data/lib/simple_form/railtie.rb +15 -0
  56. data/lib/simple_form/tags.rb +69 -0
  57. data/lib/simple_form/version.rb +2 -1
  58. data/lib/simple_form/wrappers/builder.rb +12 -35
  59. data/lib/simple_form/wrappers/leaf.rb +29 -0
  60. data/lib/simple_form/wrappers/many.rb +12 -7
  61. data/lib/simple_form/wrappers/root.rb +7 -4
  62. data/lib/simple_form/wrappers/single.rb +12 -3
  63. data/lib/simple_form/wrappers.rb +3 -1
  64. data/lib/simple_form.rb +118 -63
  65. data/test/action_view_extensions/builder_test.rb +230 -164
  66. data/test/action_view_extensions/form_helper_test.rb +107 -39
  67. data/test/components/label_test.rb +105 -87
  68. data/test/form_builder/association_test.rb +131 -62
  69. data/test/form_builder/button_test.rb +15 -14
  70. data/test/form_builder/error_notification_test.rb +11 -10
  71. data/test/form_builder/error_test.rb +188 -34
  72. data/test/form_builder/general_test.rb +247 -102
  73. data/test/form_builder/hint_test.rb +59 -32
  74. data/test/form_builder/input_field_test.rb +138 -25
  75. data/test/form_builder/label_test.rb +84 -13
  76. data/test/form_builder/wrapper_test.rb +236 -33
  77. data/test/generators/simple_form_generator_test.rb +15 -4
  78. data/test/inputs/boolean_input_test.rb +147 -13
  79. data/test/inputs/collection_check_boxes_input_test.rb +166 -71
  80. data/test/inputs/collection_radio_buttons_input_test.rb +229 -113
  81. data/test/inputs/collection_select_input_test.rb +222 -85
  82. data/test/inputs/datetime_input_test.rb +134 -47
  83. data/test/inputs/disabled_test.rb +62 -21
  84. data/test/inputs/discovery_test.rb +70 -10
  85. data/test/inputs/file_input_test.rb +4 -3
  86. data/test/inputs/general_test.rb +90 -26
  87. data/test/inputs/grouped_collection_select_input_test.rb +88 -23
  88. data/test/inputs/hidden_input_test.rb +7 -5
  89. data/test/inputs/numeric_input_test.rb +56 -46
  90. data/test/inputs/priority_input_test.rb +31 -16
  91. data/test/inputs/readonly_test.rb +68 -27
  92. data/test/inputs/required_test.rb +63 -18
  93. data/test/inputs/string_input_test.rb +76 -51
  94. data/test/inputs/text_input_test.rb +21 -8
  95. data/test/simple_form_test.rb +9 -0
  96. data/test/support/discovery_inputs.rb +39 -2
  97. data/test/support/misc_helpers.rb +176 -20
  98. data/test/support/mock_controller.rb +13 -7
  99. data/test/support/models.rb +187 -71
  100. data/test/test_helper.rb +38 -39
  101. metadata +53 -39
  102. data/lib/simple_form/core_ext/hash.rb +0 -16
  103. 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, :text => value, :count => 0)
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 swap_wrapper(name=:default, wrapper=self.custom_wrapper)
28
- old = SimpleForm.wrappers[name]
29
- SimpleForm.wrappers[name] = wrapper
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 :tag => :section, :class => "custom_wrapper", :pattern => false do |b|
59
+ SimpleForm.build tag: :section, class: "custom_wrapper", pattern: false do |b|
37
60
  b.use :pattern
38
- b.wrapper :another, :class => "another_wrapper" do |ba|
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, :tag => :div, :class => "error_wrapper" do |be|
43
- be.use :error, :wrap_with => { :tag => :span, :class => "omg_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 :tag => false, :class => 'custom_wrapper_without_top_level' do |b|
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
- b.use :hint, :wrap_with => { :tag => :span, :class => :hint }
53
- b.use :error, :wrap_with => { :tag => :span, :class => :error }
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, *(args << { :builder => CustomFormBuilder }), &block)
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, *(args << { :builder => CustomMapTypeFormBuilder }), &block)
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(:as => type))
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, *(args << { :input_html => { :class => 'custom' } }), &block)
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, :to => SimpleForm::Inputs::StringInput
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(*args)
13
+ def url_for(*)
13
14
  "http://example.com"
14
15
  end
15
16
 
16
- def url_helpers
17
- self
17
+ def url_options
18
+ {}
18
19
  end
19
20
 
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
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
@@ -1,23 +1,61 @@
1
- require 'ostruct'
1
+ # frozen_string_literal: true
2
+ Association = Struct.new(:klass, :name, :macro, :scope, :options)
2
3
 
3
- Column = Struct.new(:name, :type, :limit)
4
- Association = Struct.new(:klass, :name, :macro, :options)
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
- class Company < Struct.new(:id, :name)
28
+ Picture = Struct.new(:id, :name) do
7
29
  extend ActiveModel::Naming
8
30
  include ActiveModel::Conversion
9
31
 
10
- def self.all(options={})
11
- all = (1..3).map{|i| Company.new(i, "Company #{i}")}
12
- return [all.first] if options[:conditions].present?
13
- return [all.last] if options[:order].present?
14
- return all[0..1] if options[:include].present?
15
- return all[1..2] if options[:joins].present?
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.merge_conditions(a, b)
20
- (a || {}).merge(b || {})
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
- class Tag < Company
29
- def self.all(options={})
30
- (1..3).map{|i| Tag.new(i, "Tag #{i}")}
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
- class TagGroup < Struct.new(:id, :name, :tags)
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
- def self.human_attribute_name(attribute)
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, { :conditions => { :id => 1 } })
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
- hash = Hash.new { |h,k| h[k] = [] }
118
- hash.merge!(
119
- :name => ["can't be blank"],
120
- :description => ["must be longer than 15 characters"],
121
- :age => ["is not a number", "must be greater than 18"],
122
- :company => ["company must be present"],
123
- :company_id => ["must be valid"]
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
- [:credit_card]
245
+ ["credit_card"]
130
246
  end
131
247
  end
132
248
 
133
249
  class ValidatingUser < User
134
250
  include ActiveModel::Validations
135
- validates :name, :presence => true
136
- validates :company, :presence => true
137
- validates :age, :presence => true, :if => Proc.new { |user| user.name }
138
- validates :amount, :presence => true, :unless => Proc.new { |user| user.age }
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, :presence => true, :on => :create
141
- validates :credit_limit, :presence => true, :on => :save
142
- validates :phone_number, :presence => true, :on => :update
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
- :greater_than_or_equal_to => 18,
146
- :less_than_or_equal_to => 99,
147
- :only_integer => true
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
- :greater_than => :min_amount,
150
- :less_than => :max_amount,
151
- :only_integer => true
265
+ greater_than: :min_amount,
266
+ less_than: :max_amount,
267
+ only_integer: true
152
268
  validates_numericality_of :attempts,
153
- :greater_than_or_equal_to => :min_attempts,
154
- :less_than_or_equal_to => :max_attempts,
155
- :only_integer => true
156
- validates_length_of :name, :maximum => 25
157
- validates_length_of :description, :maximum => 50
158
- validates_length_of :action, :maximum => 10, :tokenizer => lambda { |str| str.scan(/\w+/) }
159
- validates_length_of :home_picture, :is => 12
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
- :greater_than => 17,
182
- :less_than => 100,
183
- :only_integer => true
299
+ greater_than: 17,
300
+ less_than: 100,
301
+ only_integer: true
184
302
  validates_numericality_of :amount,
185
- :greater_than => Proc.new { |user| user.age },
186
- :less_than => Proc.new { |user| user.age + 100},
187
- :only_integer => true
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
- :greater_than_or_equal_to => Proc.new { |user| user.age },
190
- :less_than_or_equal_to => Proc.new { |user| user.age + 100},
191
- :only_integer => true
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
- # TODO: Remove this check when we drop Rails 3.0 support
196
- if ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR >= 1
197
- validates_format_of :name, :with => Proc.new { /\w+/ }
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