cck_forms 3.0.1 → 3.1.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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +254 -1
  3. data/lib/cck_forms/date_time.rb +4 -0
  4. data/lib/cck_forms/engine.rb +2 -2
  5. data/lib/cck_forms/form_builder_extensions.rb +2 -1
  6. data/lib/cck_forms/neofiles_denormalize.rb +71 -0
  7. data/lib/cck_forms/parameter_type_class/album.rb +15 -11
  8. data/lib/cck_forms/parameter_type_class/base.rb +58 -63
  9. data/lib/cck_forms/parameter_type_class/boolean.rb +6 -0
  10. data/lib/cck_forms/parameter_type_class/checkboxes.rb +31 -15
  11. data/lib/cck_forms/parameter_type_class/date.rb +5 -0
  12. data/lib/cck_forms/parameter_type_class/date_range.rb +8 -2
  13. data/lib/cck_forms/parameter_type_class/date_time.rb +10 -8
  14. data/lib/cck_forms/parameter_type_class/enum.rb +11 -0
  15. data/lib/cck_forms/parameter_type_class/file.rb +27 -20
  16. data/lib/cck_forms/parameter_type_class/float.rb +3 -0
  17. data/lib/cck_forms/parameter_type_class/image.rb +3 -1
  18. data/lib/cck_forms/parameter_type_class/integer.rb +10 -4
  19. data/lib/cck_forms/parameter_type_class/integer_range.rb +18 -0
  20. data/lib/cck_forms/parameter_type_class/map.rb +23 -23
  21. data/lib/cck_forms/parameter_type_class/phones.rb +14 -15
  22. data/lib/cck_forms/parameter_type_class/string.rb +3 -0
  23. data/lib/cck_forms/parameter_type_class/string_collection.rb +6 -0
  24. data/lib/cck_forms/parameter_type_class/text.rb +4 -0
  25. data/lib/cck_forms/parameter_type_class/time.rb +4 -0
  26. data/lib/cck_forms/parameter_type_class/work_hours.rb +24 -36
  27. data/lib/cck_forms/version.rb +1 -1
  28. data/vendor/assets/javascripts/cck_forms/index.js +2 -0
  29. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b40277032af600f826f2a98dbf141a6f5c2612c
4
- data.tar.gz: 7f1283c1d9b3cc1e5fcdde697c5bcb345d96fd1a
3
+ metadata.gz: 879b5ac5dd9add74c06463c6ea0ea8d9ac919690
4
+ data.tar.gz: 1deecc0d0cb355f2b1ab22881ffea197ff920926
5
5
  SHA512:
6
- metadata.gz: 46bb42b6d45311327227f56ddce705a6626df1796844b88533c6383d38b497f37d8547ca51f482f84c2dcbef74a6bb3ad8b2f37d5e41d07877a901f55e31534a
7
- data.tar.gz: cd1050682502bfd5a6968e8cdc6b60342610604795d23c9248394a1fec1156725428e80a2c69177144cb81fea24acbb6997d1052687f4cc054faaa6f9e16fde9
6
+ metadata.gz: e493ead26cdd54b827078bfdd6ac8c63446dfb1e9e80af6cf42b0f3607d2944101650764d130fd3c1a8c1ff8290051c0c841f015676fe4fc5eefe4d86888cddf
7
+ data.tar.gz: 28a00be50aaecd6165db9dbb3158a1971de0b74d30fe18c97b7fed33ff9e95cb7f527ae9e7bbdad8bd3808f4fc6d25f947fe9c02e83ca846c171e46c99ccf202
data/README.md CHANGED
@@ -1 +1,254 @@
1
- TODO
1
+ CCK Forms
2
+ =========
3
+
4
+ CCK Forms is a companion to the yet-to-be-published CCK gem. (CCK stands for Content Construction Kit — a name borrowed
5
+ from PHP Drupal CMS.)
6
+
7
+ Whilst the CCK gem provides ability to store category-related custom fields in an ActiveModel, this gem defines possible
8
+ field types for that matter (string, enum, image album etc.)
9
+
10
+ As CCK is aimed to simplify storing & editing model fields (especially in admin panels), these custom field types define
11
+ common (and complex) notions like work hours, sets of images, WYSIWYG-capable fields and so on. "Define" here means
12
+ these field types can be stored in CCK-capable models and they all have HTML form templates. The latter implies possible
13
+ standalone usage, for example, if you just need a field of type Image inside your model, you can use CCK Forms without
14
+ CCK and still have nice looking and convenient editor form template.
15
+
16
+ The UI is written in Bootstrap 3 and can not be easily changed, sorry folks.
17
+
18
+
19
+ Criticism
20
+ ---------
21
+
22
+ Generally speaking, this gem combines two aspects: CCK-related storage things and UI editor forms. The latter is very
23
+ project & design dependent and should not be fixed in the gem (especially in the way it is now, with HTML constructed
24
+ in class methods, heavily relying on Bootstrap). Moreover, it may be a good idea to completely decouple UI into separate
25
+ gem/module using, say, Simpleforms to do the frontend job.
26
+
27
+ Then, this gem will only define pure classes to be used either with CCK or standalone as proper "types", like String
28
+ or MapPoint or anything else.
29
+
30
+
31
+ Installation, dependencies
32
+ --------------------------
33
+
34
+ ***Important***: CCK requires a MongoDB database as a mean to store data, so your models must be Mongoid documents.
35
+
36
+ Add CCK Forms and its dependencies to your gemfile:
37
+
38
+ ``` ruby
39
+ # neofiles-related
40
+ gem 'neofiles'
41
+ gem 'ruby-imagespec', git: 'git://github.com/dim/ruby-imagespec.git'
42
+ gem 'mini_magick', '3.7.0'
43
+
44
+ gem 'cck_forms'
45
+ ```
46
+
47
+ This gem requires [Neofiles](https://github.com/ilya-konanykhin/neofiles) for File, Image and Album types. Please read
48
+ its installation instructions, there are some gotchas.
49
+
50
+ Next, include CSS & JS files where needed (say, application.js or admin.js)...
51
+
52
+ ``` javascript
53
+ #= require jquery # neofiles requires jquery
54
+ #= require neofiles
55
+ #= require cck_forms
56
+ ```
57
+
58
+ ... and application.css/admin.css or whatever place you need it in:
59
+
60
+ ``` css
61
+ *= require neofiles
62
+ ```
63
+
64
+ Don't forget to set up Neofiles routing!
65
+
66
+
67
+ Usage with CCK
68
+ --------------
69
+
70
+ Basic usage only, more to come when CCK gem will finally be published.
71
+
72
+ ```ruby
73
+ # in model:
74
+ class Content
75
+ include Mongoid::Document
76
+ include Cck::Cckable
77
+
78
+ cck_config do |c|
79
+ c.category 'news' do |cc|
80
+ cc.string 'title', 'News title'
81
+ cc.enum 'region', valid_values: {world: 'World news', local: 'Local news'}
82
+ cc.image 'announce_pic', 'Announce image', hint: '100x100 only'
83
+ cc.text 'body', 'News article body'
84
+ cc.map 'map_point', 'Where did it happen?'
85
+
86
+ c.require_params %w{title body}
87
+ end
88
+
89
+ c.category 'page' do |cc|
90
+ ...
91
+ end
92
+ end
93
+
94
+ field :category_id, type: String
95
+ end
96
+
97
+ # in controller:
98
+ def edit
99
+ @content = Content.new category_id: params[:category_id]
100
+ end
101
+
102
+ # in view:
103
+ = form_for @content do |ff|
104
+ = cck_fields_for :cck_params
105
+
106
+ # elsewhere:
107
+ content = Content.find(...)
108
+ content.cck_params[:title].to_s
109
+ content.cck_params[:map_point].value[:latitude]
110
+ content.cck_params.to_h.each_pair { |k, v| puts "#{k}: #{v}" } # outputs every CCK field with its ID
111
+ ```
112
+
113
+
114
+ Standalone usage
115
+ ----------------
116
+
117
+ First, create a model and add as many CCK fields as you need:
118
+
119
+ ```ruby
120
+ class User
121
+ include Mongoid::Document
122
+
123
+ field :avatar, type: CckForms::ParameterTypeClass::Image
124
+ field :cv, type: CckForms::ParameterTypeClass::File
125
+ field :phones, type: CckForms::ParameterTypeClass::Phones
126
+
127
+ validates do |doc|
128
+ if doc.avatar.try(:value) && doc.avatar.value.width > 1000
129
+ doc.errors.add :avatar, 'must be no more than 1000 px wide'
130
+ end
131
+
132
+ if doc.phones.try(:value) && doc.phones.value.count > 2
133
+ doc.errors.add :phones, 'must have at most 2 phone numbers'
134
+ end
135
+ end
136
+ end
137
+ ```
138
+
139
+ Then in a view form use helper to output UI:
140
+
141
+ ```slim
142
+ = form_for @user, html: {class: 'form-horizontal'} do |f|
143
+ .form-group
144
+ label.control-label.col-sm-2 Avatar
145
+ .col-sm-9= f.standalone_cck_field :avatar
146
+
147
+ .form-group
148
+ label.control-label.col-sm-2 CV
149
+ .col-sm-9= f.standalone_cck_field :cv, with_desc: true
150
+
151
+ .form-group
152
+ label.control-label.col-sm-2 Phone numbers
153
+ .col-sm-9= f.standalone_cck_field :phones
154
+ end
155
+ ```
156
+
157
+ Use model fields as usual with one exception: to get a field value you need to unwrap its first with call to `value`
158
+ (as it is a wrapper class instance). Assign values directly though.
159
+
160
+ ```ruby
161
+ user = User.first
162
+ puts "User avatar file: #{neofiles_image_path user.avatar.value} (#{user.avatar.value.length} bytes)"
163
+ puts "User phone#{user.phones.try(:value).try(:count).to_i == 0 ? '' : 's'}: #{user.phones.to_s}"
164
+
165
+ user.avatar = Neofiles::Image.find(...)
166
+ user.phones = ['+7 111 222 33 44', '1231231231', {prefix: '+906', code: '1234', number: '223344'}]
167
+ user.save! # should raise exception indicating phones validation failure
168
+ ```
169
+
170
+ Common methods:
171
+ * `value`: returns the real field value. Each field type has its own value, say Phones returns an array of phone numbers
172
+ and Map returns a hash with keys `:latitude, :longitude, :zoom`
173
+ * `to_s`: string representation
174
+ * `to_html`: HTML representation
175
+ * `to_diff_value`: representation in form was/became to show history of changes
176
+ * `search`: returns a Mongoid Criteria filled with search query params specific to this particular field type
177
+ * `build_form`: builds an HTML editor form
178
+
179
+
180
+ Available field types
181
+ ---------------------
182
+
183
+ ***Album***: sortable collection of images.
184
+
185
+ ***Boolean***: checkbox.
186
+
187
+ ***Checkboxes***: several checkboxes. Requires the `valid_values` option.
188
+
189
+ ***Date, DateTime, Time***: date/date&time/time select.
190
+
191
+ ***DateRange***: two sets of selects "date from/till".
192
+
193
+ ***Enum***: select or set of radio buttons. Requires the `valid_values` option.
194
+
195
+ ***File, Image***: single file or image.
196
+
197
+ ***Integer, Float***: numeric input.
198
+
199
+ ***IntegerRange***: two inputs for "number from/till".
200
+
201
+ ***Map***: map point. Two map providers available: Google and Yandex. Google requires an API key.
202
+
203
+ ***Phones***: array of phone numbers.
204
+
205
+ ***String***: text input.
206
+
207
+ ***StringCollection***: set of strings. Represented by a textarea, one line — one string.
208
+
209
+ ***Text***: textarea.
210
+
211
+ ***WorkHours***: complex input to construct work schedule on a weekly basis.
212
+
213
+
214
+ Configuration
215
+ -------------
216
+
217
+ CCK Forms offers the following config options which can be set in `config/application.rb` or `config/environments/*.rb`:
218
+
219
+ ```ruby
220
+ # load all available type classes on application start
221
+ config.cck_forms.load_type_classes = true
222
+
223
+ # extend default form builder to add `standalone_cck_field` method
224
+ config.cck_forms.extend_form_builder = true
225
+
226
+ # how many phone numbers will the edit form contain by default for each field
227
+ config.cck_forms.phones.min_phones_in_form = 3
228
+
229
+ # which area codes are considered as mobile carrier codes (mobile and landline numbers have different HTML forms)
230
+ # the codes listed below are Kazakhstan mobile operators as of year 2016
231
+ config.cck_forms.phones.mobile_codes = %w{ 777 705 771 701 702 775 778 700 707 }
232
+
233
+ # phone number prefix
234
+ # +7 is Russia/Kazakhstan
235
+ config.cck_forms.phones.prefix = '+7'
236
+
237
+ # the glue for concatenating phone number parts: 111[glue]22[glue]33
238
+ config.cck_forms.phones.number_parts_glue = '-'
239
+ ```
240
+
241
+
242
+ Roadmap, TODOs
243
+ --------------
244
+
245
+ - Add new field type: Tags (a collection of — possibly pre-set — strings)
246
+ - Extract HTML templates into separate gem/module
247
+ - Extract map providers or at lease make them configurable
248
+ - Custom phone format on input/ouput (`#build_form, #to_html`)
249
+
250
+
251
+ License
252
+ -------
253
+
254
+ Released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -1,7 +1,11 @@
1
+ # Utility module grouping all date & time related methods
2
+ #
1
3
  module CckForms::DateTime
2
4
  extend ActiveSupport::Concern
3
5
 
4
6
  module DateTimeParser
7
+ # Create Date/DateTime object from its MongoDB/HTML representation: either a hash with keys :year, :month and so on
8
+ # or a Hash with keys (5i), (1i) etc.
5
9
  def date_object_from_what_stored_in_database(value)
6
10
  parsed_value = nil
7
11
  if value.is_a? Hash
@@ -10,8 +10,8 @@ module CckForms
10
10
  config.cck_forms = ActiveSupport::OrderedOptions.new
11
11
 
12
12
  # general
13
- config.cck_forms.load_type_classes = true
14
- config.cck_forms.extend_form_builder = true
13
+ config.cck_forms.load_type_classes = true # if true, require all default type classes
14
+ config.cck_forms.extend_form_builder = true # if true, extend FormBuilder via form_builder_extensions.rb
15
15
 
16
16
  # phones
17
17
  config.cck_forms.phones = ActiveSupport::OrderedOptions.new
@@ -1,4 +1,4 @@
1
- # Примесь для расширения ActionView::Helpers::FormBuilder, чтобы работать с нашими полями:
1
+ # Extends ActionView::Helpers::FormBuilder to add CCK Forms related methods:
2
2
  #
3
3
  # class CckEnabled
4
4
  # field :logo, type: CckForms::ParameterTypeClass::Image
@@ -9,6 +9,7 @@
9
9
  #
10
10
  module CckForms::FormBuilderExtensions
11
11
  ActionView::Helpers::FormBuilder.class_eval do
12
+ # Returns HTML for a standalone CCK field field_name
12
13
  def standalone_cck_field(field_name, options = {})
13
14
  fields_for(field_name) do |ff|
14
15
  @template.raw object.send(field_name).build_form ff, options
@@ -0,0 +1,71 @@
1
+ # Utility class to handle denormalization of Neofile::File object fields to be stored in a host object.
2
+ #
3
+ # This module is included in Album and Image parameter type classes.
4
+ #
5
+ module CckForms::NeofilesDenormalize
6
+ extend ActiveSupport::Concern
7
+
8
+ # Fields that should not be stored in a host object since they are mutable
9
+ NEOFILES_LAZY_ATTRS = %i{ no_wm description is_deleted }
10
+
11
+ module ClassMethods
12
+ # Returns all fields of Neofiles::File obj to be denormalized
13
+ def neofiles_attrs(obj)
14
+ obj.attributes.except *NEOFILES_LAZY_ATTRS
15
+ end
16
+
17
+ # Returns all fields of Neofiles::File to be denormalized or the object ID if the object itself can not be found
18
+ def neofiles_attrs_or_id(obj_or_id, klass = ::Neofiles::File)
19
+ if obj_or_id.present?
20
+ obj, id = if obj_or_id.is_a? klass
21
+ [obj_or_id, nil]
22
+ elsif obj_or_id.is_a?(::String) || obj_or_id.is_a?(::BSON::ObjectId)
23
+ [::Neofiles::File.where(id: obj_or_id).first, obj_or_id.to_s]
24
+ end
25
+
26
+ obj.try { |x| neofiles_attrs(x) } || id
27
+ end
28
+ end
29
+
30
+ # Constructs a Mongoid::Document of class klass with attrs as if it was a usual document loaded from MongoDB
31
+ def neofiles_mock(attrs, klass)
32
+ Mongoid::Factory.from_db(klass, attrs).tap do |obj|
33
+ neofiles_lazy_loadable obj
34
+ end
35
+ end
36
+
37
+ # If attrs_or_id is a Hash, constructs a mock from it. Otherwise, load an object by its ID
38
+ def neofiles_mock_or_load(attrs_or_id, klass = ::Neofiles::File)
39
+ if attrs_or_id.present?
40
+ case attrs_or_id
41
+ when ::String then klass.where(id: attrs_or_id).first
42
+ when ::BSON::ObjectId then klass.where(id: attrs_or_id).first
43
+ when ::Hash then neofiles_mock(attrs_or_id.with_indifferent_access, klass)
44
+ end
45
+ end
46
+ end
47
+
48
+ # Makes obj lazy load fields NEOFILES_LAZY_ATTRS. That is, when these fields are accessed vie getters or
49
+ # read_attribute, make a request to MongoDB to fetch fresh data (all at once)
50
+ def neofiles_lazy_loadable(obj)
51
+ def obj.__lazy_load
52
+ return if @__lazy_loaded
53
+ @__lazy_loaded = true
54
+ from_db = self.class.find(id)
55
+ attributes.merge! from_db.attributes.slice(*NEOFILES_LAZY_ATTRS)
56
+ end
57
+
58
+ def obj.read_attribute(field)
59
+ __lazy_load if field.in? NEOFILES_LAZY_ATTRS
60
+ super(field)
61
+ end
62
+
63
+ NEOFILES_LAZY_ATTRS.each do |field|
64
+ obj.define_singleton_method field do
65
+ __lazy_load
66
+ super()
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,12 +1,14 @@
1
+ # Represents an ordered collection of photos (Image types).
2
+ #
1
3
  class CckForms::ParameterTypeClass::Album
2
4
  include CckForms::ParameterTypeClass::Base
5
+ include CckForms::NeofilesDenormalize
3
6
 
4
7
  def self.name
5
8
  'Альбом'
6
9
  end
7
10
 
8
- # Преобразует данные для Монго.
9
- # Приводит переданный массив или хэш объектов Neofiles::Image или их идентификаторов в массив.
11
+ # Converts input array of Neofiles::Image or IDs to array of hashes (denormalized image data) or IDs
10
12
  def mongoize
11
13
  the_value = value.is_a?(Hash) ? value['value'] : value
12
14
 
@@ -14,28 +16,27 @@ class CckForms::ParameterTypeClass::Album
14
16
  if the_value.respond_to? :each
15
17
  the_value.each do |image|
16
18
  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?
19
+ result.push self.class.neofiles_attrs_or_id(image, ::Neofiles::Image)
18
20
  end
19
21
  end
20
22
 
21
- result
23
+ result.compact
22
24
  end
23
25
 
24
- # Преобразуем данные из Монго.
25
- # Приводим в массив (по идее, массив идентификаторов Neofiles::Image, хотя может быть что угодно).
26
+ # Converts input array of attr hashes or IDs to array if Neofiles::Image (possibly lazy loadable)
26
27
  def self.demongoize_value(value, parameter_type_class=nil)
27
28
  if value.respond_to? :each
28
- value
29
+ value = value.values if value.respond_to? :values
30
+ value.map { |x| neofiles_mock_or_load(x) }.compact
29
31
  else
30
32
  []
31
33
  end
32
34
  end
33
35
 
34
- # Строит форму для обновления файлов альбома.
36
+ # options:
35
37
  #
36
- # Ключи options:
37
- #
38
- # value - текущее значение (идентификатор или объект Neofiles::Album)
38
+ # value - current value (ID or Neofiles::Album)
39
+ # with_desc - if true, show the description edit richtext (default false)
39
40
  def build_form(form_builder, options)
40
41
  set_value_in_hash options
41
42
 
@@ -49,6 +50,7 @@ class CckForms::ParameterTypeClass::Album
49
50
  file_forms = []
50
51
 
51
52
  the_value.each do |image_id|
53
+ image_id = image_id.is_a?(::Neofiles::File) ? image_id.id : image_id
52
54
  file_forms << CckForms::ParameterTypeClass::Image.create_load_form( helper: self,
53
55
  file: image_id,
54
56
  input_name: input_name_prefix,
@@ -84,10 +86,12 @@ class CckForms::ParameterTypeClass::Album
84
86
  HTML
85
87
  end
86
88
 
89
+ # Returns empty string
87
90
  def to_s(options = nil)
88
91
  ''
89
92
  end
90
93
 
94
+ # Returns a collection of 64x64 IMGs
91
95
  def to_diff_value(options = {})
92
96
  view_context = options[:view_context]
93
97
  images_html_list = []