cck_forms 3.0.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 (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +1 -0
  4. data/Rakefile +40 -0
  5. data/lib/cck_forms/date_time.rb +58 -0
  6. data/lib/cck_forms/engine.rb +23 -0
  7. data/lib/cck_forms/form_builder_extensions.rb +18 -0
  8. data/lib/cck_forms/parameter_type_class/album.rb +100 -0
  9. data/lib/cck_forms/parameter_type_class/base.rb +275 -0
  10. data/lib/cck_forms/parameter_type_class/boolean.rb +24 -0
  11. data/lib/cck_forms/parameter_type_class/checkboxes.rb +202 -0
  12. data/lib/cck_forms/parameter_type_class/date.rb +35 -0
  13. data/lib/cck_forms/parameter_type_class/date_range.rb +53 -0
  14. data/lib/cck_forms/parameter_type_class/date_time.rb +76 -0
  15. data/lib/cck_forms/parameter_type_class/enum.rb +53 -0
  16. data/lib/cck_forms/parameter_type_class/file.rb +80 -0
  17. data/lib/cck_forms/parameter_type_class/float.rb +20 -0
  18. data/lib/cck_forms/parameter_type_class/image.rb +20 -0
  19. data/lib/cck_forms/parameter_type_class/integer.rb +77 -0
  20. data/lib/cck_forms/parameter_type_class/integer_range.rb +150 -0
  21. data/lib/cck_forms/parameter_type_class/map.rb +259 -0
  22. data/lib/cck_forms/parameter_type_class/phones.rb +204 -0
  23. data/lib/cck_forms/parameter_type_class/string.rb +13 -0
  24. data/lib/cck_forms/parameter_type_class/string_collection.rb +23 -0
  25. data/lib/cck_forms/parameter_type_class/text.rb +18 -0
  26. data/lib/cck_forms/parameter_type_class/time.rb +30 -0
  27. data/lib/cck_forms/parameter_type_class/work_hours.rb +369 -0
  28. data/lib/cck_forms/version.rb +3 -0
  29. data/lib/cck_forms.rb +4 -0
  30. data/vendor/assets/javascripts/cck_forms/jquery.workhours.js +399 -0
  31. data/vendor/assets/javascripts/cck_forms/map.js.coffee +322 -0
  32. metadata +116 -0
@@ -0,0 +1,202 @@
1
+ class CckForms::ParameterTypeClass::Checkboxes
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Галочки'
6
+ end
7
+
8
+ # {kazakh: '1', russian: '0', english: '1'} -> ['kazakh', 'english']
9
+ def mongoize
10
+ resulting_set = []
11
+
12
+ if value.is_a? Array
13
+ return value
14
+ elsif value.is_a?(String) || value.is_a?(Symbol)
15
+ return [value.to_s]
16
+ elsif !value.is_a?(Hash)
17
+ return []
18
+ end
19
+
20
+ value.each do |key, value|
21
+ resulting_set << key if value.to_s == '1'
22
+ end
23
+
24
+ resulting_set
25
+ end
26
+
27
+ # ['kazakh', 'english'] -> {'kazakh' => 1, 'russain' => 0, 'english' => 1}
28
+ # (такой формат нужен формам)
29
+ def self.demongoize_value(value, parameter_type_class=nil)
30
+ value.present? or return {}
31
+
32
+ valid_values = parameter_type_class.try!(:valid_values) || value.map { |x| [x, nil] }
33
+ valid_values.reduce({}) do |r, (key, _)|
34
+ r[key] = value.include?(key) ? '1' : '0'
35
+ r
36
+ end
37
+ end
38
+
39
+ # Перечислим все отмеченные элементы в строку.
40
+ # options[:block] - задает шаблон sprintf для каждого вхождения
41
+ # options[:only] - массив, оставить только эти ключи (можно передать одно, без массива)
42
+ # options[:except] - :only наоборот
43
+ # options[:glue] - используем его вместо ', ' для склейки значений
44
+ # options[:short] - по-возможности сокращать: если указаны все варианты, то вернется строка вида "выбрано все"
45
+ # - или "все, кроме xxx", если не выбран только xxx
46
+ def to_s(options = nil)
47
+ checked_keys, checked_elements = [], []
48
+ return '' if value.blank?
49
+
50
+ template = '%s'
51
+ if options
52
+ options = {block: options} unless options.is_a? Hash
53
+ template = options[:block] if options[:block]
54
+ else
55
+ options = {}
56
+ end
57
+
58
+ if value.respond_to? :each_pair
59
+ value.each_pair do |k, v|
60
+ include = v == '1' && (!options[:only] || options[:only] && options[:only] == k)
61
+ exclude = options[:except] && options[:except] == k
62
+ if include && !exclude
63
+ checked_elements << sprintf(template, valid_values[k])
64
+ checked_keys << k
65
+ end
66
+ end
67
+ elsif value.respond_to? :each
68
+ value.each do |k|
69
+ exclude = options[:except] && options[:except] == k
70
+ unless exclude
71
+ checked_elements << sprintf(template, valid_values[k])
72
+ checked_keys << k
73
+ end
74
+ end
75
+ end
76
+
77
+ glue = options[:glue] || ', '
78
+ if options[:short] && checked_keys
79
+ all_keys = valid_values.keys
80
+ unchecked_keys = all_keys - checked_keys
81
+ unchecked_num = unchecked_keys.count
82
+
83
+ param_title = cck_parameter ? " «#{cck_parameter.title}»" : ''
84
+
85
+ if unchecked_num == 0
86
+ return "Все значения#{param_title}"
87
+ elsif unchecked_num < checked_keys.count./(2).round
88
+ return "Все значения#{param_title}, кроме " + valid_values.values_at(*unchecked_keys).map { |x| sprintf(template, x) }.join(glue)
89
+ end
90
+ end
91
+
92
+ checked_elements.join glue
93
+ end
94
+
95
+ def search(selectable, field, query)
96
+ if query.respond_to? :each_pair
97
+ keys = []
98
+ query.each_pair do |key, value|
99
+ if value.present? && value != '0'
100
+ if key == 'any'
101
+ selectable = selectable.where(field.to_sym.ne => [])
102
+ else
103
+ keys << key
104
+ end
105
+ end
106
+ end
107
+ selectable = selectable.where(field.to_sym.all => keys) if keys.any?
108
+ else
109
+ selectable = selectable.where(field.to_s => query)
110
+ end
111
+
112
+ selectable
113
+ end
114
+
115
+ # Составим строку для map-reduce, чтобы emit вызывался на каждом отмеченном значении, например, в документе хранится:
116
+ #
117
+ # cck_params.city: ['almaty', 'astana']
118
+ #
119
+ # Мы вызовем emit('almaty', 1); emit('astana', 1).
120
+ def self.emit_map_reduce(field_name)
121
+ field_name = 'this.' + field_name
122
+ return "if(#{field_name} && #{field_name} instanceof Array) {
123
+ #{field_name}.forEach(function(key) {
124
+ emit(key, 1)
125
+ })
126
+ }"
127
+ end
128
+
129
+ # options[:block] - задает шаблон sprintf для каждого чекбокса (input, label)
130
+ # options[:map] - задает преобразование (ключ-значение) текстов в подписях, например, 'длинный текст' => 'кор. ткст'
131
+ # options[:data] - задает data-атрибуты для тэга label, по ключу из valid_values (capital: {almaty: 'yes', astana: 'no'})
132
+ # options[:as] - если :select, то показать не в виде чекбоксов, а в виде выпадайки
133
+ # options[:only] - только этот ключ (ключи)
134
+ def build_form(form_builder, options)
135
+ return '' unless valid_values.is_a?(Hash) || valid_values.is_a?(Array)
136
+
137
+ if options.is_a? Hash and options[:as] == :select
138
+ return build_select_form(form_builder, options.except(:for, :as))
139
+ end
140
+
141
+ options = {block: '<div class="form_check_box_block">%s %s</div>', map: {}}.merge(options)
142
+
143
+ set_value_in_hash options
144
+ val = options[:value]
145
+
146
+ result = ''
147
+ if valid_values.is_a? Array
148
+ method = :each_with_index
149
+ elsif valid_values.is_a? Hash
150
+ method = :each_pair
151
+ end
152
+
153
+ valid_values.send(method) do |k, v|
154
+ if !options[:only] || options[:only] == k || options[:only].try(:include?, k)
155
+ result += form_builder.fields_for :value do |ff|
156
+
157
+ begin
158
+ checked = ! val.try(:[], k).to_i.zero?
159
+ rescue
160
+ checked = false
161
+ end
162
+
163
+ v = options[:map][v] || v
164
+
165
+ # required не встраиваю, иначе пока я все чекбоксы не отмечу, не могу отправить форму
166
+ data = options[:data] ? extract_data_for_key(k, options[:data]) : nil
167
+ sprintf(options[:block], ff.check_box(k.to_sym, {checked: checked}, '1', options[:for] == :search ? nil : '0'), ff.label(k.to_sym, v, data: data)).html_safe
168
+ end
169
+ end
170
+ end
171
+
172
+ result
173
+ end
174
+
175
+
176
+
177
+ private
178
+
179
+ def build_select_form(form_builder, options)
180
+ set_value_in_hash options
181
+
182
+ values = valid_values_enum
183
+ if options[:only]
184
+ options[:only] = [options[:only]] unless options[:only].is_a? Array
185
+ values.select! { |o| o[1].in? options[:only] }
186
+ end
187
+
188
+ if options[:except]
189
+ options[:except] = [options[:except]] unless options[:except].is_a? Array
190
+ values.reject! { |o| o[1].in? options[:except] }
191
+ end
192
+
193
+ form_builder.select :value, values, {selected: options[:value], required: options[:required], include_blank: !options[:required]}, class: 'form-control'
194
+ end
195
+
196
+ def extract_data_for_key(request_key, data)
197
+ data.reduce({}) do |r, (key, values)|
198
+ r[key] = values[request_key] if values[request_key]
199
+ r
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,35 @@
1
+ class CckForms::ParameterTypeClass::Date
2
+ include CckForms::ParameterTypeClass::Base
3
+ include CckForms::DateTime
4
+
5
+ def self.name
6
+ 'Дата'
7
+ end
8
+
9
+ def build_form(form_builder, options)
10
+ set_value_in_hash options
11
+ self.class.build_date_form(form_builder, options)
12
+ end
13
+
14
+ def self.build_date_form(form_builder, options, type = '')
15
+ val = options[:value].is_a?(Hash) ? options[:value][type] : options[:value]
16
+ val = CckForms::ParameterTypeClass::Time::date_object_from_what_stored_in_database(val)
17
+ form_element_options, form_element_html = CckForms::ParameterTypeClass::Time::default_options_for_date_time_selectors(val)
18
+ form_element_html.merge!({required: options[:required]})
19
+ ('<div class="form-inline">%s</div>' % form_builder.fields_for(:value) { |datetime_builder| datetime_builder.date_select type, form_element_options, form_element_html}).html_safe
20
+ end
21
+
22
+ def to_s(options = nil)
23
+ if value.is_a? Time
24
+ the_value = {
25
+ '(1i)' => value.year,
26
+ '(2i)' => value.month,
27
+ '(3i)' => value.day,
28
+ }
29
+ end
30
+
31
+ the_value ||= value
32
+
33
+ "#{the_value.try(:[], '(3i)')}.#{the_value.try(:[], '(2i)')}.#{the_value.try(:[], '(1i)')}"
34
+ end
35
+ end
@@ -0,0 +1,53 @@
1
+ class CckForms::ParameterTypeClass::DateRange
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Диапазон между двумя датами'
6
+ end
7
+
8
+ def mongoize
9
+ value_from_form = value
10
+ return nil if value_from_form.blank?
11
+ db_representation = {}
12
+
13
+ %w(from till).each do |type|
14
+ type_hash = {}
15
+ %w((1i) (2i) (3i)).each do |field|
16
+ type_hash.merge!("#{field}" => value_from_form.try(:[], "#{type + field}"))
17
+ end
18
+ db_representation[type] = CckForms::ParameterTypeClass::Time::date_object_from_what_stored_in_database(type_hash)
19
+ end
20
+
21
+ db_representation
22
+ end
23
+
24
+ def build_form(form_builder, options)
25
+ result = []
26
+ set_value_in_hash options
27
+
28
+ [:from, :till].each do |type|
29
+ result << CckForms::ParameterTypeClass::Date.build_date_form(form_builder, options, type)
30
+ end
31
+
32
+ result.join.html_safe
33
+ end
34
+
35
+ def to_s
36
+ return '' unless value.present? && value.is_a?(Hash)
37
+ types = {}
38
+ [:from, :till].each { |type| types[type] = value[type].strftime('%d.%m.%Y') if value[type].is_a?(Time) }
39
+ from, till = types[:from], types[:till]
40
+
41
+ if from.blank?
42
+ "до #{till}"
43
+ elsif till.blank?
44
+ "от #{from}"
45
+ elsif from == till
46
+ from.to_s
47
+ else
48
+ [from, till].join(' - ')
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,76 @@
1
+ class CckForms::ParameterTypeClass::DateTime
2
+ include CckForms::ParameterTypeClass::Base
3
+ include CckForms::DateTime
4
+
5
+ def self.name
6
+ 'Дата и время'
7
+ end
8
+
9
+ def build_form(form_builder, options)
10
+ set_value_in_hash options
11
+ value = CckForms::ParameterTypeClass::Time::date_object_from_what_stored_in_database(options[:value])
12
+ form_element_options, form_element_html = CckForms::ParameterTypeClass::Time.default_options_for_date_time_selectors(value)
13
+ form_element_options.merge!({minute_step: 5})
14
+ form_element_html.merge!({required: options[:required]})
15
+ ('<div class="form-inline">%s</div>' % form_builder.fields_for(:value) { |datetime_builder| datetime_builder.datetime_select '', form_element_options, form_element_html})
16
+ end
17
+
18
+ # Формирует строковое представление даты и времени. Если options :символ, это эквивалентно options[:символ] = true,
19
+ # например:
20
+ #
21
+ # date_attr.to_s :only_date
22
+ # date_attr.to_s :only_date => true # эквивалентно
23
+ #
24
+ # Ключи options:
25
+ #
26
+ # year_obligatory - вывести год, по-умолчанию, он не показывается, если равен текущему
27
+ # only_date - только дату, без времени
28
+ # rus_date - дату по-русски, типа "2 июля"
29
+ #
30
+ # По-умолчанию, вернет строку вида "01.02.2012, 12:49". Если что-то сломалось, вернет пустую строку.
31
+ def to_s(options=nil)
32
+ value = if self.value.is_a? Time
33
+ {
34
+ '(1i)' => self.value.year,
35
+ '(2i)' => self.value.month,
36
+ '(3i)' => self.value.day,
37
+ '(4i)' => self.value.hour,
38
+ '(5i)' => self.value.min,
39
+ }
40
+ else
41
+ self.value
42
+ end
43
+
44
+ return '' unless value and
45
+ value.is_a?(Hash) and
46
+ value.try(:[], '(1i)').to_i > 0 and
47
+ value.try(:[], '(2i)').to_i > 0 and
48
+ value.try(:[], '(3i)').to_i > 0
49
+
50
+ options = {options => true} if options.is_a? Symbol
51
+ options = {} unless options.is_a? Hash
52
+
53
+ now = Date::today
54
+ date = DateTime.new value.try(:[], '(1i)').to_i, value.try(:[], '(2i)').to_i, value.try(:[], '(3i)').to_i, value.try(:[], '(4i)').to_i, value.try(:[], '(5i)').to_i
55
+ date = date.in_time_zone(Rails.application.config.time_zone)
56
+
57
+ need_year = options[:year_obligatory] || now.strftime('%Y') != date.strftime('%Y')
58
+
59
+ if options[:rus_date]
60
+ date_string = Russian::strftime(date, '%e %B' + (need_year ? ' %Y' : '')) # 2 июля
61
+ else
62
+ date_string = date.strftime('%d.%m' + (need_year ? '.%Y' : ''))
63
+ end
64
+
65
+ time_string = date.strftime '%H:%M'
66
+
67
+ if options[:only_date]
68
+ return date_string
69
+ else
70
+ return "#{date_string}, #{time_string}"
71
+ end
72
+
73
+ rescue
74
+ ''
75
+ end
76
+ end
@@ -0,0 +1,53 @@
1
+ class CckForms::ParameterTypeClass::Enum
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Значение из списка'
6
+ end
7
+
8
+ def mongoize
9
+ value.presence
10
+ end
11
+
12
+ def to_s(options = nil)
13
+ return '' if value.blank?
14
+ valid_values[value].to_s
15
+ end
16
+
17
+ def search(selectable, field, query)
18
+ if query.is_a? Hash
19
+ query = query.map { |k, v| v == '1' ? k : nil }.compact
20
+ end
21
+ query = [query] unless query.is_a? Array
22
+
23
+ if query.any?
24
+ selectable.where(field.to_sym.in => query)
25
+ else
26
+ selectable
27
+ end
28
+ end
29
+
30
+ def build_form(form_builder, options)
31
+
32
+ if options.is_a? Hash and options[:as] == 'checkboxes'
33
+ options = options.except(:for, :as)
34
+ checkboxes = CckForms::ParameterTypeClass::Checkboxes.new valid_values: self.valid_values, value: self.value
35
+ return checkboxes.build_form(form_builder, options)
36
+ end
37
+
38
+ set_value_in_hash options
39
+
40
+ values = valid_values_enum
41
+ if options[:only]
42
+ options[:only] = [options[:only]] unless options[:only].is_a? Array
43
+ values.select! { |o| o[1].in? options[:only] }
44
+ end
45
+
46
+ if options[:except]
47
+ options[:except] = [options[:except]] unless options[:except].is_a? Array
48
+ values.reject! { |o| o[1].in? options[:except] }
49
+ end
50
+
51
+ form_builder.select :value, values, {selected: options[:value], required: options[:required], include_blank: !options[:required]}, class: 'form-control '
52
+ end
53
+ end
@@ -0,0 +1,80 @@
1
+ class CckForms::ParameterTypeClass::File
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Файл'
6
+ end
7
+
8
+ def self.file_type
9
+ ::Neofiles::File
10
+ end
11
+
12
+ def file_type
13
+ self.class.file_type
14
+ end
15
+
16
+ # Если передан Neofiles::File, вернет его идентификатор, если строка - вернет ее, иначе - nil.
17
+ def mongoize
18
+ case value
19
+ when file_type then value.id
20
+ when ::String then value
21
+ end
22
+ end
23
+
24
+ # Попытаемся получить объект Neofiles::File по его идентификатору. Если не получилось, вернем nil.
25
+ def self.demongoize_value(value, parameter_type_class=nil)
26
+ file_type.find(value) unless value.blank?
27
+ rescue Mongoid::Errors::DocumentNotFound
28
+ nil
29
+ end
30
+
31
+ # Строит форму выбора и загрузки 1 картинки.
32
+ #
33
+ # Ставит ДИВ и делает аяксовый вызов метода file_compact контроллера Neofiles::AdminController.
34
+ #
35
+ # Ключи options:
36
+ #
37
+ # value - текущее значение (идентификатор или объект Neofiles::File)
38
+ #
39
+ def build_form(form_builder, options)
40
+ set_value_in_hash options
41
+ self.class.create_load_form helper: self,
42
+ file: options[:value].presence,
43
+ input_name: form_builder.object_name + '[value]',
44
+ widget_id: form_builder_name_to_id(form_builder, '[value]'),
45
+ with_desc: options[:with_desc]
46
+ end
47
+
48
+ def self.create_load_form(helper:, file:, input_name:, append_create: false, clean_remove: false, widget_id: nil, disabled: false, multiple: false, with_desc: false)
49
+ cont_id = 'container_' + (widget_id.presence || form_name_to_id(input_name))
50
+
51
+ # создаем временное поле, чтобы пока аяксовый ответ не вернется, мы могли все же отправить родительскую форму
52
+ # и не потерять при этом данные из поля
53
+ temp_field, remove_temp_field = '', ''
54
+ if file.present? && file.is_a?(String)
55
+ temp_id = "temp_file_field_#{file}"
56
+ temp_field = '<input id="' + temp_id + '" type="hidden" name="' + input_name + '" value="' + file + '">'
57
+ remove_temp_field = '$("#' + temp_id + '").remove();'
58
+ end
59
+
60
+ '<div id="' + cont_id + '"></div>
61
+ ' + temp_field + '
62
+
63
+ <script type="text/javascript">
64
+ $(function() {
65
+ $("#' + cont_id + '").load("' + helper.neofiles_file_compact_path(id: file, input_name: input_name, widget_id: widget_id, append_create: append_create ? '1' : nil, clean_remove: clean_remove ? '1' : nil, disabled: disabled ? '1' : nil, multiple: multiple ? '1' : nil, with_desc: with_desc ? '1' : nil) + '", null, function() {
66
+ $(this).children().unwrap();
67
+ ' + remove_temp_field + '
68
+ });
69
+ });
70
+ </script>'
71
+ end
72
+
73
+ def to_s
74
+ ''
75
+ end
76
+
77
+ def to_diff_value(options = {})
78
+ "Файл: #{self.value.try! :name}"
79
+ end
80
+ end
@@ -0,0 +1,20 @@
1
+ class CckForms::ParameterTypeClass::Float
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Дробное число'
6
+ end
7
+
8
+ def mongoize
9
+ value.to_f
10
+ end
11
+
12
+ def to_s(options = nil)
13
+ value.to_f != 0.0 ? value.to_f : ''
14
+ end
15
+
16
+ def build_form(form_builder, options)
17
+ set_value_in_hash options
18
+ form_builder.number_field :value, {class: 'form-control input-small'}.merge(options)
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class CckForms::ParameterTypeClass::Image < CckForms::ParameterTypeClass::File
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Картинка'
6
+ end
7
+
8
+ def self.file_type
9
+ ::Neofiles::Image
10
+ end
11
+
12
+ def file_type
13
+ self.class.file_type
14
+ end
15
+
16
+ def to_diff_value(options = {})
17
+ view_context = options[:view_context]
18
+ "<img style='width: 64px; height: 64px;' src='#{view_context.neofiles_image_path(id: value, format: '64x64', crop: 1)}'>".html_safe
19
+ end
20
+ end
@@ -0,0 +1,77 @@
1
+ class CckForms::ParameterTypeClass::Integer
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Целое число'
6
+ end
7
+
8
+ def mongoize
9
+ value.to_i
10
+ end
11
+
12
+ def to_s(options = nil)
13
+ value.to_i != 0 ? value.to_i.to_s : ''
14
+ end
15
+
16
+ # '123' -> 123
17
+ # '100/' -> $gte: 100
18
+ # '100/150' -> $gte: 100, $lte: 150
19
+ # '/150' -> $lte: 150
20
+ #
21
+ # l: 100 -> $gte: 100
22
+ # l: 100, h: 150 -> $gte: 100, $lte: 150
23
+ # h: 150 -> $lte: 150
24
+ def search(selectable, field, query)
25
+ if query.is_a?(Hash) || query.to_s.include?('/')
26
+ low, high = query.is_a?(Hash) ? [query[:l] || query['l'], query[:h] || query['h']] : query.to_s.split('/')
27
+
28
+ if low.to_i > 0
29
+ selectable = selectable.gte(field => low.to_i)
30
+ end
31
+
32
+ if high.to_i > 0
33
+ selectable = selectable.lte(field => high.to_i)
34
+ end
35
+
36
+ selectable
37
+ else
38
+ selectable.where(field => query.to_i)
39
+ end
40
+ end
41
+
42
+ # Примеры options[:values] (работает, если передано options[:as] == :select и options[:for] == :search):
43
+ #
44
+ # диапазоны: [['не больше 10', '/10'], ['11-20', '11/20'], ['21-30', '21/30'], ['свыше 30', '31/']]
45
+ # перечисление: [['один', '1'], ['два', '2'], ['три', '3']]
46
+ # комбинирование: [['один', '1'], ['два', '2'], ['три и больше', '3/']]
47
+ def build_form(form_builder, options = {})
48
+ set_value_in_hash options
49
+
50
+ default_style = {class: 'form-control input-small'}
51
+
52
+ if options[:for] == :search
53
+ res = []
54
+ as = options[:as]
55
+ only = options[:only]
56
+ options = options.except :only, :for
57
+
58
+ if as == :select
59
+ res << form_builder.select(:value, [['', '']] + options[:values], options.merge(selected: options[:value]), class: 'form-control input-small')
60
+ else
61
+ value = options[:value].is_a?(Hash) ? options[:value].symbolize_keys : {}
62
+
63
+ if !only || only == :low
64
+ res << form_builder.text_field(:l, options.merge(index: 'value', value: value[:l]).reverse_merge(default_style))
65
+ end
66
+
67
+ if !only || only == :high
68
+ res << form_builder.text_field(:h, options.merge(index: 'value', value: value[:h]).reverse_merge(default_style))
69
+ end
70
+ end
71
+
72
+ res.join ' – '
73
+ else
74
+ form_builder.number_field :value, options.reverse_merge(default_style)
75
+ end
76
+ end
77
+ end