cck_forms 3.0.0

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