abyme 0.2.2 → 0.5.1

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.
data/abyme.gemspec CHANGED
@@ -28,7 +28,21 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_development_dependency "bundler", "~> 2.0"
30
30
  spec.add_development_dependency "rake", "~> 13.0"
31
+ # Tests
31
32
  spec.add_development_dependency "rspec-rails"
32
- spec.add_development_dependency "rails-dummy"
33
+ spec.add_development_dependency 'rails-controller-testing'
34
+ spec.add_development_dependency 'database_cleaner-active_record'
35
+ spec.add_development_dependency 'capybara'
36
+ spec.add_development_dependency 'webdrivers'
37
+ spec.add_development_dependency "generator_spec"
38
+
39
+ # Dummy app
33
40
  spec.add_development_dependency "sqlite3"
41
+ spec.add_development_dependency 'rails'
42
+ spec.add_development_dependency 'pry-rails'
43
+ spec.add_development_dependency 'web-console'
44
+
45
+ spec.add_development_dependency 'puma'
46
+ spec.add_development_dependency 'simplecov'
47
+ spec.add_development_dependency 'simplecov-lcov'
34
48
  end
@@ -1,27 +1,62 @@
1
1
  import { Controller } from 'stimulus';
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ['template', 'associations', 'fields', 'newFields'];
4
+ // static targets = ['template', 'associations', 'fields', 'newFields'];
5
+ // Some applications don't compile correctly with the usual static syntax.
6
+ // Thus implementing targets with standard getters below
7
+
8
+ static get targets() {
9
+ return ['template', 'associations', 'fields', 'newFields'];
10
+ }
5
11
 
6
12
  connect() {
13
+ console.log("Abyme Connected")
14
+
7
15
  if (this.count) {
8
- this.addDefaultAssociations();
16
+ // If data-count is present,
17
+ // add n default fields on page load
18
+
19
+ this.add_default_associations();
9
20
  }
10
21
  }
11
22
 
23
+ // return the value of the data-count attribute
24
+
12
25
  get count() {
13
26
  return this.element.dataset.minCount || 0;
14
27
  }
15
28
 
29
+ // return the value of the data-position attribute
30
+ // if there is no position specified set end as default
31
+
16
32
  get position() {
17
33
  return this.associationsTarget.dataset.abymePosition === 'end' ? 'beforeend' : 'afterbegin';
18
34
  }
19
35
 
36
+ // ADD_ASSOCIATION
37
+
38
+ // this function is call whenever a click occurs
39
+ // on the element with the click->abyme#add_association
40
+ // <button> element by default
41
+
42
+ // if a data-count is present the add_association
43
+ // will be call without an event so we have to check
44
+ // this case
45
+
46
+ // check for limit reached
47
+ // dispatch an event if the limit is reached
48
+
49
+ // - call the function build_html that take care
50
+ // for building the correct html to be inserted in the DOM
51
+ // - dispatch an event before insert
52
+ // - insert html into the dom
53
+ // - dispatch an event after insert
54
+
20
55
  add_association(event) {
21
56
  if (event) {
22
57
  event.preventDefault();
23
58
  }
24
- // check for limit reached
59
+
25
60
  if (this.element.dataset.limit && this.limit_check()) {
26
61
  this.create_event('limit-reached')
27
62
  return false
@@ -33,8 +68,21 @@ export default class extends Controller {
33
68
  this.create_event('after-add');
34
69
  }
35
70
 
71
+ // REMOVE_ASSOCIATION
72
+
73
+ // this function is call whenever a click occurs
74
+ // on the element with the click->abyme#remove_association
75
+ // <button> element by default
76
+
77
+ // - call the function mark_for_destroy that takes care
78
+ // of marking the element for destruction and hiding it
79
+ // - dispatch an event before mark & hide
80
+ // - mark for descrution + hide the element
81
+ // - dispatch an event after mark and hide
82
+
36
83
  remove_association(event) {
37
84
  event.preventDefault();
85
+
38
86
  this.create_event('before-remove');
39
87
  this.mark_for_destroy(event);
40
88
  this.create_event('after-remove');
@@ -42,6 +90,12 @@ export default class extends Controller {
42
90
 
43
91
  // LIFECYCLE EVENTS RELATED
44
92
 
93
+ // CREATE_EVENT
94
+
95
+ // take a stage (String) => before-add, after-add...
96
+ // create a new custom event
97
+ // and dispatch at at the controller level
98
+
45
99
  create_event(stage, html = null) {
46
100
  const event = new CustomEvent(`abyme:${stage}`, { detail: {controller: this, content: html} });
47
101
  this.element.dispatchEvent(event);
@@ -69,9 +123,14 @@ export default class extends Controller {
69
123
  abymeAfterRemove(event) {
70
124
  }
71
125
 
72
- // UTILITIES
126
+ // BUILD HTML
127
+
128
+ // takes the html template and substitutes the sub-string
129
+ // NEW_RECORD for a generated timestamp
130
+ // then if there is a sub template in the html (multiple nested level)
131
+ // set all the sub timestamps back as NEW_RECORD
132
+ // finally returns the html
73
133
 
74
- // build html
75
134
  build_html() {
76
135
  let html = this.templateTarget.innerHTML.replace(
77
136
  /NEW_RECORD/g,
@@ -88,8 +147,15 @@ export default class extends Controller {
88
147
 
89
148
  return html;
90
149
  }
91
-
92
- // mark association for destroy
150
+
151
+ // MARK_FOR_DESTROY
152
+
153
+ // mark association for destruction
154
+ // get the closest abyme--fields from the remove_association button
155
+ // set the _destroy input value as 1
156
+ // hide the element
157
+ // add the class of abyme--marked-for-destroy to the element
158
+
93
159
  mark_for_destroy(event) {
94
160
  let item = event.target.closest('.abyme--fields');
95
161
  item.querySelector("input[name*='_destroy']").value = 1;
@@ -97,20 +163,29 @@ export default class extends Controller {
97
163
  item.classList.add('abyme--marked-for-destroy')
98
164
  }
99
165
 
100
- // check if associations limit is reached
166
+
167
+ // LIMIT_CHECK
168
+
169
+ // Check if associations limit is reached
170
+ // based on newFieldsTargets only
171
+ // persisted fields are ignored
172
+
101
173
  limit_check() {
102
174
  return (this.newFieldsTargets
103
175
  .filter(item => !item.classList.contains('abyme--marked-for-destroy'))).length
104
176
  >= parseInt(this.element.dataset.limit)
105
177
  }
106
178
 
107
- // Add default blank associations at page load
108
- async addDefaultAssociations() {
179
+ // ADD_DEFAULT_ASSOCIATION
180
+
181
+ // Add n default blank associations at page load
182
+ // call sleep function to ensure uniqueness of timestamp
183
+
184
+ async add_default_associations() {
109
185
  let i = 0
110
186
  while (i < this.count) {
111
187
  this.add_association()
112
188
  i++
113
- // Sleep function to ensure uniqueness of timestamp
114
189
  await this.sleep(1);
115
190
  }
116
191
  }
data/lib/abyme.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require "abyme/version"
2
2
  require 'abyme/view_helpers'
3
3
  require 'abyme/engine'
4
+ require 'abyme/controller'
5
+ require 'abyme/action_view_extensions/builder'
4
6
 
5
7
  module Abyme
6
8
  class Error < StandardError; end
@@ -2,31 +2,42 @@ module Abyme
2
2
  class AbymeBuilder < ActionView::Base
3
3
  include ActionView
4
4
 
5
- def initialize(association:, form:, lookup_context:, partial:, &block)
5
+ # If a block is given to the #abymize helper
6
+ # it will instanciate a new AbymeBuilder
7
+ # and pass to it the association name (Symbol)
8
+ # the form object, lookup_context optionaly a partial path
9
+ # then yield itself to the block
10
+
11
+ def initialize(association:, form:, context:, partial:, &block)
6
12
  @association = association
7
13
  @form = form
8
- @lookup_context = lookup_context
14
+ @context = context
15
+ @lookup_context = context.lookup_context
9
16
  @partial = partial
10
17
  yield(self) if block_given?
11
18
  end
19
+
20
+ # RECORDS
21
+
22
+ # calls the #persisted_records_for helper method
23
+ # passing association, form and options to it
12
24
 
13
25
  def records(options = {})
14
26
  persisted_records_for(@association, @form, options) do |fields_for_association|
15
- render_association_partial(fields_for_association, options)
27
+ render_association_partial(@association, fields_for_association, @partial, @context)
16
28
  end
17
29
  end
30
+
31
+ # NEW_RECORDS
32
+
33
+ # calls the #new_records_for helper method
34
+ # passing association, form and options to it
18
35
 
19
36
  def new_records(options = {}, &block)
20
37
  new_records_for(@association, @form, options) do |fields_for_association|
21
- render_association_partial(fields_for_association, options)
38
+ render_association_partial(@association, fields_for_association, @partial, @context)
22
39
  end
23
40
  end
24
41
 
25
- private
26
-
27
- def render_association_partial(fields, options)
28
- partial = @partial || options[:partial] || "abyme/#{@association.to_s.singularize}_fields"
29
- ActionController::Base.render(partial: partial, locals: { f: fields })
30
- end
31
42
  end
32
43
  end
@@ -0,0 +1,17 @@
1
+ module Abyme
2
+ module ActionViewExtensions
3
+ module Builder
4
+ def abyme_for(association, options = {}, &block)
5
+ @template.abyme_for(association, self, options, &block)
6
+ end
7
+
8
+ alias :abymize :abyme_for
9
+ end
10
+ end
11
+ end
12
+
13
+ module ActionView::Helpers
14
+ class FormBuilder
15
+ include Abyme::ActionViewExtensions::Builder
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Abyme
2
+ module Controller
3
+ def abyme_attributes
4
+ return [] if resource_class.nil?
5
+
6
+ resource_class.abyme_attributes
7
+ end
8
+
9
+ private
10
+
11
+ def resource_class
12
+ self.class.name.match(/(.*)(Controller)/)[1].singularize.safe_constantize
13
+ end
14
+ end
15
+ end
data/lib/abyme/engine.rb CHANGED
@@ -4,9 +4,11 @@ module Abyme
4
4
 
5
5
  config.after_initialize do
6
6
  ActiveSupport.on_load :action_view do
7
- # ActionView::Base.send :include, Abyme::ViewHelpers
8
7
  include Abyme::ViewHelpers
9
8
  end
9
+ ActiveSupport.on_load :action_controller do
10
+ include Abyme::Controller
11
+ end
10
12
  end
11
13
  end
12
14
  end
data/lib/abyme/model.rb CHANGED
@@ -1,11 +1,87 @@
1
1
  module Abyme
2
2
  module Model
3
- extend ActiveSupport::Concern
4
-
5
- class_methods do
6
- def abyme_for(association, options = {})
3
+ module ClassMethods
4
+ def abymize(association, permit: nil, reject: nil, **options)
7
5
  default_options = {reject_if: :all_blank, allow_destroy: true}
8
- accepts_nested_attributes_for association, default_options.merge(options)
6
+ nested_attributes_options = default_options.merge(options)
7
+ accepts_nested_attributes_for association, nested_attributes_options
8
+ # Save allow_destroy value for this model/association for later
9
+ save_destroy_option(association, nested_attributes_options[:allow_destroy])
10
+ Abyme::Model.permit_attributes(self.name, association, permit || reject, permit.present?) if permit.present? || reject.present?
11
+ end
12
+
13
+ alias :abyme_for :abymize
14
+
15
+ def abyme_attributes
16
+ Abyme::Model.instance_variable_get(:@permitted_attributes)[self.name]
17
+ end
18
+
19
+ private
20
+
21
+ def save_destroy_option(association, value)
22
+ Abyme::Model.instance_variable_get(:@allow_destroy)[self.name][association] = value
23
+ end
24
+ end
25
+
26
+ @permitted_attributes ||= {}
27
+ @allow_destroy ||= {}
28
+
29
+ attr_accessor :allow_destroy
30
+ attr_reader :permitted_attributes
31
+
32
+ def self.permit_attributes(class_name, association, attributes, permit)
33
+ @permitted_attributes[class_name]["#{association}_attributes".to_sym] = AttributesBuilder.new(class_name, association, attributes, permit)
34
+ .build_attributes
35
+ end
36
+
37
+ def self.included(klass)
38
+ @permitted_attributes[klass.name] ||= {}
39
+ @allow_destroy[klass.name] ||= {}
40
+ klass.extend ClassMethods
41
+ end
42
+
43
+ class AttributesBuilder
44
+ def initialize(model, association, attributes, permit = true)
45
+ @model = model
46
+ @association = association
47
+ @attributes_list = attributes
48
+ @permit = permit
49
+ @association_class = @association.to_s.classify.constantize
50
+ end
51
+
52
+ def build_attributes
53
+ nested_attributes = @association_class.abyme_attributes if @association_class.respond_to? :abyme_attributes
54
+ authorized_attributes = build_default_attributes
55
+ if @permit && @attributes_list == :all_attributes
56
+ authorized_attributes = build_all_attributes(authorized_attributes, nested_attributes)
57
+ elsif @permit
58
+ @attributes_list << nested_attributes unless (nested_attributes.blank? || @attributes_list.include?(nested_attributes))
59
+ authorized_attributes += @attributes_list
60
+ else
61
+ authorized_attributes = build_all_attributes(authorized_attributes, nested_attributes)
62
+ authorized_attributes -= @attributes_list
63
+ end
64
+ authorized_attributes
65
+ end
66
+
67
+ def destroy_allowed?
68
+ Abyme::Model.instance_variable_get(:@allow_destroy).dig(@model, @association)
69
+ end
70
+
71
+ def add_all_attributes
72
+ @association_class.column_names.map(&:to_sym).reject { |attr| [:id, :created_at, :updated_at].include?(attr) }
73
+ end
74
+
75
+ def build_all_attributes(authorized_attributes, nested_attributes)
76
+ authorized_attributes += add_all_attributes
77
+ authorized_attributes << nested_attributes unless (nested_attributes.blank? || authorized_attributes.include?(nested_attributes))
78
+ authorized_attributes
79
+ end
80
+
81
+ def build_default_attributes
82
+ attributes = [:id]
83
+ attributes << :_destroy if destroy_allowed?
84
+ attributes
9
85
  end
10
86
  end
11
87
  end
data/lib/abyme/version.rb CHANGED
@@ -1,9 +1,11 @@
1
+ # :nocov:
1
2
  module Abyme
2
3
  module VERSION
3
4
  MAJOR = 0
4
- MINOR = 2
5
- PATCH = 2
5
+ MINOR = 5
6
+ PATCH = 1
6
7
 
7
8
  STRING = [MAJOR, MINOR, PATCH].join(".")
8
9
  end
9
10
  end
11
+ # :nocov:
@@ -1,28 +1,108 @@
1
+ require_relative "abyme_builder"
2
+
1
3
  module Abyme
2
4
  module ViewHelpers
3
5
 
4
- def abymize(association, form, options = {}, &block)
6
+ # ABYME_FOR
7
+
8
+ # this helper will generate the top level wrapper markup
9
+ # with the bare minimum html attributes (data-controller="abyme")
10
+ # it takes the Symbolized name of the association (plural) and the form object
11
+ # then you can pass a hash of options (see exemple below)
12
+ # if no block given it will generate a default markup for
13
+ # #persisted_records_for, #new_records_for & #add_associated_record methods
14
+ # if a block is given it will instanciate a new AbymeBuilder and pass to it
15
+ # the name of the association, the form object and the lookup_context
16
+
17
+ # == Options
18
+
19
+ # - limit (Integer)
20
+ # you can set a limit for the new association fields to display
21
+
22
+ # - min_count (Integer)
23
+ # set the default number of blank fields to display
24
+
25
+ # - partial (String)
26
+ # to customize the partial path by default #abyme_for will expect
27
+ # a partial to bbe present in views/abyme
28
+
29
+ # - Exemple
30
+
31
+ # <%= abyme_for(:tasks, f, limit: 3) do |abyme| %>
32
+ # ...
33
+ # <% end %>
34
+
35
+ # will output this html
36
+
37
+ # <div data-controller="abyme" data-limit="3" id="abyme--tasks">
38
+ # ...
39
+ # </div>
40
+
41
+ def abyme_for(association, form, options = {}, &block)
5
42
  content_tag(:div, data: { controller: 'abyme', limit: options[:limit], min_count: options[:min_count] }, id: "abyme--#{association}") do
6
43
  if block_given?
7
44
  yield(Abyme::AbymeBuilder.new(
8
- association: association, form: form, lookup_context: self.lookup_context, partial: options[:partial]
45
+ association: association, form: form, context: self, partial: options[:partial]
9
46
  )
10
47
  )
11
48
  else
12
49
  model = association.to_s.singularize.classify.constantize
13
50
  concat(persisted_records_for(association, form, options))
14
51
  concat(new_records_for(association, form, options))
15
- concat(add_association(content: options[:button_text] || "Add #{model}"))
52
+ concat(add_associated_record(content: options[:button_text] || "Add #{model}"))
16
53
  end
17
54
  end
18
55
  end
19
56
 
57
+ alias :abymize :abyme_for
58
+
59
+ # NEW_RECORDS_FOR
60
+
61
+ # this helper is call by the AbymeBuilder #new_records instance method
62
+ # it generates the html markup for new associations fields
63
+ # it takes the association (Symbol) and the form object
64
+ # then a hash of options.
65
+
66
+ # - Exemple
67
+ # <%= abyme_for(:tasks, f) do |abyme| %>
68
+ # <%= abyme.new_records %>
69
+ # ...
70
+ # <% end %>
71
+
72
+ # will output this html
73
+
74
+ # <div data-target="abyme.associations" data-association="tasks" data-abyme-position="end">
75
+ # <template class="abyme--task_template" data-target="abyme.template">
76
+ # <div data-target="abyme.fields abyme.newFields" class="abyme--fields task-fields">
77
+ # ... partial html goes here
78
+ # </div>
79
+ # </template>
80
+ # ... new rendered fields goes here
81
+ # </div>
82
+
83
+ # == Options
84
+ # - position (:start, :end)
85
+ # allows you to specify whether new fields added dynamically
86
+ # should go at the top or at the bottom
87
+ # :end is the default value
88
+
89
+ # - partial (String)
90
+ # to customize the partial path by default #abyme_for will expect
91
+ # a partial to bbe present in views/abyme
92
+
93
+ # - fields_html (Hash)
94
+ # allows you to pass any html attributes to each fields wrapper
95
+
96
+ # - wrapper_html (Hash)
97
+ # allows you to pass any html attributes to the the html element
98
+ # wrapping all the fields
99
+
20
100
  def new_records_for(association, form, options = {}, &block)
21
101
  options[:wrapper_html] ||= {}
22
102
 
23
103
  wrapper_default = {
24
104
  data: {
25
- target: 'abyme.associations',
105
+ abyme_target: 'associations',
26
106
  association: association,
27
107
  abyme_position: options[:position] || :end
28
108
  }
@@ -31,25 +111,69 @@ module Abyme
31
111
  fields_default = { data: { target: 'abyme.fields abyme.newFields' } }
32
112
 
33
113
  content_tag(:div, build_attributes(wrapper_default, options[:wrapper_html])) do
34
- content_tag(:template, class: "abyme--#{association.to_s.singularize}_template", data: { target: 'abyme.template' }) do
114
+ content_tag(:template, class: "abyme--#{association.to_s.singularize}_template", data: { abyme_target: 'template' }) do
35
115
  form.fields_for association, association.to_s.classify.constantize.new, child_index: 'NEW_RECORD' do |f|
36
116
  content_tag(:div, build_attributes(fields_default, basic_fields_markup(options[:fields_html], association))) do
37
117
  # Here, if a block is passed, we're passing the association fields to it, rather than the form itself
38
- block_given? ? yield(f) : render(options[:partial] || "abyme/#{association.to_s.singularize}_fields", f: f)
118
+ # block_given? ? yield(f) : render(options[:partial] || "abyme/#{association.to_s.singularize}_fields", f: f)
119
+ block_given? ? yield(f) : render_association_partial(association, f, options[:partial])
39
120
  end
40
121
  end
41
122
  end
42
123
  end
43
124
  end
125
+
126
+ # PERSISTED_RECORDS_FOR
127
+
128
+ # this helper is call by the AbymeBuilder #records instance method
129
+ # it generates the html markup for persisted associations fields
130
+ # it takes the association (Symbol) and the form object
131
+ # then a hash of options.
132
+
133
+ # - Exemple
134
+ # <%= abyme_for(:tasks, f) do |abyme| %>
135
+ # <%= abyme.records %>
136
+ # ...
137
+ # <% end %>
138
+
139
+ # will output this html
140
+
141
+ # <div>
142
+ # <div data-target="abyme.fields" class="abyme--fields task-fields">
143
+ # ... partial html goes here
144
+ # </div>
145
+ # </div>
146
+
147
+ # == Options
148
+ # - collection (Active Record Collection)
149
+ # allows you to pass an AR collection
150
+ # by default every associated records will be present
151
+
152
+ # - order (Hash)
153
+ # allows you to order the collection
154
+ # ex: order: { created_at: :desc }
155
+
156
+ # - partial (String)
157
+ # to customize the partial path by default #abyme_for will expect
158
+ # a partial to bbe present in views/abyme
159
+
160
+ # - fields_html (Hash)
161
+ # allows you to pass any html attributes to each fields wrapper
162
+
163
+ # - wrapper_html (Hash)
164
+ # allows you to pass any html attributes to the the html element
165
+ # wrapping all the fields
44
166
 
45
167
  def persisted_records_for(association, form, options = {})
46
168
  records = options[:collection] || form.object.send(association)
47
169
  options[:wrapper_html] ||= {}
48
- fields_default = { data: { target: 'abyme.fields' } }
170
+ fields_default = { data: { abyme_target: 'fields' } }
49
171
 
50
172
  if options[:order].present?
51
173
  records = records.order(options[:order])
52
- # Get invalid records
174
+ # by calling the order method on the AR collection
175
+ # we get rid of the records with errors
176
+ # so we have to get them back with the 2 lines below
53
177
  invalids = form.object.send(association).reject(&:persisted?)
54
178
  records = records.to_a.concat(invalids) if invalids.any?
55
179
  end
@@ -57,28 +181,54 @@ module Abyme
57
181
  content_tag(:div, options[:wrapper_html]) do
58
182
  form.fields_for(association, records) do |f|
59
183
  content_tag(:div, build_attributes(fields_default, basic_fields_markup(options[:fields_html], association))) do
60
- block_given? ? yield(f) : render(options[:partial] || "abyme/#{association.to_s.singularize}_fields", f: f)
184
+ block_given? ? yield(f) : render_association_partial(association, f, options[:partial])
61
185
  end
62
186
  end
63
187
  end
64
188
  end
189
+
190
+ # ADD & REMOVE ASSOCIATION
191
+
192
+ # these helpers will call the #create_button method
193
+ # to generate the buttons for add and remove associations
194
+ # with the right action and a default content text for each button
65
195
 
66
- def add_association(options = {}, &block)
196
+ def add_associated_record(options = {}, &block)
67
197
  action = 'click->abyme#add_association'
198
+ options[:content] ||= 'Add Association'
68
199
  create_button(action, options, &block)
69
200
  end
70
201
 
71
- def remove_association(options = {}, &block)
202
+ def remove_associated_record(options = {}, &block)
72
203
  action = 'click->abyme#remove_association'
204
+ options[:content] ||= 'Remove Association'
73
205
  create_button(action, options, &block)
74
206
  end
75
207
 
208
+ alias :add_association :add_associated_record
209
+ alias :remove_association :remove_associated_record
210
+
76
211
  private
212
+
213
+ # CREATE_BUTTON
214
+
215
+ # this helper is call by either add_associated_record or remove_associated_record
216
+ # by default it will generate a button tag.
217
+
218
+ # == Options
219
+ # - content (String)
220
+ # allows you to set the button text
221
+
222
+ # - tag (Symbol)
223
+ # allows you to set the html tag of your choosing
224
+ # default if :button
225
+
226
+ # - html (Hash)
227
+ # to pass any html attributes you want.
77
228
 
78
229
  def create_button(action, options, &block)
79
230
  options[:html] ||= {}
80
231
  options[:tag] ||= :button
81
- options[:content] ||= 'Add Association'
82
232
 
83
233
  if block_given?
84
234
  content_tag(options[:tag], { data: { action: action } }.merge(options[:html])) do
@@ -89,6 +239,11 @@ module Abyme
89
239
  end
90
240
  end
91
241
 
242
+ # BASIC_FIELDS_MARKUP
243
+
244
+ # generates the default html classes for fields
245
+ # add optional classes if present
246
+
92
247
  def basic_fields_markup(html, association = nil)
93
248
  if html && html[:class]
94
249
  html[:class] = "abyme--fields #{association.to_s.singularize}-fields #{html[:class]}"
@@ -99,17 +254,33 @@ module Abyme
99
254
  html
100
255
  end
101
256
 
257
+ # BUILD_ATTRIBUTES
258
+
259
+ # add optionals html attributes without overwritting
260
+ # the default or already present ones
261
+
102
262
  def build_attributes(default, attr)
103
- # ADD NEW DATA ATTRIBUTES VALUES TO THE DEFAULT ONES (ONLY VALUES)
263
+ # Add new data attributes values to the default ones (only values)
104
264
  if attr[:data]
105
265
  default[:data].each do |key, value|
106
266
  default[:data][key] = "#{value} #{attr[:data][key]}".strip
107
267
  end
108
- # ADD NEW DATA ATTRIBUTES (KEYS & VALUES)
268
+ # Add new data attributes (keys & values)
109
269
  default[:data] = default[:data].merge(attr[:data].reject { |key, _| default[:data][key] })
110
270
  end
111
- # MERGE THE DATA ATTRIBUTES TO THE HASH OF HTML ATTRIBUTES
271
+ # Merge data attributes to the hash of html attributes
112
272
  default.merge(attr.reject { |key, _| key == :data })
113
273
  end
274
+
275
+ # RENDER PARTIAL
276
+
277
+ # renders a partial based on the passed path, or will expect a partial to be found in the views/abyme directory.
278
+
279
+ def render_association_partial(association, form, partial = nil, context = nil)
280
+ partial_path = partial ||"abyme/#{association.to_s.singularize}_fields"
281
+ context ||= self
282
+ context.render(partial: partial_path, locals: {f: form})
283
+ end
284
+
114
285
  end
115
286
  end