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,30 @@
1
+ class CckForms::ParameterTypeClass::Time
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!({ignore_date: true, 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.time_select '', form_element_options, form_element_html})
16
+ end
17
+
18
+ def to_s(options = nil)
19
+ if value.is_a? Time
20
+ the_value = {
21
+ '(4i)' => value.hour,
22
+ '(5i)' => value.min,
23
+ }
24
+ end
25
+
26
+ the_value ||= value
27
+
28
+ "#{the_value.try(:[], '(4i)')}:#{the_value.try(:[], '(5i)')}"
29
+ end
30
+ end
@@ -0,0 +1,369 @@
1
+ class CckForms::ParameterTypeClass::WorkHours
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Часы работы'
6
+ end
7
+
8
+ DAYS = %w{ mon tue wed thu fri sat sun }
9
+ DAYS_RU_SHORT = %w{ Пн Вт Ср Чт Пт Сб Вс }
10
+
11
+ # mon -> Пн
12
+ def self.day_en_to_ru_short(day)
13
+ DAYS_RU_SHORT[DAYS.index(day.to_s)]
14
+ end
15
+
16
+ # Входящий хэш или массив объектов WorkHoursDat
17
+ #
18
+ # mon: {open_time: ..., open_24_hours: ...}, tue: {...}, ...
19
+ #
20
+ # преобразует в хэш для Монго.
21
+ def mongoize
22
+ return {} unless value.is_a? Hash
23
+
24
+ value.reduce({}) do |r, (day_name, day_data)|
25
+ r[day_name] = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new(day_data).mongoize if day_name.in? CckForms::ParameterTypeClass::WorkHours::DAYS
26
+ r
27
+ end
28
+ end
29
+
30
+ # Конструирует хэш объектов WorkHoursDay (ключ - название дня вида :mon, см. DAYS).
31
+ def self.demongoize_value(value, parameter_type_class=nil)
32
+ return {} unless value.is_a? Hash
33
+ value.reduce({}) do |r, (day_name, day_row)|
34
+ day_row = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.demongoize(day_row.merge(day: day_name)) if day_row.is_a? Hash
35
+ r[day_name] = day_row
36
+ r
37
+ end
38
+ end
39
+
40
+ # Строит форму для редактирования режима работы. 1 строка формы - 1 день со всеми своими параметрами.
41
+ def build_form(form_builder, options)
42
+ set_value_in_hash options
43
+
44
+ options = {
45
+ value: {}
46
+ }.merge options
47
+
48
+ value = options[:value]
49
+ value = {} unless value.is_a? Hash
50
+
51
+ result = []
52
+ met_days = Hash[ CckForms::ParameterTypeClass::WorkHours::DAYS.zip(CckForms::ParameterTypeClass::WorkHours::DAYS.dup.fill(false)) ]
53
+
54
+ value.each_value do |day|
55
+ day = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new day unless day.is_a? CckForms::ParameterTypeClass::WorkHours::WorkHoursDay
56
+ form_builder.fields_for(:value, index: day.day) { |day_builder| result << day.build_form(day_builder, false, options) }
57
+ met_days[day.day] = true
58
+ end
59
+
60
+ met_days.reject! { |_, value| value }
61
+
62
+ met_days.keys.each do |day_name|
63
+ form_builder.fields_for(:value, index: day_name) { |day_builder| result << CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new(day: day_name, open_24_hours: true).build_form(day_builder) }
64
+ end
65
+
66
+ form_builder.fields_for(:template) do |day_builder|
67
+ result << CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new({}).build_form(day_builder, true, options)
68
+ end
69
+
70
+ sprintf '<div class="work-hours" id="%1$s">%2$s</div><script type="text/javascript">$(function() {$("#%1$s").workhours()})</script>', form_builder_name_to_id(form_builder), result.join
71
+ end
72
+
73
+ # Строит строку вида: "Пн—Ср 10:00—23:00; Чт—Сб круглосуточно"
74
+ def to_html(options = nil)
75
+ value = self.value
76
+ return value.to_s unless value.respond_to? :each
77
+
78
+ with_tags = options && !options.try(:[], :with_tags).nil? ? options[:with_tags] : true
79
+
80
+ value = value.deep_stringify_keys if value.respond_to? :deep_stringify_keys
81
+
82
+ # разобьем на группы дней с одинаковым значением (режимом работы), типа {'круглосуточно' => %w{mon tue wed}, ...}
83
+ groups = {}
84
+ value.send(value.respond_to?(:each_value) ? :each_value : :each) do |day|
85
+ day = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new(day) unless day.is_a? CckForms::ParameterTypeClass::WorkHours::WorkHoursDay
86
+ hash = day.to_s_without_day
87
+ groups[hash] = [] unless groups[hash]
88
+ groups[hash] << day.day
89
+ end
90
+
91
+ # построим строки для групп
92
+ result = []
93
+ groups.each_pair do |hours_description, days|
94
+ if hours_description.present?
95
+ if days.length == 7
96
+ template = with_tags ? '<span class="workhours-group">%s, <span class="workhours-group-novac">без выходных</span></span>' : '%s, без выходных'
97
+ result << sprintf(template, hours_description)
98
+ else
99
+ if days == %w{ mon tue wed thu fri }
100
+ days_description = 'будние'
101
+ elsif days == %w{ sat sun }
102
+ days_description = 'сб, вс'
103
+ else
104
+ days_description = CckForms::ParameterTypeClass::WorkHours.grouped_days_string(days).mb_chars.downcase
105
+ end
106
+ template = with_tags ? '<span class="workhours-group">%s <span class="workhours-group-days">(%s)</span></span>' : '%s (%s)'
107
+ result << sprintf(template, hours_description, days_description)
108
+ end
109
+ end
110
+ end
111
+
112
+ result.join('; ').html_safe
113
+ end
114
+
115
+ def to_s(options = nil)
116
+ to_html with_tags: false
117
+ end
118
+
119
+ # Входной массив вида %w{mon, tue, wed, sat} преобразует в сгруппированную строку вида: "Пн—Ср, Сб".
120
+ def self.grouped_days_string(days)
121
+
122
+ # разобьем на непрерывные группы типа [%w{mon tue wed}, %w{sat}]
123
+ days.sort! { |a, b| DAYS.index(a) <=> DAYS.index(b) }
124
+ prev_index = -2
125
+ groups = []
126
+ days.each do |day|
127
+ index = DAYS.index(day)
128
+ if prev_index + 1 != index
129
+ groups << []
130
+ end
131
+
132
+ groups.last << day_en_to_ru_short(day)
133
+ prev_index = index
134
+ end
135
+
136
+ # получившиеся группы преобразуем в строки и сольем воедино
137
+ groups.map do |group|
138
+ if group.length == 1
139
+ group[0]
140
+ elsif group.length == 2
141
+ group.join ', '
142
+ else
143
+ sprintf '%s–%s', group.first, group.last
144
+ end
145
+ end.join ', '
146
+ end
147
+
148
+
149
+
150
+ # Модель-представление рабочего графика одного дня недели. При получении данных из Монги преобразовываем в эту модель,
151
+ # для удобства работы (чтобы не с хэшами возиться).
152
+ #
153
+ # day - строка из массива CckForms::ParameterTypeClass::WorkHours::DAYS.
154
+ # open_time и close_time хранятся в виде хэшей {hours: 10, minutes: 5}.
155
+ class WorkHoursDay
156
+
157
+ attr_accessor :day, :open_time, :close_time, :open_24_hours, :open_until_last_client
158
+
159
+ # Инициализирует свои поля из хэша.
160
+ def initialize(other)
161
+ if other.is_a? Hash
162
+ other = other.symbolize_keys
163
+ @day = other[:day]
164
+ @open_time = other[:open_time]
165
+ @close_time = other[:close_time]
166
+ @open_24_hours = form_to_boolean(other[:open_24_hours])
167
+ @open_until_last_client = form_to_boolean(other[:open_until_last_client])
168
+ elsif other.is_a? WorkHoursDay
169
+ @day = other.day
170
+ @open_time = other.open_time
171
+ @close_time = other.close_time
172
+ @open_24_hours = other.open_24_hours
173
+ @open_until_last_client = other.open_until_last_client
174
+ end
175
+ end
176
+
177
+ # Равны ли два объекта. Да, если все их поля равны.
178
+ def ==(other)
179
+ other = self.class.new(other) unless other.is_a? self.class
180
+
181
+ self.day == other.day and
182
+ self.open_time == other.open_time and
183
+ self.close_time == other.close_time and
184
+ self.open_24_hours == other.open_24_hours and
185
+ self.open_until_last_client == other.open_until_last_client
186
+ end
187
+
188
+ # Строит целочисленный хэш на основе всех полей, кроме day, чтобы группировать одинаковые режимы работы.
189
+ def hash_without_day
190
+ sprintf('%s:%s:%s:%s', open_time, close_time, open_24_hours, open_until_last_client).hash
191
+ end
192
+
193
+ # Строит строковое описание режима работы в формате: "с 12:00 до последнего клиента"
194
+ def to_s_without_day
195
+ result = ''
196
+ if open_24_hours
197
+ return 'круглосуточно'
198
+ elsif time_present?(open_time) or time_present?(close_time)
199
+ ots, cts = time_to_s(open_time), time_to_s(close_time)
200
+ if ots and cts
201
+ result = sprintf('%s–%s', ots, cts)
202
+ elsif ots
203
+ result = sprintf('с %s', ots)
204
+ else
205
+ result = sprintf('до %s', cts)
206
+ end
207
+ end
208
+
209
+ if open_until_last_client
210
+ result += ' ' if result.present?
211
+ result += 'до последнего клиента'
212
+ end
213
+
214
+ result
215
+ end
216
+
217
+ # Строит форму редактирования одного дня.
218
+ def build_form(form_builder, template = false, options = {})
219
+ form_builder.object = self
220
+
221
+ open_time_form = form_builder.fields_for(:open_time) { |time_form| build_time_form(time_form, open_time) }
222
+ close_time_form = form_builder.fields_for(:close_time) { |time_form| build_time_form(time_form, close_time) }
223
+
224
+ input_multi_mark = if options[:multi_days]
225
+ "data-multi-days='true'"
226
+ end
227
+
228
+ if template
229
+ header = ['<ul class="nav nav-pills">']
230
+ CckForms::ParameterTypeClass::WorkHours::DAYS.each { |day| header << '<li><a href="#"><input name="' + form_builder.object_name + '[days]" type="checkbox" value="' + day + '" /> ' + CckForms::ParameterTypeClass::WorkHours.day_en_to_ru_short(day) + '</a></li>' }
231
+ header = header.push('</ul>').join
232
+ else
233
+ header = sprintf '<strong>%s</strong>:%s', CckForms::ParameterTypeClass::WorkHours::day_en_to_ru_short(day), form_builder.hidden_field(:day)
234
+ end
235
+
236
+ open_until_last_client_html = unless options[:hide_open_until_last_client]
237
+ <<HTML
238
+ <div class="checkbox">
239
+ <label class="form_work_hours_option">#{ form_builder.check_box :open_until_last_client } до&nbsp;последнего&nbsp;клиента</label>
240
+ </div>
241
+ HTML
242
+ end
243
+
244
+ <<HTML
245
+ <div #{input_multi_mark} class="form_work_hours_day#{template ? ' form_work_hours_day_template" style="display: none' : ''}">
246
+ <div class="form_work_hours_time">
247
+ #{header}
248
+ </div>
249
+ <div class="form_work_hours_time">
250
+ <table width="100%">
251
+ <tr>
252
+ <td width="60%" class="form-inline">
253
+ с #{ open_time_form }
254
+ по #{ close_time_form }
255
+ </td>
256
+ <td width="40%">
257
+ <div class="checkbox">
258
+ <label class="form_work_hours_option">#{ form_builder.check_box :open_24_hours } круглосуточно</label>
259
+ </div>
260
+ #{open_until_last_client_html}
261
+ </td>
262
+ </tr>
263
+ </table>
264
+ </div>
265
+ </div>
266
+ HTML
267
+ end
268
+
269
+
270
+
271
+ private
272
+
273
+ # Преобразует значение из запроса (чексбокс) в булево, типа 1 -> true.
274
+ def form_to_boolean(value)
275
+ return value == '1' if value.is_a? String
276
+ !!value
277
+ end
278
+
279
+ # Преобразует хэше времени {hours: ..., minutes: ...} в строку "10:42"
280
+ def time_to_s(time)
281
+ return nil unless time.is_a?(Hash) and time['hours'].present? and time['minutes'].present?
282
+ sprintf '%s:%s', time['hours'].to_s.rjust(2, '0'), time['minutes'].to_s.rjust(2, '0')
283
+ end
284
+
285
+ # Не пустое ли значение времени?
286
+ def time_present?(time)
287
+ return time.is_a?(Hash) && time['hours'].present? && time['minutes'].present?
288
+ end
289
+
290
+ # Строим форму с селектами времени вида: [18]:[45]
291
+ def build_time_form(form_builder, value)
292
+ hours = []
293
+ 24.times { |hour| hours << [hour.to_s.rjust(2, '0'), hour] }
294
+
295
+ minutes = []
296
+ (60/5).times { |minute| minutes << [(minute *= 5).to_s.rjust(2, '0'), minute] }
297
+
298
+ sprintf(
299
+ '%s : %s',
300
+ form_builder.select(:hours, hours, {include_blank: true, selected: value.try(:[], 'hours')}, class: 'form-control input-sm', style: 'width: 60px'),
301
+ form_builder.select(:minutes, minutes, {include_blank: true, selected: value.try(:[], 'minutes')}, class: 'form-control input-sm', style: 'width: 60px')
302
+ ).html_safe
303
+ end
304
+
305
+
306
+
307
+ public
308
+
309
+ # Преборазование самого себя для сохранения в Монго (хэш).
310
+ def mongoize
311
+ {
312
+ 'day' => day.to_s,
313
+ 'open_time' => self.class.mongoize_time(open_time),
314
+ 'close_time' => self.class.mongoize_time(close_time),
315
+ 'open_24_hours' => open_24_hours,
316
+ 'open_until_last_client' => open_until_last_client,
317
+ }
318
+ end
319
+
320
+ class << self
321
+
322
+ # Преборазование самого себя из представления Монго (из хэша).
323
+ def demongoize(object)
324
+ object = object.symbolize_keys
325
+ WorkHoursDay.new(
326
+ day: object[:day].to_s,
327
+ open_time: self.demongoize_time(object[:open_time]),
328
+ close_time: self.demongoize_time(object[:close_time]),
329
+ open_24_hours: object[:open_24_hours],
330
+ open_until_last_client: object[:open_until_last_client],
331
+ )
332
+ end
333
+
334
+ # "Статическое" преборазование самого себя для сохранения в Монго (хэш).
335
+ def mongoize(object)
336
+ case object
337
+ when WorkHoursDay then object.mongoize
338
+ when Hash then WorkHoursDay.new(object).mongoize
339
+ else object
340
+ end
341
+ end
342
+
343
+ # TODO: сделать нормальный evolve
344
+ def evolve(object)
345
+ object
346
+ end
347
+
348
+ # Преобразовываем значение времени для Монго. Берет Time или DateTime или хэш и выдает хэш вида:
349
+ #
350
+ # {hours: 10, minutes: 5}
351
+ def mongoize_time(time)
352
+ if time.is_a? Time or time.is_a? DateTime
353
+ {'hours' => time.hour, 'minutes' => time.min}
354
+ elsif time.is_a? Hash
355
+ time = time.stringify_keys
356
+ {'hours' => time['hours'].present? ? time['hours'].to_i : nil, 'minutes' => time['minutes'].present? ? time['minutes'].to_i : nil}
357
+ end
358
+ end
359
+
360
+ # Преобразовываем значение времени для Монго. Берет Time или DateTime или хэш и выдает хэш вида:
361
+ #
362
+ # {hours: 10, minutes: 5}
363
+ def demongoize_time(time)
364
+ mongoize_time(time)
365
+ end
366
+ end
367
+
368
+ end
369
+ end
@@ -0,0 +1,3 @@
1
+ module CckForms
2
+ VERSION = '3.0.0'
3
+ end
data/lib/cck_forms.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'cck_forms/engine'
2
+
3
+ module CckForms
4
+ end