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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9bd0c788c8c8ba1cd70dc9114891a33421105bc4
4
+ data.tar.gz: 6d1a53c9d48100fc922b37a87aa47255da929554
5
+ SHA512:
6
+ metadata.gz: 7af10e028591267acfaebd5d67a22027cd8a3b7a0d8769fbc212f9b30e99a166cba7e8aa699fff3510d8f92b50a16b05d74ce02bc62e29b1f5ffc8bc90a67066
7
+ data.tar.gz: ed6f2da8765459afd914fe42595068fa803b897f861e0650550e037f617d68276c58c59777f4d39045941cda21efd5e1e55947274c8dd5a8bcb012a0398f494d
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ Released under the MIT License.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ TODO
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'CckForms'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,58 @@
1
+ module CckForms::DateTime
2
+ extend ActiveSupport::Concern
3
+
4
+ module DateTimeParser
5
+ def date_object_from_what_stored_in_database(value)
6
+ parsed_value = nil
7
+ if value.is_a? Hash
8
+
9
+ force_timezone = true
10
+ v = value
11
+
12
+ if v.has_key? 'hour' and v.has_key? 'month'
13
+ parsed_value = DateTime.civil(v['year'], v['year'], v['day'], v['hour'], v['minute'])
14
+ elsif v.has_key? 'hour' or v.has_key? 'month'
15
+ if v.has_key? 'hour'
16
+ parsed_value = Time.parse("#{v['hour']}:#{v['minute']}")
17
+ elsif v.has_key? 'month'
18
+ parsed_value = Date.parse("#{v['day']}.#{v['month']}.#{v['year']}")
19
+ end
20
+ else
21
+ if v.has_key? '(5i)' and v.has_key? '(1i)' # date & time
22
+ parsed_value = DateTime.parse("#{v['(3i)']}.#{v['(2i)']}.#{v['(1i)']} #{v['(4i)']}:#{v['(5i)']}")
23
+ elsif v.has_key? '(5i)' # time
24
+ parsed_value = Time.parse("#{v['(4i)']}:#{v['(5i)']}")
25
+ elsif v.has_key? '(1i)' # date
26
+ parsed_value = Date.parse("#{v['(3i)']}.#{v['(2i)']}.#{v['(1i)']}")
27
+ end
28
+ end
29
+ end
30
+ value = parsed_value if parsed_value
31
+
32
+ if force_timezone || value.is_a?(Time)
33
+ value = value.change offset: ActiveSupport::TimeZone.new(Rails.application.config.time_zone).formatted_offset
34
+ end
35
+
36
+ value
37
+ end
38
+ end
39
+
40
+ module ClassMethods
41
+ include DateTimeParser
42
+
43
+ def default_options_for_date_time_selectors(value)
44
+ date_in_time_zone = value.in_time_zone(Rails.application.config.time_zone) rescue nil
45
+ [{default: date_in_time_zone, include_blank: false, with_css_classes: true}, {class: 'form-control'}]
46
+ end
47
+
48
+ def demongoize_value(value, parameter_type_class=nil)
49
+ date_object_from_what_stored_in_database value
50
+ end
51
+ end
52
+
53
+ include DateTimeParser
54
+
55
+ def mongoize
56
+ date_object_from_what_stored_in_database value
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ module CckForms
2
+ class Engine < ::Rails::Engine
3
+ config.autoload_paths << File.expand_path('../..', __FILE__)
4
+
5
+ config.after_initialize do
6
+ CckForms::ParameterTypeClass::Base.load_type_classes if Rails.application.config.cck_forms.load_type_classes
7
+ ActionView::Base.send :include, CckForms::FormBuilderExtensions if Rails.application.config.cck_forms.extend_form_builder
8
+ end
9
+
10
+ config.cck_forms = ActiveSupport::OrderedOptions.new
11
+
12
+ # general
13
+ config.cck_forms.load_type_classes = true
14
+ config.cck_forms.extend_form_builder = true
15
+
16
+ # phones
17
+ config.cck_forms.phones = ActiveSupport::OrderedOptions.new
18
+ config.cck_forms.phones.min_phones_in_form = 3
19
+ config.cck_forms.phones.mobile_codes = %w{ 777 705 771 701 702 775 778 700 707 }
20
+ config.cck_forms.phones.prefix = '+7'
21
+ config.cck_forms.phones.number_parts_glue = '-'
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ # Примесь для расширения ActionView::Helpers::FormBuilder, чтобы работать с нашими полями:
2
+ #
3
+ # class CckEnabled
4
+ # field :logo, type: CckForms::ParameterTypeClass::Image
5
+ # end
6
+ #
7
+ # = form_for @cck_enabled do |f|
8
+ # = f.standalone_cck_field :logo
9
+ #
10
+ module CckForms::FormBuilderExtensions
11
+ ActionView::Helpers::FormBuilder.class_eval do
12
+ def standalone_cck_field(field_name, options = {})
13
+ fields_for(field_name) do |ff|
14
+ @template.raw object.send(field_name).build_form ff, options
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,100 @@
1
+ class CckForms::ParameterTypeClass::Album
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Альбом'
6
+ end
7
+
8
+ # Преобразует данные для Монго.
9
+ # Приводит переданный массив или хэш объектов Neofiles::Image или их идентификаторов в массив.
10
+ def mongoize
11
+ the_value = value.is_a?(Hash) ? value['value'] : value
12
+
13
+ result = []
14
+ if the_value.respond_to? :each
15
+ the_value.each do |image|
16
+ image = image[1] if the_value.respond_to? :each_value
17
+ result.push(image.is_a?(::Neofiles::Image) ? image.id : image.to_s) if image.present?
18
+ end
19
+ end
20
+
21
+ result
22
+ end
23
+
24
+ # Преобразуем данные из Монго.
25
+ # Приводим в массив (по идее, массив идентификаторов Neofiles::Image, хотя может быть что угодно).
26
+ def self.demongoize_value(value, parameter_type_class=nil)
27
+ if value.respond_to? :each
28
+ value
29
+ else
30
+ []
31
+ end
32
+ end
33
+
34
+ # Строит форму для обновления файлов альбома.
35
+ #
36
+ # Ключи options:
37
+ #
38
+ # value - текущее значение (идентификатор или объект Neofiles::Album)
39
+ def build_form(form_builder, options)
40
+ set_value_in_hash options
41
+
42
+ options = {
43
+
44
+ }.merge options
45
+
46
+ the_value = options[:value].is_a?(Array) ? options[:value] : []
47
+ input_name_prefix = form_builder.object_name + '[value][]'
48
+ widget_id_prefix = form_builder_name_to_id form_builder, '_value_'
49
+ file_forms = []
50
+
51
+ the_value.each do |image_id|
52
+ file_forms << CckForms::ParameterTypeClass::Image.create_load_form( helper: self,
53
+ file: image_id,
54
+ input_name: input_name_prefix,
55
+ append_create: false,
56
+ clean_remove: true,
57
+ widget_id: widget_id_prefix + file_forms.length.to_s,
58
+ multiple: true,
59
+ with_desc: options[:with_desc])
60
+ end
61
+
62
+ add_file_form = CckForms::ParameterTypeClass::Image.create_load_form( helper: self,
63
+ file: nil,
64
+ input_name: input_name_prefix,
65
+ append_create: true,
66
+ clean_remove: true,
67
+ widget_id: widget_id_prefix + file_forms.length.to_s,
68
+ multiple: true,
69
+ with_desc: options[:with_desc])
70
+
71
+ id = form_builder_name_to_id form_builder, rand(1...1_000_000).to_s
72
+
73
+ <<HTML
74
+ <div class="neofiles-album-compact" id="#{id}">
75
+ #{file_forms.join}
76
+ #{add_file_form}
77
+ </div>
78
+
79
+ <script type="text/javascript">
80
+ $(function() {
81
+ $("##{id}").album();
82
+ });
83
+ </script>
84
+ HTML
85
+ end
86
+
87
+ def to_s(options = nil)
88
+ ''
89
+ end
90
+
91
+ def to_diff_value(options = {})
92
+ view_context = options[:view_context]
93
+ images_html_list = []
94
+ value.each do |image_id|
95
+ images_html_list << "<img style='width: 64px; height: 64px;' src='#{view_context.neofiles_image_path(id: image_id, format: '64x64', crop: 1)}'>"
96
+ end
97
+
98
+ images_html_list.join.html_safe if images_html_list.any?
99
+ end
100
+ end
@@ -0,0 +1,275 @@
1
+ # Базовая примесь для всех типов полей. Определяет всякие помогайки. Ее нужно включать методом include во все типы,
2
+ # например, String, Checkboxes и т. п.
3
+ #
4
+ # class CckForms::ParameterTypeClass::NewType
5
+ # include CckForms::ParameterTypeClass::Base
6
+ #
7
+ # def self.name
8
+ # 'Новый тип'
9
+ # end
10
+ # end
11
+ #
12
+ # CckForms::ParameterTypeClass::NewType.name # 'Новый тип', берется из CckForms::ParameterTypeClass::NewType::name
13
+ #
14
+ # Использование типов:
15
+ #
16
+ # field :cover_photo, type: CckForms::ParameterTypeClass::Image
17
+ # field :gallery, type: CckForms::ParameterTypeClass::Album
18
+ # field :description, type: CckForms::ParameterTypeClass::Text
19
+ #
20
+ # Что есть в базовом типе:
21
+ #
22
+ # 1) все помощники УРЛов вида edit_article_path, включаемые через include Rails.application.routes.url_helpers;
23
+ #
24
+ # 2) методы cck_param и value, которые возвращают текущий параметр (module CckForms::*::Parameter) и его значение;
25
+ #
26
+ # 3) методы with_cck_param(param) do ... и with_value(value) do ..., которые устанавливают соотв, значения методов
27
+ # cck_param/value на время выполнения блока (еще есть with_cck_param_and_value(param, value) do ...);
28
+ #
29
+ # 4) динамический метод (через method_missing и respond_to?) ..._with_value(value, args*), который делает то же,
30
+ # что и with_value, но для вызова 1 метода;
31
+ #
32
+ # 5) set_value_in_hash(hash), который кладет value в hash[:value];
33
+ #
34
+ # 6) помощники для получения ХТМЛ ID form(_builder)?_name_to_id;
35
+ #
36
+ # 7) методы для потребителей (типизированных объектов):
37
+ #
38
+ # self.code - код типа из имени модуля (CckForms::ParameterType::RichText -> rich_text)
39
+ # self.name - имя типа ("Cтрока")
40
+ #
41
+ module CckForms::ParameterTypeClass::Base
42
+ extend ActiveSupport::Concern
43
+
44
+ included do
45
+ # Кое-где понадобятся помогайки-пути, сразу их включим.
46
+ include Rails.application.routes.url_helpers
47
+
48
+ attr_accessor :value
49
+ attr_reader :valid_values_class_name, :cck_parameter
50
+
51
+ def initialize(options)
52
+ options = options.symbolize_keys
53
+
54
+ self.value = options[:value]
55
+ if value.is_a?(Hash) && value.has_key?('value')
56
+ self.value = self.value['value']
57
+ end
58
+
59
+ valid_values = options.delete(:valid_values).presence
60
+ valid_values_class_name = options.delete(:valid_values_class_name)
61
+ cck_parameter = options.delete(:cck_parameter)
62
+
63
+ @valid_values = valid_values
64
+ @valid_values_class_name = valid_values_class_name
65
+ @cck_parameter = cck_parameter
66
+ @extra_options = options[:extra_options] || options.dup
67
+ end
68
+ end
69
+
70
+
71
+
72
+ module ClassMethods
73
+ def demongoize(something_from_database)
74
+ new value: demongoize_value(something_from_database)
75
+ end
76
+
77
+ def demongoize_value(value, parameter_type_class=nil)
78
+ value
79
+ end
80
+
81
+ def mongoize(object)
82
+ case object
83
+ when self then object.mongoize
84
+ # TODO: why only these classes? does any scalar fit?
85
+ when Hash, Array, String then new(value: object).mongoize
86
+ else object
87
+ end
88
+ end
89
+
90
+ # Возвращает строку с яваскриптовым кодом для выполнения в БД кода emit операции map-reduce на поле данного типа.
91
+ # Поскольку у различных типов данные в БД хранятся по-разному, и вообще понятие "текущего значения этого типа"
92
+ # различается, мы вынуждены отдельно описывать операцию emit, там где это необходимо. Смысл в том, чтобы этот метод
93
+ # вызвал emit для каждого хранимого значения.
94
+ #
95
+ # Пример: есть поле city типа checkboxes, хранящее список городов, то-есть объекты, описываемые этим полем,
96
+ # могут быть в разных городах. Если мы хотим сделать группировочный запрос (частный случай map-reduce) в БД, чтобы,
97
+ # например, подсчитать кол-во объектов в городах, мы не можем просто сделать emit для всего поля city, поскольку
98
+ # оно содержит список городов. Мы должны вызывать emit для каждого элемента массива (т. е., для каждого идентификатора
99
+ # города). Генерацией этого кода и занимается этот метод.
100
+ #
101
+ # В частности, он используется при подсчете популярных значения различных полей (сколько объявлений, поданых
102
+ # в разные города, и т. п.)
103
+ #
104
+ # По-умолчанию, считаем, что значение, хранимое в поле "#{feild_name}" атомарно, и вызываем emit для него.
105
+ #
106
+ # Подробнее про map-reduce см. http://docs.mongodb.org/manual/applications/map-reduce/
107
+ def emit_map_reduce(feild_name)
108
+ field_name = 'this.' + feild_name
109
+ "if(#{field_name} && #{field_name} != '') emit(#{field_name}, 1)"
110
+ end
111
+
112
+ # Конвертирует имя элемента формы в ID, например facility[cck_params][1][value] -> facility_cck_params_1_value.
113
+ def form_name_to_id(name)
114
+ name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, '_').sub(/_\z/, '')
115
+ end
116
+
117
+ # Методы, которые нужны наследникам (CckForms::ParameterTypeClass::*) для отображения, сортировки и прочей настройки самих типов.
118
+ # Эти методы не нужны обычным объектам - пользователям типов.
119
+
120
+ # CckForms::ParameterTypeClass::Checkboxes -> 'checkboxes'
121
+ # CckForms::ParameterTypeClass::RichText -> 'rich_text'
122
+ def code
123
+ self.to_s.demodulize.underscore
124
+ end
125
+
126
+ # Имя типа, например, для select>option[value]
127
+ def name
128
+ nil
129
+ end
130
+ end
131
+
132
+
133
+
134
+ # Загрузит все классы-наследники.
135
+ # TODO: relies on all classes to reside in this class' directory
136
+ def self.load_type_classes
137
+ return if @type_classes_loaded
138
+
139
+ path = File.dirname(__FILE__)
140
+ Dir[path + '/*.rb'].each do |filename|
141
+ require_dependency filename unless filename.ends_with? '/base.rb'
142
+ end
143
+
144
+ @type_classes_loaded = true
145
+ end
146
+
147
+
148
+
149
+ # "Нормальные" методы, которые будут доступны всем пользователям данного модуля.
150
+
151
+ # -> [[key1, value1], [key2, value2], ...]
152
+ # Нужен для построения ХТМЛ СЕЛЕКТов.
153
+ def valid_values_enum
154
+ valid_values = self.valid_values
155
+ return [] if valid_values.blank?
156
+ result = []
157
+ method_for_enumerating = valid_values.is_a?(Array) ? :each_with_index : :each_pair
158
+ valid_values.send(method_for_enumerating) do |key, value|
159
+ result.push [value, key]
160
+ end
161
+ result
162
+ end
163
+
164
+ # -> "georgian: грузинская, albanian: албанская"
165
+ # Нужен для построения ХТМЛа и строк.
166
+ def valid_values_as_string
167
+ valid_values_enum.map { |enum| "#{enum[1]}: #{enum[0]}" }.join "\n"
168
+ end
169
+
170
+ # Чтобы получать данные из форм и сохранять в БД. Использовать во вьюхах:
171
+ # = f.text_field :valid_values_as_string
172
+ def valid_values_as_string=(string)
173
+ new_valid_values = {}
174
+ string.split("\n").reject { |line| line.blank? }.each do |line|
175
+ splitted = line.split(':', 2)
176
+ new_valid_values[splitted[0].strip] = splitted[1].strip if splitted.length == 2 and splitted[0].present?
177
+ end
178
+ self.valid_values = new_valid_values
179
+ end
180
+
181
+ # Вернет класс, указанный как valid_values_class_name.
182
+ # "City" -> City
183
+ def valid_values_class
184
+ if valid_values_class_name.present?
185
+ if valid_values_class_name.is_a? Class
186
+ valid_values_class_name
187
+ else # если это не строка, то пусть будет выборошено исключение
188
+ valid_values_class_name.constantize
189
+ end
190
+ else
191
+ nil
192
+ end
193
+ end
194
+
195
+ # Существует ли valid_values_class?
196
+ def valid_values_class?
197
+ not valid_values_class.nil?
198
+ end
199
+
200
+ # Если valid_values пустой, и есть valid_values_class, запишет в valid_values все значения
201
+ # из valid_values_class. Считает, что класс похож на ActiveRecord, и значения берет из all.
202
+ def valid_values
203
+ @valid_values ||= begin
204
+ if vv_class = valid_values_class
205
+ valid_values = {}
206
+ vv_class.all.each { |valid_value_object| valid_values[valid_value_object.id] = valid_value_object.to_s }
207
+ valid_values
208
+ end
209
+ end
210
+ end
211
+
212
+ # Строит форму редактирования. Просто input:text со всеми переданными опциями (значение можно определить через
213
+ # options[:value]).
214
+ def build_form(form_builder, options)
215
+ set_value_in_hash options
216
+ form_builder.text_field :value, options
217
+ end
218
+
219
+ # Вернем в виде HTML
220
+ def to_html(options = nil)
221
+ to_s options
222
+ end
223
+
224
+ # Чтобы принимать аргумент options
225
+ def to_s(options = nil)
226
+ value.to_s
227
+ end
228
+
229
+ # Отображение для страниц "было-стало" в админках и пр. (например, тип "карта" можнет вернуть ХТМЛ картинки-миниатюры).
230
+ def to_diff_value(options = nil)
231
+ to_html options
232
+ end
233
+
234
+ # Формируем поисковый запрос для монго на основе запроса (типа мини-DSL язык запроса, свой для каждого типа).
235
+ def search(selectable, field, query)
236
+ selectable.where(field => query.to_s)
237
+ end
238
+
239
+ # Нужен для Rails.application.routes.url_helpers
240
+ def default_url_options
241
+ {}
242
+ end
243
+
244
+ # Реализация перобразования в/из Монго по умолчанию: берем то, что в value
245
+ def mongoize
246
+ value
247
+ end
248
+
249
+ def demongoize_value
250
+ self.class.demongoize_value value, self
251
+ end
252
+
253
+ def demongoize_value!
254
+ self.value = demongoize_value
255
+ end
256
+
257
+
258
+
259
+ private
260
+
261
+ # options[:value] = value
262
+ def set_value_in_hash(options)
263
+ options[:value] = value unless options.has_key? :value
264
+ end
265
+
266
+ # См. ClassMethod.form_name_to_id
267
+ def form_name_to_id(name)
268
+ self.class.form_name_to_id name
269
+ end
270
+
271
+ # Конвертирует имя элемента формы из FormBuilder, см. form_name_to_id.
272
+ def form_builder_name_to_id(form_builder, suffix = '')
273
+ form_name_to_id([form_builder.options[:namespace], form_builder.object_name].compact.join('_') + suffix)
274
+ end
275
+ end
@@ -0,0 +1,24 @@
1
+ class CckForms::ParameterTypeClass::Boolean
2
+ include CckForms::ParameterTypeClass::Base
3
+
4
+ def self.name
5
+ 'Галочка (да/нет)'
6
+ end
7
+
8
+ def value?
9
+ value.present? && value != '0'
10
+ end
11
+
12
+ def mongoize
13
+ value?
14
+ end
15
+
16
+ def to_s(options = nil)
17
+ value? ? 'да' : 'нет'
18
+ end
19
+
20
+ def build_form(form_builder, options)
21
+ set_value_in_hash options
22
+ form_builder.check_box :value, options.merge(value: 1, checked: value?)
23
+ end
24
+ end