simple_form 5.1.0 → 5.3.0

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