dynamic_scaffold 0.10.1 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1bd3db2560f26be70fbc4b28028510b2b4b5e24e645370d405481099ca7db77
4
- data.tar.gz: b50b8c72c7095851c449db62759e80eafef7a125a397550c862c16c27d00bb8b
3
+ metadata.gz: 92e34ceaa702cf769f2d04c1f6abcf6424a0fe243aba09c3a47deb7cb0b34744
4
+ data.tar.gz: 8f14666cf201c3153402d97e12bc12131be4f06cc2afdbed862ccf90d992ff79
5
5
  SHA512:
6
- metadata.gz: 5435007e372185ea6cbc6ed5e60aaca3785eb1e1860dc7060fded062938a1a963dd3f4306363249952a7cd903a593a16e85b327c3eeb097922df394fb9b8dbaa
7
- data.tar.gz: 4c742d086ce908abba330de67d90b025f215de6d5ca79623bd333efb3aa0c4fb28c99e658e1559876944e07bed699fa05979802968ae3d7e5a585240f2d926a0
6
+ metadata.gz: f9547447542b99e5dc317381dfa2f253c26bd983f08ca0438b9fe7cd08de4643345eca7fa587f9312c0302f8743f47def9bad39e0655b0b12f41fc49e93ab50b
7
+ data.tar.gz: c2923675d8504bdf3e3c9b392a8a9d032ac0160036759ce21e6c69259b1d725004ee488266b6374cca09aa9e8a4c224cc19596d3221cf69b6656e82f478d4a9a
data/README.md CHANGED
@@ -9,6 +9,7 @@ The Scaffold system which dynamically generates CRUD and sort functions.
9
9
  * Support image upload with preview.
10
10
  * Support image crop.
11
11
  * Support [globalize](https://github.com/globalize/globalize) form fields.
12
+ * Support [cocoon](https://github.com/nathanvda/cocoon).
12
13
  * This has the views with the Twitter Bootstrap. Support bootstrap3/4.
13
14
  * Customizable and flexible.
14
15
 
@@ -181,11 +182,6 @@ class ShopController < ApplicationController
181
182
  rec.fdate name, '%Y-%m-%d %H:%M:%S'
182
183
  end
183
184
 
184
- # The `label` method also accepts block.
185
- config.list.item(:created_at, style: 'width: 180px').label 'Create Date' do |rec, name|
186
- rec.fdate name, '%Y-%m-%d %H:%M:%S'
187
- end
188
-
189
185
  # The first argument can also be omitted, to display item that is not model attribute.
190
186
  # The block is executed in the context of view, so you can call the method of view.
191
187
  config.list.item do |rec|
@@ -216,8 +212,16 @@ class ShopController < ApplicationController
216
212
 
217
213
  # Default label is I18n model attribute name.
218
214
  config.form.item :text_field, :name
219
- # You can specify `label`.
220
- config.form.item(:text_field, :name).label 'Shop Name'
215
+ # You can specify `label`. You can specify tag attributes for the second argument.
216
+ config.form.item(:text_field, :name).label('Shop Name', class: 'h3')
217
+ # You can pass the block and customize the label tag.
218
+ config.form.item(:text_field, :name).label do |text, depth, attrs|
219
+ # This block is called in the scope of view,
220
+ # So methods and variables that can be used in view are available.
221
+ tag.div class: 'h3 mb-1' do
222
+ tag.label text
223
+ end
224
+ end
221
225
 
222
226
  # You can set default value for new action.
223
227
  config.form.item(:text_field, :name).default('Foo Bar')
@@ -334,6 +338,30 @@ class ShopController < ApplicationController
334
338
  Setting the model for globalize_fields with validates is [here](https://github.com/gomo/dynamic_scaffold/wiki/Model-setting-for-globalize).
335
339
 
336
340
 
341
+ ##### cocoon
342
+
343
+ We support [cocoon](https://github.com/nathanvda/cocoon).
344
+
345
+ ```rb
346
+ # Build the form of related records in the block.
347
+ c.form.item(:cocoon, :shop_memos, add_text: 'Add Memo') do |form|
348
+ # This `form` is a form of the child hierarchy generated by `fields_for`.
349
+ form.item(:hidden_field, :id)
350
+ form.item(:text_field, :title)
351
+ form.item(:text_area, :body)
352
+ end.filter do |records|
353
+ # If you want to sort the related records, please process in the filter block.
354
+
355
+ # The records with empty id are first, then in descending order of id
356
+ records.partition do |rec|
357
+ rec.id.nil?
358
+ end.yield_self do |nils, others|
359
+ nils + others.sort_by {|rec| -rec.id }
360
+ end
361
+ end
362
+ ```
363
+
364
+
337
365
  ### Sorting
338
366
 
339
367
  You can sort records having integer column for order in the list page.
@@ -2,4 +2,4 @@
2
2
  //= require dynamic_scaffold/delete
3
3
  //= require dynamic_scaffold/pagination
4
4
  //= require dynamic_scaffold/sorter
5
- //= require dynamic_scaffold/image
5
+ //= require dynamic_scaffold/image
@@ -1,88 +1,6 @@
1
1
  <%= form_with method: method, model: @record, url: url, local: true do |form| %>
2
2
  <%-dynamic_scaffold.form.items.each do |elem|-%>
3
- <% if elem.needs_rendering?(self)%>
4
- <% if !elem.label? && elem.type?(:hidden_field) %>
5
- <%= elem.render(self, form) %>
6
- <% else %>
7
- <%- errors = elem.errors(@record) -%>
8
- <div class="<%= class_names('form-group', 'has-error': errors.present?) %>">
9
- <label><%= elem.proxy_field.label %></label>
10
- <div class="clearfix">
11
- <%- elem.insert(:before).each do |block| -%>
12
- <%= self.instance_exec(@record, &block) %>
13
- <%-end-%>
14
- <% if elem.type? :collection_check_boxes %>
15
- <%= elem.render(self, form) do |cb|%>
16
- <div class="form-check checkbox">
17
- <%= cb.label class: 'form-check-label' do%>
18
- <%= cb.check_box class: 'form-check-input' %> <%= cb.text %>
19
- <%end%>
20
- </div>
21
- <%end%>
22
- <% elsif elem.type? :collection_radio_buttons %>
23
- <%= elem.render(self, form) do |cb|%>
24
- <div class="form-check radio">
25
- <%= cb.label class: 'form-check-label' do%>
26
- <%= cb.radio_button class: 'form-check-input' %> <%= cb.text %>
27
- <%end%>
28
- </div>
29
- <%end%>
30
- <% elsif elem.type? :carrierwave_image %>
31
- <%- image = form.object.public_send(elem.name) -%>
32
- <div class="js-ds-image-wrapper">
33
- <div class="js-ds-image-preview panel panel-default card mb-1">
34
- <% if elem.options[:removable] %>
35
- <div class="text-right panel-heading card-header">
36
- <button type="button" class="btn btn-outline-danger btn-danger btn-sm js-ds-image-remove">
37
- <%= dynamic_scaffold_icon :times %>
38
- </button>
39
- </div>
40
- <% end %>
41
- <div class="panel-body card-body">
42
- <%- if elem.cropper.present? -%>
43
- <%= form.hidden_field "cropper_#{elem.name}", class: "js-ds-image-cropper", data:{options: elem.cropper.to_json} %>
44
- <div class="text-right" style="height: 24px;">
45
- <span class="js-ds-image-cropper-display badge badge-primary"></span>
46
- </div>
47
- <%- end -%>
48
- <div style="overflow: hidden;<%= elem.preview_image_style %>">
49
- <img src="<%= image.url if image.file.present? %>" alt="" class="img-responsive img-fluid">
50
- </div>
51
- </div>
52
- </div>
53
- <%= form.hidden_field "remove_#{elem.name}", value: "1", disabled:"disabled", class: 'js-ds-image-remove-flag' if elem.options[:removable]%>
54
- <%= elem.render(self, form, 'form-control-file js-ds-image') do |attr|%>
55
- <%= form.file_field(elem.name, attr) %>
56
- <%end%>
57
- <%= form.hidden_field "#{elem.name}_cache" %>
58
- </div>
59
- <% elsif elem.type? :globalize_fields %>
60
- <% elem.locales.each do |locale, name| %>
61
- <%- locale_errors = elem.locale_errors(locale, @record) -%>
62
- <%= form.globalize_fields_for(locale) do |gform|%>
63
- <div class="<%= class_names('ds-globalize-group', 'has-error': locale_errors.present?) %>">
64
- <%= tag.span(elem.lang_attributes('ds-globalize-lang')) do%>
65
- <%= name %>
66
- <%end%>
67
- <%= elem.render(self, gform, class_names('form-control', {'is-invalid': locale_errors.present?})) %>
68
- </div>
69
- <%= render_form_errors(locale_errors) -%>
70
- <%end%>
71
- <% end %>
72
- <% else %>
73
- <%= elem.render(self, form, class_names('form-control', {'is-invalid': errors.present?})) %>
74
- <% end %>
75
- <%- elem.insert(:after).each do |block| -%>
76
- <%= self.instance_exec(@record, &block) %>
77
- <%-end-%>
78
- </div>
79
- <%= render_form_errors(errors) -%>
80
- <%if elem.notes?%>
81
- <div class="form-text text-muted"><%= elem.render_notes(form.object, self) %></div>
82
- <%end%>
83
- </div>
84
- <% end %>
85
- <% end %>
3
+ <%= render 'dynamic_scaffold/bootstrap/form/row', form: form, elem: elem, depth: 0 %>
86
4
  <% end %>
87
5
  <div>
88
6
  <%= link_to dynamic_scaffold_path(:index, request_queries), class: 'btn btn-outline-secondary btn-default back' do%>
@@ -0,0 +1,10 @@
1
+ <div class="card mb-2 nested-fields">
2
+ <div class="card-header text-right p-2">
3
+ <%= link_to_remove_association dynamic_scaffold_icon(:delete), f, class: 'btn btn-danger btn-sm' %>
4
+ </div>
5
+ <div class="card-body">
6
+ <%- items.each do |elem|-%>
7
+ <%= render 'dynamic_scaffold/bootstrap/form/row', form: f, elem: elem, depth: depth + 1 %>
8
+ <% end %>
9
+ </div>
10
+ </div>
@@ -0,0 +1,95 @@
1
+ <% if elem.needs_rendering?(self)%>
2
+ <% if !elem.label? && elem.type?(:hidden_field) %>
3
+ <%= elem.render(self, form) %>
4
+ <% else %>
5
+ <%- errors = elem.errors(form) -%>
6
+ <div class="<%= class_names('form-group', 'has-error': errors.present?) %>">
7
+ <%= elem.render_label(self, depth) %>
8
+ <div class="clearfix">
9
+ <%- elem.insert(:before).each do |block| -%>
10
+ <%= self.instance_exec(@record, &block) %>
11
+ <%-end-%>
12
+ <% if elem.type? :collection_check_boxes %>
13
+ <%= elem.render(self, form) do |cb|%>
14
+ <div class="form-check checkbox">
15
+ <%= cb.label class: 'form-check-label' do%>
16
+ <%= cb.check_box class: 'form-check-input' %> <%= cb.text %>
17
+ <%end%>
18
+ </div>
19
+ <%end%>
20
+ <% elsif elem.type? :collection_radio_buttons %>
21
+ <%= elem.render(self, form) do |cb|%>
22
+ <div class="form-check radio">
23
+ <%= cb.label class: 'form-check-label' do%>
24
+ <%= cb.radio_button class: 'form-check-input' %> <%= cb.text %>
25
+ <%end%>
26
+ </div>
27
+ <%end%>
28
+ <% elsif elem.type? :carrierwave_image %>
29
+ <%- image = form.object.public_send(elem.name) -%>
30
+ <div class="js-ds-image-wrapper">
31
+ <div class="js-ds-image-preview panel panel-default card mb-1">
32
+ <% if elem.options[:removable] %>
33
+ <div class="text-right panel-heading card-header">
34
+ <button type="button" class="btn btn-outline-danger btn-danger btn-sm js-ds-image-remove">
35
+ <%= dynamic_scaffold_icon :times %>
36
+ </button>
37
+ </div>
38
+ <% end %>
39
+ <div class="panel-body card-body">
40
+ <%- if elem.cropper.present? -%>
41
+ <%= form.hidden_field "cropper_#{elem.name}", class: "js-ds-image-cropper", data:{options: elem.cropper.to_json} %>
42
+ <div class="text-right" style="height: 24px;">
43
+ <span class="js-ds-image-cropper-display badge badge-primary"></span>
44
+ </div>
45
+ <%- end -%>
46
+ <div style="overflow: hidden;<%= elem.preview_image_style %>">
47
+ <img src="<%= image.url if image.file.present? %>" alt="" class="img-responsive img-fluid">
48
+ </div>
49
+ </div>
50
+ </div>
51
+ <%= form.hidden_field "remove_#{elem.name}", value: "1", disabled:"disabled", class: 'js-ds-image-remove-flag' if elem.options[:removable]%>
52
+ <%= elem.render(self, form, 'form-control-file js-ds-image') do |attr|%>
53
+ <%= form.file_field(elem.name, attr) %>
54
+ <%end%>
55
+ <%= form.hidden_field "#{elem.name}_cache" %>
56
+ </div>
57
+ <% elsif elem.type? :globalize_fields %>
58
+ <% elem.locales.each do |locale, name| %>
59
+ <%- locale_errors = elem.locale_errors(locale, form) -%>
60
+ <%= form.globalize_fields_for(locale) do |gform|%>
61
+ <div class="<%= class_names('ds-globalize-group', 'has-error': locale_errors.present?) %>">
62
+ <%= tag.span(elem.lang_attributes('ds-globalize-lang')) do%>
63
+ <%= name %>
64
+ <%end%>
65
+ <%= elem.render(self, gform, class_names('form-control', {'is-invalid': locale_errors.present?})) %>
66
+ </div>
67
+ <%= render_form_errors(locale_errors) -%>
68
+ <%end%>
69
+ <% end %>
70
+ <% elsif elem.type? :cocoon %>
71
+ <%= form.fields_for elem.name, elem.build_children(@record) do |child_form| %>
72
+ <%= render 'dynamic_scaffold/bootstrap/form/cocoon', f: child_form, items: elem.form.items, depth: depth %>
73
+ <% end %>
74
+ <%= link_to_add_association(
75
+ "#{dynamic_scaffold_icon(:add)} #{elem.add_text}".html_safe,
76
+ form,
77
+ elem.name,
78
+ class: 'btn btn-outline-secondary btn-sm',
79
+ partial: 'dynamic_scaffold/bootstrap/form/cocoon',
80
+ render_options: { locals: { items: elem.form.items, depth: depth } }
81
+ ) %>
82
+ <% else %>
83
+ <%= elem.render(self, form, class_names('form-control', {'is-invalid': errors.present?})) %>
84
+ <% end %>
85
+ <%- elem.insert(:after).each do |block| -%>
86
+ <%= self.instance_exec(@record, &block) %>
87
+ <%-end-%>
88
+ </div>
89
+ <%= render_form_errors(errors) -%>
90
+ <%if elem.notes?%>
91
+ <div class="form-text text-muted"><%= elem.render_notes(form.object, self) %></div>
92
+ <%end%>
93
+ </div>
94
+ <% end %>
95
+ <% end %>
@@ -1,124 +1,4 @@
1
1
  module DynamicScaffold
2
- class Pagination
3
- attr_reader(
4
- :kaminari_options,
5
- :per_page,
6
- :total_count,
7
- :end_buttons,
8
- :neighbor_buttons,
9
- :gap_buttons,
10
- :highlight_current,
11
- :param_name
12
- )
13
- def initialize(options)
14
- options = {
15
- per_page: 25,
16
- window: 0, # kaminari options
17
- outer_window: 0, # kaminari options
18
- left: 0, # kaminari options
19
- right: 0, # kaminari options
20
- param_name: :page, # kaminari options
21
- total_count: true, # Whether to display total count on active page like `2 / 102`
22
- end_buttons: true, # Whether to display buttons to the first and last page.
23
- neighbor_buttons: true, # Whether to display buttons to the next and prev page.
24
- gap_buttons: false, # Whether to display gap buttons.
25
- highlight_current: false, # Whether to highlight the current page.
26
- }.merge(options)
27
- @kaminari_options = options.extract!(:window, :outer_window, :left, :right, :param_name)
28
- @param_name = @kaminari_options[:param_name]
29
- options.each {|name, value| instance_variable_set("@#{name}", value) }
30
- end
31
-
32
- def page_number(page, records)
33
- return page unless total_count
34
-
35
- "#{page} / #{records.total_pages}"
36
- end
37
-
38
- def page_class(page, _records)
39
- if page.inside_window?
40
- 'inner'
41
- elsif page.left_outer?
42
- 'left-outer'
43
- elsif page.right_outer?
44
- 'right-outer'
45
- end
46
- end
47
- end
48
-
49
- class Title
50
- attr_writer :name
51
-
52
- def initialize(config)
53
- @config = config
54
- @titles_cache = {}
55
- @name = @config.model.model_name.human
56
- end
57
-
58
- def name(&block)
59
- if block_given?
60
- @block = block
61
- elsif !@block.nil?
62
- @config.controller.view_context.instance_exec(&@block)
63
- else
64
- @name
65
- end
66
- end
67
-
68
- def current
69
- public_send(@config.controller.params[:action])
70
- end
71
-
72
- def index
73
- titles(:index)
74
- end
75
-
76
- def edit
77
- titles(:edit)
78
- end
79
-
80
- def new
81
- titles(:new)
82
- end
83
-
84
- def update
85
- titles(:edit)
86
- end
87
-
88
- def create
89
- titles(:new)
90
- end
91
-
92
- private
93
-
94
- def titles(action)
95
- unless @titles_cache[action]
96
- titles = OpenStruct.new
97
- titles.name = name
98
- titles.full = I18n.t("dynamic_scaffold.title.full.#{action}", name: titles.name)
99
- titles.action = I18n.t("dynamic_scaffold.title.action.#{action}")
100
- titles.freeze
101
- @titles_cache[action] = titles
102
- end
103
-
104
- @titles_cache[action]
105
- end
106
- end
107
-
108
- class Vars
109
- def initialize(config)
110
- @config = config
111
- @values = {}
112
- end
113
-
114
- def _register(name, block)
115
- define_singleton_method(name) do
116
- @values[name] ||= @config.controller.instance_exec(&block)
117
- @values[name]
118
- end
119
- end
120
- end
121
-
122
2
  class Config
123
3
  attr_reader(
124
4
  :model,
@@ -172,128 +52,18 @@ module DynamicScaffold
172
52
  end
173
53
  end
174
54
 
175
- class ListBuilder
176
- def initialize(config)
177
- @config = config
178
- @items = []
179
- @sorter = nil
180
- @order = []
181
- @title = nil
182
- @filter = nil
183
- end
184
-
185
- def pagination(options = nil)
186
- @pagination = Pagination.new(options) unless options.nil?
187
-
188
- @pagination
189
- end
190
-
191
- def page_param_name
192
- pagination ? pagination.param_name : nil
193
- end
194
-
195
- def sorter(params = nil)
196
- @sorter = params if params
197
- @sorter
198
- end
199
-
200
- def item(*args, &block)
201
- item = List::Item.new(@config, *args, block)
202
- @items << item
203
- item
204
- end
205
-
206
- def items
207
- if @items.empty?
208
- @config.model.column_names.each do |column|
209
- @items << List::Item.new(@config, column, nil)
210
- end
211
- end
212
- @items
213
- end
214
-
215
- def sorter_attribute
216
- @sorter.keys.first
217
- end
218
-
219
- def sorter_direction
220
- @sorter.values.first
221
- end
222
-
223
- def order(*args)
224
- @order = args unless args.empty?
225
- @order
226
- end
227
-
228
- def title(*args, &block)
229
- if args[0].is_a?(Symbol) || args[0].is_a?(String) || block_given?
230
- @title = {
231
- column_name: args[0],
232
- block: block
233
- }
234
- else
235
- record = args[0]
236
- return @config.controller.view_context.instance_exec(record, &@title[:block]) if @title[:block]
237
-
238
- record.public_send(@title[:column_name])
239
- end
240
- end
241
-
242
- def title?
243
- @title.present?
244
- end
245
-
246
- def build_sql(scope_params)
247
- sql = @config.model.all
248
- sql = sql.where scope_params
249
- ret = @config.controller.instance_exec(sql, &@filter) unless @filter.nil?
250
- sql = ret unless ret.nil?
251
- unless sql.is_a? ::ActiveRecord::Relation
252
- raise(
253
- Error::InvalidOperation,
254
- 'You must return ActiveRecord::Relation from filter block'
255
- )
256
- end
257
- sql
258
- end
259
-
260
- def filter(&block)
261
- @filter = block if block_given?
262
- @filter
263
- end
264
- end
265
-
266
- class FormBuilder
267
- def initialize(config)
268
- @config = config
269
- @items = []
270
- @permit_params = []
271
- end
55
+ class GlobalConfig
56
+ attr_accessor :form
272
57
 
273
- def items
274
- if @items.empty?
275
- @config.model.column_names.each do |column|
276
- type = :text_field
277
- type = :hidden_field if @config.scope && @config.scope.include?(column.to_sym)
278
- @items << Form::Item::SingleOption.new(@config, type, column)
279
- end
280
- end
281
- @items
282
- end
283
-
284
- def permit_params(*params)
285
- if !params.empty?
286
- @permit_params.concat(params)
287
- self
288
- else
289
- @permit_params
58
+ class Form
59
+ def label(&block)
60
+ @label = block if block_given?
61
+ @label
290
62
  end
291
63
  end
292
64
 
293
- def item(type, *args, &block)
294
- item = Form::Item::Base.create(@config, type, *args, &block)
295
- @items << item
296
- item
65
+ def initialize
66
+ @form = Form.new
297
67
  end
298
68
  end
299
69
  end
@@ -27,7 +27,9 @@ module DynamicScaffold
27
27
 
28
28
  carrierwave_image: Form::Item::CarrierWaveImage,
29
29
 
30
- globalize_fields: Form::Item::GlobalizeFields
30
+ globalize_fields: Form::Item::GlobalizeFields,
31
+
32
+ cocoon: Form::Item::Cocoon
31
33
  }.freeze
32
34
 
33
35
  class << self
@@ -39,11 +41,7 @@ module DynamicScaffold
39
41
  )
40
42
  end
41
43
 
42
- if ITEM_TYPES[type] == Form::Item::Block
43
- ITEM_TYPES[type].new(config, type, *args, block)
44
- else
45
- ITEM_TYPES[type].new(config, type, *args)
46
- end
44
+ ITEM_TYPES[type].new(config, type, *args, &block)
47
45
  end
48
46
  end
49
47
 
@@ -59,6 +57,8 @@ module DynamicScaffold
59
57
  @notes = []
60
58
  @multiple = type == :collection_check_boxes || html_attributes[:multiple]
61
59
  @inserts = { before: [], after: [] }
60
+ @label_attributes = {}
61
+ @label_block = nil
62
62
  end
63
63
 
64
64
  def notes?
@@ -85,20 +85,38 @@ module DynamicScaffold
85
85
  !@label.nil?
86
86
  end
87
87
 
88
- def label(label = nil, &block)
89
- if block_given?
90
- raise Error::InvalidParameter, 'Only the block type accepts block.' unless type? :block
88
+ def label(*args, &block)
89
+ return @label || @config.model.human_attribute_name(@name) if args.empty? && block.nil?
91
90
 
92
- @block = block
93
- end
94
- if label
95
- @label = label
96
- self
97
- else
98
- return @label if @label
91
+ @label_attributes = args.extract_options!
92
+ @label = args.first unless args.empty?
93
+ @label_block = block if block_given?
99
94
 
100
- @config.model.human_attribute_name @name
95
+ self
96
+ end
97
+
98
+ def render_label(view, depth)
99
+ if @label_block.present?
100
+ label = view.instance_exec(
101
+ proxy_field.label,
102
+ depth,
103
+ @label_attributes,
104
+ &@label_block
105
+ )
106
+ return label unless label.nil?
101
107
  end
108
+
109
+ # if DynamicScaffold.config.form.label.present?
110
+ # label = view.instance_exec(
111
+ # proxy_field.label,
112
+ # depth,
113
+ # @label_attributes,
114
+ # &DynamicScaffold.config.form.label
115
+ # )
116
+ # return label unless label.nil?
117
+ # end
118
+
119
+ view.tag.label(proxy_field.label, @label_attributes)
102
120
  end
103
121
 
104
122
  def extract_parameters(permitting)
@@ -162,10 +180,10 @@ module DynamicScaffold
162
180
  end
163
181
  end
164
182
 
165
- def errors(record)
166
- msg = record.errors.full_messages_for(proxy_field.name)
183
+ def errors(form)
184
+ msg = form.object.errors.full_messages_for(proxy_field.name)
167
185
  rel = @config.model.reflect_on_all_associations.find {|r| r.foreign_key.to_s == name.to_s }
168
- msg.concat(record.errors.full_messages_for(rel.name)) if rel.present?
186
+ msg.concat(form.object.errors.full_messages_for(rel.name)) if rel.present?
169
187
  msg
170
188
  end
171
189
 
@@ -2,7 +2,7 @@ module DynamicScaffold
2
2
  module Form
3
3
  module Item
4
4
  class Block < Base
5
- def initialize(config, type, name, block)
5
+ def initialize(config, type, name, &block)
6
6
  super(config, type, name, {})
7
7
  @block = block
8
8
  end
@@ -0,0 +1,34 @@
1
+ module DynamicScaffold
2
+ module Form
3
+ module Item
4
+ class Cocoon < Base
5
+ attr_reader :form
6
+ def initialize(config, type, name, options = {})
7
+ super
8
+ @options = options
9
+ @form = FormBuilder.new(config)
10
+ yield(@form)
11
+ end
12
+
13
+ def extract_parameters(permitting)
14
+ permitting << { "#{@name}_attributes": [*@form.items.map(&:name).push(:_destroy)] }
15
+ end
16
+
17
+ def filter(&block)
18
+ @filter = block
19
+ self
20
+ end
21
+
22
+ def add_text
23
+ @options[:add_text] || "Add #{proxy_field.label}"
24
+ end
25
+
26
+ def build_children(record)
27
+ children = record.public_send(name).to_a
28
+ children = @filter.call(children) if @filter.present?
29
+ children
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -31,8 +31,8 @@ module DynamicScaffold
31
31
  build_html_attributes(classnames)
32
32
  end
33
33
 
34
- def locale_errors(locale, record)
35
- record.errors.full_messages_for("#{@item.proxy_field.name}_#{locale}")
34
+ def locale_errors(locale, form)
35
+ form.object.errors.full_messages_for("#{@item.proxy_field.name}_#{locale}")
36
36
  end
37
37
  end
38
38
  end
@@ -1,65 +1,35 @@
1
- module ActionView
2
- module Helpers
3
- class FormBuilder
4
- #
5
- # Helper that renders translations fields
6
- # on a per-locale basis, so you can use them separately
7
- # in the same form and still saving them all at once
8
- # in the same request.
9
- #
10
- # Use it like this:
11
- #
12
- # <h1>Editing post</h1>
13
- #
14
- # <% form_for(@post) do |f| %>
15
- # <%= f.error_messages %>
16
- #
17
- # <h2>English (default locale)</h2>
18
- # <p><%= f.text_field :title %></p>
19
- # <p><%= f.text_field :teaser %></p>
20
- # <p><%= f.text_field :body %></p>
21
- #
22
- # <hr/>
23
- #
24
- # <h2>Spanish translation</h2>
25
- # <% f.globalize_fields_for :es do |g| %>
26
- # <p><%= g.text_field :title %></p>
27
- # <p><%= g.text_field :teaser %></p>
28
- # <p><%= g.text_field :body %></p>
29
- # <% end %>
30
- #
31
- # <hr/>
32
- #
33
- # <h2>French translation</h2>
34
- # <% f.globalize_fields_for :fr do |g| %>
35
- # <p><%= g.text_field :title %></p>
36
- # <p><%= g.text_field :teaser %></p>
37
- # <p><%= g.text_field :body %></p>
38
- # <% end %>
39
- #
40
- # <% end %>
41
- #
42
- def globalize_fields_for(locale, *args, &proc)
43
- raise ArgumentError, 'Missing block' unless block_given?
44
-
45
- @locales ||= []
46
-
47
- first = false
48
- unless @locales.include?(locale)
49
- @locales << locale
50
- first = true
51
- end
52
- object_name = "#{@object_name}[translations_attributes][#{@locales.index(locale) + 1}]"
53
- object = @object.translations.to_a.find {|t| t.locale.to_s == locale.to_s }
1
+ module DynamicScaffold
2
+ class FormBuilder
3
+ def initialize(config)
4
+ @config = config
5
+ @items = []
6
+ @permit_params = []
7
+ end
54
8
 
55
- # The following tags are added only once for the first time.
56
- if first
57
- @template.concat @template.hidden_field_tag("#{object_name}[id]", object ? object.id : '')
58
- @template.concat @template.hidden_field_tag("#{object_name}[locale]", locale)
9
+ def items
10
+ if @items.empty?
11
+ @config.model.column_names.each do |column|
12
+ type = :text_field
13
+ type = :hidden_field if @config.scope && @config.scope.include?(column.to_sym)
14
+ @items << Form::Item::SingleOption.new(@config, type, column)
59
15
  end
16
+ end
17
+ @items
18
+ end
60
19
 
61
- @template.fields_for(object_name, object, *args, &proc)
20
+ def permit_params(*params)
21
+ if !params.empty?
22
+ @permit_params.concat(params)
23
+ self
24
+ else
25
+ @permit_params
62
26
  end
63
27
  end
28
+
29
+ def item(type, *args, &block)
30
+ item = Form::Item::Base.create(@config, type, *args, &block)
31
+ @items << item
32
+ item
33
+ end
64
34
  end
65
35
  end
@@ -19,8 +19,7 @@ module DynamicScaffold
19
19
  end
20
20
  end
21
21
 
22
- def label(label = nil, &block)
23
- @block = block if block
22
+ def label(label = nil)
24
23
  if label
25
24
  @label = label
26
25
  self
@@ -0,0 +1,50 @@
1
+ module DynamicScaffold
2
+ module List
3
+ class Pagination
4
+ attr_reader(
5
+ :kaminari_options,
6
+ :per_page,
7
+ :total_count,
8
+ :end_buttons,
9
+ :neighbor_buttons,
10
+ :gap_buttons,
11
+ :highlight_current,
12
+ :param_name
13
+ )
14
+ def initialize(options)
15
+ options = {
16
+ per_page: 25,
17
+ window: 0, # kaminari options
18
+ outer_window: 0, # kaminari options
19
+ left: 0, # kaminari options
20
+ right: 0, # kaminari options
21
+ param_name: :page, # kaminari options
22
+ total_count: true, # Whether to display total count on active page like `2 / 102`
23
+ end_buttons: true, # Whether to display buttons to the first and last page.
24
+ neighbor_buttons: true, # Whether to display buttons to the next and prev page.
25
+ gap_buttons: false, # Whether to display gap buttons.
26
+ highlight_current: false, # Whether to highlight the current page.
27
+ }.merge(options)
28
+ @kaminari_options = options.extract!(:window, :outer_window, :left, :right, :param_name)
29
+ @param_name = @kaminari_options[:param_name]
30
+ options.each {|name, value| instance_variable_set("@#{name}", value) }
31
+ end
32
+
33
+ def page_number(page, records)
34
+ return page unless total_count
35
+
36
+ "#{page} / #{records.total_pages}"
37
+ end
38
+
39
+ def page_class(page, _records)
40
+ if page.inside_window?
41
+ 'inner'
42
+ elsif page.left_outer?
43
+ 'left-outer'
44
+ elsif page.right_outer?
45
+ 'right-outer'
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,92 @@
1
+ module DynamicScaffold
2
+ class ListBuilder
3
+ def initialize(config)
4
+ @config = config
5
+ @items = []
6
+ @sorter = nil
7
+ @order = []
8
+ @title = nil
9
+ @filter = nil
10
+ end
11
+
12
+ def pagination(options = nil)
13
+ @pagination = List::Pagination.new(options) unless options.nil?
14
+
15
+ @pagination
16
+ end
17
+
18
+ def page_param_name
19
+ pagination ? pagination.param_name : nil
20
+ end
21
+
22
+ def sorter(params = nil)
23
+ @sorter = params if params
24
+ @sorter
25
+ end
26
+
27
+ def item(*args, &block)
28
+ item = List::Item.new(@config, *args, block)
29
+ @items << item
30
+ item
31
+ end
32
+
33
+ def items
34
+ if @items.empty?
35
+ @config.model.column_names.each do |column|
36
+ @items << List::Item.new(@config, column, nil)
37
+ end
38
+ end
39
+ @items
40
+ end
41
+
42
+ def sorter_attribute
43
+ @sorter.keys.first
44
+ end
45
+
46
+ def sorter_direction
47
+ @sorter.values.first
48
+ end
49
+
50
+ def order(*args)
51
+ @order = args unless args.empty?
52
+ @order
53
+ end
54
+
55
+ def title(*args, &block)
56
+ if args[0].is_a?(Symbol) || args[0].is_a?(String) || block_given?
57
+ @title = {
58
+ column_name: args[0],
59
+ block: block
60
+ }
61
+ else
62
+ record = args[0]
63
+ return @config.controller.view_context.instance_exec(record, &@title[:block]) if @title[:block]
64
+
65
+ record.public_send(@title[:column_name])
66
+ end
67
+ end
68
+
69
+ def title?
70
+ @title.present?
71
+ end
72
+
73
+ def build_sql(scope_params)
74
+ sql = @config.model.all
75
+ sql = sql.where scope_params
76
+ ret = @config.controller.instance_exec(sql, &@filter) unless @filter.nil?
77
+ sql = ret unless ret.nil?
78
+ unless sql.is_a? ::ActiveRecord::Relation
79
+ raise(
80
+ Error::InvalidOperation,
81
+ 'You must return ActiveRecord::Relation from filter block'
82
+ )
83
+ end
84
+ sql
85
+ end
86
+
87
+ def filter(&block)
88
+ @filter = block if block_given?
89
+ @filter
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,65 @@
1
+ module ActionView
2
+ module Helpers
3
+ class FormBuilder
4
+ #
5
+ # Helper that renders translations fields
6
+ # on a per-locale basis, so you can use them separately
7
+ # in the same form and still saving them all at once
8
+ # in the same request.
9
+ #
10
+ # Use it like this:
11
+ #
12
+ # <h1>Editing post</h1>
13
+ #
14
+ # <% form_for(@post) do |f| %>
15
+ # <%= f.error_messages %>
16
+ #
17
+ # <h2>English (default locale)</h2>
18
+ # <p><%= f.text_field :title %></p>
19
+ # <p><%= f.text_field :teaser %></p>
20
+ # <p><%= f.text_field :body %></p>
21
+ #
22
+ # <hr/>
23
+ #
24
+ # <h2>Spanish translation</h2>
25
+ # <% f.globalize_fields_for :es do |g| %>
26
+ # <p><%= g.text_field :title %></p>
27
+ # <p><%= g.text_field :teaser %></p>
28
+ # <p><%= g.text_field :body %></p>
29
+ # <% end %>
30
+ #
31
+ # <hr/>
32
+ #
33
+ # <h2>French translation</h2>
34
+ # <% f.globalize_fields_for :fr do |g| %>
35
+ # <p><%= g.text_field :title %></p>
36
+ # <p><%= g.text_field :teaser %></p>
37
+ # <p><%= g.text_field :body %></p>
38
+ # <% end %>
39
+ #
40
+ # <% end %>
41
+ #
42
+ def globalize_fields_for(locale, *args, &proc)
43
+ raise ArgumentError, 'Missing block' unless block_given?
44
+
45
+ @locales ||= []
46
+
47
+ first = false
48
+ unless @locales.include?(locale)
49
+ @locales << locale
50
+ first = true
51
+ end
52
+ object_name = "#{@object_name}[translations_attributes][#{@locales.index(locale) + 1}]"
53
+ object = @object.translations.to_a.find {|t| t.locale.to_s == locale.to_s }
54
+
55
+ # The following tags are added only once for the first time.
56
+ if first
57
+ @template.concat @template.hidden_field_tag("#{object_name}[id]", object ? object.id : '')
58
+ @template.concat @template.hidden_field_tag("#{object_name}[locale]", locale)
59
+ end
60
+
61
+ @template.fields_for(object_name, object, *args, &proc)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,60 @@
1
+ module DynamicScaffold
2
+ class Title
3
+ attr_writer :name
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ @titles_cache = {}
8
+ @name = @config.model.model_name.human
9
+ end
10
+
11
+ def name(&block)
12
+ if block_given?
13
+ @block = block
14
+ elsif !@block.nil?
15
+ @config.controller.view_context.instance_exec(&@block)
16
+ else
17
+ @name
18
+ end
19
+ end
20
+
21
+ def current
22
+ public_send(@config.controller.params[:action])
23
+ end
24
+
25
+ def index
26
+ titles(:index)
27
+ end
28
+
29
+ def edit
30
+ titles(:edit)
31
+ end
32
+
33
+ def new
34
+ titles(:new)
35
+ end
36
+
37
+ def update
38
+ titles(:edit)
39
+ end
40
+
41
+ def create
42
+ titles(:new)
43
+ end
44
+
45
+ private
46
+
47
+ def titles(action)
48
+ unless @titles_cache[action]
49
+ titles = OpenStruct.new
50
+ titles.name = name
51
+ titles.full = I18n.t("dynamic_scaffold.title.full.#{action}", name: titles.name)
52
+ titles.action = I18n.t("dynamic_scaffold.title.action.#{action}")
53
+ titles.freeze
54
+ @titles_cache[action] = titles
55
+ end
56
+
57
+ @titles_cache[action]
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ module DynamicScaffold
2
+ class Vars
3
+ def initialize(config)
4
+ @config = config
5
+ @values = {}
6
+ end
7
+
8
+ def _register(name, block)
9
+ define_singleton_method(name) do
10
+ @values[name] ||= @config.controller.instance_exec(&block)
11
+ @values[name]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module DynamicScaffold
2
- VERSION = '0.10.1'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
@@ -1,10 +1,14 @@
1
1
  require 'dynamic_scaffold/version'
2
2
  require 'dynamic_scaffold/engine'
3
+ require 'dynamic_scaffold/monkeypatch/action_view/helpers/form_builder'
3
4
  require 'dynamic_scaffold/routes'
4
5
  require 'dynamic_scaffold/controller_utilities'
5
6
  require 'dynamic_scaffold/controller'
6
7
  require 'dynamic_scaffold/config'
8
+ require 'dynamic_scaffold/list_builder'
7
9
  require 'dynamic_scaffold/form_builder'
10
+ require 'dynamic_scaffold/title'
11
+ require 'dynamic_scaffold/vars'
8
12
 
9
13
  module DynamicScaffold
10
14
  module Error
@@ -16,6 +20,7 @@ module DynamicScaffold
16
20
 
17
21
  module List
18
22
  autoload :Item, 'dynamic_scaffold/list/item'
23
+ autoload :Pagination, 'dynamic_scaffold/list/pagination'
19
24
  end
20
25
 
21
26
  module Form
@@ -27,6 +32,7 @@ module DynamicScaffold
27
32
  autoload :TwoOptionsWithBlock, 'dynamic_scaffold/form/item/two_options_with_block'
28
33
  autoload :TwoOptions, 'dynamic_scaffold/form/item/two_options'
29
34
  autoload :GlobalizeFields, 'dynamic_scaffold/form/item/globalize_fields'
35
+ autoload :Cocoon, 'dynamic_scaffold/form/item/cocoon'
30
36
  end
31
37
  end
32
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamic_scaffold
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masamoto Miyata
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-22 00:00:00.000000000 Z
11
+ date: 2019-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: classnames-rails-view
@@ -240,6 +240,8 @@ files:
240
240
  - app/views/dynamic_scaffold/bootstrap/_new.html.erb
241
241
  - app/views/dynamic_scaffold/bootstrap/_pagination.html.erb
242
242
  - app/views/dynamic_scaffold/bootstrap/_save_order.html.erb
243
+ - app/views/dynamic_scaffold/bootstrap/form/_cocoon.html.erb
244
+ - app/views/dynamic_scaffold/bootstrap/form/_row.html.erb
243
245
  - app/views/dynamic_scaffold/bootstrap/kaminari/_first_page.html.erb
244
246
  - app/views/dynamic_scaffold/bootstrap/kaminari/_gap.html.erb
245
247
  - app/views/dynamic_scaffold/bootstrap/kaminari/_last_page.html.erb
@@ -263,6 +265,7 @@ files:
263
265
  - lib/dynamic_scaffold/form/item/base.rb
264
266
  - lib/dynamic_scaffold/form/item/block.rb
265
267
  - lib/dynamic_scaffold/form/item/carrier_wave_image.rb
268
+ - lib/dynamic_scaffold/form/item/cocoon.rb
266
269
  - lib/dynamic_scaffold/form/item/globalize_fields.rb
267
270
  - lib/dynamic_scaffold/form/item/single_option.rb
268
271
  - lib/dynamic_scaffold/form/item/two_options.rb
@@ -270,7 +273,12 @@ files:
270
273
  - lib/dynamic_scaffold/form_builder.rb
271
274
  - lib/dynamic_scaffold/icons/fontawesome.rb
272
275
  - lib/dynamic_scaffold/list/item.rb
276
+ - lib/dynamic_scaffold/list/pagination.rb
277
+ - lib/dynamic_scaffold/list_builder.rb
278
+ - lib/dynamic_scaffold/monkeypatch/action_view/helpers/form_builder.rb
273
279
  - lib/dynamic_scaffold/routes.rb
280
+ - lib/dynamic_scaffold/title.rb
281
+ - lib/dynamic_scaffold/vars.rb
274
282
  - lib/dynamic_scaffold/version.rb
275
283
  - lib/generators/dynamic_scaffold/USAGE
276
284
  - lib/generators/dynamic_scaffold/dynamic_scaffold_generator.rb
@@ -299,7 +307,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
299
307
  version: '0'
300
308
  requirements: []
301
309
  rubyforge_project:
302
- rubygems_version: 2.7.7
310
+ rubygems_version: 2.7.6
303
311
  signing_key:
304
312
  specification_version: 4
305
313
  summary: The Scaffold system which dynamically generates CRUD and sort functions.