simple_form 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +64 -16
  5. data/lib/generators/simple_form/install_generator.rb +2 -2
  6. data/lib/generators/simple_form/templates/README +1 -1
  7. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +1 -1
  8. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +126 -194
  9. data/lib/simple_form/components/label_input.rb +1 -1
  10. data/lib/simple_form/form_builder.rb +2 -6
  11. data/lib/simple_form/inputs/boolean_input.rb +6 -2
  12. data/lib/simple_form/inputs/grouped_collection_select_input.rb +1 -1
  13. data/lib/simple_form/inputs/priority_input.rb +16 -2
  14. data/lib/simple_form/railtie.rb +4 -0
  15. data/lib/simple_form/version.rb +1 -1
  16. data/lib/simple_form/wrappers/leaf.rb +1 -1
  17. data/lib/simple_form.rb +9 -5
  18. metadata +11 -83
  19. data/test/action_view_extensions/builder_test.rb +0 -634
  20. data/test/action_view_extensions/form_helper_test.rb +0 -172
  21. data/test/components/custom_components_test.rb +0 -62
  22. data/test/components/label_test.rb +0 -352
  23. data/test/form_builder/association_test.rb +0 -252
  24. data/test/form_builder/button_test.rb +0 -48
  25. data/test/form_builder/error_notification_test.rb +0 -80
  26. data/test/form_builder/error_test.rb +0 -281
  27. data/test/form_builder/general_test.rb +0 -539
  28. data/test/form_builder/hint_test.rb +0 -150
  29. data/test/form_builder/input_field_test.rb +0 -195
  30. data/test/form_builder/label_test.rb +0 -136
  31. data/test/form_builder/wrapper_test.rb +0 -378
  32. data/test/generators/simple_form_generator_test.rb +0 -43
  33. data/test/inputs/boolean_input_test.rb +0 -243
  34. data/test/inputs/collection_check_boxes_input_test.rb +0 -323
  35. data/test/inputs/collection_radio_buttons_input_test.rb +0 -446
  36. data/test/inputs/collection_select_input_test.rb +0 -380
  37. data/test/inputs/color_input_test.rb +0 -10
  38. data/test/inputs/datetime_input_test.rb +0 -176
  39. data/test/inputs/disabled_test.rb +0 -92
  40. data/test/inputs/discovery_test.rb +0 -142
  41. data/test/inputs/file_input_test.rb +0 -17
  42. data/test/inputs/general_test.rb +0 -133
  43. data/test/inputs/grouped_collection_select_input_test.rb +0 -183
  44. data/test/inputs/hidden_input_test.rb +0 -32
  45. data/test/inputs/numeric_input_test.rb +0 -177
  46. data/test/inputs/priority_input_test.rb +0 -50
  47. data/test/inputs/readonly_test.rb +0 -102
  48. data/test/inputs/required_test.rb +0 -158
  49. data/test/inputs/rich_text_area_input_test.rb +0 -15
  50. data/test/inputs/string_input_test.rb +0 -158
  51. data/test/inputs/text_input_test.rb +0 -37
  52. data/test/simple_form_test.rb +0 -18
  53. data/test/support/discovery_inputs.rb +0 -65
  54. data/test/support/misc_helpers.rb +0 -274
  55. data/test/support/mock_controller.rb +0 -30
  56. data/test/support/models.rb +0 -357
  57. data/test/test_helper.rb +0 -95
@@ -1,274 +0,0 @@
1
- # frozen_string_literal: true
2
- module MiscHelpers
3
- def store_translations(locale, translations, &block)
4
- I18n.backend.store_translations locale, translations
5
- yield
6
- ensure
7
- I18n.reload!
8
- I18n.backend.send :init_translations
9
- end
10
-
11
- def assert_no_select(selector, value = nil)
12
- assert_select(selector, text: value, count: 0)
13
- end
14
-
15
- def swap(object, new_values)
16
- old_values = {}
17
- new_values.each do |key, value|
18
- old_values[key] = object.send key
19
- object.send :"#{key}=", value
20
- end
21
- yield
22
- ensure
23
- old_values.each do |key, value|
24
- object.send :"#{key}=", value
25
- end
26
- end
27
-
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
53
- yield
54
- ensure
55
- SimpleForm.wrappers[name.to_s] = old
56
- end
57
-
58
- def custom_wrapper
59
- SimpleForm.build tag: :section, class: "custom_wrapper", pattern: false do |b|
60
- b.use :pattern
61
- b.wrapper :another, class: "another_wrapper" do |ba|
62
- ba.use :label
63
- ba.use :input
64
- end
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
142
- end
143
- end
144
- end
145
-
146
- def custom_wrapper_without_top_level
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|
186
- b.use :label_input
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
206
- end
207
- end
208
-
209
- def custom_wrapper_with_input_error_class
210
- SimpleForm.build tag: :div, class: "custom_wrapper", error_class: :field_with_errors do |b|
211
- b.use :label
212
- b.use :input, class: 'inline-class', error_class: 'is-invalid'
213
- end
214
- end
215
-
216
- def custom_wrapper_with_input_valid_class(valid_class: :field_without_errors)
217
- SimpleForm.build tag: :div, class: "custom_wrapper", valid_class: valid_class do |b|
218
- b.use :label
219
- b.use :input, class: 'inline-class', valid_class: 'is-valid'
220
- end
221
- end
222
-
223
- def custom_form_for(object, *args, &block)
224
- simple_form_for(object, *args, { builder: CustomFormBuilder }, &block)
225
- end
226
-
227
- def custom_mapping_form_for(object, *args, &block)
228
- simple_form_for(object, *args, { builder: CustomMapTypeFormBuilder }, &block)
229
- end
230
-
231
- def with_concat_form_for(*args, &block)
232
- concat simple_form_for(*args, &(block || proc {}))
233
- end
234
-
235
- def with_concat_fields_for(*args, &block)
236
- concat simple_fields_for(*args, &block)
237
- end
238
-
239
- def with_concat_custom_form_for(*args, &block)
240
- concat custom_form_for(*args, &block)
241
- end
242
-
243
- def with_concat_custom_mapping_form_for(*args, &block)
244
- concat custom_mapping_form_for(*args, &block)
245
- end
246
-
247
- def with_form_for(object, *args, &block)
248
- with_concat_form_for(object) do |f|
249
- f.input(*args, &block)
250
- end
251
- end
252
-
253
- def with_input_for(object, attribute_name, type, options = {})
254
- with_concat_form_for(object) do |f|
255
- f.input(attribute_name, options.merge(as: type))
256
- end
257
- end
258
-
259
- def with_input_field_for(object, *args)
260
- with_concat_form_for(object) do |f|
261
- f.input_field(*args)
262
- end
263
- end
264
- end
265
-
266
- class CustomFormBuilder < SimpleForm::FormBuilder
267
- def input(attribute_name, *args, &block)
268
- super(attribute_name, *args, { input_html: { class: 'custom' } }, &block)
269
- end
270
- end
271
-
272
- class CustomMapTypeFormBuilder < SimpleForm::FormBuilder
273
- map_type :custom_type, to: SimpleForm::Inputs::StringInput
274
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
- class MockController
3
- attr_writer :action_name
4
-
5
- def _routes
6
- self
7
- end
8
-
9
- def action_name
10
- defined?(@action_name) ? @action_name : "edit"
11
- end
12
-
13
- def url_for(*)
14
- "http://example.com"
15
- end
16
-
17
- def url_options
18
- {}
19
- end
20
-
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
30
- end
@@ -1,357 +0,0 @@
1
- # frozen_string_literal: true
2
- Association = Struct.new(:klass, :name, :macro, :scope, :options)
3
-
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
27
-
28
- Picture = Struct.new(:id, :name) do
29
- extend ActiveModel::Naming
30
- include ActiveModel::Conversion
31
-
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
54
- all
55
- end
56
-
57
- def self.all
58
- Relation.new((1..3).map { |i| new(i, "#{name} #{i}") })
59
- end
60
-
61
- def persisted?
62
- true
63
- end
64
- end
65
-
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}") }
72
- end
73
-
74
- def persisted?
75
- true
76
- end
77
- end
78
-
79
- class Tag < Company
80
- def group_method
81
- ["category-1"]
82
- end
83
- end
84
-
85
- TagGroup = Struct.new(:id, :name, :tags)
86
-
87
- class User
88
- extend ActiveModel::Naming
89
- include ActiveModel::Conversion
90
-
91
- attr_accessor :id, :name, :company, :company_id, :time_zone, :active, :age,
92
- :description, :created_at, :updated_at, :credit_limit, :password, :url,
93
- :delivery_time, :born_at, :special_company_id, :country, :tags, :tag_ids,
94
- :avatar, :home_picture, :email, :status, :residence_country, :phone_number,
95
- :post_count, :lock_version, :amount, :attempts, :action, :credit_card, :gender,
96
- :extra_special_company_id, :pictures, :picture_ids, :special_pictures,
97
- :special_picture_ids, :uuid, :friends, :friend_ids, :special_tags, :special_tag_ids,
98
- :citext, :hstore, :json, :jsonb, :hourly, :favorite_color
99
-
100
- def self.build(extra_attributes = {})
101
- attributes = {
102
- id: 1,
103
- name: 'New in SimpleForm!',
104
- description: 'Hello!',
105
- created_at: Time.now
106
- }.merge! extra_attributes
107
-
108
- new attributes
109
- end
110
-
111
- def initialize(options = {})
112
- @new_record = false
113
- options.each do |key, value|
114
- send("#{key}=", value)
115
- end if options
116
- end
117
-
118
- def new_record!
119
- @new_record = true
120
- end
121
-
122
- def persisted?
123
- !@new_record
124
- end
125
-
126
- def company_attributes=(*)
127
- end
128
-
129
- def tags_attributes=(*)
130
- end
131
-
132
- def column_for_attribute(attribute)
133
- column_type, limit = case attribute.to_sym
134
- when :name, :status, :password then [:string, 100]
135
- when :description then [:text, 200]
136
- when :age then :integer
137
- when :credit_limit then [:decimal, 15]
138
- when :active then :boolean
139
- when :born_at then :date
140
- when :delivery_time then :time
141
- when :created_at then :datetime
142
- when :updated_at then :timestamp
143
- when :lock_version then :integer
144
- when :home_picture then :string
145
- when :amount then :integer
146
- when :attempts then :integer
147
- when :action then :string
148
- when :credit_card then :string
149
- else attribute.to_sym
150
- end
151
- Column.new(attribute, column_type, limit)
152
- end
153
-
154
- begin
155
- require 'active_model/type'
156
- begin
157
- ActiveModel::Type.lookup(:text)
158
- rescue ArgumentError # :text is no longer an ActiveModel::Type
159
- # But we don't want our tests to depend on ActiveRecord
160
- class ::ActiveModel::Type::Text < ActiveModel::Type::String
161
- def type; :text; end
162
- end
163
- ActiveModel::Type.register(:text, ActiveModel::Type::Text)
164
- end
165
- def type_for_attribute(attribute)
166
- column_type, limit = case attribute
167
- when 'name', 'status', 'password' then [:string, 100]
168
- when 'description' then [:text, 200]
169
- when 'age' then :integer
170
- when 'credit_limit' then [:decimal, 15]
171
- when 'active' then :boolean
172
- when 'born_at' then :date
173
- when 'delivery_time' then :time
174
- when 'created_at' then :datetime
175
- when 'updated_at' then :datetime
176
- when 'lock_version' then :integer
177
- when 'home_picture' then :string
178
- when 'amount' then :integer
179
- when 'attempts' then :integer
180
- when 'action' then :string
181
- when 'credit_card' then :string
182
- when 'uuid' then :string
183
- when 'citext' then :string
184
- when 'hstore' then [:text, 200]
185
- when 'json' then [:text, 200]
186
- when 'jsonb' then [:text, 200]
187
- end
188
-
189
- ActiveModel::Type.lookup(column_type, limit: limit)
190
- end
191
- rescue LoadError
192
- end
193
-
194
- def has_attribute?(attribute)
195
- case attribute.to_sym
196
- when :name, :status, :password, :description, :age,
197
- :credit_limit, :active, :born_at, :delivery_time,
198
- :created_at, :updated_at, :lock_version, :home_picture,
199
- :amount, :attempts, :action, :credit_card, :uuid,
200
- :citext, :hstore, :json, :jsonb then true
201
- else false
202
- end
203
- end
204
-
205
- def self.human_attribute_name(attribute, options = {})
206
- case attribute
207
- when 'name', :name
208
- 'Super User Name!'
209
- when 'description'
210
- 'User Description!'
211
- when 'company'
212
- 'Company Human Name!'
213
- else
214
- attribute.to_s.humanize
215
- end
216
- end
217
-
218
- def self.reflect_on_association(association)
219
- case association
220
- when :company
221
- Association.new(Company, association, :belongs_to, nil, {})
222
- when :tags
223
- Association.new(Tag, association, :has_many, nil, {})
224
- when :special_tags
225
- Association.new(Tag, association, :has_many, ->(user) { where(id: user.id) }, {})
226
- when :first_company
227
- Association.new(Company, association, :has_one, nil, {})
228
- when :special_company
229
- Association.new(Company, association, :belongs_to, nil, conditions: { id: 1 })
230
- when :extra_special_company
231
- Association.new(Company, association, :belongs_to, nil, conditions: proc { { id: self.id } })
232
- when :pictures
233
- Association.new(Picture, association, :has_many, nil, {})
234
- when :special_pictures
235
- Association.new(Picture, association, :has_many, proc { where(name: self.name) }, {})
236
- when :friends
237
- Association.new(Friend, association, :has_many, nil, {})
238
- end
239
- end
240
-
241
- def errors
242
- @errors ||= begin
243
- errors = ActiveModel::Errors.new(self)
244
- errors.add(:name, "cannot be blank")
245
- errors.add(:description, 'must be longer than 15 characters')
246
- errors.add(:age, 'is not a number')
247
- errors.add(:age, 'must be greater than 18')
248
- errors.add(:company, 'company must be present')
249
- errors.add(:company_id, 'must be valid')
250
- errors
251
- end
252
- end
253
-
254
- def self.readonly_attributes
255
- ["credit_card"]
256
- end
257
- end
258
-
259
- class ValidatingUser < User
260
- include ActiveModel::Validations
261
- validates :name, presence: true
262
- validates :company, presence: true
263
- validates :age, presence: true, if: proc { |user| user.name }
264
- validates :amount, presence: true, unless: proc { |user| user.age }
265
-
266
- validates :action, presence: true, on: :create
267
- validates :credit_limit, presence: true, on: :save
268
- validates :phone_number, presence: true, on: :update
269
-
270
- validates_numericality_of :age,
271
- greater_than_or_equal_to: 18,
272
- less_than_or_equal_to: 99,
273
- only_integer: true
274
- validates_numericality_of :amount,
275
- greater_than: :min_amount,
276
- less_than: :max_amount,
277
- only_integer: true
278
- validates_numericality_of :attempts,
279
- greater_than_or_equal_to: :min_attempts,
280
- less_than_or_equal_to: :max_attempts,
281
- only_integer: true
282
- validates_length_of :name, maximum: 25, minimum: 5
283
- validates_length_of :description, in: 15..50
284
- validates_length_of :home_picture, is: 12
285
-
286
- def min_amount
287
- 10
288
- end
289
-
290
- def max_amount
291
- 100
292
- end
293
-
294
- def min_attempts
295
- 1
296
- end
297
-
298
- def max_attempts
299
- 100
300
- end
301
- end
302
-
303
- class OtherValidatingUser < User
304
- include ActiveModel::Validations
305
- validates_numericality_of :age,
306
- greater_than: 17,
307
- less_than: 100,
308
- only_integer: true
309
- validates_numericality_of :amount,
310
- greater_than: proc { |user| user.age },
311
- less_than: proc { |user| user.age + 100 },
312
- only_integer: true
313
- validates_numericality_of :attempts,
314
- greater_than_or_equal_to: proc { |user| user.age },
315
- less_than_or_equal_to: proc { |user| user.age + 100 },
316
- only_integer: true
317
-
318
- validates_format_of :country, with: /\w+/
319
- validates_format_of :name, with: proc { /\w+/ }
320
- validates_format_of :description, without: /\d+/
321
- end
322
-
323
- class HashBackedAuthor < Hash
324
- extend ActiveModel::Naming
325
- include ActiveModel::Conversion
326
-
327
- def persisted?; false; end
328
-
329
- def name
330
- 'hash backed author'
331
- end
332
- end
333
-
334
- class UserNumber1And2 < User
335
- end
336
-
337
- class UserWithAttachment < User
338
- def avatar_attachment
339
- OpenStruct.new
340
- end
341
-
342
- def avatars_attachments
343
- OpenStruct.new
344
- end
345
-
346
- def remote_cover_url
347
- "/uploads/cover.png"
348
- end
349
-
350
- def profile_image_attacher
351
- OpenStruct.new
352
- end
353
-
354
- def portrait_file_name
355
- "portrait.png"
356
- end
357
- end