dynamic_scaffold 0.10.1 → 1.0.0

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